/* ============================================================
   feed.css — the v2 Feed, APPENDED to signal.css by the packager.

   SCOPING IS THE LAW OF THIS FILE. These rules ride at the end of the
   shared stylesheet and therefore load on EVERY page of the site —
   article pages, v1 tag rivers, the lot. Two disciplines:
     1. Shell rules (viewport lock, flex column) live under body.feedhome,
        which only index.hbs sets. An unscoped html/body overflow here
        would freeze scrolling on every article page.
     2. Vocabulary v1 also owns (.card, .dek, .cred, .trail, .quote, .wim,
        .acts, .inst, .dev, .rcpb…) is scoped to the v2 CONTAINERS —
        :is(.app, .deckroot, .recwrap, .recdoc) — so the Feed's grammar can
        never restyle a v1 surface.
        CONTAINERS, not body classes, and this bit twice in one day: a
        channel deck extends v1's layout, so its body never carries
        feedhome. Scoped to the body class, a deck lost the why-block's
        box and — worse — showed the visually-hidden headline as type
        directly under the plate that already carried it.
   Everything else (pg*, rec*, brkbar, u-*, story*, tl/bd/brep) is
   v2-unique and safe bare.
   ============================================================ */

/* ---- v2 tokens ----
   Scoped to .sg, not :root, because that is where this theme keeps every
   token (a layer outside .sg paints with undefined variables — the iOS
   transparent-sheet incident). Two families:

   MOTION. Measured off Flipboard first (2026-09-07) — card-level moves
   ~160ms, the article push ~200ms — then tuned by the owner on the phone
   to 140ms for card-level, which is where it stopped feeling like a
   transition and started feeling like a cut. The layer push stays at the
   measured 200ms: it travels the full width of the screen, and the same
   duration that reads as crisp over a page height reads as a snatch over
   a whole viewport.

   NEITHER OF THESE IS THE SNAP. The pager's physics belong to the browser
   and expose no timing at all — which is the entire reason the flick feels
   right and the reason the fold was deleted. These tokens govern only the
   motion we own: the cover-to-deck transition and the breaking bar.

   THE BREAKING BAND, designed for both grounds. It cannot use --ink,
   which inverts to near-white in dark mode and would flip the bar's
   ground to paper. Dark is now the default surface, so the band is an
   elevated dark strip under a red rule; light keeps the ink band. */
.sg{
  --t-card:140ms; --t-layer:200ms; --ease-card:cubic-bezier(.3,.7,.25,1);
  --brk-bg:#131211; --brk-fg:#FFFFFF; --brk-hot:#FF6A54; --brk-rule:transparent;
  /* On paper the tile is white with a hairline, or it disappears into the
     page it is supposed to sit on. */
  --srcchip:#FFFFFF; --srcchip-ln:rgba(19,18,17,.16);
}
.sg[data-mode="dark"]{
  --brk-bg:#1B1B20; --brk-fg:#F7F5F0; --brk-hot:#FF6A54; --brk-rule:#E8412B;
  /* A DIMMED neutral, not paper white (owner, 2026-09-07: "drop the chip
     from cream to a dimmer neutral, so three little tiles don't
     out-brighten the deks on a very dark cover"). Bright enough that a
     light publisher mark still reads on it, dark enough to sit under the
     dek rather than in front of it. */
  --srcchip:#CFCAC2; --srcchip-ln:transparent;
}

/* ---- shell: header row, breaking bar, then the pager owns the rest ----
   SCOPE NOTE, twice revised.
   2026-09-07 (morning): shell CHILDREN were scoped to the v2 containers
   (.app on the cover, .deckroot on a channel deck) rather than to
   body.feedhome, because a channel deck extends v1's layout and its body
   class never carries feedhome — that mistake cost the deck its header
   and brief twice in one day.
   2026-09-07 (later): the HEADER's own rules are now scoped to NOTHING,
   because .fhead renders on every page of the site — post pages, reporter
   dossiers, About, the 404 — as of the chrome standardization. Container
   scoping would have left it unstyled on exactly the pages that needed
   fixing. That is safe here and only here: .fhead, .fwm, .hbtn, .qform,
   .chbtn, .chsheet, .chip and .chx are v2-unique, verified as ZERO hits
   each in v1's signal.css. Everything that is genuinely v1-shared
   vocabulary (.card, .cred, .trail, .quote, .wim, .inst, .dev) stays
   container-scoped and a test refuses it bare. */
body.feedhome{height:100%;overflow:hidden}
/* --shellh, set by fitShell() from visualViewport.height, is the height
   that actually holds: svh over-reported on the owner's iPhone under
   Safari's expanded bottom chrome and pushed the deck bar off-screen.
   The svh below it is the PRE-JS value and must stay — it is what paints
   before feed.js measures.
   Why the fallback is svh and not dvh (owner caught content under the
   Safari toolbar, 2026-09-07): dvh tracks the toolbar and shrinks AFTER
   layout, so a page
   laid out with the toolbar hidden gets clipped when it slides back in.
   svh is the smallest viewport: the page always fits, at the cost of a
   little unused space when the toolbar is away. For a pager where a
   clipped sentence is a defect, that is the right trade. */
.feedhome .sg{height:100vh;height:100svh;height:var(--shellh,100svh);display:flex;flex-direction:column}
.feedhome .app{flex:1;min-height:0;display:flex;flex-direction:column;width:100%;max-width:760px;
  margin:0 auto;background:var(--paper)}
.fhead{flex:none;position:relative;z-index:60;background:var(--paper);border-bottom:1px solid var(--ink)}
.fwm{font-family:var(--display);font-size:20px;font-weight:800;letter-spacing:-.042em;color:var(--ink);text-decoration:none}
.fwm i{font-style:normal;font-weight:400;color:var(--ink2)}
/* the accent character, same grammar as .wm in signal.css */
.fwm s{text-decoration:none;color:var(--acc)}
/* THE WAY BACK ALSO LIVES AT THE TOP (same report as the .dbar note).
   The header is the one edge the browser's chrome never occupies, and the
   wordmark was already the ruled way home — owner, 2026-09-07: "The
   Fabric.Media logo should always take you to the top of the cover." So
   it wears the arrow rather than competing with a second control, and the
   arrow teaches what the wordmark already did. Only inside a deck: on the
   cover there is nowhere back to. */
/* THE ARROW SHOWS ON EVERY PAGE THAT IS NOT THE COVER (owner, 2026-09-09,
   landing on a shared signal: "when I land on that share there is no way
   to get back to The Signal"). It was scoped to the two DECK states, which
   was right when a deck was the only place you could be lost — but a share
   link now lands readers on post pages, story records and dossiers, and on
   every one of them the wordmark was an unmarked link. body.feedhome is
   the cover document and the only page with nowhere to go back to. */
/* THE SLOT IS ALWAYS THERE; ONLY THE ARROW COMES AND GOES (owner,
   2026-09-09: "What I don't like is that the logo moves"). It did —
   prefixing the wordmark shifted it ~24px the moment you left the cover,
   so the one fixed landmark in the chrome was the thing that jumped.
   A nav bar reserves its leading slot whether or not there is a back
   control in it, and the title holds still. Same here: the glyph is
   rendered on every surface and merely made invisible on the cover, so
   the wordmark sits at one x from the cover to a record. */
.fwm::before{content:"\2190";margin-right:9px;font-family:var(--mono);
  font-size:15px;font-weight:400;color:var(--ink3);vertical-align:1px;visibility:hidden}
:is(.app.indeck,.deckroot) .fwm::before,
body:not(.feedhome) .fwm::before{visibility:visible}
.hbtn{font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink3);border:1px solid var(--ln2);background:transparent;padding:7px 9px;cursor:pointer;text-decoration:none}
.hbtn:hover{border-color:var(--ink);color:var(--ink)}
/* THE CHROME ALIGNS TO THE PAGE'S OWN COLUMN, whichever that is (QA,
   2026-09-07). Standardizing the header onto every page introduced the
   exact defect the owner reported that morning about the deck — "still
   seeing 'back' and the record way out on the width border" — on a new set
   of pages. Measured at 1440 on a post page: .fhead spanned the full 1440
   with the wordmark at 16px while the article's content started at 112,
   misaligned by 96px, and .ffoot had NO padding at all because its only
   rule was scoped to the v2 containers.
   Three columns exist on this site and the chrome now matches each:
     v1-templated pages  .in, max-width var(--wrap) + var(--pad)  -> 112 @1440
     the cover / a deck  the 760px shell, 16px inset
     inside a deck       .pgin's 620px reading column
   Base rule is the .in column with a 16px floor, so nothing moves below
   the width where --wrap binds. */
