/* ============================================================
   APPARAT — the GREPg frontend.

   The concept, in one line: these listings are Videotext descendants, the
   product is named after grep, and it works by regex — so the app is a
   CRITICAL EDITION of a TV listing. The broadcaster's raw text is the source
   document; the rules are its apparatus criticus.

   COLOUR. Bone paper (--paper), slate ink (--ink), one hairline (--rule), and
   exactly ONE saturated colour: --match, #BD3A1F, which is grep --color's own
   match red and teletext's key red. It is spent on the wordmark's EPg, the
   caret, the coverage figure, the dagger and the selected row's rule — and
   nowhere else. Three of those five are TEXT, so --match is an INK as well as
   a mark, and it is calibrated as one (#501). Everything that carries any
   other colour uses the seven teletext hues pastelised, one hue per target
   family (time / titles / meta / descriptions / credits / origin & links /
   consumed), with a second shade where two rules share a family so neighbours
   stay distinguishable. Those seven are marks and ONLY marks: a tint is never
   set as `color` (see "A MARK IS NEVER INK" below). That is not decoration:
   teletext used its seven colours to separate CATEGORIES of information on a
   listing page, which is exactly what rule traceability needs.

   TYPE. Three faces, each with a job that means something (see fonts.css):
     --disp   Archivo at the top of its width axis — the SYSTEM speaking:
              wordmark, column heads, record titles, big numbers.
     --mono   IBM Plex Mono — the MACHINE's data: times, field paths, rule
              keys, patterns, and the source text itself.
     --prose  Newsreader — the BROADCASTER's own words: descriptions,
              summaries, the report's prose.
   A reader can tell who wrote a line by the shape of it.

   LAYOUT. Three columns as a narrowing, not three equal panels: a dense finder,
   a reading column set to a real measure, a wider apparatus. Single hairlines
   and a ground shift only — no cards, no shadows, zero radius.

   SIGNATURE. The trace column sets the raw text as an annotated edition:
   tinted spans with superscript numerals in the engine's firing order, and a
   numbered apparatus below. Hovering any of the three places a numeral appears
   — the source span, the extracted value, the rule — lights the other two. It
   is pure CSS :has(); there is no JavaScript in that mechanic and there must
   not be. See "THE TIE" at the foot of this file.
   ============================================================ */
@import url("fonts.css");

:root{
  --paper:#FBFBF8;      /* page ground, bone, faintly warm  */
  --sheet:#FFFFFF;      /* reading surfaces                 */
  --shade:#F3F3EF;      /* the finder's recessed ground     */
  --hover:#EDEDE8;      /* row hover, one step past shade   */
  /* THE INK SCALE — three grades, all of them above the floor (#482).

     The scale used to run 17.3 : 1 / 4.4 : 1 / 2.3 : 1, and only the first of
     those was a reading. `--ink-2` had been calibrated against `--sheet`, where
     it measured 4.60, while column two stands on `--paper`, where the same
     token measured 4.44 — one token, two columns, and it missed in the one
     nobody had measured. `--ink-3` missed everywhere, by a lot.

     The owner ruled on the specimen, having seen both scales side by side:
     *"the compliant scale has a better readability."* So the third grey is
     load-bearing, and since a compliant third grey IS the old second grey
     (4.5 : 1 on paper needs a luminance of .175; `--ink-2` sat at .178) the
     whole scale moved down rather than only its bottom rung.

     CALIBRATED AGAINST THE WORST GROUND, not a chosen one. That is the general
     form of the `--ink-2` defect: which surface a component lands on is a
     layout decision, and a token calibrated against one of them passes in one
     column and misses in another by 0.07 with nothing to say so. The worst
     ground here is `--hover` — the darkest light ground and the lightest dark
     one — so every grade is set against it and clears the other three by
     construction. A row under the pointer is a row being read.

              light                     dark
              hex      L*   paper/sheet/shade/hover
     --ink    #15171B   7.7  17.31 17.94 16.13 15.28   #E9E7E1  14.36 13.20 15.16 11.95
     --ink-2  #484E55  32.9   8.11  8.41  7.56  7.16   #B4B5B7   8.65  7.96  9.13  7.20
     --ink-3  #646B74  44.9   5.20  5.39  4.84  4.59   #8F9091   5.55  5.11  5.86  4.62

     THREE GRADES IN 4.5–17.3 IS LESS ROOM THAN THREE IN 2.3–17.3, and that is
     the cost the ruling accepted: contrast is no longer available as the
     instrument of recession, so wherever a grade was doing hierarchical work
     alone, SIZE, WEIGHT or SPACE now takes it over — `.ap-pat` under `.ap-out`,
     `.cf-meta` under `.cf-count`, `.rvd-note` beside `.rvd-f`. The grades are
     still ΔL* 25.2 / 12.0 apart in light and 17.9 / 14.0 in dark, which is what
     `tests/webapp/test_ink_scale.py` holds a floor under: a later nudge for
     readability must not collapse three greys into two.

     AND NOTHING DIMS BY MULTIPLYING. `opacity` composites an in-spec colour
     back under the floor and defeats every guarantee in this block silently,
     because the token it defeats still reads correctly in the stylesheet. Where
     something has to recede, it gives up its MARK or takes the next grade down;
     it is never multiplied. The rendered sweep in that same test file
     composites ancestor opacity the way the compositor does, which is the only
     way that rule can be checked at all. */
  --ink:#15171B;        /* primary                          */
  --ink-2:#484E55;      /* secondary / labels               */
  --ink-3:#646B74;      /* tertiary / scaffolding           */
  --rule:#E3E4DF;       /* hairline                         */
  /* THE EDGE OF A CONTROL, as distinct from a hairline that only divides
     (#782). `--rule` is 1.09–1.28 : 1 on the four grounds — right for a line
     between two rows, wrong for the only visible edge of a text field or a
     filter chip, which WCAG 1.4.11 holds at 3 : 1. Calibrated the ink scale's
     way, against the worst ground (`--hover`), so it clears all four:
     paper 3.54 / sheet 3.67 / shade 3.30 / hover 3.12 light. It also serves as
     the DISABLED glyph's colour (`.brk-a:disabled`), where `opacity` used to
     composite an ink below every guarantee in the block above.
     `tests/webapp/test_ink_scale.py` holds it to the floor in both themes. */
  --rule-strong:#81868C;
  /* THE saturated colour — and an INK, which is what nobody had said (#501).
     It was #CF3F22, calibrated as a spot: 4.62 on `--paper` but 4.30 on
     `--shade` and 4.08 on `--hover`, so every string set in the app's one
     accent was a little short in column one and under the pointer — the same
     defect `--ink-2` had, on the same two grounds nobody measures, and it is a
     defect rather than #482's question because there is no hierarchy here to
     flatten: --match has one strength and it is used at it.
     #BD3A1F is the same hue and saturation (HSL 10.1 deg / .718) taken down
     four points of lightness, which is what clears `--hover`:

              light                paper/sheet/shade/hover
     --match  #BD3A1F  L* 44.2      5.35  5.54  4.98  4.72   #FF5C3D (dark, unchanged)
                                                             5.79  5.33  6.11  4.82

     It moves in ONE direction for both of its jobs, which is why it can be
     both and a --tt-* cannot: its mark form (`.toast.err`, `.btn.cfm-armed`)
     carries `--paper`, not `--on-tint`, so darker is better as ink AND better
     as a patch. Dark already cleared and does not move: brightening it further
     would take it past the coal-that-glows it is. */
  --match:#BD3A1F;      /* THE saturated colour, and an ink */

  /* the teletext seven, pastelised. one hue per target family. */
  --tt-cyan:#A8D6E2;    --tt-cyan-2:#8FC6D6;   /* time & structure   */
  --tt-red:#F7BEB0;     --tt-red-2:#EFA898;    /* titles             */
  --tt-yellow:#F1D68B;  --tt-yellow-2:#E8C46A; /* meta & flags       */
  --tt-green:#B9DCB2;   --tt-green-2:#A3CEC2;  /* descriptions       */
  --tt-magenta:#E7B6E1; --tt-magenta-2:#D9A2D6;/* credits            */
  --tt-blue:#B7C3F0;    --tt-blue-2:#A2B0E6;   /* origin & links     */
  --tt-stone:#DCDDD6;   --tt-stone-2:#CBCDC4;  /* consumed / cleaned */

  /* THE INK THAT GOES ON A TINT — one rule, one token (#478/#479).
     A --tt-* value is a MARK: a light patch laid on the page, in BOTH themes.
     The page's own ink scale is built for the page GROUND and inverts with the
     theme, so putting --ink / --ink-2 / --ink-3 on a mark works in light by
     luck and fails in dark by construction — measured at 4.2 : 1, 2.1 : 1,
     1.8 : 1 and 1.3 : 1 on the proposal card and the severity chip.
     So: anything painting a --tt-* background sets `color:var(--on-tint)`,
     and NOTHING on a tint reads from the ink scale. There is deliberately no
     --on-tint-2: a mark is small, and the room between this ink and the 4.5:1
     floor is not enough for a second grade that stays legible. Where a
     register wants to recede on a tint it recedes through the TINT (stone is
     the neutral for exactly that) or through type — size, weight, a strike —
     never through a paler ink. `tests/webapp/test_on_tint_ink.py` holds both
     halves: every tinted rule names this token, and this token clears 4.5 : 1
     against every --tt-* value in both palettes.

     A MARK IS NEVER INK — the converse, which nothing owned until #501. Four
     rules set a --tt-* value as `color` on the page: the two link-kind
     signets, the diamond before an external value, and the inheritance arrow
     on a field's level. Measured light, against paper/sheet/shade/hover:
     --tt-blue-2 2.05 / 2.12 / 1.91 / 1.81 and --tt-magenta-2 2.01 / 2.08 /
     1.87 / 1.77. Dark is NOT fine, which is how it survived being looked at:
     4.77 on `--paper` is the number the issue quotes, but the same values are
     4.38 on `--sheet` and 3.97 on `--hover`, and those are the grounds the
     links panel and a hovered row actually stand on.

     It is not a value that needs moving, because no value can serve both. To
     be a mark a tint must carry --on-tint at 4.5 : 1, which needs a relative
     luminance of at least .2133; to be ink on `--paper` it must be at most
     .1751, and on `--hover` at most .1487. The two bands are DISJOINT, so a
     hue that works as a patch cannot also work as a stroke, and "a darker ink
     variant of each mark hue" is a second palette — seven hues, two shades,
     two themes — maintained for three glyphs.

     So a --tt-* never appears in a `color`. Where a hue has to be present
     where text is, it is present as a mark BESIDE the text — the underline,
     the hairline, the chip — and the text itself takes the ink scale. Every
     one of the four had that mark already. */
  --on-tint:#15171B;

  --mono:"IBM Plex Mono",ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
  --disp:"Archivo","Helvetica Neue",Arial,sans-serif;
  --prose:"Newsreader",Georgia,"Times New Roman",serif;

  --mast-h:43px;
  --keys-h:30px;

  /* THE SOLID CONTROL: the fill of a pressed toggle and of a primary button,
     and the edge every `.btn` draws. In light mode it is the ink block on
     paper, which is what these rules always spelled. Dark mode restates all
     three, because an ink block on a dark ground is a bone-white slab — the
     brightest thing on the screen, brighter than the results it filters. */
  --solid:var(--ink);
  --on-solid:var(--paper);
  --solid-edge:var(--ink);
  --solid-mark:transparent; /* the pressed tab's underline; dark only */

  /* The page behind a dialog, dimmed (#792). It is the dialog's outset
     shadow (`.dlg` below), so it has no other value to keep. A colour, so
     dark mode restates it. */
  --scrim:rgba(21,23,27,.34);

  /* THE LAYER SCALE (#792). Every `z-index` in app.css and marketing.css
     names one of these, so the order between two layers is read here rather
     than reconstructed from numbers forty lines apart. The values are the
     ones the rules carried, with ONE change: a dialog and the page-surface
     wrapper both sat at 41, so which one painted on top was a fact about DOM
     order. A dialog is now above any page surface it opens over.
     The first four are LOCAL — each competes only inside its own scroller. */
  --z-behind:-1;     /* under its own box's content: the dialog's click shield */
  --z-sticky:2;      /* a sticky heading in a scroller: a page group's tabs */
  --z-sticky-day:4;  /* the listing's day divider, under the channel's */
  --z-sticky-head:5; /* a column head; the listing's channel divider */
  --z-stepbar:19;    /* the phone's step bar, one under the bars */
  --z-bar:20;        /* the masthead and the key row; the marketing header */
  --z-bar-pinned:30; /* the masthead held over a page surface on a phone (#790) */
  --z-page:40;       /* a page surface's wrapper */
  --z-dialog:50;     /* a dialog, above the page surface it opens over */
  --z-signin:60;     /* the sign-in view, over all of the app */
  --z-toast:70;      /* a toast, over everything */

  /* THE TYPE SCALE (#788). Every `font-size` in app.css and marketing.css
     names a step, bar the reviewed exceptions in
     tests/webapp/test_frontend_type_scale.py. The steps are the sizes the
     rules already carried — the half-pixel ramp is this design's own
     (a mono UI set at 10/10.5/11/11.5/12), so nothing moved to make a
     rounder scale. Four one-rule sizes were folded into a neighbour, each
     by at most 1px: 15.5→15, 19→20, 21→20, 29→30.
     Numbered, not named by role: a step's number says its place in the
     ramp, and a size can be retuned without renaming every rule. */
  --fs-1:8px;    --fs-2:8.5px;  --fs-3:9px;    --fs-4:9.5px;  /* marks, chips, kickers */
  --fs-5:10px;   --fs-6:10.5px; --fs-7:11px;   --fs-8:11.5px; /* the UI's body: labels, rows */
  --fs-9:12px;   --fs-10:12.5px;--fs-11:13px;  --fs-12:14px;  /* fields, controls */
  --fs-13:14.5px;--fs-14:15px;  --fs-15:16px;                 /* prose, dialog heads */
  --fs-16:18px;  --fs-17:20px;  --fs-18:23px;  --fs-19:26px;  /* headings */
  --fs-20:30px;  --fs-21:35px;  --fs-22:42px;  --fs-23:56px;  /* display: titles, lede, the refusal code */

  /* THE SPACING SCALE (#788), for `padding`, `margin` and `gap`. A step is
     its number × 2px, so `--sp-4` is 8px and the name is never ambiguous.
     It holds the EVEN sizes the rules carried; a declaration was moved onto
     it only where every length in it is a step, so nothing changed size.
     The odd-pixel rhythm (3/5/7/9/11/13px — chip and row insets set
     optically) is still raw: folding it onto this grid is a visible change,
     and a design decision rather than a refactor. The guard in
     test_frontend_type_scale.py holds the raw count to a ratchet. */
  --sp-1:2px;  --sp-2:4px;  --sp-3:6px;  --sp-4:8px;  --sp-5:10px; --sp-6:12px;
  --sp-7:14px; --sp-8:16px; --sp-9:18px; --sp-10:20px; --sp-13:26px; --sp-17:34px;
}

/* ---------- dark: the same page, broadcast rather than printed ----------
   Teletext was never printed — it was a signal, read off a black screen in
   saturated primary colour. Light mode is this app as a critical EDITION,
   set on paper; dark is the same page as it looked on the screen that
   broadcast it. Same eight base tones, same seven families, same one red —
   only --match brightens (a coal that glows rather than ink that sits on
   paper) and the seven marks gain a little saturation and a little lift,
   because a pastel tuned for white paper goes muddy on a dark ground. They
   stay well short of full teletext saturation on purpose: that is the
   near-black-plus-one-neon-accent look this app moved away from, and seven
   neon hues at once would be worse, not better. */
:root[data-theme=dark]{
  --paper:#17181B;      /* the screen, not paper — but warm, not cold-black */
  --sheet:#1E2023;      /* reading surface, lifted one step off the screen  */
  --shade:#111214;      /* the finder's recessed ground, sunk one step down*/
  --hover:#26282B;
  --ink:#E9E7E1;        /* bone, inverted: the paper's own tone as text     */
  /* NOT an inversion of the light scale, and it could not be one: the dark
     palette's total range is narrower (`--ink` is 11.95 : 1 on `--hover` where
     the light `--ink` is 15.28 : 1), so the same three RATIOS would not fit and
     the same three LUMINANCES would be nonsense. What is carried across is the
     method — each grade set against `--hover`, the lightest dark ground, at the
     same ratios the light scale takes against its own darkest one — and the
     near-neutral chromaticity these greys already had, which is the dark
     palette's own and not the light one's cool blue-grey. #482. */
  --ink-2:#B4B5B7;
  --ink-3:#8F9091;
  --rule:#2B2D30;
  --rule-strong:#737475; /* 3 : 1 on every dark ground — see the light note */
  --match:#FF5C3D;      /* the same red, brightened to read off a screen   */

  --tt-cyan:#6BB9D1;    --tt-cyan-2:#4F9AB2;
  --tt-red:#D98D79;     --tt-red-2:#BC735F;
  --tt-yellow:#CDA753;  --tt-yellow-2:#AC873C;
  --tt-green:#77AD70;   --tt-green-2:#5F9459;
  /* Four values moved so the rule above can hold (#478/#479). They sat BELOW
     the band their siblings occupy — stone at a relative luminance of .15 and
     stone-2 at .09, against .24–.42 for the rest — which is another way of
     saying they had been built as GROUNDS rather than as marks, and a ground
     carries no mark's ink: nothing, light or dark, reached 4.5 : 1 on a
     mid-grey (#6B6D68 is 3.4 : 1 to near-black and 3.9 : 1 to near-white).
     Stone's recessiveness is carried where light mode already carries it — it
     is the one NEUTRAL among six chromatic marks, not the one closest to the
     ground. The `-2` shades keep their distance from their base: ΔL* is 8.8
     (stone), 5.9 (magenta) and 4.0 (blue) here against 5.7, 5.0 and 6.9 in the
     light palette. */
  --tt-magenta:#BC7DB2;  --tt-magenta-2:#AC6EA1;
  --tt-blue:#7C8AD6;    --tt-blue-2:#7280C8;
  --tt-stone:#A2A49C;   --tt-stone-2:#8B8D84;

  /* Dark too: a mark is light on either ground, so its ink does not move. */
  --on-tint:#15171B;

  /* The light scrim is a near-black at .34, and on a near-black page that is
     next to no dimming: it takes white paper from L* 100 to 72, but `--paper`
     here from L* 8.3 to 8.2 and `--sheet` from 12.2 to 10.7, so a dialog sat
     on an undimmed page (#792). Dimming a dark ground can only darken it and
     the room is small, so it takes black at .6: `--paper` to 2.7, `--sheet`
     to 3.6, and the page's `--ink` still reads through it at about L* 40. */
  --scrim:rgba(0,0,0,.6);

  /* A pressed tab and a primary button are a LIFTED grey with bone ink
     (8.9 : 1), not an inverted slab, and the pressed mode tab carries the
     one red underneath so the selection reads without the glare. A button's
     edge takes `--rule-strong`, the 3 : 1 grade, so an outlined `.btn.ghost`
     is no longer a hairline of full ink either. */
  --solid:#3A3C40;
  --on-solid:var(--ink);
  --solid-edge:var(--rule-strong);
  --solid-mark:var(--match);

  /* Native controls follow: a <select>'s list, the date input's calendar
     icon and the scrollbars are drawn dark instead of light-on-dark. */
  color-scheme:dark;
}

*,*::before,*::after{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--mono);font-size:var(--fs-11);line-height:1.45;
  -webkit-font-smoothing:antialiased;
}
a{color:inherit;text-decoration:none}
button{font-family:inherit;color:inherit}
:focus-visible{outline:2px solid var(--match);outline-offset:1px}
[hidden]{display:none!important}

