/* The Efrito desk.
 *
 * The three ramps below — neutral, the Efrito teal that replaced Joy's blue,
 * and the status colours — are still transcribed from the Claude Design
 * project (efrito-design-system-f2458b9d): `tokens/fig-tokens.css` and
 * `tokens/brand.css`. Icon paths in `templates/icons.html` are likewise
 * transcribed from `_ds_bundle.js`. Those are the parts a later diff against
 * the design is still a real comparison of.
 *
 * Everything else deliberately is not, and this is where it stopped being one.
 * The bundle's component geometry was drawn for a marketing-weight UI and this
 * is a desk somebody reads all day, so a minimalist pass replaced it:
 *
 *   - Ten type sizes became the six tokens below, and nothing outside them
 *     appears in this file.
 *   - Boxes stopped nesting. Only a table of numbers is framed; an aside is a
 *     tint with no outline, and a group of form fields is spacing and a rule.
 *   - Zebra striping, 2px dividers and the drop shadow under every input and
 *     card are gone: one hairline was already saying it.
 *   - The page fills the window instead of floating as a rounded card on grey,
 *     which cost 96px of gutter and read as a dashboard mock rather than a
 *     work surface.
 *   - Teal is spent on actions, links, focus and selection only. Where it was
 *     decoration — the active nav item, the "also in the email" panel, the
 *     empty-state badge, a row hover — it is neutral now, so its appearance
 *     still means something.
 *   - The solid button's fill and every teal text colour moved from
 *     primary-500/600 to primary-700: white on primary-500 is 3.1:1, which
 *     fails AA at the 14px the buttons are set in.
 *
 * Written as classes rather than the bundle's React components: this UI is
 * server-rendered Jinja with no build step, and the components are pure
 * styling with no behaviour worth importing a runtime for.
 */

/* ========================================================== tokens */

:root {
  /* Neutral ramp */
  --neutral-050: rgb(251,252,254);
  --neutral-100: rgb(240,244,248);
  --neutral-200: rgb(221,231,238);
  --neutral-300: rgb(205,215,225);
  --neutral-400: rgb(159,166,173);
  --neutral-500: rgb(99,107,116);
  --neutral-600: rgb(85,94,104);
  --neutral-700: rgb(50,56,62);
  --neutral-800: rgb(23,26,28);

  /* Primary — Efrito teal, overriding Joy's blue (brand.css) */
  --primary-050: rgb(228,247,248);
  --primary-100: rgb(205,236,237);
  --primary-200: rgb(163,215,217);
  --primary-400: rgb(80,175,179);
  --primary-500: rgb(54,160,165);
  --primary-600: rgb(52,139,143);
  --primary-700: rgb(39,100,102);

  /* Status ramps */
  --danger-100: rgb(252,228,228);
  --danger-200: rgb(247,197,197);
  --danger-300: rgb(240,152,152);
  --danger-500: rgb(196,28,28);
  --danger-700: rgb(125,18,18);

  --warning-100: rgb(253,240,225);
  --warning-300: rgb(243,200,150);
  --warning-500: rgb(154,91,19);
  --warning-700: rgb(73,43,8);

  --success-100: rgb(227,251,227);
  --success-300: rgb(161,232,161);
  --success-500: rgb(31,122,31);
  --success-700: rgb(10,71,10);

  /* Surfaces. Two, and the second one only ever means "this is an aside". */
  --surface-page: rgb(255,255,255);
  --surface-sunken: rgb(244,247,250);

  /* Two rule weights, both 1px, told apart by tone: inside a block, and
     around something you can click into. */
  --border-hairline: var(--neutral-200);
  --border-control: var(--neutral-300);

  --text-heading: rgb(23,26,28);
  --text-body: rgb(50,56,62);
  --text-muted: rgb(99,107,116);
  --text-icon: var(--neutral-500);

  /* The teal this file actually paints with. The ramps above are kept entire
     because they are the transcription; these four are the four jobs. */
  --teal-ink: var(--primary-700);
  /* The one colour here that is not off the ramp: brand.css stops at 700 and
     the solid button moved onto 700 for contrast, so its hover had nowhere
     darker to go. Written out rather than added to the ramp as a step the
     design does not have. */
  --teal-ink-hover: rgb(30,79,81);
  --teal-wash: var(--primary-050);
  --teal-focus: var(--primary-600);

  /* The scale. Ten sizes made a dense page slow to read; these six are what is
     left, and every rule below uses one of them. */
  --fs-fine: 12px;    /* provenance, the second line of a pair, fine print */
  --fs-meta: 13px;    /* secondary text, small controls, table headings */
  --fs-body: 14px;    /* content, form labels, cells, buttons, nav */
  --fs-figure: 16px;  /* a total, where it has to win against its own row */
  --fs-title: 22px;   /* the one page-heading size */
  --fs-lead: 28px;    /* the single number a page is about */

  /* One radius for everything that is not a pill: a button and the table it
     sits above have no reason to round differently. */
  --radius: 6px;
  --radius-pill: 999px;

  /* The only shadow left. A menu hangs over the page and has to say so;
     nothing that sits *in* the page needs lifting off it. */
  --shadow-menu: 0 6px 20px rgba(21,21,21,0.14);

  --efrito-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  padding: 0;
  background: var(--neutral-050);
  color: var(--text-heading);
  font: 400 var(--fs-body)/1.5 var(--efrito-font);
}

