/* WikiWatch 2026 design system (TFG refresh, July 2026).
   Color tokens sampled from the mock — confirm exact values with TFG. */
:root {
  --ww-purple: #4F6DA1;
  --ww-purple-dark: #415A86;
  --teal: #50C6BE;
  --teal-dark: #2e9c93;
  --teal-tint: #C0E9E6;
  --teal-text: #0F6A61;
  --talk-tint: #B9D5F4;
  --talk-text: #334D77;
  --ww-link: #0645AD;
  --ink: #1a2033;
  --muted: #6b7280;
  /* PLACEHOLDER pending TFG confirmation: fills the real gap between --muted
     (#6b7280 / gray-500) and --ink — gray-600, the next step on the same ramp.
     Used only for the traffic "back to normal" row, which needs to read a step
     stronger than muted timestamps without becoming full body text. */
  --muted-strong: #4b5563;
  --bg: #F9FBFC;
  --card: #ffffff;
  --line: #E1E6EE;
  --band: #EEF1F7;
  --th: #949AA7;
  --danger: #d64545;
  --alert: #ed5e42;
  --before-border: #e8c468;
  --after-border: #9cc3e5;
  /* Global corner radius (TFG July 20): one 5px value for every card, content
     box, stat callout, and section. Controls (buttons, inputs, pills) keep their
     own radii; inner table-cell corners stay at --radius minus the 1px border. */
  --radius: 5px;
  /* Single horizontal page gutter for the header, article hero, main and footer,
     so they stay flush with each other. Steps down at the mobile breakpoint (see
     the "Mobile" section at the foot of this file). */
  --gutter: 4rem;
}

/* Edit List toolbar: the "View" filter set on the left, "Mark all as viewed" on
   the right. The pills and the .el-filter-form dropdown are the same filter set
   at two breakpoints — the mobile section at the foot of this file swaps them. */
