/*
 * Tathkarah ONE — the operations desk.
 *
 * ── Where this comes from ──────────────────────────────────────────────────
 *
 * The identity is the company's own, read off tathkarah.com rather than invented: the
 * orange of the logo mark (#F15D24), the navy of the site (#273863), and the two faces the
 * brand already sets its words in — Nunito for Latin, Kufi for Arabic.
 *
 * The world is an airport. Three things come from it and none of them are decoration:
 *
 *   The departures board. What needs a person is the reason anyone opens this screen at
 *   9am, so the queue is a board — dark, mono, high-contrast — set into a light console.
 *   Like a real agency: a bright office with a board on the wall. Status is the loudest
 *   thing in the row, exactly as it is on a real board.
 *
 *   Aviation's light language is already the status palette. Green centreline means go,
 *   amber edge means caution, red stop bar means halt. Every operations person reads it
 *   without being taught, so the semantic colours are named for the lights they are.
 *   `--unlit` is the colour of a lamp that is not on: it tells you nothing, which is
 *   precisely what "we have not been told" means — and why it is never zero.
 *
 *   تذكرة means "ticket". A piece of business you can pick up is drawn as a stub, with a
 *   counterfoil and a perforation, because that is what a detachable single item looks
 *   like everywhere else in this industry.
 *
 * Arabic is the default direction. Every rule uses logical properties, so one stylesheet
 * serves both directions and neither can drift out of date.
 */

:root {
  /* ── the brand, sampled from the logo file rather than remembered ──────────
   *
   * Two colours, and the console used to have only one of them. `logo.webp` is an orange
   * globe with a **blue** aircraft flying out of it and `.com` set in the same blue.
   * Sampling the file gives #f15d24 and #347bbe exactly. The blue appeared nowhere in
   * this stylesheet — every structural surface was a navy invented here — so the desk was
   * an orange-accented admin theme rather than Tathkarah's. It has a job now: the rail is
   * the blue driven down to a ground, and orange is kept for the one thing orange should
   * mean, which is *this needs a person*. */
  --sunset: #f15d24;          /* the globe — and the only colour allowed to shout */
  --sunset-deep: #c8420f;
  --sunset-lift: #ff8b52;
  --sunset-wash: #fdeee6;

  --sky: #347bbe;             /* the aircraft, and the second half of the identity */
  --sky-lift: #6aa4dc;
  --sky-deep: #1d5a96;
  --sky-wash: #e9f2fb;

  /* The rail, and every deep field: the brand blue taken down to something a white
     wordmark can sit on all day without glare. Same hue, not a neutral navy. */
  --navy: #0e2b4a;
  --navy-2: #16395f;
  --navy-3: #1d4977;

  --dusk: #1d4e82;            /* kept: the old name, now the brand blue's deep end */
  --dusk-wash: #e9f2fb;

  /* ── the paper this desk works on ──────────────────────────────────────── */
  /*
   * These were warm — a tan page (#f4f2ee) and tan inset surfaces (#edeae4), on the
   * argument that ticket stock is warm. Two things were wrong with it.
   *
   * **It did not match the rail.** The rail is the brand blue driven down to a ground.
   * A tan canvas beside a navy rail is two different companies on one screen, and the
   * eye reads the seam between them before it reads anything on the page.
   *
   * **Tan inset surfaces make every form look dirty.** `--counterfoil` is the ground of
   * every input, select and textarea in the console. At #edeae4 a form is a column of
   * grey-brown boxes; the screen reads as unfinished rather than as paper.
   *
   * So the neutrals carry the brand blue instead of the brand orange — the same few
   * degrees off true grey, pulled the other way. White cards still read as paper,
   * because what makes them read as paper is the contrast against the ground and the
   * shadow under them, not the ground's temperature.
   */
  --terminal: #eef1f6;        /* the page */
  --stock: #ffffff;           /* a card — printed ticket white */
  --counterfoil: #f5f7fa;     /* every inset surface: fields, table heads, wells */
  /* A recess in a card: a track, a groove, a well. It must stay visibly distinct
     from the card behind it in BOTH themes — --counterfoil is a hair away from
     --stock in the dark palette, so a bar drawn on it disappears. */
  --groove: #e6eaf1;
  --rule: #e3e8ef;
  --rule-strong: #c5cdda;

  /* Four levels of text, never two. Deepened: the old primary was #141b33 on a tan page,
     which is a contrast the eye has to work at all day. This is nearly black with the
     brand blue still in it. */
  --ink: #0d1526;             /* primary  */
  --ink-2: #2f3c55;           /* secondary */
  --ink-3: #626e88;           /* metadata */
  --ink-4: #97a1b5;           /* disabled */

  /* ── the board ─────────────────────────────────────────────────────────── */
  --board: #101828;
  --board-2: #1a2438;
  --board-rule: #2a3550;
  --board-ink: #e8ecf7;
  --board-ink-2: #8e9ab8;

  /* ── aviation's light language ─────────────────────────────────────────── */
  --taxiway: #1f8a5b;   --taxiway-wash: #e6f5ee;   --taxiway-lit: #35d68a;
  --edge:    #b57407;   --edge-wash:    #fdf1dd;   --edge-lit:    #ffbe4d;
  --caution: #c2410c;   --caution-wash: #fdeee6;   --caution-lit: #ff8b52;
  --stopbar: #c02348;   --stopbar-wash: #fdeaef;   --stopbar-lit: #ff5c7d;
  --approach:#2563a8;   --approach-wash:#e9f1fb;   --approach-lit:#6aa9e6;
  /* A lamp that is not on. Not "off" — unlit. It reports nothing. */
  --unlit:   #6f7593;   --unlit-wash:   #edeff5;

  --font-ar: 'Noto Kufi Arabic', 'Droid Arabic Kufi', 'Segoe UI', system-ui, sans-serif;
  --font-en: 'Nunito', system-ui, -apple-system, 'Segoe UI', sans-serif;
  /* The brand sets no monospace. The board needs one — figures and codes line up or they
     are useless — so this is a technical face doing a job the brand faces cannot. */
  --font-board: 'IBM Plex Mono', ui-monospace, 'SF Mono', monospace;

  /* Type scale, 1.25 from a 14px body, rounded to whole pixels. */
  --t-caption: 11px; --t-body: 14px; --t-h4: 16px; --t-h3: 18px;
  --t-h2: 22px; --t-h1: 28px; --t-display: 40px;

  /* Density: workbench-tight. One number, held everywhere. */
  --u: 4px;
  --pad-tight: 10px; --pad: 14px; --pad-open: 20px;

  /* Wide enough to spell every destination out. It was 4.75rem of icons
     and tooltips, which is how forty-two things become unfindable. */
  --rail: 14.5rem;
  --gap: 14px;
  --radius: 10px;
  --radius-sm: 7px;
  /* A hairline ring plus a short ambient, rather than one long soft blur. The blur on
     its own reads as fog under the card; the ring is what makes an edge look printed.
     Both stops are the ink colour, so a card never casts a grey shadow on a blue page. */
  --shadow: 0 0 0 1px rgba(13, 21, 38, 0.04), 0 1px 2px rgba(13, 21, 38, 0.05),
            0 4px 12px -6px rgba(13, 21, 38, 0.12);
  --shadow-lift: 0 0 0 1px rgba(13, 21, 38, 0.05), 0 4px 10px -2px rgba(13, 21, 38, 0.1),
                 0 18px 40px -16px rgba(13, 21, 38, 0.28);
  --speed: 150ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Night shift. The console dims to the board's own colours rather than inverting — the
   desk is lit by the board, which is what actually happens in an operations room. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --terminal: #131a2b; --stock: #1b2438; --counterfoil: #1a2437;
    --groove: #2b3550;
  --rule: #26314a; --rule-strong: #36446a;
    --ink: #e9edf8; --ink-2: #c2cade; --ink-3: #919bb8; --ink-4: #667192;
    --board: #070c17; --board-2: #121a2b; --board-rule: #232e47;
    --sunset-wash: #38190d; --dusk-wash: #18213a;
    --taxiway: #4cc08a; --taxiway-wash: #0f2d1e;
    --edge: #e0a33a;    --edge-wash: #2f2109;
    --caution: #f07a4a; --caution-wash: #33180c;
    --stopbar: #f0577a; --stopbar-wash: #33111d;
    --approach: #6aa9e6;--approach-wash: #102238;
    --unlit: #8b91b0;   --unlit-wash: #1d2337;
    --shadow: 0 1px 2px rgba(0,0,0,0.45), 0 10px 28px -14px rgba(0,0,0,0.65);
    --shadow-lift: 0 2px 8px rgba(0,0,0,0.55), 0 18px 44px -18px rgba(0,0,0,0.75);
  }
}
:root[data-theme="dark"] {
  --terminal: #131a2b; --stock: #1b2438; --counterfoil: #1a2437;
  --groove: #2b3550;
  --rule: #26314a; --rule-strong: #36446a;
  --ink: #e9edf8; --ink-2: #c2cade; --ink-3: #919bb8; --ink-4: #667192;
  --board: #070c17; --board-2: #121a2b; --board-rule: #232e47;
  --sunset-wash: #38190d; --dusk-wash: #18213a;
  --taxiway: #4cc08a; --taxiway-wash: #0f2d1e;
  --edge: #e0a33a;    --edge-wash: #2f2109;
  --caution: #f07a4a; --caution-wash: #33180c;
  --stopbar: #f0577a; --stopbar-wash: #33111d;
  --approach: #6aa9e6;--approach-wash: #102238;
  --unlit: #8b91b0;   --unlit-wash: #1d2337;
  --shadow: 0 1px 2px rgba(0,0,0,0.45), 0 10px 28px -14px rgba(0,0,0,0.65);
  --shadow-lift: 0 2px 8px rgba(0,0,0,0.55), 0 18px 44px -18px rgba(0,0,0,0.75);
}

* { box-sizing: border-box; }

html[dir="rtl"] body { font-family: var(--font-ar); }
html[dir="ltr"] body { font-family: var(--font-en); }

body {
  margin: 0; background: var(--terminal); color: var(--ink);
  font-size: var(--t-body); line-height: 1.55;
  -webkit-font-smoothing: antialiased; min-height: 100vh;
}

.num, .money, .ref, code, .board-face {
  font-family: var(--font-board);
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  direction: ltr; unicode-bidi: isolate;
}

:focus-visible { outline: 2px solid var(--sunset); outline-offset: 2px; border-radius: 4px; }

/* ── shell ────────────────────────────────────────────────────────────────── */
/* The rail is the same ground as the canvas, separated by a rule — a differently
   coloured sidebar would split the desk into two worlds. */

.shell { display: grid; grid-template-columns: var(--rail) 1fr; min-height: 100vh; }

/* ── the sidebar ───────────────────────────────────────────────────────────
 *
 * It used to be forty-two unlabelled icons in a column, found by hovering. Now it reads:
 * seven groups named for what the company does, one open at a time, every destination
 * spelled out. The rail is still the same ground as the canvas — a differently coloured
 * sidebar would split the desk into two worlds — separated by a rule and a hairline of
 * the brand orange at the top, which is the only place the chrome is allowed to shout. */

.rail {
  position: sticky; top: 0; height: 100vh; z-index: 20;
  /* The brand blue as a ground. The rail used to be the same colour as the page,
     "so the desk is not split into two worlds" — which is a defensible idea and gave
     the console no identity at all: a white sidebar beside a grey page is every
     administrative tool ever built. A travel agency's desk should be recognisable as
     theirs from across the room. */
  background: linear-gradient(180deg, var(--navy-2) 0%, var(--navy) 62%);
  border-inline-end: 0;
  display: flex; flex-direction: column;
  padding-block-end: 10px; color: #fff;
}
/* The seam, in the company's orange — the one hairline of chrome allowed to shout. */
.rail::after {
  content: ''; position: absolute; inset-block: 0; inset-inline-end: 0; width: 3px;
  background: linear-gradient(180deg, var(--sunset), var(--sunset-deep) 40%,
                              transparent 78%);
}

/* ── the brand ─────────────────────────────────────────────────────────────
 *
 * The mark and the name, on the rail itself. There was a white panel here carrying the
 * logo file scaled up half again — 111 pixels of artwork stretched to 154, which is why
 * it read as smeared, and a white box bolted to a blue rail besides. The mark is vector
 * now, so it is sharp at any size, and the name is set in type rather than photographed. */
/* Scoped to the rail on purpose. `brand` is also the modifier that makes a button the
   primary one — `class="btn brand"` — so a bare `.brand { width: 100% }` here reached
   every primary button in the console and stretched it edge to edge. The sign-in button
   printing its label against the left edge was this, not a flex problem. Two components
   cannot share a class name and only one of them win. */
.rail .brand {
  display: flex; flex-direction: column; align-items: start; gap: 7px; width: 100%;
  padding: 17px 15px 15px; border: 0; cursor: pointer; text-align: start;
  background: transparent; position: relative;
  transition: background var(--speed);
}
.rail .brand:hover { background: rgba(255, 255, 255, 0.07); }
/* The tear line, the same one the ticket stubs carry. */
.rail .brand::after {
  content: ''; position: absolute; inset-block-end: 0; inset-inline: 13px; height: 1px;
  background: repeating-linear-gradient(90deg,
    rgba(255,255,255,0.28) 0 4px, transparent 4px 9px);
}
/* The company's own lockup, not a drawing of it and not its name re-set in a face the
   brand does not use. The colour artwork carries a white wordmark, so it belongs on the
   rail's blue and nowhere near white paper. */
.brand-lockup {
  width: 11.2rem; max-width: 100%; height: auto; display: block;
}
/* It has to fit the rail. At 0.56rem with wide tracking "TRAVEL AND TOURISM" ran off the
   end and read "TRAVEL AND TOURIS", which is worse than not saying it at all. */
.brand-tag {
  display: flex; align-items: center; gap: 5px;
  font-size: 0.52rem; letter-spacing: 0.055em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.52); font-weight: 700; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.brand-tag i {
  font-style: normal; font-family: var(--font-board); font-size: 0.66rem;
  letter-spacing: 0.1em; color: #fff; background: var(--sunset);
  border-radius: 3px; padding: 0 4px 1px; font-weight: 800; flex: none;
}


/* ── the groups ───────────────────────────────────────────────────────────── */
.nav-scroll {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 10px 8px 4px; display: flex; flex-direction: column; gap: 2px;
  scrollbar-width: thin;
}