/* ---------- shared atoms ---------- */
.eyebrow{
  font-family:var(--disp);font-variation-settings:"wdth" 125;
  font-weight:700;font-size:var(--fs-4);letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-3);
}
.prose{font-family:var(--prose);font-size:var(--fs-14);line-height:1.55;color:var(--ink)}
.num{font-variant-numeric:tabular-nums}
.dots{
  flex:0 1 30px;min-width:8px;align-self:flex-end;height:9px;
  background-image:radial-gradient(circle,var(--ink-3) .55px,transparent .55px);
  background-size:4px 4px;background-position:0 4px;background-repeat:repeat-x;
  opacity:.7;
}
.sr{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* ---------- masthead: the status line ---------- */
.mast{
  display:flex;align-items:baseline;gap:var(--sp-9);flex-wrap:wrap;
  padding:11px 18px 10px;border-bottom:1px solid var(--rule);
  background:var(--sheet);position:sticky;top:0;z-index:var(--z-bar);
}
.wordmark{
  font-family:var(--disp);font-variation-settings:"wdth" 125;
  font-weight:800;font-size:var(--fs-17);letter-spacing:.005em;line-height:1;
  color:var(--ink);white-space:nowrap;background:none;border:0;padding:0;cursor:pointer;
}
.wordmark em{font-style:normal;color:var(--match)}
.mast-scope{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
.mast-sep{color:var(--ink-3)}
.mast-fact{color:var(--ink-2);font-size:var(--fs-9);white-space:nowrap}
.mast-fact b{color:var(--ink);font-weight:500}
.mast-fact a{border-bottom:1px solid var(--rule)}
/* The selected channels are the one fact whose length is the DATA's: every
   parent channel is selected at boot, so the seven radio channels beside MDR
   and WDR, set on one unbreakable line, ran a 390px phone's document 91px
   wide. It wraps between names instead; the other facts stay whole. */
.mast-fact.chanfact{white-space:normal}
.mast-right{margin-left:auto;display:flex;align-items:baseline;gap:var(--sp-7)}
.mast-id{display:flex;align-items:center;gap:var(--sp-7)}
.coverage{font-size:var(--fs-9);color:var(--ink-2);white-space:nowrap}
.mast-client{max-width:12em}
body.as-client .mast-client{border-color:var(--match);color:var(--match)}
.coverage b{color:var(--match);font-weight:600}
/* The rule and report surfaces empty it (`renderMast`). On a phone it owns a
   full-width row of its own, so an empty one would hold that row's gap open. */
.coverage:empty{display:none}
.whoami{
  font-size:var(--fs-7);color:var(--ink-2);border:1px solid var(--rule);
  background:var(--sheet);padding:2px 7px;white-space:nowrap;cursor:pointer;
}
.whoami:hover{border-color:var(--ink)}
/* A button since #627 (it opens the reader's own account): the button's own
   line box and alignment are reset so the chip keeps the span's geometry. */
button.whoami{line-height:inherit;text-align:left}
.whoami:disabled{cursor:default}
.whoami:disabled:hover{border-color:var(--rule)}
.whoami b{font-weight:500;color:var(--ink)}
.whoami .sw{color:var(--match);margin-left:var(--sp-3)}
/* The masthead's controls — language, theme, sign out (#633, #632, #664). The
   marketing page's `.mk-tools` reset carried over: no chrome at all in either
   state, the glyph in --ink, --match on hover. The box is shorter than the
   marketing page's 28px square on purpose: `--mast-h` is a 43px constant other
   rules position against, and a 22px box keeps the row the height the chips
   gave it. `flex:none` so a narrow row wraps the identity box whole rather
   than squeezing a glyph. */
.mast-tool{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:24px;height:22px;background:none;border:0;padding:0;margin:0;
  color:var(--ink);cursor:pointer;
}
.mast-tool:hover{color:var(--match)}
.mast-tool:focus-visible{outline:2px solid var(--match);outline-offset:1px}
.mast-tool svg{display:block}
/* The language control is a CODE, not a glyph (#664): `EN`/`DE`, the language
   a click switches to, in the masthead's own mono at the identity chip's 11px,
   so it reads as the same row's text rather than as a third picture. Ink is
   `currentColor` like the icons beside it, so it greys with the theme and takes
   the same --match on hover. 600 weight so its stems sit near the icons'
   2px stroke; the tracking opens two capitals to about the icons' 16px. */
.mast-tool .lang-code{
  font-family:var(--mono);font-size:var(--fs-7);font-weight:600;line-height:1;
  letter-spacing:.06em;text-transform:uppercase;
}

/* ---------- the three columns ---------- */
.deck{
  display:grid;
  grid-template-columns:326px minmax(400px,1fr) minmax(430px,1.05fr);
  height:calc(100vh - var(--mast-h) - var(--keys-h));
}
.col{overflow-y:auto;overflow-x:hidden;min-width:0}
.col-1{background:var(--shade);border-right:1px solid var(--rule)}
.col-2{background:var(--paper);border-right:1px solid var(--rule)}
.col-3{background:var(--sheet)}
.col-head{
  position:sticky;top:0;z-index:var(--z-sticky-head);display:flex;align-items:baseline;gap:var(--sp-4);
  padding:9px 14px 8px;border-bottom:1px solid var(--rule);
  background:inherit;
}
.col-head .ord{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-4);color:var(--match);letter-spacing:.06em;
}
.col-head .hint{
  margin-left:auto;font-size:var(--fs-6);color:var(--ink-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* A column that has nothing to hold does not print. On the broadcast view a
   `view` render still has something true to show in column 03 — the
   broadcaster's own source text, which /api/broadcasts/{id}/raw serves at the
   view role. On an ENTITY there is no guest-shaped version of "which rules
   read this name, in whose grammar", so the column is removed outright and the
   deck closes over it.
   Behind the desktop query on purpose: `:has()` outspecifies the phone's
   one-column `.deck`, so left unguarded it would put a 326px sidebar back on a
   390px screen. */
@media (min-width:901px){
  .deck:not(:has(.col-3)),
  .deck:has(.col-3[hidden]){grid-template-columns:326px minmax(0,1fr)}
  .deck:not(:has(.col-3)) .col-2,
  .deck:has(.col-3[hidden]) .col-2{border-right:0}
}

/* ---------- 901–1279px: TWO columns at a time (#779) ----------
   Three columns need 326 + 400 + 430 = 1,156px, so between the phone layout
   and that width an open broadcast pushed the trace off the right edge and the
   whole page scrolled sideways (measured at 1024px: scrollWidth 1,156). The
   owner's ruling (2026-10-01): two columns, Browse + Inspect, with Trace
   reached through the existing step keys — the phone's own mechanism,
   `body[data-step]`, widened from one column per step to a two-column WINDOW
   that always holds the stepped one: steps 1 and 2 show Browse + Inspect,
   step 3 Inspect + Trace, so a value and the span that produced it are still
   on screen together. Where there is no third column (an entity, or a `view`
   render without one) step 3 has nothing to slide to and the window stays on
   Browse + Inspect. The step keys are the phone's `.key.nav`, shown here; a
   key is lit while its column is ON SCREEN, which in a two-column window is
   two of them. */
@media (min-width:901px) and (max-width:1279px){
  .deck{grid-template-columns:326px minmax(0,1fr)}
  .deck > .col-2{border-right:0}
  .deck > .col-3{display:none}
  body[data-step="3"] .deck:has(> .col-3:not([hidden])){
    grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  }
  body[data-step="3"] .deck:has(> .col-3:not([hidden])) > .col-1{display:none}
  body[data-step="3"] .deck:has(> .col-3:not([hidden])) > .col-2{border-right:1px solid var(--rule)}
  body[data-step="3"] .deck > .col-3:not([hidden]){display:block}
  .keys .key.nav.mobonly{display:flex}
  body:not([data-step="3"]) .keys .key.nav[data-step="1"],
  body:not([data-step="3"]) .keys .key.nav[data-step="2"],
  body[data-step="3"]:not(:has(#col-rules:not([hidden]))) .keys .key.nav[data-step="1"],
  body[data-step="3"]:not(:has(#col-rules:not([hidden]))) .keys .key.nav[data-step="2"],
  body[data-step="3"]:has(#col-rules:not([hidden])) .keys .key.nav[data-step="2"],
  body[data-step="3"]:has(#col-rules:not([hidden])) .keys .key.nav[data-step="3"]{
    background:var(--ink);color:var(--paper);
  }
  body[data-step="3"]:has(#col-rules:not([hidden])) .keys .key.nav[data-step="1"],
  body[data-step="3"]:not(:has(#col-rules:not([hidden]))) .keys .key.nav[data-step="3"]{
    background:transparent;color:var(--ink-2);
  }
}

/* ---------- column 1: the finder ---------- */
.filters{padding:var(--sp-5) var(--sp-7) var(--sp-6);border-bottom:1px solid var(--rule)}
/* The fold toggle is a full-width invisible button: it is a heading you can
   press, so it carries no chrome of its own and the caret below is the only
   affordance. (Its `style=` attribute moved here under #572 — see the CSP.) */
#fbToggle{
  width:100%;text-align:left;background:none;border:0;padding:0 0 7px;cursor:pointer;
}
.modes{display:flex;border:1px solid var(--rule);background:var(--sheet);margin-bottom:9px}
.modes button{
  flex:1;text-align:center;padding:5px 0;font:inherit;font-size:var(--fs-7);color:var(--ink-2);
  border:0;border-right:1px solid var(--rule);background:transparent;cursor:pointer;
}
.modes button:last-child{border-right:0}
.modes button[aria-pressed="true"]{background:var(--solid);color:var(--on-solid);
  box-shadow:inset 0 -2px 0 var(--solid-mark)}
.grepbar{display:flex;align-items:center;border:1px solid var(--ink);background:var(--sheet);padding:0 7px}
.grepbar .sigil{color:var(--match);font-weight:600;padding-right:var(--sp-3)}
.grepbar input{
  flex:1;border:0;outline:0;background:transparent;font:inherit;color:var(--ink);
  padding:5px 0;min-width:0;
}
.grepbar .caret{width:7px;height:14px;background:var(--match);opacity:.85}
/* The input drops its own outline for the bar's, and the blinking caret was
   the only other cue — which `prefers-reduced-motion` stops. So the BAR wears
   the app's focus ring while anything in it has focus (#781). */
.grepbar:focus-within{outline:2px solid var(--match);outline-offset:1px}
.grepbar:focus-within .caret{animation:blink 1.1s steps(1) infinite}
@keyframes blink{50%{opacity:.15}}
.scope-row{display:flex;gap:var(--sp-3);margin-top:var(--sp-4);flex-wrap:wrap;align-items:center}
.pill{
  font:inherit;font-size:var(--fs-7);padding:2.5px 8px;border:1px solid var(--rule-strong);
  background:var(--sheet);color:var(--ink-2);white-space:nowrap;cursor:pointer;
}
.pill[aria-pressed="true"]{border-color:var(--ink);color:var(--ink)}
.pill .x{color:var(--ink-3);margin-left:5px}
.infield{
  font:inherit;font-size:var(--fs-7);color:var(--ink-2);white-space:nowrap;
  background:none;border:0;padding:0;cursor:pointer;
}
.infield b{color:var(--ink);font-weight:500;border-bottom:1.5px solid var(--tt-yellow)}

/* ---------- picker buttons + panels ---------- */
.pickers{display:flex;flex-direction:column;gap:var(--sp-2);margin-top:var(--sp-4)}
.picker-btn{
  flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--sp-3);cursor:pointer;
  font:inherit;font-size:var(--fs-7);text-align:left;color:var(--ink);
  border:1px solid var(--rule);background:var(--sheet);padding:4px 7px;
}
.picker-btn:hover{border-color:var(--ink-3)}
.picker-btn[aria-expanded="true"]{border-color:var(--ink);background:var(--shade)}
.picker-btn .pk-lb{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:700;
  font-size:var(--fs-2);letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);
  flex:0 0 auto;
}
.picker-btn .pk-v{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.picker-btn .pk-caret{flex:0 0 auto;color:var(--match);font-size:var(--fs-3)}
.picker-btn[aria-expanded="true"] .pk-caret{transform:rotate(180deg);display:inline-block}

.panel{border:1px solid var(--ink);background:var(--sheet);margin-top:var(--sp-3);padding:8px 9px 9px}
.panel-hd{display:flex;align-items:baseline;gap:var(--sp-4);padding-bottom:5px;margin-bottom:5px;border-bottom:1px solid var(--rule)}
.panel-hd .n{margin-left:auto;font-size:var(--fs-5);color:var(--ink-3)}
/* A panel's own filter box sits above a list rather than in a form, so it is a
   size down from `.inp`; and the footer button that clears or fills the whole
   list is the same underlined word `.chfam .all` uses one level in. The child
   combinator is deliberate — the `.all` buttons INSIDE `.chsel` are the family
   controls and keep their own rule. */
.panel > .inp.sm{font-size:var(--fs-7);padding:3px 6px;margin-bottom:5px}
.panel > .all{
  margin-top:var(--sp-3);font-size:var(--fs-5);color:var(--ink-2);background:0;
  border:0;border-bottom:1px solid var(--rule);cursor:pointer;padding:0;
}

/* ---------- the date selector: a month, gathered into program weeks ----------
   A month grid, but the ROW is the unit: each one is a program week, and its PW
   number sits in the gutter as a chip you can press. Pressing the chip takes
   the week; pressing a cell takes the day. That is the app's own model rather
   than a calendar bolted onto it — GET /api/weeks hands back each row's seven
   dates precisely so the grid can draw the days /api/dates never mentions,
   dimmed, and a selected week fills only the cells it actually holds a listing
   for.  Numbering travels with the week start: PW nn is the ISO week of the
   row's THURSDAY, which is why a Saturday-start PW 36 opens on 29.08.        */
.panel.cal{padding:0}
.cal-bar{
  display:flex;align-items:center;gap:var(--sp-4);padding:7px 9px 6px;
  border-bottom:1px solid var(--rule);
}
.cal-bar .eyebrow{flex:1}
.cal-x{
  font:inherit;font-size:var(--fs-12);line-height:1;color:var(--ink-2);cursor:pointer;
  background:transparent;border:1px solid var(--rule);padding:1px 6px 3px;
}
.cal-x:hover{border-color:var(--ink);color:var(--ink)}
.cal-nav{display:flex;align-items:center;gap:var(--sp-4);padding:8px 9px 6px}
.cal-arw{
  font:inherit;font-size:var(--fs-11);line-height:1;color:var(--ink);cursor:pointer;
  background:var(--sheet);border:1px solid var(--rule);padding:var(--sp-1) var(--sp-4) var(--sp-2);
}
.cal-arw:hover{border-color:var(--ink)}
.cal-arw[disabled]{color:var(--ink-3);cursor:default;border-color:var(--rule)}
.cal-titles{
  flex:1;text-align:center;
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-12);letter-spacing:-.004em;
}
.cal-month{padding:0 9px}
.cal-head,.cal-row{display:grid;grid-template-columns:32px repeat(7,1fr);gap:var(--sp-1)}
.cal-head{padding-bottom:3px}
.cal-dw{
  text-align:center;font-size:var(--fs-2);letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-3);
}
.cal-row{margin-bottom:var(--sp-1)}
.pw-chip{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:0;cursor:pointer;font:inherit;padding:2px 0 3px;
  background:var(--sheet);border:1px solid var(--rule);
}
.pw-chip .pw-k{font-size:7px;letter-spacing:.1em;color:var(--ink-3);line-height:1.1}
.pw-chip .pw-n{font-size:var(--fs-9);line-height:1.1;color:var(--ink-2)}
.pw-chip:hover{border-color:var(--ink-3)}
/* the one saturated colour, spent on the selected week and nowhere else here */
.cal-row.on .pw-chip{border-color:var(--match)}
.cal-row.on .pw-chip .pw-n{color:var(--match)}
/* Same multiply, same removal (#501): 3.27 : 1 light / 3.53 : 1 dark. The
   key stays a step behind its number the way `.ap-pat` stays behind `.ap-out`
   — 7 px against 12 px, and a letter-spacing of its own. */
.cal-row.on .pw-chip .pw-k{color:var(--match)}

.cal-day{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:0;cursor:pointer;font:inherit;padding:3px 0 4px;
  background:var(--paper);border:1px solid transparent;
}
.cal-day .dd{font-size:var(--fs-8);line-height:1.15;color:var(--ink)}
.cal-day .dn{font-size:var(--fs-1);line-height:1.15;color:var(--ink-3)}
.cal-day:hover{border-color:var(--ink-3)}
.cal-day.out .dd{color:var(--ink-3)}       /* a day the neighbouring month owns */
.cal-day.void{cursor:default}               /* a day we hold no listing for */
/* It recedes by GRADE, not by opacity (#782): the numeral takes the third
   grey and the count line already says `·` where a held day says a number. */
.cal-day.void .dd{color:var(--ink-3)}
.cal-day.void:hover{border-color:transparent}
/* teletext said things with solid blocks, and so does this */
.cal-row.on .cal-day,.cal-day.on{background:var(--ink);border-color:var(--ink)}
.cal-row.on .cal-day .dd,.cal-row.on .cal-day .dn,
.cal-day.on .dd,.cal-day.on .dn{color:var(--paper)}
/* a selected week fills only the days it HOLDS something for: filling the
   empty ones too would claim seven days of listings */
.cal-row.on .cal-day.void{background:var(--shade);border-color:var(--rule)}
.cal-row.on .cal-day.void .dd,.cal-row.on .cal-day.void .dn{color:var(--ink-3)}
/* a neighbouring month's day inside a selected week: the fill takes the next
   grade down rather than its numeral being multiplied (#782) */
.cal-row.on .cal-day.out:not(.void){background:var(--ink-2);border-color:var(--ink-2)}

.cal-key{
  display:flex;flex-wrap:wrap;gap:3px 11px;padding:8px 9px 9px;margin-top:var(--sp-2);
  border-top:1px solid var(--rule);font-size:var(--fs-5);color:var(--ink-2);
}
.cal-key span{display:flex;align-items:center;gap:5px;white-space:nowrap}
.cal-key i{width:9px;height:9px;flex:0 0 9px;display:block}
.cal-key .sw-day{background:var(--ink)}
.cal-key .sw-pw{background:transparent;border:1px solid var(--match)}

/* ---------- the channel multi-select ----------
   Not pills: every channel in its family, and the families are the point. A
   mono check box keeps it in the machine's own voice.                       */
.chsel{max-height:230px;overflow:auto;margin:0 -3px}
.chpick{
  display:flex;align-items:baseline;gap:7px;width:100%;cursor:pointer;
  font:inherit;font-size:var(--fs-8);color:var(--ink);text-align:left;
  border:0;background:transparent;padding:2.5px 3px;
}
.chpick:hover{background:var(--shade)}
.chpick .bx{flex:0 0 auto;color:var(--ink-3);font-size:var(--fs-7)}
.chpick[aria-pressed="true"] .bx{color:var(--match)}
.chpick .nm{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chpick[aria-pressed="true"] .nm{font-weight:500}
.chpick .ct{flex:0 0 auto;font-size:var(--fs-5);color:var(--ink-3);font-variant-numeric:tabular-nums}
.chpick.kid{padding-left:17px}
.chpick.kid .nm{font-size:var(--fs-7);color:var(--ink-2)}
.chfam{display:flex;align-items:baseline;gap:var(--sp-4);padding:7px 3px 3px;border-top:1px solid var(--rule);margin-top:var(--sp-2)}
.chfam:first-child{border-top:0;margin-top:0;padding-top:var(--sp-1)}
.chfam .all{
  margin-left:auto;font-family:inherit;font-size:var(--fs-5);color:var(--ink-2);
  border:0;border-bottom:1px solid var(--rule);cursor:pointer;background:0;padding:0;
}
/* The pin sits after the sub-feed control, so `margin-left:auto` on the first
   of them is what pushes the pair right as a block. */
.chfam .all + .all{margin-left:9px}
/* The field picker reuses `.chfam` as its group heading, where the right-hand
   slot is a key COUNT rather than a button. */
.chfam .ct{margin-left:auto;font-size:var(--fs-5);color:var(--ink-3)}
/* A group too large to enumerate offers a free-typed key instead of a list; the
   row is indented to the depth of the `.chpick.kid` rows it stands in for. */
.fldmore{padding:3px 3px 6px 17px}
.fldmore .inp{font-size:var(--fs-7);padding:2px 5px}

/* The two axes of the picker (issue #149): the check box is the SELECTION, the
   `hide` button is a PREFERENCE. Two buttons side by side rather than one
   nested in the other — a button inside a button is not markup, and these are
   two different actions on one row. */
.chrow{display:flex;align-items:baseline;gap:var(--sp-2)}
.chrow .chpick{flex:1 1 auto;min-width:0}
.chhide{
  flex:0 0 auto;font-family:inherit;font-size:var(--fs-5);color:var(--ink-3);
  border:0;background:0;cursor:pointer;padding:0 3px;opacity:0;
}
.chrow:hover .chhide,.chhide:focus-visible{opacity:1}
/* A hidden channel still on screen — the disclosure is open, or it is selected
   and the picker must not hide a row the listing is showing. Dimmed, never
   struck through: it is a channel this account may perfectly well see. */
.chpick.pref-off .nm{color:var(--ink-3)}
.chrow.pref-off .chhide{opacity:1}
.chfam.chhid{color:var(--ink-3)}
/* A channel GROUP (issue #896) — TV / Radio — one level above the families:
   a heavier rule and full-strength ink, so it reads as a section, not one more
   family. Its `all / none` is the family button's underlined word. */
.chgrp{display:flex;align-items:baseline;gap:var(--sp-4);padding:var(--sp-5) var(--sp-1) var(--sp-1);
  border-top:2px solid var(--ink-3);margin-top:var(--sp-3)}
.chgrp:first-child{border-top:0;margin-top:0;padding-top:var(--sp-1)}
.chgrp .eyebrow{color:var(--ink)}
.chgrp .all{
  margin-left:auto;font-family:inherit;font-size:var(--fs-5);color:var(--ink-2);
  border:0;border-bottom:1px solid var(--rule);cursor:pointer;background:0;padding:0;
}
.chgrp + .chfam{border-top:0;margin-top:0}
.chfam.chhid .all{margin-left:auto}

/* ---------- the row-style switch ---------- */
/* The switch sits at the right of its row — unless it IS the row, which is
   what a `show_diagnostics: false` account sees once the two chips beside it
   are gone. */
.viewsw{display:flex;margin-left:auto;border:1px solid var(--rule);background:var(--sheet)}
.scope-row > .viewsw:first-child{margin-left:0}
.viewsw button{
  font:inherit;font-size:var(--fs-5);letter-spacing:.04em;padding:var(--sp-1) var(--sp-4);cursor:pointer;
  border:0;border-right:1px solid var(--rule);background:transparent;color:var(--ink-2);
}
.viewsw button:last-child{border-right:0}
.viewsw button[aria-pressed="true"]{background:var(--solid);color:var(--on-solid)}

/* ---------- channel-grouped rows ----------
   Two broadcasters are never one feed. The channel is the outer gathering and
   the date the inner one — parallel logs, which is also why the per-row
   MDR/WDR badge is gone: the section already said it.                       */
.chbreak{
  display:flex;align-items:baseline;gap:var(--sp-4);padding:15px 14px 5px;
  position:sticky;top:33px;background:var(--shade);z-index:var(--z-sticky-head);
}
.chbreak .d{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-11);letter-spacing:.01em;
}
.chbreak .n{margin-left:auto;font-size:var(--fs-5);color:var(--ink-3);font-variant-numeric:tabular-nums}
.chbreak::after{content:"";position:absolute;left:14px;right:14px;bottom:0;height:2px;background:var(--ink)}
.chgroup:first-of-type .chbreak{padding-top:var(--sp-5)}

.daybreak{
  display:flex;align-items:baseline;gap:var(--sp-4);padding:13px 14px 4px;
  position:sticky;top:33px;background:var(--shade);z-index:var(--z-sticky-day);
}
.chgroup .daybreak{top:66px}
.daybreak .d{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-9);letter-spacing:.03em;
}
.daybreak .n{margin-left:auto;font-size:var(--fs-6);color:var(--ink-3)}
.daybreak::after{content:"";position:absolute;left:14px;right:14px;bottom:0;height:1px;background:var(--ink);opacity:.16}

/* The step arrows a divider wears. They sit AFTER the count, which keeps its
   `margin-left:auto` and so still reads as the right-hand end of the line.
   A disabled arrow is left in place rather than hidden: on a range of days
   every divider carries the pair and only the two at the ends are live, and
   that is the control saying where the range ends — a pair that appeared and
   vanished as the reader scrolled would say nothing at all. */
.brk-step{display:flex;flex:0 0 auto;margin-left:5px;align-self:center}
/* Borderless: two boxes on a divider are two more boxes on a page made of
   rules, and column 1 is 326px — a bordered pair pushed `MDR Fernsehen` onto
   a second line. The glyph alone is enough here, because the line it sits on
   already reads as one control's worth of chrome. */
.brk-a{
  width:17px;height:18px;font:inherit;font-size:var(--fs-11);line-height:1;
  color:var(--ink-2);background:none;border:0;cursor:pointer;padding:0;
}
.brk-a:hover:not(:disabled){color:var(--match);background:var(--shade)}
.brk-a:disabled{color:var(--rule-strong);cursor:default}
/* The name keeps its line; the count is what gives way when the column is
   narrow, because a truncated channel is unrecognisable and a truncated tally
   is still a tally. */
.chbreak .d,.daybreak .d{white-space:nowrap}
.chbreak .n,.daybreak .n{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.row{
  /* The time column, declared once and read twice: by the time itself and by
     the Obertitel, which pads itself into the title's column so it sits over
     the title rather than over the whole row. `HH.MM` is tabular and always
     five characters, so a constant can hold it — but the constant is what the
     BAND is indented by, so the time's box must not grow past it or the two
     stop agreeing. `min-width:0` below is what stops it: a flex item's
     `min-width:auto` is its CONTENT width, so a face that sets `00.25` wider
     than this pushed the title right while the Obertitel stayed put. It read
     37.5px here and 40px on CI, which is exactly the 2.5px the two were out
     by. 40px is the widest measured; the text may spill into the 8px gap on
     something wider still, and the columns stay true. */
  --time-col:40px;
  display:flex;align-items:baseline;gap:var(--sp-4);padding:var(--sp-2) var(--sp-7);width:100%;
  border-left:2px solid transparent;text-align:left;font:inherit;
  background:transparent;border-top:0;border-right:0;border-bottom:0;cursor:pointer;
}
.row:hover{background:var(--hover)}
/* The row the record beside it is showing. Keyed on `aria-current`, which is
   what `rowHtml` writes and what `openEntity` re-stamps — the `.on` class this
   replaced was written by nothing, so the listing never marked the open slot
   (#397). One state, said once, in the attribute a screen reader also reads. */
.row[aria-current="true"]{background:var(--sheet);border-left-color:var(--match)}
/* Set against the RIGHT of the column, for every row (#527). The gap a reader
   sees between a time and its title is then the row's 8px flex gap and nothing
   else — the same 8px for a frame's clock, a segment's clock and a `then`, in
   both registers and at both widths. Left-set, it was 8px PLUS whatever the
   column had left over after `HH.MM`, and that leftover is the whole of #527:
   a frame read 10.48px to its title where the segment directly under it read
   8px, with the titles themselves in one column. See the group note below for
   what this costs the indent, which is the same number measured from the other
   end. Overflow is unaffected: a face wider than the column spills to the
   RIGHT under either alignment (measured), so the note on `--time-col` above
   still holds. */
.row-time{flex:0 0 var(--time-col);min-width:0;color:var(--ink-2);
  font-variant-numeric:tabular-nums;font-size:var(--fs-10);text-align:right}
.row[aria-current="true"] .row-time{color:var(--ink)}
/* A sub-broadcast's position marker stands where a clock value would (#125).
   Italic and a size down so a column of times still scans as a column of
   times — the word is a label, not a reading. The third instrument was
   `opacity: .75`, which composited `--ink-2` back to 2.7 : 1 on a hovered row;
   it is the next GRADE now, chosen rather than multiplied (#482). */
.row-time.pos{font-style:italic;font-size:var(--fs-7);color:var(--ink-3)}
/* A slot and the segments billed inside it, read as ONE group (#471).
   `MDR um 4` bills three segments that keep their own printed times, and those
   times used to be set exactly as a slot's — so a 105-minute frame and its
   inside read as four consecutive programmes. Three things do the grouping,
   and a fourth is deliberately NOT used: a left border down the group would
   collide with the selection indicator, which is that border (owner, #471).

   1. The FACE. A segment's time takes the marker's own italic size-down,
      whether it is a clock value or a word, so weight alone separates a frame
      from its inside.
   2. The INNER COLUMN — and this clause is CORRECTED by #527. It used to read
      "set against the RIGHT of the time column, which indents it under the
      frame's time — the times of a group line up with each other and with
      nothing else on screen", and the right alignment was this rule's own.
      That made a segment's time the only one in the listing ending at the
      column's edge, so the gap from a time to its title was 8px on a segment
      and 8px + the column's leftover on a frame: `16.00 MDR um 4` and
      `16.00 Neues von hier`, one directly above the other, stood 2.48px apart
      from their titles (measured, both registers, both widths — there is no
      media query and no second `--time-col`; the whole of it is 40px of column
      against 37.52px of ink at 12.5px IBM Plex Mono).

      The indent and the defect are ONE number. A segment's time starts 7px
      right of its frame's: 2.48px of that is the leftover the frame's left
      alignment wasted, and 4.52px is decision 1's size-down. Removing the
      first is removing the second's cost, and it cannot be kept selectively:
      with a fixed column, equal trailing gaps require a shared trailing edge.
      The leftover is not a designed indent either — it is whatever the face
      happens to leave, and the note on `--time-col` above records it measured
      at 0 on CI, where this group would have had no inner column at all and
      no defect either, because they are the same quantity.

      So the alignment moved UP to `.row-time` (#527) and the inner column is
      now decision 1's doing alone: 4.52px, the difference between a 12.5px
      clock and an 11px one, ending on a shared edge. A group's times still
      indent under their frame and still line up with each other; what is given
      up is that they line up with nothing else, which was worth 2.48px of
      leftover and was zero in the environment that measured it.
   3. PROXIMITY. The rows inside a group sit 2px apart against the 8px between
      slots. The group's outer edges keep the row's own 4px, so the gap to the
      slot above and below is unchanged: the segments move toward their frame,
      the frame does not move toward its neighbours. */
.row.seg .row-time{font-style:italic;font-size:var(--fs-7);color:var(--ink-3)}
.slotgroup .row{padding-top:1px;padding-bottom:1px}
.slotgroup .row:first-child{padding-top:var(--sp-2)}
.slotgroup .row:last-child{padding-bottom:var(--sp-2)}
.row-ch{
  font-size:var(--fs-3);letter-spacing:.08em;text-transform:uppercase;padding:1px 4px;
  color:var(--ink-2);border:1px solid var(--rule);background:var(--sheet);
}
/* The TITLE'S own type, set here and not only on `.row-title`: in COMPACT the
   title is an inline-block in this block box, so the line it sits in is as
   tall as THIS element's strut, and it is bottom-aligned within it (see
   below). Inheriting the listing's 13px/1.45 made that strut 0.65px taller
   than the title, which sat the title 1.6px lower in compact than in extended
   — the row box and the time did not move, the words inside them did. Matching
   the strut to the title makes bottom-aligning it a no-op. */
.row-main{flex:1 1 0;min-width:0;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;font-size:var(--fs-10);line-height:1.3}
/* One line-height for both row styles: see the `.ext` note below — extended
   adds LINES, never air, so a row with nothing extra to show must be the same
   box in both. */
.row-title{font-size:var(--fs-10);line-height:1.3}
/* In COMPACT the row is one line, so the title clips itself rather than
   relying on the parent: an inline span's BOX is as wide as its text even when
   an ancestor hides the overflow, which is how a 60-character programme title
   pushed a 390px phone into a horizontal scroll. */
[data-rows="compact"] .row-title{
  display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;
  vertical-align:bottom;
}
.row[aria-current="true"] .row-title{font-weight:500}
/* The flags close the row: `rowHtml` writes them AFTER the occasional marks
   (`WH`, `WH?`, the dagger), so their right edge is the row's padding on every
   row and the flag group lines up down the list whether or not a row is
   marked (#630). `flex:none` says what that relies on — the group never
   shrinks; `.row-main` (flex-basis 0, min-width 0) is what gives way. */
.row-flags{display:flex;flex:none;gap:3px;white-space:nowrap}
.row-flags i{
  font-style:normal;font-size:var(--fs-1);letter-spacing:.05em;color:var(--ink-2);
  border-bottom:2px solid var(--tt-yellow);padding:0 1px;
}
.row-mark{font-size:var(--fs-5);color:var(--ink-3);white-space:nowrap}
.row-mark.rep{color:var(--ink-2);border-bottom:2px solid var(--tt-blue);font-size:var(--fs-4);letter-spacing:.04em}
/* THREE repeat states, two marks, and the difference has to be visible without
   a tooltip (#513). `WH` is a resolved link — a solid rule under it, and it
   OPENS the airing it repeats, so it takes the pointer. `WH?` is the listing's
   claim with nothing we could resolve behind it (#414's expected class): the
   same word, a question mark, and a DOTTED rule in the muted ink — an
   unfinished version of the same mark rather than a different one, because it
   is the same fact at a lower confidence. It is not interactive, and it must
   not look it: there is no anchor to open. */
.row-mark.rep[data-anchor]{cursor:pointer}
.row-mark.rep[data-anchor]:hover{color:var(--ink)}
.row-mark.rep.unres{
  color:var(--ink-3);border-bottom:1px dotted var(--ink-3);cursor:default;
}
/* the dagger: a critical edition marks a corrupt reading with one. So do we. */
.row-mark.bad{color:var(--match);font-size:var(--fs-9);line-height:1}
/* "As client X" (#903): what one company's client rules did. A dropped
   airing stays listed for us, struck through; a changed one gets a pencil. */
.row-mark.client-drop{color:var(--match);font-size:var(--fs-9);letter-spacing:.06em}
.row-mark.client-chg{color:var(--match);font-size:var(--fs-9);line-height:1}
.row.client-dropped .row-title{text-decoration:line-through;color:var(--ink-2)}
/* A slot the broadcaster dropped from its listing (#999): the client-dropped
   strike, on the time too, and the note the inspect pane opens with. */
.row.removed .row-title,.row.removed .row-time{text-decoration:line-through;color:var(--ink-2)}
.row-mark.gone{color:var(--ink-2)}
/* The priority badge (#1108): a number in a box, edit+ only. The tiers are a
   reading aid; the number is the fact, so the colours stay quiet. */
.row-mark.prio{font-variant-numeric:tabular-nums;min-width:2.2em;text-align:center;
  border:1px solid var(--rule);border-radius:3px;padding:0 .25em;color:var(--ink-2)}
.row-mark.prio.hi{border-color:var(--match);color:var(--ink)}
.row-mark.prio.lo{color:var(--ink-3)}
/* On the record the badge is a button over its breakdown (#1236): a small
   panel under the number, one part per line, opened by a tap or click and,
   where the pointer can hover, by hovering — both through app.js's one
   `data-panel` open/close, so `hidden` is always what is shown. The panel
   starts flush at the line's foot (no margin): the pointer crosses into it
   without leaving the hover wrapper. */
.rec-prio{position:relative;margin:.25em 0 0}
.rec-prio > .row-mark.prio{line-height:inherit;background:none;cursor:pointer}
@media (pointer:coarse){.rec-prio > .row-mark.prio{min-height:24px;min-width:2.75em}}
.prio-pop{position:absolute;left:0;top:100%;z-index:calc(var(--z-sticky-head) + 1);margin-top:0;display:flex;
  flex-direction:column;gap:var(--sp-1);width:max-content;max-width:min(20rem,calc(100vw - 32px));
  font-size:var(--fs-5);color:var(--ink-2)}
.removed-note{font-size:var(--fs-9);color:var(--ink-2);margin:0 0 var(--sp-4)}
.f.client-changed .f-v{box-shadow:inset 2px 0 0 var(--match);padding-left:var(--sp-3)}
.client-change{font-size:var(--fs-9);color:var(--ink-2);margin-top:var(--sp-1)}
.client-dropped-note{font-size:var(--fs-9);color:var(--match);margin:0 0 var(--sp-4)}
.tail{padding:var(--sp-7);color:var(--ink-3);font-size:var(--fs-7);text-align:center}
.tail button{
  font:inherit;font-size:var(--fs-7);color:var(--ink-2);background:none;
  border:0;border-bottom:1px solid var(--rule);cursor:pointer;padding:0;
}

/* the extended row: four more registers, each in its own voice.

   EXTENDED ADDS LINES, NOT AIR. The two styles used to differ in their chrome
   as well as their content — 6px of row padding against 4px, `flex-start`
   against `baseline`, a `line-height` set on one side only, and a
   compensating `margin-top:1px` on the time and the flags — so a broadcast
   with nothing extra to show, which is most of them, still moved when the
   style was switched: 3.3px taller, its time 3px down, its flags 3px up.
   Toggling the style is something a reader does WHILE READING, to see whether
   a slot they are looking at has more to it, and everything on screen shifting
   under them is the cost. So the row's geometry is now the base rule's in both
   styles, and the only difference is whether the `.ext` lines are there.
   Their own leading (the column `gap`, `.row-ep`'s `margin-top`) is what gives
   a multi-line row its room — air that arrives with the lines that need it. */
[data-rows="compact"] .ext{display:none}
/* ...unless the row is a TWIN (#513). A row whose compact line is
   indistinguishable from a sibling's — `incl. Weissensee` printed for episodes
   7 and 8 — is rendered WITHOUT the `ext` class on its sub-title, its serial
   numbering and its episode/topic, so the three registers that tell the pair
   apart survive the compact style. Measured: 16 rows in 8 groups on the 21748
   corpus, every one of them distinguishable.
   They stay INLINE here, where extended gives `.row-ep` a block of its own:
   `.row-main` is `nowrap` + `overflow:hidden` in compact, so the line CLIPS
   rather than wraps, which is the property that makes this safe — a compact
   row that grew a second line would move every row under the reader. */
[data-rows="compact"] .row-sub:not(.ext),
[data-rows="compact"] .row-ep:not(.ext){color:var(--ink-2);font-size:var(--fs-8)}
[data-rows="compact"] .row-ep:not(.ext)::before{content:" · "}
[data-rows="compact"] .row-no:not(.ext){
  font-size:var(--fs-6);color:var(--ink-2);font-variant-numeric:tabular-nums;
  margin-left:5px;white-space:nowrap;
}
[data-rows="extended"] .row-main{display:flex;flex-direction:column;gap:1px;white-space:normal;overflow:visible;text-overflow:clip}
[data-rows="extended"] .ext{display:inline}
/* The Obertitel: above the title, and only over the title. A line of its own
   at the head of the row (`order:-1` puts it before the time, `flex-basis:100%`
   makes it fill the line so everything else wraps under it), padded into the
   title's column so the time and the flags keep the TITLE's line. They used to
   keep this one, which read as `07.35 Kissenkino` — the strand where the title
   belongs. `border-box` is global, so the padding comes out of the 100% rather
   than adding to it. */
/* `row-gap:1px` — the leading the Obertitel had while it was a line inside
   `.row-main`. The row's 8px is a COLUMN gap between the time, the title and
   the flags; inherited as a row gap it left the strand sitting exactly midway
   between its own title and the row above's, belonging to neither. */
[data-rows="extended"] .row{flex-wrap:wrap;row-gap:1px}
[data-rows="extended"] .row-band{
  order:-1;flex:0 0 100%;padding-left:calc(var(--time-col) + var(--sp-4));
  font-family:var(--prose);font-style:italic;font-size:var(--fs-8);
  color:var(--ink-2);line-height:1.25;
}
[data-rows="extended"] .row-sub{color:var(--ink-2);font-weight:400}
[data-rows="extended"] .row-ep{
  display:block;font-size:var(--fs-7);color:var(--ink-2);line-height:1.35;
  border-left:2px solid var(--tt-red);padding-left:var(--sp-3);margin-top:var(--sp-1);
}
[data-rows="extended"] .row-no{
  font-size:var(--fs-6);color:var(--ink-2);font-variant-numeric:tabular-nums;
  margin-left:5px;white-space:nowrap;
}
[data-rows="extended"] .dots{display:none}

/* ---------- the expert search (issue #655) ----------
   Column 1's search mode. The builder sits in the filter bar where the grep
   and the pickers sit in the other modes, and uses their parts: `.inp` boxes a
   size down, `.pill` toggles, `.btn`. A clause is two lines — WHAT (field, NOT,
   remove) over HOW (operator, value) — because one line of four controls does
   not fit a 326px column, and a phone is narrower still. */
.qb{display:flex;flex-direction:column;gap:0}
.qclause{border:1px solid var(--rule);background:var(--sheet);padding:6px 7px;
  display:flex;flex-direction:column;gap:5px}
.qclause[aria-invalid="true"]{border-color:var(--match)}
.qc-line{display:flex;gap:5px;align-items:center;flex-wrap:wrap;min-width:0}
.qclause .inp.sm{font-size:var(--fs-8);padding:3px 5px;width:auto;min-width:0}
.qc-field{flex:1 1 0}
/* #689: a group heading the reader can choose ("any field of this group") */
.qc-field option.qc-grp{font-weight:600}
.qc-op{flex:0 0 auto;max-width:48%}
.qc-line > .qc-in{flex:1 1 0}
.qc-not{padding:2px 7px}
.qc-not[aria-pressed="true"]{background:var(--solid);color:var(--on-solid);border-color:var(--solid-edge)}
.qc-x{font:inherit;font-size:var(--fs-14);line-height:1;color:var(--ink-3);background:none;
  border:0;padding:0 var(--sp-1);cursor:pointer}
.qc-x:hover{color:var(--ink)}
.qc-pair{flex:1 1 0;display:flex;gap:5px;align-items:center;min-width:0}
.qc-pair .inp.sm{flex:1 1 0}
.qc-and,.qb-join{font-size:var(--fs-5);color:var(--ink-3)}
.qb-join{padding:3px 0 3px 8px;letter-spacing:.06em}
.qc-enum{display:flex;flex-wrap:wrap;gap:var(--sp-2);flex:1 1 100%}
.qc-tog{padding:1.5px 6px;font-size:var(--fs-6)}
.qc-tog[aria-pressed="true"]{background:var(--solid);color:var(--on-solid);border-color:var(--solid-edge)}
.qc-chips{flex:1 1 100%;display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;min-width:0}
.qc-chip{display:inline-flex;align-items:baseline;gap:3px;font-size:var(--fs-7);
  padding:1px 3px 1px 6px;border:1px solid var(--rule);background:var(--shade);
  border-bottom:2px solid var(--tt-magenta);max-width:100%;overflow-wrap:anywhere}
.qc-unchip{font:inherit;font-size:var(--fs-9);line-height:1;color:var(--ink-3);background:none;
  border:0;padding:0 var(--sp-1);cursor:pointer}
.qc-chips .qc-draft{flex:1 1 90px}
.qc-err{margin:0}
.qb-act{display:flex;gap:var(--sp-4);justify-content:flex-end;margin-top:var(--sp-4)}
.qb-act .btn{padding:var(--sp-2) var(--sp-6);font-size:var(--fs-8)}
.qb-add{margin-right:auto}
.qb-run{min-width:72px}
/* The results: one section per type, its heading the listing's day divider
   with the count where a day's count sits. It is the first sticky thing in
   the pane here, so it sticks to the top rather than under a channel line. */
.qgroup .qhead{top:0;margin:0;font-size:inherit;font-weight:inherit}
.qhead .qprov{font-family:var(--mono);font-weight:400;font-size:var(--fs-5);color:var(--ink-2);
  border-bottom:2px solid var(--tt-blue);letter-spacing:0;margin-left:var(--sp-2)}
.qnote{margin:0;padding:2px 14px 3px;font-size:var(--fs-6);color:var(--ink-3)}
/* A result row IS the listing's row (#786, `broadcast-row.js`): its own
   rules are `.row-*` above, plus `.row-where` below for the day and channel
   the listing's dividers would have said. */
.qempty{padding:22px 14px;text-align:center}
.qempty-h{margin:0 0 var(--sp-2);font-size:var(--fs-11);color:var(--ink)}
.qempty .note{max-width:34ch;margin:0 auto}
/* Saved searches (issue #675): one quiet line under the builder — a picker
   and a Save button — and the current search's own three actions only once
   one is picked. The builder is the busiest thing in the column; this is
   meant to be the least. */
.qs-bar,.qs-form,.qs-cur{display:flex;gap:var(--sp-3);align-items:center;margin-top:var(--sp-3);min-width:0}
.qs-bar .inp.sm,.qs-form .inp.sm{font-size:var(--fs-8);padding:3px 5px;width:auto;min-width:0;flex:1 1 0}
.qs-bar .btn,.qs-form .btn,.qs-cur .btn{padding:3px 10px;font-size:var(--fs-7);flex:0 0 auto}
.qs-cur{justify-content:flex-end;flex-wrap:wrap}
.qs-note{margin:5px 0 0;font-size:var(--fs-7)}
/* Bookmarks (issue #676): "my bookmarks" on the same quiet line as the saved
   searches, and the ones that no longer open folded away under one summary
   until asked for. */
.bm-gone{margin-top:var(--sp-2);font-size:var(--fs-7)}
.bm-gone summary{cursor:pointer;font-size:var(--fs-7)}
.bm-gone-list{list-style:none;margin:var(--sp-2) 0 0;padding:0}
.bm-gone-row{display:flex;gap:var(--sp-4);align-items:baseline;padding:var(--sp-1) 0;min-width:0}
.bm-gone-row .bm-kind{font-family:var(--mono);color:var(--ink-2)}
.bm-gone-row .btn{margin-left:auto;padding:1px 8px;font-size:var(--fs-7);flex:0 0 auto}
/* The fold is for the filters of a list; the builder IS the query, so the
   phone keeps it open and the fold's toggle has nothing to fold. */
body[data-browse="search"] #fbToggle{display:none}

/* ---------- the rules list (issue #154) ---------- */
/* Same row, another subject. The KIND is a column, not a word — rule / clean /
   split / field / check / def is a closed vocabulary, so it is set once to a
   narrow width and every key starts in the same place. */
.row-ch.rkind{
  flex:0 0 42px;text-align:left;overflow:hidden;white-space:nowrap;font-size:var(--fs-2);
}
/* The rule's ONE colour, the same swatch column 3 paints its span and its value
   with — so a rule keeps its identity between the list and the trace. A dot
   rather than a fill: the row is a line of text, and a filled row would shout
   where the trace's spans are meant to. */
.rdot{
  display:inline-block;width:7px;height:7px;margin-right:5px;border-radius:50%;
  vertical-align:baseline;
}
/* What the rule WRITES, beside the key that names it — dimmer, because the key
   is what a reader is looking for and the target is what tells two of them
   apart. */
.rtarget{color:var(--ink-3);font-size:var(--fs-7)}
.rgone{
  font-size:var(--fs-2);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);
  border:1px solid var(--rule);padding:0 3px;
}
/* An inactive rule is greyed, never hidden: it is still in the file, and a
   listing that dropped it would answer "which rules never fire" by omitting
   the ones that cannot. */
.row.off .row-title{color:var(--ink-3)}
/* Silence is the finding this list exists for, so it is marked on the row and
   not only in the tally above it. */
.row.silent .row-mark{color:var(--match)}

/* The (source, priority) BAND inside a tier — the run of rules the engine will
   try together (#457). Lighter than a day divider and NOT sticky: two sticky
   levels already stack above it, and a third would take a third of a 326px
   column's height before a row was drawn. */
.bandbreak{
  display:flex;align-items:baseline;gap:var(--sp-4);padding:9px 14px 3px;
  font-size:var(--fs-5);color:var(--ink-3);
}
.bandbreak .d{letter-spacing:.04em;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.bandbreak .n{margin-left:auto;font-variant-numeric:tabular-nums}

/* Which rule the workbench opened last, when the list in front of the reader
   does not contain it. It takes the divider's SHAPE — it is a heading for a
   list of one that is not here — but not its class: `.chbreak` is what four
   other modules wait for to know a channel-grouped listing has drawn, and a
   hidden element wearing it is first in the DOM and answers that wait with a
   box that never becomes visible. */
.ropen{
  display:flex;align-items:baseline;gap:var(--sp-4);padding:12px 14px 5px;
  position:sticky;top:0;z-index:var(--z-sticky);background:var(--shade);
}
.ropen .d{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-9);letter-spacing:.03em;white-space:nowrap;
}
.ropen .all{
  margin-left:auto;font-size:var(--fs-5);color:var(--ink-2);background:none;border:0;
  border-bottom:1px solid var(--rule);cursor:pointer;padding:0;
}

/* ---------- the workbench's "Fired on" panel (issue #154) ---------- */
.firedlist{list-style:none;margin:9px 0 0;padding:0}
/* Each item is the listing's own row (#786); the item only holds it. */
.fired{margin:0}

/* ---------- column 2: the record ---------- */
/* A MEASURE, centred in whatever the column turns out to be. The column is a
   fraction of the viewport and the record is 640px of it, so on a 2560px screen
   it was 640 pinned against the left edge of a 1093px column with 450px of
   nothing beside it — the reader's own report. Centred, the slack falls either
   side of the text instead of all of it on one. */
.rec{padding:16px 22px 34px;max-width:640px;margin-inline:auto}
.rec-when{display:flex;align-items:baseline;gap:var(--sp-5);color:var(--ink-2);font-size:var(--fs-9);flex-wrap:wrap}
/* The bookmark star (issue #676): the far end of the record's kicker line, a
   quiet outline until pressed, then the one spot colour the app has. A real
   toggle button — 32px of target, whatever the glyph measures. */
.rec-when .star{
  margin-left:auto;align-self:center;min-width:32px;min-height:32px;padding:0 var(--sp-3);
  background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;
  color:var(--ink-3);font-size:var(--fs-16);line-height:1;
}
.rec-when .star:hover{color:var(--ink);background:var(--hover)}
.rec-when .star:focus-visible{outline:2px solid var(--ink);outline-offset:1px}
.rec-when .star[aria-pressed="true"]{color:var(--match)}
.rec-when .clock{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-18);color:var(--ink);letter-spacing:-.01em;line-height:1;
}
.rec-title{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-20);line-height:1.1;letter-spacing:-.004em;margin:var(--sp-5) 0 var(--sp-2);
}
.rec-title .sub{font-size:.62em;color:var(--ink-2)}
.rec-strand{font-size:var(--fs-9);color:var(--ink-2);margin:0}
.rec-strand u{text-decoration:none;border-bottom:2px solid var(--tt-red)}
/* Where the reader has been: two arrows, one each way, each NAMING the record
   it would open. They replaced a breadcrumb trail, which read
   `Elefant, Tiger & Co. > Elefant, Tiger & Co. > Elefant, Tiger & Co.` — a
   path through a programme's link graph is a path between records all named
   after that programme, so the trail's labels could not tell its own steps
   apart. Hence the second line on each arrow: the airtime of a broadcast, the
   kind of anything else. That line is the whole point of the control. */
.visits{display:flex;gap:var(--sp-4);padding-bottom:9px}
.visits button, .visits .vgap{flex:1 1 0;min-width:0}
.visits button{
  /* Wrapping, so a narrow column drops the NOTE to a second line rather than
     eating the label down to `Ladies…`: the note is what tells two records of
     one programme apart, and the name is what a reader recognises. Both are
     needed, and neither is worth truncating while the other has room. */
  display:flex;align-items:baseline;gap:var(--sp-1) var(--sp-3);flex-wrap:wrap;
  font:inherit;font-size:var(--fs-7);line-height:1.35;
  color:var(--ink-2);background:none;border:0;border-bottom:1px solid var(--rule);
  cursor:pointer;padding:0 0 3px;text-align:left;
}
.visits .vnext{flex-direction:row;justify-content:flex-end;text-align:right}
.visits button:hover{color:var(--ink);border-color:var(--ink)}
.visits b{flex:1 1 auto;font-weight:400;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.visits i{font-style:normal;color:var(--ink-3);white-space:nowrap;flex:0 1 auto}
.visits .varr{color:var(--match);font-size:var(--fs-12);line-height:1;flex:0 0 auto}

.sect{margin-top:22px}
.sect-hd{
  display:flex;align-items:baseline;gap:var(--sp-4);padding-bottom:5px;
  border-bottom:1px solid var(--ink);
}
.sect-hd .n{margin-left:auto;font-size:var(--fs-6);color:var(--ink-3)}

.f{display:flex;gap:var(--sp-6);padding:var(--sp-3) 0;border-bottom:1px solid var(--rule);align-items:baseline}
/* 104px, down from 148: the key lost the group prefix the section heading was
   already carrying (`credits.director` -> `director`), so the column sized to
   hold it can give the width back to the values. It is 63% of what column 2
   had left for a value at 1440px, and buying it back un-wraps four of seven
   long cast rows. The rare 18-character key clips, as `meta.recording_location`
   did at 148 — the full path is on the `title`. */
.f-k{
  flex:0 0 104px;font-size:var(--fs-8);color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.f-v{flex:1 1 auto;min-width:0;font-size:var(--fs-11);overflow-wrap:anywhere}
.f-v.prose{font-size:var(--fs-13)}
.f-v .f-lead{color:var(--ink-2)}
.f-lvl{
  flex:0 0 auto;font-size:var(--fs-4);color:var(--ink-3);letter-spacing:.04em;
  white-space:nowrap;
}
/* The arrow is the mark, and its meaning is its SHAPE — it takes its label's
   own ink rather than a tint set as a stroke (#501). */
.f-lvl.inh::before{content:"↑ "}
/* A coded value's readable name, shown BESIDE the code and never in place of
   it (#249/#339): this column is where a rule's output is read. */
.gloss{color:var(--ink-2)}
/* An imported identifier, demoted behind the name it resolves to (#367). */
.gloss.idqid{font-size:var(--fs-7);color:var(--ink-3)}
button.vlink.idlink{border-bottom-color:var(--tt-magenta-2);border-bottom-style:dotted}
button.vlink.gloss{border-bottom-color:var(--tt-magenta-2);border-bottom-style:dotted}
/* Our listings' verdict on a PROVIDER's `host` credit (#643). A mark beside
   the Wikidata row, never a replacement for it. Only `contradicted` is tinted —
   it is the one a reader acts on — and the other three wear the hairline
   `.fmark` does, so four chips never rank against each other. */
.hostmark{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:0 var(--sp-2);
  color:var(--ink);border:1px solid var(--rule);white-space:nowrap;
  vertical-align:1px;cursor:help;
}
.hostmark.hm-contradicted{background:var(--tt-red);color:var(--on-tint);border-color:var(--tt-red-2)}
.hostmark.hm-corroborated{border-color:var(--tt-green-2)}
.hostmark.hm-unverifiable{color:var(--ink-2)}
/* One credit: the person, then the role against the far edge. A row rather
   than a `<br>`-separated span, because "against the far edge" is a line's
   property and an inline run has no edge to be set against. */
.crow{display:flex;flex-wrap:wrap;align-items:baseline;gap:1px 10px}
/* `normal`, against the `anywhere` this column sets for long rule values: two
   shrinkable halves under `anywhere` broke BOTH mid-word — `Schneiderhah / n`
   beside `Männervers / teher`. A name is a word. When the pair will not fit,
   the row wraps and the role keeps the far edge of the line below. */
.crow,.crow *{overflow-wrap:normal}
.cwho{min-width:0}
.crole{margin-left:auto;color:var(--ink-2);text-align:right}
/* A rolled-up value the members DISAGREED on, labelled with who said what
   (#382). Inside the field's value cell, under the value it resolved to.

   Two scopes, and they are graded apart rather than alike, because that is the
   ruling: `cross` is two broadcasters printing different values — normal, not
   a defect, and not theirs to fix — so it is set in the neutral stone the
   scaffolding uses. `within` is one broadcaster contradicting itself across
   its own airings, which IS a finding, so it takes the meta yellow. Neither
   takes --match: the one saturated colour marks a grep hit, and a divergence
   the resolver handled is not an error to alarm about. */
.cf{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:3px 10px;
  margin-top:var(--sp-3);font-size:var(--fs-8);color:var(--ink-2);
}
.cf-lead{
  flex:0 0 auto;font-size:var(--fs-4);letter-spacing:.06em;text-transform:uppercase;
  padding:1.5px 5px;color:var(--on-tint);background:var(--tt-stone);
}
.cf.within .cf-lead{background:var(--tt-yellow)}
/* A CURATED value: ours, not the broadcasters' (#409). It reuses the divergence
   row's geometry because it makes the same kind of statement about the value
   above it — where it came from — and differs only in the lead, which is set in
   the external magenta the outbound identifier link already uses for "not this
   pipeline's own output". Not the yellow: yellow is the grade for a broadcaster
   contradicting itself, and a decision of ours is not a finding about them. */
.cf.cu .cf-lead{background:var(--tt-magenta-2)}
/* A FROZEN value (#474) wears the base `.cf-lead` and NO modifier at all,
   deliberately: the neutral stone is already what a `cross` divergence wears,
   and this is the same kind of statement — where the value above it came from.
   It is emphatically not a finding, so it must not take the yellow, and it is
   not ours to have decided, so it must not take the magenta. It carried a `.fr`
   marker until #481's class check pointed out that nothing drew it and nothing
   read it: a rule restating the default would only read as if it had chosen
   something, and a class with neither rule nor reader is dead markup. */
/* A variant is one unbreakable run: the value, its asserter and its date read
   as one statement, and wrapping between them would attribute a value to the
   next line's broadcaster. */
.cf-var{display:inline-flex;align-items:baseline;gap:5px}
.cf-var .cf-val{color:var(--ink)}
.cf-var.shown .cf-val{border-bottom:1px solid var(--rule)}
.cf-who{font-size:var(--fs-5);letter-spacing:.05em;color:var(--ink-2);white-space:nowrap}
.cf-when,.cf-mark{font-size:var(--fs-5);color:var(--ink-3);white-space:nowrap}
/* The curated-fact AUTHORING panel (#409 step 4) — column 02's one writing
   surface, and the only place in this column that is a form rather than a
   document. It borrows the field grid's rhythm so a decision is typed where
   the value it is about is read, and the rolled-up value sits BESIDE the box
   as a placeholder rather than inside it: a pre-filled input is a decision
   nobody took, and this whole mechanism exists to record ones somebody did. */
.cur-body{padding:var(--sp-4) 0 var(--sp-2);display:flex;flex-direction:column;gap:7px}
.cur-f{display:flex;align-items:baseline;gap:var(--sp-4);font-size:var(--fs-9)}
.cur-p{
  flex:0 0 108px;font-size:var(--fs-5);letter-spacing:.05em;color:var(--ink-2);
  font-family:var(--mono,monospace);
}
.cur-f input,.cur-f textarea{
  flex:1 1 auto;min-width:0;font:inherit;color:var(--ink);
  background:transparent;border:0;border-bottom:1px solid var(--rule-strong);
  padding:var(--sp-1) 0;
}
.cur-f textarea{resize:vertical;line-height:1.45}
/* The underline darkening is not a focus cue on its own (#781), so the app's
   ring stays — the field keeps it and gains the darker edge besides. */
.cur-f input:focus,.cur-f textarea:focus{border-bottom-color:var(--ink)}
.cur-was{
  flex:0 0 auto;max-width:38%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-size:var(--fs-6);color:var(--ink-3);cursor:pointer;
  font-family:inherit;background:none;border:0;padding:0;text-align:left;
}
.cur-was:hover{color:var(--ink)}
.cur-none{margin:0;font-size:var(--fs-9);color:var(--ink-2)}
.cur-vs{margin:0;font-size:var(--fs-6);color:var(--ink-3)}
.cur-act{display:flex;align-items:center;gap:var(--sp-4);padding-top:var(--sp-1)}
.cur-who{font-size:var(--fs-5);letter-spacing:.05em;color:var(--ink-3)}
/* Hand-written EDITOR NOTES (#309) — directly under the rule-written notes
   section and under their own heading. Each note is the text, then a byline
   naming who wrote it: a person's remark is never drawn as if a rule wrote it.
   The two boxes reuse the curated panel's underline input face. */
.en-body{padding:var(--sp-4) 0 var(--sp-2);display:flex;flex-direction:column;gap:9px}
.en-note{display:flex;flex-direction:column;gap:3px}
.en-said{margin:0;font-size:var(--fs-10);line-height:1.5;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere}
.en-meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-2) var(--sp-5)}
.en-who{font-size:var(--fs-5);letter-spacing:.05em;color:var(--ink-3)}
.en-none{margin:0;font-size:var(--fs-9);color:var(--ink-2)}
.en-add{display:flex;flex-direction:column;gap:5px;padding-top:var(--sp-1)}
.en-text{
  width:100%;box-sizing:border-box;font:inherit;font-size:var(--fs-9);line-height:1.45;
  color:var(--ink);background:transparent;border:0;border-bottom:1px solid var(--rule-strong);
  padding:var(--sp-1) 0;resize:vertical;
}
.en-text:focus{border-bottom-color:var(--ink)}
/* A provider's DRAFT (#409 step 5), in #256's shape: the panel never writes
   it — the proposal lands in the form and the human decides. Set in the same
   external magenta the curated lead uses, because it says the same thing about
   where a value came from. */
.cur-prop{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 9px;
  padding:var(--sp-3) var(--sp-4);background:var(--tt-stone);font-size:var(--fs-7);color:var(--on-tint);
}
.cur-lead{
  font-size:var(--fs-4);letter-spacing:.06em;text-transform:uppercase;
  padding:1.5px 5px;background:var(--tt-magenta-2);color:var(--on-tint);
}
.cur-plabel{font-weight:500}
.cur-pv b{font-family:var(--mono,monospace);font-size:var(--fs-5);font-weight:400}
/* A path the proposal did NOT carry: a provider's own vocabulary, or an
   identifier where we store a value. Set back rather than hidden — a proposal
   that silently dropped half a record would read as the record being short. */
.cur-held{
  font-family:var(--mono,monospace);font-size:var(--fs-5);
  text-decoration:line-through;
}
/* `Use as draft` writes nothing — it loads the proposal into the form and says
   so in its toast — so it takes the app's SECONDARY face (#478). It is the one
   `.btn.ghost` in the app standing on a tint, and `.btn.ghost` draws itself
   from the page ink scale, so it takes the on-tint ink here like everything
   else inside this card. */
.cur-prop .btn.ghost{color:var(--on-tint);border-color:var(--on-tint)}
.chipset{display:flex;gap:5px;flex-wrap:wrap}
.chip{
  font-size:var(--fs-5);letter-spacing:.06em;padding:1.5px 6px;color:var(--on-tint);
  background:var(--tt-yellow);
}
.empty{
  padding:var(--sp-5) 0 var(--sp-2);color:var(--ink-2);font-size:var(--fs-9);
  border-bottom:1px solid var(--rule);
}
.empty b{color:var(--ink);font-weight:500}
.para{margin:0 0 11px}
.para:last-child{margin-bottom:0}
.para-lbl{font-weight:600}
/* An outbound identifier link (#389). Underlined in the external hue rather
   than the blue `vlink` uses, because those two do different things: a vlink
   opens a row THIS archive holds, an exref leaves the application entirely. */
.lk-v a.exref{
  color:var(--ink);text-decoration:none;
  border-bottom:1px solid var(--tt-magenta-2);
}
.lk-v a.exref:hover{border-bottom-width:2px}
.f-v button.vlink{
  font:inherit;background:none;border:0;padding:0;cursor:pointer;text-align:left;
  border-bottom:2px solid var(--tt-blue);color:var(--ink);
}

/* the finding marks in the record: a dagger and the message it stands for */
.finding{display:flex;gap:9px;padding:5px 0;border-bottom:1px solid var(--rule);align-items:baseline}
.finding .dg{flex:0 0 12px;color:var(--match);font-size:var(--fs-9);line-height:1.2}
.finding .fm{flex:1 1 auto;min-width:0;font-size:var(--fs-9);overflow-wrap:anywhere}
/* The key and the two provenance marks (#479). All three are set in the FULL
   ink and held back by size and tracking instead of by tone: this row sits on
   `--paper`, where `--ink-3` measures 2.2 : 1 and even `--ink-2` measures
   4.44 : 1, and a finding whose own row cannot be read is the defect this
   issue is about. The marks wear a hairline rather than a tint, because the
   severity chip beside them is the row's one tinted thing and two filled
   chips would rank against each other. */
.finding .fk{font-size:var(--fs-6);color:var(--ink);white-space:nowrap}
.finding .fmark{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:0 var(--sp-2);
  color:var(--ink);border:1px solid var(--rule);white-space:nowrap;
}
.finding .sev{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:1px 5px;
  background:var(--tt-stone);color:var(--on-tint);white-space:nowrap;
}
.finding.critical .sev,.finding.major .sev{background:var(--tt-red)}
.unclaimed{
  margin:0;padding:9px 11px;background:var(--shade);border:1px solid var(--rule);
  font-family:var(--mono);font-size:var(--fs-8);line-height:1.7;white-space:pre-wrap;
  word-break:break-word;color:var(--ink-2);
}

/* ---------- links: two kinds, and they are not the same kind of fact --------
   An INTERNAL link is an entity GREPg derived from the listing itself — the
   program this airing belongs to, its group_series, its series, the earlier
   broadcast it repeats. We wrote those, from the broadcaster's own text.
   An EXTERNAL reference is somebody else's claim about the world, carried
   under their id. The two are never mixed into one list: `Credited in` (our
   airings) and `Appears in` (a work a third party says this person is in) are
   different facts, and a panel that merged them would present the second as
   the first.  External references are `edit`+ in the product, so a guest is
   told the kind exists rather than shown it.                               */
.kind{display:flex;align-items:baseline;gap:var(--sp-4);margin:13px 0 3px}
.kind:first-child{margin-top:var(--sp-3)}
.kind .k-name{font-size:var(--fs-7);color:var(--ink)}
.kind .k-rule{flex:1;height:1px;background:var(--rule)}
.kind .k-n{font-size:var(--fs-6);color:var(--ink-3);white-space:nowrap}
/* The signet says which kind this heading opens, and it used to say it in the
   family's own hue — a MARK set as a stroke, at 2.05 : 1 and 2.01 : 1 in light
   (#501). It says it in three other ways already: the glyph's SHAPE, the
   sentence beside it, and the underline every row below it wears. So the glyph
   takes the ink scale and the hue moves one element over, onto `.k-rule` — the
   hairline that runs from the heading to its count. A rule is an edge, not
   text; it is a wider stroke of the hue than an 11 px glyph ever was.

   BOTH HAIRLINES TAKE THE `-2` SHADE — the shade the two signets already wore,
   so the hue changed element and the shade did not move at all. The pair is
   distinguished by HUE and must not also differ in weight, and the shade is
   the only thing here that could make it:

              light                              dark
              hex      L*    vs --rule  paper    hex      L*    vs --rule
     blue     #B7C3F0  79.3    1.36     1.68     #7C8AD6  59.3    4.25
     blue-2   #A2B0E6  72.5    1.66     2.05     #7280C8  55.3    3.71
     magenta  #E7B6E1  79.5    1.35     1.66     #BC7DB2  60.4    4.42
     magenta-2 #D9A2D6 73.2    1.63     2.01     #AC6EA1  54.5    3.62

   Read down the L* column: at `-2` the pair sits ΔL* 0.7 apart in BOTH
   palettes, which is as matched as this palette gets. Mixing the shades —
   base blue against magenta-2, which is what this rule said for one revision —
   puts them 6.1 apart in light and 4.7 in dark, about the size of the
   base -> -2 step itself (6.7 / 4.0 / 5.9), so the two kinds would have read
   as a heavier line and a lighter one. There is no measurement behind
   preferring base blue: at `-2` blue is the DARKER of the two by 0.7, not the
   lighter, so "blue reads heavier" is not a reason either.

   `-2` rather than base, given both are symmetric, is decided against
   `--rule` — the hairline this one replaces and the neighbour it has to be
   told apart from. In light, base reads 1.36 / 1.35 against `--rule` and `-2`
   reads 1.66 / 1.63: a base hairline is very nearly the default hairline. Dark
   inverts that (4.25 / 4.42 against 3.71 / 3.62), but both clear there by a
   wide margin, so light is the binding case.

   The ROW underlines below stay as they are — `.lk.ours` base blue at 2 px
   solid, `.lk.theirs` magenta-2 at 1.5 px dotted. They are allowed to differ
   in shade because they also differ in thickness and style, which is a second
   channel these two 1 px hairlines do not have. */
.kind .sig{font-size:var(--fs-7);line-height:1;width:12px;text-align:center;flex:0 0 12px;color:var(--ink-2)}
.kind.ours .k-rule{background:var(--tt-blue-2)}
.kind.theirs .k-rule{background:var(--tt-magenta-2)}

.lk{display:flex;gap:var(--sp-6);padding:var(--sp-3) 0;border-bottom:1px solid var(--rule);align-items:baseline}
.lk .lk-k{
  flex:0 0 148px;font-size:var(--fs-8);color:var(--ink-2);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.lk .lk-v{flex:1 1 auto;min-width:0}
.lk .cnt{flex:0 0 auto;margin-left:auto;font-size:var(--fs-6);color:var(--ink-3);white-space:nowrap}
.lk a,.lk button.vlink{font-size:var(--fs-10);font-family:inherit;background:none;border:0;padding:0;cursor:pointer;text-align:left}
/* ours: the solid underline every internal link in the edition wears */
.lk.ours a,.lk.ours button.vlink{border-bottom:2px solid var(--tt-blue);color:var(--ink)}
/* theirs: a DOTTED underline, a diamond, and the asserting party named. A
   claim is drawn as a claim. */
.lk.theirs a,.lk.theirs button.vlink{border-bottom:1.5px dotted var(--tt-magenta-2);color:var(--ink)}
/* The diamond is a reference mark, so it sits one ink grade behind the link it
   opens rather than in the hue (#501) — the row's magenta is on its dotted
   underline and on the `.who` chip, both of which are marks. */
.lk.theirs .lk-v::before{content:"\25C8";color:var(--ink-2);font-size:var(--fs-5);padding-right:5px}
.lk .who{
  font-size:var(--fs-3);letter-spacing:.07em;text-transform:uppercase;padding:1px 5px;
  background:var(--tt-magenta);color:var(--on-tint);white-space:nowrap;margin-left:7px;
}
.lk .ref{font-size:var(--fs-6);color:var(--ink-3);margin-left:var(--sp-3);white-space:nowrap}
.lk .none{color:var(--ink-2);font-size:var(--fs-10)}
/* A link to an AIRING is the listing's own row (#786), set inside the link
   line: the key column keeps the relation, the row names the airing. Its
   side padding goes — the line's gap already separates it from the key. */
.lk-air .lk-v > .row{padding:var(--sp-1) 0}
.lk.theirs .lk-v.plain::before{content:none}
.lk-note{padding:var(--sp-4) 0 var(--sp-1);font-size:var(--fs-8);color:var(--ink-2);line-height:1.45}
.lk-note b{color:var(--ink);font-weight:500}
.lk-more{padding:var(--sp-3) 0 0;font-size:var(--fs-7);color:var(--ink-3)}
.lk-more button{font:inherit;font-size:var(--fs-7);color:var(--ink-2);background:none;border:0;border-bottom:1px solid var(--rule);cursor:pointer;padding:0}

/* ---------- change history ----------
   Provenance over TIME, where the apparatus is provenance over one parse. It
   carries no rule id, no pattern and no target path, so unlike the apparatus it
   is NOT role-gated: a broadcaster may see that a value moved and why, which is
   the half of the story their own corrections live in. The two causes mean
   opposite things and are coloured as the report colours ownership.        */
.hist{margin-top:22px}
.chg{border-left:3px solid var(--tt-red-2);padding:8px 0 8px 11px;margin-top:var(--sp-5)}
.chg.internal{border-left-color:var(--tt-blue-2)}
.chg-hd{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;padding-bottom:var(--sp-2)}
.chg-when{font-size:var(--fs-7);color:var(--ink-2);font-variant-numeric:tabular-nums}
.chg-why{font-size:var(--fs-7);color:var(--ink)}
.chg-cause{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:1px 5px;
  color:var(--on-tint);background:var(--tt-red);white-space:nowrap;
}
.chg.internal .chg-cause{background:var(--tt-blue)}
.chg-n{margin-left:auto;font-size:var(--fs-5);color:var(--ink-3)}
.chg-row{display:flex;gap:var(--sp-5);align-items:baseline;padding:3px 0}
.chg-f{flex:0 0 150px;font-size:var(--fs-8);color:var(--ink-2);line-height:1.3;overflow-wrap:anywhere}
.chg-d{flex:1 1 auto;min-width:0;font-size:var(--fs-9);line-height:1.5;overflow-wrap:anywhere}
.chg-d del{
  background:var(--tt-stone);color:var(--on-tint);text-decoration:line-through;
  text-decoration-thickness:1px;padding:0 3px;
}
.chg-d ins{text-decoration:none;padding:0 3px;color:var(--on-tint)}
.chg .chg-d ins{background:var(--tt-red)}
.chg.internal .chg-d ins{background:var(--tt-blue)}
.chg-d .arw{color:var(--ink-3);padding:0 var(--sp-2)}
.chg-d .nil{color:var(--ink-3);font-style:normal}
.hist-note{margin-top:13px;padding-top:var(--sp-4);border-top:1px solid var(--rule);font-size:var(--fs-7);color:var(--ink-2);line-height:1.45}
.hist-note b{color:var(--ink);font-weight:500}

/* ---------- column 3: the apparatus ---------- */
/* Same measure argument as the record, and it bites harder here: the trace is
   set in 12px mono, so a column free to grow to 1138px set the broadcaster's
   paragraphs 150 characters wide. 840px holds about 105 with the padding
   counted, and the column keeps its background out to the edge either way. */
.work,.guest{max-width:840px;margin-inline:auto}
.work{padding:var(--sp-7) var(--sp-9) var(--sp-17)}
/* A second `.work` directly under the first is a CONTINUATION of one pane (the
   trace, then the history below it), so it does not re-open the top padding. */
.work.cont{padding-top:0}
.srcline{
  display:flex;align-items:baseline;gap:var(--sp-4);font-size:var(--fs-7);color:var(--ink-2);
  padding-bottom:9px;flex-wrap:wrap;
}
.srcline b{color:var(--ink);font-weight:500}
.srcline .grow{flex:1}
.srcline button{
  font:inherit;font-size:var(--fs-6);color:var(--ink-2);background:var(--sheet);
  border:1px solid var(--rule);padding:1px 7px;cursor:pointer;
}
.srcline button:hover{border-color:var(--ink);color:var(--ink)}
.srcline button[aria-pressed="true"]{background:var(--ink);color:var(--paper);border-color:var(--ink)}
/* The file name is a LINK to its imported-files row (#652), not a toggle. */
.srcline button.srcfile{
  font-size:var(--fs-7);color:var(--ink);font-weight:500;background:none;border:0;padding:0;
  text-decoration:underline dotted;text-underline-offset:2px;
}
.srcline button.srcfile:hover{text-decoration-style:solid}

.trace{
  margin:0;padding:13px 14px;background:var(--shade);border:1px solid var(--rule);
  font-family:var(--mono);font-size:var(--fs-9);line-height:1.85;
  white-space:pre-wrap;word-break:break-word;
}
/* The workbench's "Matched here" trace is a second `.trace` under a heading,
   so it needs the gap a `.sect` would have given it; `.tr-path` is the field
   rule's source path and `.tr-none` the "(no span)" marker, both said quietly
   because they are apparatus rather than content. */
.trace.rw-matched{margin-top:var(--sp-5)}
.tr-path,.tr-none{color:var(--ink-3)}
.sp{
  position:relative;padding:1.5px 0;border-radius:0;cursor:default;
  box-decoration-break:clone;-webkit-box-decoration-break:clone;
  transition:box-shadow .09s ease;
}
/* The reference numeral riding on a span. It used to be `--ink` at `opacity:.62`
   — an in-spec colour multiplied back to 3.9 : 1 on the paler tints and worse
   in dark, which is the mechanism #482 removed. It needs no dimming of its own:
   it is 8.5 px superscript against 12 px text, so size and position already say
   it is a mark rather than a word, and on a tint the ink is `--on-tint` by the
   rule #478 settled — the same ink the span it sits on carries. */
.sp sup{
  font-size:var(--fs-2);font-weight:600;line-height:0;vertical-align:.62em;
  padding-right:.18em;color:inherit;
}
/* A value a clean rule consumed. It is struck, and that is the whole of its
   recession: it stands ON a mark, and a mark carries `--on-tint` and nothing
   else (#478). It used to take `--ink-3` here instead — 2.9 : 1 on its own
   tint, and 1.3 : 1 once the wash below lightened the ground under it — which
   is the failure mode #478 exists to prevent, reached through a selector that
   paints no tint of its own and so slipped past the guard for it (#482). */
.sp.gone{text-decoration:line-through;text-decoration-thickness:1px}
/* Matched and kept none of: the rule's own scaffolding — `<b>`, a separating
   `:` — struck like a consumed value, because both are "in the source, not in
   the data", but lighter, because every rule has some and it is the values
   that should carry the page.
   "Lighter" is spent on the TINT, which is where #478 says a register on a
   mark recedes: it gives up its rule's hue for `--tt-stone`, the neutral of
   the seven and the colour a consumed value already wears. That replaces a
   `color-mix` wash toward `--paper` — a wash that made the ground LIGHTER in
   light mode and DARKER in dark, so no single ink could sit on the result, and
   which was itself only there to dodge `opacity` dimming the numeral. Nothing
   is being dimmed here any more, so nothing has to be dodged. */
.sp.drop{
  background:var(--tt-stone);
  text-decoration:line-through;text-decoration-thickness:1px;
}
/* The numeral is a reference mark, not part of what was struck. */
.sp.gone > sup,.sp.drop > sup{text-decoration:none}
.unp{border-bottom:2px dotted var(--match)}
/* A tag marker in the working text. It used to be `--match` at `opacity: .8`,
   which composited it to 3.28 : 1 in light and 4.27 : 1 in dark — the
   multiply the ink-scale block forbids, surviving because the rendered sweep
   skipped every `--match` string by token (#501). A marker is meant to be
   seen; it recedes from the source text around it by being a different colour,
   not a weaker one. */
.tagmark{color:var(--match)}

.ap-hd{display:flex;align-items:baseline;gap:var(--sp-4);margin:var(--sp-10) 0 0;padding-bottom:5px;border-bottom:1px solid var(--ink)}
.ap-hd .n{margin-left:auto;font-size:var(--fs-6);color:var(--ink-3)}
.aplist{list-style:none;margin:0;padding:0;min-width:0}
.ap{display:flex;gap:9px;align-items:baseline;padding:5px 4px 5px 0;border-bottom:1px solid var(--rule);min-width:0}
.ap:hover{background:var(--shade)}
.ap-n{
  flex:0 0 21px;text-align:center;font-size:var(--fs-5);font-weight:600;
  padding:1px 0;color:var(--on-tint);background:var(--tt-stone);
}
/* The numeral is the highlight toggle (#781) — a button, reset to the chip. */
button.ap-n{border:0;font-family:inherit;line-height:inherit;cursor:pointer;align-self:baseline}
.ap-key{flex:0 0 88px;font-size:var(--fs-8);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ap-key button{
  font:inherit;font-weight:500;background:none;border:0;padding:0;cursor:pointer;
  border-bottom:1px solid var(--rule);color:var(--ink);
}
.ap-p{flex:0 0 26px;font-size:var(--fs-5);color:var(--ink-3);text-align:right}
.ap-body{flex:1 1 auto;min-width:0;overflow:hidden}
.ap-out{font-size:var(--fs-8);color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ap-out b{color:var(--ink);font-weight:500}
.ap-out i{font-style:normal;padding:0 3px}
/* The pattern under the output it produced. It used to sit half a pixel below
   `.ap-out` and one grade behind it, and half a pixel is not a distinction —
   the grade was doing all of the work, at 4.4 : 1 against 2.3 : 1. With the
   scale compliant that gap is 8.1 against 5.2, so a full size step and a line
   of air carry what the colour no longer can (#482). */
.ap-pat{
  display:block;font-size:var(--fs-6);color:var(--ink-3);margin-top:3px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ap-pat .g{color:var(--match)}
.ap-tier{
  flex:0 0 auto;font-size:var(--fs-3);letter-spacing:.06em;color:var(--ink-3);
  border:1px solid var(--rule);padding:0 var(--sp-2);
}
.ap-split{display:flex;align-items:baseline;gap:9px;padding:13px 0 5px;font-size:var(--fs-7);color:var(--ink-2)}
.ap-split .eyebrow{color:var(--ink-2)}
.ap-split .rest{flex:1;height:1px;background:var(--rule)}

/* The child broadcasts a `regions+` / `sub_broadcasts+` split made from the
   traced block (#813): one entry per child, its time and title, then the
   fields the parse stores on it with the numerals that tie them to the trace.
   The child the record column is showing is marked by a rule, not a fill. */
.kids{list-style:none;margin:0;padding:0;min-width:0}
.kid{padding:var(--sp-2) 0 var(--sp-3);border-bottom:1px solid var(--rule);min-width:0}
.kid.self{box-shadow:inset 2.5px 0 0 var(--ink);padding-left:var(--sp-4)}
.kid-h{display:flex;align-items:baseline;gap:var(--sp-4);font-size:var(--fs-8);min-width:0}
.kid-h b{font-weight:500;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.kid-lv{font-size:var(--fs-5);color:var(--ink-3);white-space:nowrap}
.kid-t{font-variant-numeric:tabular-nums;color:var(--ink-2);white-space:nowrap}
.kid-t i{font-style:italic;font-size:var(--fs-6)}
.kid-self{margin-left:auto;font-size:var(--fs-5);color:var(--ink-3);white-space:nowrap}
.kid-f{list-style:none;margin:var(--sp-1) 0 0;padding:0 0 0 var(--sp-4);min-width:0}
.kid-f li{font-size:var(--fs-6);color:var(--ink-2);overflow-wrap:anywhere}
.kid-f b{color:var(--ink);font-weight:500}

/* the footnote numeral: the tie itself */
.fn{
  display:inline-block;min-width:15px;padding:0 var(--sp-1);text-align:center;
  font-size:var(--fs-3);font-weight:600;line-height:1.55;vertical-align:.32em;
  margin-left:5px;color:var(--on-tint);background:var(--tt-stone);
  font-variant-numeric:tabular-nums;cursor:default;
}
.fn.lead{margin-left:0;margin-right:5px}

/* the guest's column 3: the source document, unannotated */
.guest{padding:var(--sp-7) var(--sp-9) var(--sp-17)}
.guest .plain{
  margin:0;padding:13px 14px;background:var(--shade);border:1px solid var(--rule);
  font-family:var(--mono);font-size:var(--fs-9);line-height:1.85;
  white-space:pre-wrap;word-break:break-word;color:var(--ink);
}
.withheld{
  margin-top:var(--sp-9);border-top:1px solid var(--ink);padding-top:9px;
  font-size:var(--fs-9);color:var(--ink-2);
}
.withheld b{color:var(--ink);font-weight:500}
.withheld p{margin:var(--sp-3) 0 0;max-width:56ch}
.withheld button{
  font:inherit;font-size:var(--fs-9);background:none;border:0;padding:0;cursor:pointer;
  border-bottom:2px solid var(--tt-blue);color:var(--ink);
}

/* ---------- the fastext key row ---------- */
.keys{
  position:sticky;bottom:0;z-index:var(--z-bar);display:flex;align-items:stretch;
  border-top:1px solid var(--rule);background:var(--sheet);height:var(--keys-h);
  /* The row never widens the DOCUMENT (#704). Its keys are `nowrap` and it
     grew a key per page surface until an admin's row ran to 2,050px, and a
     flex row's overflow is the page's scrollable overflow — so every desktop
     scrolled sideways, on the listing and on every surface. The page groups
     (#705) put it back inside 1280px; this is what keeps a longer row (a
     long stat, a wider language) scrolling inside itself, as it always has
     on a phone, instead of under the whole page. */
  overflow-x:auto;overflow-y:hidden;scrollbar-width:thin;
}
.key{
  display:flex;align-items:center;gap:var(--sp-3);padding:0 11px;font-size:var(--fs-6);
  color:var(--ink-2);border-right:1px solid var(--rule);white-space:nowrap;
}
.key .sw{width:11px;height:11px;flex:0 0 11px}
.key .cnt{color:var(--ink-3)}
.key .dag{color:var(--match);font-weight:400}
.keys .fill{flex:1 1 0;min-width:0;border-right:1px solid var(--rule)}
/* The stat is the one part of the row that gives way (#704): it shrinks, with
   an ellipsis and its whole text on `title`, before any key is pushed out of
   sight — the reparse key sits right before it. A block with the row's line
   height rather than a flex box, because an ellipsis needs one. */
.keys .stat{
  display:block;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  line-height:calc(var(--keys-h) - 1px);padding:0 13px;font-size:var(--fs-6);
  color:var(--ink-2);white-space:nowrap;
}
.keys .stat b{color:var(--ink);font-weight:500}
/* The legend gives way next, and only once the stat has: a key is an action,
   the legend is a caption. A browser that rounds the mono advance to whole
   pixels (Chromium 151 on a CI runner: 7px for Plex's 6.3) draws the row ~8%
   wider, and at 1280px that pushed the reparse key off the edge. The label
   ellipsises (its full text on `title`); the swatch never shrinks. The stat's
   shrink weight is what makes the order: shrinking is shared out by basis
   times weight, so at 100 the stat takes nearly all of it until it is gone. */
.keys .stat{flex-shrink:100}
.keys .key.legend{flex:0 1 auto;min-width:0}
.keys .key.legend .lbl{min-width:0;overflow:hidden;text-overflow:ellipsis}
.keys button.key{font:inherit;font-size:var(--fs-6);background:transparent;border:0;border-right:1px solid var(--rule);cursor:pointer}
.keys button.key:hover{background:var(--shade)}
.keys .key.act{background:var(--solid);color:var(--on-solid);box-shadow:inset 0 2px 0 var(--solid-mark)}
.keys .key.act .sw{outline:1px solid var(--paper)}
/* A narrower desktop (#704): an admin's row — the legend, four keys, the
   reparse — is ~1,370px at the keys' own padding, so between the phone
   breakpoint and 1440px they sit closer together rather than scroll. */
@media (min-width:901px) and (max-width:1439px){
  .key{padding:0 var(--sp-4);gap:var(--sp-2)}
  .keys .stat{padding:0 var(--sp-4)}
}
/* The group key whose page is on screen (#705) — shaded rather than inverted:
   inverted is the phone's current STEP, and a group is not a column. */
.keys .key[aria-current="page"]{background:var(--shade);color:var(--ink)}

/* ---------- a page group's second-level selector (#705) ----------
   One tab per page of the group the role may open, prepended to each page's
   panel by `pagegroups.js` — inside the panel, so it is in the panel's focus
   trap, and sticky at the top of the panel's own scroll on a desktop. Its
   tabs start in the page's own column (`.page`: 1080px, 26px of padding). */
.subnav{
  position:sticky;top:0;z-index:var(--z-sticky);display:flex;align-items:stretch;
  background:var(--sheet);border-bottom:1px solid var(--rule);overflow-x:auto;
  padding:0 max(26px, calc((100% - 1080px) / 2 + 26px));
}
.subnav[hidden]{display:none}
.subtab{
  font:inherit;font-size:var(--fs-8);background:transparent;border:0;cursor:pointer;
  padding:8px 13px 7px;color:var(--ink-2);white-space:nowrap;
  border-bottom:2px solid transparent;
}
.subtab:first-child{padding-left:0}
.subtab:hover{color:var(--ink)}
.subtab[aria-current="page"]{color:var(--ink);border-bottom-color:var(--ink);font-weight:500}

/* ---------- the report ----------
   Channel-first: each channel is a complete report carrying its own three
   numbers, because 86.2 % coverage is the average of MDR's 78.4 and WDR's 87.6
   and describes neither. The lede holds no combined total at all, only an index
   repeating each channel's own headline.                                    */
.page{max-width:1080px;margin:0 auto;padding:26px 26px 70px}
/* A page-sized surface — the report and the rule workbench — covers the deck
   between the masthead and the key row rather than scrolling under it. Both
   carry their own masthead line, which is why the bar itself stays. */
.pagewrap{
  position:fixed;left:0;right:0;top:var(--mast-h);bottom:var(--keys-h);z-index:var(--z-page);
  overflow:auto;background:var(--paper);
}
.lede{display:grid;grid-template-columns:minmax(0,1fr) 430px;gap:40px;align-items:start;padding-bottom:var(--sp-13);border-bottom:1px solid var(--ink)}
.lede h1,.lede .lede-h{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-21);line-height:1.1;letter-spacing:-.016em;margin:var(--sp-3) 0 var(--sp-5);
}
.lede p{margin:0;max-width:56ch}
.tri{display:flex;flex-direction:column;gap:1px;background:var(--rule);border:1px solid var(--rule)}
.tri-r{display:flex;align-items:baseline;gap:var(--sp-5);background:var(--sheet);padding:9px 12px}
/* `flex:0 0 auto` so a five-figure count keeps its own width: shrunk to the
   min-width it overflowed its box and ran into the label beside it. */
.tri-r .big{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-18);line-height:1;min-width:52px;flex:0 0 auto;
}
.tri-r .lb{font-size:var(--fs-7);color:var(--ink-2);line-height:1.35;min-width:0}
.tri-r .lb b{display:block;color:var(--ink);font-weight:500;font-size:var(--fs-9)}
.tri-r .sw{width:4px;align-self:stretch;flex:0 0 4px}

.index{border:1px solid var(--rule);background:var(--sheet)}
.index-hd{padding:7px 12px 6px;border-bottom:1px solid var(--rule)}
.index-r{
  display:flex;align-items:baseline;gap:var(--sp-6);padding:9px 12px;width:100%;
  border:0;border-bottom:1px solid var(--rule);background:transparent;
  font:inherit;cursor:pointer;text-align:left;
}
.index-r:hover{background:var(--shade)}
.index-r .nm{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-14);flex:1 1 auto;letter-spacing:-.003em;
}
.tri3{display:flex;gap:var(--sp-5);flex:0 0 auto}
.tri3 b{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-17);line-height:1;width:56px;text-align:right;
}
.tri3 b:nth-child(1){color:var(--ink)}
.tri3 b:nth-child(2),.tri3 b:nth-child(3){color:var(--ink-2)}
.index-k{display:flex;gap:var(--sp-5);justify-content:flex-end;padding:5px 12px 7px}
.index-k span{
  width:56px;text-align:right;font-size:var(--fs-4);letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-3);
}
.index-k span:first-child{color:var(--ink-2)}

.chanrep{margin-top:40px;padding-top:var(--sp-2)}
.chanrep-hd{
  display:flex;align-items:baseline;gap:var(--sp-6);flex-wrap:wrap;
  border-bottom:2px solid var(--ink);padding-bottom:7px;margin-bottom:var(--sp-7);
}
.chanrep-hd h2{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-19);margin:0;letter-spacing:-.012em;
}
.chanrep-hd .sub{margin-left:auto;font-size:var(--fs-8);color:var(--ink-2)}
.chanrep .tri{max-width:620px}
.chanrep .owner{margin-top:30px}
.chanrep .owner-hd h3{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-15);margin:0;letter-spacing:-.003em;
}
.chanrep .owner-hd .who{
  font-size:var(--fs-5);letter-spacing:.07em;text-transform:uppercase;padding:1.5px 6px;color:var(--ink);
}
.owner{margin-top:var(--sp-17)}
/* Wikidata's presenters against the listing (#643): one line after the reports. */
.xhosts{margin-top:var(--sp-17);padding-top:var(--sp-7);border-top:1px solid var(--rule)}
.owner-hd{display:flex;align-items:baseline;gap:11px;border-bottom:1px solid var(--ink);padding-bottom:var(--sp-3)}
.owner-hd .cnt{margin-left:auto;font-size:var(--fs-7);color:var(--ink-2)}
.owner > p{font-family:var(--prose);font-size:var(--fs-13);line-height:1.5;margin:var(--sp-5) 0 var(--sp-1);max-width:64ch;color:var(--ink-2)}
.owner > p.prose{color:var(--ink)}

.chan{margin-top:var(--sp-10)}
.chan-hd{display:flex;align-items:baseline;gap:9px;padding-bottom:var(--sp-2);flex-wrap:wrap}
.chan-hd .lbl{font-size:var(--fs-11);font-weight:500}
.chan-hd .sub{font-size:var(--fs-6);color:var(--ink-3)}
.chan-hd .rt{margin-left:auto;font-size:var(--fs-7);color:var(--ink-2)}
.sevbar{display:flex;height:7px;background:var(--shade);margin-bottom:7px}
.pat{display:flex;gap:11px;align-items:baseline;padding:var(--sp-3) 0;border-bottom:1px solid var(--rule)}
.pat .c{flex:0 0 34px;text-align:right;font-variant-numeric:tabular-nums;font-size:var(--fs-11)}
.pat .sw{flex:0 0 4px;align-self:stretch}
.pat .txt{flex:1 1 auto;min-width:0}
.pat .msg{font-size:var(--fs-10)}
/* The field a finding is about (#1157): the message no longer names it. */
.pat .fk,.repbc .fi .fk{font-family:var(--mono);font-size:var(--fs-8);color:var(--ink-3)}
/* The example WRAPS. It was one ellipsised line — `e.g. "pipe characte…` —
   which is the half of the finding a planner can act on: the message names the
   shape, the example is the text in their own listing (#397). */
.pat .ex{display:block;font-size:var(--fs-8);color:var(--ink-2);margin-top:var(--sp-1);overflow-wrap:anywhere}
.pat .ex q{color:var(--ink);quotes:"\201C" "\201D"}
.pat .go{flex:0 0 auto;text-align:right;white-space:nowrap;font-size:var(--fs-7);color:var(--ink-2);border-bottom:1px solid var(--rule)}
.covwrap{display:flex;align-items:center;gap:11px;padding:var(--sp-3) 0}
.covbar{flex:1;height:7px;background:var(--shade);position:relative}
.covbar span{position:absolute;left:0;top:0;bottom:0;background:var(--tt-green)}
.covwrap .pc{font-size:var(--fs-9);font-variant-numeric:tabular-nums;min-width:52px;text-align:right}
.repday{margin-top:var(--sp-7)}
.repday-hd{display:flex;align-items:baseline;gap:9px;padding:5px 0 3px;border-bottom:1px solid var(--rule)}
.repday-hd .d{font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;font-size:var(--fs-9)}
.repday-hd .n{margin-left:auto;font-size:var(--fs-6);color:var(--ink-3)}
/* The WHOLE row opens the slot (#397) — it is a button, so the affordance is
   the row a reader is already reading rather than a word at the end of it. */
.repbc{display:flex;gap:var(--sp-5);align-items:baseline;padding:5px 0 5px 0;border-bottom:1px solid var(--rule);
  width:100%;text-align:left;font:inherit;color:inherit;background:transparent;
  border-top:0;border-left:0;border-right:0;cursor:pointer}
.repbc:hover{background:var(--shade)}
.repbc .t{flex:0 0 46px;font-variant-numeric:tabular-nums;font-size:var(--fs-9);color:var(--ink-2)}
.repbc .b{flex:1 1 auto;min-width:0}
.repbc .ti{font-size:var(--fs-10)}
.repbc .fi{display:block;font-size:var(--fs-8);color:var(--ink-2);margin-top:var(--sp-1);overflow-wrap:anywhere}
.repbc .fi .dg{color:var(--match);padding-right:var(--sp-2)}
.repbc .go{
  flex:0 0 auto;font-size:var(--fs-7);color:var(--ink-2);
  border-bottom:1px solid var(--rule);
}

/* ---------- the imported files (#331) ---------- */
/* Genuinely tabular data — one row per imported file, one column per fact — so
   it is a table, and the table scrolls INSIDE its own box rather than letting
   the page scroll sideways. Eight columns do not fit a phone and none of them
   is droppable: a figure a reader cannot reach is worse than one they have to
   scroll to. */
.srtbl{overflow-x:auto;margin-top:var(--sp-7)}
.srtbl table{border-collapse:collapse;width:100%;min-width:640px}
.srtbl th{
  text-align:left;font-family:var(--disp);font-variation-settings:"wdth" 125;
  font-weight:800;font-size:var(--fs-5);letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-2);padding:5px 10px 4px 0;border-bottom:1px solid var(--ink);
  white-space:nowrap;
}
.srtbl th.num,.srtbl td.num{text-align:right;font-variant-numeric:tabular-nums}
.srtbl td{
  padding:7px 10px 7px 0;border-bottom:1px solid var(--rule);
  font-size:var(--fs-10);vertical-align:top;
}
.srtbl tr.off td{color:var(--ink-3)}
/* The file a broadcast's `source` line opened this surface on (#652). */
.srtbl tr.here td{background:var(--shade)}
.srtbl tr.here td:first-child{box-shadow:inset 2px 0 0 var(--ink)}
.srtbl code{font-family:var(--mono);font-size:var(--fs-8)}
/* The second line under a cell: the week's start date, what the stored counter
   said, how many broadcasts a count is spread over. It is the qualifier that
   keeps a bare number from over-claiming, so it rides with the number rather
   than in a legend. */
