/* V2 experimental UI — app-shell layout, dark/light themes.
   Color tokens are the dataviz reference palette (pre-validated). */

:root, :root[data-theme="light"] {
  --plane: #f9f9f7;         /* page plane */
  --surface: #fcfcfb;       /* cards, panels */
  --surface-2: #f0efec;     /* recessed wells */
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --muted: #6f6d66;        /* WCAG AA on --surface (5.04:1; was #898781 = 3.41:1) */
  --hairline: #e1e0d9;
  --ring: rgba(11,11,11,.10);
  --accent: #2876d2;        /* categorical slot 1 (light); AA white-on-accent 4.55:1 */
  --on-accent: #ffffff;     /* text drawn ON --accent */
  --accent-ink: #1c5cab;
  --good: #0ca30c; --good-ink: #006300;
  --warn: #fab219; --serious: #ec835a; --critical: #d03b3b;
  --pill-pass-bg: #e3f4e8; --pill-pass-ink: #116632;
  --pill-fail-bg: #fbe7e5; --pill-fail-ink: #a02219;
  --pill-defer-bg: #fdf3dc; --pill-defer-ink: #8a6410;
  --pill-pull-bg: #fbe6d4; --pill-pull-ink: #8f5410;  /* AA 5.04:1 (was #9a5b12 = 4.48) */
  --mark: #ffe58a;
  --shadow: 0 1px 2px rgba(0,0,0,.05), 0 8px 24px -12px rgba(0,0,0,.12);
}
:root[data-theme="dark"] {
  --plane: #0d0d0d;
  --surface: #1a1a19;
  --surface-2: #232322;
  --ink: #ffffff;
  --ink-2: #c3c2b7;
  --muted: #9c9990;        /* WCAG AA on dark --surface (6.11:1) */
  --hairline: #2c2c2a;
  --ring: rgba(255,255,255,.10);
  --accent: #3987e5;        /* categorical slot 1 (dark) — kept bright (4.79:1 vs surface) */
  --on-accent: #0b0b0b;     /* near-black ON the bright accent = 5.41:1 (white was 3.64) */
  --accent-ink: #86b6ef;
  --good: #0ca30c; --good-ink: #0ca30c;
  --pill-pass-bg: #12351d; --pill-pass-ink: #7fd89a;
  --pill-fail-bg: #3c1715; --pill-fail-ink: #f1a09a;
  --pill-defer-bg: #38300f; --pill-defer-ink: #ecd27f;
  --pill-pull-bg: #3a2610; --pill-pull-ink: #f0b978;
  --mark: #6b5a13;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px -12px rgba(0,0,0,.5);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; background: var(--plane); color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif; }
a { color: var(--accent-ink); }
button { font: inherit; }

/* 2.4.7 Focus Visible — there was no designed focus indicator outside .input,
   so keyboard users lost their place. :focus-visible keeps it off mouse clicks. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;
  border-radius: 6px; }
/* The item/section flash and player cue animations can be unpleasant for
   vestibular sensitivity — honour the OS setting. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
/* Skip link (2.4.1) — visible only once focused. */
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--on-accent); padding: .55rem .9rem;
  border-radius: 0 0 8px 0; font-weight: 650; text-decoration: none; }
.skip-link:focus { left: 0; }
/* Visually hidden but available to assistive tech (route announcements). */
.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; }
kbd { font: 600 .7rem/1 system-ui; background: var(--surface-2); color: var(--ink-2);
  border: 1px solid var(--hairline); border-bottom-width: 2px; border-radius: 5px;
  padding: .15rem .35rem; }

/* ---------- shell ---------- */
.shell { display: flex; min-height: 100vh; }
.side { width: 232px; flex: 0 0 232px; position: sticky; top: 0; height: 100vh;
  height: 100dvh;  /* dvh accounts for iOS Safari's toolbars so the footer isn't hidden */
  overflow-y: auto;  /* scroll to the footer (Theme/Classic/Admin) if it overflows */
  display: flex; flex-direction: column; gap: .4rem; padding: 1rem .8rem;
  padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  background: var(--surface); border-right: 1px solid var(--hairline); }
.brand { display: flex; gap: .6rem; align-items: center; padding: .2rem .4rem 1rem; }
.brand-mark { width: 38px; height: 38px; border-radius: 10px; background: var(--accent);
  color: var(--on-accent); font-weight: 800; display: grid; place-items: center; letter-spacing: .5px; }
.brand-text b { display: block; font-size: .95rem; }
.brand-text span { font-size: .72rem; color: var(--muted); }
.nav { display: flex; flex-direction: column; gap: .15rem; }
.nav a { display: flex; align-items: center; gap: .6rem; padding: .5rem .65rem;
  border-radius: 8px; text-decoration: none; color: var(--ink-2); font-weight: 550; }
.nav a:hover { background: var(--surface-2); color: var(--ink); }
.nav a.on { background: var(--accent); color: var(--on-accent); }
.nav a.on .ni { color: var(--on-accent); }
.ni { width: 1.2em; text-align: center; color: var(--muted); font-style: normal; }
.nav-sec { font-size: .66rem; font-weight: 800; letter-spacing: .1em; color: var(--muted);
  text-transform: uppercase; padding: .9rem .65rem .25rem; }
.palette-hint { margin-top: .6rem; display: flex; gap: .55rem; align-items: center;
  background: var(--surface-2); color: var(--muted); border: 1px dashed var(--hairline);
  border-radius: 8px; padding: .5rem .65rem; cursor: pointer; text-align: left; }
.palette-hint:hover { color: var(--ink-2); }
.side-foot { margin-top: auto; display: flex; flex-direction: column; gap: .4rem;
  font-size: .8rem; }
.side-foot button, .side-foot a { background: none; border: 1px solid var(--hairline);
  color: var(--ink-2); border-radius: 8px; padding: .4rem .6rem; cursor: pointer;
  text-decoration: none; text-align: center; }
.side-foot button:hover, .side-foot a:hover { background: var(--surface-2); }
.admin-link { font-size: .72rem !important; color: var(--muted) !important;
  border-style: dashed !important; }

/* ---------- admin ---------- */
.adm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: .9rem; margin-bottom: 1.2rem; }
.adm-progress { height: 10px; border-radius: 999px; background: var(--surface-2);
  overflow: hidden; margin-top: .5rem; }
.adm-progress > i { display: block; height: 100%; background: var(--accent); }
.adm-job { display: flex; justify-content: space-between; align-items: baseline;
  padding: .55rem .2rem; border-bottom: 1px solid var(--hairline); font-size: .88rem; }
