/* ============================================================================
 * BOX BOX — three-pane workspace · "green light" design system
 *
 * Layout: rail (navigation + filters) | list (dense, scannable) | reader.
 * Nothing is a modal except a genuine destructive confirm. A running job lives
 * in a dock — the "pit wall" — at the bottom of the reader so she can keep
 * working while it runs.
 *
 * Visual language: pit-lane grey, light by day and anthracite by night. GREEN
 * is the brand and every interactive thing — the green light, the thing you
 * press. ORANGE is the retired papaya, promoted from decoration to meaning: it
 * is now the colour of anything UNSETTLED — running, unsaved, changed, warned
 * about. A deeper JADE means SETTLED — done, ready, saved, added — and never
 * appears as a control, so it cannot be mistaken for the accent. Blue is
 * reserved for "this exists only in the other language". Amber survives only on
 * the favourite star. Uppercase micro-labels and every number sit in the mono
 * stack — that is the "Martian Mono" role.
 *
 * The status language, and the rule that keeps it alive: five things must be
 * tellable apart without reading the label —
 *   running   orange   ·  done   jade  (+ chequer)  ·  failed  red
 *   favourite amber ★  ·  other language  blue
 * The accent is NOT in that list. Green-the-brand fills buttons, active edges
 * and micro-labels; jade-the-status only ever appears in a bar or a status
 * word. If you are about to make a control jade or a status green, stop.
 *
 * ---------------------------------------------------------------------------
 * THEMES — light is the default, dark is a choice, and both are derived
 *
 * The whole interface is painted from the tokens below and nothing else. Two
 * blocks define them: `:root` is LIGHT, `:root[data-theme="dark"]` is DARK.
 * theme.js always writes one of those two onto <html>, resolving the third
 * setting ("system") itself, so this file never needs a prefers-color-scheme
 * branch and never has to state a palette twice.
 *
 * The light theme is derived, not inverted. A 10% white overlay on black is
 * not a 90% one on white; it is a 6% black one. Three rules did the work:
 *
 *   1. THE RAMP RUNS AWAY FROM THE GROUND. In dark, --bg is the floor and
 *      --surface-3 the highest step, each one lighter. In light the ground is
 *      white and every step is DARKER. That keeps the two jobs the ramp does —
 *      "this panel is not the page" and "this row is under the cursor" — both
 *      working, which an inverted ramp would break: on white you cannot hover
 *      something by making it lighter. Floating things are told apart by their
 *      shadow, not by being paler than the page.
 *
 *   2. EVERY HUE SPLITS IN TWO. A colour bright enough to fill a bar is never
 *      dark enough to set type in on white — #2ec46b on white is 2.0:1. So
 *      each of the four meanings carries a FILL token and a TEXT token:
 *      --accent/--accent-text, --run/--run-text, --done/--done-text,
 *      --fail/--fail-text. In dark the two are the same value or nearly so; in
 *      light the text one is a deepened version that clears 4.5:1. If you are
 *      setting type, reach for the -text token. Every ratio is measured, and
 *      the numbers are in the block below.
 *
 *   3. SHADOWS, TINTS AND MOTIFS ARE RE-DRAWN, NOT FLIPPED. The dark shadow is
 *      one huge soft black spread because it sits on near-black; the light one
 *      is two short layers because a big black cloud on white looks like dirt.
 *      The hazard strips, the chevron field and the chequer each get their own
 *      light values so they read as the same motif rather than the same maths.
 *
 * ---------------------------------------------------------------------------
 * No CDN and no webfont — deliberate, and now enforced. Space Grotesk has no
 * Cyrillic and this app is Russian-first, so a webfont would break Russian
 * headings mid-page; the server's CSP is `default-src 'self'` with no font-src,
 * so a Google Fonts link would simply be blocked. The system stacks below cover
 * Cyrillic everywhere she might open this and cost zero network on Russian
 * mobile. --serif is the reading face and only the reader pane wears it; see
 * the markdown section for why.
 *
 * Three motifs, exactly four places, never sprinkled:
 *   1. orange hazard strip — top of the run dock, and the monitor drawer
 *   2. red hazard strip    — the destructive confirm card
 *   3. chevron field       — bottom of the login gate
 *   4. chequer             — the finished bar, and the DONE legend key.
 *      Not decoration: the chequered flag is what "this one is finished" looks
 *      like, and a texture survives a colour-blind reader and a machine with
 *      animation switched off, which a hue on its own does not.
 * ==========================================================================*/

/* Must precede anything that sets `display`: a class rule like `.shell{display:grid}`
   has the same specificity as the UA's `[hidden]{display:none}` and loads later,
   so it wins and the element stays visible. Load-bearing — keep it first. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------ light theme -
   The default. Ratios below are measured against the ground each token
   actually sits on; tools/contrast.js recomputes the whole table from this
   file, so a changed value here is checked rather than hoped about. */
:root {
  color-scheme: light;

  /* SAMPLED, not chosen. Every value below was read out of the mockup Ethan
     supplied (tools/, scratch script: decode the PNG, histogram it, find the
     fills by connected region). Two earlier passes guessed - first a blue-grey
     dashboard ramp, then an over-corrected beige - and both were wrong in the
     way a guess is wrong: plausible and not what he asked for.

     The grounds are essentially WHITE with the faintest warm cast. The mockup
     measures #fdfdfb in the rail, #fdfcfa in the list and #fefefe on the
     document card. The separation between panes comes from a hairline, not
     from a step in tone, which is why the beige version read as heavy. */
  /* Not #ffffff. The mockup measures #fdfdfb in the rail and #fdfcfa in the
     list - barely off white, and that hair of warmth is the "papery" thing
     Ethan keeps pointing at. The pass before this one over-corrected into
     beige; the pass after it over-corrected into pure white. This is the
     measurement. */
  /* THREE GROUNDS, NOT TWO.

     The rail and the reader were both --bg, so two of the three columns were
     literally the same colour and the only thing between them was a hairline.
     Ethan asked for the paper to be "a smidge more beige, the smallest
     amount" and for the columns to be told apart by tone.

     So: one step warmer overall, and a ramp outward from the page. The
     reader is the lightest because that is where the words are; the list is
     a step back; the rail is furthest back because it is furniture. Each
     step is two or three units - enough to read as a different surface,
     nowhere near the beige the earlier pass over-corrected into. */
  --bg:        #fdfcf9;   /* the page: reader, log and diff fields */
  --bg-2:      #faf8f3;   /* one step back: the list column, inputs, code */
  --bg-rail:   #f7f5ef;   /* furthest back: the rail */
  --bg-chat:   #f6f7f3;   /* the list column while it holds conversations */
  --bg-sunk:   #f6f4f0;   /* below the page: the polish output column       */
  --surface:   #f8f7f3;   /* cards, and the first hover step                */
  --surface-2: #f2efea;   /* secondary buttons, menus, table heads          */
  --surface-3: #e8e4dd;   /* button hover, toast, flash — the top step      */
  --row-on:    #f5f6f1;   /* the selected library row - measured            */

  /* THE DOCUMENT CARD, and the ground it floats on. Two tokens rather than
     one because the ramp runs the other way in the two themes: on paper a
     floating thing is LIGHTER than the page, on anthracite it is lighter
     too - but "lighter" is --bg in one and --surface in the other. Writing
     the card as --bg and the ground as --bg-2 looks right in light and puts
     a DARKER card on a lighter page in dark, which reads as a hole. */
  --card:        #ffffff;
  --card-ground: #fbfaf7;

  /* On white a hairline and a control outline cannot be the same weight. A
     secondary button here is --surface-2 on --bg, which is 1.1:1 — the fill
     does not define it, so the BORDER has to, and that is the reverse of dark
     where the fill does all the work. Hence four strengths, not one. */
  --line:      #f0ece6;   /* hairline rules and pane edges - measured #f2efea */
  --line-3:    #e7e2da;   /* soft card, chip and code borders */

  /* The mockup draws its control borders at about #f2efea, which is invisible
     and which this app will not ship: tools/contrast.js holds control borders
     and input outlines to 3:1 because on a white ground the border IS the
     control. So these three keep their weight and only lose the beige. If a
     control looks heavier here than in the picture, that is the reason. */
  --line-2:    #918d86;   /* control borders, and they carry the control */
  --line-hi:   #75716b;   /* a border under the cursor, and a focused field */
  --field:     #888480;   /* an input outline IS the input, on a ground this pale */

  /* The ink carries a green cast rather than a warm one - the mockup sets its
     headline in #0e2620, which is near-black with the brand in it. */
  --text:      #17201c;   /* the black text she asked for   */
  --text-hi:   #0e2620;   /* headings, strong, the loudest ink - measured */
  --text-2:    #414a45;   /* quotes, quiet panels           */
  --muted:     #5a625d;
  --faint:     #68706b;   /* micro-labels are small, so this one still clears
                              AA rather than resting on the large-text
                              exemption. */

  /* ACCENT — green. The brand, and every interactive thing: buttons, active
     edges, selected chips, focus rings, ## headings. Never a status.
     --accent is the FILL (3.1:1 on white, so an edge or a bar is visible) and
     it carries near-black ink exactly as it does in dark, because a green
     light with dark type on it is the mark this app is built around. Type set
     IN green uses --accent-text. */
  /* MEASURED #254b3c. The old accent was #16a75c, a bright emerald, and it is
     the single thing that made the app look nothing like the mockup however
     much else matched. A deep forest green is a DARK fill, which flips one
     rule the palette used to run on: --accent-ink is now light, because white
     type on forest green is the whole look. */
  --accent:      #254b3c;
  --accent-hi:   #1a3a2d;               /* hover: deeper still */
  --accent-ink:  #f6faf7;               /* text ON accent - white, now that the fill is dark */
  --accent-text: #254b3c;               /* dark enough to set type in, on any of the grounds */
  --accent-tint: rgba(37,75,60,.07);
  --accent-line: #4a7462;               /* the focus ring */
  --accent-soft: #1a3a2d;               /* text on an accent TINT panel */

  /* RUN — the retired papaya, carrying meaning now instead of decorating.
     Anything unsettled: a job running, an unsaved edit, a warning, a partial
     dossier. If it is orange, it has not landed yet. */
  --run:      #dc6900;                  /* 3.5:1 on --bg, 3.1:1 on a card */
  --run-hi:   #f5811a;                  /* the brighter tip of a moving bar */
  --run-dk:   #a84f00;                  /* the second stripe of the hazard  */
  --run-text: #b05500;                  /* 5.1:1 on --bg */
  --run-ink:  #8f4300;                  /* text on a run TINT panel — 6.2:1 */
  --run-tint: rgba(220,105,0,.12);
  --run-line: rgba(176,85,0,.42);
  --run-fade: rgba(220,105,0,.22);      /* the unfilled half of a running bar */
  --run-soft: rgba(220,105,0,.45);
  --run-glow: rgba(220,105,0,.30);

  /* DONE — settled. Deliberately a step towards teal, so a finished bar can
     never be read as a control. Only ever a bar or a word. */
  --done:      #068a68;                 /* 4.4:1 fill */
  --done-text: #056b52;                 /* 6.5:1 on --bg */
  --done-ink:  #04593f;                 /* text on a done TINT — the diff's added lines */
  --done-tint: rgba(6,138,104,.13);
  --done-line: rgba(5,107,82,.45);
  --green:     var(--done);             /* legacy alias — nothing new should use this */

  /* FAIL — red. --fail fills, --fail-text sets type; the two tints are the
     quiet panel and the hover on a destructive control. */
  --fail:        #d92b1c;               /* 4.9:1 fill */
  --fail-text:   #c0261a;               /* 5.9:1 on --bg */
  --fail-dk:     #8e1a10;               /* the second stripe of the red hazard */
  --fail-tint:   rgba(217,43,28,.08);
  --fail-tint-2: rgba(217,43,28,.15);
  --fail-line:   rgba(192,38,26,.38);
  --fail-surface: #fdf2f1;              /* the destructive confirm card */

  /* BLUE — "this exists only in the other language", and links. */
  --blue:      #0f7fa8;                 /* 4.6:1 — dots and segments */
  --blue-hi:   #075779;
  --blue-text: #0a6f96;                 /* 5.6:1 — links */
  --blue-tint: rgba(15,127,168,.08);
  --blue-line: rgba(10,111,150,.35);

  --star: #b5811a;                      /* the favourite, and nothing else - the
                                           mockup uses #db9f42, which measures 2.4:1
                                           on this ground; this is the same hue held
                                           to the 3:1 a status colour has to clear. */

  /* Rounder than it was (8/10/12/14). A 2px corner is not a decoration, it is
     the difference between a form control and a thing you want to touch, and
     it is the other half of why this felt clinical. */
  --radius:       10px;
  --radius-lg:    14px;
  --radius-dock:  16px;
  --radius-modal: 18px;

  /* Measured off the mockup: the rail divider falls at x=274 and the list
     divider at x=574, on a 1660 canvas. The rail was 38px too narrow for a
     two-line lockup and the list 44px too wide for what is in it. */
  --rail: 274px;
  --list: 300px;

  /* Inter first, because it is what the mockup is set in and it is installed
     on a lot of machines already. It is NOT fetched - the CSP is default-src
     'self' with no font-src, so a webfont link would simply be blocked, and
     the app is Russian-first besides. If it is not on the machine the stack
     falls to Segoe UI and nothing about the layout moves. */
  --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", "Noto Sans", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Roboto Mono", monospace;

  /* The reading face. System serifs only, for the same reason there is no
     webfont, and every one of them carries Cyrillic.

     CAMBRIA AHEAD OF GEORGIA, and the reason is numerals. Georgia sets
     old-style figures by default, so "Март 2013 года" comes out with the 2
     and the 1 hanging below the baseline and the 0s at x-height - it reads as
     lowercase, and next to the mockup (which uses lining figures throughout)
     it looks like a different document. A dossier is full of years, lap
     times, points and character counts, so this is not a detail here. */
  --serif: ui-serif, Charter, "Bitstream Charter", "Source Serif 4", Cambria,
           Constantia, "Iowan Old Style", Georgia, "Noto Serif",
           "Liberation Serif", "Times New Roman", serif;

  /* The reading column. --measure is in em of --read, so the column is a count
     of characters and not a count of pixels: change the size and it still holds
     about seventy characters, which is the whole point of it. Everything in the
     markdown section spaces itself in em of --read for the same reason. */
  --read:     17px;
  --read-lh:  1.66;
  --measure:  33.5em;

  /* THE DOCUMENT COLUMN, which is not the reading line.

     --measure is one line of prose. This is the width of the page the prose
     sits on, and everything that acts on that page - the title, the tabs,
     the language chips, the images panel, the ask row - has to line up with
     it or the reader stops being one object.

     It was only ever applied to the card. On a 3440px monitor that left the
     card centred at 1517..2497 while the tabs above it and the ask field
     below it ran 574..3440: a thousand pixels between the words and the
     controls for the words. */
  --doc-col:  980px;

  /* Two short layers, not one huge cloud: on white a wide black spread reads
     as a smudge, so the light shadow is a contact shadow plus a soft one. */
  --shadow:  0 10px 28px rgba(31,25,17,.13), 0 2px 5px rgba(31,25,17,.07);
  --overlay: rgba(36,30,24,.42);        /* the scrim behind a confirm */
  --sheen:   rgba(255,255,255,.62);     /* the sweep along a running bar */
  --glow:    rgba(22,167,92,.10);       /* the halo above the generate screen */
  --hatch-a: #c3bcae;                   /* abandoned: a hatched grey, no alarm */
  --hatch-b: #ded8cc;

  --chequer-ink: rgba(0,0,0,.34);
  --chevron-ink: rgba(11,122,67,.07);

  --hazard-run:    repeating-linear-gradient(-60deg,var(--run) 0 6px,var(--run-dk) 6px 12px);
  --hazard-papaya: var(--hazard-run);   /* legacy alias */
  --hazard-red:    repeating-linear-gradient(-60deg,var(--fail) 0 6px,var(--fail-dk) 6px 12px);
  --chevron-field: repeating-linear-gradient(-60deg,var(--chevron-ink) 0 2px,transparent 2px 14px);

  /* Motif 4 — the chequered flag. Two 45° grids offset by half a cell. The
     cell size is set where it is used, so it fits the bar it lands on. */
  --chequer: linear-gradient(45deg, var(--chequer-ink) 25%, transparent 25%, transparent 75%, var(--chequer-ink) 75%),
             linear-gradient(45deg, var(--chequer-ink) 25%, transparent 25%, transparent 75%, var(--chequer-ink) 75%);
}