.srtbl .sub{display:block;font-size:var(--fs-6);color:var(--ink-3);margin-top:var(--sp-1);white-space:nowrap}
.srtbl .acchip{margin-left:var(--sp-3)}

/* ---------- the rule workbench ---------- */
.two{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:30px;align-items:start}
/* The workbench's vertical rhythm. These were `style="margin-top:…"` attributes
   until #572 retired `style-src-attr 'unsafe-inline'` — spacing, never a
   decision, so each is named for the place it spaces rather than for its
   number: the form under the lede, the heading that opens it, and the two
   depths of section that follow (30px where a section starts a new subject,
   26px between the siblings of one). */
.two.rw-form{margin-top:30px}
.sect-hd.rw-lead{margin-bottom:13px}
.sect.rw-major{margin-top:30px}
.sect.rw-minor{margin-top:var(--sp-13)}
#ruleDesc{margin-top:11px}
.fld{margin-bottom:13px}
.fld label{display:block;margin-bottom:3px}
.inp{
  width:100%;border:1px solid var(--rule-strong);background:var(--sheet);padding:7px 9px;
  font:inherit;font-size:var(--fs-10);color:var(--ink);
}
textarea.inp{resize:vertical;line-height:1.6}
.inp.pattern{font-size:var(--fs-11);line-height:1.6;background:var(--shade);border-color:var(--ink);min-height:56px}
.inp:focus{outline:2px solid var(--match);outline-offset:-1px}
.inp::placeholder{color:var(--ink-3)}
.inp[readonly]{background:var(--shade);color:var(--ink-2)}
.row3{display:grid;grid-template-columns:repeat(3,1fr);gap:11px}
.note{font-size:var(--fs-7);color:var(--ink-2);margin-top:var(--sp-2);line-height:1.45}
.note b{color:var(--ink);font-weight:500}
.note.err{color:var(--match)}
.btns{display:flex;gap:9px;margin-top:17px;align-items:center;flex-wrap:wrap}
.btn{
  font:inherit;font-size:var(--fs-9);padding:6px 15px;border:1px solid var(--solid-edge);white-space:nowrap;
  background:var(--solid);color:var(--on-solid);cursor:pointer;
}
.btn.ghost{background:transparent;color:var(--ink)}
/* Disabled by token, not by `opacity` (#782): the button gives up its fill
   and its ink takes the third grade, both of which stay measurable. */