.adm-job:last-child { border-bottom: 0; }
.adm-job .when { color: var(--muted); font-size: .78rem; }
.adm-log { font-size: .82rem; }
.adm-log .row { display: flex; gap: .7rem; align-items: baseline; padding: .4rem .2rem;
  border-bottom: 1px solid var(--hairline); }
.adm-log .row:last-child { border-bottom: 0; }
.adm-log .st { font-weight: 650; }
.adm-log .st.ok { color: var(--good-ink); }
.adm-log .st.bad { color: var(--pill-fail-ink); }
/* Document matches shown INSIDE their agenda item, rather than in a separate
   "Found inside documents" section. Indented and hairline-led so it reads as
   evidence supporting the item above it, not as a result of its own. */
.doc-hits { margin-top: .45rem; border-left: 2px solid var(--hairline);
  padding-left: .7rem; display: flex; flex-direction: column; gap: .45rem; }
.doc-hit a { font-size: .82rem; font-weight: 600; }
.doc-hit .snip { margin-top: .1rem; }

/* Collapsible admin section. The migration log grows without bound and was
   pushing everything below it — including the visit numbers — off the bottom
   of a phone screen. Open on desktop where there's room, shut on mobile. */
.adm-fold > summary { cursor: pointer; color: var(--ink-2); font-weight: 650;
  font-size: .84rem; padding: .35rem 0; list-style: none; }
.adm-fold > summary::-webkit-details-marker { display: none; }
.adm-fold > summary::before { content: "▸ "; color: var(--muted); }
.adm-fold[open] > summary::before { content: "▾ "; }
.adm-fold > summary:hover { color: var(--ink); }
/* Cap the height so even expanded it can't run away with the page. */
.adm-fold .adm-log { max-height: 22rem; overflow-y: auto; }

/* Attendance table (Insights) */
.att-tbl { display: flex; flex-direction: column; gap: .35rem; }
.att-row { display: grid; grid-template-columns: 8.5rem 1fr 3.2rem auto;
  gap: .6rem; align-items: center; font-size: .86rem; }
.att-name { font-weight: 650; }
.att-bar { background: var(--surface-2); border-radius: 999px; height: 8px; overflow: hidden; }
.att-bar > i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.att-pct { font-variant-numeric: tabular-nums; text-align: right; font-weight: 650; }
.att-n { white-space: nowrap; font-size: .76rem; }
@media (max-width: 720px) {
  .att-row { grid-template-columns: 6.5rem 1fr 3rem; }
  .att-n { display: none; }
}
/* Visits sparkline (admin) */
.vspark { display: flex; align-items: flex-end; gap: 2px; height: 30px; }
.vspark .vbar { flex: 1 1 auto; min-width: 2px; background: var(--accent);
  border-radius: 2px 2px 0 0; opacity: .85; }
.vspark .vbar:hover { opacity: 1; }

/* Roll-call test bench (admin) */
.bench-ta { font-family: ui-monospace, monospace; font-size: .8rem; line-height: 1.45;
  resize: vertical; min-height: 7rem; }
.bench-bar { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin: .6rem 0; }
.bench-out { margin-top: .4rem; }
.bench-motion { border: 1px solid var(--hairline); border-radius: 10px;
  padding: .6rem .75rem; margin-bottom: .5rem; background: var(--surface-2); }
.bench-mh { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-bottom: .4rem; }
.bench-votes { display: flex; gap: .35rem; flex-wrap: wrap; }
.bench-note { background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  border-radius: 8px; padding: .5rem .7rem; font-size: .82rem; line-height: 1.45;
  margin-top: .4rem; }
.bench-cfg { margin-top: .6rem; font-size: .8rem; }
.bench-cfg summary { cursor: pointer; color: var(--muted); }
.bench-cfg pre { overflow-x: auto; background: var(--surface-2); border-radius: 8px;
  padding: .6rem .7rem; font-size: .74rem; line-height: 1.4; margin-top: .4rem; }
.adm-warn { background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  border-radius: 10px; padding: .6rem .9rem; font-size: .85rem; margin-bottom: 1rem; }
.adm-live { border-color: color-mix(in srgb, var(--good) 45%, var(--hairline)) !important;
  background: color-mix(in srgb, var(--good) 7%, var(--surface)); margin-bottom: 1rem; }
.adm-live h2 { color: var(--good-ink); }
.health { border-radius: 8px; padding: .45rem .7rem; font-size: .85rem; font-weight: 600;
  margin-bottom: .7rem; }
.health.h-ok, .health.h-done { background: var(--pill-pass-bg); color: var(--pill-pass-ink); }
.health.h-warn { background: var(--pill-defer-bg); color: var(--pill-defer-ink); }
.log-reason { font-size: .76rem; color: var(--pill-fail-ink); background: var(--pill-fail-bg);
  border-radius: 6px; padding: .3rem .6rem; margin: .2rem 0 .3rem 1.6rem;
  font-family: ui-monospace, monospace; word-break: break-word; }
.row-wrap { border-bottom: 1px solid var(--hairline); }
.row-wrap:last-child { border-bottom: 0; }
.row-wrap .row { border-bottom: 0 !important; }
.adm-log .log-row { cursor: pointer; }
.adm-log .log-row .log-meet { color: var(--ink-2); }
.adm-log .caret { color: var(--muted); font-size: .7rem; margin-left: .3rem; }
.log-detail { font-size: .8rem; padding: .3rem .2rem .6rem 1.6rem; display: grid; gap: .15rem; }
.log-detail b { color: var(--muted); font-weight: 600; margin-right: .3rem; }

/* server stats */
.srv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: .7rem; }
.srv-stat { background: var(--surface-2); border-radius: 10px; padding: .6rem .7rem; }
.srv-label { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.srv-val { font-size: 1.35rem; font-weight: 750; margin: .1rem 0; }
.srv-val.warn { color: var(--pill-fail-ink); }
.srv-sub { font-size: .74rem; color: var(--muted); }

/* recent activity (uploads + mirrored docs) */
.act-list { display: flex; flex-direction: column; }
.act-row { display: flex; gap: .6rem; align-items: baseline; padding: .5rem .2rem;
  border-bottom: 1px solid var(--hairline); font-size: .84rem; }
.act-row:last-child { border-bottom: 0; }
.act-ic { flex: 0 0 auto; }
.act-main { flex: 1 1 auto; min-width: 0; }
.act-main a { text-decoration: none; }
.act-main a b { font-weight: 650; }
.act-sub { font-size: .76rem; color: var(--muted); margin-top: .1rem;
  overflow: hidden; text-overflow: ellipsis; }
.act-row .d { flex: 0 0 auto; color: var(--muted); font-size: .76rem; white-space: nowrap; }
.act-row .when { color: var(--muted); font-size: .76rem; }
.adm-more { margin-top: .7rem; font-size: .82rem; }

/* password change */
.pw-form { display: flex; flex-direction: column; gap: .6rem; max-width: 380px; }
.pw-msg { font-size: .84rem; min-height: 1em; }
.pw-msg.ok { color: var(--good-ink); }
.pw-msg.err { color: var(--pill-fail-ink); }
.pw-note { font-size: .78rem; color: var(--pill-defer-ink); background: var(--pill-defer-bg);
  border-radius: 8px; padding: .5rem .7rem; }

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* Unofficial-site notice, home page only. A sticky banner in the header was
   tried and rejected — it ate ~32px of every screen on every route to say
   something you only need to read once. */
.home-note { display: flex; align-items: flex-start; gap: .55rem;
  margin: 0 0 1.1rem; padding: .6rem .85rem; border-radius: 10px;
  background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  border: 1px solid color-mix(in srgb, var(--pill-defer-ink) 26%, transparent);
  font-size: .82rem; line-height: 1.45; }
.home-note .hn-ico { flex: 0 0 auto; font-size: .95rem; line-height: 1.3; }
.topbar { position: sticky; top: 0; z-index: 30; display: flex; align-items: center;
  gap: .9rem; padding: .65rem 1.2rem; background:
  color-mix(in srgb, var(--plane) 82%, transparent); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hairline); }