.fhead .r1{display:flex;align-items:center;gap:10px;
  padding:9px max(16px, calc((100% - var(--wrap)) / 2 + var(--pad)))}
/* A v2 shell IS already a 760px column; its chrome sits at the shell's own
   inset, matching the cover's text. */
:is(.app,.deckroot) .fhead .r1{padding-inline:16px}
/* ---- SEARCH: AN ICON THAT BECOMES A FIELD ----
   The row is three controls wide on a 375px phone and the field was
   spending half of it on a placeholder. Collapsed it is a 34px button;
   .fhead.searching gives it the row and stands the channel chip down,
   because expanded field + chip + wordmark do not fit a phone and
   squeezing all three is how the field got small enough to be useless.
   The input stays in the DOM at every state — it is a real form, it still
   submits without JS, and collapsing is a width change rather than a
   teardown, so a half-typed query survives an accidental collapse. */
.qform{flex:none;margin:0;display:flex;align-items:center;min-width:0;
  transition:flex-grow 140ms var(--ease-card,ease)}
.qbtn{flex:none;width:34px;height:32px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--ln2);background:transparent;color:var(--ink2);cursor:pointer;padding:0}
.qbtn:hover{border-color:var(--ink);color:var(--ink)}
.qform input{width:0;flex:1;min-width:0;padding:0;border:0;background:var(--el);color:var(--ink);
  font-family:var(--mono);font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  opacity:0;pointer-events:none;transition:opacity 120ms linear}
.qform input::placeholder{color:var(--ink3);text-transform:uppercase}
.qform input:focus{outline:none}
.fhead.searching .qform{flex:1 1 auto}
.fhead.searching .qbtn{border-right:0}
.fhead.searching .qform input{width:auto;opacity:1;pointer-events:auto;padding:8px 10px;
  border:1px solid var(--ln2);border-left:0}
.fhead.searching .chbtn{display:none}
@media (prefers-reduced-motion:reduce){.qform,.qform input{transition:none}}
/* CONTROLS TO THE RIGHT EDGE (owner, 2026-09-14: "move the search icon and
   the nav dropdown toward the right edge of the header with space in the
   middle"). The wordmark keeps the left; the search form takes the auto
   margin, so it and the channel chip sit flush right and the expanding
   field still grows into the gap. */
.fhead .r1 .qform{margin-left:auto}
.chbtn{white-space:nowrap;flex:none}
.chsheet{position:absolute;left:0;right:0;top:100%;border-top:1px solid var(--ln);background:var(--el);
  padding:12px 16px 14px;box-shadow:0 14px 28px rgba(19,18,17,.12);z-index:61}
.chsheet .chips{display:flex;flex-direction:column;gap:2px}
.chip{display:flex;width:100%;justify-content:flex-start;font-family:var(--mono);font-size:10.5px;
  font-weight:600;letter-spacing:.16em;text-transform:uppercase;padding:11px 10px;border:0;
  border-bottom:1px solid var(--ln);color:var(--ink2);text-decoration:none}
.chip.on{background:var(--el2);color:var(--ink)}
.chip .dot{display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--cc,var(--ink3));
  margin-right:6px;align-self:center}
.chx{display:flex;gap:8px;margin-top:12px}

/* ---- the breaking bar (design pass, 2026-09-07) ----
   Measured on the live bar with a real breaking story up: 44 of 82
   headline characters were clipped to one ellipsized line, the page
   counter was overlapping what survived, there was no time at all, and a
   full-bleed saturated red band sat directly above a saturated channel
   plate — two big colour fields fighting for the same screen.

   Four decisions:
   1. THE GROUND IS INK, NOT RED. The theme already speaks in inverted
      ink bands (footer, units, follow bar), and ink does not compete with
      six channel hues. Red stays reserved and therefore precious: it is
      the pip and the word, not the field.
   2. THE HEADLINE NEVER CLIPS. Two lines, clamped — a breaking headline
      is the last text on the site that should be truncated.
   3. THE TIME IS THE NEWS. "Breaking" in red says the class; HH:mm says
      how breaking. v1's strip carried a time and the port dropped it.
      Colour never carries the meaning alone (the word is still there).
   4. THE BAR YIELDS. Its job is to point at something NOT on your screen;
      once you reach that signal the plate's own red frame and chip say it
      twice over, so the bar collapses to a hairline (.seen) and stops
      taxing 4.6% of every full-viewport page for 24 hours. */
.brkbar{flex:none;display:grid;grid-template-columns:auto 1fr;gap:5px 12px;align-items:baseline;
  padding:9px 15px 11px;background:var(--brk-bg);color:var(--brk-fg);text-decoration:none;
  border-bottom:2px solid var(--brk-rule);
  overflow:hidden;max-height:110px;
  transition:padding var(--t-card) ease,max-height var(--t-card) ease,background var(--t-card) ease}
.brkbar .k{grid-column:1;display:flex;align-items:center;gap:7px;font-family:var(--mono);font-size:8.5px;
  font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--brk-hot)}
.brkbar .k::before{content:"";width:5px;height:5px;border-radius:50%;background:#E8412B;flex:none;
  box-shadow:0 0 0 3px rgba(232,65,43,.28);animation:brkpip 2.4s ease-in-out infinite}
.brkbar .tm{grid-column:2;justify-self:end;font-family:var(--mono);font-size:8.5px;letter-spacing:.12em;
  color:color-mix(in srgb, var(--brk-fg) 52%, transparent);font-variant-numeric:tabular-nums}