.btn[disabled]{background:var(--shade);border-color:var(--rule-strong);color:var(--ink-3);cursor:default}
.btns .warn{font-size:var(--fs-7);color:var(--ink-2);margin-left:auto}
.spark{display:flex;align-items:flex-end;gap:var(--sp-1);height:46px;margin:9px 0 4px}
/* No background here: every bar carries its RULE's family class, and a
   default with element specificity would beat it. */
.spark i{flex:1;display:block;min-height:1px}
.spark i.dim{background:var(--tt-stone);height:2px}
.sparkax{display:flex;justify-content:space-between;font-size:var(--fs-5);color:var(--ink-3)}
.desc{font-family:var(--prose);font-size:var(--fs-13);line-height:1.55;color:var(--ink);white-space:pre-wrap}

/* the rule workbench, refused */
.refusal{max-width:620px;margin:0 auto;padding:70px 0 40px}
.refusal .code{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-23);line-height:1;color:var(--match);letter-spacing:-.02em;
}
.refusal h1{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-19);margin:var(--sp-5) 0 var(--sp-6);letter-spacing:-.01em;
}
.refusal p{font-family:var(--prose);font-size:var(--fs-14);line-height:1.55;margin:0 0 var(--sp-6);max-width:56ch}
.refusal code{font-size:var(--fs-11);background:var(--shade);padding:1px 4px}
.refusal .go{
  display:inline-block;margin-top:var(--sp-4);font:inherit;font-size:var(--fs-10);
  border:0;background:none;padding:0;cursor:pointer;
  border-bottom:2px solid var(--tt-blue);color:var(--ink);
}