.burger { display: none; background: none; border: 0; color: var(--ink); font-size: 1.2rem;
  cursor: pointer; }
.crumb { font-weight: 650; font-size: .95rem; flex: 1; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.topsearch { display: flex; align-items: center; gap: .5rem; background: var(--surface);
  border: 1px solid var(--hairline); color: var(--muted); border-radius: 999px;
  padding: .35rem .5rem .35rem .8rem; cursor: pointer; }
.topsearch:hover { border-color: var(--accent); }
.view { padding: 1.4rem clamp(1rem, 3vw, 2.2rem) 4rem; max-width: 1180px; width: 100%;
  margin: 0 auto; animation: fadein .18s ease; }
/* Opacity ONLY — a transform here would make .view a containing block for
   position:fixed, which re-anchors the floating mini-player to <main>
   instead of the viewport (mobile bar landed 56px low; desktop bottom-right
   clamping broke too). Never add transform/filter/perspective to .view. */
@keyframes fadein { from { opacity: 0; } }

/* ---------- shared bits ---------- */
h1.pagetitle { font-size: 1.5rem; margin: .2rem 0 .3rem; letter-spacing: -.01em; }
.sub { color: var(--muted); font-size: .88rem; margin: 0 0 1.2rem; }
.card { background: var(--surface); border: 1px solid var(--hairline);
  border-radius: 14px; box-shadow: var(--shadow); }
.chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.chip { border: 1px solid var(--hairline); background: var(--surface); color: var(--ink-2);
  border-radius: 999px; padding: .28rem .75rem; font-size: .8rem; cursor: pointer; }
.chip:hover { border-color: var(--accent); color: var(--ink); }
.chip.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* Toggle chip. A filled chip alone can't say whether the fill means "active"
   or just "this is a button" — you had to click once and watch it change to
   learn which. A switch reads as a switch BEFORE the first click, and shows
   its state at rest, which was the actual problem. */
.chip.toggle { display: inline-flex; align-items: center; gap: .45rem; padding-left: .5rem; }
.chip.toggle .tgl { flex: 0 0 auto; width: 26px; height: 15px; border-radius: 999px;
  position: relative; transition: background .16s ease;
  background: color-mix(in srgb, var(--ink-2) 38%, transparent); }
.chip.toggle .tgl::after { content: ""; position: absolute; top: 2px; left: 2px;
  width: 11px; height: 11px; border-radius: 50%; background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35);
  transition: transform .16s cubic-bezier(.4, 0, .2, 1); }
.chip.toggle.on .tgl { background: color-mix(in srgb, var(--on-accent) 55%, transparent); }
.chip.toggle.on .tgl::after { transform: translateX(11px); background: var(--on-accent); }
@media (prefers-reduced-motion: reduce) {
  .chip.toggle .tgl, .chip.toggle .tgl::after { transition: none; }
}
.input { background: var(--surface); border: 1px solid var(--hairline); color: var(--ink);
  border-radius: 10px; padding: .55rem .8rem; font: inherit; width: 100%; }
.input:focus { outline: 2px solid var(--accent); border-color: transparent; }
select.input { width: auto; }
/* color:inherit let muted text sit on the highlight at 4.15:1. --ink is
   near-black in light (15.8:1 on the yellow) and white in dark (6.8:1). */
mark { background: var(--mark); color: var(--ink); border-radius: 3px; padding: 0 .12em; }
.skeleton { height: 90px; border-radius: 14px; background:
  linear-gradient(100deg, var(--surface) 40%, var(--surface-2) 50%, var(--surface) 60%);
  background-size: 200% 100%; animation: sk 1.1s infinite linear; }
@keyframes sk { to { background-position: -200% 0; } }
.empty { text-align: center; color: var(--muted); padding: 3rem 1rem; }

/* pills */
.pill { display: inline-flex; align-items: center; gap: .3em; font-size: .7rem;
  font-weight: 650; border-radius: 5px; padding: .12rem .5rem; white-space: nowrap;
  vertical-align: middle; }
/* A pill that HAS an explanation becomes a <button>, but it must LOOK exactly
   like a plain pill at rest — a permanent dotted underline was tried and read
   as visual noise across a 40-item agenda. So the cue is hover-only: the
   underline and the help cursor appear when a mouse is over it, and the resting
   state is indistinguishable from a pill with nothing to say.
   Trade-off, stated plainly: touch has no hover, so on a phone the pills carry
   no at-rest hint that tapping does anything. Tapping still works. */
.pill.has-gloss { border: 0; font: inherit; font-size: .7rem; font-weight: 650;
  cursor: help; -webkit-appearance: none; appearance: none;
  text-decoration: none; }
.pill.has-gloss:hover, .pill.has-gloss[aria-expanded="true"] {
  text-decoration: underline dotted currentColor 1px;
  text-underline-offset: 2px; text-decoration-skip-ink: none; }
.pill.has-gloss:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.gloss-pop { position: fixed; z-index: 300; max-width: min(320px, calc(100vw - 16px));
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--hairline); border-radius: 10px;
  padding: .55rem .7rem; font-size: .8rem; line-height: 1.45; font-weight: 400;
  box-shadow: 0 12px 34px rgba(0, 0, 0, .28); }
