/* ============================================================================
   briefing.css — daily briefing render (Stage 1 chunk 1.2; dark re-theme chunk 3).
   Smart-Brevity card stack on the dark #0e0e0e system. Brand tokens +
   @font-face declarations come from global.css; this file is layout only.
   Every franchise card is a visual PEER — the fact card is NOT a hero.
   ============================================================================ */
.bf-page {
  min-height: 100svh;
  background: var(--bg); /* warm beige in light mode */
  /* + status-bar/camera inset on top so the wordmark clears the notch / Dynamic
     Island (the Daily header is normal-flow, so no top:0 bar selector covers it). */
  padding: calc(28px + var(--safe-top)) 18px 64px;
}

/* §ED742 · THE RECORDER FAMILY (.ba-* / .wv*) IS NO LONGER IN THIS FILE.
   It moved, rule for rule and comment for comment, to css/recorder.css,
   which every page that loads this sheet now loads FIRST. Six rules that
   were shared between .ra-card / .bf-* and .ba-* selectors were split; the
   halves left here say so in place. See css/recorder.css's header. */

.bf-wrap { max-width: 620px; margin: 0 auto; }

/* ── Header — ONE utility row (the paper carries the nameplate/date/edition now):
   culture pill left, icon-only calendar trigger right. ───────────────────── */
.bf-header { text-align: center; margin: 2px 0 26px; }

.bf-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* Positioned ancestor for .ar-cal-panel on /briefing ONLY (see the
     .bf-header-row .ar-calbar override below) — full content-column width,
     so the calendar centers on the column instead of on the narrow
     flush-right trigger. /archive's standalone .ar-calbar (not nested here)
     is untouched. */
  position: relative;
}

/* .bf-wordmark / .bf-kicker / .bf-nav: no longer used by /briefing (the daily
   card's own nameplate carries them now) — kept only because archive.html's
   header still renders this trio and shares this stylesheet. Do not delete
   without checking archive.html first. */
.bf-wordmark {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 1.5rem;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  margin: 0 0 6px;
}

.bf-kicker {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-muted); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
  margin: 0 0 4px;
}

html.theme-light .bf-kicker { color: var(--accent-ink); }

/* WO-CONV-3 (§8.3 register 3c) — the standing ambient rule line atop the
   archive/Collection headers. Neutral ink always (§3a: ambient affordances on
   a reading-primary view are never lime), quieter than .bf-kicker since it's
   a supporting line, not the page's own title. */
.ar-rule-line {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--text-subtle);
  margin: 0 0 4px;
}

.bf-nav { margin: 10px 0 0; }

.bf-nav a {
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-primary); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
  text-decoration: none;
}

html.theme-light .bf-nav a { color: var(--accent-ink); }

.bf-nav a:hover { text-decoration: underline; }

.bf-nav a:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

/* ── Culture selector (tease + seam) — subtle editorial row, not an app switcher.
   Live culture is selectable; not-yet-built ones show a muted "Soon" badge. ── */
.bf-culture { margin: 2px 0 8px; }

.bf-cults { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }

.bf-cult {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; cursor: pointer;
  font-family: var(--font-body); font-size: 0.8rem; color: var(--text-subtle);
  padding: 3px 4px; border-radius: 8px;
}

.bf-cult.is-active {
  position: relative;
  color: var(--text-primary); font-weight: 700;
}

/* FLAT ink underline (not a cupped/pill shape): a straight 2px rule the width of
   the active tab's text, barely-rounded ends, sitting just below the word.
   Neutral, not lime — the loved-state heart is the card's one signal (§3a
   punctuation law: lime is never decoration/tab-indicator chrome). */
.bf-cult.is-active::after {
  content: ''; position: absolute; left: 4px; right: 4px; bottom: 0;
  height: 2px; border-radius: 1px; background: var(--text-primary);
}

.bf-cult.is-locked { color: var(--text-subtle); cursor: default; opacity: 0.6; }

/* Locked cultures recede — dim "SOON" pill so the active Czech clearly leads. */
.bf-cult-badge {
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-disabled); background: var(--border);
  border-radius: 999px; padding: 1px 6px;
}

.bf-cult:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.bf-cults-hint {
  font-family: var(--font-body); font-size: 0.74rem; color: var(--text-subtle);
  text-align: center; margin: 6px 0 0; min-height: 1em;
}

/* ── Culture DROPDOWN (Daily) — compact trigger pill + bottom sheet ──────────
   Replaces the row above on /briefing (DB-driven: all is_live cultures). Trigger
   centres in the header (.bf-header is text-align:center). The sheet reuses the
   notif-sheet safe-area pattern: the backdrop's bottom padding carries
   var(--safe-bottom, 0px) so the sheet floats above the home indicator. ── */
.bf-cult-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: 999px; cursor: pointer; padding: 7px 14px;
  min-height: 44px; /* a11y medium-pass (2026-07-11): was ~35.6px */
  font-family: var(--font-numeric);
  font-size: 0.9rem; font-weight: 700; color: var(--text-primary);
  -webkit-tap-highlight-color: transparent;
}

.bf-cult-trigger-flag { font-size: 1.05rem; line-height: 1; }

.bf-cult-trigger-name { line-height: 1; max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bf-cult-trigger-caret { font-size: 0.85rem; color: var(--text-subtle); margin-left: 2px; }

.bf-cult-trigger:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* §ED176 — the selector sits at the top LEFT of .bf-header-row and the calendar
   trigger at the top RIGHT, and only one of them was raised. These four
   declarations are COPIED VERBATIM from .ar-cal-trigger (briefing.css:1789) —
   the same resting shadow, the same hover pair, the same 0.97 press, the same
   transition list. Nothing invented: no new shadow, no new radius, no new
   token. What is deliberately NOT copied is .ar-cal-trigger's SHAPE — its 13px
   radius, 15px padding and --border (the selector keeps its 999px pill,
   --border-strong and 44px min-height). Jake asked for raised and hovering
   like the calendar button, not for the selector to become one.
   ONE RULE SERVES BOTH SURFACES — no is-app fork. The shadows are plain
   rgba(0,0,0,·) rather than a theme token, which is exactly what the calendar
   button already does in both themes on both surfaces; forking here would mean
   inventing an elevation the calendar button does not have. */
.bf-cult-trigger {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  transition: background 140ms ease, transform 120ms ease, box-shadow 140ms ease;
}

.bf-cult-trigger:hover { background: var(--surface-3); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5); }

.bf-cult-trigger:active { transform: scale(0.97); }

.cult-sheet-backdrop {
  position: fixed; inset: 0; z-index: 9200;
  background: var(--scrim);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0 0 var(--safe-bottom, 0px);
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.cult-sheet-backdrop.is-visible { opacity: 1; visibility: visible; }

.cult-sheet {
  width: 100%; max-width: 480px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 24px 24px 0 0; padding: 20px 16px 28px;
  transform: translateY(100%);
  transition: transform 0.35s cubic-bezier(0.32, 0.72, 0, 1);
}

.cult-sheet-backdrop.is-visible .cult-sheet { transform: translateY(0); }

.cult-sheet-title {
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-subtle); text-align: center; margin: 0 0 12px;
  text-wrap: balance;
}

.cult-sheet-list { display: flex; flex-direction: column; gap: 4px; }

.cult-sheet-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  background: none; border: none; cursor: pointer; text-align: left;
  padding: 13px 12px; border-radius: 14px;
  font-family: var(--font-numeric);
  font-size: 1rem; font-weight: 600; color: var(--text-primary);
  -webkit-tap-highlight-color: transparent;
}

.cult-sheet-item:hover,
.cult-sheet-item.is-current { background: var(--surface-2); }

.cult-sheet-item-flag { font-size: 1.3rem; line-height: 1; flex-shrink: 0; }

.cult-sheet-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cult-sheet-item-check { color: var(--accent-ink); font-size: 1rem; flex-shrink: 0; }

.cult-sheet-item:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; }

/* ── WEB: culture picker is an ANCHORED POPOVER, not a bottom sheet ──────────
   html.is-app (native, see briefing.html's capability-gate script) keeps every
   rule above untouched — the sheet still rises from the bottom exactly as
   before. On web (html:not(.is-app)) js/card.js's mountCultureDropdown appends
   .cult-sheet into #bf-culture itself (now position:relative here) instead of
   nesting it inside .cult-sheet-backdrop, so it can anchor top:100% under the
   trigger pill the same way .ar-cal-panel anchors under its own trigger above
   — same floats-over-the-feed mechanic, just a narrower box. .cult-sheet-item
   rows are reused completely unstyled here; only the outer container differs.
   .cult-sheet-backdrop is repurposed into a plain full-viewport, invisible
   click-outside catcher (no dimming — a popover this small doesn't earn a
   scrim), decoupled from the panel entirely so its own position:fixed never
   becomes the panel's containing block. ── */
/* §ED529 — `html:not(.is-app) #bf-culture { position: relative; }` DELETED.
   It was the anchor for a position:absolute .cult-sheet nested inside it. The
   sheet is a <body> child now and anchors off a measured rect, so this rule has
   nothing left to position. */
html:not(.is-app) .cult-sheet-backdrop {
  background: transparent; -webkit-backdrop-filter: none; backdrop-filter: none;
  display: block; padding: 0;
  opacity: 1; visibility: visible; pointer-events: none;
  transition: none;
}

html:not(.is-app) .cult-sheet-backdrop.is-visible { pointer-events: auto; }

/* §ED529 — THE COMMENT ON THE OLD z-index WAS FALSE, AND THAT IS THE WHOLE BUG.
   It read "above .cult-sheet-backdrop's 9200 — clicks on the panel never fall
   through to the click-catcher". They fell through every time. .cult-sheet-
   backdrop is appended to <body>, so its 9200 is in the ROOT stacking context;
   .cult-sheet was appended into #bf-culture, i.e. inside .bf-header (a stacking
   context at z-index 20) inside .bf-wrap (a stacking context at z-index 1), so
   its 9210 was scoped inside .bf-header and never competed with 9200 at all.
   The transparent, pointer-events:auto catcher therefore painted over every
   row and swallowed the click: elementFromPoint() at the centre of a culture
   row returned `DIV.cult-sheet-backdrop is-visible`, e.target WAS the backdrop
   so its own `if (e.target !== backdrop) return` guard did not fire, and the
   handler just closed the sheet. That is the reported "clicking a culture does
   nothing at all" — nothing to do with the membership lock.
   FIX: js/card.js appends the sheet to <body> on web too, position:fixed, with
   top/left written by positionSheet(). Now 9210 and 9200 are siblings in one
   context and the comment is true. See scripts/audit-ed529-chrome.js. */
html:not(.is-app) .cult-sheet {
  position: fixed; top: 0; left: 0; right: auto; margin: 0;
  width: max-content; min-width: 220px; max-width: min(320px, calc(100vw - 32px));
  border-radius: 16px; padding: 10px;
  z-index: 9210;
  border: 1px solid var(--border-strong); box-shadow: var(--shadow-hero);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px) scale(0.97); transform-origin: top left;
  transition: opacity 160ms ease-in, transform 160ms ease-in, visibility 0s linear 160ms;
}

html:not(.is-app) .cult-sheet.is-visible {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateY(0) scale(1);
  transition: opacity 160ms ease-out, transform 180ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s;
}

html:not(.is-app) .cult-sheet-title { margin: 0 0 8px; }

/* §ED529 — the sheet itself is tabindex="-1" and takes focus on open so Tab
   continues into the rows (it is a <body> child now, not the trigger's DOM
   neighbour). That is a programmatic focus on a container, never a target the
   reader tabbed to, so it must not paint a ring: the ring belongs to the rows,
   which keep their own :focus-visible outline. The element is still focused,
   so the role="dialog"/aria-label announcement is unaffected. */
html:not(.is-app) .cult-sheet:focus { outline: none; }

/* ── Cards (all franchises are coequal — same weight) ───────────────────── */
.bf-stack { display: flex; flex-direction: column; gap: 14px; }

.bf-card {
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
  border-radius: 14px;
  padding: 20px 20px 18px;
}

/* Light mode: --surface is only ~10% relatively brighter than --bg (cream on
   tan), versus dark mode's --surface being ~64% relatively brighter than
   --bg (#171717 on #0e0e0e) — the same absolute gap reads far flatter once
   both are pushed into the light-luminance range, so a border alone doesn't
   give the card enough figure-ground separation. --shadow-card already
   exists for exactly this (global.css theme-light block, olive-brown per
   PREMIUM-POLISH-SPEC §8 "Elevation on paper") but was never wired to a
   selector — .bf-daily gets --shadow-hero already; this gives the other
   (non-hero) cards the same paper-over-paper lift instead of reading flat.
   Dark mode untouched — .bf-card there already reads elevated via contrast
   alone. */
html.theme-light .bf-card {
  box-shadow: var(--shadow-card);
}

/* Collection shelf — a kept fact's card is a tap target to its day's edition
   (js/collection.js). Same hover/press language as .ar-worth-row's clickable
   rows (briefing.css) rather than inventing a new affordance. */
.bf-card-linked { cursor: pointer; transition: transform 140ms ease, border-color 140ms ease; }

.bf-card-linked:hover { border-color: var(--border-accent); }

.bf-card-linked:active { transform: scale(0.99); }

.bf-card-linked:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.bf-label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 10px;
}

/* One fact unit inside a card (a card may hold >1 item). Transparent border at
   rest (not "none") so the loved-state green outline below doesn't shift
   layout when it appears — same treatment as .bf-guessmoment further down. */
.bf-item {
  border: 1.5px solid transparent;
  border-radius: 10px;
  transition: border-color 160ms ease;
}

.bf-item + .bf-item { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }

.bf-actions { margin-top: 2px; }

/* Tier-2 'love' reaction (fact cards only) — light, on-brand, distinct from Report. */
.bf-love {
  flex: 0 0 auto;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 12px 0 0;
  font-size: 1.05rem;
  line-height: 1;
  color: var(--text-muted);
  transition: color 0.12s ease, transform 0.08s ease;
}

.bf-love:hover { color: var(--text-subtle); }

.bf-love.is-on { color: var(--accent-ink); }

.bf-love:active { transform: scale(1.15); }

.bf-love:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-reported {
  display: inline-block;
  margin-left: 14px;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-primary); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
}

html.theme-light .bf-reported { color: var(--accent-ink); }

.bf-text {
  font-family: var(--font-body);
  font-size: 1.05rem;      /* ~16–17px lead — the "what + why it matters" */
  line-height: 1.6;
  color: var(--text-primary);
  margin: 0 0 12px;
}

.bf-source {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--text-subtle);
  margin: 0 0 10px;
  overflow-wrap: break-word;
}

/* ARCH-CORRECTION notice (R6.3) — deliberately NOT an alarm/warning
   treatment (no red, no icon): a correction notice states a fact, it does
   not apologize or alarm. Same neutral register as .bf-source, set apart
   only by a hairline top rule so it reads as an appended editorial note. */
.bf-correction {
  margin: 10px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.bf-correction-label {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 4px;
}

.bf-correction-body,
.bf-correction-replacement {
  font-family: var(--font-body);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-subtle);
  margin: 0 0 4px;
}

.bf-correction-replacement:last-child { margin-bottom: 0; }

/* ── Unified daily card — "B · The Masthead" (dark · premium-editorial) ──────── */
.bf-daily {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  overflow: hidden;
  /* Promoted to the shared --shadow-hero token (global.css Phase A) — the
     top-weighted lime glow this card originated, now shared app-wide. Carries
     its own light-mode olive-glow variant automatically. */
  box-shadow: var(--shadow-hero);
  transition: transform 160ms ease, box-shadow 160ms ease;
}

/* Gentle, subtle hover-lift — the card rises a touch and the glow firms up. */
.bf-daily:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-hero-hover);
}

/* Masthead — ONE publication nameplate: elevated by LUMINANCE (stands proud of
   the card body) + a lime top-edge, now with --edge-light (T0) as its catch
   light. Row 1 = the Culturily wordmark itself (the "The Daily" title that
   used to sit under it is retired — the wordmark alone carries the nameplate
   now, per the strict Fraunces rule: Fraunces stays ONLY on this wordmark,
   nowhere else on the card); Row 2 = the dateline folio under a hairline —
   it absorbs the old corner edition number, page date, and culture marker. */
.bf-masthead {
  display: flex;
  flex-direction: column;
  padding: 14px 28px 12px;
  background: var(--surface-3);
  border-top: 2px solid var(--border-strong);        /* structural edge, not a lime flourish */
  box-shadow: var(--edge-light);
}

.bf-mast-wordmark {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.45rem; font-weight: 600;
  letter-spacing: -0.01em;
  text-wrap: balance;
  color: var(--text-primary);
  margin: 0 0 4px;
}

.bf-mast-dateline {
  font-family: var(--font-body);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
  margin: 8px 0 0;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

/* Past-day (isolated single-day view, /briefing only) additions to the dateline.
   Dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink. */
.bf-mast-archive { color: var(--text-subtle); letter-spacing: 0.06em; }

html.theme-light .bf-mast-archive { color: var(--accent-ink); }

.bf-mast-back {
  position: relative; /* a11y medium-pass (2026-07-11): hit-slop base, see ::before */
  align-self: flex-start;
  margin-top: 8px;
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 700;
  color: var(--text-primary);
  -webkit-tap-highlight-color: transparent;
}

html.theme-light .bf-mast-back { color: var(--accent-ink); }

/* Was ~18px tall (12px text * 1.5 line-height, zero padding). Invisible
   hit-slop instead of real padding — same pattern as .pf-icon-btn's
   ::before{inset:-4px} — so the visible label/spacing doesn't shift. */
.bf-mast-back::before { content: ''; position: absolute; inset: -13px 0; }

.bf-mast-back:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

/* ── §ED509 — THE HONEST STATE ────────────────────────────────────────────────
   "Today's edition isn't ready yet — this is the last one published."

   NOT AN ERROR, and styled so nothing suggests one: no red, no warning tint, no
   alert glyph. Nothing has broken from the reader's side — they are on the
   current page, looking at the most recent edition that exists. NOT AN ARCHIVE
   TAG either: .bf-mast-archive is a small-caps marker for a day the reader
   CHOSE, and this reader chose nothing, so the two must never share a treatment.

   The grammar is an editorial aside inside the paper: neutral ink, a rule on the
   leading edge, sitting under the dateline's own hairline. One step firmer than
   the dateline so it cannot be skimmed past, and no firmer than that.

   AT THE §ED306 TYPE FLOOR (1rem/16px) DELIBERATELY. This is prose a reader has
   to actually read and act on, not furniture. .bf-mast-dateline above it sits at
   0.75rem and predates the floor (§ED475's unswept backlog) — that is a debt,
   not a precedent, and matching it here would deepen it.

   Fully token-driven, so light and dark are covered by global.css's own
   palettes rather than by a second theme rule here. */
.bf-mast-stale {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.45;
  color: var(--text-secondary);
  margin: 10px 0 0;
  padding: 8px 0 2px 12px;
  border-left: 3px solid var(--border-strong);
  text-wrap: pretty;
}

.bf-mast-stale-lede { color: var(--text-primary); font-weight: 700; }

/* Ribbon slot (item #8, R4) — structural shape only; ships hidden/empty here
   (composeHookCard sets [hidden]). The tutorial build populates the content
   and un-hides it — this is the shell it drops into, not a live element yet. */
.bf-ribbon-slot {
  background: var(--accent-dim);
  padding: 10px 28px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent-ink);
  text-align: center;
}

/* Top padding tightened (was 30px) — the masthead/nameday/rootline group above
   the fact reads as one compact block now; the fact headline's own generous
   lead-in (.bf-quote's padding-top) is where the deliberate air lives instead. */
.bf-daily-body { padding: 22px 32px 28px; }

/* Standing root-line: the daily emotional frame — warm muted italic dek. Quiet
   context, not competing with the hero below. Nudged back up slightly (was
   0.95rem/--text-subtle) — the declutter pass had softened this past
   legibility; still a step short of the pre-declutter weight/size. */
.bf-rootline {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--text-muted);
  margin: 0 0 10px;
}

/* Name-day folio — a franchise line between the masthead's dateline and the
   root-line (moved out of the old bf-frame metadata row, which is gone: the
   date it carried is now the dateline folio's job). The dot is neutral now —
   decoration doesn't get to be lime (§3a); the card's one signal is the loved
   heart. Label + name nudged back up slightly (were 0.68rem/500/--text-subtle
   and 0.8rem/500) — still short of the original 700/600 weights, just past
   the point the declutter pass had softened them to. */
.bf-namefolio { display: flex; align-items: center; gap: 7px; margin: 0 0 10px; }

.bf-namefolio-label {
  font-family: var(--font-display);
  font-variant: small-caps;
  font-size: 0.72rem;
  font-weight: 580;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.bf-namefolio-dot { width: 4px; height: 4px; border-radius: 50%; background: var(--text-muted); }

.bf-namefolio-name { font-family: var(--font-body); font-size: 0.85rem; font-weight: 560; color: var(--text-primary); } /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */

html.theme-light .bf-namefolio-name { color: var(--accent-ink); }

/* Edge case only — a day with a name-day but NO fact block, so composeCards has
   no paper to fold the folio into (renderNameDayCard fallback). Not a bordered
   peer card, just enough surface so the line doesn't float bare on the page. */
.bf-namefolio-standalone { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-card); padding: 18px 20px; }

.bf-namefolio-standalone .bf-namefolio { margin: 0; }

/* Name-day save toggle (114_user_saved_namedays.sql) — signed-in readers only.
   Each name in the folio becomes its own tap target: same font/size/weight as
   the plain .bf-namefolio-name it replaces, so a day with nothing saved reads
   IDENTICALLY to before. Calm/almanac styling per the ruling: no lime, no
   celebration, no counter — just a quiet ring that fills when saved. */
.bf-namefolio-names { font-family: var(--font-body); font-size: 0.85rem; font-weight: 560; }