.brkbar .h{grid-column:1/-1;font-family:var(--display);font-size:15px;font-weight:700;letter-spacing:-.022em;
  line-height:1.2;color:var(--brk-fg);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
@keyframes brkpip{0%,100%{opacity:1}50%{opacity:.3}}
@media (prefers-reduced-motion:reduce){.brkbar,.brkbar .k::before{transition:none;animation:none}}
/* Yielded: the reader has reached (or tapped through to) the signal the
   bar was pointing at. .instant is the already-read-on-arrival case — the
   bar should render small, not animate down while you watch. */
.brkbar.instant{transition:none}
.brkbar.seen{max-height:4px;padding:0;background:#E8412B}
.brkbar.seen>*{opacity:0}
/* The counter clears the bar. :has() rather than a JS class — the bar's
   presence is a fact about the document, and the overlap was a live
   defect the port introduced by carrying the offset only in the
   prototype. */

/* ---- the pager ---- */
#app.fpager{flex:1;min-height:0;position:relative;overflow-y:auto;
  scroll-snap-type:y mandatory;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;scrollbar-width:none}
#app.fpager::-webkit-scrollbar{width:0}
.pg{height:100%;scroll-snap-align:start;scroll-snap-stop:always;border-bottom:1px solid var(--ln);
  overflow:hidden;position:relative;content-visibility:auto}
.pg[hidden]{display:none}
.pgin{height:100%;display:flex;flex-direction:column;max-width:620px;margin:0 auto;width:100%;
  padding:14px 16px calc(14px + env(safe-area-inset-bottom))}
.pg.card{cursor:pointer;padding:0}
.pg.card:focus-visible{outline:2px solid var(--acc);outline-offset:-2px}
.pg .pgh{margin:0;font-family:var(--display);font-size:24px;font-weight:800;letter-spacing:-.035em;line-height:1.06}
.pg .pgh a{color:var(--ink);text-decoration:none}
.pg .kick{margin-bottom:10px;flex:none}
.pg .plate{aspect-ratio:16/9;flex:none}
/* ---- the watch affordance ----
   A video signal keeps its plate (identity + headline) and gains a play
   control; playing swaps the plate for the iframe in place. The control
   is a real <button> so the page-wide record tap leaves it alone. */
.cardlink.haswatch{position:relative}
.playbtn{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:4;
  display:inline-flex;align-items:center;gap:9px;padding:11px 16px 11px 13px;cursor:pointer;
  background:rgba(10,10,12,.72);border:1px solid rgba(255,255,255,.55);color:#fff;
  backdrop-filter:blur(3px);font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.18em;text-transform:uppercase}
.playbtn:hover{background:rgba(10,10,12,.88);border-color:#fff}
.playbtn i{width:0;height:0;border-left:11px solid #fff;border-top:7px solid transparent;
  border-bottom:7px solid transparent;flex:none}
.pg .watch{margin:0;flex:none}
.pg .watch iframe{display:block;width:100%;aspect-ratio:16/9;height:auto;border:0;background:#000}
/* The chart is DATA, so it is never cropped: it keeps its own aspect ratio
   inside the space the plate had, and the caption is the publication's own
   words. A chart cropped to 16/9 is a chart with its axis cut off. */
.chartbtn b{width:13px;height:13px;flex:none;position:relative}
.chartbtn b::before,.chartbtn b::after{content:"";position:absolute;bottom:0;width:3px;background:#fff}
.chartbtn b::before{left:1px;height:7px}
.chartbtn b::after{left:6px;height:13px}
.pg .chartfig{display:flex;flex-direction:column;gap:7px;min-height:0;overflow:hidden}
.pg .chartfig img{display:block;width:100%;height:auto;max-height:46vh;object-fit:contain;
  background:var(--el2);border:1px solid var(--ln)}
.pg .chartfig figcaption{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink3);line-height:1.5}
.pg .chartfig.failed{border:1px dashed var(--ln2);padding:18px;align-items:center;justify-content:center}
.pg .chartfig.failed p{margin:0;font-family:var(--mono);font-size:9px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink3)}
@media (max-height:700px){.pg .plate{aspect-ratio:16/8}}
/* WHICH TEXT MAY BE CLAMPED, AND WHY (2026-09-07). Both of these had
   fixed clamps and both were being cut mid-line once the breaking bar
   took 75px off the page. They are not equal, though: the dek is
   reproduced in full inside the Record's lead card, while the why was
   cut from the Record on 2026-09-01 and exists ONLY here. So the dek is
   the single flexible item — it absorbs all the slack, and feed.js sets
   its clamp from the space actually available (--dl) so the cut always
   lands on a line boundary instead of through a word. */
.pg .dek{font-family:var(--body);font-size:16.5px;line-height:1.47;color:var(--ink2);margin:14px 0 0;
  /* SHRINK, NEVER GROW. flex-grow:1 made the dek's BOX taller than its
     text once the clamp was computed, opening a dead gap between the dek
     and the why (owner screenshot, 2026-09-07). The clamp already fits
     the space; the box must not claim more than its lines. */
  flex:0 1 auto;min-height:0;overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:var(--dl, 5);-webkit-box-orient:vertical}
.pg .dev{flex:none;margin:0 0 10px}
/* The why is NEVER truncated — it has no full copy anywhere else. */
.pg .wim{flex:none;margin-top:13px}
.pg .cred{flex:none}
.pgfoot{margin-top:auto;flex:none;display:flex;gap:8px;align-items:stretch;padding-top:14px}
.pgfoot .orig{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;padding:12px 13px;
  font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;
  border:1px solid var(--acc);color:var(--acc);text-decoration:none;text-align:center}
.pgfoot .orig:hover{background:var(--acc-t)}
.pgfoot .orig.dis{border-color:var(--ln2);color:var(--ink3);opacity:.55}
.pgfoot .shr{flex:none;display:flex;align-items:center;justify-content:center;padding:11px 15px;
  border:1px solid var(--ln2);background:transparent;cursor:pointer;color:var(--ink3)}
.pg--fin{display:flex;flex-direction:column;justify-content:flex-end;overflow-y:auto}
.pgcount{position:fixed;right:10px;top:calc(60px + var(--brk-h, 0px) + 8px);z-index:40;pointer-events:none;
  font-family:var(--mono);font-size:8.5px;letter-spacing:.12em;color:var(--ink3);
  background:var(--paper);border:1px solid var(--ln);padding:4px 7px}
.reccue{position:absolute;right:0;top:14px;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.18em;text-transform:uppercase;color:var(--ink3)}

/* ---- the Feed's page/card grammar, scoped to v2 contexts only ---- */
:is(.app,.deckroot,.recwrap,.recdoc) .card h3.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
:is(.app,.deckroot,.recwrap,.recdoc) .cardlink{display:block;text-decoration:none;color:inherit}
:is(.app,.deckroot,.recwrap,.recdoc) .card .tier.brk{color:#E8412B;border-color:#E8412B}
:is(.app,.deckroot,.recwrap,.recdoc) .card .plate .brk-x::after{content:none}
.recwrap .card,.recdoc .card{display:block;border-bottom:0;padding:0 0 8px}
.recwrap .card .plate,.recdoc .card .plate{aspect-ratio:16/9}
.recwrap .card .dek,.recdoc .card .dek{font-family:var(--body);font-size:16.5px;line-height:1.47;color:var(--ink2);margin:14px 0 0}
:is(.app,.deckroot,.recwrap,.recdoc) .wim{padding:12px 14px;border-left:3px solid var(--acc);background:var(--el2)}
:is(.app,.deckroot,.recwrap,.recdoc) .wim .lb{display:block;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--acc);margin-bottom:5px}
:is(.app,.deckroot,.recwrap,.recdoc) .wim p{margin:0;font-family:var(--body);font-size:15.5px;line-height:1.45;color:var(--ink)}
:is(.app,.deckroot,.recwrap,.recdoc) .cred{margin:16px 0 0;padding-top:13px;border-top:1px solid var(--ln);position:relative}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .lb{display:block;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink3);margin-bottom:7px}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .ln{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .who{font-family:var(--display);font-size:15px;font-weight:700;
  letter-spacing:-.018em;color:var(--ink);border-bottom:1.5px solid var(--acc);padding-bottom:1px;text-decoration:none}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .at{font-family:var(--mono);font-size:10px;letter-spacing:.05em;color:var(--ink2);text-decoration:none}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .more{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink3)}
:is(.app,.deckroot,.recwrap,.recdoc) .cred .ext i{font-style:normal;font-size:.85em;margin-left:4px;opacity:.65}
:is(.app,.deckroot,.recwrap,.recdoc) .dev{display:flex;align-items:center;gap:9px;font-family:var(--mono);font-size:9px;
  font-weight:600;letter-spacing:.18em;text-transform:uppercase;color:var(--acc)}
:is(.app,.deckroot,.recwrap,.recdoc) .dev .pip{width:6px;height:6px;border-radius:50%;background:var(--acc);flex:none}
:is(.recwrap,.recdoc) .acts{display:grid;grid-template-columns:1fr auto;gap:8px;margin-top:14px}
:is(.recwrap,.recdoc) .acts>*{display:flex;align-items:center;justify-content:center;gap:7px;padding:12px 13px;
  font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;
  border:1px solid var(--ln2);background:transparent;cursor:pointer;color:var(--ink3);text-decoration:none}
:is(.recwrap,.recdoc) .acts .orig{border-color:var(--acc);color:var(--acc)}
:is(.recwrap,.recdoc) .rcpb{margin:12px 0 0;padding:14px;background:var(--el2);border-left:2px solid var(--ln2)}
:is(.recwrap,.recdoc) .rgh{display:flex;width:100%;align-items:baseline;gap:10px;padding:11px 0;border:0;
  background:transparent;cursor:pointer;text-align:left;font-family:var(--mono);font-size:9.5px;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ink2)}
:is(.recwrap,.recdoc) .rgh .ct{margin-left:auto;font-weight:400;letter-spacing:.08em;color:var(--ink3)}
:is(.recwrap,.recdoc) .rgh .pm{flex:none;width:11px;text-align:center;color:var(--acc)}
:is(.recwrap,.recdoc) .rgrp>.trail{display:none;padding:0 0 13px}
:is(.recwrap,.recdoc) .rgrp.on>.trail{display:grid}
:is(.recwrap,.recdoc) .trail{list-style:none;margin:0;padding:0;display:grid;gap:11px}
:is(.recwrap,.recdoc) .trail li{display:grid;grid-template-columns:auto 1fr;gap:10px;align-items:start}
:is(.recwrap,.recdoc) .trail .ty{font-family:var(--mono);font-size:8.5px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;padding:3px 6px;border:1px solid var(--ln2);color:var(--ink3);white-space:nowrap}
:is(.recwrap,.recdoc) .trail .ty.f{color:var(--acc);border-color:var(--acc)}
:is(.recwrap,.recdoc) .trail .no{font-family:var(--body);font-size:14.5px;line-height:1.35;color:var(--ink2)}
:is(.recwrap,.recdoc) .trail .no i{font-style:normal;display:block;font-family:var(--mono);font-size:9px;
  letter-spacing:.1em;color:var(--ink3);margin-top:4px}