.gloss-pop[hidden] { display: none; }
.pill.type { background: var(--surface-2); color: var(--ink-2); }
.pill.t-info { background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  color: var(--accent-ink); }
.pill.t-ceremonial { background: color-mix(in srgb, #9085e9 18%, var(--surface));
  color: color-mix(in srgb, #9085e9 70%, var(--ink)); }
.pill.t-civic { background: var(--pill-pass-bg); color: var(--pill-pass-ink); }
.pill.consent-passed { background: var(--pill-pass-bg); color: var(--pill-pass-ink); }
.pill.consent-pulled { background: var(--pill-pull-bg); color: var(--pill-pull-ink); }
.pill.consent-proposed { background: var(--surface-2); color: var(--muted); }
.pill.act-pass { background: var(--pill-pass-bg); color: var(--pill-pass-ink); }
.pill.act-fail { background: var(--pill-fail-bg); color: var(--pill-fail-ink); }
.pill.act-defer { background: var(--pill-defer-bg); color: var(--pill-defer-ink); }
.pill.act-neutral { background: var(--surface-2); color: var(--ink-2); }
.pill.prov { background: var(--surface-2); color: var(--muted); font-weight: 500; }
.tstamp { font-size: .74rem; font-weight: 650; color: var(--accent-ink);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border-radius: 5px; padding: .12rem .5rem; text-decoration: none; white-space: nowrap; }
.tstamp:hover { background: color-mix(in srgb, var(--accent) 24%, var(--surface)); }

/* vote chips */
.votes-row { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .35rem; }
.vchip { font-size: .74rem; font-weight: 600; border-radius: 6px; padding: .2rem .55rem;
  border: 1px solid transparent; }
.vchip.aye { background: var(--pill-pass-bg); color: var(--pill-pass-ink); }
.vchip.nay { background: var(--pill-fail-bg); color: var(--pill-fail-ink); }
.vchip.absent, .vchip.abstain { background: var(--pill-defer-bg); color: var(--pill-defer-ink); }
.mlabel { font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); }

/* ---------- dashboard ---------- */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: .9rem; margin-bottom: 1.4rem; }
.tile { padding: 1rem 1.1rem; }
.tile .t-num { font-size: 1.9rem; font-weight: 750; letter-spacing: -.02em; line-height: 1.1; }
.tile .t-cap { color: var(--muted); font-size: .8rem; margin-top: .2rem; }
.dash-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: .9rem; }
@media (max-width: 900px) { .dash-grid { grid-template-columns: 1fr; } }
.panel { padding: 1rem 1.1rem; }
.panel h2 { margin: 0 0 .7rem; font-size: .95rem; }
.chart-wrap { position: relative; }
.chart-tip { position: absolute; pointer-events: none; background: var(--ink);
  color: var(--plane); font-size: .75rem; font-weight: 600; border-radius: 6px;
  padding: .25rem .55rem; transform: translate(-50%, -130%); white-space: nowrap;
  opacity: 0; transition: opacity .1s; }
.mini-list { display: flex; flex-direction: column; }
.mini-list a { display: flex; justify-content: space-between; gap: .8rem;
  align-items: baseline; padding: .5rem .2rem; text-decoration: none; color: var(--ink);
  border-bottom: 1px solid var(--hairline); font-size: .88rem; }
.mini-list a:last-child { border-bottom: 0; }
.mini-list a:hover { color: var(--accent-ink); }
.mini-list .d { color: var(--muted); font-size: .78rem; white-space: nowrap; }

/* ---------- home (public-first) ---------- */
.hero { padding: 1.3rem 1.4rem; border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--hairline));
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 9%, var(--surface)),
              var(--surface) 60%); }
.hero .h-kicker { font-size: .72rem; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent-ink); display: flex; gap: .5rem;
  align-items: center; }
.hero .h-live { background: var(--critical); color: #fff; border-radius: 5px;
  padding: .1rem .5rem; letter-spacing: .04em; }
.hero .h-date { font-size: 1.55rem; font-weight: 800; letter-spacing: -.02em;
  margin: .25rem 0 .1rem; }
.hero .h-sub { color: var(--ink-2); font-size: .92rem; }
.hero .h-actions { display: flex; flex-wrap: wrap; gap: .55rem; margin-top: .95rem; }
.h-pending { font-size: .88rem; color: var(--muted); line-height: 1.4; max-width: 46ch; }
.btn { display: inline-flex; align-items: center; gap: .45rem; border-radius: 9px;
  padding: .5rem .95rem; font-weight: 650; font-size: .88rem; cursor: pointer;
  text-decoration: none; border: 1px solid var(--hairline); background: var(--surface);
  color: var(--ink); }
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:hover { filter: brightness(1.08); }
/* "not yet downloaded/migrated" placeholder — where the link WILL live once
   the resource is mirrored; amber + dashed border + no pointer = not a link */
.btn.pend { background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  border: 1px dashed var(--pill-defer-ink); cursor: default; opacity: .85; }
.btn.pend:hover { border-color: var(--pill-defer-ink); }
.pill.pend { background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  border: 1px dashed color-mix(in srgb, var(--pill-defer-ink) 50%, transparent); }
.att-pend { color: var(--muted); cursor: default; }
.home-search { display: flex; gap: .6rem; margin: 1.3rem 0; }
.home-search .input { font-size: 1rem; padding: .7rem 1rem; }
.home-h { font-size: 1.02rem; font-weight: 750; margin: 1.5rem 0 .6rem; }
.home-h small { color: var(--muted); font-weight: 500; margin-left: .5rem; }
.strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: .7rem; }
.tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: .7rem; margin-top: .4rem; }
.tool { padding: .85rem 1rem; text-decoration: none; color: var(--ink); display: block; }
.tool:hover { border-color: var(--accent); }
.tool b { display: flex; gap: .5rem; align-items: center; }
.tool span { color: var(--muted); font-size: .8rem; display: block; margin-top: .2rem; }

/* ---------- meetings list ---------- */
/* Meetings browse facets — chips, not dropdowns (counts stay visible). */
.facets { display: grid; gap: .45rem; margin: 0 0 1.1rem; }
.facet { display: grid; grid-template-columns: 3.2rem 1fr; gap: .6rem;
  align-items: start; }
.facet-l { font-size: .72rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); padding-top: .42rem; }
.chip-n { font-size: .72rem; opacity: .6; font-variant-numeric: tabular-nums;
  margin-left: .1rem; }
.chip.on .chip-n { opacity: .85; }
.facet-count { font-size: .78rem; color: var(--muted); padding-left: 3.8rem; }
.facet-count b { color: var(--ink-2); font-weight: 700; }
@media (max-width: 620px) {
  .facet { grid-template-columns: 1fr; gap: .2rem; }
  .facet-l { padding-top: 0; }
  .facet-count { padding-left: 0; }
}
.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1rem;
  align-items: center; }
.mgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
  gap: .8rem; }