.nav-head, .nav-leaf {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 7px 9px; border: 0; border-radius: 8px; cursor: pointer;
  background: transparent; color: rgba(255, 255, 255, 0.74); font: inherit;
  font-size: 0.83rem;
  text-align: start; transition: color var(--speed), background var(--speed);
}
.nav-head { font-weight: 700; color: #fff; }
.nav-head:hover, .nav-leaf:hover {
  background: rgba(255, 255, 255, 0.09); color: #fff;
}

.nav-head-icon { display: grid; place-items: center; flex: none;
  color: rgba(255, 255, 255, 0.55); }
.nav-head-icon svg { width: 1.05rem; height: 1.05rem; }
.nav-head:hover .nav-head-icon, .nav-group.open .nav-head-icon { color: var(--sunset-lift); }

.nav-head-text, .nav-leaf-text {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.nav-chev { display: grid; place-items: center; flex: none;
  color: rgba(255, 255, 255, 0.4); }
.nav-chev svg { width: 0.85rem; height: 0.85rem;
  transform: rotate(90deg); transition: transform var(--speed); }
.nav-group.open .nav-chev svg { transform: rotate(-90deg); }

/* Closed by default. `grid-template-rows` rather than `height`, so it opens to whatever
   it actually is without anybody measuring it in JavaScript. */
.nav-kids {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--speed);
}
/* One inner wrapper, so the grid has exactly *one* row to collapse. Without it every
   child is its own implicit row, `0fr` sizes only the first, and the group never shuts —
   which is how the first version of this sidebar rendered all seven groups open and was
   the same forty-two-item list it replaced. */
.nav-kids-inner { min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; gap: 1px; }
.nav-group.open .nav-kids { grid-template-rows: 1fr; }

.nav-group.open + .nav-group { margin-block-start: 2px; }

/* A child sits under its parent's icon, with a spine drawn down the group so the eye
   can see where it belongs without a box round it. */
.nav-kids-inner .nav-leaf {
  font-size: 0.81rem; padding-inline-start: 30px; border-radius: 7px;
  position: relative; color: rgba(255, 255, 255, 0.66);
}
.nav-kids-inner .nav-leaf::before {
  content: ''; position: absolute; inset-inline-start: 17px;
  inset-block: 0; border-inline-start: 1px solid rgba(255, 255, 255, 0.16);
}
.nav-kids-inner .nav-leaf:first-child::before { inset-block-start: 4px; }
.nav-kids-inner .nav-leaf:last-child::before  { inset-block-end: 4px; }

/* Where you are standing.
 *
 * This was a saturated orange slab across the whole item with a glow under it — the
 * loudest thing on the screen, competing every second with the one place orange is
 * supposed to mean *this needs a person*. An accent used for "you are here" cannot also
 * be an alarm; whichever it is asked to do second, it stops doing.
 *
 * So: a raised surface, white text, and the orange reduced to an indicator on the rail's
 * own edge. It reads instantly and takes nothing away from the alarms. */
.nav-leaf.on, .nav-kids-inner .nav-leaf.on {
  background: rgba(255, 255, 255, 0.13); color: #fff; font-weight: 700;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
  position: relative;
}
.nav-leaf.on::after {
  content: ''; position: absolute; inset-block: 5px; inset-inline-start: -8px;
  width: 3px; border-start-end-radius: 3px; border-end-end-radius: 3px;
  background: var(--sunset);
}
.nav-leaf.on .nav-head-icon { color: var(--sunset-lift); }
.nav-kids-inner .nav-leaf.on::before { border-color: transparent; }
.nav-leaf.on .nav-count { background: rgba(255, 255, 255, 0.2); color: #fff; }

.nav-leaf.top { font-weight: 700; color: #fff; margin-block-end: 4px; }

/* A count on a destination, and — when its group is shut — rolled up onto the group so
   nothing waiting is hidden by the thing that tidied the list. */
.nav-count {
  flex: none; min-width: 18px; height: 18px; padding-inline: 5px; border-radius: 999px;
  background: var(--stopbar); color: #fff; font-family: var(--font-board);
  font-size: 10px; font-weight: 800; display: grid; place-items: center;
}
.nav-count.roll { background: rgba(255, 255, 255, 0.14); color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.18); }
.nav-leaf.on .nav-count { background: rgba(255,255,255,0.22); color: #fff; }

/* ── the foot ─────────────────────────────────────────────────────────────── */
/* Two panels stacked in one column of a split, so the right-hand side of the pulse
   carries both the holds and the departures instead of emptying out under one card. */
.stack { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }

.rail-foot {
  display: flex; gap: 4px; padding: 8px 10px 0; margin-block-start: 4px;
  border-block-start: 1px solid rgba(255, 255, 255, 0.13);
}
.rail-item {
  width: 2.2rem; height: 2.2rem; border-radius: 8px;
  display: grid; place-items: center; cursor: pointer;
  color: var(--ink-3); background: transparent; border: 0; position: relative;
  transition: color var(--speed), background var(--speed);
}
.rail-item svg { width: 1.1rem; height: 1.1rem; }
.rail-item { color: rgba(255, 255, 255, 0.6); }
.rail-item:hover { color: #fff; background: rgba(255, 255, 255, 0.11); }

/* Narrow screens: the sidebar becomes a strip across the top rather than eating half
   the width. Groups still open; they just open downward over the page. */
/* Narrow: the sidebar lies down across the top. The group panel *wraps onto its own
   line* rather than hanging as a dropdown — an absolutely positioned panel is clipped
   the moment the strip needs to scroll, which is exactly what happened: the open group
   rendered as a white sliver cut off behind the page. Wrapping has no such edge. */
@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .rail { position: static; height: auto; flex-direction: row; flex-wrap: wrap;
          align-items: center; border-inline-end: 0;
          border-block-end: 1px solid var(--rule); padding-block-end: 0; }
  /* The brand keeps its own line. Sharing one with a nav that wraps to three leaves it
     floating in the middle of nowhere, vertically centred against a stack. */
  .brand { flex-basis: 100%; border-block-end: 1px dashed var(--rule);
           padding: 11px 14px; }
  .nav-scroll { flex: 1; flex-direction: row; flex-wrap: wrap; align-items: center;
                padding: 8px; overflow: visible; }
  .nav-head, .nav-leaf.top { width: auto; }
  /* The panel takes the whole line under the strip, so nothing is ever cut off. */
  .nav-group { display: contents; }
  .nav-group .nav-kids { order: 99; flex-basis: 100%; width: 100%; }
  .nav-group.open .nav-kids-inner {
    flex-direction: row; flex-wrap: wrap; gap: 4px;
    padding: 6px 2px 2px; border-block-start: 1px dashed var(--rule);
    margin-block-start: 6px;
  }
  .nav-kids-inner .nav-leaf { padding-inline-start: 10px; width: auto; }
  .nav-kids-inner .nav-leaf::before { display: none; }
  .rail-foot { border-block-start: 0; padding: 0 8px; }
}

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

/* The header, as the top edge of a boarding pass: printed stock, a rule of the company's
   two colours under it, and the tear line the rest of the desk is torn from. */
.topbar {
  position: sticky; top: 0; z-index: 15;
  /* **Does not wrap above 1024px.** `flex-wrap: wrap` makes a browser move an item to a
     new line *before* it shrinks anything — wrapping wins over shrinking — so the page
     title never got the chance to ellipsise, and what took the second line was whatever
     came last. That was the account control, landing under the title: exactly the place
     it had been moved out of. Giving the title `min-inline-size: 0` and the search a
     flexible basis did nothing while this was on, because neither was ever reached.
     Below 1024 the search does take its own line, which is the behaviour that rule was
     written for. See `.findall`. */
  display: flex; align-items: center; gap: var(--gap); flex-wrap: nowrap;
  padding: 13px var(--pad-open) 12px;
  background: color-mix(in srgb, var(--stock) 92%, transparent);
  backdrop-filter: blur(14px); border-block-end: 1px solid var(--rule);
}
/* The same single accent the strip carries, and for the same reason: two hard-edged
   bands of different brand colours stopping a third of the way across read as a chart
   somebody left in the chrome. One colour, fading out. */
.topbar::after {
  content: ''; position: absolute; inset-block-end: -1px; inset-inline: 0; height: 2px;
  background: linear-gradient(90deg, var(--sunset) 0 14%,
                              color-mix(in srgb, var(--sunset) 45%, transparent) 26%,
                              transparent 40%);
}
/* The page's name carries the bar now that it is not sharing it with the session. */
.topbar h1 { margin: 0; font-size: var(--t-h1); font-weight: 800;
  letter-spacing: -0.032em; color: var(--ink); line-height: 1.1;
  /* It shrinks before it shoves. A long page name pushing the account control onto a
     second line is the same fault as a fixed-width search doing it. */
  min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar .sub { color: var(--ink-3); font-size: 12px; font-weight: 600; }

/* Who is signed in. A session control, sitting with the other session controls. */
.whoami {
  display: flex; align-items: center; gap: 9px; min-width: 0; flex: none;
  padding-inline-start: var(--gap);
  border-inline-start: 1px solid var(--rule);
}
.whoami-mark {
  flex: none; display: grid; place-items: center;
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--navy); color: #fff;
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em;
}
.whoami-who { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.whoami-who b { font-size: 0.8rem; font-weight: 800; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.whoami-who span { font-size: 0.68rem; font-weight: 700; color: var(--ink-3);
  letter-spacing: 0.04em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* On a narrow window the name goes and the mark stays — an initial still says who is
   signed in, which is the whole job of the control. The bar no longer wraps at any width
   (see `.findall`), so this is about legibility rather than about rescuing a layout. */
@media (max-width: 1100px) { .whoami-who { display: none; } }
.main { position: relative; }
/* `grow` means "take the space that is left" everywhere, not only in the three places
   that happened to need it first. New components assumed the utility existed, and a
   count silently welded to its label is what happens when it does not. */
.grow { flex: 1; min-inline-size: 0; }

.chip-bar {
  display: flex; align-items: center; gap: 7px; color: var(--ink-3);
  font-size: 12px; font-weight: 700; padding: 5px 11px;
  border: 1px solid var(--rule); border-radius: 999px; background: var(--stock);
  /* The clock is one line. Squeezed, it broke after «بتوقيت» and
     made the whole bar two rows tall. */
  white-space: nowrap; }
.chip-bar .lamp { width: 6px; height: 6px; border-radius: 50%; background: var(--taxiway);
  box-shadow: 0 0 0 3px var(--taxiway-wash); }
@media (prefers-reduced-motion: no-preference) {
  .chip-bar .lamp { animation: breathe 3s ease-in-out infinite; }
  @keyframes breathe { 50% { opacity: 0.35; } }
}

.lang-toggle { display: flex; border: 1px solid var(--rule); border-radius: 999px;
  overflow: hidden; background: var(--stock); }
.lang-toggle button {
  border: 0; background: transparent; color: var(--ink-3); padding: 5px 12px;
  cursor: pointer; font-size: 12px; font-weight: 800; font-family: inherit;
  transition: all var(--speed);
}
.lang-toggle button.on { background: var(--sky-deep); color: #fff; }

.theme-toggle button { padding: 5px 10px; display: grid; place-items: center; }
.theme-toggle svg { width: 0.92rem; height: 0.92rem; }

/* ── layout ───────────────────────────────────────────────────────────────── */

.page { padding: var(--pad-open); display: flex; flex-direction: column; gap: var(--pad-open); }
.grid { display: grid; gap: var(--gap); }
.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.g5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
/* 1.9fr says the board leads and the context serves it — not two peers. */
.split { grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); align-items: start; }
/* A grid *item* still defaults to `min-width: auto`, so a wide child — a table of twenty
   columns — pushes its own column past the track the template caps, and the **page body**
   ends up scrolling sideways instead of the table scrolling inside its own box. Capping
   the track is not enough; the item has to be allowed to be narrower than its content. */
.grid > * { min-inline-size: 0; }
@media (max-width: 1180px) { .g4, .g5 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .split, .g3 { grid-template-columns: 1fr; } }
@media (max-width: 720px) { .g4, .g5, .g2 { grid-template-columns: 1fr; } }

.card {
  background: var(--stock); border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: var(--shadow); display: flex; flex-direction: column; min-width: 0;
}
.card-head { display: flex; align-items: center; gap: 8px; padding: var(--pad) var(--pad) 8px; }
/* A heading a person reads across a room, not a caption. It was 11px in --ink-2 with
   0.1em tracking: correct as a label and too quiet to be the thing that tells you which
   card you are in, on screens that carry five of them. */
.card-head h2 { margin: 0; font-size: 0.78rem; font-weight: 800;
  letter-spacing: 0.085em; text-transform: uppercase; color: var(--ink);
  display: flex; align-items: center; gap: 8px; }
/* A short bar of the brand orange at the start of every section title. Small, repeated,
   and the reason a page of cards reads as one company's rather than a component kit's. */
.card-head h2::before {
  content: ''; width: 3px; height: 0.85rem; border-radius: 2px; flex: none;
  background: var(--sunset);
}
.card-head .grow { flex: 1; }

/* A ticket's own headline. Not a card heading: `.card-head h2` is the label treatment —
   small, letter-spaced, uppercase — and a sentence set that way is the hardest thing on
   the page to read, on the line that matters most. */
.ticket-headline { padding: var(--pad) var(--pad) 0.35rem; }
.ticket-headline .num {
  font-family: var(--font-board); font-size: 12px; font-weight: 700;
  color: var(--ink-3); display: block; margin-block-end: 3px;
}
.ticket-headline h1 {
  margin: 0; font-size: var(--t-h2); font-weight: 800; line-height: 1.3;
  color: var(--ink); text-wrap: balance;
}
.card-body { padding: 0 var(--pad) var(--pad); }
.card-body.flush { padding: 0; }

/* ── the departures board ─────────────────────────────────────────────────── */
/*
 * The focal element of the whole console. It is a physical object set into the desk:
 * dark stock, mono face, status shouting. Everything else on the page is quieter than
 * this on purpose.
 */
.board {
  background: var(--board); border-radius: var(--radius); overflow: hidden;
  border: 1px solid var(--board-rule);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.05), var(--shadow-lift);
}
.board-head {
  display: flex; align-items: center; gap: 10px;
  padding: 13px var(--pad); border-block-end: 1px solid var(--board-rule);
  background: var(--board-2);
}
.board-head h2 { margin: 0; font-size: var(--t-caption); font-weight: 800;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--board-ink-2);
  font-family: var(--font-board); }
.board-head .grow { flex: 1; }
.board-total {
  font-family: var(--font-board); font-size: var(--t-h2); font-weight: 700;
  color: var(--sunset-lift); line-height: 1;
}

.board-row {
  display: grid; grid-template-columns: 4px 3.8rem 1fr auto;
  align-items: center; gap: 14px;
  padding: 0; border-block-end: 1px solid var(--board-rule);
  cursor: pointer; transition: background var(--speed);
}
.board-row:last-child { border-block-end: 0; }
.board-row:hover { background: var(--board-2); }
.board-lamp { align-self: stretch; }
.board-row.critical .board-lamp { background: var(--stopbar-lit); }
.board-row.high .board-lamp     { background: var(--caution-lit); }
.board-row.medium .board-lamp   { background: var(--edge-lit); }
.board-row.low .board-lamp      { background: var(--approach-lit); }

.board-count {
  font-family: var(--font-board); font-size: var(--t-h1); font-weight: 700;
  line-height: 1; text-align: center; padding-block: 17px;
  text-shadow: 0 0 18px currentColor;
  /* Stated, because the board is a dark object set into a light console and anything
     inside it must take its colour from the board's own palette. This rule only ever
     gave a colour to a row with a *severity*, so an ordinary row — the ticketing queue,
     where the figure is money rather than an alarm — inherited the page's ink and
     printed near-black on near-black. The amounts were there and could not be read. */
  color: var(--board-ink);
}
.board-row.critical .board-count { color: var(--stopbar-lit); }
.board-row.high .board-count     { color: var(--caution-lit); }
.board-row.medium .board-count   { color: var(--edge-lit); }
.board-row.low .board-count      { color: var(--approach-lit); }

.board-what { min-width: 0; padding-block: 14px; }
.board-what .title { font-weight: 700; color: var(--board-ink); font-size: var(--t-h4);
  line-height: 1.3; }
.board-what .meta { font-size: 11px; color: var(--board-ink-2);
  font-family: var(--font-board); letter-spacing: 0.02em; }
.board-go { padding-inline-end: var(--pad); color: var(--board-ink-2); }
.board-row:hover .board-go { color: var(--sunset-lift); }

/* A hold burning down. Amber, then red — the same warning any tarmac gives. */
.clock {
  font-family: var(--font-board); font-weight: 700; font-size: 13px;
  padding: 3px 8px; border-radius: 5px; letter-spacing: 0.02em;
}
/* A lamp reads differently on stock than on the board. `--edge-lit` and `--stopbar-lit`
   are the colours of a lit lamp seen in the dark — on a white card they are a pale wash
   nobody can read, which is what a departure "18h" away looked like. On paper the ink
   tones; on the board the lit ones. */
.clock.warm { color: var(--edge);    background: var(--edge-wash); }
.clock.hot  { color: var(--stopbar); background: var(--stopbar-wash); }
.board .clock.warm { color: var(--edge-lit);    background: rgba(255,190,77,0.13); }
.board .clock.hot  { color: var(--stopbar-lit); background: rgba(255,92,125,0.15); }

.board-empty { padding: 34px var(--pad); text-align: center; color: var(--board-ink-2);
  font-size: 13px; }
.board-empty .lamp {
  width: 9px; height: 9px; border-radius: 50%; background: var(--taxiway-lit);
  box-shadow: 0 0 12px var(--taxiway-lit); display: inline-block;
  margin-inline-end: 8px; vertical-align: middle;
}

/* ── the strip: money, demoted on purpose ─────────────────────────────────── */
/* These are the numbers you check second. They read as one instrument panel, not four
   competing cards. */
/* The month's figures, drawn as the counterfoil strip off a ticket: one piece of stock,
   divided by perforations rather than by lines, with the company's colours printed along
   the top edge the way a real pass carries its band. */
.strip {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  background: var(--stock); border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden; position: relative;
}
/* One accent, not three.
 *
 * This was a banded orange–blue–orange gradient across the whole top edge, and the cells
 * below it were separated by a dashed tear with a punched hole drilled at each end. Both
 * were the ticket metaphor taken past the point where it says anything: at this size the
 * holes read as rendering artefacts and the bands read as a progress bar that never
 * moves. The metaphor is kept where it is legible and load-bearing — the sign-in card's
 * notch, the stub on a piece of pickable work — and the strip, which is the first thing
 * anybody reads at 9am, is left to be read. */
.strip::before {
  content: ''; position: absolute; inset-block-start: 0; inset-inline: 0; height: 3px;
  background: linear-gradient(90deg, var(--sunset), var(--sunset-deep));
}
@media (max-width: 720px) { .strip { grid-template-columns: repeat(2, minmax(0,1fr)); } }
.strip .cell {
  padding: var(--pad) var(--pad-open) calc(var(--pad) + 2px);
  border-inline-end: 1px solid var(--rule); position: relative;
}
.strip .cell:last-child { border-inline-end: 0; }
/* A single reading, not a panel of them. No grid, no clipping — see `metric()`. */
.metric { min-width: 0; }
.metric > .cell { padding: 0; }
/* A reading's figure starts where its label starts.
 *
 * `.num` sets `direction: ltr` so digits keep their order — right, and necessary. But that
 * also anchors the figure to the *left* of its box, so on an Arabic screen a label sat at
 * the right of its column with the number under the far left, hard against the next
 * reading along. `text-align: end` puts the figure back under its own label without
 * touching the digit order. */
[dir="rtl"] .cell .value.num { text-align: end; }

/* A field the screen filled in for you.
 *
 * `readonly` on its own looks identical to an editable field, so somebody clicks into it
 * and nothing happens. Shown as settled rather than as a box waiting for an answer. */
.field.locked input, .field.locked textarea, .field.locked select {
  background: var(--counterfoil); color: var(--ink-2); cursor: default;
  border-style: dashed;
}
/* A row of readings that wraps rather than squeezes.
 *
 * `g2` and `g3` divide whatever they are given, however little that is. In the narrow
 * column of a split layout, three readings got about 78px each and `21,100.000` needs 90 —
 * so three figures ran into one another and read as one long number. A minimum width and
 * `auto-fit` puts them on two lines instead, which is legible; a squeezed row is not. */
.readings { display: grid; gap: 0.5rem;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); }
.strip .cell:last-child { border-inline-end: 0; }
/* Scoped to `.cell`, not to `.strip`.
 *
 * `cell()` renders wherever a reading is wanted — the partner screen puts three of them
 * in a plain grid — and everything below was written as `.strip .…`, so outside a strip
 * the unit lost its margin and every figure read `0.000KWD` with no space, and an unlit
 * reading lost the dashes that say *nobody has said*, which is the one thing about it
 * that matters. A strip is one arrangement of cells, not the only one. */
/* The figure carries the row, so it is given the weight to. The label was --ink-3 at
   11px above a 22px figure: two quiet greys, and the reading a person came for did not
   arrive first. */
.cell .label { font-size: var(--t-caption); font-weight: 800; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3); margin-block-end: 7px; }
.cell .value { font-size: var(--t-h1); font-weight: 800; letter-spacing: -0.035em;
  line-height: 1.05; color: var(--ink); }
.cell .value.words { font-size: var(--t-h4); letter-spacing: 0; line-height: 1.3;
  text-wrap: balance; }
.cell .value .unit { font-size: 12px; color: var(--ink-3); font-weight: 700;
  margin-inline-start: 5px; letter-spacing: 0; }
.cell .foot { margin-block-start: 3px; font-size: 12px; color: var(--ink-3); }
/* An unlit reading. Never a zero. */
.cell.unlit .value { color: var(--unlit); font-size: var(--t-h4); font-weight: 700;
  display: inline-flex; align-items: center; gap: 8px; }
.cell.unlit .value::before {
  content: ''; width: 20px; height: 3px; border-radius: 2px;
  background: repeating-linear-gradient(90deg, var(--unlit) 0 4px, transparent 4px 9px);
}

/* ── the stub ─────────────────────────────────────────────────────────────── */

.stub {
  position: relative; display: flex; align-items: stretch; background: var(--stock);
  border: 1px solid var(--rule); border-radius: var(--radius-sm); overflow: hidden;
  transition: box-shadow var(--speed), border-color var(--speed);
}
.stub + .stub { margin-block-start: 7px; }
.stub:hover { box-shadow: var(--shadow-lift); border-color: var(--rule-strong); }
.stub-counterfoil {
  flex: 0 0 auto; width: 3.3rem; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; padding: 9px 4px;
  background: var(--counterfoil); border-inline-end: 2px dashed var(--rule-strong);
  position: relative;
}
.stub-counterfoil::before, .stub-counterfoil::after {
  content: ''; position: absolute; inset-inline-end: -7px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--terminal); border: 1px solid var(--rule);
}
.stub-counterfoil::before { inset-block-start: -7px; }
.stub-counterfoil::after { inset-block-end: -7px; }
.stub-body { flex: 1; min-width: 0; padding: 9px 13px; display: flex;
  align-items: center; gap: 10px; }
.stub-body .grow { flex: 1; min-width: 0; }
.stub .title { font-weight: 700; color: var(--ink); font-size: 14px; }
.stub .meta { font-size: 12px; color: var(--ink-3); }

/* ── tables ───────────────────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th {
  text-align: start; font-weight: 800; font-size: 10px; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-3); padding: 9px var(--pad);
  white-space: nowrap; position: sticky; top: 0; background: var(--groove); z-index: 1;
  border-block-end: 1px solid var(--rule-strong);
}
tbody td { padding: 11px var(--pad); border-block-end: 1px solid var(--rule); }
tbody tr:last-child td { border-block-end: 0; }
/* A clickable row says so before it is clicked. It was a wash of the brand blue on hover
   and nothing else; the marker on the leading edge is what tells somebody scanning a long
   board which row the pointer is actually on. */
tbody tr[data-go] { cursor: pointer; transition: background var(--speed); }
tbody tr[data-go] > td:first-child { position: relative; }
tbody tr[data-go] > td:first-child::before {
  content: ''; position: absolute; inset-block: 0; inset-inline-start: 0; width: 3px;
  background: var(--sunset); opacity: 0; transition: opacity var(--speed);
}
tbody tr[data-go]:hover { background: var(--dusk-wash); }
tbody tr[data-go]:hover > td:first-child::before { opacity: 1; }
/* A total stays visible while its rows scroll past.
 *
 * `.table-wrap` caps at 27rem and scrolls, so a table of more than about a dozen rows put
 * its `tfoot` below the fold — and the total is the one figure the table exists to
 * produce. It was hidden on the depreciation run and on the trial balance, on any month
 * with enough lines. Sticky, with its own background, because a transparent one lets the
 * scrolling rows show through the number. */
tfoot td { padding: 9px var(--pad); border-block-start: 2px solid var(--rule-strong);
  font-weight: 800; position: sticky; bottom: 0; background: var(--stock); z-index: 1; }
/* A heading inside a table body — the day a run of rows belongs to. The file register
   groups by it, because that is how somebody looks for "the passport she sent in July".
   It is not a row of data and must not read as one. */
tbody tr.group-row td {
  background: var(--groove); color: var(--ink-2);
  font-size: var(--t-caption); font-weight: 800; letter-spacing: 0.08em;
  text-transform: uppercase; padding-block: 6px;
  border-block-end: 1px solid var(--rule-strong);
  position: sticky; top: 2.2rem; z-index: 1;
}

td.num, th.num { text-align: end; }
/* A table is a surface, whether or not somebody wrapped it in a card.
 *
 * Several screens — the ticket board among them — put `.table-wrap` straight on the page,
 * and it had no ground of its own: a header row and a column of rules bleeding into the
 * canvas with no edge anywhere. It read as a page that had not finished loading. Inside a
 * card the card is already the surface, so the rule is undone there rather than doubled. */
.table-wrap { overflow-x: auto; max-height: 27rem; overflow-y: auto;
  background: var(--stock); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow); }
.card .table-wrap { background: none; border: 0; box-shadow: none;
  border-radius: 0 0 var(--radius) var(--radius); }

/* ── status ───────────────────────────────────────────────────────────────── */

/* A reading's own verdict, inline in a table cell.
 *
 * Written on seven screens — the reference lists, the sale-flow matrix, and the ticket
 * board's deadline column — and styled by **nothing**. `<span class="state stop">` on
 * "a line nobody read" is the one cell that column exists to shout from, and it rendered
 * as plain body text. A class naming nothing is as silent as a `var()` naming nothing:
 * no error, no warning, and every screen looks *nearly* right.
 *
 * Narrower than `.pill`, which carries a lamp and a lifecycle state. This is a verdict on
 * a value, so it borrows the aviation palette and nothing else. */
.state {
  display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px;
  border-radius: 5px; font-size: 11px; font-weight: 800; white-space: nowrap;
  background: var(--unlit-wash); color: var(--unlit);
}
.state.ok   { background: var(--taxiway-wash); color: var(--taxiway); }
.state.wait { background: var(--edge-wash);    color: var(--edge); }
.state.stop { background: var(--stopbar-wash); color: var(--stopbar); }

/* A note that carries the finding, rather than a remark. `.note.stopbar` is written on
   the deadline nobody could read — the thing the whole ticket module was built around —
   and on the reconciliation line a person has to decide. It had no rule either. */
.note.stopbar {
  background: var(--stopbar-wash); color: var(--ink);
  border-inline-start-color: var(--stopbar);
}
/* What the supplier said, word for word. Set in the board face because it is a machine's
   output and reads as one — `ERR1012:: … *UNABLE - CLASS DOES NOT EXIST FOR THIS FLIGHT*`
   — and because an agent quotes it back to the carrier character by character, so the
   digits and the pipes have to be unambiguous. It wraps rather than scrolls: a reason
   that runs off the edge of its box is a reason nobody reads to the end of. */
.said-verbatim {
  margin-block-start: 0.3rem;
  font-family: var(--font-board); font-size: 0.74rem; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
  /* Always left-to-right: this is a carrier's own string and mirroring it under RTL
     would move the fare-class letters away from the flight numbers they belong to. */
  direction: ltr; text-align: start;
}