/* ------------------------------------------------------------- dark theme -
   Anthracite. The original palette, re-stated in the same token names so every
   rule in this file, promptlab.css and monitor.css works unchanged in both. */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* Warmed by the same hand as the light theme: the anthracite carried a blue
     cast, and at night a blue cast is what a machine looks like. Same
     lightness step for step, so nothing measured here moves. */
  --bg:        #0d0c0a;
  --bg-2:      #121110;
  /* The same ramp, running the other way: away from the page is DARKER on
     anthracite, where on paper it was warmer. */
  --bg-rail:   #090807;
  --bg-chat:   #0f1210;
  --bg-sunk:   #100f0d;
  --surface:   #191714;
  --surface-2: #211e1a;
  --surface-3: #2a2621;
  --row-on:    #1b1815;

  --card:        #171512;   /* a step ABOVE the page, same as in light */
  --card-ground: #0d0c0a;

  --line:      #252220;
  --line-2:    #38332d;
  --line-3:    #2a2622;
  --line-hi:   #443e36;
  --field:     #2a2622;   /* the original dark input outline, warmed but not
                             lifted — it is the one measurement in this file
                             that does not reach 3:1, and it predates the
                             light theme */

  --text:      #f0eeea;
  --text-hi:   #ffffff;
  --text-2:    #c9c5bd;
  --muted:     #979289;
  --faint:     #8b8574;   /* nudged up once from #5e626b, which measured 3.6:1
                              and never cleared AA on any ground here. */

  /* The light theme took the mockup's forest green, which is invisible on
     anthracite - so dark keeps a lifted version of the SAME hue rather than
     the old emerald. Same family, different lightness, which is what a theme
     pair is. Here the fill is light enough to carry dark ink again. */
  --accent:      #57a385;
  --accent-hi:   #6fb99b;
  --accent-ink:  #0a1712;               /* text ON accent */
  --accent-text: #7bbfa2;               /* type set IN green, on the dark ground */
  --accent-tint: rgba(87,163,133,.13);
  --accent-line: rgba(87,163,133,.7);    /* the focus ring */
  --accent-soft: #b6dcc9;               /* text on an accent TINT panel */

  --run:      #f97c1f;
  --run-hi:   #ff8b33;
  --run-dk:   #c85f12;
  --run-text: #f97c1f;
  --run-ink:  #ffc79a;
  --run-tint: rgba(249,124,31,.10);
  --run-line: rgba(249,124,31,.45);
  --run-fade: rgba(249,124,31,.25);
  --run-soft: rgba(249,124,31,.55);
  --run-glow: rgba(249,124,31,.50);

  --done:      #0ba06d;                 /* 5.8:1 on --bg, 5.0:1 on --surface-2 */
  --done-text: #0ba06d;
  --done-ink:  #2cc48c;                 /* lifted for 12px mono on its own tint */
  --done-tint: rgba(11,160,109,.14);
  --done-line: rgba(11,160,109,.60);
  --green:     var(--done);

  --fail:        #ff4538;
  --fail-text:   #ff8078;
  --fail-dk:     #b3271d;
  --fail-tint:   rgba(255,69,56,.08);
  --fail-tint-2: #2a1412;
  --fail-line:   #4a2320;
  --fail-surface: #1a1214;

  --blue:      #43c6f0;
  --blue-hi:   #7ad8f7;
  --blue-text: #43c6f0;
  --blue-tint: rgba(67,198,240,.06);
  --blue-line: rgba(67,198,240,.30);

  --star: #f9c22e;

  --shadow:  0 18px 50px rgba(0,0,0,.65);
  --overlay: rgba(6,7,9,.8);
  --sheen:   rgba(255,255,255,.5);
  --glow:    rgba(46,196,107,.06);
  --hatch-a: #443e36;
  --hatch-b: #2e2a24;

  --chequer-ink: rgba(0,0,0,.38);
  --chevron-ink: rgba(46,196,107,.05);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  height: 100dvh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}

:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

/* ------------------------------------------------------------------ atoms - */

/* The mono role: micro-labels, counts, clocks, captions. Never uppercased here —
   #confirm-type carries .mono and must show exactly what she typed. */
.mono {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .14em;
  font-variant-numeric: tabular-nums;
}

/* Accent-family uppercase micro-label. Colour varies by context. */
.ulabel {
  display: block;
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1.4;
}
.ulabel.accent { color: var(--accent-text); }

.muted  { color: var(--muted); }
.small  { font-size: 12.5px; }
.mt     { margin-top: 28px; }
.mt-s   { margin-top: 8px; }
.err    { color: var(--fail-text); font-size: 13px; margin-top: 10px; }
.danger { color: var(--fail-text); }

/* 6px pulsing ORANGE dot — "this is alive right now". Orange, not accent: the
   dot is a status, and a status is never the brand colour. */
.livedot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--run); flex: none; display: inline-block;
  animation: bb-pulse 1.4s ease-in-out infinite;
}
.livedot.green { background: var(--done); }

/* The pit-entry chevron that follows the wordmark and joins the pipeline cards. */
/* Decoration, not a control - it carries aria-hidden in the markup. The
   opacity is gone because --accent-text is already the measured value; dimming
   a measured colour is how a mark ends up "barely apparent". */
.chev {
  color: var(--accent-text); font-family: var(--mono); font-weight: 700;
  font-size: 14px; letter-spacing: -.08em; line-height: 1; flex: none;
}

/* THE WORDMARK — the pit box.
   "Box" is not a shape here, it is a place: the rectangle painted on the pit
   lane floor that the car has to stop inside. So the FIRST word wears one and
   the second word stands free on the tarmac. Two words, one boxed — which is
   also how the radio call works: the instruction, then the repeat.

   What it deliberately is not: the earlier lockup was a bordered slab with the
   second word set solid in orange, and a coloured slab carrying dark type reads
   as a certain other wordmark before it reads as anything else. Three things
   break that geometry here. There is no slab — the container has no background
   and no border at all. The fill inside the box is an 11% tint on near-black,
   which is a shade rather than a colour. And the type inside is accent ON dark,
   never dark on accent, so the box never becomes a plate.

   The box wears the same thick left edge every active thing in this app wears
   (.rail-item.is-on, .row-item.is-on, .mon-run.is-on) — the mark is built out
   of the interface it belongs to instead of being dropped on top of it.

   Mono, uppercase, wide-tracked: the instrument voice this system already
   reserves for labels, counts and clocks. It should read like something on a
   timing screen, not like a bold sans in a rounded rectangle. */
/* The mark is a chequered flag and the word beside it.

   It used to be the word BOX in a bordered box, which was a good joke about
   a pit box and read, at a glance and at 15px, as a form field. The mockup
   uses the chequer, the app already uses the chequer for "this one is
   finished", and the favicon is the same object - so this is the app
   agreeing with itself rather than a new idea. */
.mark {
  display: inline-flex; align-items: center; gap: .6em;
  line-height: 1;
  white-space: nowrap;
}

.mark-flag {
  width: 22px; height: 22px; flex: none;
  fill: var(--accent);
  stroke: none;
}

.mark-word {
  font-family: var(--sans);
  font-size: 17px; font-weight: 700;
  letter-spacing: .02em;
  color: var(--text-hi);
}

.mark-lg .mark-flag { width: 34px; height: 34px; }
.mark-lg .mark-word { font-size: 27px; letter-spacing: .01em; }

.gate-mark { display: inline-flex; align-items: center; justify-content: center; gap: 10px; }
.gate-mark .chev { font-size: 18px; }