.bf-nameday-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  padding: 2px 1px;
  margin: 0;
  font: inherit;
  color: var(--text-primary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

html.theme-light .bf-nameday-toggle { color: var(--accent-ink); }

.bf-nameday-toggle:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-nameday-icon { display: inline-flex; color: var(--text-subtle); }

.bf-nameday-toggle.is-saved .bf-nameday-icon { color: currentColor; }

.bf-nameday-toggle.is-saved .bf-nameday-ring { fill: currentColor; }

/* Free-tier cap (MONETIZATION-MODEL.md §7-R2, 115_user_saved_namedays_free_
   limit.sql) — an unsaved name once a free reader already has 3. Dimmed, not
   hidden or struck through: still reads as a name, not a broken control. */
.bf-nameday-toggle.is-locked { opacity: 0.5; }

/* "Today is X — one of yours" — the personal-touch line, quiet and
   editorial like .bf-rootline (never a badge, never colored, no icon). Sits
   between the folio and the root-line only when it has something to say. */
.bf-namefolio-mine {
  font-family: var(--font-body);
  font-style: italic;
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: -4px 0 10px;
}

/* Folio invitation (Part 3, "make membership feel like membership",
   2026-07-23) — a member's OTHER culture edition, mentioned as one quiet
   masthead line, same register as .bf-namefolio-mine (italic, muted, no
   badge). It IS tappable (in-place culture switch), unlike that line, so it
   gets cursor:pointer + a slightly bolder arrow as the one tap affordance —
   still no lime, no border, no card: this is furniture, not a CTA. */
.bf-culture-invite {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-style: italic;
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: -4px 0 10px;
  cursor: pointer;
}

.bf-culture-invite:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-culture-invite-arrow { font-style: normal; font-weight: 700; color: var(--text-primary); }

html.theme-light .bf-culture-invite-arrow { color: var(--accent-ink); }

/* Free-cap note — appended once, after the folio, on the proactive-lock tap
   or the server rejection. Same quiet register as .bf-namefolio-mine: one
   plain sentence, no badge, no button, a plain-text link is the only accent. */
.bf-nameday-limit-note {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: -4px 0 10px;
}

.bf-nameday-limit-link { color: var(--text-primary); text-decoration: underline; }

html.theme-light .bf-nameday-limit-link { color: var(--accent-ink); }

/* Hook — the daily fact, marked by a short neutral horizontal DASH above it
   (was lime — decoration doesn't get to be lime, §3a). Set for SENTENCES (the
   editorial-voice hook reads ~8-10 wrapped lines on mobile, not a poster
   fragment). .is-short (card.js, text ≤ ~90 chars) steps back up to a poster
   size for short punchy facts. The hook stays the hero in both themes. Font
   §ED430 · back to Fraunces, at 400. Jake's FINAL ruling makes fact titles
   Fraunces 400, which supersedes the old "Fraunces is the wordmark only"
   reservation (VISUAL-IDENTITY §2) for this element. --font-display now
   RESOLVES to Fraunces, so the token is already correct and only the weight
   moves (480 -> 400). Fraunces' opsz axis is live again (9-144, measured);
   browsers apply font-optical-sizing:auto by default, so it tracks font-size
   without the hand-set opsz 40 this comment used to describe.
   Generous padding-top here (was 2px) is deliberate — the hero's arrival
   needs clear air after the quiet pre-fact stack above (masthead/nameday/
   rootline), not a snug transition; the size bump below is only half the
   "hero" effect, the breathing room is the other half. */
.bf-quote { padding-top: 26px; }

.bf-accent-dash {
  display: block;
  width: 36px; height: 4px;
  background: var(--text-muted);
  border-radius: 999px;
  margin: 0 0 14px;
}

.bf-hook-text {
  position: relative;
  font-family: var(--font-display);
  font-weight: 400;   /* §ED430 · ruled */
  font-size: 2rem;
  line-height: 1.18;
  letter-spacing: -0.015em;
  color: var(--text-primary);
  margin: 0;
  overflow-wrap: break-word;   /* long Czech names never overflow the card */
  text-wrap: balance;
}

.bf-hook-text.is-short { font-size: 2.4rem; }

/* Fact deck (§1.7, FACT-ANATOMY-SPEC.md 2026-07-15 amendment) — fact_text,
   demoted from hero to deck ONLY when an approved title takes the hero slot
   above it (card.js renderFactRead: appended only when fact.title is
   present). Body-serif, first-class (full text_primary, not the debrief's
   muted tone) — never truncated, always renders in full.
   (Renamed "standfirst" -> "deck" in this comment 2026-07-23: that word now
   names a DIFFERENT, new block — see .bf-standfirst below, the placement
   line between the title and THIS deck — so reusing it here would read as
   the same thing twice.) */
.bf-fact-deck {
  font-family: var(--font-body);
  font-size: 1.2rem;
  line-height: 1.5;
  color: var(--text-primary);
  max-width: 62ch;
  margin: 14px 0 0;
}

/* Standfirst (§1.7 amendment, 2026-07-23) — a placement line for the card's
   LEAD entity, between the hero title and the deck above: "Benjamin
   Franklin — a statesman of American independence (1706-1790)." Deliberately
   subordinate to .bf-hook-text in every dimension (smaller, uppercase-tracked
   label register instead of display type, muted color) — a second headline
   here would reopen the emotional-spine two-subject law (§1.7's own register
   bans exist for the same reason). Server-gated to render only alongside an
   approved, hash-fresh title (10-assemble-daily.js's gateRenderableStandfirst),
   so it never appears floating with no title above it. */
.bf-standfirst {
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: var(--text-muted);
  max-width: 62ch;
  margin: 10px 0 0;
}

/* Hook → story seam — the classic head/dek separator: one quiet hairline, no
   second lime flourish (the dash stays the column's only one). */
.bf-hook-seam { width: 24px; height: 1px; background: var(--border); margin: 20px auto; border: none; }

/* Debrief — grounded background/story below the hook. */
.bf-debrief {
  font-family: var(--font-body);
  font-size: 1.02rem;
  line-height: 1.62;
  color: var(--text-muted);
  max-width: 62ch;
  margin: 16px 0 0;
}

/* Trust line — standing provenance, ALWAYS visible (never tap-gated behind
   Read-more, which keeps the URL/subdomain/report affordances). No lime here —
   the hook's accent-dash stays the column's one flourish. */
.bf-trust { font-family: var(--font-body); font-size: 0.78rem; color: var(--text-subtle); margin: 16px 0 0; }

/* Reaction row — love pill (live count) + social proof. */
.bf-reaction { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 26px; }

.bf-love-pill {
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 9999px;
  padding: 11px 18px 11px 15px; cursor: pointer;
  min-height: 44px; /* a11y low-pass (2026-07-11): was ~42px on desktop */
  transition: transform 140ms ease, background 140ms ease;
}

.bf-love-pill:hover { transform: translateY(-1px); background: var(--surface-3); }

.bf-love-pill:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* §ED240 — the save pill's glyph is the bookmark now; the heart went to
   .bf-like-pill. Same ink it always carried, on the new mark. */
.bf-love-pill .bf-bookmark { color: var(--accent-ink); }

.bf-love-lbl { font-family: var(--font-body); font-size: 0.84rem; font-weight: 700; color: var(--text-primary); }

.bf-love-count { font-family: var(--font-body); font-size: 0.78rem; color: var(--text-subtle); }

.bf-love-pill.is-on { background: var(--accent); border-color: var(--accent); }   /* loved → lime fill, dark text */

.bf-love-pill.is-on .bf-bookmark { color: var(--accent-text); fill: var(--accent-text); }

.bf-love-pill.is-on .bf-love-lbl { color: var(--accent-text); }

.bf-love-pill.is-on .bf-love-count { color: color-mix(in srgb, var(--accent-text) 55%, transparent); }

/* LIKE (§ED240) — the reaction, beside the save. Same pill geometry so the
   two read as one control group, but NEVER lime-filled in either state: the
   view's one lime action is the save's is-on fill, and a second filled pill
   beside it would breach the palette invariant every time a reader did both.
   The on-state says its piece through the heart's ink (--accent-ink, exactly
   the treatment .bf-love-pill already gives that glyph on this surface) and
   a strengthened border — not a fill. Label ink is --text-primary in both
   states, so nothing here rests on a muted tone for legibility. */
.bf-like-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 16px; border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-primary);
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}

.bf-like-pill:hover { transform: translateY(-1px); background: var(--surface-3, var(--surface-2, var(--surface))); }

.bf-like-pill:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.bf-like-pill .bf-heart { color: var(--text-subtle); transition: color 120ms ease; }

.bf-like-pill.is-on { border-color: var(--accent-ink); }

.bf-like-pill.is-on .bf-heart { color: var(--accent-ink); }

.bf-like-pill .bf-like-lbl { color: var(--text-primary); }

/* Green "kept" outline on the fact's own OUTER card, toggled in card.js's
   paint() via row.closest('.bf-daily, .bf-item'). Matches --accent, the same
   lime used for .bf-masthead's border-top "lime top-edge" (the card's own
   top-bar accent) — was --success (a darker, distinct mint/forest green)
   until this pass; changed to read as the SAME brand accent as the rest of
   the card rather than a separate, slightly-too-dark green. Border-color
   only (.bf-item already carries a transparent border at rest to color
   here, and .bf-daily carries its own non-transparent border already) — no
   fill, kept deliberately subtle. .bf-guessmoment is deliberately NOT
   targeted here — it always nests inside .bf-daily/.bf-item, so coloring it
   drew the outline on an inner box instead of framing the actual card (see
   card.js paint()). */
.bf-daily.is-loved,
.bf-item.is-loved {
  border-color: var(--accent);
}

/* A2 — love-pill micro-interaction. Both classes are added by card.js only on
   the tap-driven transition into loved (never on the async server-
   reconciliation repaint), so they never replay spuriously. Reduced-motion is
   handled globally (global.css collapses animation-duration app-wide). */
@keyframes bf-heart-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.35); }
  65%  { transform: scale(0.9); }
  100% { transform: scale(1); }
}

.bf-heart.bf-pop { animation: bf-heart-pop 250ms ease-out; }

@keyframes bf-count-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.bf-love-count.bf-rise { animation: bf-count-rise 220ms ease-out; }

.bf-social { font-family: var(--font-body); font-size: 0.78rem; color: var(--text-subtle); }

/* ── BLUR + OVERLAY guess gate (calendar-scrim pattern) ──────────────────────
   The lead fact renders BLURRED behind an overlay (filter:blur, same approach as
   the calendar scrim); a guess panel hovers on top with the thin stem + 4 tiles.
   On a guess the overlay drops and the blur clears (the fact reads sharp), and a
   small result chip lands at the BOTTOM. Masthead/date above stay sharp. */
/* Transparent border at rest (matches .bf-item's treatment) — this is the
   closest()-matched container for every TESTABLE fact (hero or secondary),
   so it needs its own border to color; .is-gated's veil pseudo-element uses
   the same 16px radius, kept in sync here for a consistent outline. */
.bf-guessmoment {
  margin-top: 22px;
  border: 1.5px solid transparent;
  border-radius: 16px;
  transition: border-color 160ms ease;
}

/* GATED — the fact renders NOW but blurred unreadable; the overlay panel stacks in
   the SAME grid cell on top. */
.bf-guessmoment.is-gated { display: grid; }

.bf-guessmoment.is-gated > .bf-gated,
.bf-guessmoment.is-gated > .bf-guess-overlay { grid-area: 1 / 1; }

.bf-gated { filter: blur(0); transition: filter 520ms cubic-bezier(0.22, 1, 0.36, 1); }

/* Veiled fact — FULL natural height (the grid cell stretches it); blurred unreadable.
   §ED137: THIS RULE IS THE SIGHTED READER'S GATE ONLY, and nothing more. `filter`
   is a paint-time effect and `pointer-events` a hit-testing rule; neither removes
   the subtree from the accessibility tree or the tab order, so for four months a
   screen reader read the whole answer before the guess. The AT gate is the `inert`
   (+ aria-hidden) attribute card.js sets on .bf-gated at build time and clears in
   reveal() — see js/card.js's buildGuessMoment. Do not conclude from this rule
   that the gate is CSS's job: CSS closes one channel of two. */
.bf-guessmoment.is-gated .bf-gated { filter: blur(10px); pointer-events: none; user-select: none; }

/* VEIL — a translucent tint over the blurred fact (calendar-scrim pattern lineage),
   covering the FULL gated block and extending BEYOND the floating panel. Above the
   fact (z0), below the panel (z2). R5: the backdrop-filter (blur+brightness) layer
   that used to live here is GONE — a perf fix (backdrop-filter is expensive in
   WKWebView, and this was stacking a SECOND blur on top of .bf-gated's own
   `filter: blur(10px)`, for one veil). The content layer's own blur + this plain
   translucent tint now carry the whole effect. Device-verify (dark + light): if
   the veil visibly cheapens without the extra brightness lift, that's the one
   thing in this commit to revert. */
.bf-guessmoment.is-gated::before {
  content: ''; grid-area: 1 / 1; z-index: 1; pointer-events: none; border-radius: 16px;
  background: color-mix(in srgb, var(--text-primary) 6%, transparent);
}

.bf-guess-overlay { z-index: 2; align-self: start; }

/* A1 — animated exit: card.js adds .is-exiting on commit() and removes the node
   ~200ms later (matching this duration) instead of an instant removeChild.
   prefers-reduced-motion is handled globally (global.css collapses transition
   durations app-wide), so this degrades to an effectively-instant removal there. */
.bf-guess-overlay.is-exiting {
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 200ms ease-out, transform 200ms ease-out;
  pointer-events: none;
}

/* The guess panel — a frosted "door": TRANSLUCENT (--glass-panel, T0/R5 token —
   was a hand-written rgba + a theme-light override, now one declaration) so the
   blurred story shows through underneath (teasing shape/length) while staying
   unreadable; lime-tinted hairline is the panel's interaction affordance, the
   ambient shadow lifts it off the blur (var(--shadow-float), already
   per-theme) — the lime-tinted GLOW that used to ride along the shadow is
   gone (§3a: no glow-at-scale). The old dark-mode-only text-shadow is gone —
   it existed to keep text legible over the FROST layer's brightness boost
   (removed above), which no longer applies. */
.bf-guess {
  background: var(--glass-panel);
  border: 1px solid var(--border-accent); border-radius: 18px;
  padding: 24px 22px;
  box-shadow: var(--shadow-float);
}

.bf-guess-eyebrow {
  display: flex; align-items: center; gap: 8px; margin: 0 0 14px;
  font-family: var(--font-body); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
}

html.theme-light .bf-guess-eyebrow { color: var(--accent-ink); }

.bf-guess-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--text-muted); flex: 0 0 auto; }

.bf-guess-stem {
  font-family: var(--font-body); font-size: 1.16rem; line-height: 1.45; font-weight: 500;
  color: var(--text-primary); margin: 0 0 20px;
}

.bf-guess-hint {
  font-family: var(--font-body); font-size: 0.74rem; color: var(--text-muted);
  margin: 16px 0 0; text-align: center;
}

/* Option tiles — letter badge + text rows. Translucent (--glass-tile, T0/R5
   token) so the blurred story reads continuously across the panel; border
   reuses the standard --border/--border-strong ladder instead of one-off
   alpha values, which retires the theme-light override entirely. */
.bf-opts { display: flex; flex-direction: column; gap: 10px; }

.bf-opt {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--glass-tile); border: 1.5px solid var(--border);
  border-radius: 13px; padding: 13px 14px; cursor: pointer; color: var(--text-primary);
  transition: border-color 120ms ease, background 120ms ease, transform 100ms ease;
}

.bf-opt:hover { border-color: var(--border-strong); }

.bf-opt:active { transform: scale(0.99); }

.bf-opt:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.bf-opt:disabled { cursor: default; }

.bf-opt-badge {
  flex: 0 0 auto; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px; background: var(--surface-3); color: var(--text-subtle);
  font-family: var(--font-display); font-weight: 700; font-size: 0.875rem;
  /* 14px bold clears the WCAG "large text" line — needed so the
     is-wrong state's --error-ink-on-fill ratio (~4.2:1 in dark theme)
     qualifies under the 3:1 large-text tier instead of failing 4.5:1. */
}

.bf-opt-text { font-family: var(--font-body); font-size: 0.95rem; font-weight: 500; }

/* INLINE correct/wrong OPTION FEEDBACK (card.js commit()) — the SAME pattern
   the arcade quiz uses (guest-lesson.js/lesson.js .option-card is-correct/
   is-wrong/is-reveal/is-dimmed), reused here on .bf-opt instead of a centered
   overlay or a page-level toast — both broke on-device (the toast overlapped
   the header, the overlay never appeared). This page doesn't load
   components.css, so the same brand-locked red values are duplicated here
   rather than pulled in wholesale — colors match exactly, just scoped to
   .bf-opt's own badge/text structure instead of .option-card's. Correct now
   uses the semantic --success green (was brand lime) — quiz feedback is
   functional state, not brand punctuation; the loved heart stays the card's
   one lime signal (§3a), and this also brings is-correct in line with
   is-wrong below, which already used semantic --error. */
.bf-opt.is-correct {
  border-color: var(--success);
  background: var(--success-dim);
  box-shadow: 0 0 0 1px var(--success) inset, 0 2px 10px color-mix(in srgb, var(--success) 12%, transparent);
}

.bf-opt.is-correct .bf-opt-badge { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }

/* Reveal — the right answer shown inline after a WRONG pick (replaces the old
   "Not quite — it was X" copy; the correct option itself IS the answer now). */
.bf-opt.is-reveal {
  border-color: var(--success);
  background: var(--success-dim);
}

.bf-opt.is-reveal .bf-opt-badge { background: color-mix(in srgb, var(--success) 14%, transparent); color: var(--success); }

.bf-opt.is-wrong {
  border-color: var(--error);
  background: var(--error-dim);
  box-shadow: 0 0 0 1px var(--error) inset, 0 2px 10px color-mix(in srgb, var(--error) 12%, transparent);
}

.bf-opt.is-wrong .bf-opt-badge { background: color-mix(in srgb, var(--error) 16%, transparent); color: var(--error-ink); }

.bf-opt.is-dimmed { opacity: 0.4; }

/* Result banner — COMPACT but readable at a glance, at the BOTTOM after the read.
   THREE shapes (card.js buildResultChip): GUESS (STREAK ONLY now — the verdict
   badge/headline and wrong-answer reveal live inline on the guess options
   above, see .bf-opt.is-correct/.is-wrong/.is-reveal), READ completion (R4 — no correctness, flame badge + the
   PROMOTED streak cell), and DAY-1 (R4 — full-width band, self-limiting). Not
   the old full-screen flame; the reading stays the hero. Entrance = BLOOM
   (.m-bloom, global.css T0 primitive, applied via Motion.bloom() in card.js)
   — replaces the old plain-CSS slide-up. */
.bf-result {
  display: flex; align-items: center; gap: 13px;
  margin-top: 18px; padding: 13px 15px; border-radius: 14px; border: 1px solid;
  font-family: var(--font-body);
}

.bf-result.is-read    { background: var(--accent-dim); border-color: var(--border-accent); }

.bf-result-badge {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 1.15rem; line-height: 1;
}

.bf-result-badge.is-flame { background: var(--accent); color: var(--accent-text); }

.bf-result-mid { flex: 1 1 auto; min-width: 0; }

.bf-result-head {
  font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; line-height: 1.1; margin: 0;
  text-wrap: balance;
}

.bf-result.is-read    .bf-result-head { color: var(--text-primary); } /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */

html.theme-light .bf-result.is-read .bf-result-head { color: var(--accent-ink); }

.bf-result-sub { font-size: 0.86rem; color: var(--text-muted); margin: 3px 0 0; }

.bf-result-streak {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
  color: var(--text-subtle); font-size: 0.8rem; font-weight: 600;
  font-family: var(--font-numeric);   /* the streak digit — numbers use --font-numeric, never --font-display */
}

.bf-result-flame { color: var(--accent-ink); }

.bf-result-flame svg { width: 16px; height: 16px; }

/* GUESS result, streak-only — this chip no longer signifies right/wrong (that
   moved to the top banner), so shrink it to a modest pill and drop the
   green/red tint. Scoped to correct/wrong only — is-day1 and is-read keep the
   base .bf-result sizing/background above untouched. */
.bf-result.is-correct,
.bf-result.is-wrong {
  padding: 8px 12px;
  margin-top: 10px;
  background: var(--surface-well);
  border-color: var(--border);
}

/* READ completion (R4) — the PROMOTED streak cell: "Day N" is the thing the
   eye lands on (a reward, not a footnote), the self-narrative label below it. */
.bf-result-cell {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right;
}

.bf-result-cell-n {
  font-family: var(--font-numeric);
  font-weight: 800; font-size: 1.05rem; line-height: 1; color: var(--text-primary); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
}

html.theme-light .bf-result-cell-n { color: var(--accent-ink); }

.bf-result-cell-label { font-family: var(--font-body); font-size: 0.72rem; color: var(--text-subtle); }

/* DAY-1 (R4) — self-limiting, full-width Beat-2 band; shared by whichever path
   (guess or read) first reaches streakCount===1. Replaces the shapes above. */
.bf-result.is-day1 { flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; background: var(--accent-dim); border-color: var(--border-accent); }

.bf-result-day1-head { display: flex; align-items: center; gap: 10px; }

.bf-result.is-day1 .bf-result-head { color: var(--text-primary); } /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */

html.theme-light .bf-result.is-day1 .bf-result-head { color: var(--accent-ink); }

.bf-result.is-day1 .bf-result-sub { color: var(--text-muted); margin: 0; }

/* ANON STREAK NUDGE — logged-out only, sits quietly under the result banner. No
   fill, subtle text: it must read as a footnote to the banner, not a peer.
   font-family was ALREADY var(--font-body) (Montserrat) — checked git history,
   it has been since this rule was first introduced (2026-06-27) and there is
   no other CSS rule or theme override touching --font-body/--font-display
   that could flip it; per DESIGN-INVARIANTS.md this is correct as-is. Size
   bumped per the 2026-07-08 design pass — 0.78rem read as too small for a
   CTA line, not just a caption. */
.bf-streak-nudge {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-subtle);
  text-decoration: none;
  margin: 10px 0 0 2px;
  padding: 2px 0;
  transition: color 120ms ease;
}

.bf-streak-nudge:hover { color: var(--text-muted); }

/* DAILY COLLECTION NUDGE — logged-in equivalent of the anon streak nudge above,
   but promoted to a proper button-weight tap target (was a bare inline-text
   footnote — too subtle to act on). Matches the existing .pf-change-culture-
   link row pattern (profile.html) rather than a loud lime CTA: padded, a
   subtle border/surface tint, calm/editorial text color. */
.bf-collection-nudge {
  display: flex;
  align-items: center;
  width: 100%;
  margin-top: 14px;
  padding: 12px 15px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface-well);
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--text-primary);
  text-decoration: none;
  transition: background 120ms ease, border-color 120ms ease;
}

.bf-collection-nudge:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* CONVERSION WALL (ONBOARDING-CONVERSION-AUDIT §2) — dual-trigger anon signup
   ask. Same visual family as .bf-result.is-day1 (--accent-dim band,
   border-accent) per PREMIUM-POLISH-SPEC's result-chip convention. */
.bf-reaction-wrap { display: block; }

.bf-wall-slot:not([hidden]) { display: block; }

/* WO-CONV-3 (§8.3/§8.7) — the first-save disclosure, once ever. Neutral ink,
   no lime (§3a: an ambient affordance on a reading-primary view). */
.bf-first-save {
  display: flex; align-items: center; gap: 10px;
  margin-top: 10px; padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}

.bf-first-save-text {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--text-subtle);
  margin: 0;
  flex: 1;
}

.bf-first-save-dismiss {
  flex: 0 0 auto;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  padding: 4px 6px;
}

/* ── "This one's ours" — the reader's own one-line note (119_user_fact_notes.sql).
   The FIRST feature that puts the reader's OWN WORDS on a card, so a note must
   read UNMISTAKABLY as theirs, never as sourced/verified content. Three signals
   carry the distinction at once: (1) an explicit "Your note" label, (2) a
   RECESSED well — sourced text sits on the flat card surface, the note sits
   inset in a well — and (3) a NEUTRAL grey left rule, deliberately NOT the olive
   source-accent (.bf-accent-dash / .bf-trust), so the two can never blur. Calm,
   no arcade, no celebration. Theme-aware via the shared tokens (well/border/ink). */
.bf-note-zone { margin: 10px 0 2px; }

/* Empty state — a quiet ambient trigger, not a heavy button. */
.bf-note-add {
  display: inline-flex; align-items: center; gap: 6px;
  border: none; background: transparent; cursor: pointer;
  font-family: var(--font-body); font-size: 0.8rem; font-weight: 600;
  color: var(--text-subtle); padding: 2px 0;
}

.bf-note-add:hover { color: var(--text-muted); }

.bf-note-add-ic { display: inline-flex; }

.bf-note-add:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

/* The saved note — recessed well + neutral left rule + label. */
.bf-note {
  padding: 9px 12px 9px 13px;
  background: var(--surface-well);
  border-left: 3px solid var(--border-strong);
  border-radius: 0 8px 8px 0;
}

.bf-note-label {
  display: block;
  font-family: var(--font-body);
  font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 3px;
}