.note.warn { background: var(--edge-wash); border-inline-start-color: var(--edge); }
.note.risk { background: var(--stopbar-wash); border-inline-start-color: var(--stopbar); }
.note.good { background: var(--taxiway-wash); border-inline-start-color: var(--taxiway); }

/* The primary modifier in this stylesheet is `.brand`. Two submit buttons ask for
   `.primary` and were drawn as ordinary ones — kept as an alias rather than renamed at
   the call sites, because the next person to write one will reach for this name too. */
.btn.primary {
  background: var(--sunset); border-color: var(--sunset-deep); color: #fff;
  box-shadow: 0 1px 2px rgba(200, 66, 15, 0.28), 0 2px 8px -2px rgba(200, 66, 15, 0.32);
}
.btn.primary:hover { background: var(--sunset-deep); }

/* A form that sits inside a table row, and a heading row above one. */
.row-form { display: flex; flex-wrap: wrap; align-items: end; gap: 8px; }
.row-head { display: flex; align-items: center; gap: 8px; font-weight: 800;
  font-size: 0.82rem; color: var(--ink); }
.rows { display: flex; flex-direction: column; gap: 4px; }
tfoot tr.total-row td, tr.total-row td { font-weight: 800; }
.decisions { display: flex; flex-direction: column; gap: 8px; }
.decision-head { display: flex; align-items: center; gap: 8px;
  font-weight: 800; font-size: 0.82rem; }
/* A block drawn as a detachable stub — the ticket metaphor where it earns its place. */
.stub-block { border: 1px solid var(--rule); border-radius: var(--radius-sm);
  padding: var(--pad-tight) var(--pad); background: var(--stock); }
/* A list with no bullets and no indent: rows of documents, not prose. */
ul.plain { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px; }

.pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px;
  border-radius: 5px; font-size: 11px; font-weight: 800; white-space: nowrap;
}
.pill::before { content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 5px currentColor; }
.pill.draft, .pill.quoted, .pill.unpaid, .pill.pending, .pill.unmatched, .pill.queued,
.pill.adt, .pill.chd, .pill.inf { color: var(--ink-3); background: var(--counterfoil); }
.pill.draft::before, .pill.quoted::before, .pill.unpaid::before, .pill.pending::before,
.pill.unmatched::before, .pill.queued::before { box-shadow: none; }
.pill.held { color: var(--edge); background: var(--edge-wash); }
.pill.booked { color: var(--approach); background: var(--approach-wash); }
.pill.ticketed, .pill.paid, .pill.sent, .pill.succeeded, .pill.matched, .pill.posted,
.pill.approved, .pill.settled, .pill.issued,
/* How a call went. `pill(status)` renders the value as a class, and these three had no
   rule at all — so the one column on the calls table that says whether a customer was
   answered was drawn as plain body text, in both languages. The same fault the four chat
   states had, found the same way: by sweeping for a class the stylesheet does not have. */
.pill.answered { color: var(--taxiway); background: var(--taxiway-wash); }
.pill.unanswered { color: var(--stopbar); background: var(--stopbar-wash); }
/* Still ringing is neither. It is a call in progress, and colouring it as lost is the
   whole reason the status is three values rather than a boolean. */
.pill.waiting { color: var(--edge); background: var(--edge-wash); }
.pill.issue_failed, .pill.dead, .pill.failed { color: var(--stopbar); background: var(--stopbar-wash); }
.pill.cancelled, .pill.expired, .pill.voided, .pill.reversed, .pill.refunded,
.pill.exchanged { color: var(--caution); background: var(--caution-wash); }
.pill.partial, .pill.ambiguous, .pill.disputed { color: var(--edge); background: var(--edge-wash); }
/* A conversation's four states.
 *
 * They had no rule at all, in either language — `CHAT_STATE` has carried a `tone` for
 * each since it was written and nothing read it, so every state pill on every chat screen
 * was the plain default and an escalated conversation looked exactly like a closed one.
 * The Arabic screens were worse still: `pill()` was being handed the translated label, so
 * the class became «مغلقة» and there was nothing for a rule to match even once one
 * existed. */
.pill.open { color: var(--edge); background: var(--edge-wash); }
.pill.assigned { color: var(--approach); background: var(--approach-wash); }
.pill.escalated { color: var(--stopbar); background: var(--stopbar-wash); }
/* Finished, and quiet about it — the same treatment as every other settled state. */
.pill.closed { color: var(--ink-3); background: var(--counterfoil); }
.pill.closed::before { box-shadow: none; }

.pill.unknown { color: var(--unlit); background: var(--unlit-wash); }
.pill.unknown::before { box-shadow: none; opacity: 0.5; }

/* ── the journey ──────────────────────────────────────────────────────────── */

.spine { position: relative; padding-inline-start: 26px; }
.spine::before {
  content: ''; position: absolute; inset-block: 14px; inset-inline-start: 6px; width: 2px;
  border-radius: 2px;
  background: repeating-linear-gradient(180deg, var(--rule-strong) 0 5px, transparent 5px 10px);
}
.beat { position: relative; padding-block: 8px; }
.beat::before {
  content: ''; position: absolute; inset-inline-start: -23px; inset-block-start: 14px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--stock); border: 2px solid var(--rule-strong);
}
.beat.financial::before { background: var(--sunset); border-color: var(--sunset);
  box-shadow: 0 0 0 4px var(--sunset-wash); }
.beat.problem::before { background: var(--stopbar); border-color: var(--stopbar);
  box-shadow: 0 0 0 4px var(--stopbar-wash); }
.beat .when { font-size: 11px; color: var(--ink-3); font-family: var(--font-board); }
.beat .what { font-weight: 800; color: var(--ink); font-size: 15px; }

.fanout { display: flex; gap: 4px; margin-block-start: 5px; flex-wrap: wrap; }
.chip {
  font-size: 10px; padding: 2px 6px; border-radius: 4px; font-weight: 700;
  font-family: var(--font-board); border: 1px solid var(--rule); color: var(--ink-3);
  background: var(--counterfoil);
}
.chip.succeeded { color: var(--taxiway); background: var(--taxiway-wash);
  border-color: color-mix(in srgb, var(--taxiway) 30%, transparent); }
.chip.failed { color: var(--edge); background: var(--edge-wash);
  border-color: color-mix(in srgb, var(--edge) 30%, transparent); }
.chip.dead { color: var(--stopbar); background: var(--stopbar-wash);
  border-color: color-mix(in srgb, var(--stopbar) 35%, transparent); }

/* ── money, taken apart ───────────────────────────────────────────────────── */

.breakdown { display: flex; flex-direction: column; gap: 4px; }
.breakdown .line {
  display: flex; align-items: center; gap: 10px; padding: 7px 11px;
  border-radius: var(--radius-sm); background: var(--groove);
}
.breakdown .line .grow { flex: 1; }
.breakdown .line .src { font-size: 11px; color: var(--ink-3); }
.breakdown .line.total {
  background: var(--sunset-wash); border: 1px dashed color-mix(in srgb, var(--sunset) 50%, transparent);
  font-weight: 800; margin-block-start: 3px;
}
.breakdown .line.total .amount { color: var(--sunset-deep); font-size: var(--t-h4); }
/* A figure reads left-to-right in both languages, sign included.
 *
 * Without the isolation an Arabic page put the minus on the wrong end — `4.000−` — which
 * is not a smaller number, it is a different-looking one, and a reader checking a margin
 * has to stop and work out what they are seeing. */
.amount { font-family: var(--font-board); font-variant-numeric: tabular-nums; font-weight: 700;
  direction: ltr; unicode-bidi: isolate; }
.amount.in { color: var(--taxiway); }
.amount.out { color: var(--caution); }

/* ── bars ─────────────────────────────────────────────────────────────────── */

.bars { display: flex; flex-direction: column; gap: 7px; }
.bar-row { display: grid; grid-template-columns: 7.5rem 1fr 5rem; align-items: center;
  gap: 10px; font-size: 13px; }
.bar-track { height: 8px; background: var(--groove); border-radius: 5px; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 5px;
  background: linear-gradient(90deg, var(--sunset), var(--sunset-lift));
  transition: width 700ms cubic-bezier(0.2, 0.8, 0.2, 1); }
html[dir="rtl"] .bar-fill { background: linear-gradient(270deg, var(--sunset), var(--sunset-lift)); }
.bar-unknown { display: block; height: 100%;
  background: repeating-linear-gradient(90deg, var(--unlit-wash) 0 5px, transparent 5px 10px); }

/* ── misc ─────────────────────────────────────────────────────────────────── */

.empty { padding: 34px var(--pad); text-align: center; color: var(--ink-3); font-size: 13px; }
.empty .big { font-size: 20px; margin-block-end: 6px; color: var(--taxiway); }
.muted { color: var(--ink-3); }
.tiny { font-size: 12px; }
.right { margin-inline-start: auto; }
.row { display: flex; align-items: center; gap: 8px; }
.wrap { flex-wrap: wrap; }

.btn {
  border: 1px solid var(--rule); background: var(--stock); color: var(--ink);
  padding: 6px 12px; border-radius: var(--radius-sm); cursor: pointer;
  font-size: 13px; font-weight: 700; font-family: inherit; transition: all var(--speed);
  /* Stated rather than assumed. Several call sites set `justify-content: center` inline
     expecting a flex box and got whatever the button already was — which is how the
     sign-in button, the one control on the first screen anybody sees, printed its label
     hard against the left edge of a full-width bar. */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  text-align: center;
}
.btn {
  border-color: var(--rule-strong);
  box-shadow: 0 1px 2px rgba(13, 21, 38, 0.05);
}
.btn:hover { background: var(--counterfoil); border-color: var(--ink-4); }
/* Pressed, rather than only recoloured. A control that moves under the finger is the
   cheapest signal that it did something, and it costs one line. */
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn.brand {
  background: var(--sunset); border-color: var(--sunset-deep); color: #fff;
  box-shadow: 0 1px 2px rgba(200, 66, 15, 0.28), 0 2px 8px -2px rgba(200, 66, 15, 0.32);
}
.btn.brand:hover { background: var(--sunset-deep); border-color: var(--sunset-deep); }

/* An inset block on a white card. `--counterfoil` is now a very light blue-grey rather
   than a tan, which is right for a field but leaves a note almost invisible against the
   card — so it takes the deeper `--groove` and a real leading rule. It has to read as
   *set into* the card at a glance, because most of them carry somebody's own words. */
.note {
  padding: 10px 13px; border-radius: var(--radius-sm); font-size: 13px;
  background: var(--groove); color: var(--ink-2);
  border-inline-start: 3px solid var(--rule-strong);
}
.note.warn { border-inline-start-color: var(--edge); background: var(--edge-wash); }
.note.risk { border-inline-start-color: var(--stopbar); background: var(--stopbar-wash); }
.note.ok { border-inline-start-color: var(--taxiway); background: var(--taxiway-wash); }
.note.brand { border-inline-start-color: var(--sunset); background: var(--sunset-wash); }

.skeleton { background: linear-gradient(90deg, var(--counterfoil), var(--rule), var(--counterfoil));
  background-size: 200% 100%; border-radius: var(--radius-sm); height: 18px; }
@media (prefers-reduced-motion: no-preference) {
  .skeleton { animation: shimmer 1.4s ease-in-out infinite; }
  @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
}

/* ── signing in: the ticket, whole ────────────────────────────────────────── */

/* The front door is the one screen in the console that carries a photograph, and it is
   the view this company sells: an apron at dusk with the evening's last departure lifting
   away over the skyline. Every other screen is a working surface and stays plain — a
   picture behind a ledger is noise. Here there is nothing to read but three fields, and
   room to say what the platform is for.
 *
 * It commits to one look rather than following the theme. The card carries the light; the
 * ground behind it is the same dusk under either setting, because a night photograph
 * lightened for a light theme is only a washed-out photograph. */
.signin-wrap { min-height: 100vh; display: grid; place-items: center; padding: 2rem 1rem;
  /* The horizon out of an aircraft window at dusk, in the company's two colours and
     nothing louder — the orange low and to one side, the blue above it. This is the
     floor, not the design: it is what a narrow window, a failed request and a browser
     that will not decode WebP all fall back to, so it has to stand on its own. */
  --tear-notch: var(--terminal);
  background:
    radial-gradient(760px 340px at 78% 108%, var(--sunset-wash), transparent 66%),
    radial-gradient(900px 460px at 18% -12%, var(--sky-wash), transparent 64%),
    var(--terminal); }

@media (min-width: 46rem) {
  /* Above a laptop's width, where the frame is wide enough to hold a 3:1 picture without
     cropping it to a smear. A phone keeps the gradient, which also spares it the 88 KB. */
  .signin-wrap {
    /* The scrim is heaviest in the middle, under the card, so the photograph is at its
       strongest out at the edges where nothing has to be read over it. */
    background:
      radial-gradient(1250px 720px at 50% 44%, rgba(9, 24, 41, 0.34),
                      rgba(9, 24, 41, 0.58) 58%, rgba(5, 14, 25, 0.86)),
      url('scene/dusk-apron.webp') 50% 54% / cover no-repeat,
      var(--navy);
    /* The notch is a hole bitten out of the ticket, and it is *painted* — a circle in the
       page's own colour, not a cut. With a photograph behind the card, the page's own
       colour is two cream dots sitting on the dusk. */
    --tear-notch: #0c1d31;
  }
  /* The ticket is printed stock, and printed stock does not have a night mode. Over the
     photograph the dark palette put a navy card on a navy dusk with navy fields inside
     it — which is how this form was once unusable, and it would have been unusable in
     the same way again. So the card is pinned to the colours it is printed in, in both
     settings, by re-declaring the palette *on the card* rather than restyling each piece:
     the fields, the notes, the focus ring and the refusal all follow without a rule of
     their own. Everything below the card stays on the theme.
   *
     The voucher already does this — a document that reads differently by time of day is
     not a document — and `test_console_layout.py` guards it there. */
  .signin-wrap .signin {
    --stock: #ffffff; --counterfoil: #edeae4; --groove: #e3dfd7;
    --rule: #e2ded6; --rule-strong: #c9c3b8;
    --ink: #141b33; --ink-2: #3d4667; --ink-3: #6b7590; --ink-4: #98a1b8;
    --sunset-wash: #fdeee6; --stopbar: #d1435f; --stopbar-wash: #fbe9ec;
  }

  /* A ticket resting on a surface rather than printed on it, with an edge of its own.
     The ticket is outlined in three pieces — head, tear, body — in a rule colour picked
     against the page. In the dark palette that is navy on navy, and over the photograph
     the outline disappears: the fields float with no card under them.
   *
     Scoped through `.signin-wrap` for weight as much as for meaning — the wrapper is
     written above the pieces it contains, so a rule of equal specificity here loses to
     the base one further down the file. The first attempt did exactly that and changed
     nothing at all. */
  .signin-wrap .signin .body { box-shadow: 0 26px 70px -22px rgba(0, 0, 0, 0.8); }
  .signin-wrap .signin .head,
  .signin-wrap .signin .body { border-color: rgba(255, 255, 255, 0.26); }
  .signin-wrap .signin .tear { border-inline-color: rgba(255, 255, 255, 0.26); }
}
.signin { width: min(24rem, 100%); }
/* The head is the company's own stock: white, because that is what the logo is drawn
   for. It used to be a navy panel with an orange wash over it, and the mark inside it was
   a redrawing — between them the first thing anybody saw of this platform was a colour
   scheme the company does not use. */
.signin .head { background: #fff; padding: 22px 20px 16px;
  border-radius: var(--radius) var(--radius) 0 0; position: relative; overflow: hidden;
  border: 1px solid var(--rule); border-block-end: 0;
  display: flex; flex-direction: column; align-items: center; gap: 10px; }
.signin .head > * { position: relative; }
.signin .tear {
  height: 16px; background: var(--stock); border-inline: 1px solid var(--rule);
  background-image:
    radial-gradient(circle at 0 50%, var(--tear-notch) 8px, transparent 8px),
    radial-gradient(circle at 100% 50%, var(--tear-notch) 8px, transparent 8px),
    repeating-linear-gradient(90deg, var(--rule-strong) 0 5px, transparent 5px 10px);
  background-repeat: no-repeat, no-repeat, repeat-x;
  background-position: left center, right center, center center;
  background-size: auto, auto, auto 1px;
}
.signin .body {
  background: var(--stock); border: 1px solid var(--rule); border-block-start: 0;
  border-radius: 0 0 var(--radius) var(--radius); padding: 18px 20px 22px;
  display: flex; flex-direction: column; gap: 13px; box-shadow: var(--shadow);
}
.field { display: flex; flex-direction: column; gap: 6px; }
.field span { font-size: var(--t-caption); font-weight: 800; color: var(--ink-2);
  letter-spacing: 0.06em; text-transform: uppercase; }
/* Fields sit on the card, not in a recess.
 *
 * They were `--counterfoil`, which was a tan and is now a very light blue-grey — but the
 * change worth making is not the tint. A filled box on a white card reads as *disabled*,
 * and a form of them reads as a screen you are looking at rather than one you are working
 * in. White ground, a real border, and an inner hairline so the edge stays crisp against
 * the card. The colour arrives on focus, which is where it earns attention. */
.field input, .field textarea, .field select {
  background: var(--stock);
  box-shadow: inset 0 1px 2px rgba(13, 21, 38, 0.04);
}
.field input {
  padding: 10px 12px; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  color: var(--ink); font-family: inherit; font-size: 14px;
  transition: border-color var(--speed), box-shadow var(--speed), background var(--speed);
}
.field input::placeholder, .field textarea::placeholder { color: var(--ink-4); }
.field input:hover, .field textarea:hover, .field select:hover {
  border-color: var(--ink-4);
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--sunset); background: var(--stock);
  box-shadow: 0 0 0 3px var(--sunset-wash);
}
/* A field the browser refused, marked so it is still obvious after the native bubble
   fades. `:user-invalid` rather than `:invalid` on purpose: the latter paints every
   required field red before anybody has typed a character, which trains people to ignore
   it. This only fires once they have tried. */
.field input:user-invalid, .field textarea:user-invalid, .field select:user-invalid {
  border-color: var(--stopbar); background: var(--stopbar-wash);
}
.field input:user-invalid:focus, .field textarea:user-invalid:focus {
  box-shadow: 0 0 0 3px var(--stopbar-wash);
}


/* ── the itinerary ────────────────────────────────────────────────────────── */
/*
 * A booking is a journey before it is a row in a ledger. Drawn the way a boarding pass
 * draws it: two ports, the carrier above the line, the direction of travel on it.
 */
.legs { display: flex; flex-direction: column; gap: 10px; }
.leg {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 16px;
  padding: 12px 16px; background: var(--groove); border-radius: var(--radius-sm);
}
.leg-port .code {
  font-family: var(--font-board); font-size: var(--t-h1); font-weight: 700;
  letter-spacing: 0.02em; color: var(--ink); line-height: 1;
}
.leg-port .at { font-size: 11px; color: var(--ink-3); font-family: var(--font-board);
  margin-block-start: 3px; }
.leg-port.end { text-align: end; }
.leg-path { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.leg-carrier { font-family: var(--font-board); font-size: 11px; font-weight: 700;
  color: var(--ink-2); letter-spacing: 0.06em; }
.leg-line { position: relative; width: 100%; height: 2px; border-radius: 2px;
  background: repeating-linear-gradient(90deg, var(--rule-strong) 0 4px, transparent 4px 8px); }
.leg-plane {
  position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  transform: translate(-50%, -50%); background: var(--counterfoil);
  padding-inline: 6px; color: var(--sunset); font-size: 11px; line-height: 1;
}
html[dir="rtl"] .leg-plane { transform: translate(50%, -50%); }
.leg-class { font-size: 10px; color: var(--ink-3); text-transform: uppercase;
  letter-spacing: 0.08em; font-weight: 700; }

/* ═══════════════════════════════════════════════════════════════════════════
 * The board, properly
 *
 * A departures board flips. That is the whole reason anyone looks up at one — movement
 * means something changed, and your eye is caught before you have read a word. Digits
 * here are split-flap reels: they settle on load, staggered, and they settle again
 * whenever a count actually changes. Nothing loops, nothing decorates. Movement is
 * reserved for "this number is not what it was".
 * ═══════════════════════════════════════════════════════════════════════════ */

/* A number reads left to right in every language. Without this the digits lay out in
   the page's direction and 17 renders as 71 — which is worse than useless on a board. */
.flap { display: inline-flex; gap: 1px; direction: ltr; unicode-bidi: isolate; }
.flap-digit {
  position: relative; overflow: hidden; width: 0.62em; height: 1em;
  border-radius: 2px; background: rgba(255, 255, 255, 0.04);
}
.flap-reel {
  display: block; will-change: transform;
  transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
}
.flap-reel span { display: block; height: 1em; line-height: 1; text-align: center; }
@media (prefers-reduced-motion: reduce) {
  .flap-reel { transition: none; }
  .flap-digit { background: none; }
}
/* On paper — the money strip — the reel runs on ticket stock, not board black. */
.on-paper .flap-digit { background: rgba(20, 27, 51, 0.035); }

/* ── the day band ─────────────────────────────────────────────────────────── */
/*
 * The console opens by stating the day, the way an operations room does: the date, the
 * one fact that decides whether today is calm, and the state of the desk. One display
 * moment, then everything after it is quieter.
 */
.day-band {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--pad-open);
  padding: var(--pad-open) var(--pad-open) var(--pad-open);
  /* A blue ground, moving through the brand's own blues rather than sitting flat. */
  background: linear-gradient(100deg, var(--navy) 0%, var(--dusk) 58%, var(--sky-deep) 100%);
  color: #fff; border-radius: var(--radius);
  position: relative; overflow: hidden;
}
/* The sunset over the wing from their own hero photograph, reduced to its light.
 *
 * It was a 900×300 wash at 0.55 laid across most of the band, and orange over blue at that
 * strength is neither: it made a dusty mauve — the only colour on the console that is in
 * neither half of the identity — on the largest block of colour on the page. Tight, weak,
 * and anchored behind the figure it is lighting, it reads as warm light on blue, which is
 * what a sunset reduced to its light was always meant to be. */