a { color: var(--teal-ink); }
a:hover { color: var(--teal-ink-hover); }

/* Figures that get compared to each other must line up. */
.num, .tally, .money, .stay, .nights, .acct { font-variant-numeric: tabular-nums; }

/* ========================================================== app frame */

/* Full bleed up to the cap, so the worklist and the rate table get the width.
   The hairline sides are what stops it looking unfinished on a wide monitor;
   there is no card, because the desk is the window rather than a document
   lying on one. */
.app {
  max-width: 1600px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  background: var(--surface-page);
  border-left: 1px solid var(--border-hairline);
  border-right: 1px solid var(--border-hairline);
  min-height: 100vh;
}

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px 12px;
  border-right: 1px solid var(--border-hairline);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px 20px;
  text-decoration: none;
}
.brand img { height: 22px; width: auto; display: block; }
.brand span { font-size: var(--fs-body); font-weight: 600; color: var(--text-heading); }

.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--radius);
  color: var(--text-body);
  font-size: var(--fs-body);
  font-weight: 500;
  text-decoration: none;
}
.nav-item:hover { background: var(--neutral-100); color: var(--text-body); }
/* Where you are is weight and fill, not colour: teal is reserved for the
   things you can act on, and five nav items make a poor place to spend it. */
.nav-item.on { background: var(--neutral-100); color: var(--text-heading); font-weight: 600; }
.nav-item .tally { margin-left: auto; font-size: var(--fs-meta); color: var(--text-muted); }

.sidebar-note {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px 10px 4px;
  border-top: 1px solid var(--border-hairline);
}
.sidebar-note .line { display: flex; align-items: center; gap: 8px; }
.sidebar-note strong { font-size: var(--fs-meta); font-weight: 600; color: var(--text-heading); }
.sidebar-note span { font-size: var(--fs-fine); line-height: 1.5; color: var(--text-muted); }
.sidebar-note.ok .line { color: var(--success-500); }
.sidebar-note.bad .line { color: var(--danger-500); }

.main { display: flex; flex-direction: column; min-width: 0; }

.page-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 18px 28px;
  border-bottom: 1px solid var(--border-hairline);
}
.page-head .titles { display: flex; flex-direction: column; gap: 2px; margin-right: auto; min-width: 0; }
.page-head h1 { margin: 0; font-size: var(--fs-title); font-weight: 600; letter-spacing: -0.015em; }
.page-head p { margin: 0; max-width: 80ch; font-size: var(--fs-meta); color: var(--text-muted); }

/* ========================================================== buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 36px;
  padding: 4px 14px;
  border: none;
  border-radius: var(--radius);
  font: 500 var(--fs-body)/1.3 var(--efrito-font);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s ease, box-shadow .15s ease;
}
.btn:focus-visible { outline: 2px solid var(--teal-focus); outline-offset: 2px; }
.btn.sm { min-height: 30px; padding: 2px 10px; gap: 6px; font-size: var(--fs-meta); }

.btn.solid {
  background: var(--teal-ink);
  color: #fff;
  font-weight: 600;
}
.btn.solid:hover { background: var(--teal-ink-hover); color: #fff; }

/* The design had three secondary buttons — outlined, soft and neutral — and a
   screen with one primary action needs one secondary. They share a rule rather
   than being renamed, because which of the three a template reached for still
   records what its author meant by it. */
