/* Vellum feed grammar.
 *
 * Wave 5b (Hakim, 2026-08-18): "re the parts with a feed, make it exactly similar to the main
 * feed we have in terms of design." These rules were scoped to #feed-root — one id, one
 * surface. They are scoped to .f-surface now, so My Resources' panes wear the SAME grammar by
 * carrying the class rather than by copying the rules. "Exactly similar" then holds by
 * construction: there is one description of a feed card, and changing it changes every
 * surface that uses it. A second copy is how two surfaces drift while both look right on the
 * day they ship.
 *
 * #feed-root keeps working by carrying .f-surface in the markup — nothing about the feed moved.
 */
/* Vellum Feed — self-contained.
   Kept out of index.html so the feed can be built while another branch holds that file, and so the
   diff against it stays three lines instead of six hundred. Every colour here is from BRAND.md; the
   variables are declared locally so this file renders correctly even if loaded before the app's own
   stylesheet. */
.f-surface {
  --f-accent: #1a3d7c; --f-accent-soft: #6fb3ff; --f-ink: #0a1628; --f-ink-2: #3d4a63;
  --f-ink-3: #8a94a8; --f-panel: #fff; --f-hair: rgba(13,33,71,.12);
  /* Elevation — the ⋯ menu, the sort menu, the Ask card. Identical to --f-panel in LIGHT, so
     this costs zero light-mode pixels; it exists because in DARK elevation has to be carried
     by lightness rather than by a shadow, and a navy-alpha shadow over a navy ground is
     invisible (palette spec §2.1, R7). Those three surfaces already carry a --f-hair border,
     which is the other half of the same rule. */
  --f-panel-2: #fff;
  /* .f-sum read `var(--f-chip, #f6f3ec)` — a FALLBACK, which is not a definition. The token
     was declared nowhere in the tree, so the AI-summary panel had a warm-cream ground that no
     theme could reach and no test could see (project law #1; spec R9). Defined here at its
     own former fallback value: byte-identical in light, themeable at last. */
  --f-chip: #f6f3ec;
  --f-scrim: rgba(13,33,71,.34);
  --f-shadow-menu: rgba(13,33,71,.16); --f-shadow-ask: rgba(13,33,71,.22);
  /* Surfaces, inks and washes this file shares by NAME with index.html's :root, declared HERE
     rather than borrowed — for the reason in the header note above: feed.css has to render
     correctly on its own, and it is loaded after index.html's <style> but owns a scope that
     :root cannot see into. A var() whose token lives only in the other file is invalid at
     computed-value time: no error, no red test, the element simply does not paint. Every light
     value below is a byte-exact copy of the literal it replaces, so shadowing :root inside
     .f-surface is a no-op in light mode. */
  --panel-soft: #fdfcf9;             /* .f-card, .f-ask-q, .f-ask-anchor — the off-white well */
  --chip: #f3efe7;                   /* .f-preview-media, the image placeholder */
  --hover: #faf7f2;                  /* menu / action hover. Must LIFT off its surface in BOTH
                                        themes: in light the page ground is darker than the
                                        panel, in dark it is lighter, so a hover that reuses
                                        the ground reads as a hole (spec R13). */
  --band: #eef0f3;                   /* the 6px phone separator; direction inverts in dark */
  --on-accent: #fff;                 /* label on an --f-accent fill */
  --on-ink: #fff;                    /* label on an --f-ink fill — the pressed pills */
  --on-identity: #fff;               /* initials on a generated identity hue — NEVER flips */
  /* The "no identity known" disc. feed.js already emits `style="background:var(--avatar-fallback)"`
     at three call sites (feed.js:256, 1245, 1356) and the token is currently defined NOWHERE in
     the tree — the declaration is invalid today and those discs paint transparent, white
     initials on a white card. This closes it for every .f-surface subtree; index.html still
     owes the :root and html.phone-chrome copies for #ms-follows, which has no .f-surface
     ancestor (see the note at feed.js:241–255). Pseudo-identity: it carries white initials, so
     it stays dark in both themes and must never be aliased to the flipping --ink-2. */
  --avatar-fallback: #3d4a63;
  --wash-navy: rgba(13,33,71,.045); --wash-navy-2: rgba(13,33,71,.04);
  --wash-hover: rgba(26,61,124,.06);
  /* The brass and desktop-green tokens retired (Hakim 2026-08-17, redesign D2 + point 4):
     navy is the one accent — every former consumer of those tokens reads the accent now,
     and tests pin that neither token name returns. The metadata label is the single survivor:
     that retirement left its brass as five RAW LITERALS, and a raw literal cannot theme.
     #76581f on a dark panel measures 1.9:1 — the worst pair in the tree under a naive flip
     (spec R6). The names below are the palette spec's §3.6 ones and are deliberately NOT in
     this file's own --f- namespace: the pins in tests/test_mobile_ui_round5.py:380 and
     tests/ui_pass3_polish.test.mjs:20 are substring assertions on the retired name, so
     re-using it — even inside a comment — would fail them. Do not rename these to match. */
  --brass-ink: #76581f; --brass-bg: rgba(176,138,62,.07);
  --brass-bd: rgba(176,138,62,.38); --brass-bd-2: rgba(176,138,62,.42);
  --brass-tint: rgba(176,138,62,.045);
  --f-serif: "Iowan Old Style", Palatino, "Palatino Linotype", Georgia, serif;
  --f-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  max-width: 620px; margin: 0 auto; padding-bottom: 80px;
}