/* ---------- LLM proposals ---------- */
.llmbar{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:9px;padding:var(--sp-1) 0 0}
.llmlbl{display:block;margin:9px 0 3px}
.llmout{
  margin-top:var(--sp-5);border-left:3px solid var(--tt-yellow-2);padding:8px 0 8px 11px;
  font-size:var(--fs-8);color:var(--ink-2);line-height:1.5;
}
.llmout b{color:var(--ink);font-weight:500}
.llmout .draft{font-size:var(--fs-9);color:var(--ink);word-break:break-all;padding:3px 0}

/* ---------- overlays ---------- */
/* Every dialog carries its own scrim, so nothing has to remember to show one
   (#678). Two layers, because one cannot do both jobs:
   - the DIMMING is an outset box-shadow. A shadow paints outside the border
     box, so it can never land on the sheet. A `::before` scrim cannot dim: the
     fixed-position `.dlg` is a stacking context, and its `z-index:-1` child
     paints ABOVE the dialog's own background (and under its text), so the
     sheet goes grey and the page stays bright;
   - the CLICK SHIELD is a transparent `::before` over the whole viewport, so a
     click beside the dialog lands on the dialog, not the page under it.
   Both need `.dlg` to carry NO transform (nor filter, contain, will-change):
   any of those makes the dialog the containing block of its fixed
   descendants, and the viewport-wide `inset:0` shrinks to the dialog's box.
   Hence the centring by `inset:0;margin:auto`, not `translate(-50%,-50%)`. */
.dlg::before{content:"";position:fixed;inset:0;z-index:var(--z-behind)}
.dlg{
  position:fixed;z-index:var(--z-dialog);background:var(--sheet);border:1px solid var(--ink);
  inset:0;margin:auto;height:fit-content;
  box-shadow:0 0 0 100vmax var(--scrim);
  width:min(720px,calc(100vw - 28px));max-height:calc(100vh - 56px);
  display:flex;flex-direction:column;
}
.dlg-hd{display:flex;align-items:baseline;gap:var(--sp-5);padding:10px 14px 9px;border-bottom:1px solid var(--ink)}
.dlg-hd h2{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-14);margin:0;letter-spacing:-.004em;
}
.dlg-hd .n{margin-left:auto;font-size:var(--fs-6);color:var(--ink-3)}
.dlg-hd .x{
  font:inherit;font-size:var(--fs-12);line-height:1;color:var(--ink-2);cursor:pointer;
  background:transparent;border:1px solid var(--rule);padding:1px 6px 3px;
}
.dlg-bd{padding:var(--sp-6) var(--sp-7) var(--sp-7);overflow:auto;min-height:0}
.dlg-ft{display:flex;gap:9px;align-items:center;padding:var(--sp-5) var(--sp-7);border-top:1px solid var(--rule);flex-wrap:wrap}
.dlg-ft .grow{flex:1}
.dlg.narrow{width:min(400px,calc(100vw - 28px))}
/* The account dialog (#627): two forms, one subject each, the second set off
   by a rule rather than by a second box. */
.pfsec + .pfsec{border-top:1px solid var(--rule);margin-top:var(--sp-8);padding-top:var(--sp-7)}
.pfsec > .eyebrow{margin:0 0 var(--sp-5)}
.pfsec .btns{margin-top:var(--sp-3)}
#profilePanel .dlg-hd .x{margin-left:auto}

/* the sign-in view: not a dialog over the app, but the whole page —
   there is no public access, so there is nothing behind it to see. */
.signin{
  position:fixed;inset:0;z-index:var(--z-signin);background:var(--paper);
  display:flex;align-items:center;justify-content:center;padding:var(--sp-10);
}
.signin form{width:min(340px,100%)}
.signin .wordmark{font-size:var(--fs-20);display:block;margin-bottom:var(--sp-2)}
.signin .lead{font-size:var(--fs-9);color:var(--ink-2);margin:0 0 var(--sp-9)}
.signin .fld label{margin-bottom:var(--sp-2)}

/* the error page (#495) — the whole page for a browser that mistyped a URL,
   built by `webapp/errors.py` rather than by any JS module. It is styled HERE,
   with the same tokens as everything else, for the reason the theme tests
   state: a colour written into that module would be right in light and wrong
   in dark with nothing to report it. It takes the sign-in view's shape because
   it answers the same shape of question — one centred column, nothing behind
   it — and it is deliberately not a dialog: there is no app underneath. */
.errpage{display:flex;align-items:center;justify-content:center;min-height:100%;padding:24px}
.err{width:min(420px,100%)}
.err .wordmark{font-size:var(--fs-20);display:block;margin-bottom:var(--sp-7)}
.err-code{margin:0 0 var(--sp-3)}
.err-head{
  font-family:var(--disp);font-variation-settings:"wdth" 112;
  font-weight:700;font-size:var(--fs-17);line-height:1.2;margin:0 0 var(--sp-4);color:var(--ink);
}
.err-lead{font-family:var(--prose);font-size:var(--fs-14);line-height:1.55;color:var(--ink-2);margin:0 0 var(--sp-7)}
/* The address that produced it: the machine's own data, so the machine's face.
   `anywhere` because a stale link can carry a very long query. */
.err-path{
  font-size:var(--fs-9);color:var(--ink-3);margin:0 0 var(--sp-9);overflow-wrap:anywhere;
  border-left:2px solid var(--rule);padding-left:var(--sp-4);
}
.err-links{display:flex;gap:var(--sp-8);flex-wrap:wrap}
.err-links a{font-size:var(--fs-9);color:var(--ink);border-bottom:1px solid var(--match)}
.err-links a:hover{color:var(--match)}