.btn.outlined,
.btn.soft,
.btn.neutral {
  background: transparent;
  color: var(--text-body);
  box-shadow: inset 0 0 0 1px var(--border-control);
}
.btn.outlined:hover,
.btn.soft:hover,
.btn.neutral:hover { background: var(--neutral-100); color: var(--text-heading); }

.btn.plain { background: transparent; color: var(--teal-ink); }
.btn.plain:hover { background: var(--teal-wash); }

.btn[disabled] { opacity: .55; cursor: default; }

/* ========================================================== chips */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  font: 500 var(--fs-meta)/1.5 var(--efrito-font);
  white-space: nowrap;
  text-decoration: none;
}
.chip.sm { min-height: 20px; padding: 0 6px; font-size: var(--fs-fine); gap: 4px; }

.chip.success { background: var(--success-100); color: var(--success-700); }
.chip.danger  { background: var(--danger-100);  color: var(--danger-700); }
.chip.warning { background: var(--warning-100); color: var(--warning-700); }
.chip.neutral { background: var(--neutral-100); color: var(--text-muted); }
.chip.primary { background: var(--teal-wash);   color: var(--teal-ink); }

.chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  opacity: .6;
  cursor: pointer;
}
.chip-x:hover { opacity: 1; }
a.chip-x { text-decoration: none; }

/* ========================================================== tabs */

.tabs {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 28px;
  border-bottom: 1px solid var(--border-hairline);
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 40px;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-muted);
  text-decoration: none;
}
.tab:hover { color: var(--text-heading); }
.tab.on { font-weight: 600; color: var(--text-heading); box-shadow: inset 0 -2px 0 var(--teal-ink); }
.tab .tally { font-size: var(--fs-meta); color: var(--text-muted); }
.tab .tally.alert { color: var(--danger-500); }
.tabs .aside { margin-left: auto; font-size: var(--fs-meta); color: var(--text-muted); }

/* ========================================================== worklist */

.queue-head,
.queue-row {
  display: grid;
  grid-template-columns: 128px minmax(170px, 1.1fr) 150px minmax(0, 1.5fr) 110px;
  gap: 16px;
  align-items: center;
  padding: 0 28px;
}
.queue-head {
  height: 32px;
  border-bottom: 1px solid var(--border-hairline);
  font-size: var(--fs-meta);
  font-weight: 600;
  color: var(--text-muted);
}
.queue-head .right { text-align: right; }

/* A hairline per row is the whole separation. Striping on top of it was two
   devices doing one job, and it fought with the status chips for attention. */
.queue-row {
  min-height: 48px;
  padding-top: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-hairline);
  text-decoration: none;
  color: inherit;
}
.queue-row:hover { background: var(--surface-sunken); color: inherit; }
.queue-row:focus-visible { outline: 2px solid var(--teal-focus); outline-offset: -2px; }
.queue-row > * { min-width: 0; }