.f-surface .f-hd { margin: 4px 0 18px; }
.f-surface .f-hd h2 { font-family: var(--f-serif); font-size: 21px; margin: 0 0 3px; font-weight: 600; }
.f-surface .f-hd p { margin: 0; color: var(--f-ink-2); font-size: 13.5px; }

.f-surface .f-post {
  /* Hakim, 2026-08-18: "even the corner of the whole card should be straight, not rounded."
     Same reasoning as the link card's side borders one rule below — once the media runs to
     the post's edge, a 13px radius clips the image's top corners and the post reads as a
     rounded frame around a square picture. Squaring the post is what makes the edge-to-edge
     treatment land; the phone has always been square here, so this is the two faces agreeing. */
  background: var(--f-panel); border: 1px solid var(--f-hair); border-radius: 0;
  padding: 15px 16px 11px; margin-bottom: 14px;
}
.f-surface .f-post--metadata {
  border-color: var(--brass-bd);
  background: linear-gradient(180deg, var(--brass-tint), var(--f-panel) 92px);
}
.f-surface .f-metadata-label {
  display: inline-flex; align-items: center; margin: -1px 0 8px; padding: 3px 7px;
  border: 1px solid var(--brass-bd-2); border-radius: 999px;
  color: var(--brass-ink); background: var(--brass-bg); font-family: var(--f-mono);
  font-size: 9px; line-height: 1.3; letter-spacing: .055em; text-transform: uppercase;
}
.f-surface .f-repost-label {
  margin: -2px 0 10px; color: var(--f-ink-2); font-family: var(--f-mono);
  font-size: 10px; line-height: 1.35; letter-spacing: .025em;
}

/* byline — the name and avatar are a link to the agent's page */
.f-surface .f-by { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.f-surface .f-byline {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  text-decoration: none; color: inherit;
}
.f-surface .f-byline:hover .f-nm { text-decoration: underline; }
.f-surface .f-av {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 50%; display: grid; place-items: center;
  /* --on-identity, not --on-ink: the disc's background is an identity hue written inline by
     feed.js, so it does not flip and neither may its initials (spec A1, §4.1). */
  font-family: var(--f-mono); font-size: 11px; color: var(--on-identity); letter-spacing: .02em;
  position: relative; overflow: hidden;
}
/* The portrait covers the initials rather than replacing them: if it never arrives — no
   portrait for this persona, a 404, an offline shell — the mark underneath is what shows. */
.f-surface .f-av-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
}
.f-surface .f-who { flex: 1; min-width: 0; }
.f-surface .f-nm {
  font-family: var(--f-serif); font-size: 14.5px; font-weight: 600; color: var(--f-ink);
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
}
.f-surface .f-badge {
  font-family: var(--f-mono); font-size: 8.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--f-accent); border: 1px solid var(--f-accent-soft); border-radius: 4px;
  padding: 1.5px 5px; font-weight: 400;
}
.f-surface .f-mt { font-family: var(--f-mono); font-size: 10.5px; color: var(--f-ink-3); margin-top: 2px; }
.f-surface .f-attr { color: var(--f-accent); }
.f-surface .f-follow {
  font-family: var(--f-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  border: 1px solid var(--f-hair); background: transparent; color: var(--f-ink-2);
  border-radius: 999px; padding: 5px 12px; cursor: pointer; white-space: nowrap;
}
/* Token background, token foreground — both halves, or the pressed pill goes white-on-white
   the moment --f-ink flips (spec R3, and it only shows after a press, so QA walks past it). */
.f-surface .f-follow[aria-pressed="true"] { background: var(--f-ink); color: var(--on-ink); border-color: var(--f-ink); }

/* "why you are seeing this" — the ranking explaining itself, per card */
.f-surface .f-why {
  font-family: var(--f-mono); font-size: 9.5px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--f-accent); margin: -2px 0 7px;
}

/* the hook */
.f-surface .f-hook {
  font-family: var(--f-serif); font-size: 16.5px; line-height: 1.52; margin: 0 0 12px;
  color: var(--f-ink); letter-spacing: -.003em;
}