.day-band::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(340px 190px at 88% 50%,
                              rgba(241, 93, 36, 0.3), transparent 72%);
  pointer-events: none;
}
/* A gradient has no logical direction. Both of these are composed around *where the
   figure is* — and the figure moves to the other end under RTL, so on every Arabic
   screen the warm light sat behind the empty half and the count sat in the dark corner.
   The same fault the toast had, in the one property that cannot express it logically, so
   it is stated instead. */
html[dir="rtl"] .day-band {
  background: linear-gradient(260deg, var(--navy) 0%, var(--dusk) 58%, var(--sky-deep) 100%);
}
html[dir="rtl"] .day-band::after {
  background: radial-gradient(340px 190px at 12% 50%,
                              rgba(241, 93, 36, 0.3), transparent 72%);
}
.day-band > * { position: relative; z-index: 1; }
.day-date { font-family: var(--font-board); font-size: 12px; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(255,255,255,0.62); font-weight: 600; }
.day-headline { font-size: var(--t-h2); font-weight: 800; letter-spacing: -0.02em;
  margin-block-start: 3px; text-wrap: balance; line-height: 1.25; }
.day-headline b { color: var(--sunset-lift); }
.day-figure { text-align: end; }
.day-figure .n { font-family: var(--font-board); font-size: var(--t-display); font-weight: 700;
  line-height: 1; letter-spacing: -0.02em; }
.day-figure .n.calm { color: #7be0ac; }
.day-figure .n.busy { color: var(--sunset-lift); }
.day-figure .cap { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: rgba(255,255,255,0.6); font-weight: 700; margin-block-start: 4px; }
@media (max-width: 860px) {
  .day-band { grid-template-columns: 1fr; }
  .day-figure { text-align: start; }
}

/* ── lifecycle: where a booking has got to ────────────────────────────────── */
/*
 * A flight-progress strip for the thing this company sells. Stations passed are solid,
 * the current one is lit, the rest are waiting. Nothing else on screen tells you where
 * a booking is at a glance.
 */
.track { display: flex; align-items: center; gap: 0; min-width: 0; }
.track-stop {
  display: flex; align-items: center; gap: 0; flex: 0 0 auto;
}
.track-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--rule-strong); border: 2px solid transparent;
}
.track-stop.done .track-dot { background: var(--taxiway); }
.track-stop.now .track-dot {
  background: var(--sunset); box-shadow: 0 0 0 4px var(--sunset-wash);
  border-color: var(--sunset);
}
.track-stop.fail .track-dot { background: var(--stopbar); box-shadow: 0 0 0 4px var(--stopbar-wash); }
.track-leg { width: 22px; height: 2px; background: var(--rule); flex: 0 0 auto; }
.track-leg.done { background: var(--taxiway); }
.track-label {
  font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; font-weight: 700;
  color: var(--ink-3); margin-inline-start: 7px;
}
.track-stop.now .track-label { color: var(--sunset-deep); }
.track-stop.fail .track-label { color: var(--stopbar); }

/* ── the manifest: bookings as board rows ─────────────────────────────────── */
/*
 * A list of bookings at a travel company is a departures list. It was a generic table;
 * it is now the object it has always been — reference, route, carrier, state, money —
 * scanned down a mono column the way anyone reads a board.
 */
.manifest { display: flex; flex-direction: column; }
.manifest-row {
  display: grid; align-items: center; gap: 14px;
  grid-template-columns: 9.5rem minmax(8rem, 1fr) auto 7rem;
  padding: 11px var(--pad); border-block-end: 1px solid var(--rule);
  cursor: pointer; transition: background var(--speed);
}
.manifest-row:last-child { border-block-end: 0; }
.manifest-row:hover { background: var(--dusk-wash); }
.manifest-ref { font-family: var(--font-board); font-weight: 700; font-size: 13px;
  color: var(--ink); }
.manifest-sub { font-size: 11px; color: var(--ink-3); margin-block-start: 1px; }
.manifest-route {
  display: flex; align-items: center; gap: 8px; font-family: var(--font-board);
  font-size: 14px; font-weight: 700; color: var(--ink-2);
}
.manifest-route .arrow { color: var(--sunset); font-size: 11px; }
.manifest-route .carrier { font-size: 11px; color: var(--ink-3); font-weight: 600; }
.manifest-money { text-align: end; font-family: var(--font-board); font-weight: 700; }
.manifest-money .margin { font-size: 11px; color: var(--taxiway); font-weight: 700; }
@media (max-width: 860px) {
  .manifest-row { grid-template-columns: 1fr auto; }
  .manifest-route, .manifest-money .margin { display: none; }
}

/* A table whose columns are short labels beside one real one.
 *
 * A `width: 100%` table with five short cells spreads them evenly across the card, so a
 * row reading "voice · the customer sent it · never collected" arrives as three words
 * separated by two inches of nothing and the card reads as a layout that failed. Giving
 * the first column the slack makes every other column shrink to its own content. */
/* A table with more columns than fit.
 *
 * Left to wrap, every cell collapses to its longest word — `Tathkarah Saudi WhatsApp`
 * becomes three lines and `Waiting for an agent` three more, so a row that carries one
 * line of information is four lines tall and the screen holds five conversations. The
 * container already scrolls sideways, which is what makes not wrapping the right answer
 * here and the wrong one in a table of prose. */
.table-wrap.wide table td,
.table-wrap.wide table th { white-space: nowrap; }

.table-wrap.tight table td:first-child,
.table-wrap.tight table th:first-child { inline-size: 100%; }
/* And the shrunk columns must not then wrap. Left to themselves they collapse to their
   longest word, so "the customer sent it" becomes four stacked lines — which is worse
   than the spreading it was meant to fix. */
.table-wrap.tight table td:not(:first-child),
.table-wrap.tight table th:not(:first-child) { white-space: nowrap; }

/* ── the conversation list ────────────────────────────────────────────────── */
/*
 * Not `.manifest`. A booking row is a reference, a route and a figure — three columns of
 * roughly equal weight, which is what `.manifest` lays out. A conversation is a *person*
 * and the last thing they said, and forcing it into the booking grid gave the screen its
 * two worst faults: the name had nowhere to go (so the row led with a reference nobody
 * recognises) and the pills had nowhere to go either, so they piled into the middle
 * column in a heap.
 *
 * Four regions, in the order the eye needs them: who · what was last said · the state ·
 * when and who has it.
 */
.conv { display: flex; flex-direction: column; }
.conv-row {
  display: grid; align-items: start; gap: 12px;
  grid-template-columns: 34px minmax(0, 1fr) auto;
}
/* With the tick column. Declared as its own rule rather than by changing the one above,
   because the row is drawn without a tick for anybody who cannot act on several at once
   and a three-column grid with a missing first child shifts everything one place. */
.conv-row:has(.conv-pick) {
  grid-template-columns: auto 34px minmax(0, 1fr) auto;
  padding: 12px var(--pad); border-block-end: 1px solid var(--rule);
  cursor: pointer; transition: background var(--speed);
  position: relative;
}
.conv-row:last-child { border-block-end: 0; }
.conv-row:hover { background: var(--dusk-wash); }
/* The leading marker a clickable row grows on hover, the same one the ticket rows use.
   Logical inset, so it appears on the correct edge under RTL without a second rule. */
.conv-row::before {
  content: ''; position: absolute; inset-block: 6px; inset-inline-start: 0;
  inline-size: 3px; border-radius: 0 2px 2px 0; background: var(--sunset);
  opacity: 0; transition: opacity var(--speed);
}
.conv-row:hover::before { opacity: 1; }
/* Waiting on us and open: the row itself says so, so a supervisor scanning the list does
   not have to read every chip to find the ones that are somebody's problem right now. */
.conv-row.on-us { background: color-mix(in srgb, var(--sunset) 5%, transparent); }
.conv-row.on-us::before { opacity: 0.45; }
.conv-row.junk { opacity: 0.55; }

.conv-mark {
  inline-size: 34px; block-size: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--dusk); color: #fff;
  font-size: 12px; font-weight: 800; letter-spacing: 0.02em;
}
/* A conversation nobody has put a name to. Held apart visually because it is a different
   fact from a named customer and it is the one an agent can *fix*. */
.conv-mark.nameless { background: var(--groove); color: var(--ink-3); }
.conv-mark.vip { background: var(--sunset); }

.conv-body { min-inline-size: 0; display: flex; flex-direction: column; gap: 3px; }
.conv-name {
  font-weight: 700; font-size: 14px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.conv-name .conv-unnamed { font-weight: 600; color: var(--ink-3); font-style: italic; }
.conv-ref {
  font-family: var(--font-board); font-size: 11px; color: var(--ink-3);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
/* The last thing said. One line, clipped — the row is a way into the conversation, not a
   substitute for opening it. */
.conv-last {
  font-size: 12.5px; color: var(--ink-2); display: flex; align-items: baseline; gap: 6px;
  min-inline-size: 0;
}
.conv-last .txt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.conv-last .none { color: var(--ink-4); font-style: italic; }
/* Which way it went. A glyph rather than a word, because it is repeated on every row and
   the word would be the widest thing in the column. */
.conv-dir { flex: none; font-weight: 800; font-size: 11px; color: var(--ink-4); }
.conv-dir.in { color: var(--sunset-deep); }
.conv-dir.out { color: var(--sky-deep); }
.conv-dir.internal { color: var(--ink-4); }
.conv-tags { display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-block-start: 2px; }

.conv-side {
  display: flex; flex-direction: column; align-items: flex-end; gap: 4px;
  text-align: end; flex: none;
}
.conv-when { font-size: 11px; color: var(--ink-3); white-space: nowrap; }
.conv-agent {
  font-size: 11px; font-weight: 700; color: var(--ink-2); white-space: nowrap;
  display: flex; align-items: center; gap: 5px;
}
.conv-agent .dot {
  inline-size: 6px; block-size: 6px; border-radius: 50%; background: var(--taxiway);
  flex: none;
}
/* Nobody owns it. The state that goes wrong quietly — it is on no agent's screen, so no
   agent is late with it. */
.conv-agent.nobody { color: var(--sunset-deep); }
.conv-agent.nobody .dot { background: var(--sunset); }
.conv-count { font-size: 11px; color: var(--ink-4); white-space: nowrap; }

@media (max-width: 900px) {
  .conv-row { grid-template-columns: 34px minmax(0, 1fr); }
  .conv-side { grid-column: 2; flex-direction: row; align-items: center;
    justify-content: flex-start; gap: 10px; }
}

/* The filters, in a bar rather than loose on the page.
 *
 * The search box and four chips were floating directly on the canvas with no surface
 * under them, which is what made the top of this screen read as unfinished. */
.conv-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px var(--pad); background: var(--stock);
  border: 1px solid var(--rule); border-radius: var(--radius);
  box-shadow: var(--shadow); margin-block-end: var(--gap);
}
.conv-bar .findall { flex: 1 1 16rem; min-inline-size: 12rem; }
.conv-bar .findall input { inline-size: 100%; }

/* A strip that does not have to be four cells.
 *
 * `.strip` is `repeat(4, 1fr)` with `overflow: hidden`, so a fifth reading either wrapped
 * into a second row of orphans or was simply left out — which is why this screen showed
 * four month-to-date figures and none of the three that say what is happening now. */
.strip.fluid { grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }
/* Dividers drawn *by the cells*, and clipped at the edges.
 *
 * `.strip` draws its rules with `border-inline-end` and clears the last one, which only
 * works when there is exactly one row: wrapped, the second row has no line above it and a
 * stray rule hanging off its end. Painting them through a 1px grid gap fixes that and
 * introduces a worse fault — the empty tracks beside a short last row become visible grey
 * tiles. A shadow belongs to the cell, so an empty track draws nothing, and the outermost
 * edges fall outside `overflow: hidden` and never appear. */
.strip.fluid .cell {
  border-inline-end: 0;
  box-shadow: 1px 0 0 var(--rule), 0 1px 0 var(--rule);
}

/* ── sparklines ───────────────────────────────────────────────────────────── */

/* The chart stretches; its ink does not. See `sparkline()` for what that was doing. */
.spark-wrap { position: relative; display: block; margin-block-start: 8px; }
.spark-tip {
  position: absolute; right: 0; width: 7px; height: 7px; border-radius: 50%;
  background: var(--sunset); transform: translate(50%, -50%);
  box-shadow: 0 0 0 2.5px var(--stock), 0 0 0 4px color-mix(in srgb, var(--sunset) 30%, transparent);
}
.spark { display: block; width: 100%; height: 30px; overflow: visible; }
.spark .fill { fill: var(--sunset); opacity: 0.12; }
.spark .line { vector-effect: non-scaling-stroke;
  fill: none; stroke: var(--sunset); stroke-width: 1.6;
  stroke-linejoin: round; stroke-linecap: round; }
.spark .tip { fill: var(--sunset); }
.spark .tip-ring { fill: none; stroke: var(--sunset); stroke-width: 1.4; opacity: 0.35; }
.spark .base { vector-effect: non-scaling-stroke; stroke: var(--rule); stroke-width: 1; stroke-dasharray: 2 3; }

/* ── empty states, designed ───────────────────────────────────────────────── */
/*
 * An empty console is the normal state of a good day, and the first hour of a new
 * company. Both deserve a sentence that says which one this is.
 */
.nothing {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 40px var(--pad-open); text-align: center;
}
.nothing-mark {
  width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center;
  background: var(--counterfoil); color: var(--ink-3);
}
.nothing-mark svg { width: 20px; height: 20px; }
.nothing.good .nothing-mark { background: var(--taxiway-wash); color: var(--taxiway); }
.nothing-title { font-weight: 800; color: var(--ink); font-size: var(--t-h4); }
.nothing-said { font-size: 13px; color: var(--ink-3); max-width: 30rem; text-wrap: balance; }
.board .nothing-mark { background: rgba(255,255,255,0.06); color: var(--board-ink-2); }
.board .nothing.good .nothing-mark { background: rgba(53,214,138,0.14); color: var(--taxiway-lit); }
.board .nothing-title { color: var(--board-ink); }
.board .nothing-said { color: var(--board-ink-2); }

/* ═══════════════════════════════════════════════════════════════════════════
 * Doing things
 *
 * Every act in this console happens in a sheet that slides in from the edge, and every
 * sheet says three things before it says anything else: what will happen, to what, and —
 * where the act is gated — that a reason is required and who else has to agree.
 * ═══════════════════════════════════════════════════════════════════════════ */

.scrim {
  position: fixed; inset: 0; z-index: 40; background: rgba(20, 27, 51, 0.42);
  backdrop-filter: blur(2px); opacity: 0;
  animation: fade var(--speed) forwards;
}
@keyframes fade { to { opacity: 1; } }

.sheet {
  position: fixed; inset-block: 0; inset-inline-end: 0; z-index: 41;
  width: min(30rem, 100%); background: var(--stock);
  border-inline-start: 1px solid var(--rule); box-shadow: var(--shadow-lift);
  display: flex; flex-direction: column;
  transform: translateX(var(--slide-from, 100%));
  animation: slide 220ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
html[dir="rtl"] .sheet { --slide-from: -100%; }
@keyframes slide { to { transform: translateX(0); } }
@media (prefers-reduced-motion: reduce) {
  .scrim, .sheet { animation: none; opacity: 1; transform: none; }
}

.sheet-head {
  padding: var(--pad-open) var(--pad-open) var(--pad);
  background: var(--dusk); color: #fff; position: relative; overflow: hidden;
}
/* **Decoration never takes a click.**
 *
 * This wash covers the whole header, and a pseudo-element hit-tests as its own parent —
 * so `elementFromPoint` over the close button returned `.sheet-head` and the **X did not
 * close the sheet**. `.sheet-head > *` lifts the real children above it, which is why the
 * title looked fine and only the one interactive control in the header was dead. Nothing
 * here is meant to be clickable, so it opts out of hit-testing entirely rather than
 * relying on every future child remembering to out-stack it. */
.sheet-head::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(241, 93, 36, 0.3), transparent 75%);
}
.sheet-head > * { position: relative; z-index: 1; }
.sheet-kicker { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase;
  color: rgba(255,255,255,0.65); font-weight: 700; font-family: var(--font-board); }
.sheet-title { font-size: var(--t-h3); font-weight: 800; margin-block-start: 4px;
  text-wrap: balance; }
.sheet-target { font-family: var(--font-board); font-size: 12px;
  color: var(--sunset-lift); margin-block-start: 5px; }
.sheet-close {
  /* **Above its siblings, not level with them.**
   *
   * `.sheet-head > *` lifts every child to `z-index: 1` so they clear the gradient wash.
   * That put the close button on the same layer as the kicker and the title — which are
   * full-width blocks that come *after* it in the markup, so they painted over it and ate
   * the click. `elementsFromPoint` over the X returned `.sheet-kicker` first.
   *
   * The second time a header element has covered this button. The first was the wash
   * itself, which now opts out of hit-testing; this is the text that sits on top of it. */
  z-index: 2;
  position: absolute; inset-block-start: 14px; inset-inline-end: 14px;
  width: 28px; height: 28px; border-radius: 8px; border: 0; cursor: pointer;
  background: rgba(255,255,255,0.12); color: #fff; font-size: 16px; line-height: 1;
  display: grid; place-items: center;
}
.sheet-close:hover { background: rgba(255,255,255,0.22); }

/* The tear line again — a sheet is a stub you are filling in. */
.sheet-tear {
  height: 14px; flex: 0 0 auto;
  background-image:
    radial-gradient(circle at 0 50%, var(--terminal) 7px, transparent 7px),
    radial-gradient(circle at 100% 50%, var(--terminal) 7px, transparent 7px),
    repeating-linear-gradient(90deg, var(--rule-strong) 0 5px, transparent 5px 10px);
  background-repeat: no-repeat, no-repeat, repeat-x;
  background-position: left center, right center, center center;
  background-size: auto, auto, auto 1px;
  background-color: var(--stock);
}

.sheet-body { flex: 1; overflow-y: auto; padding: var(--pad-open);
  display: flex; flex-direction: column; gap: var(--pad); }
.sheet-foot {
  padding: var(--pad) var(--pad-open); border-block-start: 1px solid var(--rule);
  display: flex; gap: 8px; align-items: center; background: var(--groove);
}
.sheet-foot .grow { flex: 1; }

.field textarea {
  padding: 10px 12px; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  color: var(--ink); font-family: inherit; font-size: 14px;
  resize: vertical; min-height: 4.5rem;
}
.field textarea:focus { outline: none; border-color: var(--sunset); background: var(--stock);
  box-shadow: 0 0 0 3px var(--sunset-wash); }
.field select {
  padding: 10px 12px; border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  color: var(--ink); font-family: inherit; font-size: 14px; cursor: pointer;
}
.field .hint { font-size: 11px; color: var(--ink-3); font-weight: 500;
  text-transform: none; letter-spacing: 0; }

/* A gated act announces itself before the form does. */
.gate-warning {
  padding: 11px 13px; border-radius: var(--radius-sm);
  background: var(--sunset-wash); border: 1px dashed color-mix(in srgb, var(--sunset) 45%, transparent);
  font-size: 13px; color: var(--ink-2);
}
.gate-warning b { color: var(--sunset-deep); }

/* ── the actions a row offers ─────────────────────────────────────────────── */

.actions { display: flex; gap: 6px; flex-wrap: wrap; }
.action {
  border: 1px solid var(--rule); background: var(--stock); color: var(--ink-2);
  padding: 5px 11px; border-radius: 999px; cursor: pointer;
  font-size: 12px; font-weight: 700; font-family: inherit;
  transition: all var(--speed); white-space: nowrap;
}
.action:hover { border-color: var(--sunset); color: var(--sunset-deep);
  background: var(--sunset-wash); }
/* `\01F512` — the six-digit form. Written as `\\1F512` this rendered the literal
   text \1F512 on every gated button in the console, because the doubled backslash
   is an escaped backslash and what followed it was just characters. */
.action.gated::before { content: '\01F512'; margin-inline-end: 5px; font-size: 10px; }
.action.danger:hover { border-color: var(--stopbar); color: var(--stopbar);
  background: var(--stopbar-wash); }
.action[disabled] { opacity: 0.45; cursor: not-allowed; }
.action[disabled]:hover { border-color: var(--rule); color: var(--ink-2); background: var(--stock); }

/* ── a decision row: two people, made visible ─────────────────────────────── */