.bf-note-text {
  font-family: var(--font-body);
  font-size: 0.9rem; line-height: 1.5;
  color: var(--text-primary);
  margin: 0; overflow-wrap: break-word;
}

.bf-note-edit {
  margin-top: 6px;
  border: none; background: transparent; cursor: pointer;
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 600;
  color: var(--text-subtle); padding: 0; text-decoration: underline;
}

.bf-note-edit:hover { color: var(--text-muted); }

/* Inline editor — one line, calm. */
.bf-note-editor { display: flex; flex-direction: column; gap: 8px; }

.bf-note-input {
  width: 100%; box-sizing: border-box;
  font-family: var(--font-body); font-size: 0.9rem; line-height: 1.5;
  color: var(--text-primary); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 8px; padding: 9px 11px;
}

.bf-note-input::placeholder { color: var(--text-subtle); }

.bf-note-input:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }

.bf-note-actions { display: flex; gap: 8px; align-items: center; }

.bf-note-save, .bf-note-cancel, .bf-note-delete {
  font-family: var(--font-body); font-size: 0.78rem; font-weight: 600; cursor: pointer;
  border-radius: 7px; padding: 6px 14px;
}

.bf-note-save { border: none; background: var(--accent-ink); color: #fff; }       /* olive-ink action, calm (matches the stepped-down email CTA), never raw lime */

.bf-note-cancel { border: 1px solid var(--border-strong); background: transparent; color: var(--text-muted); }

.bf-note-delete { margin-left: auto; border: none; background: transparent; color: var(--text-subtle); text-decoration: underline; padding: 6px 2px; }

.bf-note-save:focus-visible, .bf-note-cancel:focus-visible, .bf-note-delete:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* Calm free-cap note — mirrors .bf-nameday-limit-note exactly. */
.bf-note-limit {
  font-family: var(--font-body); font-size: 0.8125rem;
  color: var(--text-muted); margin: 8px 0 0;
}

.bf-note-limit-link { color: var(--text-primary); text-decoration: underline; }

html.theme-light .bf-note-limit-link { color: var(--accent-ink); }

.bf-wall {
  position: relative;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  width: 100%; margin-top: 12px; padding: 14px 40px 14px 15px;
  border-radius: 14px; border: 1px solid var(--border-accent);
  background: var(--accent-dim);
  font-family: var(--font-body);
  transition: opacity 200ms ease, transform 200ms ease;
}

.bf-wall.is-dismissing { opacity: 0; transform: translateY(-4px); }

/* a11y follow-up (2026-07-11): explicit, local reduced-motion guard for the
   wall's slide/fade — see the identical rationale comment on TURN
   (global.css, near mTurnIn/mTurnOut): the blanket `*` rule (global.css
   § Reduced motion) already forces this to near-instant today, so this is
   defense-in-depth/legibility, not a behavior change. Dismissal (the
   interaction) is untouched — only its motion is suppressed. */
@media (prefers-reduced-motion: reduce) {
  .bf-wall { transition-duration: 0.01ms; }
}

.bf-wall-head {
  margin: 0; font-size: 0.92rem; line-height: 1.35; color: var(--text-primary); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
}

html.theme-light .bf-wall-head { color: var(--accent-ink); }

.bf-wall-cta {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 14px 16px; border-radius: 10px;
  background: var(--accent); color: var(--accent-text);
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 700;
  text-decoration: none; cursor: pointer; border: none;
  transition: background 120ms ease, transform 120ms ease;
}

.bf-wall-cta:hover { background: var(--accent-hover); }

.bf-wall-cta:active { transform: scale(0.97); }

.bf-wall-close {
  position: absolute; top: 10px; right: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; border: none; padding: 0;
  background: transparent; color: var(--text-subtle); cursor: pointer;
}

.bf-wall-close:hover { color: var(--text-muted); background: color-mix(in srgb, var(--text-primary) 6%, transparent); }

/* Hit-slop only — the visible 26px circle is unchanged; -9px on every side
   brings the actual tappable area to 44x44 (same technique as .pf-icon-btn). */
.bf-wall-close::before {
  content: '';
  position: absolute;
  inset: -9px;
}

.bf-wall-close-ic svg { display: block; width: 14px; height: 14px; }

@keyframes bf-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* Reduced motion — the blur clears with a simple fade, no filter transition.
   a11y follow-up (2026-07-11), resolved: global.css's universal reduced-
   motion kill-switch (`*{transition-duration:0.01ms !important}`) wins over
   this rule's 160ms regardless of specificity, since !important beats
   non-!important outright — so this fade collapses to near-instant, same as
   everywhere else. Confirmed correct, not a gap: a media query that only
   ever suppresses/shortens motion never changes the guess-reveal LOGIC
   (content still appears the instant it's un-gated, just without an
   animated fade) — no device test needed for that class of change, only
   for anything that could alter WHEN/WHETHER content appears, which this
   doesn't. Left as-is rather than adding !important to force the 160ms
   through, since that would mean MORE motion for reduced-motion users than
   the already-compliant instant reveal, not less. */
@media (prefers-reduced-motion: reduce) {
  .bf-gated { transition: none; }
  .bf-guessmoment:not(.is-gated) .bf-gated { animation: bf-fade-in 160ms linear both; }
}

/* Inline icons. */
.bf-ic { display: inline-flex; align-items: center; flex: 0 0 auto; }

.bf-ic svg { display: block; }

/* WO-VIS Wave B: IC_LINK (card.js) used to hardcode stroke="#a89a7e" regardless
   of theme; now stroke="currentColor", scoped here to the muted tone it had. */
.bf-row-src .bf-ic { color: var(--text-subtle); }

/* Read-more disclosure — quiet inline; depth hidden by default (MOD 1). */
.bf-depth { margin-top: 24px; }

.bf-depth-summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-body); font-size: 0.84rem; font-weight: 600;
  color: var(--text-primary); padding: 12px 0; /* a11y medium-pass: was 4px (~28px tall); dark-theme small text is never lime — Wave D follow-up, light stays --accent-ink below */
}

html.theme-light .bf-depth-summary { color: var(--accent-ink); }

.bf-depth-summary::-webkit-details-marker { display: none; }

.bf-depth-summary:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-chev { color: var(--accent-ink); transition: transform 160ms ease; }

.bf-depth[open] .bf-chev { transform: rotate(180deg); }

.bf-depth-rows {
  margin-top: 14px; padding-top: 20px;
  border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px;
}

.bf-depth-context {
  font-family: var(--font-body); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-subtle); margin: 0 0 6px;
}

.bf-depth-row { display: flex; align-items: center; gap: 9px; padding: 11px 0; text-decoration: none; }

a.bf-depth-row { cursor: pointer; }

.bf-depth-sep { height: 1px; background: var(--border); }

.bf-row-text { font-family: var(--font-body); font-size: 0.84rem; color: var(--text-muted); overflow-wrap: break-word; }

.bf-row-strong { color: var(--text-primary); font-weight: 600; overflow-wrap: break-word; }

.bf-row-muted { color: var(--text-subtle); }

/* Source link — provenance, deliberately quiet (not a loud link); the title
   underlines subtly only on hover/focus. */
.bf-row-src { text-decoration: none; }

.bf-row-src .bf-row-strong { text-decoration: none; }

.bf-row-src:hover .bf-row-strong,
.bf-row-src:focus-visible .bf-row-strong { text-decoration: underline; }

.bf-row-src:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

/* ENTITY ARCHIVE — "More on <entity> — N pieces in the archive" (below the
   seam, after Read-more). Same disclosure shape as .bf-depth (details/
   summary, chevron rotates open) so it reads as one family of "there's more
   here, tap to see" affordances rather than a second, competing pattern. */
.bf-entity-archive { margin-top: 18px; }

.bf-entity-archive-summary {
  cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-body); font-size: 0.84rem; font-weight: 600;
  color: var(--text-primary); padding: 12px 0;
}

html.theme-light .bf-entity-archive-summary { color: var(--accent-ink); }

.bf-entity-archive-summary::-webkit-details-marker { display: none; }

.bf-entity-archive-summary:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-entity-archive[open] .bf-chev { transform: rotate(180deg); }

.bf-entity-archive-rows {
  margin-top: 10px; padding-top: 14px;
  border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 2px;
}

.bf-entity-archive-row {
  display: flex; align-items: center; gap: 10px;
  /* wrap so the R2 attribution line drops BELOW the hook rather than competing
     with it for the row's width — see .bf-entity-archive-source */
  flex-wrap: wrap;
  padding: 11px 0;
  text-decoration: none;
  font-family: var(--font-body); font-size: 0.84rem; color: var(--text-muted);
}

.bf-entity-archive-row:not(:last-child) { border-bottom: 1px solid var(--border); }

.bf-entity-archive-row:hover { color: var(--text-primary); }

.bf-entity-archive-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.bf-entity-archive-hook { flex: 1; overflow-wrap: break-word; }

.bf-entity-archive-row.is-locked .bf-entity-archive-hook { color: var(--text-subtle); }

/* R2 attribution on an archive row (CORPUS-RIGHTS.md R2) — its own full-width
   line under the hook, deliberately quieter than the hook it credits. Renders
   only when the endpoint supplied a source, so there is no empty line. */
.bf-entity-archive-source {
  flex: 1 0 100%;
  margin-top: 3px;
  font-size: 0.74rem; line-height: 1.35;
  color: var(--text-subtle);
  overflow-wrap: break-word;
}

.bf-entity-archive-lock { flex: 0 0 auto; color: var(--text-disabled); display: inline-flex; }

.bf-entity-archive-lock svg { display: block; }

/* Report flag — muted outline by default; on report the ICON fills olive
   (like the loved heart, both themes). The "Reported — thanks" TEXT goes
   neutral on dark (small text is never lime — Wave D follow-up) and stays
   olive on light. */
.bf-flag { color: var(--text-subtle); }

.bf-row-report.is-reported .bf-flag { color: var(--accent-ink); }

.bf-row-report.is-reported .bf-flag svg path { fill: currentColor; }

.bf-row-report.is-reported .bf-row-text { color: var(--text-primary); font-weight: 600; }

html.theme-light .bf-row-report.is-reported .bf-row-text { color: var(--accent-ink); }

.bf-row-soon {
  font-family: var(--font-body); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-subtle);
}

/* On This Day — year lead + lived-memory ("25 years ago today") badge. */
.bf-otd-year { color: var(--text-primary); }

.bf-otd-badge {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-muted); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below. Background wash (--accent-dim) is translucent, not a solid fill, so the button-fill exemption doesn't apply here */
  background: var(--accent-dim);
  border-radius: 999px;
  padding: 2px 9px;
  margin: 0 0 8px;
}

html.theme-light .bf-otd-badge { color: var(--accent-ink); }

.bf-report {
  display: inline-block;
  margin-left: 14px;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-subtle);
  text-decoration: underline;
}

.bf-report:hover { color: var(--text-primary); }

/* ── States ─────────────────────────────────────────────────────────────── */
.bf-state {
  text-align: center;
  font-family: var(--font-body);
  color: var(--text-subtle);
  padding: 40px 16px;
}

.bf-state .bf-state-h {
  font-family: var(--font-display);
  font-size: 1.3rem;
  color: var(--text-primary);
  margin: 0 0 8px;
  text-wrap: balance;
}

.bf-retry {
  margin-top: 14px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  padding: 9px 16px;
  border: 1.5px solid var(--border-strong);
  border-radius: 10px;
  background: var(--surface-4);
  color: var(--text-primary);
  cursor: pointer;
}

/* A6 — masthead-shaped loading skeleton, replacing the plain "Loading…" text.
   Mirrors .bf-daily's shape — imprint + nameplate + dateline bars in the mast,
   then hook/debrief lines in the body — so the page doesn't jump when the real
   card lands. Shimmer recipe ported from components.css's .skeleton (NOT
   loaded on briefing.html/archive.html, which only load global.css +
   briefing.css) — same GPU-friendly transform-sweep, scoped under bf- so it
   can't collide if components.css is ever added here later. Archive's
   #ar-loader reuses this same treatment (hardcoded HTML, still just
   .bf-skel-kicker — a single bar is enough for a loading TILE, not a full page
   mount, so it's untouched here). */
.bf-skeleton {
  border-radius: 22px;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
}

.bf-skel-mast {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 28px 14px;
  background: var(--surface-3);
  border-top: 2px solid var(--border-accent);
}

.bf-skel-imprint { width: 22%; height: 9px; }

.bf-skel-title { width: 46%; height: 20px; margin-top: 2px; }

.bf-skel-dateline { width: 64%; height: 11px; margin-top: 8px; }

.bf-skel-body { padding: 30px 32px 28px; display: flex; flex-direction: column; gap: 12px; }

.bf-skel-line {
  position: relative;
  overflow: hidden;
  border-radius: 6px;
  background: var(--surface-2);
}

.bf-skel-line::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--text-primary) 6%, transparent) 50%, transparent 100%);
  animation: bf-skel-sweep 1.3s ease-in-out infinite;
}

/* color-mix(var(--text-primary)) inverts per theme automatically (white tint
   dark, dark tint light), so the loading sweep stays visible without a
   separate light override. */
@keyframes bf-skel-sweep { to { transform: translateX(100%); } }

.bf-skel-kicker  { width: 34%; height: 12px; }

.bf-skel-hook    { width: 92%; height: 26px; margin-top: 6px; }

.bf-skel-hook-2  { width: 68%; height: 26px; }

.bf-skel-debrief   { width: 100%; height: 14px; margin-top: 10px; }

.bf-skel-debrief-2 { width: 82%; height: 14px; }

/* ── Bottom anonymous / signup prompt ───────────────────────────────────── */
.bf-foot { margin-top: 26px; text-align: center; }

.bf-signup {
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
  border-radius: 14px;
  padding: 18px 20px;
}

.bf-signup p { font-family: var(--font-body); }

.bf-signup-h {
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 4px;
}

.bf-signup-sub { font-size: 0.84rem; color: var(--text-subtle); margin: 0 0 14px; }

/* Waitlist capture — email + lime "Join the waitlist" (reuses waitlist-signup). */
.bf-wl { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }

.bf-wl-email {
  flex: 1 1 220px; max-width: 300px;
  font-family: var(--font-body); font-size: 0.92rem;
  padding: 11px 14px;
  border: 1.5px solid var(--border-strong);
  border-radius: 12px;
  background: var(--surface-4);
  color: var(--text-primary);
}

.bf-wl-email:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }

.bf-wl-btn {
  font-family: var(--font-display); font-weight: 600; font-size: 0.92rem;
  padding: 11px 20px; border: none; border-radius: 12px;
  background: var(--accent);      /* sanctioned lime fill: dark text on lime */
  color: var(--accent-text); cursor: pointer;
}

.bf-wl-btn:hover { background: var(--accent-hover); }

.bf-wl-btn:disabled { opacity: 0.6; cursor: default; }

/* Native variant's CTA is an <a>, not a <button> — .bf-wl-btn covers the shared
   fill/type; this just makes the link render like one. Sized up from the
   shared .bf-wl-btn baseline (2026-07-08 design pass) — this is the button an
   anon SAMPLE reader actually converts through, so it earns a bigger tap
   target + more visual weight than the plain "Join the waitlist" web form
   button it shares a base class with; that one is untouched. Cascade note:
   this rule must stay AFTER .bf-wl-btn in source order (same specificity —
   single class each) for these overrides to win. */
.bf-signup-cta {
  display: inline-block;
  text-decoration: none;
  font-size: 1.05rem;
  font-weight: 700;
  padding: 15px 30px;
}

.bf-wl-msg { font-family: var(--font-body); font-size: 0.82rem; margin: 10px 0 0; min-height: 1em; }

.bf-wl-msg.is-ok { color: var(--text-primary); font-weight: 600; } /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */

html.theme-light .bf-wl-msg.is-ok { color: var(--accent-ink); }

.bf-wl-msg.is-err { color: var(--error); }

.bf-anon {
  font-family: var(--font-body);
  font-size: 0.76rem;
  color: var(--text-subtle);
  margin: 12px 0 0;
}

/* ── The Archive — LEDGER OF DAYS GROUPED BY MONTH (2026-07-17 rebuild) ────────
   Index altitude, not reading altitude: a compact row per published day, not a
   card. Zero CTAs, zero lime anywhere in this block — the shelf is the
   argument (MONETIZATION-MODEL §8.1.1, amended); the one membership utterance
   on the page is the standing rule line in the header, not here. */
.ar-ledger { display: flex; flex-direction: column; gap: 34px; }

.ar-month { display: flex; flex-direction: column; }

.ar-month-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 0 2px 9px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 2px;
}

.ar-month-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.08rem;
  color: var(--text-primary);
  margin: 0;
}

/* Older-month count badge ("June 2026 — 30 pieces") — the title-depth
   fallback's compensating signal: past ARCHIVE_TITLE_DEPTH_MONTHS a section
   shows no titles (list-briefings.js withholds them server-side), so the
   count is what proves cadence/scale instead. Absent on a recent (titled)
   section — see js/archive.js's appendRow. */
.ar-month-count {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-subtle);
}

.ar-month-rows { display: flex; flex-direction: column; }

/* Rows are LINKS, not surfaces — a real <a href="/briefing.html?date=…">, no
   JS-intercepted click. Tapping a locked row opens the day view, which
   carries the spine + membership wall THERE (§8.1.1); this row never does. */
.ar-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 11px 2px;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: inherit;
  transition: background 120ms ease;
}

.ar-row:last-child { border-bottom: none; }

.ar-row:hover { background: var(--border); }

.ar-row:active { opacity: 0.7; }

.ar-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.ar-row-date {
  flex: 0 0 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.15;
}

.ar-row-daynum { font-family: var(--font-display); font-weight: 600; font-size: 1rem; color: var(--text-primary); }

.ar-row-weekday { font-family: var(--font-body); font-size: 0.6rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-subtle); }

/* Title dominates when present (recent months); absent past the depth
   threshold — a date-only row is deliberately lighter, not padded to match. */
.ar-row-title {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-size: 0.94rem;
  line-height: 1.35;
  color: var(--text-primary);
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

/* Today's badge is the ONLY visual difference an unlocked today's row ever
   carries. Neutral ink — never lime; this isn't a CTA. */
.ar-row-badge {
  flex: 0 0 auto;
  margin-left: auto;
  font-family: var(--font-body);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-subtle);
  white-space: nowrap;
}

/* The lock mark — a small seal, not a button; the pitch lives on the day view
   (briefing.html?date=), never here. */
.ar-row-lock { flex: 0 0 auto; margin-left: auto; color: var(--text-disabled); display: inline-flex; }

.ar-row-lock svg { display: block; }

.ar-loader {
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--text-subtle);
  padding: 18px 0;
}

#ar-sentinel { height: 1px; }

/* Locked teaser (WO-CONV-1) — the spine standard: hook + source, no body, no
   blur. Neutral ink throughout — the lime budget reserves the accent for a
   view's own membership-intent action (WO-CONV-2), not this passive read. */
.ar-locked {
  background: var(--surface);
  border: 1.5px dashed var(--border-strong);
  border-radius: 16px;
  padding: 28px 22px;
  text-align: center;
}

.ar-locked-hook {
  font-family: var(--font-display);
  font-size: 1.15rem;
  color: var(--text-primary);
  margin: 0 0 8px;
  text-wrap: balance;
}

/* Standfirst on the LOCKED spine (Ruling 1, 2026-07-29) — the identification
   line a reader meets before any detail, carried into the teaser so a locked
   day names its subject instead of only headlining it. Subordinate to
   .ar-locked-hook in exactly the way .bf-standfirst is to .bf-hook-text
   (smaller, body-face, muted) — the two-subject law applies here too. */
.ar-locked-standfirst {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-muted);
  max-width: 46ch;
  margin: 0 auto 10px;
  /* Same rag treatment as .ar-locked-hook above — a centred three-line clause
     otherwise orphans its closing date on a line of its own at desktop width. */
  text-wrap: balance;
}

.ar-locked-source {
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--text-subtle);
  margin: 0 0 10px;
}

/* WO-CONV-3: the Collection shelf's quiet sub-line under a frozen kept piece
   ("Kept 12 July · members reopen every piece") — archive's lockedTeaser
   calls omit this (subLine unset), so it never renders there. */
.ar-locked-subline {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-subtle);
  font-style: italic;
  margin: 0 0 10px;
}

/* THE SEAL (§ED240) — the shape of the withheld body, so a locked spine can
   no longer be mistaken for a short piece. Grammar lifted from index.html's
   .lv2-alm-seal (labelled sealed block, uppercased in the sheet, action
   beneath). ONE deliberate difference: the border is SOLID and the fill is
   surface-2, where the landing's seal is dashed — .ar-locked is itself a
   dashed box, and a dashed box directly inside a dashed box reads as a
   rendering fault rather than as nesting. Neutral ink throughout: no lime
   here, the budget is spent by the membership card once it is summoned. */
.ar-seal {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2, var(--surface));
  padding: 14px 16px 16px;
  margin: 0 auto 16px;
  max-width: 34ch;
  text-align: left;
}

.ar-seal-label {
  display: block;
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  /* Ratified as the label "THE STORY" (index.html:1635) — cased here, not in
     the DOM, for the same reason it is cased there. */
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 0 0 10px;
}

.ar-seal-shape { display: flex; flex-direction: column; gap: 7px; }

.ar-seal-rule {
  display: block;
  height: 7px;
  border-radius: 3px;
  background: var(--border-strong);
}

.ar-locked-today {
  font-family: var(--font-body);
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--text-primary);
  text-decoration: underline;
  margin: 0 0 14px;
  display: inline-block;
}

/* Tap-reveal for the membership card — deliberately NEUTRAL, not lime (see
   lockedTeaser's comment: the lime budget is reserved for the card's own CTA
   once summoned, never for the reveal action itself). */
.ar-locked-reveal {
  display: block;
  margin: 0 auto;
  padding: 10px 16px;
  border-radius: 10px;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-subtle);
  font-family: var(--font-body);
  font-size: 0.82rem;
  cursor: pointer;
}

.ar-locked-reveal:hover { background: color-mix(in srgb, var(--text-primary) 5%, transparent); }

/* THE MEMBERSHIP CARD (WO-CONV-2, §8.1/§8.3) — one component, every wall.
   Neutral concrete throughout except the single lime CTA (the palette
   invariant's "one primary action per view" — this card's only chromatic
   element). Lives inside .ar-locked, directly under the spine + the
   "read today free" link, never a modal. */
.bf-mwall {
  border-top: 1px solid var(--border);
  margin-top: 6px;
  padding-top: 16px;
  text-align: left;
}

.bf-mwall-head {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 400;
  color: var(--text-primary);
  margin: 0 0 8px;
  text-wrap: balance;
}

html.theme-light .bf-mwall-head { color: var(--accent-ink); }

.bf-mwall-body {
  font-family: var(--font-body);
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--text-subtle);
  margin: 0 0 10px;
}

.bf-mwall-status {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0 0 14px;
}

.bf-mwall-cta {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 13px 18px; border-radius: 10px;
  background: var(--accent); color: var(--accent-text);
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 700;
  border: none; cursor: pointer;
  transition: background 120ms ease, transform 120ms ease;
}

.bf-mwall-cta:hover { background: var(--accent-hover); }

.bf-mwall-cta:active { transform: scale(0.97); }

/* §8-A free-account card's secondary action — plain text, never a second
   lime element alongside .bf-mwall-cta in the same view (palette invariant). */
.bf-mwall-login {
  display: block; margin-top: 12px;
  font-family: var(--font-body); font-size: 0.8rem;
  color: var(--text-subtle); text-decoration: underline;
}

.bf-mwall-login:hover { color: var(--text-primary); }

