/* ============================================================
   GLOBAL MEDIA & ENTERTAINMENT WIRE — terminal stylesheet
   Design family shared with the AXIOM Global Entertainment Index
   (navy #0A2032 · blues #1D6BB4/#4D9BE0/#5BA6E8 · Inter + Newsreader
    · up #1f9d6b / down #cf4b3d · soft 10-12px radii)
   ============================================================ */

:root {
  --bg:        #0A2032;
  --bg2:       #081B2B;
  --panel:     #0E2A3D;
  --panel2:    #13314A;
  /* A text field must not be the same colour as the surface it sits on. The
     newsletter input was painted --panel INSIDE a --panel panel — pixel
     identical in both themes, measured — so its only affordance was a 1px
     border and it did not read as something you could type into. --field is
     the fill that lifts it off the panel: lighter in both themes, which in
     dark means a step up from --panel and in light means plain white. */
  --field:     #163952;
  --grid:      #1B374F;
  --grid-soft: #163045;
  --text:      #E9F1F8;
  --text-dim:  #A9BDCD;
  --faint:     #7A93A6;
  --brand:     #4D9BE0;
  --brand-l:   #5BA6E8;
  --brand-d:   #1D6BB4;
  --pale:      #E6F1FB;
  --up:        #1f9d6b;
  --up-l:      #3ec78f;
  --down:      #cf4b3d;
  --violet:    #B07CE8;
  --footer:    #071521;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --serif: "Newsreader", Georgia, "Times New Roman", serif;
  /* the ✕ used by the search field's clear button, masked so it takes
     the theme colour instead of the browser's fixed grey bitmap */
  --x-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M5 5l14 14M19 5L5 19'/%3E%3C/svg%3E");
}

/* light theme — the newsletter's PAPER TWIN per the brand kit:
   ink #0A2032 on white, wash #F2F5F8, rule #D8E0E8, muted #5B6B7A,
   accent_dark #2F6FAE for text-level accents (AA on paper) */