.decision { display: flex; flex-direction: column; gap: 8px; }
.decision-row {
  display: grid; grid-template-columns: 4px 1fr auto; gap: 12px; align-items: center;
  padding: 12px var(--pad); border-block-end: 1px solid var(--rule);
}
.decision-row:last-child { border-block-end: 0; }
.decision-row .mark { align-self: stretch; border-radius: 3px; background: var(--sunset); }
.decision-row.blocked .mark { background: var(--rule-strong); }
.decision-what { font-weight: 700; color: var(--ink); }
.decision-why { font-size: 12px; color: var(--ink-3); margin-block-start: 2px; }
.decision-why b { color: var(--ink-2); }
.decision-blocked {
  font-size: 11px; color: var(--edge); background: var(--edge-wash);
  padding: 3px 9px; border-radius: 5px; font-weight: 700;
}

/* ── toast ────────────────────────────────────────────────────────────────── */

/* Two faults, both only visible on a toast long enough to reach an edge — and the console
   is Arabic-first, so the worse of them was the one nobody saw.

   **`left`, not `inset-inline-start`.** Centring is physical: the middle of the screen is
   the middle of the screen in either direction. Paired with `translateX(-50%)`, which is
   also physical, the logical property flipped to `right: 50%` in Arabic while the
   transform went on shifting left — so every toast on every Arabic screen sat a full
   width off-centre and ran off the edge. It read as a message too long to fit.

   **A width of its own.** Without one a long sentence grows past the viewport in either
   direction. The sentences that most need reading are the longest ones — this was found on
   the refusal telling somebody that reopening a ticket had left a deadline cancelled. */
.toast {
  position: fixed; inset-block-end: 20px; left: 50%;
  transform: translateX(-50%); z-index: 60;
  max-width: min(560px, calc(100vw - 2rem)); text-align: center;
  padding: 11px 18px; border-radius: 18px; font-weight: 700; font-size: 13px;
  box-shadow: var(--shadow-lift); display: flex; align-items: center; gap: 9px;
  background: var(--ink); color: var(--stock);
}
.toast.ok { background: var(--taxiway); color: #fff; }
.toast.bad { background: var(--stopbar); color: #fff; }
@media (prefers-reduced-motion: no-preference) {
  .toast { animation: rise 200ms cubic-bezier(0.16, 1, 0.3, 1); }
  @keyframes rise { from { transform: translate(-50%, 14px); opacity: 0; } }
}

/* ═══ the reconciliation workbench ══════════════════════════════════════════
   Built around the exception. A clean run is a quiet row; an open one carries a count
   in the accent, because that count is the accountant's actual to-do list. */

.recon-runs { display: flex; flex-direction: column; }
.recon-run {
  display: flex; align-items: center; gap: 0.7rem; width: 100%;
  padding: 0.7rem var(--pad); background: none; border: 0;
  border-block-end: 1px solid var(--rule); cursor: pointer; text-align: start;
  font: inherit; color: inherit; transition: background var(--speed);
}
.recon-run:hover { background: var(--counterfoil); }
.recon-run.on { background: var(--sunset-wash); }
.recon-run-mark {
  inline-size: 3px; block-size: 26px; border-radius: 2px; flex: none;
  background: var(--taxiway);
}
.recon-run.open .recon-run-mark { background: var(--edge); }
.recon-run-body { display: flex; flex-direction: column; gap: 1px; flex: 1; min-inline-size: 0; }
.recon-run-tally { display: flex; flex-direction: column; align-items: flex-end; }
.recon-run-tally .num { font-variant-numeric: tabular-nums; font-weight: 700; }
.recon-run-open {
  min-inline-size: 26px; padding: 2px 7px; border-radius: 999px;
  background: var(--sunset); color: #fff; font-weight: 800;
  font-size: var(--t-caption); text-align: center; font-variant-numeric: tabular-nums;
}

.recon-lines { display: flex; flex-direction: column; }
.recon-line {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.6rem var(--pad); border-block-end: 1px solid var(--rule);
  border-inline-start: 3px solid transparent;
}
.recon-line.warn { border-inline-start-color: var(--edge); background: var(--edge-wash); }
.recon-line.bad  { border-inline-start-color: var(--stopbar); background: var(--stopbar-wash); }
.recon-line.ok   { border-inline-start-color: var(--taxiway); }
.recon-line-no {
  font-family: var(--font-board); font-size: var(--t-caption);
  color: var(--ink-4); min-inline-size: 1.8rem;
}
.recon-line-body { display: flex; flex-direction: column; gap: 1px; flex: 1; min-inline-size: 0; }
.recon-line-desc { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recon-line-amount { font-variant-numeric: tabular-nums; font-weight: 700; }

/* The candidates the machine found and would not choose between. */
.cand-list { display: flex; flex-direction: column; gap: 6px; margin-block-start: 0.6rem; }
.cand {
  display: flex; align-items: center; gap: 0.7rem; width: 100%; text-align: start;
  padding: 0.6rem 0.7rem; border: 1px solid var(--rule); border-radius: var(--radius-sm);
  background: var(--stock); cursor: pointer; font: inherit; color: inherit;
  transition: border-color var(--speed), transform var(--speed);
}
.cand:hover { border-color: var(--sunset); transform: translateX(0); }
.cand:disabled { opacity: 0.5; cursor: wait; }
.cand.exact { border-color: var(--taxiway); background: var(--taxiway-wash); }
.cand-body { display: flex; flex-direction: column; gap: 1px; flex: 1; min-inline-size: 0; }
.cand-amount { font-variant-numeric: tabular-nums; font-weight: 700; }
.cand-gap {
  font-size: var(--t-caption); color: var(--ink-3); font-variant-numeric: tabular-nums;
  padding: 2px 6px; border-radius: 4px; background: var(--groove);
}
.cand-gap.exact { background: var(--taxiway); color: #fff; }

details.fold summary {
  cursor: pointer; padding: 0.6rem var(--pad); font-size: var(--t-caption);
  color: var(--ink-3); user-select: none;
}
details.fold summary::marker { color: var(--ink-4); }

/* ═══ a score, taken apart ══════════════════════════════════════════════════
   The unmeasured component is drawn as a void rather than an empty bar, because an
   empty bar reads as zero and this is the one thing that must never read as zero. */

.score-head { text-align: center; padding: var(--pad-open) 0 var(--pad); }
.score-total {
  font-family: var(--font-board); font-size: var(--t-display); font-weight: 800;
  color: var(--ink); line-height: 1;
}
.score-total .of { font-size: var(--t-h3); color: var(--ink-4); font-weight: 600; }
.score-grade {
  display: inline-block; margin-block-start: 0.5rem; padding: 3px 12px;
  border-radius: 999px; background: var(--dusk); color: #fff;
  font-weight: 800; letter-spacing: 0.06em;
}
.score-head.missing .score-total {
  color: var(--unlit); font-size: var(--t-h2);
  /* The board face is for figures only. It has no Arabic shaping, so Arabic set in it
     comes out as disconnected letters — and the missing headline is words, not a number. */
  font-family: inherit; letter-spacing: 0;
}
.score-why {
  max-inline-size: 34ch; margin: 0.6rem auto 0; font-size: var(--t-caption);
  color: var(--ink-3); line-height: 1.55;
}

.score-parts { display: flex; flex-direction: column; gap: 0.75rem; padding-block-start: 0.3rem; }
.score-part-head { display: flex; align-items: baseline; gap: 0.5rem; font-size: var(--t-body); }
.score-part-head .num { font-variant-numeric: tabular-nums; font-weight: 700; }
.score-part-head .of { color: var(--ink-4); font-weight: 500; }
.score-track {
  block-size: 7px; border-radius: 4px; background: var(--groove);
  overflow: hidden; margin-block-start: 4px;
}
.score-fill {
  display: block; block-size: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--sunset-deep), var(--sunset));
}
.score-void {
  display: block; block-size: 100%;
  background: repeating-linear-gradient(45deg,
    var(--unlit) 0 2px, transparent 2px 7px);
  opacity: 0.55;
}
.score-part.unmeasured .score-part-head { color: var(--ink-3); }

.weights { display: flex; flex-direction: column; }
.weight-row {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0; border-block-end: 1px solid var(--rule); font-size: var(--t-body);
}
.weight-row:last-child { border-block-end: 0; }
/* A distribution's bar. `.bar` already existed in this stylesheet — scoped under
   `.queue-row` — so a `<span class="bar">` inside a `.weights` list passed the
   class-exists guard and rendered as nothing at all: transparent, zero wide. A class that
   exists somewhere else is as silent as one that does not exist anywhere.

   Sized inline by the caller as a percentage of the largest band; the flex basis is what
   stops a wide bar from squeezing the label beside it. */
.weights .bar {
  /* **A span is inline, and inline-size does not apply to one.** Without this the bar had
     no layout at all — computed width came back as the literal `0%`, which is what a
     percentage on an element the box model is not applying to looks like. */
  display: block;
  flex: 0 0 auto;
  block-size: 6px;
  min-inline-size: 2px;
  border-radius: 999px;
  background: var(--approach);
  opacity: 0.7;
}
/* The band label is one token either side of a dash and must not wrap — broken over three
   lines it read as three separate figures stacked in a column. */
.weights .band { white-space: nowrap; font-family: var(--font-board); }
/* The track the bar is drawn in. A bare flex filler cannot give a child a percentage
   width — the percentage needs a box with a resolved size, which is what this is. */
.weights .bandbar { flex: 1; display: block; min-inline-size: 0; }
.weight-chip {
  font-family: var(--font-board); font-size: var(--t-caption);
  padding: 1px 7px; border-radius: 4px; background: var(--groove); color: var(--ink-2);
  max-width: 100%; overflow-wrap: anywhere;
}
/* A value that is words rather than a code.
 *
 * The chip is set in the board's monospace, which is right for a reference, a figure or a
 * status key and wrong for a sentence — «الجمعة القادمة» and «الكويت» came out cramped and
 * mis-shaped, because a Latin monospace has no Arabic in it and the browser was
 * substituting glyph by glyph. Anything a person wrote gets the reader's own face. */
.weight-chip.said {
  font-family: inherit; font-size: 0.78rem; font-weight: 700; color: var(--ink);
  letter-spacing: 0;
}

/* ═══ the directory ═════════════════════════════════════════════════════════ */

.dept { border-block-end: 1px solid var(--rule); }
.dept-head {
  display: flex; align-items: center; padding: 0.5rem var(--pad);
  background: var(--counterfoil); font-size: var(--t-caption);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);
}
.dept-count { font-variant-numeric: tabular-nums; font-weight: 700; }
.person {
  display: flex; align-items: center; gap: 0.7rem; width: 100%; text-align: start;
  padding: 0.5rem var(--pad); background: none; border: 0; cursor: pointer;
  font: inherit; color: inherit; transition: background var(--speed);
}
.person:hover { background: var(--counterfoil); }
.person.on { background: var(--sunset-wash); }
.person-initials {
  inline-size: 28px; block-size: 28px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--dusk); color: #fff;
  font-size: var(--t-caption); font-weight: 800; letter-spacing: 0.02em;
}
.person-body { display: flex; flex-direction: column; gap: 0; min-inline-size: 0; }
.person-name { font-weight: 600; }

.alias-list { display: flex; flex-direction: column; }
.alias {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.5rem var(--pad); border-block-end: 1px solid var(--rule);
}
.alias.orphan { border-inline-start: 3px solid var(--edge); background: var(--edge-wash); }
.alias-system {
  font-family: var(--font-board); font-size: var(--t-caption);
  padding: 2px 7px; border-radius: 4px; background: var(--dusk); color: #fff;
  min-inline-size: 4.5rem; text-align: center;
}
.leave-balance {
  margin-block-start: 0.8rem; padding: 0.7rem; border-radius: var(--radius-sm);
  background: var(--groove);
}
.leave-balance .num { font-variant-numeric: tabular-nums; font-weight: 800; }

/* ═══ templates: both languages, side by side ═══════════════════════════════ */

.tpl-list { display: flex; flex-direction: column; }
.tpl { padding: 0.7rem var(--pad); border-block-end: 1px solid var(--rule); }
.tpl.off { opacity: 0.55; }
.tpl-head { display: flex; align-items: center; gap: 0.5rem; margin-block-end: 0.4rem; }
.tpl-bodies { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.tpl-body {
  padding: 0.5rem 0.6rem; border-radius: var(--radius-sm); background: var(--groove);
  font-size: var(--t-caption); line-height: 1.6; color: var(--ink-2);
}
@media (max-width: 900px) { .tpl-bodies { grid-template-columns: 1fr; } }

/* ═══ the journey ═══════════════════════════════════════════════════════════
   A vertical rail with the modules that reacted hanging off each step. Money steps get
   the accent; a step whose consequences failed gets the stop bar, because the action
   standing while its effects did not is precisely the state worth seeing. */

.journey { display: flex; flex-direction: column; padding: var(--pad); }
.jstep { display: flex; gap: 0.8rem; }
.jstep-rail { display: flex; flex-direction: column; align-items: center; flex: none; }
.jstep-dot {
  inline-size: 11px; block-size: 11px; border-radius: 50%; margin-block-start: 4px;
  background: var(--stock); border: 2.5px solid var(--rule-strong);
}
.jstep.financial .jstep-dot { border-color: var(--sunset); background: var(--sunset); }
.jstep.problem .jstep-dot { border-color: var(--stopbar); background: var(--stopbar); }
.jstep-line { inline-size: 2px; flex: 1; background: var(--rule); margin-block: 3px; }
.jstep-body { flex: 1; min-inline-size: 0; padding-block-end: 1.1rem; }
.jstep-head { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.jstep-what { font-weight: 700; }
.jstep-from {
  font-family: var(--font-board); font-size: var(--t-caption);
  padding: 1px 6px; border-radius: 4px; background: var(--groove); color: var(--ink-3);
}
.pill.financial { background: var(--sunset-wash); color: var(--sunset-deep); }
.badish { color: var(--stopbar); }
.warnish { color: var(--edge); }

.touched { display: flex; flex-wrap: wrap; gap: 6px; }
.touched-module {
  padding: 4px 11px; border-radius: 999px; background: var(--dusk-wash);
  color: var(--dusk); font-size: var(--t-caption); font-weight: 700;
}

/* A number reads left to right in every language. Without this the digits lay out in the
   page's direction and 17 renders as 71 — which is worse than useless on a board. */
.flap { direction: ltr; unicode-bidi: isolate; }

/* A button that sits inside a row of text rather than at the foot of a form. */
.btn.tiny { padding: 3px 9px; font-size: var(--t-caption); border-radius: 5px; }
/* A candidate the engine would not have trusted: offered, but marked as outside the
   tolerance so nobody picks it thinking the machine agreed. */
.cand-gap.wide {
  background: var(--edge-wash); color: var(--edge);
  outline: 1px dashed var(--edge); outline-offset: -1px;
}
/* The bank naming this document is why the row is first. Say it, don't just sort by it. */
.cand-named {
  margin-inline-start: 7px; padding: 1px 7px; border-radius: 999px;
  background: var(--approach-wash); color: var(--approach);
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
}
/* The chevron points the way the page reads, so it mirrors in Arabic. */
.chev { inline-size: 15px; block-size: 15px; display: block; }
html[dir="rtl"] .chev { transform: scaleX(-1); }

/* A rendered template, in both languages, each in its own direction. */
.tpl-preview { display: flex; flex-direction: column; gap: 8px; margin-block-start: 0.8rem; }
.tpl-rendered {
  padding: 0.65rem 0.75rem; border-radius: var(--radius-sm);
  background: var(--groove); line-height: 1.65; font-size: var(--t-body);
}
.tpl-subject { font-weight: 800; margin-block: 2px 4px; }

/* Recent journeys — the way into the inspector when you don't already know an id. */
.journey-list { display: flex; flex-direction: column; }
.journey-row {
  display: flex; align-items: center; gap: 0.7rem; width: 100%; text-align: start;
  padding: 0.55rem var(--pad); background: none; border: 0;
  border-block-end: 1px solid var(--rule); cursor: pointer; font: inherit; color: inherit;
  transition: background var(--speed);
}
.journey-row:hover { background: var(--counterfoil); }
.journey-row.broken { border-inline-start: 3px solid var(--stopbar); }
.journey-count {
  min-inline-size: 24px; padding: 2px 6px; border-radius: 5px; flex: none;
  background: var(--groove); color: var(--ink-2); font-weight: 800;
  font-size: var(--t-caption); text-align: center; font-variant-numeric: tabular-nums;
}
.journey-row .grow { display: flex; flex-direction: column; gap: 1px; }
.journey-opened { font-weight: 600; }

/* ═══ lists that can grow without limit ═════════════════════════════════════
   Seventeen alerts is a normal Tuesday, and a card that grows to fit them pushes
   everything below it off the screen — so the queue that needs attention hides the
   panels that explain it. These scroll inside their own card instead. The cap is
   generous enough that a short list never looks clipped. */
.queue, .journey-list, .recon-lines, .alias-list, .tpl-list, .decision, .cand-list {
  max-block-size: 27rem; overflow-y: auto; overscroll-behavior: contain;
}
/* The roster is the page's subject on the directory, so it gets more room. */
.card-body.flush > .dept:first-child { border-block-start: 0; }

/* A scrollbar that belongs to this design rather than the operating system's. */
.queue::-webkit-scrollbar, .journey-list::-webkit-scrollbar,
.recon-lines::-webkit-scrollbar, .alias-list::-webkit-scrollbar,
.tpl-list::-webkit-scrollbar, .decision::-webkit-scrollbar,
.cand-list::-webkit-scrollbar { inline-size: 9px; }
.queue::-webkit-scrollbar-thumb, .journey-list::-webkit-scrollbar-thumb,
.recon-lines::-webkit-scrollbar-thumb, .alias-list::-webkit-scrollbar-thumb,
.tpl-list::-webkit-scrollbar-thumb, .decision::-webkit-scrollbar-thumb,
.cand-list::-webkit-scrollbar-thumb {
  background: var(--rule-strong); border-radius: 999px;
  border: 2px solid transparent; background-clip: content-box;
}

/* ═══ the alert queue ═══════════════════════════════════════════════════════
   A row per condition that is currently true. The severity bar carries the urgency so
   the list reads at a glance without colouring the whole row and shouting. */
.queue-row {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.55rem var(--pad); border-block-end: 1px solid var(--rule);
}
.queue-row[data-go] { cursor: pointer; transition: background var(--speed); }
.queue-row[data-go]:hover { background: var(--counterfoil); }
.queue-row .bar {
  inline-size: 3px; block-size: 30px; border-radius: 2px; flex: none;
  background: var(--unlit);
}
.queue-row.critical .bar { background: var(--stopbar); }
.queue-row.high     .bar { background: var(--caution); }
.queue-row.medium   .bar { background: var(--edge); }
.queue-row.low      .bar { background: var(--approach); }
.queue-row .title { font-weight: 600; }
.queue-row .meta {
  font-size: var(--t-caption); color: var(--ink-3);
  font-family: var(--font-board);
}

/* Where a person's quality findings repeat — a pattern read as a pattern. */
.checks { display: flex; flex-direction: column; gap: 0.55rem; }
.check-row { display: flex; align-items: center; gap: 0.7rem; font-size: var(--t-body); }
.check-row .num { font-variant-numeric: tabular-nums; font-weight: 700; min-inline-size: 1.6rem;
                  text-align: end; }
.check-track {
  inline-size: 44%; block-size: 7px; border-radius: 4px;
  background: var(--groove); overflow: hidden; flex: none;
}
.check-fill {
  display: block; block-size: 100%; border-radius: 4px;
  background: linear-gradient(90deg, var(--caution), var(--edge));
}
/* A number that is also a way in. Reads as text until you point at it. */
.linky {
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: pointer; border-block-end: 1px dotted var(--ink-4);
}
.linky:hover { color: var(--sunset); border-block-end-color: var(--sunset); }
.finding {
  display: inline-block; margin: 1px 3px 1px 0; padding: 1px 7px; border-radius: 4px;
  background: var(--caution-wash); color: var(--caution); font-size: 11px; font-weight: 600;
}

/* ═══ the event catalogue ═══════════════════════════════════════════════════
   Not documentation about the system — the system's own list, read back. */
.cat-group { border-block-end: 1px solid var(--rule); }
.cat-group-head {
  display: flex; align-items: center; padding: 0.5rem var(--pad);
  background: var(--counterfoil); font-size: var(--t-caption);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3);
  position: sticky; inset-block-start: 0; z-index: 1;
}
.cat-event {
  padding: 0.7rem var(--pad); border-block-end: 1px solid var(--rule);
  border-inline-start: 3px solid transparent;
}
.cat-event:last-child { border-block-end: 0; }
.cat-event.financial { border-inline-start-color: var(--sunset); }
.cat-event-head { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
.cat-name { font-family: var(--font-board); font-weight: 700; font-size: var(--t-body); }
.cat-trigger {
  font-size: var(--t-caption); color: var(--ink-2);
  margin-block: 3px 6px; line-height: 1.55;
}
.cat-meta {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-block-start: 4px;
}
.cat-label {
  font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-4); min-inline-size: 4.2rem;
}
.cat-field {
  font-family: var(--font-board); font-size: 10px; padding: 1px 6px;
  border-radius: 4px; background: var(--groove); color: var(--ink-2);
}

/* One event, opened. */
.ev-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-block-start: 0.6rem; }
.ev-cell {
  display: flex; flex-direction: column; gap: 1px;
  padding: 0.45rem 0.6rem; border-radius: var(--radius-sm); background: var(--groove);
}
.ev-payload { display: flex; flex-direction: column; gap: 4px; }
.ev-field { display: flex; align-items: baseline; gap: 0.6rem; }
.ev-value { font-weight: 600; overflow-wrap: anywhere; }
.ev-consumers { display: flex; flex-direction: column; gap: 4px; }
.ev-consumer { display: flex; align-items: center; gap: 0.5rem; }
.jstep[data-event] { cursor: pointer; }
.jstep[data-event]:hover .jstep-what { color: var(--sunset); }
@media (max-width: 700px) { .ev-grid { grid-template-columns: 1fr; } }
.perm {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.4rem var(--pad); border-block-end: 1px solid var(--rule);
  font-size: var(--t-caption);
}
.perm:last-child { border-block-end: 0; }