/* Quiet link to /membership.html (the sales-gap fix, ruled 2026-07-17) — same
   neutral-ink treatment as .bf-mwall-login, for the identical reason: never a
   second lime element alongside the card's own CTA/plan buttons. */
.bf-mwall-more {
  display: block; margin-top: 12px;
  font-family: var(--font-body); font-size: 0.8rem;
  color: var(--text-subtle); text-decoration: underline;
}

.bf-mwall-more:hover { color: var(--text-primary); }

.bf-mwall-form {
  display: flex; gap: 8px; flex-wrap: wrap;
}

.bf-mwall-email {
  flex: 1 1 200px; min-width: 0;
  padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--border-strong);
  background: var(--surface-2, var(--surface));
  color: var(--text-primary);
  font-family: var(--font-body); font-size: 0.86rem;
}

.bf-mwall-email:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.bf-mwall-submit {
  padding: 12px 16px; border-radius: 10px;
  background: var(--accent); color: var(--accent-text);
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 700;
  border: none; cursor: pointer; white-space: nowrap;
}

.bf-mwall-submit:hover { background: var(--accent-hover); }

.bf-mwall-submit:disabled { opacity: 0.65; cursor: default; }

.bf-mwall-error { font-family: var(--font-body); font-size: 0.78rem; color: var(--error, #b3261e); margin: 8px 0 0; }

.bf-mwall-error:empty { margin: 0; }

.bf-mwall-done {
  font-family: var(--font-body); font-size: 0.86rem; font-weight: 600;
  color: var(--text-primary); margin: 0;
}

/* Art.16(m) immediate-access withdrawal-waiver consent (terms.html "Right of
   withdrawal (EU/UK)") — sits between the status line and the plan picker,
   so it's read and actively checked BEFORE a plan tap can fire checkout.
   Plain concrete/border checkbox, no lime (this is an affirmation control,
   not the primary action — the plan buttons remain the CTAs). */
.bf-mwall-consent {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 12px; cursor: pointer;
}

.bf-mwall-consent-box {
  flex: 0 0 auto; margin: 2px 0 0;
  width: 16px; height: 16px;
  accent-color: var(--accent-ink, var(--accent));
  cursor: pointer;
}

.bf-mwall-consent-text {
  font-family: var(--font-body); font-size: 0.78rem; line-height: 1.45;
  color: var(--text-subtle);
}

.bf-mwall-consent-text a {
  color: var(--text-primary); text-decoration: underline; text-underline-offset: 2px;
}

.bf-mwall-consent-text a:hover { color: var(--accent-ink, var(--accent)); }

/* Monthly/annual checkout picker — two co-equal choices, so all neutral (no
   lime): the palette invariant's "one primary action per view" budget
   doesn't apply to a menu with no single primary among them. The annual
   option carries the founding-rate offer (MONETIZATION-MODEL §7-P): a quiet
   struck-through list price + the active founding price + a small label —
   deliberately restrained (no red slash, no badge chrome) per the calm
   editorial register; still no lime, same budget reasoning as above. */
.bf-mwall-picker {
  display: flex; gap: 8px; flex-wrap: wrap;
}

.bf-mwall-plan {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 12px 16px; border-radius: 10px;
  background: transparent; color: var(--text-primary);
  border: 1px solid var(--border-strong);
  font-family: var(--font-body); font-size: 0.86rem;
  cursor: pointer; text-align: left;
  transition: border-color 120ms ease, background 120ms ease;
}

.bf-mwall-plan:hover { border-color: var(--accent); }

.bf-mwall-plan:disabled { opacity: 0.55; cursor: default; }

.bf-mwall-plan-label { font-weight: 600; }

.bf-mwall-plan-price {
  display: flex; align-items: baseline; gap: 6px;
  font-family: var(--font-numeric); font-weight: 700;
}

.bf-mwall-plan-strike {
  font-weight: 400; font-size: 0.82rem;
  color: var(--text-subtle); text-decoration: line-through;
}

.bf-mwall-plan-note {
  font-family: var(--font-body); font-weight: 400; font-size: 0.72rem;
  color: var(--text-subtle);
}

@media (max-width: 480px) {
  .bf-mwall-form { flex-direction: column; }
  /* flex-basis:200px on .bf-mwall-email means WIDTH in the desktop row
     layout above, but the SAME basis reads as HEIGHT once flex-direction
     flips to column here — reset to auto or the input stretches tall. */
  .bf-mwall-email { flex: 0 0 auto; }
  .bf-mwall-cta, .bf-mwall-submit { width: 100%; }
  .bf-mwall-picker { flex-direction: column; }
  .bf-mwall-plan { width: 100%; }
}

/* ── Responsive ─────────────────────────────────────────────────────────────
   Desktop keeps the generous editorial feel above. On phones (~≤480px) the card
   scales down, padding tightens, and every tap target stays ≥44px. The shared
   card.js means this covers BOTH the /briefing card and the /archive scroll. */
@media (max-width: 480px) {
  .bf-page { padding: calc(12px + var(--safe-top)) 12px 52px; }   /* + camera/status-bar inset (phone width → this rule wins) */
  .bf-wrap { max-width: 100%; }

  /* Mobile-only: tighter nameplate padding, same left-aligned two-row layout. */
  .bf-masthead { padding: 11px 18px 10px; }
  .bf-mast-wordmark { font-size: 1.2rem; }
  .bf-daily-body { padding: 16px 18px 22px; }   /* tightened top — less pre-fact scroll */

  .bf-quote { padding-top: 20px; }   /* still generous, just a touch tighter than desktop's 26px */
  .bf-accent-dash { margin-bottom: 11px; }   /* slightly tighter on phones */
  .bf-hook-text { font-size: 1.75rem; line-height: 1.2; }         /* smaller but still the hero */
  .bf-hook-text.is-short { font-size: 2rem; }
  .bf-standfirst { font-size: 0.86rem; }
  .bf-fact-deck { font-size: 1.05rem; line-height: 1.48; }
  .bf-debrief { font-size: 0.98rem; line-height: 1.58; }

  .bf-reaction { gap: 10px; margin-top: 22px; }
  .bf-love-pill { padding: 12px 18px; }                     /* ≥44px touch target */
  .bf-like-pill { padding: 12px 18px; }                     /* §ED240 — same */
  .bf-social { font-size: 0.76rem; }

  /* a11y medium-pass: base rule's padding:12px already covers mobile too — this
     10px override would have REGRESSED it below the new 44px floor. Removed. */
  .bf-depth-row { padding: 13px 0; }                        /* ≥44px touch target */

  /* Culture selector wraps and stays tappable on narrow screens. */
  .bf-cults { gap: 6px; }
  .bf-cult { padding: 6px 6px; }
}

/* Larger phones / small tablets — a gentle middle step. */
@media (min-width: 481px) and (max-width: 640px) {
  .bf-daily-body { padding: 18px 24px 24px; }
  .bf-hook-text { font-size: 2.1rem; }
}

/* Desktop width for .bf-wrap lives entirely in the DESKTOP LAYER block
   below (WO-WEB-2, ≥900px → 720px) — that rule comes later in the
   cascade and already wins over any ≥768px/≥1024px value here, so the
   old pre-WO-WEB-2 steps that used to live at this point were dead code
   (only ever "live" in the narrow 768-899px gap, which is not one of
   this app's supported tiers — see docs/APP-WEB-ARCHITECTURE.md's single
   900px breakpoint rule). Removed rather than left shadowed. */
/* ── Archive jump-to-date calendar (design direction C · "The Strip") ────────
   A dropdown navigation aid within the archive scroll (not a page). Olive header
   band, cream grid, lime dots on briefing days, olive-filled today, legend, and a
   "Worth revisiting" list of the most-loved past dailies. */
/* §ED529 — `z-index: 45 /* trigger + panel above the scrim (40) *\/` DELETED.
   It never did that. On /briefing .ar-calbar is a flex item of .bf-header-row,
   so the 45 did create a stacking context — but one nested inside .bf-header's
   own context at 20, so neither the trigger nor the panel was ever above the
   scrim. A declaration measured never to have had its stated effect is deleted,
   not re-commented. position:relative stays: /archive still centres the bar. */
.ar-calbar { text-align: center; margin: 0 0 18px; position: relative; }

/* /briefing ONLY: .ar-calbar sits inside .bf-header-row (flex, space-between,
   "culture pill left, calendar trigger right"), so it's shrunk to the
   trigger's own width and flush against the row's right edge — .ar-cal-panel
   centering "within" that narrow box pushed the panel off the right edge of
   the content column. Un-positioning it here hands .ar-cal-panel's containing
   block up to .bf-header-row (the full-width row, see its position:relative
   above), so the panel centers on the column instead. /archive's .ar-calbar
   is a standalone full-width block (not nested in .bf-header-row) — this
   selector doesn't match there, so its calendar is untouched. */
.bf-header-row .ar-calbar { position: static; }

.ar-cal-trigger {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 13px;
  padding: 10px 15px; cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
  font-family: var(--font-body); font-size: 0.88rem; font-weight: 600; color: var(--text-primary);
  transition: background 140ms ease, transform 120ms ease, box-shadow 140ms ease;
}

.ar-cal-trigger:hover { background: var(--surface-3); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5); }

.ar-cal-trigger:active { transform: scale(0.97); }

.ar-cal-trigger:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

/* R5: the two icon SVGs hardcoded #4A6300/#8a7f68 strokes regardless of theme
   (never adapted for dark mode) — now `stroke="currentColor"` + token-driven
   color here instead. */
.ar-cal-ico { color: var(--accent-ink); }

.ar-cal-chev { color: var(--text-subtle); transition: transform 160ms ease; }

.ar-cal-trigger.is-open .ar-cal-chev { transform: rotate(180deg); }

/* Scrim — soft blurred layer over the feed while the calendar floats. R5:
   background moved to T0's --scrim token (the guess-panel's OWN backdrop-
   filter is gone, R5 item #2, but this small blur(2px) modal scrim is a
   separate, much cheaper case — left as-is). */
/* §ED529 — z-index RAISED 40 -> 9100, AND THE PANEL MOVED OUT FROM UNDER IT.
   The 40 was never the number that decided this. The panel carried z-index 50
   and still rendered UNDER the scrim, dimmed and blurred with the rest of the
   page, because it lived inside .bf-header — which is a stacking context three
   times over (position:sticky + z-index:20, transform:translateZ(0), and
   backdrop-filter). Its 50 was scoped INSIDE that context; against the root the
   whole header painted at 20, below this scrim's 40. Measured, not read:
   elementFromPoint() at the centre of the open panel's own month header
   returned `DIV.ar-cal-scrim is-open`.
   Raising a z-index alone could not have fixed that, so js/card.js's
   mountCalendar now reparents BOTH this scrim and .ar-cal-panel to <body>,
   where they are siblings in the root stacking context and these numbers
   finally mean what they say. See scripts/audit-ed529-chrome.js. */
.ar-cal-scrim {
  position: fixed; inset: 0; z-index: 9100;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 200ms ease-in, visibility 0s linear 200ms;
}

.ar-cal-scrim.is-open { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 200ms ease-out, visibility 0s; }

/* Panel — FLOATS over the feed (does not push it), anchored below the trigger.
   §ED529 — position:absolute -> fixed, and top/left are now written by
   mountCalendar's positionPanel() rather than by this sheet. The element is a
   <body> child now (see the scrim note above), so it has no positioned ancestor
   to anchor to; the anchor box is measured at open time from .bf-header-row on
   /briefing and .ar-calbar on /archive — the SAME two boxes that used to be its
   containing block, so the resting geometry is unchanged in both places.
   z-index 9110 sits just above the scrim's 9100, and both are now in the root
   stacking context where that comparison is actually performed. */
.ar-cal-panel {
  position: fixed; top: 0; left: 0;
  width: min(380px, calc(100vw - 28px));
  z-index: 9110; text-align: left;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px) scale(0.96); transform-origin: top center;
  transition: opacity 200ms ease-in, transform 200ms ease-in, visibility 0s linear 200ms;
}

.ar-cal-panel.is-open {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateY(0) scale(1);
  transition: opacity 200ms ease-out, transform 200ms cubic-bezier(0.22, 1, 0.36, 1), visibility 0s;
}

.ar-cal {
  background: var(--surface); border: 1px solid var(--border); border-radius: 20px; overflow: hidden;
  /* Matches the daily card exactly — the shared --shadow-hero token (global.css
     Phase A), which this panel's glow recipe was promoted FROM. */
  box-shadow: var(--shadow-hero);
}

/* header band — elevated by luminance */
.ar-cal-head { background: var(--surface-3); padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; }

.ar-cal-nav {
  width: 44px; height: 44px; /* a11y medium-pass (2026-07-11): was 34x34 */
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 10px; background: var(--border); border: none; cursor: pointer;
  color: var(--text-subtle); /* WO-VIS Wave B: CAL_CHEV_L/R (card.js) used to
     hardcode stroke="#EAF4C8" regardless of theme; now stroke="currentColor" */
  transition: background 120ms ease, transform 120ms ease;
}

.ar-cal-nav:hover { background: var(--border-strong); }

.ar-cal-nav:active { transform: scale(0.9); }

.ar-cal-nav:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }

.ar-cal-titlewrap { display: flex; align-items: baseline; gap: 8px; }

.ar-cal-month { font-family: var(--font-display); font-weight: 500; font-size: 1.4rem; color: var(--text-primary); }

.ar-cal-year { font-family: var(--font-body); font-size: 0.8rem; color: var(--text-subtle); }

.ar-cal-body { padding: 16px 20px 14px; }

.ar-cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 6px; }

.ar-cal-dowd { text-align: center; font-family: var(--font-body); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.04em; color: var(--text-subtle); }

.ar-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.ar-cal-cell {
  position: relative; height: 44px; /* a11y medium-pass (2026-07-11): was 38px */
  display: flex; align-items: center; justify-content: center;
  border-radius: 9px; font-family: var(--font-body); font-size: 0.86rem; color: var(--text-subtle);
  transition: background 120ms ease, transform 120ms ease;
  /* Non-selectable days (empty / future / still-loading) are NOT inputs — show the
     default arrow, never a text I-beam. Only .has-dot / .is-today opt back into a
     pointer below. Kills the "typing cursor on a blank calendar" look. */
  cursor: default; user-select: none; -webkit-user-select: none;
}

.ar-cal-cell.is-blank { visibility: hidden; }

.ar-cal-cell.is-empty { color: var(--text-subtle); }

.ar-cal-cell.is-future { color: var(--text-disabled); opacity: 0.5; }

.ar-cal-cell.has-dot { color: var(--text-primary); font-weight: 600; cursor: pointer; }

.ar-cal-cell.has-dot:hover { background: var(--surface-3); }

.ar-cal-cell.is-today { background: var(--accent); color: var(--accent-text); font-weight: 700; cursor: pointer; }

.ar-cal-cell.is-today:hover { background: var(--accent-hover); }

.ar-cal-cell.has-dot:active, .ar-cal-cell.is-today:active { transform: scale(0.9); }

.ar-cal-cell:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }

/* The day currently shown in the isolated single-day view. Explicit state (set via
   mountCalendar.setSelected) — an ink ring, distinct from today's lime fill.
   (Was a bare-lime ring; thin strokes need --accent-ink on light, §3a.) */
.ar-cal-cell.is-selected { box-shadow: inset 0 0 0 2px var(--accent-ink); color: var(--text-primary); font-weight: 700; cursor: pointer; }

/* The has-edition marker repeats on every day with content — dozens can be on
   screen at once, so it's a density/wayfinding mark, not "the" signal; neutral
   ink. Today's own marker stays lime (is-today, above) — it's the one that
   matters on this view. */
.ar-cal-dot { position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); width: 5px; height: 5px; border-radius: 50%; background: var(--text-muted); }

.ar-cal-legend { display: flex; align-items: center; gap: 14px; padding: 0 20px 14px; }

.ar-cal-leg { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-body); font-size: 0.72rem; color: var(--text-subtle); }

.ar-cal-leg-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--text-muted); }

.ar-cal-leg-today { width: 11px; height: 11px; border-radius: 4px; background: var(--accent); }

/* "Worth revisiting" — most-loved past dailies */
.ar-worth { background: var(--surface-2); border-top: 1px solid var(--border); padding: 16px 20px 18px; }

.ar-worth-label { font-family: var(--font-body); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-subtle); margin: 0; }

.ar-worth-list { display: flex; flex-direction: column; margin-top: 10px; }

.ar-worth-row {
  display: flex; align-items: baseline; gap: 12px; width: 100%; text-align: left;
  background: none; border: none; border-bottom: 1px solid var(--border); padding: 9px 8px; cursor: pointer;
  border-radius: 8px;
  transition: background 120ms ease, opacity 120ms ease;
}

.ar-worth-row:last-child { border-bottom: none; }

.ar-worth-row:hover { background: var(--border); }

.ar-worth-row:hover .ar-worth-t { text-decoration: underline; }

.ar-worth-row:active { opacity: 0.65; }

.ar-worth-row:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

.ar-worth-d { flex: 0 0 34px; font-family: var(--font-display); font-size: 0.88rem; font-weight: 600; color: var(--text-primary); } /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */

html.theme-light .ar-worth-d { color: var(--accent-ink); }

.ar-worth-t { flex: 1; font-family: var(--font-display); font-size: 0.92rem; line-height: 1.3; color: var(--text-primary); }

/* Archive door inside the calendar panel (Daily only) — the deleted page-header
   "View all daily facts →" link now lives inside the time-travel control. */
.ar-cal-foot { border-top: 1px solid var(--border); padding: 12px 20px; text-align: center; }

.ar-cal-foot-link {
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-primary); /* dark-theme small text is never lime — Wave D follow-up; light stays --accent-ink below */
  text-decoration: none;
}

html.theme-light .ar-cal-foot-link { color: var(--accent-ink); }

.ar-cal-foot-link:hover { text-decoration: underline; }

.ar-cal-foot-link:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: 4px; }

/* Brief highlight when the calendar jumps to a day's row (jumpToDate). */
@keyframes ar-flash { 0% { box-shadow: 0 0 0 3px transparent; } 25% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent); } 100% { box-shadow: 0 0 0 3px transparent; } }

.ar-row-flash { animation: ar-flash 1.6s ease; border-radius: 10px; }

/* Calendar — respect reduced motion (fade only, no transform/scale animation). */
@media (prefers-reduced-motion: reduce) {
  .ar-cal-panel, .ar-cal-panel.is-open { transform: none; transition: opacity 120ms linear, visibility 0s; }
  .ar-cal-panel:not(.is-open) { transition: opacity 120ms linear, visibility 0s linear 120ms; }
  .ar-cal-scrim, .ar-cal-scrim.is-open { transition: opacity 120ms linear, visibility 0s; }
  .ar-cal-cell, .ar-cal-nav, .ar-worth-row, .ar-cal-trigger, .ar-cal-chev, .ar-row { transition: none; }
  .ar-cal-cell:active, .ar-cal-nav:active, .ar-cal-trigger:active { transform: none; }
  /* §ED176: the selector now carries the calendar trigger's transition and
     press, so it must carry its reduced-motion exemption too. */
  .bf-cult-trigger { transition: none; }
  .bf-cult-trigger:active { transform: none; }
  .ar-row-flash { animation: none; }
  .bf-daily { transition: none; }
  .bf-daily:hover { transform: none; }
}

/* Calendar — comfortable tap targets on phones (≥44px). */
@media (max-width: 480px) {
  /* a11y medium-pass (2026-07-11): .ar-cal-cell/.ar-cal-nav's base rules are
     now 44px themselves — the 40px .ar-cal-nav override here would have
     REGRESSED it below the floor on mobile specifically. Removed; .ar-cal-cell's
     44px is now redundant with the base rule but harmless, left as-is. */
  .ar-cal-cell { height: 44px; }
  .ar-worth-row { padding: 12px 8px; }
  .ar-cal-trigger { padding: 12px 16px; }
}

/* ── Shared bottom nav (chunk 6) ─────────────────────────────────────────────
   The Daily now carries the ONE shared bottom nav (rendered by js/bottom-nav.js).
   These rules are a self-contained copy of the .bottom-nav block from
   components.css — the Daily pages load global.css (for the vars) + briefing.css
   but NOT components.css, so the nav styling lives here. Keep in sync if the
   nav chrome ever changes. Also reserve bottom space so the fixed nav never
   covers the footer / last card. */
/* --nav-bottom-h already includes the home-indicator inset, so it's not added
   again here (would double the bottom gap). */
.bf-page { padding-bottom: calc(var(--nav-bottom-h) + 28px); }

@media (max-width: 480px) {
  .bf-page { padding-bottom: calc(var(--nav-bottom-h) + 20px); }
}

/* Floating pill — kept in lockstep with the .bottom-nav block in components.css.
   Outer = inset carrier (height + padding-bottom preserve the safe-area contract,
   untouched); the pill chrome lives on .bottom-nav-inner. */
.bottom-nav {
  position: fixed; bottom: 0; left: 0; right: 0;
  height: var(--nav-bottom-h);
  background: transparent;        /* was var(--surface) */
  border-top: none;               /* was 1px solid var(--border) */
  z-index: var(--z-sticky);
  display: flex; align-items: center; justify-content: center;
  padding-bottom: var(--safe-bottom, 0px);
  padding-left: 16px; padding-right: 16px;   /* gutter from screen edge */
  pointer-events: none;           /* transparent margins never eat taps */
  overflow: visible;              /* active circle pops above the pill */
  /* Do NOT add view-transition-name — see the .bottom-nav note in
     components.css. Cross-document View Transitions are now fully disabled
     (global.css), so this is moot for the current jump, but re-enabling VT
     without also re-solving the cross-page snapshot mismatch would bring it
     back regardless of naming. */
}

.bottom-nav-inner {
  display: flex; width: 100%; max-width: var(--app-width); margin-inline: auto;
  height: 58px; padding: 0 8px;
  border-radius: var(--r-full);
  /* §ED441 §7b / DELETED §ED451: `background: rgba(18,18,20,.92)` lived here.
     §ED441 rendered it in all four surfaces (web light, web dark, shell light,
     shell dark) and the literal LOST every time — global.css's
     --c-glass-chrome wins on the web, app-nav.css's --surface in the shell. It
     painted nowhere. Removed rather than left as clutter because the failure
     mode is invisible: it is one specificity bump away from repainting the bar
     near-black, and a colour that did not change looks exactly like a colour
     that was not meant to change (§ED440 T1).

     THE `border` AND `box-shadow` BELOW ARE LEFT IN PLACE DELIBERATELY. Both
     are also overridden (global.css's specular-edge block on the web,
     app-nav.css §5 in the shell), so both are very probably dead too — but
     §ED441 §7b MEASURED the background only, and this pass deletes what was
     measured, not what is inferred. Filed as a follow-up rather than taken on
     reasoning. */
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  pointer-events: auto; overflow: visible;
}

.bottom-nav-item {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  cursor: pointer; color: rgba(255, 255, 255, 0.68);
  transition: color var(--t-fast) var(--ease);
  border: none; background: none; padding: var(--sp-1) 2px;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation; position: relative;
  text-decoration: none; overflow: visible;
}

.bottom-nav-item:hover { color: rgba(255, 255, 255, 0.9); }

.bottom-nav-item.is-active {
  outline: none;   /* glyph is fixed lime + label hidden, so no item colour needed */
}

.bottom-nav-item.is-active .bottom-nav-label { visibility: hidden; }