/* the source card — the fact, beneath the invitation */
.f-surface .f-card {
  display: block; border: 1px solid var(--f-hair);
  overflow: hidden; background: var(--panel-soft);
  /* Hakim, 2026-08-18: the media reaches the post's edges, end to end, zero padding — the
     LinkedIn treatment. The post pads its TEXT by 16px, which is right for text and wrong for
     media, so the card breaks out of exactly that padding. The side borders and the radius go
     with the inset: a border that stops at the post's edge reads as a seam, not a frame. */
  margin-left: -16px; margin-right: -16px;
  border-left: 0; border-right: 0; border-radius: 0;
}
.f-surface .f-card:hover { border-color: var(--f-accent-soft); }
.f-surface .f-card-hit { display: block; text-decoration: none; color: inherit; cursor: pointer; }
/* Hakim, 2026-08-18: the image reaches the post's left and right edges, end to end, zero
   padding — the LinkedIn treatment. The post carries 15px/16px of horizontal padding for its
   TEXT, which is right for text and wrong for media, so the link card breaks out of it and
   the media spans the full post width. The card's side borders and its top corner radius go
   with the inset: a border that stops at the post edge reads as a seam, not a frame.
   The text BELOW the image keeps its own padding — only the media goes edge to edge. */
.f-surface .f-card > .f-card-hit > *:not(.f-preview-media) { padding-left: 16px; padding-right: 16px; }
.f-surface .f-preview-media {
  width: 100%; aspect-ratio: 16 / 9; box-sizing: border-box; overflow: hidden; background: var(--chip);
  border-bottom: 1px solid var(--f-hair);
}
.f-surface .f-preview-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.f-surface .f-card-in { padding: 12px; display: flex; gap: 12px; align-items: flex-start; }
.f-surface .f-logo {
  width: 48px; height: 48px; flex: 0 0 48px; border-radius: 6px; display: grid; place-items: center;
  /* identity hue written inline by feed.js — see .f-av above */
  font-family: var(--f-mono); font-size: 16px; font-weight: 700; color: var(--on-identity); letter-spacing: -.02em;
}
.f-surface .f-card-txt { min-width: 0; }
.f-surface .f-src {
  font-family: var(--f-mono); font-size: 9.5px; color: var(--f-ink-3); letter-spacing: .04em;
  text-transform: uppercase;
}
.f-surface .f-ttl {
  font-family: var(--f-serif); font-size: 14.5px; font-weight: 600; margin: 3px 0 2px;
  line-height: 1.35; color: var(--f-ink);
}
.f-surface .f-dt { font-family: var(--f-mono); font-size: 10px; color: var(--f-ink-3); }
.f-surface .f-vf {
  display: flex; align-items: center; gap: 6px; font-family: var(--f-mono); font-size: 9.5px;
  color: var(--f-accent); border-top: 1px solid var(--f-hair); padding: 6px 12px;
  background: var(--wash-navy);
}
/* Round 12 (Hakim, 2026-08-12): the provenance line reads in the house blue, not brass. */
.f-surface .f-tr { font-family: var(--f-mono); font-size: 9.5px; color: var(--f-accent); padding: 0 12px 7px; }

/* counts sit above the actions, aggregate only — no user is ever shown to another */
.f-surface .f-counts {
  font-family: var(--f-mono); font-size: 10px; color: var(--f-ink-3);
  margin: 11px 0 5px; padding-bottom: 6px; border-bottom: 1px solid var(--f-hair);
}
.f-surface .f-counts:empty { display: none; }

/* Hakim, 2026-08-19: "the buttons are left aligned, can you make them centre aligned... the
   three dots remain as they are." The verbs centre as a group and the ⋯ keeps its corner: it is
   not one of the verbs, it is the overflow. Absolute rather than margin-left:auto, because auto
   would push the verbs off-centre by exactly the ⋯'s width — that centres the ROW, not the
   buttons the eye actually reads. Symmetric padding reserves the same space on both sides. */
.f-surface .f-acts { display: flex; gap: 18px; flex-wrap: wrap; position: relative; margin-top: 10px;
  justify-content: center; padding-left: 34px; padding-right: 34px; }