/* Every version of a verdict — including the ones that were superseded. */
.verdicts { display: flex; flex-direction: column; gap: 6px; }
.verdict {
  padding: 0.55rem 0.7rem; border-radius: var(--radius-sm);
  background: var(--groove); border-inline-start: 3px solid var(--rule-strong);
}
.verdict.current { border-inline-start-color: var(--taxiway); }
.verdict.superseded { opacity: 0.72; }
.verdict-head { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
.verdict-v { font-family: var(--font-board); font-weight: 800; }

/* A threshold nobody has set. Drawn as unlit rather than empty — an empty chip reads as
   zero, and zero is a claim this console will not make on somebody's behalf. */
.weight-chip.unset {
  background: var(--unlit-wash); color: var(--unlit);
  border: 1px dashed var(--rule-strong);
}
/* A statement row that corrects another. Marked, because a correction is a thing a
   partner queries and needs to be able to find. */
tr.corrected td { background: var(--edge-wash); }
/* A reversed charge line. Still there — nothing is deleted — but struck through, so the
   eye reaches the total without having to add up which lines still count. */
.breakdown .line.corrected { opacity: 0.62; }
.breakdown .line.corrected .amount { text-decoration: line-through; }

/* ═══ a conversation, as a conversation ═════════════════════════════════════
   Their words on one side, ours on the other. The bot gets its own mark because
   "who said this" is the first question anyone reviewing a chat asks. */
/* A window, not a page that grows.
 *
 * The transcript ran the full height of whatever was in it, so a conversation of forty
 * messages made the page forty messages tall: the composer sat at the bottom of all of
 * it, sending moved the ground under the reader, and there was no way to glance back
 * three messages without losing the box. Bitrix scrolls the transcript inside a fixed
 * frame with the composer pinned under it, and that is right — the thing you are writing
 * into should not move because of what you wrote.
 *
 * `min()` rather than a fixed height: on a laptop the viewport decides, and on a short
 * window a frame taller than the screen is the same problem again. */
.thread {
  display: flex; flex-direction: column; gap: 10px; padding: var(--pad);
  max-block-size: min(58vh, 34rem); overflow-y: auto; overscroll-behavior: contain;
}
.bubble {
  max-inline-size: 78%; padding: 0.6rem 0.8rem; border-radius: var(--radius);
  background: var(--groove); position: relative;
}
.bubble.them { align-self: flex-start; border-start-start-radius: 3px; }
.bubble.us {
  align-self: flex-end; border-start-end-radius: 3px;
  background: var(--sunset-wash);
}
.bubble.bot { background: var(--dusk-wash); }
.bubble-who {
  font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin-block-end: 2px;
}
.bubble.us .bubble-who { color: var(--sunset-deep); }
.bubble.bot .bubble-who { color: var(--dusk); }
.bubble-body { line-height: 1.6; overflow-wrap: anywhere; }
.bubble-at { font-size: 10px; color: var(--ink-4); margin-block-start: 4px; }
/* An attachment in the transcript. Openable from where the agent is looking, rather than
   printed as body text and openable only from a panel further down the page. */
.bubble-file { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* A file where the person is looking.
 *
 * Downloading is not seeing: a photograph has to be looked at and a voice note played,
 * and a button that puts a file in the Downloads folder answers a different question from
 * the one an agent has with a customer on the telephone. */
.media-slot { margin-block-start: 6px; }
.media-slot:empty { margin-block-start: 0; }
.media-image {
  display: block; max-inline-size: 100%; max-block-size: 16rem; inline-size: auto;
  /* A floor, because an image smaller than its own frame is one nobody can see. A
     1-pixel PNG drew as three pixels of nothing and read as a picture that had failed to
     load; the ground and the minimum make it a small tile that is plainly a small
     picture. `object-fit: contain` so nothing is stretched to reach the minimum. */
  min-inline-size: 4rem; min-block-size: 3rem; object-fit: contain;
  border-radius: var(--radius-sm); border: 1px solid var(--rule);
  background: var(--counterfoil);
}
.media-audio { inline-size: 100%; max-inline-size: 22rem; block-size: 34px; }
.media-video {
  display: block; max-inline-size: 100%; max-block-size: 18rem;
  border-radius: var(--radius-sm);
}
/* A PDF in the browser's own viewer, which is the one an agent already knows how to read
   a ticket in. Given a real height, because an embed with none collapses to nothing and
   reads as a file that failed to open. */
.media-doc {
  inline-size: 100%; block-size: 26rem; border: 1px solid var(--rule);
  border-radius: var(--radius-sm); background: var(--counterfoil);
}
/* In a table cell the picture is a thumbnail, not a page. */
.media-cell { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.media-cell .media-image { max-block-size: 3.4rem; }
.media-cell .media-audio { max-inline-size: 14rem; }
/* No embed here: a document in a cell opens in a tab, so nothing is hidden and then
   offered — the button that appeared to work and showed nothing was this rule. */

.bubble-file-kind {
  font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3); padding: 2px 7px; border-radius: 999px;
  background: var(--stock); border: 1px solid var(--rule-strong);
}

/* This machine's clock is out, and the console is showing the server's instead. Marked
   rather than only corrected: a workstation an hour out stamps every screenshot, email and
   spreadsheet that leaves it, and none of those come through here. */
#clock.askew { color: var(--sunset-deep); font-weight: 700; cursor: help; }

/* ── files and media, in a drawer ─────────────────────────────────────────
 *
 * The register used to be a full-width table above the conversation, so a chat with six
 * files pushed the transcript off the screen and every agent scrolled past it all day.
 * It is a panel beside the chat now — the console's own right-side drawer with a
 * different body, so the scrim, the slide and RTL come with it.
 */
.filebar { display: flex; flex-direction: column; min-block-size: 0; }
.filebar-tabs {
  display: flex; gap: 2px; padding-block-end: 8px;
  border-block-end: 1px solid var(--rule); margin-block-end: 10px;
}
.filebar-tab {
  flex: 1; font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  padding: 7px 6px; border: 0; border-radius: 6px 6px 0 0; background: none;
  color: var(--ink-3); display: flex; align-items: center; justify-content: center;
  gap: 5px; position: relative;
}
.filebar-tab:hover { color: var(--ink); background: var(--counterfoil); }
.filebar-tab.on { color: var(--ink); }
/* The underline sits on the container's own rule, which is why the tab row has one. */
.filebar-tab.on::after {
  content: ''; position: absolute; inset-inline: 6px; inset-block-end: -9px;
  block-size: 2px; border-radius: 2px; background: var(--sunset);
}

/* The day a file arrived. The old panel's headings run across a year, because a customer
   who rings back twelve months later is answered from this. */
/* The list itself scrolls, not the drawer's own body — so the tabs stay put while
   somebody looks through a year of a customer's files. */
.filebar-body { min-block-size: 0; overflow-y: auto; }

.filebar-day {
  font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); margin-block: 12px 6px;
}
.filebar-day:first-child { margin-block-start: 0; }
.filebar-list { display: flex; flex-direction: column; gap: 6px; }
.filebar-item {
  display: flex; align-items: center; gap: 10px; padding: 8px;
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
  background: var(--stock);
}
/* A file nobody fetched. Held apart because it is the one the customer believes they
   sent, which is the whole reason the register exists. */
.filebar-item.uncollected { border-style: dashed; opacity: 0.75; }
.filebar-what { flex: 1; min-inline-size: 0; }
.filebar-name {
  font-size: 13px; font-weight: 600; color: var(--ink);
  overflow-wrap: anywhere;
}
.filebar-item .media-image { max-block-size: 3rem; min-block-size: 2.2rem; }

/* ── the drop lists ───────────────────────────────────────────────────────
 *
 * A native `<select>` popup is drawn by the browser, in its own font and its own colours,
 * ignoring the dark theme and the direction — so every dropdown on a console that is
 * otherwise entirely this company's was visibly Chrome's. The real select is still there
 * and still holds the value; it is just not what anybody looks at.
 */
.picky { position: relative; display: block; inline-size: 100%; }
.picky select {
  /* Hidden without `display: none`, so the browser can still focus it for validation and
     a form that reports a missing required value has something to point at. */
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  inline-size: 100%; block-size: 100%; opacity: 0; pointer-events: none;
}
.picky-face {
  inline-size: 100%; text-align: start; cursor: pointer; font: inherit; font-size: 14px;
  padding: 10px 12px; color: var(--ink); background: var(--stock);
  border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  box-shadow: inset 0 1px 2px rgba(13, 21, 38, 0.04);
  display: flex; align-items: center; gap: 8px;
  transition: border-color var(--speed), box-shadow var(--speed);
}
/* The chevron, drawn rather than typed: a text arrow inherits the body font and sits at
   whatever baseline that font decides. Logical inset so it flips under RTL. */
.picky-face::after {
  content: ''; margin-inline-start: auto; flex: none;
  inline-size: 8px; block-size: 8px; border: 2px solid var(--ink-3);
  border-block-start: 0; border-inline-start: 0;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--speed);
}
.picky.open .picky-face::after { transform: rotate(225deg) translate(-3px, -3px); }
.picky-face:hover { border-color: var(--ink-4); }
.picky-face:focus-visible, .picky.open .picky-face {
  outline: none; border-color: var(--sunset); box-shadow: 0 0 0 3px var(--sunset-wash);
}
/* Nothing chosen yet reads as a placeholder, not as a value. */
.picky-face.unset { color: var(--ink-4); }
.picky-face:disabled { background: var(--counterfoil); color: var(--ink-4); cursor: default; }

.picky-list {
  position: absolute; inset-block-start: calc(100% + 4px); inset-inline: 0; z-index: 40;
  max-block-size: 17rem; overflow-y: auto; padding: 4px;
  background: var(--stock); border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lift, 0 8px 28px rgba(13,21,38,.16));
}
/* The search box inside a long list. It sits at the top of the popup and sticks there,
   so scrolling the options does not scroll away the thing you are typing into. */
.picky-hunt {
  position: sticky; inset-block-start: 0; z-index: 1;
  inline-size: 100%; font: inherit; font-size: 13px; padding: 7px 9px;
  margin-block-end: 4px; color: var(--ink); background: var(--counterfoil);
  border: 1px solid var(--rule-strong); border-radius: 5px;
}
.picky-hunt::placeholder { color: var(--ink-4); }
.picky-hunt:focus {
  outline: none; border-color: var(--sunset); background: var(--stock);
}

.picky-option {
  display: block; inline-size: 100%; text-align: start; font: inherit; font-size: 14px;
  padding: 8px 10px; border: 0; border-radius: 5px; background: none; color: var(--ink);
  cursor: pointer;
}
.picky-option:hover, .picky-option:focus-visible {
  background: var(--dusk-wash); outline: none;
}
.picky-option.on { background: var(--sunset-wash); color: var(--sunset-deep); font-weight: 700; }
/* The row that takes what somebody typed rather than something already on the list. Held
   apart from the real options because choosing it is a different act — it puts a word on
   the board that nobody has used before. */
.picky-option.picky-new {
  color: var(--sunset-deep); font-weight: 700;
  border: 1px dashed var(--sunset); margin-block-start: 2px;
}
/* An optgroup's label. The one thing the native list did legibly, and the reason the
   stage picker groups by board at all. */
.picky-group {
  font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); padding: 8px 10px 4px;
}
.picky-group:first-child { padding-block-start: 4px; }

/* ── the composer ─────────────────────────────────────────────────────────
 *
 * The bar under the transcript, and the action bar under that. Replying was a button that
 * opened a dialogue, which is fine for a form and wrong for a chat: the thing a person
 * does forty times a day should be a box that is already there.
 *
 * **The verbs live here, not in the page header.** Join · Forward · Close is the old
 * panel's action bar and it sits at the foot of the conversation, next to the box — which
 * is where an agent's eye already is. Up in the header they were a scroll away from the
 * only part of the screen anybody works in, and on a long transcript they were simply not
 * on screen.
 */
.composer {
  border-block-start: 1px solid var(--rule); background: var(--counterfoil);
  padding: 12px var(--pad); display: flex; flex-direction: column; gap: 10px;
}
/* An internal note is not a reply, and the box has to look different while one is being
   written — an agent who forgets which mode they are in tells a colleague something the
   customer reads, or tells the customer nothing at all. */
.composer.internal { background: var(--dusk-wash); }

/* A file is over the conversation.
 *
 * The whole composer is the target, not the paperclip: somebody dragging a passport
 * photograph aims at the chat, and a drop zone the size of a button is one they miss. It
 * has to be unmistakable while it is armed — a drag that lands on nothing looks exactly
 * like a drag that worked and did not upload. */
.composer.dropping {
  background: var(--sunset-wash);
  outline: 2px dashed var(--sunset); outline-offset: -6px;
}
.composer.dropping::before {
  content: attr(data-drop-said); display: block; text-align: center;
  font-size: 12px; font-weight: 700; color: var(--sunset-deep);
  padding-block-end: 4px;
}
.composer textarea {
  inline-size: 100%; min-block-size: 4.5rem; resize: vertical;
  font: inherit; font-size: 14px; line-height: 1.6; color: var(--ink);
  padding: 10px 12px; background: var(--stock);
  border: 1px solid var(--rule-strong); border-radius: var(--radius-sm);
  box-shadow: inset 0 1px 2px rgba(13, 21, 38, 0.04);
  transition: border-color var(--speed), box-shadow var(--speed);
}
.composer textarea::placeholder { color: var(--ink-4); }
.composer textarea:hover { border-color: var(--ink-4); }
/* The console's own field focus — a hairline border in the accent with a soft wash
   behind it. The global `:focus-visible` rule is a hard 2px outline set 2px off the
   element, which on a box this size reads as an alarm rather than a cursor. */
.composer textarea:focus, .composer textarea:focus-visible {
  outline: none; border-color: var(--sunset);
  box-shadow: 0 0 0 3px var(--sunset-wash);
}
.composer.internal textarea:focus, .composer.internal textarea:focus-visible {
  border-color: var(--sky); box-shadow: 0 0 0 3px var(--sky-wash);
}

.composer-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.composer-hint {
  font-size: 11px; color: var(--ink-3); flex: 1; min-inline-size: 6rem;
}

/* The paperclip. A label wrapping a hidden file input, because the browser's own file
   control cannot be styled and reads as a form from 1998 sitting in a chat composer. */
.composer-clip {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--ink-2);
  padding: 6px 11px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: var(--stock);
  transition: background var(--speed), border-color var(--speed), color var(--speed);
}
.composer-clip:hover { border-color: var(--sunset); color: var(--sunset-deep); }
.composer-clip input { position: absolute; opacity: 0; inline-size: 0; block-size: 0; }
/* The keyboard has to reach it: the input it wraps is invisible, so the ring goes on the
   label rather than on a control nobody can see. */
.composer-clip:focus-within {
  border-color: var(--sunset); box-shadow: 0 0 0 3px var(--sunset-wash);
}

/* The microphone. Recording is the one state on this bar that has to be unmistakable —
   an agent who does not notice they are still recording sends four minutes of the room. */
/* Scoped to `.btn`, because it *is* a button modifier: a bare `.composer-mic` rule that
   lays something out is inherited by anything else that ever wears the name. */
.btn.composer-mic { display: inline-flex; align-items: center; gap: 6px; }
.btn.composer-mic.on {
  background: var(--stopbar); border-color: var(--stopbar); color: #fff;
  animation: mic-live 1.4s ease-in-out infinite;
}
@keyframes mic-live { 50% { opacity: 0.72; } }
@media (prefers-reduced-motion: reduce) { .btn.composer-mic.on { animation: none; } }

/* The eye. A real control rather than the browser's checkbox, which on a tinted ground is
   a grey square nobody reads as a mode switch. */
.composer-eye {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--ink-2);
  padding: 5px 10px 5px 7px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: var(--stock);
  transition: background var(--speed), border-color var(--speed), color var(--speed);
}
.composer-eye:hover { border-color: var(--ink-4); }
.composer-eye input { position: absolute; opacity: 0; pointer-events: none; }
.composer-eye .dot {
  inline-size: 15px; block-size: 15px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--rule-strong); background: var(--stock);
  transition: background var(--speed), border-color var(--speed);
}
.composer.internal .composer-eye {
  border-color: var(--sky); background: var(--sky-wash); color: var(--sky-deep);
}
.composer.internal .composer-eye .dot { background: var(--sky); border-color: var(--sky); }

/* Join · Forward · Close, and everything after them. Separated from the box by a rule
   rather than by space alone: they act on the conversation, not on what is typed, and a
   Send button beside a Close button with nothing between them is a mis-click waiting. */
.composer-acts {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding-block-start: 10px; border-block-start: 1px dashed var(--rule);
}
/* The three carry weight; everything after them does not.
 *
 * They were `.action` chips in a row of eight identical ones up in the page header, and
 * the report was "found the buttons but they're kinda hidden" — which is what a primary
 * verb looks like when it is drawn the same as every secondary one beside it. */
.composer-acts .btn { font-weight: 700; }
.composer-acts .btn.tiny { font-weight: 600; color: var(--ink-2); }
/* A hairline between the verbs that change who is dealing with this and the ones that
   are housekeeping. */
.composer-more {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding-inline-start: 10px; margin-inline-start: 4px;
  border-inline-start: 1px solid var(--rule-strong);
}
.composer-shut {
  padding: 12px var(--pad); border-block-start: 1px solid var(--rule);
  background: var(--counterfoil); display: flex; align-items: center; gap: 10px;
  flex-wrap: wrap; font-size: 12px; color: var(--ink-2);
}

/* ── a route ───────────────────────────────────────────────────────────────
 *
 * `KWI ✈ BOM`, with the flight path drawn between the codes. It is the notation the
 * whole trade already reads — on the ticket, on the board, in the GDS — and it is the
 * one piece of this console that says *travel* without anybody having to be told.
 * Always left-to-right: a sector runs origin to destination in both languages. */
.route {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-board); font-weight: 700;
}
.route > b {
  font-size: 0.9rem; font-weight: 800; color: var(--ink); letter-spacing: 0.02em;
}
.route-path {
  position: relative; flex: none; width: 3.1rem; height: 0.85rem;
  display: grid; place-items: center;
}
/* The path itself: a dotted arc from one code to the other. */
.route-path i {
  position: absolute; inset-inline: 0; inset-block-start: 50%;
  border-block-start: 1.5px dotted var(--rule-strong);
}
.route-path svg {
  width: 0.82rem; height: 0.82rem; position: relative;
  color: var(--sky); transform: rotate(90deg);
  background: var(--stock); padding: 0 1px;
}
/* On the dark board the path is drawn on the board's own stock, not the card's. */
.board .route-path svg, .stub .route-path svg { background: transparent; }
.board .route > b { color: var(--board-ink); }
.board .route-path i { border-color: var(--board-rule); }

/* The flight number sits beside the sector, quieter than it — which is the true
   ranking: people look up a route first and the number only to confirm it. */
.flightno {
  font-family: var(--font-board); font-size: 0.72rem; font-weight: 700;
  color: var(--ink-3); margin-inline-start: 9px; letter-spacing: 0.04em;
}

/* ── the sign-in pass ──────────────────────────────────────────────────────── */
/* The logo on its own stock, the way it is printed everywhere else the company uses it:
   orange and blue on white. Tinting it to survive a dark head would be wearing somebody
   else's version of the identity. */
/* The sign-in card and the voucher print on white stock, where the colour lockup's white
   wordmark would vanish. They use the light-ground file at or below its native 111px,
   which is exactly where it is sharp. */
.signin-logo { width: 6.9rem; height: auto; display: block; }

.signin-said {
  font-size: 0.63rem; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 700; text-align: center;
}

/* A place rather than a sector: one end, a pin, no path. */
.route.place { gap: 5px; }
.route-pin { display: grid; place-items: center; flex: none; color: var(--sunset); }
.route-pin svg { width: 0.8rem; height: 0.8rem; }
.route.place > b { font-family: var(--font-en); letter-spacing: 0; }

/* ── the search that covers everything ──────────────────────────────────────
 *
 * In the topbar, so it is reachable from wherever somebody is standing rather than from
 * one screen they have to navigate to first. The results hang beneath it as an overlay:
 * a panel that pushed the page down would move the thing the person was reading every
 * time they typed a letter. */
/* It gives up space before anything else does.
 *
 * It was a fixed `min(22rem, 34vw)` with `flex: none`, and the top bar wraps — so at any
 * width where the row did not quite fit, something took a second line, and what took it
 * was whatever came last. That was the account control, which landed *under the page
 * title*: exactly the place it had just been moved out of. Chasing it with a breakpoint
 * is guessing at the viewport when what matters is the space left beside the rail, which
 * the rail's own width changes. A flexible search absorbs the shortfall at every width
 * and no breakpoint has to know anything. */