/* Round 4 — kept in lockstep with components.css (which is the canonical copy;
   read the NAV-JUMP HISTORY comment there for the full round 1-4 history). The
   active tab's pop circle is now an ALWAYS-PRESENT, ALWAYS-SAME-POSITION layer
   (.bottom-nav-pop) for every tab, on every page — only its opacity toggles.
   Nothing about its position ever changes on any state, so there's no position
   for any transition mechanism (CSS, native, or unknown) to ever mis-render. */
.bottom-nav { transform: none !important; }

.bottom-nav-item { transform: none !important; touch-action: manipulation; }

.bottom-nav-item:active { transform: none !important; }

.bottom-nav-item:focus:not(.is-active) { transform: none !important; outline: none; }

.bottom-nav-item:active:not(.is-active) { outline: none; }

.bottom-nav-icon {
  line-height: 1; position: relative; display: inline-flex; align-items: center; justify-content: center;
  transform: none !important; transition: none !important;
}

.bottom-nav-icon-rest { transition: opacity 100ms ease !important; }

.bottom-nav-item.is-active .bottom-nav-icon-rest { opacity: 0; }

/* -6px (structural rearchitecture pass, was -18px): kept in lockstep with the
   .bottom-nav-pop note in components.css — the item stretches to the pill's
   full height, so its top edge is the pill's top edge; -6px contains the
   circle inside the pill (~6px protrusion) instead of floating above it. */
/* §ED441 §7c / DELETED §ED451 — the disc chrome only. The layer, its
   `position: absolute`, its opacity pair and its 100ms transition stay: they
   are the regular->fill cross-fade that IS the active state on both surfaces.
   See the fuller note at the same rule in css/components.css. */
.bottom-nav-pop {
  position: absolute;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 100ms ease !important;
}

.bottom-nav-item.is-active .bottom-nav-pop { opacity: 1; }

.bottom-nav-item.is-active .bottom-nav-icon { transform: none !important; }

.bottom-nav-item * { transform: none !important; }

/* var(--accent), not --accent-ink: the pill is a fixed near-black floating
   dock in BOTH themes (not theme-following), so the icon always needs the
   bright tier for contrast — --accent-ink is tuned for LIGHT backgrounds,
   which this pill never has. */
.bottom-nav-svg-icon { display: block; color: var(--accent); }

/* §ED441 §7b / DELETED §ED451: the `font-size: 9px` on .bottom-nav-label
   resolved nowhere — 11px on the web, 10px in the shell. See the fuller note
   at the same rule in css/components.css. */
.bottom-nav-label { font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; line-height: 1; }