/* 390px: the gate lockup is the widest fixed-size thing on the screen. */
@media (max-width: 420px) {
  .mark-lg .mark-word { font-size: 22px; }
  .mark-lg .mark-flag { width: 28px; height: 28px; }
}

/* ----------------------------------------------------------------- inputs - */

.input {
  width: 100%; background: var(--bg-2); border: 1px solid var(--field);
  color: var(--text); font: inherit; padding: 10px 12px;
  border-radius: var(--radius); outline: none;
  transition: border-color .15s, background .15s;
}
.input::placeholder { color: var(--faint); }
.input:focus { border-color: var(--line-hi); background: var(--surface); }
.input-sm { padding: 8px 11px; font-size: 13.5px; border-radius: 7px; }
.input-center { text-align: center; padding: 12px 14px; border-radius: 9px; }
.input.mono { font-size: 13px; letter-spacing: .08em; background: var(--bg); }

/* The one big field on the generate screen. */
.input-hero {
  font-size: 21px; font-weight: 600; letter-spacing: -.01em;
  padding: 16px 18px; border-radius: var(--radius-lg);
  border-color: var(--line-2); margin-top: 10px;
}

.textarea {
  resize: none; min-height: 160px; line-height: 1.65; font-size: 14.5px;
  padding: 14px 16px; border-radius: var(--radius-lg);
}

.btn {
  appearance: none; background: var(--surface-2); border: 1px solid var(--line-2);
  color: var(--text); font: inherit; font-size: 13.5px; font-weight: 600;
  padding: 9px 16px; border-radius: var(--radius); cursor: pointer;
  /* 41px on a mouse, which is fine, and 41px under a thumb, which is not.
     --tap is 32 with a pointer and 44 on a touch screen, so this line is
     inert on the desktop and only bites where it needs to. */
  min-height: var(--tap);
  transition: background .14s, border-color .14s, color .14s, transform .06s;
  white-space: nowrap;
}
.btn:hover { background: var(--surface-3); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; }
.btn.sm { padding: 7px 12px; font-size: 12.5px; border-radius: 7px; }

.btn-primary {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-ink); font-weight: 700;
}
.btn-primary:hover:not(:disabled) { background: var(--accent-hi); border-color: var(--accent-hi); }

.btn-danger { background: transparent; border-color: var(--fail-line); color: var(--fail-text); }
.btn-danger:hover { background: var(--fail-tint-2); }

/* Accent outline — Deepen. Loud enough to find, quiet enough not to compete. */
.btn-accent-outline { background: none; border-color: var(--accent-line); color: var(--accent-text); }
.btn-accent-outline:hover { background: var(--accent-tint); }

.btn-block { width: 100%; }

.ghost {
  background: none; border: 1px solid transparent; color: var(--muted);
  font: inherit; font-size: 13px; font-weight: 600;
  cursor: pointer; padding: 6px 11px; border-radius: 7px;
  transition: background .13s, color .13s, border-color .13s;
}
.ghost:hover { color: var(--text); background: var(--surface); }
.ghost:focus-visible { color: var(--text); background: var(--surface); }
.ghost.sm { padding: 4px 9px; font-size: 12px; }
.pane-back {
  border-color: var(--line-3); flex: none; display: none;
  align-items: center; justify-content: center;
  min-width: var(--tap); min-height: var(--tap); padding: 0;
}
.pane-back:hover { border-color: var(--line-hi); }

/* ------------------------------------------------------------------ icons -
 * Ethan: "I can't really see the minimised ones and stuff. It's just barely
 * apparent." Three separate things were making that true and all three are
 * fixed here rather than one at a time:
 *
 *   1. SIZE. The glyphs were set at 11-13px inside 14px boxes. An icon that
 *      carries meaning on its own needs to be about the size of a capital
 *      letter of body text, not smaller than one.
 *   2. WEIGHT. Several were --muted at 80% opacity, which is a colour chosen
 *      to recede stacked on a second instruction to recede. The opacity is
 *      gone; the tokens do the whole job, and they are measured.
 *   3. TARGET. The hit areas were about 25px square. --tap is 32 on a mouse
 *      and 44 on a finger, which is the number every platform asks for.
 *
 * The drawings themselves are inline SVG in index.html rather than text
 * glyphs, wherever the app does not rewrite the button's textContent. Four
 * reasons, in order of how much they matter here: a glyph is whatever the
 * system font decides it is, and 🗑 arrives as a colour emoji on Windows,
 * which this interface does not have anywhere else; ⧗ and ⏻ are missing from
 * enough font stacks to fall back to a box; ⌘ means "the Mac command key" and
 * nothing about prompts; and a stroked path stays sharp at any size and takes
 * currentColor, so one rule colours it in both themes.
 *
 * Where app.js or theme.js writes textContent - the star, the sort arrow, the
 * theme and motion cycles, the language code - the glyph stays and only its
 * size and contrast change. The list-collapse chevron is drawn in CSS instead,
 * for the same reason: its glyph is owned by app.js.
 */

:root { --tap: 32px; }
@media (pointer: coarse) { :root { --tap: 44px; } }

.ico {
  width: 17px; height: 17px; flex: none;
  fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
  /* the button takes the click, never the drawing inside it */
  pointer-events: none;
}
.ico-fill { fill: currentColor; stroke: none; }

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid transparent; color: var(--muted);
  font-family: inherit; font-size: 16px; line-height: 1;
  min-width: var(--tap); min-height: var(--tap); padding: 0 5px;
  border-radius: 7px; cursor: pointer;
  transition: background .13s, color .13s, border-color .13s;
}
.iconbtn:hover,
.iconbtn:focus-visible { color: var(--text-hi); background: var(--surface-2); }
.iconbtn.bordered { border-color: var(--line-3); }
.iconbtn.bordered:hover,
.iconbtn.bordered:focus-visible { background: none; border-color: var(--line-hi); }
#doc-fav.bordered:hover { color: var(--star); }
.iconbtn.on { color: var(--star); }
.iconbtn.push { margin-left: auto; }

.langbtn {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 1px solid var(--line-2); color: var(--muted);
  cursor: pointer; padding: 0 9px; min-height: var(--tap); border-radius: 6px;
  font-size: 12.5px; font-weight: 700; letter-spacing: .06em;
  transition: color .13s, border-color .13s, background .13s;
}
.langbtn:hover,
.langbtn:focus-visible { color: var(--text-hi); border-color: var(--line-hi); background: var(--surface-2); }

.seg {
  display: inline-flex; background: var(--bg-2);
  border: 1px solid var(--line-3); border-radius: var(--radius);
  overflow: hidden; margin-top: 10px;
}
.seg button {
  background: none; border: none; border-right: 1px solid var(--line-3);
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600;
  padding: 9px 18px; cursor: pointer; transition: background .13s, color .13s;
}
.seg button:last-child { border-right: none; }
.seg button:hover { color: var(--text); }
.seg button.is-on { background: var(--accent); color: var(--accent-ink); font-weight: 700; }

/* ------------------------------------------------------------------- gate - */