.el-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-bottom: 1rem; }
.el-filters { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.el-view-label { font-weight: 700; font-size: 0.88rem; }
.el-filter-form { display: none; margin: 0; gap: 0.5rem; align-items: center; }
.el-filter-select { height: 2.4rem; min-width: 7rem; }

/* Edit List: dedicated edit-kind icon column between Type and Summary. Larger,
   heavier read than the old inline icons (per the mockup), centered with room
   on both sides. Empty cell when the edit is unclassified (no guessed icons). */
.ev-icon-col, td.ev-icon { width: 3rem; text-align: center; padding-left: 0.4rem; padding-right: 0.4rem; }
td.ev-type { width: 5.5rem; }
td.ev-icon i { font-size: 1.35rem; color: #3a4252; vertical-align: middle; }
td.ev-icon i.ev-alert { color: var(--alert); }
/* Same edit-kind glyph in the Edit Detail / Event Detail header (shared partial
   article/_edit_kind_icon.html) — sized for the header line, list cell keeps its
   own size above. */
.edit-header .ev-icon-glyph { flex: none; font-size: 1.2rem; color: #3a4252; }
.ev-icon-glyph.ev-alert { color: var(--alert); }
/* traffic "back to normal" row: subordinate, but a step stronger than muted
   since it carries real numbers (--muted-strong is a TFG placeholder). */
.traffic-normal-text { color: var(--muted-strong); }

/* Non-edit events + deletion-class high-alert treatment (Phase 3) */
/* Event chips get a dedicated peach fill (#F7D2AF, per TFG July 20 mockup); the
   dark-warm text follows the same tint+dark-text pattern as the Article/Talk
   chips (she specified only the background). Traffic keeps the old neutral gray. */
.chip-event { background: #F7D2AF; color: #7a4a1f; }
.chip-traffic { background: #eceef3; color: #5b6270; }
.filter-pill.on-event { background: #F7D2AF; border-color: #F7D2AF; color: #7a4a1f; }
.alert-row > td, tr.alert-row > td { background: #fff6f4; }
.alert-text { color: var(--alert); font-weight: 700; }
.revdel-flag { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.revdel-notice {
  background: #fff6f4; border: 1px solid #f6d9d2; border-radius: var(--radius);
  padding: 0.5rem 0.7rem; font-size: 0.82rem; color: #8a3b28; margin: 0 0 0.7rem;
}
.wl-deleted { color: var(--alert); font-weight: 600; white-space: nowrap; }
* { box-sizing: border-box; }
body {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  margin: 0; color: var(--ink); background: var(--bg);
  display: flex; flex-direction: column; min-height: 100vh;
  font-size: 14px;
}
a { color: var(--ww-link); text-decoration: underline; text-underline-offset: 2px; }
/* An icon inside a link doesn't take the link's underline (TFG Aug 1): the text
   stays underlined, the glyph doesn't. `text-decoration: none` on the <i> can't
   do this — a decoration propagated from an ancestor is not cancellable by a
   descendant — but propagation skips atomic inlines, so inline-block is what
   does the work. App-wide, so any new icon link picks it up for free. */
a .ph { display: inline-block; }

/* Headings (TFG Aug 1): one weight for every level and one size per level, set
   on the elements themselves so a heading can't drift by living in a different
   card. The per-selector `font-weight: 700` / `font-size` declarations these
   replace were removed below rather than left to be overridden. Heading-role
   CLASSES (.sr-title, .section-h, the .wl-section group band) restate the 600,
   since an element selector can't reach them. */
h1, h2, h3, h4, h5, h6 { font-weight: 600; }
h1 { font-size: 1.6rem; }
h3 { font-size: 1rem; }

/* header */
header.site {
  background: #fff; border-bottom: 1px solid var(--line);
  padding: 1.2rem var(--gutter); display: flex; align-items: center; gap: 2rem;
}
header.site .brand {
  font-weight: 800; letter-spacing: 0.02em; text-decoration: none; font-size: 1.05rem;
  color: var(--ww-purple); display: flex; align-items: center; gap: 0.3rem;
}
header.site .brand .mark { color: var(--teal); font-size: 1.3rem; }
header.site nav { display: flex; align-items: center; margin-left: auto; }
header.site nav a { color: var(--ink); text-decoration: none; font-size: 0.88rem; margin-left: 1.4rem; }
header.site nav a:hover { color: var(--ww-purple); }
/* Mobile ≡ menu. Hidden above the breakpoint, where the nav row carries the
   links; the mobile section at the foot of this file does the swap. Panel styling
   follows the ••• row menus (same border, radius, shadow, hover fill).
   NOTE (designer review): the mockups show the closed ≡ only, so this open
   treatment — a right-aligned dropdown panel under the header — is our
   convention pick, not TFG's. */
.nav-menu { display: none; position: relative; margin-left: auto; }
.nav-menu > summary {
  list-style: none; cursor: pointer; color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  width: 2.75rem; height: 2.75rem; margin-right: -0.6rem; border-radius: 6px;
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary .ph { font-size: 1.6rem; display: block; }
.nav-menu[open] > summary { background: var(--band); }
header.site nav.nav-menu-panel {
  position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 30; min-width: 190px;
  flex-direction: column; align-items: stretch; margin-left: 0;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(26, 32, 51, 0.12); padding: 0.35rem;
}
header.site nav.nav-menu-panel a {
  margin-left: 0; padding: 0.6rem 0.6rem; border-radius: 5px; font-size: 0.9rem;
}
header.site nav.nav-menu-panel a:hover { background: var(--bg); color: var(--ink); }

/* article hero + tabs (light) */
.article-hero { background: #fff; border-bottom: 1px solid var(--line); padding: 2rem var(--gutter) 0; }
.article-hero h1 { margin: 0 0 2rem; }
.tabs { display: flex; gap: 1.6rem; }
.tabs a {
  padding: 0 0 0.7rem; font-size: 0.88rem; color: var(--ink); text-decoration: none;
  border-bottom: 3px solid transparent; display: flex; align-items: center; gap: 0.4rem;
}
.tabs a.active { font-weight: 700; border-bottom-color: var(--teal); }
.tabs a:not(.active):hover { color: var(--ww-purple); }

main { flex: 1; width: 100%; padding: 1.5rem var(--gutter); }
main.full { max-width: none; padding: 0; }

/* cards, lists */
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 1.25rem; margin-bottom: 1.25rem; }
.card h3 { margin: 0 0 0.75rem; }
/* Flat rows inside one container: no floating rounded per-row cards. Rows are
   separated by thin dividers; the enclosing .wl-section or .table-card draws
   the single border + rounded corners. */
table.list { width: 100%; border-collapse: collapse; }
table.list th {
  text-align: left; padding: 1rem 0.9rem; color: var(--th); font-weight: 500;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  border-bottom: 1px solid var(--line); background: var(--card);
}
table.list td {
  background: var(--card); padding: 0.85rem 0.9rem; font-size: 0.86rem; vertical-align: middle;
  border-bottom: 1px solid var(--line);
}
table.list tbody tr:last-child td { border-bottom: none; }
/* Edit List rows sit a touch taller than the shared row height (TFG July 20). */
table.list.edit-list td { padding-top: 1.3rem; padding-bottom: 1.3rem; }
/* Unread rows: teal left edge — a crisp square strip drawn as an absolutely
   positioned bar so it ignores the row's corner radius (no lozenge on the last
   row) and can't be out-specified by any border rule. */
table.list td:first-child { position: relative; }
table.list tr.unseen td:first-child::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--teal);
}

/* Standalone tables (Edit List, Top Editors, Search Results) get the single
   container the Watchlist gets from .wl-section. No overflow:hidden — that would
   clip the unread bar's ends at the rounded corners — so the corner *cells* are
   rounded instead, and the absolutely-positioned bar stays square on every row. */
.table-card {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  margin-bottom: 1.25rem;
}
.table-card table.list { margin: 0; }
.table-card table.list thead th:first-child { border-top-left-radius: 4px; }
.table-card table.list thead th:last-child { border-top-right-radius: 4px; }
/* search results have no thead: round the first row's top corners instead */
.table-card table.search-list tbody tr:first-child td:first-child { border-top-left-radius: 4px; }
.table-card table.search-list tbody tr:first-child td:last-child { border-top-right-radius: 4px; }
.table-card table.list tbody tr:last-child td:first-child { border-bottom-left-radius: 4px; }
.table-card table.list tbody tr:last-child td:last-child { border-bottom-right-radius: 4px; }

/* buttons */
.btn {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.5rem 1rem; border-radius: 7px; border: 1px solid var(--line);
  background: #fff; cursor: pointer; font-size: 0.85rem; text-decoration: none;
  color: var(--ink); font-family: inherit; font-weight: 500;
}
.btn-primary, .btn-dark {
  background: var(--ww-purple); border-color: var(--ww-purple); color: #fff; font-weight: 600;
}
.btn-primary:hover, .btn-dark:hover { background: var(--ww-purple-dark); }
.btn-icon { padding: 0.4rem 0.55rem; border: none; background: transparent; color: var(--muted); }
.btn-icon:hover { color: var(--danger); }
.btn-arrow {
  padding: 0.35rem 0.6rem; background: var(--ww-purple); color: #fff; border: none; border-radius: 6px;
  font-size: 0.95rem; text-decoration: none;
}
.btn-arrow.disabled { background: var(--line); color: var(--muted); pointer-events: none; }
input[type=text], input[type=search], input[type=email], input[type=password], select, textarea {
  padding: 0.45rem 0.6rem; border: 1px solid var(--line); border-radius: 6px;
  font-size: 0.86rem; font-family: inherit; background: #fff;
}

/* chips + pills */
.chip {
  display: inline-block; font-size: 0.72rem; font-weight: 600;
  border-radius: 999px; padding: 0.18rem 0.65rem;
}
.chip-article { background: var(--teal-tint); color: var(--teal-text); }
.chip-talk { background: var(--talk-tint); color: var(--talk-text); }
.count-pill {
  display: inline-block; background: var(--teal); color: #fff; font-size: 0.72rem;
  font-weight: 700; border-radius: 999px; padding: 0.12rem 0.6rem; margin-left: 0.35rem;
}
.badge {
  display: inline-block; font-size: 0.72rem; color: var(--muted);
  background: var(--bg); border: 1px solid var(--line); border-radius: 999px; padding: 0.15rem 0.6rem;
}
.filter-pill {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.35rem 0.9rem; border-radius: 999px; border: 1px solid var(--line);
  background: #fff; color: var(--ink); text-decoration: none; font-size: 0.83rem; font-weight: 600;
}
.filter-pill.on-article { background: var(--teal-tint); border-color: var(--teal-tint); color: var(--teal-text); }
.filter-pill.on-talk { background: var(--talk-tint); border-color: var(--talk-tint); color: var(--talk-text); }

/* toggle switch */
.toggle { position: relative; display: inline-block; width: 40px; height: 22px; }
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .slider {
  position: absolute; cursor: pointer; inset: 0; background: var(--line);
  border-radius: 999px; transition: 0.15s;
}
.toggle .slider:before {
  content: "\2715"; position: absolute; height: 16px; width: 16px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%; transition: 0.15s;
  display: flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 700; line-height: 1; color: var(--muted);
}
.toggle input:checked + .slider { background: var(--teal); }
.toggle input:checked + .slider:before { transform: translateX(18px); content: "\2713"; color: var(--teal-text); }

.messages { list-style: none; padding: 0; margin: 0 0 1rem; }
.messages li { padding: 0.5rem 0.75rem; border-radius: var(--radius); margin-bottom: 0.5rem; background: #ecfdf5; border: 1px solid #a7f3d0; font-size: 0.88rem; }
.messages li.error { background: #fef2f2; border-color: #fecaca; }
.muted { color: var(--muted); }

/* diff (Edit View) */
.diff-wrap { overflow-x: auto; }
.diff-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.diff-panel { border: 2px solid var(--line); border-radius: var(--radius); background: #fff; padding: 1rem; }
.diff-panel.before { border-color: var(--before-border); }
.diff-panel.after { border-color: var(--after-border); }
table.diff { width: 100%; border-collapse: collapse; font-family: ui-monospace, Menlo, monospace; font-size: 0.76rem; table-layout: fixed; }
table.diff td { padding: 0.15rem 0.4rem; vertical-align: top; word-wrap: break-word; }
td.diff-deletedline { background: #fdf3e0; border: 1px solid var(--before-border); width: 50%; }
td.diff-addedline { background: #eaf3fb; border: 1px solid var(--after-border); width: 50%; }
td.diff-context { background: #f8f9fa; width: 50%; }
td.diff-marker { width: 1.2rem; color: var(--muted); }
.diff-lineno { font-weight: 700; padding-top: 0.6rem; }
/* Before/After need a clear gutter between the two halves (TFG Aug 1) — the
   July 29 relayout dropped the old bordered .diff-panel boxes for a bare table,
   and with them the breathing room. The rows are Wikipedia's own markup, so
   there is no spacer cell to add: the After side's marker column doubles as the
   divider. These two lengths are the whole geometry — the <col> widths in
   article/revision_detail.html and the .diff-heads rules below are both
   expressed in terms of them, so the gutter moves from one place. */
.diff-card { --diff-marker: 1.2rem; --diff-gutter: 2.5rem; }
/* +/- pushed to the gutter's right edge, so it still reads as belonging to the
   After cell beside it rather than trailing the Before text. */
table.diff td.diff-marker:nth-child(3) { text-align: right; }
/* The "Line N:" rows are two colspan=2 cells, so the right-hand one starts at
   the gutter; indent it to overhang its column by the same 1.2rem the left one
   overhangs the Before column (gutter + the cell's 0.4rem padding - 1.2rem). */
table.diff td.diff-lineno + td.diff-lineno { padding-left: calc(var(--diff-gutter) - 0.8rem); }
/* Column headers on the same geometry as the colgroup, so each coloured rule
   spans exactly the half it labels. */
.diff-heads {
  display: grid; gap: var(--diff-gutter); margin-bottom: 0.9rem;
  grid-template-columns: calc(var(--diff-marker) + (100% - var(--diff-marker) - var(--diff-gutter)) / 2) 1fr;
}
.diff-heads h3 { margin: 0; padding-bottom: 0.5rem; }
.diff-heads .before { border-bottom: 3px solid var(--before-border); }
.diff-heads .after { border-bottom: 3px solid var(--after-border); }
del.diffchange { background: #f5d9a8; text-decoration: none; }
ins.diffchange { background: #c4ddf2; text-decoration: none; }

.embed-frame { width: 100%; height: calc(100vh - 220px); border: none; background: #fff; }

footer.site {
  background: #fff; border-top: 1px solid var(--line); padding: 1rem var(--gutter);
  font-size: 0.76rem; color: var(--muted); display: flex; justify-content: space-between; align-items: center;
}

.detail-cols { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1.25rem; }
/* Edit Detail (TFG July 29 relayout): two rows rather than one tall right-hand
   stack, so a short or long "What Happened" no longer strands whitespace beside
   it. Row 1 = What Happened (wide) + Wikipedia Summary; row 2 = Edit Details +
   Editor Summary. The two rows' column ratios are measured off her mockup and
   deliberately differ, so the dividers don't line up between rows. Row-mates
   share a height (default stretch) — that's what closes the whitespace gap. The
   row carries the vertical rhythm, so cards inside drop their own bottom margin.
   Line-height 1.4 on the body text. */
.ed-row { display: grid; gap: 1.25rem; margin-bottom: 1.25rem; line-height: 1.4; }
.ed-row > .card { margin-bottom: 0; }
.ed-row-primary { grid-template-columns: 3fr 1fr; }
.ed-row-secondary { grid-template-columns: 1fr 2fr; }
/* "Edit Detail" subheading + "Back to Edit List", above the header card. */
.ed-subhead { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin: 0 0 1.2rem; }
.ed-subhead h2 { font-size: 1rem; margin: 0; }
.ed-subhead a { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.85rem; }
/* Section links sit directly under their card title, one row, leading icons. */
.section-links { display: flex; flex-wrap: wrap; gap: 0.4rem 1.25rem; margin: 0.5rem 0 0.9rem; font-size: 0.85rem; }
.section-links a { display: inline-flex; align-items: center; gap: 0.3rem; }
/* Multi-column detail layouts collapse to one column on the shared mobile
   breakpoint. These were written at 800px before Stage A settled on 720px;
   folded onto the one value so there's a single place to move it. Collapsing
   .ed-row in DOM order gives the mobile deck's stack exactly: What Happened,
   Wikipedia Summary, Edit Details, Editor Summary, then Before/After. */
@media (max-width: 720px) {
  .ed-row, .detail-cols, .diff-panels { grid-template-columns: 1fr; }
}
.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.35rem 1.5rem; font-size: 0.85rem; }
.kv .k { color: var(--muted); }
.kv .v { font-weight: 600; }

/* Watchlist two-section layout (Articles / Pages) */
.wl-section { margin-bottom: 1.1rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); }
/* Shaded header band spanning the section. Collapsed: all four corners rounded
   (it's the whole visible box). Open: only the top corners. */
.wl-section > summary {
  list-style: none; cursor: pointer; padding: 0.75rem 1rem; display: flex; align-items: center;
  gap: 0.55rem; font-weight: 600; color: var(--ink); user-select: none;
  background: var(--band); border-radius: 4px;
}
.wl-section[open] > summary { border-radius: 4px 4px 0 0; border-bottom: 1px solid var(--line); }
.wl-section > summary::-webkit-details-marker { display: none; }
/* CSS-drawn thin caret (no icon font): collapsed = caret-down (rotate 45deg),
   expanded = caret-up (rotate -135deg). */
.wl-caret {
  display: inline-block; width: 0.5rem; height: 0.5rem; flex: none;
  border-right: 1.5px solid var(--ink); border-bottom: 1.5px solid var(--ink);
  transform: rotate(45deg); transition: transform 0.15s; margin-bottom: 3px;
}
.wl-section[open] > summary .wl-caret { transform: rotate(-135deg); margin-bottom: 0; margin-top: 3px; }
.wl-section .sec-count { background: var(--card); color: var(--muted); border-radius: 999px; padding: 0.05rem 0.6rem; font-size: 0.78rem; font-weight: 700; }
/* Table flush to the container edges; round the last row so the white fill
   doesn't poke past the container's rounded bottom (no overflow:hidden here —
   the ••• row menu must be able to escape the container). */
/* Shared fixed column layout so PAGE / LATEST ACTIVITY / ALERTS / DIGEST / •••
   start at identical x-positions in every section, regardless of content. */
.wl-section table.list { width: 100%; table-layout: fixed; }
.wl-section table.list th:nth-child(1), .wl-section table.list td:nth-child(1) { width: 26%; }
.wl-section table.list th:nth-child(2), .wl-section table.list td:nth-child(2) { width: 42%; }
.wl-section table.list th:nth-child(3), .wl-section table.list td:nth-child(3) { width: 11%; }
.wl-section table.list th:nth-child(4), .wl-section table.list td:nth-child(4) { width: 15%; }
.wl-section table.list th:nth-child(5), .wl-section table.list td:nth-child(5) { width: 6%; }
/* PAGE cell: chip + truncating title + count pill on one line */
.wl-page-inner { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.wl-page-inner .chip, .wl-page-inner .count-pill { flex: none; }
.wl-title { color: var(--ink); font-weight: 600; text-decoration: none; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* LATEST ACTIVITY: truncate the (single) link rather than wrap/push columns */
.wl-section td.wl-activity { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wl-section table.list tbody tr:last-child td:first-child { border-bottom-left-radius: 4px; }
.wl-section table.list tbody tr:last-child td:last-child { border-bottom-right-radius: 4px; }
.chip-page { background: #e8eaf4; color: var(--ww-purple); }

/* Non-mainspace page detail header (no article tabs) */
.page-kind { display: flex; align-items: center; gap: 0.6rem; padding-bottom: 1rem; color: var(--muted); font-size: 0.88rem; }

/* Right-aligned numeric table columns (Top Editors, etc.) */
th.num, td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Top Editors: the inline wording the mobile two-column condensation needs
   (see templates/article/top_editors.html). Off above the breakpoint, where the
   column headers already say it. */
.te-short, .te-suffix { display: none; }

/* Edit-detail header, boxed to match the row/card style */
.edit-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.edit-header .eh-main { display: flex; align-items: center; gap: 0.6rem; min-width: 0; }
.edit-header .eh-line { font-size: 0.95rem; }
.edit-header .eh-actions { display: flex; gap: 1rem; align-items: center; flex-shrink: 0; }
/* Share affordance: outlined box next to the prev/next arrows (per mockup) */
.btn-share {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.1rem; height: 2.1rem; border-radius: 6px; cursor: pointer;
  background: #fff; border: 1px solid var(--line); color: var(--ww-purple); font-size: 1.05rem;
}
.btn-share:hover { background: var(--bg); }

/* Overview callout stat cards */
.stat-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.9rem; margin-bottom: 1.25rem; }
.stat-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 1rem 0.9rem; text-align: center; }
.stat-num { font-size: 1.7rem; font-weight: 600; color: var(--teal); line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 0.74rem; color: var(--muted); margin-top: 0.3rem; }
/* 6 across → 3 on a tablet; the mobile section drops to 2 (and to 4 cards). */
@media (max-width: 900px) { .stat-grid { grid-template-columns: repeat(3, 1fr); } }

/* Overview enrichment (roadmap item 2) */
/* align-items:stretch so row-mates match heights (Activity == The Page), per
   TFG July 20 mockup. Columns stay 1/3 : 2/3. */
.ov-grid { display: grid; grid-template-columns: 1fr 2fr; gap: 1.25rem; align-items: stretch; margin-bottom: 1.25rem; }
/* The grid gap governs BOTH axes (TFG Aug 1): .card's own bottom margin stacked
   on top of the row gap, so the vertical space between cards read as twice the
   horizontal. Same fix as .ed-row > .card above. */
.ov-grid > .card { margin-bottom: 0; }
.ov-card h3 { display: flex; align-items: center; }
.ov-card .kv { grid-template-columns: auto 1fr; gap: 0.75rem 1rem; }
.ov-card .kv .v { text-align: left; word-break: break-word; }
@media (max-width: 900px) { .ov-grid { grid-template-columns: 1fr; } }

.year-chart { display: flex; align-items: flex-end; gap: 0.4rem; height: 180px; overflow-x: auto; padding-top: 1rem; }
/* The edit total rides on top of its own bar, the year stays below (TFG Aug 1).
   Stacked together under the bar, the two muted 0.68rem lines read as one label
   and the total was lost in the years. The total is absolutely positioned
   against the bar (bottom:100%) rather than laid out in a track of its own, so
   it tracks each bar's height instead of lining up across the chart, and the
   bars keep their proportions exactly — the chart's own padding-top is the room
   the tallest bar's total sits in. */
.yc-col { display: grid; grid-template-rows: 1fr auto; justify-items: center; align-items: end; min-width: 34px; flex: 1; height: 100%; }
.yc-bar { position: relative; width: 60%; min-height: 2px; background: var(--teal); border-radius: 3px 3px 0 0; transition: height 0.2s; }
.yc-col:hover .yc-bar { background: var(--ww-purple); }
.yc-val {
  position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
  margin-bottom: 0.2rem; white-space: nowrap;
  font-size: 0.68rem; color: var(--muted); font-variant-numeric: tabular-nums;
}
.yc-label { font-size: 0.68rem; color: var(--muted); margin-top: 0.25rem; }

/* Watchlist add bar (consolidated search + URL add) */
/* One shared height for every add-bar control (field, attached ⊕, group
   dropdown, "Mark all as viewed") so the whole row is a single aligned line.
   Sized to the button's natural height — the field/⊕/dropdown shrink to match;
   the buttons don't grow. */
.add-bar { --addbar-h: 2.15rem; display: flex; align-items: center; gap: 0.75rem; padding-top: 1rem; padding-bottom: 1rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
.add-form { display: flex; align-items: center; gap: 0.5rem; flex: 1; min-width: 320px; }
.add-to { font-size: 0.82rem; white-space: nowrap; }
.add-form select { font-size: 0.82rem; height: var(--addbar-h); }
.add-bar .btn { height: var(--addbar-h); display: inline-flex; align-items: center; }
/* Field + submit form one continuous control: field rounded on the left, the
   button flush on its right with matching corners, no gap (per the mockup). */
.add-combo { display: flex; align-items: stretch; flex: 1; min-width: 0; }
/* .add-combo .add-field (0,2,0) must outrank the base input[type=search] rule
   (0,1,1), or the field keeps its all-round radius and right border. */
.add-combo .add-field { flex: 1; min-width: 0; height: var(--addbar-h); border-radius: 6px 0 0 6px; border-right: none; }
.mark-all { margin-left: auto; }

/* Group section header actions */
.sec-title { }
.sec-actions { margin-left: auto; display: inline-flex; gap: 0.1rem; }
.sec-actions .btn-icon { color: var(--muted); padding: 0.2rem 0.4rem; }
.sec-actions .btn-icon:hover { color: var(--ww-purple); }
/* Slightly larger group edit/trash glyphs (TFG July 20). */
.sec-actions .btn-icon .ph { font-size: 1.2rem; }

/* Search results block (rendered above the group sections) */
.search-results { margin-bottom: 1.5rem; }
.sr-title, .section-h { font-size: 1.25rem; font-weight: 600; margin: 0 0 1.2rem; }
table.search-list td { vertical-align: middle; }
.sr-name { font-weight: 700; color: var(--ww-link); }
.sr-snippet {
  font-size: 0.82rem; margin-top: 0.2rem; line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
td.sr-action { width: 4rem; text-align: right; }
/* compact square "+" add button */
.btn-add {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 7px; cursor: pointer;
  background: var(--ww-purple); color: #fff; border: 1px solid var(--ww-purple);
  font-size: 1.05rem;
}
.btn-add:hover { background: var(--ww-purple-dark); }
/* Add-time group popover (TFG July 29), anchored under a search result's ⊕.
   A <details> so open/close is native and needs no re-binding after an HTMX
   swap; the panel escapes .table-card, which deliberately has no overflow
   clipping. Sized to the mockup: stacked full-width group select + submit. */
.add-pop { position: relative; display: inline-block; }
.add-pop > summary { list-style: none; cursor: pointer; }
.add-pop > summary::-webkit-details-marker { display: none; }
.add-pop-panel {
  position: absolute; right: 0; top: calc(100% + 0.35rem); z-index: 20; width: 210px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(26, 32, 51, 0.12); padding: 0.6rem; text-align: left;
}
.add-pop-panel form { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; }
.add-pop-panel .add-pop-group { width: 100%; height: 2.2rem; }
.add-pop-panel .add-pop-new { width: 100%; }
.add-pop-panel .add-pop-submit { width: 100%; justify-content: center; }
/* Add-bar submit: purple button with a ⊕ (plus-circle) icon, attached flush to
   the right of the search field — rounded on the right only, matching height. */
.btn-round {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.4rem; height: var(--addbar-h); border-radius: 0 6px 6px 0; cursor: pointer; flex: none;
  background: var(--ww-purple); color: #fff; border: 1px solid var(--ww-purple);
  font-size: 1.3rem;
}
.btn-round:hover { background: var(--ww-purple-dark); }
/* Digest dropdown: comfortable padding with a custom caret so the text and
   chevron stay centered with clear separation (native caret spacing varies). */
/* Shared native-select treatment: a custom caret with comfortable padding so
   text and caret never crowd the right edge. Applied to every <select> in the
   app so all dropdowns match; per-select rules add only size (height/min-width). */
.ww-select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  font-size: 0.82rem; padding: 0 2.1rem 0 0.75rem;
  border: 1px solid var(--line); border-radius: 6px;
  background-color: #fff; color: var(--ink); font-family: inherit; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.7rem center; background-size: 0.72rem;
}
.digest-select { min-width: 7.5rem; height: 2.2rem; }
/* "already watching" — inert outline checkmark, not a button */
/* "already watching" = a filled check-square (spec: check-square filled).
   Filled via CSS rather than the Phosphor fill weight, to avoid a font load. */
.added-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 7px;
  background: var(--teal); color: #fff; font-size: 1rem;
}

/* Row ••• actions menu */
.row-actions { text-align: right; position: relative; }
.row-menu { display: inline-block; }
.row-menu > summary { list-style: none; cursor: pointer; }
.row-menu > summary::-webkit-details-marker { display: none; }
.row-menu > summary { padding: 0.3rem 0.5rem; color: var(--muted); }
.row-menu > summary:hover { color: var(--ink); }
/* Open menu: shade the ••• so it's clear which row's menu is active (TFG July 20). */
.row-menu[open] > summary { background: var(--band); border-radius: 6px; }
.row-menu > summary .ph { font-size: 1.5rem; display: block; }
.row-menu-panel {
  position: absolute; right: 0.5rem; top: 100%; z-index: 20; min-width: 180px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(26, 32, 51, 0.12); padding: 0.35rem; text-align: left;
}
.row-menu-panel form { margin: 0; }
.rm-head { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.03em; padding: 0.3rem 0.5rem 0.2rem; }
/* Menu items carry a leading icon in both decks, so they're flex rows rather
   than plain blocks; the icon-less group buttons in the submenu read the same. */
.rm-item {
  display: flex; align-items: center; gap: 0.55rem;
  width: 100%; text-align: left; background: none; border: none;
  padding: 0.4rem 0.5rem; border-radius: 5px; cursor: pointer; font-size: 0.85rem;
  color: var(--ink); font-family: inherit;
}
.rm-item:hover { background: var(--bg); }
.rm-item .ph { font-size: 1.1rem; flex: none; }
.rm-current { font-weight: 700; color: var(--ww-purple); }
.rm-current::after { content: " \2713"; }
.rm-sep { height: 1px; background: var(--line); margin: 0.35rem 0.25rem; }
.rm-danger { color: var(--danger); }
.rm-danger:hover { background: #fef2f2; }
/* "Change group": one menu row that reveals the group list (both decks). The
   revealed list is indented under it so it reads as a sub-level. */
.rm-sub > summary { list-style: none; }
.rm-sub > summary::-webkit-details-marker { display: none; }
.rm-sub-list { padding-left: 1.15rem; }
/* Alerts/Digest block at the top of the menu: mobile only (mobile deck note 2).
   Above the breakpoint the row's own Alerts and Digest columns carry these, and
   this copy — plus the separator that would follow it — stays out of the way. */
.rm-prefs, .rm-prefs-sep { display: none; }
.rm-prefs .rm-head { padding: 0 0 0.25rem; }
.rm-prefs .digest-select { min-width: 6.5rem; }
/* Bottom "Mark all as viewed": mobile only (see watchlist/_mark_all.html). */
.mark-all-bottom { display: none; }

/* --- Freemium gated sections (F2) -----------------------------------------
   Placeholder treatment pending TFG design review: blur the gated content and
   float a small upgrade card over it. Kept deliberately plain. */
.gate-wrap { position: relative; }
.gate-wrap .gate-blur { filter: blur(5px); opacity: 0.55; pointer-events: none; user-select: none; }
.gate-overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  text-align: center; padding: 1rem;
}
.gate-overlay .gate-msg {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 0.6rem 1rem; font-size: 0.9rem; color: var(--ink);
  box-shadow: 0 2px 10px rgba(26, 32, 51, 0.12);
}
.gate-overlay .gate-msg i { color: var(--muted); margin-right: 0.3rem; }
.digest-locked { font-size: 0.85rem; white-space: nowrap; }
.digest-locked i { margin-right: 0.2rem; }
.history-locked td { background: var(--bg); }

/* --- Public auth pages (login / signup / password reset) --------------------
   Standalone full-bleed gradient screen with a centered white card. The
   designer's July 20 deck only covered Login; signup + reset reuse this shell
   and are pending her review. Gradient stops sampled from the Login mockup. */
.auth-body {
  min-height: 100vh; margin: 0; padding: 1.5rem;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(155deg, #4FC1BD 0%, #2F7D94 50%, #163256 100%);
}
.auth-card {
  /* flex:none resets the base `main { flex:1 }` rule (body is a column flexbox),
     so the card is content-height and centers instead of filling the viewport. */
  flex: none;
  background: var(--card); border-radius: var(--radius);
  box-shadow: 0 14px 44px rgba(16, 32, 60, 0.28);
  padding: 2.25rem 2.25rem 2rem; width: 100%; max-width: 380px; text-align: center;
}
.auth-logo { height: 40px; margin: 0 0 1.1rem; }
.auth-card h1 { margin: 0 0 1.5rem; }
.auth-sub { color: var(--muted); font-size: 0.88rem; margin: 0 0 1.25rem; }
.auth-card form { text-align: left; }
.auth-field { margin-bottom: 0.9rem; }
.auth-field label { display: block; font-size: 0.8rem; color: var(--muted-strong); margin-bottom: 0.25rem; }
/* Visually hidden but still reachable by screen readers — for a field whose
   visible name lives elsewhere (start.html's "Enter your work email to
   start" heading) and whose <label> exists only to satisfy the accessible
   name, matching the deck's own placeholder-in-field pattern. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* 2026-09-03: `textarea` added. It was missing, which is why Wiki Brief's
   Contact message box rendered at its intrinsic ~188px next to 695px inputs
   — the rule that would have sized it (.contact-page textarea) never
   matched, because that class isn't on the page. See the audit §10.4. */
.auth-card input[type=email], .auth-card input[type=password],
.auth-card input[type=text], .auth-card textarea { width: 100%; }
/* 2026-09-03: SCOPED to .auth-body — the operator app's own login/signup
   shell (auth_base.html) — rather than deleted outright.
   TFG's review asks for this rule gone because every button in the Wiki
   Brief deck is content-width, and it was making five of them full-width
   (Search, Confirm, Manage x2, Contact). But .auth-card is shared: eleven
   operator screens (login, signup, the four password-reset steps,
   unsubscribe, feed-mute, the three bink_domains screens) render through
   auth_base.html and have always had full-width primary buttons. Those are
   outside this pass's scope and have no deck to answer to, so the rule is
   narrowed to them instead of removed from under them. The Brief shell
   (base.html, body.brief-body) no longer matches it. */
.auth-body .auth-card .btn-primary { width: 100%; justify-content: center; margin-top: 0.3rem; }
.auth-err { color: var(--danger); font-size: 0.85rem; margin: 0 0 0.8rem; }
.auth-field .auth-err { margin: 0.2rem 0 0; }
.auth-hint { display: block; color: var(--muted); font-size: 0.78rem; margin-top: 0.2rem; }
.auth-links { margin: 1.25rem 0 0; font-size: 0.85rem; color: var(--muted); }
.auth-links a { text-decoration: none; }
.auth-links a:hover { text-decoration: underline; }
.auth-body .messages { text-align: left; }

/* --- Beutler Wiki Brief: subject identification ---------------------------- */
/* The candidate list and the confirm card. The 380px auth card is narrow for a
   list of article titles, so the search flow widens it rather than wrapping
   every title onto three lines. */
.auth-card.brief-wide { max-width: 560px; }
.brief-explainer {
  margin: 1.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--line);
  text-align: left; color: var(--muted); font-size: 0.84rem; line-height: 1.55;
}
.brief-explainer p { margin: 0 0 0.7rem; }
.brief-explainer p:last-child { margin-bottom: 0; }
.brief-explainer-house { color: var(--muted-strong); font-weight: 600; }
/* --- Multi-card screens, 2026-09-03 ----------------------------------------
   Choose Article (page 7) and Manage (pages 12-13) are not one card with
   sections in it — the deck draws them as several separate bordered cards
   stacked on the cream ground. base.html still wraps every app screen in a
   single <main class="auth-card">, so those screens opt that wrapper out of
   its own card styling with `brief-stack` and supply their own .brief-card
   children instead. One shell, two layouts, no second base template. */
.brief-body .auth-card.brief-stack {
  background: none; border: 0; padding: 0; box-shadow: none; border-radius: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.brief-card {
  background: #fff; border: 1px solid var(--neutral-dark);
  border-radius: var(--brief-radius-sm); padding: 33px 28px; text-align: left;
}
/* Card headings are BOLD SANS in the deck, not the serif that .brief-body h1,
   h2 sets for the page-level headings — checked against pages 7, 12 and 13,
   where "Choose Article" (an h1) is Castoro and "What we're watching for you"
   (an h2 inside a card) is a bold Inter. */
.brief-body .brief-card > h2 {
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
  margin: 0 0 22px; font-size: 1rem; font-weight: 700; color: #000000;
}
.brief-card > :first-child { margin-top: 0; }
.brief-card > :last-child { margin-bottom: 0; }

/* Choose Article. Each result is its own card (page 7: 756x91 outer, 8px
   apart), with the Select button right-aligned and vertically centred — both
   of which the previous divider-list build already got right and which are
   preserved here. */
.brief-results {
  list-style: none; margin: 0; padding: 0; text-align: left;
  display: flex; flex-direction: column; gap: 8px;
}
.brief-result {
  display: flex; align-items: center; justify-content: space-between; gap: 0.9rem;
  padding: 24px 27px 24px 28px;
  background: #fff; border: 1px solid var(--neutral-dark);
  border-radius: var(--brief-radius-sm);
}
.brief-result form { margin: 0; flex: none; }
.brief-result-text { min-width: 0; }
.brief-result-title { margin: 0; font-weight: 700; font-size: 0.875rem; color: var(--bink-darkgray); }
/* One line, ellipsised. The deck's own snippets are all single-line and end
   in "…", which is what makes its result cards a uniform 89px tall; letting
   a real lead sentence wrap to two lines is what made ours range 64-79px. */
.brief-result-snippet {
  margin: 0.35rem 0 0; font-size: 0.875rem; color: var(--neutral-gray);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brief-result-snippet .searchmatch { font-weight: 600; color: var(--bink-darkgray); }
/* Select carries `btn btn-primary` in the template now, so it inherits the
   one gradient and the one button geometry. This class only holds what is
   specific to sitting in a flex row. Previously it defined its OWN
   `linear-gradient(135deg, #C93A27, #A64691)` — a second, different brand
   gradient that existed nowhere else. */
.brief-result-select { flex: none; }
/* The results page's second way out. A button, because it POSTs, styled as a
   link — the primary action on that page is picking an article, and this
   must not compete with it. */
.brief-results-out { margin: 1rem 0 0; }
.brief-assess-instead {
  background: none; border: 0; padding: 0; margin: 0.4rem 0 0;
  font: inherit; font-size: 0.85rem; color: var(--hyperlink);
  text-align: left; cursor: pointer; text-decoration: none;
}
.brief-assess-instead:hover { text-decoration: underline; }

/* Confirm's article box (page 8: 698x125). Was --band/--line, both from the
   operator app's blue-gray ramp; the deck draws it white with the same
   Basic/Dark border every card uses. */
.brief-confirm-card {
  text-align: left; padding: 18px 28px; margin: 0 0 1.1rem;
  background: #fff; border: 1px solid var(--neutral-dark);
  border-radius: var(--brief-radius-sm);
}
.brief-confirm-title { margin: 0 0 0.4rem; font-size: 1.05rem; color: #000000; }
.brief-confirm-lead { margin: 0 0 0.5rem; font-size: 0.88rem; line-height: 1.55; color: var(--neutral-gray); }
.brief-confirm-card .auth-hint a { color: var(--hyperlink); }
.brief-ai-disclaimer { margin: 0 0 1.1rem; }
/* Confirm's action row (page 8). The button and "No, search again" share a
   row, the link sitting 20px to the right of the button's right edge and
   centred on it. It used to be a block <p> AFTER </form>, which put it on its
   own line 3px below. Unlike pages 5-6, this one is a plain hyperlink-blue
   text link, not the outline button — the deck really does treat the same
   words two ways depending on the screen. */
.brief-confirm-actions { display: flex; align-items: center; gap: 20px; }
.brief-confirm-actions form { margin: 0; }
.brief-confirm-actions .brief-back-link {
  color: var(--hyperlink); font-size: 0.875rem; text-decoration: underline;
}

/* --- BINK brand palette (2026-09-03) --------------------------------------
   Transcribed VERBATIM from design/WikiBrief_ColorPalette.pdf, whose swatches
   carry real text — these are read values, not sampled ones, so there is no
   measurement error in this block at all. Named with the palette's own group
   and swatch names so a value can be traced back to the source document in
   one step.

   Brief-scoped by convention, not by selector: they live on :root because
   custom properties have to, but nothing outside the Wiki Brief block reads
   them. The operator app keeps --muted / --muted-strong / --line / --band,
   which are a different (blue-tinted) ramp and are deliberately untouched —
   see docs/WW2_WikiBrief_DesignAudit_2026-09-03.md §13B for where those
   still leak into Brief screens. */
:root {
  --bink-darkgray: #222222;    /* palette: Neutral / DarkGray  */
  --bink-blorange: #EA503B;    /* palette: Neutral / Blorange  */
  --bink-wintermint: #9BD6C7;  /* palette: Neutral / Wintermint — not yet used */
  --neutral-light: #FFF8F3;    /* palette: Basic / Light — page ground        */
  --neutral-dark: #F2E8DC;     /* palette: Basic / Dark — card borders, fields */
  --neutral-gray: #666666;     /* palette: Basic / Gray — body copy            */
  --hyperlink: #0645AD;        /* palette: Accent / Hyperlink                  */
}

/* --- Wiki Brief visual rebuild (2026-08-28 Part B, from TFG's deck) --------
   Every value here traces to docs/WW2_WikiBrief_VisualExtraction_v1.md and
   the placeholder list handed to TFG — check there before trusting a value
   as final. Two brand tokens beyond the gradient itself: */
:root {
  /* 2026-09-03: repointed at the palette's own Basic/Light. The old
     #fff8f2 was a ~14,000px sample off the previous deck and came back one
     unit low on blue; the palette document states #FFF8F3 outright, so the
     sampled approximation is retired rather than kept alongside it. */
  --brief-cream: var(--neutral-light);
  --brief-ink: #1f1f1f;     /* MEASURED (approximate): body text */
  /* 2026-08-29: the card, pill, email field, and button corners were each
     measured independently at the re-anchored (945px-card) scale and came
     back 3px / 3px / 4px / 2px. The two cleanest reads — card and pill,
     via two different measurement methods — landed on the SAME value
     (3px); field and button were each within 1px of it, and the button
     read was already flagged as the noisiest of the four. That spread is
     consistent with one intended radius plus measurement noise at this
     small a scale, not four deliberately distinct values — nobody
     specifies a 3px card next to a 2px button. Collapsed to one token so
     TFG has one number to correct, not four to reconcile. */
  --brief-radius-sm: 5px;
  /* 2026-09-03: 3px -> 5px, and a second radius introduced.
     The 3px was four separate noisy reads (3/3/4/2) off the OLD deck,
     collapsed into one token on the reasoning that nobody specifies a 3px
     card next to a 2px button. The reasoning held; the number didn't.
     The 260827 deck is 1:1 and its shapes are vector, so the radius can be
     read from the PATH rather than from pixels: a flattened rounded rect's
     straight edge run begins exactly one radius in from each corner. Every
     card measures its top run starting +5.0px and its right run +5.0px
     (result cards, heading cards, both Manage cards); every CONTROL
     measures +6.0px (all four inputs, the textarea, the Confirm box, and
     every outline button). Two exact values, twelve elements, zero spread
     — that is a deliberate two-step scale, not measurement noise, so it
     is kept as two tokens rather than averaged into one.
     6px also happens to be what the shared input rule already used, which
     is why Brief inputs never looked wrong; it was the 3px button override
     that did. */
  --brief-radius-control: 6px;
  /* 2026-08-29: angle and stop positions MEASURED off an 8x render of page 3
     (a 6x6 grid sample plus a 30-point corner-to-corner diagonal profile,
     avoiding the pill chips) — this replaces the earlier 135deg/0-55-100%
     guess, which had the diagonal backwards. The orange stop sits in the
     TOP-RIGHT of the panel and the purple stop in the BOTTOM-LEFT, i.e. the
     gradient runs toward bottom-left, not bottom-right; "to bottom left" is
     used instead of a fixed angle so it keeps hitting the exact opposite
     corner regardless of the panel's own rendered aspect ratio, same as the
     deck's box. Red is reached by ~38% along that diagonal, not the 55%
     midpoint the old guess assumed, and purple plateaus by ~92% — both from
     the sampled profile, not eyeballed. */
  --brief-gradient: linear-gradient(to bottom left, #D1882D 0%, #C93A27 38%, #A64691 92%);
  /* The breadcrumb's own gradient is a 2-stop SUBSET of the brand gradient
     (red -> purple, no orange): the deck's own sampled breadcrumb pixels
     never reached the lighter #D1882D stop, and skipping it is what lets
     fully opaque white text clear 4.5:1 at every point along the bar without
     a separate dark overlay band — seem measured samples in the extraction
     doc. */
  --brief-gradient-breadcrumb: linear-gradient(90deg, #C93A27 0%, #A64691 100%);
}

.brief-body {
  min-height: 100vh; margin: 0; padding: 1.5rem;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: var(--brief-cream);
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
/* 2026-09-03: the APP shell is top-anchored; the pre-gate shell above stays
   centred (TFG's stated exception for the lander and its two sibling states).
   `justify-content: center` on a min-height:100vh column makes the space
   above the wordmark a function of CONTENT HEIGHT, not of any value in this
   file — it measured 35px on Choose Article (content taller than the
   viewport, so it pinned to the padding) and 227px on Search, across screens
   the deck draws identically at 60.6px. See the audit §3.2.
   padding-top is the whole mechanism now: the nav's own box top sits at
   padding-top, and the wordmark's ink starts ~10.6px into that box (half-
   leading of a 35px Castoro line). 50px + 10.6px = 60.6px, the deck's
   number. Measured against the live render rather than computed from
   line-height, which varies by browser. */
.brief-body-app {
  justify-content: flex-start;
  padding-top: 55px;
}
/* --- Body line-height, 2026-09-10 (TFG review item 2) -----------------------
   One inherited value for every run of body copy in the Brief flow. It was
   scattered: 1.5 on the Confirm lead and the textarea, 1.4 on the brief page's
   source line, 1.571 on its section bodies, 1.667 on its colophon, 1.55 on the
   explainer and the manage note — and NOTHING at all on the pre-gate screens,
   including About, which fell through to the browser's `normal` (~1.2 for
   Inter). TFG's note reads "1.55 everywhere, matching the About page": About is
   the reference for how it should look, not for the value it carried, since it
   carried none.
   Scoped to text elements rather than set on .brief-body, so headings keep
   their own leading and buttons keep their `line-height: 1` — putting 1.55 on
   the body would have re-flowed every card measured against the deck. */
.brief-body p,
.brief-body li,
.brief-body dd,
.brief-body .auth-sub,
.brief-body .auth-hint { line-height: 1.55; }

.brief-body h1, .brief-body h2, .brief-body .brief-wordmark-name {
  /* 2026-08-29: identified as Castoro — Bill recognized it directly, and the
     same column-density correlation used to find Libre Caslon Text (the
     prior best guess of 7 tested) confirms it decisively: Castoro scores
     0.919 against the page-3 lander sample and 0.989 against the much
     larger title-slide sample — against Libre Caslon Text's 0.878/0.890 on
     the same two references. Checked whether "Castoro Titling" (a real,
     separate display/titling cut of the same family) fits the wordmark's
     tighter look better than the text roman — it does not: Titling is a
     wide-tracked, letter-spaced, effectively all-caps-reading cut that
     scores near zero (0.006/0.062) against the same references. The regular
     text cut is already the near-exact match. Castoro ships only a 400
     weight (no real bold), so font-weight is pinned to 400 here — letting
     an h1/h2's browser-default bold request through would synthesize a
     fake bold, the same thing being avoided for the wordmark's "by" below. */
  font-family: "Castoro", Georgia, "Times New Roman", serif;
  font-weight: 400;
}
.brief-body .auth-card {
  /* 2026-08-29: re-anchored the app shell on the same deck-frame-width
     scale factor as the lander (1440 / 530pt = 2.717), measured against
     the Search screen (page 6) rather than assumed to inherit. Radius
     matches --brief-radius-sm (measured independently on this card's own
     corner, landing in the same 9-10px-native/3px-render family as every
     other radius on this build — see that token's definition).
     Width: Search itself renders on the BASE .auth-card (380px) — it never
     opts into .brief-wide — but the deck's card measures 751px at this
     scale, essentially double. .brief-wide's own 560px was no closer, and
     was itself never checked against a deck reference either. Unified
     every brief app-shell card to the one measured width here rather than
     maintaining two unverified guesses.
     Padding: measured 29/27/34/33px (left/right/top/bottom) — four
     numbers within a couple of px of each other, read as one intended
     pair plus noise the same way the four corner radii were, not four
     deliberately distinct values. */
  /* 2026-09-03, all four read directly off the 1:1 deck:
     width  754px — the card's own content box on pages 4-9 and 12-16
                    (343 -> 1097). The 756px outer figure in the audit
                    includes the 1px border on each side.
     border 1px Basic/Dark — the deck draws every card as a white rect
                    inside a #F2E8DC rect exactly 1px larger on each edge.
                    That is a border, not a shadow.
     shadow none  — there is no drop shadow anywhere in this deck. The old
                    one was invented to give the card an edge on the cream
                    ground; the border is what the deck actually uses, so
                    keeping both would double the separation.
     radius 5px   — see --brief-radius-sm.
     box-sizing is already border-box globally, so max-width 754 + a 1px
     border renders a 754px OUTER box; the deck's own content box is 754
     inside the border. Bumped to 756 so the content column matches. */
  max-width: 756px; padding: 34px 28px; text-align: left;
  border: 1px solid var(--neutral-dark);
  border-radius: var(--brief-radius-sm);
  box-shadow: none;
}
/* The base .auth-card centers everything (a WikiWatch login card is short
   and reads fine centered); every brief app-shell frame checked against
   the deck is left-aligned throughout, including the h1 and subtitle that
   sit outside the <form> and so aren't reached by .auth-card form's own
   text-align: left — inherited from .auth-card's own text-align above. */
/* Vertical rhythm, measured the same way as the lander's (band-detected
   text rows on page 6, each gap independent so a compounding error in one
   doesn't hide inside another) — the shared .auth-card/.auth-field/.auth-sub
   rules these override were never checked against a deck reference at all;
   they're WikiWatch's own login-form spacing, incidentally reused here. */
.brief-body .auth-card h1 { margin-bottom: 5px; font-size: 1.5rem; } /* 24px, the deck's own h1 size */
.brief-body .auth-card .auth-sub { margin-bottom: 32px; }
.brief-body .auth-card .auth-field label { margin-bottom: 14px; }
.brief-body .auth-card .auth-field { margin-bottom: 34px; }
/* 2026-09-03: was the bare literal #6b5d52 — a warm brown-gray that was
   never in the palette. The deck sets every lead paragraph, hint and input
   placeholder in Basic/Gray, confirmed by sampling pages 4, 5, 6, 7, 8, 9
   and 16. */
.brief-body .auth-sub, .brief-body .auth-hint { color: var(--neutral-gray); }
/* 2026-08-29: the default browser focus ring (a heavy blue outline
   belonging to no design system here) is replaced with a deliberate one —
   border + soft glow in the gradient's red stop. outline is removed only
   because a replacement is given in the same rule, and :focus-visible (not
   :focus) keeps it keyboard-only, not a mouse-click ring. */
.brief-body input[type=email]:focus-visible,
.brief-body input[type=text]:focus-visible,
.brief-body textarea:focus-visible {
  outline: none; border-color: #C93A27;
  box-shadow: 0 0 0 3px rgba(201, 58, 39, 0.18);
}
/* 2026-09-03: was #a89a8c, a warm gray in no palette. The deck's own
   placeholder ink samples #666666 on pages 4 and 14. (Page 1's lander field
   samples #222222 instead — a one-page inconsistency in the deck itself;
   Basic/Gray is used everywhere here since two pages agree on it and one
   does not.) */
.brief-body input::placeholder,
.brief-body textarea::placeholder { color: var(--neutral-gray); }

/* --- Fields, 2026-09-03 -----------------------------------------------------
   One rule for every text control in the flow, replacing the shared
   operator-app input styling (white fill + 1px #E1E6EE, a blue-gray border
   from a different palette) that these had been inheriting.
   Deck values, all direct reads at 1:1:
     fill    Basic/Dark, and NO border — the deck draws these as a filled
             #F2E8DC rect with no stroke at all. The transparent 1px border
             is kept only so :focus-visible can colour it without the field
             changing size.
     height  44px — every field on pages 1, 4 and 14 measures exactly 44.
     padding 15px horizontal — the deck's placeholder ink starts 16px in
             (387-371 on page 4), which is 15px of padding plus the glyph's
             own left bearing.
     radius  6px — see --brief-radius-control. */
.brief-body input[type=email],
.brief-body input[type=text],
.brief-body textarea {
  background: var(--neutral-dark);
  border: 1px solid transparent;
  border-radius: var(--brief-radius-control);
  padding: 0 15px;
  min-height: 44px;
  font-size: 0.875rem;
  color: var(--brief-ink);
}
/* A textarea has no single line to centre, so it takes real vertical padding
   and its height comes from `rows` plus this. Page 14's message box measures
   699x153 against 44px single-line fields. */
.brief-body textarea {
  padding: 12px 15px; min-height: 153px; line-height: 1.55;
  resize: vertical;
}
/* Headings and field labels are pure black in the deck, on every page.
   These were inheriting --ink (#1a2033) and --muted-strong (#4b5563), both
   from the operator app's blue-gray ramp. */
.brief-body .auth-card h1,
.brief-body .auth-card h2,
.brief-body .brief-pregate-card h1 { color: #000000; }
.brief-body .auth-field label,
.brief-body .field-label { color: #000000; font-size: 0.875rem; font-weight: 600; }

/* The lander's email field never matched `.auth-card input[...]` — the
   pre-gate shell uses .brief-pregate-card, not .auth-card — so it fell back
   to the browser's intrinsic size=20 width and rendered 177.7px inside a
   345px column, against a 346px paragraph directly above it. That is the
   whole bug; the fix is simply making the rule reach the element. */
.brief-pregate-card input[type=email],
.brief-pregate-card input[type=text] { width: 100%; }

/* Gradient-filled primary buttons (design v3: "buttons now also use the
   gradient") — scoped to .brief-body so the operator app's flat
   --ww-purple buttons are untouched. */
/* 2026-09-03: was `.brief-pregate-card .btn` — scoped to the lander because
   that was the only button ever measured against a deck reference. It is now
   `.brief-body .btn`, i.e. EVERY button in the Wiki Brief flow, per TFG's
   review: the five that were full-width (Search, Confirm, Manage x2,
   Contact) take the lander's Continue treatment exactly, and content-width
   with it. The rule that made them full-width is now scoped to the operator
   app — see .auth-body .auth-card .btn-primary above.

   All four values read straight off the 1:1 deck rather than derived:
     font-size 13px  — the deck's own text-span size on every button
                       ("Continue", "Search Wikipedia", "Select", "Save
                       cadence" all report sz=13.0)
     padding  0 15px — every button in the deck starts its label ink exactly
                       15px in from its left edge: 387-372, 1015-1000,
                       769-754, 331-316. Eight buttons, one number.
     height   40px   — every gradient button in the deck is exactly 40px
                       tall; the outline ones read 42px because their 1px
                       border sits outside a 40px box.
     radius   6px    — measured off the outline buttons' own vector paths
                       (see --brief-radius-control).
   Height is min-height + the base .btn's align-items:center rather than
   symmetric vertical padding: padding maths drifts with line-height across
   browsers, a min-height doesn't. */
.brief-body .btn {
  font-size: 0.8125rem; font-weight: 600; line-height: 1;
  padding: 0 15px; min-height: 40px; width: auto;
  border-radius: var(--brief-radius-control);
  justify-content: center;
}
.brief-body .btn-primary {
  background: var(--brief-gradient); border: none; color: #fff;
}
.brief-body .btn-primary:hover { filter: brightness(1.05); }
/* Secondary buttons are NOT one treatment across every screen — confirmed by
   comparing the deck's own frames directly: a bordered white button on the
   no-results/no-article screens, but a plain text link on Confirm
   specifically ("No, search again"). Both already match this markup; this
   rule only covers the bordered case, since the text-link case needs no
   button styling at all.
   :not(.btn-primary) is load-bearing, not decoration: every primary button
   carries BOTH classes ("btn btn-primary"), so without this exclusion this
   rule and .btn-primary above tie on specificity (two classes each) and
   source order — not intent — decides which background wins. That tie
   previously ate the gradient silently; the failure mode was every primary
   button rendering flat white, and it would recur the next time a rule got
   reordered without this guard. */
/* 2026-09-03: retuned from #e4d5c4 / #3d2b1f — neither of which was in the
   palette — to the deck's own outline treatment, sampled off pages 5 and 6
   ("Get a notability read instead", "No, search again"): white fill, a 1px
   Basic/Gray border, Basic/Gray text.
   Both of those stay BUTTONS. Confirm's "No, search again" (page 8) is a
   different thing that happens to share the label — a plain hyperlink-blue
   text link, no border — and is styled separately; see
   .brief-confirm-actions below. The deck really does treat the same words
   two ways on two screens, confirmed by comparing the frames directly. */
.brief-body .btn:not(.btn-primary) {
  background: #fff; border: 1px solid var(--neutral-gray); color: var(--neutral-gray);
}
/* The Brief page's footer row (Download PDF / Download Markdown / Copy link /
   Email this brief), page 16. A SECOND outline weight, lighter than the one
   above: the deck uses a Basic/Dark border and black text there rather than
   Basic/Gray on both. Sampled off page 16's own button row, and distinct
   from pages 5-6 — the two are not the same component at different sizes.
   Defined now so the Brief page's own pass has it ready; nothing in this
   pass renders it (that template is deliberately out of scope and still
   carries its own inline <style>). */
.brief-body .btn-format {
  background: #fff; border: 1px solid var(--neutral-dark); color: #000000;
}
/* Two-button rows (pages 5, 6, 11) and single-button rows (page 9). Left
   aligned — every button in the deck starts at the card's own text column,
   x=372 — and 9px apart, which is the gap measured between the primary's
   right edge and the secondary's left on both pages 5 and 6. These rows
   were `justify-content: center` with a 0.6rem gap, centred against a deck
   that centres nothing. */
.brief-button-row {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin: 24px 0 0;
}
.brief-button-row form { margin: 0; }

/* --- Manage your briefs (pages 12-13), 2026-09-03 --------------------------
   Was one card with four <h2> sections stacked in it and their margins set by
   inline style= attributes. The deck draws a full-width header card, then two
   equal cards side by side, and this screen now matches: 756px container,
   18px gutter, so each column lands on the deck's own 369px. 16px below the
   header card (the stack's own gap, raised from the 8px Choose Article uses —
   both measured, they genuinely differ). */
.brief-body .auth-card.brief-stack-lg { gap: 16px; }
.brief-manage-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.brief-manage-grid > .brief-card { min-width: 0; }
/* Ruled rows, 52px pitch, with a rule above the first item and below the last
   — all four positions measured off page 12 (y=345/397/449/501 against a
   52px pitch). The rule colour is the same Basic/Dark the card borders use. */
.brief-manage-list { list-style: none; margin: 0; padding: 0; }
.brief-manage-list > li {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 52px; border-top: 1px solid var(--neutral-dark);
}
.brief-manage-list > li:last-child { border-bottom: 1px solid var(--neutral-dark); }
.brief-manage-list form { margin: 0; }
.brief-watch-link { color: var(--hyperlink); font-size: 0.875rem; text-decoration: underline; }
/* A watched subject whose brief isn't ready has nothing to open; it reads as
   plain text at the same size rather than as a dead link. */
.brief-watch-link-off { color: var(--bink-darkgray); font-size: 0.875rem; }
/* Required-field marker on form labels (TFG review item 4). aria-hidden on the
   glyph itself — the input's own `required` is what a screen reader announces,
   and a spoken "star" after every label is noise. */
.brief-body .req { color: var(--neutral-gray); font-weight: 400; }
/* The stop control is an ✕ glyph in the deck, not a "Stop" button. It stays a
   real POST (the mail-scanner protection this page is built around depends on
   that) — only its presentation changes. */
.brief-watch-stop {
  background: none; border: 0; padding: 4px; cursor: pointer; line-height: 1;
  color: var(--bink-darkgray); font-size: 1rem; display: flex; align-items: center;
  min-height: 0;
}
.brief-watch-stop:hover { color: var(--bink-blorange); }
/* The cadence rows take the same ruled treatment as the watch rows so the two
   cards read as one component. The RADIO ITSELF is untouched — input type,
   the square box, the checkmark and its gradient all keep their existing
   rules (.brief-radio-square*, below); this only sets the row's own box. */
.brief-manage-card .brief-cadence-choices { gap: 0; }
.brief-manage-card .brief-cadence-choices .brief-radio-square {
  min-height: 52px; border-top: 1px solid var(--neutral-dark); gap: 16px;
  font-size: 0.875rem; color: var(--bink-darkgray);
  /* the new `.brief-body .auth-field label` rule is 600 and these labels sit
     inside an .auth-field; the deck's cadence labels are regular weight. */
  font-weight: 400;
}
.brief-manage-card .brief-cadence-choices .brief-radio-square:last-of-type {
  border-bottom: 1px solid var(--neutral-dark);
}
/* 28px below the last rule to the button top, measured on page 12. */
.brief-manage-card .brief-button-row,
.brief-manage-card form .btn { margin-top: 28px; }
.brief-manage-card .auth-field { margin-bottom: 0; }
.brief-manage-note { margin: 22px 0 0; font-size: 0.875rem; color: var(--neutral-gray); line-height: 1.55; }

/* --- Contact (page 14), 2026-09-03 -----------------------------------------
   The Send button and the "we'll only use this" note share the last row:
   button left, note right-aligned against the card's right edge, vertically
   centred on each other. It used to be a block <p> on its own line below the
   button, left-aligned.
   Bottom spacing: page 14 measures 43px from the button's bottom edge to the
   card's inner bottom. The card's own padding-bottom is 34px (measured on
   page 4 and shared by every app card), so this row carries the remaining 9px
   itself rather than giving Contact a different card padding from every other
   screen. Top spacing: 25px from the textarea's bottom to the button's top,
   also page 14. */
.brief-contact-actions {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin: 25px 0 9px;
}
.brief-contact-actions .auth-hint { margin: 0; text-align: right; }

/* --- The two-column pre-gate shell (base_pregate.html) ---------------------
   Lander, About, both lander errors, and — not shown in the deck, applied
   for consistency — Terms/check-email/confirmed. */
.brief-pregate-shell {
  display: flex; width: 100%;
  /* 2026-09-03: THE SCALE FACTOR BELOW IS HISTORY, NOT METHOD. Read this
     before deriving anything new from it.

     The current reference deck — design/WikiBrief_Mockups_260827.pdf — is
     drawn at 1440 x 906 pt (page 16 at 1440 x 2047), i.e. **1:1 with a
     1440px viewport**. One PDF point is one CSS pixel. There is no scale
     factor, no anchor to recover, and no cap-height division needed: every
     target reads straight off the page geometry. See
     docs/WW2_WikiBrief_DesignAudit_2026-09-03.md §0.

     Everything below (and in the sibling comments on .brief-pregate-card,
     .brief-wordmark-*, .brief-pregate-panel and .brief-pregate-step)
     describes work against the PREVIOUS deck, whose artboards were 530pt
     wide and therefore did need an anchor. That derivation — 1440 / 530.0pt
     = 2.717 — applied to that deck ONLY. It is kept because the values it
     produced are still in force and someone changing one needs to know
     where it came from; it is not a procedure to repeat.

     The 260827 deck CONFIRMS the numbers that derivation reached: the
     945px shell width below, the panel's 88px inset, the pill stack's 20px
     gap, and the wordmark mark's 33px of visible ink all re-measure to the
     same values directly. Where the old pass was wrong, this pass replaces
     the value from a direct read rather than by re-deriving a factor.

     --- original note, retained ---
     2026-08-29, RE-ANCHORED (third pass). The previous pass anchored its
     scale factor on the CARD's own width (760px) — but that 760px was
     never derived from the deck, it was just the max-width already sitting
     in the codebase. Ratios against an arbitrary anchor stay arbitrary no
     matter how precisely they're measured, which is exactly why that pass
     converged internally (everything consistent with everything else) but
     still landed body copy at ~11px: correct proportions, wrong absolute
     scale.
     The real anchor is the deck's own browser-frame — the plain cream
     rectangle each screen is drawn inside, no browser chrome, no window
     bezel. Measured on the same 8x render: 530.0pt x 331.26pt, an exact
     16:10. That ratio alone doesn't distinguish 1280 / 1440 / 1920, so
     three candidate viewports were computed and checked against what
     body copy should plausibly look like — 1280 gives 12.3px (too small
     for even a compact form), 1920 gives 18.5px body text inside a 1260px
     card (doesn't pass a smell test), 1440 gives ~13.8px, which rounds to
     the 14px a compact form card's body copy normally runs — confirmed
     with Bill rather than assumed. Scale factor: 1440 / 530.0pt = 2.717.
     Card width FALLS OUT of that (was fixed before): the deck's card
     measures 347.8pt -> 347.8 x 2.717 = 945px, replacing the old 760px
     max-width entirely, not just the values inside it. */
  max-width: 945px;
  /* Radius, re-measured off the deck at the new scale (every radius set
     before this anchor fix is suspect, not just this one — see the button,
     pill and email-field rules below for the same correction): 9.5px
     native-8x-render -> 3.2px at 945px card width -> --brief-radius-sm
     (3px), the token this and the pill radius both independently landed
     on — see that token's definition. */
  background: #fff; border-radius: var(--brief-radius-sm); overflow: hidden;
  box-shadow: 0 16px 44px -16px rgba(80, 50, 20, 0.28);
}
/* Everything below is measured-px-at-8x-render x (1440 / 4240) — 4240 being
   the frame's own width at that same 8x render, i.e. every value here is
   ultimately a fraction of the DECK'S FRAME, not of this card. Font sizes
   go through one more step: the fraction gives a target CAP HEIGHT, and
   dividing by the font's own real cap-height-to-em ratio (read from its
   OS/2.sCapHeight table, not estimated — Castoro 0.699, Inter 0.7275)
   gives the font-size that actually produces that cap height. Rounded to
   the nearest half-pixel for fonts and whole pixel for spacing per Bill's
   note that sub-pixel values are false precision and render inconsistently
   across browsers — nothing below is carrying a third decimal into the
   stylesheet. */
.brief-pregate-card {
  /* left/right re-verified fresh (2026-08-29) rather than carried forward
     unchecked: averaged the email field's and button's own left edge
     offset from the card's left edge, both via the same box-edge 50%-
     crossing method used everywhere else — 202.2px native-8x -> 69px
     here (was 70px from a slightly less precise first pass; the 1px
     difference is inside measurement tolerance, not a substantive
     change). Compared directly against the panel's own inset below. */
  /* 2026-09-03: 121/69 -> 117/68, both re-read off the 1:1 deck. Top: the
     wordmark ink was landing 127.6px below the card's top against the deck's
     123.6px. Horizontal: the deck's left column is 483px wide with a 68.4px
     inset, giving a 346px content column — the email field and the paragraph
     above it both fill exactly that. At 69px the column came out 343.9px, so
     the field could not reach 346 however the width rule was written. */
  flex: 1 1 51%; padding: 117px 68px; text-align: left;
  min-width: 0;
}
/* 2026-08-29, third look: the previous pass fixed "by"'s font-family and
   font-size in isolation, and separately fixed the panel padding, and
   separately fixed the radii — each correct on its own terms, but nobody
   checked whether the three lockup pieces (name / "by" / mark) still summed
   to the deck's own total lockup width, which is why the mark still read
   small and the two gaps still read wrong even after "by" itself was fixed.
   Re-measured as one system instead: cropped the full lockup row out of the
   deck (icon + "Beutler / Ink" text included, not just the "by"), took its
   ink-pixel bounding box as one unit, and expressed every piece — including
   both gaps — as a fraction of that unit's width, all off the same
   background-distance ink detector so the numbers are comparable to each
   other:
     name "Wiki Brief"  : 0.520 of total width (unchanged — already close)
     gap, name -> "by"   : 0.045
     "by"                : 0.056 (unchanged — the previous fix holds)
     gap, "by" -> mark   : 0.053
     mark (icon + text)  : 0.327 of total width — this is the one that was
                            short. Cross-checked two ways: against name's
                            own already-validated width (486/934 of the
                            deck's total -> mark should be 313px total *
                            0.327 = 102px of visible ink, against 87px
                            actually rendering), and independently against
                            the master frame-width scale factor applied
                            straight to the deck's mark measurement (305px
                            native-8x-render * 0.339623 = 104px) — both land
                            within 2% of each other.
   Gaps re-set as individual margins below (a single flex `gap` can't
   produce two different values, and the deck's two gaps aren't equal to
   each other — 0.045 vs 0.053 of total width, confirmed on this same crop,
   not assumed). Mark height corrected below; see its own comment for why
   the shortfall was in the asset, not the CSS. */
/* 2026-09-03: margin-bottom 28px -> 11px. Same reference error the nav had:
   the deck's 46.1px runs from the wordmark's INK bottom to the paragraph's ink
   top, but this margin runs from the wordmark box's bottom edge, which sits
   ~17px lower than the ink. 28px rendered a 63.1px ink gap; 11px renders
   46.2px. */
.brief-wordmark {
  margin: 0 0 11px; display: flex; align-items: baseline;
}
.brief-wordmark-name { font-size: 2.188rem; color: var(--brief-ink); margin-right: 14px; } /* 35px */
/* Font-family/size fix from the previous pass holds — confirmed independently
   by a second, different method (matching ink-pixel width instead of
   ascender height: 16.9px vs. the ascender method's 16.5px, within 3% of
   each other, both nowhere near the old wrong 23.5px).
   Baseline is new this pass: the deck doesn't sit "by" on the wordmark's own
   baseline the way `align-items: baseline` does by default — measured
   against the same ink crop, "by"'s baseline (the bottom of the "b",
   excluding the "y" descender) sits 19px native-8x above "Wiki Brief"'s own
   baseline, i.e. ~6px at render scale. Reads as "by" being vertically
   centered against the wordmark's cap-height band rather than baseline-
   aligned with it. Lifted with a relative offset rather than fighting the
   flex alignment. */
.brief-wordmark-by {
  font-family: "Castoro", Georgia, "Times New Roman", serif;
  font-size: 1.031rem; font-style: italic; color: var(--brief-ink);
  position: relative; top: -6px; margin-right: 17px;
} /* 16.5px, lifted 6px off the shared baseline */
/* 2026-09-03: THE COMPENSATION IS GONE — height is now the deck's real
   target, not an inflated box.
   The previous asset (179x63) had whitespace baked into it: its ink filled
   only 95% of the canvas width and 87% of its height, so `height` (which
   sets the BOX, not the ink) had to be inflated to 38px = 33 / 0.873 to
   land 33px of visible ink. That note ended: "if TFG ships a tightly-
   cropped re-export, re-measure that file's own ink-to-canvas ratio, and
   if it's ~1.0, height should simply become 33px."
   That is exactly what happened. The mark is now derived from
   design/BINK_Logo_brand.png (2084x669, the deck's own embedded asset),
   re-exported to static/img/beutler-ink-lockup.png (103x33) and
   -2x.png (206x66). Its alpha-channel ink bbox was measured the same way
   as the old file's and comes back **1.0000 x 1.0000** — the canvas IS
   the ink, no padding on any edge. So the ratio is 1.0 and the
   compensation collapses: 33px box = 33px ink.
   Cross-check: the deck renders this mark at 103 x 33.2px on every page,
   and 33px x the asset's own 3.1151 aspect = 102.8px wide. Matches.
   The old file was also fully OPAQUE (alpha min=max=255) and painted a
   white rectangle on the cream ground; the new one is ~50% transparent
   with all four corners at alpha 0. Both verified by reading the alpha
   channel, not by eye.
   Same relationship holds for .brief-nav-brand-mark's em value below,
   which moves 1.086em -> 0.943em (= 33/35) for the same reason.

   2026-08-30, 4th pass — the vertical drift, measured this time by ink
   pixels rather than CSS boxes: `align-items: baseline` on .brief-wordmark
   aligns a replaced element (this <img>) by its own bottom margin edge,
   which put the mark's visible ink roughly 5px too HIGH relative to "Wiki
   Brief" — measured on-screen (ink-pixel bbox centers, not element boxes)
   against the deck's own crop, where the mark's ink center sits almost
   exactly at "Wiki Brief"'s cap-height-band center (very slightly below,
   inside measurement noise — treated as centered, not as a further
   sub-pixel offset). Corrected with an explicit downward nudge rather than
   fighting the flex alignment, same approach as "by"'s own offset above. */
.brief-wordmark-mark { height: 33px; width: auto; display: block; position: relative; top: 5px; }
.brief-pregate-card h1 { font-size: 1.4rem; margin: 0 0 0.9rem; }
/* Vertical rhythm below the wordmark: card padding, the paragraph's own
   size, and the three gaps between paragraph / field label / input /
   button are all independently measured off the deck (band-detected text
   rows and border edges, not inferred from each other), so a compounding
   error in one doesn't hide inside another. */
.brief-pregate-card .auth-sub { text-align: left; margin: 0 0 51px; font-size: 0.875rem; } /* 14px */
.brief-field-intro { margin: 0 0 21px; }
/* Scoped here rather than changing the shared .auth-field rule (0.9rem),
   which every other login-style form in the app also uses. */
.brief-pregate-card .auth-field { margin-bottom: 31px; }
.brief-pregate-card form { text-align: left; }
.brief-pregate-panel {
  /* 2026-08-29: horizontal padding measured, not assumed equal to the left
     column's. Pill inset from the panel edge is ~260.2px native-8x-render
     (left and right agree within 0.75px of each other, both re-verified
     with the same box-edge-to-box-edge method used on the left column) ->
     88px at this scale — the left column's own padding is 69px, re-measured
     fresh alongside this rather than carried forward. ~19px apart, which at
     this scale is ~7pt in the deck itself: too large to be eyeballing
     error, confirmed as two genuinely distinct values rather than one
     value plus noise (unlike the four corner radii). Flagged for TFG
     rather than silently resolved either way — see the checklist.
     Vertical is NOT a comparable padding: the pill stack (4 pills + 3 gaps
     = 827.5px native-8x) sits inside a panel matching the card's full
     866.7px-native-8x height with justify-content: center, which alone
     accounts for the measured ~410px-native-8x gap above/below the stack.
     That's centering slack, not a deliberate vertical inset to match
     against anything — left as-is. */
  flex: 1 1 49%; padding: 2rem 88px; min-width: 0;
  background: var(--brief-gradient);
  display: flex; flex-direction: column; justify-content: center;
  /* Pill height/gap (55px / 20px) were already re-derived against the
     frame-width anchor in the same pass as the wordmark and body-text
     fixes, not carried over from the pre-anchor pass — confirmed before
     leaving them untouched here, per the instruction to re-check rather
     than assume a horizontal-only padding change leaves them valid.
     Horizontal padding doesn't affect flex-column vertical spacing, so
     there was nothing to actually re-derive this time, but it was checked
     rather than assumed. */
  gap: 20px;
}
/* Chip fill is still EYEBALLED as translucent (the panel's own text areas
   didn't isolate cleanly when sampled). Text weight (500, not 600) came
   from a direct read against the icon stroke weight and didn't need
   re-checking. The radius did: `border-radius: 999px` forces a full
   stadium end (radius = half the chip's own height, ~22px at this card
   size) no matter what number sits under it — the deck's chips are NOT a
   stadium shape, they're a rounded rectangle with a small, specific
   radius, measured directly off the same corner-profile method used on
   the card: 9.5px native-8x-render -> 3px at this card's scale, i.e.
   essentially the SAME radius as the card itself, not a pill shape at
   all. This was wrong before the anchor fix too — it isn't a casualty of
   the 760px-card error, it's a different mistake (a plausible-looking
   default that was never checked against the deck), caught at the same
   time. */
.brief-pregate-step {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  background: rgba(255, 255, 255, 0.16); border-radius: var(--brief-radius-sm);
  /* pill text cap-height -> 13.0px font-size (Inter); vertical padding
     retuned to land the whole chip at its measured height (55px at this
     card's scale) with the corrected text size. Horizontal padding wasn't
     independently measured, left proportional. */
  padding: 20px 18px; color: #fff; font-size: 0.812rem; font-weight: 500;
}
/* scaled proportionally with the pill text size (not an independent
   measurement of its own). */
.brief-pregate-step i { font-size: 0.95rem; flex: none; }
.brief-pregate-footer-note, .brief-pregate-footer-links {
  margin: 1.5rem 0 0; text-align: center; font-size: 0.82rem; color: var(--neutral-gray);
}
.brief-pregate-footer-house {
  margin: 0.4rem 0 0; text-align: center; font-size: 0.82rem;
  font-weight: 650; color: var(--brief-ink);
}
.brief-pregate-footer-note a, .brief-pregate-footer-links a { color: var(--ww-link); }
@media (max-width: 720px) {
  .brief-pregate-shell { flex-direction: column; max-width: 420px; }
  .brief-pregate-panel { flex-direction: row; flex-wrap: wrap; }
}

/* --- The app shell's nav + breadcrumb (base_app.html) ----------------------
   Both sit ABOVE the centered card, full-width within the same max column so
   they line up with it. */
.brief-nav {
  /* max-width re-measured with the app shell (2026-08-29): 560px was never
     checked against a deck reference either, same as the card it sits
     above — unified to the same 751px. margin-bottom measured the same
     way: the gap from nav to breadcrumb on page 6 comes out to 51px at
     this scale, not the 16px the old 1rem produced — caught by the
     overlay, not by the box-geometry check alone (nav and breadcrumb each
     matched the deck fine on their OWN size; it was the gap between them
     that was still wrong). */
  /* 2026-09-03: 51px -> 36px. The 51px was the right MEASUREMENT applied
     from the wrong REFERENCE. The deck's 50.6px gap runs from the
     wordmark's BASELINE to the top of the breadcrumb bar; this margin runs
     from the nav BOX's bottom edge, which sits ~14.6px lower than that
     baseline (descender space in a 35px Castoro line box). So 51px here
     rendered a 65.6px baseline-to-bar gap. 36px + 14.6px = 50.6px.
     Same correction applies on the no-breadcrumb screens (Manage, Contact),
     where the gap runs baseline -> first card instead and the deck shows
     the same ~50px. */
  width: 100%; max-width: 756px; margin: 0 0 36px;
  display: flex; align-items: center; justify-content: space-between;
}
.brief-nav-brand {
  /* Missed in the 2026-08-28 Lora swap and the 2026-08-29 Castoro
     identification — same wordmark text, same face. font-weight: 650 forced
     a synthetic bold on both Lora and Castoro (neither ships one); dropped
     now that faux weights are being cleaned up everywhere else on this
     wordmark.
     2026-08-29 (app-shell pass): this was rendering the wordmark as bare
     "Wiki Brief" text at 1rem — the deck's nav (page 6) carries the SAME
     three-part lockup as the lander ("Wiki Brief" + italic "by" + the
     Beutler Ink mark), and the wordmark's own cap-height measures the same
     here as on the lander, within noise. Markup rebuilt to match (see
     _nav.html); font-size now matches .brief-wordmark-name exactly rather
     than being a separate, much smaller, never-measured guess. */
  display: flex; align-items: baseline;
  font-family: "Castoro", Georgia, serif; font-weight: 400;
  color: #ffffff; font-size: 2.188rem; text-decoration: none;
}
.brief-nav-brand-name { margin-right: 0.4em; } /* 14/35 of the name size */
/* ratio corrected alongside .brief-wordmark-by's own fix (2026-08-29,
   second look) — the 0.671 here inherited the same descender-vs-cap-height
   measurement error; real ratio is 16.5/35.
   2026-08-29, system pass: baseline lift and post-gap added to match
   .brief-wordmark-by — see its comment for the measurement. */
.brief-nav-brand-by {
  font-size: 0.4714em; font-style: italic;
  position: relative; top: -0.171em; margin-right: 0.486em;
} /* 16.5/35 of the name size; 6/35 lift; 17/35 trailing gap */
.brief-nav-brand-mark {
  height: 0.943em; width: auto; display: block; position: relative; top: 0.143em;
} /* 33/35 of the name size (was 38/35 — see .brief-wordmark-mark for why the
     inflation went away); 5/35 downward nudge, same fix as .brief-wordmark-mark */
/* 2026-09-03: icon size 1.15rem -> 1.5rem per TFG's review. Gap raised from
   0.9rem (14.4px) to 25px: the deck's icons sit 25.25px apart ink-to-ink,
   measured on both gaps independently and agreeing exactly. */
.brief-nav-icons { display: flex; gap: 25px; }
.brief-nav-icons a {
  color: #ffffff; font-size: 1.5rem; line-height: 1; opacity: 0.92;
  text-decoration: none;
}
.brief-nav-icons a:hover { opacity: 1; }
/* Sits directly on a gradient page ground on the deck's own pages (the app
   shell's page background is still --brief-cream here, not the gradient —
   see .brief-body above — so nav text needs to work on cream, not on the
   brand gradient the way the deck's own top bar does). White would fail on
   cream; using the ink color instead for the app-shell nav specifically. */
.brief-body .brief-nav-brand,
.brief-body .brief-nav-icons a { color: var(--brief-ink); opacity: 0.85; }
.brief-body .brief-nav-icons a:hover { opacity: 1; }
/* Active section, 2026-09-03. The deck marks the CURRENT section's icon in
   Blorange — the faders icon on pages 12-13 (Manage) and the envelope on
   pages 14-15 (Contact) — and leaves every other icon #000000. The funnel
   screens (pages 4-9) mark nothing at all, so there is deliberately no
   `.nav-at-search` rule: "in the funnel" is the unmarked default state.
   Sampled directly off the deck's own icon fills, not inferred.
   Full opacity, not the 0.85 the inactive icons carry — a dimmed accent
   colour would read as a third state that doesn't exist in the deck.
   The class sits on <body> alongside .brief-body, so these are descendant
   selectors from the body itself, and their extra attribute selector puts
   them above the .brief-body rule they override. */
.nav-at-settings .brief-nav-icons a[data-nav="settings"],
.nav-at-contact .brief-nav-icons a[data-nav="contact"] {
  color: var(--bink-blorange); opacity: 1;
}

.brief-breadcrumb {
  /* 2026-08-29: max-width unified with the card/nav above (751px, was
     560px, same unverified-guess pattern). Radius was 999px — a full
     stadium, same mistake as the pregate pills — measured directly off
     this bar's own corner on page 6 and it's the same small radius as
     everything else on this build, not a stadium at all. */
  /* 2026-09-03: the four steps were `flex: 1` equal-width cells with
     centred text, which pushed 114px of empty bar between one label's ink
     and the next. They are now natural-width items in a 15px-gap row, per
     TFG's review ("15px between each item, text -> arrow -> text"). The
     gap is applied twice per boundary — once by the row's own `gap` (label
     to arrow) and once by the arrow's own `gap` inside its <li> (arrow to
     next label) — because the arrow is a ::before living INSIDE the second
     <li>, so a single flex gap on the row can only reach one side of it.
     Left inset 28px so the first label lines up with the card's own text
     column below (the deck puts both at x=372 against a 343 edge). */
  width: 100%; max-width: 756px; margin: 0 0 20px; padding: 0 28px;
  min-height: 53px; box-sizing: border-box;
  list-style: none; display: flex; align-items: center; gap: 15px;
  border-radius: var(--brief-radius-sm);
  background: var(--brief-gradient-breadcrumb);
  font-size: 0.78rem; font-weight: 600; color: #ffffff;
}
.brief-breadcrumb li {
  flex: 0 0 auto; white-space: nowrap; padding: 0;
  display: flex; align-items: center; gap: 15px;
}
/* 2026-09-03: scaled up, not swapped, per the review. Two changes:
   - `position: absolute` -> static. Absolute took it out of flow, which is
     why it could sit at left:-0.1rem regardless of the bar's layout; it now
     has to be a real flex item for the 15px gaps to reach it.
   - font-size 0.78rem (12.48px, inherited) -> 2rem. The glyph's ink at the
     old size measured 4.16 x 5.75px against the deck's 9 x 16.5px chevron.
     32px lands its ink height on the deck's 16.5px.
   CAVEAT, flagged rather than silently resolved: this separator is the text
   character U+203A, not a Phosphor icon — the review calls it "the existing
   Phosphor caret", but Phosphor is not what is rendering here and never was.
   Scaling it as instructed matches the deck's HEIGHT but overshoots its
   WIDTH: U+203A's ink is proportionally wider than the deck's chevron
   (aspect 0.72 vs 0.55), so at the height that matches, the width comes out
   ~12px against a 9px target. Both dimensions are only reachable by
   swapping the glyph — `ph-caret-right` is already loaded on these pages and
   would do it — which the review explicitly ruled out. Opacity dropped in
   favour of full white: a dimmed separator on this gradient is the same
   contrast problem the step labels already avoid. */
.brief-breadcrumb li + li::before {
  /* 2026-09-03: swapped from the text character U+203A to Phosphor's own
     caret-right (\e13a). The audit flagged that this separator never was a
     Phosphor caret despite being described as one, and that U+203A cannot hit
     both of the deck's dimensions at once — its ink is proportionally wider
     than the deck's chevron (aspect 0.72 against 0.55), so matching the 16.5px
     height overshot the 9px width. A real chevron matches both. Phosphor is
     already loaded on this shell; the codepoint is read from its own
     stylesheet. Size tuned against the rendered ink, not computed. */
  content: "\e13a"; font-family: "Phosphor"; font-size: 24px;
  line-height: 1; color: #ffffff; font-weight: 400;
  -webkit-font-smoothing: antialiased;
}
/* Active/inactive is shown by weight and an underline, not by dimming the
   text color — dimmed white on this gradient cannot reliably clear 4.5:1
   (see the breadcrumb-gradient comment above), so every step's TEXT stays
   fully opaque white regardless of state, at every step.
   2026-08-29: the highlight-pill background this used to carry (a
   translucent white chip behind the active step) doesn't exist on the
   deck — sampled the pixels directly behind "Search" on page 6 looking
   for it and found none, just the gradient's own smooth progression; the
   deck differentiates the active step by weight alone. Dropped, along
   with its own 999px stadium radius (the same mistake as the bar itself,
   see above).
   2026-08-30: weight alone read as "all four steps look the same" —
   Search/Choose Article/Confirm/Preparing render identically until one is
   actually current. Added a 2px underline under the current step's own
   label (text-decoration, not a border, so it hugs the text rather than
   the full flex cell) sitting a few px below the baseline
   (text-underline-offset) — opaque white throughout, so contrast is
   unaffected. Completed steps get no separate treatment (no checkmark,
   no italic list — implies a back-navigation that doesn't exist), so
   .is-done carries no rule of its own; the class stays on the template
   only so a future need has something to hook. TFG's component — flagged
   for her per the instruction that asked for this, not silently folded
   into "already fixed". */
/* 2026-09-10 (TFG review item 1): moved from `li.is-active` to the LABEL
   inside it. On the li, `text-decoration` propagated into the li's own
   ::before separator, so every active step that has one — Choose Article,
   Confirm, Preparing — drew an underlined chevron. A propagated decoration is
   painted by the ancestor and cannot be cancelled from the descendant, so
   `text-decoration: none` on the ::before would not have fixed it; the
   decoration has to start below the separator instead. */
.brief-breadcrumb li.is-active { font-weight: 700; }
.brief-breadcrumb li.is-active .brief-breadcrumb-label {
  text-decoration: underline; text-decoration-color: #fff;
  text-decoration-thickness: 2px; text-underline-offset: 4px;
}

/* --- Cadence radio group, styled square (2026-08-28) -----------------------
   Real <input type="radio"> — mutually exclusive semantics per the explicit
   Part B instruction — styled to look like the deck's square indicators
   (page 14) rather than a native round radio. Shape EYEBALLED, not
   measured. */
.brief-cadence-choices { display: flex; flex-direction: column; gap: 0.6rem; }
/* .brief-cadence-choices .brief-radio-square (two classes), not bare
   .brief-radio-square (one) — this label sits inside .auth-field, and
   .auth-field label {display:block;...} (one class + one element = higher
   specificity than a single class alone) would otherwise win and collapse
   this back to a stacked, unstyled label. Same failure shape as the
   .btn-primary/.btn tie above: two rules matching the same element, and
   specificity — not this file's intent — deciding the outcome. */
.brief-cadence-choices .brief-radio-square {
  display: flex; align-items: center; gap: 0.55rem; font-weight: 400;
  cursor: pointer;
}
.brief-cadence-choices .brief-radio-square input[type="radio"] {
  position: absolute; opacity: 0; width: 1px; height: 1px;
}
.brief-radio-square-box {
  width: 16px; height: 16px; flex: none; border-radius: 4px;
  border: 1.5px solid #c9b8a8; background: #fff; position: relative;
}
.brief-cadence-choices .brief-radio-square input[type="radio"]:checked + .brief-radio-square-box {
  background: var(--brief-gradient); border-color: transparent;
}
.brief-cadence-choices .brief-radio-square input[type="radio"]:checked + .brief-radio-square-box::after {
  content: ""; position: absolute; left: 5px; top: 2px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg);
}
.brief-cadence-choices .brief-radio-square input[type="radio"]:focus-visible + .brief-radio-square-box {
  outline: 2px solid #A64691; outline-offset: 2px;
}

/* --- The brief page (brief.html), 2026-09-03 --------------------------------
   Page 16 of design/WikiBrief_Mockups_260827.pdf. That page is 1440x2047pt —
   a TALLER artboard than the rest of the deck, but the same 1440 width and
   verifiably the same 1:1 scale: its wordmark ink sits at y=60.57, its cards
   measure 756px and its BINK mark 103x33.2px, all identical to pages 4-14. So
   every number below is a direct read, no scale factor.

   This page used to be a standalone document with its own inline stylesheet
   and its own palette. It is on the app shell now; these rules are the whole
   of what is specific to it. */
/* NOTE for anyone adding a per-card override below: this is a two-class
   selector, so a single-class rule like `.brief-doc-header { padding-top: … }`
   loses to it and does nothing, whatever its source order. Per-card padding
   overrides have to carry the `.brief-doc` prefix too. Third instance of this
   tie-break in this file — see .btn-primary/.btn and the cadence label. */
.brief-doc .brief-card { padding: 28px 28px 26px; }

/* Header card (756x156 outer). Three lines on an article brief — title, source
   URL, prepared-on — plus an eyebrow on notability briefs only.
   Its own padding-top, 5px more than a section card's: the deck sets the title
   ink 31.9px below the card's inner edge and the URL ink 21px below the title,
   and the total card height alone doesn't pin that split — the same 156px comes
   out of several padding/margin pairs, only one of which puts the lines where
   the deck does. Measured ink-to-ink, not from the box. */
.brief-doc .brief-doc-header { padding-top: 33px; }
/* `.brief-body` prefix is load-bearing, not decoration: the flow pass's
   `.brief-body .auth-card h1 { margin-bottom: 5px }` is two classes plus an
   element and sits EARLIER in this file, so a bare `.brief-doc-header h1`
   (one class plus an element) loses to it and this margin silently does
   nothing. Same tie-break shape as .btn-primary/.btn and the cadence label. */
.brief-body .brief-doc-header h1 { margin: 0 0 7px; }
.brief-doc-eyebrow {
  margin: 0 0 6px; font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--neutral-gray);
}
.brief-doc-source { margin: 0 0 21px; font-size: 0.875rem; line-height: 1.55; }
.brief-doc-source a { color: var(--hyperlink); }
.brief-doc-source + .brief-doc-source { margin-top: -8px; }
.brief-doc-prepared { margin: 0; font-size: 0.75rem; color: var(--neutral-gray); }

/* Section cards (756x159 outer, 8px apart — the stack's own gap). Heading is a
   flex row so the icon and the text sit on one line with a measured gap. */
.brief-doc-body { display: flex; flex-direction: column; gap: 8px; }
.brief-doc-body > .brief-card > h2 {
  display: flex; align-items: center; gap: 15px;
  margin: 0 0 16px; font-size: 1rem; font-weight: 700; color: #000000;
  font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
}
.brief-doc-body > .brief-card > :last-child { margin-bottom: 0; }
.brief-doc-body p, .brief-doc-body li {
  font-size: 0.875rem; line-height: 1.55; color: var(--neutral-gray);
}
.brief-doc-body p { margin: 0 0 12px; }
.brief-doc-body ul { margin: 0 0 12px; padding-left: 20px; }
.brief-doc-body li { margin: 0 0 7px; }
/* The coverage entries link to the pieces they name. Deliberately quiet: the
   same ink as the prose, underlined only, so the list still reads as a
   judgment about coverage rather than as a set of buttons. */
/* `:not(.btn)` keeps the CTA out of it — that anchor lives inside the close
   card, so the prose link rule would otherwise underline a gradient button. */
.brief-doc-body a:not(.btn) { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.brief-doc-body a:not(.btn):hover { text-decoration-color: var(--bink-blorange); }
.brief-doc-body a:not(.btn):focus-visible { outline: 2px solid var(--bink-blorange); outline-offset: 2px; }
.brief-doc-body strong { color: var(--bink-darkgray); }

/* Section icons, bound to `data-section` and NEVER to heading text: the key is
   what the validator enforces, while six of the eight article headings are
   written by the model and could come back reworded. Rendered as a ::before on
   the heading rather than as markup, so the stored body_html is untouched and
   an UNMAPPED section generates no pseudo-element at all — no icon, no gap, no
   broken glyph. Codepoints read from the Phosphor 2.1.1 Regular stylesheet the
   shell already loads.
   Size 20px: the deck's icons ink between 16.5x19.5 (file) and 23.3x13.5
   (user-list), which is one 20px em box across differently-shaped glyphs, not
   eight different sizes. */
.brief-doc-body > .brief-card > h2::before {
  font-family: "Phosphor"; font-weight: 400; font-style: normal;
  font-size: 20px; line-height: 1; color: var(--bink-blorange);
  flex: none; -webkit-font-smoothing: antialiased;
}
/* The deck's eight, in its order. */
.brief-doc-body > [data-section="executive"] > h2::before      { content: "\e8f2"; } /* book-open-text */
.brief-doc-body > [data-section="current_state"] > h2::before  { content: "\e3b4"; } /* pencil-simple */
.brief-doc-body > [data-section="history"] > h2::before        { content: "\e3e8"; } /* question */
.brief-doc-body > [data-section="editing_dynamics"] > h2::before { content: "\e73c"; } /* user-list */
.brief-doc-body > [data-section="page_conditions"] > h2::before { content: "\e230"; } /* file */
.brief-doc-body > [data-section="attention"] > h2::before      { content: "\e154"; } /* chart-line */
.brief-doc-body > [data-section="watch_items"] > h2::before    { content: "\e220"; } /* eye */
.brief-doc-body > [data-section="close"] > h2::before          { content: "\eadc"; } /* list-checks */
/* The notability screen's own sections, which the deck never drew. `read` is
   its executive equivalent and takes the same glyph; `watch` is its own
   creation-watch promise, not the article brief's `watch_items`, but an eye is
   right for both. */
.brief-doc-body > [data-section="read"] > h2::before           { content: "\e8f2"; } /* book-open-text */
.brief-doc-body > [data-section="claim"] > h2::before          { content: "\e606"; } /* seal-check */
.brief-doc-body > [data-section="coverage"] > h2::before       { content: "\e344"; } /* newspaper */
.brief-doc-body > [data-section="discounted"] > h2::before     { content: "\e266"; } /* funnel */
.brief-doc-body > [data-section="limits"] > h2::before         { content: "\e4e2"; } /* warning-circle */
.brief-doc-body > [data-section="watch"] > h2::before          { content: "\e220"; } /* eye */
/* Conditional, and on both kinds. */
.brief-doc-body > [data-section="accuracy"] > h2::before       { content: "\e2ce"; } /* info */

/* The CTA lives INSIDE the close card, below its body copy (page 16: 179x40,
   25px under the last line). No surrounding panel — the section's own words
   already make the case, and an earlier build that wrapped this in a tinted
   block made it twice in three inches. */
.brief-doc-cta { margin: 25px 0 0; }
.brief-doc-cta .btn { padding: 0 33px; }

/* Footer card (756x186 outer): the four format actions, then the colophon. */
.brief-doc-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 29px; }
/* One px more than the section cards': the deck's footer sits its colophon a
   little further off the bottom edge than a section's last line. */
.brief-doc .brief-doc-footer { padding-bottom: 27px; }
.brief-doc-colophon {
  margin: 0; font-size: 0.75rem; line-height: 1.55; color: var(--neutral-gray);
}
/* Requester view only. Collapses to nothing rather than to an empty container:
   a page that sometimes shows this and sometimes shows a gap where it would be
   is a page that leaks who is viewing. */
.brief-doc-manage { margin: 16px 0 0; font-size: 0.8125rem; }
.brief-doc-manage a { color: var(--hyperlink); }

/* --- Print, 2026-09-03 -------------------------------------------------------
   Carried over from the standalone stylesheet this page used to have, and
   updated for the card layout. This governs browser Print-to-PDF (Ctrl+P) — a
   DIFFERENT path from the Download PDF button, which is built by ReportLab in
   core/wikibrief/render.py and never sees any of this. Dropping this block
   would silently regress printing while leaving the download untouched.
   Cards flatten to plain sections separated by a rule; anything that cannot be
   clicked on paper is hidden. The colophon stays — it is the method statement
   and the AI disclaimer, and a printed copy needs both. */
@media print {
  .brief-body.brief-body-app { padding: 0; display: block; background: #fff; }
  .brief-nav-icons, .brief-doc-cta, .brief-doc-actions, .brief-doc-manage { display: none; }
  .brief-nav { margin-bottom: 18px; }
  .brief-doc.brief-stack { gap: 0; max-width: none; }
  .brief-doc .brief-card {
    border: 0; border-radius: 0; padding: 14px 0; background: #fff;
    border-bottom: 1px solid var(--neutral-dark);
    break-inside: avoid; page-break-inside: avoid;
  }
  .brief-doc .brief-doc-footer { border-bottom: 0; }
  /* On paper the underline is noise and the address is unreachable, so the
     entries print as plain text. The PDF download carries the real links. */
  .brief-doc-body a { text-decoration: none; }
}

/* --- Error states, 2026-09-10 -----------------------------------------------
   TFG's review: "some lander errors still render as inline red text beside the
   field rather than using the deck's icon-heading-plus-copy treatment."

   The deck's treatment, measured off page 2 of the 260827 deck (PLACEHOLDER):
   a 19.7px warning-circle in #000000, 11px of gap, then a bold 14px heading on
   the same row; the body copy below returns to the column's own left edge. No
   red anywhere — the deck's error is a black notice, not an alert colour, and
   red on the cream ground was never in either deck.

   Scoped to .brief-body so the operator app's own .auth-err (seven templates
   under templates/login.html, accounts/ and bink_domains/) is untouched. */
.brief-error { margin: 0 0 24px; }
.brief-error-head {
  display: flex; align-items: center; gap: 11px;
  margin: 0 0 8px; font-size: 0.875rem; font-weight: 700; color: #000000;
}
.brief-error-head::before {
  content: "\e4e2"; /* ph-warning-circle */
  font-family: "Phosphor"; font-weight: 400; font-size: 20px; line-height: 1;
  flex: none; -webkit-font-smoothing: antialiased;
}
.brief-error-body { margin: 0; font-size: 0.875rem; line-height: 1.55; color: var(--neutral-gray); }

/* --- The house line, 2026-09-10 ---------------------------------------------
   "From Beutler Ink, the Wikipedia consultancy." sits under the card on EVERY
   app screen in both of TFG's decks — pages 4-14 of the 260827 desktop deck and
   all seven mobile frames — and was not built. The only screen without it is
   page 16, the brief page, which carries its own colophon instead and overrides
   the block to nothing. */
.brief-house {
  width: 100%; max-width: 756px; margin: 35px 0 0;
  text-align: center; font-size: 0.875rem; font-weight: 650;
  color: var(--bink-darkgray);
}
.brief-house a { color: inherit; }

/* --- Mobile, 2026-09-10 ------------------------------------------------------
   design/WikiBrief_MockupsMobile_260909.pdf, seven frames at 402pt.

   SCALE, established before anything was built rather than assumed. The frames
   are 1:1 with a 402px viewport, and the evidence is type: body copy is 14,
   h1 is 24, section headings are 16, the italic "by" is 17 and small copy is
   12-13 — every one identical to the 1440px desktop deck. Nothing was scaled.
   (The BINK mark renders 97.8x31.6 against desktop's 103x33.2, which looks like
   a 0.95 scale but is not one: it travels with the wordmark, which TFG set to
   29 here against desktop's 35. See .brief-nav below.)

   Every number in this block is a direct read off those frames, PLACEHOLDER
   until she confirms:

     side gutter    29px   (a 344px card centred in 402)
     wordmark ink   52.1px from the top, 29px type
     nav icons      centred row, 30.9px under the wordmark ink, 25.2px apart
     breadcrumb     30.2px under the icons, 342x43, 13px labels
     card           19px under the breadcrumb, 344 outer, 20px padding
     house line     35.3px under the card

   The breakpoint is 640px rather than 402px: everything between a phone and a
   tablet wants the stacked treatment, and the desktop layout has nothing left
   to give below about 800px anyway. */
@media (max-width: 640px) {
  /* 47px, not the 52.1 her frame measures to the wordmark's ink: this padding
     is measured from the nav BOX's top edge, and the wordmark's ink starts
     ~5px into it. Tuned against the render, like the desktop one. */
  .brief-body-app { padding: 47px 29px 40px; }

  /* The nav stacks: lockup centred, icons centred beneath it. On desktop the
     two sit on one row at opposite ends. */
  .brief-nav {
    flex-direction: column; align-items: center; gap: 0;
    max-width: none; margin: 0 0 30px;
  }
  .brief-nav-brand { font-size: 1.813rem; } /* 29px, her mobile wordmark size */
  /* 19px, not her measured 30.9: that gap runs from the wordmark's INK bottom
     and this margin runs from the brand box's bottom edge, which sits ~12px
     lower (the mark's own box plus the line's descender space). Same reference
     correction the desktop nav margin needed. */
  .brief-nav-icons { margin: 19px 0 0; gap: 25px; }

  /* Breadcrumb: shorter bar, smaller labels, tighter separators. Her frames
     keep all four steps on one line at this width — "Choose Article", the
     longest, measures 89.8px of 342 — so nothing wraps and nothing is hidden. */
  /* Fitting four steps into 342px, measured rather than guessed. Each boundary
     costs the gap TWICE — once from the row, once from inside the <li> that
     owns the separator — plus the separator's own box, which is its font-size
     wide even though its ink is only ~6px. At the first attempt (13px labels,
     8px gaps, 17px separators) the row measured 330.7px against 314px of
     content box and "Preparing" was clipped off the right edge.
     12px labels + 4px gaps + 15px separators measures ~300px, which clears her
     402px frame and a 390px phone.
     Below that it stops fitting, so the bar scrolls rather than clipping: a
     breadcrumb that silently hides the step you are heading for is worse than
     one you can nudge. Scrollbar hidden — it is a 40px-tall bar, not a pane. */
  .brief-breadcrumb {
    max-width: none; min-height: 43px; padding: 0 15px; gap: 4px;
    font-size: 0.75rem; margin: 0 0 19px;
    overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  }
  .brief-breadcrumb::-webkit-scrollbar { display: none; }
  .brief-breadcrumb li { gap: 4px; }
  .brief-breadcrumb li + li::before { font-size: 15px; }

  /* Cards: full width of the gutter, tighter padding (20px, against 28). */
  .brief-body .auth-card { max-width: none; padding: 24px 20px; }
  .brief-doc .brief-card,
  .brief-card { padding: 24px 20px; }
  .brief-doc .brief-doc-header { padding-top: 26px; }
  .brief-house { max-width: none; }

  /* Choose Article: the Select button sits at the card's top right and the
     snippet runs the full width beneath the title, rather than the title and
     snippet sharing a column beside the button. Absolute rather than a flex
     row so the snippet can pass under it. */
  .brief-result { display: block; position: relative; padding: 20px; }
  .brief-result-text { padding-right: 0; }
  .brief-result-title { padding-right: 108px; }
  .brief-result form { position: absolute; top: 16px; right: 16px; margin: 0; }
  .brief-result-snippet { white-space: normal; }

  /* Manage: three stacked cards, not two columns. */
  .brief-manage-grid { grid-template-columns: 1fr; gap: 8px; }
  .brief-body .auth-card.brief-stack-lg { gap: 8px; }

  /* Confirm's action row wraps rather than squeezing the link against the
     button; the two-button rows do the same. */
  .brief-confirm-actions,
  .brief-button-row { flex-wrap: wrap; gap: 12px; }

  /* Contact's action row stacks — the note has nowhere to sit at this width. */
  .brief-contact-actions { flex-direction: column; align-items: flex-start; gap: 12px; }
  .brief-contact-actions .auth-hint { text-align: left; }

  /* The brief page's own footer row wraps its four format buttons. */
  .brief-doc-actions { gap: 8px; }

  /* The pre-gate shell stacks: white card, then the gradient panel with the
     four pills full width, then the footer. Her lander frame keeps the two in
     ONE rounded container, so the shell keeps its radius and overflow and only
     its direction changes. */
  /* The pre-gate ground keeps its own padding — .brief-body-app's does not
     reach it — and her lander frame puts the container at 342px inside 402,
     i.e. 30px gutters, one px off the app screens' 29. `:not()` so the app
     rule above wins on the screens it governs regardless of source order. */
  .brief-body:not(.brief-body-app) { padding: 24px 30px; }
  .brief-pregate-shell { flex-direction: column; max-width: none; }
  .brief-pregate-card { padding: 40px 29px; flex: none; }
  .brief-pregate-panel { flex-direction: column; flex: none; padding: 29px; gap: 16px; }
  .brief-pregate-step { width: 100%; }
  .brief-wordmark-name { font-size: 1.813rem; }
  .brief-pregate-footer-note,
  .brief-pregate-footer-links,
  .brief-pregate-footer-house { max-width: none; }
}

/* --- Account settings + contact (in-app, extend base.html) ------------------ */
.settings-page, .contact-page { max-width: 480px; margin: 0 auto; }
.settings-page h1, .contact-page h1 { text-align: center; margin: 0.5rem 0 1.5rem; }
.set-card h3 { display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.9rem; }
.set-lock { color: var(--muted); font-size: 0.9rem; }
.set-note { color: var(--muted); font-size: 0.8rem; margin: 0.8rem 0 0; }
.email-change > summary {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  cursor: pointer; list-style: none;
}
.email-change > summary::-webkit-details-marker { display: none; }
.email-change-link { color: var(--ww-link); font-size: 0.9rem; }
.email-change > summary:hover .email-change-link { text-decoration: underline; }
.email-change[open] > summary .email-change-link { color: var(--muted); }
.set-readonly { display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem 1rem; }
.set-readonly .k { font-weight: 600; font-size: 0.82rem; }
.set-readonly .v { color: var(--ink); }
.set-fields { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.9rem; }
.set-fields input { flex: 1; min-width: 8rem; }
.contact-page textarea { width: 100%; min-height: 8rem; resize: vertical; }
/* Reason dropdown: give the shared .ww-select a real input-height (it otherwise
   collapses to the bare native height) so it matches the message box's input
   styling, per the July 20 mockup. */
.contact-page .ww-select { height: 2.4rem; margin-top: 0.3rem; }
.contact-page textarea { margin-top: 0.3rem; }
.contact-readonly { display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem 1rem; margin-bottom: 1rem; }
.contact-readonly .k { font-weight: 600; font-size: 0.82rem; }
.field-label { display: block; font-weight: 600; font-size: 0.82rem; margin: 0.6rem 0 0.1rem; }

/* --- Help guide -------------------------------------------------------------
   Every <p> and <ul> on this page used to carry an inline font-size:0.9rem;
   that lives here now, so the page's body size moves from one place. The measure
   is capped because the page is one long read — at the full 1280px gutter width
   a 0.9rem line runs past 150 characters, which is why the pre-rewrite page was
   hard to scan. */
.help-page { max-width: 820px; }
.help-page p, .help-page li { font-size: 0.9rem; line-height: 1.55; }
.help-page li { margin-bottom: 0.3rem; }
.help-page .card > :last-child { margin-bottom: 0; }
.help-lede { font-size: 0.95rem; color: var(--muted-strong); margin: 0 0 1.25rem; }
/* A captured screenshot of a real WikiWatch surface. Images are 2x, so they are
   sized by the column and never by their intrinsic width. */
.help-figure { margin: 1.1rem 0; }
/* Each figure links to its own full-size file. These are desktop captures, so
   at the mobile width they render around a quarter size and their UI text is
   unreadable — tapping through is the only way to actually see the thing the
   caption describes. The anchor carries no underline: it wraps the image, and
   the caption below is the label. */
.help-figure a { display: block; text-decoration: none; }
.help-figure img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.help-figure figcaption { font-size: 0.78rem; color: var(--muted); margin-top: 0.4rem; }
/* Badge reference: the app's table typography at reference-table density, and
   it scrolls rather than wraps its columns on a phone. */
.badge-table-wrap { overflow-x: auto; margin: 0.25rem 0 0; }
.badge-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.badge-table th {
  text-align: left; padding: 0.45rem 0.6rem; color: var(--th); font-weight: 500;
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em;
  border-bottom: 1px solid var(--line); white-space: nowrap;
}
.badge-table td { padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
.badge-table tbody tr:last-child td { border-bottom: none; }
.badge-table td:first-child { font-weight: 600; }
.badge-note { font-size: 0.85rem; color: var(--muted); margin: 0.9rem 0 0; }
.badge-note .ph { color: var(--muted); }

/* Freemium alert-slot toggle cell (limit B): keep the toggle and its locked
   note aligned. Placeholder styling pending TFG. */
/* Toggle on top, the limit note stacked beneath it so the note stays inside the
   narrow Alerts column instead of overflowing into Digest. The note wraps at
   word/hyphen boundaries (never mid-word) on narrow widths; its full text is
   always in the tooltip. */
.alert-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; min-width: 0; }
.alert-limit-note { font-size: 0.7rem; line-height: 1.2; display: flex; align-items: flex-start; gap: 0.2rem; max-width: 100%; }
.alert-limit-note i { flex: none; margin-top: 0.1em; }

/* Freemium F3: persistent unverified-email banner (quiet, above the app). */
.verify-banner {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  flex-wrap: wrap; padding: 0.5rem 1rem; font-size: 0.85rem;
  background: #fff8e6; color: #7a5b00; border-bottom: 1px solid #f0e2b8;
}
.verify-banner i { vertical-align: -0.1em; }
.verify-resend {
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--ww-link); text-decoration: underline;
}

/* --- Mobile (Stage A, mobile deck July 29) ---------------------------------
   One breakpoint for the whole mobile foundation: 720px. The header nav row's
   natural width (brand + six links + gutters) crowds just under ~700px, so the
   swap sits just above that; tablet portrait (768px) still fits the desktop
   layout comfortably and keeps it. Reuse this value for later mobile work
   rather than introducing a second near-identical breakpoint. */
@media (max-width: 720px) {
  /* 4rem gutters eat 128px of a 375px screen. 1.25rem matches the modest edge
     padding in the mobile mockups and keeps header/hero/main/footer flush. */
  :root { --gutter: 1.25rem; }

  /* Nav row out, ≡ menu in. Every mobile slide centers the logo with the ≡ at
     the right, so the brand is centered on the header rather than left-aligned
     (absolutely, so it centers on the header and not on the space left over
     beside the ≡). Scoped with :has() to headers that actually carry a ≡ — the
     client-report shell (templates/client/base.html) reuses header.site with its
     own two-link nav and no ≡, and must keep its left-aligned brand. */
  header.site nav.nav-row { display: none; }
  .nav-menu { display: block; }
  header.site:has(.nav-menu) { position: relative; }
  header.site:has(.nav-menu) .brand {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  }

  /* Six tabs overflow a phone; scroll them horizontally instead (mockup shows
     the row running off the right edge). Negative margin + matching padding
     lets the row scroll to the physical screen edge while the first tab stays
     on the gutter. The active underline is the tab's own border-bottom, so it
     rides along unchanged; the scrollbar is hidden so it can't sit on top of
     it or shift the row off the hero's bottom edge. */
  .tabs {
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    margin: 0 calc(-1 * var(--gutter)); padding: 0 var(--gutter);
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs a { flex: none; white-space: nowrap; }

  /* --- Watchlist (Stage B, mobile deck Watchlist + Search Results slides) --- */

  /* Rows collapse to what the slide shows: title + unread badge on the left,
     ••• on the right. Latest Activity drops out (it's a tap away on the Edit
     List), Alerts and Digest move into the ••• menu, and the header row goes
     with them — a single content column needs no column labels. */
  .wl-section table.list thead { display: none; }
  .wl-section table.list td.wl-activity,
  .wl-section table.list td.wl-alerts,
  .wl-section table.list td.wl-digest { display: none; }
  /* table-layout stays fixed — it can't exceed the table's own width, so the
     title truncates instead of pushing the row wide — but the desktop column
     percentages have to be re-dealt between the two surviving cells, or the
     leftover 68% is split evenly and ••• gets a third of the row. */
  .wl-section table.list td:nth-child(1) { width: auto; }
  .wl-section table.list td:nth-child(5) { width: 3.25rem; }
  /* Group headers keep the caret and the rename/delete icons; the slide drops
     the article count at this width. */
  .wl-section .sec-count { display: none; }

  /* ••• menu: room for the alerts toggle and digest dropdown side by side, and
     still clear of both gutters at 375px (335px of content). */
  .row-menu-panel { min-width: 15rem; }
  .rm-prefs { display: flex; gap: 1.1rem; padding: 0.3rem 0.5rem 0.5rem; }
  .rm-prefs-sep { display: block; }

  /* "Mark all as viewed" leaves the add bar for the foot of the list, full
     width (deck note 3), so the articles start higher up the screen. */
  .add-bar .mark-all { display: none; }
  .mark-all-bottom { display: block; margin: 0 0 1.25rem; }
  .mark-all-bottom .btn { width: 100%; justify-content: center; height: 2.6rem; }

  /* Add bar: the field + ⊕ have the row to themselves and must be free to
     shrink below the desktop minimum. */
  .add-form { min-width: 0; }

  /* Search results: centered heading, and the ⊕ / ✓ aligned with the result
     title — desktop centers it against a one-line row, here the snippet wraps
     to two. Fixed layout + break-word keeps a long title inside the card. */
  .sr-title { text-align: center; }
  table.search-list { table-layout: fixed; }
  table.search-list td { vertical-align: top; }
  td.sr-action { width: 3.25rem; }
  .sr-name { overflow-wrap: break-word; }

  /* --- Article pages + account (Stage C, mobile deck slides 4-7) ------------ */

  /* Edit List toolbar: pills out, dropdown in (deck note 2), so the filter set
     and "Mark all as viewed" share one row. The dropdown carries its own label
     (aria-label="View"), so the standalone one goes with the pills. */
  .el-filters { display: none; }
  .el-filter-form { display: flex; }
  .el-filter-select { flex: 1; min-width: 0; }

  /* Edit List rows become stacked cards: chip + kind icon on the left of the
     first line with the date opposite, the linked summary on a second line
     spanning the card. The editor column drops out entirely (deck note 3) — that
     detail lives on the Edit Detail view. The table's parts are re-declared as
     blocks so each row can be its own two-row grid; `grid-area` does the
     reordering, since the date follows the summary in the DOM. */
  table.list.edit-list, .edit-list tbody, .edit-list td { display: block; }
  .edit-list thead { display: none; }
  .edit-list tr {
    display: grid; position: relative;
    grid-template-columns: auto auto 1fr;
    grid-template-areas: "chip icon date" "summary summary summary";
    align-items: center; gap: 0.55rem 0.6rem;
    padding: 0.85rem 0.9rem; border-bottom: 1px solid var(--line);
  }
  .edit-list tbody tr:last-child { border-bottom: none; }
  /* Selector matches table.list td / table.list.edit-list td, which set the
     cell padding and the row divider — the row itself carries both now. */
  table.list.edit-list td { width: auto; padding: 0; border-bottom: none; }
  .edit-list td.ev-type { grid-area: chip; }
  .edit-list td.ev-icon { grid-area: icon; width: auto; padding: 0; text-align: left; }
  .edit-list td:nth-child(3) { grid-area: summary; }
  .edit-list td:nth-child(4) { grid-area: date; justify-self: end; }
  .edit-list td:nth-child(5) { display: none; }
  /* A row that is one message rather than a record — the empty state here and
     on Top Editors — spans the whole card instead of landing in the first,
     narrow grid area. (The free plan's locked-history row needs no class: its
     message is already the cell the summary area picks up.) */
  .edit-list tr > td.row-wide { grid-area: auto; grid-column: 1 / -1; }
  /* The unread bar spans the whole card, so it moves off the first cell — now
     just the chip — onto the row itself. */
  .edit-list tr.unseen td:first-child::before { display: none; }
  .edit-list tr.unseen::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: var(--teal);
  }

  /* Edit Detail / Event Detail header: chip + icon with the actions opposite,
     the edit's top line on a second row across the card (mockup). display:
     contents lifts .eh-main's children into the header's own grid so the top
     line can leave the chip's row. */
  .edit-header {
    display: grid; grid-template-columns: auto auto 1fr;
    grid-template-areas: "chip icon actions" "line line line";
    align-items: center; gap: 0.7rem 0.6rem;
  }
  .edit-header .eh-main { display: contents; }
  .edit-header .chip { grid-area: chip; }
  .edit-header .ev-icon-glyph { grid-area: icon; }
  .edit-header .eh-line { grid-area: line; }
  .edit-header .eh-actions { grid-area: actions; justify-self: end; }

  /* Overview callout stats: four on a phone (deck note 1) — "Edits (7 days)"
     and "Inbound links" drop out. Both numbers survive on the Activity and The
     Page cards below, so nothing is lost, only the row. */
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .stat-grid .stat-card:nth-child(1), .stat-grid .stat-card:nth-child(6) { display: none; }

  /* Overview cards are grid items, so their automatic minimum size is their
     min-content — and the edits-per-year chart's min-content (one 34px column
     per year) is wider than a phone. That stretched every card in the stacked
     column past the gutter, since they share one track. min-width:0 lets a card
     take the track's width and the chart scroll inside it, which is what its own
     overflow-x:auto is already there for. */
  .ov-grid > .card { min-width: 0; }

  /* Before/After gutter back to the marker's own width: 2.5rem out of a 335px
     card would cost the two monospace columns more than the separation is worth
     at this size. The .diff-heads grid and the <col> widths both follow it. */
  .diff-card { --diff-gutter: var(--diff-marker); }

  /* Key/value rows (Overview, Edit Details): the label column's `auto` track is
     sized to its longest label, which at this width leaves the value column
     narrower than its own content and pushes the card past the gutter. Let both
     tracks shrink, and break the long descriptions and language lists. */
  .kv { grid-template-columns: minmax(0, auto) minmax(0, 1fr); }
  .kv .v { overflow-wrap: anywhere; }

  /* Top Editors condenses to two columns: name over experience badge, edits to
     this article over total edits; the registered year drops out (deck note 1).
     Same block-plus-grid mechanism as the Edit List rows above. A fixed second
     track keeps the numbers on one x-position down the list — each row is its
     own grid, so an auto track would ragged-edge them. */
  table.list.top-editors, .top-editors thead, .top-editors tbody,
  .top-editors th, .top-editors td { display: block; }
  .top-editors tr {
    display: grid; grid-template-columns: minmax(0, 1fr) 8.5rem;
    grid-template-areas: "name edits" "badge total";
    gap: 0.3rem 0.6rem; padding: 0.85rem 0.9rem;
    border-bottom: 1px solid var(--line);
  }
  .top-editors tbody tr:last-child { border-bottom: none; }
  table.list.top-editors th, table.list.top-editors td { padding: 0; border-bottom: none; }
  .top-editors tr > :nth-child(1) { grid-area: name; overflow-wrap: anywhere; }
  .top-editors tr > :nth-child(2) { grid-area: edits; }
  .top-editors tr > :nth-child(3) { grid-area: badge; }
  .top-editors tr > :nth-child(4) { display: none; }
  .top-editors tr > :nth-child(5) { grid-area: total; font-weight: 700; }
  .top-editors tr > td.row-wide { grid-area: auto; grid-column: 1 / -1; }
  .top-editors thead th:nth-child(3), .top-editors thead th:nth-child(5) { display: none; }
  .top-editors th.num, .top-editors td.num { text-align: left; white-space: normal; }
  .te-short, .te-suffix { display: inline; }
  .te-long { display: none; }
  .te-suffix { font-weight: 400; }

  /* Badge reference: three nowrap columns need ~352px and the card offers 295,
     and a table that scrolls with no affordance just reads as clipped mid-word.
     Let the columns wrap instead — a reference table can take two-line cells. */
  .badge-table { font-size: 0.8rem; }
  .badge-table th, .badge-table td { padding: 0.45rem 0.4rem; white-space: normal; }

  /* Account Settings: the two password fields stack full-width (mockup) rather
     than sharing a row at a width where neither reads as a real field. */
  .set-fields { flex-direction: column; }
  .set-fields input { flex: none; width: 100%; }
}

/* --- Card content resets (must stay after .brief-body .auth-card .auth-sub) */
.brief-body .auth-card .brief-card > :last-child { margin-bottom: 0; }
.brief-body .auth-card .brief-card > :first-child { margin-top: 0; }