.mcard { padding: .95rem 1.05rem; text-decoration: none; color: var(--ink);
  display: flex; flex-direction: column; gap: .45rem;
  transition: transform .12s ease, border-color .12s ease; }
.mcard:hover { transform: translateY(-2px); border-color: var(--accent); }
.mcard .m-date { font-weight: 750; font-size: 1.02rem; letter-spacing: -.01em; }
.mcard .m-body { color: var(--muted); font-size: .78rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.mcard .m-meta { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: auto; }
.more-card { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .2rem; padding: .95rem 1.05rem; border-style: dashed; box-shadow: none;
  background: none; color: var(--ink-2); font: inherit; font-weight: 700;
  cursor: pointer; text-decoration: none; text-align: center;
  transition: transform .12s ease, border-color .12s ease, color .12s ease; }
.more-card:hover { transform: translateY(-2px); border-color: var(--accent); color: var(--ink); }
.more-card small { color: var(--muted); font-weight: 500; font-size: .75rem; }
.yearhead { grid-column: 1/-1; font-size: .85rem; font-weight: 800; color: var(--muted);
  text-transform: uppercase; letter-spacing: .08em; margin: .6rem 0 -.2rem; }

/* ---------- meeting detail ---------- */
.mt-head { margin-bottom: 1rem; }
.mt-meta { color: var(--muted); font-size: .85rem; display: flex; flex-wrap: wrap;
  gap: .4rem 1rem; align-items: center; }
.mt-layout { display: grid; grid-template-columns: 205px 1fr; gap: 1.2rem;
  align-items: start; }
@media (max-width: 900px) { .mt-layout { grid-template-columns: 1fr; }
  .outline { display: none; } }
.outline { position: sticky; top: 64px; font-size: .82rem; max-height: calc(100vh - 90px);
  overflow: auto; padding: .6rem; }
.outline a { display: block; padding: .3rem .5rem; border-radius: 6px; color: var(--ink-2);
  text-decoration: none; border-left: 2px solid transparent; }
.outline a:hover { background: var(--surface-2); }
.outline a.here { color: var(--accent-ink); border-left-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent); }
.player-dock { margin: .9rem 0; }
.player-bar { display: flex; align-items: center; justify-content: space-between;
  gap: .6rem; margin-bottom: .45rem; flex-wrap: wrap; }
.pmode { display: flex; gap: .3rem; }
.pmode .chip.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.player-frame { position: relative; }
/* small: modest inline size; large: fill the column, capped so the video
   never exceeds ~70% of the viewport height */
/* Fill the column, capped so the video never exceeds ~70% of viewport height.
   This was the "large" half of a size toggle whose "small" half (max-width:
   640px) computed to the identical width on any phone — the column is narrower
   than both caps — so the button appeared to do nothing there. Toggle dropped;
   this is now simply how the inline player sizes. */
.player-dock .player-frame { max-width: min(100%, calc(70vh * 16 / 9)); }
/* "keep in view" detached state — a small window: our title bar on top, the
   video below it. */
.player-frame.floating { position: fixed; right: 18px; bottom: 18px; z-index: 80;
  width: min(440px, calc(100vw - 36px)); max-width: none;
  background: #1c1c1e;
  border-radius: 14px; overflow: hidden;
  transition: transform .32s cubic-bezier(.4, 0, .2, 1), opacity .32s ease;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .5), 0 0 0 1px rgba(255, 255, 255, .08); }
/* dodge: slide the mini-player down out of the way of a jumped-to section/item,
   then it slides back (showing the freshly-cued frame). Transform only, so the
   remembered drag position (left/top) is untouched. */
.player-frame.floating.dodge { transform: translateY(135%); opacity: .55; }
.player-frame.floating iframe { border-radius: 0; box-shadow: none; display: block; }

/* Our chrome is a TITLE BAR ABOVE the video, not a scrim over it.
   It used to be a gradient over the top ~34% of the frame — which is exactly
   where a YouTube embed puts its title, share, "Watch on YouTube" and gear.
   Hovering the PiP therefore dimmed those (our black gradient) and swallowed
   their clicks (pointer-events: auto). Two sets of controls cannot share the
   same pixels, so ours moved out: the video below now belongs entirely to
   YouTube and every one of its controls is reachable.
   A side benefit: the bar is always visible, so dragging is discoverable
   instead of something you had to hover to find. */
.pf-hover { display: none; }
.player-frame.floating { display: flex; flex-direction: column; }
.player-frame.floating .pf-hover { display: flex; position: static;
  /* The iframe comes first in the DOM, so without this the bar lands BELOW the
     video — directly under YouTube's own control bar, reading as two stacked
     toolbars. order:-1 puts it on top where a window title bar belongs.
     Deliberately CSS rather than reordering the markup: the iframe staying
     first keeps it first in tab order, ahead of our ✕. */
  order: -1;
  flex: 0 0 auto; height: auto; min-height: 0;
  align-items: center; justify-content: space-between; gap: .5rem;
  padding: .2rem .28rem .2rem .42rem;
  background: #1c1c1e; color: rgba(255, 255, 255, .82);
  opacity: 1; pointer-events: auto;
  cursor: grab; touch-action: none; user-select: none; }
.player-frame.floating iframe { flex: 0 0 auto; }
/* Grip dots — the drag affordance, on EVERY breakpoint. This was mobile-only,
   with desktop showing a "Drag to move" label instead; the dots say the same
   thing in a fraction of the width, so the bar can be shorter, and one
   affordance for both beats two. No drop-shadow any more: it existed to lift
   the dots off video pixels, and they now sit on a solid bar. */
.player-frame.floating .pf-hover::before { content: ""; flex: 0 0 auto;
  width: 22px; height: 12px;
  background-image: radial-gradient(circle, rgba(255, 255, 255, .78) 1.3px, transparent 1.6px);
  background-size: 6px 6px; }
/* The bar no longer fades in on hover — it's always there, outside the video —
   so the old :hover/.near/.active opacity gating is gone. `.near` still earns
   its keep for the resize handle below: pointer events don't reach the parent
   document while the cursor is over the cross-origin iframe, so a
   document-level pointermove is the only way to know the cursor is in/near. */
.pf-hover.active { cursor: grabbing; }
/* Accent ring while a drag is in flight. On desktop this echoes the .cue flash;
   on touch it's the ONLY "you have hold of it" feedback there can be, since
   there's no hover state and the finger covers the grip. */
.player-frame.floating.dragging {
  box-shadow: 0 18px 50px rgba(0, 0, 0, .5), 0 0 0 2px var(--accent); }