.f-surface .f-acts [data-menu] { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
.f-surface .f-acts [data-save][aria-pressed="true"] { color: var(--f-accent); font-weight: 600; }
/* Round 12 (Hakim): a saved post must LOOK saved — the bookmark fills, the social-media
   idiom. ICON.save is stroke-drawn, so currentColor fills it in the active colour on
   every surface (navy on both faces since the 08-17 redesign ruling — desktop adopted
   the phone's colour when brass retired). */
.f-surface .f-acts [data-save][aria-pressed="true"] svg { fill: currentColor; }
.f-surface .f-acts [data-menu] { margin-left: auto; }
.f-surface .f-menu {
  display: none; position: absolute; right: 0; bottom: 38px; z-index: 30; min-width: 200px;
  background: var(--f-panel-2); border: 1px solid var(--f-hair); border-radius: 10px;
  box-shadow: 0 10px 30px var(--f-shadow-menu); padding: 5px;
}
.f-surface .f-menu.open { display: block; }
.f-surface .f-menu button {
  /* Hakim, 2026-08-18: every row in the ⋯ carries an icon, as "Report this post" already did.
     flex, not block, so the glyph and the label sit on one baseline and a wrapping label
     indents under itself rather than under the icon. */
  display: flex; align-items: center; gap: 8px;
  width: 100%; text-align: left; background: transparent; border: 0;
  font-family: var(--f-mono); font-size: 11px; color: var(--f-ink-2); padding: 8px 10px;
  border-radius: 7px; cursor: pointer;
}
.f-surface .f-menu button svg { width: 13px; height: 13px; flex: 0 0 auto; }
.f-surface .f-menu button span { min-width: 0; overflow-wrap: anywhere; }
.f-surface .f-menu button:hover { background: var(--hover); color: var(--f-ink); }
.f-surface .f-menu button:disabled { color: var(--f-ink-3); cursor: default; }
.f-surface .f-acts button {
  display: inline-flex; align-items: center; gap: 6px; font-family: var(--f-mono); font-size: 10.5px;
  letter-spacing: .03em; background: transparent; border: 1px solid transparent;
  color: var(--f-ink-2); border-radius: 7px; padding: 7px 9px; cursor: pointer;
}
.f-surface .f-acts button:hover { background: var(--hover); border-color: var(--f-hair); }
.f-surface .f-acts button[aria-pressed="true"] { color: var(--f-accent); font-weight: 600; }
.f-surface .f-acts svg { width: 14px; height: 14px; flex: 0 0 14px; }
.f-surface .f-acts .f-deadline { color: var(--f-accent); }   /* Vellum navy, not brass (Hakim, 2026-08-15) */

.f-surface .f-locked {
  margin-top: 9px; font-family: var(--f-mono); font-size: 10.5px; color: var(--f-ink-3);
  border-top: 1px dashed var(--f-hair); padding-top: 8px;
}
.f-surface .f-locked a { color: var(--f-accent); }

/* free-tier wall */
.f-surface .f-wall {
  background: var(--f-panel); border: 1px solid var(--f-accent-soft); border-radius: 13px;
  padding: 20px; text-align: center; margin-top: 6px;
}
.f-surface .f-wall b { font-family: var(--f-serif); font-size: 16px; }
.f-surface .f-wall p { color: var(--f-ink-2); font-size: 13.5px; margin: 7px 0 13px; }
.f-surface .f-wall a {
  display: inline-block; background: var(--f-accent); color: var(--on-accent); text-decoration: none;
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  padding: 10px 18px; border-radius: 999px;
}
.f-surface .f-empty { color: var(--f-ink-2); font-size: 14px; padding: 30px 0; text-align: center; }

/* the sort control — one funnel, LinkedIn-style; the ordering rules live as captions in its menu */
.f-surface .f-hd { display: flex; justify-content: flex-end; margin: 2px 0 12px; }
.f-surface .f-sort { position: relative; }
.f-surface .f-sort-btn {
  display: inline-flex; align-items: center; gap: 7px; font-family: var(--f-mono);
  font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--f-ink-2);
  background: transparent; border: 0; cursor: pointer; padding: 6px 8px; border-radius: 7px;
}
.f-surface .f-sort-btn:hover { background: var(--wash-hover); color: var(--f-ink); }
.f-surface .f-sort-btn svg { width: 15px; height: 15px; }
.f-surface .f-sort-menu {
  display: none; position: absolute; right: 0; top: 34px; z-index: 60; min-width: 250px;
  background: var(--f-panel-2); border: 1px solid var(--f-hair); border-radius: 11px;
  box-shadow: 0 12px 36px var(--f-shadow-menu); padding: 6px;
}
.f-surface .f-sort.open .f-sort-menu { display: block; }
.f-surface .f-sort-menu button {
  display: block; width: 100%; text-align: left; background: transparent; border: 0;
  cursor: pointer; padding: 8px 11px; border-radius: 8px;
}
.f-surface .f-sort-menu button:hover { background: var(--hover); }
.f-surface .f-sort-menu button b {
  display: block; font-family: var(--f-mono); font-size: 11px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--f-ink); font-weight: 600;
}
.f-surface .f-sort-menu button[aria-pressed="true"] b { color: var(--f-accent); }
.f-surface .f-sort-menu button small {
  display: block; font-family: var(--f-serif); font-size: 12px; color: var(--f-ink-2);
  margin-top: 1px;
}

/* the continuous-feed sentinel */
.f-surface .f-more {
  font-family: var(--f-mono); font-size: 10.5px; color: var(--f-ink-3);
  text-align: center; padding: 18px 0;
}