/* ── DESKTOP LAYER (WO-WEB-2) — additive ≥900px only; base/mobile rules above are LAW, never edited ── */
@media (min-width: 900px) {
  .bottom-nav { display: none; }

  .bf-page { padding-top: 64px; padding-bottom: 104px; }
  .bf-wrap { max-width: 720px; }

  .bf-masthead { padding: 24px 64px 18px; }
  .bf-mast-wordmark { font-size: 1.75rem; }
  .bf-daily-body { padding: 40px 64px 48px; }

  .bf-quote { padding-top: 32px; }
  .bf-hook-text { font-size: 2.75rem; line-height: 1.15; letter-spacing: -0.02em; }
  .bf-hook-text.is-short { font-size: 3.5rem; line-height: 1.08; letter-spacing: -0.025em; }
  .bf-rootline { font-size: 1.125rem; }
  .bf-standfirst { font-size: 1rem; margin-top: 12px; }
  .bf-fact-deck { font-size: 1.25rem; line-height: 1.52; }
  .bf-debrief { font-size: 1.0625rem; line-height: 1.65; }
  .bf-trust, .bf-mast-dateline { font-size: 0.8125rem; }

  .bf-stack { gap: 24px; }

  /* Entrance — one-shot fade + rise on the stack's direct children, staggered
     by nth-child in 80ms steps and capped (a long stack never queues a late
     card behind a multi-second delay). Reduced motion already collapses
     globally (global.css's universal kill-switch further down this cascade),
     but this stays wrapped in its own no-preference guard too — same
     defense-in-depth convention as .bf-wall's local guard above — so
     reduced-motion users never even see the keyframes referenced. */
  @media (prefers-reduced-motion: no-preference) {
    @keyframes bf-desktop-enter {
      from { opacity: 0; transform: translateY(12px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    .bf-stack > * {
      animation: bf-desktop-enter 450ms cubic-bezier(0.22, 1, 0.36, 1) both;
    }
    .bf-stack > *:nth-child(1) { animation-delay: 0ms; }
    .bf-stack > *:nth-child(2) { animation-delay: 80ms; }
    .bf-stack > *:nth-child(3) { animation-delay: 160ms; }
    .bf-stack > *:nth-child(4) { animation-delay: 240ms; }
    .bf-stack > *:nth-child(5) { animation-delay: 320ms; }
    .bf-stack > *:nth-child(n+6) { animation-delay: 400ms; }
  }
}

/* ═══ SCREEN 1 · THE CONTINUITY GATE (§ED110, RE-INHERITED §ED112) ══════════
   The screen an anonymous reader meets AFTER spending their one complete
   edition (§ED109). Ruled by Jake to inherit the landing system WHOLESALE
   (§ED108-4).

   §ED110 MIRRORED four landing rulesets into this file, because briefing.html
   loaded global.css + briefing.css only and landing-v2.css was not on the
   page. §ED112 fixed THE CAUSE, not the symptom: briefing.html now links
   css/landing-v2.css, and the gate's markup (js/card.js buildContinuityGate)
   carries the landing's OWN classes — .lv2-eyebrow, .lv2-h1, .lv2-cta,
   .lv2-cta-secondary. The four mirrored rulesets (.bf-gate-eyebrow, -h1,
   -cta, -secondary) are DELETED. There is now ONE source of truth for the
   landing identity: css/landing-v2.css. Do not re-copy them here.

   WHAT THAT CHANGED, AND IT IS THE WHOLE POINT: .bf-gate-h1 had eased the
   landing's clamp one step down — clamp(1.875rem, 5vw, 2.75rem) against
   .lv2-h1's clamp(2.5rem, 6.5vw, 4rem). That easing is what Jake read as
   "small". It is gone. The gate headline is now the landing hero's scale
   exactly: 30px → 40px at 390px wide, 44px → 64px at desktop.

   WHAT SURVIVES HERE, AND ONLY THIS: the wrapper, which has no landing
   equivalent — the landing hero sits at the top of a cold page; this sits at
   the foot of a reading column that has already run its full length and needs
   its own rule and rhythm — plus two POSITION-ONLY overrides, named below.
   Face, size, weight, colour and tracking all still come from landing-v2.css.

   LIME BUDGET (VISUAL-IDENTITY §3a): exactly ONE lime element on this screen —
   the .lv2-cta fill, inherited with its ACHROMATIC shadow (§4 bans coloured
   object-shadows). Eyebrow, headline and secondary link carry no accent.
   ═════════════════════════════════════════════════════════════════════════ */
.bf-gate {
  text-align: center;
  margin: var(--sp-10) auto 0;
  padding: var(--sp-10) var(--sp-5) var(--sp-8);
  max-width: 560px;
  border-top: 1px solid var(--border);
}

/* POSITION-ONLY №1. The landing puts .lv2-sub between h1 and CTA (a further
   32px of air); the gate CUTS .lv2-sub (js/card.js says why), so the h1's own
   20px bottom margin would leave the CTA crowded. Spacing, not type. */
.bf-gate .lv2-h1 { margin-bottom: var(--sp-8); }

/* POSITION-ONLY №2. .lv2-cta-secondary is inline-block on the landing, sitting
   beside other inline content; in the gate it is the only thing on its line. */
.bf-gate .lv2-cta-secondary { display: block; }

/* ═══ SCREEN 2 · THE MEMBERSHIP OFFER (§ED112) ══════════════════════════════
   The screen that explains what membership IS. It did not exist before
   §ED112: a new free reader went from signup straight into the product with
   no moment that ever said what the paid tier gives.

   SHAPE IS RULED, NOT CHOSEN. MONETIZATION-MODEL.md §366: "An inline card —
   never a modal, never an interstitial, never navigation away." So this is a
   surface card appended at the FOOT of a finished edition, never a thing
   between a reader and today's card (THE-BREAKOUT-THESIS §1a — see the timing
   block in js/card.js, which is where the ruling is enforced).

   IDENTITY IS INHERITED, NOT REBUILT. Shell values are .lv2-almanac's
   (landing-v2.css: --surface, 1px --border, radius 12, 32px 28px). Everything
   inside carries the landing's own classes: .lv2-h1 (headline), .lv2-sub
   (body), .lv2-cta (the one lime action), .lv2-cta-micro (status). No new
   type scale is invented here and none is mirrored — same rule as Screen 1.

   LIME BUDGET: one — the .lv2-cta fill. The dismiss control is a hairline
   glyph button carrying no accent, reusing .bf-wall-close's grammar.
   ═════════════════════════════════════════════════════════════════════════ */
.bf-offer {
  position: relative;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px 28px;
  margin: var(--sp-10) auto 0;
  max-width: 620px;
}

/* Card rhythm — the landing hero's 20px/32px cadence compressed one step
   because this sits inside a bordered card, not on open page. Spacing only. */
.bf-offer .lv2-h1 { margin-bottom: 16px; }

.bf-offer .lv2-sub { margin-bottom: 20px; }

/* MONETIZATION-MODEL §366 puts the status line ABOVE the intent tap;
   .lv2-cta-micro is authored for below one, so this is a placement flip. */
.bf-offer-status { margin: 0 0 18px; }

.bf-offer-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 8px;
  color: var(--text-subtle);
  cursor: pointer;
}

.bf-offer-close:hover { color: var(--text-primary); }

.bf-offer-close-ic { width: 18px; height: 18px; }

/* ═══════════════════════════════════════════════════════════════════════════
   §ED271 — THE READER'S ANSWERS ON /record  (#rec-answers-section)

   Lives here rather than in css/record.css because record.html already loads
   briefing.css and this build's write permissions cover briefing.css only.
   Purely additive: every selector below is namespaced .rec-answers / .ra-*, so
   no page that loads briefing.css at an older cache version is affected.

   COLOUR: no new literals. Question and write-in take --text-primary (the
   app's primary body pairing); the count takes --text-subtle, the exact token
   the already-shipped .rec-section-empty uses on this same page. The tap chip
   is a --border outline with --text-primary text — an outline, not a fill, so
   it introduces no new foreground/background pair to verify.
   ═══════════════════════════════════════════════════════════════════════════ */
.rec-answers {
  margin-top: 26px;
}

.rec-answers-count {
  color: var(--text-subtle);
  font-family: var(--font-body);
  font-size: 0.88rem;
  margin: 0 0 12px;
}

.rec-answers-count:empty { display: none; }

#rec-answers-list > .ra-card + .ra-card { margin-top: 12px; }

/* §ED285 — the card was reading "like a Word document": one body face at one
   size for dateline, question and answer, on the same surface as every other
   card. Type and colour only; no element is added, moved or reworded.

   SURFACE: --surface-well (dark #101010, the deepest surface token defined —
   deeper than .bf-card's --surface #1D1D1D) so the answer cards sit as wells
   the reader's own words live inside, not as more floating chrome. Light
   INVERTS by the theme's own elevation law (global.css:350 "white is the
   ceiling, nesting descends"), so light takes --surface — which is what
   .bf-card already resolved to there, i.e. light's card face is unchanged and
   keeps its shipped --shadow-card lift. */
.ra-card {
  padding: 18px 20px;
  background: var(--surface-well);
}

html.theme-light .ra-card { background: var(--surface); }

/* The question, verbatim from the snapshot stored at answer time.

   It is semantically a heading and is now typeset as one: --font-display
   (§ED430: Fraunces 400) at display size. The ELEMENT stays a <p>
   (js/record-answers.js:119) — promoting it to <h3> is a markup change in a
   file outside this build's write permissions, and is reported rather than
   made. Cormorant defaults to old-style figures, which would set any year or
   number in a question at x-height against the lining figures of the dateline
   and answer beneath it; lining-nums holds one numeral system across the card. */
.ra-question {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: 1.5rem;
  line-height: 1.22;
  font-weight: 400;   /* §ED430 · ruled */
  letter-spacing: -0.005em;
  font-variant-numeric: lining-nums;
  margin: 8px 0 0;
}

/* The tap. Rendered only when its label resolves — see js/record-answers.js.
   An outline chip, deliberately quieter than the write-in: the reader's own
   words outrank the option they picked. */
/* §ED469 / Jake's ruling: this WAS its own one-off accent treatment
   (--accent-ink text on an --accent-dim wash) — a second, different way of
   saying "this is what you picked" from the one already ruled for exactly
   that idea (css/landing-a2.css .p-opt:first-child, "the one the reader
   took" — lifted into the app as .ba-zone .ba-option.is-chosen /
   .ba-zone .ba-answer above). Two treatments for the same idea is how the
   two-limes defect happened in the first place; this is now that same
   recipe, verbatim — lime tint at 22% over the surface, the border at 52%
   into the hairline, ink at the primary tier, not accent-ink. COLOUR AND
   BORDER ONLY changed; size/shape are .ra-value's own, unrelated to the
   lime budget.
   STATIC FALLBACK FIRST, same shape as the other two color-mix sites in
   this file (briefing.css ~2939/3168): an engine without color-mix
   (WKWebView below iOS 16.4) keeps a flat tint + the chosen-row's own
   solid accent-ink border instead of losing the border declaration
   entirely (an invalid color-mix() invalidates the whole `border`
   shorthand, not just the colour). */
.ra-value {
  display: inline-block;
  color: var(--c-ink);
  background: #EEF7CC;                      /* light fallback, pre-color-mix */
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.2;
  border: 1px solid var(--c-accent-ink);
  border-radius: 999px;
  padding: 5px 11px;
  margin: 10px 0 0;
}

html:not(.theme-light) .ra-value,
html[data-theme="dark"] .ra-value { background: #23291A; }  /* dark fallback */

@supports (background: color-mix(in oklab, red 22%, blue)) {
  .ra-value,
  html:not(.theme-light) .ra-value,
  html[data-theme="dark"] .ra-value {
    background: color-mix(in oklab, var(--c-accent) 22%, var(--c-surface));
    border-color: color-mix(in oklab, var(--c-accent) 52%, var(--c-line));
  }
}

/* The write-in reuses .bf-note wholesale (label + text), so it matches the
   reader's notes elsewhere on this page byte for byte. Only the top margin is
   set here, to sit clear of the chip above it. */
.ra-note { margin-top: 12px; }

/* CONSEQUENCE of deepening the card: .bf-note's own background is
   --surface-well (briefing.css:1041), which on a --surface-well card would
   erase the write-in's figure entirely in dark. Scoped to .ra-card only, the
   note steps back UP the ladder (--surface) so the reader's words keep the
   inset-block reading they have everywhere else. Light is untouched by that
   inversion — there the card is --surface, so the note keeps --surface-well
   and stays the recessed one. Same two tokens either way, swapped. */
.ra-card .bf-note { background: var(--surface); }

html.theme-light .ra-card .bf-note { background: var(--surface-well); }

/* Print — the page IS the print artifact (css/record.css @media print). The
   chip's border survives; nothing here is interactive, so nothing is hidden. */
@media print {
  .rec-answers-count { color: #000; }
  .ra-question, .ra-value { color: #000; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   §ED286 — THE ANSWER CARD: A FILLED BAND OVER A CONTRASTING BODY

   ONE object, two states. /briefing asks (band + tap options in the body);
   /record remembers (band + chosen label + the reader's words). The shell —
   radius, border, band fill, band ink, body surface, lift — is declared ONCE
   across both selector sets below so the two ends of the loop cannot drift
   apart. Only the bleed geometry differs, and even that is driven off the
   same two custom properties.

   THE BAND'S INK IS --accent-text, NOT --accent-ink. This is the load-bearing
   correction of this build. In LIGHT, --accent-ink is #586F00 (global.css:394)
   and pairs with lime at 4.84:1 — legible. But in DARK, global.css:110 sets
   `--accent-ink: var(--accent)`, i.e. #CCFF00 — the identical value as the
   fill. A band of --accent carrying --accent-ink text renders lime-on-lime at
   1.00:1 in dark: an invisible question. --accent-text (#161616, global.css:53)
   is declared in :root and is NOT overridden in the light block, so it resolves
   to the same value in BOTH themes, and its own comment names it "the ink used
   for text-on-lime in both themes," verified 15.40:1. That single token is what
   makes the light band shippable without inventing a hex: the light band is not
   a weaker cousin of the dark one, it is the SAME band. Recomputed here from
   the token values by WCAG relative luminance: #161616 on #CCFF00 = 15.40:1,
   both themes, AAA.

   ZERO colour literals: every declaration below is a var() reference.
   ═══════════════════════════════════════════════════════════════════════════ */
/* THE SHELL. --card-pad-* are the single source of truth for the inset: the
   band bleeds by negating exactly them, so padding can never fall out of step
   with the bleed. overflow:hidden is what clips the band into the radius —
   without it the fill would square off the top two corners. */
/* §ED742 · SPLIT FROM A SHARED RULE. .ba-zone used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
.ra-card {
  --card-pad-x: 20px;
  --card-pad-y: 18px;
  padding: var(--card-pad-y) var(--card-pad-x);
  border: 1.5px solid var(--border-strong);
  border-radius: 14px;
  overflow: hidden;
  background: var(--surface-well);
}

/* Light inverts the elevation ladder (global.css:350 — "white is the ceiling,
   nesting descends"), so the body takes --surface there, exactly as §ED285
   ruled for .ra-card. Soft lift is the --shadow-card family and light-only:
   .bf-card already establishes that dark needs no shadow because luminance
   contrast alone elevates it. --shadow-accent is deliberately NOT used — it
   resolves to `none` on web light (global.css:440), so it would have made the
   lift asymmetric between themes by accident rather than by design. */
/* §ED742 · SPLIT FROM A SHARED RULE. html.theme-light .ba-zone used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
html.theme-light .ra-card {
  background: var(--surface);
  box-shadow: var(--shadow-card);
}

/* THE BAND — fill and ink, shared by every band piece on both surfaces.
   .is-answered-q is included explicitly: it carries its own colour at equal
   specificity, and must not pull the answered question off the band.
   §ED469 / Jake's ruling: .rec-fact-date REMOVED from this group. §ED460
   counted date band + question band + answer chip as three stacked accent
   elements on one /record or vault card against the briefing's one; Jake
   ruled the QUESTION keeps the lime as the licensed answer-zone exception
   and the date band loses it — see the neutral rule below. */
/* §ED742 · SPLIT FROM A SHARED RULE. .ba-zone > .ba-question, .ba-zone > .ba-question.is-answered-q used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
.ra-card > .ra-question {
  background: var(--accent);
  color: var(--accent-text);
  margin-left: calc(var(--card-pad-x) * -1);
  margin-right: calc(var(--card-pad-x) * -1);
  padding-left: var(--card-pad-x);
  padding-right: var(--card-pad-x);
}

/* The question, both surfaces, one declaration: §ED430 Fraunces 400 at the
   display size (was Cormorant 500) — §ED285's one-declaration structure is
   kept, only the face and weight move. lining-nums is RETAINED and still
   load-bearing: Fraunces, like Cormorant, defaults to old-style figures,
   which would set a year in the question at x-height against the lining
   figures of the body beneath it. Verified on the shipped file — the fvar
   carries opsz 9-144 and wght 100-900. */
/* §ED742 · SPLIT FROM A SHARED RULE. .ba-zone > .ba-question, .ba-zone > .ba-question.is-answered-q used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
.ra-card > .ra-question {
  font-family: var(--font-display);
  font-size: 1.5rem;
  line-height: 1.22;
  font-weight: 400;   /* §ED430 · ruled */
  letter-spacing: -0.005em;
  font-variant-numeric: lining-nums;
}

/* /record's dateline eyebrow — WAS the top half of a two-piece band filled
   identically with the question below it (Jake's §ED469 ruling: that made
   the date its own separate accent element, one of three stacked on a
   single card against the briefing's one). Now plain card content: the
   shell's own top inset (.ra-card's padding, no negative-margin bleed
   trick), small/uppercase/tracked exactly as record.css's standalone
   .rec-fact-date already sets it — record.html gets the same declaration
   twice (harmless, same value); vault.html, which never loads record.css,
   was getting NO typography for this element outside the old band rule and
   needs it declared here to keep the eyebrow legible at all. */
.ra-card > .rec-fact-date {
  font-family: var(--font-body);
  /* THE 16px FLOOR (§ED306) — identical to css/record.css's .rec-fact-date by
     intent, not by coincidence. §ED469 copied that rule here at 0.78rem
     (12.48px) and carried its sub-floor size with it; both are now on the
     floor token.

     CASCADE — MEASURED, NOT ASSUMED. record.html links BOTH sheets and so
     carries both rules, which looks like a specificity race. It is not one:
     `.ra-card` is never rendered on record.html at all. §ED426 retired
     js/record-answers.js from that page (see the comment at record.html's
     .rec-answers section) and moved the answer cards to /vault.html, so the
     only .rec-fact-date record.html produces is js/record.js's, inside a
     plain .bf-card. The two rules are therefore DISJOINT in practice, one
     per surface:
         vault.html   -> THIS rule (vault never loads record.css)
         record.html  -> record.css's .rec-fact-date (no .ra-card exists here)
     Were an .ra-card ever reintroduced to record.html, this selector (0,2,0)
     would take it from record.css's (0,1,0) on specificity, independent of
     link order — record.html loads briefing.css FIRST, so load order alone
     would have handed it the other way. Verified by
     scripts/measure-ed475-rec-fact-date.js, which asserts the absence rather
     than trusting it.

     THE REAL HAZARD IS THE COPY, NOT THE CASCADE. Two disjoint rules that
     must stay identical are a divergence waiting to happen, and had already
     diverged: this one used --c-ink-3 while record.css's used --text-subtle,
     so /record drew its dateline in a different ink from /vault. Unified on
     --c-ink-3. audit-ed440-tokens.js's type-floor check now reads BOTH
     files, so a future sub-floor edit to either fails the audit. */
  font-size: var(--c-t-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-ink-3);
  margin: 0 0 6px;
}

/* The question is now the band ON ITS OWN (Jake's ruling), but unlike
   /briefing's .ba-question it is NOT the first thing in the card — the
   neutral dateline sits above it in normal flow. It therefore does NOT
   bleed to the card's top the way .ba-question does (that trick assumes
   nothing sits above it; here it would overlap the dateline). It keeps its
   existing bottom bleed unchanged and gets its own top inset instead of
   inheriting the eyebrow's old padding-top:0/margin-top:0 hand-off. */
.ra-card > .ra-question {
  margin-top: 6px;
  padding-top: var(--card-pad-y);
  padding-bottom: var(--card-pad-y);
  margin-bottom: var(--card-pad-y);
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 · THE OVERRIDE LAYER — LAST THING IN THIS FILE. APPEND NOTHING AFTER.
   ══════════════════════════════════════════════════════════════════════════

   WHAT THIS IS. The landing's design language (prototype/prototype-a2.css,
   ratified) applied to the Daily, by mapping this sheet's existing classes
   onto the --c-* tokens in css/tokens.css. It restyles ONLY the classes
   §ED440 Phase B names.

   WHY IT IS AN APPEND AND NOT A REWRITE — and what that costs. This sheet is
   ~131KB with no test coverage; rewriting it in place with nothing to catch a
   regression is the worse risk. The cost is real and is recorded as technical
   debt T1 in docs/ED440-DESIGN-LANGUAGE.md: the same classes are now styled
   in TWO places, and an edit in the body of the file can be silently
   overridden a hundred kilobytes further down. A future "briefing.css
   consolidation" pass folds this block back in and deletes the superseded
   literals. Do not attempt it without a screenshot harness green first —
   shots/ed440/phase-b/ is the seed of one.

   HOUSE RULES FOR THIS BLOCK, so it stays reversible:
     - It wins by SOURCE ORDER at equal specificity. No !important anywhere.
     - Where a longer selector was unavoidable, the rule it beats is named.
     - Every declaration names the token it applies. This block is a MAPPING
       from old literal to new token, never a second design.
     - Delete the block and the Daily returns exactly to its pre-§ED440 look.

   NOT IN SCOPE, deliberately: no new copy, no new mechanism, no new lock
   reasons, no change to who sees what. Nohemi is NOT referenced — it is
   unreachable in production (netlify.toml force-404s /prototype/*) and the
   landing's card interior is Hanken + mono anyway (§ED398), both of which
   this app already ships. So the card is a full-fidelity lift as-is.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── THE PAGE GROUND — L0 ───────────────────────────────────────────────── */
.bf-page { background: var(--c-bg); }

/* ── THE EDITION CARD — L2 ──────────────────────────────────────────────
   Was: --shadow-hero, a top-weighted LIME GLOW. That glow is the single
   loudest "developer tool" signal on the surface and DESIGN-INVARIANTS.md
   already bans lime glow at scale on web. It is replaced by real elevation:
   a warm hairline plus the L2 ambient lift. */
.bf-daily {
  background: var(--c-surface);
  border: var(--c-e2-border);
  border-radius: var(--c-e2-radius);
  box-shadow: var(--c-e2-shadow);
  /* transform+opacity only, and the hover lift reads --c-amp-sm so reduced
     motion nulls the distance rather than each rule guarding itself. */
  transition: box-shadow var(--c-m-expand) var(--c-e-out),
              transform  var(--c-m-expand) var(--c-e-out);
}

.bf-daily:hover {
  transform: translateY(calc(-1 * var(--c-amp-sm)));
  box-shadow: var(--c-e3-shadow);
}

/* ── MASTHEAD ──────────────────────────────────────────────────────────── */
.bf-masthead { background: var(--c-surface); border-bottom: 1px solid var(--c-line-2); }

.bf-mast-wordmark { color: var(--c-ink); }

.bf-mast-dateline { color: var(--c-ink-2); font-size: var(--c-t-micro); }

.bf-mast-archive  { color: var(--c-ink-3); font-size: var(--c-t-micro); }

/* ── DESIGNED EMPTY / ERROR / OFFLINE ───────────────────────────────────
   ONE renderer (js/briefing.js showState) serves all three. Offline and
   error carry byte-identical copy by §ED181-2 ruling and that is unchanged
   here — §ED440 restyles, it does not write copy.
   No red card: --c-danger is spent on the retry control's focus ring only. */
.bf-state { color: var(--c-ink-2); font-size: var(--c-t-body); }

.bf-state .bf-state-h {
  font-family: var(--c-f-display);
  color: var(--c-ink);
  font-size: 1.5rem;
  line-height: 1.2;
}

.bf-retry {
  font-family: var(--c-f-body);
  font-size: var(--c-t-micro);
  padding: 11px 20px;
  border: var(--c-e1-border);
  border-radius: var(--c-r-sm);
  background: var(--c-surface-2);
  color: var(--c-ink);
  box-shadow: var(--c-e1-shadow);
  transition: transform var(--c-m-tap) var(--c-e-standard),
              background-color var(--c-m-tap) var(--c-e-standard);
}

.bf-retry:hover  { background: var(--c-bg-deep); }

.bf-retry:active { transform: scale(.97); }

.bf-retry:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* Action-waiting -> inline progress on the control itself, never a page
   spinner (js/briefing.js sets .is-retrying for the duration of the load). */
.bf-retry.is-retrying { opacity: .65; pointer-events: none; }

/* ── SKELETON — content-waiting, so it must match the REAL layout ───────
   The skeleton now models the question block too (js/briefing.js
   renderSkeleton), because modelling only masthead + body made the page
   visibly GROW when the options arrived. A skeleton of the wrong shape is
   worse than none. */
.bf-skeleton {
  background: var(--c-surface);
  border: var(--c-e1-border);
  border-radius: var(--c-e2-radius);
  box-shadow: var(--c-e1-shadow);
}

.bf-skel-mast {
  background: var(--c-surface);
  border-top: none;                    /* was a 2px lime --border-accent rule */
  border-bottom: 1px solid var(--c-line-2);
}

.bf-skel-line { background: var(--c-skeleton); border-radius: var(--c-r-sm); }

.bf-skel-line::after {
  background: linear-gradient(90deg, transparent 0%, var(--c-skeleton-sheen) 50%, transparent 100%);
}

/* The question block's own bars. */
.bf-skel-ask   { padding: 0 32px 28px; display: flex; flex-direction: column; gap: 12px; }

.bf-skel-q     { width: 86%; height: 18px; margin-top: 4px; }

.bf-skel-opts  { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }

.bf-skel-opt   { height: 34px; border-radius: var(--c-r-full); }

.bf-skel-opt-1 { width: 42%; }

.bf-skel-opt-2 { width: 34%; }

.bf-skel-opt-3 { width: 52%; }

.bf-skel-opt-4 { width: 28%; }

/* ── THE CELEBRATION (§ED440) ───────────────────────────────────────────
   THE one spring in the app, spent on the one act that matters: the reader
   answering. It happens once a day and was completely unmarked before this
   — grep Motion js/reader-answer.js returned nothing.

   IT FIRES ON SERVER-CONFIRMED SAVE, NEVER ON TAP. js/reader-answer.js
   passes celebrate:true only from send()'s commit callback, i.e. only after
   a genuine 2xx write. The 409 (answer already existed) and 503 (edit
   refused) branches both route through adoptExisting() and return BEFORE
   commit, so a refused or failed save can never celebrate. Restoring a
   prior answer never celebrates either.

   TRANSFORM AND OPACITY ONLY. No width, no height, no top/left. */
@keyframes bf-answer-land {
  0%   { transform: scale(.94); opacity: 0; }
  60%  { transform: scale(1.02); opacity: 1; }
  100% { transform: scale(1);    opacity: 1; }
}

@keyframes bf-tick-in {
  from { opacity: 0; transform: scale(.7); }
  to   { opacity: 1; transform: scale(1); }
}

/* ── THE LOCKED STATE — RESTYLE ONLY (§ED240 seal preserved) ────────────
   No new mechanism, no new copy, no new lock reasons, nothing about who
   sees what. The seal — ruled lines standing in the shape of the withheld
   body — is kept exactly as ruled; it gets the warm palette and nothing
   else. The lime budget stays where lockedTeaser put it: the reveal action
   is NEUTRAL, and lime arrives once, on the membership card, only after a
   reader taps for it. Many locked spines can share one scroll. */
.ar-locked {
  background: var(--c-surface);
  border: 1px dashed var(--c-line);
  border-radius: var(--c-e2-radius);
  box-shadow: var(--c-e1-shadow);
}

.ar-locked-hook { font-family: var(--c-f-display); color: var(--c-ink); }

.ar-locked-standfirst { color: var(--c-ink-2); font-size: var(--c-t-body); }

.ar-locked-source, .ar-locked-subline { color: var(--c-ink-3); font-size: var(--c-t-micro); }

.ar-seal {
  background: var(--c-surface-2);
  border: var(--c-e1-border);
  border-radius: var(--c-r-md);
}

.ar-seal-label { color: var(--c-ink-3); font-size: var(--c-t-micro); }

.ar-seal-rule  { background: var(--c-line); }

.ar-locked-today { color: var(--c-ink); font-size: var(--c-t-micro); }

.ar-locked-reveal {
  border: var(--c-e1-border);
  border-radius: var(--c-r-sm);
  color: var(--c-ink-2);
  font-size: var(--c-t-micro);
  background: transparent;
  transition: background-color var(--c-m-toggle) var(--c-e-out),
              transform var(--c-m-tap) var(--c-e-standard);
}

.ar-locked-reveal:hover  { background: var(--c-surface-2); }

.ar-locked-reveal:active { transform: scale(.98); }

.ar-locked-reveal:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* ── MODALS / SHEETS — L3. SCRIM, NO BLUR ──────────────────────────────
   Backdrop blur is licensed to ONE consumer in this product (a sticky
   header over scrolling content) and the calendar panel is not it. */
.ar-cal-scrim {
  background: var(--c-e3-scrim);
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}

.ar-cal-panel {
  background: var(--c-surface);
  border: var(--c-e3-border);
  border-radius: var(--c-e3-radius);
  box-shadow: var(--c-e3-shadow);
  transition: transform var(--c-m-sheet) var(--c-e-out),
              opacity   var(--c-m-sheet) var(--c-e-out);
}

.ar-cal-trigger {
  background: var(--c-surface);
  border: var(--c-e1-border);
  border-radius: var(--c-r-full);
  color: var(--c-ink-2);
  box-shadow: var(--c-e1-shadow);
  transition: transform var(--c-m-tap) var(--c-e-standard);
}

.ar-cal-trigger:active { transform: scale(.97); }

.ar-cal-trigger:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* ── THE ONE LICENSED BLUR: the sticky header over scrolling content ──── */
/* §ED440 PHASE E — TWO DEFECTS FIXED HERE, BOTH FOUND BY THE STRUCTURAL
   CHECK IN scripts/audit-ed440-scroll-perf.js AND NEITHER VISIBLE BY EYE.
   1. NO SATURATE. Phase D shipped `blur(var(--c-blur-header))` alone, so this
      surface was the frosted-plastic version of the material while the
      masthead and the nav had the full recipe. It now reads the assembled
      --c-glass-filter token, which cannot be used without its saturate.
   2. IT IS NOT STICKY. Computed position was `static`, with no fixed or
      sticky ancestor — so this is a blurred element INSIDE the scroller, the
      one case Part 3 forbids on performance grounds, re-running its backdrop
      pass every frame. The comment above has claimed "sticky header" since
      Phase D while the element was never positioned. Named a header, priced
      as one, and behaving as neither. */
.bf-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--c-glass-chrome);
  -webkit-backdrop-filter: var(--c-glass-filter);
          backdrop-filter: var(--c-glass-filter);
  border-bottom: 1px solid var(--c-glass-chrome-line);
  box-shadow: inset 0 -1px 0 var(--c-specular-under);
  transform: translateZ(0);
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .bf-header { background: var(--c-surface); }
}

/* ── FOOT / SIGNUP ─────────────────────────────────────────────────────── */
.bf-signup-h   { font-family: var(--c-f-display); color: var(--c-ink); }

.bf-signup-sub { color: var(--c-ink-2); font-size: var(--c-t-body); }

.bf-anon       { color: var(--c-ink-3); font-size: var(--c-t-micro); }

.bf-signup-cta {
  background: var(--c-accent);
  color: var(--c-on-accent);
  border-radius: var(--c-r-sm);
  transition: transform var(--c-m-tap) var(--c-e-standard);
}

.bf-signup-cta:active { transform: scale(.98); }

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE D · ITEMS 2, 3, 4, 5 — THE ROOM, THE GROUND, THE OPTIONS,
   THE BAND.

   SCOPING, DELIBERATE. Everything below hangs off `.bf-page`, which exists on
   briefing / record / vault / archive and on NEITHER index.html NOR
   membership.html — both of which load this sheet. membership.html is
   explicitly out of scope this pass (it gets its own, and becomes theme-aware
   then) and index.html is the landing. Scoping to .bf-page excludes both
   structurally rather than by hoping nobody notices.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── ITEM 2 · THE ROOM SYSTEM, SWITCHED ON ───────────────────────────────
   The tokens have existed since Phase A and NOTHING HAS EVER READ THEM. The
   app did not lack a macro rhythm system; it had one and had never switched
   it on. Measured before this rule (Phase C, 1440x900):

     landing section gaps   90 · 135 · 216 · 216 · 134 · 135   THREE ranks
     app block gaps         10 · 11 · 12 · 14 · 16 · 20 · 26   NO ranks

   The app's LARGEST macro gap was 3.5x SMALLER than the landing's SMALLEST
   section gap, so nothing on the page was ranked and it read as one
   undifferentiated run. That is the mechanical answer to "cramped".

   AND IT HOLDS AT 375. The ladder is viewport-HEIGHT-derived
   (clamp(132px, 24vh, 280px)), so it scales with the reading surface rather
   than collapsing with the width. The old rules did the opposite: they more
   than HALVED the card's interior air on the surface most readers use
   (.bf-daily-body was 40/64/48 at 1440 and 16/18/22 at 375). Those two
   overrides, at briefing.css:1822 and :1848, are what this replaces. */
.bf-page {
  background: var(--c-bg);
  padding-top: var(--c-room-sm);
  padding-left: var(--c-gutter);
  padding-right: var(--c-gutter);
  padding-bottom: calc(var(--c-room-lg) + var(--nav-bottom-h, 0px));
}

/* MASTHEAD CLEARANCE. .app-topnav is position:fixed, 70px, and exists only
   at >=900px (global.css:1092). Without this the culture pill slides UNDER
   the fixed bar — which is exactly what the first draft of this layer did,
   caught in an injected 1440 frame during Phase C. */
@media (min-width: 900px) {
  .bf-page { padding-top: calc(70px + var(--c-room-sm)); }
}

/* ITEM 2 · MEASURE. Text ran the full 620/720px column at every width. The
   landing ranks WIDTH as well as room: --c-measure for sections,
   --c-measure-narrow for the reading column. The card is the reading
   column. */
.bf-page .bf-wrap { max-width: var(--c-measure-narrow); }

/* THE CARD IS SEATED, NOT BUTTED. The dominant beat on the page gets room
   above it and nothing else does — room is a RANK, spent once. --room-sm
   under the utility row, --room-md before the foot. The app has ONE beat,
   not the landing's six, so it spends one rank down from --room-xl. */
.bf-page .bf-header { margin: 0 0 var(--c-room-sm); }

.bf-page .bf-foot   { margin-top: var(--c-room-md); }

/* Interior rank: the reading block separates from the answer zone by more
   than its own paragraphs separate from each other. */
.bf-page .bf-reaction-wrap { margin-top: var(--c-room-sm); }

.bf-page .bf-daily-body { padding: 40px 64px 48px; }

@media (max-width: 899px) {
  /* HOLDS instead of halving — the whole point of a vh-derived ladder. */
  .bf-page .bf-daily-body { padding: 32px 24px 36px; }
}

/* ── ITEM 3 · THE SHELL ONTO WARM TOKENS ─────────────────────────────────
   Measured before this rule: page ground #FFFFFF light / #101010 (S 0%)
   dark, .ba-zone and .bf-signup on pure #FFFFFF. Part 1 REFUSES pure #000
   and #FFF grounds and refuses cool greys anywhere. Phase B moved the CARD
   to warm tokens and the SHELL never followed, which is the located cause of
   "the surroundings look default" — a warm card on an achromatic ground. */
/* SPECIFICITY, MEASURED NOT ASSUMED — this rule lost its first draft and the
   loss was LIGHT-ONLY, which is the T1 defect class exactly: every dark frame
   was correct and hid it. briefing.css:2618 carries
     html.theme-light .ra-card, html.theme-light .ba-zone
       { background: var(--surface); box-shadow: var(--shadow-card); }
   at (0,2,1), which beats a bare `.bf-page .ba-zone` (0,2,0). Caught by
   scripts/audit-ed440-tokens.js reading computed style, not by eye.
   The leading `html` brings this to (0,2,1) and it wins on source order.
   Its box-shadow is overridden too, deliberately: --shadow-card is
   `0 6px 20px rgba(22,22,22,.16)` — one shadow at 3x the landing's alpha,
   cool-tinted and with no contact edge, i.e. the exact geometry Part 1
   mechanic 1 rules against. L1 is the correct elevation for this surface. */
/* §ED742 · SPLIT FROM A SHARED RULE. html .bf-page .ba-zone used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
html .bf-page .bf-signup {
  background: var(--c-surface);
  border-color: var(--c-line);
  box-shadow: var(--c-e1-shadow);
}

/* (c) THE CULTURE SELECTOR — a brand moment, not a control.
   Was: bg #FAFAFA (achromatic), a rgba(22,22,22,.16) border — DOUBLE the
   landing's .13 hairline and cool-tinted — and box-shadow rgba(0,0,0,.4),
   a pure-black 40% shadow on warm paper, the single cheapest declaration
   measured this session. Its label was 14.4px, under the floor.
   The LIME hairline is what makes it read as identity rather than chrome;
   the landing's .p-chip does the same and carries no shadow at all. */
.bf-page .bf-cult-trigger {
  background: var(--c-surface);
  border: 1px solid color-mix(in srgb, var(--c-accent-ink) 30%, transparent);
  box-shadow: none;
  border-radius: var(--c-r-full);
  padding: 10px 18px;
  font-size: var(--c-t-micro);
  font-weight: 500;
  color: var(--c-ink);
  transition: border-color var(--c-m-toggle-out) var(--c-e-standard),
              background   var(--c-m-toggle-out) var(--c-e-standard),
              transform    var(--c-m-tap)        var(--c-e-standard);
}

.bf-page .bf-cult-trigger:hover {
  border-color: var(--c-accent-ink);
  background: var(--c-surface-2);
  box-shadow: none;
  transition-duration: var(--c-m-toggle), var(--c-m-toggle), var(--c-m-tap);
}

.bf-page .bf-cult-trigger:active { transform: scale(.98); }

.bf-page .ar-cal-trigger {
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  box-shadow: var(--c-e1-shadow);
  border-radius: var(--c-r-full);
  color: var(--c-ink-2);
}

/* ── §ED469 · THE SAME BAND, ON /record AND THE VAULT ────────────────────
   §ED460 measured this as a separate defect from the one above, not a
   duplicate: the shell comment at line ~2612 already explains ONE band
   shared by .ra-card (/record, the vault) and .ba-zone (the Daily) — but
   only .ba-zone ever got the P4b dial (item 5 above). .ra-card's band was
   still reading the undialled .ra-question rule two screens up
   (var(--accent)/var(--accent-text), flat #CCFF00 in both themes) — a
   reader's own private vault blasting full-brightness lime the briefing
   deliberately dims in dark. Same fix, same tokens, same reason for the
   higher (0,3,0) specificity: .ra-question shares (0,2,0) with
   .ba-zone > .ba-question, and this must not lose silently the way the T1
   defect already did once in this file.
   Every /record- and vault-rendered card sits under <main class="bf-page">
   (record.html, vault.html — verified), so this scope matches exactly.
   .rec-fact-date is DELIBERATELY absent here — Jake's ruling took it off
   the band entirely (see the neutral rule at line ~2694); it no longer
   reads any accent token at all. */
.bf-page .ra-card > .ra-question {
  background: var(--c-band-fill);
  color: var(--c-on-accent);
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · THE LOCKED STATE — EXECUTION REBUILD (Part 1)

   THE CONCEPT IS §ED240'S AND IT IS KEPT: the withholding must be VISIBLE, and
   what stands in for the body is its SHAPE. A gate nobody can see is not a
   gate, it is a disappointing edition. Nothing about the mechanism, the four
   lock reasons, the entitlement logic or the copy moves in this block.

   WHAT MOVES IS THE EXECUTION, because the seal was conceptually right and
   visually dead: a headline, then four flat grey rules. Two-dimensional. In
   light mode especially — warm paper, grey rules, nothing lit — it read as a
   rendering failure rather than as a wall.

   THE REBUILD, IN FOUR PARTS:
     a) THE SPINE RENDERS SHARP, AT FULL INK, in the treatment it carries when
        unlocked. This is the largest single change and it costs nothing: the
        server was already sending real hook, title, standfirst and source and
        the old seal spent them at reduced size and reduced ink, as though
        they were also being withheld. They are not. They are the hook.
     b) THE BODY PREVIEW DISSOLVES — real prose, sharp at the top, ramping
        into blur and out to the ground. Two stacked copies with
        complementary masks (see js/card.js lockedTeaser for why one element
        cannot do this).
     c) THE SEAL LINES CONTINUE THAT DISSOLVE below the preview, gradient-
        masked into the ground instead of sitting as flat rules. They now
        read as the text carrying on past where it can still be resolved,
        which is what they were always meant to mean.
     d) A GLASS CARD FLOATS OVER THE BOUNDARY where sharp becomes faded. It
        is the ONLY element at chrome elevation on this view.

   LIGHT AND DARK DO NOT SHIP AT THE SAME SETTINGS, and that is Part 1's
   explicit instruction. On a dark ground a fade toward the ground is doing
   most of the work for free — ink and ground are far apart, so the ramp is
   legible over a short distance. On warm paper the ink/ground distance is
   smaller, the fade runs out of contrast almost immediately, and the same
   numbers produce a preview that looks merely faint. Light therefore gets a
   LONGER sharp region, a SHALLOWER blur, and a stronger glass edge. Every
   divergence below is marked LIGHT: or DARK: and none of it is symmetric.
   ══════════════════════════════════════════════════════════════════════════ */
/* ── (1a) THE SPINE, SHARP ───────────────────────────────────────────────
   Phase D's .ar-locked-hook was --c-f-display at inherited size. It now takes
   the hero treatment: the same face, weight and tracking .bf-hook-text
   carries on an unlocked read, so a locked card and an open one present their
   headline identically. Nothing here is dimmed, tinted or reduced. */
.bf-page .ar-locked-hook,
.ar-locked.has-preview .ar-locked-hook {
  font-family: var(--c-f-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 4.4vw, 2rem);
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--c-ink);
  font-variant-numeric: lining-nums;
}

.bf-page .ar-locked-standfirst {
  color: var(--c-ink-2);
  font-size: var(--c-t-body);
  line-height: var(--c-lh-body);
}

/* .ar-locked-source IS DELIBERATELY NOT IN THIS RULE. It was, and that was a
   self-inflicted specificity defeat: this selector is (0,2,0) and the citation
   amendment's `html .ar-locked-source` is (0,1,1), so THIS rule won and the
   locked state's source line rendered --c-ink-3 grey while every other
   citation in the app carried the accent. Jake's amendment names the locked
   state's source_title explicitly — it is one of the few sharp elements on
   that screen and it should carry weight. Caught on frame 15, confirmed by
   computed style, not by argument. The citation block at the foot of this
   sheet owns this element's colour and size now. */
.bf-page .ar-locked-subline { color: var(--c-ink-3); font-size: var(--c-t-micro); }

/* ── (1b) THE DISSOLVE ───────────────────────────────────────────────────
   THE STACK. Two copies of the preview occupy the same box. The sharp one is
   in flow and gives the container its height; the blurred one is absolutely
   positioned on top of it and is inert (aria-hidden in the DOM, pointer-
   events:none here, never selectable).

   WHY overflow IS NOT hidden. Clipping would cut the blur's own spread and
   put a hard edge exactly where the design needs none. The masks end the
   content instead, which is what a mask is for. */
/* ONE MEASURE FOR THE WHOLE DISSOLVING COLUMN — the preview and the seal
   lines beneath it must be the same width or the "text continuing past where
   it can be resolved" reading breaks at the seam. 34ch is the seal's own
   §ED240 value, kept.
   THE MEASURE HOLDS: this is narrower than --c-measure-narrow and is a ch
   unit, so it tracks the body face and cannot be widened by a layout. */
.ar-locked { --ar-lock-measure: 34ch; }

.ar-locked-body {
  position: relative;
  margin: 18px auto 0;
  max-width: var(--ar-lock-measure);
  /* LEFT-ALIGNED, INSIDE A CENTRED BLOCK. .ar-locked centres its spine (a
     three-line hook and a short standfirst read as a centred clause, which is
     §ED240's composition and is not this pass's to change). Body prose is not
     a clause: centred body text has a ragged LEFT edge, and a ragged left
     edge under a fade reads as damage rather than as continuation. Part 1a
     also asks for the spine to render as it would when unlocked, and an
     unlocked debrief (.bf-debrief) is left-aligned. So the column is left-set
     and the BLOCK is centred — the card's composition survives and the prose
     gets a real rag. */
  text-align: left;
  /* The blurred copy bleeds a few px past the text box; give it room so the
     bleed lands on ground rather than on the next element. */
  padding-bottom: 6px;
}

.ar-locked-preview,
.ar-locked-preview-soft {
  margin: 0;
  font-family: var(--c-f-body);
  font-size: var(--c-t-body);
  line-height: var(--c-lh-body);
  color: var(--c-ink);
  /* The measure holds inside the dissolve exactly as it does in open prose —
     a paywall is not a reason to reflow the column. */
  max-width: 100%;
}

.ar-locked-preview-soft {
  position: absolute;
  inset: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* LIGHT: the sharp copy holds full ink to 48% and does not finish until 82%.
   The long plateau is the "more contrast in the sharp region" Part 1 calls
   for — on paper the reader needs to have genuinely READ something before it
   starts to go. */
.ar-locked-preview {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 48%, rgba(0,0,0,.35) 68%, transparent 82%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 48%, rgba(0,0,0,.35) 68%, transparent 82%);
}

/* LIGHT: a SHALLOWER blur (3.5px). On paper a deep blur turns text into a
   grey smear that reads as damage; the ink/ground distance is small enough
   that a light touch already destroys legibility while keeping the TEXTURE
   of prose, which is the thing that must survive. */
.ar-locked-preview-soft {
  filter: blur(3.5px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 26%, #000 58%, #000 74%, transparent 97%);
          mask-image: linear-gradient(to bottom, transparent 26%, #000 58%, #000 74%, transparent 97%);
}

/* DARK: the ramp starts earlier and runs deeper. Ink #F5F2EC on ground
   #100E0C is a 17:1 separation — the fade has contrast to spend and can
   afford to begin at 38%. The blur goes to 5px for the same reason: light
   text on dark blurs into a GLOW rather than a smear, and a shallow blur
   there just looks like a focus error. */
html:not(.theme-light) .ar-locked-preview,
html[data-theme="dark"] .ar-locked-preview {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 38%, rgba(0,0,0,.30) 62%, transparent 78%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 38%, rgba(0,0,0,.30) 62%, transparent 78%);
}

html:not(.theme-light) .ar-locked-preview-soft,
html[data-theme="dark"] .ar-locked-preview-soft {
  filter: blur(5px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 20%, #000 50%, #000 70%, transparent 95%);
          mask-image: linear-gradient(to bottom, transparent 20%, #000 50%, #000 70%, transparent 95%);
}

/* REDUCED MOTION does not apply here — nothing moves. But REDUCED
   TRANSPARENCY does, and it is a real user setting that this design must
   answer: a reader who has asked the OS to reduce transparency is telling us
   that layered, low-contrast material is hard for them to parse. Drop the
   blurred copy entirely and let the sharp one end at its mask. The wall still
   works; the dissolve just becomes a fade. */
@media (prefers-reduced-transparency: reduce) {
  .ar-locked-preview-soft { display: none; }
  .ar-locked-preview {
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 92%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 92%);
  }
}

/* ── (1c) THE SEAL LINES, CONTINUING THE DISSOLVE ────────────────────────
   The rules keep their §ED240 job — the shape of the withheld body — and
   stop being flat. They carry REAL INK at the top and are masked out into
   the ground, so the column reads as continuing past the point where it can
   still be resolved rather than as four grey bars parked under the text.

   The label keeps full ink and is NOT masked: it is the one string here that
   must stay readable, because it is what names the group for a screen reader
   and what tells a sighted reader what the shape stands for. */
/* THE SEAL STOPS BEING A BOX. §ED240 gave it a bordered surface-2 card so the
   rules read as a deliberate sealed block rather than as a rendering fault —
   correct then, wrong now: with real prose dissolving directly above it, a
   boxed panel CUTS the column exactly where the design needs it to continue.
   The dissolve is what now says "sealed", so the box is redundant and it was
   the second reason the old state read as furniture.
   Background, border and padding all go; the shape lines stay. */
.bf-page .ar-seal {
  border: 0;
  padding: 0;
  background: none;
  margin: 0 auto;
  max-width: var(--ar-lock-measure);
  text-align: left;
}

.bf-page .ar-seal-label {
  color: var(--c-ink-3);
  font-size: var(--c-t-micro);
  letter-spacing: .10em;
  text-transform: uppercase;
}

.bf-page .ar-seal-shape {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-top: 12px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.55) 45%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.55) 45%, transparent 100%);
}

/* Real ink, not a grey. --c-ink-2 at 38% on paper is the weight of a line of
   set text seen out of focus, which is what these stand for. The old value
   was a flat --border hairline and it is the single reason the seal read as
   dead: a rule at hairline weight is FURNITURE, and furniture is not what a
   withheld paragraph looks like. */
.bf-page .ar-seal-rule {
  height: 9px;
  border-radius: 3px;
  background: color-mix(in srgb, var(--c-ink-2) 38%, transparent);
}

html:not(.theme-light) .bf-page .ar-seal-rule,
html[data-theme="dark"] .bf-page .ar-seal-rule {
  background: color-mix(in srgb, var(--c-ink-2) 30%, transparent);
}

/* ── (1d) THE GLASS CARD OVER THE BOUNDARY ───────────────────────────────
   THE ONE CHROME-ELEVATION ELEMENT ON THIS VIEW, and the full Part 3 recipe:
   backdrop blur AND saturate, warm tint, hairline, specular top edge, warm
   thickness line beneath. It is glass because it is chrome — it floats above
   content — which is exactly the division tokens.css §5 draws.

   THE NEGATIVE MARGIN IS THE DESIGN, not a nudge. The card is pulled UP so it
   straddles the point where the preview stops being sharp. Placing it below
   the dissolve would make it a footer; placing it across the dissolve makes
   it the thing that INTERRUPTED the reading. */
/* THE CARD MUST CLEAR THE TAB BAR. Caught on frame 12 at 375: the floating
   nav pill covered the "read today's edition" link entirely, so the locked
   state shipped with its secondary action unreachable on every phone. The
   nav is fixed and 64px + safe-area, and .bf-page's own bottom padding was
   sized for a state that did not end in a card. */
.ar-locked { padding-bottom: calc(var(--safe-bottom, 0px) + 96px); }

.ar-lock-card {
  position: relative;
  z-index: 2;
  /* Default: a normal gap. THE OVERLAP IS OPT-IN and belongs to the preview
     variant only. Frame 13 showed why: with no preview the column is ~120px
     shorter, so a fixed -34px pull swallowed all but two of the seal's shape
     lines and the no-preview option was being photographed WORSE THAN IT IS.
     A comparison Jake is asked to rule on must not be rigged by a constant
     tuned for the other option. */
  margin-top: 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 22px 20px 18px;
  border-radius: var(--c-r-lg);
  background: var(--c-glass-chrome);
  -webkit-backdrop-filter: var(--c-glass-filter);
          backdrop-filter: var(--c-glass-filter);
  transform: translateZ(0);
  border: 1px solid var(--c-glass-chrome-line);
  box-shadow: var(--c-specular-edge), var(--c-e2-shadow);
}

/* THE OVERLAP — only where there is a dissolve to straddle. */
.ar-locked.has-preview .ar-lock-card { margin-top: -34px; }

/* LIGHT: a STRONGER edge, per Part 1. Warm paper behind warm glass gives very
   little luminance separation, so on light the card's own outline is most of
   what makes it read as a separate pane at all. The hairline goes to a real
   1.5px and picks up a second, warmer outer ring so the card has a visible
   lip against the paper. Dark needs neither — there the tint alone separates. */
html.theme-light .ar-lock-card,
html[data-theme="light"] .ar-lock-card {
  /* THE TINT GOES DARKER THAN THE SURFACE IT FLOATS ON, and this is the fix
     the first render forced. --c-glass-chrome is rgba(255,253,250,.66) and
     the card beneath it is --c-surface #FFFDFA: white glass on white paper.
     The card was invisible — a lime button and a lock apparently floating on
     nothing. Frame 12 showed it; no amount of edge tuning would have saved a
     fill with nowhere to sit.
     Real glass over white paper does not read by its FILL, it reads by being
     slightly deeper than the sheet under it and by casting a shadow onto it.
     So light gets its own tint one step down the warm ramp (toward
     --c-bg-deep) rather than the shared chrome white. Dark needs none of
     this: there the tint is already lighter than its ground and separates on
     its own. */
  background: rgba(247, 243, 236, .72);
  border-width: 1.5px;
  /* L3, not L2. This is the only floating pane on the view and on paper the
     SHADOW is most of what says "floating" — the fill can only do so much
     when ground and surface are four points apart in lightness. */
  box-shadow: var(--c-specular-edge),
              0 0 0 1px rgba(20, 18, 14, .05),
              var(--c-e3-shadow);
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  html.theme-light .ar-lock-card,
  html[data-theme="light"] .ar-lock-card { background: #F7F3EC; }
}

/* MANDATORY FALLBACK — verified, not assumed. Without a backdrop filter the
   glass card would be a 66% tint sitting directly on top of the preview text:
   text over text, and the least readable thing on the screen. Fully opaque. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .ar-lock-card { background: var(--c-surface); }
}

/* ── (1e) THE LOCK AFFORDANCE — TWO OPTIONS ──────────────────────────────
   Neither is a grey padlock glyph on a flat field, which is what Part 1e
   rules out and what every product ships. */
.ar-lock-mark {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--c-r-full);
  flex: 0 0 auto;
}

.ar-lock-mark svg { width: 21px; height: 21px; display: block; }

/* OPTION 1 — THE WELL (default). A circular dish pressed INTO the glass. The
   light source is above, so the inside of the top edge catches it and the
   floor of the dish falls into shadow — which is the inverse of the specular
   on a raised pane, and is why it reads as pressed rather than applied. Same
   one light source; the geometry is what changes, not the lighting.
   It spends NO accent, which is why it is the default: the lime budget on
   this view belongs to the unlock action (Part 1d). */
.ar-lock-mark.is-well {
  background: color-mix(in srgb, var(--c-ink) 5%, transparent);
  color: var(--c-ink-2);
  box-shadow: inset 0 2px 3px rgba(20, 18, 14, .13),
              inset 0 -1px 0 var(--c-specular);
}

html:not(.theme-light) .ar-lock-mark.is-well,
html[data-theme="dark"] .ar-lock-mark.is-well {
  background: rgba(0, 0, 0, .28);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .45),
              inset 0 -1px 0 rgba(255, 252, 245, .07);
}