.gate {
  height: 100dvh; display: grid; place-items: center; padding: 24px;
  position: relative; overflow: hidden;
}
/* Motif 3 of 3 — chevron field over the bottom 200px, masked to fade upward. */
.gate::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 200px;
  background: var(--chevron-field);
  -webkit-mask-image: linear-gradient(to top, #000, transparent);
          mask-image: linear-gradient(to top, #000, transparent);
  pointer-events: none;
}
.gate-card {
  position: relative; z-index: 1;
  width: 100%; max-width: 340px;
  display: flex; flex-direction: column; gap: 16px; text-align: center;
}
.gate-card .gate-mark { align-self: center; }
.gate-sub { color: var(--muted); font-size: 13.5px; }
.gate-card .btn-primary { padding: 12px 16px; font-size: 14.5px; border-radius: 9px; }

/* ------------------------------------------------------------------ shell - */

.shell {
  display: grid;
  grid-template-columns: var(--rail) var(--list) 1fr;
  height: 100dvh;
  overflow: hidden;
}

/* ------------------------------------------------------------------- rail - */

/* Padding measured off the mockup: the New Dossier button runs x 25..246 in
   a 274 rail, so 25 each side; the settings row ends 34px above the floor. */
.rail {
  background: var(--bg-rail);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  padding: 22px 25px 20px;
  overflow-y: auto;
  min-height: 0;
}
/* The lockup: the mark, and one line under it saying what this is. */
.rail-top { display: flex; align-items: flex-start; gap: 8px; padding: 2px 4px 20px; }
.rail-brand { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.rail-sub {
  font-family: var(--mono); font-size: 9px; font-weight: 500;
  letter-spacing: .14em; text-transform: uppercase; color: var(--faint);
  padding-left: 2px;
}
.rail-top .chev { margin-top: 2px; }
.rail-new { margin-bottom: 16px; padding: 11px 16px; font-size: 14px; }

.rail-nav { display: flex; flex-direction: column; gap: 1px; }
/* 47px from one row to the next in the mockup (195, 243, 290, 337). */
.rail-item {
  display: flex; align-items: center; gap: 13px;
  background: none; border: none; color: var(--muted);
  font: inherit; font-size: 14px; font-weight: 500;
  padding: 12px 12px; border-radius: 10px; cursor: pointer; text-align: left; width: 100%;
  transition: background .13s, color .13s;
}
.rail-item:hover { background: var(--surface); color: var(--text); }
/* A rounded pill rather than a bar with an edge: the edge said "tab", and
   these are places, not tabs. */
.rail-item.is-on {
  background: var(--accent-tint); color: var(--text-hi); font-weight: 600;
}
/* The rail icons are drawn, so this rule only says what colour they take. The
   size comes from .ico, which every icon in the app shares. */
.ri-ico { color: var(--faint); }
.rail-item:hover .ri-ico { color: var(--text-2); }
.rail-item.is-on .ri-ico { color: var(--accent-text); }
.rail-item { min-height: var(--tap); }
.ri-label { flex: 1; min-width: 0; }
.ri-count { font-size: 10.5px; color: var(--faint); }
.rail-item.is-on .ri-count { color: var(--muted); }
.ri-count.is-live { color: var(--run-text); }
/* The live dot sits tight against the count, not on the row's own 10px gap. */
.rail-item .livedot { margin-right: -4px; }

.rail-sep { height: 1px; background: var(--line); margin: 22px 2px 0; }
/* 65px from the last nav row to the QUICK TOOLS label in the mockup. */
.rail-head { padding: 22px 12px 10px; color: var(--faint); }
/* flex-grow, so the leftover height collects HERE and not at the bottom of
   the rail. This is what pins the settings row to the floor. min-height 0
   and its own scroll so a long tag list cannot push the foot off. */
.rail-tags-wrap { margin-top: 18px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.rail-tags-wrap:empty { flex: 1 1 auto; }
.rail-tags-wrap .rail-head { padding-top: 0; }
.rail-tags { display: flex; flex-direction: column; gap: 1px; }
.rail-tag {
  display: flex; justify-content: space-between; gap: 8px;
  background: none; border: none; color: var(--muted);
  font: inherit; font-size: 13px; padding: 5px 11px; border-radius: 6px;
  cursor: pointer; text-align: left; width: 100%;
}
.rail-tag:hover { background: var(--surface); color: var(--text); }
.rail-tag.is-on { color: var(--accent-text); font-weight: 700; }
.rail-tag span:last-child {
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .06em; color: var(--faint); font-variant-numeric: tabular-nums;
}

/* Engine status — reads existing S.state, holds no state of its own. */
.engine-card {
  flex: none; background: var(--bg-2);
  border: 1px solid var(--line); border-radius: 12px;
  padding: 11px 13px; margin-top: 26px;
}
.engine-row { display: flex; align-items: center; gap: 7px; }
.engine-card .livedot { width: 7px; height: 7px; }
.engine-label { font-size: 9.5px; text-transform: uppercase; color: var(--muted); }
#engine-state { margin-left: auto; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.engine-sub { margin-top: 5px; font-size: 11.5px; line-height: 1.4; color: var(--faint); }

.engine-card.is-working .livedot { background: var(--run); }
.engine-card.is-working #engine-state { color: var(--run-text); }
.engine-card.is-ready .livedot { background: var(--done); animation: none; }
.engine-card.is-ready #engine-state { color: var(--done-text); }
.engine-card.is-off .livedot { background: var(--faint); animation: none; }
.engine-card.is-off #engine-state { color: var(--faint); }

/* PAUSED wears the run colour rather than the dead one, and does not blink.
   Grey would file it with "the engine is unavailable", which is the one
   reading it must not have: this is a decision somebody made, it is on
   purpose, and it can be undone from this very card. */
.engine-card.is-paused .livedot { background: var(--run); animation: none; }
.engine-card.is-paused #engine-state { color: var(--run-text); }
.engine-card.is-paused { border-color: var(--run-line); background: var(--run-tint); }

/* It is a control, so it has to say so under the cursor and take a focus
   ring like everything else that can be pressed. */
.engine-card { cursor: pointer; transition: border-color .15s, background .15s; }
.engine-card:hover { border-color: var(--line-2); }
.engine-card:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

/* The sentence under a button that has just gone grey. Without it, a
   disabled control is an interface refusing to say why. */
.gen-paused {
  margin-top: 10px; padding: 9px 12px;
  background: var(--run-tint); border: 1px solid var(--run-line);
  color: var(--run-ink); border-radius: var(--radius);
  font-size: 13px; line-height: 1.45;
}

/* On the floor. margin-top:auto is the belt to the tags-wrap's braces: if
   the rail ever holds something that does not grow, this still drops. */
.rail-foot {
  display: flex; gap: 6px; align-items: center;
  flex: none;
  padding: 16px 0 0; margin-top: auto; border-top: 1px solid var(--line);
}

/* The three themes, side by side, with the chosen one filled. */
.themeset { display: flex; align-items: center; gap: 2px; }

.themebtn {
  display: inline-flex; align-items: center; justify-content: center;
  /* Was a flat 32. --tap keeps that on a mouse and gives it 44 on a screen
     she taps, where these sit in a row and are easy to miss. */
  width: var(--tap); height: var(--tap); padding: 0;
  background: none; border: 0; border-radius: 9px;
  color: var(--faint); cursor: pointer;
  transition: background .13s, color .13s;
}
.themebtn .ico { width: 15px; height: 15px; }
.themebtn:hover { background: var(--surface); color: var(--text); }
.themebtn.is-on { background: var(--accent-tint); color: var(--accent-text); }

/* The language pill sits at the far end of the row, away from the three
   theme buttons - it is a different kind of choice. */
#lang-btn { margin-left: auto; }

/* ------------------------------------------------------------------- list - */

.listpane {
  background: var(--bg-2);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
/* No rule under it in the mockup: the columns are told apart by their own
   dividers, and a second line here made the top of the list look boxed. */
.list-head {
  display: flex; gap: 8px; align-items: center;
  padding: 16px 13px 10px;
}
.list-head .input { flex: 1; min-width: 0; background: var(--bg); }

/* Label left, sort right - the sort control belongs beside the thing it
   sorts, not up in the search row where it read as a search option. */
.list-title {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 13px 8px 16px;
  color: var(--faint);
  font-size: 10px; letter-spacing: .12em;
}

/* The favourite, in the corner of the card, at the amber the mockup uses. */
.row-star {
  position: absolute; top: 12px; right: 14px;
  font-size: 14px; line-height: 1; color: var(--star);
  pointer-events: none;
}

.sortbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  background: none; border: 0; border-radius: 8px;
  color: var(--faint); cursor: pointer;
}
.sortbtn .ico { width: 14px; height: 14px; }
.sortbtn:hover { background: var(--surface); color: var(--text); }

/* 13px each side, from the card measuring 274 wide in a 300 column. */
.rows {
  flex: 1; overflow-y: auto; min-height: 0; padding: 4px 13px 12px;
  display: flex; flex-direction: column; gap: 6px;
}

.row-item {
  position: relative;
  display: block; width: 100%; text-align: left;
  background: none; border: 1px solid transparent; color: inherit; font: inherit;
  padding: 15px 16px; border-radius: 12px; cursor: pointer;
  transition: background .13s, border-color .13s;
}
.row-item:hover { background: var(--surface); }
/* The selected dossier is a card with a thick green edge - the same edge
   every active thing in this app wears, and the same one in the wordmark. */
.row-item.is-on {
  background: var(--bg); border-color: var(--line-3);
  box-shadow: inset 3px 0 0 var(--accent), 0 1px 2px rgba(31, 25, 17, .05);
}

.row-title {
  display: flex; align-items: center; gap: 7px;
  font-size: 14px; font-weight: 600; letter-spacing: -.01em;
}
.row-title .star { flex: none; font-size: 13px; line-height: 1; color: var(--star); }
.row-title .livedot { margin-left: auto; }
.row-title .live-tag {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto; flex: none;
  font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--run-text);
}
.row-title .livedot + .live-tag,
.row-title .live-tag .livedot { margin-left: 0; }

.row-meta { display: flex; align-items: center; gap: 8px; margin-top: 7px; }
.row-tags, .row-activity {
  font-size: 11.5px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row-tags { color: var(--muted); }
.row-activity { color: var(--faint); }
.row-stat, .row-clock {
  margin-left: auto; flex: none;
  font-size: 10.5px; font-weight: 400; letter-spacing: .06em; color: var(--faint);
}

/* Three-segment sector strip — replaces the old R/H/S pips.
   jade = exists in the UI language · orange = generating right now
   blue = exists only in the other language · line-3 = missing.
   3px is too short to hold the chequer, so here colour carries it alone. */
.sectors-mini { display: flex; gap: 3px; flex: none; width: 54px; }
.seg-mini {
  --seg: var(--line-3);
  --seg-ink: var(--faint);
  flex: 1; min-width: 0; height: 3px; border-radius: 2px; background: var(--seg);
}
.seg-mini.done { --seg: var(--done); --seg-ink: var(--done); }
.seg-mini.live { --seg: var(--run);  --seg-ink: var(--run); }
.seg-mini.alt  { --seg: var(--blue);   --seg-ink: var(--blue); }
.seg-mini.none { --seg: var(--line-3); --seg-ink: var(--faint); }

.list-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 16px; border-top: 1px solid var(--line);
}
#list-count { font-size: 12px; color: var(--faint); }
.legend {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  font-size: 9.5px; font-weight: 400; letter-spacing: .06em; color: var(--faint);
}
.legend-dot { width: 7px; height: 7px; border-radius: 2px; flex: none; display: inline-block; }
.legend-dot:not(:first-child) { margin-left: 6px; }
/* The legend is where the language is taught, so the DONE key is the one place
   in the list pane that says it twice: jade, and the chequer. */
.legend-dot.done {
  background-color: var(--done);
  background-image: var(--chequer);
  background-size: 7px 7px;
  background-position: 0 0, 3.5px 3.5px;
}
.legend-dot.live { background: var(--run); }

.empty { text-align: center; padding: 56px 24px; }
.empty-big { font-size: 16px; font-weight: 700; margin-bottom: 6px; }

/* ----------------------------------------------------------------- reader - */

.reader {
  background: var(--bg); position: relative;
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.rview { display: flex; flex-direction: column; min-height: 0; flex: 1; overflow: hidden; }

.welcome {
  margin: auto; text-align: center; padding: 30px;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
}

/* ---- one column for the whole reader ------------------------------------
   The border still spans the pane - a rule that stops half way looks like a
   mistake - but what sits inside it lines up with the card. */
.doc-head {
  display: flex; align-items: flex-start; gap: 12px;
  /* LEFT on the column, RIGHT in the corner.

     Putting both edges on the document column pulled the star and the ...
     inward with the title, and on a wide monitor they ended up floating in
     the middle of the header with a field of empty space to their right.
     They are window furniture - they act on the whole dossier, not on the
     text - so they belong in the corner, while the title still lines up with
     the card it names. */
  padding: 20px 24px 14px max(24px, (100% - var(--doc-col)) / 2);
  border-bottom: 1px solid var(--line); flex: none;
}
.doc-id { min-width: 0; flex: 1; }
/* The reading serif, and big. This is the name of a person whose story she
   is about to tell; at 23px bold sans it was a filename. The pane titles
   below stay sans, because "New dossier" is a screen and not a subject. */
.doc-id h1 {
  font-family: var(--serif);
  font-size: 34px; font-weight: 600; letter-spacing: -.02em; line-height: 1.1;
  color: var(--text-hi);
}
.doc-id .h-pane { font-family: var(--sans); font-size: 19px; font-weight: 700; }
.doc-id .ulabel {
  margin-top: 5px; font-size: 10px; font-weight: 400; letter-spacing: .12em;
  color: var(--faint);
}
/* tags / words / "updated 2d ago" */
.doc-micro {
  display: flex; align-items: center; flex-wrap: wrap; gap: 14px; margin-top: 7px;
  font-size: 10px; font-weight: 400; letter-spacing: .1em;
  text-transform: uppercase; color: var(--faint);
}
.doc-micro .sep { color: var(--line-2); }
.doc-head-actions { display: flex; align-items: center; gap: 4px; flex: none; }
.head-note {
  margin-left: auto; align-self: center; flex: none;
  font-size: 10px; font-weight: 400; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
}
#trash-empty { margin-left: auto; flex: none; }

/* Anchored in JS to the button that opens it - see openDocMenu(). It used
   to be pinned to a fixed right: 18px / top: 62px, which only lined up with
   the ... while that button happened to sit in the corner at one particular
   window size. The offsets below are the fallback for a first paint. */
.menu {
  position: absolute; right: 18px; top: 62px; z-index: 30;
  background: var(--surface-2); border: 1px solid var(--line-2);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 5px; min-width: 190px;
}
.menu-item {
  display: block; width: 100%; text-align: left;
  background: none; border: none; color: var(--text);
  font: inherit; font-size: 13.5px; padding: 8px 11px; border-radius: 6px;
  cursor: pointer; text-decoration: none;
}
.menu-item:hover { background: var(--surface-3); }
.menu-item.danger { color: var(--fail-text); }
.menu-sep { height: 1px; background: var(--line); margin: 5px 0; }

.doctabs {
  display: flex; align-items: center; gap: 4px;
  padding: 0 max(24px, (100% - var(--doc-col)) / 2);
  border-bottom: 1px solid var(--line); flex: none;
  overflow-x: auto; scrollbar-width: none;
}
.doctabs::-webkit-scrollbar { display: none; }
.doctab {
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600;
  padding: 11px 12px; margin-bottom: -1px; cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 7px;
  transition: color .13s, border-color .13s;
}
.doctab:hover { color: var(--text); }
.doctab.is-on { color: var(--text); border-bottom-color: var(--accent); }
.tdot { width: 5px; height: 5px; border-radius: 50%; background: var(--done); flex: none; }
.tdot.alt  { background: var(--blue); }
.tdot.none { background: var(--line-2); }

/* "● EN / ● RU ONLY" — what the dots above actually mean. */
.tab-legend {
  margin-left: auto; align-self: center; flex: none;
  display: inline-flex; align-items: center; gap: 12px; padding-right: 2px;
  font-size: 9.5px; font-weight: 400; letter-spacing: .1em; color: var(--faint);
}
.tab-legend .tdot { display: inline-block; }
.tab-legend .done { color: var(--done-text); }
.tab-legend .alt  { color: var(--blue-text); }

/* Both languages of a part, always visible, always actionable. Replaces the
   old "this part does not exist in Russian" banner, which only appeared on the
   fallback case and left her unable to see or make the other language. */
.langpick { gap: 8px; }
.lang-chip {
  display: inline-flex; align-items: baseline; gap: 8px;
  background: var(--bg); border: 1px solid var(--line-3);
  color: var(--muted); font: inherit; font-size: 13.5px; font-weight: 600;
  padding: 9px 16px; border-radius: 999px; cursor: pointer;
  min-height: var(--tap);
  transition: border-color .14s, color .14s, background .14s;
}
.lang-chip:hover { color: var(--text); border-color: var(--line-2); }

/* FILLED. The language being shown is a solid green pill with white on it -
   a tint with a border says "selectable", and both chips then look the same
   at a glance, which is the one thing this control must never do. */
.lang-chip.is-on {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
  cursor: default;
}
.lang-chip .lang-state {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint);
}
.lang-chip.is-on .lang-state { color: var(--accent-ink); opacity: .72; }
/* the language that does not exist yet - the state word is the call to action */
.lang-chip.absent { border-style: dashed; }
.lang-chip.absent .lang-name { color: var(--faint); }
.lang-chip.absent:hover .lang-state { color: var(--accent-text); }
/* the language that cannot be made yet, because its English source is missing */
.lang-chip.blocked { opacity: .55; border-style: dotted; }
.lang-chip.blocked:hover { border-color: var(--line-3); color: var(--muted); }
.lang-chip.blocked:hover .lang-state { color: var(--faint); }

.langbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px max(24px, (100% - var(--doc-col)) / 2);
  background: var(--blue-tint);
  border-bottom: 1px solid var(--line); font-size: 13px; color: var(--muted); flex: none;
}

/* 57px of gutter each side of the card in the mockup (pane 575..1660, card
   632..1596). It scales down on a narrower window rather than crushing the
   card, because the card has its own max-width. */
.doc-scroll {
  flex: 1; overflow-y: auto; min-height: 0;
  /* The card centres itself inside this. Everything else in here - the
     images panel, above all - was left running the full width of the pane. */
  padding: 30px max(clamp(16px, 5vw, 57px), (100% - var(--doc-col)) / 2) 48px;
}
/* THE COLUMN, not a left margin.

   max-width with no auto margin pins the content to the left edge of a pane
   that is much wider than 760px, so the queue and the trash sat in a narrow
   strip with a field of dead space beside them - Ethan: "why is it kind of
   over on the left? why is there a bunch of dead space on the right?"

   Centred on --doc-col like everything else in the reader, so these two read
   as the same document surface as a dossier rather than as a different app. */
.doc-scroll.pad {
  padding: 18px max(24px, (100% - var(--doc-col)) / 2) 28px;
  max-width: none;
}

.doc-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 14px max(clamp(16px, 4vw, 40px), (100% - var(--doc-col)) / 2);
  border-top: 1px solid var(--line); flex: none;
}
/* Everything on this row is 46px tall, which is what the mockup measures
   for the Deepen pill, the ask field and the send button alike. A row of
   controls at three different heights is the thing that reads as unfinished.*/