/* the Ask panel — an overlay over the feed, continuity preserved */
.f-surface .f-ask-veil {
  /* z 90 sat UNDER the phone's sticky header (z 96): the card's ✕ rendered beneath the
     banner — Hakim's round-4 report. Above all page chrome now; safe-area keeps the card
     clear of the notch, and env() is 0 on the web so 9vh stands unchanged there. */
  position: fixed; inset: 0; background: var(--f-scrim); z-index: 90;
  display: flex; align-items: flex-start; justify-content: center;
  padding: max(9vh, calc(env(safe-area-inset-top, 0px) + 12px)) 16px 16px;
}
.f-surface .f-ask {
  position: relative; width: 100%; max-width: 600px; max-height: 78vh; overflow-y: auto;
  background: var(--f-panel-2); border: 1px solid var(--f-hair); border-radius: 14px;
  padding: 18px 18px 16px; box-shadow: 0 18px 50px var(--f-shadow-ask);
}
.f-surface .f-ask-x {
  position: absolute; top: 10px; right: 12px; border: 0; background: transparent;
  font-size: 20px; color: var(--f-ink-3); cursor: pointer; line-height: 1;
}
.f-surface .f-ask-ctx {
  font-family: var(--f-serif); font-size: 13.5px; line-height: 1.5; color: var(--f-ink-2);
  padding-right: 26px; margin-bottom: 12px;
}
.f-surface .f-ask-row { display: flex; gap: 8px; }
.f-surface .f-ask-q {
  flex: 1; padding: 9px 12px; font: 13px var(--f-serif); color: var(--f-ink);
  border: 1px solid var(--f-hair); border-radius: 9px; outline: none; background: var(--panel-soft);
}
.f-surface .f-ask-q:focus { border-color: var(--f-accent-soft); }
.f-surface .f-ask-go {
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  border: 1px solid var(--f-accent-soft); background: transparent; color: var(--f-accent);
  border-radius: 9px; padding: 0 16px; cursor: pointer;
}
.f-surface .f-ask-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
.f-surface .f-ask-chips button {
  font-family: var(--f-mono); font-size: 10px; border: 1px solid var(--f-hair);
  background: transparent; color: var(--f-ink-2); border-radius: 999px; padding: 5px 11px;
  cursor: pointer;
}
.f-surface .f-ask-chips button:hover { border-color: var(--f-accent-soft); color: var(--f-accent); }
.f-surface .f-ask-out { margin-top: 14px; }
.f-surface .f-ask-out p {
  font-family: var(--f-serif); font-size: 14px; line-height: 1.58; color: var(--f-ink);
  margin: 0 0 10px;
}
.f-surface .f-ask-out a { color: var(--f-accent); text-decoration: none; }
/* The summary a reader opened from the post (Hakim, 2026-08-18). It sits ABOVE the action
   row, between the card and the verbs, so the thing you asked for appears where you pressed
   rather than at the bottom of the post. */
.f-surface .f-sum {
  position: relative;
  margin: 10px 0 2px; padding: 11px 12px; background: var(--f-chip);
  border: 1px solid var(--f-hair); border-radius: 9px;
}
/* The summary's own ⋯ — see the note beside its handler in feed.js: reporting "this post"
   reports the AGENT'S hook, and a reviewer opening that report would read the wrong text. */
.f-surface .f-sum-more { position: absolute; top: 6px; right: 8px; background: none; border: 0;
  color: var(--f-ink-3); font-size: 17px; line-height: 1; padding: 4px 7px; cursor: pointer; }
.f-surface .f-sum-more:hover { color: var(--f-accent); }
/* Hakim, 2026-08-19: "the disclaimer is too large font, consider smaller font in gray color,
   otherwise it is distracting." It inherited the summary's reading size, so a provenance note
   competed with the text it describes. It is chrome: mono, small, quiet. */
.f-surface .f-sum-lbl { margin-bottom: 7px; padding-right: 30px; font-family: var(--f-mono);
  font-size: 10px; line-height: 1.5; color: var(--f-ink-3); }
/* The summary label was a hover-disclosure: an (i) whose text sat at max-width:0 and expanded
   on hover. Two faults, both reported by Hakim on 2026-08-19. On the PHONE, @media (hover:none)
   sets white-space:normal while the width stays 0, so the sentence wrapped into a column of
   nothing — the "large empty space". On the DESKTOP, expanding it reflowed the row out from
   under the pointer, which dropped the hover, which collapsed it again: the flicker. His call,
   and the right one: "why not make the line short - just say AI summary and that's it." */
.f-surface .f-sum-body { margin: 0; font-size: 14px; line-height: 1.6; color: var(--f-ink-2); }
.f-surface .f-sum-wait, .f-surface .f-sum-plan {
  margin: 0; font-family: var(--f-mono); font-size: 11px; color: var(--f-ink-3);
}
html.phone-chrome .f-surface .f-sum { border-radius: 0; margin-left: -14px; margin-right: -14px; }
.f-surface .f-ask-note {
  font-family: var(--f-mono); font-size: 9.5px; color: var(--f-ink-3); letter-spacing: .03em;
  margin-bottom: 10px;
}
/* Wave 5b: the note rests as an info icon and spreads on hover, focus or tap (Hakim,
   2026-08-18). The feed is its own bundle, so the idiom is declared here too; the shared
   PROPERTY — that the sentence is the button's accessible name, so it is never hover-gated
   for a screen reader — is asserted across both surfaces in tests/ask_disclosure.test.mjs. */
.f-surface .ask-disc { display: inline-flex; align-items: center; max-width: 100%; }
.f-surface .ask-disc-i { display: inline-flex; background: none; border: 0; padding: 0; margin: 0;
  color: inherit; cursor: pointer; line-height: 0; }
.f-surface .ask-disc-i svg { width: 13px; height: 13px; }
.f-surface .ask-disc-t { max-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  transition: max-width .2s ease, margin-left .2s ease; }
.f-surface .ask-disc:hover .ask-disc-t,
.f-surface .ask-disc:focus-within .ask-disc-t,
.f-surface .ask-disc.open .ask-disc-t { max-width: min(62ch, 100%); margin-left: 6px; }
/* Scoped to .open — see the note in index.html. Unscoped, a CLOSED disclosure wrapped one
   word per line inside a zero-width box and reserved a tall band of empty space on every
   touch screen. */