:is(.recwrap,.recdoc) .trail .no a{color:var(--ink);text-decoration:none}
:is(.recwrap,.recdoc) .trail li.roll{grid-template-columns:1fr;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--acc)}
:is(.recwrap,.recdoc) .quote{margin:14px 0 0;padding:12px 14px;background:var(--paper);border-left:2px solid var(--acc)}
:is(.recwrap,.recdoc) .quote p{margin:0;font-family:var(--body);font-style:italic;font-size:14.5px;line-height:1.5;color:var(--ink)}
:is(.recwrap,.recdoc) .quote .h{font-family:var(--mono);font-style:normal;font-size:9px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink3);margin-bottom:8px}
:is(.recwrap,.recdoc) .quote .cite{display:block;margin-top:9px;font-family:var(--mono);font-size:9px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink3);font-style:normal}
:is(.recwrap,.recdoc) .method{margin:13px 0 0;font-family:var(--body);font-size:14px;line-height:1.5;color:var(--ink2)}
:is(.recwrap,.recdoc) .inst{list-style:none;margin:18px 0 0;padding:13px 0 0;border-top:1px solid var(--ln)}
:is(.recwrap,.recdoc) .inst .h{font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink3);margin:0 0 9px}
:is(.recwrap,.recdoc) .inst li{display:grid;grid-template-columns:56px 1fr;gap:12px;padding:10px 0;border-top:1px solid var(--ln)}
:is(.recwrap,.recdoc) .inst li:first-of-type{border-top:0}
:is(.recwrap,.recdoc) .inst time{font-family:var(--mono);font-size:9.5px;color:var(--ink3);padding-top:2px}
:is(.recwrap,.recdoc) .inst h4{font-family:var(--display);font-size:14.5px;font-weight:600;letter-spacing:-.018em;
  line-height:1.25;margin:0;color:var(--ink2)}

/* ---- the record layer ---- */
.recwrap{position:fixed;inset:0;z-index:90;background:var(--paper);color:var(--ink);
  overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  transform:translateX(103%);transition:transform var(--t-layer) var(--ease-card);
  box-shadow:-18px 0 44px rgba(19,18,17,.28)}
.recwrap[hidden]{display:none}
.recwrap.on{transform:none}
/* The bar aligns to .recbody's 680px column, the same way the deck's
   chrome aligns to .pgin's 620 (owner, 2026-09-07: "still seeing 'back'
   and the record way out on the width border"). Its own padding is 18px,
   so that is the floor. Padding rather than max-width, so the bar's rule
   and sticky background stay full-bleed while its contents move. */
.rectop{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:10px;
  padding-block:8px;padding-inline:max(14px, calc((100% - 680px) / 2 + 18px));
  background:var(--paper);border-bottom:2px solid var(--ink)}
.recback{display:inline-flex;align-items:center;gap:8px;margin-left:-8px;padding:10px 12px;border:0;
  background:none;cursor:pointer;font-family:var(--mono);font-size:10px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink)}
.rectop .rtl{margin-left:auto;font-family:var(--mono);font-size:9px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink3)}
/* Share sits at the far edge, opposite Back — the two things you can do
   with a record, at the two ends of its own bar. */
.rectop .rtl{margin-left:auto}
.recshr{display:inline-flex;align-items:center;gap:7px;margin-left:14px;padding:8px 11px;
  border:1px solid var(--ln2);background:transparent;cursor:pointer;color:var(--ink2);
  font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.14em;text-transform:uppercase}
.recshr:hover{border-color:var(--ink);color:var(--ink)}
/* The confirmation a silent clipboard write never gave. */
.recshr.copied{border-color:var(--acc);color:var(--acc)}
.recshr.copied .rsl::after{content:" \2713"}
.pgfoot .shr.copied{border-color:var(--acc);color:var(--acc)}
@media (max-width:400px){.recshr .rsl{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}}
.recbody{max-width:680px;margin:0 auto;padding:6px 18px calc(40px + env(safe-area-inset-bottom))}
@media (prefers-reduced-motion:reduce){.recwrap{transition:none}}

/* ---- the record's document (layer AND story tag pages) ---- */
.recdoc{max-width:680px;margin:0 auto;padding:6px 18px 40px}
.story{padding:4px 0 10px}
.storyhd{border-bottom:2px solid var(--ink);padding-bottom:14px;margin-bottom:16px}
.storyhd .sr{display:flex;align-items:center;gap:8px;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--cc,var(--ink3));margin-bottom:9px}
.storyhd .sr span{width:6px;height:6px;border-radius:50%;background:var(--cc,var(--ink3))}
.storyhd h1{font-family:var(--display);font-size:28px;font-weight:900;letter-spacing:-.042em;line-height:.98;
  margin:0 0 10px;color:var(--ink);text-wrap:balance}
.storyhd .comp{font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--ink3)}
.storyhd .comp b{color:var(--ink);font-weight:600}
.edcov{border-left:3px solid var(--acc);background:var(--el2);padding:16px 18px;margin:18px 0 22px}
.edcov .lb{font-family:var(--mono);font-size:9px;font-weight:600;letter-spacing:.2em;text-transform:uppercase;
  color:var(--acc);margin-bottom:10px}
.edcov .claim{font-family:var(--display);font-size:19px;font-weight:700;letter-spacing:-.028em;line-height:1.24;
  color:var(--ink);margin:0 0 12px;text-wrap:balance}
.edcov p{font-family:var(--body);font-size:16px;line-height:1.52;color:var(--ink2);margin:0 0 11px}
.edcov p:last-of-type{margin-bottom:0}
.edcov .rev{font-family:var(--mono);font-size:8.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink3);margin-top:12px}
.edcov.empty{border:1px dashed var(--ln2);border-left:1px dashed var(--ln2);background:transparent;padding:15px 17px}
.edcov.empty .lb{color:var(--ink3)}
.edcov.empty p{font-size:15px;color:var(--ink3)}
.tl .tlh{display:flex;align-items:baseline;gap:10px;border-bottom:2px solid var(--ink);padding-bottom:8px}
.tl .tlh h2{font-family:var(--display);font-size:14px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;margin:0;color:var(--ink)}
.tl .tlh .ct{margin-left:auto;font-family:var(--mono);font-size:9px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
.bd{border-bottom:1px solid var(--ln);padding:14px 0}
.bd:last-child{border-bottom:0}
.bd.origin{background:var(--el2);margin:0 -14px;padding:14px}
.bsig{display:grid;grid-template-columns:58px 1fr;gap:12px;align-items:baseline}
.bsig .dt{font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--ink3)}
.bsig .hd{font-family:var(--display);font-size:16px;font-weight:700;letter-spacing:-.022em;line-height:1.24;color:var(--ink);text-decoration:none}
.brep{list-style:none;margin:8px 0 0;padding:0 0 0 70px}
.brep li{display:grid;grid-template-columns:50px 1fr;gap:10px;padding:5px 0}
.brep .dt{font-family:var(--mono);font-size:8.5px;color:var(--ink3)}
.brep .rp{font-family:var(--body);font-size:14px;line-height:1.36;color:var(--ink2)}
.brep .rp s{text-decoration:none;font-family:var(--mono);font-size:8.5px;color:var(--ink3);margin-left:7px}
.brep .rp em{font-style:normal;font-family:var(--mono);font-size:8px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink);margin-right:7px}
@media(max-width:560px){.brep{padding-left:0}.bsig{grid-template-columns:1fr;gap:3px}}
.storymast{padding:20px 0 18px;border-bottom:2px solid var(--ink)}
.storymast h1{font-family:var(--display);font-size:32px;font-weight:900;letter-spacing:-.042em;line-height:.98;
  margin:12px 0 0;color:var(--ink)}
.storymast .repnote{margin-top:12px}

/* ---- search results (in the record layer) ---- */
.qbar{display:flex;align-items:center;gap:10px;border:1px solid var(--ln2);background:var(--el);
  padding:11px 13px;margin:16px 0}
.qbar span{font-family:var(--body);font-size:16px;color:var(--ink)}
.qbar em{font-style:normal;margin-left:auto;font-family:var(--mono);font-size:9px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink3)}
.recwrap .res{display:block;border-bottom:1px solid var(--ln);padding:15px 0;text-decoration:none}
.recwrap .res .k{font-family:var(--mono);font-size:8.5px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;margin-bottom:6px}
.recwrap .res h4{font-family:var(--display);font-size:18px;font-weight:800;letter-spacing:-.028em;line-height:1.18;margin:0 0 6px;color:var(--ink)}
.recwrap .res p{font-family:var(--body);font-size:14.5px;line-height:1.42;color:var(--ink2);margin:0}