.diffrow{display:flex;gap:var(--sp-5);align-items:baseline;padding:5px 0;border-bottom:1px solid var(--rule)}
.diffrow .p{flex:0 0 150px;font-size:var(--fs-8);color:var(--ink-2);overflow-wrap:anywhere}
.diffrow .d{flex:1 1 auto;min-width:0;font-size:var(--fs-9);overflow-wrap:anywhere}
.diffrow .d del{background:var(--tt-stone);color:var(--on-tint);text-decoration:line-through;padding:0 3px}
.diffrow .d ins{background:var(--tt-green);color:var(--on-tint);text-decoration:none;padding:0 3px}

/* ---------- toast ---------- */
/* The host of the two live regions (#776). It is fixed and always rendered —
   a `display:none` region is not announced — and draws nothing while empty;
   only a `.toast` inside it takes pointer events. The tone is the card's
   colour: neutral is the inverted ink, `ok` and `warn` are MARKS (a tint
   carrying --on-tint, #478) in the success and caution families, and `err` is
   the one saturated colour, which is also why only it carries a close key. */
.toasts{
  position:fixed;left:50%;bottom:44px;transform:translateX(-50%);z-index:var(--z-toast);
  display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);
  width:min(520px,calc(100vw - 24px));pointer-events:none;
}
.toast{
  pointer-events:auto;display:flex;align-items:baseline;gap:var(--sp-5);max-width:100%;
  background:var(--ink);color:var(--paper);font-size:var(--fs-9);padding:7px 14px;
}
.toast-msg{min-width:0;overflow-wrap:anywhere}
.toast.ok{background:var(--tt-green);color:var(--on-tint);border-left:4px solid var(--on-tint)}
.toast.warn{background:var(--tt-yellow);color:var(--on-tint);border:1px dashed var(--on-tint)}
.toast.err{background:var(--match);color:var(--paper);font-weight:600}
.toast-x{
  flex:0 0 auto;border:0;background:transparent;color:inherit;font:inherit;
  font-size:var(--fs-14);line-height:1;padding:0 var(--sp-1);cursor:pointer;
}
.toast-x:focus-visible{outline-color:var(--paper)}

/* ---------- mobile: one column at a time ----------
   A corpus of thousands of broadcasts cannot live on one scrolling page, so the
   three columns become three screens and the fastext row becomes the way
   between them.                                                             */
.stepbar{display:none}
.mobonly{display:none}

/* ---------- accounts (issue #158) ----------
   CARDS, never a table. This surface is built phone-first — the operations on
   it are the ones done with no laptop, and the terminal it replaces is one a
   mobile browser cannot type into at all — so an eight-column table would put
   the failure back where it was. Every row is a card whose first line carries
   the identifying fact and whose rest sits under it, which reads the same at
   390px and at 1440 and only gets wider.

   The colour discipline is the app's: pastel spots on the chips, the one
   saturated --match reserved for a state that is WRONG (disabled, expired, a
   refused sign-in). Nothing here paints a surface.                          */
.acsec{border-top:1px solid var(--rule);margin-top:var(--sp-9)}
.acsec:first-of-type{border-top:0}
.acsec-h{
  display:flex;align-items:baseline;gap:9px;width:100%;padding:13px 0 9px;
  background:none;border:0;cursor:pointer;text-align:left;color:var(--ink);
}
.acsec-h .n{margin-left:auto;font-size:var(--fs-7);color:var(--ink-3);font-family:var(--mono)}
.acsec-h .chev{font-size:var(--fs-5);color:var(--ink-3);transition:transform .12s}
/* Inverted against `.ovsec.open`, like `dom.js`'s `collapsible` everywhere
   else: the class is ADDITIVE, so a script that fails to load leaves every
   section expanded rather than permanently shut. */
.acsec.collapsed .acsec-b{display:none}
.acsec.collapsed .acsec-h .chev{transform:rotate(-90deg)}
.acsec-b{padding-bottom:var(--sp-3)}

.accard{border:1px solid var(--rule);background:var(--sheet);padding:9px 11px;margin-bottom:7px}
.accard.off{background:var(--shade)}
.accard.new{border-color:var(--ink)}
.acrow{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.acrow b{font-size:var(--fs-11);font-family:var(--mono);letter-spacing:-.01em}
.acrow .grow{flex:1}
.acmeta{font-size:var(--fs-7);color:var(--ink-2);margin-top:var(--sp-2);line-height:1.45}
/* The problems an ingest run reports (#481). `.acmeta` is the muted register
   every card's second line is set in, so without this the failures read as
   quietly as the metadata beside them. `.acchip.bad` is the same mark one
   element over. */
.acmeta.bad{color:var(--match)}
.acchips{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-top:var(--sp-3)}
.acgroup{
  font-family:var(--disp);font-size:var(--fs-5);letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin:12px 0 5px;
}
.acgroup:first-child{margin-top:var(--sp-1)}
.acnone{font-size:var(--fs-8);color:var(--ink-3);padding:var(--sp-3) 0}

.acchip{
  display:inline-block;font-size:var(--fs-5);font-family:var(--mono);line-height:1.6;
  padding:0 var(--sp-3);border:1px solid var(--rule);background:var(--shade);color:var(--ink-2);
}
.acchip.wide{background:var(--tt-cyan);border-color:var(--tt-cyan-2);color:var(--on-tint)}
.acchip.warn{background:var(--tt-yellow);border-color:var(--tt-yellow-2);color:var(--on-tint)}
.acchip.bad{border-color:var(--match);color:var(--match)}

/* `lnk` is the card's own affordance: a text control that does not look like a
   button, because a card with three buttons on its title line reads as a form
   before you have decided to open one. */
.lnk{
  background:none;border:0;border-bottom:1px solid var(--rule);padding:0;
  font-size:var(--fs-7);color:var(--ink-2);cursor:pointer;font-family:var(--disp);
}
.lnk:hover{color:var(--ink);border-color:var(--ink)}

.acform{border-top:1px solid var(--rule);margin-top:9px;padding-top:var(--sp-5)}
.acform .fld{margin-bottom:var(--sp-5)}
.acform select.inp{appearance:auto}
.acboxes{display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-7);padding:3px 0}
.acbox{display:flex;align-items:center;gap:5px;font-size:var(--fs-8);color:var(--ink)}
/* 16px is a thumb target, not a decoration: the checkbox list IS the channel
   allowlist editor, and it is the control this surface exists to reach. */
.acbox input{width:16px;height:16px}
.acbtns{display:flex;flex-wrap:wrap;gap:7px;margin:var(--sp-6) 0 var(--sp-2)}
/* An invited name and the `.<company>` the server appends to it (#751): the
   suffix sits after the field, in the field's own face, so what is typed and
   what the account will be called read as one name. */
.acsuffix{display:flex;align-items:center;gap:var(--sp-1)}
.acsuffix .inp{flex:1 1 auto;min-width:0}
.acsfx{font-family:var(--mono);font-size:var(--fs-9);color:var(--ink-2);white-space:nowrap}
.btn.wide{width:100%;margin-top:var(--sp-2)}

/* The sign-in trail is a LINE rather than a card: it is read by scanning, it
   holds no controls, and forty of them in cards would bury the four sections
   above it. */
.acline{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-4);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--rule);font-size:var(--fs-8);
}
.acline b{font-family:var(--mono);font-size:var(--fs-8);font-weight:500}
.acwhen{font-family:var(--mono);font-size:var(--fs-6);color:var(--ink-3);flex:0 0 132px}
.acout{color:var(--ink-2)}
.acline.bad .acout{color:var(--match)}
.acip{margin-left:auto;font-family:var(--mono);font-size:var(--fs-5);color:var(--ink-3)}

/* The one-time password. `--mono` and a wide letter-spacing because the value
   has to be read aloud or typed on another device, and that is the one job
   this field has: an `l` that could be a `1` costs a phone call. */
#pwValue{
  font-family:var(--mono);font-size:var(--fs-14);letter-spacing:.06em;
  background:var(--shade);border-color:var(--ink);text-align:center;
}
#pwWarn{border-left:2px solid var(--match);padding-left:var(--sp-4)}

/* ---------- client API keys, quotas, usage (issue #680) ----------
   The accounts page's cards and chips, plus three things of its own: a quota
   line (a bar, the numbers, whose limit it is), a key line inside a company's
   card, and the usage tables — which borrow `.srtbl` and so scroll inside
   themselves at 390px rather than widening the page. The bar's green is the
   report's coverage bar; the one saturated colour marks a spent budget. */
.akq{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-3);font-size:var(--fs-7);color:var(--ink-2)}
.akq .lb{flex:0 0 88px;white-space:nowrap;font-family:var(--disp);font-size:var(--fs-5);letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}
.akq .num{font-family:var(--mono);font-variant-numeric:tabular-nums}
.akreset{font-size:var(--fs-6);color:var(--ink-3)}
.akbar{display:inline-block;flex:1 1 90px;min-width:70px;height:7px;background:var(--shade);position:relative}
.akbar span{position:absolute;left:0;top:0;bottom:0;background:var(--tt-green)}
.akbar.over span{background:var(--match)}
.srtbl .akbar{display:block;width:120px;margin-top:var(--sp-2)}
.akkeys{margin-top:var(--sp-4);border-top:1px solid var(--rule)}
.akkey{padding:var(--sp-3) 0;border-bottom:1px solid var(--rule)}
.akkey:last-child{border-bottom:0}
.akkey code{font-family:var(--mono);font-size:var(--fs-7);color:var(--ink-2)}
.akkey.off b{color:var(--ink-3);font-weight:500}
.akfilter{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--sp-4) var(--sp-6);margin:var(--sp-2) 0 var(--sp-4)}
.akfilter .fld{margin:0;min-width:130px}
.akfilter select.inp{appearance:auto}
.akfilter .acbtns{margin:0}
.aktbl{margin-top:var(--sp-2)}
.akerr{color:var(--match)}
#akSecret{
  font-family:var(--mono);font-size:var(--fs-11);letter-spacing:.02em;
  background:var(--shade);border-color:var(--ink);
}
#akSecretWarn{border-left:2px solid var(--match);padding-left:var(--sp-4);font-weight:600}

/* ---------- settings: the canonical config files (issue #701) ----------
   The accounts page's cards, forms and chips, plus three things of its own:
   the file line with its Export at the top of a section, a sub-feed row (its
   slug, label and names side by side, wrapping on a phone), and the go-live
   checklist — each check a full-width line, because the sentence IS the
   check and a phone has to show all of it. */
.sttools{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-3) var(--sp-5);margin:var(--sp-1) 0 var(--sp-4)}
.sttools code{font-family:var(--mono);font-size:var(--fs-7);color:var(--ink-2)}
.stsubs{margin:var(--sp-2) 0 var(--sp-3);border-top:1px solid var(--rule)}
.stsub{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);padding:5px 0;border-bottom:1px solid var(--rule)}
.stsub code{font-family:var(--mono);font-size:var(--fs-7);color:var(--ink-2);flex:0 0 150px}
.stsub .inp{flex:1 1 150px;min-width:0}
.stchecks{display:flex;flex-direction:column;gap:var(--sp-3);margin:var(--sp-4) 0}
.stcheck{align-items:flex-start;line-height:1.45}
.stcheck input{flex:0 0 auto;margin-top:var(--sp-1)}
/* Step 2 (#701): the verbatim textareas (whitespace and newlines kept), and a
   work row's narrow year/minutes inputs. */
textarea.stverbatim{font-family:var(--mono);font-size:var(--fs-9);white-space:pre-wrap}
.stwork .stnum{flex:0 1 90px;min-width:0}

@media (max-width:900px){
  .deck{grid-template-columns:1fr;height:auto}
  .col{overflow:visible;border-right:0}
  body[data-step] .deck > .col{display:none}
  body[data-step="1"] .deck > .col-1,
  body[data-step="2"] .deck > .col-2,
  body[data-step="3"] .deck > .col-3{display:block}
  /* On a phone the whole app is one scrolling document — the masthead is
     static, the deck is `height:auto`, the key row is sticky — and a page
     surface has to join it rather than float over it. It did not: the override
     here set `height:auto;overflow:visible` but left `position:fixed` with
     `top`/`bottom`, and those pin a fixed box to the viewport whatever
     `height` says. So the report sat in a 771px box holding 13,202px of
     content, with `overflow:visible` having taken away the base rule's
     `overflow:auto` — nothing to scroll, and a fixed box adds nothing to the
     document's own scroll either. The page was simply cut off, which is also
     why zooming out did not reach it.

     `position:static` puts it in flow, where `top`/`bottom` stop applying: the
     document grows and scrolls as it does everywhere else on a phone. The deck
     it used to cover by painting over it is hidden instead — the panels sit
     before `.keys` in the document, so the sticky key row still pins to the
     bottom and the masthead keeps carrying "back to the listing", which is the
     close control these surfaces deliberately have instead of a ✕ of their
     own. Covering it, as the fixed box did (`--mast-h` is the DESKTOP mast's
     height; the mobile one is static and taller), left that control unreachable
     on the one layout where it is the only one. */
  .pagewrap{position:static;height:auto;overflow:visible}
  body[data-surface="report"] .deck,
  body[data-surface="rule"] .deck,
  body[data-surface="accounts"] .deck,
  body[data-surface="apikeys"] .deck,
  body[data-surface="company"] .deck,
  body[data-surface="settings"] .deck,
  body[data-surface="ingest"] .deck,
  body[data-surface="characterize"] .deck,
  body[data-surface="conflicts"] .deck,
  body[data-surface="credits"] .deck,
  body[data-surface="wikidata"] .deck,
  body[data-surface="sources"] .deck,
  /* The step bar navigates BETWEEN COLUMNS, and a page surface has none on
     screen — it went out with the deck when the fixed box painted over both. */
  body[data-surface="report"] .stepbar,
  body[data-surface="rule"] .stepbar,
  body[data-surface="accounts"] .stepbar,
  body[data-surface="apikeys"] .stepbar,
  body[data-surface="company"] .stepbar,
  body[data-surface="settings"] .stepbar,
  body[data-surface="ingest"] .stepbar,
  body[data-surface="characterize"] .stepbar,
  body[data-surface="conflicts"] .stepbar,
  body[data-surface="credits"] .stepbar,
  body[data-surface="wikidata"] .stepbar,
  body[data-surface="sources"] .stepbar{display:none}
  .mast{padding:9px 13px 8px;gap:var(--sp-5);position:static}
  /* The identity chips belong on the wordmark's row, not three lines below it.
     `.mast-right` is a DESKTOP grouping and nothing more — it exists to be
     pushed right by one `margin-left:auto` — but on a phone it groups two
     things that want opposite rows: the chips beside the wordmark, the coverage
     figure under the scope line. `display:contents` dissolves the box so both
     become flex children of `.mast` itself and can be ordered independently;
     no other mechanism lifts a child out of a flex container, and the DOM
     stays exactly as the desktop layout wants it. The row break is then the
     `width:100%` on the two full-width items — a flex item that fills the line
     starts a new one. */
  .mast-right{display:contents}
  .wordmark{order:1}
  /* The theme switch rides IN the box — the owner asked for it beside the
     identity — so the gap and the chip padding are what pay for it. Measured
     at 360px with a signed-in user: 9px gaps put the box 35px over the line,
     6px and a 5px chip padding put it 17px over, and the remaining 17 is what
     `GREP//g` costs over `GREPg`. So at 390 the whole identity now rides the
     wordmark's row where it used to wrap, and at 360 a SIGNED-IN identity —
     three chips — takes the next row WHOLE, which is the box doing its job.
     An API token, the shape with no `sign out`, fits at both.

     The switch was briefly ordered to the last row instead, to keep the
     identity unwrapped at 360. That trades a control three rows from the chip
     it sits beside on every other screen for one row at one width, and the
     row it saves is not the one the listing starts on. */
  .mast-id{order:2;margin-left:auto;gap:var(--sp-3)}
  .mast-id .whoami{padding:2px 5px}
  #whoCo{display:none}          /* see `applyIdentity` — the tooltip keeps it */
  .mast-scope{order:3;width:100%;gap:var(--sp-3)}
  .coverage{order:4;width:100%;white-space:normal}
  .mast .mast-fact.deskfact,
  .mast .mast-sep:has(+ .deskfact){display:none}
  .col-head{position:static}
  /* NOT `static`: both carry their rule as an absolutely-positioned `::after`,
     and a static parent is no containing block — so the rule resolved against
     the INITIAL one and painted at a fixed document coordinate about a
     viewport-height down, a 2px black bar lying across whatever row happened to
     be there. `relative` with no offsets flows identically to `static` (which
     is all this override wants: the phone scrolls the document, so a header
     sticking at `top:33px` would float over the list) while keeping the rule
     attached to the header it underlines.

     `top:auto` on BOTH, and that is the half this override got wrong the
     first time: `static` ignores `top`, `relative` obeys it, so swapping one
     for the other quietly turned the desktop's sticky offsets into real
     displacements. The day line was neutralised and the channel line was not
     — which pushed every channel heading 33px down the page, onto the day
     heading directly beneath it. The day line was not missing on a phone; it
     was underneath. */
  .daybreak,.chbreak{position:relative;top:auto}
  .chgroup .daybreak{top:auto}   /* beats the desktop `top:66px`, same weight */
  .rec{padding:14px 15px 26px}
  .work,.guest{padding:12px 13px 26px}
  .rec-title{font-size:var(--fs-18)}
  .f,.link,.lk{flex-wrap:wrap;gap:3px 12px}
  .f-k,.lk .lk-k{flex:0 0 100%}
  .lk .cnt{margin-left:0}
  .f-lvl{margin-left:auto}
  .ap{flex-wrap:wrap;gap:5px 8px}
  .ap-key{flex:0 0 auto}
  .ap-body{flex:1 1 100%;padding-left:30px}
  .chg-row{flex-wrap:wrap;gap:var(--sp-1) var(--sp-5)}
  .chg-f{flex:0 0 100%}
  /* The sign-in line's time column is a fixed 132px on a desktop so the rows
     align into a scannable gutter; at 390px that is a third of the line, so it
     gives up the width and the row wraps instead. */
  .acwhen{flex:0 0 auto}
  .acip{margin-left:0}
  /* One control per line: three ghost buttons side by side at 390px are three
     targets a thumb cannot tell apart. */
  .acbtns .btn{width:100%}
  .lede{grid-template-columns:1fr;gap:var(--sp-10)}
  .lede h1,.lede .lede-h{font-size:var(--fs-19)}
  .two{grid-template-columns:1fr;gap:22px}
  .row3{grid-template-columns:1fr;gap:0}
  .page{padding:18px 15px 70px}
  .subnav{padding:0 15px}
  .owner-hd,.chan-hd{flex-wrap:wrap;gap:4px 9px}
  .owner-hd .cnt,.chan-hd .rt{margin-left:0;flex:0 0 100%}
  .owner-hd h2,.owner-hd h3{font-size:var(--fs-15)}
  .chanrep-hd h2{font-size:var(--fs-17)}
  .chanrep-hd .sub{margin-left:0;flex:0 0 100%}
  .index-r{flex-wrap:wrap;gap:var(--sp-2) var(--sp-6)}
  .index-r .nm{flex:0 0 100%}
  /* The name takes its own line here, so the three counts have the full row to
     share — one third each, which fits any count this report can produce
     instead of fitting the ones it produced when the width was measured. At a
     fixed 46px a four-figure count simply painted over its neighbour: nothing
     overflowed the row, the glyphs just ran into each other ("7 2 683"). The
     key row below distributes identically, so the labels stay under their
     numbers by construction rather than by two widths being kept in step. */
  .tri3{flex:1 1 100%;min-width:0}
  .tri3 b,.index-k span{flex:1 1 0;min-width:0;width:auto}
  .tri3 b{font-size:var(--fs-16)}
  .pat{gap:var(--sp-4)}
  /* `flex:0 0 100%` plus a 45px indent is 45px wider than the row it sits in —
     the severity chip hung off the right edge of a surface with nothing to
     scroll. The fix for that gave it the rest of the row and left the words
     against its LEFT edge, so `major · 2` read as a second column of the
     message rather than as the grade of it (#397). It takes its own width and
     the row's right edge instead; the message keeps everything else. */
  .pat .go{flex:0 0 auto;margin-left:auto}
  .trace,.guest .plain{font-size:var(--fs-8);padding:11px 10px}
  .chsel{max-height:none}
  .cal-day{padding:6px 0 7px}
  .cal-day .dd{font-size:var(--fs-11)}
  .cal-day .dn{font-size:var(--fs-3)}
  .pw-chip .pw-n{font-size:var(--fs-11)}
  .cal-titles{font-size:var(--fs-15)}
  .refusal{padding:var(--sp-17) 0}
  .refusal .code{font-size:var(--fs-22)}
  .keys .key.legend{display:none}
  /* On the CLASS, not on `.stat` (#481): the reparse KEY carries `deskonly`
     too — `app.js` says so where it explains that the accounts key is
     deliberately not `deskonly` — and while this named the element it sat on,
     the one destructive control in the app stayed on the phone. */
  .keys .deskonly{display:none}
  /* The key strip SCROLLS rather than shrinking its labels to initials: the
     four the concept draws fill the width, and anything after them is one
     swipe away instead of unreadable. */
  .keys{overflow-x:auto;overflow-y:hidden}
  .mobonly{display:flex}
  /* The four steps the concept draws FILL the width; anything after them keeps
     its own size and is one swipe away, rather than every label shrinking to an
     initial so a fifth key can sit on screen. */
  .key.nav{flex:1 0 auto;justify-content:center;min-width:0}
  .key:not(.nav){flex:0 0 auto}
  .stepbar{
    display:flex;align-items:stretch;position:sticky;top:0;z-index:var(--z-stepbar);
    background:var(--sheet);border-bottom:1px solid var(--ink);height:34px;
  }
  .stepbar .back,.stepbar .fwd{
    display:flex;align-items:center;gap:var(--sp-3);padding:0 var(--sp-6);font:inherit;font-size:var(--fs-8);
    color:var(--ink);background:transparent;border:0;cursor:pointer;
  }
  .stepbar .back{border-right:1px solid var(--rule)}
  .stepbar .fwd{border-left:1px solid var(--rule)}
  .stepbar .back .ar,.stepbar .fwd .ar{color:var(--match);font-size:var(--fs-11);line-height:1}
  .stepbar .back[disabled]{color:var(--ink-3);cursor:default}
  .stepbar .back[disabled] .ar{color:var(--ink-3)}
  .stepbar .fwd[disabled]{display:none}
  .stepbar .here{display:flex;align-items:center;gap:7px;padding:0 var(--sp-6);font-size:var(--fs-7);color:var(--ink-2);flex:1;min-width:0}
  .stepbar .here .ord{
    font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
    font-size:var(--fs-4);color:var(--match);letter-spacing:.06em;
  }
  .stepbar .here .nm{
    font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
    font-size:var(--fs-9);color:var(--ink);letter-spacing:.02em;
  }
  /* The step bar already names the column, so the column's own eyebrow is a
     second `01 Browse` two lines under the first. Only the HINT survives here,
     and it gets the whole width — which is also what stops `120 of 153 rows ·
     more below` ellipsising on a 390px screen. */
  .col-head{padding:7px 12px 6px}
  .col-head .ord,.col-head .eyebrow{display:none}
  .col-head .hint{margin-left:0}
  .col-head:not(:has(.hint:not(:empty))){display:none}

  /* The filter bar folds away (#269): on one column it sat on top of the list
     it filters. ONE rule decides what the fold hides, and it lives here, so
     the desktop layout cannot collapse. `#modeSeg` is outside `#filterGroup`
     in the markup and therefore survives the fold — see index.html. */
  .filters.collapsed #filterGroup{display:none}
  #fbToggle::after{content:" ▾";color:var(--match)}
  .filters.collapsed #fbToggle::after{content:" ▸"}
}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* ---------- the teletext seven, as classes ----------
   One hue per target family, a second shade so two rules in the same family
   stay apart. `colors.js` maps a rule's target to one of these seven names and
   its stable key to the shade; nothing else assigns a colour.              */
.c-time{background:var(--tt-cyan)}      .c-time-2{background:var(--tt-cyan-2)}
.c-title{background:var(--tt-red)}      .c-title-2{background:var(--tt-red-2)}
.c-meta{background:var(--tt-yellow)}    .c-meta-2{background:var(--tt-yellow-2)}
.c-desc{background:var(--tt-green)}     .c-desc-2{background:var(--tt-green-2)}
.c-credit{background:var(--tt-magenta)} .c-credit-2{background:var(--tt-magenta-2)}
.c-link{background:var(--tt-blue)}      .c-link-2{background:var(--tt-blue-2)}
.c-gone{background:var(--tt-stone)}     .c-gone-2{background:var(--tt-stone-2)}
/* The fourteen above are the app's biggest run of tinted surfaces and they
   carry the source text itself, so they take the on-tint ink as one rule
   rather than fourteen (#478/#479). The sparkline's bars wear the same classes
   and hold no text, so this costs them nothing. */
.c-time,.c-time-2,.c-title,.c-title-2,.c-meta,.c-meta-2,.c-desc,.c-desc-2,
.c-credit,.c-credit-2,.c-link,.c-link-2,.c-gone,.c-gone-2{color:var(--on-tint)}