#deepen-btn { height: 46px; padding: 0 18px; font-size: 13.5px; border-radius: 14px; }
.ask { display: flex; gap: 10px; flex: 1; }
.ask .input {
  flex: 1; min-width: 0; height: 46px;
  border-radius: 14px; padding-left: 18px; font-size: 14px;
}

/* Send is a shape, not a word. "Спросить" set the width of the button from
   the length of a Russian verb, and the row never sat straight. */
/* 68 x 46 in the mockup, and squarer than a pill. */
.ask-send {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 68px; height: 46px; padding: 0;
  border-radius: 14px;
}
.ask-send .ico { width: 18px; height: 18px; }

.missing { text-align: center; padding: 50px 20px; color: var(--muted); }
.missing .btn { margin-top: 14px; }

.row { display: flex; gap: 8px; }
.row .input { flex: 1; min-width: 0; }

/* --------------------------------------------------------------- generate - */

#rv-new .doc-scroll { position: relative; padding: 0 24px 40px; }
.gen-glow {
  position: absolute; left: 0; right: 0; top: 0; height: 420px; pointer-events: none;
  background: radial-gradient(700px 380px at 50% -10%, var(--glow), transparent 70%);
}
.gen-body { position: relative; max-width: 640px; margin: 0 auto; padding-top: 48px; }

/* Three stages joined into one pipeline: each feeds the next, so they read
   as one run and not as three independent checkboxes. */
.pipeline { display: flex; align-items: stretch; margin-top: 10px; }
.pipe-card {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px;
  background: var(--surface); border: 1px solid var(--line-3);
  padding: 14px 16px; cursor: pointer; user-select: none; color: var(--faint);
  transition: border-color .14s, color .14s;
}
.pipe-card:first-child { border-radius: var(--radius-lg) 0 0 var(--radius-lg); }
.pipe-card:last-child  { border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.pipe-card.is-on { border-color: var(--accent-line); color: var(--text); }
.pipe-card input { accent-color: var(--accent); width: 14px; height: 14px; flex: none; }
.pipe-name { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; }
.pipe-sub {
  font-size: 10px; font-weight: 400; letter-spacing: .06em;
  text-transform: uppercase; color: var(--faint);
}
.pipe-link { align-self: center; flex: none; padding: 0 7px; }
.pipe-note { margin-top: 10px; font-size: 12px; color: var(--faint); }

#gen-go { font-size: 15px; padding: 13px 16px; border-radius: 9px; }
#gen-note { color: var(--faint); }

.tipbox {
  display: flex; gap: 10px; margin-top: 34px;
  border-top: 1px dashed var(--line-3); padding-top: 16px;
}
.tipbox .ulabel { flex: none; padding-top: 2px; color: var(--accent-text); }
.tipbox .small { font-size: 12.5px; line-height: 1.6; color: var(--muted); }

/* ----------------------------------------------------------------- polish - */

.polish-cols { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1fr; }
.polish-in {
  display: flex; flex-direction: column; gap: 10px; min-height: 0;
  padding: 24px 26px; border-right: 1px solid var(--line);
}
.polish-in .textarea { flex: 1; min-height: 0; }
.polish-out {
  display: flex; flex-direction: column; gap: 10px; min-height: 0;
  padding: 24px 26px; background: var(--bg-sunk);   /* one step under the reader field */
}
.polish-out-head { display: flex; align-items: center; gap: 8px; }
.polish-out-head .ghost { margin-left: auto; border-color: var(--line-3); }
.pol-panel {
  flex: 1; min-height: 0; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 14px 16px; background: var(--bg);
}

/* ------------------------------------------------------------------ trash - */

.trash-row {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.trash-row:last-child { border-bottom: none; }
.trash-main { flex: 1; min-width: 0; }
.trash-row > .t-title { flex: 1; min-width: 0; }
.t-title { font-weight: 600; }
.trash-when {
  display: block; margin-top: 3px;
  font-size: 10.5px; font-weight: 400; letter-spacing: .08em; color: var(--faint);
}

/* ------------------------------------------------------------------- dock - */
/* The pit wall. "–" hides it and the job keeps running; Cancel is the only
   thing that stops one; ✕ exists only once there is nothing left to stop. */

.dock { position: absolute; right: 18px; bottom: 64px; left: auto; z-index: 40; }
/* 64px, not 16 — it must clear the reader footer so Ask stays reachable
   while a job runs. She should never have to hide the dock to type. */

.dock-main {
  position: relative; overflow: hidden;     /* overflow clips the hazard strip */
  width: min(432px, calc(100vw - 36px));
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--radius-dock); box-shadow: var(--shadow);
  padding: 15px 16px 14px;                  /* 12px + the 3px strip */
}
/* Motif 1 of 4 — the orange hazard strip. Work in progress in the pit lane. */
.dock-main::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--hazard-run);
}

/* Minimised: a small pill that stays out of the way but keeps the job visible.
   This is what "–" produces — hiding must never look like stopping. */
.dock-pill {
  display: none; align-items: center; gap: 9px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: 999px; box-shadow: var(--shadow);
  padding: 9px 16px; cursor: pointer;
  color: var(--text); font: inherit; font-size: 13px; font-weight: 600;
  max-width: min(340px, calc(100vw - 36px));
}
.dock-pill:hover { background: var(--surface-2); }
.dock-pill #pill-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#pill-clock { font-size: 11px; }
.dock[data-state="min"] .dock-main { display: none; }
.dock[data-state="min"] .dock-pill { display: inline-flex; }

.dock-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.dock-id { min-width: 0; }
#dock-kind { font-size: 9px; }
/* Carries .ulabel.accent in the markup, but it labels a RUNNING job — one of
   three places the accent class is overruled by the status language. */
#dock-kind.accent { color: var(--run-text); }
.dock-id strong {
  display: block; font-size: 15px; font-weight: 600; margin-top: 3px; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dock-right { display: flex; align-items: center; gap: 4px; flex: none; }
/* Hide and Dismiss sit on a dark-ish card at the bottom of the reader and are
   the two Ethan called out by name. They keep the shared target and get the
   border, so "minimise" is a button on the pit wall rather than a dash. */
.dock-right .iconbtn { border-color: var(--line-2); }
.dock-right .iconbtn:hover,
.dock-right .iconbtn:focus-visible { border-color: var(--line-hi); }
.clock {
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
#dock-clock { margin-right: 4px; }

/* Sectors: one bar per stage, read left to right like a timing screen. */
.sectors { display: flex; gap: 4px; margin: 12px 0 7px; }
.sector { flex: 1; min-width: 0; }
/* A part is one label over one OR TWO bars - the second is the Russian half,
   which is a separate stage doing separate work and so keeps its own progress.
   The gap between the pair is smaller than the gap between parts, so a glance
   reads four things rather than eight. */
.sector-bars { display: flex; gap: 2px; }
.sector-bar {
  position: relative; overflow: hidden; flex: 1; min-width: 0;
  height: 5px; border-radius: 3px; background: var(--line-3);
}
/* Motif 4 of 4 — the chequer. A finished bar is flagged in, not merely
   coloured in. At 5px the 2.5px cells read as texture against the flat orange
   of a running bar and the flat red of a failed one, which is the point: the
   three states differ in SURFACE as well as in hue, so the strip still parses
   with the colour taken away. */
.sector-bar.is-done {
  background-color: var(--done);
  background-image: var(--chequer);
  background-size: 5px 5px;
  background-position: 0 0, 2.5px 2.5px;
}
.sector-bar.is-fail { background: var(--fail); }
.sector-bar.is-run {
  background: linear-gradient(90deg, var(--run) 55%, var(--run-fade) 55%);
}
.sector-bar.is-run::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--sheen), transparent) no-repeat;
  background-size: 40px 100%;
  animation: bb-sweep 1.5s linear infinite;
}
.sector-name {
  margin-top: 5px; font-family: var(--mono);
  /* Half the labels are gone, so the survivors get the room: 9.5px and a
     tighter track fit "RESEARCH" and "COMMENTS" whole, where 8.5px with
     .12em tracking truncated both to stumps. */
  font-size: 9.5px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sector.run .sector-name  { color: var(--run-text); }
.sector.done .sector-name { color: var(--done-text); }
.sector.fail .sector-name { color: var(--fail-text); }

/* What it is actually doing, in words, plus the numbers behind it. */
/* Two lines of very different type - 13.5px semibold over 10.5px mono - and
   a 2px gap with no line-height on either. The mono line rode up into the one
   above it and read as sitting high rather than as the line under it. Even
   line-heights and a gap you can see fix the rhythm without moving anything
   else. */
.dock-activity { display: flex; flex-direction: column; gap: 5px; }
.dock-msg { font-size: 13.5px; line-height: 1.3; font-weight: 600; color: var(--text); }
.dock-msg.done { color: var(--done-text); }
.dock-msg.fail { color: var(--fail-text); }
.dock-detail { font-size: 10.5px; line-height: 1.3; font-weight: 400; letter-spacing: .04em; color: var(--faint); }

.dock-more {
  background: none; border: none; color: var(--muted);
  font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0;
  padding: 8px 0 2px; cursor: pointer; text-align: left; width: 100%;
}
.dock-more:hover { color: var(--text); }

.dock-body { margin-top: 6px; display: flex; flex-direction: column; gap: 8px; }
.dock-facts { display: flex; flex-wrap: wrap; gap: 6px; }
.fact {
  font-size: 11px; font-weight: 600; color: var(--muted);
  background: var(--bg-2); border: 1px solid var(--line-3);
  padding: 4px 9px; border-radius: 999px; font-variant-numeric: tabular-nums;
}
.fact b { color: var(--text); font-weight: 700; }

.dock-foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.dock-foot .btn-danger { flex: none; }
.dock-foot #dock-open { flex: 1; font-weight: 700; }
.dock-note { font-size: 11px; line-height: 1.35; color: var(--faint); }
/* The caption belongs to Cancel. No Cancel, nothing to reassure her about —
   the finished state must never read as if something could still be stopped. */
#run-cancel[hidden] + .dock-note { display: none; }

.log {
  max-height: 190px; overflow-y: auto;
  background: var(--bg); border: 1px solid var(--line-3); border-radius: var(--radius);
  padding: 10px 12px; font-family: var(--mono); font-size: 11.5px; line-height: 1.6;
  color: var(--muted); white-space: pre-wrap; word-break: break-word;
}
.log .ev-tool  { color: var(--run-text); }
.log .ev-stage { color: var(--text); font-weight: 700; display: block; margin-top: 7px; }
.log .ev-error { color: var(--fail-text); }
.log .ev-warn  { color: var(--run-text); }
.log .ev-done  { color: var(--done-text); font-weight: 700; }

/* ------------------------------------------------------------------ queue - */

.queue-card {
  background: var(--surface); border: 1px solid var(--line-3);
  border-radius: var(--radius-lg); padding: 14px 16px; margin-bottom: 10px;
}
.queue-head { display: flex; align-items: center; gap: 8px; }
.queue-card .livedot { width: 7px; height: 7px; }
.queue-title {
  flex: 1; min-width: 0; font-size: 14.5px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.queue-card .clock { margin-left: auto; flex: none; font-size: 11.5px; }

/* Inside a card the strip runs the full width and carries the stage names. */
.queue-card .sectors-mini { width: auto; flex: 1; gap: 4px; margin: 12px 0 7px; }
.queue-card .seg-mini {
  height: auto; background: none; border-radius: 0;
  display: flex; flex-direction: column;
}
.queue-card .seg-mini::before {
  content: ""; display: block; height: 4px; border-radius: 2px; background: var(--seg);
}
.queue-card .sector-name { color: var(--seg-ink); }

.queue-foot { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.queue-what { font-size: 12.5px; color: var(--text-2); min-width: 0; }
.queue-when {
  font-family: var(--mono); font-size: 10.5px; font-weight: 400;
  color: var(--faint); font-variant-numeric: tabular-nums;
}
.queue-foot .btn { margin-left: auto; flex: none; }
.queue-note { margin-top: 16px; font-size: 12px; color: var(--faint); }

/* ------------------------------------------------------------------ modal - */

.modal {
  position: fixed; inset: 0; z-index: 90;
  background: var(--overlay); backdrop-filter: blur(4px);
  display: grid; place-items: center; padding: 24px;
}
.modal-card {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--radius-modal); box-shadow: var(--shadow);
  padding: 24px; width: 100%; max-width: 400px;
}
/* Motif 2 of 3 — red hazard strip. Only on a genuinely destructive confirm. */
.modal-card.is-danger::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--hazard-red);
}
.modal-card h2 { font-size: 17px; font-weight: 700; letter-spacing: -.01em; margin: 4px 0 8px; }
.modal-card p { color: var(--muted); font-size: 13.5px; line-height: 1.6; }
.modal-card label { display: block; margin-top: 16px; }
.modal-card .input { margin-top: 8px; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }
.modal-actions .btn { padding: 9px 16px; font-size: 13px; }