@media (hover: none) {
  /* See the note in index.html: wrapping alone left the opened sentence in a zero-width,
     1128px-tall column, because the text is a flex item that can shrink to 0. */
  .f-surface .ask-disc.open { display: block; }
  .f-surface .ask-disc.open .ask-disc-t { display: block; max-width: none; margin-left: 0;
    white-space: normal; }
}

.f-surface .f-ask-lbl {
  font-family: var(--f-mono); font-size: 9.5px; color: var(--f-accent); letter-spacing: .08em;
  text-transform: uppercase; margin-bottom: 4px;
}
.f-surface .f-ask-anchor {
  border: 1px solid var(--f-hair); border-radius: 10px; background: var(--panel-soft);
  padding: 10px 12px; margin-bottom: 6px;
}
.f-surface .f-ask-srcs {
  border-top: 1px solid var(--f-hair); padding-top: 8px; font-family: var(--f-mono);
  font-size: 10.5px; color: var(--f-ink-2); line-height: 1.7;
}
.f-surface .f-ask-wait { color: var(--f-ink-2); }

/* agent profile */
.f-surface .f-back {
  display: inline-block; font-family: var(--f-mono); font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--f-ink-2); text-decoration: none; margin: 2px 0 12px;
}
.f-surface .f-back:hover { color: var(--f-accent); }
.f-surface .f-agent-card {
  display: flex; gap: 14px; align-items: flex-start; background: var(--f-panel);
  border: 1px solid var(--f-hair); border-radius: 13px; padding: 18px; margin-bottom: 18px;
}
.f-surface .f-av-lg { width: 56px; height: 56px; flex: 0 0 56px; font-size: 17px; }
.f-surface .f-agent-who { flex: 1; min-width: 0; }
.f-surface .f-agent-who .f-nm { font-size: 17px; }
.f-surface .f-agent-bio {
  font-family: var(--f-serif); font-size: 14px; line-height: 1.55; color: var(--f-ink);
  margin: 9px 0 0;
}
.f-surface .f-agent-note { font-size: 12.5px; color: var(--f-ink-2); margin: 7px 0 0; line-height: 1.5; }

@media (max-width: 720px) {
  .f-surface { padding-left: 2px; padding-right: 2px; }
  .f-surface .f-hook { font-size: 16px; }
  .f-surface .f-banner { height: 88px; font-size: 32px; }
  .f-surface .f-acts button { padding: 8px 8px; }
  .f-surface .f-acts button span { display: none; }   /* icons only on a phone */
  .f-surface .f-acts .f-deadline span { display: inline; }
}

/* the one digest preference, shown on the Deadlines view */
.f-surface .f-rem-pref {
  font-family: var(--f-mono); font-size: 10.5px; color: var(--f-ink-2);
  display: flex; align-items: center; gap: 5px; margin: -4px 0 12px;
}
.f-surface .f-rem-pref button {
  font-family: var(--f-mono); font-size: 10.5px; border: 1px solid var(--f-hair);
  background: transparent; color: var(--f-ink-2); border-radius: 6px; padding: 3px 9px; cursor: pointer;
}
.f-surface .f-rem-pref button[aria-pressed="true"] {
  /* the second token-bg / literal-fg pair — see .f-follow[aria-pressed] above (spec R3) */
  background: var(--f-ink); color: var(--on-ink); border-color: var(--f-ink);
}

/* ══ Round 2 — LinkedIn-identical phone cards (Hakim ruling 3, 2026-08-09) ═══════════════
   html.phone-chrome is the page's round-2 gate: the shell at any width, or phone-width web.
   Cards run the full screen width, flat white, separated by a thin light-gray band; the
   action row becomes evenly spaced icon+label buttons (existing handlers, restyle only);
   chrome typography moves to the system stack while the hook and record title keep their
   serif — content, not chrome. The navy/white palette rides the same variables. */