/* Isolating one rule: everything it did not write recedes — by giving up its
   MARK, not by having its ink multiplied (#482).

   This state persists for as long as a rule is selected, so what recedes is
   text a reader is left looking at, not a hover flicker. It used to recede by
   `opacity: .3` / `.38`, which took the source text of the broadcast — the
   smallest, densest type in the app — to 1.7 : 1, and the apparatus with it.
   `opacity` blends a whole subtree with what is behind it, so it defeats every
   contrast guarantee the palette makes, silently, in the one direction no
   token can see.

   The mark IS the claim: a tinted span says "a rule read this". So a span that
   is not the selected rule's stops making the claim — it drops to plain text on
   the block's own ground, one grade back — and the selected rule's spans are
   the only colour left on the page, which isolates harder than dimming did.
   The apparatus row does the same thing one element at a time: its numeral
   chip gives up its family tint for the hairline, and the key it names steps
   back a grade. */
.work.iso .sp:not(.lit-sel){background:none;color:var(--ink-2)}
.work.iso .ap:not(.lit-sel) .ap-n{background:var(--rule);color:var(--ink-2)}
.work.iso .ap:not(.lit-sel) .ap-key button,
.work.iso .ap:not(.lit-sel) .ap-out b{color:var(--ink-2);font-weight:400}
.rec.iso .fn:not(.lit-sel){background:var(--rule);color:var(--ink-2)}
.ap.lit-sel{box-shadow:inset 2.5px 0 0 var(--match)}

/* ============================================================
   THE TIE — pure CSS, no script, and it must stay that way.

   A numeral appears in three places at once: on the tinted span in the source
   text, on the extracted value in the record column, and on the rule in the
   apparatus. Hovering ANY of them lights the other two. `body:has([data-n="7"]
   :hover)` is the whole mechanic; the numerals are generated up to --fn-max
   below, which is comfortably above the largest firing count the corpus
   produces (19 on the densest broadcast, 40-odd on the densest WDR Lokalzeit
   parent). A broadcast with more firings than this simply stops linking above
   the cap — it does not break.
   ============================================================ */
body:has([data-n="1"]:hover) .sp[data-n="1"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="1"]:hover) .ap[data-n="1"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="1"]:hover) .fn[data-n="1"]{background:var(--ink);color:var(--paper)}
body:has([data-n="2"]:hover) .sp[data-n="2"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="2"]:hover) .ap[data-n="2"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="2"]:hover) .fn[data-n="2"]{background:var(--ink);color:var(--paper)}
body:has([data-n="3"]:hover) .sp[data-n="3"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="3"]:hover) .ap[data-n="3"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="3"]:hover) .fn[data-n="3"]{background:var(--ink);color:var(--paper)}
body:has([data-n="4"]:hover) .sp[data-n="4"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="4"]:hover) .ap[data-n="4"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="4"]:hover) .fn[data-n="4"]{background:var(--ink);color:var(--paper)}
body:has([data-n="5"]:hover) .sp[data-n="5"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="5"]:hover) .ap[data-n="5"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="5"]:hover) .fn[data-n="5"]{background:var(--ink);color:var(--paper)}
body:has([data-n="6"]:hover) .sp[data-n="6"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="6"]:hover) .ap[data-n="6"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="6"]:hover) .fn[data-n="6"]{background:var(--ink);color:var(--paper)}
body:has([data-n="7"]:hover) .sp[data-n="7"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="7"]:hover) .ap[data-n="7"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="7"]:hover) .fn[data-n="7"]{background:var(--ink);color:var(--paper)}
body:has([data-n="8"]:hover) .sp[data-n="8"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="8"]:hover) .ap[data-n="8"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="8"]:hover) .fn[data-n="8"]{background:var(--ink);color:var(--paper)}
body:has([data-n="9"]:hover) .sp[data-n="9"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="9"]:hover) .ap[data-n="9"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="9"]:hover) .fn[data-n="9"]{background:var(--ink);color:var(--paper)}
body:has([data-n="10"]:hover) .sp[data-n="10"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="10"]:hover) .ap[data-n="10"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="10"]:hover) .fn[data-n="10"]{background:var(--ink);color:var(--paper)}
body:has([data-n="11"]:hover) .sp[data-n="11"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="11"]:hover) .ap[data-n="11"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="11"]:hover) .fn[data-n="11"]{background:var(--ink);color:var(--paper)}
body:has([data-n="12"]:hover) .sp[data-n="12"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="12"]:hover) .ap[data-n="12"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="12"]:hover) .fn[data-n="12"]{background:var(--ink);color:var(--paper)}
body:has([data-n="13"]:hover) .sp[data-n="13"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="13"]:hover) .ap[data-n="13"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="13"]:hover) .fn[data-n="13"]{background:var(--ink);color:var(--paper)}
body:has([data-n="14"]:hover) .sp[data-n="14"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="14"]:hover) .ap[data-n="14"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="14"]:hover) .fn[data-n="14"]{background:var(--ink);color:var(--paper)}
body:has([data-n="15"]:hover) .sp[data-n="15"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="15"]:hover) .ap[data-n="15"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="15"]:hover) .fn[data-n="15"]{background:var(--ink);color:var(--paper)}
body:has([data-n="16"]:hover) .sp[data-n="16"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="16"]:hover) .ap[data-n="16"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="16"]:hover) .fn[data-n="16"]{background:var(--ink);color:var(--paper)}
body:has([data-n="17"]:hover) .sp[data-n="17"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="17"]:hover) .ap[data-n="17"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="17"]:hover) .fn[data-n="17"]{background:var(--ink);color:var(--paper)}
body:has([data-n="18"]:hover) .sp[data-n="18"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="18"]:hover) .ap[data-n="18"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="18"]:hover) .fn[data-n="18"]{background:var(--ink);color:var(--paper)}
body:has([data-n="19"]:hover) .sp[data-n="19"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="19"]:hover) .ap[data-n="19"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="19"]:hover) .fn[data-n="19"]{background:var(--ink);color:var(--paper)}
body:has([data-n="20"]:hover) .sp[data-n="20"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="20"]:hover) .ap[data-n="20"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="20"]:hover) .fn[data-n="20"]{background:var(--ink);color:var(--paper)}
body:has([data-n="21"]:hover) .sp[data-n="21"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="21"]:hover) .ap[data-n="21"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="21"]:hover) .fn[data-n="21"]{background:var(--ink);color:var(--paper)}
body:has([data-n="22"]:hover) .sp[data-n="22"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="22"]:hover) .ap[data-n="22"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="22"]:hover) .fn[data-n="22"]{background:var(--ink);color:var(--paper)}
body:has([data-n="23"]:hover) .sp[data-n="23"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="23"]:hover) .ap[data-n="23"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="23"]:hover) .fn[data-n="23"]{background:var(--ink);color:var(--paper)}
body:has([data-n="24"]:hover) .sp[data-n="24"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="24"]:hover) .ap[data-n="24"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="24"]:hover) .fn[data-n="24"]{background:var(--ink);color:var(--paper)}
body:has([data-n="25"]:hover) .sp[data-n="25"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="25"]:hover) .ap[data-n="25"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="25"]:hover) .fn[data-n="25"]{background:var(--ink);color:var(--paper)}
body:has([data-n="26"]:hover) .sp[data-n="26"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="26"]:hover) .ap[data-n="26"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="26"]:hover) .fn[data-n="26"]{background:var(--ink);color:var(--paper)}
body:has([data-n="27"]:hover) .sp[data-n="27"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="27"]:hover) .ap[data-n="27"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="27"]:hover) .fn[data-n="27"]{background:var(--ink);color:var(--paper)}
body:has([data-n="28"]:hover) .sp[data-n="28"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="28"]:hover) .ap[data-n="28"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="28"]:hover) .fn[data-n="28"]{background:var(--ink);color:var(--paper)}
body:has([data-n="29"]:hover) .sp[data-n="29"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="29"]:hover) .ap[data-n="29"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="29"]:hover) .fn[data-n="29"]{background:var(--ink);color:var(--paper)}
body:has([data-n="30"]:hover) .sp[data-n="30"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="30"]:hover) .ap[data-n="30"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="30"]:hover) .fn[data-n="30"]{background:var(--ink);color:var(--paper)}
body:has([data-n="31"]:hover) .sp[data-n="31"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="31"]:hover) .ap[data-n="31"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="31"]:hover) .fn[data-n="31"]{background:var(--ink);color:var(--paper)}
body:has([data-n="32"]:hover) .sp[data-n="32"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="32"]:hover) .ap[data-n="32"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="32"]:hover) .fn[data-n="32"]{background:var(--ink);color:var(--paper)}
body:has([data-n="33"]:hover) .sp[data-n="33"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="33"]:hover) .ap[data-n="33"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="33"]:hover) .fn[data-n="33"]{background:var(--ink);color:var(--paper)}
body:has([data-n="34"]:hover) .sp[data-n="34"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="34"]:hover) .ap[data-n="34"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="34"]:hover) .fn[data-n="34"]{background:var(--ink);color:var(--paper)}
body:has([data-n="35"]:hover) .sp[data-n="35"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="35"]:hover) .ap[data-n="35"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="35"]:hover) .fn[data-n="35"]{background:var(--ink);color:var(--paper)}
body:has([data-n="36"]:hover) .sp[data-n="36"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="36"]:hover) .ap[data-n="36"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="36"]:hover) .fn[data-n="36"]{background:var(--ink);color:var(--paper)}
body:has([data-n="37"]:hover) .sp[data-n="37"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="37"]:hover) .ap[data-n="37"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="37"]:hover) .fn[data-n="37"]{background:var(--ink);color:var(--paper)}
body:has([data-n="38"]:hover) .sp[data-n="38"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="38"]:hover) .ap[data-n="38"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="38"]:hover) .fn[data-n="38"]{background:var(--ink);color:var(--paper)}
body:has([data-n="39"]:hover) .sp[data-n="39"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="39"]:hover) .ap[data-n="39"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="39"]:hover) .fn[data-n="39"]{background:var(--ink);color:var(--paper)}
body:has([data-n="40"]:hover) .sp[data-n="40"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="40"]:hover) .ap[data-n="40"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="40"]:hover) .fn[data-n="40"]{background:var(--ink);color:var(--paper)}
body:has([data-n="41"]:hover) .sp[data-n="41"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="41"]:hover) .ap[data-n="41"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="41"]:hover) .fn[data-n="41"]{background:var(--ink);color:var(--paper)}
body:has([data-n="42"]:hover) .sp[data-n="42"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="42"]:hover) .ap[data-n="42"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="42"]:hover) .fn[data-n="42"]{background:var(--ink);color:var(--paper)}
body:has([data-n="43"]:hover) .sp[data-n="43"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="43"]:hover) .ap[data-n="43"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="43"]:hover) .fn[data-n="43"]{background:var(--ink);color:var(--paper)}
body:has([data-n="44"]:hover) .sp[data-n="44"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="44"]:hover) .ap[data-n="44"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="44"]:hover) .fn[data-n="44"]{background:var(--ink);color:var(--paper)}
body:has([data-n="45"]:hover) .sp[data-n="45"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="45"]:hover) .ap[data-n="45"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="45"]:hover) .fn[data-n="45"]{background:var(--ink);color:var(--paper)}
body:has([data-n="46"]:hover) .sp[data-n="46"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="46"]:hover) .ap[data-n="46"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="46"]:hover) .fn[data-n="46"]{background:var(--ink);color:var(--paper)}
body:has([data-n="47"]:hover) .sp[data-n="47"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="47"]:hover) .ap[data-n="47"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="47"]:hover) .fn[data-n="47"]{background:var(--ink);color:var(--paper)}
body:has([data-n="48"]:hover) .sp[data-n="48"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="48"]:hover) .ap[data-n="48"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="48"]:hover) .fn[data-n="48"]{background:var(--ink);color:var(--paper)}
body:has([data-n="49"]:hover) .sp[data-n="49"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="49"]:hover) .ap[data-n="49"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="49"]:hover) .fn[data-n="49"]{background:var(--ink);color:var(--paper)}
body:has([data-n="50"]:hover) .sp[data-n="50"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="50"]:hover) .ap[data-n="50"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="50"]:hover) .fn[data-n="50"]{background:var(--ink);color:var(--paper)}
body:has([data-n="51"]:hover) .sp[data-n="51"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="51"]:hover) .ap[data-n="51"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="51"]:hover) .fn[data-n="51"]{background:var(--ink);color:var(--paper)}
body:has([data-n="52"]:hover) .sp[data-n="52"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="52"]:hover) .ap[data-n="52"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="52"]:hover) .fn[data-n="52"]{background:var(--ink);color:var(--paper)}
body:has([data-n="53"]:hover) .sp[data-n="53"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="53"]:hover) .ap[data-n="53"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="53"]:hover) .fn[data-n="53"]{background:var(--ink);color:var(--paper)}
body:has([data-n="54"]:hover) .sp[data-n="54"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="54"]:hover) .ap[data-n="54"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="54"]:hover) .fn[data-n="54"]{background:var(--ink);color:var(--paper)}
body:has([data-n="55"]:hover) .sp[data-n="55"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="55"]:hover) .ap[data-n="55"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="55"]:hover) .fn[data-n="55"]{background:var(--ink);color:var(--paper)}
body:has([data-n="56"]:hover) .sp[data-n="56"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="56"]:hover) .ap[data-n="56"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="56"]:hover) .fn[data-n="56"]{background:var(--ink);color:var(--paper)}
body:has([data-n="57"]:hover) .sp[data-n="57"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="57"]:hover) .ap[data-n="57"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="57"]:hover) .fn[data-n="57"]{background:var(--ink);color:var(--paper)}
body:has([data-n="58"]:hover) .sp[data-n="58"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="58"]:hover) .ap[data-n="58"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="58"]:hover) .fn[data-n="58"]{background:var(--ink);color:var(--paper)}
body:has([data-n="59"]:hover) .sp[data-n="59"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="59"]:hover) .ap[data-n="59"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="59"]:hover) .fn[data-n="59"]{background:var(--ink);color:var(--paper)}
body:has([data-n="60"]:hover) .sp[data-n="60"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="60"]:hover) .ap[data-n="60"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="60"]:hover) .fn[data-n="60"]{background:var(--ink);color:var(--paper)}
body:has([data-n="61"]:hover) .sp[data-n="61"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="61"]:hover) .ap[data-n="61"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="61"]:hover) .fn[data-n="61"]{background:var(--ink);color:var(--paper)}
body:has([data-n="62"]:hover) .sp[data-n="62"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="62"]:hover) .ap[data-n="62"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="62"]:hover) .fn[data-n="62"]{background:var(--ink);color:var(--paper)}
body:has([data-n="63"]:hover) .sp[data-n="63"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="63"]:hover) .ap[data-n="63"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="63"]:hover) .fn[data-n="63"]{background:var(--ink);color:var(--paper)}
body:has([data-n="64"]:hover) .sp[data-n="64"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="64"]:hover) .ap[data-n="64"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="64"]:hover) .fn[data-n="64"]{background:var(--ink);color:var(--paper)}
body:has([data-n="65"]:hover) .sp[data-n="65"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="65"]:hover) .ap[data-n="65"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="65"]:hover) .fn[data-n="65"]{background:var(--ink);color:var(--paper)}
body:has([data-n="66"]:hover) .sp[data-n="66"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="66"]:hover) .ap[data-n="66"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="66"]:hover) .fn[data-n="66"]{background:var(--ink);color:var(--paper)}
body:has([data-n="67"]:hover) .sp[data-n="67"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="67"]:hover) .ap[data-n="67"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="67"]:hover) .fn[data-n="67"]{background:var(--ink);color:var(--paper)}
body:has([data-n="68"]:hover) .sp[data-n="68"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="68"]:hover) .ap[data-n="68"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="68"]:hover) .fn[data-n="68"]{background:var(--ink);color:var(--paper)}
body:has([data-n="69"]:hover) .sp[data-n="69"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="69"]:hover) .ap[data-n="69"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="69"]:hover) .fn[data-n="69"]{background:var(--ink);color:var(--paper)}
body:has([data-n="70"]:hover) .sp[data-n="70"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="70"]:hover) .ap[data-n="70"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="70"]:hover) .fn[data-n="70"]{background:var(--ink);color:var(--paper)}
body:has([data-n="71"]:hover) .sp[data-n="71"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="71"]:hover) .ap[data-n="71"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="71"]:hover) .fn[data-n="71"]{background:var(--ink);color:var(--paper)}
body:has([data-n="72"]:hover) .sp[data-n="72"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="72"]:hover) .ap[data-n="72"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="72"]:hover) .fn[data-n="72"]{background:var(--ink);color:var(--paper)}
body:has([data-n="73"]:hover) .sp[data-n="73"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="73"]:hover) .ap[data-n="73"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="73"]:hover) .fn[data-n="73"]{background:var(--ink);color:var(--paper)}
body:has([data-n="74"]:hover) .sp[data-n="74"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="74"]:hover) .ap[data-n="74"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="74"]:hover) .fn[data-n="74"]{background:var(--ink);color:var(--paper)}
body:has([data-n="75"]:hover) .sp[data-n="75"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="75"]:hover) .ap[data-n="75"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="75"]:hover) .fn[data-n="75"]{background:var(--ink);color:var(--paper)}
body:has([data-n="76"]:hover) .sp[data-n="76"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="76"]:hover) .ap[data-n="76"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="76"]:hover) .fn[data-n="76"]{background:var(--ink);color:var(--paper)}
body:has([data-n="77"]:hover) .sp[data-n="77"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="77"]:hover) .ap[data-n="77"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="77"]:hover) .fn[data-n="77"]{background:var(--ink);color:var(--paper)}
body:has([data-n="78"]:hover) .sp[data-n="78"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="78"]:hover) .ap[data-n="78"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="78"]:hover) .fn[data-n="78"]{background:var(--ink);color:var(--paper)}
body:has([data-n="79"]:hover) .sp[data-n="79"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="79"]:hover) .ap[data-n="79"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="79"]:hover) .fn[data-n="79"]{background:var(--ink);color:var(--paper)}
body:has([data-n="80"]:hover) .sp[data-n="80"]{box-shadow:0 0 0 1.5px var(--ink)}
body:has([data-n="80"]:hover) .ap[data-n="80"]{background:var(--shade);box-shadow:inset 2.5px 0 0 var(--ink)}
body:has([data-n="80"]:hover) .fn[data-n="80"]{background:var(--ink);color:var(--paper)}

/* ============================================================
   THE MEASUREMENT SURFACES

   The rule-change review, the version browser, the LLM proposals and the
   external-record inspector each own a small vocabulary of their own. Their
   LOGIC was already right against the real backend and is untouched; what
   follows is that vocabulary drawn in this edition's terms — the same
   hairlines, the same teletext families, the same one red.
   ============================================================ */

.hint{font-size:var(--fs-7);color:var(--ink-2);line-height:1.45;padding:var(--sp-2) 0}
.hint.err,.no{color:var(--match)}
.ok{color:var(--ink)}
.warn{color:var(--ink-2)}
.vlink{
  font:inherit;background:none;border:0;padding:0;cursor:pointer;color:var(--ink);
  border-bottom:2px solid var(--tt-blue);
}
.mast .vlink{font-size:var(--fs-9);border-bottom-width:1px;border-color:var(--rule)}
.spin{
  display:inline-block;width:8px;height:8px;background:var(--match);
  animation:pulse .9s steps(2) infinite;margin-right:5px;
}
@keyframes pulse{50%{opacity:.2}}

/* ---------- the review panel ---------- */
.rv-grid{display:flex;flex-wrap:wrap;gap:1px;background:var(--rule);border:1px solid var(--rule)}
.rv-chip{
  flex:1 1 120px;display:flex;flex-direction:column;gap:1px;background:var(--sheet);
  padding:7px 10px;font-size:var(--fs-6);color:var(--ink-2);
}
.rv-chip b{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-17);line-height:1;color:var(--ink);
}
.rv-chip.good b{color:var(--ink)}
.rv-chip.bad b{color:var(--match)}
.rv-status .warn{color:var(--ink-2);font-weight:500}
.rv-status .no{color:var(--match);font-weight:500}
.rv-note{font-size:var(--fs-7);color:var(--ink-2);line-height:1.45;padding-top:5px}
.rv-scanline{font-size:var(--fs-7);color:var(--ink-2);padding:var(--sp-4) 0 var(--sp-2)}
.rv-list{margin-top:var(--sp-2)}
.rv-row{border-bottom:1px solid var(--rule)}
/* The header is the listing's own row (#786); these two are its marks. */
.rv-n{font-size:var(--fs-6);color:var(--ink-3)}
.rv-region{font-size:var(--fs-3);letter-spacing:.06em;background:var(--tt-cyan);padding:0 var(--sp-2);color:var(--on-tint)}
/* Under the title's column: the row's 14px padding, its 40px time column and
   its 8px gap. */
.rv-diffs{display:none;padding:0 0 8px 62px}
.rv-row.open .rv-diffs{display:block}
.rv-diff{display:flex;gap:9px;align-items:baseline;padding:3px 0;font-size:var(--fs-8)}
.rv-path{flex:0 0 190px;color:var(--ink-2);overflow-wrap:anywhere}
.rv-old{background:var(--tt-stone);color:var(--on-tint);padding:0 3px;overflow-wrap:anywhere}
.rv-arrow{color:var(--ink-3)}
.rv-new{background:var(--tt-green);color:var(--on-tint);padding:0 3px;overflow-wrap:anywhere}
/* A phone (#779): the 118px indent and the 190px path left the old and new
   values ~40px between them at 375px. The indent goes, the path takes a line
   of its own, and old → new wrap beneath it. */
@media (max-width:900px){
  .rv-diffs{padding:0 0 var(--sp-4) var(--sp-6)}
  .rv-diff{flex-wrap:wrap;gap:2px 9px}
  .rv-path{flex:0 0 100%}
  .rv-old,.rv-new{min-width:0}
}
/* The absent side of an addition or a removal prints `∅`. There is no value
   there, so it wears no MARK — a tint says "here is a value", and a tint
   halved by `opacity` said the same thing more quietly while taking its ink
   from 8.6 : 1 to 3.4 : 1 (#482). Dim by choosing a colour, not by multiplying
   one: it steps out of the mark and onto the page, a grade back. */
.rv-diff.add .rv-old,.rv-diff.rem .rv-new{
  background:none;color:var(--ink-3);padding:0;
}
.rv-elapsed{color:var(--ink-3)}
.rv-log{
  margin:var(--sp-3) 0 0;padding:var(--sp-4) var(--sp-5);background:var(--shade);border:1px solid var(--rule);
  font-size:var(--fs-7);line-height:1.6;white-space:pre-wrap;max-height:180px;overflow:auto;
}