/* OPTION 2 — THE HALO. The lime at low alpha behind the mark, so the accent
   is present at the LOCK and not only at the action beneath it. The glyph
   takes --c-accent-ink (the readable olive at the same 72° hue), never the
   raw lime, which is unreadable as a stroke on paper. */
.ar-lock-mark.is-halo {
  background: radial-gradient(closest-side,
                rgba(204, 255, 0, .30) 0%,
                rgba(204, 255, 0, .12) 62%,
                rgba(204, 255, 0, 0) 100%);
  color: var(--c-accent-ink);
  box-shadow: var(--c-specular-edge-soft);
}

html:not(.theme-light) .ar-lock-mark.is-halo,
html[data-theme="dark"] .ar-lock-mark.is-halo {
  background: radial-gradient(closest-side,
                rgba(204, 255, 0, .22) 0%,
                rgba(204, 255, 0, .08) 62%,
                rgba(204, 255, 0, 0) 100%);
  color: var(--c-accent);
}

/* ── (1f) THE ACTIONS ────────────────────────────────────────────────────
   Copy unchanged; both strings are the ones this teaser already shipped. */
.ar-lock-card .ar-locked-reveal {
  order: 1;
  width: 100%;
  max-width: 320px;
  padding: 15px 22px;
  border: 1px solid var(--c-line);
  border-radius: var(--c-r-sm);
  background: transparent;
  color: var(--c-ink);
  font-family: var(--c-f-body);
  font-size: var(--c-t-micro);
  font-weight: 600;
  cursor: pointer;
}