/* --------------------------------------------------------------- markdown - */
/* THE READER. She reads three-thousand-word dossiers and nine-slide scripts on
 * this screen for hours at a time, so this section is not decoration - it is
 * the product. Four decisions carry it.
 *
 * 1. A MEASURE. The pane is as wide as the window and a line of text that runs
 *    the whole way across it is the single biggest thing wrong with reading
 *    here: the eye loses the start of the next line. --measure caps the column
 *    at about seventy characters, which is where continuous prose has been set
 *    for four hundred years, and the column is centred in whatever is left.
 *
 * 2. A SERIF, FOR THE PROSE ONLY. The interface stays sans and the labels stay
 *    mono; the moment you are inside a dossier the letterforms change, and that
 *    difference is doing a job - it says this is a document, not a screen. It
 *    is a system stack for the same reason there is no webfont anywhere else in
 *    this app: a Google Fonts link is blocked by the CSP and would be unreliable
 *    from Russia even if it were not. Every face in --serif carries Cyrillic
 *    (New York on Apple, Georgia on Windows, Noto Serif on Android), so a
 *    Russian dossier is set in the same voice as an English one rather than
 *    falling out of the stack into whatever the browser has lying around.
 *    Headings keep the families they had: h1 and h3 in the interface sans, h2
 *    and h4 in the mono label voice. Sans headings over serif text is an old
 *    magazine pairing and it keeps the structure sounding like Box Box.
 *
 * 3. ONE RHYTHM. Every vertical gap is a multiple of the reading size, so it
 *    holds when the size changes on a phone. Headings carry a large space above
 *    and a small one below, and the thing directly under a heading has its top
 *    margin removed entirely - so a heading sits with what it introduces
 *    instead of floating between two blocks.
 *
 * 4. THE STRUCTURES ARE TREATED AS WHAT THEY ARE. md.js tags four repeating
 *    shapes (see its header) and each is styled here: Fast Facts become a card
 *    of labelled rows, the Timeline and Numbers get a rail with the date or the
 *    figure hanging off it, a slide's stage directions become records with mono
 *    labels, a Myth-Busted claim leads its own paragraph, quotes get their
 *    attribution as a caption, and the Sources list gets a numbered gutter with
 *    the URL allowed to break anywhere it likes.
 *
 * Nothing in here animates, and nothing needs to.
 */

.md {
  font-family: var(--serif);
  font-size: var(--read);
  line-height: var(--read-lh);
  /* The measure lives on the blocks inside a dossier card now - see
     docview.css. Everywhere else .md is the column itself, so it keeps it. */
  max-width: var(--measure);
  width: 100%;
  color: var(--text);
  overflow-wrap: break-word;
  /* Cyrillic and Latin both benefit; on a serif this is what keeps "AV" and
     "Ту" from opening up at reading size. */
  font-kerning: normal;
  text-rendering: optimizeLegibility;
}
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }

/* Cyrillic sets denser than Latin - a taller x-height, almost no descenders -
   so the same leading reads as a tighter block. It gets a little more air. */
.md:lang(ru) { line-height: calc(var(--read-lh) + .05); }

/* -- headings -- */

.md h1 {
  font-family: var(--sans);
  font-size: 1.62em; font-weight: 700; line-height: 1.18; letter-spacing: -.022em;
  color: var(--text-hi);
  margin: 1.7em 0 .34em;
}
/* ## becomes an accent micro-label with a hairline rule running right. */
.md h2 {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  font-family: var(--mono); font-size: .715em; font-weight: 600; line-height: 1.25;
  letter-spacing: .16em; text-transform: uppercase; color: var(--accent-text);
  margin: 2.5em 0 .8em;
}
.md h2::after { content: ""; flex: 1 1 40px; height: 1px; background: var(--line); }
.md h3 {
  font-family: var(--sans);
  font-size: 1.06em; font-weight: 700; line-height: 1.32; letter-spacing: -.012em;
  color: var(--text-hi);
  margin: 1.9em 0 .4em;
}
.md h4 {
  font-family: var(--mono); font-size: .6em; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  margin: 1.5em 0 .35em;
}
/* A heading belongs to what comes after it, so nothing after one holds a top
   margin of its own. This is the rule that stops headings floating. */
.md h1 + *, .md h2 + *, .md h3 + *, .md h4 + * { margin-top: 0; }
/* The standfirst under the title - "*Researched 2026-08-21 · 22 sources*". */
.md .md-standfirst {
  font-size: .94em; color: var(--muted); font-style: normal;
  margin: 0 0 1.6em; padding-bottom: 1.1em; border-bottom: 1px solid var(--line);
}
.md .md-standfirst em { font-style: italic; color: inherit; }

/* -- prose -- */

.md p { margin: .82em 0; }
.md strong { color: var(--text-hi); font-weight: 700; }
.md em { color: inherit; }
.md del { color: var(--muted); }
/* -- the two kinds of reference --
 * A dossier's references do two different things and are told apart by colour,
 * which is the language this app already speaks: BLUE opens a page, GREEN is
 * the thing you press. So a locator is a link and looks like one everywhere on
 * the web; a search is accent-green and carries a magnifier, because it does
 * not open the source - it goes and looks for it. Confusing the two would be
 * the false affordance worth avoiding: she would click "skysports.com"
 * expecting Sky and get Google.
 *
 * Both keep a 1px underline in every state. In a page of serif prose an
 * underline is what says "link" at a glance, and colour alone would fail the
 * one reader in twelve who cannot separate these two hues.
 */
.md a {
  color: var(--blue-text); text-decoration: none;
  border-bottom: 1px solid var(--blue-line);
  overflow-wrap: anywhere;    /* a bare source URL must never push the column */
}
.md a:hover,
.md a:focus-visible { color: var(--blue-hi); border-bottom-color: var(--blue-hi); }

.md a.md-search {
  color: var(--accent-text);
  border-bottom-color: var(--accent-line);
}
.md a.md-search:hover,
.md a.md-search:focus-visible {
  color: var(--accent-hi); border-bottom-color: var(--accent-hi);
  background: var(--accent-tint);
}
/* Sized in em so it tracks the reading size, and set on the baseline rather
   than centred so it does not open the line it sits on. */
.md .md-lens {
  width: .72em; height: .72em;
  margin-left: .2em; vertical-align: -.02em; flex: none;
  opacity: .75;
}
.md a.md-search:hover .md-lens,
.md a.md-search:focus-visible .md-lens { opacity: 1; }

/* A citation. 124 of them in one dossier, so it stays the quiet raised number
   it already was and only the underline says it is reachable; the colour
   arrives on hover, where it is answering a question she has just asked. */
.md a.md-cite {
  color: inherit; border-bottom: none;
  /* currentColor, so the hint is exactly as loud as the number it sits under
     and no louder - a fixed grey would have been invisible in one theme. */
  text-decoration: underline dotted;
  text-underline-offset: .18em;
  cursor: pointer;
}
.md a.md-cite:hover,
.md a.md-cite:focus-visible {
  color: var(--accent-text);
  text-decoration: underline solid var(--accent-line);
}

/* Where a citation lands. A flash, not a mark: she asked for source 17, she is
   shown source 17, and the page is not left wearing the answer afterwards. */
.md ol.md-sources > li.src-lit {
  background: var(--accent-tint);
  box-shadow: inset 3px 0 0 var(--accent);
  transition: background .25s, box-shadow .25s;
}
.md hr { border: none; border-top: 1px solid var(--line); margin: 2.2em 0; }

.md ul, .md ol { margin: .82em 0 .82em 1.25em; padding: 0; }
.md li { margin: .3em 0; padding-left: .15em; }
.md li::marker { color: var(--faint); }
.md li > ul, .md li > ol { margin: .3em 0 .3em 1.1em; }

/* -- quotes -- */
/* A driver's own words, and who they were said to. The quote is the thing on
   the page; the attribution is the caption under it, in the label voice, so it
   stops reading as the last sentence of the quote. */
.md blockquote {
  margin: 1.35em 0; padding: .1em 0 .1em 1.05em;
  border-left: 3px solid var(--accent-line);
  color: var(--text-2);
}
.md blockquote p { margin: .35em 0; font-size: 1.02em; }
.md blockquote cite {
  display: block; margin-top: .5em;
  font-family: var(--sans); font-style: normal;
  font-size: .74em; line-height: 1.45; letter-spacing: .01em;
  color: var(--faint);
}
.md blockquote cite em { font-style: italic; }

/* -- code, citations, counts -- */