html.phone-chrome .f-surface {
  max-width: none;
  --f-accent: #0d2147; --f-accent-soft: #b9c8dd; --f-ink: #0a1628; --f-ink-2: #3f4c5e;
  --f-ink-3: #64748b; --f-hair: #e5e7eb;
  --f-mono: -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
html.phone-chrome .f-surface { padding-left: 0; padding-right: 0; }
html.phone-chrome .f-surface .f-hd { padding: 0 14px; }
html.phone-chrome .f-surface .f-post {
  border: 0; border-radius: 0; margin: 0;
  /* The LinkedIn band between flat cards. It is the one surface in the file whose direction
     INVERTS: in light it is lighter than the card, in dark it must go BELOW the card's
     lightness or the phone feed reads as a ladder of bright rules (spec R10). */
  border-bottom: 6px solid var(--band);
  padding: 12px 14px 4px;
}
html.phone-chrome .f-surface .f-card {
  /* the phone squares the card to the panel white; the desktop keeps --panel-soft */
  background: var(--f-panel);
  /* the phone's post padding is 14px, not 16 — the break-out has to match the padding it is
     escaping or the media overhangs the screen and the page scrolls sideways (ruling 6). */
  margin-left: -14px; margin-right: -14px;
}
html.phone-chrome .f-surface .f-card > .f-card-hit > *:not(.f-preview-media) {
  padding-left: 14px; padding-right: 14px;
}
html.phone-chrome .f-surface .f-acts {
  justify-content: space-around; gap: 0; flex-wrap: nowrap;
  border-top: 1px solid var(--f-hair); margin-top: 8px; padding-top: 2px;
}
html.phone-chrome .f-surface .f-acts > button {
  flex: 1 1 0; min-width: 0; flex-direction: column; gap: 3px;
  padding: 7px 1px; border: 0; font-size: 9px; letter-spacing: 0;
}
html.phone-chrome .f-surface .f-acts > button span {
  display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; font-size: 9px;
}
html.phone-chrome .f-surface .f-acts svg { width: 19px; height: 19px; flex: 0 0 19px; }
/* Hakim, 2026-08-19: "the three dots in the post, when opened on the phone, the icon is too
   large." ICON.dots is fill:currentColor — three solid discs — while every verb beside it is a
   1.5-stroke outline. At an identical 19px the filled mark carries far more ink and reads as
   the biggest thing in the row. It is the one glyph in the set that needs to be smaller to
   look the same size. */
html.phone-chrome .f-surface .f-acts [data-menu] svg { width: 15px; height: 15px; flex: 0 0 15px; }
html.phone-chrome .f-surface .f-acts [data-menu] { margin-left: 0; flex: 0 1 34px; }
html.phone-chrome .f-surface .f-agent-hd { padding: 0 14px; }
html.phone-chrome .f-surface .f-wall,
html.phone-chrome .f-surface .f-empty { margin-left: 14px; margin-right: 14px; }
/* agent names and metas are chrome — system stack; .f-hook and .f-ttl stay serif (content) */
html.phone-chrome .f-surface .f-nm { font-family: var(--f-mono); }
/* round 4 (Hakim): the Follow/Following pill was too large — compact, LinkedIn-weight */
html.phone-chrome .f-surface .f-follow { padding: 3px 10px; font-size: 9px; letter-spacing: .05em; }
/* R-DESKTOP (round 9): the veil's z-index rise to clear the phone's sticky header is a
   phone concern — main shipped 90 and desktop has nothing layered between. */
html.phone-chrome .f-surface .f-ask-veil { z-index: 9300; }
/* round-5 addendum: the saved state is Vellum blue, not gold — navy reads on white where
   #6fb3ff (2.2:1) does not; the light blue stays the on-navy highlight */
html.phone-chrome .f-surface .f-acts [data-save][aria-pressed="true"] { color: var(--f-accent); }
/* round 6 (ruling 12): the verified line joins the palette — navy, not green; the ✓ stays */
html.phone-chrome .f-surface .f-vf { color: var(--f-accent); background: var(--wash-navy-2); }

/* ══ DARK ═════════════════════════════════════════════════════════════════════════════════
   Appearance (Hakim, 2026-08-29). Three states, two blocks, one set of values.

   WHY THE SELECTORS LOOK LIKE THIS. The attribute lives on <html>, not on .f-surface, so the
   guards are ancestor selectors. The specificity ladder is the whole trick and it is not
   optional:

     .f-surface                                          (0,1,0)  light, desktop
     html.phone-chrome .f-surface                        (0,2,1)  light, phone
     html:not([data-theme="light"]) .f-surface           (0,2,1)  dark, desktop
     html.phone-chrome:not([data-theme="light"]) .f-s.   (0,3,1)  dark, phone
     html[data-theme="dark"] .f-surface                  (0,2,1)  dark, desktop, explicit
     html.phone-chrome[data-theme="dark"] .f-surface     (0,3,1)  dark, phone, explicit

   The desktop dark guard TIES with the phone light block at (0,2,1), so these blocks must sit
   at the END of the file — after every light declaration — or the phone keeps its light tokens
   on a dark ground and only half the shell flips (spec R1, trap 3). The phone dark blocks then
   restate the six tokens the phone light block declares, at (0,3,1), so the precedence holds
   however the file is later reordered.

   `:not([data-theme="light"])` and not a bare media query: absence of the attribute IS the
   System state, so an explicit Light must be able to beat a dark OS. Never write
   data-theme="system".

   The two dark sets below are written twice, deliberately and identically — @media cannot be
   made conditional on an attribute. A guard test diffs them; keep them byte-for-byte equal.

   THE GROUND IS THE BRAND NAVY (Hakim override 1), not the palette spec's #10131a charcoal:
   panel #132a55, elevated #1a3465, chip #1f3a6b, band #091a38, ink #eef1f6 / #c3ccda / #a2b1c4,
   accent #8ab7f0, soft #7099c3. --f-ink-3 and --f-accent-soft take the corrected values
   (#a2b1c4, #7099c3); the spec's #98a5b8 and #55779f measured 4.48 and 2.81 on the navy chip
   and failed AA and the 3:1 UI minimum respectively.
   ════════════════════════════════════════════════════════════════════════════════════════ */

@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) .f-surface {
    --f-accent: #8ab7f0; --f-accent-soft: #7099c3; --f-ink: #eef1f6; --f-ink-2: #c3ccda;
    --f-ink-3: #a2b1c4; --f-panel: #132a55; --f-hair: rgba(233,240,255,.13);
    --f-panel-2: #1a3465;
    --f-chip: #1f3a6b;
    --f-scrim: rgba(2,4,8,.66);
    /* Every shadow in this file was navy-alpha, and navy-alpha over a navy ground is nothing
       at all. True black at ~3× the light alpha, and the elevated surfaces keep their
       --f-hair border — on dark it is the border that carries elevation (spec R7). */
    --f-shadow-menu: rgba(0,0,0,.55); --f-shadow-ask: rgba(0,0,0,.62);
    --panel-soft: #132a55;             /* there is no dark equivalent of "1% warmer than
                                          white"; the off-white well converges on the panel */
    --chip: #1f3a6b;
    --hover: #1f3a6b;                  /* LIFTS off both the panel (1.26) and the elevated
                                          menu ground (1.09) — light mode's own hover is 1.07
                                          on white, so this is at least as legible as today.
                                          It is NOT the spec's #242a34: that was computed
                                          against the charcoal panel and is fractionally
                                          DARKER than the navy panel, i.e. exactly the hole
                                          spec R13 warns about. */
    --band: #091a38;                   /* below the panel, not above it (spec R10) */
    --on-accent: #0b1220;
    --on-ink: #0b1220;                 /* --f-ink is near-white in dark; the pressed pill's
                                          label has to be the dark ink. 16.5:1. */
    --on-identity: #fff;               /* declared, and deliberately unchanged: the discs'
                                          hues are generated, so their initials never flip */
    --avatar-fallback: #66758f;
    --wash-navy: rgba(233,240,255,.05); --wash-navy-2: rgba(233,240,255,.045);
    --wash-hover: rgba(138,183,240,.10);
    --brass-ink: #d9b169; --brass-bg: rgba(217,177,105,.12);
    --brass-bd: rgba(217,177,105,.42); --brass-bd-2: rgba(217,177,105,.46);
    --brass-tint: rgba(217,177,105,.09);
  }
  html.phone-chrome:not([data-theme="light"]) .f-surface {
    --f-accent: #8ab7f0; --f-accent-soft: #7099c3; --f-ink: #eef1f6; --f-ink-2: #c3ccda;
    --f-ink-3: #a2b1c4;
    /* the phone hairline is the one that cannot self-adapt: #e5e7eb is OPAQUE, so darkening
       the panel alone leaves a bright grey rule on every card (spec R11) */
    --f-hair: rgba(233,240,255,.13);
  }
  /* Identity discs keep their generated hue — a lightness lift would rescue 13 discs from the
     ground and destroy the white initials on 10 of them, and the hue set is not enumerable
     anyway (publisherMark hashes an arbitrary host). A ring gives the disc its SHAPE back at
     zero cost to the initials (spec §4.2). */
  html:not([data-theme="light"]) .f-surface .f-av,
  html:not([data-theme="light"]) .f-surface .f-logo {
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.40);
  }
}