[data-theme="light"] {
  --bg:        #FFFFFF;
  --bg2:       #F2F5F8;
  --panel:     #F2F5F8;
  --panel2:    #E6F1FB;
  /* white, because on paper "lighter than the panel" is the panel's own
     ground — the usual light-theme well. --panel2 is bluer, not lighter. */
  --field:     #FFFFFF;
  --grid:      #D8E0E8;
  --grid-soft: #E9EEF3;
  --text:      #0A2032;
  --text-dim:  #5B6B7A;
  /* was #7E99B5 — 2.95:1 on white and 2.70:1 on --bg2, well under the 4.5:1
     WCAG AA needs for text this size. It carries every timestamp, source chip,
     count and micro-label on the default theme, so the whole secondary layer
     of the page was failing. Same hue (211deg) and saturation, walked down in
     lightness only, to 4.95:1 on white and 4.52:1 on the panel — and still
     lighter than --text-dim, so faint still reads as fainter. */
  --faint:     #547392;
  --brand:     #2F6FAE;
  --brand-l:   #2F6FAE;
  --brand-d:   #1D6BB4;
  --pale:      #0A2032;
  --up:        #1f9d6b;
  --up-l:      #178a5d;
  --down:      #cf4b3d;
  --footer:    #F2F5F8;
}
[data-theme="light"] .wordmark .gme { color: #5B6B7A; }
[data-theme="light"] .wordmark .wire { color: #1D6BB4; }
[data-theme="light"] .topbar {
  background: linear-gradient(180deg, rgba(255,255,255,.97), rgba(247,250,253,.93));
  box-shadow: 0 1px 0 rgba(14,42,61,.04);
}
[data-theme="light"] .ticker-wrap { background: #F2F7FB; }
[data-theme="light"] .gei-strip { background: #FFFFFF; }
[data-theme="light"] .searchbox input { background: #FFFFFF; }
[data-theme="light"] .btn { background: #FFFFFF; }
[data-theme="light"] .gei-strip .lvl,
[data-theme="light"] .idxbig .lvl { color: #0E2A3D; }
[data-theme="light"] .btn:hover { color: #0E2A3D; }
[data-theme="light"] .pill:hover { color: #0E2A3D; }
[data-theme="light"] .tick:hover { color: #0E2A3D; }

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scrollbar-color: var(--grid) var(--bg); }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  transition: background .2s ease, color .2s ease;
}

/* publication logos (favicons) */
.pubico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 4px; flex: none;
  background: rgba(255,255,255,.92); border: 1px solid var(--grid-soft);
  overflow: hidden; vertical-align: -4px;
}
.pubico img { width: 14px; height: 14px; display: block; }
.pubico.mono {
  font-family: var(--mono); font-size: 9px; font-weight: 700;
  color: var(--brand-l); background: var(--panel2); border-color: var(--grid);
}
a { color: inherit; text-decoration: none; }
a:hover { color: var(--brand-l); }
::selection { background: var(--brand-d); color: #fff; }

.micro {
  font-family: var(--mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--faint);
}
.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.up   { color: var(--up-l); }
.down { color: var(--down); }

/* ---------------------------------------------------------- topbar */
.topbar {
  position: sticky; top: 0; z-index: 100;
  display: flex; align-items: center; gap: 18px;
  /* content lines up with the centered body (max-width 1560 + 20px gutter);
     collapses to a flat 20px once the viewport is narrower than the body */
  padding: 0 max(20px, calc((100% - 1560px) / 2 + 20px)); height: 56px;
  background: linear-gradient(180deg, rgba(8,27,43,.97), rgba(10,32,50,.93));
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--grid);
}
/* terminal lockup per the brand kit: mono, uppercase, tracked, brand blue —
   the serif masthead belongs to the newsletter, never inside the terminal */
.wordmark { display: flex; align-items: baseline; gap: 9px; white-space: nowrap; }
.wordmark .gme {
  font-family: var(--mono); font-weight: 600; font-size: 11.5px;
  letter-spacing: .06em; color: var(--text-dim); text-transform: uppercase;
}
.wordmark .wire {
  font-family: var(--mono); font-weight: 600; font-size: 17px;
  letter-spacing: .08em; color: var(--brand); text-transform: uppercase;
}
.wordmark .tag { font-size: 10px; color: var(--faint); letter-spacing: .09em; }

/* ---- the phone masthead: stack the two long words ------------------------
   Measured at 390px: the wordmark took 179px and the tools 173px, leaving
   38px — not enough for a 44px touch target, so the jump menu had nowhere to
   go. GLOBAL over ENTERTAINMENT sets the block to the width of its longest
   word instead of their sum, which buys the button its place.

   The two words are separate spans with a literal space between them, so at
   every width above this breakpoint the wordmark renders exactly as it always
   has — one line, one space. Nothing here changes the desktop masthead. */
@media (max-width: 620px) {
  .wordmark .gme {
    display: inline-flex; flex-direction: column;
    line-height: 1.02; letter-spacing: .05em;
  }
  /* the space between the spans would otherwise print as a stray gap above
     ENTERTAINMENT once they are stacked */
  .wordmark .gme .w1 { margin-bottom: -1px; }
  .wordmark { align-items: center; gap: 7px; }
}

/* ---- the jump menu ------------------------------------------------------- */
/* `.btn.navbtn`, not `.navbtn`: the button also carries .btn, whose own
   display is declared LATER in this file (line ~264) at equal specificity, so
   a bare .navbtn rule loses and the button appeared at every width. Caught by
   measuring at 1440 rather than by reading. */
.btn.navbtn { display: none; }
/* Shown exactly where the rail stops sitting beside the stories (1120px, the
   same breakpoint .shell collapses at). Above that everything is already in
   view and a jump menu would be furniture. */
@media (max-width: 1120px) {
  .btn.navbtn { display: inline-flex; }
}
.navsheet {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px; padding: 10px var(--gut, 12px) 12px;
  border-top: 1px solid var(--grid-soft); background: var(--panel);
}
.navsheet[hidden] { display: none; }
.navsheet a {
  display: flex; align-items: center; gap: 8px; min-height: 44px;
  padding: 0 12px; border: 1px solid var(--grid); border-radius: var(--r-row, 8px);
  background: var(--field); color: var(--text); text-decoration: none;
  font-family: var(--mono); font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase;
}
.navsheet a:hover { border-color: var(--brand-l); }
.navsheet a .n {
  margin-left: auto; color: var(--faint); font-size: 10px; letter-spacing: .04em;
}
/* The wordmark is the way home from every page. It sits inside .wordmark so
   the existing `.wordmark .gme` / `.wordmark .wire` descendant rules — including
   the light-theme overrides at the foot of this file — keep applying unchanged. */
.wm-home {
  display: flex; align-items: baseline; gap: 9px;
  text-decoration: none; color: inherit; cursor: pointer;
}
.wm-home:hover .wire { opacity: .8; }
.wm-home:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 3px; }

.gei-strip {
  display: flex; align-items: center; gap: 10px;
  padding: 5px 13px; border: 1px solid var(--grid); border-radius: 10px;
  background: var(--panel); white-space: nowrap; cursor: default;
}
.gei-strip .lvl { font-family: var(--mono); font-weight: 600; font-size: 14px; color: #fff; }
.gei-strip .chg { font-family: var(--mono); font-size: 11.5px; }
.gei-strip svg { display: block; }

.clocks { display: flex; gap: 11px; margin-left: auto; }
/* .clocks is a nowrap flex row, so its width is the sum of its children and an
   extra clock eats into the search box beside it. UTC was added at position 4,
   which moved every later clock along by one:
   1 LA · 2 NY · 3 SAO · 4 UTC · 5 LDN · 6 BER · 7 DXB · 8 MUM · 9 SIN · 10 TYO · 11 SEL
   Narrow screens cut from the 6th (was the 5th), so the same four cities as
   before survive, plus UTC. Between there and 1520px the 11th is cut, holding
   the row to the ten clocks that already fitted — SEL is the one to lose, as
   Seoul and Tokyo are both GMT+9 and always render the identical time. */
@media (max-width: 1520px) { .clocks .clock:nth-child(n+11) { display: none; } }
@media (max-width: 1360px) { .clocks .clock:nth-child(n+6) { display: none; } }
.clock  { text-align: right; }
.clock .t { font-family: var(--mono); font-size: 12.5px; color: var(--text-dim); }

/* flexible spacer: brand + GEI tape sit left, search + controls push to the
   right gutter so the top line is flush with the body on both edges */
.searchbox { position: relative; margin-left: auto; }
.searchbox input {
  width: 240px; padding: 8px 10px 8px 30px;
  background: var(--panel); border: 1px solid var(--grid); border-radius: 10px;
  color: var(--text); font-family: var(--sans); font-size: 13px; outline: none;
  transition: border-color .15s, width .2s;
}
.searchbox input::placeholder { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; }
.searchbox input:focus { border-color: var(--brand-d); width: 300px; }
/* ⌕ is a hairline glyph and was set at 13px, which read as a smudge at the
   size the rest of the bar works at. */
.searchbox .icon {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  color: var(--text-dim); font-size: 17px; line-height: 1; pointer-events: none;
}
.searchbox kbd {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  font-family: var(--mono); font-size: 10px;
  color: var(--faint); border: 1px solid var(--grid); border-radius: 4px; padding: 0 5px;
}
/* The hint and the clear button both live at the right edge, so they sat on
   top of each other the moment anything was typed. The hint is only useful
   on an empty field — once there is a term, the ✕ owns that corner. */
.searchbox input:not(:placeholder-shown) ~ kbd { display: none; }
/* Safari/Chrome draw the clear affordance as a fixed light-grey bitmap that
   all but disappears on the dark bar. Mask it so it takes the theme's colour. */
.searchbox input::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; margin-left: 6px; cursor: pointer;
  background-color: var(--text-dim);
  -webkit-mask: var(--x-mask) center / 13px 13px no-repeat;
  mask: var(--x-mask) center / 13px 13px no-repeat;
}
.searchbox input::-webkit-search-cancel-button:hover { background-color: var(--brand-l); }

.btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border: 1px solid var(--grid); border-radius: 9px;
  background: var(--panel); color: var(--text-dim);
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  cursor: pointer; transition: all .15s; white-space: nowrap;
}
.btn:hover { border-color: var(--brand-d); color: #fff; }
.btn.primary { background: var(--brand-d); border-color: var(--brand-d); color: #fff; }
.btn.primary:hover { background: var(--brand); }
.btn .badge {
  background: var(--brand-l); color: #08131d; border-radius: 8px;
  padding: 0 6px; font-weight: 700; font-size: 10px;
}

/* ---------------------------------------------------------- ticker */
.ticker-wrap {
  border-bottom: 1px solid var(--grid);
  background: var(--footer);
  overflow: hidden; height: 36px; display: flex; align-items: center;
}
.ticker-label {
  flex: none; padding: 0 16px; height: 100%; display: flex; align-items: center;
  background: var(--brand-d); color: #fff;
  font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .15em;
}
.ticker { flex: 1; overflow: hidden; position: relative; height: 100%; }
.ticker-track {
  display: inline-flex; align-items: center; height: 36px;
  white-space: nowrap; will-change: transform;
  animation: tickerscroll var(--ticker-dur, 220s) linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
@keyframes tickerscroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.tick {
  font-family: var(--mono); font-size: 12px; color: var(--text-dim);
  padding: 0 26px 0 0;
}
.tick b { color: var(--brand-l); font-weight: 600; padding-right: 8px; }
.tick:hover { color: #fff; }

/* ---------------------------------------------------------- layout */
.shell {
  display: grid; grid-template-columns: minmax(0, 1fr) 360px;
  gap: 0; max-width: 1560px; margin: 0 auto;
}
.main { min-width: 0; padding: 16px 20px 60px; }
.rail {
  border-left: 1px solid var(--grid);
  padding: 16px 16px 60px;
  display: flex; flex-direction: column; gap: 18px;
}

/* ---------------------------------------------------------- filters */
.filters { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 6px; }
.pill {
  padding: 5px 12px; border: 1px solid var(--grid); border-radius: 999px;
  background: transparent; color: var(--text-dim);
  font-family: var(--sans); font-size: 12px; font-weight: 500; letter-spacing: .01em;
  cursor: pointer; transition: all .12s; white-space: nowrap;
}
.pill:hover { border-color: var(--brand-d); color: #fff; }
.pill.active { background: var(--brand-d); border-color: var(--brand-d); color: #fff; }
/* A source with nothing today stays on screen, dimmed. Hiding it is how Reddit
   and HN vanished from the panel without anyone noticing — there was simply
   one fewer button than yesterday, and no way to tell "quiet today" from
   "broken for weeks". Dim, still clickable, still counted. */
.pill.empty { opacity: .45; }
.pill.empty.active { opacity: 1; }
.pill .n { color: var(--faint); padding-left: 5px; font-family: var(--mono); font-size: 10.5px; }
.pill.active .n { color: rgba(255,255,255,.75); }
.filters .sep { width: 1px; height: 18px; background: var(--grid); margin: 0 6px; }
/* `outline: none` was on this rule since the bar was written, which is why
   four of the five controls in the filter bar had NO focus indicator at all —
   a keyboard user tabbing from the last chip into the source list simply lost
   the caret. The ring is now declared for real in the FILTERS LAYER below. */
.filters select {
  background: var(--panel); color: var(--text-dim); border: 1px solid var(--grid);
  border-radius: 8px; padding: 5px 8px; font-family: var(--mono); font-size: 11px;
}

.feedmeta { display: flex; align-items: center; justify-content: space-between; margin: 10px 2px 8px; }
.feedmeta .count { font-family: var(--mono); font-size: 11px; color: var(--faint); }

/* ---------------------------------------------------------- story cards */
.story {
  display: grid; grid-template-columns: 74px minmax(0,1fr) auto;
  gap: 12px; padding: 12px 14px;
  border: 1px solid transparent; border-bottom: 1px solid var(--grid-soft);
  border-radius: 10px; position: relative; transition: background .12s, border-color .12s;
  cursor: pointer;
}
.story:hover, .story.sel { background: var(--panel); border-color: var(--grid); }
.story.sel { box-shadow: inset 2px 0 0 var(--brand); }
.story.expanded { background: var(--panel); border-color: var(--grid); }
.story .when { padding-top: 3px; }
.story .when .ago { font-family: var(--mono); font-size: 11px; color: var(--text-dim); display: block; }
.story .when .new {
  display: inline-block; margin-top: 4px; font-family: var(--mono); font-size: 9px;
  letter-spacing: .1em; color: var(--brand-l); border: 1px solid rgba(91,166,232,.45);
  border-radius: 4px; padding: 0 4px;
}
.story h3 {
  font-family: var(--serif); font-size: 16.5px; font-weight: 600;
  line-height: 1.32; letter-spacing: -.01em;
}
.story h3 a:hover { color: var(--brand-l); }
/* headlines expand the story in place — READ AT SOURCE is the way out */
.story h3 .tlink { cursor: pointer; }
.story h3 .tlink:hover { color: var(--brand-l); }
.story .sum { color: var(--text-dim); font-size: 12.5px; margin-top: 3px; }
.compact .story .sum { display: none; }
.compact .story { padding: 8px 14px; }
.story .meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 7px; }
.srcchip {
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  color: var(--brand-l); border: 1px solid rgba(91,166,232,.35);
  padding: 1px 7px; border-radius: 5px; text-transform: uppercase;
}
.catchip { font-family: var(--mono); font-size: 10px; color: var(--faint); text-transform: uppercase; letter-spacing: .08em; }
.tagchip { font-family: var(--mono); font-size: 10px; color: var(--text-dim); opacity: .75; }
.trchip {
  font-family: var(--mono); font-size: 10px; padding: 1px 8px; border-radius: 999px;
  border: 1px solid; opacity: .95; cursor: pointer;
}
.coverage { font-family: var(--mono); font-size: 10px; color: var(--brand-l); cursor: pointer; }
.alsoOn { display: none; margin-top: 5px; padding-left: 2px; }
.alsoOn.open { display: block; }
.alsoOn a { font-family: var(--mono); font-size: 11px; color: var(--text-dim); margin-right: 12px; }
.alsoOn a:hover { color: var(--brand-l); }

.story .actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.score-dot { display: flex; align-items: center; gap: 5px; }
.score-dot i { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }
.score-dot .v { font-family: var(--mono); font-size: 10px; color: var(--faint); }
.starbtn {
  background: none; border: 1px solid var(--grid); border-radius: 7px;
  color: var(--faint); width: 26px; height: 24px; cursor: pointer; font-size: 13px;
  transition: all .12s;
}
.starbtn:hover { border-color: var(--brand-l); color: var(--brand-l); }
.starbtn.on { color: var(--brand-l); border-color: var(--brand-l); background: rgba(91,166,232,.1); }
.voter { display: flex; gap: 3px; }
.votebtn {
  background: none; border: 1px solid var(--grid); border-radius: 6px;
  color: var(--faint); width: 22px; height: 20px; cursor: pointer; font-size: 9px;
  line-height: 1; transition: all .12s; padding: 0;
}
.votebtn.vup:hover { border-color: var(--up); color: var(--up-l); }
.votebtn.vdn:hover { border-color: var(--down); color: var(--down); }
.votebtn.vup.on { color: var(--up-l); border-color: var(--up); background: color-mix(in srgb, var(--up-l) 10%, transparent); }
.votebtn.vdn.on { color: var(--down); border-color: var(--down); background: rgba(207,75,61,.1); }
.votebtn.kill { width: 47px; height: 18px; font-size: 9px; opacity: .55; }
.votebtn.kill:hover { opacity: 1; border-color: var(--down); color: var(--down); background: rgba(207,75,61,.12); }

/* ---------------------------------------------------------- inline expansion */
.story-expand {
  grid-column: 1 / -1;
  margin-top: 4px; padding: 14px 4px 4px;
  border-top: 1px solid var(--grid);
  cursor: default;
  animation: expandIn .18s ease;
}
@keyframes expandIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.story-expand .xbody {
  font-family: var(--serif); font-size: 15px; line-height: 1.68; color: var(--pale);
  max-width: 66ch;
}
.story-expand .xbody p { margin-bottom: 11px; }
.story-expand .xnote {
  margin: 10px 0 0; padding: 9px 12px; border: 1px dashed var(--grid); border-radius: 9px;
  font-size: 11.5px; color: var(--faint); max-width: 66ch;
}
.story-expand .xsec { margin-top: 16px; }
.story-expand .xsec .micro { display: block; margin-bottom: 7px; color: var(--brand-l); }
.rel {
  display: block; padding: 8px 11px; border: 1px solid var(--grid-soft); border-radius: 9px;
  margin-bottom: 6px; cursor: pointer; transition: border-color .12s, background .12s;
  max-width: 66ch;
}
.rel:hover { border-color: var(--brand-d); background: var(--panel2); }
.rel .t { font-family: var(--serif); font-size: 13.5px; font-weight: 600; }
.rel .s { font-family: var(--mono); font-size: 10px; color: var(--faint); }
/* The company's own filing, offered above the outlet links because it is the
   only one of them that is not an account of the news but the thing itself. */
.xfiling {
  display: flex; align-items: center; gap: 10px; margin: 12px 0 2px;
  padding: 9px 12px; border: 1px solid var(--brand-d); border-radius: 9px;
  background: rgba(47,111,174,.10); color: var(--text-dim);
  font-size: 12.5px; text-decoration: none;
  transition: background .12s, border-color .12s;
}
.xfiling:hover { background: rgba(47,111,174,.20); border-color: var(--brand-l); }
.xfiling .ff {
  flex: none; font-family: var(--mono); font-size: 10px; font-weight: 700;
  letter-spacing: .06em; color: #fff; background: var(--brand-d);
  padding: 3px 7px; border-radius: 5px;
}
.xfiling .fa { margin-left: auto; color: var(--brand-l); }
.xactions { display: flex; gap: 8px; align-items: center; margin-top: 14px; flex-wrap: wrap; }
.xactions .btn.listening { border-color: var(--brand-l); color: var(--brand-l); animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }

.loadmore { margin: 18px auto; display: block; }

/* ---------------------------------------------------------- the desk */
/* One gutter for the whole surface. The rank column, the memo's hanging
   indent, the header and the briefing rail all derived their left edge
   independently and none of them agreed: the memo started 16px in, the header
   14px, the ranks 15px and the headline column 49px. Three numbers, declared
   once, so the memo's first word lands exactly on the #1 headline. */
.desk {
  --dpad: 15px;          /* the surface's own left/right gutter */
  --rankw: 24px;         /* the rank column                     */
  --rankgap: 10px;       /* rank → headline                     */
  border: 1px solid var(--grid); border-radius: 12px; overflow: hidden;
  margin-bottom: 16px; background: var(--panel);
}
@media (max-width: 780px) { .desk { --dpad: 12px; --rankw: 18px; --rankgap: 8px; } }

/* ---- paper & navy contrast on the desk -------------------------------
   The desk sits on --panel, one step darker than the feed's white row, and
   three tokens that clear AA there do not clear it here. Measured on the
   panel (#F2F5F8) AND on the hovered row (#E6F1FB), same hues walked down in
   lightness only — the identical method the story's instrument line already
   documents, so the desk stays quieter than the headline and the figures stay
   quieter than the outlet:
     --faint  #5F7B92 → 4.05/3.87  sub, chevron, ages, counts  → #4E6980 5.25/5.02
     --up-l   #178A5D → 3.97/3.80  lead rank, ▲ momentum       → #12734D 5.35/5.12
     --down   #CF4B3D → 4.06/3.88  the movers' red             → #B3392C 5.42/5.18
   And the one pair navy fails: #CF4B3D is 3.34:1 on #0E2A3D.

   CONTINUITY: these three now live on [data-theme="light"] at the root of the
   stylesheet, because the rail measured the identical walk-down against the
   identical ground and the feed measured a THIRD set against white. One
   token, one value — see the note beside them. */
/* The header is a <button>. Three columns on the desk — label, provenance,
   chevron — and two rows on a phone, where the chevron spans them and stays
   pinned top-right instead of drifting to the end of the wrapped sub-line. */
.desk-h {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "t s c";
  align-items: center; column-gap: 12px;
  width: 100%; min-height: 44px; padding: 9px var(--dpad);
  background: linear-gradient(180deg, var(--panel2), var(--panel));
  border: 0; border-bottom: 1px solid var(--grid); border-radius: 11px 11px 0 0;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.desk.collapsed .desk-h { border-bottom: 0; border-radius: 11px; }
.desk-h:hover .chev { color: var(--brand-l); }
.desk-h:hover .sub { color: var(--text-dim); }
.desk-h:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; }
.desk-h .title { grid-area: t; font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--brand-l); }
.desk-h .sub {
  grid-area: s; font-family: var(--mono); font-size: 10px; color: var(--faint);
  font-variant-numeric: tabular-nums lining-nums;
}
/* the only affordance saying the whole desk folds away, at 11px and the
   quietest ink on the surface: a 13px glyph in a hit-sized cell instead */
.desk-h .chev {
  grid-area: c; justify-self: end; color: var(--faint);
  font-size: 13px; line-height: 1; width: 18px; text-align: right;
}

/* ---- the memo: typeset, not poured ----------------------------------
   One 3-4 sentence paragraph filled five lines at 1440 and TEN at 390, all
   one size, so the shape of the day was invisible until you had read it. Not
   a word changes: app.js splits the same string on its own sentence
   boundaries and this lays the pieces out — the lead takes the left column at
   reading size on the SAME 1.25fr/1fr the body below uses, the supporting
   sentences stack as marked lines on the right, and the closing "Ahead:"
   becomes a labelled strip across the foot of both. Reading order on a phone
   is the paragraph's own order, because that is also the DOM order. */
/* One column, always. The two-column split put the memo's points beside the
   write-up and left a measured 25% of the block empty at both 1440 and 1280,
   so it is gone; memoHTML no longer emits .memo-rest or sets .split. */
.desk-memo {
  display: grid; grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "lead" "ahead";
  border-bottom: 1px solid var(--grid-soft); font-family: var(--serif);
}
.desk-memo .memo-lead { grid-area: lead; padding: 13px var(--dpad) 15px; }
/* the indent is the rank column + its gap, so the memo's first word and the
   #1 headline below it share one left edge — and the rule fills the gutter
   for the paragraph's whole height rather than ticking its first line */
.desk-memo .memo-lead p {
  position: relative; padding-left: calc(var(--rankw) + var(--rankgap));
  font-size: 17px; line-height: 1.47; color: var(--text);
}
/* No measure cap. A 68ch cap used to apply whenever the memo was unsplit, and
   with the split gone it would have held the paragraph to ~600px inside a
   1014px block — re-creating on the right exactly the void this change
   removes. The operator asked for full width; this is where it is granted. */
.desk-memo .memo-lead p::before {
  content: ""; position: absolute; left: 0; top: .30em; bottom: .18em;
  width: 3px; border-radius: 2px; background: var(--brand);
}
.desk-memo .memo-ahead {
  grid-area: ahead; border-top: 1px solid var(--grid-soft);
  padding: 10px var(--dpad) 12px;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  column-gap: 12px; align-items: baseline;
}
.desk-memo .memo-ahead .lbl { color: var(--brand-l); white-space: nowrap; }
.desk-memo .memo-ahead p {
  font-size: 14.5px; line-height: 1.5; color: var(--text-dim); max-width: 82ch;
}
.desk-memo .llmtag { font-family: var(--mono); font-size: 9px; color: var(--faint); letter-spacing: .1em; margin-left: 6px; }

.desk-body { display: grid; grid-template-columns: minmax(0,1.25fr) minmax(0,1fr); }
.desk-front { border-right: 1px solid var(--grid-soft); }
.fp-item {
  display: flex; gap: var(--rankgap); padding: 11px var(--dpad);
  border-bottom: 1px solid var(--grid-soft); cursor: pointer; transition: background .12s;
}
.fp-item:last-child { border-bottom: none; }
.fp-item:hover { background: var(--panel2); }
/* the row is the open control (role=button, tabindex=0) and had no focus
   style at all; inset so the ring is not clipped by the row above */
.fp-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; border-radius: 6px; }
.fp-item .rank {
  font-family: var(--mono); font-size: 15px; font-weight: 700; color: var(--brand-l);
  width: var(--rankw); flex: none; padding-top: 1px;
  font-variant-numeric: tabular-nums lining-nums;
}
/* the digit tracks its headline so the two share a baseline instead of the
   rank floating half a line above the lead */
.fp-item.lead .rank { color: var(--up-l); font-size: 17px; padding-top: 2px; }
/* 13.5px is a caption, and these are the ten headlines the desk chose out of
   215 — smaller than the memo that introduces them and smaller than the story
   river underneath. Both steps go up one notch and the ratio between lead and
   rest is kept, so the ranking still reads at a glance. */
.fp-item .t { font-family: var(--serif); font-size: 15px; font-weight: 600; line-height: 1.34; }
/* the lead story was distinguished by a green digit alone — a 15px cue on a
   surface whose whole job is to say what today is about */
.fp-item.lead .t { font-size: 19px; line-height: 1.3; }
@media (max-width: 780px) { .fp-item.lead .t { font-size: 17.5px; } }
/* ---- the instrument line under each headline -------------------------
   Four facts in one flat grey with only a gap between them read as one long
   string. Middots (the punctuation the sub-line and the rail already use)
   part them, and the outlet — the one thing a reader scans for — steps up to
   --text-dim while the figures stay quiet and tabular. */
.fp-item .s {
  font-family: var(--mono); font-size: 9.5px; color: var(--faint); margin-top: 4px;
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  font-variant-numeric: tabular-nums lining-nums;
}
.fp-item .s > span + span::before {
  content: "·"; margin-right: 6px; color: var(--faint);
}
.fp-item .s > .pubico + span::before { content: none; margin: 0; }
.fp-item .s .src { color: var(--text-dim); font-weight: 500; }
.fp-item .s .sig { color: var(--violet); }
/* #B07CE8 is 2.90:1 on paper — the same walk-down the story line already
   documents for its SIGNAL count. */
[data-theme="light"] .fp-item .s .sig { color: #7C46C2; }
.fp-item .backers { display: inline-flex; gap: 3px; }
.fp-item .backers i {
  width: 14px; height: 14px; border-radius: 50%; display: inline-flex;
  align-items: center; justify-content: center;
  font-family: var(--mono); font-style: normal; font-size: 7px; font-weight: 700; color: #fff;
}
.desk-editors { display: flex; flex-direction: column; }
.ed-row { padding: 9px 14px; border-bottom: 1px solid var(--grid-soft); cursor: pointer; transition: background .12s; }
.ed-row:hover, .ed-row.open { background: var(--panel2); }
.ed-row .r1 { display: flex; align-items: center; gap: 9px; }
.ed-av {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 10px; font-weight: 700; color: #fff;
}
.ed-row .nm { font-weight: 700; font-size: 12.5px; }
.ed-row .rl { font-family: var(--mono); font-size: 9.5px; color: var(--faint); letter-spacing: .08em; text-transform: uppercase; }
.ed-row .beat { font-size: 11px; color: var(--text-dim); margin: 5px 0 0 35px; display: none; }
.ed-row.open .beat { display: block; }
.ed-picks { margin: 7px 0 2px 35px; display: none; }
.ed-row.open .ed-picks { display: block; }
.ed-pick { padding: 6px 0; border-top: 1px dashed var(--grid-soft); }
.ed-pick .pt { font-family: var(--serif); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.ed-pick .pt:hover { color: var(--brand-l); }
.ed-pick .pw { font-size: 11.5px; color: var(--text-dim); font-style: italic; margin-top: 2px; }
.ed-pick.contrarian .pt::before { content: "⚡ "; }
.ed-pick .ctag { font-family: var(--mono); font-size: 8.5px; letter-spacing: .1em; color: var(--down); }
.desk-side { padding: 6px var(--dpad) 12px; display: flex; flex-direction: column; }
.side-sec { padding: 11px 0 7px; border-bottom: 1px solid var(--grid-soft); }
.side-sec:first-child { padding-top: 7px; }
.side-sec:last-child { border-bottom: none; }
.side-sec > .micro { display: block; margin-bottom: 8px; color: var(--brand-l); }
/* Every briefing row is a filter control: role=button + tabindex in app.js,
   and a ring here. Inset by 2px so it does not collide with the row above. */
.desk-side :is(.mv.linked, .wl, .cov):focus-visible {
  outline: 2px solid var(--brand); outline-offset: -1px; border-radius: 5px;
}
/* "INDEX MOVERS ↗" is a real link inside the section label and had neither a
   ring nor a hit area — the padding is cancelled by the margin so the label
   line does not move, and the tap target still comes out 44px tall. */
.desk-side .side-sec > .micro a {
  display: inline-block; padding: 15px 2px; margin: -15px 0;
}
.desk-side .side-sec > .micro a:focus-visible {
  outline: 2px solid var(--brand); outline-offset: -1px; border-radius: 4px;
}
.mv { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
/* a ticker is machine text: mono and tabular, so PSM.DE, 079160.KS and
   0700.HK stack into one column instead of three ragged ones */
.mv .mt {
  font-family: var(--mono); font-weight: 600; font-size: 11px; width: 74px; flex: none;
  font-variant-numeric: tabular-nums lining-nums; letter-spacing: -.01em;
  color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* the company name is the human key to the row and was one step quieter than
   the machine one; --text-dim is the same ink the story's outlet carries */
.mv .mn { font-size: 11.5px; color: var(--text-dim); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---- the two columns the script writes and the stylesheet never declared ---
   app.js emits <span class="mvs"> (wire stories behind the move) and
   <span class="mvbar"><i style="width:N%"> (the magnitude gauge). Neither had
   a rule. mvbar is a flex item with no basis, so it collapsed to zero and the
   <i> inside it — inline, so its height did nothing either — has never drawn a
   single pixel: the comment above mrow() describes a gauge no reader has seen.
   Both get a fixed column here, which is also what lets the counts and the
   percentages sit in one straight edge. */
.mv .mvs {
  width: 24px; flex: none; text-align: right;
  font-variant-numeric: tabular-nums lining-nums;
}
.mv .mvbar {
  width: 52px; flex: none; display: block; height: 6px; border-radius: 3px;
  background: var(--grid-soft); overflow: hidden;
}
.mv .mvbar i { display: block; height: 100%; min-width: 2px; border-radius: 3px; }
.mv i { height: 7px; border-radius: 3px; flex: none; }
.mv i.up { background: var(--up); }
.mv i.down { background: var(--down); }
/* a percentage that updates, in the column the eye runs down: mono, tabular
   and fixed-width so +10.61% and -0.26% line up on the sign and the point */
.mv .num {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  font-variant-numeric: tabular-nums lining-nums;
  width: 62px; text-align: right; flex: none; white-space: nowrap;
}
.mv .mstories { font-variant-numeric: tabular-nums lining-nums; }
/* the header row names the five columns; unstyled it inherited body type and
   "72H" stood 15px tall above the 11px tickers it labels */
.mv.mvhead {
  cursor: default; padding: 0 0 6px; margin-bottom: 4px;
  border-bottom: 1px solid var(--grid-soft);
}
.mv.mvhead :is(.mt, .mn, .mvs, .num) {
  font-family: var(--mono); font-size: 9px; font-weight: 500;
  letter-spacing: .1em; color: var(--faint);
}
.mv.mvhead .mvbar { background: none; }
/* gainers turn into decliners at this row and nothing said so */
.mv.turn { margin-top: 6px; padding-top: 8px; border-top: 1px dashed var(--grid); }
.wl { display: flex; align-items: center; gap: 8px; padding: 5px 0; cursor: pointer; }
.wl:hover .wn { color: var(--brand-l); }
.wl .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.wl .wn { font-size: 12.5px; font-weight: 600; flex: 1; min-width: 0; }
/* ▲27 and 50/7D are two numeric columns read down, not across */
.wl .num {
  font-family: var(--mono); font-size: 10.5px; font-weight: 600;
  font-variant-numeric: tabular-nums lining-nums;
  width: 36px; text-align: right; flex: none;
}
.wl .wc {
  font-family: var(--mono); font-size: 10px; color: var(--faint);
  font-variant-numeric: tabular-nums lining-nums;
  width: 48px; text-align: right; flex: none; letter-spacing: .02em;
}
.cov { display: flex; align-items: center; gap: 8px; padding: 4px 0; cursor: pointer; }
.cov:hover .cn { color: var(--brand-l); }
/* 74px, matching .mv .mt. These two tables stack in one column and their
   ticker columns were 74 and 66, so INDEX MOVERS' company names started 8px
   right of MOST COVERED's — a second ragged edge under the one just fixed.
   74 is set by the widest mover ticker (PVRINOX.NS); no covered ticker is
   near it, so matching costs the name column 8px it does not need. */
.cov .ci {
  font-family: var(--mono); font-size: 10px; font-weight: 600; width: 74px; flex: none;
  letter-spacing: -.01em;
  color: var(--brand-l); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cov .cn { font-size: 11.5px; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* --brand-d is 2.69:1 on the navy panel, and opacity is not a colour */
/* the same track the movers gauge uses, so the two magnitude columns on this
   surface are one instrument read the same way */
.cov .covbar {
  width: 52px; flex: none; display: block; height: 6px; border-radius: 3px;
  background: var(--grid-soft); overflow: hidden;
}
.cov .covbar i {
  display: block; height: 100%; min-width: 2px;
  border-radius: 3px; background: var(--brand);
}
.cov .num {
  font-family: var(--mono); font-size: 10.5px; color: var(--faint);
  font-variant-numeric: tabular-nums lining-nums;
  width: 24px; text-align: right; flex: none;
}
.desk.collapsed .desk-memo, .desk.collapsed .desk-body { display: none; }
@media (max-width: 900px) {
  .desk-body { grid-template-columns: minmax(0, 1fr); }
  .desk-front { border-right: none; }
  /* the memo needs nothing here any more: it is one full-width column at every
     width, so there is no split to undo and no bullet gutter to align */
}

/* ---------------------------------------------------------- rail panels */
.panel { border: 1px solid var(--grid); border-radius: 12px; background: var(--panel); overflow: hidden; }
.panel-h {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--grid);
  background: linear-gradient(180deg, var(--panel2), var(--panel));
}
.panel-h .title { font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--brand-l); }
.panel-h .sub { font-family: var(--mono); font-size: 10px; color: var(--faint); }
.panel-b { max-height: 420px; overflow-y: auto; }
.panel-b::-webkit-scrollbar { width: 8px; }
.panel-b::-webkit-scrollbar-thumb { background: var(--grid); border-radius: 4px; }

/* storylines */
.trk { padding: 10px 14px; border-bottom: 1px solid var(--grid-soft); cursor: pointer; transition: background .12s; }
.trk:hover { background: var(--panel2); }
.trk .r1 { display: flex; align-items: center; gap: 8px; }
.trk .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.trk .nm { font-weight: 600; font-size: 13px; flex: 1; letter-spacing: -.01em; }
.trk .mo { font-family: var(--mono); font-size: 10.5px; }
/* row two is the 7-day chart alone, running the full panel width inside the
   same 14px gutter every other rail row uses */
.trk .r2 { display: block; margin-top: 7px; }
.trk .r2 svg { display: block; width: 100%; }
.trk .cnt { font-family: var(--mono); font-size: 10.5px; color: var(--faint); white-space: nowrap; }
.trk .last { font-size: 11.5px; color: var(--text-dim); margin-top: 5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trk.active { background: var(--panel2); box-shadow: inset 2px 0 0 var(--brand); }

/* docket */
.dk { padding: 9px 14px; border-bottom: 1px solid var(--grid-soft); }
.dk .r1 { display: flex; gap: 8px; align-items: baseline; }
.formchip {
  flex: none; font-family: var(--mono); font-size: 10px; font-weight: 700;
  color: var(--violet); border: 1px solid rgba(176,124,232,.35);
  padding: 0 5px; border-radius: 4px;
}
.formchip.court { color: var(--down); border-color: rgba(207,75,61,.4); }
/* non-US primary sources — a different regulator, not a different severity */
.formchip.intl { color: var(--brand-l); border-color: rgba(47,111,174,.45); }
.dk .mkt { font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
           text-transform: uppercase; color: var(--faint); margin-top: 3px; }
.dk .co { font-size: 12.5px; font-weight: 600; }
.dk .tt { font-size: 11.5px; color: var(--text-dim); margin-top: 2px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.dk .tm { font-family: var(--mono); font-size: 10px; color: var(--faint); margin-left: auto; white-space: nowrap; }
.dktabs { display: flex; gap: 4px; }
.dktabs .pill { padding: 3px 9px; font-size: 10px; font-family: var(--mono); }

/* signal */
.post { padding: 10px 14px; border-bottom: 1px solid var(--grid-soft); }
.post .r1 { display: flex; align-items: center; gap: 8px; }
.post img.av { width: 20px; height: 20px; border-radius: 50%; background: var(--grid); }
.post .who { font-size: 12px; font-weight: 600; }
.post .hdl { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.post .pf { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; margin-left: auto; }
.pf.bluesky { color: var(--brand-l); }
.pf.reddit { color: #E87C5B; }
.pf.hn { color: #F0883E; }
.pf.pod, .pf.pods { color: var(--violet); }
.tostory {
  background: none; border: 1px solid var(--grid); border-radius: 6px;
  color: var(--brand-l); font-family: var(--mono); font-size: 9px; letter-spacing: .08em;
  padding: 1px 7px; cursor: pointer;
}
.tostory:hover { border-color: var(--brand-d); }

/* desk take + signal inside story expansion */
.desk-take {
  margin: 0 0 12px; padding: 10px 13px; max-width: 66ch;
  border-left: 3px solid var(--brand-d); border-radius: 0 9px 9px 0;
  background: var(--panel2);
  font-family: var(--serif); font-style: italic; font-size: 13.5px; color: var(--text);
}
.desk-take .micro { display: block; margin-bottom: 4px; color: var(--brand-l); font-style: normal; }
.sig-row {
  display: block; padding: 7px 11px; border: 1px solid var(--grid-soft); border-radius: 9px;
  margin-bottom: 6px; max-width: 66ch; transition: border-color .12s;
}
.sig-row:hover { border-color: var(--brand-d); }
.sig-row .pf { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; margin-right: 8px; }
.sig-row .who { font-size: 12px; font-weight: 600; margin-right: 6px; }
.sig-row .hdl { font-family: var(--mono); font-size: 10px; color: var(--faint); }
.sig-row .tx { display: block; font-size: 12px; color: var(--text-dim); margin-top: 3px; }
.sig-row .eng { font-family: var(--mono); font-size: 10px; color: var(--faint); margin-top: 3px; display: block; }
.sigchip { font-family: var(--mono); font-size: 10px; color: var(--violet); }
/* The headline's money figure. Deliberately NOT green/red and not tinted by
   size: it states what the story says and makes no claim about whether the
   number is big for its kind — the wire cannot support that claim yet. */
.figchip {
  font-family: var(--mono); font-size: 10px; letter-spacing: .04em;
  color: var(--text-dim); border: 1px solid var(--grid);
  padding: 1px 6px; border-radius: 5px;
}
.langchip {
  font-family: var(--mono); font-size: 10px; letter-spacing: .05em;
  color: var(--up-l); border: 1px solid color-mix(in srgb, var(--up-l) 35%, transparent);
  padding: 1px 6px; border-radius: 5px;
}
.movechip {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  padding: 1px 7px; border-radius: 5px; border: 1px solid;
  font-variant-numeric: tabular-nums;
}
.movechip.up { color: var(--up-l); border-color: color-mix(in srgb, var(--up-l) 40%, transparent); }
.movechip.down { color: var(--down); border-color: rgba(207,75,61,.4); }
/* A pill from an earlier session than this payload. Green/red is the wire
   saying "this is happening now"; a previous close has not earned that, so it
   loses the colour and carries its date. Overrides .up/.down, which is why it
   comes after them. */
.movechip.prev, .movechip.prev.up, .movechip.prev.down {
  color: var(--text-dim); border-color: var(--grid);
}
.movechip .mvday {
  margin-left: 5px; padding-left: 5px; border-left: 1px solid var(--grid);
  font-weight: 400; color: var(--faint); text-transform: uppercase;
  letter-spacing: .04em;
}
.mv.linked { cursor: pointer; border-radius: 6px; }
.mv.linked:hover { background: var(--panel2); }
.mv .mstories { font-size: 10px; color: var(--brand-l); font-weight: 700; }
/* Unclamped, a post ran anywhere from 111px to 235px, so the panel height
   needed to guarantee five was ~870px — a rail box taller than the viewport.
   Clamping the body to four lines bounds a post at ~125px instead, which is
   what makes "at least five visible" achievable at a sane height. Four lines
   is enough to read a Bluesky post; the link opens the rest. */
.post .tx {
  font-size: 12.5px; color: var(--text-dim); margin-top: 5px;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.post .eng { display: flex; gap: 12px; margin-top: 5px; font-family: var(--mono); font-size: 10.5px; color: var(--faint); }

/* index panel */
.idxbig { display: flex; align-items: baseline; gap: 10px; padding: 12px 14px 6px; }
.idxbig .lvl { font-family: var(--mono); font-size: 26px; font-weight: 700; color: #fff; }
.idxrow { display: flex; gap: 16px; padding: 0 14px 12px; }
.idxrow .k { font-family: var(--mono); font-size: 9.5px; color: var(--faint); letter-spacing: .1em; }
.idxrow .v { font-family: var(--mono); font-size: 13px; }
.idxspark { padding: 0 14px 14px; }
/* provenance, one fact per line rather than a single run-on rule */
.idxasof {
  margin-top: 9px; padding-top: 8px; border-top: 1px solid var(--grid-soft);
  font-size: 9.5px; letter-spacing: .06em; line-height: 1.65;
  display: flex; flex-direction: column; gap: 1px;
}
.idxasof a { color: var(--brand-l); text-decoration: none; }
.idxasof a:hover { text-decoration: underline; }

/* ---------------------------------------------------------- drawer */
.drawer {
  position: fixed; top: 0; right: -520px; width: 500px; max-width: 94vw; height: 100vh;
  background: var(--bg2); border-left: 1px solid var(--grid); z-index: 200;
  transition: right .25s ease; display: flex; flex-direction: column;
  /* shadow only while open — parked off-canvas, a permanent 50px blur
     bleeds back into the viewport as a dark band on the right edge */
  box-shadow: none;
}
.drawer.open { right: 0; box-shadow: -18px 0 50px rgba(0,0,0,.5); }
.drawer-h { padding: 16px 18px; border-bottom: 1px solid var(--grid); display: flex; align-items: center; gap: 10px; }
.drawer-h .title { font-family: var(--mono); letter-spacing: .15em; color: var(--brand-l); font-size: 12px; }
.drawer-h .x { margin-left: auto; }
.drawer-b { flex: 1; overflow-y: auto; padding: 14px 18px; }
.drawer-f { padding: 12px 18px; border-top: 1px solid var(--grid); display: flex; gap: 8px; }
.nl-item { display: flex; gap: 9px; padding: 8px 0; border-bottom: 1px solid var(--grid-soft); align-items: flex-start; }
.nl-item .t { font-size: 12.5px; flex: 1; font-family: var(--serif); }
.nl-item .s { font-family: var(--mono); font-size: 10px; color: var(--faint); display: block; margin-top: 2px; }
.nl-item .rm { background: none; border: none; color: var(--faint); cursor: pointer; font-size: 14px; }
.nl-item .rm:hover { color: var(--down); }
.nl-sec { margin: 14px 0 4px; }
.nl-sec.micro { color: var(--brand-l); }
.drawer textarea {
  /* --field, not --panel: the brief builder's textarea had the same fault as
     the newsletter input — painted the same colour as the drawer behind it,
     pixel identical in light. A field has to look like a field. */
  width: 100%; min-height: 70px; background: var(--field); color: var(--text);
  border: 1px solid var(--grid); border-radius: 9px; padding: 9px; font-family: var(--sans);
  font-size: 12.5px; resize: vertical; outline: none;
}
.drawer textarea:focus { border-color: var(--brand-d); }
.hint { font-size: 11.5px; color: var(--faint); margin: 8px 0; }

.overlay { position: fixed; inset: 0; background: rgba(3,10,17,.6); z-index: 150; display: none; }
.overlay.on { display: block; }

/* ---------------------------------------------------------- modal */
.modal {
  position: fixed; inset: 0; z-index: 300; display: none;
  align-items: center; justify-content: center; background: rgba(3,10,17,.7);
}
.modal.on { display: flex; }
.modal-card {
  width: 620px; max-width: 92vw; max-height: 80vh; overflow-y: auto;
  background: var(--bg2); border: 1px solid var(--grid); border-radius: 12px; padding: 20px;
}
.modal-card h2 { font-family: var(--mono); font-size: 13px; letter-spacing: .15em; color: var(--brand-l); margin-bottom: 12px; }
.srcrow { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid var(--grid-soft); font-size: 12.5px; }
.srcrow .ok { color: var(--up-l); font-family: var(--mono); font-size: 11px; }
.srcrow .fail { color: var(--down); font-family: var(--mono); font-size: 11px; }

/* toast */
.toast {
  position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%) translateY(80px);
  background: var(--panel2); border: 1px solid var(--brand-d); color: var(--text);
  font-family: var(--mono); font-size: 12px; padding: 10px 18px; border-radius: 10px;
  z-index: 400; transition: transform .25s; pointer-events: none;
}
.toast.on { transform: translateX(-50%) translateY(0); }
/* The toast is click-through by default so a passing confirmation never eats
   a click on the story underneath. A toast that OFFERS something has to opt
   back in — without this the kill toast's UNDO was unclickable, which is why
   it never worked. Scoped to .act so ordinary toasts stay inert. */
.toast.act { pointer-events: auto; }
.killwhy {
  display: inline-block; margin: 2px 2px; padding: 3px 7px; cursor: pointer;
  border: 1px solid var(--grid); border-radius: 6px; font-weight: 600;
  font-size: 11px; letter-spacing: .04em; color: var(--muted);
  pointer-events: auto; white-space: nowrap;
}
.killwhy:hover { border-color: var(--brand-d); color: var(--text); }
.killwhy[data-why="__undo"] { border-color: var(--up-l); color: var(--up-l); }
.killwhy[data-why="__skip"] { border-style: dashed; color: var(--faint); }

/* a killed story awaiting its reason — holds the slot the story had, so the
   headline is still readable while it is being judged */
.whyrow {
  border: 1px dashed var(--down); border-radius: 10px;
  background: var(--panel2); padding: 10px 12px; margin-bottom: 10px;
}
.whyrow .wr-h { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.whyrow .wr-tag {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .08em;
  color: var(--down); border: 1px solid var(--down); border-radius: 4px;
  padding: 1px 5px; flex: none;
}
.whyrow .wr-t {
  font-size: 13px; font-weight: 600; color: var(--text-dim);
  text-decoration: line-through; text-decoration-color: var(--faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.whyrow .wr-c { display: flex; flex-wrap: wrap; align-items: center; gap: 1px; }
.whyrow .wr-q {
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  color: var(--faint); margin-right: 6px;
}

/* operator unlock — an in-page form, because prompt() is optional in a
   browser and throwing on it used to take the whole terminal down */
.oplock {
  position: fixed; inset: 0; z-index: 600; display: flex;
  align-items: center; justify-content: center;
  background: rgba(4, 12, 20, .62);
}
.oplock-box {
  width: min(420px, 92vw); background: var(--panel);
  border: 1px solid var(--brand-d); border-radius: 12px; padding: 18px 20px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .45);
}
.oplock-h {
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em;
  color: var(--brand-l); margin-bottom: 8px;
}
.oplock-box input {
  width: 100%; margin: 10px 0 4px; padding: 9px 11px; background: var(--panel2);
  border: 1px solid var(--grid); border-radius: 8px; color: var(--text);
  font-family: var(--mono); font-size: 13px;
}
.oplock-box input:focus { outline: none; border-color: var(--brand-d); }
.oplock-a { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }

/* footer */
.foot {
  border-top: 1px solid var(--grid); margin-top: 30px; padding: 18px;
  background: var(--footer);
  display: flex; gap: 20px; align-items: center; justify-content: center; flex-wrap: wrap;
  font-family: var(--mono); font-size: 10.5px; color: var(--faint); letter-spacing: .08em;
}

/* empty state */
.empty { text-align: center; padding: 80px 20px; color: var(--faint); }
.empty .big { font-family: var(--mono); font-size: 15px; color: var(--text-dim); margin-bottom: 10px; }
.empty code { background: var(--panel); border: 1px solid var(--grid); padding: 2px 8px; border-radius: 6px; font-size: 12px; }

/* ---------------------------------------------------------- responsive */
@media (max-width: 1120px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .rail { border-left: none; border-top: 1px solid var(--grid); }
  .clocks { display: none; }
}
@media (max-width: 720px) {
  .gei-strip .spark { display: none; }
  .searchbox input { width: 130px; }
  .searchbox input:focus { width: 170px; }
  .story { grid-template-columns: minmax(0,1fr) auto; }
  .story .when { display: none; }
  .wordmark .tag { display: none; }
}
@media print {
  .topbar, .ticker-wrap, .rail, .filters, .drawer, .foot { display: none !important; }
}

/* ============================================================
   READABILITY LAYER — typography scale refresh (2026-07-02).
   Later rules win the cascade: this block re-declares sizes so
   nothing reader-facing sits below ~11px, body copy reads at
   15px+, and secondary text gets more contrast in both themes.
   ============================================================ */

:root {
  --text-dim: #B7C9D8;
  --faint:    #8CA3B5;
}
[data-theme="light"] {
  --text-dim: #35526B;
  /* CONTINUITY: --faint, --up-l, --down and --violet were each re-declared
     inside .desk, .rail and (as raw hexes) on the story's instrument line,
     because three builders each measured the same hue against their own
     ground. The result was one token with three values: the same grey was
     #5F7B92 on the feed, #55708A on the instrument line and #4E6980 in the
     rail. These are the darkest of the three sets — every one of them was
     already shipping somewhere — so hoisting them here only RAISES contrast
     on the surfaces that had the lighter value, and it makes the wire read
     as one instrument. Measured on white (the feed's ground, the weakest):
       --faint  #4E6980  5.34:1     --up-l  #12734D  5.86:1
       --down   #B3392C  5.94:1     --violet #7C46C2 5.98:1 */
  --faint:    #4E6980;
  --up-l:     #12734D;
  --down:     #B3392C;
  --violet:   #7C46C2;
}
/* the navy twin: #CF4B3D is 3.71:1 on #0A2032 and 3.34:1 on the desk panel.
   .desk, .rail and the story line each patched it separately; one value now. */
[data-theme="dark"], :root:not([data-theme="light"]) { --down: #EA6A5C; }

body { font-size: 15px; line-height: 1.5; }

/* micro labels & chips */
.micro { font-size: 11px; }
.srcchip, .catchip, .tagchip, .trchip, .coverage, .sigchip { font-size: 11px; }
.formchip { font-size: 11px; }
.score-dot .v { font-size: 11px; }
.searchbox kbd { font-size: 11px; }

/* topbar */
.wordmark .gme { font-size: 13.5px; }
.wordmark .wire { font-size: 22px; }
.gei-strip .lvl { font-size: 15px; }
.gei-strip .chg { font-size: 12.5px; }
.clock .t { font-size: 13.5px; }
.searchbox input { font-size: 14px; }
.btn { font-size: 12px; }

/* ticker */
.tick { font-size: 13px; }
.ticker-label { font-size: 11px; }

/* filters & feed meta */
.pill { font-size: 13px; padding: 6px 13px; }
.pill .n { font-size: 11.5px; }
.filters select { font-size: 12px; padding: 6px 9px; }
.feedmeta .count { font-size: 12px; }
.feedmeta .micro { font-size: 11px; }

/* story cards */
.story h3 { font-size: 18px; line-height: 1.34; }
.story .sum { font-size: 13.5px; line-height: 1.5; }
.story .when .ago { font-size: 12px; }
.story .when .new { font-size: 10px; }
.alsoOn a { font-size: 12px; }
.starbtn { width: 30px; height: 27px; font-size: 15px; }

/* inline expansion */
.story-expand .xbody { font-size: 16px; line-height: 1.7; }
.story-expand .xnote { font-size: 12.5px; }
.desk-take { font-size: 14.5px; line-height: 1.55; }
.rel .t { font-size: 14.5px; line-height: 1.4; }
.rel .s { font-size: 11px; }
.sig-row .who { font-size: 13.5px; }
.sig-row .hdl { font-size: 11px; }
.sig-row .tx { font-size: 13.5px; line-height: 1.5; }
.sig-row .eng { font-size: 11px; }
.sig-row .pf { font-size: 10px; }
.xactions .btn { font-size: 12px; }

/* the desk */
.desk-h .title { font-size: 12px; }
.desk-h .sub { font-size: 11px; }
.desk-memo .memo-lead p { font-size: 18px; line-height: 1.5; }
.desk-memo .memo-ahead p { font-size: 15px; line-height: 1.55; }
.fp-item .rank { font-size: 17px; }
.fp-item .t { font-size: 15.5px; line-height: 1.38; }
.fp-item .s { font-size: 11px; gap: 7px; }
.desk-watch .wt { font-size: 10.5px; }

/* rail panels */
.panel-h .title { font-size: 12px; }
.panel-h .sub { font-size: 10.5px; }
.idxbig .lvl { font-size: 29px; }
.idxrow .k { font-size: 10.5px; }
.idxrow .v { font-size: 14px; }
.trk .nm { font-size: 14px; }
.trk .mo { font-size: 11.5px; }
.trk .cnt { font-size: 11.5px; }
.trk .last { font-size: 12.5px; }
.trk { padding: 11px 14px; }
.dk .co { font-size: 13.5px; }
.dk .tt { font-size: 12.5px; line-height: 1.45; }
.dk .tm { font-size: 11px; }
.dk { padding: 10px 14px; }
.post .who { font-size: 13.5px; }
.post .hdl { font-size: 11.5px; }
.post .pf { font-size: 10px; }
.post .tx { font-size: 13.5px; line-height: 1.5; }
.post .eng { font-size: 11.5px; }
.post { padding: 11px 14px; }
.tostory { font-size: 10.5px; padding: 2px 8px; }
.dktabs .pill { font-size: 11px; padding: 4px 10px; }

/* drawer & modal */
.drawer-h .title { font-size: 13px; }
.nl-item .t { font-size: 14px; line-height: 1.4; }
.nl-item .s { font-size: 11px; }
.drawer textarea { font-size: 13.5px; }
.hint { font-size: 12.5px; line-height: 1.5; }
.modal-card h2 { font-size: 14px; }
.srcrow { font-size: 13.5px; }
.srcrow .ok, .srcrow .fail { font-size: 11.5px; }

/* toast, footer, empty */
.toast { font-size: 13px; }
.foot { font-size: 11.5px; }
.empty .big { font-size: 16px; }
.empty { font-size: 14px; }

/* breathing room to match the larger type */
.shell { grid-template-columns: minmax(0, 1fr) 384px; }
.panel-b { max-height: 480px; }
.story { padding: 13px 15px; }
.story .meta { gap: 8px; margin-top: 8px; }
@media (max-width: 1120px) { .shell { grid-template-columns: minmax(0, 1fr); } }

/* ============================================================
   TYPE CONSOLIDATION — two families only.
   Newsreader  = editorial voice (story text, memo, takes, masthead-adjacent)
   Inter       = body copy, summaries, buttons
   IBM Plex Mono = labels, tickers, timestamps, tape, chips — the Wire's
   fingerprint per the brand kit. (An earlier consolidation aliased --mono
   to Inter here; the kit reversed that call, so the alias is gone.)
   Numeric contexts keep tabular figures so columns align.
   ============================================================ */

/* tabular, lining figures wherever numbers stack or tick */
.num, .gei-strip .lvl, .gei-strip .chg, .clock .t,
.idxbig .lvl, .idxrow .v, .story .when .ago, .dk .tm,
.post .eng, .sig-row .eng, .score-dot .v, .trk .cnt, .trk .mo,
.fp-item .rank, .pill .n {
  font-variant-numeric: tabular-nums lining-nums;
}

/* storylines panel always shows in full — no inner scroll */
#trkBody { max-height: none; overflow: visible; }

/* overflow guards: flex children must be allowed to shrink, else long
   headlines spill across the desk columns */
.fp-item > div { flex: 1; min-width: 0; }
.fp-item .t { overflow-wrap: break-word; }
.fp-item .s .pubico { width: 16px; height: 16px; }
.fp-item .s .pubico img { width: 12px; height: 12px; }
.wl .wn, .cov .ce, .mv .mn { min-width: 0; }
.desk-memo { overflow-wrap: break-word; }

/* rail panel headers: let tab rows wrap instead of overflowing */
.panel-h { flex-wrap: wrap; row-gap: 6px; }
.dktabs { flex-wrap: wrap; }
/* (.desk-h used to wrap as a flex row; it is an explicit two-row grid on the
   phone now, which is what keeps the chevron pinned instead of floating.) */

/* row of outlet icons next to the coverage chip */
.alsoicons { display: inline-flex; gap: 3px; vertical-align: -4px; }
.alsoicons .pubico { width: 17px; height: 17px; }
.alsoicons .pubico img { width: 13px; height: 13px; }

/* off-canvas elements (drawer) must never create sideways scroll */
html, body { overflow-x: hidden; }

/* ============================================================
   MOBILE LAYER — phones & small tablets
   ============================================================ */
@media (max-width: 780px) {
  html, body { overflow-x: hidden; }
  .clocks { display: none; }             /* six clocks don't fit a phone */

  /* category chips: one swipeable row instead of a wall of wrapped pills */
  .filters { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px;
             scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .filters::-webkit-scrollbar { display: none; }
  .filters .pill { flex: 0 0 auto; }
  .filters select { flex: 0 0 auto; }

  /* finger-sized teach & kill controls */
  .votebtn { width: 30px; height: 26px; font-size: 11px; }
  .votebtn.kill { width: 54px; height: 24px; font-size: 10px; opacity: .7; }
  .starbtn { width: 30px; height: 26px; }
  .xactions { flex-wrap: wrap; }

  .panel-b { max-height: 340px; }
  .topbar { gap: 10px; padding: 0 12px; height: 50px; }
  .wordmark { gap: 6px; }
  .wordmark .gme { font-size: 9px; letter-spacing: .1em; }
  .wordmark .wire { font-size: 18px; }
  .gei-strip { display: none; }          /* index lives on in the rail panel */
  #srcBtn { display: none; }
  .searchbox { flex: 1; min-width: 0; }
  .searchbox input, .searchbox input:focus { width: 100%; }
  .searchbox kbd { display: none; }
  .btn { padding: 7px 10px; }

  .ticker-label { padding: 0 10px; }
  .main { padding: 12px 12px 50px; }
  .rail { padding: 12px 12px 50px; gap: 14px; }

  .filters { gap: 5px; }
  .pill { padding: 5px 10px; font-size: 12px; }
  .filters select { padding: 5px 6px; font-size: 11px; }

  .story { padding: 12px 10px; gap: 10px; }
  .story h3 { font-size: 16px; }
  .story-expand .xbody { font-size: 15px; }
  .story-expand { padding-top: 12px; }
  .xactions .btn { padding: 8px 10px; }

  /* two rows, chevron spanning both and pinned right */
  .desk-h {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "t c" "s c";
    column-gap: 8px; row-gap: 2px; padding: 8px var(--dpad);
  }
  .desk-memo .memo-lead { padding: 11px var(--dpad) 12px; }
  .desk-memo .memo-lead p { font-size: 16.5px; line-height: 1.45; }
  .desk-memo .memo-ahead p { font-size: 14.5px; line-height: 1.5; }
  /* the label stacks over its line on a phone and the block lands on the same
     gutter as the lead, the bullets and the #1 headline */
  .desk-memo .memo-ahead {
    grid-template-columns: minmax(0, 1fr); row-gap: 4px;
    padding: 10px var(--dpad) 12px calc(var(--dpad) + var(--rankw) + var(--rankgap));
  }
  .fp-item { padding: 10px var(--dpad); }
  .fp-item .rank { font-size: 15px; }
  .desk-side { padding: 4px var(--dpad) 10px; }
  /* 44px minimum on every filter row — they were 21px tall targets. Only the
     rows that are actually controls: the column header is not one, and giving
     it 44px put a hole between the label and the first ticker. */
  .desk-side :is(.mv.linked, .wl, .cov) { min-height: 44px; padding: 6px 0; }
  /* five fixed columns left the company name 96px on a 340px row, so
     MFE-MediaForEurope was "MFE-MediaFor…". The machine columns give back
     what they do not need — a ticker is 9 characters at most and a move is 7 */
  /* the two ticker columns move together, or the company names under them
     go ragged again at this width the way they did at full width */
  .mv .mt, .cov .ci { width: 66px; }
  .mv .mvbar, .cov .covbar { width: 40px; }
  .mv .num { width: 56px; }

  .drawer { width: 100vw; max-width: 100vw; right: -102vw; }
  .drawer.open { right: 0; }
  .modal-card { padding: 14px; }
  .toast { max-width: 92vw; text-align: center; }
  .foot { gap: 10px; font-size: 10.5px; padding: 14px 10px; flex-wrap: wrap; }
}

/* small phones */
@media (max-width: 480px) {
  .story h3 { font-size: 15.5px; }
  .story { padding: 11px 8px; }
  .desk-memo .memo-lead p { font-size: 15.5px; }
  .desk-memo .memo-ahead p { font-size: 14px; }
  .fp-item .t { font-size: 14.5px; }
  .wordmark .gme { font-size: 8px; letter-spacing: .08em; }
  .ticker-label { display: none; }
  .searchbox input::placeholder { font-size: 11px; }
}

/* modern viewport unit for full-height drawer on iOS Safari */
.drawer { height: 100vh; height: 100dvh; }

/* uppercase micro-labels read wider in Inter — ease the tracking a touch */
.micro, .panel-h .title, .desk-h .title, .drawer-h .title,
.ticker-label, .btn { letter-spacing: .1em; }
.srcchip, .catchip, .formchip { letter-spacing: .05em; font-weight: 600; }

/* overview headlines in Inter — serif stays for reading text & editorial voice
   (expansion body, desk memo & takes, the "Wire" wordmark) */
.story h3 { font-family: var(--sans); font-weight: 700; font-size: 17px; letter-spacing: -.015em; line-height: 1.36; }
.fp-item .t { font-family: var(--sans); font-weight: 600; font-size: 14.5px; letter-spacing: -.01em; }
.rel .t { font-family: var(--sans); font-weight: 600; font-size: 13.5px; letter-spacing: -.005em; }
.nl-item .t { font-family: var(--sans); font-size: 13.5px; }
.trk .nm, .dk .co { letter-spacing: -.005em; }

/* grid-blowout guards: children may never inflate their column */
.story > * { min-width: 0; }
#feed, .main > * { max-width: 100%; }

/* iOS: sub-16px inputs trigger focus-zoom; keep text stable in landscape */
html { -webkit-text-size-adjust: 100%; }
@media (max-width: 780px) {
  .searchbox input, .drawer textarea { font-size: 16px; }
}

/* ============================================================
   MEDIA CFO — newsletter signup panel
   ============================================================ */
.mcfo-pitch { margin: 0 0 10px; font-size: 12.5px; line-height: 1.55;
              color: var(--muted, var(--faint)); }
.mcfo-form { display: flex; gap: 6px; }
.mcfo-form input {
  flex: 1; min-width: 0; padding: 8px 10px;
  font: 500 13px/1.2 var(--sans, Inter, sans-serif);
  background: var(--field); border: 1px solid var(--grid);
  border-radius: 8px; color: var(--text);
}
.mcfo-form input::placeholder { color: var(--faint); }
.mcfo-form input:focus { outline: none; border-color: var(--brand-l); }
/* A valid address earns a tick. No JavaScript: the input is type=email and
   required, so the browser already knows, and :valid is that knowledge for
   free. Required is what makes it safe — an EMPTY field is :invalid, so the
   tick cannot appear before anything has been typed.
   The mark is drawn in #1f9d6b, which is --up's value in BOTH themes (dark
   and light define it identically), so one asset is correct on both fills. A
   data URI cannot read a custom property, and this is the reason it does not
   need to. If --up is ever re-toned per theme, this hex must follow it. */
.mcfo-form input:valid {
  padding-right: 27px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.6l3.3 3.2L13 4.8' fill='none' stroke='%231f9d6b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 13px 13px;
}
.mcfo-btn { background: var(--brand-d); border-color: var(--brand-d);
            color: #fff; font-weight: 700; white-space: nowrap; }
.mcfo-btn:hover { background: var(--brand); border-color: var(--brand); }
.mcfo-fine { margin: 8px 0 0; letter-spacing: .08em; }
@media (max-width: 780px) { .mcfo-form input { font-size: 16px; } }

/* THE TRADES — subscribe-at-the-source panel */
.trd { display: block; padding: 8px 0; border-bottom: 1px solid var(--grid-soft);
       text-decoration: none; }
.trd:last-of-type { border-bottom: none; }
.trd-n { display: block; font-weight: 700; font-size: 13px; color: var(--text);
         letter-spacing: -.005em; }
.trd-b { display: block; font-size: 11.5px; color: var(--faint);
         margin-top: 1px; line-height: 1.45; }
.trd-go { display: block; font-family: var(--mono); font-size: 10px;
          letter-spacing: .12em; color: var(--brand-l); margin-top: 3px; }
.trd:hover .trd-n { color: var(--brand-l); }

/* ============================================================
   POLISH LAYER (2026-07-18) — topbar collapse, panel spacing,
   3-line summaries, light-theme signup button
   ============================================================ */

/* topbar: the strips step out as the window narrows — nothing ever clips.
   Full set needs ~1420px: clocks go first, then the GEI strip; the AXIOM
   GEI panel in the rail carries the index below that. */
.gei-strip { flex: 0 1 auto; min-width: 0; overflow: hidden; }
@media (max-width: 1439px) { .clocks { display: none; } }
@media (max-width: 1259px) { .gei-strip { display: none; } }
@media (max-width: 1560px) { .searchbox input:focus { width: 240px; } }

/* signup + trades panels get real padding (.panel-b has none by design —
   every panel body brings its own) */
.panel-b.mcfo { padding: 11px 14px 13px; }
#trdBody { padding: 9px 14px 13px; }

/* the SUBSCRIBE button is solid in BOTH themes — the light-theme
   [data-theme="light"] .btn override was painting it white-on-white */
.mcfo-btn, [data-theme="light"] .mcfo-btn {
  background: var(--brand-d); border-color: var(--brand-d); color: #fff;
}
.mcfo-btn:hover, [data-theme="light"] .mcfo-btn:hover {
  background: var(--brand); border-color: var(--brand); color: #fff;
}

/* trades rows: a touch more air */
.trd { padding: 9px 0; }
.trd-b { margin-top: 2px; }
.trd-go { margin-top: 4px; }

/* HOUSE RULE: every story shows ~three lines of summary/preview, never cut
   mid-sentence — the trim happens at sentence boundaries in previewText(),
   so no CSS clamp (a clamp chops visually; a sentence trim reads clean) */

/* trades v2: publication logos + a taller box (more of the ten visible) */
.trd { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px;
       align-items: start; }
.trd .pubico { margin-top: 2px; }
.trd-tx { min-width: 0; display: block; }
#trdPanel .panel-b { max-height: 620px; }
@media (max-width: 780px) { #trdPanel .panel-b { max-height: 460px; } }

/* ============================================================
   INDEX FAMILY (2026-07-19) — the blue AXIOM band in both themes,
   mirroring globalentertainmentindex.com; white VC lockup top-right.
   ============================================================ */
.topbar, [data-theme="light"] .topbar {
  background: #1D6BB4;
  border-bottom: 1px solid rgba(255,255,255,.16);
  box-shadow: none; backdrop-filter: none;
}
.wordmark .gme, [data-theme="light"] .wordmark .gme { color: rgba(255,255,255,.78); }
.wordmark .wire, [data-theme="light"] .wordmark .wire { color: #fff; }
.topbar .btn, [data-theme="light"] .topbar .btn {
  background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.30); color: #fff;
}
.topbar .btn:hover, [data-theme="light"] .topbar .btn:hover {
  background: rgba(255,255,255,.22); color: #fff;
}
.topbar .searchbox input, [data-theme="light"] .topbar .searchbox input {
  background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.26); color: #fff;
}
.topbar .searchbox input:focus { border-color: rgba(255,255,255,.5); }
.topbar .searchbox input::placeholder { color: rgba(255,255,255,.55); }
.topbar .searchbox .icon { color: rgba(255,255,255,.62); }
.topbar .searchbox kbd { color: rgba(255,255,255,.65); border-color: rgba(255,255,255,.3); background: transparent; }
.topbar .gei-strip, [data-theme="light"] .gei-strip {
  background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.24);
}
.topbar .gei-strip .lvl, [data-theme="light"] .gei-strip .lvl { color: #fff; }
.topbar .clock .c { color: rgba(255,255,255,.55); }
.topbar .clock .t, [data-theme="light"] .topbar .clock .t { color: #fff; }
.axm { height: 26px; width: auto; display: block; }
@media (max-width: 1000px) { .axm { display: none; } }

/* THE CALENDAR — verified industry diary */
.cal { display: grid; grid-template-columns: 92px minmax(0,1fr) auto;
       gap: 10px; align-items: baseline; padding: 8px 14px;
       border-bottom: 1px solid var(--grid-soft); text-decoration: none; }
.cal:last-of-type { border-bottom: none; }
.cal-d { font-family: var(--mono); font-size: 10.5px; letter-spacing: .04em;
         color: var(--brand-l); white-space: nowrap; }
.cal-n { display: block; font-weight: 600; font-size: 12.5px; color: var(--text);
         letter-spacing: -.005em; line-height: 1.35; }
.cal-c { display: block; font-size: 11px; color: var(--faint); margin-top: 1px; }
.cal-t1 { color: var(--brand-l); font-weight: 700; font-size: 9.5px;
          letter-spacing: .08em; }
.cal-in { font-family: var(--mono); font-size: 10px; color: var(--text-dim);
          white-space: nowrap; }
.cal:hover .cal-n { color: var(--brand-l); }
/* 520px cut off the twelfth row and the "FULL CALENDAR — n verified" line
   underneath it, so the count was never visible. Content height instead, same
   as the storylines, podcast and trades panels. */
#calPanel .panel-b { max-height: none; overflow: visible; }

/* Five posts, guaranteed rather than estimated.
   The first pass said 580px on the claim that "a .post runs ~105px". It does
   not: nothing clamped .post .tx, so posts ran 111-235px and three cleared the
   panel. Worse, the clamp added next did nothing either — the anchor carried an
   inline display:block, and -webkit-line-clamp needs display:-webkit-box, so
   the inline style silently outranked it. Both are fixed.
   With .tx at three lines the tallest post measures 175px (three lines plus an
   "ON THE WIRE →" row), so 880px = 5 x the WORST case, not 5 x the average —
   which is what the last two estimates got wrong. It still scrolls past five. */
#sgBody { max-height: 880px; }
#calBody .mcfo-fine { padding: 8px 14px 2px; }

/* The diary's own scale, given the room it earns: two figures on a row of
   their own, then the link underneath as a link. */
.cal-foot { padding: 13px 14px 14px; border-top: 1px solid var(--grid); }
.cal-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cal-stats .cs { display: flex; flex-direction: column; gap: 2px; }
.cal-stats .n {
  font-family: var(--mono); font-size: 20px; font-weight: 600;
  line-height: 1; color: var(--ink);
  font-variant-numeric: tabular-nums lining-nums;
}
.cal-stats .l {
  font-size: 10.5px; line-height: 1.3; color: var(--faint);
}
.cal-more {
  display: block; margin-top: 12px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
  color: var(--brand-l); text-decoration: none;
}
.cal-more:hover { text-decoration: underline; }

/* world clocks: their own strip under the blue band, above the ticker —
   the index site's pattern. Overrides every earlier hide rule. */
.clock-strip {
  display: flex; align-items: center;
  padding: 5px 20px; background: var(--bg2);
  border-bottom: 1px solid var(--grid);
  overflow-x: auto; scrollbar-width: none;
}
.clock-strip::-webkit-scrollbar { display: none; }
.clock-strip .clocks {
  display: flex !important; gap: 30px; margin: 0 auto; flex-wrap: nowrap;
}
.clock-strip .clock {
  display: flex !important; gap: 7px; align-items: baseline;
  text-align: left; flex: none;
}
.clock-strip .clock::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand); align-self: center; flex: none;
}
.clock-strip .clock .t { color: var(--text); font-size: 12px; }
.clock-strip .micro { letter-spacing: .1em; }
@media (max-width: 780px) {
  .clock-strip { padding: 4px 12px; }
  .clock-strip .clocks { gap: 18px; margin: 0; }
}

/* mobile desk: keep the provenance line — "N stories reviewed · convened
   Xm ago" is the desk's credibility, phones included */
@media (max-width: 780px) {
  /* .06em of tracking pushed a 51-character provenance line to 320px in a
     314px cell and left "UTC" alone on a second row. Same size, tracking
     walked back to .02em — 300px, and it holds a four-digit pool too. */
  .desk-h .sub { display: inline; font-size: 9.5px; letter-spacing: .02em; }
}

/* footer, index-pattern: brand block + credits, parent line, copy line */
/* The footer is a sibling of .shell inside an unstyled #app, and .ftr is
   full-bleed — so a 1100px .ftwrap centred itself under a 1560px page and sat
   210px right of the story column at 1600px, 150 at 1440, 70 at 1280. It is the
   only band on the page that did not line up with the content above it.
   Use the same full-bleed expression the topbar and clock-strip already use so
   the footer's left edge lands exactly on the feed's. */
/* The index's brand blue, fixed rather than themed — it is the same band on
   both properties and reads as one house style. Everything inside therefore
   sets its own colour against blue instead of inheriting the page's. */
.ftr { background: #1D6BB4; color: #e9f2fb; border-top: none;
       padding: 34px max(20px, calc((100% - 1560px) / 2 + 20px)) 30px;
       margin-top: 30px; }
.ftwrap { max-width: 1520px; margin: 0; }
/* GEI's footer is one left-flush narrative stack, not columns — brand, then
   credits, then parent company, then the rule, then the disclaimer. */
.ftcols { display: flex; flex-direction: column; align-items: flex-start; gap: 22px; }
.ftlogo { height: 40px; width: auto; display: block; margin-bottom: 12px;
          opacity: .95; }
/* the band is always blue now, so the mark is always white on it */
.ftlogo { filter: brightness(0) invert(1); }
[data-theme="light"] .ftlogo { filter: brightness(0) invert(1); }
.ftdesc { font-size: 12.5px; line-height: 1.6; color: #e9f2fb; max-width: 560px; }
.ftdesc b { color: #fff; }
.ftdesc a, .cred a, .ftcopy a, .ftdisc a { color: #fff; font-weight: 600; }
.ftdesc a:hover, .cred a:hover, .ftcopy a:hover, .ftdisc a:hover { text-decoration: underline; }
.ftcredits { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; }
.cred { font-size: 12px; line-height: 1.55; color: rgba(255,255,255,.82); }

/* Anthropic attribution — same lockup as the index footer. The logo is a
   440x49 black PNG, so it is inverted to sit on the dark band. */
.madeby { display: inline-flex; align-items: center; gap: 8px;
          text-decoration: none; width: fit-content; color: rgba(255,255,255,.82); }
.madeby .anthropic-logo { height: 13px; width: auto; display: block;
          filter: brightness(0) invert(1); opacity: .92; }
.madeby:hover { text-decoration: none; }
.madeby:hover .anthropic-logo { opacity: 1; }

/* Keys were a 10px mono run-on string tacked onto the copyright line, and
   omitted Esc. Real <kbd> chips, on their own row, legible. */
.ftkeys { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px;
          margin-top: 24px; padding-top: 16px;
          border-top: 1px solid rgba(255,255,255,.24); }
.ftkeys-l { font-family: var(--mono); font-size: 9.5px; letter-spacing: .14em;
            text-transform: uppercase; color: rgba(255,255,255,.7); margin-right: 2px; }
.ftkeys .k { display: inline-flex; align-items: center; gap: 6px;
             font-size: 11.5px; color: #e9f2fb; }
.ftkeys kbd {
  font-family: var(--mono); font-size: 11px; line-height: 1;
  min-width: 20px; padding: 5px 6px; text-align: center;
  color: #fff; background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.3); border-bottom-width: 2px;
  border-radius: 4px;
}
/* star and brief only do anything on an unlocked device */
.ftkeys .k.op { display: none; }
body.operator .ftkeys .k.op { display: inline-flex; }

.ftparent { margin: 22px 0 0; font-size: 13px; font-weight: 600; color: #fff; }
.ftdisc { margin: 12px 0 0; padding-top: 16px; max-width: 900px;
          font-size: 11.5px; line-height: 1.65; color: rgba(255,255,255,.72);
          border-top: 1px solid rgba(255,255,255,.24); }
.ftcopy { margin: 10px 0 0; font-size: 12px; color: rgba(255,255,255,.82); }
/* the operator's room links get their own line, with room to breathe */
#footOps { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px;
           font-family: var(--mono); font-size: 10px; letter-spacing: .08em; }
#footOps:empty { margin-top: 0; }
#footOps a { color: #fff; font-weight: 600; text-decoration: none; }
#footOps a:hover { text-decoration: underline; }
@media (max-width: 780px) {
  .ftcols { gap: 18px; }
  .ftr { padding: 22px 14px 20px; }
  .ftkeys { gap: 6px 12px; }
}

/* world clocks: their own strip between the blue band and the ticker —
   the index site's pattern (city · time with a dot bullet) */
.clock-strip {
  display: flex; align-items: center; height: 30px;
  padding: 0 max(20px, calc((100% - 1560px) / 2 + 20px));
  background: var(--bg2); border-bottom: 1px solid var(--grid);
  overflow-x: auto; scrollbar-width: none;
}
.clock-strip::-webkit-scrollbar { display: none; }
.clock-strip .clocks { display: flex; gap: 30px; margin: 0 auto; }
.clock-strip .clocks .clock {
  display: flex; align-items: center; gap: 7px; text-align: left; flex: none;
}
.clock-strip .clocks .clock::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand); flex: none;
}
.clock-strip .clocks .clock:nth-child(n+5) { display: flex; }  /* all six show */
.clock-strip .clock .t { color: var(--text); font-size: 12px; }
.clock-strip .clock .micro { letter-spacing: .1em; }
@media (max-width: 780px) { .clock-strip { padding: 0 12px; }
  .clock-strip .clocks { gap: 18px; margin: 0; } }

/* the GEI tape on the blue band: bright chip so the numbers read again */
.topbar .gei-strip, [data-theme="light"] .topbar .gei-strip {
  background: #FFFFFF; border-color: rgba(255,255,255,.5);
}
.topbar .gei-strip .lvl, [data-theme="light"] .topbar .gei-strip .lvl { color: #0A2032; }
.topbar .gei-strip .micro { color: #5B6B7A; }

/* Media CFO masthead — the signature plate from the Media CFO Design
   System: rose-duotone LA aerial + the stacked bracket wordmark
   (deeper-plum register). Deliberately NOT the wire's blues; the AXIOM
   credit stays text-only, never blue. */
.mcfo-mast {
  display: block; background: #1a0b14; border: 1px solid #3d2230;
  border-radius: 9px; overflow: hidden; margin-bottom: 10px;
  text-align: center;
}
.mcfo-plate { display: block; width: 100%; height: auto; }
/* analytics consent banner (rendered by assets/ga.js) — terminal-styled */
.axw-consent {
  position: fixed; left: 16px; bottom: 16px; z-index: 4000; max-width: 356px;
  display: flex; flex-direction: column; gap: 11px;
  background: var(--panel); border: 1px solid var(--grid); border-radius: 11px;
  padding: 14px 15px; box-shadow: 0 12px 36px rgba(0,0,0,.30);
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55; color: var(--text-dim);
}
.axw-consent-btns { display: flex; gap: 8px; justify-content: flex-end; }
.axw-consent button {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em;
  text-transform: uppercase; padding: 7px 15px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--grid); background: transparent; color: var(--text-dim);
  transition: all .12s;
  /* These were 30px tall. The state harness never saw them because TARGETS_JS
     only looked inside nav/header/topbar, and the consent banner is a fixed
     panel outside all of them — so the FIRST control every new reader touches
     was the one control not held to the 44px bar. It matters most on DECLINE:
     the privacy-preserving answer must not be the harder one to hit. */
  min-height: 44px;
}
.axw-consent .axw-consent-yes { background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600; }
.axw-consent .axw-consent-yes:hover { filter: brightness(1.08); }
.axw-consent .axw-consent-no:hover { border-color: var(--text-dim); color: var(--text); }
@media (max-width: 520px) { .axw-consent { left: 10px; right: 10px; bottom: 10px; max-width: none; } }
/* subscribe confirmation — replaces the form in place, cover art stays */
.mcfo-ok {
  margin-top: 10px; padding: 12px 12px; border-radius: 9px;
  background: rgba(62, 199, 143, .1); border: 1px solid rgba(62, 199, 143, .35);
  color: var(--up-l); font-size: 13px; line-height: 1.5; text-align: center;
}
.mcfo-fallback {
  margin-top: 10px; padding: 12px 12px; border-radius: 9px;
  background: var(--panel-2, rgba(125,125,125,.08)); border: 1px solid var(--grid);
  color: var(--text-dim); font-size: 13px; line-height: 1.5; text-align: center;
}
a.mcfo-fbk { display: inline-block; margin-top: 9px; text-decoration: none; }

/* EXPANSION WIDTH — the rail always stays; within the main column the
   expansion uses the space generously (the old 66ch caps read as
   half-width) */
.story-expand .xbody, .story-expand .xnote, .story-expand .rel { max-width: none; }

/* "+N MORE PUBLICATIONS" under the named trades. The number is the point, so
   it carries the weight and the label sits quiet beside it. */
.trd-more {
  display: flex; align-items: center; gap: 11px;
  margin-top: 11px; padding-top: 12px; border-top: 1px solid var(--grid);
  cursor: help;
}
.trd-more-n {
  font-family: var(--mono); font-size: 30px; line-height: 1; font-weight: 600;
  color: var(--brand-l); letter-spacing: -.02em; flex: 0 0 auto;
}
.trd-more-l {
  font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--faint); line-height: 1.5;
}

/* ---- ON THE FEED: the podcast carousel between Calendar and Docket ----
   One vertical rhythm: artwork, then identity, then two labelled sections,
   then the nav. Every gap is a multiple of 6px and the section labels carry
   the spacing, so nothing needs ad-hoc margins to look right. */
/* The rail's panels are scroll-boxes: .panel-b caps at 340px and scrolls,
   which is right for a LIST (the docket, the calendar) and wrong for a card
   meant to be read whole. With full-width artwork the cap was consumed by the
   image alone, and the title, episodes and listen buttons sat below an inner
   scrollbar — present in the DOM, invisible on the page. This panel grows to
   its content instead, and so does THE TRADES for the same reason — both are
   cards to be read whole, not lists to be scrolled.

   Padding matches .panel-b.mcfo exactly, so the artwork sits inset the same
   way the newsletter plate does one panel up the rail rather than bleeding to
   the panel edge. */
#podPanel .panel-b,
#trdPanel .panel-b {
  max-height: none; overflow: visible;
}
#podPanel .panel-b { padding: 11px 14px 13px; }
.pod {
  display: flex; flex-direction: column; gap: 12px;
}
.pod-art {
  display: block; width: 100%; aspect-ratio: 1 / 1; border-radius: 10px;
  object-fit: cover; background: var(--panel); border: 1px solid var(--grid);
}
.pod-id { display: flex; flex-direction: column; gap: 3px; }
/* Every show must occupy exactly the same height or the rail below it jumps
   each time the reader clicks through — and it now rotates on its own, which
   would make the whole column twitch unattended. The artwork is already a
   fixed 1:1, so the variable parts are the wrapping text: each is clamped to a
   line count AND floored at that same count, so short and long alike measure
   the same. */
.pod-title {
  font-weight: 600; font-size: 15px; line-height: 1.25; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: calc(2 * 1.25 * 15px);
}
.pod-pub {
  font-family: var(--mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--faint);
}
.pod-blurb {
  font-size: 12px; line-height: 1.45; color: var(--dim); margin: 0;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; min-height: calc(3 * 1.45 * 12px);
}

.pod-lab {
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--brand-l);
  display: block; margin-bottom: 7px;
}
.pod-sec { border-top: 1px solid var(--grid); padding-top: 11px; }

.pod-ep {
  display: flex; gap: 10px; align-items: flex-start; padding: 7px 0;
  color: var(--ink); text-decoration: none; line-height: 1.35;
}
.pod-ep + .pod-ep { border-top: 1px solid var(--grid); }
.pod-ep:hover { color: var(--brand-l); }
/* feeds that publish no per-episode link: same row, no hover affordance */
.pod-ep.nolink { cursor: default; color: var(--dim); }
.pod-ep.nolink:hover { color: var(--dim); }
.pod-ep.nolink:hover .n { color: var(--grid); }
.pod-ep .n {
  font-family: var(--mono); font-size: 19px; line-height: 1; font-weight: 600;
  color: var(--grid); flex: 0 0 auto; min-width: 15px;
  transition: color .12s;
}
.pod-ep:hover .n { color: var(--brand-l); }
.pod-ep .t {
  font-size: 12px; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: calc(2 * 1.35 * 12px);
}

/* two rows of platform buttons, whether the show has two links or six */
.pod-listen { display: flex; flex-wrap: wrap; gap: 6px; min-height: 52px; align-content: flex-start; }
.pod-listen a {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 9px; border: 1px solid var(--grid); border-radius: 999px;
  color: var(--dim); text-decoration: none;
  font-family: var(--mono); font-size: 9px; letter-spacing: .08em;
  text-transform: uppercase; line-height: 1;
  transition: color .12s, border-color .12s, background .12s;
}
.pod-listen a:hover {
  color: var(--ink); border-color: var(--brand-l);
  background: color-mix(in srgb, var(--brand-l) 12%, transparent);
}
.pod-listen svg { width: 12px; height: 12px; flex: 0 0 12px; }

.pod-nav {
  display: flex; align-items: center; justify-content: space-between;
  border-top: 1px solid var(--grid); padding-top: 11px;
}
/* The arrows were grey-on-grey and read as decoration. They are the only way
   to steer the carousel by hand, so they carry the brand colour outright
   rather than only on hover. */
.pod-nav button {
  background: rgba(47,111,174,.12); border: 1px solid var(--brand-l);
  border-radius: 7px; color: var(--brand-l); cursor: pointer;
  width: 38px; height: 28px; font-size: 14px; line-height: 1;
  transition: background .12s, color .12s, border-color .12s;
}
.pod-nav button:hover { background: var(--brand-l); color: #fff; }
.pod-nav button:focus-visible { outline: 2px solid var(--brand-l); outline-offset: 2px; }
.pod-dots {
  font-family: var(--mono); font-size: 9px; letter-spacing: .12em;
  color: var(--faint);
}

/* Countdown to the next show. Zero height until the timer is actually
   running, so a panel that is holding still does not imply it is about to
   move — the bar appearing IS the signal. */
.pod-timer {
  height: 0; margin-top: 0; overflow: hidden; border-radius: 3px;
  background: var(--grid); transition: height .18s, margin-top .18s;
}
/* 4px with a visible track: this is the only thing on the panel that says a
   change is coming, so it has to read as a gauge rather than a hairline. */
.pod-timer.on { height: 4px; margin-top: 10px; }
.pod-timer span {
  display: block; height: 100%; width: 0; border-radius: 3px;
  background: linear-gradient(90deg, var(--brand-d), var(--brand-l));
}
@keyframes podfill { from { width: 0; } to { width: 100%; } }
@media (prefers-reduced-motion: reduce) {
  /* honour the preference: no sweeping bar, but the panel still turns, so
     show a static rule rather than nothing at all */
  .pod-timer span { animation: none !important; width: 100%; opacity: .45; }
}

/* teach-everywhere controls (operator): unlink / hide / correct */
.relwrap { position: relative; display: block; }
.relwrap .rel { display: block; }
.fixx {
  position: absolute; top: 6px; right: 6px; width: 20px; height: 20px;
  border: 1px solid var(--grid); border-radius: 6px; background: var(--panel);
  color: var(--faint); font-size: 10px; line-height: 1; cursor: pointer;
  /* Resting opacity, NOT 0. At 0 these were undiscoverable: the only way to
     learn the ▲ existed was to already know, and on a touch screen :hover
     never fires at all so it could not be revealed by any gesture. */
  opacity: .4; transition: opacity .12s, color .12s, border-color .12s;
}
/* An outlet row carries TWO of these — ▲ promote and ✕ unlink — and both were
   pinned to right:6px, so they sat exactly on top of each other. The ✕ is
   later in the DOM, so it painted over the ▲ and took the click: pressing
   where the ▲ appeared to be UNLINKED the story instead of promoting it, the
   precise opposite of the intent. Give the ▲ its own lane. */
.relwrap .fixx[data-lead] { right: 32px; }
.relwrap:hover .fixx, .xnote:hover .fixx { opacity: 1; }
.fixx:hover { color: var(--down); border-color: var(--down); }
.fixx[data-lead]:hover { color: var(--up); border-color: var(--up); }
@media (hover: none) {
  .fixx { opacity: .85; }          /* no hover to reveal them with */
}
.xnote { position: relative; }
.xnote .fixx { top: 8px; right: 8px; }
select.fixcat { appearance: auto; -webkit-appearance: menulist; max-width: 180px; }

/* the index boxes link home */
a.gei-strip { cursor: pointer; }
a.gei-strip:hover { border-color: rgba(255,255,255,.6); }
#idxPanel .panel-h a:hover { color: var(--brand) !important; }

/* ============================================================
   CHROME LAYER (2026-08-08) — masthead · world-clock rail · tape.
   Measured faults this fixes at 390px: the search placeholder was
   guillotined to "SEARCH THE", the clock rail ran off the viewport
   with no way to tell it scrolled, and the tape was cut mid-word at
   both ends. Measured faults at 1440px: ~245px of dead air between
   the index tape and the search field, no focus ring on any control
   in the bar, and two text/background pairs under 4.5:1.
   Appended last on purpose — this layer owns the chrome and must
   win over the four earlier passes that all touched it.
   ============================================================ */

/* ---- the banner as one unit -------------------------------------------- */
/* Masthead, clock rail and tape are one <header> now: 122px at 1440, 124px at
   390, sharing one gutter and one set of rules.
   NOT sticky, and deliberately so. .topbar has carried position:sticky since
   the first pass and has never once pinned — `html, body { overflow-x: hidden }`
   (added to stop the off-canvas drawer creating sideways scroll) makes BODY a
   scroll container, and a sticky child pins inside its scroll container, which
   in this case never scrolls; the viewport does. Measured: header.top = -900 at
   scrollY 900. Pinning it for real would cost 124px of a 844px phone AND cover
   the top of whatever is scrolled to underneath, so the declaration goes rather
   than the bug being "fixed" into a band that eats a seventh of the screen.
   If a later round does want it pinned, the lever is body's overflow-x, not
   another z-index. */
header.chrome { position: relative; z-index: 100; background: var(--bg2); }
.chrome .topbar { position: static; }

/* ---- masthead --------------------------------------------------------- */
.chrome .topbar { gap: 16px; flex-wrap: nowrap; }
.chrome .wordmark { flex: 0 0 auto; }
/* rgba(255,255,255,.78) on #1D6BB4 measured 4.06:1 — under AA for 13.5px
   text, and this is half the masthead. .92 takes it to 4.99:1. */
.chrome .wordmark .gme { color: rgba(255,255,255,.92); }
.chrome .wm-sec { color: rgba(255,255,255,.82); margin-left: 12px; }

/* the tools cluster travels together, so the bar has two anchors instead of
   four free-floating items */
.chrome .topbar-tools {
  display: flex; align-items: center; gap: 12px;
  margin-left: auto; flex: 0 0 auto; min-width: 0;
}
.chrome .topbar-tools .searchbox { margin-left: 0; }
.chrome .axmlink {
  display: flex; align-items: center; padding-left: 14px;
  border-left: 1px solid rgba(255,255,255,.26);
}

/* the index tape now takes the slack instead of leaving a hole in the middle
   of the bar: the figures stay left, the spark absorbs the rest */
.chrome .topbar .gei-strip {
  flex: 1 1 auto; min-width: 0; max-width: 660px; min-height: 36px;
  gap: 12px; overflow: hidden;
}
.chrome .topbar .gei-strip svg {
  flex: 1 1 90px; min-width: 80px; max-width: none; width: auto; height: 24px;
}
/* The chip is forced white in BOTH themes, so it kept the dark theme's
   --up-l (#3ec78f): 2.13:1 on white. These two are the paper-safe pair,
   5.04:1 and 5.94:1, and they only apply to the chip in the bar — the rail's
   index panel keeps the palette's own greens. */
.chrome .topbar .gei-strip .up   { color: #157F55; }
.chrome .topbar .gei-strip .down { color: #B3392C; }
/* The level and both changes reprint on every intraday refresh. Proportional
   figures re-flowed the whole chip — and the sparkline with it — every time a
   digit changed width. */
.chrome .topbar .gei-strip .lvl,
.chrome .topbar .gei-strip .chg { font-variant-numeric: tabular-nums; }
/* "1D" and "YTD" qualify a change, they are not one. Painted in the same
   green as the figure beside them they read as data, and the eye had no way
   to tell the session's move from the year's. Grey at 5.28:1 on the white
   chip: still legible, no longer a number. */
.chrome .topbar .gei-strip .pd {
  font-style: normal; color: #5B6B7A; letter-spacing: .04em;
}

/* ---- search ----------------------------------------------------------- */
/* The field sat on rgba(255,255,255,.12) over the blue band, which put the
   placeholder at 2.23:1. Deepening the well instead of lightening the text
   gets white to 7.7:1 and the placeholder to 5.6:1, and it reads as an input
   rather than a ghost. The focus-grow is gone: 240→300px shoved the AXIOM
   lockup sideways every time the field was clicked. */
/* WCAG 1.4.11: the border IS the field — it is the only thing that says a
   control is there. rgba(255,255,255,.34) over the well measured 2.33:1
   against the band, under the 3:1 a UI boundary needs. .52 measures 3.35:1
   on the field and 3.31:1 on the buttons, and the bar now reads as three
   controls rather than three suggestions. */
.chrome .searchbox input,
.chrome .topbar .searchbox input {
  width: 300px; height: 36px; padding: 0 12px 0 32px;
  background: rgba(8,27,43,.30); border-color: rgba(255,255,255,.52);
  color: #fff; transition: border-color .15s, box-shadow .15s;
}
.chrome .searchbox input:focus { width: 300px; border-color: #fff; }
.chrome .topbar .searchbox input::placeholder { color: rgba(255,255,255,.80); opacity: 1; }
.chrome .topbar .searchbox .icon { color: rgba(255,255,255,.86); left: 10px; }
.chrome .topbar .searchbox kbd { color: rgba(255,255,255,.82); border-color: rgba(255,255,255,.50); }
.chrome .topbar .btn {
  background: rgba(8,27,43,.26); border-color: rgba(255,255,255,.52); color: #fff;
  flex: 0 0 auto;
}
.chrome .topbar .btn:hover {
  background: rgba(8,27,43,.45); border-color: #fff; color: #fff;
}
.chrome .iconbtn {
  width: 38px; height: 36px; padding: 0; justify-content: center;
  font-size: 14px; flex: 0 0 auto;
}
.chrome .axmlink { flex: 0 0 auto; }

/* ---- focus, everywhere ------------------------------------------------ */
/* The bar had exactly one focus ring (the wordmark) and the field explicitly
   set outline:none, so a keyboard reader crossing the chrome went blind. */
.chrome .topbar :is(a, button, input):focus-visible {
  outline: 2px solid #fff; outline-offset: 2px; border-radius: 8px;
}
.chrome .clock-strip:focus-visible,
.chrome .ticker :is(a, button):focus-visible {
  outline: 2px solid var(--brand); outline-offset: -2px; border-radius: 4px;
}
/* A mask clips the element's OWN outline as readily as its content: with the
   edge fade on, the last 34px of the rail's focus ring — and the ring on any
   headline that happened to be near the end of the tape — faded to nothing.
   Focus lifts the fade. The keyboard reader gets a whole, unbroken ring and,
   as a bonus, the whole tape: on the ticker this pairs with the pause that
   :focus-within already triggers, so a focused headline is legible end to end
   instead of dissolving at the edge it is sitting on. */
.chrome .clock-strip:is(.ovf-s, .ovf-e):focus-visible,
.chrome .ticker:focus-within {
  -webkit-mask-image: none; mask-image: none;
}

/* ---- world-clock rail ------------------------------------------------- */
.chrome .clock-strip {
  height: 30px; padding: 0 max(20px, calc((100% - 1560px) / 2 + 20px));
  background: var(--bg2); border-bottom: 1px solid var(--grid);
  overflow-x: auto; overscroll-behavior-x: contain;
  /* proximity, not mandatory: a drag still lands where the finger left it,
     but it settles on a whole city rather than parking a time half under the
     fade. scroll-padding keeps the snapped clock on the page gutter instead
     of jammed against the bezel. */
  scroll-snap-type: x proximity;
  scroll-padding-left: max(20px, calc((100% - 1560px) / 2 + 20px));
}
/* Eleven cities need 1024px. Wider than that they spread across the full
   gutter-to-gutter width; narrower they scroll, and the fade is what says so
   — set by JS from the actual scroll position, so the rail dims on the side
   that really does have more rail on it and NEVER on the side that doesn't.
   Before this the right edge was dimmed permanently whenever the rail
   overflowed: scroll to the end and the last city stayed greyed while the
   first was guillotined at the left with no fade at all. */
.chrome .clock-strip .clocks {
  flex: 0 0 auto; min-width: 100%; margin: 0; gap: 20px;
  justify-content: space-between; flex-wrap: nowrap;
}
.chrome .clock-strip .clock { scroll-snap-align: start; }
.chrome .clock-strip.ovf-e {
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 34px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 34px), transparent 100%);
}
.chrome .clock-strip.ovf-s {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 100%);
}
.chrome .clock-strip.ovf-s.ovf-e {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 34px, #000 calc(100% - 34px), transparent 100%);
}
.chrome .clock-strip .clock { gap: 7px; }
/* the city code was --faint: 4.07:1 on the light band. --text-dim is 7.4:1
   and still reads quieter than the time beside it. */
.chrome .clock-strip .clock .micro { color: var(--text-dim); font-size: 10.5px; }
/* eleven clocks reprint every 30s — without tabular figures a 1 replacing a
   4 shortens the string and shunts the whole rail sideways */
.chrome .clock-strip .clock .t {
  font-size: 12.5px; color: var(--text); font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.chrome .clock-strip .clock::before { width: 4px; height: 4px; background: var(--brand); }

/* ---- headline tape --------------------------------------------------- */
.chrome .ticker-wrap { height: 36px; }
/* The flag keeps bleeding to the viewport edge — that is the tape's identity —
   but the WORD now starts on the page gutter, so LATEST, the G of GLOBAL and
   the LA bullet all sit on one column. Measured before: the tape band was the
   only one of the three whose content began 20px left of everything above it.
   The percentage resolves against .ticker-wrap, which is the full viewport. */
.chrome .ticker-label {
  flex: none; letter-spacing: .16em;
  padding: 0 14px 0 max(20px, calc((100% - 1560px) / 2 + 20px));
}
/* A tape is cut off at both ends by definition — the fix is that it should
   dissolve like a tape instead of hitting a wall mid-word. 54px of fade was
   a whole word of tape spent on the dissolve; 30px is ~4 characters, which
   still reads as a fade and hands ~24px of legible headline back. */
.chrome .ticker {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 30px, #000 calc(100% - 44px), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 30px, #000 calc(100% - 44px), transparent 100%);
}
.chrome .ticker-track { height: 100%; align-items: stretch; }
.chrome .tick {
  display: flex; align-items: center; padding: 0 24px 0 0;
  font-size: 12.5px;
}
/* WCAG 2.2.2: hover already paused it for a mouse. Keyboard focus and touch
   had no way to stop a moving target. */
.chrome .ticker:focus-within .ticker-track { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .chrome .ticker-track { animation: none; }
  .chrome .ticker { overflow-x: auto; scrollbar-width: none; }
  .chrome .ticker::-webkit-scrollbar { display: none; }
}

/* ---- 390px ----------------------------------------------------------- */
@media (max-width: 780px) {
  /* 54px, not 48: the field and the theme control are 44px tap targets and at
     48 their borders all but touched the edges of the band */
  /* 8px left the field's border 8px from the E of WIRE — measured, the two
     blocks read as one collision. 14px separates the identity from the tools
     and still leaves the field 123px, which carries the short placeholder
     with 35px to spare. */
  .chrome .topbar { height: 54px; padding: 0 12px; gap: 14px; }
  /* 8-9px mono uppercase was decoration, not a wordmark */
  .chrome .wordmark { gap: 7px; }
  .chrome .wordmark .gme { font-size: 9.5px; letter-spacing: .09em; }
  .chrome .wordmark .wire { font-size: 19px; }
  /* 44px tap target — but min-height alone only grew the BOX. The two spans
     are baseline-aligned, and a baseline-aligned flex line parks at the top of
     any spare cross-space, so the glyphs sat at the very top of the 44px box:
     measured optical centre y=18 against the field's and the band's y=27. The
     wordmark rode 9px high in its own masthead. Symmetric padding makes the
     box AND its contents centre on the same line as the tools. */
  .chrome .wm-home { min-height: 44px; padding: 10px 0; }
  .chrome .wm-sec { display: none; }             /* the <h1> below says it */
  .chrome .topbar-tools { flex: 1 1 auto; gap: 6px; }
  /* the field takes what is left of the bar, but a 504px search box on a
     780px tablet is not "flexible", it is unshaped */
  .chrome .searchbox { flex: 1 1 auto; min-width: 0; max-width: 330px; }
  .chrome .searchbox input,
  .chrome .searchbox input:focus,
  .chrome .topbar .searchbox input {
    width: 100%; height: 44px;
    /* belt and braces: if a narrower phone ever squeezes the field below the
       short placeholder, it ellipsises instead of cutting a letter in half */
    text-overflow: ellipsis;
  }
  .chrome .iconbtn { width: 44px; height: 44px; font-size: 15px; }
  .chrome .topbar .btn { min-height: 44px; }

  /* This band is a drag target on a phone: eleven cities are 973px of rail in
     a 390px viewport, so the reader has to move it. 26px was 4px of clearance
     above and below an 18px row — a hairline to catch with a thumb. 34px is a
     band you can actually grab, and it is still the shortest of the three. */
  .chrome .clock-strip { height: 34px; padding: 0 12px; scroll-padding-left: 12px; }
  .chrome .clock-strip .clocks { gap: 16px; padding-right: 22px; }
  .chrome .clock-strip .clock { gap: 6px; }
  .chrome .clock-strip .clock .micro { font-size: 10px; }
  .chrome .clock-strip .clock .t { font-size: 12px; }
  .chrome .clock-strip.ovf-e {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 26px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 26px), transparent 100%);
  }
  .chrome .clock-strip.ovf-s {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 100%);
  }
  .chrome .clock-strip.ovf-s.ovf-e {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
  }

  /* the tape's links are the only 44px-able targets on this band, so the band
     is 44px and each headline fills it */
  .chrome .ticker-wrap { height: 44px; }
  /* 12px left, the phone gutter: LATEST now starts on the same column as the
     G of WIRE's wordmark and the LA bullet, not 3px inboard of both */
  .chrome .ticker-label { display: flex; padding: 0 10px 0 12px; font-size: 9.5px; letter-spacing: .12em; }
  .chrome .tick { padding: 0 20px 0 0; font-size: 12.5px; }
  .chrome .ticker {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 20px, #000 calc(100% - 30px), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 20px, #000 calc(100% - 30px), transparent 100%);
  }
}

/* The AXIOM lockup hides its IMG below 1000px but the anchor stayed in the
   flex row — a 15px ghost (its rule and left padding) plus a 6px gap, stealing
   21px from the search field on every phone. */
@media (max-width: 1000px) { .chrome .axmlink { display: none; } }

/* Between 1260 and ~1290 the tape's own content was 9px wider than the box it
   was given and clipped inside its overflow:hidden. The curve is the one part
   that can lose width without losing a figure. */
@media (max-width: 1400px) {
  .chrome .topbar .gei-strip svg { flex: 1 1 60px; min-width: 56px; }
}

/* small phones: the lockup gives back the 20px the field needs at 360-400px */
@media (max-width: 430px) {
  .chrome .wordmark .gme { font-size: 9px; letter-spacing: .08em; }
  .chrome .wordmark .wire { font-size: 17.5px; }
  .chrome .searchbox input, .chrome .topbar .searchbox input { padding-left: 30px; }
}

/* Chromium reserves the search field's clear button (13px + 6px margin)
   INSIDE the content box even when the field is empty, which is the last 19px
   the placeholder needed: at 360px it printed "SEA…" with 55px of measured
   room for 45px of text. No value, nothing to clear, no reservation. */
.chrome .searchbox input:placeholder-shown::-webkit-search-cancel-button {
  display: none;
}

/* Deepening the search well (above) left the masked ✕ at --text-dim, which on
   the light theme is #35526B on #17558E — 1.04:1, an invisible control. It is
   white on the blue bar in both themes now, like everything else in the well. */
.chrome .searchbox input::-webkit-search-cancel-button {
  background-color: rgba(255,255,255,.82);
}
.chrome .searchbox input::-webkit-search-cancel-button:hover { background-color: #fff; }

/* ============================================================
   FEED LAYER (2026-08-08) — the story river.
   Measured faults at 390px: the average card ran 307px, so 2.7
   stories cleared an 844px screen; the dek printed up to eight
   lines; one row's chip block wrapped to FOUR lines and 191px (15
   chips); the age was display:none below 720px, so the phone lost
   the one figure every row shares; and the score pill took 53px of
   a 366px column away from the headline.
   Measured faults at 1440px: the dek measured 861px — ~115
   characters a line; the age gutter was left-aligned, so "9h" and
   "24h" never lined up; the tracker pill printed in --text and
   outshouted every other chip on the row; and six text/background
   pairs on the instrument line sat under 4.5:1 on paper.
   Appended last on purpose — this layer owns the river and has to
   win over the readability and mobile passes that also touch .story.
   ============================================================ */

/* ---- the row shell -------------------------------------------------- */
.story {
  grid-template-columns: 58px minmax(0, 1fr) auto;
  column-gap: 14px; row-gap: 0;
  padding: 10px 14px 9px;
  border-radius: 8px;
}
/* The card is role="button" and tabindex="0" and had NO focus style at all:
   a keyboard reader tabbing the river could not see where they were. Inset,
   so the ring is not clipped by the row above. */
.story:focus-visible {
  outline: 2px solid var(--brand); outline-offset: -2px; border-radius: 8px;
}
.story :is(a, button, .trchip, .coverage):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 5px;
}

/* ---- the age gutter, as a real column ------------------------------- */
/* "AGO" is a constant seven-character mono string, so right-aligning the
   whole label lines the UNIT up and lets the digits grow leftwards — 9h, 24h
   and 14d share one edge instead of three. 58px carries the longest form
   ("59m AGO" = 48px) and hands 16px back to the headline. */
.story .when { padding-top: 3px; text-align: right; }
.story .when .ago {
  font-size: 11.5px; letter-spacing: .01em;
  font-variant-numeric: tabular-nums lining-nums;
}
.story .when .ago b { font-weight: 700; color: var(--text); }
.story .when .new { margin-top: 5px; }

/* ---- headline over dek: widen the gap between them ------------------ */
.story h3 {
  font-size: 17px; line-height: 1.3; letter-spacing: -.016em;
  overflow-wrap: break-word;
}
/* Two faults in one rule. The dek ran the full 861px column — ~115
   characters a line, roughly double a comfortable measure — and it ran to
   whatever length the feed sent, which is what made a phone card 307px tall.
   Capped to 78ch and clamped, it is a two-line standfirst under the
   headline instead of a second paragraph competing with it.
   NOTHING is lost: clamping is presentational and the card is already an
   expander — opening it prints the dek in full (rule below). */
.story .sum {
  margin-top: 5px; font-size: 13px; line-height: 1.5;
  overflow-wrap: break-word;
  /* Three lines, the house rule since 18 Jul. The pass had cut this to two
     and capped the measure at 78ch; both are gone — the dek runs the full
     width of the row and gets its third line back. Still clamped rather than
     unbounded: the expander prints it whole. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
.story.expanded .sum { display: block; -webkit-line-clamp: none; overflow: visible; }

/* ---- the instrument line ------------------------------------------- */
/* One line of instrumentation, not a scatter of pills: every chip is the
   same 19px tall on the same baseline, the radii agree, and the groups are
   separated by a hairline instead of by each pill carrying its own outline.
   Provenance · figures · storyline · tags, in DOM order. */
.story .meta {
  margin-top: 8px; gap: 6px; row-gap: 5px; align-items: center;
  min-height: 19px;
}
.story .meta > * { flex: 0 0 auto; }
.story .meta .msep {
  width: 1px; height: 12px; background: var(--grid); margin: 0 2px;
  border-radius: 0;
}
.story .meta :is(.srcchip, .catchip, .figchip, .langchip, .movechip,
                 .trchip, .sigchip, .tagchip, .coverage) {
  display: inline-flex; align-items: center; height: 19px;
  font-size: 10.5px; line-height: 1; white-space: nowrap;
}
.story .meta :is(.srcchip, .figchip, .langchip, .movechip) {
  padding: 0 7px; border-radius: 4px;
}
.story .meta .srcchip { border-color: color-mix(in srgb, var(--brand) 45%, transparent); }
/* Money in the headline, lifted out to be scanned — so it has to be scannable
   down the column, not just readable in place. */
.story .meta .figchip { font-variant-numeric: tabular-nums lining-nums; }
.story .meta .movechip { font-size: 10.5px; }
.story .meta .pubico { width: 18px; height: 18px; }

/* The tracker pill declared `border: 1px solid` and no colour, so it
   inherited --text and printed as the blackest, heaviest object on the row —
   louder than the source, the figure and the price put together, for what is
   only a storyline label. The tracker's own hue is already on the element as
   --trk (set in renderFeed, and until now unused by any rule): spend it on a
   6px dot, exactly as the comment there says, and let the label take the
   theme's quiet ink. The dot is decoration beside its own text label, so the
   config palette does not have to clear a text contrast bar to be used. */
.story .meta .trchip {
  gap: 6px; padding: 0 9px; border-radius: 999px;
  color: var(--text-dim); border-color: var(--grid); opacity: 1;
}
.story .meta .trchip::before {
  content: ""; flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%;
  background: var(--trk, var(--brand));
}
.story .meta .trchip:hover { border-color: var(--brand); color: var(--text); }

/* The outlet count is the one pressable thing on the line — it opens the
   list of who else has the story — and it was styled as plain text among
   bordered chips. It now reads as a control, in the brand tone it already had. */
.story .meta .coverage {
  padding: 0 7px; border-radius: 4px;
  border: 1px solid color-mix(in srgb, var(--brand) 38%, transparent);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.story .meta .coverage:hover {
  border-color: var(--brand); background: color-mix(in srgb, var(--brand) 18%, transparent);
}
/* opacity is not a colour: .75 over paper took the hashtags to 4.27:1. */
.story .meta .tagchip { opacity: 1; }

/* ---- paper contrast on the instrument line --------------------------
   Every pair below was measured on #FFF (the row) and on #F2F5F8 (the row
   under hover / expanded). Same hues, walked down in lightness only, so the
   line reads exactly as it did — quieter than the headline, the desk quieter
   than the source — while clearing 4.5:1 at 10.5px.
     --faint     #5F7B92 → 4.45:1  desk name, hashtags, score  → #55708A 5.15/4.70
     --up-l      #178A5D → 4.35:1  green price pill, CATCH-UP  → #157F55 5.01/4.58
     --down      #CF4B3D → 4.47:1  red price pill              → #B3392C 5.96/5.45
     --violet    #B07CE8 → 3.06:1  SIGNAL count                → #7C46C2 5.93/5.42
   The green and the red are the same paper-safe pair the chrome layer
   already settled on for the index tape.

   CONTINUITY: this block used to hard-code #55708A / #157F55 while the desk
   and the rail carried #4E6980 / #12734D for the same two roles — the same
   grey and the same green, a step apart, on two halves of one screen. The
   tokens now hold the darker pair everywhere, so these rules simply point at
   them: the ink is identical whether a figure sits on the feed or the rail,
   and it is 5.34:1 / 5.86:1 on white rather than 4.45 / 4.35. */
[data-theme="light"] .story .meta :is(.catchip, .tagchip),
[data-theme="light"] .story .meta .movechip .mvday,
[data-theme="light"] .story .score-dot .v { color: var(--faint); }
[data-theme="light"] .story .meta :is(.langchip, .movechip.up) { color: var(--up-l); }
[data-theme="light"] .story .meta .movechip.down { color: var(--down); }
[data-theme="light"] .story .meta .sigchip { color: var(--violet); }
/* a previous close keeps its neutral tone — it has not earned the colour */
.story .meta .movechip.prev,
[data-theme="light"] .story .meta .movechip.prev,
[data-theme="light"] .story .meta .movechip.prev.up,
[data-theme="light"] .story .meta .movechip.prev.down { color: var(--text-dim); }

/* ---- the score, right-aligned so the column reads down -------------- */
.story .actions { gap: 7px; }
.story .score-dot { gap: 6px; }
.story .score-dot .v {
  min-width: 2.2ch; text-align: right;
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- 390px ---------------------------------------------------------- */
@media (max-width: 780px) {
  /* The age was thrown away below 720px because a 74px gutter did not fit.
     It does not need a gutter: age and score become one kicker strip above
     the headline, which restores the figure AND hands the body the 53px the
     score column was holding — 313px of text width becomes 342px. */
  .story {
    --rowpad: 12px;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "when act" "body body";
    column-gap: 10px; row-gap: 3px;
    /* 5px, not 10: the instrument line carries its own 10px of internal pad,
       so a symmetric card padding put 20px under the chips against 14px over
       them and the row read as bottom-heavy against the rule below it. */
    padding: 9px var(--rowpad) 5px;
  }
  .story .when {
    grid-area: when; display: flex; align-items: center; gap: 8px;
    padding-top: 0; text-align: left; min-width: 0;
  }
  .story .when .ago { display: inline-block; font-size: 11px; }
  .story .when .new { margin-top: 0; }
  .story .body { grid-area: body; }
  .story .actions { grid-area: act; flex-direction: row; align-items: center; gap: 8px; }
  .story .story-expand { grid-column: 1 / -1; }

  .story h3 { font-size: 16px; line-height: 1.28; }
  /* Two lines at 344px is ~104 characters of standfirst — more than either
     reference prints on a phone, and it is what buys the fold four and a half
     stories instead of two and a half. The rest of the dek is one tap away in
     the expansion, as on the desktop. */
  .story .sum { -webkit-line-clamp: 3; font-size: 13px; margin-top: 4px; }

  /* ONE line, always. A wrapping chip block was the single biggest cost on
     the phone: 15 chips over four lines added 172px to a card and destroyed
     the river's rhythm, so no two rows were the same height. Nothing is
     dropped — the line scrolls, it runs to the card's own edge so the cut
     lands on the page gutter and reads as tape rather than as clipping, and
     the fade is set from the real scroll position by syncMetaOverflow(),
     exactly like the clock rail, so a row whose chips all fit is never
     dimmed and a row scrolled to its end stops advertising a right edge.
     44px tall because the chips in it are controls (see below). */
  .story .meta {
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
    overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; margin-top: 4px; gap: 6px;
    min-height: 44px; padding: 10px 0;
    margin-right: calc(-1 * var(--rowpad)); padding-right: var(--rowpad);
  }
  .story .meta::-webkit-scrollbar { display: none; }
  .story .meta :is(.srcchip, .catchip, .figchip, .langchip, .movechip,
                   .trchip, .sigchip, .tagchip, .coverage) { height: 24px; }
  .story .meta .msep { display: none; }   /* nothing wraps, so nothing to group */
  /* The two chips on the line that DO something — the outlet list and the
     storyline filter — are 24px of ink inside a 44px band, so the band is
     the target: the pad fills the strip exactly, which is also why it is not
     clipped by the scroller's own overflow. */
  .story .meta :is(.coverage, .trchip) { position: relative; }
  .story .meta :is(.coverage, .trchip)::after {
    content: ""; position: absolute; left: 0; right: 0; top: -10px; bottom: -10px;
  }
  .story .meta.ovf-e {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
  }
  .story .meta.ovf-s {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 100%);
  }
  .story .meta.ovf-s.ovf-e {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 30px), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 30px), transparent 100%);
  }
  /* a mask clips the element's own outline as readily as its content — the
     focus ring on a chip near the fade dissolved. Focus lifts the fade. */
  .story .meta:focus-within { -webkit-mask-image: none; mask-image: none; }

  /* operator controls: 30x26, 30x26 and 54x24 were all under the 44px
     minimum. The glyphs keep their size — the HIT AREA grows to 44px, and
     the ▲▼ pair is spaced 10px so two 44px boxes abut instead of overlapping
     and stealing each other's taps. */
  .story .voter { gap: 10px; }
  .story :is(.starbtn, .votebtn) {
    position: relative; min-height: 34px; min-width: 34px;
  }
  .story .votebtn.kill { min-width: 54px; opacity: .8; }
  .story :is(.starbtn, .votebtn)::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: max(100%, 44px); height: 44px; transform: translate(-50%, -50%);
  }
}

/* small phones keep the 16px headline the wider body column now affords */
@media (max-width: 480px) {
  .story { --rowpad: 10px; }
  .story h3 { font-size: 16px; }
}

/* ============================================================
   FEED LAYER · R1 (2026-08-09) — terminal grade on the story river.
   Measured faults on top of the layer above:
     · 1440 — the three columns of a row landed on three different
       baselines (age 26.6, headline 27.1, score 22.0), so the score
       floated above the line it belongs to; the row is role="button"
       and only the headline text itself lit on hover, so a pointer
       anywhere else in the row got a wash and no promise; and the
       instrument line carried three ink heights (19px chips, an 18px
       publication logo, a 17px outlet strip) while the outlet strip
       sat a full 6px gap away from the count it illustrates, reading
       as a stray glyph rather than as that control's own evidence.
     · 390 — the row averaged 177.7px, so 4.7 stories cleared an 844px
       fold; and the provenance group alone measured 389px on a 354px
       line (logo 18 + source 64 + desk 75 + "+2 MORE OUTLETS" 110 +
       five outlet logos 102 + gaps), which means the deal figure and
       the price pill — the two things a reader scans a wire FOR —
       were off the right edge of every single row before a swipe.
   Appended last: this layer has to win over the feed layer above it.
   ============================================================ */

/* ---- the instrument line: one ink height, one group ------------------ */
/* Every object on the line is now the same 19px tall on the same centre,
   so it reads as a rule of instruments rather than as a scatter. */
.story .meta .pubico { width: 19px; height: 19px; border-radius: 5px; }
.story .meta .pubico img { width: 14px; height: 14px; }

/* The outlet count and the logo strip are ONE control — the count opens
   the list the logos preview. Stacked with a 5px overlap behind a ring of
   the row's own paper, they read as the count's evidence instead of as
   loose glyphs, and they hand 27px of a 354px phone line back to the
   numbers. All five stay: nothing is dropped, they are shingled. */
/* -4px and a 1px ring, not more: a favicon in a 19px well has 2.5px of
   padding round it, so the shingle eats one and a half pixels of the mark
   and no more. At -5px with a 2px ring it took a quarter of every logo and
   five outlets read as a smear. */
.story .meta .alsoicons { gap: 0; margin-left: -1px; }
.story .meta .alsoicons .pubico {
  width: 19px; height: 19px; margin-left: -4px;
  box-shadow: 0 0 0 1px var(--bg);
}
.story .meta .alsoicons .pubico img { width: 14px; height: 14px; }
/* the ring has to follow the row it sits on, or a hovered row prints five
   white notches down the middle of the panel wash */
.story:hover .meta .alsoicons .pubico,
.story.sel .meta .alsoicons .pubico,
.story.expanded .meta .alsoicons .pubico { box-shadow: 0 0 0 2px var(--panel); }

/* Numbers that tick or that a reader compares down the column: the price
   pill moves with the session, the figure is compared story to story, the
   outlet count changes as a cluster grows. */
.story .meta :is(.movechip, .figchip, .coverage, .sigchip) {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- the river rule -------------------------------------------------- */
/* --grid-soft is #E9EEF3 on paper — 1.09:1, which is not a rule, it is a
   rumour. The eye has to track 1,016px from a headline to its score and
   had nothing to run along. Same token family, one step firmer. */
.story { border-bottom-color: var(--grid); }

@media (min-width: 781px) {
  /* ---- the three columns land on ONE baseline ------------------------ */
  /* Age, headline and score were each top-aligned to a different box, so
     their baselines fell at 26.8, 28.0 and 22.0 — a row that is supposed
     to read straight across in three columns instead stepped down twice.
     Both gutters are now packed to the headline's baseline. */
  .story .when { padding-top: 4px; }
  .story .actions { padding-top: 5px; }

  /* ---- the row is a button, so hovering it says so ------------------- */
  .story:hover h3 .tlink,
  .story:focus-visible h3 .tlink { color: var(--brand-l); }

  /* ---- rhythm: the dek is a standfirst, not a second paragraph ------- */
  /* Leading only — the 78ch measure and the two-line clamp are untouched,
     so not one word fewer is printed. */
  .story .sum { line-height: 1.45; margin-top: 4px; }
  .story .meta { margin-top: 7px; }
}

/* ---- 390px ----------------------------------------------------------- */
@media (max-width: 780px) {
  /* The vertical budget, measured on the row above: 10px over the age,
     4px + the band's own 10px over the chips, then 10px + 5px of card
     padding under them. The 44px band STAYS 44px — the two chips in it
     are controls and 44px is the target — but it is pulled into the card
     padding it was doubling with, so the hit rectangles are unchanged to
     the pixel and the row is ~23px shorter. The band's bottom edge now
     lands exactly on the card's, never inside the next story. */
  .story { padding: 8px var(--rowpad) 10px; row-gap: 2px; }
  .story .actions { padding-top: 0; }
  .story .when .ago { font-size: 11px; line-height: 1.1; }
  .story h3 { line-height: 1.24; }
  .story .sum { line-height: 1.45; margin-top: 3px; }
  .story .meta {
    margin-top: -4px; margin-bottom: -10px;
    padding-top: 10px; padding-bottom: 10px;
  }

  /* ---- and made to FIT --------------------------------------------- */
  /* 389px of provenance on a 354px line meant the figure and the price
     were never on screen. A point of chip padding, a pixel of gap and the
     shingled logos bring the group under the fold of the line, so the
     numeric cluster is visible without a swipe — and the line still
     scrolls, with the same fade, for the rows that run longer. */
  .story .meta { gap: 5px; }
  .story .meta :is(.srcchip, .figchip, .langchip, .movechip, .coverage) {
    padding: 0 6px;
  }
  .story .meta .trchip { padding: 0 8px; gap: 5px; }
  .story .meta .catchip { letter-spacing: .05em; }
  .story .meta .alsoicons .pubico { margin-left: -6px; }
}

/* ============================================================
   FEED LAYER · R2 (2026-08-09) — the story river, resolved.
   Measured faults left on the layer above:
     · 390 — the instrument line was a private horizontal scroller
       on every one of sixty rows, and it was ALWAYS overflowing:
       202 chips sat off the right edge of the viewport at rest,
       median line 499px on a 344px column. The provenance group
       alone was 231px of it (logo 19 + source 103 + desk 123), so
       the deal figure and the price pill — the two things a reader
       scans a wire FOR — were off-screen on nearly every row, and
       the only way to them was a drag no reader knows is there.
       The scroller also bled 10px past its own card with a negative
       margin, which made .body itself overflow (354 into 344) on
       all sixty rows.
     · 1440 — the headline ran the full 872px column while the dek
       stopped at 640px, so a row that is meant to read as one block
       had two different right edges, 232px apart. The borrowed-
       excerpt credit ("VIA NYT MEDIA") was inline-styled to --faint:
       4.44:1 on the row's own paper, the last AA failure in the
       river and the one the walk-down could not reach.
   Appended last: this layer has to win over both feed layers above.
   ============================================================ */

/* ---- the borrowed-excerpt credit ------------------------------------ */
/* Same hue, walked down in lightness only, exactly as the instrument
   line's own pairs were: #5F7B92 → #55708A, 4.44:1 → 5.15:1 on the row
   and 4.70:1 on the row under hover. */
.story .sum .viasrc { color: var(--faint); }
[data-theme="light"] .story .sum .viasrc { color: #55708A; }

/* ---- the dateline group --------------------------------------------- */
/* One flex item on the instrument line, so it moves as a unit and the
   6px rhythm of the line is unchanged where it stands. */
.story .meta .prov { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }

/* The outlet count is an inline-flex box, so the word beside the number is a
   flex item and the space in the markup between them is collapsed away —
   "+2MORE OUTLETS". The gap belongs to the layout, not to the string. */
.story .meta .coverage .cvw { margin-left: .34em; }

/* An instrument line with nothing left on it still reserved the 44px its
   chips would have needed. */
.story .meta.bare { display: none; }

@media (min-width: 781px) {
  /* ---- one measure, one right edge — REVERTED 10 Aug ---------------
     This capped the headline and dek at 41rem so they shared a right edge;
     before the pass the headline ran the full column and the dek stopped at
     78ch, 232px apart. The shared edge is better typography and the wrong
     call for this product: on a terminal the column is the box, and the
     operator reads the resulting white space as the row not using the space
     it was given. A ragged right on two lines of type costs less than a
     third of every row's width. Both now run the full column again, and the
     dek carries three lines instead of two. */

  /* ---- rhythm ------------------------------------------------------- */
  /* Leading and padding only — nothing is clamped, cut or hidden. The
     row is a four-step block (age/headline/standfirst/instruments) and
     the steps between them were all but equal, so nothing grouped. */
  .story { padding: 9px 14px 8px; }
  .story h3 { line-height: 1.26; }
  .story .sum { line-height: 1.42; margin-top: 4px; }
  .story .meta { margin-top: 6px; }
  /* both gutters repacked to the headline's new first baseline */
  .story .when { padding-top: 3px; }
  .story .actions { padding-top: 4px; }
}

/* ---- 390 ------------------------------------------------------------- */
@media (max-width: 780px) {
  /* ---- the kicker becomes a dateline -------------------------------- */
  /* It was carrying an age and 250px of nothing. It now opens the row the
     way a wire opens a story: when, who, which desk. Wraps rather than
     clips on the eight rows whose outlet name is long enough to need it. */
  .story .when { flex-wrap: wrap; gap: 3px 6px; }
  /* wraps INSIDE the group too: "MUSIC BUSINESS WORLDWIDE" plus a desk name
     is 322px on a 306px kicker, and a dateline that clips its own desk is
     the fault this layer exists to remove. It breaks, it does not truncate. */
  .story .when .prov {
    display: inline-flex; align-items: center; flex-wrap: wrap;
    gap: 3px 5px; min-width: 0;
  }
  /* 16px of ink, not 19: the dateline is a caption over the headline, and at
     19px it stood taller than the age it sits beside and added 6px to every
     row in the river for the sake of two chips. */
  .story .when .prov :is(.srcchip, .catchip) {
    display: inline-flex; align-items: center; height: 16px;
    font-size: 10px; line-height: 1; white-space: nowrap; letter-spacing: .02em;
  }
  .story .when .prov .srcchip {
    padding: 0 5px; border-radius: 4px;
    border-color: color-mix(in srgb, var(--brand) 45%, transparent);
  }
  .story .when .prov .pubico { width: 16px; height: 16px; border-radius: 4px; }
  .story .when .prov .pubico img { width: 12px; height: 12px; }

  /* ---- the instrument line stops being a scroller -------------------- */
  /* Sixty private horizontal scrollers is not a tape, it is sixty places
     to lose a figure. The line wraps now: nothing is off-screen, nothing
     is clipped mid-glyph, and .body no longer overflows its own card
     because the 10px bleed the scroller needed is gone with it.
     row-gap is 20px and not 6px on purpose — see the hit rectangles
     below: it is exactly the room two 44px targets need to stack without
     stealing each other's taps. Two rows in three never wrap at all and
     pay nothing for it. */
  .story .meta {
    flex-wrap: wrap; overflow: visible;
    column-gap: 3px; row-gap: 22px;
    margin-right: 0; padding-right: 0;
    overscroll-behavior-x: auto;
    -webkit-mask-image: none; mask-image: none;
  }
  .story .meta.ovf-s, .story .meta.ovf-e {
    -webkit-mask-image: none; mask-image: none;
  }

  /* ---- and made narrower, so most rows never wrap -------------------- */
  /* "+2 MORE OUTLETS" is 109px; the five shingled logos beside it already
     say outlets. The count and the logos stay, the word folds into the
     chip's aria-label (renderFeed), so the line loses 83px and no reader
     — including one reading by ear — loses a syllable. */
  .story .meta .cvw {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  /* 22px of ink at 10px, matching the dateline above and the base chip sizes
     the desktop river already uses — five per cent of line width back, and
     two pixels off every line that does wrap. */
  .story .meta :is(.srcchip, .catchip, .figchip, .langchip, .movechip,
                   .trchip, .sigchip, .tagchip, .coverage) {
    font-size: 10px; height: 22px;
  }
  .story .meta .movechip { font-size: 10px; }
  .story .meta :is(.srcchip, .figchip, .langchip, .movechip, .coverage) { padding: 0 5px; }
  .story .meta .trchip { padding: 0 7px; gap: 4px; }
  .story .meta .trchip::before { width: 5px; height: 5px; }
  /* the shingle starts AFTER the count, not on top of it: at -8px on every
     logo the first one sat over the "+2" chip's own border and the control
     read as damaged rather than as stacked. The overlap between logos is
     unchanged, so all five still fit. */
  .story .meta .alsoicons { margin-left: 1px; }
  .story .meta .alsoicons .pubico { margin-left: 0; }
  .story .meta .alsoicons .pubico + .pubico { margin-left: -8px; }

  /* the two controls on the line keep their full 44x44 — and 22px of ink in a
     22px row-gap is exactly the room two of them need to stack without
     stealing each other's taps. */
  .story .meta :is(.coverage, .trchip)::after { top: -11px; bottom: -11px; }

  /* ---- rhythm -------------------------------------------------------- */
  /* 16px headlines on a 344px column run to three lines; the leading was
     set for two. Nothing is clamped further — the dek keeps its two lines
     and the headline keeps every word. */
  .story { padding: 7px var(--rowpad) 9px; row-gap: 2px; }
  /* Leading and tracking, not size: the headline stays 16px and keeps every
     word. Inter is drawn loose for text and wants to be tightened at display
     sizes — at -.024em three headlines in sixty come back onto two lines. */
  .story h3 { line-height: 1.19; letter-spacing: -.024em; }
  /* the standfirst steps down half a point so the gap between it and the
     headline reads as a rank rather than as two sizes of the same thing */
  .story .sum { font-size: 12.5px; line-height: 1.38; margin-top: 2px; }
}

/* the desk name keeps its paper-safe ink wherever the dateline stands —
   the walk-down above was scoped to .meta and the group now also lives in
   the kicker (#5F7B92 4.45:1 → #55708A 5.15:1 on the row, 4.70:1 hovered) */
[data-theme="light"] .story .when .prov .catchip { color: #55708A; }

/* ============================================================
   GEI TAPE (2026-08-09) — the index strip as a tearsheet.

   renderGeiStrip() has been emitting labelled fields — .gf cells of
   .gk (column head) over .gv (figure), hairline .gsep between them —
   and this stylesheet had not one rule for any of them. Measured at
   1440px before this block: the whole tape printed as a single run of
   unstyled inline spans, "AXIOM GEI90 NAMES  LAST3,575.60  1D+71.14
   +2.03%", every label welded to the figure it heads, and the last two
   columns (the curve and AS OF) clipped away entirely inside the chip's
   overflow:hidden. So the two facts the JS added specifically so a
   reader could trust the tape — the benchmark and the date — were still
   invisible, and the level, the session and the year still read as one
   continuous number.

   The reference is a market-movers table: a head over every column,
   right-aligned tabular figures, name-over-ticker in the identity cell,
   absolute paired with per cent, a curve column and a dated footnote.
   All seven already exist in the markup. This gives them columns.

   Inks are hard-coded rather than tokenised because this chip is forced
   WHITE in BOTH themes (line ~1607) — one background, one set of pairs:
   #14558E 7.7:1 · #0A2032 16.4:1 · #5B6B7A 5.5:1 · #157F55 5.0:1 ·
   #B3392C 5.9:1, all on #FFFFFF.
   ============================================================ */

.chrome .topbar .gei-strip {
  display: flex; align-items: stretch; gap: 0;
  padding: 0 3px; min-height: 42px;
  border-radius: 9px; overflow: hidden;
  /* the cells set their own leading; the tape must not inherit the bar's */
  line-height: 1.1;
}

/* one column: head over figure, figures right-aligned so +71.14, +7.26%
   and +13.85% hang on the same edge the way a tearsheet's Change column
   does */
.chrome .topbar .gei-strip .gf {
  display: flex; flex-direction: column; justify-content: center;
  align-items: flex-end; gap: 3px;
  flex: 0 0 auto; padding: 5px 10px; min-width: 0;
}
.chrome .topbar .gei-strip .gk {
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: .11em; color: #5B6B7A; white-space: nowrap;
}
.chrome .topbar .gei-strip .gv {
  font-family: var(--mono); font-size: 14px; font-weight: 600;
  color: #0A2032; white-space: nowrap;
  font-variant-numeric: tabular-nums lining-nums;
}
/* .up/.down at line ~1905 are 0,4,0 and so is the rule above; these are
   0,5,0 so the session, the year and the benchmark keep their direction */
.chrome .topbar .gei-strip .gv.up   { color: #157F55; }
.chrome .topbar .gei-strip .gv.down { color: #B3392C; }

/* the identity cell inverts the pair — the mark is the loud line and the
   constituent count is the quiet one, exactly as the reference sets a
   company name over its ticker */
.chrome .topbar .gei-strip .gid { align-items: flex-start; padding-left: 11px; }
.chrome .topbar .gei-strip .gid .gk {
  font-size: 12px; font-weight: 700; letter-spacing: .05em; color: #14558E;
}
.chrome .topbar .gei-strip .gid .gv.gsub {
  font-size: 9.5px; font-weight: 500; letter-spacing: .1em; color: #5B6B7A;
}
/* the whole chip is one link to the index; the mark is where that reads */
.chrome .topbar a.gei-strip:hover .gid .gk { text-decoration: underline; }

/* LAST is the headline figure of the tape and outranks the changes */
.chrome .topbar .gei-strip .gv.glvl { font-size: 16px; font-weight: 700; }

/* points and per cent share the session cell. 7px of air and a weight step
   is what stops "+71.14+2.03%" reading as one eleven-digit number. */
.chrome .topbar .gei-strip .gpts { font-weight: 700; margin-right: 7px; }

/* The S&P is the yardstick, not the index. A 2%-blue wash groups it away
   from our own three figures without dropping it a rank — every pair in it
   still clears AA on the tint (#157F55 4.68:1 · #B3392C 5.58:1 ·
   #5B6B7A 5.14:1).
   
   The wash has to be a COLUMN, not a chip. The strip carries
   `padding: 5px 3px 6px`, so a background painted on the cell's own box
   stopped 6px short of the top and 7px short of the bottom — aligned to
   neither the strip nor the 27px dividers beside it, and floating between
   the two. Being the only tinted thing in the tape, it then read as a
   selected or hovered state rather than a label. The negative margins pull
   it out to the strip's padding edge and the padding puts the figures back
   where they were: same type, same baseline, full-height band. */
.chrome .topbar .gei-strip .gbench {
  position: relative;
  /* No background here and no margin games. The first fix painted the wash on
     the cell box and pulled it out with negative margins tuned to the strip's
     `padding: 5px 3px 6px` — which is 54px tall at 1024 and 43px at 1440, so
     the numbers were right at one width and overshot at another, and the
     figures drifted off their neighbours' baseline with them.
     A stretched pseudo-element needs to know none of that: it overshoots in
     both directions and the strip's own `overflow: hidden` shears it to
     whatever height the strip happens to be. The cell keeps its original box,
     so the figures never move. */
}
.chrome .topbar .gei-strip .gbench::before {
  content: ""; position: absolute; left: 0; right: 0; top: -40px; bottom: -40px;
  background: #F3F7FA;
}
/* the figures ride above the wash */
.chrome .topbar .gei-strip .gbench > * { position: relative; }

/* the dated footnote the reference prints under its table, kept on the tape
   because a phone never scrolls to a footer that is not there */
.chrome .topbar .gei-strip .gasof .gv.gstamp {
  font-size: 11px; font-weight: 500; color: #5B6B7A; text-transform: uppercase;
  letter-spacing: .04em;
}

/* the rule between columns */
.chrome .topbar .gei-strip .gsep {
  flex: 0 0 1px; width: 1px; align-self: stretch;
  margin: 7px 0; background: rgba(10,32,50,.18);
}

/* the curve is a column too: it takes the slack the figures leave */
.chrome .topbar .gei-strip svg {
  flex: 1 1 80px; min-width: 56px; width: auto; height: 26px;
  align-self: center; margin: 0 9px;
}

/* ---- the tape takes its own line before it can clip --------------------
   Measured, not guessed: the seven cells plus a 56px curve come to 607px of
   min-content, and the masthead spends 247 on the wordmark, 444 on the tools,
   32 on gaps and 40 on gutters — so the tape stops fitting at 1370px, and the
   breakpoint sits ten pixels clear of it. The old rule hid the tape at 1259,
   i.e. it clipped for 110px of viewport first and then the index simply
   vanished from every laptop and every phone. It wraps instead: same chip,
   same columns, its own full-width row under the wordmark. */
@media (max-width: 1379px) {
  .chrome .topbar { flex-wrap: wrap; height: auto; row-gap: 8px; }
  .chrome .topbar .gei-strip {
    display: flex; order: 2; flex: 1 0 100%; max-width: none;
    padding-top: 5px; padding-bottom: 6px;
  }
  /* full width, so the curve is worth drawing wide */
  .chrome .topbar .gei-strip svg { flex: 1 1 120px; min-width: 80px; }
  /* Wrapping is opt-in for the tape ONLY. The tools cluster carries
     `flex: 0 0 auto`, so on a wrapping line its base size is its content —
     490px of search field and buttons — and at 390 it took a second line of
     its own, pushing the masthead to 197px. A near-zero basis puts it back
     beside the wordmark and lets the field shrink into whatever is left, the
     way it did when the bar could not wrap; flex-end keeps the cluster on the
     right margin at the wide end of this range, where auto margins no longer
     apply because the item can now grow. */
  .chrome .topbar-tools { flex: 1 1 60px; min-width: 0; justify-content: flex-end; }
}

/* ---- 390px -------------------------------------------------------------
   Answering the open question two ways.

   Should it render at all? Yes. The level, the session and the benchmark
   are the densest facts the wire holds, and the only other place they
   exist is the rail's AXIOM GEI panel — which on a phone sits below the
   entire story river, some fifteen screens down. A masthead that carries
   eleven clock faces and no index is not a terminal.

   How, when seven columns need ~600px and the phone has 366? Not by
   scrolling. The reference wraps: at 390 its table keeps all five columns
   and lets a long name run to three lines, so every figure is on screen
   with no gesture and no hidden state. A swipeable tape would have put
   the benchmark and the date behind an edge fade — which is exactly where
   they already were before this round. So the row becomes a two-row grid:
   same seven cells, same order, same right-aligned columns, ruled like a
   table. Nothing is dropped and nothing needs a thumb.

   Four equal tracks, seven cells, DOM order preserved so the read order and
   the visual order still agree — and the session cell stacks its points over
   its per cent, which is the reference's own Change column verbatim. Side by
   side "+71.14 +2.03%" is the widest figure on the tape and needs two tracks,
   which left 64px of dead air beside it; stacked it fits one, and every rule
   in the table lands on a track edge. */
@media (max-width: 780px) {
  .chrome .topbar {
    height: auto; row-gap: 6px; padding-top: 5px; padding-bottom: 6px;
  }
  .chrome .topbar .gei-strip {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    padding: 0; overflow: hidden; min-height: 0;
  }
  /* the hairlines become the grid's own rules, drawn on the cells, so they
     line up across both rows instead of floating between them */
  .chrome .topbar .gei-strip .gsep { display: none; }
  .chrome .topbar .gei-strip .gid    { grid-area: 1 / 1 / 2 / 2; }
  .chrome .topbar .gei-strip .glast  { grid-area: 1 / 2 / 2 / 3; }
  .chrome .topbar .gei-strip .gses   { grid-area: 1 / 3 / 2 / 4; }
  .chrome .topbar .gei-strip .gytd   { grid-area: 1 / 4 / 2 / 5; }
  .chrome .topbar .gei-strip .gbench { grid-area: 2 / 1 / 3 / 2; }
  .chrome .topbar .gei-strip svg     { grid-area: 2 / 2 / 3 / 4; }
  .chrome .topbar .gei-strip .gasof  { grid-area: 2 / 4 / 3 / 5; }

  /* leading is where a two-row table on a phone wins or loses its band:
     at 1.14/2px the tape stood 92px tall, a fifth of the masthead, for the
     same seven facts it now prints in 77 */
  .chrome .topbar .gei-strip { line-height: 1.05; }
  .chrome .topbar .gei-strip .gf {
    padding: 3px 8px; gap: 1px; min-height: 30px;
    border-left: 1px solid rgba(10,32,50,.14);
  }
  /* In the flex tape the wash is a stretched pseudo-element, because that
     strip pads its cells and the band has to reach past it. This grid has
     `padding: 0` and the cell already fills its own row — and worse, an
     overshooting pseudo would climb into row 1 and sit under the figures
     there. So in the grid the cell box IS the band, painted plainly. */
  .chrome .topbar .gei-strip .gbench::before { content: none; }
  .chrome .topbar .gei-strip .gbench { background: #F3F7FA; }
  /* `@media (max-width: 720px) { .gei-strip .spark { display: none } }` has
     been in this file since long before the tape carried labels, and it is
     why the curve column came back empty on the first wrapped build. The
     grid has two tracks for it; it draws. */
  .chrome .topbar .gei-strip svg {
    display: block; flex: none; width: 100%; height: 20px; min-width: 0;
    margin: 0; padding: 0 8px; align-self: center;
    border-left: 1px solid rgba(10,32,50,.14);
  }
  .chrome .topbar .gei-strip :is(.gid, .gbench) { border-left: 0; }
  .chrome .topbar .gei-strip :is(.gbench, .gasof, svg) {
    border-top: 1px solid rgba(10,32,50,.14);
  }
  .chrome .topbar .gei-strip .gv.glvl { font-size: 15px; }
  .chrome .topbar .gei-strip .gv { font-size: 13px; }
  /* the session, stacked: points on top and bolder, per cent under it —
     right-aligned explicitly because the block <b> makes the per cent an
     anonymous block of its own, which would otherwise set flush left */
  .chrome .topbar .gei-strip .gses .gv { text-align: right; line-height: 1.08; }
  .chrome .topbar .gei-strip .gpts { display: block; margin-right: 0; }
}

/* small phones: 364px of grid is 91px a track, and "3,575.60" set at 15px
   mono measures 72px before its gutters. One step down on the two widest
   glyph runs keeps the whole table inside a 360px screen. */
@media (max-width: 430px) {
  .chrome .topbar .gei-strip .gf { padding: 3px 7px; }
  .chrome .topbar .gei-strip .gv.glvl { font-size: 14px; }
  .chrome .topbar .gei-strip .gid .gk { font-size: 11.5px; letter-spacing: .03em; }
}

/* The wrapped tape is the widest focusable thing in the bar; the shared
   masthead ring sits 2px outside it and would be clipped by the header's
   own edge at 390. An inset rule is painted inside the box and cannot be. */
.chrome .topbar a.gei-strip:focus-visible {
  outline: 2px solid #fff; outline-offset: 2px;
  box-shadow: inset 0 0 0 2px #14558E;
}

/* ============================================================
   INDEX FAMILY, CONTINUED — the movers table and the rail panel
   read off the same figures as the tape and should print like it.
   ============================================================ */

/* Zebra, the way the reference stripes its rows: ten tickers is exactly the
   depth at which the eye starts losing which per cent belongs to which name.
   Header is child 1, so the tint starts on the first data row.

   Opaque, and DOWN rather than up on the dark theme. rgba(255,255,255,.035)
   composites to #163244, which drops the declines from 4.74:1 to 4.33:1 —
   a stripe that quietly fails AA is worse than no stripe. Sinking the row
   instead lands on #0C2434: 7.4:1 on the gainers, 5.1:1 on the decliners. */
.mvtable .mv.linked:nth-child(even) { background: #0C2434; }
[data-theme="light"] .mvtable .mv.linked:nth-child(even) { background: #F4F8FB; }
.mvtable .mv.linked { padding-left: 6px; padding-right: 6px; margin: 0 -6px; }

/* Measured on the light theme, where the whole index family lives on paper:
   --up #1f9d6b is 3.50:1 on white, 3.06:1 on the zebra tint and 2.93:1 on
   the hover tint, and --down #cf4b3d is 4.46:1 — four failing pairs carrying
   every figure in the panel. #146B49 clears every one of those backgrounds
   (6.50 / 6.10 / 5.68:1) and #B3392C likewise (5.94 / 5.60 / 5.21:1), so the
   figures hold whether a row is striped, hovered or neither. Text only: the
   magnitude gauge keeps --up/--down, which clear the 3:1 a graphic needs. */
[data-theme="light"] :is(.mvtable, #idxPanel) :is(.num, .v, .lvl).up { color: #146B49; }
[data-theme="light"] :is(.mvtable, #idxPanel) :is(.num, .v, .lvl).down { color: #B3392C; }

/* the rail panel's periods were a bare flex row that spaced 1W, 1M, YTD and
   S&P YTD by their own width, so no two figures started on a column. Equal
   auto-columns give the same cells the tape's alignment — auto-flow rather
   than a fixed four, because m1 and spYtd are both optional in the payload
   and a fixed track count would leave a hole where one is missing. */
.idxrow {
  display: grid; grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr); column-gap: 8px;
}
.idxrow .kv { min-width: 0; }
.idxrow .k { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.idxrow .v { display: block; margin-top: 3px; }
/* the benchmark is ruled off, so the one figure in this panel that is NOT
   the index reads as the yardstick — the job the wash does on the tape */
.idxrow .kv:nth-child(4):last-child {
  padding-left: 8px; border-left: 1px solid var(--grid-soft);
}

/* the curve takes its ink from the theme now (see renderIndexPanel) */
.idxspark { color: var(--up-l); }
.idxspark.down { color: var(--down); }
.idxspark.flat { color: var(--faint); }

/* --faint carries the period heads, the panel's own sub-link and the
   provenance line, and at 11px on #F2F5F8 it measures 4.05:1 — the same
   walk-down the story dateline already took, to 4.72:1.

   CONTINUITY: #55708A was the feed's private value for this role and it left
   AXIOM GEI as the one panel of nine whose sub-line was a different grey to
   the other eight. --faint is that walk-down now, everywhere. */
[data-theme="light"] #idxPanel :is(.panel-h .sub, .idxrow .k, .idxasof span) {
  color: var(--faint);
}

/* ============================================================
   FILTERS LAYER · R1 (2026-08-09) — the chip bar as an
   instrument panel.

   Same seventeen chips, same four selects, same density toggle,
   same palette. What changes is that the bar now has a shape: a
   CHIP GROUP and a CONTROL GROUP, ruled apart, instead of one
   flat run of twenty-two items with a 1px <span class="sep">
   floating somewhere in it. Measured faults this closes, all
   from build/audit.py --sel .pillbar:
     · 10 controls with no focus ring       (0 now)
     · 10 touch targets 27px tall at 390    (44px now)
     · 12 chips parked past the right bezel
       at 390 with nothing saying so        (folded and counted)
     ·  1 count colour at 4.44:1 on paper   (5.10:1)
   ============================================================ */

:root {
  /* The chip outline was --grid: 1.33:1 against the page on paper and 1.35:1
     on the navy. An outlined control whose outline is invisible is not an
     outlined control — WCAG 1.4.11 wants 3:1 for the boundary that identifies
     it. Same hue family, walked until it clears: 3.22:1 on white, 3.24:1 on
     #0A2032. Scoped to the filter bar, so docket/street/signal tabs keep the
     hairline they were drawn with. */
  --pill-line: #52708C;
  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='%23B7C9D8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4l3 3 3-3'/%3E%3C/svg%3E");
}
[data-theme="light"] {
  --pill-line: #7E92A6;
  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='%2335526B' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 4l3 3 3-3'/%3E%3C/svg%3E");
}

.pillbar {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 9px; margin: 2px 0 12px;
}
.pillbar [hidden] { display: none !important; }

/* the chips wrap into TRUE rows: an explicit row-gap, and every chip the same
   height whatever its label, so two wrapped rows read as a grid and not as a
   scatter */
.pillbar .fcats {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 6px; row-gap: 6px; min-width: 0;
}

/* the group boundary is a RULE on the controls, not an inline separator: a
   flex item cannot be orphaned to the end of a wrapped row if it is a border */
.pillbar .fctrls {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 6px; row-gap: 6px; min-width: 0;
  padding-top: 9px; border-top: 1px solid var(--grid-soft);
}

.pillbar .pill {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 32px; padding: 0 13px;
  border: 1px solid var(--pill-line); border-radius: 999px;
  background: transparent; color: var(--text-dim);
  font-family: var(--sans); font-size: 13px; font-weight: 500; line-height: 1;
  white-space: nowrap; cursor: pointer;
  transition: background-color .12s, border-color .12s, color .12s;
}
/* `color:#fff` on hover has been in the base rule since before the paper
   theme existed, so hovering a chip on the default theme printed white text
   on a white page. Both themes now resolve through tokens. */
.pillbar .pill:hover {
  background: var(--panel2); border-color: var(--brand-d); color: var(--text);
}

/* ACTIVE, unmistakable: the filled chip keeps its brand fill and gains a
   doubled edge (a 1px ring outside the 1px border — no layout shift, so the
   row cannot re-wrap when the selection moves) and an opaque count plate. */
.pillbar .pill.active {
  background: var(--brand-d); border-color: var(--brand-d); color: #fff;
  box-shadow: 0 0 0 1px var(--brand-d);
}
.pillbar .pill.active:hover {
  background: #17558F; border-color: #17558F; box-shadow: 0 0 0 1px #17558F;
}

/* counts are figures and align like figures. --faint measured 4.44:1 on
   paper at 12px — the audit's own reading — so the count that tells you how
   big a category is was the one thing in the bar failing AA. #55708A is the
   same walk-down the index panel's period heads already took: 5.10:1. */
.pillbar .pill .n {
  font-family: var(--mono); font-size: 11px; font-weight: 500; line-height: 1;
  font-variant-numeric: tabular-nums; padding: 0; color: var(--faint);
}
[data-theme="light"] .pillbar .pill .n { color: #55708A; }
/* rgba(255,255,255,.75) composites to 3.85:1 on #1D6BB4. Opaque, 4.79:1, and
   still a step down from the white label so the hierarchy survives. */
.pillbar .pill.active .n { color: #E7F0FA; }

/* the fold: phone only, driven by measurement in foldCats() */
.pillbar .pill.folded { display: none; }
/* CONTINUITY: these two are .pill, they sit in the row of .pill, and they were
   the only two members of it set in mono at 11px/.07em — so one filter bar
   printed two typefaces at two sizes, and #densityBtn made a third variant
   inside a single row of chips. They keep their ink (the fold control is
   brand-blue because it does something different); they give up the voice. */
.pillbar .pill-more { color: var(--brand); }
.pillbar .pill-more:hover { color: var(--brand); }
.pillbar .pill-more .chev { font-size: 9px; margin-left: -3px; }

/* the four selects stop being four different widths of browser default and
   become one control type: same height, same radius, same chevron on the
   same inset, so their left edges and their glyphs line up in a row */
.pillbar select {
  appearance: none; -webkit-appearance: none;
  display: inline-flex; align-items: center;
  min-height: 32px; min-width: 0; max-width: 100%;
  padding: 0 30px 0 11px;
  border: 1px solid var(--pill-line); border-radius: 8px;
  background-color: var(--panel);
  background-image: var(--chev);
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 10px 10px;
  color: var(--text);
  font-family: var(--mono); font-size: 12px; font-weight: 500;
  line-height: 1; letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  text-overflow: ellipsis; cursor: pointer;
  transition: border-color .12s;
}
.pillbar select:hover { border-color: var(--brand-d); }
/* a <select> sizes itself to its WIDEST option, so the source list — sixty
   publication names, the longest of them 24 characters — printed a 530px box
   with "ALL SOURCES" adrift at one end of it and the chevron at the other.
   Capped, and the long names ellipsis instead of stretching the row. */
.pillbar #srcSel { max-width: 250px; }

/* one ring for every interactive thing in the bar, drawn OUTSIDE the control
   so it never hides the label it is pointing at. 5.2:1 on paper, 5.6:1 on
   navy — a focus indicator has to clear 3:1 in its own right. */
.pillbar :is(button, select):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}

/* ---- phone ------------------------------------------------------------ */
@media (max-width: 780px) {
  /* undo the swipe row. It fitted seven of seventeen chips on a 390px screen
     and put the other ten past the bezel — measured, not guessed: twelve
     OVERFLOW findings from audit.py at 390. Nothing is dropped; the ones that
     do not fit two rows are folded behind a toggle that PRINTS THEIR COUNT. */
  .pillbar {
    flex-wrap: nowrap; overflow: visible; padding-bottom: 0; gap: 8px;
    margin-bottom: 10px;
  }
  .pillbar .fcats { flex-wrap: wrap; overflow: visible; }

  /* 44px, everywhere, including the selects — they were 27px */
  .pillbar .pill {
    min-height: 44px; padding: 0 12px; font-size: 12.5px; gap: 6px;
  }
  .pillbar .pill .n { font-size: 11px; }
  .pillbar select {
    min-height: 44px; font-size: 11px; padding: 0 26px 0 9px;
    background-position: right 9px center; background-size: 9px 9px;
  }
}

/* ---- phone, narrow ---------------------------------------------------- */
/* Only where the five controls genuinely cannot sit on one line. Above this
   they keep their own widths — a 12-track grid stretched across a 768px
   tablet printed the word COMPACT alone in the middle of a 480px pill. */
@media (max-width: 560px) {
  /* the controls become a real two-row table rather than five items wrapping
     wherever they land: source over range·sort·region, each cell on a track,
     so the row has columns instead of a ragged right edge */
  .pillbar .fctrls {
    display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: 6px; row-gap: 6px;
  }
  /* rows are declared, not inferred: the density toggle is last in the DOM
     (it belongs after the filters it re-renders) and auto-placement duly gave
     it a third row of its own with three empty tracks beside it */
  .pillbar #srcSel     { grid-area: 1 / 1 / 2 / 9; }
  .pillbar #densityBtn { grid-area: 1 / 9 / 2 / 13; }
  .pillbar #rangeSel   { grid-area: 2 / 1 / 3 / 4; }
  .pillbar #sortSel    { grid-area: 2 / 4 / 3 / 9; }
  .pillbar #regionSel  { grid-area: 2 / 9 / 3 / 13; }
  .pillbar .fctrls > * { width: 100%; max-width: 100%; }
}

/* ============================================================
   RAIL LAYER (2026-08-09) — nine panels, one grammar.

   The rail was nine boxes built at nine different times, each with
   its own internal rhythm. Measured, on the same page:

     panel head    48px (GEI) · 62px (DOCKET) · 95px (STREET, wrapped)
     row gutter    14px everywhere, row padding 8 / 9 / 10 / 11px
     left edge of  the docket's company names moved with the width of
                   the form chip in front of them — 8-K at 41px in,
                   10-K at 48px — and each row's description started
                   back at the gutter, under neither of them
     right edge    ages, counts and momentum all ended at the gutter
                   but none of them was a column: "▲ 27" and "▼ 3"
                   put their arrows two glyphs apart
     the signals   a two-word display name wrapped onto a second line
                   while its @handle stayed vertically centred beside it

   Nothing here changes what a panel says. It gives all nine the same
   three-part row — LEAD SLOT · BODY · FIGURE — the same gutter, the
   same header line, and one right-hand figure column that is tabular
   and actually aligns. Every pair touched is re-measured for AA below.
   ============================================================ */

.rail {
  --rgut: 14px;   /* one gutter, every panel, every row            */
  --rrow: 10px;   /* one row padding                               */
  --rhh:  40px;   /* the header title line — identical on all nine */
  --rchip: 46px;  /* the docket's form-type column                 */
}

/* ---- paper contrast on the rail ---------------------------------------
   The rail sits on --panel (#F2F5F8) with --panel2 (#E6F1FB) under its
   headers and hovered rows — the same pair the desk measured, so the same
   three walked-down hues apply, plus the violet the story line already
   uses for its SIGNAL count:
     --faint   #5F7B92 → 4.05/3.87  subs, ages, counts, engagement → #4E6980 5.25/5.02
     --up-l    #178A5D → 3.97/3.80  ▲ momentum                     → #12734D 5.35/5.12
     --down    #CF4B3D → 4.06/3.88  ▼ momentum                     → #B3392C 5.42/5.18
     --violet  #B07CE8 → 2.78/2.66  every form chip on the docket  → #7C46C2 5.93/5.42
   The chip's own border was rgba(176,124,232,.35) — 1.2:1, an outline you
   cannot see, so the thing that reads as a chip was doing it on padding
   alone. WCAG 1.4.11 wants 3:1 for that boundary.

   CONTINUITY: the four values this block used to declare are the ones now
   held at [data-theme="light"] root — the desk had reached the same three
   independently and the feed a fourth. Declared once. */

/* ---- the header line --------------------------------------------------
   One grid for all nine: a fixed-height title row, and a tab strip that
   drops BELOW it instead of pushing the title line taller on the one panel
   (THE STREET) whose four tabs do not fit beside a title. The title line is
   now the same 40px whether the panel has a sub-label, a tab row or
   neither, which is the thing you actually read down the rail. */
.rail .panel-h {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  grid-template-rows: var(--rhh) auto;
  grid-template-areas: "t s" "b b";
  align-items: center; column-gap: 12px; row-gap: 0;
  padding: 0 var(--rgut);
}
.rail .panel-h .title { grid-area: t; align-self: center; }
.rail .panel-h .sub {
  grid-area: s; justify-self: end; align-self: center;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums lining-nums;
}
/* the sub is a link on three panels and static text on the rest; only the
   links get the affordance, so a label never pretends to be clickable */
.rail .panel-h a.sub:hover { color: var(--brand-l); text-decoration: underline; }

/* The tab strip: its own row, same gutter, same chip, wherever it appears —
   and a real grid rather than a wrap. Four street tabs cannot sit beside a
   title in a 351px panel at any type size I would ship (measured: 367px of
   chip for 321px of room), so they wrapped and only that panel's header grew
   by 33px. Equal tracks instead: three per row, the fourth starting a second
   row on the same left edge, so DOCKET, STREET and SIGNALS all read as the
   same control. */
.rail .panel-h .dktabs {
  grid-area: b; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  column-gap: 5px; row-gap: 5px; padding-bottom: 9px; margin-top: -2px;
}
.rail .dktabs .pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 28px; padding: 0 11px; border-radius: 999px;
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  line-height: 1; letter-spacing: .04em; white-space: nowrap;
  font-variant-numeric: tabular-nums lining-nums;
}
/* "quiet today" was drawn with opacity:.45, which composites --faint down to
   2.0:1 — the one tab telling you a source is empty was the one tab you
   could not read. The information moves to the outline: dashed edge, full
   ink, still clickable, still counted. */
.rail .dktabs .pill.empty {
  opacity: 1; border-style: dashed; color: var(--faint);
}

/* ---- one row shape ----------------------------------------------------
   LEAD SLOT (fixed) · BODY (flexes, ellipses) · FIGURE (right, tabular).
   Applied to the docket, the street, the diary and the signals alike. */
.rail :is(.dk, .cal, .trk, .post) { padding: var(--rrow) var(--rgut); }
.rail :is(.dk, .cal) { min-height: 52px; }

/* the docket & the street: the form chip becomes a COLUMN, so every company
   name starts on one left edge whatever is in front of it, and the filing
   description hangs under the name rather than under the chip. `.r1` is a
   wrapper with no job left, so it stops being a box. */
.rail .dk {
  display: grid; grid-template-columns: var(--rchip) minmax(0, 1fr) auto;
  column-gap: 10px; row-gap: 3px; align-content: center; align-items: baseline;
}
.rail .dk .r1 { display: contents; }
.rail .dk .formchip {
  grid-column: 1; grid-row: 1; justify-self: stretch; text-align: center;
  padding: 1px 3px; border-color: var(--violet); color: var(--violet);
  font-variant-numeric: tabular-nums lining-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail .dk .formchip.court { border-color: var(--down); color: var(--down); }
.rail .dk .formchip.intl  { border-color: var(--brand-d); color: var(--brand-l); }
.rail .dk .co  { grid-column: 2; grid-row: 1; min-width: 0; overflow-wrap: break-word; }
.rail .dk .tm  {
  grid-column: 3; grid-row: 1; justify-self: end; margin-left: 0;
  font-variant-numeric: tabular-nums lining-nums;
}
.rail .dk .tt  { grid-column: 2 / -1; }
.rail .dk .mkt { grid-column: 2 / -1; margin-top: 0; }
/* the empty state is one sentence, not a row — it gets no chip column */
.rail .dk:not(:has(.formchip)) { grid-template-columns: minmax(0, 1fr); }
.rail .dk:not(:has(.formchip)) .tt { grid-column: 1 / -1; }
/* THE STREET's chip is a weekday and a date, not a form type — so it needs a
   wider column, not a different chip. The tracking used to be zeroed here to
   buy "MON 08-10" its 74px, which made the docket and the street print the
   same class two ways, one panel apart. The column pays for the tracking
   instead (measured: 69.4px of chip in an 80px cell), and the chip stays the
   chip. */
.rail #stBody { --rchip: 80px; }

/* the storylines: name flexes, the 7-day count and the momentum are two
   fixed cells on the right, and the arrow gets a column of its own so
   ▲ 27 and ▼ 3 put their triangles on the same x instead of two glyphs
   apart. The count keeps its "/ 7D" unit; the figure inside it is what
   steps up in weight, so the unit stops competing with the number. */
.rail .trk .r1 {
  display: grid; grid-template-columns: 8px minmax(0, 1fr) auto auto;
  column-gap: 8px; align-items: center;
}
.rail .trk .nm {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail .trk .cnt {
  justify-self: end; font-variant-numeric: tabular-nums lining-nums;
}
.rail .trk .cnt b { color: var(--text-dim); font-weight: 600; }
.rail .trk .mo {
  justify-self: end; display: inline-grid; grid-template-columns: 11px 20px;
  column-gap: 2px; align-items: baseline; text-align: right;
  font-variant-numeric: tabular-nums lining-nums;
}
.rail .trk .mo i { font-style: normal; text-align: center; }
.rail .trk .mo.flat { color: var(--faint); }
.rail .trk .last { margin-top: 6px; }
/* the row is a filter control: it needs to be reachable and to show it */
.rail .trk { border-radius: 0; }

/* the signals: the display-name row stops wrapping. A two-word name used to
   break onto a second line with its @handle floating alongside the break;
   now name and handle share one line and ellipse in that order, and the
   avatar keeps its slot even when the image 404s, so the names stay in a
   column. */
.rail .post .r1 { display: flex; flex-wrap: nowrap; align-items: center; gap: 8px; }
.rail .post img.av { width: 22px; height: 22px; flex: none; }
/* the name is the identity and the handle is the address, so the handle
   gives up its characters first — twelve times faster, which in practice
   means the name only ever ellipses once the handle already has */
.rail .post .who {
  flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail .post .hdl {
  flex: 1 12 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail .post .pf { flex: none; margin-left: 8px; }
.rail .post .tx { margin-top: 6px; }
/* engagement was one text node with two &nbsp; between each pair, so three
   numbers that change every refresh never lined up with the row above.
   Three cells of one width, tabular, glyph in its own box. */
.rail .post .eng {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0; row-gap: 6px; margin-top: 7px;
}
.rail .post .eng .m {
  position: relative; display: inline-flex; align-items: baseline; gap: 5px;
  min-width: 50px; font-variant-numeric: tabular-nums lining-nums;
}
.rail .tostory { flex: none; white-space: nowrap; }
.rail .post .eng .m i {
  font-style: normal; width: 13px; text-align: center; color: var(--faint);
}
/* ♥ ⇄ 💬 carry the meaning visually; the word behind each figure is there
   for a screen reader, which otherwise heard "141 107 4" */
.rail .post .eng .m .sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.rail .post .eng .ago {
  margin-left: auto; padding-left: 8px; white-space: nowrap;
  font-variant-numeric: tabular-nums lining-nums;
}

/* the diary: same gutter, same figure column */
.rail .cal { column-gap: 10px; }
.rail .cal-in { justify-self: end; font-variant-numeric: tabular-nums lining-nums; }
.rail .cal-t1 { white-space: nowrap; }
.rail .cal-foot { padding: 13px var(--rgut) 14px; }
.rail #calBody .mcfo-fine { padding: 8px var(--rgut) 2px; }

/* the index head: four periods, four tracks, so the figures sit under their
   own labels instead of under whichever label happened to be shorter */
.rail .idxbig, .rail .idxspark { padding-left: var(--rgut); padding-right: var(--rgut); }
.rail .idxrow {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px 10px; padding: 0 var(--rgut) 12px;
}
.rail .idxrow .k, .rail .idxrow .v { grid-row: auto; }
.rail .idxrow .v { font-variant-numeric: tabular-nums lining-nums; }

/* the trades & the newsletter: the same gutter as every list above them */
.rail #trdBody { padding: 4px var(--rgut) 12px; }
.rail .panel-b.mcfo, .rail #podPanel .panel-b { padding: 11px var(--rgut) 13px; }
.rail .trd { padding: 8px 0; }
.rail .trd-go { display: inline-flex; align-items: center; }

/* ---- focus, everywhere ------------------------------------------------
   The rail had ten interactive elements with no focus indicator at all —
   the GEI links, the newsletter field, the diary rows, the docket rows.
   One ring, 5.2:1 on paper, drawn inside on the rows that run full-bleed
   (the panel clips at its radius, so an outset ring on the first row is a
   ring with its top cut off). */
.rail :is(a, button, select, input, [role="button"], [tabindex]):focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px;
}
.rail :is(.dk, .cal, .trk, .post .tx, .pod-ep, .trd-go, .cal-more):focus-visible {
  outline-offset: -2px;
}
.rail .mcfo-form input:focus-visible { outline-offset: 1px; }

/* ---- phone ------------------------------------------------------------ */
@media (max-width: 780px) {
  .rail { --rgut: 13px; --rrow: 11px; --rhh: 44px; }
  /* every tab, every listen button, every subscribe link is a finger target */
  .rail .dktabs .pill { min-height: 44px; padding: 0 12px; font-size: 11.5px; }
  .rail .post .eng { flex-wrap: wrap; row-gap: 6px; }
  .rail .tostory { min-height: 44px; padding: 0 12px; font-size: 10.5px; }
  .rail .pod-listen a { min-height: 44px; padding: 0 12px; }
  .rail .pod-listen { min-height: 0; }
  .rail .pod-nav button { width: 52px; height: 44px; }
  /* the SUBSCRIBE link becomes the row's finger target, so the row stops
     paying for bottom padding the 44px box already provides */
  .rail .trd { padding: 7px 0 0; }
  .rail .trd-go { margin-top: 0; }
  .rail .trd-go, .rail .cal-more { min-height: 44px; }
  .rail .mcfo-form input, .rail .mcfo-btn { min-height: 44px; }
  .rail .idxasof a { display: inline-flex; align-items: center; min-height: 44px; }
  .rail .panel-h a.sub { display: inline-flex; align-items: center; min-height: 44px; }
  /* the diary's date column is 92px of a 366px panel on a phone */
  .rail .cal { grid-template-columns: 78px minmax(0, 1fr) auto; }
}

/* ============================================================
   CONTINUITY LAYER · R1 (2026-08-09)
   Six surfaces were raised by six builders who could not see each
   other's work. Nothing here redesigns any of them; it makes the
   values they each arrived at independently agree, and moves the
   ones that were repeated into tokens so the next pass cannot
   fork them again.

   Measured forks this closes (build/audit.py + a computed-style
   probe across index.html and calendar.html at 1440 and 390):
     · ONE TOKEN, THREE VALUES — see [data-theme="light"] above.
     · FOUR SIZES for the same mono label: a section head was 12px
       in the rail and 11px on the desk; a sub-line 10.5px in the
       rail, 11px on the desk, 10.5px on the calendar; a column
       header 9px on the desk and 10px on the calendar.
     · THREE CHIP GEOMETRIES: the feed's data chip was r4/7px/10.5,
       the docket's r4/3px/11, the diary's r6/6px/10.
     · TWO RULE COLOURS for one job: the feed's row divider was
       --grid, every other row on both pages --grid-soft, which is
       #E9EEF3 on a #F2F5F8 panel — 1.05:1, a rumour of a rule.
     · FIVE FOCUS TREATMENTS: the wire ring (2px --brand) on most
       things, white on the band, and — on .btn and every footer
       link — no rule at all, so those fell through to Chrome's own
       3px auto / 1px #005FCC. The diary drew a 6px rectangular
       ring around a 999px pill.
     · TWO GUTTERS: 15px on the desk, 14px everywhere else.
   ============================================================ */

:root {
  /* ---- the radius scale. Was 4/5/6/8/9/11/12/999 with 5, 6 and 9
     belonging to no shape on screen — they were focus rings and one
     button guessing at the panel radius. */
  --r-panel: 12px;   /* the desk, every rail panel                */
  --r-row:    8px;   /* a story row, a control, a field           */
  --r-chip:   4px;   /* a rectangular data chip                   */
  --r-pill: 999px;   /* a filter chip, a tab                      */

  /* ---- the focus ring, one specification. Width and colour never
     vary; only the offset does, and only between "the control has
     room around it" (+2) and "the control is flush to its row" (-2). */
  --ring-w: 2px;
  --ring-c: var(--brand);
  --ring-o: 2px;
  --ring-i: -2px;

  /* ---- the mono label scale. Three rungs, tracked at a tenth of an
     em on every one, which is what the surfaces already did wherever
     they agreed at all. */
  --fs-sec: 12px;    /* AXIOM GEI · INDEX MOVERS · AUGUST 2026     */
  --fs-sub: 11px;    /* the provenance line under a section head   */
  --fs-col: 10px;    /* the label naming a column of figures       */
  --ls-mono: .1em;

  /* ---- the data chip */
  --chip-h:  19px;
  --chip-px:  7px;
  --chip-fs: 10.5px;

  /* ---- one gutter */
  --gut: 14px;

  /* ---- one row divider. --grid-soft is 1.05:1 on the panel it is
     drawn on: not a hairline, an absence. --grid is 1.33:1 on white
     and 1.22:1 on the panel — the weight the feed already ships and
     the only one of the two that survives a photograph. */
  --rule: var(--grid);
}

/* ---- one gutter ------------------------------------------------------- */
/* The desk derived 15px, the rail 14px and the story row 14px. Three
   columns of one screen whose left edges were a pixel apart all the way
   down. --dpad still drives the desk's rank column and memo indent, so the
   memo's first word keeps landing on the #1 headline — it just lands on the
   rail's edge too now. */
.desk { --dpad: var(--gut); }
@media (max-width: 780px) { .desk { --dpad: 12px; } }

/* ---- one row divider -------------------------------------------------- */
.rail :is(.dk, .trk, .cal, .post, .trd, .pod-ep) { border-bottom-color: var(--rule); }
.fp-item, .desk-memo, .mv.mvhead, .mv.turn, .side-sec,
.pillbar .fctrls { border-color: var(--rule); }
.mv.turn { border-top-color: var(--rule); }

/* ---- one mono label scale --------------------------------------------- */
/* the section head: brand ink, --fs-sec, .1em. The rail's nine panel titles
   were already here; the desk's three side-section labels were a size below
   them and sitting on the same horizon.

   The desk also ran its label and its provenance together as one brand-blue
   string, so "CLICK FOR THE WHY" was set as loudly as INDEX MOVERS while the
   nine panels beside it split exactly this line into a brand title and a
   faint sub. app.js now emits the same two parts; this types them the same,
   and the header is a baseline-aligned row so a long provenance runs on
   rather than pushing the label onto a second line. */
.desk-side .side-sec > .micro,
.rail .panel-h .title { font-size: var(--fs-sec); letter-spacing: var(--ls-mono); }
.desk-side .side-sec > .micro {
  display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 9px; row-gap: 2px;
}
.desk-side .side-sec > .micro > .sub {
  font-size: var(--fs-sub); letter-spacing: var(--ls-mono); color: var(--faint);
}

/* the provenance sub-line: same size and same ink on both, so "215 STORIES
   REVIEWED · CONVENED 1D AGO" and "GLOBAL ENTERTAINMENT INDEX ↗" read as one
   register instead of two. --faint is now a single value (see above), which
   is what closes the #4E6980 / #55708A split these two used to carry. */
.desk-h .sub,
.rail .panel-h .sub {
  font-size: var(--fs-sub); color: var(--faint); letter-spacing: var(--ls-mono);
}

/* the column header over a stack of figures. The movers' table set these at
   9px — the smallest type anywhere on either page — while the diary set the
   identical role at 10px. */
.mv.mvhead :is(.mt, .mn, .mvs, .num) {
  font-size: var(--fs-col); letter-spacing: var(--ls-mono);
}

/* ---- one data chip ---------------------------------------------------- */
/* The docket's form chip sat 1.5px taller than the feed's chips on a 11px
   body with 3px of side padding; the diary's carried a 6px radius. One
   height, one radius, one type size — the borders and the hues stay exactly
   as each surface set them, because those carry meaning. */
.rail .dk .formchip {
  min-height: var(--chip-h); padding: 0 3px;
  border-radius: var(--r-chip); font-size: var(--chip-fs);
}
.story .meta :is(.srcchip, .figchip, .movechip, .langchip, .coverage) {
  min-height: var(--chip-h); border-radius: var(--r-chip);
}
/* the storyline chip stays round — it is a filter, not a reading, and the
   dot inside it says so — but it stops being a pixel taller than its
   neighbours on the same line. */
.story .meta .trchip { min-height: var(--chip-h); border-radius: var(--r-pill); }

/* ---- one filter-pill count -------------------------------------------- */
/* the diary's count was 10.5px against the wire's 11px inside an otherwise
   identical chip. */
.pill .n { font-size: var(--fs-sub); }

/* ---- one radius ------------------------------------------------------- */
/* .btn guessed 9px, one off the 8px every field and story row uses. */
.btn { border-radius: var(--r-row); }

/* ---- one momentum figure ---------------------------------------------- */
/* THE DESK IS WATCHING and THE STORYLINES print the same two numbers for the
   same storylines, two columns apart. They disagreed on order (▲27 50/7D vs
   50 / 7D ▲27), on size (10.5/10 vs 11.5/11.5), on weight and on whether the
   slash gets spaces. app.js now emits both in the desk's order and unspaced;
   this types them the same and gives the desk the rail's arrow cell, so the
   triangles line up in a column on both. */
.wl .num, .rail .trk .mo {
  display: inline-grid; grid-template-columns: 9px 17px; column-gap: 2px;
  width: auto; align-items: baseline; text-align: right;
}
.wl .num i, .rail .trk .mo i { font-style: normal; text-align: center; }
.wl .num.flat, .rail .trk .mo.flat { color: var(--faint); }
.rail .trk :is(.cnt, .mo) { font-size: var(--chip-fs); }
.rail .trk .mo { font-weight: 600; }
.rail .trk .cnt { font-size: var(--fs-col); color: var(--faint); letter-spacing: .02em; }
/* the count is context, the momentum is the news — the rail was stepping the
   count's figure up in weight and colour as well, so the row had two things
   shouting where the desk's identical row has one. */
.rail .trk .cnt b { color: inherit; font-weight: inherit; }

/* ---- one focus ring ---------------------------------------------------
   Two things were missing rather than merely different: .btn and every link
   in the footer had no :focus-visible rule at all, so a keyboard reader got
   Chrome's default — a 3px auto ring in the button's own text colour on one,
   #005FCC on the other, a blue that appears nowhere in this palette. The
   ring is declared once here and the radius follows the shape it is drawn
   round, which is the one thing a ring must never guess at. */
:where(.btn, .ftr a, .foot a, .axw-consent button,
       .drawer input, .modal input, .oplock-box input):focus-visible {
  outline: var(--ring-w) solid var(--ring-c);
  outline-offset: var(--ring-o);
  border-radius: var(--r-row);
}
/* And the ring has to arrive when the key is pressed. `.btn` and `.pill` both
   declared `transition: all`, and `all` includes outline-width and
   outline-color — so their ring FADED UP over 120-150ms from the initial
   `medium currentColor` while the filter bar's chips (which list their
   transitioned properties) snapped. Two focus behaviours, one keyboard. */
.btn, .pill { transition-property: background-color, border-color, color, box-shadow, opacity; }
/* the last inset ring still drawn at its own offset */
.desk-h:focus-visible { outline-offset: var(--ring-i); }
/* on the blue band the ring is white, as the chrome layer established.
   `.ftr .ft-brand a` was a typo for a class that does not exist anywhere —
   the markup says `ftbrand`, no hyphen — so this half of the rule matched
   nothing and all seven footer links kept the default brand-blue ring on a
   brand-blue band: 1.05:1 in light, 1.86:1 in dark, both far under the 3:1
   WCAG minimum for a non-text indicator. A keyboard user simply could not
   see where focus was. Fixing the typo would still only have covered one of
   the seven, because the other six live in .ftcredits, .ftdisc and .ftcopy.
   The band is the thing that makes the ring invisible, so the band is what
   the selector should name. */
.topbar :is(.btn, a):focus-visible,
.ftr a:focus-visible { outline-color: #fff; }
/* the rings that were drawn at a radius their element does not have. A flush
   row — no radius of its own — takes the 6px the desk's ten headlines and all
   nine rail panels already ring their rows at; the desk's three figure lists
   were the only ones at 5px and 1px in. */
.desk-side :is(.mv.linked, .wl, .cov):focus-visible {
  border-radius: 6px; outline-offset: var(--ring-i);
}
.story :is(a, button, .coverage):focus-visible { border-radius: var(--r-chip); }
.story .trchip:focus-visible { border-radius: var(--r-pill); }
.pillbar .pill:focus-visible { border-radius: var(--r-pill); }

/* ---- one tape row height ---------------------------------------------- */
/* the headline tape's links were 43px at 390 — one pixel under the bar, and
   the only control on the phone that missed it by rounding. */
@media (max-width: 780px) {
  .chrome .tick { min-height: 44px; }
  /* the same button, four heights: the chrome layer gave .topbar .btn 44px,
     the rail gave .mcfo-btn 44px and the diary gave .btn.primary 44px, each
     in its own rule — so the ones nobody owned (LOAD MORE, and every control
     in the drawer and the operator box) stayed at 31px. One declaration. */
  .btn { min-height: 44px; }
}

/* ============================================================
   WAVE 1 DEFECT CLOSE-OUT (2026-08-08)
   Five measured faults from build/audit.py, all reproduced at both
   widths before and after. Nothing here is a design change.
   ============================================================ */

/* .mvtable: 6px overflow at BOTH widths (sw 429/cw 423 at 1440,
   sw 346/cw 340 at 390). The rows carry `margin: 0 -6px` so the zebra
   stripe reaches the panel edge — a bleed — but .mvtable had no padding
   for them to bleed INTO, so every row sat 6px wider than its parent.
   Giving the table the matching padding puts the rows' outer edge exactly
   on the table's border box: same visual result, no overflow.

   RIGHT ONLY. This was `padding-left: 6px` too, and the left half was doing
   no work: it indented TICKER to 604 while the section's own INDEX MOVERS
   heading sat at 598, and while every row of MOST COVERED below it sat at
   598 — so the one table in the desk column started six pixels right of
   everything it was stacked with. Measured both ways: right-only holds
   overflow at 0 at 1440 and 390 exactly as the two-sided version did, and
   puts the first column back on 598. Removing both sides is what brings the
   overflow back, which is how we know which half was load-bearing. */
.mvtable { padding-right: 6px; }

/* Four sub-AA pairs on the blue footer, and one in the masthead search.
   The audit could not see these until it composited alpha — measuring
   rgba(255,255,255,.72) as if it were opaque white compares white to
   white. Ratios below are against the composited blue the reader sees. */
.ftkeys-l { color: rgba(255,255,255,.92); }   /* 3.56:1 -> 5.02:1 */
.ftdisc   { color: rgba(255,255,255,.90); }   /* 3.67:1 -> 4.83:1 */
.cred     { color: rgba(255,255,255,.94); }   /* 4.27:1 -> 5.24:1 */
.ftcopy   { color: rgba(255,255,255,.94); }   /* same pair, same fix */

/* The Esc chip is white on a 14%-white wash over the blue, compositing to
   ~#3D7FC0 and leaving white at 4.19:1. It is the FOOTER key caps
   (.ftkeys kbd), not the masthead search — the first fix targeted
   .searchbox kbd and the ratio did not move, which is how the real owner
   was found. Deepening the wash rather than brightening the glyph keeps it
   reading as a key cap. */
.ftkeys kbd { background: rgba(3, 32, 60, .40);
              border-color: rgba(255,255,255,.44); }

/* .wm-sec is the masthead's section label ("THE CALENDAR"). The chrome pass
   set it to .82 on the blue band, which composites to 4.27:1 — it only shows
   on the sub-pages, so index.html audited clean and this surfaced when the
   calendar was measured separately. */
.chrome .wm-sec, .wm-sec { color: rgba(255,255,255,.94); }

/* ============================================================
   WAVE 2 · THE SEAMS BETWEEN SURFACES (2026-08-09)

   Seven surfaces were improved by seven builders who could not see
   each other's work. Each one is right on its own; laid side by side
   they disagree about objects they share. Nothing here restyles a
   surface — every rule takes a property that had two or three values
   for ONE job and routes the minority to whichever value the majority
   of surfaces already shipped.

   Measured before and after with Playwright at 1440 and 390 on both
   pages; build/audit.py stays at 0 hard defects throughout.

   Two findings from the surveys are deliberately NOT actioned, because
   measurement said they were not real:
     · "a.trd and a.pod-ep have 0px row inset in a 14px rail" — their
       inset is on .panel-b (11px 14px), and their content-left measures
       15px, the same as .trk, .dk, .cal, .post and .panel-h. Padding
       them would have INTRODUCED the seam it looked like they had.
     · "20 of 60 .formchip are tracked normal, the same class both
       ways" — real, but caused by one rule (.rail #stBody) buying a
       74px column, not by drift. Fixed at the column, above.
   ============================================================ */

:root {
  /* ---- one tracking for the mono data chip. The chip vocabulary ran
     .05em (srcchip, catchip, langchip, formchip, cl-chip — and the
     diary's 1,421 chips alone carry it), .04em (figchip), .08em
     (tostory) and normal (tagchip, trchip, sigchip, movechip,
     coverage). One value, and it is the one most of the product and
     all of the diary already used. */
  --ls-chip: .05em;

  /* ---- one hover duration. 114 controls transitioned at .12s and 11
     at .15s — .btn and the search field, which sit next to each other
     in the masthead and settled at different speeds. */
  --dur: .12s;
}

/* ---- one data chip ----------------------------------------------------
   The chips already agreed on height (19px), size (10.5px) and radius
   after Wave 1. What still differed was tracking, the line-height inside
   the box, and whether figures were tabular. Mono is fixed-pitch, so
   tabular-nums costs nothing in width here — it only stops the diary's
   TIER 1 and the rail's dates from sitting a hair off their neighbours. */
.story .meta :is(.srcchip, .catchip, .tagchip, .trchip, .langchip,
                 .figchip, .sigchip, .movechip, .coverage),
.rail .dk .formchip,
.tostory {
  letter-spacing: var(--ls-chip);
  line-height: 1;
  font-variant-numeric: tabular-nums lining-nums;
}
/* the docket chip is a grid item, so it was blockified and its text sat to
   the line-height rather than in the middle of the box the way every chip
   on the feed does */
.rail .dk .formchip {
  display: inline-flex; align-items: center; justify-content: center;
}
/* r6 belonged to no other shape on either page — the radius scale is
   4 / 8 / 12 / 999 and this is a rectangular data chip */
.tostory { border-radius: var(--r-chip); }

/* THE CALENDAR panel's tier marker was the one Inter chip in a mono
   vocabulary, at 9.5px/700 — the smallest type on either page — while the
   diary prints the identical datum as a mono chip. Same instrument, same
   voice. */
.cal-t1 {
  font-family: var(--mono); font-size: var(--fs-col); font-weight: 600;
  letter-spacing: var(--ls-chip);
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- figures that align, align -----------------------------------------
   Aligned columns that were still proportional. The rule is "a figure a
   reader compares down a column, or one that reprints in place as the page
   updates" — so the counters, the dates and the chip figures are here and
   PROSE IS NOT: .sum, .tlink, .tt, .cl-name and .trd-b carry numbers inside
   sentences, where tabular spacing is a defect, not a fix.

   The sharpest case: the rail's .cal-d prints "13 AUG" proportional while
   the diary's td.c-date prints the same string tabular, and in the rail the
   very next cell (.cal-in "IN 4D") was already tabular — two figures in one
   row disagreeing. */
.cal-d, .cal-in,
.dk .tm,
.srcrow .ok, .srcrow .fail,
.pod-ep .n, .pod-dots,
.trd-more-n,
.feedmeta .count,
.idxrow .k,
.chrome .topbar .gei-strip :is(.gk, .gv) {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ---- one hover speed --------------------------------------------------- */
.btn, .searchbox input, #q { transition-duration: var(--dur); }

/* ---- the focus rings that were missing or drawn to their own spec ------
   Wave 1 declared the ring once, but three controls still fell outside it:

     · #nlIntro, the brief-builder's textarea. The :where() list above names
       `.drawer input` and there is no `textarea` in it, so the one control
       on the page you type a paragraph into had outline-style:none — no ring
       at all, not a different one.
     · .panel-b, THE STREET's body. It is overflow-y:auto, which makes Chrome
       give it a tab stop of its own, and nothing had ever styled it — so it
       took the UA default, 1px auto rgb(0,95,204), a blue that appears
       nowhere in this palette. It is a flush box inside a panel, so it rings
       inset like every other flush row.
     · .axw-consent's two buttons still carried `transition: all .12s` from
       their own rule, and `all` includes outline-width and outline-color —
       so their ring ANIMATED from 3px currentColor to 2px brand over 120ms
       while every other ring on the page snapped. Wave 1 fixed exactly this
       for .btn and .pill and the consent box was not in the list. */
/* NOT :where() here, deliberately. Wave 1's ring list is wrapped in :where()
   so it never fights a surface's own styling — but `.drawer textarea` sets
   `outline: none` in its own rule at (0,1,1), and a zero-specificity ring
   cannot beat it. This is the one control on the page you type a paragraph
   into; its ring has to win. */
.drawer textarea:focus-visible,
.modal textarea:focus-visible,
.oplock-box textarea:focus-visible {
  outline: var(--ring-w) solid var(--ring-c);
  outline-offset: var(--ring-o);
  border-radius: var(--r-row);
}
/* r9 is not on the radius scale (4 / 8 / 12 / 999) — it was one field
   guessing at the 8px every other field and control uses. */
.drawer textarea { border-radius: var(--r-row); }
.panel-b:focus-visible {
  outline: var(--ring-w) solid var(--ring-c);
  outline-offset: var(--ring-i);
  border-radius: var(--r-chip);
}
.axw-consent button {
  transition-property: background-color, border-color, color, box-shadow, opacity, filter;
}

/* ---- ring geometry: the last five outliers -----------------------------
   The dominant ring is 2px --brand at ±2px offset. These five each guessed
   their own offset or radius: the newsletter checkbox rang at radius 0, the
   signup field at +1, the desk's section links at -1, and the desk header at
   the 11px it happens to be drawn at rather than the 8px control radius. */
#mcfoEmail:focus-visible { outline-offset: var(--ring-o); border-radius: var(--r-row); }
/* a 13px checkbox takes the chip radius, not the 8px field radius — an 8px
   ring round a 13px box is a circle */
#nlSignal:focus-visible { outline-offset: var(--ring-o); border-radius: var(--r-chip); }
.desk-side .micro a:focus-visible {
  outline-offset: var(--ring-o); border-radius: var(--r-chip);
}

/* ---- the chip scale steps down on a phone, on BOTH pages ----------------
   The feed already shrank its chips 10.5 → 10 at 780 in its own media rule,
   but the token every other chip reads stayed at its desktop value — so on a
   390px screen the diary's 1,421 chips and the rail's docket chips printed a
   half-point larger than the identical chip on the feed directly above them.
   Scaling the token scales all three at once. */
@media (max-width: 780px) {
  :root { --chip-fs: 10px; --chip-px: 5px; }
}

/* ---- the hover the zebra was eating -----------------------------------
   .mv.linked:hover paints --panel2 and has since the desk was written, but
   .mvtable .mv.linked:nth-child(even) paints the zebra stripe at one class
   more specificity — so on the INDEX MOVERS table the odd rows lit under the
   cursor and the even rows did nothing. Alternating feedback down a list of
   identical controls, which reads as the table being half broken rather than
   as a stripe. Same tint, stated where it wins. */
.mvtable .mv.linked:hover,
[data-theme="light"] .mvtable .mv.linked:hover { background: var(--panel2); }

/* ---- one hover speed on the rows --------------------------------------
   Of 810 interactive elements, 114 eased and the rest snapped — and the
   split ran inside single surfaces: .trk eased while .dk, .cal, .post,
   .trd and .pod-ep, the five rows stacked directly under it in the same
   rail, changed colour instantly. They are the same object doing the same
   thing; they now do it at the same speed. */
.rail :is(.dk, .cal, .post, .trd, .pod-ep),
.rail :is(.dk, .cal, .post, .trd, .pod-ep) :is(.co, .cal-n, .tx, .trd-n, .t),
.desk-side :is(.wl, .cov, .mv.linked),
.desk-side :is(.wl, .cov) :is(.wn, .cn) {
  transition: background-color var(--dur), color var(--dur);
}

/* The not-responding state. It is the one screen a reader meets when the wire
   has failed them, so it gets the same care as the rest — not a bare string. */
/* Scoped to #app, because `.empty` is not this state's private name: app.js
   also stamps it on a social tab whose count is zero (`<button class="pill
   empty">HN 0</button>`). An unscoped `max-width: 46ch; margin: 0 auto` on
   that pill rendered it at half the width of its grid cell — a rule written
   for a full-page failure screen quietly reshaping a 52px tab. The failure
   screen is always the sole child of #app; the pill never is. */
#app > .empty { padding: 56px 20px 64px; max-width: 46ch; margin: 0 auto;
                text-align: center; }
#app > .empty .big { font-family: var(--mono); font-size: 13px; letter-spacing: .1em;
              color: var(--text); margin-bottom: 10px; }
#app > .empty p { color: var(--text-dim); font-size: 13.5px; line-height: 1.6; margin: 0; }
#app > .empty .empty-act { margin-top: 18px !important; }
/* --r-row, not a literal: the verifier caught this adding a tenth distinct
   radius in the very commit that argued against them. */
#app > .empty .btn { font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
              min-height: 44px; padding: 0 20px; cursor: pointer;
              color: #fff; background: var(--brand); border: 1px solid var(--brand);
              border-radius: var(--r-row); }
#app > .empty .btn:hover { filter: brightness(1.08); }
#app > .empty .btn:disabled { opacity: .6; cursor: default; }
#app > .empty .empty-dev { margin-top: 20px !important; font-size: 11.5px; opacity: .75; }
#app > .empty .empty-dev code { font-family: var(--mono); }

/* NOTHING MATCHES — the state a reader reaches by filtering the wire to zero.
   Held to the same bar as the resting page: 44px controls, AA contrast, and
   an aria-live region so a screen reader is told the river emptied. */
.nores { padding: 40px 14px 48px; max-width: 52ch; }
.nores-h { font-family: var(--mono); font-size: 12px; letter-spacing: .1em;
           color: var(--text); margin-bottom: 8px; }
.nores p { color: var(--text-dim); font-size: 13.5px; line-height: 1.6; margin: 0; }
.nores p b { color: var(--text); font-weight: 600; }
.nores-act { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px !important; }
/* The 28-character cap in app.js is a ceiling on the WORD count; this is the
   ceiling on the BOX. A capped label still ran 346px wide in a 338px column at
   390px, so the pill also truncates to whatever space it actually has. */
.nores-act .pill { min-height: 44px; cursor: pointer; max-width: 100%;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nores-all { border-color: var(--brand) !important; color: var(--brand) !important; }

/* The GEI strip is filled by renderGeiStrip() at paint. When the payload never
   arrives, the anchor stays — an empty 366x2px link in the topbar of the
   failure screen: focusable, clickable, invisible, and pointing off-site.
   Nothing to read means nothing to show. */
.gei-strip:empty,
.chrome .topbar .gei-strip:empty { display: none; }

/* HIT AREA — the meta chips are 22px tall inside a meta line that is already
   44px tall. Half the bar, on the two controls a mobile reader reaches for
   most: "+8 MORE OUTLETS" and the storyline chips.

   The fix is an invisible extension, not a bigger chip. `min-height: 44px`
   would inflate the visible pill and change the feed's density, which is the
   product. An ::after stretched to the full meta band doubles what a thumb
   can hit and moves nothing: the chips stay 22px, the rows stay the height
   they are.

   Vertical only, deliberately. The gap to the neighbouring .alsoicons is 4px,
   so any horizontal expansion would start stealing its taps — a bigger target
   that hits the wrong thing is worse than a small one that hits the right
   one. Above and below there is nothing to steal from: the headline link sits
   97px clear and the row itself is not clickable. */
.story .meta .coverage,
.story .meta .trchip { position: relative; }
.story .meta .coverage::after,
.story .meta .trchip::after {
  content: ""; position: absolute; left: 0; right: 0; top: -11px; bottom: -11px;
}

/* ---------------------------------------------------------------- the gate */
/* Early access. assets/gate.js runs before app.js and shows this when there is
   no valid key; the wire's own markup is never populated, because the payload
   never arrives. Nothing here is a blur over real content — there is no content
   behind it to reveal, which is the entire point of the design. */
html[data-gated] body { overflow: hidden; }
.gate {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; background: var(--bg);
}
.gate-box {
  width: 100%; max-width: 420px;
  background: var(--panel); border: 1px solid var(--grid);
  border-radius: 14px; padding: 26px 26px 22px;
  box-shadow: 0 1px 0 var(--grid-soft), 0 24px 64px -32px rgba(0,0,0,.55);
}
.gate-mark {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .07em; color: var(--text-dim); line-height: 1.05;
  display: flex; flex-direction: column; margin-bottom: 20px;
}
.gate-mark span {
  font-size: 17px; letter-spacing: .08em; color: var(--brand);
  text-transform: uppercase;
}
.gate-h {
  font-family: var(--serif); font-size: 24px; font-weight: 600;
  margin: 0 0 8px; color: var(--text); letter-spacing: -.01em;
}
.gate-p { font-family: var(--serif); font-size: 15px; line-height: 1.5;
          color: var(--text-dim); margin: 0 0 18px; }
.gate-f { display: flex; gap: 8px; }
.gate-f input {
  flex: 1; min-width: 0; padding: 10px 12px;
  font: 500 14px/1.2 var(--mono); letter-spacing: .04em;
  background: var(--field); border: 1px solid var(--grid);
  border-radius: 8px; color: var(--text);
}
.gate-f input:focus { outline: none; border-color: var(--brand-l); }
.gate-f .btn { min-height: 44px; white-space: nowrap; }
.gate-msg { font-family: var(--mono); font-size: 11.5px; line-height: 1.5;
            color: var(--down); margin: 12px 0 0; min-height: 1.2em; }
.gate-foot { font-family: var(--serif); font-size: 12.5px; line-height: 1.5;
             color: var(--faint); margin: 16px 0 0; }
@media (max-width: 460px) {
  .gate-box { padding: 20px 18px 18px; border-radius: 12px; }
  .gate-f { flex-direction: column; }
  .gate-f input { font-size: 16px; }   /* 16px or iOS zooms the page on focus */
}
.gate-foot a { color: var(--brand-l); text-decoration: none; border-bottom: 1px solid var(--grid); }
.gate-foot a:hover { border-bottom-color: var(--brand-l); }