.stack { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.stack.right { text-align: right; }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.who-name { font-size: var(--fs-body); font-weight: 600; color: var(--text-heading); }
.who-sub  { font-size: var(--fs-fine); color: var(--text-muted); }
.stay     { font-size: var(--fs-body); color: var(--text-body); }
.nights   { font-size: var(--fs-fine); color: var(--text-muted); }
.subject  { font-size: var(--fs-body); color: var(--text-body); }
/* The one figure a worklist row is scanned for, so it is the one thing on the
   row set larger than the words around it. */
.money    { font-size: var(--fs-figure); font-weight: 600; color: var(--text-heading); }
.rates-n  { font-size: var(--fs-fine); color: var(--text-muted); }

.footnote { margin: 0; padding: 16px 28px; max-width: 86ch; font-size: var(--fs-meta); color: var(--text-muted); }

/* ========================================================== enquiry */

.enquiry-head {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 28px 18px;
  border-bottom: 1px solid var(--border-hairline);
}
.back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-meta);
  font-weight: 500;
  text-decoration: none;
  width: fit-content;
}
.enquiry-title { display: flex; align-items: flex-start; gap: 20px; }
.enquiry-title .titles { display: flex; flex-direction: column; gap: 6px; min-width: 0; margin-right: auto; }
.enquiry-title h1 { margin: 0; font-size: var(--fs-title); font-weight: 600; letter-spacing: -0.015em; max-width: 46ch; }
.enquiry-title p { margin: 0; font-size: var(--fs-meta); color: var(--text-muted); }
.verdict { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.verdict span { font-size: var(--fs-meta); color: var(--text-muted); }

/* Four facts and three both fit without a breakpoint, which is why this is
   auto-fit: the confirm page states one more than the enquiry does. */
.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 16px 28px;
  padding-top: 14px;
  border-top: 1px solid var(--border-hairline);
}
.fact { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Sentence case, as the templates write it. These were tracked-out capitals,
   which shout a label that exists to be read second. */
.fact .eyebrow { font-size: var(--fs-fine); font-weight: 500; color: var(--text-muted); }
.fact .value { font-size: var(--fs-body); color: var(--text-heading); overflow-wrap: anywhere; }
.fact .value.bad { color: var(--danger-500); font-weight: 500; }
.fact .sub { font-size: var(--fs-fine); color: var(--text-muted); overflow-wrap: anywhere; }
.fact.actions { align-items: flex-end; justify-content: flex-end; }

.panes { display: grid; grid-template-columns: 480px minmax(0, 1fr); align-items: start; }
.pane { display: flex; flex-direction: column; gap: 16px; padding: 20px 28px 32px; min-width: 0; }
.pane.ask { border-right: 1px solid var(--border-hairline); }
.pane h2 { margin: 0; font-size: var(--fs-body); font-weight: 600; }
.pane .lede { margin: 0; max-width: 80ch; font-size: var(--fs-meta); color: var(--text-muted); }
.pane-head { display: flex; align-items: flex-end; gap: 20px; flex-wrap: wrap; }
.pane-head .titles { display: flex; flex-direction: column; gap: 2px; margin-right: auto; min-width: 0; }
/* A form is a run of blocks like any other, and needs the same rhythm between
   them; the rows of buttons are the one thing that stays a row. */
.pane > form:not(.doings) { display: flex; flex-direction: column; gap: 16px; }

/* ========================================================== fields */

.field { display: flex; flex-direction: column; }
.field > .label { font-size: var(--fs-meta); font-weight: 500; color: var(--text-heading); padding-bottom: 5px; }
.field .help { max-width: 70ch; font-size: var(--fs-fine); color: var(--text-muted); padding-top: 4px; }
.field .help.bad { color: var(--danger-500); }
/* A partner login where staff is expected: a remark, not a failure. */
.fact .sub.warn { color: var(--warning-500); }

input[type="text"], input[type="number"], input[type="date"], select {
  display: block;
  width: 100%;
  min-height: 36px;
  padding: 7px 10px;
  font: 400 var(--fs-body)/1.5 var(--efrito-font);
  color: var(--text-body);
  background: var(--surface-page);
  border: none;
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--border-control);
}
input.sm, select.sm { min-height: 30px; padding: 4px 8px; }
input:focus, select:focus {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--teal-focus);
}

.chipbox {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 5px 8px;
  border-radius: var(--radius);
  background: var(--surface-page);
  box-shadow: inset 0 0 0 1px var(--border-control);
}
.chipbox input[type="text"] {
  flex: 1 1 8rem;
  min-width: 8rem;
  min-height: 24px;
  padding: 0;
  background: none;
  box-shadow: none;
}
.chipbox input::placeholder { color: var(--text-muted); }

/* Pickers. The control is a plain <select> or <input list> until the script
   in base.html upgrades it; everything below styles what it builds, so with
   scripting off none of it applies and the browser's own dropdown shows. */
.combo { position: relative; display: block; min-width: 0; }
.combo-native { display: none; }
/* Qualified, so it outranks the padding on input[type="text"] rather than
   letting the text run under the arrow. */
input.combo-input { padding-right: 28px; }
/* In a chip box the typing area starts after the chips, so a menu hung from
   it would sit halfway across the field. Static here, and the chip box itself
   is what the menu measures against. */
.chipbox .combo { position: static; flex: 1 1 10rem; }
.chipbox .combo-input { width: 100%; }

.combo-arrow {
  position: absolute;
  top: 50%;
  right: 11px;
  width: 7px;
  height: 7px;
  margin-top: -5px;
  border-right: 1.5px solid var(--text-icon);
  border-bottom: 1.5px solid var(--text-icon);
  transform: rotate(45deg);
  /* Decoration: a click on it is a click on the field. */
  pointer-events: none;
}
.chipbox .combo-arrow { right: 4px; }