.pf-actions { display: flex; gap: .35rem; }
/* Desktop resizes with the edge handle, so −/+ are redundant there and only
   add two more of our controls competing with YouTube's own. They stay on
   touch, where the handle is hidden (26px is under a comfortable tap target)
   and they are the ONLY way to resize. Keyed to hover/fine-pointer rather than
   width so a sideways phone doesn't lose its only sizing control. */
@media (hover: hover) and (pointer: fine) {
  .player-frame.floating #pfSmaller,
  .player-frame.floating #pfBigger { display: none; }
}
.pf-btn { width: 24px; height: 24px; border-radius: 50%; border: 0; padding: 0;
  background: rgba(28, 28, 30, .55); color: #fff; font-size: .9rem; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: background .12s ease, transform .12s ease; }
.pf-btn:hover { background: rgba(28, 28, 30, .85); transform: scale(1.08); }

/* one-shot accent flash (.cue, ~1s) when the cursor first enters the player —
   spotlights the controls in the theme accent, then settles back to normal */
.player-frame.floating.cue { animation: cuering .95s ease; }
@keyframes cuering {
  0%, 40% { box-shadow: 0 18px 50px rgba(0, 0, 0, .5), 0 0 0 2px var(--accent); }
  100% { box-shadow: 0 18px 50px rgba(0, 0, 0, .5), 0 0 0 1px rgba(255, 255, 255, .08); }
}
.player-frame.floating.cue .pf-btn { animation: cuebtn .95s ease; }
@keyframes cuebtn {
  0%, 40% { background: var(--accent); transform: scale(1.14); }
  100% { background: rgba(28, 28, 30, .55); transform: scale(1); }
}
.player-frame.floating.cue .pfloat-resize { opacity: 1; }
.player-frame.floating.cue .pfloat-resize::after { animation: cuecorner .95s ease; }
@keyframes cuecorner {
  0%, 40% { border-color: var(--accent); width: 13px; height: 13px; }
}

/* Resize handle on the RIGHT EDGE, lifted clear of YouTube's control bar.
   It used to sit flush in the bottom-right corner — exactly on YouTube's
   fullscreen button — and because opacity:0 elements still receive clicks, it
   was an INVISIBLE 26x26 click-blocker over that button at all times. Hit
   testing confirmed it: every other point on YouTube's bar resolved to the
   iframe, that one resolved to #pfsize. Clicking fullscreen silently did
   nothing, which reads as "the whole control bar is broken".
   `bottom: 42px` clears YouTube's bar (~40px, and it never renders shorter
   than ~28px); staying on the right edge means the existing drag-right-to-grow
/* Resize handle in the frame's bottom-right corner, where a resize grip
   belongs. It was briefly moved up to `bottom: 42px` on the theory that it
   covered YouTube's fullscreen button — but that was inferred from a hit test
   that only proved OUR element occupied the point, not that YouTube had a
   control beneath it. Shane, who can actually see the rendered embed, reported
   fullscreen (and share, and forward) working the whole time. A cross-origin
   iframe cannot be inspected, so his observation is the evidence, not mine.
   What DOES matter and stays: pointer-events follows opacity. An opacity:0
   element still receives clicks, so while our chrome is hidden the grip must
   not intercept anything — that part was a real bug regardless. */
.pfloat-resize { display: none; }
.player-frame.floating .pfloat-resize { display: block; position: absolute;
  right: 0; bottom: 0; width: 26px; height: 26px; z-index: 82;
  cursor: nwse-resize; touch-action: none;
  opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.player-frame.floating:hover .pfloat-resize,
.player-frame.floating.near .pfloat-resize { opacity: 1; pointer-events: auto; }
.pfloat-resize::after { content: ""; position: absolute; right: 7px; bottom: 7px;
  width: 10px; height: 10px;
  border-right: 2.5px solid rgba(255, 255, 255, .8);
  border-bottom: 2.5px solid rgba(255, 255, 255, .8);
  border-bottom-right-radius: 4px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6)); }

/* Any touch device: there is no hover, so anything still hover-gated would be
   unreachable. The bar itself is always visible now, but the resize handle is
   not — and on a tablet (768px, so not caught by the phone block below) it is
   shown, so it needs to be reachable without a hover. */
@media (pointer: coarse) {
  .player-frame.floating .pfloat-resize { opacity: 1; }
}
/* Mobile portrait: the SAME corner mini-player as desktop, just phone-sized. It
   used to be a bespoke full-width bar pinned under the topbar, which ate ~284px
   — a third of the screen — and covered whatever row you had just tapped. A
   corner PiP covers ~12% and, more importantly, shares one code path with
   desktop instead of a second set of rules to reason about. Touch has no hover,
   so the controls stay visible, and the grab bar carries a dotted grip because
   there's no cursor change to advertise that it drags. The corner resize grip
   stays off (26px is under a comfortable touch target and it fights page
   scrolling) — the −/+ buttons do sizing instead. */
@media (max-width: 720px) {
  .player-frame.floating { right: 12px; bottom: 12px; left: auto; top: auto;
    width: min(200px, 52vw); border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .45), 0 0 0 1px rgba(255, 255, 255, .1); }
  /* min-height keeps a grabbable band rather than just the button row. Dropped
     34 -> 30 alongside the 26px buttons: the bar was 25% of a 200px-wide
     mini-player, which is the one place its height genuinely hurts. */
  .player-frame.floating .pf-hover { min-height: 30px;
    padding: .15rem .25rem; gap: .28rem; }
  .player-frame.floating .pfloat-resize { display: none; }
}
/* Touch targets, keyed to the POINTER not the width — a tablet is over 720px
   but still finger-driven. Desktop uses the smaller 24px buttons set above,
   which is the WCAG 2.5.8 minimum and plenty for a mouse.
   26px, not 30: the bar costs proportionally FAR more on a phone (38px of a
   151px mini-player was a quarter of it) than on a desktop, and the button
   size is what sets the bar's height. 26 still clears 2.5.8's 24x24 floor,
   which is the hard stop — going below it to reclaim a few more pixels would
   trade an accessibility requirement for cosmetics. */
@media (pointer: coarse) {
  .pf-btn { width: 26px; height: 26px; font-size: .88rem; }
}
/* Phone held sideways: ~400px of height leaves no useful room for a PiP AND the
   agenda (the mini-player alone would cover a quarter of it), so the detached
   video takes the whole screen — rotate back to portrait to read, or ✕ to
   dismiss it. `pointer: coarse` keeps a merely-short desktop window out of this.
   Note a small phone in landscape (667x375) matches the max-width:720px block
   above too; this one follows it, so it wins on equal specificity.
   The !importants beat the inline left/top/width a previous drag wrote — JS
   clears those as well, but this holds on the first paint before JS runs. */