.findall {
  position: relative;
  flex: 1 1 12rem; min-inline-size: 7rem; max-inline-size: 22rem;
}
/* It shrinks, and then it takes its own line. It does **not** disappear.
 *
 * The first version hid it below 1100px on the reasoning that the topbar had no room —
 * which is true, and is an argument for giving it room, not for removing the one control
 * that reaches everything. Steven could not find it, and he was working in a window a
 * hand's breadth narrower than the one it was built in. A search nobody can find is a
 * search nobody has. */
@media (max-width: 1024px) {
  .topbar { flex-wrap: wrap; }
  .findall { flex-basis: 100%; max-inline-size: none; order: 9; }
  /* On its own line it should not also be squeezed by the chips beside it. */
  .topbar > .chip-bar { order: 8; }
}
.findall input {
  inline-size: 100%; padding: 7px 12px; font: inherit; font-size: 0.85rem;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--counterfoil); color: var(--ink);
}
.findall input:focus {
  outline: none; border-color: var(--sunset); background: var(--stock);
  box-shadow: 0 0 0 3px var(--sunset-wash);
}
.findall-hits {
  position: absolute; inset-block-start: calc(100% + 6px); inset-inline-start: 0;
  inline-size: min(26rem, 80vw); z-index: 40;
  background: var(--stock); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow-lift);
  max-block-size: 26rem; overflow-y: auto; padding: 4px;
}
.findall-group + .findall-group { border-block-start: 1px solid var(--rule); }
.findall-kind {
  display: flex; justify-content: space-between; gap: 0.6rem;
  padding: 7px 10px 4px; font-size: var(--t-caption); font-weight: 800;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3);
}
.findall-hit {
  display: flex; align-items: baseline; gap: 0.5rem; inline-size: 100%;
  text-align: start; padding: 6px 10px; border: 0; background: none; font: inherit;
  color: var(--ink); cursor: pointer; border-radius: var(--radius-sm);
}
.findall-hit:hover, .findall-hit:focus-visible { background: var(--counterfoil); }
.findall-hit b { font-weight: 700; }
.findall-note { padding: 10px; font-size: 0.82rem; color: var(--ink-2); }

@media print { .findall { display: none; } }

/* ── the nights, chosen in two taps ─────────────────────────────────────────
 *
 * A sheet rather than a dropdown: the calendar is the whole question while it is open, and
 * a stay is chosen once per voucher, so it earns the room. It comes up from the bottom on
 * a phone and sits in the middle on a desk, which is where a person's eye already is.
 *
 * The selected range is drawn as one continuous band — square in the middle, rounded at
 * the two ends — so four nights read as a stay rather than as four days that happen to be
 * marked. That is the whole reason for the component. */
.calov {
  position: fixed; inset: 0; z-index: 9000; display: none;
  background: rgba(9, 18, 32, 0.62);
  align-items: flex-end; justify-content: center;
}
.calov.show { display: flex; }
@media (min-width: 560px) { .calov { align-items: center; } }

.calpanel {
  background: var(--stock); color: var(--ink);
  inline-size: 100%; max-inline-size: 27rem; max-block-size: 88vh;
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--rule); border-radius: var(--radius) var(--radius) 0 0;
  box-shadow: 0 -10px 44px rgba(0, 0, 0, 0.3);
}
@media (min-width: 560px) { .calpanel { border-radius: var(--radius); } }

.calhd {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-block-end: 1px solid var(--rule);
  font-size: 0.95rem;
}
.calx {
  border: 0; background: none; color: var(--ink-3); font: inherit; font-size: 1rem;
  cursor: pointer; padding: 2px 6px; border-radius: var(--radius-sm);
}
.calx:hover { color: var(--ink); background: var(--counterfoil); }

.calbody { flex: 1; min-block-size: 0; overflow-y: auto; padding: 2px 14px 14px; }
.calmo { margin-block-start: 10px; }
.calmh {
  text-align: center; font-weight: 800; font-size: 0.85rem; color: var(--sunset-deep);
  margin: 4px 0 6px;
}
.calgrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px 0; }
.caldow {
  text-align: center; font-size: var(--t-caption); font-weight: 700; color: var(--ink-3);
  block-size: 26px; line-height: 26px;
}
.cald {
  block-size: 40px; line-height: 40px; text-align: center; font-size: 0.9rem;
  font-family: var(--font-board); font-variant-numeric: tabular-nums;
  cursor: pointer; user-select: none; border-radius: var(--radius-sm);
}
.cald.calblank { cursor: default; pointer-events: none; }
.cald:not(.calblank):not(.calfrom):not(.calto):hover { background: var(--counterfoil); }

/* The band. Square through the middle, rounded at the ends, so the nights between read as
   one stay. `border-radius` is written logically so the band still opens the right way
   round on an Arabic screen. */
.cald.calin { background: var(--sunset-wash); border-radius: 0; }
.cald.calfrom, .cald.calto {
  background: var(--sunset); color: #fff; font-weight: 800;
}
.cald.calfrom { border-start-start-radius: var(--radius-sm);
                border-end-start-radius: var(--radius-sm);
                border-start-end-radius: 0; border-end-end-radius: 0; }
.cald.calto { border-start-end-radius: var(--radius-sm);
              border-end-end-radius: var(--radius-sm);
              border-start-start-radius: 0; border-end-start-radius: 0; }
.cald.calfrom.calto { border-radius: var(--radius-sm); }
/* Today is marked, never selected for you. An agent recording a stay that already
   happened should not have to undo a guess the screen made. */
.cald.caltoday:not(.calfrom):not(.calto) {
  box-shadow: inset 0 0 0 2px var(--sky); color: var(--sky-deep); font-weight: 800;
}

/* A confirm that cannot be pressed has to look like one. It was full-strength orange
   while disabled, so the screen said *press me* and then ignored the press — which reads
   as a broken button rather than as a step not yet finished. */
.calbar .btn[disabled] {
  background: var(--groove); color: var(--ink-4); cursor: not-allowed; box-shadow: none;
}
.calbar .btn[disabled]:hover { background: var(--groove); }

.calbar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 16px; border-block-start: 1px solid var(--rule);
  background: var(--counterfoil);
}
.calsel { font-size: 0.85rem; }
.calsel b { color: var(--sunset-deep); font-family: var(--font-board); }

/* How long the stay is, beside the dates that decide it. Quiet, because it is derived —
   nobody types it — but present, because it is what the room rate is multiplied by. */
.nights {
  margin-block-start: 0.4rem; font-size: var(--t-caption); font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--sky-deep);
}

/* A field that opens something rather than taking typing. It must not look like a box
   waiting for an answer, and it must not look disabled either. */
.field input.opens { cursor: pointer; }
.field input.opens:hover { border-color: var(--sunset); }

/* ── money on a form ────────────────────────────────────────────────────────
 *
 * Four of the fields on a room card are money and the rest are words, and they all looked
 * identical — the fare sat in the same grey box as the cancellation policy. The two
 * figures underneath are what the company pays and what the customer pays, which are the
 * most important numbers on the screen, and they were eleven-pixel grey text.
 *
 * The colour here carries the meaning rather than decorating it: one tinted well says
 * *this part is money*, and the derived figures are read out of it rather than typed into
 * it — which is also the rule the whole system runs on, that a total is never typed. */
.money-well {
  margin-block-start: 0.7rem;
  padding: var(--pad-open); border-radius: var(--radius);
  background: var(--dusk-wash); border: 1px solid var(--rule);
}
.money-well-head {
  font-size: var(--t-caption); font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-3); margin-block-end: 0.6rem;
  display: flex; align-items: center; gap: 7px;
}
.money-well-head::before {
  content: ''; width: 3px; height: 0.78rem; border-radius: 2px; flex: none;
  background: var(--sky);
}
.money-derived {
  display: flex; flex-wrap: wrap; gap: 0 2.4rem;
  margin-block-start: 0.8rem; padding-block-start: 0.7rem;
  border-block-start: 1px dashed var(--rule-strong);
}
/* The figure the guest is charged is the one being decided, so it is the one in the
   company's colour. The cost and the margin beside it stay quiet — they are the working,
   not the answer. */
.money-derived .metric:nth-child(2) .value { color: var(--sunset-deep); }

/* ── what the whole voucher comes to ────────────────────────────────────────
 * It was a line of small print in a corner, which is a strange place for the figure the
 * customer is going to be charged. */
.stay-total { margin-block-end: var(--gap); }
.stay-total .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.stay-total .cell:last-child .value { color: var(--sunset-deep); }
@media (max-width: 720px) {
  .stay-total .strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── looking a property up ──────────────────────────────────────────────────
 *
 * A search box above the fields it fills, and the shortlist under it. The results are a
 * list of buttons rather than a `<select>`: each one carries three lines — the name, the
 * city and star rating, and the telephone number — and a native option element can hold
 * one. */
.hotel-find { display: flex; gap: 0.7rem; align-items: end; }
/* A panel, not a stretch of the form.
 *
 * It was `--stock` on a card that is also `--stock`, with a hairline border — so on a
 * dark screen the results ran edge to edge into the fields below them and read as part of
 * the form rather than as an answer hovering over it. */
.hotel-hits {
  display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--rule-strong); border-radius: var(--radius);
  /* `--groove`, not `--counterfoil`. The palette's own note says why: counterfoil is a
     hair away from stock in the dark palette, so a panel drawn on it disappears into the
     card behind it — which is exactly what happened here, and the results read as a
     stretch of the form. Groove is a distinct surface in *both* palettes. */
  background: var(--groove); box-shadow: var(--shadow-lift);
  margin-block-end: 0.7rem; overflow: hidden;
  max-block-size: 17rem; overflow-y: auto;
}
.hotel-hit {
  display: grid; grid-template-columns: 1fr auto; gap: 0 0.8rem; text-align: start;
  padding: 8px 12px; border: 0; background: none; font: inherit; cursor: pointer;
  color: var(--ink); border-block-end: 1px solid var(--rule);
}
.hotel-hit:last-child { border-block-end: 0; }
.hotel-hit:hover, .hotel-hit:focus-visible { background: var(--stock); }
/* The star rating in the colour a star is, so a four-star property is legible at a
   glance rather than being three more grey characters in a grey line. */
.hotel-hit .tiny:first-of-type { color: var(--ink-3); }
.hotel-hit .stars { color: var(--edge); letter-spacing: 0.05em; }
.hotel-hit b { grid-column: 1; font-weight: 700; }
.hotel-hit .tiny:first-of-type { grid-column: 1; }
.hotel-hit .tiny:last-of-type { grid-column: 2; grid-row: 1 / span 2; align-self: center;
  font-family: var(--font-board); }
@media (max-width: 720px) {
  .hotel-find { flex-direction: column; align-items: stretch; }
  .hotel-hit { grid-template-columns: 1fr; }
  .hotel-hit .tiny:last-of-type { grid-column: 1; grid-row: auto; }
}

/* ── a guest on the stay form ───────────────────────────────────────────────
 *
 * Five fields and the control that takes the row away. The control sits in a slot that is
 * always rendered, empty on the last remaining guest — a room with nobody in it is not a
 * room — so the fields do not change width when a second guest appears beside the first.
 *
 * `min-inline-size: 0` on the grid because a flex child will not shrink below its content
 * by default, and five fields refusing to shrink push the button off the card. */
.pax-row {
  display: flex; align-items: end; gap: 8px; margin-block-end: 0.4rem;
}
.pax-drop {
  inline-size: 2rem; flex: none; display: flex; justify-content: center;
  padding-block-end: 3px;
}
.pax-drop .action {
  inline-size: 2rem; block-size: 2rem; padding: 0; justify-content: center;
  font-size: 1rem; line-height: 1;
}
@media (max-width: 1180px) {
  /* The grid collapses to two columns here, so the row is tall and the control belongs at
     its top rather than floating beside the last field. */
  .pax-row { align-items: start; }
  .pax-drop { padding-block-start: 1.35rem; padding-block-end: 0; }
}

/* ── the queue strip ────────────────────────────────────────────────────────
 *
 * The old console's five hotel queues, side by side. It was a vertical list in a sidebar
 * there; here the sidebar is the whole company, so the queues belong to the screen they
 * filter. Same five, same order, same words. */
/* A disclosure that opens a form. It was a bare `<details>` — the browser's own triangle
   and a line of body text sitting on the page above the board, which is not what the one
   control that creates work should look like. Styled as the secondary action it is: a
   control when closed, a heading when open. */
.raise-ticket > summary {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  list-style: none; width: fit-content;
  padding: 8px 15px 8px 12px; border-radius: 999px;
  border: 1px solid var(--rule-strong); background: var(--stock);
  box-shadow: 0 1px 2px rgba(13, 21, 38, 0.05);
  font-size: 0.85rem; font-weight: 800; color: var(--ink-2);
  transition: all var(--speed);
}
.raise-ticket > summary::-webkit-details-marker { display: none; }
.raise-ticket > summary::before {
  content: '+'; display: grid; place-items: center; flex: none;
  width: 1.15rem; height: 1.15rem; border-radius: 50%;
  background: var(--sunset); color: #fff; font-size: 0.9rem; font-weight: 700;
  line-height: 1; transition: transform var(--speed);
}
.raise-ticket > summary:hover { border-color: var(--sunset); color: var(--ink); }
.raise-ticket[open] > summary {
  background: var(--sunset-wash); border-color: var(--sunset); color: var(--sunset-deep);
}
.raise-ticket[open] > summary::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  .raise-ticket > summary::before { transition: none; }
}

.queue-tabs {
  display: flex; gap: 6px; flex-wrap: wrap; margin-block-end: calc(var(--gap) * -0.4);
}
.queue-tab {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 7px 14px; border-radius: 999px; font: inherit; font-size: 0.82rem;
  font-weight: 700; color: var(--ink-2);
  background: var(--stock); border: 1px solid var(--rule-strong);
  box-shadow: 0 1px 2px rgba(13, 21, 38, 0.04);
  transition: all var(--speed);
}
.queue-tab:hover { border-color: var(--ink-4); color: var(--ink); }
.queue-tab:hover { border-color: var(--rule-strong); color: var(--ink); }
.queue-tab b {
  font-size: 0.74rem; min-width: 1.15rem; text-align: center;
  padding: 1px 5px; border-radius: 999px;
  background: var(--counterfoil); color: var(--ink-2);
}
.queue-tab.on {
  background: var(--sky-deep); border-color: var(--sky-deep); color: #fff;
}
.queue-tab.on b { background: rgba(255, 255, 255, 0.22); color: #fff; }

/* ── the voucher, as a document ─────────────────────────────────────────────
 *
 * A guest holds this at a hotel desk in a country they do not live in, so it is laid out
 * as a printed document rather than as a screen: white stock, a rule under every heading,
 * no colour doing work that a photocopier would lose. The company's two colours appear
 * once each — the mark, and the heading bars.
 */
.voucher-actions {
  display: flex; align-items: center; gap: 0.6rem; margin-block-end: var(--gap);
}
.voucher {
  /* Its own palette, on purpose.
   *
   * The document followed the console's theme, so printing it from night mode gave a
   * black bar behind every room heading and near-black rules on white paper — the same
   * page, printed twice, came out looking like two different companies. A voucher is
   * printed stock and has no dark mode, so every colour on it is stated here and nothing
   * below reads a theme token. */
  --doc-ink: #17202c;
  --doc-quiet: #5b6675;
  --doc-rule: #d9dde4;
  --doc-tint: #f2f4f7;
  --doc-blue: #1d5a96;
  --doc-orange: #f15d24;
  --doc-warm: #fffaf7;

  background: #fff; color: var(--doc-ink); max-width: 48rem; margin-inline: auto;
  padding: 2rem 2.2rem 1.6rem; border: 1px solid var(--doc-rule);
  border-radius: 12px; box-shadow: 0 10px 28px -18px rgba(20, 27, 51, 0.25);
  font-size: 13px; line-height: 1.55;
}
.v-head {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 1rem;
  padding-block-end: 1rem; border-block-end: 2px solid var(--doc-orange);
}
.v-mark { width: 5.4rem; height: auto; display: block; }
.v-head h1 {
  margin: 0; font-size: 1.5rem; font-weight: 800; letter-spacing: -0.02em;
  color: var(--doc-blue); text-align: center;
}
.v-support { font-size: 10.5px; line-height: 1.5; text-align: end; color: #3a4657; }
.v-support b { display: block; color: var(--doc-ink); }

.v-refs {
  display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; justify-content: center;
  padding-block: 0.9rem; font-size: 14px;
}
.v-refs b { color: var(--doc-blue); }

.v-bar {
  margin: 1.1rem 0 0; padding: 6px 12px; font-size: 11px; font-weight: 800;
  letter-spacing: 0.09em; text-transform: uppercase;
  background: var(--doc-blue); color: #fff; border-radius: 4px 4px 0 0;
}
.v-rows { border: 1px solid var(--doc-rule); border-block-start: 0; }
.v-rows > div {
  display: grid; grid-template-columns: 11rem 1fr; gap: 0.8rem;
  padding: 7px 12px; border-block-end: 1px solid var(--doc-rule);
}
.v-rows > div:last-child { border-block-end: 0; }
.v-rows span { color: var(--doc-quiet); }

/* Check in · Check out · Rooms · Nights, and later Payment date · Amount. */
.v-band {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  border: 1px solid var(--doc-rule); margin-block-start: 0.9rem;
}
.vb-cell { padding: 8px 12px; border-inline-end: 1px solid var(--doc-rule); }
.vb-cell:last-child { border-inline-end: 0; }
.vb-label {
  font-size: 10px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--doc-quiet);
  font-weight: 700;
}
.vb-value { font-weight: 700; font-size: 14px; margin-block-start: 2px; }

.v-room { border: 1px solid var(--doc-rule); border-block-start: 0; }
.v-room-head {
  padding: 7px 12px; background: var(--doc-tint); font-weight: 700;
  border-block-end: 1px solid var(--doc-rule);
}
.v-pax { width: 100%; border-collapse: collapse; }
.v-pax th, .v-pax td {
  padding: 6px 12px; text-align: start; border-block-end: 1px solid var(--doc-rule);
}
/* Stated, and stated over the console's own `thead th`, which is sticky, dark and pinned
   to a theme. Inherited, it printed the passenger header as a black bar across the page —
   and only for whoever happened to be in night mode when they pressed print. */
.voucher .v-pax th {
  font-size: 10px; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--doc-quiet); background: var(--doc-tint); position: static;
  border-block-end: 1px solid var(--doc-rule); font-weight: 700;
}
.voucher tbody td { padding: 6px 12px; border-block-end: 1px solid var(--doc-rule); }
.v-pax tr:last-child td { border-block-end: 0; }
.v-policy { padding: 8px 12px; font-size: 12px; border: 1px solid var(--doc-rule);
  border-block-start: 0; background: var(--doc-warm); }

.v-rules { margin: 0; border: 1px solid var(--doc-rule); border-block-start: 0;
  padding: 10px 12px 10px 2rem; font-size: 12px; }
.v-rules li { margin-block-end: 4px; }
.v-foot {
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  margin-block-start: 1.2rem; padding-block-start: 0.8rem;
  border-block-start: 1px solid var(--doc-rule); font-size: 11px; color: var(--doc-quiet);
}

/* ── printing ───────────────────────────────────────────────────────────────
 * What leaves the printer is the document and nothing else: no rail, no header, no
 * buttons, no shadow. The colours are kept and told to print — a voucher in grey is a
 * different document — and a room is never split across a page turn. */