.combo-menu {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  min-width: 260px;
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--surface-page);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--border-hairline), var(--shadow-menu);
}
.combo.up .combo-menu { top: auto; bottom: calc(100% + 4px); }

.combo-option {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 5px 8px;
  border-radius: var(--radius);
  cursor: pointer;
}
/* Both truncate rather than wrap: an account number broken one digit per line
   is not a number. The value shrinks last - it is what gets chosen, while the
   note beside it is only how you recognise it - so the note gives way first
   and by a long way. */
.combo-option .name,
.combo-option .note {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The value takes the width it needs up to two thirds of the row; the note
   lives in what is left. Shrink factors alone were not enough - a long address
   beside a short account number still clipped the number, which is the one
   thing on the row that has to be read in full. */
.combo-option .name {
  flex: 0 0 auto;
  max-width: 66%;
  font-size: var(--fs-body);
  color: var(--text-body);
}
.combo-option .note {
  flex: 0 1 auto;
  margin-left: auto;
  font-size: var(--fs-fine);
  color: var(--text-muted);
}
/* Teal earns its place here: this is the row about to be chosen. */
.combo-option.on { background: var(--teal-wash); }
.combo-option.on .name, .combo-option.on .note { color: var(--teal-ink); }
.combo-none { padding: 6px 8px; font-size: var(--fs-fine); color: var(--text-muted); }

/* Fluid rather than a fixed 220px: the same class pairs two date fields in the
   480px form and two addresses on the full-width quote page, and a capped
   column clipped the address. */
.pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; max-width: 560px; }
.triple { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }

/* One room per rule, no card: the fields inside already carry an edge each,
   and a border around them was a box around boxes. */
.room-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 12px;
  border-top: 1px solid var(--border-hairline);
}
.room-card .row { display: flex; align-items: center; gap: 8px; }
.room-card .row strong { font-size: var(--fs-meta); font-weight: 600; color: var(--text-heading); }
.room-card .row .count { margin-left: auto; font-size: var(--fs-fine); color: var(--text-muted); }

.quoted { margin: -6px 0 0; font-size: var(--fs-meta); color: var(--text-muted); }

.doings { display: flex; align-items: center; gap: 12px; padding-top: 4px; flex-wrap: wrap; }
.doings .when { max-width: 70ch; font-size: var(--fs-fine); color: var(--text-muted); }

/* Narrowing is a different act from searching: it changes what you are shown,
   never what is asked of the platform. The rule says so without a paragraph. */
.narrow {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border-hairline);
}
.narrow .livefilter { display: flex; flex-direction: column; gap: 14px; }
.narrow .chipbox select {
  flex: 1 1 10rem;
  min-width: 10rem;
  min-height: 24px;
  padding: 0;
  background: none;
  box-shadow: none;
}

/* ========================================================== rates */

.headline { display: flex; align-items: baseline; gap: 8px; }
.headline .big {
  font-size: var(--fs-lead);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
}
.headline .unit { font-size: var(--fs-meta); color: var(--text-muted); }

/* A table of numbers is the one thing on a page that keeps an outline. */
.table-wrap {
  border: 1px solid var(--border-control);
  border-radius: var(--radius);
  overflow-x: auto;
  background: var(--surface-page);
}
table { width: 100%; border-collapse: collapse; }
th {
  height: 34px;
  padding: 6px 8px;
  text-align: left;
  border-bottom: 1px solid var(--border-hairline);
  font: 600 var(--fs-meta)/1.5 var(--efrito-font);
  color: var(--text-muted);
  white-space: nowrap;
}
td {
  padding: 8px;
  font: 400 var(--fs-body)/1.5 var(--efrito-font);
  color: var(--text-body);
  vertical-align: middle;
  border-top: 1px solid var(--border-hairline);
}
tbody tr:first-child td { border-top: 0; }
th.right, td.right { text-align: right; }
th:first-child, td:first-child { padding-left: 14px; }
th:last-child, td:last-child { padding-right: 14px; }
/* Outside the worklist's .stack these are two facts about one row, not a
   sentence: a cell is not a flex column, so they have to say it themselves. */