/* ---------- the version browser ---------- */
.rver-cur,.rver-h{
  display:flex;gap:var(--sp-5);align-items:baseline;width:100%;text-align:left;font:inherit;
  padding:7px 0;border-bottom:1px solid var(--rule);background:transparent;border-top:0;
  border-left:0;border-right:0;
}
.rver-h{cursor:pointer}
.rver-h:hover{background:var(--shade)}
.rver-ct{
  flex:0 0 auto;font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;
  padding:1px 5px;background:var(--tt-stone);color:var(--on-tint);white-space:nowrap;
}
.rver-ct.ct-current{background:var(--tt-cyan)}
.rver-ct.ct-revert{background:var(--tt-blue)}
.rver-ct.ct-activate,.rver-ct.ct-deactivate{background:var(--tt-yellow)}
.rver-when{flex:0 0 auto;font-size:var(--fs-7);color:var(--ink-2);font-variant-numeric:tabular-nums}
.rver-sum{flex:1 1 auto;min-width:0;font-size:var(--fs-8);color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rver-b{display:none}
.rver-row.open .rver-b{display:block}
.rver-diff{padding:var(--sp-4) 0 var(--sp-5);border-bottom:1px solid var(--rule)}
.rver-dh{font-size:var(--fs-8);color:var(--ink-2);padding-bottom:var(--sp-3)}
.rver-dh b{color:var(--ink);font-weight:500}
.rvd{display:flex;gap:9px;align-items:baseline;padding:3px 0;font-size:var(--fs-8)}
.rvd-f{flex:0 0 108px;color:var(--ink-2)}
/* A field's two versions sit side by side, and one of them can be a rule's
   whole rationale — several hundred words. Capped and scrollable, so a long
   `description` cannot push the panel's own controls off the screen. */
.rvd-old,.rvd-new{
  flex:1 1 0;min-width:0;overflow-wrap:anywhere;line-height:1.6;
  max-height:8.4em;overflow:auto;
}
.rvd-old i.rvd-del{background:var(--tt-red);color:var(--on-tint);font-style:normal}
.rvd-new i.rvd-ins{background:var(--tt-green);color:var(--on-tint);font-style:normal}
.rvd-arrow{flex:0 0 auto;color:var(--ink-3)}
/* An editorial note standing where a diff would be — "unchanged", "not
   recorded". It sits in the same row as `.rvd-f` at the same size, so with the
   scale compliant the grade alone no longer says that one of them is a field
   name and the other is us talking: the italic does, in the voice the app uses
   for its own asides everywhere else (#482). */
.rvd-note{flex:1 1 auto;color:var(--ink-3);font-style:italic}
.rver-acts{display:flex;gap:9px;padding-top:9px}

/* ---------- the LLM proposals ---------- */
.llm-status{font-size:var(--fs-8);color:var(--ink-2);padding:var(--sp-4) 0}
.llm-status.err{color:var(--match)}
.llm-p{border-left:3px solid var(--tt-yellow-2);padding:8px 0 9px 11px;margin-top:var(--sp-5)}
.llm-ph{display:flex;gap:9px;align-items:baseline;flex-wrap:wrap;padding-bottom:var(--sp-2)}
.llm-pk{font-size:var(--fs-7);color:var(--ink)}
.llm-pd{font-size:var(--fs-7);color:var(--ink-2)}
.llm-v{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:1px 5px;
  background:var(--tt-stone);color:var(--on-tint);white-space:nowrap;
}
.llm-v.ok{background:var(--tt-green)}
.llm-v.bad{background:var(--tt-red)}
.llm-v.unk{background:var(--tt-stone)}
.llm-f{display:flex;gap:9px;align-items:baseline;padding:var(--sp-1) 0;font-size:var(--fs-8)}
.llm-fl{flex:0 0 84px;color:var(--ink-2)}
.llm-fv{flex:1 1 auto;min-width:0;word-break:break-all}
.llm-why,.llm-un,.llm-meta,.llm-note{font-size:var(--fs-7);color:var(--ink-2);padding-top:var(--sp-2);line-height:1.45}
/* A proposal's own errors and warnings (#481). `.llm-note` was missing from the
   line above, so a validator's refusal printed at body size in full ink beside
   the eleven-pixel notes around it. The error tone is restated here because
   `.no` is declared earlier in the file and would otherwise lose the cascade to
   the rule it shares specificity with. */
.llm-note.no{color:var(--match)}
.llm-pa{display:flex;gap:9px;padding-top:var(--sp-4);flex-wrap:wrap}

/* ---------- the external inspector ---------- */
/* The one proposal action that costs a corpus replay, marked as the primary of
   the three (#481). It was written `.chip.pri` and no element in the app has
   ever carried `chip pri` — `llm.js` writes `btn pri` — so the mark landed
   nowhere and three identical buttons offered themselves equally. Cyan is the
   tint the key strip already uses for a corpus-wide act; `--on-tint` because
   `.btn`'s own `--paper` ink is for the ink-filled face (#478). */
.btn.pri{background:var(--tt-cyan);border-color:var(--tt-cyan-2);color:var(--on-tint)}

/* ---------- the curation queues: the shared row shape (#789) ----------
   The conflict queue (`cf-*`) and the characterization queue (`ch-*`) are two
   page surfaces over one layout — controls, a tally, a list of items, a
   badge, an action line — and carried sixteen identical rule bodies and one
   identical media block, one copy per prefix. That shape is `.queue-*` now;
   each queue keeps under its own prefix only what is its own (the badge hues,
   the readings, the form). */
.queue-controls{display:flex;gap:var(--sp-4);flex-wrap:wrap;margin-top:var(--sp-8)}
.queue-pick select{
  font:inherit;font-size:var(--fs-8);padding:4px 7px;background:var(--sheet);
  color:var(--ink);border:1px solid var(--rule);
}
.queue-tally{padding:var(--sp-4) var(--sp-6) var(--sp-5)}
.queue-tally-row{display:flex;gap:7px;align-items:baseline;flex-wrap:wrap;padding:var(--sp-2) 0}
.queue-tally-row .eyebrow{flex:0 0 108px;white-space:nowrap}
.queue-chip{font-size:var(--fs-7);color:var(--ink-2)}
.queue-chip b{color:var(--ink);font-weight:500}

.queue-list{margin-top:var(--sp-17);display:flex;flex-direction:column;gap:1px;background:var(--rule);
  border:1px solid var(--rule)}
.queue-item{background:var(--sheet);padding:13px 14px 12px}
.queue-hd{display:flex;gap:9px;align-items:baseline;flex-wrap:wrap}
.queue-title{
  font-family:var(--disp);font-variation-settings:"wdth" 125;font-weight:800;
  font-size:var(--fs-14);letter-spacing:-.003em;
}
.queue-hd .fill{flex:1 1 auto}
.queue-badge{
  font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;padding:1px 5px;
  background:var(--tt-stone);color:var(--on-tint);white-space:nowrap;
}
.queue-acts{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-top:11px}
.queue-hint{font-size:var(--fs-7);color:var(--ink-3)}
.queue-form-acts{display:flex;gap:9px;margin-top:var(--sp-4);flex-wrap:wrap}

@media (max-width:900px){
  .queue-tally{display:none}
  .queue-hd .fill{display:none}
}

/* ---------- the conflict-review queue (#47) ----------
   A page surface beside the report, so it inherits `.page` / `.lede` /
   `.index` and adds only the row shape those do not have: a question, its
   readings, and what somebody can do about it.

   The badges are the palette's pastel SPOTS used as marks, one hue per thing
   they say — green for a clear question, yellow for a contested one, cyan for
   a spelling, magenta for two rival readings, blue for two broadcasters, stone
   for one already answered. Nothing here is `--match`: that is the saturated
   colour for a finding IN a listing, and a divergence is a question of ours. */
.cf-path{font-family:var(--mono);font-size:var(--fs-7);color:var(--ink-2)}
.cf-rung{font-size:var(--fs-5);color:var(--ink-3)}
.cf-b-clear{background:var(--tt-green)}
.cf-b-contested{background:var(--tt-yellow)}
.cf-b-variant{background:var(--tt-cyan)}
.cf-b-rival{background:var(--tt-magenta)}
.cf-b-cross{background:var(--tt-blue)}
.cf-b-settled{background:var(--tt-stone)}

.cf-reads{list-style:none;margin:var(--sp-5) 0 0;padding:0;display:flex;flex-direction:column;gap:7px}
/* The reading the record currently shows is marked, not ranked: most-recent-
   wins means it is not necessarily the one most airings carry, and that gap is
   often the whole reason the question is worth asking. */
.cf-read{border-left:3px solid var(--tt-stone);padding:1px 0 1px 10px}
.cf-win{border-left-color:var(--tt-cyan-2)}
/* Three lines, three registers: how many airings carry this reading, the
   reading itself, and where it came from. The count and the provenance were
   the same size and told apart by grade alone — 4.4 : 1 against 2.3 : 1, which
   is a distinction the compliant scale cannot make (8.1 against 5.2). So the
   provenance takes a size step and a line of air below the reading, and the
   count keeps the weight: it is the number this whole panel is sorted by. */
.cf-count{display:block;font-size:var(--fs-6);font-weight:500;color:var(--ink-2)}
.cf-text{display:block;font-family:var(--prose);font-size:var(--fs-12);line-height:1.5;color:var(--ink)}
.cf-meta{display:block;font-size:var(--fs-5);color:var(--ink-3);margin-top:var(--sp-1)}
.cf-note{margin:9px 0 0;font-size:var(--fs-8);color:var(--ink-2);line-height:1.45}
.cf-form{margin-top:var(--sp-5);border-top:1px solid var(--rule);padding-top:var(--sp-5)}
.cf-form textarea{
  width:100%;font:inherit;font-size:var(--fs-9);padding:7px 8px;background:var(--shade);
  color:var(--ink);border:1px solid var(--rule);resize:vertical;
}

/* ---------- the work-identity queue (#796) ----------
   The conflict surface's second item kind (`identity.js`): an incoming
   program beside each existing work it shares a title with, their evidence
   on rows that line up across the sides, and the keys they disagree on marked
   twice — a tint AND a word, since a tint alone is not read by everyone. The
   tint is yellow, the palette's meta spot: a year or a credit is metadata,
   and the saturated `--match` stays reserved for a finding in a listing. */
.id-switch{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-bottom:var(--sp-6)}
.id-switch .btn[aria-pressed="true"]{background:var(--tt-cyan);border-color:var(--tt-cyan-2);color:var(--on-tint)}
.id-saved{margin-top:var(--sp-5);padding:var(--sp-3) var(--sp-5);border-left:3px solid var(--tt-cyan-2);
  background:var(--sheet);font-size:var(--fs-8);color:var(--ink)}
.id-tol ul{margin:0;padding:var(--sp-4) var(--sp-6) var(--sp-5) var(--sp-10);
  font-size:var(--fs-7);color:var(--ink-2);line-height:1.45}
.id-tol li+li{margin-top:var(--sp-2)}
.id-q{margin:var(--sp-3) 0 0;font-size:var(--fs-8);color:var(--ink-2);line-height:1.45}
.id-b-split{background:var(--tt-blue)}
.id-b-merge{background:var(--tt-magenta)}
.id-b-class{background:var(--tt-stone)}
.id-sides{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:var(--sp-5);
  margin-top:var(--sp-5)}
.id-side{min-width:0;border-top:2px solid var(--tt-stone);padding-top:var(--sp-3)}
.id-side[data-side="incoming"]{border-top-color:var(--tt-cyan-2)}
.id-side-h{margin-top:var(--sp-1)}
/* An id is case-exact; the eyebrow's uppercase would misquote it. */
.id-pid{text-transform:none;letter-spacing:.04em}
.id-open{font-size:var(--fs-12);text-align:left}
.id-meta{margin:var(--sp-1) 0 0;font-size:var(--fs-6);color:var(--ink-3)}
.id-ev{margin:var(--sp-4) 0 0;display:flex;flex-direction:column;gap:var(--sp-1)}
.id-ev-row{display:grid;grid-template-columns:96px minmax(0,1fr);gap:var(--sp-4);
  padding:var(--sp-1) var(--sp-2);font-size:var(--fs-7)}
.id-ev-row dt{color:var(--ink-3)}
.id-ev-row dd{margin:0;color:var(--ink);overflow-wrap:anywhere}
.id-ev-row.id-dis{background:var(--tt-yellow);color:var(--on-tint)}
.id-ev-row.id-dis dt,.id-ev-row.id-dis dd{color:var(--on-tint)}
.id-dis-tag{font-size:var(--fs-2);letter-spacing:.09em;text-transform:uppercase;white-space:nowrap}
.id-airings{margin-top:var(--sp-4)}
.id-hidden{border-top-style:dashed}
.id-hidden-p{margin:var(--sp-2) 0 0;font-size:var(--fs-7);color:var(--ink-2)}
.id-decide{margin-top:var(--sp-5);border-top:1px solid var(--rule);padding-top:var(--sp-5)}
.id-decide .btn{white-space:normal;text-align:left}
.id-note{display:flex;flex-direction:column;gap:var(--sp-2)}
.id-note textarea{
  width:100%;font:inherit;font-size:var(--fs-9);padding:var(--sp-3) var(--sp-4);background:var(--shade);
  color:var(--ink);border:1px solid var(--rule);resize:vertical;
}
.id-pattern{margin-top:var(--sp-5)}
.id-pattern summary{cursor:pointer;font-size:var(--fs-8);color:var(--ink-2)}
.id-pattern .queue-hint{margin:var(--sp-3) 0 0}
.id-pattern .id-note{margin-top:var(--sp-4)}
.id-dec-state{margin:var(--sp-2) 0 0;font-size:var(--fs-7);color:var(--ink)}

@media (min-width:901px){ #fbToggle{display:none} }

/* ---------- the characterization queue + its form (#229 step 3) ----------
   A page surface beside the conflict queue, inheriting `.page` / `.lede` /
   `.index` from it and adding only what those do not have: a show, the label
   it already composes, the facets it still wants, and the form that answers
   them.

   The stage badges are the palette's pastel spots used as marks, one hue per
   stage and read as a progression — stone for nothing known, yellow for partly
   known, cyan for complete but unconfirmed, green for confirmed by a person.
   Nothing here is `--match`: that saturated colour is for a finding IN a
   listing, and this is a backlog of ours. */
/* The per-facet counts are the axis a curator picks up, so they ARE the filter
   rather than a number beside one. A pressed chip keeps the same face and
   gains the rule under it — the list below is the feedback that matters. */
.ch-facet{
  font:inherit;font-size:var(--fs-7);color:var(--ink-2);background:none;
  border:0;border-bottom:1px solid transparent;padding:0 0 1px;cursor:pointer;
}
.ch-facet b{color:var(--ink);font-weight:500}
.ch-facet.on{border-bottom-color:var(--tt-cyan-2);color:var(--ink)}

.ch-airings{font-size:var(--fs-6);font-weight:500;color:var(--ink-2)}
.ch-b-uncharacterized{background:var(--tt-stone)}
.ch-b-partial{background:var(--tt-yellow)}
.ch-b-unverified{background:var(--tt-cyan)}
.ch-b-verified{background:var(--tt-green)}
/* The label a show ALREADY composes and the facets it still wants, on one
   line: "partly answered" is the legal state this whole model is built for,
   and a row that printed only the gap would make a curator open the show to
   find out what it already says. */
.ch-label{margin:var(--sp-4) 0 0;font-size:var(--fs-10);color:var(--ink-2);
  display:flex;gap:var(--sp-5);align-items:baseline;flex-wrap:wrap}
.ch-label b{color:var(--ink);font-weight:500}
.ch-gap{font-size:var(--fs-6);color:var(--ink-3);display:inline-flex;gap:5px;
  align-items:baseline;flex-wrap:wrap}
.ch-miss{font-family:var(--mono);font-size:var(--fs-5);color:var(--ink-2);
  border-bottom:1px dotted var(--rule)}

.ch-form{margin-top:11px;border-top:1px solid var(--rule);padding-top:11px}
.ch-sig{margin:0 0 9px;font-size:var(--fs-7);color:var(--ink-2)}
.ch-fields{display:flex;flex-direction:column;gap:7px}
.ch-field{display:flex;gap:9px;align-items:flex-start;flex-wrap:wrap}
.ch-fname{flex:0 0 96px;font-size:var(--fs-7);color:var(--ink-2);padding-top:var(--sp-2);
  display:flex;gap:5px;align-items:baseline}
/* A facet the label cannot be composed without. It is a word rather than an
   asterisk because the form is read by somebody deciding, not filling in. */
.ch-req{font-style:normal;font-size:var(--fs-2);letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-3)}
/* The values, one button each (#666). A small facet is one row; `topic` is a
   grid, so twenty-two values read as a table a curator scans rather than a
   line that wraps wherever the column happens to end. */
.ch-opts{flex:1 1 0;min-width:0;display:flex;flex-wrap:wrap;gap:var(--sp-2);
  padding-top:5px}
.ch-opts.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(112px,1fr))}
.ch-opt{
  position:relative;font-family:var(--mono);font-size:var(--fs-7);line-height:1.3;
  padding:3px 8px;min-width:4.5em;max-width:100%;text-align:left;cursor:pointer;
  display:inline-flex;flex-direction:column;align-items:flex-start;
  justify-content:flex-end;gap:var(--sp-1);
  background:var(--sheet);color:var(--ink-2);border:1px solid var(--rule);
}
.ch-opt:hover{background:var(--hover);color:var(--ink)}
.ch-opt.ch-none{font-family:inherit;color:var(--ink-3)}
/* SELECTED is a fill: the one look that means "somebody decided". "Not
   stated" checked is the ABSENCE of a decision, so it takes only a ground
   shift — a form nobody has touched reads as empty, and the first real choice
   is the first ink on it. */
.ch-opt[aria-checked="true"]{background:var(--ink);color:var(--paper);
  border-color:var(--ink)}
.ch-opt.ch-none[aria-checked="true"]{background:var(--shade);color:var(--ink-2);
  border-color:var(--ink-3)}
/* SUGGESTED is an outline, never a fill: what the pipeline or an outside
   record thinks, drawn in pencil. A generated answer is marked on its value
   and is not selected — pre-selecting it would turn confirming into a click
   on a form the curator never read (#229's line, kept by #666). */
.ch-opt.suggested{border:1px dashed var(--ink-2)}
.ch-opt.suggested[aria-checked="true"]{border-style:solid}
/* The provenance tags sit INSIDE the button, in a row above the value, like a
   siglum over a reading. They used to hang off the top edge, absolutely
   placed — and two or three of them on a short value ("yes") ran left over
   the neighbouring buttons and off the column on a phone. In flow they wrap
   within the button instead, and the value under them stays readable. */
.ch-sugs{order:-1;display:flex;flex-wrap:wrap;gap:var(--sp-1)}
.ch-sug{font-family:var(--disp);font-size:var(--fs-1);font-weight:600;line-height:1;
  letter-spacing:.06em;text-transform:uppercase;padding:1px 3px;white-space:nowrap;
  background:var(--tt-cyan);color:var(--on-tint)}
.ch-sug[data-prov^="external:"]{background:var(--tt-blue)}
/* PRE-FILLED from the listing (#759): selected, but not DECIDED. A pastel
   ground under a dashed rule — never the ink fill, which stays the one look
   that means "somebody decided" — so a form opened on a suggestion does not
   read as a confirmed one. A click on the value makes it the editor's and the
   class goes. */
.ch-sug[data-prov="listing"]{background:var(--tt-yellow)}
.ch-opt.prefill[aria-checked="true"]{background:var(--tt-yellow);
  color:var(--on-tint);border:1px dashed var(--ink)}
.ch-prefill-note{margin:0 0 9px;font-size:var(--fs-7);color:var(--ink-2);
  border-left:3px solid var(--tt-yellow);padding-left:var(--sp-4)}
.ch-note{
  width:100%;margin-top:var(--sp-5);font:inherit;font-size:var(--fs-9);padding:7px 8px;
  background:var(--shade);color:var(--ink);border:1px solid var(--rule);
  resize:vertical;
}

@media (max-width:900px){
  .ch-fields{gap:var(--sp-7)}
  .ch-field{gap:var(--sp-2)}
  .ch-fname{flex:0 0 100%;padding-top:0}
  .ch-opts{flex-basis:100%}
  .ch-opts.grid{grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
}

/* ---- A fetched region's four states (#785) ----
   One spelling for loading / empty / error / capped, written by
   `async-region.js` for every panel and page body that reads something. They
   replace six per-module markups (`tail`, `note`, `hint`, `acnone`, `empty`,
   `qempty`) for those states, so a page and a panel say "loading" alike.
   Quiet by default — a state is scaffolding, not content — with the one
   saturated colour kept for the failure, which is the state that asks for an
   action. */
.state{margin:0;padding:var(--sp-6) var(--sp-7);font-size:var(--fs-8);line-height:1.45;color:var(--ink-2)}
.state-loading,.state-empty{text-align:center}
.state-error{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--sp-4) var(--sp-6);
  color:var(--match);text-align:center}
.state-error p{margin:0;max-width:60ch}
.state-retry{flex:0 0 auto}
.state-trunc{padding:var(--sp-3) 0;text-align:left;font-variant-numeric:tabular-nums}

/* ---- One confirmation for a destructive action (#783) ----
   `confirm.js` arms a button in place: its label becomes the question and it
   takes the one saturated colour, which marks the thing about to happen — a
   `.lnk` keeps its shape (underline, no box), a `.btn` fills. Disarmed, the
   class goes and the button is what it was. */
.cfm-armed{color:var(--match);border-color:var(--match)}
.lnk.cfm-armed{font-weight:600}
.btn.cfm-armed{background:var(--match);border-color:var(--match);color:var(--paper)}

/* ---- A page surface keeps its way out on a phone (#790) ----
   Below 900px the masthead is static and scrolls away with the page — and on
   a page surface it carries the ONLY close control, "back to the listing"
   (the surfaces have no ✕ of their own, by design). Measured at 375px: after
   3,000px of the report the control was 3,000px off screen, and the reader's
   only way out was the browser's back gesture. So while a surface is up the
   masthead sticks. Its height is not a constant on a phone (the identity
   wraps at 360px, a crumb can take a second line), so `page-surface.js`
   writes the live one to `--mast-sticky-h`; the page group's selector sticks
   beneath it rather than under it, and an in-page jump lands below it. */
@media (max-width:900px){
  /* A sticky box sticks inside its containing block, and `body` is
     `height:100%` — one viewport — so the masthead rode along for a screen
     and then left with the body's box. Grown to the page, it holds. */
  body:not([data-surface="work"]){height:auto;min-height:100%}
  body:not([data-surface="work"]) .mast{position:sticky;top:0;z-index:var(--z-bar-pinned)}
  body:not([data-surface="work"]) .subnav{top:var(--mast-sticky-h,0px)}
  body:not([data-surface="work"]) .pagewrap [id]{
    scroll-margin-top:calc(var(--mast-sticky-h,0px) + 8px);
  }
}
/* A step key names a column of the working view, so none is current while a
   surface covers it: the surface's own key is (`aria-current`). `renderKeys`
   draws it that way; this holds it for a surface that does not repaint the
   row on opening — the rule workbench. */
body:not([data-surface="work"]) .keys .key.nav.act{background:transparent;color:var(--ink-2)}
body:not([data-surface="work"]) .keys .key.nav.act .sw{outline:0}
/* The key row scrolls inside itself when it is wider than the screen — on a
   375px phone an editor's row is ~565px — and nothing said so: Curation and
   Settings were simply off the edge. A shade at whichever edge has more
   behind it, the scroll-shadow idiom: the two `local` covers travel with the
   content and hide a shade once its edge is reached, the two `scroll` shades
   stay put at the box's edges. Background only, so no key is ever covered. */
.keys{
  background:
    linear-gradient(to right, var(--sheet) 40%, transparent) left center / 28px 100% no-repeat local,
    linear-gradient(to left, var(--sheet) 40%, transparent) right center / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--keys-fade), transparent) left center / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--keys-fade), transparent) right center / 14px 100% no-repeat scroll,
    var(--sheet);
  --keys-fade:color-mix(in srgb, var(--ink) 38%, transparent);
}

/* ---------- one broadcast row everywhere (#786) ---------- */
/* What a row OUTSIDE the listing says in place of the channel line and the day
   divider the listing draws above it: `Sa 05.09. · WDR Fernsehen`. A trailing
   column like the flags, so a compact row stays one line; it never shrinks —
   the title is what gives way, as it does for the flags. */
.row-where{flex:none;font-size:var(--fs-5);color:var(--ink-3);white-space:nowrap;
  font-variant-numeric:tabular-nums}

/* ---------- landmarks: the skip link and the column headings (#780) ---------- */
/* The skip link is the document's first Tab stop and is off screen until it
   has focus. Fixed rather than absolute, so it shows over the sticky masthead
   (#790) wherever the page is scrolled: one above the bars, under every
   surface, dialog and the sign-in view, none of which it can be reached from
   (modal.js traps the focus). */
.skip{
  position:fixed;left:8px;top:8px;z-index:calc(var(--z-bar) + 1);transform:translateY(-200%);
  padding:7px 12px;background:var(--ink);color:var(--paper);
  font-size:var(--fs-9);font-weight:600;text-decoration:none;
}
.skip:focus{transform:none}
/* Its target is focused only to move the Tab order there; the row the next
   Tab reaches draws the ring. */
#listpane:focus{outline:none}
/* A column's eyebrow is an <h2> now; `.eyebrow` already sets every font
   property, so only the heading's own margin has to go for it to look as the
   <span> did. */
.col-head > h2.eyebrow{margin:0}
@media (max-width:900px){
  /* The phone hides the eyebrow (the step bar names the column) and a column
     head with no hint. Both stay HEADINGS: visually hidden, not removed, so a
     screen reader still finds each column by its name. */
  .col-head > h2.eyebrow{
    display:block;position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
  }
  .deck .col-head:not(:has(.hint:not(:empty))){
    display:block;position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
  }
  /* The list it skips to is not on screen at steps 2 and 3. */
  body:not([data-step="1"]) .skip{display:none}
}

/* ---------- credit verification (#876) ----------
   The Curation group's fourth page, on the queues' shared row shape
   (`.queue-*`) and the identity queue's toggle (`.id-switch`). What is its
   own: the two counts flanking a pair of person rows, the fact lines under
   them, and the alias entry — monospace and selectable, since copying it by
   hand into `curated/person_identity.json` is the only thing the page offers. */
.cr-count{font-size:var(--fs-6);color:var(--ink-3);white-space:nowrap}
.cr-vs{color:var(--ink-3)}
.cr-dup{font-size:var(--fs-12)}
.cr-series{font-size:var(--fs-7)}
.cr-b-fold{background:var(--tt-cyan)}
.cr-b-name_part{background:var(--tt-yellow)}
.cr-b-film{background:var(--tt-magenta)}
.cr-b-episode{background:var(--tt-blue)}
/* The Wikidata page (#975) borrows the `cr-*` card; its verdict badges. */
.wd-b-close{background:var(--tt-yellow)}
.wd-b-near{background:var(--tt-magenta)}
.wd-b-listed{background:var(--tt-cyan)}
.wd-b-not_listed{background:var(--tt-stone)}
.cr-facts{list-style:none;margin:var(--sp-4) 0 0;padding:0;font-size:var(--fs-8);
  color:var(--ink-2);line-height:1.5}
.cr-facts b{color:var(--ink);font-weight:500}
.cr-people{margin:var(--sp-4) 0 0;font-size:var(--fs-8);color:var(--ink-2);line-height:1.5}
.cr-more{display:inline}
.cr-more summary{display:inline;cursor:pointer;color:var(--ink-3)}
.cr-alias{margin-top:var(--sp-5);border-top:1px solid var(--rule);padding-top:var(--sp-4)}
/* Apply / Ignore (#975): the merge it would make, the two buttons, and the
   answer of the write beside them. */
.cr-merge{margin:0;font-size:var(--fs-8);color:var(--ink-2);overflow-wrap:anywhere}
.cr-merge b{color:var(--ink);font-weight:500}
.cr-actions{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap;
  margin-top:var(--sp-3)}
.cr-status{font-size:var(--fs-7);color:var(--ink-3)}
.cr-alias-hd{display:flex;gap:var(--sp-4);align-items:center;justify-content:space-between}
.cr-alias-text{margin:var(--sp-3) 0 var(--sp-2);padding:var(--sp-3) var(--sp-5);
  font-family:var(--mono);font-size:var(--fs-7);line-height:1.5;white-space:pre-wrap;
  overflow-wrap:anywhere;background:var(--sheet);border-left:3px solid var(--tt-stone);
  color:var(--ink);user-select:all}