.ar-lock-card .ar-locked-today {
  order: 2;
  color: var(--c-ink-2);
  font-size: var(--c-t-micro);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* THE LIME, SPENT ONCE. .is-solo is set by lockedTeaser only for a caller
   whose wallSource is a briefing surface — i.e. a view that renders exactly
   ONE of these. The archive and the Collection stack many and keep §ED240's
   entirely-neutral treatment, which is the ruling this must not break.
   See the note at the call site in js/card.js. */
.ar-lock-card.is-solo .ar-locked-reveal {
  background: var(--c-accent);
  color: var(--c-on-accent);
  border-color: transparent;
  /* The specular belongs on the lime too — it is a raised object under the
     same light as everything else, and without the edge it is the one flat
     element on a lit screen. */
  box-shadow: var(--c-specular-edge-soft), var(--c-e1-shadow);
}

html:not(.theme-light) .ar-lock-card.is-solo .ar-locked-reveal,
html[data-theme="dark"] .ar-lock-card.is-solo .ar-locked-reveal {
  /* Dark spends the BAND token, not raw lime — same divergence and same
     emission reason as the answer band (§ED440 F1 / item 5). */
  background: var(--c-band-fill);
}

/* Pressed state — Part 5b. The card's actions are tappable and must answer. */
.ar-lock-card .ar-locked-reveal:active { transform: scale(.97); }

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · THE CITATION LINE (Jake's amendment, mid-pass)

   WHY IT MOVES. "Published, sourced material first" is the product's central
   claim, and the line that carries it was set as body prose — the same ink,
   the same weight, the same everything as the sentence above it. A trust
   signal that is typographically indistinguishable from the text it vouches
   for is not doing its job. This is a HIERARCHY correction.

   IT IS ONE DECLARATION AND IT IS CORRECT IN BOTH THEMES, because
   --c-accent-ink is already the per-theme TEXT tier of the one accent:
       LIGHT  #3D4D00   olive     8.38:1 on paper   (AAA at small sizes)
       DARK   #A8D400   H 72.45°  11.08:1 on ground
   VERIFIED IN THE SHEET, not assumed from the amendment: css/tokens.css:367
   carries #A8D400 as the dark --c-accent-ink and states its measurement.

   THIS DOES NOT BREAK ONE-ACCENT-PER-VIEW. Olive and #A8D400 are hue 72 —
   the same colour at different lightness, which is the rule tokens.css §1
   already states ("dark mode is this same hue at a different lightness,
   never a second palette"). There is no second accent hue anywhere here.

   IT DOES AMEND THE LIME-TEXT LAW, and that amendment is written into
   docs/DESIGN-INVARIANTS.md in the same commit — a law left contradicting
   the code is how the next pass reverts this without knowing it was ruled.
   Amended: lime is permitted as small text on dark ONLY at --c-accent-ink or
   lower emission, and ONLY on the citation role. Every other small-text role
   on dark stays --c-ink. STREAK NUMERALS IN PARTICULAR STAY NEUTRAL — with
   the citation carrying the accent, a lime streak would put two accent
   elements in one view, which is the thing the budget exists to prevent.

   SCOPE — EVERY CITATION ROLE IN THE APP, greppped rather than guessed:
     .bf-source                  briefing secondary fact, collection, /record
     .bf-trust                   briefing HERO fact ("Sourced from … ·
                                 cross-checked") — the same role, other branch
                                 of the same if/else at js/card.js:1483/1486,
                                 so the two can never both appear for one fact
     .ar-locked-source           the locked state
     .bf-entity-archive-source   archive entity rows
     .vault-fact-source          the vault
   NOT .lv2-alm-source: that is the LANDING's almanac, a different surface
   with its own ratified design language, and this amendment is scoped to the
   app. Flagged in the report rather than changed silently.
   ══════════════════════════════════════════════════════════════════════════ */
html .bf-source,
html .bf-trust,
html .ar-locked-source,
html .bf-entity-archive-source,
html .vault-fact-source {
  color: var(--c-accent-ink);
  /* THE 16px FLOOR. Measured before this pass: these roles were set below it
     (see the §ED440 Phase E report / D2). A citation set at 12-14px on an
     audience that skews middle-aged reads as an afterthought no matter what
     colour it is — the size was as much of the problem as the ink, so both
     move or neither is fixed. --c-t-micro is the 16px floor in rem. */
  font-size: var(--c-t-micro);
  /* Slightly tighter tracking than body: the line is now coloured AND at the
     floor, and without this it starts to shout. It is a credential, not a
     headline. */
  letter-spacing: -0.004em;
  /* OPACITY 1 IS LOAD-BEARING, NOT TIDINESS. vault.html:134 sets
     `.vault-fact-source { opacity: .62 }` in a page-level <style>. Opacity is
     a different PROPERTY from colour, so nothing above overrides it: the
     amendment's #A8D400 would have been composited to 62% against the ground,
     landing near #6E8A00 — a different lightness on the same hue, at roughly
     half the ruled contrast, and it would have measured as "shipped" while
     being nothing of the sort. That is the present-but-null defect class this
     repo already has a name for. The ink tier does the recession now; the
     opacity was doing it twice. */
  opacity: 1;
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · OPTICAL SIZING (Part 5a)

   WHAT THE SHIPPED FILE ACTUALLY CARRIES — read from the woff2's fvar table,
   not from the foundry's spec sheet:

       assets/fonts/fraunces.woff2      opsz  9 -> 144   (default   9)
                                        wght  100 -> 900 (default 900)
       assets/fonts/fraunces-ext.woff2  identical
       assets/fonts/hanken-grotesk*     wght  100 -> 900 only
       CabinetGrotesk-Variable          wght  100 -> 900 only
       cormorant-garamond               wght  300 -> 700 only

   THERE IS NO `SOFT` AXIS AND NO `WONK` AXIS IN THE SHIPPED FILE. The full
   Fraunces from the foundry has both; this subset does not carry them.
   Confirmed twice, by two independent methods: the fvar table lists two axes,
   and `font-variation-settings: 'wonk' 1` renders byte-identically to no
   setting at all (311.344px both ways, measured). So the WONK evaluation Part
   5a asks for CANNOT be rendered from this file — there is nothing to turn on.
   It is reported rather than faked; see the §ED440 Phase E report.

   THE OTHER PART 5a PREMISE IS ALSO FALSE, AND IN OUR FAVOUR: "we almost
   certainly ship one static instance". We do not. `font-optical-sizing: auto`
   is the browser default, the axis is live, and it is already working —
   measured at 40px, `auto` renders 311.344px wide against `none` at
   320.609px. The axis has been doing its job unremarked.

   SO WHAT IS ACTUALLY LEFT TO WIN, AND IT IS REAL. `auto` sets opsz to the
   font size IN POINTS, which is px x 0.75. A 40px display title therefore
   asks for opsz 30 — out of a range that runs to 144. It is barely into
   display territory. Measured at 40px:

       opsz   9  (text end)        320.609px
       opsz  30  (what auto picks) 311.344px
       opsz 144  (display end)     265.141px   <- 17% narrower than auto

   That is not a scale change; the letterforms physically differ — tighter
   fit, higher stroke contrast, smaller apertures, which is what a display cut
   IS. Below, the display roles ask for the display end of the axis explicitly
   instead of accepting the point-size mapping.

   BOTH AXES ARE ALWAYS NAMED TOGETHER, AND THAT IS LOAD-BEARING.
   `font-variation-settings` replaces the whole variation state, so naming
   opsz alone would drop wght to the fvar DEFAULT — and Fraunces' wght default
   is 900, not 400. A one-axis declaration here would have silently set every
   display string on the app in Black. Named pairs cannot do that.

   72 AND NOT 144. The extreme end is a poster cut; at 32-40px on a phone its
   apertures close up and it starts to cost legibility on an audience that
   skews older, which is the same audience the 16px floor exists for. 72 is
   the middle of the display half and takes most of the difference.
   RENDERED BOTH WAYS for Jake — shots/ed440/phase-e/16-opsz-*.
   ══════════════════════════════════════════════════════════════════════════ */
/* §ED742 · SPLIT FROM A SHARED RULE. html .ba-zone > .ba-question, html .ba-zone > .ba-question.is-answered-q used to share this declaration
   block; that half now lives in css/recorder.css. KEEP THE TWO IDENTICAL —
   scripts/audit-ed742.js §5 diffs them. */
html .bf-hook-text,
html .ar-locked-hook,
html .ra-card > .ra-question {
  font-variation-settings: 'opsz' 72, 'wght' 400;
}

/* The masthead wordmark is the one display string that should take the axis
   to its end: it is set once, large, and it is a LOGO — a closed aperture is
   an identity there rather than a legibility cost. */
html .app-topnav-brand {
  font-variation-settings: 'opsz' 144, 'wght' 400;
}

@keyframes wvPulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

/* ── (4f) PLAYBACK — §ED463, REUSING vault.html's affordance verbatim ──────
   §ED440-DESIGN-LANGUAGE.md R8: `.vault-audio` is the REAL player (genuine
   empty and error substates, native <audio controls>, the real decoded-buffer
   waveform via window.Waveform) and the landing's `.p-wave` is a picture of
   one — R8 names this "the one place the app leads" and forbids downgrading
   it. This is that component, not a second one: identical class names,
   identical rules, so js/reader-answer.js's play button (anonymous and
   signed-in readers alike, §ED463) and js/vault.js's are the same visual
   vocabulary, not two.

   DUPLICATED HERE, NOT MOVED. vault.html:80-143 keeps these inline in its own
   <style> block, deliberately namespaced to that page. briefing.html (where
   reader-answer.js actually mounts an answer block) loads briefing.css, not
   vault.html — so the rules are restated here, verbatim, rather than adding a
   new stylesheet link to a page ed462-landing owns. Keep both copies in sync
   by hand if either changes; a future pass may want to promote both to a
   shared sheet, which is a decision about vault.html this file does not make. */
.vault-media { margin-top: 12px; }

.vault-media-item { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.vault-media-label { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; opacity: .6; }

.vault-media-play { font: inherit; font-size: .875rem; padding: 4px 12px; border-radius: 999px; border: 1px solid currentColor; background: none; color: inherit; cursor: pointer; }

.vault-media-gone { font-size: .8125rem; opacity: .7; margin: 6px 0 0; }

.vault-audio { width: 100%; margin-top: 8px; }

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · PART 2 — THE DESKTOP SPLIT LAYOUT. RULED 2026-08-20, SHIPPED.

   Jake compared three renders (L1 companion rail, L2 split, L3 split+rail)
   and took L2 — read on the left, respond on the right, both visible at
   once, no scrolling between the fact and the question. Shipped
   UNCONDITIONALLY below at >=1024px: the html[data-ed440-layout] attribute
   this comparison used is gone from these rules, because the comparison is
   over.

   L1 AND L3 ARE DROPPED, NOT DELETED. Both put the signup's lime CTA on
   screen at the same time as the lime citation band, which the
   one-lime-per-view budget cannot absorb — see docs/ED440-DESIGN-LANGUAGE.md
   R10, the same law that dropped the HALO lock affordance. L1 is additionally
   blocked structurally: its rail promises streak, today's-progress and a
   vault-preview module, none of which exist as components today (see "WHAT
   THE RAIL CONTAINS, STATED HONESTLY" below) — worth rebuilding if those
   three get built, recorded here so the work is not re-derived. Their rules
   stay below, still gated on html[data-ed440-layout="L1"|"L3"], reachable
   only by the render script — see docs/ED440-DESIGN-LANGUAGE.md R11.

   THE DIAGNOSIS IS CORRECT AND MEASURED. At 1440 the card is 820px inside a
   1440 viewport: ~620px doing nothing. But --c-measure-narrow is RIGHT for
   reading and wrong for the PAGE, and widening it would damage the column to
   solve a layout problem. L2 keeps the measure and uses the space by
   splitting fact and response into two columns instead.

   COLLAPSES BELOW 1024px to today's single column, untouched here — mobile
   at 375 is already good and does not change.

   WHAT THE RAIL CONTAINS, STATED HONESTLY (kept for L1/L3, dormant below).
   Part 2 described a rail carrying streak, today's progress, a vault preview
   and the culture selector. Only some of that exists as a component today:
   the culture selector and the calendar trigger (.bf-header) and the account
   card (.bf-foot) are real and are what the rail was built from. A streak
   module, a progress module and a vault preview DO NOT EXIST on this page —
   .bf-result is nested inside .bf-daily-body and cannot be grid-placed out
   of it without a DOM change, which a render-only pass must not make.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 1024px) {
  /* THE SHIP: L2. The page gets the wide measure, the PROSE COLUMN DOES NOT.
       --c-measure (1080) is the page rail; the reading column stays capped by
       the card's own padding and by --ar-lock-measure / --c-measure-narrow
       wherever those already apply. Split happens INSIDE the card so the card
       stays one object.
  
       §ED527b — `max-width: 1240px` WAS HERE AND NEVER APPLIED. It is 0-1-0 and
       `.bf-page .bf-wrap` at :3388 is 0-2-0; a media query adds no specificity,
       so source order was never consulted and this declaration was discarded on
       every render for the whole life of L2. It is DELETED rather than repaired
       in place: left here it is a live trap, one edit to :3388 away from
       silently widening the rail on every desktop edition. The rail now belongs
       to the §ED527b block below, which owns it conditionally and at a
       specificity that actually wins. */
  .bf-wrap {
      display: grid;
      align-items: start;
      column-gap: clamp(24px, 3vw, 48px);
      grid-template-columns: minmax(0, 1fr);
    }
  
  .bf-daily-body {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
      column-gap: clamp(28px, 3vw, 56px);
      align-items: start;
    }
  
  /* Everything defaults to the reading column; only the answer zone crosses. */
  .bf-daily-body > * { grid-column: 1; }
  
  .bf-reaction-wrap {
      grid-column: 2;
      grid-row: 1 / span 99;
      position: sticky;
      top: 92px;
    }
  
  /* ── L1 · COMPANION RAIL — DROPPED, kept for the render script only ─────
       The card holds the measure and sits centred-left; the rail carries the
       page's context modules. Attribute-gated: html[data-ed440-layout] is set
       by nothing but scripts/render-ed440-screens.js. */
  html[data-ed440-layout="L1"] .bf-wrap {
      grid-template-columns: minmax(0, 820px) minmax(240px, 320px);
      grid-template-areas: 'card rail' 'card railfoot';
      grid-template-rows: auto 1fr;
    }
  
  html[data-ed440-layout="L1"] .bf-stack  { grid-area: card; }
  
  html[data-ed440-layout="L1"] .bf-header { grid-area: rail; position: static; }
  
  html[data-ed440-layout="L1"] .bf-foot   { grid-area: railfoot; display: block !important; margin-top: 20px; }
  
  /* The rail's modules become cards rather than a toolbar row. */
  html[data-ed440-layout="L1"] .bf-header-row,
    html[data-ed440-layout="L3"] .bf-header-row {
      flex-direction: column;
      align-items: stretch;
      gap: 12px;
    }
  
  html[data-ed440-layout="L1"] .bf-cult-trigger,
    html[data-ed440-layout="L3"] .bf-cult-trigger { width: 100%; justify-content: flex-start; }
  
  html[data-ed440-layout="L1"] .bf-header,
    html[data-ed440-layout="L3"] .bf-header {
      padding: 18px;
      border-radius: var(--c-r-lg);
      border: var(--c-e1-border);
      box-shadow: var(--c-e1-shadow);
    }
  
  /* ── L3 · SPLIT + RAIL — the bento — DROPPED, kept for the render script
       only. L2's split, plus a thin rail for the context modules. */
  html[data-ed440-layout="L3"] .bf-wrap {
      max-width: 1400px;
      grid-template-columns: minmax(0, 1fr) minmax(220px, 280px);
      grid-template-areas: 'card rail' 'card railfoot';
      grid-template-rows: auto 1fr;
    }
  
  html[data-ed440-layout="L3"] .bf-stack  { grid-area: card; }
  
  html[data-ed440-layout="L3"] .bf-header { grid-area: rail; position: static; }
  
  html[data-ed440-layout="L3"] .bf-foot   { grid-area: railfoot; display: block !important; margin-top: 20px; }
  
  html[data-ed440-layout="L3"] .bf-daily-body {
      grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
    }
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED527b · THE DESKTOP COLUMN — L2 AMENDED

   §ED527 measured the shipped desktop edition page and found the prose
   rendering in a 267px column at 1440 with a 380px near-empty column beside
   it. Three defects, all fixed here; fixing only the first leaves the second
   and third live.

   D1 · THE PAGE RAIL LOST ON SPECIFICITY.
        `.bf-page .bf-wrap { max-width: var(--c-measure-narrow) }` (:3388,
        0-2-0) outranked L2's `.bf-wrap { max-width: 1240px }` (0-1-0). A MEDIA
        QUERY ADDS NO SPECIFICITY, so source order was never reached. The
        `grid-template-columns` half of the same L2 block had no competitor and
        DID apply: the split shipped, the widening did not, and an 820px card
        that used to give prose ~692px gave it 267px. Strictly worse than
        before §ED440. See DESIGN-INVARIANTS.md I-1.

   D2 · A COLUMN RESERVED FOR A ZONE THAT IS USUALLY ABSENT.
        With no approved reader_question, column two holds a 71px pill row in
        380px of reserved width — the state for 56% of the last 111 editions.

   D3 · THE SPLIT WAS GATED BELOW THE WIDTH AT WHICH IT FITS.
        L2 opened the split at 1024px. `.bf-wrap` resolves to (viewport − 102px)
        capped at its max-width, so the 1240px rail is only reachable at
        viewport >= 1342. Fixing D1 alone would have left 1024–1340 rendering a
        381–604px column.

   THE SHAPE OF THE FIX: ONE COLUMN IS THE DESKTOP DEFAULT; THE SPLIT IS THE
   OPT-IN. Below, the reading measure lands at ~690px in BOTH states, so the
   answer rail becomes ADDITIVE width rather than subtractive — which is what
   L2's own comment said it wanted ("L2 keeps the measure and uses the space").

   MECHANISM, STATED. The split is conditional on `:has(.ba-zone)`.

     · Why :has() and not a JS-set class — .ba-zone is built synchronously by
       js/reader-answer.js:buildAnswerBlock inside .bf-reaction-wrap, and this
       stays a render-only change: no DOM edit, no class to keep in sync, and
       the condition re-evaluates for free if the zone is ever mounted late.

     · Why the polarity is POSITIVE. §ED527's first draft wrote the inverse,
       `.bf-wrap:has(.bf-daily-body:not(:has(.ba-zone)))` — :has() CANNOT NEST,
       so that selector is invalid and a parser DROPS IT SILENTLY. Making the
       single column the default means the conditional only ever needs a flat,
       positive `:has(.ba-zone)`. There is nothing here that can silently drop.

     · What an engine without :has() does. It discards these three rules and
       renders one 690px column at every desktop width — the correct fallback,
       not a broken one. scripts/audit-ed527-layout.js asserts CSS.supports(
       'selector(:has(.ba-zone))') anyway, so a green can never mean "the
       selector was never evaluated".

   SPECIFICITY, COUNTED, because that is the bug being fixed:
     .bf-page .bf-daily-body                              0-2-0
     .bf-page .bf-daily-body:has(.ba-zone)                0-3-0  wins
     .bf-page .bf-daily-body > .bf-reaction-wrap          0-3-0
     .bf-page .bf-daily-body:has(.ba-zone) > .bf-reaction-wrap  0-4-0  wins
     .bf-page .bf-wrap                            (:3388) 0-2-0
     .bf-page .bf-wrap:has(.bf-daily-body .ba-zone)       0-4-0  wins
   Every override in this block beats its competitor on SPECIFICITY, not on
   source order — so it survives being moved, and survives another rule being
   appended after it.

   GUARDED BY scripts/audit-ed527-layout.js, which renders briefing.html with
   real payloads at 8 widths x 2 themes x both edition kinds and asserts the
   prose column never falls below 590px (62ch, .bf-debrief's own declared
   measure) on desktop and never shrinks as the viewport grows. Its --plant
   arm re-creates D1 and is expected to go red.

   MOBILE IS UNTOUCHED. Nothing here applies below 1024px.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 1024px) {

  /* D1 + D2 · ONE COLUMN IS THE DESKTOP DEFAULT. The 820px card and its 64px
     side padding give the prose ~692px — the measure --c-measure-narrow was
     chosen for, and what the page rendered before L2's split took 380px + a
     43px gutter out of a card that never grew to pay for them. */
  .bf-page .bf-daily-body {
    display: block;
  }
  /* Sticky is meaningless in block flow and would pin the pill row to the
     viewport as the reader scrolls past it. */
  .bf-page .bf-daily-body > .bf-reaction-wrap {
    position: static;
    top: auto;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · PART 5e — THE EMPTY STATE

   "Today's edition is still at the printers." plus a button, set as two grey
   paragraphs in the middle of a blank card. For a reader who arrives before
   the edition lands, THIS IS THE PRODUCT — the only screen they see — and it
   currently looks like an error page for a service that is down.

   NO NEW COPY. Both strings and the retry button are exactly what
   js/briefing.js showState() already renders; this is presentation only,
   which is also why it needs no JS change at all.

   IT BORROWS THE LOCKED STATE'S VOCABULARY DELIBERATELY. A press mark in a
   specular-edged well, then the heading at display size, then rules
   dissolving into the ground — the same grammar as the seal, saying the same
   kind of thing: there IS something here, it is not available to you yet.
   A reader who meets both screens meets one design.
   ══════════════════════════════════════════════════════════════════════════ */
html .bf-page .bf-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  padding: var(--c-room-sm) 20px calc(var(--c-room-sm) + 8px);
  max-width: 34ch;
  margin-inline: auto;
}

/* THE MARK — a press/plate glyph in the same pressed well the lock uses.
   ::before so no DOM change is needed. Drawn, not an emoji, and it carries no
   text so it needs no label: the heading beneath it is the accessible name of
   this screen and the mark is decoration. */
html .bf-page .bf-state::before {
  content: '';
  width: 56px;
  height: 56px;
  border-radius: var(--c-r-full);
  background:
    /* the plate: two ruled lines, centred — a page on the press */
    linear-gradient(var(--c-ink-3), var(--c-ink-3)) center 22px / 22px 2px no-repeat,
    linear-gradient(var(--c-ink-3), var(--c-ink-3)) center 30px / 14px 2px no-repeat,
    color-mix(in srgb, var(--c-ink) 5%, transparent);
  box-shadow: inset 0 2px 3px rgba(20, 18, 14, .13),
              inset 0 -1px 0 var(--c-specular);
  opacity: .8;
}

html:not(.theme-light) .bf-page .bf-state::before,
html[data-theme="dark"] .bf-page .bf-state::before {
  background:
    linear-gradient(var(--c-ink-3), var(--c-ink-3)) center 22px / 22px 2px no-repeat,
    linear-gradient(var(--c-ink-3), var(--c-ink-3)) center 30px / 14px 2px no-repeat,
    rgba(0, 0, 0, .28);
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, .45),
              inset 0 -1px 0 rgba(255, 252, 245, .07);
}

html .bf-page .bf-state .bf-state-h {
  font-family: var(--c-f-display);
  font-variation-settings: 'opsz' 72, 'wght' 400;
  font-size: clamp(1.375rem, 3.6vw, 1.75rem);
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: var(--c-ink);
  margin: 0;
}

/* THE DISSOLVE — the same rules as the seal, saying the same thing: an
   edition exists, it is just not here yet. */
html .bf-page .bf-state::after {
  content: '';
  width: 100%;
  max-width: 26ch;
  height: 34px;
  margin-top: 4px;
  background:
    linear-gradient(var(--c-ink-2), var(--c-ink-2)) 0 0    / 100% 9px no-repeat,
    linear-gradient(var(--c-ink-2), var(--c-ink-2)) 0 18px / 62% 9px no-repeat;
  opacity: .22;
  border-radius: 3px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.5) 45%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,.5) 45%, transparent 100%);
}

/* ══════════════════════════════════════════════════════════════════════════
   §ED440 PHASE E · PART 5f — THE CULTURE SELECTOR AS A BRAND MOMENT

   It reads as a form control: a bordered pill with a caret, the same object
   as a sort dropdown. But this is the reader saying WHO THEY ARE — it is the
   single most identity-laden interaction in the product and the one a new
   reader meets first.

   WHAT CHANGES: the trigger gains the flag as a real object (a raised, lit
   token rather than an inline emoji in a text run), the name takes the
   display face, and the open sheet becomes GLASS — it is chrome, it floats
   above content, so it belongs to the glass layer by the Part 3 rule rather
   than by decoration.
   NO COPY CHANGES and no DOM changes.
   ══════════════════════════════════════════════════════════════════════════ */
html .bf-cult-trigger {
  gap: 10px;
  padding: 10px 16px;
  border-radius: var(--c-r-full);
  border: 1px solid var(--c-line);
  box-shadow: var(--c-specular-edge-soft), var(--c-e1-shadow);
}

/* The flag becomes a struck token — round, raised, lit from the one source —
   instead of an emoji sitting in a line of text. */
html .bf-cult-trigger-flag {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--c-r-full);
  background: var(--c-surface-2);
  box-shadow: inset 0 1px 0 var(--c-specular), 0 1px 2px rgba(20, 18, 14, .10);
  font-size: 1rem;
  line-height: 1;
}

/* §ED763 (F3) — THE "CHOOSE CULTURE" STATE. The chip is now shown to a reader
   who has not said where they are from (js/card.js renderTrigger), so it needs a
   state with no flag in it. The struck token above is a raised 28px disc; left
   empty it reads as a broken chip, so it is removed rather than blanked. The
   label stays "Choose culture" — an invitation, naming no culture, which is
   exactly what §ED612 required of a reader whose heritage is unknown. */
.bf-cult-trigger.is-unset .bf-cult-trigger-flag { display: none; }

html .bf-cult-trigger-name {
  font-family: var(--c-f-display);
  font-variation-settings: 'opsz' 40, 'wght' 400;
  font-size: var(--c-t-micro);
  color: var(--c-ink);
  max-width: 180px;
}

html .bf-cult-trigger-caret { color: var(--c-ink-3); }

/* THE OPEN SHEET IS CHROME, SO IT IS GLASS. Full recipe, specular edge, L3
   elevation — the same material as the masthead and the nav, because it is
   the same KIND of thing: a surface floating above the content.

   §ED528 — THE PANEL WAS SEE-THROUGH. --c-glass-chrome is .66 alpha (light) /
   .72 (dark), and blur alone does not destroy legibility: the wordmark and the
   dateline sit directly behind this panel on /briefing and read straight
   through it. Measured, not eyeballed — scripts/audit-ed528-cult-sheet.js
   composites the panel over a known text colour and compares against the same
   pixel with the panel removed.

   THE TREATMENT IS NOT REMOVED — this is an OPACITY fix, per §ED440's own
   licence for glass on the chrome layer. The tint, the blur, the specular
   edge, the line and the L3 elevation are all untouched. What changes is that
   the tint is now composited over an OPAQUE base instead of over the page:
   --c-surface, an existing §ED440 token (#FFFDFA light / #191714 dark), is
   painted underneath and --c-glass-chrome is layered on top of it as a solid
   gradient. Net alpha is 1 by construction, so nothing behind can be legible.

   WHY NOT RAISE THE TOKEN ITSELF: --c-glass-chrome is shared with the masthead
   and the nav (global.css), and tokens.css documents a coupling — "push this
   to .72 and the specular stops reading". Raising it would silently restyle
   two other surfaces and break that coupling to fix a third. The fix is scoped
   to the one panel that has opaque content behind it.

   backdrop-filter is KEPT deliberately: it composites nothing now, but it is
   the treatment this panel is declared to have, and removing it would also
   drop the containing block/stacking context the panel's positioning sits on.
   The cost is one blur on a small, transient, click-to-open panel. */
html .cult-sheet {
  background:
    linear-gradient(var(--c-glass-chrome), var(--c-glass-chrome)),
    var(--c-surface);
  -webkit-backdrop-filter: var(--c-glass-filter);
          backdrop-filter: var(--c-glass-filter);
  transform: translateZ(0);
  border: 1px solid var(--c-glass-chrome-line);
  border-radius: var(--c-r-lg);
  box-shadow: var(--c-specular-edge), var(--c-e3-shadow);
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  html .cult-sheet { background: var(--c-surface); }
}

html .cult-sheet-title {
  font-family: var(--c-f-display);
  font-variation-settings: 'opsz' 40, 'wght' 400;
  font-size: var(--c-t-micro);
  color: var(--c-ink-2);
}

html .cult-sheet-item {
  border-radius: var(--c-r-md);
  color: var(--c-ink);
  font-size: var(--c-t-micro);
  gap: 12px;
  transition: background-color var(--c-m-toggle-out) var(--c-e-standard),
              transform var(--c-m-tap) var(--c-e-standard);
}

html .cult-sheet-item:active { transform: scale(.98); }

html .cult-sheet-item-flag {
  display: grid;
  place-items: center;
  width: 30px; height: 30px;
  border-radius: var(--c-r-full);
  background: var(--c-surface-2);
  box-shadow: inset 0 1px 0 var(--c-specular);
  font-size: 1.05rem; line-height: 1;
}

html .cult-sheet-item.is-current {
  background: var(--c-accent-wash);
  box-shadow: var(--c-specular-edge-soft);
}

html .cult-sheet-item-check { color: var(--c-accent-ink); }

/* §ED528 — A LOCKED CULTURE IS STILL SHOWN, AND STILL TAPPABLE.
   Same reasoning as the archive showing locked titles: the shelf is the
   argument, so the row keeps its flag, its name and its full 44px hit area —
   it recedes, it does not disappear and it is never disabled. The lock is the
   ONLY thing added; tapping still switches culture and lands on the membership
   wall, which is where the price is stated (js/card.js's renderList note).

   Deliberately NOT `cursor: not-allowed` and NOT `pointer-events: none` —
   both would state a refusal the product does not make.
   .bf-cult.is-locked (the older row selector, further up this file) uses
   opacity .6 for its never-tappable "SOON" cultures; this uses ink colour
   instead, because these ARE tappable and must not read as inert. */
html .cult-sheet-item.is-locked .cult-sheet-item-name { color: var(--c-ink-2); }

html .cult-sheet-item.is-locked .cult-sheet-item-flag { opacity: .72; }

html .cult-sheet-item-lock {
  flex-shrink: 0;
  font-size: .95rem;
  line-height: 1;
  color: var(--c-ink-3);
}

html .cult-sheet-item.is-locked:hover .cult-sheet-item-lock { color: var(--c-ink-2); }

/* ── §ED529 · A LOCKED CULTURE READS AS MEMBERS-ONLY, NOT AS BROKEN ────────
   The icon carried the whole message and a 0.95rem glyph is not a message.
   The word goes next to it. Sized and coloured off the SAME two tokens the
   sheet's own title already uses (--c-t-micro, --c-ink-3), so this adds no
   new type step and no new ink tier. It is a state label, not an action:
   no accent, no border, no button affordance — §ED528's ruling that the row
   stays tappable and unrefused is untouched, and the one-lime-action budget
   (§ED240/Part 1d) is not spent on a label. */
html .cult-sheet-item-tag {
  flex-shrink: 0;
  font-family: var(--c-f-body, var(--font-body));
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--c-ink-3);
}

html .cult-sheet-item.is-locked:hover .cult-sheet-item-tag { color: var(--c-ink-2); }

/* THE READER'S OWN CULTURE IS OWNED, AND OWNED IS FULL STRENGTH.
   .is-locked dims the flag chip to .72; the current row must never inherit
   that or a lock and a possession start to look alike. Stated explicitly
   rather than relied on: is-current and is-locked are independent classes and
   nothing in renderList() forbids a reader from holding both at once. */
html .cult-sheet-item.is-current .cult-sheet-item-name { color: var(--c-ink); opacity: 1; }

html .cult-sheet-item.is-current .cult-sheet-item-flag { opacity: 1; }

html .cult-sheet-item.is-current { opacity: 1; }

/* ── §ED529 · ADD A CULTURE — the one row that navigates ───────────────────
   Separated from the culture list by a hairline so it reads as a different
   KIND of thing (an action, not a choice), and given the same 30px chip the
   flags wear so the row's left edge still lines up. Neutral by §ED240: the
   Daily already spends its single lime action on the membership wall's own
   CTA, and this sheet can be open while that card is on screen. */
html .cult-sheet-add {
  display: flex; align-items: center; gap: 12px;
  margin-top: 6px; padding: 13px 12px;
  border-top: 1px solid var(--c-glass-chrome-line);
  border-radius: var(--c-r-md);
  color: var(--c-ink-2);
  font-family: var(--font-numeric);
  font-size: var(--c-t-micro);
  font-weight: 600;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--c-m-toggle-out) var(--c-e-standard),
              color var(--c-m-toggle-out) var(--c-e-standard);
}

html .cult-sheet-add:hover { background: var(--c-surface-2); color: var(--c-ink); }

html .cult-sheet-add:focus-visible { outline: 2px solid var(--c-accent-ink); outline-offset: -2px; }

/* §ED529 — the four dropdown glyphs are inline SVG now (js/card.js's
   cultIcon(), and the note above it explains why the icon FONT was not the
   answer). The old rules sized them with font-size, which an <svg> with
   explicit width/height ignores; these three give the wrappers a box to sit in
   and let `color` keep driving the stroke. Sizes come from cultIcon()'s own
   argument, so there is exactly one place that decides how big a glyph is. */
html .bf-cult-trigger-caret,
html .cult-sheet-item-check,
html .cult-sheet-item-lock {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}

html .cult-sheet-add-icon {
  display: grid; place-items: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border-radius: var(--c-r-full);
  background: var(--c-surface-2);
  box-shadow: inset 0 1px 0 var(--c-specular);
  font-size: 1rem; line-height: 1;
}

/* OPACITY, NOT COLOUR — the transform/opacity-only rule applies to the
   speaking state too, so the icon breathes rather than tinting. */
@keyframes ba-speak { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* ── THE QUESTION-SHAPED SKELETON ────────────────────────────────────────
   js/briefing.js's renderSkeleton() draws these three bars when the fact
   surface is off. They model the question (two lines) and the record
   affordance (one tall block) — the shape that actually lands, so the wait
   does not become a layout jump. .bf-skel-line carries the shimmer already. */
.bf-skel-q      { height: 44px; width: 92%; margin-bottom: 10px; }

.bf-skel-q-2    { height: 44px; width: 64%; margin-bottom: 26px; }

.bf-skel-rec    { height: 168px; width: 100%; border-radius: var(--c-r-xl); }

/* The honest-state link (js/briefing.js showState opts.link). */
.bf-state-link { display: inline-block; margin-top: 14px; font-size: var(--c-t-micro); }

/* ── NARROW ──────────────────────────────────────────────────────────────
   The prototype's own 560px breakpoint. Nothing changes shape; the record
   affordance loses some of its height so the writing box stays reachable
   without a scroll on a short phone. */
@media (max-width: 560px) {
  .bf-skel-rec { height: 140px; }
}