td > .who-name, td > .who-sub { display: block; }
/* Said under the thing it is about, at the size of something read second. */
td .at { display: block; font-size: var(--fs-fine); line-height: 1.5; color: var(--text-muted); }

/* One line per rate, as the design has it; the container scrolls if a room
   name is long rather than the row growing taller. */
.room-cell { display: flex; align-items: center; gap: 10px; padding: 2px 0; white-space: nowrap; }
.room-cell .name { font-size: var(--fs-body); font-weight: 600; color: var(--text-heading); }
.room-cell .at { font-size: var(--fs-fine); color: var(--text-muted); }
.flag { font-size: var(--fs-fine); font-weight: 600; }
.flag.good { color: var(--success-500); }
.flag.warn { color: var(--warning-500); }
.cell { font-size: var(--fs-body); color: var(--text-body); }
.cell.muted { color: var(--text-muted); }
.cell.bad { color: var(--danger-500); }
.cell.total { font-size: var(--fs-figure); font-weight: 600; color: var(--text-heading); }

/* Rates grouped: room, then board, then the rates themselves. Three tracks
   run through all of it - the summaries are grids and the tables are fixed
   layout on the same widths - so "from 2,090" on a closed room sits in the
   same column as the totals it summarises. The widths are the design's
   (Th 104px / 124px) and four rules share them, so changing one means
   changing all four and re-deriving the copied numbers. */
.rate-groups {
  border: 1px solid var(--border-control);
  border-radius: var(--radius);
  background: var(--surface-page);
  overflow: hidden;
}
.rate-head,
.rate-groups summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 104px 124px;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
}
.rate-head {
  height: 34px;
  border-bottom: 1px solid var(--border-hairline);
  font: 600 var(--fs-meta)/1.5 var(--efrito-font);
  color: var(--text-muted);
  white-space: nowrap;
}
.rate-head .right, .rate-groups summary .right { text-align: right; }

.rate-groups summary {
  cursor: pointer;
  list-style: none;
  min-height: 44px;
}
/* The default triangle is replaced by the design's ExpandMore, which rotates. */
.rate-groups summary::-webkit-details-marker { display: none; }
.rate-groups summary:hover { background: var(--surface-sunken); }
.rate-groups summary:focus-visible { outline: 2px solid var(--teal-focus); outline-offset: -2px; }
.rate-groups .who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.rate-groups .chev {
  display: flex;
  color: var(--neutral-400);
  transition: transform .15s ease;
}
.rate-groups details[open] > summary .chev { transform: rotate(180deg); }
.rate-groups .stack { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.rate-groups .name {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-heading);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rate-groups .sub { font-size: var(--fs-fine); color: var(--text-muted); white-space: nowrap; }
.rate-groups .from { display: flex; align-items: baseline; justify-content: flex-end; gap: 4px; }
.rate-groups .from .lead { font-size: var(--fs-fine); color: var(--text-muted); }
.rate-groups .from .num { font-size: var(--fs-body); font-weight: 600; color: var(--text-heading); }

.room-group + .room-group { border-top: 1px solid var(--border-hairline); }
/* The indent is what says a board sits inside a room; a second background
   said it again, in a tint too faint to read as anything but a smudge. */
.board-group { border-top: 1px solid var(--border-hairline); }
.board-group > summary { min-height: 38px; padding-left: 26px; }
.board-group > summary .name { font-weight: 500; }
.board-group > summary .sub { margin-left: 2px; }

/* The rates line up under the same three tracks the summaries use. */
.rate-groups table { table-layout: fixed; background: var(--surface-page); }
/* Including the first one: here the row above is a board summary, not a
   heading that has already drawn the line. */
.rate-groups td,
.rate-groups tbody tr:first-child td { border-top: 1px solid var(--border-hairline); }
.rate-groups td:nth-child(2) { width: 104px; }
.rate-groups td:nth-child(3) { width: 124px; }
.rate-groups td:first-child { padding-left: 42px; }

/* Under the rate: where it comes from, then what it costs to cancel. Both are
   read after the price has done its work, so both sit at the fine size - and
   "Cancellation" is a label for a value rather than a heading, which is why it
   is a word in the line and not a rule above it. */
.cxl {
  display: block;
  padding-top: 2px;
  font-size: var(--fs-fine);
  line-height: 1.5;
  color: var(--text-body);
}
.cxl .eyebrow { margin-right: 6px; font-weight: 600; color: var(--text-muted); }
.cxl .eyebrow.warn { color: var(--warning-500); }
.cxl .unsaid { color: var(--text-muted); }

.smallprint { margin: 0; max-width: 86ch; font-size: var(--fs-fine); color: var(--text-muted); }

/* An aside: a tint and nothing else. It was a bordered card, in a page where
   the bordered things are the tables you read numbers off. */
.callout,
.alsosaid {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border-radius: var(--radius);
  background: var(--surface-sunken);
}
.callout .line { display: flex; align-items: center; gap: 8px; color: var(--text-muted); }
.callout strong,
.alsosaid .eyebrow { font-size: var(--fs-meta); font-weight: 600; color: var(--text-heading); }
.callout span,
.alsosaid span { font-size: var(--fs-meta); color: var(--text-body); }
.callout a { overflow-wrap: anywhere; }

details.original {
  padding-top: 14px;
  border-top: 1px solid var(--border-hairline);
}
details.original summary { cursor: pointer; font-size: var(--fs-meta); font-weight: 600; color: var(--text-heading); }
details.original .from { margin: 12px 0 4px; font-size: var(--fs-fine); color: var(--text-muted); }
details.original > pre {
  margin: 0 0 16px;
  font: 400 var(--fs-body)/1.65 var(--efrito-font);
  color: var(--text-body);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-width: 78ch;
}

/* What is about to be sent, and the configuration a check ran against: read as
   a block of text rather than as part of the page around it. */
.preview {
  margin: 0;
  padding: 14px 16px;
  border-radius: var(--radius);
  background: var(--surface-sunken);
  overflow-x: auto;
}
.preview pre {
  margin: 0;
  font: 400 var(--fs-meta)/1.7 var(--efrito-font);
  color: var(--text-body);
  white-space: pre-wrap;
  max-width: 86ch;
}

/* ========================================================== empty */

.empty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  padding: 56px 28px;
  max-width: 62ch;
}
/* The icon, not the icon in a tinted tile: an empty screen is the one place
   with nothing to compete with, so it needs no help being seen. */