@media print {
  @page { size: A4; margin: 12mm; }
  html, body { background: #fff !important; }
  .rail, .topbar, .no-print, .toast, .sheet, .sheet-backdrop { display: none !important; }
  .shell { display: block !important; }
  .page { padding: 0 !important; }
  .voucher {
    max-width: none; margin: 0; border: 0; box-shadow: none; padding: 0;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .v-room, .v-band, .v-rows, .v-policy { break-inside: avoid; }
  /* The rules are editable now, so the list has no length anybody controls. Holding the
     whole block together was right when it was five fixed sentences; with a property that
     adds its own it either leaves half a blank page behind it or, once the list is taller
     than a page, the browser gives up on the instruction and splits it anywhere. Each
     rule stays whole; the list is allowed to run on. */
  .v-rules { break-inside: auto; }
  .v-rules li { break-inside: avoid; }
  .v-bar { break-after: avoid; }
}

/* The transaction history: dense, scannable, one row per change. */
table.history td { vertical-align: top; padding-block: 7px; }
table.history .num { color: var(--ink-3); }

/* The desk's search. One box, because a person ringing up says whatever they have. */
.deskfind {
  display: flex; gap: 0.5rem; align-items: center; margin-block-end: var(--gap);
}
.deskfind input {
  flex: 1; min-width: 0; padding: 9px 13px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--stock); color: var(--ink); font: inherit; font-size: 0.86rem;
  transition: border-color var(--speed), box-shadow var(--speed);
}
.deskfind input:focus {
  outline: none; border-color: var(--sky);
  box-shadow: 0 0 0 3px var(--sky-wash);
}
.deskfind input::placeholder { color: var(--ink-4); }

/* A field the document needs and nobody filled in. Said, not silently omitted — a row that
   vanishes reads as "this hotel has no address" rather than "nobody typed one". */
.v-missing { font-style: italic; font-weight: 500; color: var(--doc-quiet); }
/* The room's three readings sit inside the room's own box. */
.v-band.inner { margin: 0; border-inline: 0; border-block-start: 0; }

/* A share of the answers, drawn against the largest one.
 *
 * Against the largest rather than against the total: with five answers and a handful of
 * replies every bar would be a sliver and the shape of the answer — which is the whole
 * point of drawing it — would be invisible. The number beside it is the real share, so
 * nothing here is the only place a figure appears.
 */
.meter {
  display: block;
  height: 8px;
  border-radius: 4px;
  background: var(--groove);
  overflow: hidden;
}

.meter > i {
  display: block;
  height: 100%;
  border-radius: 4px;
  /* The brand blue, not the orange. Orange in this console means *this needs a person*,
     and a bar chart of survey answers needs nobody — spending the alarm colour on it is
     how the alarm stops meaning anything. */
  background: var(--dusk);
  /* A share of nothing still has to be visible as a share of nothing, so a zero-width
     bar stays a zero-width bar rather than being nudged up to a minimum that would read
     as "one or two". */
  min-width: 0;
}

/* ── the two kinds of line that are not a message ──────────────────────────
 *
 * `bitrix24-replacement-gaps.md` §12.1. The old transcript carries eleven system lines in
 * order — the conversation starting, a contact created, the bot giving up, an enquiry
 * transferred by the queuing rules, somebody joining — and ONE had them in a side list
 * nobody opens. They belong in the flow, at the moment they happened.
 */

.sys-line {
  /* Centred and quiet. It is the room speaking, not a person in it, so it sits between
     the two sides rather than on either. */
  align-self: center;
  max-width: 80%;
  margin: 0.45rem auto;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  background: var(--groove);
  color: var(--ink-3);
  font-size: 0.8rem;
  text-align: center;
  line-height: 1.5;
}

.sys-line .tiny { margin-inline-start: 0.4rem; }

/* An internal note. This must look **unmistakably** unlike a message the customer
 * received: an agent glancing at a thread and reading a note as something the customer
 * has been told is how a customer gets told nothing at all while everybody believes they
 * were told. So: the warning colour, a dashed edge, and the words say it too — colour is
 * never the only carrier.
 */
.bubble.note-bubble {
  align-self: center;
  max-width: 88%;
  background: var(--edge-wash);
  border: 1px dashed var(--edge);
  color: var(--ink);
}

.bubble.note-bubble .bubble-who {
  color: var(--edge);
  font-weight: 800;
}

.bubble.note-bubble .bubble-body { white-space: pre-wrap; }

/* The last note, on a board row. Quiet enough not to compete with the title it sits
   under, and clipped rather than wrapped: a board is scanned down the left edge, and one
   long note pushing every row below it out of rhythm costs more than the words are
   worth. */
.note-on-row {
  color: var(--ink-3);
  font-style: italic;
  margin-block-start: 0.2rem;
  max-width: 34rem;
}

/* ── the board, as a board ─────────────────────────────────────────────────
 *
 * `bitrix24-replacement-gaps.md` §15.3. The old Daily Job screen is a kanban and it is
 * the shape the desk knows; the replacement rendered a table.
 *
 * Horizontal scroll is the whole point of the layout, so it is on the strip itself and
 * never on the page — a page that scrolls sideways takes the navigation with it.
 */
.kanban {
  display: flex;
  gap: 0.7rem;
  overflow-x: auto;
  padding-block-end: 0.6rem;
  /* Enough to see a card and a half of the next column, so it is obvious there is more
     to the right without a scrollbar having to say so. */
  scroll-snap-type: x proximity;
}

.kan-col {
  flex: 0 0 17rem;
  scroll-snap-align: start;
  background: var(--counterfoil);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  max-height: 42rem;
}

.kan-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.7rem;
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  /* The old board colours every column differently, which at 34 columns is a rainbow
     that says nothing. Here the colour is the stage's **group** — schedule, money,
     hotel, escalation — so two columns of the same colour really are the same kind of
     work, and a person learns the palette in a day rather than never. */
  background: var(--dusk);
  color: #fff;
  font-weight: 700;
  font-size: 0.82rem;
}

.kan-head.kan-schedule   { background: var(--approach); }
.kan-head.kan-money      { background: var(--taxiway); }
/* The accounting board's own three, and the two terminal columns.
 *
 * `.kan-head.kan-<group>` is how a column gets its colour, and four groups had no rule —
 * `refund`, `void` and `credit`, which is **every column on the money board**, and
 * `closed`, which is both terminal columns. They fell back to an unstyled header, so the
 * board a supervisor reads all day was the one board with no colour on it at all. */
.kan-head.kan-refund     { background: var(--edge); }
.kan-head.kan-void       { background: var(--caution); }
.kan-head.kan-credit     { background: var(--approach); }
/* Finished. Deliberately the quietest of them: a closed column is only on the board when
   somebody has asked for it, and it should not compete with the work. */
.kan-head.kan-closed     { background: var(--ink-3); }
.kan-head.kan-documents  { background: var(--edge); }
.kan-head.kan-hotel      { background: var(--dusk); }
.kan-head.kan-services   { background: var(--dusk); }
.kan-head.kan-escalation { background: var(--stopbar); }
.kan-head.kan-meta       { background: var(--caution); }
.kan-head.kan-sales      { background: var(--taxiway); }
.kan-head.kan-intake     { background: var(--approach); }

.kan-name { flex: 1; line-height: 1.35; }

.kan-count {
  background: rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  padding: 0.05rem 0.45rem;
  font-variant-numeric: tabular-nums;
}

.kan-money {
  padding: 0.35rem 0.7rem;
  border-block-end: 1px solid var(--rule);
  font-family: var(--font-board);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--stock);
}

.kan-cards {
  padding: 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  overflow-y: auto;
}

.kan-card {
  background: var(--stock);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 0.55rem 0.6rem;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 0.18rem;
  /* Colour only on hover. A card that lifts moves the card below it, and a column being
     read is a column where nothing should move under the eye. */
  transition: border-color .12s, background-color .12s;
}

.kan-card:hover { border-color: var(--sunset); }
.kan-card:focus-visible { outline: 2px solid var(--sunset); outline-offset: 2px; }

.kan-ref {
  font-family: var(--font-board);
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--ink-3);
}

.kan-title { font-size: 0.86rem; line-height: 1.4; font-weight: 600; }

.kan-foot {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-block-start: 0.25rem;
}

/* ── several conversations at once ──────────────────────────────────────────
   `bitrix24-replacement-gaps.md` §19.2. The tick column and the bar it feeds. */
.conv-pick {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: 0.15rem;
  cursor: pointer;
}
.conv-pick input { inline-size: 1rem; block-size: 1rem; cursor: pointer; }

.conv-bulk {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.75rem;
  background: var(--groove);
  border-block-end: 1px solid var(--rule);
  /* It sits above a list somebody scrolls, and the point of it is that it is reachable
     while the ticks that filled it are still on screen. */
  position: sticky;
  inset-block-start: 0;
  z-index: 3;
}
.conv-bulk-count {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* ── when the telephone rang ────────────────────────────────────────────────
   3CX's *Call Distribution*, which is a table there and a shape here: twenty-four
   buckets read at a glance rather than counted. */
.hourbars {
  display: grid;
  grid-template-columns: repeat(24, 1fr);
  align-items: end;
  gap: 2px;
  block-size: 4.5rem;
}
.hourbar { display: flex; flex-direction: column; justify-content: flex-end; block-size: 100%; }
.hourbar-fill {
  background: var(--approach);
  border-radius: 2px 2px 0 0;
  min-block-size: 2px;
  transition: background var(--speed);
}
/* The busiest hour, marked. A histogram whose tallest bar is the same colour as the rest
   makes somebody measure it with their eye. */
.hourbar-fill.peak { background: var(--stopbar); }
.hourbar-tick {
  font-size: 0.6rem;
  color: var(--ink-4);
  text-align: center;
  font-variant-numeric: tabular-nums;
  padding-block-start: 2px;
}

/* A switch leg, under the customer call it belongs to. Set back and quiet: it is the
   evidence, not the answer — the row above says what happened to a person. */
.call-leg > td { padding-block: 0.25rem; border-block-start: 0; }
.call-leg-mark { color: var(--ink-4); padding-inline-end: 0.35rem; }
.call-leg { background: var(--groove); }

/* ── the calls filter bar ────────────────────────────────────────────────────
   It was twenty pill chips in three rows — a block of identical controls taller than the
   data under it, and louder. 3CX puts two controls and a search box above a full-width
   table, and it is right: the table is the screen and the filters are how you aim it. */
.callbar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-block-end: 0.5rem;
}
.callbar.quiet { align-items: center; gap: 0.35rem; margin-block-end: 0.7rem; }
/* ── the two-tap date range ──────────────────────────────────────────────────
   One control, opened from the filter row, closing on Escape or a click away. */
.daterange { position: relative; }
.cal-pop {
  position: absolute;
  inset-block-start: calc(100% + 0.35rem);
  inset-inline-start: 0;
  z-index: 40;
  min-inline-size: 17rem;
  padding: 0.7rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--stock);
  box-shadow: 0 0 0 1px var(--rule), 0 10px 24px -12px rgba(0, 0, 0, 0.45);
}
.cal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-block-end: 0.5rem;
}
.cal-title { font-weight: 650; font-size: 0.85rem; }
.cal-step {
  font: inherit; font-size: 1rem; line-height: 1;
  padding: 0.15rem 0.5rem; border-radius: var(--radius-sm);
  border: 1px solid var(--rule); background: transparent; color: var(--ink-2);
  cursor: pointer;
}
/* The chevrons are drawn as written, so they must be flipped under RTL — the previous
   month is to the *right* on an Arabic screen and an unflipped ‹ points at next. */
html[dir="rtl"] .cal-step { transform: scaleX(-1); }
.cal-step:hover { color: var(--ink); border-color: var(--rule-strong); }
/* `minmax(0, 1fr)`, not `1fr`. A bare `1fr` is `minmax(auto, 1fr)`, and the cell's own
   `aspect-ratio: 1` then feeds its height back in as an automatic minimum width — the
   seven columns resolved to 70px each inside a 280px grid and the calendar spilled out of
   the popover, over the page, with the rows behind showing through it. The same trap as
   `.grid > *` needing `min-inline-size: 0`. */
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
.cal-dow {
  font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--ink-3); text-align: center; padding-block-end: 0.25rem;
}
.cal-cell {
  font: inherit; font-size: 0.78rem;
  aspect-ratio: 1; display: grid; place-items: center;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink); cursor: pointer;
}
.cal-cell.empty { cursor: default; }
.cal-cell:hover:not(.empty) { background: var(--groove); }
/* The two days somebody chose, and everything between them. The ends are solid so the
   range reads as a range rather than as two unrelated selections. */
/* Neither `--on-approach` nor `--card` exists in this stylesheet, and a var() naming
   nothing is silently dropped — so the two chosen days would have carried the body colour
   on a solid blue ground, which in dark mode is near-white on light blue. `--stock` is
   the card surface and is the readable answer on both themes. */
.cal-cell.edge { background: var(--approach); color: var(--stock); font-weight: 650; }
.cal-cell.inside { background: var(--approach-wash); color: var(--approach); }
.cal-cell.today { border-color: var(--rule-strong); }
.cal-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  margin-block-start: 0.6rem;
}
.cal-foot .btn[disabled] { opacity: 0.45; cursor: not-allowed; }

/* The recording controls under a call, and the player they turn into. Inline rather than
   a dialogue: listening to a call is the thing a supervisor does twenty times in a row,
   and a modal each time is twenty dismissals.

   The treatment is the QC cockpit's, because that is where the desk already reads calls:
   a labelled block with the player at full width inside it, and the transcript offered
   beside it rather than hidden. */
.tape-row {
  display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap;
  margin-block-start: 0.4rem;
}
.tape-box {
  margin-block-start: 0.5rem;
  padding: 0.6rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--counterfoil);
}
.tape-head {
  display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap;
  margin-block-end: 0.5rem;
}
.tape-label {
  font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-3);
}
/* The real element, kept as the source of truth and hidden once ours is drawn. Not
   `display:none` — a hidden media element is allowed to keep playing, and this is the
   thing that is actually playing. */
.tape { inline-size: 100%; block-size: 2.25rem; display: block; }
.tape.player-source {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  overflow: hidden; clip-path: inset(50%);
  pointer-events: none;
}

/* ── the transport ──────────────────────────────────────────────────────────
   A browser's own audio controls cannot be styled at all — Chrome draws a white pill in
   its own font, which on a dark card is the only thing on the screen that is not ours.
   Same problem the native `<select>` had, same answer. */
.player {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--stock);
}
.player-play {
  flex: 0 0 auto;
  inline-size: 2rem; block-size: 2rem;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: var(--approach); color: var(--stock);
  cursor: pointer;
  transition: transform var(--speed), background var(--speed);
}
.player-play:hover { transform: scale(1.06); }
.player.playing .player-play { background: var(--ink); }
/* The glyphs are drawn shapes, not text, so they must not be flipped under RTL — a play
   triangle pointing backwards is the one control nobody misreads twice. */
.player-glyph { display: grid; place-items: center; line-height: 0; }
/* The glyphs are drawn shapes, so they must not be flipped under RTL — a play triangle
   pointing backwards is the one control nobody misreads twice, and the back-ten arrow
   means "back" by its own curve rather than by which way the page runs. */
html[dir="rtl"] .player-glyph svg,
html[dir="rtl"] .player-step svg,
html[dir="rtl"] .player-mute svg { transform: none; }
.player-step, .player-rate, .player-mute {
  flex: 0 0 auto;
  display: inline-grid; place-items: center;
  font: inherit; font-size: 0.72rem; line-height: 1;
  padding: 0.25rem 0.4rem;
  min-inline-size: 2rem; min-block-size: 1.8rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3);
  cursor: pointer;
  transition: color var(--speed), border-color var(--speed), background var(--speed);
}
.player-step:hover, .player-rate:hover, .player-mute:hover {
  color: var(--ink); border-color: var(--rule);
}
.player-rate.on, .player-mute.on {
  color: var(--approach); border-color: var(--approach); background: var(--approach-wash);
}
.player-time {
  flex: 0 0 auto;
  font-family: var(--font-board);
  font-size: 0.72rem;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* The scrubber. A real `<input type=range>`, so it drags, takes arrow keys and is
   announced — a div with a click handler does none of those, and this console has a guard
   about controls a keyboard cannot reach. */
.player-track {
  /* Declared here rather than only relied on through `var(--played, 0%)`: the script sets
     it as the recording plays, and a custom property that exists nowhere in the sheet
     reads to anybody — and to the guard — as a name left behind by a rename. */
  --played: 0%;
  flex: 1;
  min-inline-size: 4rem;
  block-size: 1.15rem;
  appearance: none; -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
}
.player-track:focus-visible { outline: 2px solid var(--approach); outline-offset: 3px; }
/* `--played` is set by the script: a range input cannot be filled by CSS alone, and the
   two vendor tracks below have to be written out separately because a browser drops a
   whole selector list if it does not recognise one of its pseudo-elements. */
.player-track::-webkit-slider-runnable-track {
  block-size: 4px; border-radius: 999px;
  background: linear-gradient(to right,
    var(--approach) 0 var(--played, 0%), var(--groove) var(--played, 0%) 100%);
}
.player-track::-moz-range-track {
  block-size: 4px; border-radius: 999px;
  background: linear-gradient(to right,
    var(--approach) 0 var(--played, 0%), var(--groove) var(--played, 0%) 100%);
}
/* A gradient cannot express a logical direction, so under RTL it is stated — the same
   fix the day band needed, where the light sat behind the empty half on every Arabic
   screen. The browser mirrors the *thumb* by itself; the painted track it does not. */
html[dir="rtl"] .player-track::-webkit-slider-runnable-track {
  background: linear-gradient(to left,
    var(--approach) 0 var(--played, 0%), var(--groove) var(--played, 0%) 100%);
}
html[dir="rtl"] .player-track::-moz-range-track {
  background: linear-gradient(to left,
    var(--approach) 0 var(--played, 0%), var(--groove) var(--played, 0%) 100%);
}
.player-track::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  inline-size: 0.75rem; block-size: 0.75rem;
  margin-block-start: -0.32rem;
  border-radius: 50%;
  background: var(--approach);
  box-shadow: 0 0 0 3px var(--stock);
  transition: transform var(--speed);
}
.player-track::-moz-range-thumb {
  inline-size: 0.75rem; block-size: 0.75rem;
  border: 0; border-radius: 50%;
  background: var(--approach);
  box-shadow: 0 0 0 3px var(--stock);
}
.player-track:hover::-webkit-slider-thumb { transform: scale(1.15); }
/* The transcript, as a dialogue. The customer on one side and the agent on the other is
   how the QC cockpit draws it and how every messaging screen in this console already
   reads — a wall of prose with names in it is not a transcript anybody finishes. */
.tape-text:empty { display: none; }
.turns {
  display: flex; flex-direction: column; gap: 0.4rem;
  margin-block-start: 0.6rem;
  max-block-size: 22rem; overflow-y: auto;
}
.turn {
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-sm);
  background: var(--stock);
  /* The speaker's own edge, on the side they are drawn from — mirrored under RTL by the
     logical property rather than by a second rule. */
  border-inline-start: 3px solid var(--rule-strong);
  max-inline-size: 85%;
}
.turn.us { align-self: flex-start; border-inline-start-color: var(--approach); }
.turn.them {
  align-self: flex-end;
  border-inline-start: 0;
  border-inline-end: 3px solid var(--stopbar);
}
.turn-who {
  font-size: 0.6rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); margin-block-end: 0.15rem;
}
.turn.us .turn-who { color: var(--approach); }
.turn.them .turn-who { color: var(--stopbar); }
.turn-said { font-size: var(--t-body); line-height: 1.5; }

/* A name that opens the person's file. Underlined on hover rather than always, because
   a list where every name is underlined reads as a page of links rather than as data. */
.ref-link {
  color: var(--approach);
  cursor: pointer;
  text-decoration: none;
  border-radius: 2px;
}
.ref-link:hover { text-decoration: underline; }
.ref-link:focus-visible { outline: 2px solid var(--approach); outline-offset: 2px; }

/* ── the time-of-day window ──────────────────────────────────────────────────
   The quick bands and an exact window in one popover, the way the QC cockpit does it. */
.timepick { position: relative; }
.win-pop {
  position: absolute;
  inset-block-start: calc(100% + 0.35rem);
  inset-inline-start: 0;
  z-index: 40;
  inline-size: 19rem;
  padding: 0.7rem;
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--stock);
  box-shadow: 0 0 0 1px var(--rule), 0 10px 24px -12px rgba(0, 0, 0, 0.45);
}
.win-label {
  font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-3);
  margin-block: 0.15rem 0.4rem;
}
.win-label + .win-label, .win-row + .win-label { margin-block-start: 0.7rem; }
.win-quick { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.win-row {
  display: flex; align-items: center; gap: 0.3rem;
  margin-block-end: 0.35rem;
}
.win-side {
  inline-size: 2.6rem;
  font-size: 0.72rem; color: var(--ink-3);
}
/* The four boxes. A number field, not a list of 24×60 — somebody who wants half past nine
   types 9 30 faster than they scroll to it. */
.win-num {
  inline-size: 2.6rem;
  padding: 0.3rem 0.2rem;
  text-align: center;
  font-family: var(--font-board); font-size: 0.85rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--rule);
  background: var(--stock);
  color: var(--ink);
}
.win-num:focus-visible { outline: 2px solid var(--approach); outline-offset: 1px; }
.win-colon { color: var(--ink-3); }
.win-half {
  font: inherit; font-size: 0.68rem; font-weight: 600;
  padding: 0.28rem 0.45rem;
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3);
  cursor: pointer;
}
.win-half.on {
  background: var(--approach); border-color: var(--approach); color: var(--stock);
}
.win-note { margin-block: 0.55rem 0.6rem; line-height: 1.45; }
/* The calendar, when the exact half of a filter is one. It brings its own grid; this only
   gives it the room, because the popover is narrower than the standalone one was. */
.win-cal { margin-block-start: 0.2rem; }
.win-cal .cal-cell { font-size: 0.72rem; }
.win-foot { display: flex; justify-content: flex-end; gap: 0.4rem; }

/* The period the whole screen is read against. The top bar already writes the view's
   name, so this row leads with the one thing it cannot say — and it has to read as a
   heading, not as another caption, or nobody registers which days they are looking at. */
.lede {
  font-size: 0.95rem;
  font-weight: 650;
  color: var(--ink);
}
/* A filter is a label and a control, not a headline. */
.tiny-field { min-inline-size: 8.5rem; }
.tiny-field > span {
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* The on/off narrowings. Deliberately quieter than the drop lists beside them: each is a
   single yes, and a row of them shouting is what made the first version unreadable. */
.chiplet {
  font: inherit;
  font-size: 0.72rem;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  transition: border-color var(--speed), color var(--speed), background var(--speed);
}
.chiplet:hover { color: var(--ink); border-color: var(--rule-strong); }
.chiplet.on {
  background: var(--approach-wash);
  border-color: var(--approach);
  color: var(--approach);
  font-weight: 600;
}