html[data-theme="dark"] .f-surface {
  --f-accent: #8ab7f0; --f-accent-soft: #7099c3; --f-ink: #eef1f6; --f-ink-2: #c3ccda;
  --f-ink-3: #a2b1c4; --f-panel: #132a55; --f-hair: rgba(233,240,255,.13);
  --f-panel-2: #1a3465;
  --f-chip: #1f3a6b;
  --f-scrim: rgba(2,4,8,.66);
  --f-shadow-menu: rgba(0,0,0,.55); --f-shadow-ask: rgba(0,0,0,.62);
  --panel-soft: #132a55;
  --chip: #1f3a6b;
  --hover: #1f3a6b;
  --band: #091a38;
  --on-accent: #0b1220;
  --on-ink: #0b1220;
  --on-identity: #fff;
  --avatar-fallback: #66758f;
  --wash-navy: rgba(233,240,255,.05); --wash-navy-2: rgba(233,240,255,.045);
  --wash-hover: rgba(138,183,240,.10);
  --brass-ink: #d9b169; --brass-bg: rgba(217,177,105,.12);
  --brass-bd: rgba(217,177,105,.42); --brass-bd-2: rgba(217,177,105,.46);
  --brass-tint: rgba(217,177,105,.09);
}
html.phone-chrome[data-theme="dark"] .f-surface {
  --f-accent: #8ab7f0; --f-accent-soft: #7099c3; --f-ink: #eef1f6; --f-ink-2: #c3ccda;
  --f-ink-3: #a2b1c4;
  --f-hair: rgba(233,240,255,.13);
}
html[data-theme="dark"] .f-surface .f-av,
html[data-theme="dark"] .f-surface .f-logo {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.40);
}