.md code {
  font-family: var(--mono); font-size: .8em; color: var(--accent-soft);
  background: var(--surface-2); border: 1px solid var(--line-3);
  padding: 1.5px 5px; border-radius: 4px;
  overflow-wrap: anywhere;
}
.md pre {
  background: var(--bg-2); border: 1px solid var(--line-3); border-radius: var(--radius);
  padding: 12px 14px; overflow-x: auto; max-width: 100%; margin: 1.1em 0;
  -webkit-overflow-scrolling: touch;
}
.md pre code {
  background: none; border: none; padding: 0; color: var(--muted);
  font-size: .76em; overflow-wrap: normal;
}
/* Source refs - [17][21]. Raised and shrunk so they sit beside the sentence
   rather than inside it; line-height 0 keeps the raise from opening the line. */
.md .ref {
  font-family: var(--mono); font-size: .62em; font-weight: 600; letter-spacing: .02em;
  color: var(--faint); position: relative; top: -.4em; line-height: 0;
  margin-left: .1em; white-space: nowrap;
}
/* The slide script's character count. A measurement, so it is set as one. */
.md code.count {
  color: var(--faint); background: var(--bg-2); border-color: var(--line);
  font-size: .68em; font-variant-numeric: tabular-nums; letter-spacing: .04em;
  padding: 1px 5px; vertical-align: .12em;
}

/* -- the four shapes -- */

/* head: a claim in bold with its answer behind it. Myth-Busted, hook material,
   the unverified list. The claim gets its own line and its own weight. */
.md [data-lead="head"] > strong:first-child {
  display: block;
  font-family: var(--sans); font-size: .96em; font-weight: 700;
  line-height: 1.34; letter-spacing: -.008em;
  color: var(--text-hi); margin-bottom: .2em;
}
.md [data-lead="head"] { margin: 1.3em 0; }

/* key: "Full name: …", "Why it works: …". The label stays on the line - these
   values are prose and a stacked label would double the height of a page of
   them - but it is coloured off the body so the eye can run down the keys. */
.md [data-lead="key"] > strong:first-child { color: var(--accent-text); font-weight: 700; }

/* label: an ALL-CAPS stage direction. ON SCREEN, VO, IMAGE, SEARCH. Here the
   label DOES stack, because there are seven per slide and they are a form to be
   filled in, not a sentence. Consecutive ones join into one ruled block. */
.md [data-lead="label"] {
  margin: .7em 0; padding: .1em 0 .1em .85em;
  border-left: 2px solid var(--line-3);
}
.md [data-lead="label"] + [data-lead="label"] { margin-top: -.35em; }
.md [data-lead="label"] > strong:first-child {
  display: block;
  font-family: var(--mono); font-size: .58em; font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--faint); margin-bottom: .1em;
}

/* term: "1944, 13 August — born Bushey Heath". md.js has already dropped the
   dash, because once the date is on its own line the dash is punctuation left
   over from a layout that no longer exists. */
.md [data-lead="term"] > strong:first-child {
  display: block;
  font-family: var(--mono); font-size: .74em; font-weight: 700;
  letter-spacing: .06em; color: var(--accent-text);
  font-variant-numeric: tabular-nums;
  margin-bottom: .1em;
}

/* -- lists that are all one shape -- */

/* Fast Facts: eleven labelled bullets are one card, not eleven bullets. */
.md ul.md-defs {
  list-style: none; margin: 1.1em 0; padding: 0;
  border: 1px solid var(--line-3); border-radius: var(--radius-lg);
  background: var(--bg-2);
  overflow: hidden;
}
.md ul.md-defs > li {
  margin: 0; padding: .6em .95em; border-top: 1px solid var(--line);
  font-size: .95em;
}
.md ul.md-defs > li:first-child { border-top: none; }

/* Timeline, and Numbers That Land: a rail with the date or the figure hanging
   off it. The same shape serves both because it is the same shape. */
.md ul.md-terms {
  list-style: none; margin: 1.2em 0; padding: 0 0 0 1.05em;
  border-left: 1px solid var(--line-3);
}
.md ul.md-terms > li {
  position: relative; margin: 0; padding: .5em 0 .6em;
}
.md ul.md-terms > li::before {
  content: ""; position: absolute; left: calc(-1.05em - 3px); top: 1.05em;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--line-2);
}

/* Two ALL-CAPS bullets side by side - the RU / EN split. Same record shape. */
.md ul.md-labels { list-style: none; margin: 1.1em 0; padding: 0; }
.md ul.md-labels > li { margin: 0; padding: 0; }

.md ul.md-leads > li { margin: .9em 0; }

/* Sources: a numbered gutter, the URL free to break wherever it must, and the
   note after it stepped back so the list scans as links first. */
.md ol.md-sources {
  list-style: none; margin: 1.2em 0; padding: 0;
  counter-reset: src;
  font-size: .92em;
}
.md ol.md-sources > li {
  counter-increment: src;
  position: relative; margin: 0; padding: .5em 0 .5em 2.6em;
  border-top: 1px solid var(--line);
  line-height: 1.5;
}
.md ol.md-sources > li:first-child { border-top: none; }
.md ol.md-sources > li::before {
  content: counter(src);
  position: absolute; left: 0; top: .62em;
  width: 2em; text-align: right;
  font-family: var(--mono); font-size: .78em; font-weight: 600;
  color: var(--faint); font-variant-numeric: tabular-nums;
}

/* -- tables -- */

.table-wrap { overflow-x: auto; margin: 1.2em 0; -webkit-overflow-scrolling: touch; max-width: 100%; }
.md table { border-collapse: collapse; width: 100%; font-size: .88em; }
.md th, .md td { border: 1px solid var(--line); padding: 7px 10px; text-align: left; vertical-align: top; }
.md th {
  background: var(--surface-2); font-family: var(--mono); font-weight: 600;
  font-size: .78em; letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
}

.qa-item { border-bottom: 1px solid var(--line); padding: 15px 0; }
.qa-item:last-child { border-bottom: none; }
.qa-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 5px; }
.qa-q { flex: 1; min-width: 0; font-weight: 700; color: var(--text-hi); }
.qa-del {
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--faint); cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0; border-radius: 6px; flex: none;
  min-width: var(--tap); min-height: var(--tap);
  opacity: 0; transition: opacity .13s, color .13s, background .13s;
}
.qa-item:hover .qa-del { opacity: 1; }
.qa-del:hover { color: var(--fail-text); background: var(--surface-2); }
.qa-del:focus-visible { opacity: 1; }
/* There is no hover on a phone, so a control that only appears on hover is a
   control that does not exist there. It stays quiet, not invisible. */
@media (pointer: coarse) { .qa-item .qa-del { opacity: .6; } }
.qa-date { margin-top: 7px; font-size: 11px; color: var(--faint); }

/* "this dossier is only partly written" — unsettled, therefore orange. */
.notice {
  background: var(--run-tint); border: 1px solid var(--run-line);
  color: var(--run-ink); border-radius: var(--radius);
  padding: 9px 12px; font-size: 13px; margin-bottom: 13px;
}

/* ------------------------------------------------------------------ toast - */

.toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  background: var(--surface-3); border: 1px solid var(--line-2);
  color: var(--text); font-size: 13.5px; font-weight: 600;
  padding: 10px 17px; border-radius: 999px; box-shadow: var(--shadow); z-index: 95;
  max-width: calc(100vw - 32px); text-align: center;
}

/* ------------------------------------------------------------- responsive - */

@media (max-width: 1180px) { :root { --rail: 236px; --list: 280px; } }

/* ================================================================= *
 * THE FOLDING LIST COLUMN                                           *
 * ----------------------------------------------------------------- *
 * Ethan: the list column "takes up a ton of space and doesn't need
 * to". Folded, it becomes a narrow spine that still carries the count,
 * so the library is one click away rather than gone. The reader gets
 * the whole width back, which is the point - that is where the words
 * she is actually reading live.
 *
 * Done by swapping the grid template rather than by hiding the column,
 * so the reader genuinely reflows into the space instead of being
 * overlapped by it.
 * ================================================================= */

:root { --spine: 40px; }

.listspine {
  grid-column: 2;
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 10px 0;
  background: var(--bg);
  border: 0;
  border-right: 1px solid var(--line);
  cursor: pointer;
  color: var(--muted);
  font: inherit;
  transition: background .13s, color .13s;
}
.listspine:hover,
.listspine:focus-visible { color: var(--accent-text); background: var(--surface); }
.listspine:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* The reopen control, and the one Ethan could not find. It is a boxed chevron
   rather than a loose glyph, so the spine reads as a button with a control on
   it instead of a strip of grey with a mark in it. */
.listspine .spine-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  border: 1px solid var(--line-2); border-radius: 7px;
  transition: border-color .13s, background .13s;
}
.listspine:hover .spine-ico,
.listspine:focus-visible .spine-ico { border-color: var(--accent-line); background: var(--accent-tint); }

/* Turned on its side so the word reads up the spine. Both languages keep
   this label short for exactly that reason. */
.listspine .spine-label,
.listspine .spine-count {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
}
.listspine .spine-label {
  letter-spacing: .14em;
  text-transform: uppercase;
  font-size: 11px;
}
.listspine .spine-count { font-size: 10px; opacity: .6; }

.shell.list-collapsed { grid-template-columns: var(--rail) var(--spine) 1fr; }
.shell.list-collapsed .listpane { display: none; }
.shell.list-collapsed .listspine { display: flex; }

/* The fold control. app.js owns its textContent - it writes ‹ or › - so the
   glyph is switched off and the chevron is drawn from two borders instead:
   sharp at any size, currentColor, and no data: URI for the CSP to argue with.
   It carries a border by default because it is the control Ethan looked for
   and did not see, and an unbordered icon on a pane header reads as a label. */
/* app.js writes ↓ or A into this one, so it keeps its glyph and gains the
   shared target and a border, next to the fold control it sits beside. */
#sort-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap); min-height: var(--tap); padding: 0;
  font-size: 15px; border-color: var(--line-3); flex: none;
}
#sort-btn:hover,
#sort-btn:focus-visible { color: var(--text-hi); border-color: var(--line-hi); background: var(--surface); }

#list-collapse {
  flex: none; font-size: 0;
  border-color: var(--line-3);
}
#list-collapse::after {
  content: "";
  width: 9px; height: 9px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateX(2px) rotate(45deg);
}
#list-collapse:hover,
#list-collapse:focus-visible { color: var(--accent-text); border-color: var(--accent-line); background: var(--accent-tint); }
.shell.list-collapsed #list-collapse::after { transform: translateX(-2px) rotate(-135deg); }

/* Below the two-pane breakpoint the list IS the navigation, so folding it
   away would strand her. The control disappears and the fold is ignored. */
@media (max-width: 940px) {
  #list-collapse, .listspine { display: none !important; }
  .shell.list-collapsed { grid-template-columns: var(--list) 1fr; }
  .shell.list-collapsed .listpane { display: flex; }
}
/* Two panes: the rail collapses into the list header. */
@media (max-width: 940px) {
  .shell { grid-template-columns: var(--list) 1fr; }
  .rail { position: fixed; left: 0; top: 0; bottom: 0; width: 236px; z-index: 60;
          transform: translateX(-100%); transition: transform .18s; background: var(--bg); }
  .shell[data-pane="rail"] .rail { transform: none; box-shadow: var(--shadow); }
  .list-head .pane-back { display: inline-flex; }
  .doc-head { padding: 16px 20px 12px; }
  .doctabs { padding: 0 20px; }
  .doc-scroll { padding: 22px 22px 28px; }
  .polish-in, .polish-out { padding: 20px; }
}