.empty .badge { display: inline-flex; color: var(--neutral-400); }
.empty h2, .empty strong { margin: 0; font-size: var(--fs-figure); font-weight: 600; letter-spacing: -0.01em; }
.empty p, .empty > span:not(.badge) { margin: 0; font-size: var(--fs-body); line-height: 1.6; color: var(--text-body); }
.empty .buttons { display: flex; gap: 12px; padding-top: 4px; flex-wrap: wrap; }

/* ========================================================== notices */

.notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0;
  padding: 10px 28px;
  font-size: var(--fs-meta);
  color: var(--text-body);
  border-bottom: 1px solid var(--border-hairline);
}
/* Flex children default to min-width:auto, which lets a long token push the
   whole page wider than the viewport rather than wrapping. */
.notice > span { min-width: 0; max-width: 104ch; overflow-wrap: anywhere; }
/* A tinted band ends where its colour ends; a rule under it as well was a
   second edge on one boundary. */
.notice.warn { background: var(--warning-100); color: var(--warning-700); border-bottom: 0; }
.notice.bad { background: var(--danger-100); color: var(--danger-700); border-bottom: 0; }
.notice code { font-size: var(--fs-fine); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* Inside a pane it is a block among blocks, not a band across the page — the
   pane has already paid the 28px gutter once, and an untinted one needs the
   aside's own tint to read as one. */
.pane > .notice { padding: 10px 14px; border-radius: var(--radius); border-bottom: 0; }
.pane > .notice:not(.warn):not(.bad) { background: var(--surface-sunken); }

/* ========================================================== basket */

/* The pick sits inside the first column rather than adding a fourth: the three
   tracks (minmax(0,1fr) 104px 124px) are the design's Th widths and the room
   and board summaries share them, so a column added here would have to be
   added in four rules at once and the copied values re-derived. */
.room-cell .pick { display: flex; align-items: center; }
.room-cell input[type="checkbox"] {
  width: 16px;
  height: 16px;
  min-height: 0;
  margin: 0;
  accent-color: var(--teal-ink);
}
/* `input:focus` above sets an inset box-shadow, which on a checkbox reads as
   an empty text field. A ring is what a checkbox wants. */
.room-cell input[type="checkbox"]:focus {
  box-shadow: none;
  outline: 2px solid var(--teal-focus);
  outline-offset: 2px;
}

/* There was no textarea rule at all before the quote page: the form block
   above styles input[type=text|number|date] and select only. Same inset ring
   idiom, and vertical resize because a long quote is the normal case. */
textarea {
  display: block;
  width: 100%;
  min-height: 300px;
  padding: 12px 14px;
  font: 400 var(--fs-body)/1.7 var(--efrito-font);
  color: var(--text-body);
  background: var(--surface-page);
  border: none;
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--border-control);
  resize: vertical;
}
textarea:focus {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--teal-focus);
}
textarea[readonly] { color: var(--text-muted); background: var(--surface-sunken); }