/* ---- units: full-page dark interstitials ---- */
.pg--unit{background:#131211;border-bottom:1px solid rgba(255,255,255,.14)}
.pg--unit .pgin{justify-content:center}
.u{color:#F7F5F0;--uc:#E8412B}
.u .k{display:flex;align-items:center;gap:7px;font-family:var(--mono);font-size:8.5px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:rgba(255,255,255,.5);margin-bottom:14px}
.u .k::before{content:"";width:5px;height:5px;background:var(--uc);flex:none}
.u .k .fwm-u{font-family:var(--display);font-size:12px;font-weight:800;letter-spacing:-.03em;text-transform:none;color:#fff}
.u .k .fwm-u span{color:#7D93FF}
.u .k .fwm-u i{font-style:normal;font-weight:400;color:rgba(255,255,255,.66)}
.u p{font-family:var(--body);font-size:16px;line-height:1.46;color:rgba(247,245,240,.82);margin:0 0 14px}
.u .unm{font-family:var(--display);font-weight:900;font-size:42px;letter-spacing:-.045em;line-height:.92;color:#fff;margin:0 0 12px}
.u .ulogo{display:block;margin:0 0 12px;max-width:170px}
.u .ulogo-tvrev{width:118px;height:20px;background:#fff;
  -webkit-mask:url(../img/tvrev-logo.svg) no-repeat left center/contain;
  mask:url(../img/tvrev-logo.svg) no-repeat left center/contain}
.u .ugo{display:inline-block;margin-top:12px;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--uc);text-decoration:none}
.u-net{--uc:#4FC3A1} .u-measure{--uc:#53C6DE} .u-ispot{--uc:#84BD00} .u-analyst{--uc:#A9BAFF} .u-signal{--uc:#7D93FF}
.u-analyst blockquote{font-family:var(--body);font-size:21px;line-height:1.32;font-style:italic;color:#fff;margin:0 0 16px}
.u-analyst .who{display:flex;align-items:center;gap:11px;padding-top:14px;border-top:1px solid rgba(255,255,255,.14);text-decoration:none}
.u-analyst .who .av{width:38px;height:38px;border-radius:50%;overflow:hidden;flex:none}
.u-analyst .who .av img{width:100%;height:100%;object-fit:cover;display:block}
.u-analyst .who .wi{display:flex;flex-direction:column;gap:2px}
.u-analyst .who .n{font-family:var(--display);font-size:14px;font-weight:700;letter-spacing:-.02em;color:#fff}
.u-analyst .who .ro{font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.5)}
.u-analyst .who .ugo{margin:0 0 0 auto}

/* ---- the final page ---- */
:is(.app,.deckroot) .endcap{padding:30px 16px 40px;text-align:center;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink3)}
:is(.app,.deckroot) .endcap b{display:block;font-family:var(--display);font-size:17px;font-weight:700;
  letter-spacing:-.02em;text-transform:none;color:var(--ink2);margin-bottom:8px}
.ffoot{background:#131211;color:#F7F5F0;
  padding:30px max(18px, calc((100% - var(--wrap)) / 2 + var(--pad))) calc(34px + env(safe-area-inset-bottom))}
:is(.app,.deckroot) .ffoot{padding-inline:18px}
.ffoot .fbrand{font-family:var(--display);font-size:23px;font-weight:800;letter-spacing:-.042em;color:#fff}
.ffoot .fbrand span{color:#7D93FF}
.ffoot .fbrand i{font-style:normal;font-weight:400;color:rgba(255,255,255,.66)}
.ffoot .ftag{font-family:var(--body);font-size:15px;line-height:1.46;color:rgba(247,245,240,.72);margin:9px 0 22px;max-width:44ch}
.ffoot .fcols{display:grid;grid-template-columns:1fr 1fr;gap:22px}
.ffoot .fcol{display:flex;flex-direction:column;gap:8px}
.ffoot .fcol h5{margin:0 0 3px;font-family:var(--mono);font-size:8.5px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:rgba(255,255,255,.45)}
.ffoot .fcol a{font-family:var(--body);font-size:15px;color:rgba(247,245,240,.9);text-decoration:none}

/* ===================== LEVEL 1: THE COVER =====================
   Scrolls; never pages. Read off Flipboard's For You: a hero card, two
   squares, then the sections grid, under ordinary vertical scrolling.
   Full-viewport paging starts inside a feed, not at the front door. */
.cover{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;padding-bottom:calc(30px + env(safe-area-inset-bottom))}
.cover[hidden]{display:none}
.hero{display:block;text-decoration:none;color:inherit;cursor:pointer;padding:0 0 4px}
/* The heading is in the DOM for crawlers and for a reader without JS; the
   plate carries it visually once dressed. Same pattern the pager uses. */
.cover h2.vh,.cover h3.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.hero .plate{aspect-ratio:16/10}
.hero .dek{font-family:var(--body);font-size:16.5px;line-height:1.45;color:var(--ink2);
  margin:13px 0 0;padding:0 16px}
/* THE ITEM FOOT: outlet left, record door right. One row, because that is
   the row a signal page already makes of the same two facts — the credit
   line and its "The Record →" whisper. The cover is where a reader learns
   the page's grammar, so it uses the page's. */
:is(.hero,.feature) .itemfoot{display:flex;align-items:baseline;gap:12px;padding:11px 16px 0}
:is(.hero,.feature) .itemfoot .cue{margin-left:auto;padding:0}
:is(.hero,.feature) .src{font-family:var(--mono);font-size:9px;letter-spacing:.1em;color:var(--ink3);
  min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
:is(.hero,.feature) .src:empty{display:none}
.hero .cue{display:block;padding:11px 16px 0;font-family:var(--mono);font-size:9px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--acc)}
/* Two squares, side by side — the shape the reference uses for the pair
   under the hero, and the reason the cover reads as an issue rather than
   a list: three items of two different weights. */
.sqs{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--ln);
  margin-top:20px;border-top:1px solid var(--ln);border-bottom:1px solid var(--ln)}
.sq{background:var(--paper);padding:0 0 12px;text-decoration:none;color:inherit;cursor:pointer;display:block}
.sq .plate{aspect-ratio:1/1}
/* ---- THE CHROME ALIGNS TO THE READING COLUMN, IN A DECK ----
   (owner, 2026-09-07: "maybe the top nav on the signal pages should align
   with the column width, not the browser width")
   Measured: the header row is inset 16px from the shell while .pgin caps
   at 620px centred inside 760, so a card's content starts 70px further in
   and the wordmark hangs 54px to its left. The inset below is exactly
   .pgin's own: (shell - 620)/2 + 16, floored at 16 so nothing changes
   below 652px, where the column already fills the shell.

   ONLY IN A DECK, and that is the whole subtlety. The COVER's text sits at
   16px — its plates are full-bleed to the shell — so the header cannot
   align to both surfaces at once. .indeck (set by showDeck) and .deckroot
   are exactly the two states where a reading column exists to align to.

   Padding rather than max-width, so the header's rule and the bar's band
   stay full-bleed while their contents move. */
:is(.app.indeck,.deckroot) .fhead .r1,
:is(.app.indeck,.deckroot) .dbar{
  padding-inline:max(16px, calc((100% - 620px) / 2 + 16px));
}

/* THE COVER'S PICTURES ARE CAPPED BY VIEWPORT HEIGHT (QA, 2026-09-07).
   Measured at 1440x900: the cover ran 1763px — 1.96 screens — and all six
   channel tiles sat 224px BELOW the fold, which is the one thing the
   rejected desktop grid had fixed. The arithmetic says why: hero 569 +
   squares 420 = 989px of a 900px viewport before the channels even start.

   The cause is that both plates are sized by ASPECT RATIO off their width
   while the constraint is height, so a wide, short window gets tall
   pictures. So they get a height cap as well. This is NOT the reflow the
   owner locked out: same ranks, same order, same composition, and the
   theme already does exactly this for pager plates at
   @media (max-height:700px).

   IT IS INERT ON A PHONE, which is the point. At 375x812 the ratio gives a
   234px hero plate and 40svh is 325 — the cap never binds, so the cover
   the owner approved is untouched. It only bites where the window is wide
   relative to its height, which is where the problem was.
   The headline is unaffected: dressPlate sizes it in cqw, which is WIDTH,
   and the bottom-anchored block still clears the shorter frame (measured
   227px of type in a 378px plate). */
.hero .plate{max-height:40svh}
.sq .plate{max-height:26svh}

/* The record's headline is a link now (see singleRecordHTML) — it is the
   way back up to the signal from a cold share link. It must read as the
   headline it is, not as a link, until you reach for it. */
:is(.recwrap,.recdoc) .storyhd h1 a.hd{color:inherit;text-decoration:none}
:is(.recwrap,.recdoc) .storyhd h1 a.hd:hover{text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:3px}
:is(.recwrap,.recdoc) .storyhd .dek{margin:10px 0 0;font-family:var(--body);font-size:16.5px;
  line-height:1.45;color:var(--ink2)}

/* THE ARTICLE HERO'S HEADLINE, BELOW 560px ONLY. dressPlate sizes every
   headline at 5.33% of its plate's width, which on a 311px phone plate is
   16.6px — the number the retired width floor was protecting readers from.
   Above 560px its own ratio is right and is left alone; this is scoped to
   exactly the case that was broken. 7.3cqw puts a phone hero at ~23px, the
   same weight the pager's cards carry, so the page, the card and the share
   image finally show one composition. */
@media (max-width:560px){
  .art-head .plate.dressed .cardhd{font-size:7.3cqw!important;line-height:1.05}
}

/* ---- THE SOURCE MARK ----
   The monogram IS the chip and the icon covers it: the letter is the
   element's own text, the <img> is absolutely positioned over it, and
   feed.js gives that img onerror="this.remove()". So an outlet with no
   file (7 of 73, tubefilter.com among them) shows a letter rather than a
   broken image, and the asset set is free to lag the catalogue.
   Scoped nowhere, and no longer v2-only: since 2026-09-13 signal.js draws
   it as well, so it renders on the cover, in the deck, in the record's
   trail AND on the editorial article page's credit row and timeline. Its
   size is set once here and never per surface. */
.smk{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:17px;height:17px;flex:none;margin-right:7px;border-radius:4px;
  background:var(--srcchip);border:1px solid var(--srcchip-ln);box-sizing:border-box;
  font-family:var(--display);font-size:10px;font-weight:800;line-height:1;color:#131211;
  overflow:hidden;vertical-align:-4px}
.smk img{position:absolute;inset:0;width:100%;height:100%;padding:2px;
  object-fit:contain;box-sizing:border-box;background:var(--srcchip)}
/* The credit row and the trail lay their rows out as flex/grid lines; the
   mark must not stretch or push the baseline around in either. */
:is(.app,.deckroot,.recwrap,.recdoc) .cred .ln{display:flex;align-items:center;flex-wrap:wrap}
:is(.app,.deckroot,.recwrap,.recdoc) .trail .no{display:flex;align-items:center;min-width:0}
.sq .src .sw,:is(.hero,.feature) .src .sw{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* The outlet is a hostname and must read as one: lowercase, not the
   uppercase a channel word wanted. The separator carries the second fact
   at a lower weight so the outlet stays the thing you read first. */
.sq .src{display:block;padding:9px 11px 0;font-family:var(--mono);font-size:8.5px;letter-spacing:.1em;
  color:var(--ink3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sq .src:empty{display:none}
/* THE SMALL PLATES GET THEIR OWN HEADLINE RATIO. dressPlate types a plate
   headline at 5.33% of plate width — right for a 296px card and up, and
   ~10px on a 186px cover square. cqw against the plate's own container
   (the theme already sets container-type on .plate) puts it back in the
   readable range; !important because the fitter writes an inline size. */
/* THE HERO OUTRANKS THE SQUARES IN TYPE, not only in area (owner,
   2026-09-07: "The headline on the hero on the landing page can be bigger
   than the headlines on the two other tiles. Like Flipboard.").
   dressPlate sizes every headline at 5.33% of its plate's width, so the
   hero was only ~14% larger than a square — the squares already carry
   their own 9.4cqw override to fit a 1/1 plate, which had quietly closed
   the gap. At 8cqw against the squares' 9.4cqw OF A HALF-WIDTH CONTAINER,
   the hero reads about 1.7x, and the ratio holds at every width because
   both are container-relative.
   TUNED ON THE PHONE (owner, same day): "needs to be more anchored vs
   vertical center and it can be a bit smaller than the big you made" —
   8cqw down to 7, and the block dropped from the base 12.5cqw bottom to
   8cqw. Fewer lines sitting lower reads as anchored; the same lines
   centred read as floating, which is what four lines of 30px type did to
   a 234px plate. The bottom is free to use: .mk, the only thing that
   lives down there, is SHARE-ONLY (meta.plateCredit) and renders empty on
   the page.
   !important because dressPlate sets the size as an inline style, and an
   important declaration is the only thing that outranks one. That also
   means the JS step-down for a pathological title no longer applies here
   — the same trade the squares have been making since they shipped. */
:is(.hero,.feature) .plate.dressed .cardhd{font-size:7cqw!important;line-height:1.04;bottom:8cqw}
.sq .plate.dressed .cardhd{font-size:9.4cqw!important;line-height:1.06;
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
/* The type title is the no-JS and crawler copy; the dressed plate carries
   it visually, so it steps aside once the plate has it. */
.sq .sqt{display:block;padding:5px 11px 0;font-family:var(--display);font-size:14px;font-weight:700;
  letter-spacing:-.026em;line-height:1.14;color:var(--ink)}
.sq:has(.plate.dressed) .sqt{display:none}

/* ---- the feature (Ghost's own `featured` flag, one click in the editor) ----
   Wide, one example, above the channels: the cover's editorial slot, as
   distinct from the hero (which is simply the newest) and from the squares
   (which are the two behind it). Absent entirely when nothing is starred. */
.feature{display:block;text-decoration:none;color:inherit;cursor:pointer;
  margin-top:24px;padding:0 0 16px;border-top:1px solid var(--ln);border-bottom:1px solid var(--ln);
  background:var(--el)}
.feature .flab{display:block;padding:13px 16px 11px;font-family:var(--mono);font-size:8.5px;
  font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--acc)}
.feature .plate{aspect-ratio:16/9}
.feature .ftitle{display:block;padding:14px 16px 0;font-family:var(--display);font-size:22px;
  font-weight:800;letter-spacing:-.036em;line-height:1.08;color:var(--ink)}
.feature:has(.plate.dressed) .ftitle{display:none}
.feature .fdek{margin:11px 0 0;padding:0 16px;font-family:var(--body);font-size:15.5px;
  line-height:1.45;color:var(--ink2)}
/* THE FOOTER'S CHILDREN ARE UNSCOPED TOO (owner, 2026-09-09: "this footer
   is a mess too" — a story page, links running together in the system
   sans with no columns).
   Same bug as the header, one level down and shipped by the same hand: the
   chrome standardization unscoped .ffoot itself so the dark band appeared
   everywhere, and left every rule INSIDE it under :is(.app,.deckroot). So
   on any page without a v2 container — a story record, a post, a dossier,
   About — the footer had its background and none of its typography or
   layout. Container styled, contents naked.
   Safe for the same measured reason as the header: .fbrand, .ftag, .fcols,
   .fcol and .flg are v2-unique, zero hits each in v1's signal.css. Only
   .ffoot's own padding stays per-context, because that IS per-context —
   a v2 shell is already a 760px column.

   The legal bar closes the footer — copyright and cadence, the same two
   facts v1's .lg carries. Sits under the columns at a lower voltage. */
.ffoot .flg{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:26px;
  padding-top:16px;border-top:1px solid rgba(247,245,240,.14);
  font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;
  color:rgba(247,245,240,.42)}

.covh{display:flex;align-items:baseline;gap:9px;padding:26px 16px 11px}
.covh h2{margin:0;font-family:var(--display);font-size:26px;font-weight:900;letter-spacing:-.042em;color:var(--ink)}
.covh .ct{margin-left:auto;font-family:var(--mono);font-size:9px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink3)}
.chgrid{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--ln);
  border-top:1px solid var(--ln);border-bottom:1px solid var(--ln)}
.chtile{background:var(--paper);padding:13px 12px 15px;text-decoration:none;display:flex;
  flex-direction:column;gap:6px;border-left:3px solid var(--cc,var(--acc));cursor:pointer}
.chtile:hover{background:var(--el)}
.chtile .n{font-family:var(--mono);font-size:8.5px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--cc,var(--acc))}
.chtile .nm{font-family:var(--display);font-size:17px;font-weight:800;letter-spacing:-.032em;color:var(--ink)}
.chtile .lt{font-family:var(--body);font-size:12.5px;line-height:1.32;color:var(--ink2);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ch-measurement{--cc:#4FC3A1} .ch-streaming{--cc:#A48CFF} .ch-creators{--cc:#F27BB0}
.ch-ad-tech{--cc:#7D93FF} .ch-retail-media{--cc:#53C6DE} .ch-agencies{--cc:#E8955A}

/* ============ ONE COMPOSITION AT EVERY WIDTH (owner, 2026-09-07) ======
   The cover does NOT reflow on a wide display. Hero full width, the two
   squares side by side under it, channels two across, the whole thing a
   760px column with the gutters growing as the display does. Phone to 5K,
   one layout.

   THIS REPLACED A DESKTOP GRID THAT SHIPPED AND WAS REMOVED THE SAME
   HOUR, and the reason is worth keeping because the measurements argued
   for the grid. At 1440x900 the column leaves 47% of the screen empty and
   needs 1.2-1.7 screens; a grid (hero left, squares as a rail, channels
   three across) closed the gutter to 22% and brought all six tiles above
   the fold. Better numbers, and the owner still chose the column after
   seeing the real page at ~1010px: the cover is an ISSUE, and an issue
   has one composition. A reader who moves between a phone and a desktop
   should recognise the same object, and a hero that becomes a rail is a
   different object.

   So a desktop change here is a change to the WHOLE cover, at every
   width, or it is not made. If the gutters ever need to earn their keep,
   the honest move is a wider column (raise the cap), never a reflow.
   ==================================================================== */

/* ============ A MOUSE GETS A FEED, A THUMB GETS A PAGER ==============
   (owner, 2026-09-07: "Any way to close up the vertical spacing of the
   cards in a browser so they are more of a feed, contiguous")
   Measured on the deck at 1440x1050 before this: a page 1050px tall
   holding 569px of content, so the card ended a third of the way down
   and "Read the original" was pinned 600px below it by margin-top:auto.
   On a phone that emptiness is the page and the flick fills it; on a
   desktop it is just a hole, and the taller the window the bigger it is.

   So the pager's physics are chosen by INPUT DEVICE, not by width:
     coarse pointer -> full-viewport pages, mandatory snap, snap-stop
                       always. One flick, one signal, nothing skimmed.
     fine pointer   -> cards size to their content and butt up against
                       each other, no snap at all. A wheel scrolls a feed.
   A touchscreen at 1200px keeps the thumb's physics, which is the whole
   reason this is not a width query.

   THE DEK CLAMP COMES OFF WITH THE PAGE HEIGHT. --dl is computed from the
   space a full-viewport page has left over (see fitDeks), and a
   content-height page has no leftover to measure — fitDeks would clamp
   every dek to its floor of 2 lines. Contiguous cards have room to simply
   show the dek, so they do.
   ==================================================================== */
@media (pointer:fine){
  #app.fpager{scroll-snap-type:none}
  #app.fpager .pg{height:auto;scroll-snap-align:none;scroll-snap-stop:normal;
    overflow:visible;content-visibility:visible}
  #app.fpager .pgin{height:auto;padding:18px 16px 20px}
  /* margin-top:auto is what pinned the exits to the bottom of a
     full-viewport page. In a feed they follow the credit row. */
  #app.fpager .pgfoot{margin-top:0;padding-top:16px}
  #app.fpager .dek{display:block;-webkit-line-clamp:unset;overflow:visible}
  /* The plate is the tallest thing on a card and 16/9 at 620px is 349px;
     a feed reads better with a slightly shorter picture, and it keeps
     three cards reachable in a 1050px window. */
  #app.fpager .pg .plate{aspect-ratio:16/8}
}
/* D9, 2026-09-14: A STRAY `}` LIVED HERE AND IT WAS EATING A RULE.
   The file carried one closing brace too many from this point on -- 301
   rules parsed, brace depth -1 -- and Chrome's error recovery swallows the
   NEXT rule after a stray `}` at top level. Measured on the LIVE site
   before the fix: the CSSOM went straight from `@media (pointer:fine)` to
   `body:has(#chdeck) .sg`, and `body:has(#chdeck){height:100%;overflow:
   hidden}` -- the rule that LOCKS THE BODY on a channel deck -- was never
   there at all. Live getComputedStyle(document.body).overflow read
   `visible` on /tag/ad-tech/ at 375px. The deck still worked, because .sg
   takes the height and #app.fpager scrolls inside it, which is why nobody
   saw it; what an unlocked body buys you is the document rubber-banding
   behind the pager, the exact class of iOS defect this shell exists to
   avoid. Found only because the same brace then swallowed a whole @media
   block whole and silently. */

/* ============ LEVEL 2: A CHANNEL TAG PAGE IS A DECK ============
   tag.hbs still extends the v1 layout — one template serves channels,
   reporters and story records, and duplicating default.hbs to give the
   deck its own shell would buy two files that drift. Instead the deck
   marks itself with #chdeck and takes the viewport over, hiding the v1
   chrome around it. :has() is doing real work here: the deck's presence
   is a fact about the document, not a class someone has to remember to
   add. Reporter and story pages carry no #chdeck, so they keep the v1
   shell untouched. */
/* D9, 2026-09-14: THE TAKEOVER IS A PHONE IDEA AND IS NOW GATED AS ONE.
   Ungated, it was the reason the six channel pages -- the SECTIONS of the
   site -- were still the app at 1440px while every other template had
   returned to v1. Below 1024px every rule in here is byte-for-byte what it
   was; above it the document scrolls and v1's shell is simply left alone.
   .deckroot is hidden in the min-width block further down; hiding it here
   instead would need a second query saying the same thing twice. */
@media (max-width:1023.98px){
body:has(#chdeck){height:100%;overflow:hidden}
body:has(#chdeck) .sg{height:100vh;height:100svh;height:var(--shellh,100svh);display:flex;flex-direction:column}
/* THE RECORD LAYER IS THE EXCEPTION, and leaving it out cost the record
   entirely on every channel deck (owner, 2026-09-07: "can't seem to get to
   the record in the browser from here:
   /tag/streaming/#/s/story-netflix-prime-video-international-content-...").
   This rule exists so a channel deck can take the viewport from v1's
   chrome — the header, the v1 river, the footer that default.hbs renders
   around it. But the record mounts as a DIRECT CHILD of .sg (it has to:
   outside .sg it paints var(--paper) as invalid, which is the iOS
   transparent-sheet incident), so it was collected as v1 chrome and
   hidden. It opened, built its body, took the .on class, and was
   display:none the whole time — which is why the symptom was "nothing
   happens" rather than an error.
   The record worked from the cover throughout, because the cover document
   has no #chdeck and this rule never applied there. That asymmetry is why
   a whole surface can be dead while every test and every check passes. */
body:has(#chdeck) .sg>*:not(.deckroot):not(.recwrap){display:none}
}
.deckroot{flex:1;min-height:0;display:flex;flex-direction:column;width:100%;max-width:760px;
  margin:0 auto;background:var(--paper)}
.chmast{padding:2px 0 16px}
.chmast h1{margin:11px 0 0;font-family:var(--display);font-size:34px;font-weight:900;
  letter-spacing:-.045em;line-height:.96;color:var(--ink)}
.chmast .std{margin:11px 0 0;font-family:var(--body);font-size:15.5px;line-height:1.5;color:var(--ink2)}
/* The claim: the brief's own first sentence, shown only while it is
   closed. Body face rather than display — it is a sentence the desk
   wrote, not a headline someone set — and it sits under the lockup's
   own hairline so the block still reads as one object. */
.sopclaim{margin:12px 0 0;padding-top:12px;border-top:1px solid var(--ln);
  font-family:var(--body);font-size:16.5px;line-height:1.42;letter-spacing:-.006em;
  color:var(--ink);max-width:52ch}
.sop[aria-expanded="true"] .sopclaim{display:none}

/* PAGE ZERO IS A HEAD BLOCK, NOT A PAGE (owner, 2026-09-07). It takes only
   the height its content needs — the ident, the count and the CLOSED brief
   — so the first signal begins immediately beneath it and is visible
   without a flick. It keeps scroll-snap-align, so a flick still lands
   cleanly on signal one; it simply stops spending a whole viewport on a
   preamble. Expanding the brief grows the block, which is the right
   trade: a reader who opens it has asked for the room. */
.pg--sop{height:auto;overflow:visible}
.pg--sop .pgin{height:auto;padding-bottom:4px}

/* THE DECK'S BOTTOM BAR — the only persistent chrome inside a deck, and
   the answer to "where am I / how do I get out". Left: the way up.
   Right: sideways, without going up. */
/* THE BAR CLEARS THE BROWSER'S OWN CHROME (owner, 2026-09-07: "the
   browser chrome obscures abil to use the cover button" — in Safari; "I
   don't see it in Chrome").
   Not a transient overlap. iOS Safari retracts its floating bottom bar on
   a DOCUMENT scroll, and this page has none: body is locked and the pager
   scrolls an inner element, so that chrome stays expanded for the life of
   the session and sits on the deck bar the whole time. env(safe-area-
   inset-bottom) does not cover it either — that inset is the home
   indicator (~34px) and the expanded chrome measures ~87px on the owner's
   phone, which is where the 46px comes from.
   --chrome is set to 0 by fitShell() on any browser whose viewport
   already excludes its chrome, so only the browsers that need the
   clearance pay for it. */
.dbar{flex:none;display:flex;align-items:center;gap:10px;padding:8px 13px calc(8px + env(safe-area-inset-bottom));
  border-top:2px solid var(--ink);background:var(--paper)}
@supports (-webkit-touch-callout:none){
  .dbar{padding-bottom:calc(8px + env(safe-area-inset-bottom) + var(--chrome,46px))}
}
.dbar[hidden]{display:none}
.dbar .bk{display:inline-flex;align-items:center;gap:8px;padding:7px 4px;text-decoration:none;
  font-family:var(--mono);font-size:9.5px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink)}
.dbar .bk:hover{color:var(--acc)}
.dbar .id{margin-left:auto;display:inline-flex;align-items:center;gap:7px;border:0;background:none;
  cursor:pointer;font-family:var(--display);font-size:14px;font-weight:800;letter-spacing:-.028em;color:var(--ink)}
.dbar .id s{text-decoration:none;font-family:var(--mono);font-size:9px;color:var(--ink3)}

/* ---- the episode player, the audio member of the media family ---- */
.listenbtn b{width:13px;height:13px;flex:none;border-radius:50%;border:2px solid #fff;position:relative}
.listenbtn b::after{content:"";position:absolute;left:3.5px;top:2.5px;width:0;height:0;
  border-left:5px solid #fff;border-top:3.5px solid transparent;border-bottom:3.5px solid transparent}
.pg .listenfig{margin:0;flex:none;display:flex;flex-direction:column;gap:8px}
.pg .listenfig iframe{display:block;width:100%;height:192px;border:0;background:var(--el2)}
.pg .listenfig figcaption{font-family:var(--mono);font-size:8.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--ink3)}

/* ---- the transition between levels (owner build item) ----
   Cross-document, so the cover and the deck it opens are one movement
   rather than a blink. 160ms, the figure measured off the reference.
   Browsers without it simply cut, which is what they do today. */
@media (prefers-reduced-motion: no-preference){
  @view-transition{navigation:auto}
  ::view-transition-old(root),::view-transition-new(root){animation-duration:var(--t-card,160ms)}
}

/* ============================================================
   TWO SURFACES, ONE DOCUMENT (2026-09-13)
   ------------------------------------------------------------
   The app on a phone, the publication on a desktop. Both chromes
   and both layouts are in every page's HTML and CSS picks one at
   1024px -- never a user-agent switch, because Ghost renders one
   HTML per URL and it is cached, so a UA switch hands a share
   crawler whichever copy the cache happens to hold, and gives
   search two homes to index.

   THE THEME HAD NO min-width QUERY AT ALL before this, so the
   cover rendered its phone layout at every width. Everything
   below is therefore ADDITIVE and gated: nothing under 1024px
   can regress, because nothing under 1024px is touched.
   ============================================================ */

/* The publication chrome is desktop-only. The four-group footer is NOT
   in this switch -- the owner wants it at both sizes and it already
   collapses 4 -> 2 -> 1 on its own. */
.sg-head,.sg-sub{display:none}

/* D10: and the desktop EDITION is the same idea pointing the other way --
   authored in every home document, shown only above 1024px. display:none
   is what makes it free on a phone: a display:none flex child of the
   locked shell takes no height, so the cover is untouched by it. */
#edition{display:none}

/* .ffoot survives only as feed.js's clone source for the deck endcap
   (finHTML reads [data-ffoot]). The clone lands in the pager, so hiding
   the authored one inside #cover leaves the endcap untouched. */
#cover > .ffoot{display:none}

@media (min-width:1024px){
  /* the DOCUMENT scrolls again. The locked body and the inner scroller are
     a phone idea -- they exist so the pager can own the viewport, and the
     pager is not what a desktop reader lands on. */
  body.feedhome{height:auto;overflow:visible}
  /* D10: .sg stops being the phone's flex column too. It kept
     height:var(--shellh) and display:flex here, so the desktop home was a
     viewport-high box its own content overflowed -- harmless while the
     cover was short, and simply wrong once the edition is the page. Above
     1024px the home is now structurally the same document as every other
     template: .sg is a block, the document is the scroller. */
  .feedhome .sg{height:auto;display:block}
  /* .feedhome .app, not .app -- the 760px phone shell is set at that
     specificity (feed.css:92) and a bare .app loses to it.
     UNCAPPED (D10). 1120px was the desktop COVER's measure, and the cover
     is not what a desktop reader lands on any more. Every other page wraps
     at --wrap (1280) through .in, and the home has to share those margins
     or its footer and river sit 96px narrower than the nav directly above
     them. The edition brings its own .in; .app just gets out of the way. */
  .feedhome .app{display:block;flex:none;min-height:0;height:auto;
    max-width:none;margin:0}
  /* ...except the breaking bar, which is a bare <a> and not a wrapped
     section, so uncapping .app would send it edge to edge with 15px of
     phone padding. Full-bleed background, wrapped content: the padding
     grows to meet .in's own content edge and never drops below --pad. */
  .feedhome .brkbar{padding-inline:max(var(--pad),calc((100% - var(--wrap)) / 2 + var(--pad)))}

  .fhead{display:none}
  .sg-head{display:block}
  .sg-sub{display:block}

  /* D9: A CHANNEL IS A SECTION AGAIN ABOVE 1024px. The deck stops painting
     over v1 and tag.hbs's .dos-grid -- the lead column and the 296px rail
     it has emitted on every channel page since 2.0.0, in the HTML the whole
     time -- is what a desktop reader gets: the layout of 6398782, the last
     v1 channel page before the feed.
     The deck is DISPLAY:NONE rather than removed, because one document has
     to carry both layouts (one HTML per URL, and it is cached). feed.js
     still hydrates it; a hidden pager measures zero and draws nothing,
     which costs a desktop reader nothing and keeps the phone's markup
     honest. Note v1's own .dos-grid collapses to one column under 1180px
     (signal.css:705), so 1024-1180 is chrome + single column -- that is
     what v1 did at that width and it is deliberately not overridden. */
  .deckroot{display:none}

  /* THE FRONT DOOR IS THE EDITION (D10, owner 2026-09-14: "we did have a
     landing page layout as the index for the signal theme at its most
     evolved state... can we still use that as the landing on desktop and
     keep the 'app' landing for mobile display? Really bifurcate for
     over/under 1024?").

     3.0.0 answered the desktop home by WIDENING THE COVER -- hero left,
     the two squares as a 360px rail, channel tiles three across. It read
     well and it was still the app's layout, which left `/` as the only URL
     on the site not wearing the publication at 1440px. So the cover now
     stops at 1024px with the rest of the phone, and #edition -- the v1
     river, the 296px rail, the promos and the units -- is the desktop
     landing. Recover the desktop cover grid from
     `a0f6559:signal-theme/assets/css/feed.css` if it is ever wanted back.

     THE FOOTER IS THE EXCEPTION AND IT HAS TO BE. {{> footer}} is authored
     INSIDE #cover on purpose (index.hbs:166): a footer placed beside .app
     takes its height out of the height-locked phone shell and collapses
     the cover to a sliver. So the switch hides the cover's CHILDREN and
     leaves #cover itself standing as the footer's container. */
  #cover > *:not(.sg-foot){display:none}
  .cover{display:block;flex:none;min-height:0;overflow:visible;padding:0}
  .cover > .sg-foot{margin:0}
  /* feed.js sets [hidden] on the cover when the route is #/all. Nothing on
     a desktop page links there -- the deck's doors are all inside the
     hidden cover -- but a pasted /#/all URL would otherwise take the
     footer down with it. Declining the phone's reveal at this width is the
     same ruling as everything else here: both layouts ship, CSS picks. */
  .cover[hidden]{display:block}

  #edition{display:block}

  /* The whole-feed deck is the phone's level 2 and has no desktop door.
     display:none rather than removed, because one document carries both
     layouts; feed.js still hydrates it, and a hidden pager measures zero
     and draws nothing. */
  #app,#alldeckbar{display:none}
}