/* One pane at a time. */
@media (max-width: 720px) {
  .shell { grid-template-columns: 1fr; }
  .listpane, .reader { grid-column: 1; grid-row: 1; }
  .shell[data-pane="reader"] .listpane { display: none; }
  .shell:not([data-pane="reader"]) .reader { display: none; }
  .doc-head .pane-back { display: inline-flex; }
  .doc-head { padding: 13px 14px 10px; flex-wrap: wrap; }
  .doc-scroll { padding: 16px 15px 26px; }
  .doctabs { padding: 0 14px; }
  .doc-id h1 { font-size: 20px; }
  .doc-id .h-pane { font-size: 18px; }
  .head-note { flex-basis: 100%; margin-left: 0; text-align: left; }
  .tab-legend { display: none; }
  /* One number moves and the whole page re-sets itself: the type, every
     margin, and the measure, because all three are stated in em of --read.
     16px rather than 17 because a phone is held closer, and the column has
     stopped being the constraint - the screen is. */
  :root { --read: 16px; }
  #rv-new .doc-scroll { padding: 0 15px 32px; }
  .gen-body { padding-top: 28px; }
  .input-hero { font-size: 19px; padding: 14px 15px; }
  /* the pipeline turns vertical, and the chevrons turn with it */
  .pipeline { flex-direction: column; }
  .pipe-card,
  .pipe-card:first-child,
  .pipe-card:last-child { border-radius: var(--radius-lg); }
  .pipe-link { transform: rotate(90deg); padding: 3px 0; }
  /* polish stacks: her text on top, the result under it */
  .polish-cols { display: flex; flex-direction: column; overflow-y: auto; }
  .polish-in { border-right: none; border-bottom: 1px solid var(--line); }
  .polish-in .textarea { min-height: 180px; }
  .pol-panel { min-height: 180px; }
  .dock { right: 10px; left: 10px; bottom: 62px; width: auto; }
  .dock-main { width: auto; }
  .dock-pill { max-width: 100%; }
  .doc-foot { flex-wrap: wrap; }
  .row { flex-direction: column; }
  .list-foot .legend { display: none; }
}

/* Motion.
   Ethan's PC has animations disabled system-wide, so browsers force
   prefers-reduced-motion and the app looks frozen to him. The ◐ button sets
   data-motion="on", which opts back in regardless of the OS setting;
   data-motion="off" forces them off even on a machine that allows them.
   bb-pulse and bb-sweep live inside these guards. */
@keyframes bb-sweep { from { background-position: -80px 0; } to { background-position: 80px 0; } }
@keyframes bb-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="on"]) *,
  :root:not([data-motion="on"]) *::before,
  :root:not([data-motion="on"]) *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  :root:not([data-motion="on"]) .sector-bar.is-run::after { display: none; }
  :root:not([data-motion="on"]) .livedot { animation: none; }
}
:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after {
  animation-duration: .001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .001ms !important;
}
:root[data-motion="off"] .sector-bar.is-run::after { display: none; }
:root[data-motion="off"] .livedot { animation: none; }


/* ---------------------------------------------------- tab language badges -
   Absolute, not relative: each tab says which language versions EXIST. The one
   currently on screen is accent; the other is muted; a part with no versions
   shows a dim dash. Replaces the old single dot, whose meaning changed
   depending on which language you were viewing. */
.tab-langs { display: inline-flex; gap: 3px; margin-left: 7px; }
.tlang {
  font-size: 8.5px; font-weight: 700; letter-spacing: .1em;
  padding: 2px 4px; border-radius: 3px;
  border: 1px solid var(--line-3); color: var(--faint); line-height: 1;
}
.tlang.is-on { border-color: var(--accent-line); color: var(--accent-text); background: var(--accent-tint); }
.tlang.none { border-style: dashed; }

/* Give the reader a real measure and centre it, instead of a thin column
   pinned to the left of a wide pane. */
.doc-scroll > .md,
.doc-scroll > .missing { margin-left: auto; margin-right: auto; }

/* ================================================================= *
 * DEEPEN UPDATES, LIFTED TO THE TOP                                 *
 * ----------------------------------------------------------------- *
 * A Deepen pass appends to the END of the dossier, which is right for
 * the file and wrong for reading - new material lands below forty
 * kilobytes of document and cannot be found. These sit at the top
 * instead, collapsed, so what changed is the first thing visible and
 * the dossier itself is still the body of the page.
 * ================================================================= */

.updates { margin: 0 0 32px; }

.updates-label {
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent-text);
  margin: 0 0 10px;
}

.update {
  border: 1px solid var(--accent-line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-tint);
  margin-bottom: 10px;
}

.update > summary {
  cursor: pointer;
  padding: 11px 14px;
  font-weight: 600;
  font-family: var(--sans);
  color: var(--text);
  list-style: none;
}
.update > summary::-webkit-details-marker { display: none; }

/* A caret drawn rather than a glyph, so it is identical on every machine
   and rotates without a webfont. */
.update > summary::before {
  content: ''; display: inline-block;
  width: 6px; height: 6px; margin-right: 10px;
  border-right: 2px solid var(--accent-text);
  border-bottom: 2px solid var(--accent-text);
  transform: rotate(-45deg) translateY(-2px);
  transition: transform .15s;
}
.update[open] > summary::before { transform: rotate(45deg) translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
  .update > summary::before { transition: none; }
}

.update > summary:hover { color: var(--accent-text); }
.update > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.update-body { padding: 0 16px 14px; }
.update-body > :first-child { margin-top: 0; }

/* ------------------------------------------------------------------ rename -
   Renaming borrows the modal shell and none of the confirm card's alarm: no
   hazard strip, no red button, no type-the-word gate. It is not destructive
   and it should not look it. */
#rename-form { gap: 0; }
#rename-form h2 { margin-bottom: 6px; }
#rename-sub { font-size: 12.5px; line-height: 1.5; margin-bottom: 14px; }
#rename-input { width: 100%; font-size: 15px; padding: 11px 14px; }
#rename-form .modal-actions { margin-top: 16px; }

/* --------------------------------------------------------- the chat column -
   The conversation history, in the list column beside the chat - where a chat
   history lives in every other tool. It swaps with the dossier list rather
   than stacking under it: they answer different questions and the screen is
   only ever asking one of them. */


/* ================================================================= *
 * THE COLUMN WHEN IT IS SHOWING CONVERSATIONS                       *
 * ----------------------------------------------------------------- *
 * Same column, different contents, and until now identical chrome -
 * so opening a new dossier looked like the library had emptied
 * itself, with no visible way back. Ethan: "I don't even know how to
 * get back to the actual research docs."
 *
 * Two changes, both deliberately small. A conversation is a different
 * KIND of thing from a dossier, not a more important one, so this
 * marks the difference without competing with the reader: the ground
 * cools by one step, and the accent edge says which mode is on.
 * ================================================================= */
.listpane.is-chats { background: var(--bg-chat); }
.listpane.is-chats .list-title {
  border-bottom: 1px solid var(--line);
  color: var(--accent-text);
}

/* The way back. Reads as a control rather than a label, because it is
   the one thing in this column she has to be able to find. */
.listback {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0 4px 0 0;
  min-height: var(--tap);
  color: var(--accent-text); cursor: pointer;
  font: inherit; font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
}
.listback .ico { width: 13px; height: 13px; }
.listback:hover { color: var(--text-hi); }
.listback:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
/* With the back control present the label is the destination, not the
   heading, so it steps back and lets the control lead. */
.listpane.is-chats #list-label { margin-left: auto; }

.chat-new {
  display: block; width: 100%; text-align: left;
  background: none; border: 1px dashed var(--line-2); color: var(--muted);
  font: inherit; font-size: 13px; font-weight: 500;
  padding: 11px 14px; border-radius: 12px; cursor: pointer;
  margin-bottom: 8px;
}
.chat-new:hover { border-style: solid; border-color: var(--accent); color: var(--text); }

.chat-none { padding: 10px 4px; }

.chat-row {
  display: flex; align-items: stretch;
  border: 1px solid transparent; border-radius: 12px;
  transition: background .13s, border-color .13s;
}
.chat-row:hover { background: var(--surface); }
.chat-row.is-on {
  background: var(--bg); border-color: var(--line-3);
  box-shadow: inset 3px 0 0 var(--accent), 0 1px 2px rgba(31, 25, 17, .05);
}

.chat-pick {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: baseline; gap: 10px;
  background: none; border: 0; padding: 12px 6px 12px 14px;
  cursor: pointer; text-align: left; font: inherit; color: inherit;
}

.chat-title {
  flex: 1 1 auto; min-width: 0;
  font-size: 13.5px; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.chat-meta { flex: none; font-size: 10.5px; color: var(--faint); }

/* Only on hover: a row of delete crosses down the column reads as a warning
   about the list rather than as a thing you may do to one item in it. */
.chat-del {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; background: none; border: 0; padding: 0;
  color: var(--faint); cursor: pointer; opacity: 0;
  transition: opacity .12s, color .12s;
}
.chat-del .ico { width: 12px; height: 12px; }
.chat-row:hover .chat-del { opacity: 1; }
.chat-del:hover { color: var(--fail-text); }
.chat-del:focus-visible { opacity: 1; }

/* ================================================================= *
 * WHAT THIS RUN WILL MAKE                                           *
 * ----------------------------------------------------------------- *
 * A bar between the header and whichever route she is using, because
 * both of them produce the same run and both have to show the same
 * settings. It was inside the manual form, which meant the chat route
 * spawned work with settings nobody could see.
 *
 * Two rows on a phone, one on anything wider. Deliberately quiet: it
 * is a statement of what is about to happen, not the main event.
 * ================================================================= */
.genopts {
  flex: none;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 22px;
  padding: 12px max(20px, (100% - var(--doc-col)) / 2) 13px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-2);
}
.genopt { display: flex; align-items: center; gap: 10px; min-width: 0; }
.genopt-wide { flex: 1 1 auto; }
.genopts .ulabel { flex: none; }
.genopts .pipeline { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; }
.genopts .pipe-note { flex-basis: 100%; margin: 0; }

@media (max-width: 720px) {
  .genopts { padding-left: 15px; padding-right: 15px; gap: 8px 14px; }
  .genopt { flex-wrap: wrap; }
}

/* The bar has to survive a narrow reader pane. Three faults the sweep found:
   the language segment's buttons were 38px under a thumb, the pipeline could
   not wrap because its cards would not shrink, and the chevrons between them
   held the row open past the pane's right edge. */
.genopts .seg button { min-height: var(--tap); }
.genopt { flex-wrap: wrap; min-width: 0; }
.genopts .pipeline { min-width: 0; row-gap: 6px; }
.genopts .pipe-card { max-width: 100%; min-width: 0; }
.genopts .pipe-card .pipe-name { min-width: 0; overflow-wrap: anywhere; }
/* The chevrons are hidden at every width now - see the chip rules below. */

/* ================================================================= *
 * THE PICKER, AS A ROW OF CHIPS                                     *
 * ----------------------------------------------------------------- *
 * .pipe-card was drawn for the vertical form: a tall panel with the
 * name over a second line of meta, four of them stretched edge to
 * edge with chevrons between. Dropped into this bar it became four
 * boxes 120px tall with "Comments" wrapping to "Comment / s" and the
 * minutes wrapping under the model name - Ethan: "weirdly formatted
 * and just really huge and ugly for no reason".
 *
 * In the bar they are chips: one line each, name and time side by
 * side, sized to their contents. The card styling is untouched for
 * anywhere else it is used.
 * ================================================================= */
.genopts .pipeline { align-items: center; gap: 8px; }

.genopts .pipe-card {
  flex: 0 0 auto;
  flex-direction: row; align-items: center; gap: 8px;
  padding: 0 12px; min-height: var(--tap);
  border-radius: 999px;
  white-space: nowrap;
}
/* The row ends are round now, so the first/last squaring goes. */
.genopts .pipe-card:first-child,
.genopts .pipe-card:last-child { border-radius: 999px; }

.genopts .pipe-card .pipe-name { font-size: 13px; overflow-wrap: normal; }
.genopts .pipe-card .pipe-sub {
  font-size: 10px; letter-spacing: .04em; white-space: nowrap;
}

/* Decoration from the vertical layout, pointing between panels that are
   now chips on one line. */
.genopts .pipe-link { display: none; }

/* The two labels sit on the same baseline as their controls rather than
   floating in a column of their own. */
.genopts .ulabel { align-self: center; }

/* The "cut short" notice now carries the way out of it, so it is a row rather
   than a sentence: the text takes the space it needs and the button sits at the
   end of it. */
.notice { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.notice > span { flex: 1 1 240px; min-width: 0; }
.notice .btn { flex: none; }