.basket { display: flex; flex-direction: column; gap: 16px; }
.basket form { display: flex; flex-direction: column; gap: 16px; }
.basket .sell input { width: 104px; text-align: right; }
.basket .triple { align-items: end; }
.basket .triple .field .cell { padding-top: 4px; }

/* Announced to a screen reader, out of the way of everyone else. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ========================================================== responsive */

@media (max-width: 1180px) {
  .panes { grid-template-columns: minmax(0, 1fr); }
  .pane.ask { border-right: 0; border-bottom: 1px solid var(--border-hairline); }
}

@media (max-width: 1000px) {
  /* Stacked, the sidebar is a row of links and `min-height: 100vh` has to land
     somewhere: auto rows would share it out and stretch the nav's wrapped
     lines apart by a hundred-odd pixels of nothing. The list takes what it
     needs and the page underneath absorbs the rest. */
  .app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); border: 0; }
  .sidebar { flex-direction: row; flex-wrap: wrap; align-content: flex-start; align-items: center; border-right: 0; border-bottom: 1px solid var(--border-hairline); min-width: 0; }
  .brand { padding: 4px 8px; }
  .sidebar-note { margin-top: 0; flex-basis: 100%; padding-bottom: 0; }
  .queue-head { display: none; }
  .queue-row { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .stack.right { text-align: left; }

  /* The header and its action stack rather than running off the edge, and the
     tabs scroll sideways on their own instead of widening the page. */
  .page-head, .enquiry-title { flex-wrap: wrap; }
  .page-head .titles, .enquiry-title .titles { flex-basis: 100%; }
  .tabs { overflow-x: auto; gap: 16px; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs .aside { display: none; }
  .tab { flex: 0 0 auto; }
  .notice { padding-left: 16px; padding-right: 16px; }
}

@media (max-width: 560px) {
  .pair, .triple { grid-template-columns: minmax(0, 1fr); }
  .page-head, .enquiry-head, .pane, .tabs, .queue-row, .footnote { padding-left: 16px; padding-right: 16px; }
  /* Below this width the browser is a phone's, and anything under 16px in a
     field makes it zoom the page on focus. */
  input[type="text"], input[type="number"], input[type="date"], select, textarea { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* The door: sign in, and first run.
 *
 * A centred column rather than a card, because the rule above is that only a
 * table of numbers is framed. The sidebar is absent rather than empty - a page
 * nobody has signed in to should not be listing what this deployment runs.
 */
.app.door { grid-template-columns: minmax(0, 1fr); }
.doorway {
  width: 100%;
  max-width: 340px;
  margin: 12vh auto 0;
  padding: 0 24px 64px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.doorway.wide { max-width: 520px; }
.doormark { width: 32px; height: 32px; }
.doorway h1 { font-size: var(--fs-lead); font-weight: 600; color: var(--text-heading); }
.doorway .lede { font-size: var(--fs-body); line-height: 1.6; color: var(--text-muted); }
.doorway form { display: flex; flex-direction: column; gap: 16px; }
.doorway .doings { margin-top: 8px; }
.doorway .btn.solid { width: 100%; justify-content: center; }

/* Who is signed in, under the check note. */
.whoami {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--border-hairline);
}
.whoami .who {
  font-size: var(--fs-fine);
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.whoami form { flex: none; }

/* People, and the two password affordances on Settings. */
.rowdoings { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.rowdoings form.inline { display: flex; align-items: center; gap: 4px; }
.rowdoings input[type="password"] { width: 150px; }
.field .help .forget { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; }
.field .help .forget input { width: auto; min-height: 0; }