@media (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
  .player-frame.floating { inset: 0 !important; width: auto !important;
    max-width: none; border-radius: 0; z-index: 200; background: #000;
    display: flex; flex-direction: column; box-shadow: none; }
  /* NO chrome of ours at all here. Every control the bar carries is
     meaningless fullscreen — there is nothing to drag to, nothing to resize —
     and leaving just the ✕ cost a strip of height that pushed the video down
     off centre for the sake of one button. Rotating back to portrait is the
     exit, and it's the same gesture that got you here.
     display:none rather than absolute-overlay on purpose: an overlay would sit
     back on top of the embed's title/share row, which is the bug the
     bar-outside-the-video layout exists to avoid. */
  .player-frame.floating .pf-hover,
  .player-frame.floating .pfloat-resize { display: none; }
  /* Sole flex child now, so it takes the full height and letterboxes the sides. */
  .player-frame.floating iframe { flex: 1 1 auto; min-height: 0;
    height: auto; width: auto; max-width: 100%; align-self: center;
    aspect-ratio: 16/9; border-radius: 0; }
  .player-frame.floating.dodge { transform: none; opacity: 1; }  /* nothing to dodge */
}
/* Set by JS only while the fullscreen player is up (see syncTheaterLock): stops
   a swipe on the letterbox bars from scrolling the agenda behind the video. */
html.theater, html.theater body { overflow: hidden; }
/* display:block matters: an inline iframe sits on the text baseline, leaving a
   ~6px sliver of frame below the video. Harmless to look at, but the frame's
   height is what updateFloat reserves for the dock (minHeight = offsetHeight),
   so the reservation was overshooting the video by that sliver. */
.player-frame iframe { width: 100%; aspect-ratio: 16/9; border: 0; display: block;
  border-radius: 12px; box-shadow: var(--shadow); }
.tile-link { text-decoration: none; color: inherit; display: block; }
.tile-link:hover { border-color: var(--accent); }
/* deep-linked item: pulse an accent ring so the eye lands on it */
/* Clear the sticky topbar. Was a hardcoded 70px guess at its height, which
   goes wrong whenever the bar changes size (it grows with the user's font
   size); consume the value JS measures instead, plus breathing room. */
.item-acc { scroll-margin-top: calc(var(--topbar-h, 58px) + 12px + var(--float-h, 0px)); }
.item-acc.flash { animation: itemflash 2.5s ease-out; }
@keyframes itemflash {
  0%   { box-shadow: 0 0 0 3px var(--accent); }
  70%  { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }
  100% { box-shadow: 0 0 0 3px transparent; }
}
.sec-h { scroll-margin-top: calc(var(--topbar-h, 58px) + 8px + var(--float-h, 0px)); /* land below the sticky header on outline jumps */
  display: flex; align-items: center; gap: .6rem; margin: 1.4rem 0 .5rem;
  font-weight: 800; font-size: .92rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-2); border-radius: 6px; }
/* same one-shot accent pulse as deep-linked items, for outline-jump sections */
.sec-h.flash { animation: itemflash 2.5s ease-out; }
.sec-h:first-child { margin-top: 0; }
.item-acc { margin: .45rem 0; overflow: hidden; }
.item-acc summary { list-style: none; cursor: pointer; padding: .7rem .95rem;
  display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.item-acc summary::-webkit-details-marker { display: none; }
.item-acc summary:hover { background: var(--surface-2); }
.item-acc .inum { font-weight: 750; color: var(--accent-ink); white-space: nowrap; }
.item-acc .ititle { flex: 1 1 300px; min-width: 200px; }
/* Pills + timestamp as one right-aligned group. They used to be direct flex
   children of the summary, so once they overflowed one line the extras wrapped
   individually to a new flex line that starts at the LEFT margin — a lone
   "▶ 10:02" stranded under the title. Grouped, they wrap as a block and stay
   right-aligned, and any internal wrap also stays right (justify-content). */
.item-acc .imeta { display: flex; flex-wrap: wrap; gap: .45rem;
  align-items: center; justify-content: flex-end; margin-left: auto; }
/* Above phone width, title and meta ALWAYS share one row and each wraps
   inside its own box. Without nowrap the summary is a wrapping flex row, so a
   4th pill made the whole meta group jump to a second flex line and the title
   then grew to full width — leaving one item (5.9) structurally unlike every
   other item on the page. Now the title column is the same width whether an
   item has one pill or four; only the pills stack. */
@media (min-width: 721px) {
  /* nowrap is the whole fix. The title keeps its original `flex: 1 1 300px`
     so it still absorbs the free space (which is what keeps title widths
     consistent between items); .imeta simply shrinks toward min-content and
     wraps its own pills instead of being pushed to a new flex line. */
  .item-acc summary { flex-wrap: nowrap; }
}
/* No margin-left:auto here any more — .imeta now carries it, and two auto
   margins on one flex line split the free space and centre the group. The
   caret still sits right because .ititle grows to fill the row. */
.item-acc .caret { color: var(--muted); transition: transform .15s; }
.item-acc[open] .caret { transform: rotate(90deg); }
.item-body { padding: .2rem .95rem .9rem; border-top: 1px solid var(--hairline);
  display: flex; flex-direction: column; gap: .6rem; font-size: .9rem; }
.official, .cnotes { background: var(--surface-2); border-radius: 8px;
  padding: .55rem .75rem; white-space: pre-line; color: var(--ink-2); font-size: .84rem; }
details.memo { margin: .5rem 0 0; }
details.memo summary { cursor: pointer; font-size: .86rem; font-weight: 650;
  color: var(--accent); }
details.memo > div { font-size: .86rem; line-height: 1.55;
  background: var(--surface-2); border: 1px solid var(--hairline);
  border-radius: 8px; padding: .7rem .9rem; margin-top: .45rem;
  max-width: 66ch; overflow-wrap: anywhere; }
details.memo h4 { margin: .8rem 0 .2rem; font-size: .72rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em; color: var(--accent); }
details.memo h4:first-child { margin-top: 0; }
.memo-body { white-space: pre-line; }
.memo-meta { color: var(--muted); font-size: .8rem; margin: .5rem 0 0; }
table.memo-budget { border-collapse: collapse; margin: .7rem 0 0; font-size: .84rem; }
table.memo-budget caption { text-align: left; font-weight: 800; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .06em; color: var(--accent);
  padding-bottom: .25rem; }
table.memo-budget th { text-align: left; font-weight: 500; color: var(--muted);
  padding: .15rem 1rem .15rem 0; white-space: nowrap; }
table.memo-budget td { font-variant-numeric: tabular-nums; font-weight: 700; }
.att-row { display: flex; flex-wrap: wrap; gap: .4rem .9rem; font-size: .84rem; }
.att-row a { text-decoration: none; }
.expand-all { margin-left: auto; }
.access-well { font-size: .84rem; }
.access-well summary { cursor: pointer; color: var(--accent-ink); font-weight: 600;
  padding: .55rem .8rem; list-style: none; }
.access-well div { padding: 0 .8rem .7rem; color: var(--ink-2); white-space: pre-line; }

/* ---------- search & votes ---------- */
.res-group { margin: 1.1rem 0 .4rem; font-weight: 700; font-size: .85rem; }
.res-group a { text-decoration: none; }
.res-item { padding: .6rem .9rem; margin: .4rem 0; }
.res-item .snip { color: var(--muted); font-size: .82rem; margin-top: .3rem;
  border-left: 3px solid var(--hairline); padding-left: .6rem; }
.facet-row { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center;
  margin: .45rem 0; }
.facet-label { font-size: .7rem; font-weight: 800; letter-spacing: .06em;
  color: var(--muted); text-transform: uppercase; min-width: 4rem; }
.statnote { background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--hairline));
  border-radius: 10px; padding: .6rem .9rem; font-size: .88rem; margin: .6rem 0; }

/* ---------- command palette ---------- */
.pal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 100;
  display: grid; place-items: start center; padding-top: 12vh;
  backdrop-filter: blur(2px); }
.pal-overlay[hidden] { display: none; }  /* explicit display beats [hidden] otherwise */
.pal { width: min(640px, 92vw); background: var(--surface); border-radius: 14px;
  border: 1px solid var(--hairline); box-shadow: 0 24px 80px rgba(0,0,0,.35);
  overflow: hidden; }
.pal input { width: 100%; border: 0; outline: 0; background: transparent;
  color: var(--ink); font: inherit; font-size: 1.05rem; padding: 1rem 1.1rem;
  border-bottom: 1px solid var(--hairline); }
.pal-results { max-height: 46vh; overflow: auto; padding: .4rem; }
.pal-results .pr { display: flex; gap: .7rem; align-items: baseline; padding: .55rem .7rem;
  border-radius: 8px; cursor: pointer; font-size: .9rem; }
.pal-results .pr.sel, .pal-results .pr:hover { background: var(--surface-2); }
.pal-results .pr .k { color: var(--muted); font-size: .75rem; min-width: 82px; }

/* ---------- responsive ---------- */
@media (max-width: 780px) {
  /* z-index 90: above the video player (80-82, which becomes a full-width bar
     pinned to the top on mobile) so the slide-in nav isn't hidden behind it;
     still below the search overlay (100). */
  .side { position: fixed; z-index: 90; transform: translateX(-100%);
    transition: transform .18s ease; box-shadow: var(--shadow); }
  .side.open { transform: none; }
  .burger { display: block; }
  .topsearch span { display: none; }
}

/* Subject cloud (Insights). Sized by rank, not raw count — a linear scale on
   a long tail makes everything below the top two look the same. */
.cloud { display: flex; flex-wrap: wrap; gap: .35rem .8rem; align-items: baseline;
  line-height: 1.9; }
.cloud-w { text-decoration: none; color: var(--accent-ink); font-weight: 650; }
.cloud-w:hover { text-decoration: underline; color: var(--ink); }


/* Dual-thumb year range. Two native range inputs share one track: their own
   tracks are transparent and pointer-events:none, with pointer-events re-armed
   on the thumbs only, so whichever thumb you grab is the one that moves. The
   visible track and the selected-range fill are drawn beneath them. Native
   inputs keep arrow-key control and correct screen-reader semantics, which a
   div-based slider would have to reimplement. */
.rslider { position: relative; height: 26px; margin: .5rem 0 .1rem; }
.rslider .rtrack, .rslider .rfill { position: absolute; top: 10px; height: 5px;
  border-radius: 999px; pointer-events: none; }
.rslider .rtrack { left: 0; right: 0; background: var(--surface-2); }
.rslider .rfill { background: var(--accent); }
.rslider input[type=range] { position: absolute; top: 3px; left: 0; width: 100%;
  height: 20px; margin: 0; background: none; pointer-events: none;
  -webkit-appearance: none; appearance: none; }
.rslider input[type=range]::-webkit-slider-thumb { -webkit-appearance: none;
  pointer-events: auto; width: 18px; height: 18px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--accent); cursor: grab;
  box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.rslider input[type=range]::-moz-range-thumb { pointer-events: auto;
  width: 14px; height: 14px; border-radius: 50%; background: var(--surface);
  border: 2px solid var(--accent); cursor: grab;
  box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.rslider input[type=range]::-moz-range-track { background: none; }
.rslider input[type=range]:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--accent); outline-offset: 2px; }
.rslider input[type=range]:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--accent); outline-offset: 2px; }
/* Labels sit BENEATH their control points; the outer two are the archive's
   bounds and drop out when a thumb comes close enough to collide. */
.rmarks { position: relative; height: 1.2rem; margin-bottom: .7rem; }
.rmarks span { position: absolute; transform: translateX(-50%);
  font-size: .74rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rmarks .bound { color: var(--muted); }
.rmarks .active { color: var(--ink); font-weight: 700; }
.folded { color: var(--muted); font-size: .72rem; }

/* The "?" beside a motion the Clerk recorded as FAILED while more members
   voted aye than nay. Deliberately NOT styled like the label pills: those sit
   in dense rows where a permanent cue reads as noise, so their hint is
   hover-only. This marker appears twice in the entire archive, and a reader
   staring at "FAILED 3-2" has no way to guess an explanation exists — so it
   carries a visible affordance at rest. 24px min in both axes for WCAG 2.5.8
   (target size), which the .7rem pill padding alone would not reach. */
.pill.why {
  min-width: 24px; min-height: 24px; justify-content: center;
  padding: 0 .3rem; margin-left: .15rem; border-radius: 50%;
  background: var(--pill-fail-bg); color: var(--pill-fail-ink);
  font-size: .74rem; font-weight: 800; line-height: 1; }
.pill.why:hover, .pill.why[aria-expanded="true"] {
  text-decoration: none; filter: brightness(.93); }

/* The roll-call checksum warning: the Clerk's stated count disagrees with the
   names listed. Amber rather than red — the record is inconsistent, which is a
   caution about our reading, not a claim that the motion failed. Sits inline
   with the tally it qualifies, and carries a visible affordance at rest for
   the same reason .why does: it appears rarely, so nobody learns to look. */
.pill.vwarn {
  min-height: 24px; padding: 0 .5rem; margin-left: .15rem;
  background: var(--pill-defer-bg); color: var(--pill-defer-ink);
  font-size: .68rem; font-weight: 700; letter-spacing: .01em; }
.pill.vwarn:hover, .pill.vwarn[aria-expanded="true"] {
  text-decoration: none; filter: brightness(.93); }
