/* ============================================================
   MORNINGSTAR SECURITY — next edition, v2
   News · Puzzles · Jobs. Magazine front page: desks → source
   panels in columns. Colour is meaningful, never decorative:
     red    = act now / live        green = verified / correct
     gold   = starred / notable     ink   = everything else
     desk hues = wayfinding only (dots, active chips)
   Signature: the rubber stamp.
   ============================================================ */
:root{
  color-scheme:light dark; /* we handle both — UA must not force-darken */
  --sans:"Archivo","Helvetica Neue",Arial,sans-serif;
  --serif:"Newsreader",Georgia,"Times New Roman",serif;
  --disp:"Audiowide","Archivo",sans-serif;
  --rs:1;
}
/* ============================================================
   CONTRAST RULE (audited 2026-08-27). Every foreground token is held to
   WCAG AA 4.5:1 against --paper2, NOT --paper. --paper2 is the darker of the
   two surfaces a token sits on — cards, active chips, panel fills — so a
   palette tuned against --paper alone passes on the page and fails inside
   every box on it. Four of the five light editions failed that way: dawn had
   --act, --ok, --bad, --gold and --faint all under 4.5 on --paper2, heritage
   had four. The corrections hold hue and saturation and drop HSL lightness
   1-6%, which is below the threshold where an edition's character reads as
   changed. Before editing a colour below, check it both ways.
   ============================================================ */
/* ============================================================
   SEVEN EDITIONS. Colour is meaningful, never decorative:
     --act  now / live         --ok    verified / correct
     --gold starred / notable   --mark  the highlighter
     --ink  everything else     desk hues = wayfinding only
   Token names --paper/--paper2 are kept (used site-wide); --p/--p2
   mirror them so markup lifted from the design prototype resolves too.
   Default (no data-theme yet) is phosphor, the dark edition of record.
   ============================================================ */
:root,[data-theme="phosphor"]{color-scheme:dark;
  --p:#0B0E11;--p2:#13171B;--paper:#0B0E11;--paper2:#13171B;--ink:#E6ECE8;--dim:#AAB4AE;--faint:#77827C;
  --rule:#1E2429;--rule2:#374046;--act:#00AEEF;--ok:#7ED9A3;--gold:#D9BE69;--mark:#B8E92E;--hi:#B8E92E;
  --bad:#E0796F;--cg0:#F2C33C;--cg1:#3FBF63;--cg2:#3D8BEF;--cg3:#E8483C;--cgink:#0B0E11;
  --t0:#857F6A;--t1:#958E73;--t2:#ABA184;--t3:#E6ECE8;
  --d-vuln:#D08862;--d-intel:#63B0B0;--d-blue:#7FA3CF;--d-cloud:#7FA3C4;--d-ops:#C49A6A;--d-policy:#A98CD6;--d-research:#9CBF6A;--d-culture:#CE93B4;--d-auton:#7FB0C4;--d-strike:#D08268;--d-seas:#7EA0C6;--d-industry:#C29455;--d-doctrine:#A292CC;--d-grid:#C4AC55;--d-trade:#C87E96;--d-state:#83B896;--d-rules:#A67EC2;--d-hardware:#C0925A;--d-privacy:#7FA8A0;--d-crypto:#C4B05A;--d-ew:#5FA8C8;--d-isr:#7FA890;--d-strategic:#8A8AD0;--d-definnov:#5FB0A8;--d-tradecraft:#B8A462;--d-infowar:#D0785A;--d-geopol:#C8A05E;--d-surveil:#A87EC2;--d-corppower:#6FA0A0;--d-decentral:#7BB86A;--d-neurorights:#C88ED0;--d-foresight:#8296C4;--d-ai:#5FB6A0;--d-quantum:#9A8CE0;--d-biotech:#7BC06A;--d-neuro:#D07FB0;--d-robotics:#6FA0D0;--d-spacef:#8288D8;--d-climate:#A6BC5C;--d-mesh:#5FB2C4;--d-signals:#D0925F;--d-qa:#8FA6BC}
[data-theme="slate"]{color-scheme:light;
  --p:#EDEEE9;--p2:#E1E3DC;--paper:#EDEEE9;--paper2:#E1E3DC;--ink:#23262A;--dim:#4E545B;--faint:#5E646B;
  --rule:#D2D5CD;--rule2:#9BA29A;--act:#8E3B2A;--ok:#1F6E62;--gold:#7C5F19;--mark:#8E3B2A;--hi:#8E3B2A;
  --bad:#A9412E;--cg0:#EFBE33;--cg1:#37B657;--cg2:#3585E8;--cg3:#E5473A;--cgink:#16181B;
  --t0:#6F6A59;--t1:#635D48;--t2:#5E5843;--t3:#23262A}
[data-theme="paper"]{color-scheme:light;
  --p:#F2EDDF;--p2:#EAE4D2;--paper:#F2EDDF;--paper2:#EAE4D2;--ink:#201D15;--dim:#55503F;--faint:#6B6452;
  --rule:#D7CFB8;--rule2:#A79D7F;--act:#A6301F;--ok:#1E6E4C;--gold:#805F0D;--mark:#A6301F;--hi:#A6301F;
  --bad:#A6301F;--cg0:#EFBE33;--cg1:#37B657;--cg2:#3585E8;--cg3:#E5473A;--cgink:#1A1710;
  --t0:#6F6A59;--t1:#635D48;--t2:#5E5843;--t3:#201D15}
[data-theme="dawn"]{color-scheme:light;
  --p:#EFEDF6;--p2:#E4E0F0;--paper:#EFEDF6;--paper2:#E4E0F0;--ink:#241E33;--dim:#544C6B;--faint:#675F7F;
  --rule:#D6D1E5;--rule2:#A9A1C6;--act:#AD3F29;--ok:#296F51;--gold:#7F5E0E;--mark:#6C4FD8;--hi:#6C4FD8;
  --bad:#AD3F29;--cg0:#EFBE33;--cg1:#37B657;--cg2:#3585E8;--cg3:#E5473A;--cgink:#1D1829;
  --t0:#736A8E;--t1:#655C80;--t2:#544C6B;--t3:#241E33}
[data-theme="heritage"]{color-scheme:light;
  --p:#FBFBF9;--p2:#F0F1EC;--paper:#FBFBF9;--paper2:#F0F1EC;--ink:#1A1D21;--dim:#4A5056;--faint:#676D73;
  --rule:#DDDFD9;--rule2:#A9ADA6;--act:#B94906;--ok:#297B3C;--gold:#0073A2;--mark:#00AEEF;--hi:#0083B8;
  --bad:#C0392B;--cg0:#EFBE33;--cg1:#37B657;--cg2:#3585E8;--cg3:#E5473A;--cgink:#14161A;
  --t0:#6B7278;--t1:#5B6167;--t2:#4A5056;--t3:#1A1D21}
/* the four light editions share the darker, higher-contrast section hues */
[data-theme="slate"],[data-theme="paper"],[data-theme="dawn"],[data-theme="heritage"]{
  --d-vuln:#9A4B2E;--d-intel:#2E6E6E;--d-blue:#3B5E86;--d-cloud:#4A6B8A;--d-ops:#7A5A3A;--d-policy:#6B4E8E;--d-research:#567331;--d-culture:#8F5877;--d-auton:#44707F;--d-strike:#8A4A3A;--d-seas:#3A5A7A;--d-industry:#7A5A2E;--d-doctrine:#6A5A8A;--d-grid:#786A28;--d-trade:#8E4E5E;--d-state:#4E6E5E;--d-rules:#6E4E7E;--d-hardware:#7A5A2E;--d-privacy:#3E6E62;--d-crypto:#6E6028;--d-ew:#2E6E8E;--d-isr:#3E6E52;--d-strategic:#3E4E9E;--d-definnov:#2E706A;--d-tradecraft:#6E5E2E;--d-infowar:#8E4A3A;--d-geopol:#8A6A2E;--d-surveil:#6B4E8E;--d-corppower:#3E6E6E;--d-decentral:#4E7331;--d-neurorights:#8E4E9E;--d-foresight:#4E5E7E;--d-ai:#2E7060;--d-quantum:#4E3E9E;--d-biotech:#3E7A31;--d-neuro:#8E4E77;--d-robotics:#3B5E86;--d-spacef:#3E4696;--d-climate:#5E7331;--d-mesh:#2E6E7E;--d-signals:#8E5E2E;--d-qa:#4E5E70}
*{box-sizing:border-box;margin:0;padding:0}
html{scrollbar-color:var(--rule2) var(--paper);scroll-padding-top:70px;background:var(--paper)}
body{color-scheme:inherit}
body{background:var(--paper);color:var(--ink);
  font:400 calc(15px*var(--rs))/1.55 "Newsreader",Georgia,"Times New Roman",serif;
  -webkit-font-smoothing:antialiased}
body.lock{overflow:hidden}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input{font:inherit;color:var(--ink)}
/* Links take the theme's ink, never the UA's blue. Explicit per-link colours
   (.reyebrow a, .chall, etc.) are more specific and still win. Prose links
   inside body copy keep an underline so they stay recognisable. */
a{color:inherit;text-decoration:none}
a:hover{color:inherit}
/* The prose links. The list enumerates the containers prose lives in, so a
   prose container that is not one of them gets a link with no affordance at
   all: .sub is a <div>, and the engagement page's "Against the listing" was
   the same colour, weight, style and size as the sentence around it, with no
   underline. Not a contrast failure — there was nothing to contrast. .dnote
   escaped it only by being a <p>; .locknote is a <span> and did not, so the
   passport's "Sit a sprint" was invisible in the same way. */
p a[href],li a[href],.rlede a[href],dd a[href],
.sub a[href],.locknote a[href]{text-decoration:underline;text-underline-offset:2px;text-decoration-color:var(--rule2)}
p a[href]:hover,li a[href]:hover,.rlede a[href]:hover,dd a[href]:hover{text-decoration-color:var(--act);color:var(--ink)}
h2,h3{font-size:inherit;font-weight:inherit}
:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
/* Skip to content. Off-screen until focused, then the first thing you land on.
   main carries tabindex="-1" so following it actually moves focus; that focus
   is programmatic and wants no ring of its own. */
.skip{position:absolute;left:-9999px;top:0;z-index:100;
  font-family:"Archivo",sans-serif;font-size:14px;letter-spacing:.06em}
.skip:focus{left:0;padding:10px 14px;background:var(--paper);color:var(--ink);
  border:1px solid var(--ink);outline:2px solid var(--ink);outline-offset:2px}
main:focus{outline:none}
.mono{font-family:"Archivo","Helvetica Neue",Arial,sans-serif}
.sheet{max-width:1120px;margin:0 auto;padding:0 18px}
.ic{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;vertical-align:-1.5px;flex:0 0 auto}
.ic.f{fill:currentColor;stroke:none}

/* ---------- masthead ---------- */
.folio{display:flex;justify-content:space-between;align-items:baseline;gap:8px 14px;flex-wrap:wrap;
  padding:9px 0;border-bottom:1px solid var(--rule);
  font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.07em;
  text-transform:uppercase;color:var(--dim)}
.folio b{color:var(--ink);font-weight:600}
/* A laptop running on a prod snapshot says so (pageCtx.DevSnapshot). */
.devstrip{padding:4px 0;border-bottom:1px dashed var(--gold);color:var(--gold);
  font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.07em;text-transform:uppercase}
@media (max-width:700px){.hidesm{display:none}}
h1.masthead{text-align:center;font-weight:800;letter-spacing:.015em;
  font-size:clamp(30px,6.2vw,52px);line-height:1.05;margin:18px 0 8px;
  font-variation-settings:"opsz" 72}
h1 .mstar{color:var(--act);font-size:.62em;vertical-align:.16em;padding:0 .06em}
.motto{text-align:center;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.16em;text-transform:uppercase;color:var(--dim);
  padding-bottom:13px;border-bottom:3px double var(--rule2)}
/* flex-wrap, because the row could not shrink below its content. Ten tabs at
   flex:1 with the default min-width:auto floor at min-content, so a 360px phone
   needed 509px of page: Puzzles, Jobs, Wiki and Search were off the right edge
   with no horizontal scroll to reach them, and the masthead was clipped with
   them. Four destinations unreachable on a phone. The admin bar had been given
   overflow-x:auto for this; the reader's bar never was. */
.tabs{display:flex;flex-wrap:wrap;border-bottom:1px solid var(--rule)}
.tab{flex:1 1 auto;min-width:80px;text-align:center;padding:12px 6px 10px;
  font-family:"Archivo",sans-serif;font-size:calc(12px*var(--rs));font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dim);
  border-bottom:3px solid transparent}
.tab .tsub{display:block;font-size:calc(9.5px*var(--rs));font-weight:400;letter-spacing:.09em;color:var(--faint);margin-top:2px}
.tab.on{color:var(--ink);border-bottom-color:var(--hi)}
.tab:hover:not(.on){color:var(--ink)}

/* ---------- shared atoms ---------- */
.kick{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.b{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;position:relative;
  border:1px solid var(--ink);padding:10px 14px;color:var(--ink);
  display:inline-flex;align-items:center;gap:6px;min-height:40px}
.b::after{content:"";position:absolute;inset:-4px}
.b:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}
.b.act{border-color:var(--act);color:var(--act)}
.b.solid{background:var(--act);border-color:var(--act);color:var(--paper)}
.b.solid:hover{opacity:.9;background:var(--act)}
.b.ok{border-color:var(--ok);color:var(--ok)}
.b:disabled{opacity:.45;cursor:default}
.g{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));letter-spacing:.04em;
  color:var(--dim);padding:4px 6px;display:inline-flex;align-items:center;gap:4px;
  position:relative;min-height:24px}
.g::after{content:"";position:absolute;inset:-10px -6px}
.g:hover{color:var(--ink)}
.g.on{color:var(--gold)}
.pill{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);
  padding:2px 8px;border-radius:2px;white-space:nowrap;position:relative}
button.pill::after{content:"";position:absolute;inset:-8px -4px}
.pill.kev{border-color:var(--act);color:var(--act);font-weight:600}
.pill.first{border-color:var(--gold);color:var(--gold);font-weight:600}
.pill.novel{border-color:var(--gold);color:var(--gold);font-weight:600}
.pill.promo{border-color:var(--faint);color:var(--faint)}

/* Clusters river: subject sub-sections + collapsed retellings */
.clwin{margin-left:10px;color:var(--faint)}
.clwin a{color:var(--dim)}
/* /world country header: pzhead with the window toggle dropped onto its own
   small-caps line, so the controls read as controls, not sentence tail. The
   flag sits in the heading; the country outline hangs right as a watermark. */
.worldhead{position:relative;margin-bottom:18px}
.worldback{display:inline-flex;align-items:center;gap:6px;color:var(--dim)}
.worldback:hover{color:var(--ink)}
.worldback .ic{width:13px;height:13px}
.pillflag{height:10px;width:auto;margin-right:6px;vertical-align:-1px;
  border-radius:1px;outline:1px solid var(--rule)}
.whflag{height:.78em;width:auto;margin-right:.38em;vertical-align:-.04em;
  border:1px solid var(--rule);border-radius:2px;background:var(--paper)}
.whmap{position:absolute;right:0;top:10px;bottom:12px;width:min(240px,30%);
  background:var(--dim);opacity:.16;pointer-events:none;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center right;mask-position:center right;
  -webkit-mask-size:contain;mask-size:contain}
@media(max-width:700px){.whmap{display:none}}
.worldhead .sub .clwin{display:block;margin:12px 0 0;font-family:var(--sans);font-style:normal;
  font-size:calc(10.5px*var(--rs));font-weight:600;letter-spacing:.12em;text-transform:uppercase}
.worldhead .sub .clwin b{color:var(--ink)}
/* A subject sub-heading inside a section block. Lighter than .sechd on every
   axis — smaller, indented, no rule — so the section stays the unit of
   navigation and the subject reads as a division of it. */
.subhd{display:flex;align-items:baseline;gap:8px;margin:16px 0 6px 10px;
  padding-left:10px;border-left:2px solid var(--rule2)}
.subhd h4{margin:0;font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));
  font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--dim)}
.subhd .n{font-size:calc(9.5px*var(--rs));letter-spacing:.06em;color:var(--faint)}
.subhd .submore,.sechd .submore{font-size:calc(9.5px*var(--rs));letter-spacing:.06em;color:var(--act);
  text-decoration:none;margin-left:auto}
.subhd .submore:hover,.sechd .submore:hover{text-decoration:underline;text-underline-offset:2px}
.secgroup [id^="sub-"]{scroll-margin-top:64px}
.cluster{border-bottom:1px solid var(--rule)}
.clmore{margin:-4px 0 8px 2px;font-size:calc(12px*var(--rs))}
.clmore summary{cursor:pointer;color:var(--dim)}
.clmore ul{margin:6px 0 4px 16px;list-style:"› "}
.clmore li{margin:2px 0}
.clmore a{color:var(--ink)}
.clsrc{color:var(--faint);margin-left:6px}

/* Vendors & Software browse */
.vtabs{margin:4px 0 12px;font-size:calc(12px*var(--rs))}
.vtabs a{color:var(--dim)}
.vtabs a.on{color:var(--ink);font-weight:600}
.letterbar{display:flex;flex-wrap:wrap;gap:2px 8px;margin:0 0 12px;font-size:calc(13px*var(--rs))}
.letterbar a{color:var(--dim);padding:1px 3px}
.letterbar a.on{color:var(--ink);font-weight:700;border-bottom:2px solid var(--act)}
.letterbar .dead{color:var(--faint);opacity:.45;padding:1px 3px}
.vtable{width:100%;border-collapse:collapse;font-size:calc(13px*var(--rs));margin-bottom:18px}
.vtable td{padding:6px 8px;border-top:1px solid var(--rule);vertical-align:top}
/* Column headers in the house small-caps (same treatment as .kick / .sechd .n),
   so the header row reads as a label strip rather than a first data row. */
.vtable th{padding:5px 8px;border-bottom:2px solid var(--rule2);text-align:left;
  font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
.vtable th.vnum{text-align:right}
.vtable .vname a{color:var(--ink);font-weight:600}
.vtable .vby{margin-left:6px;color:var(--faint);font-size:.9em}
/* Vendor column (products view only): the parent vendor is context for the
   product named beside it, so it stays quieter than the .vname link and never
   wraps mid-name. */
.vtable .vvend{white-space:nowrap;color:var(--faint)}
.vtable .vvend a{color:var(--dim)}
.vtable .vnum{color:var(--faint);white-space:nowrap;text-align:right}
.vtable .vcats .pill{margin:0 3px 3px 0}
.cattree .catlist{list-style:none;margin:4px 0 4px 14px;padding:0}
.cattree>.catlist{margin-left:0}
.cattree li{margin:3px 0}
.cattree a{color:var(--ink)}
.cattree .tc{color:var(--faint);font-size:.85em;margin-left:4px}
.skychart{margin:10px 0 6px}

/* Wiki kind tiers: locked (members) taxonomy entries + the moderators'
   boxed admin-only section (same hierarchy, its own container) */
.taxkind.lockd{opacity:.85}
.taxkind .lockg{margin-left:4px;font-size:.8em;filter:grayscale(1)}
.adminbox{margin-top:26px;border:1px dashed var(--act);padding:12px 14px;background:var(--paper2)}
.adminbox-hd{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--act);margin-bottom:8px}
.adminbox-hd a{color:var(--act)}
.adminbox .taxkind{opacity:.85}

/* Wiki list mode: the compact reference table for thin-content kinds */
.wlist{width:100%;border-collapse:collapse;font-size:calc(13px*var(--rs));max-width:760px}
.wlist td{padding:5px 8px;border-top:1px solid var(--rule)}
.wlist td a{color:var(--ink)}
.wlist .wlfacet{color:var(--dim)}
.wlist .wlsrc{color:var(--faint);white-space:nowrap;text-align:right}
.kindfb{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:18px;padding-top:10px;border-top:1px solid var(--rule)}
.kindfb select,.kindfb input{background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);padding:4px 6px;font-size:12px}
.kindfb input{min-width:220px}
.pill.clue{border-color:var(--act);color:var(--act)}
.pill.tagon{background:var(--ink);border-color:var(--ink);color:var(--paper)}
button.pill:hover{color:var(--ink);border-color:var(--ink)}
button.pill.kev:hover,button.pill.clue:hover{color:var(--act);border-color:var(--act);background:rgba(166,48,31,.07)}
button.pill.tagon:hover{color:var(--paper)}

/* the signature: the rubber stamp */
.stamp{display:inline-block;border:3px double var(--act);color:var(--act);
  padding:7px 16px;transform:rotate(-5deg);
  font-family:"Archivo",sans-serif;font-weight:600;letter-spacing:.18em;
  font-size:calc(13px*var(--rs));text-transform:uppercase;text-align:center}
.stamp small{display:block;font-size:calc(9.5px*var(--rs));font-weight:400;letter-spacing:.13em;margin-top:3px;opacity:.85}
.stamp.ok{border-color:var(--ok);color:var(--ok)}
.stamp.sm{font-size:calc(10px*var(--rs));padding:4px 10px;transform:rotate(-3deg)}
.stamp-in{animation:stampIn .45s cubic-bezier(.2,1.35,.4,1) both}
@keyframes stampIn{0%{transform:rotate(-14deg) scale(1.6);opacity:0}
  60%{transform:rotate(-5deg) scale(.97);opacity:1}100%{transform:rotate(-5deg) scale(1)}}
@keyframes flashbg{0%{background:rgba(166,48,31,.13)}100%{background:transparent}}
.flash{animation:flashbg 1.3s ease-out}
@keyframes fadeUp{from{transform:translateY(6px);opacity:0}to{transform:translateY(0);opacity:1}}
.fade-up{animation:fadeUp .28s ease both}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important;scroll-behavior:auto!important}}

/* ---------- NEWS ---------- */
.wireflash{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;
  border:1px solid var(--act);padding:9px 12px;margin:16px 0 0;
  font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));color:var(--ink)}
.wireflash .wf{color:var(--act);font-weight:600;letter-spacing:.13em}
.wireflash button{text-decoration:underline;text-underline-offset:2px;position:relative}
.wireflash button::after{content:"";position:absolute;inset:-9px -4px}
.wireflash button:hover{color:var(--act)}

.topbox{border-top:3px double var(--rule2);margin:16px 0 4px;padding-top:10px}
/* Indented to match the rows under it, which carry 11px at the sides to hold
   the keyboard ring off their headlines. */
.tophd{display:flex;justify-content:space-between;align-items:center;gap:8px 12px;flex-wrap:wrap;padding:0 11px 6px}
.seg{display:inline-flex;border:1px solid var(--rule2)}
.seg button{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--dim);padding:7px 12px;position:relative}
.seg button::after{content:"";position:absolute;inset:-6px 0}
.seg button.on{background:var(--ink);color:var(--paper)}
.toprow{display:flex;gap:11px;align-items:baseline;padding:11px;border-top:1px solid var(--rule);
  width:100%;text-align:left}
.toprow:hover .tt{text-decoration:underline;text-underline-offset:3px}
.toprow .rank{font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));color:var(--act);flex:0 0 20px;text-align:right}
.toprow .tt{font-size:calc(16.5px*var(--rs));font-weight:500;line-height:1.3;flex:1}
.toprow .tm{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));color:var(--faint);flex:0 0 auto;letter-spacing:.03em}
.toprow .tm b{color:var(--dim);font-weight:500}
/* Cast attribution on a starred row. Gold, like the NOVEL* pill, because both
   mark a star a machine put there. The AI badge inside it shrinks to match the
   metadata line rather than the byline it was sized for. */
.toprow .tm .cstar{color:var(--gold);white-space:nowrap}
.toprow .tm .cstar .aibadge{font-size:calc(7.5px*var(--rs));padding:0 3px;vertical-align:1px}
.topnote{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));color:var(--faint);
  line-height:1.5;padding:8px 0 0;border-top:1px solid var(--rule)}
.topnote a{color:var(--dim);text-decoration:underline;text-underline-offset:2px}
/* The source name already hides here; the cast byline is the more useful of
   the two on a narrow screen, so it stays. */
@media (max-width:640px){.toprow .tm b{display:none}}

.teaser{display:flex;gap:10px 14px;align-items:center;justify-content:space-between;flex-wrap:wrap;
  border:1px solid var(--rule);border-left:3px solid var(--act);padding:10px 13px;margin:14px 0 2px}
.teaser .pt{font-size:calc(14.5px*var(--rs));font-weight:500}
.teaser .pm{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));color:var(--dim);letter-spacing:.05em;margin-top:2px}
.teaser .tbtns{display:flex;gap:8px;flex-wrap:wrap}

.trendrow{display:flex;gap:8px 10px;align-items:center;flex-wrap:wrap;padding:13px 0 3px}
.trendrow .tr-chips{display:flex;gap:6px;flex-wrap:wrap;flex:1;min-width:0}
.search{font-size:calc(16px*var(--rs));font-family:"Archivo",sans-serif;background:var(--paper);
  border:1px solid var(--rule2);padding:8px 10px;width:200px;max-width:100%}
.search::placeholder{color:var(--faint);font-size:calc(13px*var(--rs))}
@media (max-width:640px){.search{width:100%}}

.newsbar{position:sticky;top:0;z-index:30;background:var(--paper);
  border-bottom:1px solid var(--rule);margin:10px -18px 0;padding:8px 18px;
  display:flex;gap:8px;align-items:center}
.chips{display:flex;gap:6px;overflow-x:auto;scrollbar-width:none;flex:1;padding:2px 0}
.chips::-webkit-scrollbar{display:none}
.chip{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.07em;
  text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);
  padding:8px 12px;border-radius:2px;white-space:nowrap;position:relative}
.chip::after{content:"";position:absolute;inset:-6px 0}
.chip:hover{color:var(--ink);border-color:var(--ink)}
.chip.on{background:var(--hue,var(--ink));border-color:var(--hue,var(--ink));color:var(--paper)}
.chip.star.on{background:var(--gold);border-color:var(--gold)}
.chip .cn{opacity:.75;font-size:calc(9px*var(--rs));margin-left:4px}
/* The topics run: the subject blocks promoteSubjects pulls out of the
   aggregator sections. Borderless and lower-case against the sections'
   outlined caps, because a section and a tag are not the same kind of thing
   and the bar used to say they were. The label is not a chip and is not
   clickable; it is what tells the reader the run has changed. */
.chiplbl{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);align-self:center;padding:0 2px 0 8px;
  white-space:nowrap;border-left:1px solid var(--rule2);margin-left:4px}
.chip.topic{border-color:transparent;text-transform:none;letter-spacing:.02em;
  font-size:calc(10.5px*var(--rs));color:var(--faint);padding-left:6px;padding-right:6px}
.chip.topic:hover{color:var(--ink);border-color:var(--rule2)}

.secgroup{margin-top:6px}
.sechd{display:flex;align-items:center;gap:10px;margin:26px 0 12px;
  font-family:"Archivo",sans-serif;font-size:calc(12px*var(--rs));font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink)}
.sechd .dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
.sechd i{flex:1;height:1px;background:var(--rule)}
.sechd .n{font-size:calc(9.5px*var(--rs));font-weight:400;letter-spacing:.09em;color:var(--faint)}
/* A subject heading is a cut WITHIN the news, not a peer of a desk section:
   no hue dot, lower case, lighter. It carries "all coverage" to the tag, which
   a section heading never does, so the two are told apart by more than weight. */
.subjhd{font-size:calc(11px*var(--rs));font-weight:500;letter-spacing:.06em;
  text-transform:none;color:var(--dim);margin-top:20px}
.subjhd h3{font:inherit;letter-spacing:inherit}
.colwrap{columns:1;column-gap:16px}
@media (min-width:760px){.colwrap{columns:2}}
@media (min-width:1080px){.colwrap{columns:3}}
.panel{break-inside:avoid;border:1px solid var(--rule);margin:0 0 14px;background:var(--paper)}
/* A section with a single source panel would otherwise sit narrow in the left
   column with the other columns empty. Let that lone panel span the full width
   and flow its items across the same number of columns, so there are no gaps. */
.pbody article.story{break-inside:avoid;-webkit-column-break-inside:avoid}
/* Bare cards sit directly in .colwrap on /world/* — without this a card
   fragments at the column break and its tail reads as a titleless story. */
.colwrap>article.story{break-inside:avoid;-webkit-column-break-inside:avoid}
@media (min-width:760px){
  .colwrap:has(.panel:only-child){columns:1}
  .colwrap:has(.panel:only-child) .pbody{columns:2;column-gap:16px}
}
@media (min-width:1080px){
  .colwrap:has(.panel:only-child) .pbody{columns:3}
}
.phd{display:flex;align-items:center;gap:8px;padding:9px 11px;
  border-bottom:1px solid var(--rule);background:var(--paper2)}
.phd .pdot{width:7px;height:7px;border-radius:50%;flex:0 0 auto}
/* The source title is the panel's masthead. It keeps the paper's serif — the
   voice it has always had here — and only takes the size and weight of a
   heading, because the global `h2,h3{font-size:inherit;font-weight:inherit}`
   reset (top of this file) means a heading gets neither unless it asks. This
   one never asked, so the source name rendered at body size and body weight,
   indistinguishable from the stories under it. Family, case and tracking are
   deliberately NOT set: the sans, uppercase treatment belongs to the section
   bars and the chrome, not to the name of a newspaper's source. */
.phd h3{flex:1;min-width:0;font-size:calc(19px*var(--rs));
  font-weight:700;line-height:1.2;letter-spacing:-.008em;color:var(--ink)}
.phd h3 a,.phd h3 button{font:inherit;letter-spacing:inherit;text-transform:inherit;
  color:inherit;text-align:left;position:relative}
.phd h3 a::after,.phd h3 button::after{content:"";position:absolute;inset:-10px -4px}
.phd h3 a:hover,.phd h3 button:hover{text-decoration:underline;text-underline-offset:3px}
.phd .claimed{color:var(--gold);font-size:calc(11px*var(--rs));flex:0 0 auto}
.phd .count{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));color:var(--gold);
  border:1px solid var(--gold);border-radius:9px;padding:1px 7px;flex:0 0 auto}
.phd .count.zero{color:var(--faint);border-color:var(--rule2)}
.fold{color:var(--faint);transition:transform .15s;position:relative;padding:2px 4px}
.fold::after{content:"";position:absolute;inset:-11px -8px}
.fold:hover{color:var(--ink)}
.panel.closed .fold{transform:rotate(-90deg)}
.panel.closed .pbody{display:none}
.panel.closed .pmore{display:none}
/* "+ More from <source>" — jump to the source's full feed */
.pmore{display:block;text-align:center;font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));
  font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--dim);padding:9px 11px;border-top:1px solid var(--rule);transition:color .12s,background .12s}
.pmore:hover{color:var(--act);background:color-mix(in srgb,var(--act) 7%,transparent)}
.pmore .pplus{display:inline-block;border:1px solid currentColor;width:14px;height:14px;line-height:12px;text-align:center;font-size:.82em;margin-right:5px;vertical-align:.05em}
.ptail{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));color:var(--faint);
  letter-spacing:.05em;padding:6px 11px;border-bottom:1px solid var(--rule)}
.who{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));color:var(--d-policy);letter-spacing:.03em}

/* 11px all round, and none of it is decoration: the keyboard ring is drawn a
   pixel inside this box (see "the ring on a river row"), so this padding IS the
   ring's gutter — 10px on every side once the ring is inset. It was 9px 11px,
   which left only 8px of it above and below the headline. The sides stay at
   11px so a headline still lines up with its panel head (.phd). */
article.story{border-bottom:1px solid var(--rule);padding:11px}
article.story:last-child{border-bottom:0}
.srow{display:flex;gap:9px;align-items:baseline}
.srow .hlh{flex:1;min-width:0}
.srow .hl{font-size:calc(16px*var(--rs));font-weight:500;line-height:1.32;text-align:left;position:relative}
.srow .hl::after{content:"";position:absolute;inset:-6px -4px}
.srow .hl:hover{text-decoration:underline;text-underline-offset:3px}
article.story.read .hl{color:var(--dim);font-weight:400}
.srow .age{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));color:var(--faint);flex:0 0 auto}

.smeta{display:flex;gap:2px 7px;align-items:center;flex-wrap:wrap;margin-top:5px}
.smeta .src{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.smeta .spacer{flex:1}
.sum{margin:7px 0 2px;font-size:calc(13.5px*var(--rs));line-height:1.55;color:var(--dim);max-width:60ch;
  animation:fadeUp .25s ease both}

/* wire mode */
.dayhr{display:flex;align-items:center;gap:12px;margin:24px 0 2px}
.dayhr span{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.15em;
  text-transform:uppercase;color:var(--dim);white-space:nowrap}
.dayhr i{flex:1;height:1px;background:var(--rule)}
#wireview article.story{padding:11px}
#wireview .srow .age{flex-basis:40px;text-align:right;order:-1}
.emptyriver{padding:26px 0;color:var(--dim);font-size:calc(14px*var(--rs))}

#toTop{position:fixed;right:16px;bottom:16px;z-index:40;width:44px;height:44px;
  border:1px solid var(--rule2);background:var(--paper);color:var(--dim);
  display:none;align-items:center;justify-content:center}
#toTop.show{display:flex}
#toTop:hover{color:var(--ink);border-color:var(--ink)}
#toTop .ic{transform:rotate(180deg)}

/* ---------- PUZZLES ---------- */
.pzhead{padding:22px 0 14px;border-bottom:1px solid var(--rule)}
.pzhead h2,.cahead h2{font-size:calc(26px*var(--rs));font-weight:700;line-height:1.15;margin:4px 0 5px}
.pzhead .sub,.cahead .sub{font-size:calc(14px*var(--rs));color:var(--dim);max-width:58ch}
.statline{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px;
  font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--dim)}
.statline .run{color:var(--act);font-weight:600}
.workshop{display:flex;gap:8px;overflow-x:auto;padding:6px 0 14px;scrollbar-width:none}
.workshop::-webkit-scrollbar{display:none}
.wk{border:1px dashed var(--rule2);padding:9px 12px;flex:0 0 auto;opacity:.75}
.wk .kick{color:var(--faint)}
.wk div{font-size:calc(11.5px*var(--rs));color:var(--faint);font-family:"Archivo",sans-serif;margin-top:2px}

/* Editorial-team block — the masthead's own voice. Two layouts share the card
   (itemCard + desk chips):
   • editorialSection (news pages): a peer section — gold sechd bar over the same
     .colwrap 1/2/3-column grid the desk sections use, one .panel cell per piece.
   • editorialLede (Long Reads): the pieces lead the page in the SAME single
     column as the reads below, flush (no indent); a footer rule is all that
     separates them. */
.edgrid .edcard{break-inside:avoid;border:1px solid var(--rule);background:var(--paper);margin:0 0 14px}
.edgrid .edcard article.story{border-bottom:0}
.edgrid .eddesks{padding:0 11px 11px}
/* A lone editorial piece spans the row, as a single-source section does — no
   stranded narrow card in the left column. */
.edgrid:has(.edcard:only-child){columns:1}

.edlede{border-bottom:2px solid var(--rule2);margin-bottom:8px}
.edledehd{display:flex;align-items:baseline;gap:10px;margin:2px 0 2px;
  font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));font-weight:700;
  letter-spacing:.11em;text-transform:uppercase;color:var(--gold)}
.edlede .edcard{border-bottom:1px solid var(--rule)}
.edlede .edcard:last-child{border-bottom:0}
.edlede .eddesks{padding:0 0 11px 2px}

.edby{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));font-weight:400;
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.eddesks{display:flex;flex-wrap:wrap;align-items:center;gap:6px}
.edon{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.edpill{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.05em;
  text-transform:uppercase;color:var(--dim);border:1px solid var(--rule2);border-radius:9px;padding:1px 8px}
.edpill:hover{border-color:var(--gold);color:var(--gold)}

.gamewrap{padding:16px 0 8px;max-width:660px}
.ghd{display:flex;justify-content:space-between;align-items:baseline;gap:8px 12px;flex-wrap:wrap;margin-bottom:10px}
.gback{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--dim);position:relative}
.gback::after{content:"";position:absolute;inset:-10px -6px}
.gback:hover{color:var(--ink)}
.gclock{font-family:"Archivo",sans-serif;font-size:calc(12px*var(--rs));color:var(--ink)}
.gclock.low{color:var(--act);font-weight:600}
.gmsg{font-size:calc(13px*var(--rs));color:var(--dim);min-height:19px}
.tgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.tile{font-family:"Archivo",sans-serif;font-size:calc(13px*var(--rs));font-weight:600;
  text-transform:uppercase;line-height:1.25;text-align:center;
  border:1px solid var(--rule2);min-height:64px;padding:5px 4px;
  display:flex;align-items:center;justify-content:center;word-break:break-word;transition:transform .08s}
.tile:active{transform:scale(.95)}
.tile.on{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.gband{border:1px solid var(--rule);border-left:5px solid var(--t0);
  padding:8px 11px;margin-bottom:7px;animation:fadeUp .3s ease both}
.gband .gn{font-family:"Archivo",sans-serif;font-size:calc(11.5px*var(--rs));font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;color:var(--ink)}
.gband .gn .tw{color:var(--faint);font-weight:400;letter-spacing:.08em}
.gband .gw{font-family:"Archivo",sans-serif;font-size:calc(11.5px*var(--rs));color:var(--dim);margin-top:3px}
.gctrl{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;margin-top:12px}
.gend{border:1px solid var(--rule);padding:15px;margin-top:14px;animation:fadeUp .3s ease both}
.gend .big{font-size:calc(21px*var(--rs));font-weight:700;text-align:center;margin-bottom:8px}
.gnote{font-size:calc(13.5px*var(--rs));line-height:1.5;margin-top:7px}
.gnote b{font-weight:600}
.gnote .why{color:var(--dim)}
.trap{font-size:calc(12.5px*var(--rs));color:var(--gold);margin-top:9px}
.trap .why{color:var(--dim)}
.xlink{font-size:calc(12.5px*var(--rs));margin-top:11px;padding-top:9px;border-top:1px solid var(--rule);color:var(--dim)}
.xlink button{text-decoration:underline;text-underline-offset:2px;position:relative}
.xlink button::after{content:"";position:absolute;inset:-9px -4px}
.xlink button:hover{color:var(--act)}
.slotrow{display:flex;gap:5px;flex-wrap:wrap;min-height:38px;margin:10px 0}
.slot{font-family:"Archivo",sans-serif;font-size:calc(12px*var(--rs));border:1px dashed var(--rule2);
  color:var(--faint);padding:9px 12px;border-radius:2px}
.slot.fill{border-style:solid;border-color:var(--rule2);color:var(--ink);cursor:pointer;position:relative}
.slot.fill::after{content:"";position:absolute;inset:-6px 0}
.slot.fill.hit{border-color:var(--ok);color:var(--ok)}
.slot.fill.near{border-color:var(--gold);color:var(--gold)}
.slot.fill.trapd{border-color:var(--act);color:var(--act)}
.pool{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.pool .tile{min-height:52px}
.qa{border:1px solid var(--rule);padding:11px 13px;margin-bottom:7px;
  font-family:"Archivo",sans-serif;font-size:calc(13px*var(--rs));line-height:1.55}
.clueline{font-family:"Archivo",sans-serif;font-size:calc(12.5px*var(--rs));line-height:1.6;
  display:flex;gap:8px;padding:2px 0}
.clueline .mk{color:var(--faint);flex:0 0 auto}
.clueline.planted{color:var(--gold)}
.clueline.planted .mk{color:var(--gold)}
.choice{display:block;width:100%;text-align:left;border:1px solid var(--rule);
  padding:12px 13px;margin-bottom:7px;font-size:calc(14.5px*var(--rs));line-height:1.45}
button.choice:hover{border-color:var(--ink)}
.choice.right{border-color:var(--ok)}
.choice.wrong{border-color:var(--act)}
.choice.flagged{border-color:var(--gold)}
.choice.mute{opacity:.55}
.two{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.two .choice{text-align:center;font-weight:600;margin:0}
.two .choice.benign{color:var(--ok)}
.two .choice.mal{color:var(--act)}
.optgrid{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.optgrid .choice{margin:0;text-align:center;font-family:"Archivo",sans-serif;
  font-size:calc(11px*var(--rs));font-weight:600;text-transform:uppercase;min-height:52px;
  display:flex;align-items:center;justify-content:center}
.scrow{display:flex;gap:8px;margin-top:10px}
.scin{flex:1;font-size:calc(16px*var(--rs));font-family:"Archivo",sans-serif;background:var(--paper);
  border:1px solid var(--rule2);padding:9px 11px;min-width:0}
.scin::placeholder{color:var(--faint);font-size:calc(13px*var(--rs))}
.sharebox{border:1px dashed var(--rule2);padding:11px;margin-top:12px}
.sharebox .shd{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:7px}
pre.sharetext{font-family:"Archivo",sans-serif;font-size:calc(11.5px*var(--rs));line-height:1.5;
  white-space:pre-wrap;color:var(--ink)}
.platforms{display:flex;gap:6px;flex-wrap:wrap;margin-top:9px}
.platforms .b{padding:8px 11px;font-size:calc(10px*var(--rs));min-height:36px}
.platforms .b.sent{border-color:var(--ok);color:var(--ok)}

/* ---------- JOBS ---------- */
.cahead{padding:22px 0 14px;border-bottom:1px solid var(--rule)}
.standing{border:1px solid var(--rule);margin:16px 0;padding:14px}
.statgrid{margin:14px 0;display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--rule);
  border:1px solid var(--rule);margin:10px 0 12px}
.stat{background:var(--paper);padding:9px 10px}
.stat b{display:block;font-size:calc(19px*var(--rs));font-weight:700}
.stat span{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim)}
@media (max-width:560px){.statgrid{grid-template-columns:1fr 1fr}}
.sechd2{display:flex;align-items:baseline;justify-content:space-between;gap:8px 10px;flex-wrap:wrap;
  margin:24px 0 4px;padding-top:12px;border-top:3px double var(--rule2)}
/* Section-header kicker used across the jobs desk. Scales with the reader Aa
   control, unlike the fixed 12px inline it replaces. */
.kick.k2{font-size:calc(12px*var(--rs))}
/* Skills — the item bank. One question at a time, so the box is the page's
   centre of gravity rather than a card in a grid. */
.qbox{border-top:3px double var(--ink);border-bottom:1px solid var(--rule);padding:16px 0 18px;margin:10px 0 18px}
.qmeta{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
.qstem{font:500 calc(18px*var(--rs))/1.4 var(--serif);margin:0 0 14px;max-width:70ch}
/* Options are submit buttons: one click answers, and with JS off it still
   works. .wline is the puzzle desk's answer row — same affordance, same look. */
.qopts{display:flex;flex-direction:column;gap:2px}
.qopts .wline{cursor:pointer;background:transparent;color:inherit;font:inherit}
.qopts .wline:hover{border-color:var(--ink)}
/* Authoring: a radio marks the key, the text field beside it is the option. */
.qwrite{display:flex;flex-direction:column;gap:6px;margin:10px 0 4px}
.qopt{display:flex;align-items:center;gap:10px}
.qopt input[type=text],.qopt input:not([type]){flex:1}
.qopt input[name^="opt"]{flex:1;min-width:0}
.qmine{margin-top:10px}
.qrow{display:flex;gap:14px;align-items:baseline;padding:9px 0;border-bottom:1px solid var(--rule)}
.qrs{flex:1;min-width:0}
.qrn{flex:0 0 auto;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.07em;text-transform:uppercase;color:var(--faint)}

/* Jobs — the identity radio group. Set off from the fields around it because
   it is the one control on the form that decides what a stranger sees. */
.idmode{border-left:3px solid var(--gold);padding:8px 0 8px 14px;margin:10px 0}
.idmode .idlbl{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--dim);display:block;margin-bottom:6px}
/* clear, not display:block. The legend is a 100%-wide float (the standard
   hack for positioning a <legend>), and .pcheck is display:flex, which makes
   each label a block formatting context. A BFC beside a float shrinks to the
   space left over, and beside a full-width float that is zero: the first
   radio collapsed to 0px and spilled its words off the right edge one letter
   at a time, at every viewport width. It was the checked default, so the
   option most members were reading was the unreadable one. The display:block
   written here to avoid that was dead from the day a later .pcheck rule set
   display:flex!important. Clearing the float works whatever the display is. */
.idmode .pcheck{clear:both;margin:3px 0}

/* Jobs — "posting pending review" confirmation line. */
.jobmsg{color:var(--ok);font:600 calc(13px*var(--rs)) var(--sans);margin:0 0 14px;
  border-left:3px solid var(--ok);padding:5px 0 5px 12px}
.wline{display:block;width:100%;text-align:left;border:1px solid var(--rule);
  padding:10px 12px;margin-bottom:6px;font-size:calc(13.5px*var(--rs));line-height:1.5}
.wline.flagged{border-color:var(--ink);background:rgba(0,0,0,.04)}
[data-theme="dark"] .wline.flagged{background:rgba(255,255,255,.05)}
.wline.found{border-color:var(--ok)}
.wline.missed{border-color:var(--act)}
.wline.falseflag{border-color:var(--gold)}
.wline .wv{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;display:block;margin-top:4px}
.ad{border-bottom:1px solid var(--rule);padding:14px 0}
.ad .at{font-size:calc(17px*var(--rs));font-weight:600}
.ad .am{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.07em;
  text-transform:uppercase;color:var(--dim);margin:3px 0 7px}
.ad .ab{font-size:calc(13.5px*var(--rs));color:var(--dim);max-width:60ch;line-height:1.5}
/* Ads run three months and come down on their own; the countdown sits on the
   meta line, quiet until the last fortnight so the board isn't urgent for
   twelve weeks. Its own float-right rather than another " · " so it reads as
   the ad's clock and not as another attribute of the role. */
.ad .adleft{float:right;color:var(--faint);letter-spacing:.06em}
.ad .adleft.soon{color:var(--act)}
.wants{display:flex;gap:6px;flex-wrap:wrap;margin:10px 0 4px}
.want{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.05em;
  text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);
  padding:6px 10px;border-radius:2px;position:relative}
button.want::after{content:"";position:absolute;inset:-6px 0}
.want.met{border-color:var(--ok);color:var(--ok)}
button.want:not(.met):hover{border-color:var(--gold);color:var(--gold)}
.lodgedrow{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.wgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(226px,1fr));gap:12px;padding:14px 0}
.wchips{display:flex;gap:5px;flex-wrap:wrap}

/* ---------- overlays ---------- */
.scrim{position:fixed;inset:0;background:rgba(24,20,12,.45);z-index:60;
  display:none;align-items:flex-start;justify-content:center;padding:52px 16px 24px;overflow-y:auto}
.scrim.show{display:flex}
.modal{background:var(--paper);border:1px solid var(--ink);max-width:500px;width:100%;
  box-shadow:6px 6px 0 rgba(32,29,21,.22);padding:15px 16px;animation:fadeUp .22s ease both}
.mhd{display:flex;justify-content:space-between;align-items:baseline;gap:10px;
  padding-bottom:9px;margin-bottom:11px;border-bottom:1px solid var(--rule)}
.mx{color:var(--dim);padding:4px 8px;position:relative}
.mx::after{content:"";position:absolute;inset:-8px}
.mx:hover{color:var(--ink)}
.mh3{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);margin:14px 0 6px}
.mrow{display:flex;gap:8px;align-items:baseline;padding:4px 0;font-size:calc(13.5px*var(--rs));line-height:1.45}
.mrow .n{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));color:var(--faint);flex:0 0 auto}
.mrow button{text-align:left;position:relative}
.mrow button::after{content:"";position:absolute;inset:-6px -3px}
.mrow button:hover{text-decoration:underline;text-underline-offset:2px}
.note{border-left:3px solid var(--rule2);padding:7px 10px;margin-bottom:8px;font-size:calc(13.5px*var(--rs));line-height:1.5}
.note .nwho{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.11em;
  text-transform:uppercase;color:var(--dim);display:block;margin-bottom:2px}
.note.author{border-left-color:var(--gold)}
.note.author .nwho{color:var(--gold)}
.nstar{float:right;margin-left:8px;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));
  color:var(--faint);border:1px solid var(--rule2);border-radius:9px;padding:3px 8px;
  display:inline-flex;gap:4px;align-items:center;position:relative}
.nstar::after{content:"";position:absolute;inset:-8px}
.nstar:hover:not(:disabled){color:var(--gold);border-color:var(--gold)}
.nstar:disabled{color:var(--gold);border-color:var(--gold)}
.composer{display:flex;gap:8px;align-items:center;margin-top:11px}
.composer input{flex:1;background:var(--paper);border:1px solid var(--rule2);
  padding:9px 10px;font-size:calc(16px*var(--rs));min-width:0}
.composer input::placeholder{color:var(--faint);font-size:calc(13.5px*var(--rs))}
.composer .cnt{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));color:var(--faint)}
#toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:80;
  background:var(--ink);color:var(--paper);font-family:"Archivo",sans-serif;
  font-size:calc(11px*var(--rs));letter-spacing:.05em;padding:9px 15px;opacity:0;pointer-events:none;
  transition:opacity .2s;max-width:calc(100vw - 32px);text-align:center}
#toast.show{opacity:1}

footer{margin-top:44px;border-top:3px double var(--rule2);padding:16px 0 46px;
  font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.05em;
  color:var(--dim);line-height:2}
footer .r{color:var(--act)} footer .gr{color:var(--ok)} footer .au{color:var(--gold)}
/* Follow the Display control: theme + size (var tokens, calc*--rs above) and
   density — the compact reading density tightens the footer like the rest. */
body[data-density="compact"] footer{line-height:1.55;padding:12px 0 30px}

/* section hues (--d-*) are defined per-edition in the token blocks above */
/* ---- edition: ember (late-shift warm dark) ---- */
[data-theme="ember"]{color-scheme:dark;--p:#191009;--p2:#211509;--mark:#E3B45E;--hi:#E3B45E;--paper:#191009;--paper2:#211509;--ink:#F1DFC4;--dim:#D6B48A;--faint:#AA865E;
  --rule:#3B2A17;--rule2:#584120;--act:#FF7A50;--ok:#95C68A;--gold:#E3B45E;
  --bad:#E8836A;--cg0:#F5C34A;--cg1:#4CC46A;--cg2:#4A90F0;--cg3:#EE5340;--cgink:#191009;
  --t0:#A8845C;--t1:#C09A6A;--t2:#DBBB86;--t3:#F1DFC4;--d-vuln:#D98A66;--d-intel:#6FB8B8;--d-blue:#8FAFD1;--d-cloud:#8FB0CC;--d-ops:#CBA070;--d-policy:#B49AD6;--d-research:#A7C078;--d-culture:#D69AB8;--d-auton:#8FB8C8;--d-strike:#D68A72;--d-seas:#89A9CB;--d-industry:#C79A5E;--d-doctrine:#AF9AD0;--d-grid:#CDB35E;--d-trade:#D08AA0;--d-state:#8FBFA0;--d-rules:#B08AC8;--d-hardware:#C0925A;--d-privacy:#8FA89A;--d-crypto:#CDB35E;--d-ew:#6FA8C0;--d-isr:#8FA890;--d-strategic:#8A8AC8;--d-definnov:#6FB0A0;--d-tradecraft:#BAA060;--d-infowar:#D0826A;--d-geopol:#C79A5E;--d-surveil:#AF8AC8;--d-corppower:#7FA0A0;--d-decentral:#8BB872;--d-neurorights:#C88AD0;--d-foresight:#8A96B8;--d-ai:#6FB89A;--d-quantum:#A88ED0;--d-biotech:#8BC072;--d-neuro:#D08AA8;--d-robotics:#7FA0C8;--d-spacef:#8A8AD0;--d-climate:#B0B85E;--d-mesh:#6FB0C0;--d-signals:#D0925F;--d-qa:#9FB2C6}
/* ---- edition: e-ink (grayscale, no motion) ---- */
[data-theme="eink"]{--p:#FFFFFF;--p2:#F3F3F1;--mark:#0E0E0E;--hi:#0E0E0E;--paper:#FFFFFF;--paper2:#F3F3F1;--ink:#0E0E0E;--dim:#2B2B2B;--faint:#4E4E4E;
  --rule:#BDBDBD;--rule2:#8F8F8F;--act:#0E0E0E;--ok:#0E0E0E;--gold:#0E0E0E;
  /* e-ink has no hue to spend: the four groups separate by tone, and the
     tier shapes (circle/square/triangle/diamond) carry the actual meaning. */
  --bad:#0E0E0E;--cg0:#E8E8E6;--cg1:#D2D2D0;--cg2:#BCBCBA;--cg3:#A6A6A4;--cgink:#0E0E0E;
  --t0:#757575;--t1:#616161;--t2:#353535;--t3:#0E0E0E;--d-vuln:#464646;--d-intel:#464646;--d-blue:#464646;--d-cloud:#464646;--d-ops:#464646;--d-policy:#464646;--d-research:#464646;--d-culture:#464646;--d-auton:#464646;--d-strike:#464646;--d-seas:#464646;--d-industry:#464646;--d-doctrine:#464646;--d-grid:#464646;--d-trade:#464646;--d-state:#464646;--d-rules:#464646;--d-hardware:#464646;--d-privacy:#464646;--d-crypto:#464646;--d-ew:#464646;--d-isr:#464646;--d-strategic:#464646;--d-definnov:#464646;--d-tradecraft:#464646;--d-infowar:#464646;--d-geopol:#464646;--d-surveil:#464646;--d-corppower:#464646;--d-decentral:#464646;--d-neurorights:#464646;--d-foresight:#464646;--d-ai:#464646;--d-quantum:#464646;--d-biotech:#464646;--d-neuro:#464646;--d-robotics:#464646;--d-spacef:#464646;--d-climate:#464646;--d-mesh:#464646;--d-signals:#464646;--d-qa:#464646}
[data-theme="eink"] *{transition:none!important;animation:none!important;box-shadow:none!important}
[data-theme="eink"] .b.solid,[data-theme="eink"] .chip.on{background:#0E0E0E;color:#fff}
[data-theme="eink"] .stamp{border-color:#0E0E0E;color:#0E0E0E}
/* ---- text size (Aa) ---- */
/* Reader type-scale — a genuinely useful range (Compact −15% ↔ Large +32%). */
[data-size="s"]{--rs:.85}
[data-size="m"]{--rs:1}
[data-size="l"]{--rs:1.32}
/* Scale the base body size too, so prose that doesn't opt into calc() moves. */
body{font-size:calc(15px*var(--rs))}
/* ---- desk switcher ---- */
.deskbar{display:flex;gap:4px 8px;align-items:baseline;justify-content:center;flex-wrap:wrap;margin:2px 0 8px;font-family:"Archivo",sans-serif}
.deskbar .dk{font-size:calc(10.5px*var(--rs));letter-spacing:.16em;padding:4px 8px;color:var(--dim);position:relative}
.deskbar .dk::after{content:"";position:absolute;inset:-9px -3px}
.deskbar .dk.on{color:var(--ink);font-weight:700;border-bottom:2px solid var(--act)}
.deskbar .dksep{color:var(--gold);font-size:calc(8px*var(--rs))}
.deskbar .lens{flex-basis:100%;text-align:center;font-family:"Newsreader",serif;font-style:italic;font-size:calc(12.5px*var(--rs));color:var(--faint);margin-top:1px}
/* ---- five tabs ---- */
.tab{padding:9px 2px 7px}
@media(max-width:560px){.tab{font-size:calc(10px*var(--rs));letter-spacing:.08em}.tab .tsub{font-size:calc(8px*var(--rs))}}
/* ---- media badges & lists ---- */
.mbadge{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));border:1px solid var(--rule2);padding:1px 6px;color:var(--dim);white-space:nowrap;align-self:center}
.mbadge.lf{border-style:dashed}
.vlist{border-top:2px solid var(--ink);margin-top:6px}
.vlist .story{border-bottom:1px solid var(--rule)}

/* ---- display panel (Aa) ---- */
.dlabel{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.16em;color:var(--dim);margin-top:12px}
.dgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:8px 0 2px}
@media(max-width:480px){.dgrid{grid-template-columns:repeat(2,1fr)}}
.dsw{border:1px solid var(--rule2);padding:0;text-align:center;position:relative;background:none}
.dsw .swatch{display:block;padding:12px 6px 9px;font-family:var(--serif);font-size:calc(19px*var(--rs));border-bottom:1px solid var(--rule2)}
.dsw .swatch i{font-style:normal;font-size:.7em;margin-left:2px;vertical-align:.12em}
.dsw[data-arg="phosphor"] .swatch{background:#0B0E11;color:#E6ECE8}
.dsw[data-arg="phosphor"] .swatch i{color:#B8E92E}
.dsw[data-arg="heritage"] .swatch{background:#FBFBF9;color:#1A1D21}
.dsw[data-arg="heritage"] .swatch i{color:#00AEEF}
.dsw[data-arg="paper"] .swatch{background:#F2EDDF;color:#201D15}
.dsw[data-arg="paper"] .swatch i{color:#A6301F}
.dsw[data-arg="slate"] .swatch{background:#EDEEE9;color:#23262A}
.dsw[data-arg="slate"] .swatch i{color:#8E3B2A}
.dsw[data-arg="dawn"] .swatch{background:#EFEDF6;color:#241E33}
.dsw[data-arg="dawn"] .swatch i{color:#6C4FD8}
.dsw[data-arg="ember"] .swatch{background:#191009;color:#F1DFC4}
.dsw[data-arg="ember"] .swatch i{color:#E3B45E}
.dsw[data-arg="eink"] .swatch{background:#FFFFFF;color:#0E0E0E;filter:grayscale(1)}
.dsw[data-arg="eink"] .swatch i{color:#0E0E0E}
.dsw .swname{display:block;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.13em;padding:7px 4px 1px;color:var(--ink)}
.dsw .swnote{display:block;font-family:"Newsreader",serif;font-style:italic;font-size:calc(11px*var(--rs));color:var(--faint);padding:0 4px 8px}
.dsw.on{border:2px solid var(--ink);margin:-1px}
.dsw.on::after{content:"✓";position:absolute;top:3px;right:6px;font-size:calc(12px*var(--rs));font-weight:700;color:var(--ok)}
.dsize{display:flex;gap:8px;margin:8px 0 2px}
.dsz{flex:1;border:1px solid var(--rule2);padding:8px 6px 7px;text-align:center;background:none;color:var(--ink)}
.dsz b{display:block;font-family:"Newsreader",serif;font-weight:500;line-height:1.25}
.dsz[data-arg="s"] b{font-size:calc(13px*var(--rs))}.dsz[data-arg="m"] b{font-size:calc(16px*var(--rs))}.dsz[data-arg="l"] b{font-size:calc(19.5px*var(--rs))}
.dsz{cursor:pointer;transition:border-color .12s,background .12s}
.dsz:hover{border-color:var(--dim)}
.dsz b{color:var(--ink)}
.dsz .swname{display:block;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.13em;color:var(--dim);margin-top:3px}
.dsz.on{border:2px solid var(--ink);padding:7px 5px 6px;background:color-mix(in srgb,var(--ink) 6%,transparent)}
.dsz.on .swname{color:var(--ink)}
/* density preview — paragraph-style bars, looser vs tighter */
.dden .dsz{padding-top:0}
.dprev{display:flex;flex-direction:column;gap:5px;padding:11px 14px 5px}
.dprev i{display:block;height:2px;background:var(--dim);border-radius:1px}
.dprev i:last-child{width:62%}
.dprev.tight{gap:2.5px}
.dprev.tight i{height:1.5px}
.dsz.on .dprev i{background:var(--ink)}
.dnote{font-family:"Newsreader",serif;font-style:italic;font-size:calc(11.5px*var(--rs));color:var(--faint);margin-top:14px}

/* ---- video masonry ---- */
.vmasonry{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:18px 16px;margin-top:12px}
@media(max-width:600px){.vmasonry{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:14px 12px}}
.vcard{break-inside:avoid;-webkit-column-break-inside:avoid;display:inline-block;width:100%;
  border:1px solid var(--rule2);border-top:3px solid var(--hue);margin:0 0 14px;background:var(--paper);vertical-align:top}
.vthumb{display:flex;width:100%;align-items:center;justify-content:center;position:relative;
  background:var(--paper2);border-bottom:1px solid var(--rule2);color:var(--hue)}
.vthumb::after{content:"";position:absolute;inset:0}
.vthumb .ic{width:30px;height:30px;fill:currentColor;stroke:none}
.vthumb.audio .ic{fill:none;stroke:currentColor;stroke-width:1.8}
.vdur{position:absolute;right:6px;bottom:6px;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));
  letter-spacing:.06em;background:var(--ink);color:var(--paper);padding:2px 6px}
.vbody{padding:9px 10px 10px}
.vsrc{display:flex;gap:6px;align-items:baseline;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--dim);margin-bottom:4px}
.vsrc .age{margin-left:auto;white-space:nowrap}
/* The dateline is fixed-width truth; the channel name yields to it. */
.vsrc .vsrcname{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vcard .hl{font-size:calc(15px*var(--rs))}
.vcard.read .hl{color:var(--dim)}
.vstory{display:block;margin-top:5px;font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));letter-spacing:.02em;color:var(--gold)}
.vstory:hover{color:var(--act)}
.vcard .smeta{margin-top:6px}
.vcard .sum{margin-top:7px}

/* ---- panel-review fixes ---- */
.hl{text-align:left;position:relative}
.hl::after{content:"";position:absolute;inset:-10px 0}
.tab .tsub{font-size:calc(9px*var(--rs));letter-spacing:.06em}
@media(max-width:479px){.tab .tsub{display:none}.tab{padding:13px 2px 12px}}
.chip .cn{font-size:calc(10px*var(--rs))}
.vdur{font-size:calc(10px*var(--rs))}
button.pill::after{inset:-13px -6px}
.wlink{position:relative;text-decoration:underline;color:var(--dim)}
.wlink::after{content:"";position:absolute;inset:-14px -8px}
.dsw .swnote{color:var(--dim)}

/* ---- archive months, rail, threads, status ---- */
.mgroup{background:color-mix(in srgb, var(--ink) calc(var(--tint,0)*4%), var(--paper));
  margin:0 -18px;padding:0 18px;transition:background .2s}
.mohd{display:flex;justify-content:space-between;align-items:baseline;gap:10px;
  border-top:3px double var(--ink);border-bottom:1px solid var(--rule2);
  margin:22px 0 4px;padding:7px 0 6px;font-family:"Archivo",sans-serif;
  font-size:calc(10.5px*var(--rs));letter-spacing:.16em;color:var(--dim)}
.mohd .mono{font-size:calc(10px*var(--rs));color:var(--faint)}
#daterail{position:fixed;right:10px;top:38%;z-index:55;writing-mode:vertical-rl;
  background:var(--ink);color:var(--paper);font-size:calc(10px*var(--rs));letter-spacing:.14em;
  padding:10px 5px;opacity:0;pointer-events:none;transition:opacity .25s;font-family:"Archivo",sans-serif}
#daterail.show{opacity:.94}
.thread{border-bottom:1px solid var(--rule);padding:12px 0 12px 10px;border-left:3px solid var(--hue)}
.thread .thn{margin:6px 0 8px;border-left:2px solid var(--rule2);padding-left:10px}
.thread .thn .mrow{display:flex;gap:8px;align-items:baseline;padding:3px 0;font-size:calc(13px*var(--rs))}
.thread .who{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.06em;color:var(--dim);white-space:nowrap}
.thread .who.au{color:var(--gold)}
.strow{display:flex;gap:8px;align-items:baseline;padding:6px 0;border-bottom:1px solid var(--rule);font-size:calc(13px*var(--rs))}
.strow .dot{width:7px;height:7px;border-radius:50%;flex:none;align-self:center}
.strow .sname{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:38%}
.strow .sfill{flex:1;color:var(--faint);font-size:calc(10px*var(--rs));letter-spacing:.04em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.schip{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.1em;padding:2px 7px;border:1px solid;flex:none}
.schip.ok{color:var(--ok);border-color:var(--ok)}
.schip.slow{color:var(--gold);border-color:var(--gold)}
.schip.down{color:var(--act);border-color:var(--act);font-weight:700}
#statLamp{font-size:calc(9px*var(--rs));vertical-align:1px}
/* ---- datasets, dial, keyboard ---- */
.mbadge.ds{background:var(--ink);color:var(--paper);border-color:var(--ink);letter-spacing:.08em}
.trendrow .seg{margin-left:auto}
/* ---- the keyboard ring ----
   The ring is an `outline` on everything, and it has to stay one. It is the
   only way to draw a box round a card that costs NO layout: the needle moves
   the class on every scroll frame, and a ring that changed a layout property
   (`position`, or a pseudo-element appearing) re-laid the page out twice a
   frame. Measured on /desk/cyber, whose panels are multicol: a scroll settled
   in 250ms-13s with a pseudo-element ring against 120-900ms with an outline.
   So the geometry below is all offsets and paddings, never boxes.

   Which leaves one real constraint: an outline takes ONE offset for all four
   edges. A river row carries padding above and below and none at the sides, so
   any offset that clears the headline vertically clears it horizontally by
   nothing — which is what made the ring read as a wire pulled tight round the
   row. The answer is the row's own padding: every row the ring lands on now
   carries 11px at the sides as well (14 on the deepest of them, the discussion
   row), and the ring sits a pixel INSIDE the row, where that padding is its
   gutter and both of the row's hairlines stay visible.
   See .drow, article.story, .eprow, .toprow, .byline, .envrow, .histrow. */
.vcard.kfocus{outline:2px solid var(--act);outline-offset:4px}
/* The ring must scroll clear of the sticky section bar, or a card arrowed to
   from below lands underneath it. scroll-margin, not scroll-padding: kSetFocus
   scrolls the CARD, and block:"nearest" is a no-op once any part of it shows. */
.story,.vcard,.eprow,.toprow,.wikicard,.taxkind,.byline,.envrow,.histrow{scroll-margin-top:70px}
/* Cards the ring reaches that are not story cards. A .wikicard is a real box
   in a 12px grid gap, so it takes a tile's +4px and still leaves 6px between
   the blue and its neighbour. The rail items and flush tail links cannot
   afford it — see below. */
.wikicard.kfocus{outline:2px solid var(--act);outline-offset:4px}
/* A kind chip is the same shape of thing in a much tighter row: .taxkinds sets
   gap:6px, and a ring reaching 4px + 2px of its own width landed exactly on
   the next chip's border, reading as one box touching another. 2px out keeps
   it off its neighbour, and the chip's own border and 5/9px padding are what
   hold the blue clear of the word. */
.taxkind.kfocus{outline:2px solid var(--act);outline-offset:2px}
/* Rail items (Hak5 and the other channel strips, /r/cyberpunk, Watch's shorts
   door, "Up next") ring exactly like a .vcard — the ring floats OUTSIDE the
   tile rather than being drawn across the thumbnail.
   These rails are overflow-x:auto, so an outward offset would be clipped at the
   strip's edges (an outline is not part of the scrollable overflow area, so it
   is cut, not scrolled to). The rails below carry enough padding to hold the
   ring, with a negative margin putting the tiles back where they were. */
.clip.kfocus,.pic.kfocus,.shtile.kfocus,.vclip.kfocus{outline:2px solid var(--act);outline-offset:4px}
.bandrow,.shtiles,.vrelated{scroll-padding-inline:7px}
/* The panel's own tail link ("+ More from The Register — Security") is a
   full-width block sitting flush under the panel, with 8px/12px of padding of
   its own: an outward offset would draw through the panel's rule, and -1px
   still leaves the ring clear of the words. */
.pmore.kfocus{outline:2px solid var(--act);outline-offset:-1px}
/* The other three tails are not blocks. .chall ("All from Hak5 →") and
   .submore are bare inline links with no padding at all, so -1px drew the ring
   through their own letters; .epmore has a dashed border of its own, and the
   ring landed a pixel inside it as a second line. All three have paper around
   them rather than a rule, so they ring outward like everything else. */
.chall.kfocus,.epmore.kfocus,.submore.kfocus{outline:2px solid var(--act);outline-offset:3px}
/* The reader page's "Read at <source>" button, which the ring is seeded on when
   an article opens. It is a filled .b.solid, so the ring cannot go inside it —
   the fill is already var(--act) and the outline would vanish into it. 3px out
   onto the paper instead, a hair tighter than a card's 4px because the actions
   bar's rules sit close above and below. */
.rsrcgo.kfocus{outline:2px solid var(--act);outline-offset:3px}
/* eink paints .b.solid near-black, so the blue ring reads as a stray hairline
   against it. Ring it in the same ink the button is set in. */
[data-theme="eink"] .rsrcgo.kfocus{outline-color:#0E0E0E}
/* Ranked table rows: outline on a <tr> is unreliable across engines, so ring
   the cells and let the row's own borders close the box. */
.vtable tbody tr.kfocus td,.wlist tbody tr.kfocus td{outline:2px solid var(--act);outline-offset:-2px}
/* Its own scroll-margin rule rather than joining the shared list above: a
   discussion row arrowed to from below otherwise lands under the section bar. */
.thr{scroll-margin-top:70px}
/* ---- the ring on a river row ----
   Every row below is flush against its neighbours under a shared hairline, so
   the ring is drawn a pixel INSIDE the row: an outward offset would put the
   blue through the next row's rule, and it would split the whitespace between
   two rows unevenly — at +2px the ring stood 11px off its own headline and 7px
   off the row above's, which is what read as crowding the neighbour. A pixel in
   leaves both hairlines showing, and the row's own padding — 11px on every side
   now, 14 on .thr — is the gutter. Anything that changes those paddings changes
   how tight the ring looks, which is why each of them says so. */
article.story.kfocus,.cluster:has(> .story.kfocus),.eprow.kfocus,
.thr.kfocus,.toprow.kfocus,.byline.kfocus,.envrow.kfocus,.histrow.kfocus{
  outline:2px solid var(--act);outline-offset:-1px}
/* The wrapper rings on the story's behalf, so the story must not ring as well.
   On the clusters river .clmore ("+N more tellings") is a sibling of the
   focused article inside .cluster, and on the editorial strip .eddesks ("Also
   on") is a sibling inside .edcard: a ring round the story alone leaves the
   fold outside the box it belongs to. The card is the unit the reader is
   looking at. */
.cluster > .story.kfocus,.edcard > .story.kfocus{outline:none}
/* An .edgrid card is not a river row — it has a border of its own and 14px of
   air under it — so its ring floats outside the card rather than sitting on
   that border. */
.edcard:has(> .story.kfocus),.edlede .edcard:has(> .story.kfocus){outline:2px solid var(--act);outline-offset:2px}
.krow{display:flex;gap:14px;align-items:baseline;padding:5px 0;border-bottom:1px solid var(--rule);font-size:calc(13.5px*var(--rs))}
.krow .keys{min-width:118px;white-space:nowrap}
kbd{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));border:1px solid var(--rule2);
  border-bottom-width:2px;padding:1px 6px;background:var(--paper2);color:var(--ink)}

/* ---- plates, briefs, lanes, cv ---- */
.wirephoto{margin:10px 0 4px;border:1px solid var(--rule2)}
.wirephoto svg{display:block;width:100%;height:96px;background:var(--paper2)}
.wirephoto figcaption{display:flex;justify-content:space-between;gap:10px;padding:5px 8px;
  border-top:1px solid var(--rule2);font-size:calc(11.5px*var(--rs));font-style:italic;font-family:"Newsreader",serif;color:var(--dim)}
.wirephoto .cred{font-family:"Archivo",sans-serif;font-style:normal;font-size:calc(9px*var(--rs));letter-spacing:.14em;color:var(--faint);white-space:nowrap;align-self:center}
.vthumb .vart{position:absolute;inset:0;opacity:.5;pointer-events:none}
.vthumb .vart svg{width:100%;height:100%}
.vthumb .ic{position:relative}
.tagbrief{border:1px solid var(--rule2);border-left:4px solid var(--ink);background:var(--paper2);
  padding:9px 12px;margin:10px 0 2px;font-size:calc(13.5px*var(--rs))}
.tagbrief .kick{margin-right:8px}
.lanes{margin-top:14px}
.lane{padding:9px 0 10px;border-bottom:1px solid var(--rule)}
.lane .lhd{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.lane .lhd b{font-size:calc(14.5px*var(--rs))}
.lane .lprog{margin-left:auto;font-size:calc(10px*var(--rs));color:var(--dim)}
.lane .lhd .stamp{margin-left:auto}
.lbar{height:4px;background:var(--rule);margin:7px 0 6px;position:relative}
.lbar i{position:absolute;inset:0 auto 0 0;background:var(--ink);transition:width .3s}
.lane.open .lbar i{background:var(--ok)}
.lreq{display:flex;gap:6px 14px;flex-wrap:wrap;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.05em;color:var(--faint)}
.lreq .ok{color:var(--ok)}
.cvpre{font-family:"Archivo",sans-serif;font-size:calc(11.5px*var(--rs));line-height:1.55;white-space:pre-wrap;
  background:var(--paper2);border:1px solid var(--rule2);padding:12px 14px;margin:0;color:var(--ink)}

@media(max-width:640px){.trendrow .seg{margin-left:0}}

@media(max-width:640px){.strow .sfill{display:none}}
@media(max-width:560px){.tab{font-size:calc(10px*var(--rs));letter-spacing:.03em}}

/* font vars (--sans/--serif/--disp) are defined in the top :root block */
/* ---- broadsheet measure: the prototype's 1200px page ---- */
.sheet{max-width:1200px;padding:0 20px}
body{line-height:1.5}
.hl{font-weight:600;letter-spacing:-.008em;line-height:1.3;font-size:calc(17px*var(--rs))}
.sum{font-size:calc(15.2px*var(--rs));line-height:1.62;max-width:68ch}
.pzhead h2{font-size:calc(38px*var(--rs));letter-spacing:-.02em;line-height:1.08}
.pzhead .sub{font-size:calc(16px*var(--rs));max-width:60ch}
.story{padding:14px 0}
.story:hover{background:color-mix(in srgb, var(--ink) 3%, transparent)}
/* ---- one voice for chrome; mono is the wire ---- */
.tab,.kick,.b,.g,.seg button,.dlabel,.deskbar .dk,.vsrc,.chip,.ipill,.cpill,.lnk,.sel,.jnav button{font-family:var(--sans)}
.tab{font-weight:700}.kick{font-weight:600;letter-spacing:.14em}
/* ---- out of the boxes: hairlines + whitespace, not borders ---- */
.panel{border:0;background:transparent;padding:0 0 6px}
.phd{background:transparent;border:0;border-top:2px solid var(--ink);padding:8px 0 2px}
.ptail{background:transparent;border:0;padding:2px 0 4px;font-style:italic}
.teaser{border:0;background:transparent;border-top:3px double var(--ink);border-bottom:1px solid var(--rule);padding:14px 2px}
.ad{border:0;background:transparent;border-top:1px solid var(--rule);padding:18px 0}
.standing{border:0;background:transparent;border-top:3px double var(--ink);padding:14px 0 6px}
.statgrid .stat{border:0;background:transparent;border-left:2px solid var(--rule);padding-left:10px}
.tagbrief{background:transparent;border:0;border-left:3px solid var(--gold);padding:8px 0 8px 14px}
.vcard{border:0;background:transparent}
.vcard .vthumb{border:1px solid var(--rule2)}
.vbody{padding:9px 2px 4px}
.modal{border-width:1px}
.gnote{background:transparent}
/* ---- career layer ---- */
.jnav{display:flex;margin:6px 0 18px}
.jnav button{flex:1;padding:9px 6px;font-size:calc(12px*var(--rs));font-weight:600}
.jnav .jc{font-size:calc(9.5px*var(--rs));opacity:.7;margin-left:3px}
.jseg{display:inline-flex;margin:6px 0 14px}
.jsec{border-top:1px solid var(--rule);padding:16px 0 14px}
.jshd{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}
.sel{font-size:calc(12px*var(--rs));padding:7px 10px;background:var(--paper);color:var(--ink);border:1px solid var(--rule2)}
.ta{width:100%;margin-top:8px;padding:10px 12px;font-size:calc(15px*var(--rs));font-family:"Newsreader",serif;background:transparent;border:1px solid var(--rule2);color:var(--ink)}
.lnk{background:none;border:0;text-decoration:underline;color:var(--dim);font-size:calc(12.5px*var(--rs));padding:2px 4px;position:relative}
.lnk::after{content:"";position:absolute;inset:-10px -6px}
.lnk.act{color:var(--act)}
.dz{display:flex;flex-direction:column;gap:2px;align-items:center;text-align:center;padding:22px 12px;margin:6px 0;
  border:2px dashed var(--rule2);cursor:pointer}
.dz.drag{border-color:var(--ink);background:color-mix(in srgb,var(--ink) 4%,transparent)}
.cvcard{border-left:3px solid var(--ok);padding:10px 0 10px 14px;margin:10px 0}
.chiprow{display:flex;gap:6px 8px;flex-wrap:wrap;align-items:center}
.cpill{font-size:calc(11px*var(--rs));padding:4px 10px;border:1px solid var(--rule2);color:var(--dim);display:inline-flex;gap:5px;align-items:center}
.cpill.held{border-color:var(--ink);color:var(--ink);font-weight:600}
.cpill.warn{border-color:var(--gold);color:var(--gold)}
.cpill.big{padding:6px 12px}
.cpill.lock{opacity:.75}
.ipill{font-size:calc(12px*var(--rs));padding:6px 13px;border:1px solid var(--rule2);color:var(--ink);background:transparent}
.ipill.on{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.tdot{display:inline-block;border-radius:50%}
.gaprow{display:flex;gap:10px;align-items:baseline;padding:7px 0;font-size:calc(14.5px*var(--rs))}
/* wrap: a fourth button in this row pushed the overview 60px past a phone. */
.openrow{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;border-top:1px solid var(--rule);margin-top:14px;padding-top:12px}
.tog{width:46px;height:26px;background:var(--rule2);position:relative;flex:none}
.tog i{position:absolute;top:3px;left:3px;width:20px;height:20px;background:var(--paper);transition:left .2s}
.tog.on{background:var(--ok)}.tog.on i{left:23px}
.rl{border-top:1px solid var(--rule);padding:12px 0 10px}
.rlhd{display:flex;gap:10px;align-items:baseline;width:100%;text-align:left}
.rlhd .rn{font-family:"Newsreader",serif;font-weight:600;font-size:calc(17px*var(--rs));flex:1}
.rlhd .rp{font-size:calc(16px*var(--rs));font-weight:600}
.rlbar{height:6px;background:color-mix(in srgb,var(--ink) 10%,transparent);position:relative;overflow:hidden;margin:8px 0 5px}
.rlbar i,.rlbar em{position:absolute;top:0;bottom:0;left:0}
.rlbar i{background:var(--ok)}
.rlbar em{background:var(--gold);opacity:.55}
.skrow{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;padding:7px 0 6px;border-top:1px dashed var(--rule)}
.skrow b{font-size:calc(14px*var(--rs));font-weight:600}
.ski{width:18px;text-align:center;font-weight:700}
.ski.v{color:var(--ok)}.ski.c{color:var(--gold)}.ski.n{color:var(--faint)}
.locknote{font-size:calc(14.5px*var(--rs))}
.bq{border-top:1px solid var(--rule);padding:13px 0}
.vpill{font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));padding:5px 12px;border:1px solid var(--rule2);color:var(--dim);background:transparent}
.vpill.up{border-color:var(--ok);color:var(--ok)}
.vpill.dn{border-color:var(--act);color:var(--act)}
.vpill:disabled{opacity:.45}
.cardrow{border-top:1px solid var(--rule);padding:12px 0}
.opt{display:block;width:100%;text-align:left;border:1px solid var(--rule2);background:transparent;
  color:var(--ink);padding:10px 12px;margin-top:8px;font-size:calc(14.5px*var(--rs));line-height:1.45}
.opt.good{border-color:var(--ok)}.opt.mid{border-color:var(--gold)}.opt.bad{border-color:var(--act)}
[data-theme="eink"] .story:hover{background:transparent}
[data-theme="eink"] .rlbar i{background:#0E0E0E}
@media(max-width:560px){.jnav button{font-size:calc(10.5px*var(--rs));padding:9px 2px}.pzhead h2{font-size:calc(30px*var(--rs))}}

/* ============================================================
   PRESS LAYER — v3, after the panel sat.
   Three voices, one job each:
     Besley (display)     — the nameplate, page titles, big figures
     Newsreader (text)    — headlines and prose, optical axis engaged
     Archivo (wire)       — chrome, labels, and anything that is data
   The sans is gone. The pills are gone. Boxes become rules:
   top rules, left rules, column rules. Corners are square —
   this is a paper, not an app.
   ============================================================ */

/* ---- display ---- */
h1.masthead{font-family:var(--disp);font-weight:800;letter-spacing:.004em;font-size:clamp(32px,6.6vw,56px)}
h1 .mstar{font-size:.5em;vertical-align:.22em}
.motto{letter-spacing:.2em}
.pzhead h2,.cahead h2{font-family:var(--disp);font-weight:800;font-size:calc(clamp(27px,4.6vw,36px)*var(--rs));line-height:1.1;letter-spacing:-.004em}
.pzhead .sub,.cahead .sub{font-style:italic;font-size:calc(15.5px*var(--rs))}
.stat b{font-family:var(--disp);font-weight:700;font-size:calc(22px*var(--rs))}
.gend .big{font-family:var(--disp);font-weight:700}
.ad .at{font-family:var(--disp);font-weight:700;font-size:calc(19px*var(--rs))}
.rlhd .rn{font-family:var(--disp);font-weight:600}
.lane .lhd b{font-family:var(--disp);font-weight:600;font-size:calc(15px*var(--rs))}

/* ---- text: Newsreader as a headline face, not a default ---- */
.hl,.toprow .tt{font-weight:500;letter-spacing:0;font-variation-settings:"opsz" 21}
.hl{line-height:1.27}
article.story.read .hl{font-weight:400}
.sum{line-height:1.6}
.deskbar .lens{font-size:calc(13px*var(--rs))}

/* ---- the wire voice, aired out a little ---- */
.tab{font-weight:600;letter-spacing:.1em}
.kick{letter-spacing:.16em}
.b{letter-spacing:.11em;padding:9px 15px}
.chip{padding:7px 11px}

/* ---- the pill era ends ---- */
.b,.g,.chip,.seg,.seg button,.pill,.want,.slot,.count,.phd .count,.nstar,.ipill,.cpill,.vpill,.sel,.opt,.ta,.dz,.lnk,.tog,.tog i,.rlbar,.rlbar i,.rlbar em,
.nlchecks input[type=checkbox],.deskrows input[type=checkbox]{border-radius:0}
.tog{width:42px;height:22px;background:var(--paper);border:1px solid var(--rule2)}
.tog i{top:2px;left:2px;width:16px;height:16px;background:var(--rule2)}
.tog.on{background:var(--paper);border-color:var(--ok)}
.tog.on i{left:22px;background:var(--ok)}

/* ---- out of the boxes, second pass ---- */
.colwrap{column-gap:28px;column-rule:1px solid var(--rule)}
.panel{margin:0 0 22px}
.sechd{margin:32px 0 14px}
.jsec{padding:20px 0 16px}
.ad{padding:20px 0}
.qa{border:0;border-left:3px solid var(--rule2);padding:6px 0 6px 13px}
.gend{border:0;border-top:3px double var(--ink);padding:14px 2px 4px}
.gband{border:0;border-left:5px solid var(--t0);background:color-mix(in srgb,var(--ink) 3%,transparent)}
.choice{border:0;border-bottom:1px solid var(--rule);border-left:3px solid transparent;margin-bottom:0}
.two .choice,.optgrid .choice{border:1px solid var(--rule2)}
.opt{border:0;border-bottom:1px solid var(--rule);border-left:3px solid transparent;margin-top:0;padding:11px 10px}
.wline{border:0;border-left:3px solid var(--rule2);border-bottom:1px solid var(--rule);margin-bottom:0;padding:10px 10px 10px 12px}
.wireflash{border:0;border-top:3px solid var(--act);border-bottom:1px solid var(--rule);padding:9px 2px}
.workshop{gap:18px}
.wk{border:0;border-left:1px dashed var(--rule2);padding:2px 0 2px 12px}
[data-theme="eink"] .gband{background:transparent}
@media(max-width:560px){h1.masthead{letter-spacing:0}}

/* ---- interleaved bands: the reel & the picture desk ---- */
.band{margin:28px 0 6px;border-top:1px solid var(--rule);padding-top:12px}
.bandhd{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:11px}
.bandcr{font:500 calc(9.5px*var(--rs)) "Archivo",sans-serif;letter-spacing:.09em;color:var(--faint);text-transform:uppercase}
/* The kicker is a LINK now (the channel's own archive), so it must keep looking
   like a kicker and not like body copy: inherit .kick's colour, underline only
   on hover/focus. Scoped to .bandhd — .kick is a shared atom used in a dozen
   places that are still plain text. */
.bandhd a.kick{color:var(--dim);text-decoration:none}
.bandhd a.kick:hover,.bandhd a.kick:focus-visible{color:var(--act);text-decoration:underline;text-underline-offset:3px}
/* padding + matching negative margin: room for the keyboard ring's outward
   offset (app.css ~810) without moving the tiles a pixel. Vertical too — an
   overflow-x:auto box computes overflow-y to auto, so the ring's top edge would
   be clipped just as surely as its left one. */
.bandrow{display:flex;gap:16px;overflow-x:auto;padding:7px 7px 8px;margin:-7px -7px 0;scrollbar-width:thin}
.clip,.pic{flex:0 0 212px;background:none;border:0;padding:0;margin:0;text-align:left;font:inherit;color:inherit}
.clip{cursor:pointer}
.cthumb{position:relative;display:block;height:112px;border:1px solid var(--rule);background:var(--paper2)}
.cthumb svg:first-child{width:100%;height:100%}
.cthumb>.ic{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:30px;height:30px;color:var(--paper);background:var(--ink);padding:7px}
.cthumb .vdur{position:absolute;right:6px;bottom:6px;font:600 calc(9.5px*var(--rs)) "Archivo",sans-serif;background:var(--ink);color:var(--paper);padding:2px 5px;letter-spacing:.06em}
.cch{display:block;margin-top:8px;font:600 calc(9.5px*var(--rs)) "Archivo",sans-serif;letter-spacing:.11em;text-transform:uppercase;color:var(--dim)}
.clip .ct{display:block;font-family:"Newsreader",serif;font-variation-settings:"opsz" 21;font-size:calc(14.5px*var(--rs));line-height:1.32;margin-top:3px}
.clip:hover .ct{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
.pic figcaption{font-size:calc(12px*var(--rs));color:var(--dim);margin-top:7px;line-height:1.4;font-style:italic}
.pic .cred{display:block;font:500 calc(9px*var(--rs)) "Archivo",sans-serif;letter-spacing:.1em;color:var(--faint);margin-top:3px;text-transform:uppercase;font-style:normal}
@media(max-width:640px){.clip,.pic{flex-basis:178px}.cthumb{height:96px}}

/* ---- reading time / compact / panel-more / wire-more / tag bar ---- */
.rt{font:500 calc(9px*var(--rs)) "Archivo",sans-serif;letter-spacing:.08em;color:var(--faint);text-transform:uppercase;white-space:nowrap}
/* Compact rows keep their tight 7px of leading — that is what the reader
   chose — but take the same 11px at the sides as a full card. Side padding
   costs no rows on the screen and it is what holds the keyboard ring off the
   headline; without it the ring ran against the words in this mode only. */
.story.cmpct{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;padding:7px 11px;border-bottom:1px solid var(--rule)}
.story.cmpct .hl{flex:1 1 auto;min-width:58%;font-size:calc(14.5px*var(--rs));line-height:1.32}
.story.cmpct .g{font:500 calc(10.5px*var(--rs)) "Archivo",sans-serif;white-space:nowrap}
.story.cmpct .g.z{color:var(--faint)}
.story.cmpct .sum{flex-basis:100%;margin:3px 0 2px}
.pmore{display:block;width:100%;text-align:center;letter-spacing:.25em;border-top:1px dashed var(--rule);margin-top:2px;padding:8px 12px;
  font:500 calc(10px*var(--rs)) "Archivo",sans-serif;letter-spacing:.07em;color:var(--dim);text-transform:uppercase}
.pmore:hover{color:var(--ink);background:color-mix(in srgb,var(--ink) 3%,transparent)}
.wiremore{display:block;width:100%;text-align:center;border:1px dashed var(--rule2);margin:18px 0 6px;padding:12px;
  font:600 calc(10.5px*var(--rs)) "Archivo",sans-serif;letter-spacing:.1em;color:var(--dim);text-transform:uppercase}
.wiremore:hover{color:var(--ink)}
.wiretags{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:12px 0 2px;padding-bottom:10px;border-bottom:1px solid var(--rule)}
.wiretags .kick{font-size:calc(9.5px*var(--rs))}
.wiretags .cn{opacity:.6;margin-left:4px;font-size:calc(9px*var(--rs))}
.wiretags .clr{color:var(--act);border-color:var(--act)}

/* ---- panel round 2: reach, figures, quiet chrome ---- */
.teaser.slim{padding:8px 12px;margin:12px 0 2px}
.teaser.slim .pt{font-size:calc(13.5px*var(--rs))}
.g{padding:5px 7px;margin:-3px -2px}
.pill{padding:4px 8px}
.chip{min-height:32px}
.pmore{min-height:34px}
.mono,.age,.stat b,.statline,.gclock,.vdur,.rp,.count{font-feature-settings:"tnum" 1}

/* ---- force-dark shield: darkeners skip gradient paint ---- */
html,body{background-image:linear-gradient(var(--paper),var(--paper))}
.panel,.modal,.topbox,.teaser,.standing,.ad,.jsec,.bq,.rl,.gend,.qa{background-image:linear-gradient(var(--paper),var(--paper))}

/* ---- editor's desk ---- */
.edesk{border:1px solid var(--gold);padding:14px 16px 16px;margin:0 0 26px;background-image:linear-gradient(var(--paper),var(--paper))}
.edhd{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
.edesk .edays{margin:10px 0 2px;flex-wrap:wrap}
.rev{border-top:1px solid var(--rule);margin-top:16px;padding-top:12px}
.revhd{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:9px}

/* ===== live-app additions: small item images, density, display panel ===== */
/* Per-item image (og:image) renders as a small thumbnail, never full-bleed. */
/* Anchored sections clear the sticky section scroll bar. [id^=sec-] covers
   every scrollspy anchor (the media rivers' section groups and the clusters
   page's sechd anchors included), same offset as the .secgroup rule the news
   desks already relied on — a chip jump never hides a header under the bar. */
.secgroup,[id^="sec-"]{scroll-margin-top:52px}
.story.withpic{display:flex;gap:12px;align-items:flex-start}
.story.withpic .stext{flex:1;min-width:0}
.sthumb{order:2;flex:0 0 auto;width:78px;height:78px;overflow:hidden;margin-top:2px;
  border:1px solid var(--rule);background:var(--paper2)}
.sthumb img{width:100%;height:100%;object-fit:cover;display:block}
@media(max-width:520px){.sthumb{width:60px;height:60px}}
.wirelist{display:flex;flex-direction:column}
/* pager — the foot of a paged listing (a source archive, a tag). Both ends are
   always drawn; at the first and last page the end that has nowhere to go is a
   dead <span>, so the row keeps its width and the live end keeps its position
   as the reader walks through. 44px minimum, because paging is a thing readers
   do repeatedly and often on a phone. */
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin:18px 0 6px;padding-top:14px;border-top:1px solid var(--rule)}
.pgb{font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;color:var(--ink);
  border:1px solid var(--rule2);padding:9px 14px;min-height:44px;
  display:inline-flex;align-items:center;white-space:nowrap}
.pgb:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}
.pgb.off{color:var(--faint);border-color:var(--rule);cursor:default}
.pgb.off:hover{background:none}
.pgn{font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim);
  text-align:center;line-height:1.4;font-feature-settings:"tnum" 1}
.pgn em{display:block;font-style:normal;color:var(--faint);font-size:calc(10px*var(--rs))}
@media(max-width:400px){.pgb{padding:9px 10px;letter-spacing:.03em}}
.starN{font-feature-settings:"tnum" 1}
/* item-page lead image, capped medium (not full width) */
.leadimg{float:right;max-width:220px;width:40%;height:auto;margin:0 0 10px 16px;border:1px solid var(--rule)}
@media(max-width:520px){.leadimg{float:none;width:100%;max-width:100%;margin:0 0 12px}}
/* Article images: a small gallery strip at the top of the abstract, never a
   wide inline float. Inline images in the prose are capped too. */
.artgallery{display:flex;gap:8px;flex-wrap:wrap;margin:6px 0 14px}
.artgallery img{height:118px;width:auto;max-width:100%;object-fit:cover;border:1px solid var(--rule2);display:block}
@media(max-width:520px){.artgallery img{height:auto;width:100%}}
/* embedded video viewing area on the reader page (16:9) */
.rvideo{position:relative;width:100%;aspect-ratio:16/9;margin:8px 0 16px;background:#000;border:1px solid var(--rule2)}
.rvideo iframe,.rvideo video{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
.rlede img,.rlede figure{max-width:100%;height:auto}
.rlede img{max-height:220px;width:auto;display:block;margin:10px 0;border:1px solid var(--rule2)}

/* Full vs Compact density — preference stored in localStorage, set on <body>. */
[data-density="compact"] .story .sum,
[data-density="compact"] .story .sthumb,
[data-density="compact"] .story .rt{display:none}
[data-density="compact"] .fp-sum{display:none}

/* Long Reads is the one river a reader picks from BY LENGTH — the shelf's
   whole promise is the sit-down — so there the estimate stops being a faint
   aside between the source name and the tags. It leads the meta row, in ink,
   at a size that reads without leaning in, and it survives compact density,
   which hides .rt everywhere else. The number itself is unchanged; only its
   standing on the page is. The hook is main[data-tab] (layout.html), because
   media.html draws Watch and Listen from this same markup and neither wants
   this. Hovering gives the captured word count (ItemCard.WordsLabel). */
main[data-tab="longform"] .story .rt{order:-1;display:inline-flex;align-items:center;gap:4px;
  font:600 calc(10.5px*var(--rs)) "Archivo",sans-serif;letter-spacing:.07em;color:var(--ink);
  background:var(--paper2);padding:3px 8px}
/* Compact hides the estimate everywhere else; on Long Reads it is the one
   thing the row is for, so it stays — and stays a flex chip, or it loses
   its baseline against the tag pills. */
[data-density="compact"] main[data-tab="longform"] .story .rt{display:inline-flex}
[data-density="compact"] .story{padding:7px 11px}

/* Display control (theme + type size + density) — header dropdown. */
.dispctl.hdr{position:relative;display:inline-block}
.dispctl.hdr>summary{cursor:pointer;list-style:none;font-family:var(--sans);font-size:calc(11px*var(--rs));letter-spacing:.04em;color:var(--dim)}
.dispctl.hdr>summary::-webkit-details-marker{display:none}
.dispctl.hdr>summary:hover{color:var(--ink)}
.dispctl.hdr[open]>summary{color:var(--ink)}
.dispctl .disppanel{position:absolute;right:0;top:calc(100% + 6px);z-index:60;min-width:300px;
  background:var(--paper);border:1px solid var(--rule2);padding:12px 14px;text-align:left;
  box-shadow:0 8px 26px rgba(0,0,0,.16)}
.dispctl .dgrid{grid-template-columns:repeat(2,1fr);gap:6px;margin:8px 0 2px}
.dispctl .dsw .swname{padding:5px 4px 1px;font-size:calc(9px*var(--rs))}
.dispctl .dsw .swnote{padding:0 4px 6px;font-size:calc(10px*var(--rs))}
.dispctl .dlabel{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.1em;color:var(--faint);text-transform:uppercase;margin:2px 0 6px}
.dispctl .drow{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
.dispctl .drow:last-child{margin-bottom:0}
.dispctl .drow .g.on{color:var(--gold)}

/* jobs nav tabs rendered as links (server-side ?tab= navigation) */
.jnav a{flex:1;padding:9px 6px;font-size:calc(12px*var(--rs));font-weight:600;text-align:center;
  text-decoration:none;color:var(--dim);border-right:1px solid var(--rule2);position:relative}
.jnav a:last-child{border-right:0}
.jnav a:hover{color:var(--ink)}
.jnav a.on{background:var(--ink);color:var(--paper)}
.jnav a.on:hover{color:var(--paper)}
.wgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(258px,1fr));gap:12px}

/* ===== iteration 5: favicons, density, section + desk styling ===== */
/* Source favicon / monogram on cards. */
.smeta .src{display:inline-flex;align-items:center;gap:5px}
.fav{width:14px;height:14px;flex:0 0 auto;border:1px solid var(--rule);display:inline-flex;
  align-items:center;justify-content:center;font:700 calc(8px*var(--rs)) "Archivo",sans-serif;color:var(--dim);
  background:var(--paper2);overflow:hidden;line-height:1}
/* Favicons render greyscale — colour is meaningful in this paper, never
   decorative, so source marks sit in the monochrome band with everything else. */
img.fav{object-fit:cover;filter:grayscale(1);opacity:.9}

/* Compact view: favicon only (no source name), no tags. */
[data-density="compact"] .smeta .srcname{display:none}
[data-density="compact"] .smeta a.pill{display:none}

/* Most Starred window toggle (links styled like .seg). */
.tophd .seg a{font-family:var(--sans);font-size:calc(9.5px*var(--rs));font-weight:600;letter-spacing:.04em;
  padding:5px 9px;color:var(--dim);text-decoration:none;border-right:1px solid var(--rule2)}
.tophd .seg a:last-child{border-right:0}
.tophd .seg a:hover{color:var(--ink)}
.tophd .seg a.on{background:var(--ink);color:var(--paper)}

/* Colour-coded section headers (--sh = the section hue). */
.sechd{color:var(--sh,var(--ink));border-left:4px solid var(--sh,var(--rule2));padding-left:10px}
.sechd .dot{background:var(--sh,var(--ink))}
.sechd i{background:var(--sh,var(--rule));opacity:.35}
.sechd .n{color:var(--faint)}

/* Desks rendered as tabs. */
.deskbar{gap:4px 6px}
.deskbar .dksep{display:inline;color:var(--gold);font-size:calc(8px*var(--rs))}
.deskbar .dk{border:0;padding:4px 8px;font-weight:500;color:var(--dim);letter-spacing:.16em;border-bottom:2px solid transparent}
.deskbar .dk:hover{color:var(--ink)}
.deskbar .dk.on{color:var(--ink);font-weight:700;border-bottom:2px solid var(--hi);background:none}

/* ---- desk move, in flight ----
   Tab/⇧Tab unloads the document, and until the next desk paints the keystroke
   has no answer at all — so the reader presses it again. This is the receipt:
   a hairline at the top of the viewport, and the chip you asked for lit as
   pending. --act (now/live) rather than the settled chip's --hi, so in-flight
   and arrived never read as the same state.

   The RESTING state is visible on purpose. The blanket animation:none of the
   reduced-motion query (~line 259) and of the e-ink edition (~line 663) would
   otherwise leave a keyframe-revealed bar invisible — the same trap already
   written up for cxReveal. Motion is garnish; the rule itself is the message.

   ::before, not ::after — ::after is the chip's invisible hit-area expander. */
#navwait{position:fixed;left:0;top:0;height:2px;width:100%;z-index:100;
  background:color-mix(in srgb,var(--act) 20%,transparent);
  opacity:0;transition:opacity .12s ease;pointer-events:none}
#navwait.on{opacity:1}
#navwait i{display:block;height:100%;width:100%;background:var(--act);
  transform-origin:0 50%;animation:navwait 1.1s cubic-bezier(.4,0,.2,1) infinite}
@keyframes navwait{0%{transform:scaleX(.04);opacity:1}60%{transform:scaleX(.7);opacity:1}100%{transform:scaleX(1);opacity:.3}}
.deskbar .dk.pending{color:var(--ink);border-bottom:2px solid var(--act);opacity:.7}
/* Same receipt for the section row, which ⇧Tab now walks. .tab already
   carries a 3px bottom border (transparent at rest, --hi when on), so this
   only recolours it — declaring a border here would change the row's height
   under the pending chip and shift every tab beside it. */
.tabs .tab.pending{color:var(--ink);border-bottom-color:var(--act);opacity:.7}

/* Share popover (platform-specific buttons). */
.sharepop{position:absolute;z-index:90;min-width:190px;background:var(--paper);border:1px solid var(--rule2);
  box-shadow:0 10px 30px rgba(0,0,0,.2);padding:6px}
.sharepop .shtitle{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);padding:4px 8px 6px}
.sharepop .shbtn{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  font-family:var(--sans);font-size:calc(12.5px*var(--rs));color:var(--ink);text-decoration:none;padding:7px 8px}
.sharepop .shbtn:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}

/* Video page: real previews (YouTube) or art-plate placeholders. */
.vmasonry .vthumb{position:relative;aspect-ratio:16/9;height:auto;overflow:hidden;padding:0;border:1px solid var(--rule2);background:#000}
.vmasonry .vthumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .35s ease}
.vmasonry .vthumb.art{background:color-mix(in srgb, var(--hue,var(--ink)) 12%, var(--paper2))}
.vmasonry .vthumb .ic{position:relative;z-index:1;width:34px;height:34px;color:#fff;background:rgba(0,0,0,.55);border-radius:50%;padding:9px;transition:transform .2s ease, background .2s ease}
.vmasonry .vthumb.art .ic{color:var(--hue,var(--ink));background:transparent}
.vmasonry .vdur{z-index:1}
.vmasonry .vsrc{display:flex;align-items:center;gap:5px}
/* Hover: lift the card, zoom the still, light the play button (YouTube red). */
.vcard{transition:transform .18s ease}
.vcard:hover{transform:translateY(-3px)}
.vcard:hover .vthumb img{transform:scale(1.06)}
.vcard:hover .vthumb .ic{transform:scale(1.12);background:#c8281e}
.vcard:hover .vthumb.audio .ic{background:var(--sh,#c8281e)}
.vcard:hover .hl{text-decoration:underline;text-underline-offset:3px}
/* Channel header (one per source on Watch). */
.vidsec{margin-top:4px}
/* Watch: the "Shorts" band divider between long-form videos and shorts */
.shortsband{display:flex;align-items:baseline;gap:10px;margin:40px 0 4px;padding-bottom:6px;border-bottom:3px double var(--ink)}
.shortsband .kick{font-size:calc(15px*var(--rs));color:var(--act)}
.shortsband .shortsdek{font:italic 400 calc(12.5px*var(--rs)) var(--serif);color:var(--faint)}
.vidhd{display:flex;align-items:center;gap:10px;margin:32px 0 6px;padding-bottom:10px;border-bottom:2px solid var(--sh,var(--rule2))}
.vidhd i{flex:1}
.vidhd .chfav{width:26px;height:26px;border-radius:50%;object-fit:cover;flex:0 0 auto;border:1px solid var(--rule2);background:var(--paper2)}
.vidhd span.chfav{display:flex;align-items:center;justify-content:center;font:700 12px var(--sans);color:var(--sh,var(--ink))}
.vidhd .chname{font-family:"Archivo",sans-serif;font-weight:700;font-size:calc(15.5px*var(--rs));letter-spacing:.01em;color:var(--sh,var(--ink))}
.vidhd .chn{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.vidhd .chall{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--dim);white-space:nowrap}
.vidhd .chall:hover{color:var(--sh,var(--ink))}
@media(max-width:600px){.vidhd .chall{display:none}}

/* Interspersed media bands: real images in the thumbnails. */
.cthumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.pic .ct{display:block;font-family:"Newsreader",serif;font-size:calc(14px*var(--rs));line-height:1.35;margin-top:7px;color:var(--dim)}

/* ===== iteration 8: tags row, newsletter link, section nav, display popover ===== */
/* Card tags: a single clipped row. */
.smeta .tags{display:inline-flex;gap:2px 7px;min-width:0;overflow:hidden;flex-wrap:nowrap;white-space:nowrap;align-items:center}

/* Colour-coded section nav — multi-line at rest, collapses to a single scrolling
   line once stuck. The collapse is a height change; if it fired the instant the
   bar pins, scroll-anchoring would compensate scrollY and oscillate the state
   (the old flicker). The JS toggles `.stuck` with a scroll BUFFER + hysteresis
   band wider than the collapse delta, so it can't oscillate (see app.js). */
.newsbar-sentinel{height:1px}
.newsbar .chips{flex-wrap:wrap;overflow-x:visible}
.newsbar .chip{border-color:var(--hue,var(--rule2))}
.newsbar .chip .cn{color:var(--hue,var(--faint));opacity:.95;font-weight:600}
.newsbar .chip.all{border-color:var(--rule2)}
.newsbar .chip:hover{background:var(--hue,var(--ink));border-color:var(--hue,var(--ink));color:var(--paper)}
.newsbar .chip:hover .cn{color:var(--paper)}
.newsbar.stuck .chips{flex-wrap:nowrap;overflow-x:auto}

/* Display popover always on-screen; a bottom sheet on narrow windows. */
.dispctl .disppanel{max-width:calc(100vw - 20px)}
@media(max-width:640px){
  .dispctl.hdr{position:static}
  .dispctl .disppanel{position:fixed;left:10px;right:10px;top:auto;bottom:14px;min-width:0;max-width:none;
    box-shadow:0 -8px 30px rgba(0,0,0,.24)}
}

/* Watch & Listen media dividers. */
.mediahd{font-family:var(--disp);font-weight:800;font-size:calc(22px*var(--rs));margin:26px 0 6px;
  border-bottom:2px solid var(--rule2);padding-bottom:6px;display:flex;align-items:center;gap:8px}
.mediahd .ic{fill:currentColor}

/* ============================================================
   PROTOTYPE 2 CHROME — the nameplate star, the watermark,
   the rotating epigraph, and the folio furniture.
   ============================================================ */
/* faint compass-star watermark behind the whole page */
.watermark{position:fixed;inset:0;z-index:0;display:flex;align-items:center;justify-content:center;pointer-events:none}
.watermark svg{width:min(76vmin,860px);height:min(76vmin,860px);color:var(--mark);opacity:.02}
[data-theme="eink"] .watermark{display:none}
.sheet{position:relative;z-index:1}

/* nameplate: a classic serif, the star in the highlighter ink */
h1.masthead{font-family:Georgia,"Times New Roman",serif;font-weight:800;letter-spacing:.004em;
  font-size:clamp(32px,5vw,52px);line-height:1.05;margin:16px 0 8px;font-variation-settings:normal}
h1.masthead a{font-family:inherit}
h1.masthead svg.mstar{width:.62em;height:.62em;color:var(--mark);vertical-align:.09em;margin:0 .05em;display:inline-block}

/* rotating epigraph, replacing the static motto.

   The box must not change height as quotes rotate, or the whole page walks.
   A fixed two-line reserve (37.5px, from the quote's own 12.5px/1.5 line)
   proved wrong twice over: each line box also carries the strut of
   .epi-inner's inherited body font (15px/1.55 = 23.25px), so two real lines
   are ~46.5px; and on narrow viewports the long quotes wrap to three lines,
   which no fixed reserve chosen at desktop width can hold.

   So the box now sizes itself to its tallest quote AT THE CURRENT WIDTH:
   app.js stacks a hidden copy of every quote (.epi-sizer) in the same grid
   cell as the live one, and the tallest copy sets the height. The min-height
   below is only the no-JS / static-motto floor. The quote is centred in the
   cell so shorter quotes sit still in the middle.

   .epi-inner is inline text flow, not a flex row. As a wrapping flex container
   it put the nowrap byline on a line of its OWN, so a two-line quote already
   rendered as three. The sizer copies reuse .epi-q/.epi-by so they wrap
   exactly like the live quote. */
.epigraph{display:grid;align-items:center;justify-items:center;text-align:center;
  padding-bottom:12px;border-bottom:3px double var(--rule2);
  min-height:calc(37.5px*var(--rs) + 15px);margin-top:2px}
.epigraph .epi-inner,.epigraph .epi-sizer{grid-area:1/1;max-width:88ch}
.epigraph .epi-inner{transition:opacity .45s ease}
.epigraph .epi-sizer{visibility:hidden;display:grid;justify-items:center}
.epigraph .epi-sizer>span{grid-area:1/1}
.epigraph .epi-q{font:italic 400 calc(12.5px*var(--rs))/1.5 var(--serif);color:var(--dim)}
.epigraph .epi-by{font:500 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--faint);white-space:nowrap;margin-left:9px}

/* folio furniture: the invitations gift box. Icon-only, so it carries a title
   and an aria-label — the label is the accessible name, not the glyph. */
.folio .folioinv{display:inline-flex;align-items:center;vertical-align:-3px;color:var(--gold)}
.folio .folioinv .ic{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.folio .folioinv:hover{color:var(--act)}

/* folio furniture: the notification count (0254). Two states and no third —
   a number when something is waiting, a middot when nothing is. The middot
   rather than nothing at all so the masthead does not reflow the moment a
   reply lands, and dimmed to --faint so a quiet count reads as furniture
   rather than as a thing demanding to be cleared.

   A Cast reply is emitted one tier below the default threshold, so it never
   reaches this element. That is deliberate and it is the whole bargain: the
   personas can be as talkative as the dials allow without any of it turning
   into a number chasing the reader around the site. */
.folio .folionotif{display:inline-flex;align-items:center;vertical-align:-1px;color:var(--faint)}
.folio .folionotif.on{color:var(--gold)}
.folio .folionotif:hover{color:var(--act)}
.folio .folionotif .notifn{font-variant-numeric:tabular-nums;font-size:11px;line-height:1;
  min-width:1.1em;text-align:center}
.folio .folionotif.on .notifn{background:var(--gold);color:var(--paper);border-radius:7px;
  padding:2px 5px;font-weight:600}
.folio .folionotif .notifzero{opacity:.55}

/* The notifications page. Rows are the ordinary comment partial with a line of
   context above them, so a reply reads the same here as in its thread. */
.ntflist{margin-top:14px}
.ntf{padding:10px 0;border-bottom:1px solid var(--rule2)}
.ntf:last-child{border-bottom:0}
.ntfon{font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--faint)}
.ntfon a{color:var(--dim)}
.ntfon a:hover{color:var(--act)}
.ntfdot{color:var(--gold);margin-right:4px}
/* Unread rows get a rule, not a fill: a background wash on a list this dense
   fights the comment partial's own left border. */
.ntfnew{border-left:2px solid var(--gold);padding-left:10px;margin-left:-12px}
/* An AI reply is present but quieter, matching where it sits on the level
   scale. Never hidden — the badge inside the partial is the disclosure and
   this must not compete with it. */
.ntfai{opacity:.88}
.ntfempty{margin-top:24px;max-width:52ch}
.ntfempty p{margin:0 0 8px}

/* ============================================================
   NEWS VIEW — wire flash, most-starred + newsletter rail,
   tag tracking, and the "on the wire" control row.
   ============================================================ */
.newsrow{display:flex;gap:8px 26px;flex-wrap:wrap;border-top:3px double var(--rule2);margin-top:14px;padding-top:10px}
.newsrow-main{flex:2 1 480px;min-width:0}
.newsrow-rail{flex:1 1 260px;border-left:1px solid var(--rule);padding:6px 0 6px 22px}
.newsrow .topbox{border-top:0;margin-top:0;padding-top:0}
@media(max-width:760px){.newsrow-rail{border-left:0;padding:14px 0 6px;flex-basis:100%}}

/* ============================================================
   NEWSLETTER — the two-step signup.

   Step 1 is one field and one button: the rail, and the footer ask at the end
   of a river, take an address and nothing else. Step 2 is /newsletter/desks,
   where there is room to say what each desk actually covers.

   Desk choice is a retention setting, not an acquisition one, and it used to
   sit BELOW the Subscribe button it submitted: anyone who typed an address and
   pressed Enter posted the form without ever seeing the choices.

   Two house rules this block used to break, both load-bearing. Colour is
   meaningful — --gold means starred/notable, and it was the "selected desk"
   fill and the rail's kicker. And the paper is hairlines and whitespace, not
   boxes ("out of the boxes", above) — the segmented cells and the .nlsignup
   card were the last filled boxes left in it. Selected is --ink now,
   everywhere, and every frame here is a rule.
   ============================================================ */
.nlrail{display:flex;flex-direction:column;gap:9px}
/* A real heading, not a styled span. The aside is aria-labelledby it, and
   without a heading element a screen reader walking the page by headings finds
   nothing at all where the newsletter is. Dressed as the house kicker but in
   --ink: the gold it used to wear says "starred", which this is not. */
/* line-height is explicit because the title wraps: "The MorningStar Security
   Newsletter" is two lines in a 260px rail, and the `font:` shorthand above
   would otherwise leave it at `normal` — ~13px of leading for 11px uppercase
   set at .16em, which sets the two lines almost touching. */
.nlhd{font:600 calc(11px*var(--rs))/1.35 var(--sans);letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink);margin:0}
.nlwhen{font:400 calc(11.5px*var(--rs)) var(--sans);letter-spacing:.04em;
  color:var(--dim);margin:0}
.nldek{font:italic 400 calc(13.5px*var(--rs))/1.5 var(--serif);color:var(--dim);margin:0}
.nlform{display:flex;flex-direction:column;gap:9px;align-items:stretch}
.nlfield{display:flex;flex-direction:column;gap:5px}
/* Was 9px in --faint with the hint folded inside it as an <em>. A label is the
   NAME of a control; a hint is a sentence about it, and gets its own line in
   sentence case rather than being uppercased along with the name. */
.nllabel{font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.09em;
  text-transform:uppercase;color:var(--dim)}
.nlhint{font:italic 400 calc(12.5px*var(--rs))/1.45 var(--serif);color:var(--faint)}
.nlentry{display:flex;width:100%;margin-top:2px}
/* max(16px,…) is not a typographic choice: iOS Safari zooms the viewport on
   focus for any input under 16px, and with no maximum-scale in the viewport tag
   the page stays zoomed with the Subscribe button pushed off the right edge. */
.nlentry input{flex:1;min-width:0;border:1px solid var(--dim);background:var(--paper);padding:9px 11px;
  font:400 max(16px,calc(12.5px*var(--rs))) var(--sans);color:var(--ink)}
.nlentry input:focus{outline:none;border-color:var(--ink)}
.nlentry input:focus-visible{outline:2px solid var(--ink);outline-offset:2px;border-color:var(--ink)}
.nlentry .b.solid{padding:9px 15px;white-space:nowrap}
/* Directly under the button now, and second only to the dek. These are the
   three promises a security readership actually cares about, and they used to
   be last and smallest — 10px in the faintest colour in the box. */
.nlfoot{font:400 calc(11.5px*var(--rs)) var(--sans);letter-spacing:.04em;color:var(--dim)}
/* How the letter is ranked, said once under the ask rather than in the
   headline. Set at note size: a reader checks this AFTER deciding they want
   the thing, and at prose size it out-shouted the offer above it. */
.nlrank{font:400 calc(11.5px*var(--rs)) var(--sans);line-height:1.6;color:var(--faint);
  margin:14px 0 0;max-width:60ch}
.nlrank a{color:var(--faint);text-decoration-color:var(--rule2)}
.nlrank a:hover{color:var(--ink)}
/* --act is right for the proof link: it is "go and read this now", not
   "this is notable". The letter itself is the strongest thing the widget has. */
.nlproof{font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.11em;
  text-transform:uppercase;margin:0}
.nlproof a{color:var(--act)}
/* What the reader is agreeing to, said rather than asked. The whole point of
   the two-step flow is that nobody has to make this choice to subscribe. */
.nldefault{font:400 calc(13px*var(--rs))/1.5 var(--serif);color:var(--faint);margin:0}

/* ---- the tick box: one shape for every many-of control here ----
   Cadence and desks are BOTH many-of on this model — wants_daily, wants_weekly
   and wants_monthly are three independent booleans, and a reader may hold all
   three — so they carry the same shape, because the arity is the truth being
   told. There is no radio in this flow to be confused with.

   Drawn from the input itself (appearance:none) rather than the old pattern of
   an opacity:0 input under a filled label cell. That pattern needed :has(), a
   @supports fallback for focus, and a ✓ glyph to survive forced colours; a real
   checkbox needs none of it and lands on the control's own focus ring. */
.nlchecks input[type=checkbox],.deskrows input[type=checkbox]{
  appearance:none;-webkit-appearance:none;width:17px;height:17px;margin:0;flex:0 0 auto;
  border:1px solid var(--dim);background:transparent;display:grid;place-content:center;cursor:pointer}
.nlchecks input[type=checkbox]:checked,.deskrows input[type=checkbox]:checked{
  background:var(--ink);border-color:var(--ink)}
.nlchecks input[type=checkbox]:checked::after,.deskrows input[type=checkbox]:checked::after{
  content:"";width:9px;height:5px;border:2px solid var(--p);border-top:0;border-right:0;
  transform:rotate(-45deg) translate(1px,-2px)}
.nlchecks input:focus-visible,.deskrows input:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
/* appearance:none strips a control entirely under forced colours — background
   and border both — so the box has to be redrawn in system colours or the
   checked state simply is not there. This is the file's only forced-colors
   block; it moved here from the segmented control it replaced. */
@media (forced-colors:active){
  .nlchecks input[type=checkbox],.deskrows input[type=checkbox]{
    forced-color-adjust:none;border-color:ButtonText;background:Canvas}
  .nlchecks input[type=checkbox]:checked,.deskrows input[type=checkbox]:checked{
    background:Highlight;border-color:Highlight}
  .nlchecks input[type=checkbox]:checked::after,.deskrows input[type=checkbox]:checked::after{
    border-color:HighlightText}
}
.nlchecks{display:flex;flex-direction:column}
.nlchecks label{display:flex;align-items:center;gap:11px;min-height:44px;padding:9px 0;
  cursor:pointer;border-bottom:1px solid var(--rule);
  font:400 calc(14px*var(--rs)) var(--sans);color:var(--dim)}
.nlchecks label:last-child{border-bottom:0}
.nlchecks label:hover{color:var(--ink)}
.nlchecks label:has(input:checked){color:var(--ink);font-weight:600}

/* ---- the second ask, at the end of a desk river ----
   No card and no fill: a double rule above and a hairline below, which is the
   same punctuation .standing and .teaser use to end a run of stories. */
.nlfooter-ask{display:flex;flex-wrap:wrap;align-items:center;gap:10px 26px;
  border-top:3px double var(--rule2);border-bottom:1px solid var(--rule);
  padding:16px 0;margin:24px 0 4px}
.nlfooter-ask>div{flex:1 1 320px;min-width:0}
.nlfa-line{font:400 calc(15px*var(--rs))/1.4 var(--serif);color:var(--ink);margin:3px 0 0}
.nlfa-form{flex:0 1 380px;display:flex;flex-direction:column;gap:5px}

/* ---- step 2: /newsletter/desks, and the same component on /newsletter
   and in Profile → Newsletter ---- */
.nlprefs{max-width:640px;margin:10px 0 4px}
.nlprefs-folio{display:flex;flex-wrap:wrap;gap:4px 16px;align-items:baseline;
  justify-content:space-between;padding-bottom:8px}
/* --ok = verified/correct, which is exactly what this line reports: the
   address is in. It is not decoration, and it is not a heading colour. */
.nlstep{color:var(--ok)}
.nlprefs h1{font-size:calc(30px*var(--rs));letter-spacing:-.02em;line-height:1.12;margin:2px 0 0}
.nlprefs .sub{font-size:calc(15.2px*var(--rs));line-height:1.6;color:var(--dim);
  max-width:60ch;margin:9px 0 0}
/* Embedded in a /profile section: no folio, no headline, and the standfirst
   drops to the size of the notes around it so the pane reads as part of the
   section rather than as a page that wandered in. */
.nlprefs.embed{max-width:none;margin:0}
.nlprefs.embed .sub{font-size:calc(12.5px*var(--rs));line-height:1.55;color:var(--faint);
  margin:0;max-width:66ch}
.nlprefs.embed .nlprefsform{margin-top:12px;gap:16px}
.nlprefsform{margin-top:20px;display:flex;flex-direction:column;gap:20px}
/* The 2px ink top rule the source panels use (.phd, line 899) — this list is a
   panel of choices, and it gets the same masthead. */
.deskrows{list-style:none;border-top:2px solid var(--ink)}
.deskrows li{border-bottom:1px solid var(--rule)}
.deskrows label{display:grid;grid-template-columns:22px 1fr;gap:3px 0;align-items:start;
  padding:17px 0;min-height:46px;cursor:pointer}
.deskrows input[type=checkbox]{grid-row:1/3;margin-top:2px}
.dr-name{font:600 calc(15px*var(--rs)) var(--sans);letter-spacing:.01em;color:var(--dim)}
.dr-lens{font:400 calc(13.5px*var(--rs))/1.5 var(--serif);color:var(--faint)}
.deskrows label:has(input:checked) .dr-name{color:var(--ink);font-weight:700}
.deskrows label:has(input:checked) .dr-lens{color:var(--dim)}
/* Not a flex container, deliberately: a <legend> inside a flex fieldset renders
   differently in every browser, which is why the control it replaces avoided
   fieldset/legend altogether and hand-rolled the roles instead. Block layout
   costs nothing here and the legend is a real one. */
.nlcadence{border:0;padding:0;display:block}
.nlcadence .nlchecks{margin-top:6px;max-width:340px}
.nlprefs-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px 18px;
  border-top:1px solid var(--rule);padding-top:16px}
.nlskip{font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.11em;
  text-transform:uppercase;color:var(--dim)}
.nlskip:hover{color:var(--ink)}
.nlchange{font:400 calc(12.5px*var(--rs)) var(--serif);font-style:italic;color:var(--faint)}

/* the /newsletter acquisition block. Was a --paper2 card inside a --rule2
   border — the one boxed, filled cell left on the page. */
.nlsignup{max-width:520px;margin:18px 0 4px}
.nlsignup .nlfoot{margin-top:2px}
.nldone{font:400 calc(15px*var(--rs))/1.45 var(--serif);color:var(--ink);margin:0 0 6px}
.nlfeed{font:400 calc(11.5px*var(--rs)) var(--sans);color:var(--dim);margin:12px 0 0;
  padding-top:10px;border-top:1px solid var(--rule)}
/* visually hidden, available to assistive tech — the standard clip pattern.
   Used by the feeds matrix to give twenty identical "RSS" links each a unique
   accessible name without printing it. */
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
/* the feeds matrix under the /newsletter form: desks down, cadences across.
   A real table — the th scopes are the accessibility, the CSS only dresses it.
   The wrap scrolls sideways on its own so a narrow phone never scrolls the
   page body horizontally. */
.nlfeeds{margin-top:26px}
.nlfeeds:focus{outline:none}
.nlfeedsdek{font:400 calc(13px*var(--rs))/1.5 var(--sans);color:var(--dim);
  margin:8px 0 0;max-width:64ch}
.nlmatrixwrap{overflow-x:auto;margin-top:12px}
.nlmatrix{border-collapse:collapse;font-family:var(--sans);font-size:calc(12px*var(--rs))}
.nlmatrix th,.nlmatrix td{padding:7px 14px 7px 0;border-bottom:1px solid var(--rule);text-align:left}
.nlmatrix thead th{font-size:calc(10.5px*var(--rs));font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint)}
.nlmatrix tbody th{font-weight:600;color:var(--ink);padding-right:22px;white-space:nowrap}
.nlmatrix tbody th em{font-style:normal;font-weight:400;color:var(--faint);
  letter-spacing:.03em;margin-left:7px}
.nlmatrix td a{font-weight:600;letter-spacing:.05em;color:var(--act);text-decoration:none}
.nlmatrix td a:hover{text-decoration:underline;text-underline-offset:3px}
@media(max-width:520px){.nlmatrix tbody th em{display:none}}

/* your-tags tracking */
.tagtrack{border-left:3px solid var(--gold);margin-top:14px;padding:4px 0 6px 14px}
.tagtrack .tthd{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.tagtrack .ttdek{font:italic 400 calc(12.5px*var(--rs)) var(--serif);color:var(--dim)}
.ttchips{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:8px}
.trackchip{white-space:nowrap;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.05em;text-transform:uppercase;background:var(--ink);color:var(--paper);padding:6px 10px;border:0;cursor:pointer}
#trackInput{border:1px solid var(--rule2);background:var(--paper);padding:7px 10px;font:400 calc(11.5px*var(--rs)) var(--sans);color:var(--ink);width:200px;max-width:100%}
.ttchips .b{padding:7px 12px}
.ttnote{font:400 calc(10.5px*var(--rs)) var(--sans);color:var(--faint)}

/* on-the-wire controls */
.hotwire{display:flex;gap:8px 10px;align-items:center;flex-wrap:wrap;padding:13px 0 3px}
.hotwire .hwspacer{flex:1}
.hotwire .hwsearch{display:inline-flex}
.hotwire .denseg{margin-left:0}
.hotwire .denseg .g{margin:0;padding:7px 12px;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.07em;text-transform:uppercase;color:var(--dim)}
.hotwire .denseg .g.on{background:var(--ink);color:var(--paper)}
.chip.hottag{min-height:0}

/* matching tracked tags gild gold across every desk (set by app.js) */
.pill.tracked{border-color:var(--gold);color:var(--gold)}

/* ============================================================
   ARTICLE READER (item page) — prototype V9.
   ============================================================ */
/* The story TL;DR on a cluster card. Serif and full measure: it is prose about
   the story, not another piece of furniture around the headline. */
.cldeck{margin:6px 0 2px;font:400 calc(13.5px*var(--rs))/1.5 var(--serif);color:var(--dim);
  max-width:62ch}

/* The clusters rail: the window's biggest stories, a day and a week. Numbers
   lead because the list is ordered by them and nothing else here is. */
.clbig{margin-bottom:22px}
.clbig .sechd{margin:0 0 10px}
.clbig .sechd h3{font-size:calc(10.5px*var(--rs));letter-spacing:.16em}
.clbiglist{margin:0;padding:0;list-style:none;counter-reset:clbig}
.clbiglist li{padding:9px 0;border-bottom:1px solid var(--rule)}
.clbiglist li:last-child{border-bottom:0}
.clbiglist .hl{font:600 calc(13.5px*var(--rs))/1.35 var(--serif);color:var(--ink)}
.clbiglist .hl:hover{text-decoration:underline;text-underline-offset:2px}
.clbigmeta{margin-top:4px;font:500 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint);display:flex;gap:6px;align-items:baseline}
.clbigmeta b{color:var(--dim);font-weight:700;font-variant-numeric:tabular-nums}

/* The article and its side panel. The wrap carries the measure below the
   breakpoint so the article is unchanged there and the rail just follows it in
   flow; above it, the wrap widens and becomes the two columns. 1180px is where
   780 of text plus a 280 rail plus the gutters stop squeezing either. */
.readerwrap{max-width:780px;margin:0 auto}
.rrail{margin-top:26px;font-family:var(--sans)}
@media(min-width:1180px){
  .readerwrap{max-width:1104px;display:grid;grid-template-columns:minmax(0,780px) 280px;
    gap:0 44px;align-items:start}
  /* min-width:0, and NOT max-width:none.
     
     A grid item's min-width defaults to auto, which resolves to its MIN-CONTENT
     width — and min-width beats max-width. The first version of this rule set
     max-width:none and let the article find its own size: on a video page with
     a transcript the min-content width is over three thousand pixels, so the
     reader blew out to 3372px inside a 1104px wrap and took the player with it,
     3372x1897 where it should have been 780x439. Below the breakpoint it looked
     perfect, because there the article is not a grid item and min-width:auto
     means zero.
     
     min-width:0 lets the item shrink to its track; .reader's own max-width:780px
     then does the clamping it always did. */
  .readerwrap>.reader{min-width:0}
  .rrail{position:sticky;top:20px;margin-top:64px;min-width:0}
}
.reader{max-width:780px;margin:0 auto}
.rback{display:inline-block;padding:16px 0 0;font:500 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:uppercase;color:var(--dim)}
.rback:hover{color:var(--ink)}
.reyebrow{display:flex;gap:6px 10px;align-items:baseline;flex-wrap:wrap;margin-top:14px;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.12em;text-transform:uppercase}
.reyebrow a{color:var(--dim)}
.reyebrow .rsrc{text-decoration:underline;text-decoration-color:var(--rule2);text-underline-offset:2px}
.reyebrow .rsrc:hover{color:var(--ink)}
/* The source's favicon leads its name, as it does on the cards. */
.reyebrow .rsrc .fav{vertical-align:-3px;margin-right:6px}
.reyebrow .dotsep{color:var(--faint)}
.rtitle{font:800 calc(clamp(28px,4.8vw,42px)*var(--rs))/1.12 var(--disp);letter-spacing:-.004em;margin:8px 0 10px;text-wrap:pretty}
/* The big star — the story's vote box. On a wide sheet it hangs in the left
   margin so the title keeps the column's rule line (the same left edge the
   discussion opens on); where no margin exists it sits in-column left of the
   title. The h1 is first in the DOM; the grid moves the button to the left
   column. Starred state leans on the sitewide [data-a="star"].on .ic gold,
   and marks itself with the double rule — the sheet's "acted upon" register. */
.rtitlerow{display:grid;grid-template-columns:auto 1fr;gap:0 calc(18px*var(--rs));align-items:start;margin:8px 0 10px}
.rtitlerow .rtitle{grid-column:2;grid-row:1;margin:0}
.bigstar{grid-column:1;grid-row:1;display:flex;flex-direction:column;align-items:center;gap:4px;
  min-width:calc(58px*var(--rs));margin-top:5px;padding:11px 10px;border:1px solid var(--rule2);
  background:none;cursor:pointer;color:var(--dim)}
.bigstar .ic{width:24px;height:24px;transition:transform .15s ease}
.bigstar .starN{font:700 calc(13px*var(--rs)) var(--sans);letter-spacing:.02em}
.bigstar .bslbl{font:600 calc(8.5px*var(--rs)) var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.bigstar:hover{border-color:var(--ink);color:var(--ink);background:color-mix(in srgb,var(--ink) 6%,transparent)}
.bigstar.on{border:3px double var(--gold);padding:9px 8px;color:var(--gold)}
.bigstar.on .ic{fill:currentColor;stroke:none;transform:scale(1.08)}
.bigstar.on .starN,.bigstar.on .bslbl{color:var(--gold)}
@media(min-width:1080px){
  /* Wide sheet: the box steps off the column into the left margin, and the
     title takes back the rule line. right:100% keeps the hang width-proof. */
  .rtitlerow{display:block;position:relative}
  .bigstar{position:absolute;right:100%;top:5px;margin:0 calc(18px*var(--rs)) 0 0}
}
@media(max-width:560px){
  /* No margin to hang in — the box drops under the headline as a compact
     horizontal control so the title keeps its full measure. */
  .rtitlerow{display:block}
  .bigstar{flex-direction:row;gap:8px;min-width:0;min-height:44px;margin:10px 0 0;padding:8px 14px}
  .bigstar.on{padding:6px 12px}
}
.rlede{font:400 calc(17.5px*var(--rs))/1.55 var(--serif);color:var(--dim);max-width:62ch;text-wrap:pretty;margin:0}
.rbadges{display:flex;gap:3px 7px;align-items:center;flex-wrap:wrap;margin-top:12px}
.ractions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;border-top:1px solid var(--rule);border-bottom:3px double var(--rule2);margin-top:14px;padding:12px 0}
.ractions .b{min-height:38px}
/* reactions — anonymous counted chips in the story's own response strip
   (reactions plan §5.1), directly under the actions bar and inside the
   article measure so they read as reacting to the STORY, not the
   discussion. The double rule that terminates the story moves from the
   actions bar to this strip when it exists (the :has below). Zero-count
   quick picks are in the DOM (they are the palette "+ React" reveals) but
   hidden at rest: an empty emoji row is a dead-mall signal and must not
   compete with the star. Gold marks the reader's own, same as the starred
   state; the count never carries a name. */
.ractions:has(+ .rreact){border-bottom:1px solid var(--rule)}
.rreact{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:0;padding:12px 0;
  border-bottom:3px double var(--rule2)}
.rchip{display:inline-flex;align-items:center;gap:7px;min-height:32px;padding:4px 11px;
  border:1px solid var(--rule);border-radius:0;background:transparent;color:var(--dim);
  font:600 calc(12px*var(--rs)) var(--sans);cursor:pointer}
.rchip .rem{font-size:calc(13px*var(--rs));line-height:1}
.rchip .rctN{font-feature-settings:"tnum" 1;color:var(--ink)}
.rchip:hover{border-color:var(--ink);color:var(--ink);background:color-mix(in srgb,var(--ink) 6%,transparent)}
.rchip.on{border-color:var(--gold);color:var(--gold)}
.rchip.on .rctN{color:var(--gold)}
.rchip.zero{display:none}
.rreact.open .rchip.zero{display:inline-flex}
/* In the open palette the zeros do not earn their ink — the emoji's presence
   already says zero. (Chips used to carry a written gloss here, "Agree",
   "Insightful"; it is gone. An emoji that needs captioning is the wrong
   emoji, and the captions only ever appeared on the curated picks, so the
   reader-invented ones read as second class.) */
.rreact.open .rchip.zero .rctN{display:none}
/* "+ React" stays visible open or closed — it is the toggle; the glyph
   flips to a minus. The label speaks in the star's small-caps voice so the
   invitation is legible without out-shouting the star. */
.rchip.rmore{color:var(--faint);justify-content:center}
.rmore .rmlbl{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.14em;text-transform:uppercase}
.rmore .rmminus{display:none}
.rreact.open .rmore .rmplus{display:none}
.rreact.open .rmore .rmminus{display:inline}
/* the free input (0188): any emoji, revealed with the palette. 16px floor
   so iOS Safari does not zoom the viewport (same fix as .nlentry,
   app.css ~1321); focus-visible ring per the house input pattern. */
.rfree{display:none}
.rreact.open .rfree{display:inline-flex;gap:6px;align-items:center}
.rfreein{width:15ch;min-height:32px;padding:4px 8px;border:1px solid var(--rule);border-radius:0;
  background:transparent;color:var(--ink);font:600 max(16px,calc(12px*var(--rs))) var(--sans);text-align:center}
.rfreein:focus{outline:none;border-color:var(--ink)}
.rfreein:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.rfreein::placeholder{color:var(--faint)}
.rfreehint{display:none}
@media(hover:hover){.rreact.open .rfreehint{display:inline;font:400 calc(9.5px*var(--rs)) var(--sans);color:var(--faint)}}
/* fingers get the page's full-size targets (cf. .bigstar at ≤560px) */
@media(pointer:coarse){.rchip{min-height:44px;padding:8px 13px}.rfreein{min-height:44px}}
[data-theme="eink"] .rchip .rem{filter:grayscale(1)}
/* transcript — a video's captured captions, collapsed under the actions bar.
   Set in the reading serif at a slightly smaller size than the lede: it is
   spoken word, so it reads as a secondary register to the piece itself. */
.rtranscript{margin-top:14px;border-bottom:1px solid var(--rule)}
.rtranscript>summary{cursor:pointer;list-style:none;display:flex;align-items:baseline;gap:9px;padding:9px 0;user-select:none}
.rtranscript>summary::-webkit-details-marker{display:none}
.rtranscript>summary::before{content:"▸";color:var(--faint);font-size:11px;line-height:1}
.rtranscript[open]>summary::before{content:"▾"}
.rtranscript>summary:hover .kick{color:var(--act)}
.rtranscript .rtmeta{font:400 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.06em;color:var(--faint)}
.rtbody{max-width:62ch;max-height:60vh;overflow-y:auto;padding:2px 0 14px}
.rtbody p{font:400 calc(15px*var(--rs))/1.6 var(--serif);color:var(--dim);margin:0 0 11px;text-wrap:pretty}
.rtbody .rtnote{font:italic 400 calc(12px*var(--rs)) var(--serif);color:var(--faint);border-left:2px solid var(--rule2);padding-left:9px}
.rdiscuss{margin-top:22px}
.sechd2solo{display:flex;align-items:center;gap:10px;margin-bottom:10px;font:600 calc(12px*var(--rs)) var(--sans);letter-spacing:.16em;text-transform:uppercase;color:var(--ink)}
.sechd2solo i{flex:1;height:1px;background:var(--rule)}
.sechd2solo .rnote{font:400 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:none;color:var(--faint)}
.rcompose{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}
.rcompose textarea{width:100%;background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);padding:9px 10px;font:400 calc(14px*var(--rs)) var(--serif)}
.rcompose .b.solid{align-self:flex-start}
.rempty,.rsignin{font:italic 400 calc(13px*var(--rs)) var(--serif);color:var(--faint);margin-bottom:8px}
/* the Cast's AI badge — unconditional wherever a cast member's name appears;
   disclosure is the license for the feature (work-community-plan charter) */
.aibadge{display:inline-block;font:700 calc(8.5px*var(--rs)) var(--sans);letter-spacing:.12em;color:var(--paper);background:var(--dim);border-radius:2px;padding:1px 4px;vertical-align:1px;cursor:help}

/* The supporter mark (0232, S1): a dollar a year, worn quietly beside the
   name. Gold like the masthead star, never a shouty pill — the tier buys
   fun, and the badge should read as thanks rather than as rank. */
.suppmark{color:var(--gold,#c9a94a);margin-left:.25em;cursor:help;font-size:.9em}
.castbio{font:italic 400 calc(14px*var(--rs))/1.5 var(--serif);color:var(--dim);border-left:2px solid var(--rule2);padding-left:12px;margin:14px 0 18px;max-width:60ch}
.castdisclose{font:400 calc(11px*var(--rs))/1.5 var(--sans);color:var(--faint);margin:0 0 10px}
.castdisclose a{color:var(--faint)}
.castpicks{margin:20px 0 8px;border-top:1px solid var(--rule);padding-top:10px}
.cphd{margin-bottom:4px}
.castpick{display:flex;gap:10px;align-items:flex-start;padding:7px 0}
.castpick+.castpick{border-top:1px dashed var(--rule)}
.cpstar{flex:0 0 auto;margin-top:1px}
.cpstar .ic{width:14px;height:14px;fill:var(--gold);stroke:none}
.cpmain{flex:1;min-width:0}
.cpwho{font:700 calc(10px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.cpwho a{color:var(--ink)}
.cpwho a:hover{text-decoration:underline;text-underline-offset:2px}
.cpnote{margin-top:3px;font:italic 400 calc(13.5px*var(--rs))/1.55 var(--serif);color:var(--ink)}
/* inline reply — a details disclosure inside each comment; the summary reads
   like the vote glyphs beside it, the open form reuses the composer look */
.creply{display:inline-block;vertical-align:top}
.creply summary{cursor:pointer;list-style:none;display:inline;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--faint);padding:0 4px}
.creply summary::-webkit-details-marker{display:none}
.creply summary:hover{color:var(--ink)}
.creply[open]{display:block;margin-top:6px}
.creply .rcompose{margin:6px 0 4px}
.creply .rcompose textarea{font-size:calc(13px*var(--rs))}

/* wiki landing — category cards, top-ruled */
.wikigrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:6px 28px;margin-top:16px}
/* 6px at the sides, not 2: with 2 the keyboard ring (4px out, see "the keyboard
   ring") stood six pixels off the card's own title while sitting fourteen off
   the top and bottom. */
.wikicard{border-top:2px solid var(--ink);padding:10px 6px 14px;text-decoration:none;color:var(--ink);display:block}
.wkkind{display:block;font:600 calc(9px*var(--rs)) var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.wktitle{display:block;font:600 calc(16px*var(--rs))/1.3 var(--serif);margin-top:3px}
.wikicard:hover .wktitle{text-decoration:underline;text-underline-offset:3px}

/* ============================================================
   LISTEN — shows grouped, episode rows with play buttons (V2).
   ============================================================ */
.show{margin-top:20px}
/* Indented to match the episode rows under it, which carry 11px at the sides
   to hold the keyboard ring off their titles. */
.showhd{display:flex;align-items:baseline;gap:8px 10px;flex-wrap:wrap;border-bottom:2px solid var(--ink);padding:0 11px 7px}
.showhd .shname{font:700 calc(13px*var(--rs)) var(--sans);letter-spacing:.12em;text-transform:uppercase}
.showhd .shn{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.showhd .shfill{flex:1}
.showhd .shlink{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);padding:4px 9px}
.showhd .shlink:hover{border-color:var(--ink);color:var(--ink)}
.epmore{display:inline-block;margin-top:10px;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--dim);border:1px dashed var(--rule2);padding:7px 12px}
.epmore:hover{color:var(--ink);border-color:var(--ink);background:color-mix(in srgb,var(--ink) 3%,transparent)}
.epmore .pplus{display:inline-block;border:1px solid currentColor;width:14px;height:14px;line-height:12px;text-align:center;font-size:.82em;margin-right:5px;vertical-align:.05em}
.eprow{display:flex;gap:14px;align-items:flex-start;padding:14px 11px;border-bottom:1px solid var(--rule)}
.epplay{flex:0 0 44px;height:44px;border:1px solid var(--act);color:var(--act);display:flex;align-items:center;justify-content:center}
.epplay:hover{background:var(--act);color:var(--paper)}
.epplay .ic{width:16px;height:16px;fill:currentColor;stroke:none}
.epbody{flex:1;min-width:0}
.epbody .hl{display:block;font:500 calc(16.5px*var(--rs))/1.3 var(--serif)}
.epbody .hl:hover{text-decoration:underline;text-underline-offset:3px}
.epmeta{display:flex;gap:4px 12px;flex-wrap:wrap;margin-top:5px;font:500 calc(10px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.epacts{display:flex;gap:2px;align-items:center;flex:0 0 auto}

/* ============================================================
   NEWS-PAGE fidelity: tracked-tag article highlight, footer
   links, clearer article separators.
   ============================================================ */
/* A story carrying a tracked tag gets the gold edge (app.js adds .tracked-edge) */
article.story.tracked-edge{border-left:3px solid var(--gold);padding-left:11px}
.toprow.tracked-edge{border-left:3px solid var(--gold);padding-left:11px}

/* footer links take theme ink, underlined, never UA-blue */
footer a{color:var(--dim);text-decoration:underline;text-underline-offset:2px;text-decoration-color:var(--rule2)}
footer a:hover{color:var(--ink);text-decoration-color:var(--act)}
footer .sep{color:var(--faint);margin:0 2px}

/* hairline between every article in a source panel (belt-and-braces) */
.panel .pbody article.story{border-bottom:1px solid var(--rule)}
.panel .pbody article.story:last-child{border-bottom:0}

/* "Your Tags" envelope — newest stories touching a tracked tag (client-built) */
.envelope{border-top:3px double var(--ink);margin-top:14px;padding-top:10px}
/* Indented to match the rows under it (see .envrow). */
.envhd{display:flex;justify-content:space-between;align-items:baseline;gap:8px 12px;flex-wrap:wrap;padding:0 11px 2px}
.envdek{font:italic 400 calc(12px*var(--rs)) var(--serif);color:var(--faint)}
.envrow{display:flex;gap:11px;align-items:baseline;padding:11px;border-top:1px solid var(--rule);width:100%;text-decoration:none;color:inherit}
.envrow:hover .envtt{text-decoration:underline;text-underline-offset:3px}
.envtag{white-space:nowrap;font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;border:1px solid var(--gold);color:var(--gold);padding:3px 8px}
.envtt{font:500 calc(16.5px*var(--rs))/1.3 var(--serif);flex:1;min-width:0}
.envmeta{font:400 calc(10px*var(--rs)) var(--sans);color:var(--faint);white-space:normal;text-align:right;flex:0 1 auto;max-width:46%}

/* Listen: platform icons (Spotify / Apple Podcasts) on the show header */
.platicon{display:inline-flex;align-items:center;justify-content:center;width:30px;height:26px;border:1px solid var(--rule2);color:var(--dim)}
.platicon .ic{width:16px;height:16px}
.platicon:hover{color:var(--ink);border-color:var(--ink)}

/* Listen: sort control (Top rated / Newest / A–Z) */
.segsort{display:inline-flex;margin-top:10px;border:1px solid var(--rule2)}
.segsort a{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:uppercase;color:var(--dim);padding:6px 12px;border-right:1px solid var(--rule2)}
.segsort a:last-child{border-right:0}
.segsort a:hover{color:var(--ink)}
.segsort a.on{background:var(--ink);color:var(--paper)}

/* Empty state for a desk-scoped river — names the desk, offers the others. */
.deskempty{padding:44px 0 40px;text-align:center;border-top:1px solid var(--rule)}
.deskempty .deone{font:italic 400 calc(17px*var(--rs)) var(--serif);color:var(--dim);margin:0}
.deskempty .detwo{font:600 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin:12px 0 0}
.deskempty .detwo a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--rule2)}
.deskempty .detwo a:hover{border-bottom-color:var(--ink)}
.deskempty .detwo .sep{margin:0 7px;color:var(--rule2)}

/* Listen: "Top episodes" band */
.toplisten{margin:16px 0 6px;padding:12px 14px;border:1px solid var(--rule2);background:color-mix(in srgb,var(--ink) 3%,transparent)}
.toplabel{display:flex;align-items:baseline;gap:9px;margin-bottom:4px}
.toplabel .kick{color:var(--act)}
.toplabel .topdek{font:italic 400 calc(12px*var(--rs)) var(--serif);color:var(--faint)}
.toplisten .eprow{padding:10px 0}
.toplisten .eprow:last-child{border-bottom:0}
.toplisten .epshow{color:var(--dim);font-weight:600}

/* Listen: 5-star show-rating widget on the show header */
.shrate{display:inline-flex;align-items:center;gap:1px}
.shrate .strbtn{font-size:calc(15px*var(--rs));line-height:1;color:var(--rule2);background:transparent;border:0;padding:0 1px;cursor:pointer}
.shrate .strbtn.fill{color:var(--dim)}
.shrate .strbtn.mine{color:var(--gold)}
.shrate .strbtn:hover{color:var(--gold)}
.shravg{font:600 calc(10px*var(--rs)) var(--sans);color:var(--faint);margin-left:5px;font-feature-settings:"tnum" 1}
.platicon.spotify:hover{color:#1DB954;border-color:#1DB954}
.platicon.apple:hover{color:#9933CC;border-color:#9933CC}

/* ============================================================
   CYBER CONNECTIONS — the daily board.
   Layout, spacing and animation choreography are ported from the
   design mock; every colour comes from the edition tokens so the
   board reads in all seven editions. Group identity is carried by
   BOTH hue (--cg0..--cg3) and shape (the #shape-* symbols), never
   hue alone.
   ============================================================ */
.cxwrap{max-width:980px;margin:0 auto;padding:8px 0 56px}
/* No `.cxwrap button{font:inherit}` reset here: at (0,1,1) it outranks every
   .cxtile/.cxbtn rule below and drags the whole board back to body serif. The
   site-wide `button{font:inherit}` already does the reset at (0,0,1). */
.cxwrap :focus-visible,.cxarch :focus-visible,.cxmodal :focus-visible{outline:2px solid var(--act);outline-offset:3px}
body.cxlock{overflow:hidden}
.cxhide{display:none!important}

/* --- masthead row --- */
.cxtop{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:26px}
.cxbrand{font:800 calc(10px*var(--rs)) var(--sans);letter-spacing:.17em;text-transform:uppercase;color:var(--dim)}
.cxacts{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cxtopbtn{border:1px solid var(--rule2);background:transparent;color:var(--dim);border-radius:999px;
  padding:9px 15px;font:800 calc(11px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;cursor:pointer}
.cxtopbtn:hover{border-color:var(--ink);color:var(--ink)}

/* --- hero --- */
.cxhero{text-align:center}
.cxhero h1{font:700 calc(clamp(30px,5.6vw,46px)*var(--rs))/1 var(--disp);letter-spacing:-.02em;margin:0}
.cxmeta{margin-top:10px;font:400 calc(12.5px*var(--rs)) var(--sans);color:var(--dim);letter-spacing:.02em}
.cxmeta b{color:var(--gold);font-weight:600}
.cxinstr{margin:20px auto 0;max-width:650px;font:italic 400 calc(18px*var(--rs))/1.45 var(--serif);color:var(--dim)}
.cxgame{margin-top:28px}

/* --- solved group rows --- */
.cxsolved{display:grid;gap:8px;margin-bottom:12px}
/* Rest visible; the entrance comes from cxReveal's 0% keyframe. A hidden
   resting state (opacity:0 + reveal-to-show) breaks the e-ink edition, whose
   blanket `animation:none` would never run the reveal, leaving solved rows
   invisible. This way they show even with animation off. */
.cxrow{border-radius:4px;padding:13px 16px;text-align:center;background:var(--cg0);
  animation:cxReveal .65s cubic-bezier(.18,.84,.22,1) both}
.cxrow.l1{background:var(--cg1)}
.cxrow.l2{background:var(--cg2)}
.cxrow.l3{background:var(--cg3)}
.cxrow strong{display:flex;align-items:center;justify-content:center;gap:7px;color:var(--cgink);
  font:700 calc(12.5px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;margin-bottom:4px}
.cxrow strong svg{width:11px;height:11px;flex:none}
.cxrow span{display:block;font:600 calc(12.5px*var(--rs)) var(--sans);color:var(--cgink);opacity:.86}

/* --- the board --- */
.cxboard{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.cxtile{min-height:98px;padding:12px 10px;border:1px solid var(--rule2);border-radius:4px;
  background:var(--paper2);color:var(--ink);text-align:center;
  /* real casing matters here: 0x90, Get-Process, %PDF, .exe */
  font:700 calc(16px*var(--rs))/1.25 var(--sans);text-transform:none;letter-spacing:0;
  cursor:pointer;user-select:none;overflow-wrap:anywhere;
  transition:background .14s ease,transform .14s ease,box-shadow .14s ease,border-color .14s ease,opacity .26s ease}
.cxtile:hover{background:color-mix(in srgb,var(--ink) 7%,var(--paper2));border-color:var(--dim)}
.cxtile:active{transform:scale(.985)}
.cxtile[aria-pressed="true"]{background:color-mix(in srgb,var(--gold) 16%,var(--paper2));
  border-color:var(--gold);color:var(--ink);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--gold) 34%,transparent)}
.cxtile.correct{color:var(--cgink);border-color:transparent;animation:cxPulse .95s cubic-bezier(.16,.84,.22,1)}
.cxtile.wrong{background:color-mix(in srgb,var(--bad) 18%,var(--paper2));border-color:var(--bad);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--bad) 48%,transparent);animation:cxShake .76s cubic-bezier(.2,.75,.25,1)}
.cxtile.dupe{animation:cxNudge .44s ease}
.cxtile.gone{opacity:0;transform:translateY(-16px) scale(.95);pointer-events:none}

/* --- status line --- */
.cxstatus{margin:18px 0 14px;display:grid;gap:10px;justify-items:center}
.cxsel{display:flex;justify-content:center;align-items:center;gap:22px;flex-wrap:wrap;
  font:700 calc(15px*var(--rs)) var(--sans);color:var(--dim)}
.cxcount{color:var(--ink)}
.cxmis{display:flex;align-items:center;gap:10px}
.cxdots{display:flex;gap:7px}
.cxdot{width:12px;height:12px;border-radius:50%;border:1px solid var(--dim);background:transparent;
  transition:transform .24s ease,background .24s ease,border-color .24s ease,opacity .24s ease}
.cxdot.used{background:var(--bad);border-color:var(--bad);opacity:.45;transform:scale(.74)}
.cxdot.pop{animation:cxPop .52s ease}
.cxmsg{min-height:26px;padding:6px 14px;border-radius:999px;font:400 calc(13.5px*var(--rs)) var(--sans);
  color:var(--dim);background:var(--paper2);border:1px solid var(--rule)}
.cxmsg.good{color:var(--ok);border-color:color-mix(in srgb,var(--ok) 42%,var(--rule))}
.cxmsg.warn{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 42%,var(--rule))}

/* --- buttons ---
   Gold-filled buttons take var(--paper), not var(--cgink): gold and paper sit
   at opposite ends of every edition's tone scale, whereas --cgink is tuned for
   the light --cg* group fills and goes black-on-black over e-ink's near-black
   gold. --cgink stays correct on the solved rows and collapsing tiles. */
.cxctrl,.cxshareacts,.cxnextacts{display:flex;justify-content:center;gap:10px;flex-wrap:wrap}
.cxbtn{display:inline-flex;align-items:center;justify-content:center;min-width:108px;padding:11px 18px;
  border-radius:999px;border:1px solid var(--rule2);background:transparent;color:var(--dim);
  font:800 calc(11px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;cursor:pointer}
.cxbtn:hover{border-color:var(--ink);color:var(--ink)}
.cxbtn.primary{border-color:var(--act);background:var(--act);color:var(--paper)}
.cxbtn.gold{border-color:var(--gold);background:var(--gold);color:var(--paper)}
.cxbtn:disabled{opacity:.28;cursor:not-allowed}
.cxbtn:disabled:hover{border-color:var(--rule2);color:var(--dim)}

/* --- result card + share --- */
.cxresult{margin:24px auto 0;max-width:700px;border:1px solid var(--rule2);background:var(--paper2);
  border-radius:12px;padding:20px;text-align:center;animation:cxReveal .65s cubic-bezier(.18,.84,.22,1) forwards}
.cxresult h2{font:700 calc(23px*var(--rs)) var(--disp);margin:0 0 8px}
.cxscore{color:var(--gold);font:800 calc(13px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase}
.cxresultsub{margin-top:4px;color:var(--dim);font:400 calc(12.5px*var(--rs)) var(--sans)}
.cxshare{margin:16px auto;max-width:460px;padding:16px;border-radius:10px;background:var(--paper);
  border:1px solid var(--rule);color:var(--dim);white-space:pre-line;text-align:left;
  font:650 calc(14px*var(--rs))/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.cxsharestatus{min-height:18px;margin-top:10px;color:var(--faint);font:400 calc(11.5px*var(--rs)) var(--sans)}
.cxnext{margin-top:18px;padding-top:18px;border-top:1px solid var(--rule);display:grid;gap:12px}
.cxnext h3{font:600 calc(14px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--dim);margin:0}

/* --- shared overlay chrome --- */
.cxback{position:fixed;inset:0;z-index:120;background:color-mix(in srgb,var(--paper) 68%,transparent);
  backdrop-filter:blur(2px);opacity:0;pointer-events:none;transition:.18s ease}
.cxback.open{opacity:1;pointer-events:auto}

/* --- archive drawer --- */
.cxarch{position:fixed;z-index:130;right:0;top:0;bottom:0;width:min(460px,100vw);background:var(--paper);
  border-left:1px solid var(--rule2);box-shadow:-20px 0 60px rgba(0,0,0,.34);
  transform:translateX(100%);transition:transform .22s ease;display:flex;flex-direction:column}
.cxarch.open{transform:translateX(0)}
.cxarchhd,.cxmodalhd{padding:18px;border-bottom:1px solid var(--rule2);display:flex;
  align-items:flex-start;justify-content:space-between;gap:16px}
.cxarchhd h2,.cxmodalhd h2{font:700 calc(22px*var(--rs))/1.1 var(--disp);margin:4px 0 6px}
.cxarchhd p,.cxmodalhd p{margin:0;color:var(--dim);font:400 calc(12.5px*var(--rs))/1.45 var(--sans)}
.cxclose{min-width:40px;height:40px;border:1px solid var(--rule2);background:transparent;color:var(--dim);
  border-radius:999px;cursor:pointer;flex:none}
.cxclose:hover{border-color:var(--ink);color:var(--ink)}
.cxarchbody{overflow:auto;padding:16px 18px 24px}
.cxstats{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px}
.cxstat{background:var(--paper2);border:1px solid var(--rule);border-radius:10px;padding:12px}
.cxstat b{display:block;font:700 calc(18px*var(--rs)) var(--disp)}
.cxstat span{color:var(--dim);font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase}

/* --- calendar --- */
.cxcal{border:1px solid var(--rule);background:var(--paper2);border-radius:12px;overflow:hidden}
.cxcalbar{display:grid;grid-template-columns:42px 1fr 42px;align-items:center;gap:8px;padding:10px;
  border-bottom:1px solid var(--rule)}
.cxcalnav{width:38px;height:38px;border-radius:999px;border:1px solid var(--rule2);background:transparent;
  color:var(--dim);cursor:pointer}
.cxcalnav:hover:not(:disabled){border-color:var(--ink);color:var(--ink)}
.cxcalnav:disabled{opacity:.3;cursor:not-allowed}
.cxcaltitle{text-align:center;font:700 calc(14px*var(--rs)) var(--sans)}
.cxweekdays,.cxcalgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.cxweekdays{padding:10px 10px 4px;color:var(--faint);font:600 calc(9.5px*var(--rs)) var(--sans);
  text-align:center;text-transform:uppercase;letter-spacing:.05em}
.cxcalgrid{padding:6px 10px 10px}
.cxday{min-height:52px;border:1px solid transparent;border-radius:8px;background:var(--paper);
  color:var(--dim);display:grid;place-items:center;position:relative;cursor:pointer}
.cxday.empty{background:transparent;cursor:default}
.cxday:not(.empty):not(:disabled):hover{border-color:var(--dim)}
.cxday.today{border-color:var(--gold)}
.cxday.dim{opacity:.18}
.cxday:disabled{opacity:.28;cursor:default}
.cxdaynum{font:700 calc(11.5px*var(--rs)) var(--sans)}
.cxdaydot{position:absolute;width:7px;height:7px;border-radius:50%;bottom:6px}
.cxdaydot.solved{background:var(--ok)}
.cxdaydot.incomplete{background:var(--act)}
.cxdaydot.unplayed{background:var(--faint)}
.cxdaydot.current{background:var(--gold)}
.cxfilters{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}
.cxfilter{border:1px solid var(--rule2);border-radius:999px;background:transparent;color:var(--dim);
  padding:8px 13px;font:500 calc(12.5px*var(--rs)) var(--sans);cursor:pointer}
.cxfilter[aria-pressed="true"]{background:var(--paper2);border-color:var(--gold);color:var(--gold)}
.cxlegend{display:flex;gap:12px;flex-wrap:wrap;color:var(--dim);font:400 calc(11px*var(--rs)) var(--sans);margin-bottom:10px}
.cxlegend span{display:flex;align-items:center;gap:6px}
.cxlegend i{width:7px;height:7px;border-radius:50%;display:inline-block}
.cxlegend .l-solved{background:var(--ok)}
.cxlegend .l-incomplete{background:var(--act)}
.cxlegend .l-unplayed{background:var(--faint)}
.cxlegendnote{margin:0 0 14px;color:var(--dim);font:400 calc(11.5px*var(--rs))/1.45 var(--sans)}
.cxlegendnote b{color:var(--ink)}
.cxdetail{margin-top:14px;border:1px solid var(--rule);background:var(--paper2);border-radius:10px;padding:14px}
.cxdetail h3{font:700 calc(15px*var(--rs)) var(--disp);margin:0 0 6px}
.cxdetailmeta{color:var(--dim);font:400 calc(12px*var(--rs)) var(--sans);margin-bottom:12px}
.cxdetailacts{display:flex;gap:8px;flex-wrap:wrap}
.cxsmallbtn{display:inline-flex;align-items:center;border:1px solid var(--rule2);border-radius:999px;
  background:transparent;color:var(--dim);padding:7px 14px;text-decoration:none;
  font:700 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.07em;text-transform:uppercase;cursor:pointer}
.cxsmallbtn:hover{border-color:var(--ink);color:var(--ink)}
.cxsmallbtn.primary{border-color:var(--gold);background:var(--gold);color:var(--paper)}

/* --- how-to-play modal --- */
.cxmodal{position:fixed;z-index:140;left:50%;top:50%;width:min(620px,calc(100vw - 28px));
  max-height:calc(100vh - 40px);overflow:auto;transform:translate(-50%,-46%) scale(.98);opacity:0;
  pointer-events:none;background:var(--paper);border:1px solid var(--rule2);border-radius:14px;
  box-shadow:0 26px 80px rgba(0,0,0,.48);transition:.2s ease}
.cxmodal.open{transform:translate(-50%,-50%) scale(1);opacity:1;pointer-events:auto}
.cxhowbody{padding:18px}
.cxstep{display:grid;grid-template-columns:44px 1fr;gap:14px;align-items:start;padding:14px 0;
  border-bottom:1px solid var(--rule)}
.cxstep:last-child{border-bottom:0}
.cxstepnum{width:38px;height:38px;border-radius:50%;display:grid;place-items:center;background:var(--paper2);
  border:1px solid var(--rule2);color:var(--gold);font:700 calc(15px*var(--rs)) var(--disp)}
.cxstep h3{font:700 calc(15px*var(--rs)) var(--sans);margin:0 0 5px}
.cxstep p{margin:0;color:var(--dim);font:400 calc(13.5px*var(--rs))/1.5 var(--sans)}
.cxdemorow{display:grid;grid-template-columns:repeat(4,1fr);gap:5px;margin-top:10px}
.cxdemotile{min-height:44px;border-radius:4px;background:var(--paper2);border:1px solid var(--rule2)}
.cxdemotile.on{border-color:var(--gold);background:color-mix(in srgb,var(--gold) 16%,var(--paper2))}
.cxdemosolved{margin-top:10px;height:44px;border-radius:4px;background:var(--cg2)}

/* --- animations --- */
@keyframes cxShake{
  0%{transform:translateX(0) scale(1)}
  12%{transform:translateX(-8px) scale(1.01)}  24%{transform:translateX(8px) scale(1.01)}
  36%{transform:translateX(-7px) scale(1.01)}  48%{transform:translateX(7px) scale(1.01)}
  60%{transform:translateX(-5px) scale(1.005)} 72%{transform:translateX(5px) scale(1.005)}
  84%{transform:translateX(-2px)}              100%{transform:translateX(0) scale(1)}
}
@keyframes cxNudge{0%,100%{transform:translateX(0)}40%{transform:translateX(5px)}70%{transform:translateX(-3px)}}
@keyframes cxPulse{
  0%{transform:scale(1);box-shadow:none}
  25%{transform:scale(1.04);box-shadow:0 0 0 3px color-mix(in srgb,var(--gold) 18%,transparent)}
  55%{transform:scale(1.015);box-shadow:0 0 0 10px transparent}
  100%{transform:scale(1);box-shadow:none}
}
@keyframes cxPop{0%{transform:scale(1)}35%{transform:scale(1.85)}100%{transform:scale(.74)}}
@keyframes cxReveal{
  0%{opacity:0;transform:translateY(14px) scale(.985)}
  65%{opacity:1;transform:translateY(-2px) scale(1.01)}
  100%{opacity:1;transform:translateY(0) scale(1)}
}
@media(prefers-reduced-motion:reduce){
  .cxwrap *,.cxwrap *::before,.cxwrap *::after,.cxarch,.cxmodal,.cxback{
    animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
}
@media(max-width:700px){
  .cxtop{margin-bottom:20px}
  .cxboard{grid-template-columns:repeat(2,1fr)}
  .cxtile{min-height:88px;font-size:calc(15px*var(--rs))}
  .cxarch{width:100vw}
  .cxinstr{font-size:calc(16px*var(--rs))}
}

/* wiki index taxonomy */
.taxgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:6px 28px;margin-top:14px}
.taxcat{border-top:2px solid var(--ink);padding:9px 2px 14px}
.taxname{font:600 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.13em;text-transform:uppercase;display:flex;justify-content:space-between;align-items:baseline}
.taxname .taxtot{font:400 calc(10px*var(--rs)) var(--sans);color:var(--faint);letter-spacing:.06em}
.taxkinds{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.taxkind{font:500 calc(11px*var(--rs)) var(--sans);letter-spacing:.02em;border:1px solid var(--rule2);color:var(--dim);padding:5px 9px;text-decoration:none}
.taxkind:hover{border-color:var(--ink);color:var(--ink)}
.taxkind .taxn{color:var(--faint);font-size:calc(9.5px*var(--rs));margin-left:2px}

/* Scrollspy: the section-nav chip for the section currently in view. */
.newsbar .chip.here{background:var(--hue,var(--ink));border-color:var(--hue,var(--ink));color:var(--paper)}
.newsbar .chip.here .cn{color:var(--paper)}

/* puzzle byline + build-your-own form */
.pzform{max-width:640px;margin-top:16px}
.pzf{display:flex;flex-direction:column;gap:5px;margin-bottom:14px}
.pzf>span{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.12em;text-transform:uppercase;color:var(--dim)}
.pzf>span em{font-style:italic;text-transform:none;letter-spacing:0;color:var(--faint)}
.pzf input{background:var(--paper);border:1px solid var(--rule2);padding:9px 11px;font:400 calc(14px*var(--rs)) var(--serif);color:var(--ink)}
.pzgroups{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:4px 0 16px}
@media(max-width:560px){.pzgroups{grid-template-columns:1fr}}
.pzgroup{border:0;border-top:2px solid var(--ink);padding:8px 0 10px;margin:0;min-width:0}
.pzgroup legend{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.12em;text-transform:uppercase;color:var(--dim);padding:0}
.pzgroup .pzgtier{margin-left:6px;font-size:calc(11px*var(--rs));letter-spacing:0}
.pzgname{width:100%;background:var(--paper);border:1px solid var(--rule2);padding:8px 10px;font:500 calc(13px*var(--rs)) var(--serif);color:var(--ink);margin-top:6px}
.pzwords{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:6px}
.pzwords input{background:var(--paper);border:1px solid var(--rule);padding:7px 9px;font:400 calc(12.5px*var(--rs)) var(--sans);color:var(--ink);min-width:0}
.pzsubmit{display:flex;gap:14px;align-items:center;margin-top:8px}
.pzfine{margin-top:8px;color:var(--faint);font:400 calc(11.5px*var(--rs)) var(--sans)}

/* Jobs community gate + CV drop-zone */
.joblock{border-left:3px solid var(--gold);padding:12px 0 14px 16px;margin:8px 0 6px}
.joblock .jlp{font:400 calc(14px*var(--rs))/1.55 var(--serif);color:var(--dim);max-width:60ch;margin:6px 0 12px}
.joblock .dz{max-width:460px}
.joblock .dz .dzk{font:600 calc(12px*var(--rs)) var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--ink)}
.joblock .dz .dzs{font:400 calc(12.5px*var(--rs)) var(--serif);color:var(--dim);margin-top:3px}
.joblock .jlalt{margin-top:12px;font:500 calc(11px*var(--rs)) var(--sans);letter-spacing:.05em;color:var(--faint)}

/* ============================================================
   KARMA — rank insignia (SVG) + profile standing card.
   Tier colours come from the edition's own scheme.
   ============================================================ */
.rankbadge{display:inline-flex;align-items:center;gap:4px;vertical-align:-2px}
.rankbadge .rankico{width:14px;height:14px;color:var(--dim)}
.rankbadge .rankname{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--dim)}
.rankbadge.mini{gap:0;margin-right:4px}
.rankbadge.mini .rankico{width:12px;height:12px}
.rankbadge.t0 .rankico{color:var(--faint)}
.rankbadge.t1 .rankico{color:var(--dim)}
.rankbadge.t2 .rankico,.rankbadge.t2 .rankname{color:var(--ok)}
.rankbadge.t3 .rankico,.rankbadge.t3 .rankname{color:var(--mark)}
.rankbadge.t4 .rankico,.rankbadge.t4 .rankname{color:var(--gold)}
.rankbadge.t5 .rankico,.rankbadge.t5 .rankname{color:var(--act)}

.profhead{display:flex;align-items:center;gap:14px;margin:24px 0 10px;
  /* The masthead device: a page about a person opens the way a section does. */
  border-top:3px double var(--rule2);padding-top:14px}
.profmark{flex:0 0 auto;width:56px;height:56px;object-fit:cover;border:1px solid var(--rule2);filter:grayscale(1)}
.profname .rankbadge{vertical-align:middle}
.profown{font-family:var(--mono,"Archivo",sans-serif);font-size:calc(12px*var(--rs));margin-top:20px}
/* Three numbers, not four — the fourth restated the rank card above it. */
.statgrid.three{grid-template-columns:repeat(3,1fr)}
/* Recent bylines — what the member has actually written, which is what the
   reader clicked their name to find out. */
.bylines{display:flex;flex-direction:column;border-top:1px solid var(--rule)}
.byline{display:block;padding:11px;border-bottom:1px solid var(--rule);text-decoration:none}
.byline:hover{background:var(--paper2)}
.bytt{display:block;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.07em;text-transform:uppercase;color:var(--faint)}
.byex{display:block;font-family:"Newsreader",serif;font-size:calc(15px*var(--rs));
  line-height:1.45;color:var(--ink);margin:3px 0 2px;max-width:66ch}
.bymeta{display:block;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.06em;color:var(--faint)}
.profname{font-family:var(--disp);font-weight:800;font-size:clamp(24px,4vw,32px)}
.profmeta{font-size:calc(11px*var(--rs));color:var(--faint);text-transform:uppercase;letter-spacing:.06em;margin-top:4px}
.karmacard{display:flex;gap:16px;align-items:center;border:1px solid var(--rule2);border-left:4px solid var(--dim);padding:14px 16px;margin:6px 0}
.karmacard .kc-badge{flex:0 0 auto;width:54px;height:54px;display:flex;align-items:center;justify-content:center;border:1px solid var(--rule2)}
.karmacard .kc-ico{width:34px;height:34px;color:var(--dim)}
.karmacard .kc-body{flex:1;min-width:0}
.karmacard .kc-rank{font:800 calc(18px*var(--rs)) var(--disp);display:flex;align-items:baseline;gap:8px}
.karmacard .kc-tier{font:500 calc(9px*var(--rs)) var(--sans);letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.karmacard .kc-karma{font:400 calc(13px*var(--rs)) var(--sans);color:var(--dim);margin:2px 0 6px}
.karmacard .kc-karma b{font-weight:700;color:var(--ink)}
.karmacard .kc-bar{height:6px;background:color-mix(in srgb,var(--ink) 10%,transparent);position:relative;overflow:hidden}
.karmacard .kc-bar i{position:absolute;inset:0 auto 0 0;background:var(--dim)}
.karmacard .kc-next{font:400 calc(10.5px*var(--rs)) var(--sans);color:var(--faint);margin-top:5px}
.karmacard.t2{border-left-color:var(--ok)}.karmacard.t2 .kc-ico{color:var(--ok)}.karmacard.t2 .kc-bar i{background:var(--ok)}
.karmacard.t3{border-left-color:var(--mark)}.karmacard.t3 .kc-ico{color:var(--mark)}.karmacard.t3 .kc-bar i{background:var(--mark)}
.karmacard.t4{border-left-color:var(--gold)}.karmacard.t4 .kc-ico{color:var(--gold)}.karmacard.t4 .kc-bar i{background:var(--gold)}
.karmacard.t5{border-left-color:var(--act)}.karmacard.t5 .kc-ico{color:var(--act)}.karmacard.t5 .kc-bar i{background:var(--act)}

/* Connections group-tier dots. Same four board colours (--cg0..--cg3 —
   yellow/green/blue/red) as the solved rows and the share grid, so a tier
   reads the same everywhere it appears. */
.tierdot{width:12px;height:12px;vertical-align:-2px;margin-right:3px;flex:0 0 auto;color:var(--c)}
.tierdot.t0{--c:var(--cg0)}
.tierdot.t1{--c:var(--cg1)}
.tierdot.t2{--c:var(--cg2)}
.tierdot.t3{--c:var(--cg3)}
/* The four board colours are tuned as fills sitting behind dark ink. As a
   12px mark on pale paper the same hue washes out, so the light editions
   deepen it rather than swap it for a different hue. */
[data-theme="slate"] .tierdot,[data-theme="paper"] .tierdot,
[data-theme="dawn"] .tierdot,[data-theme="heritage"] .tierdot{color:color-mix(in srgb,var(--c) 66%,#000)}
/* e-ink has no hue to spend: the dots fall back to the tone ramp and the
   shape carries the tier, same bargain as the board itself. */
[data-theme="eink"] .tierdot.t0{color:var(--t0)}
[data-theme="eink"] .tierdot.t1{color:var(--t1)}
[data-theme="eink"] .tierdot.t2{color:var(--t2)}
[data-theme="eink"] .tierdot.t3{color:var(--t3)}

/* Puzzle star rating (no drop-down) — reverse-order fill trick */
.starrate{display:inline-flex;flex-direction:row-reverse;justify-content:flex-start}
.starrate input{position:absolute;width:1px;height:1px;opacity:0}
.starrate label{cursor:pointer;color:var(--rule2);padding:0 2px;line-height:0}
.starrate label svg{width:26px;height:26px;fill:currentColor;display:block}
.starrate label:hover,.starrate label:hover ~ label,.starrate input:checked ~ label{color:var(--gold)}
.starrate input:focus-visible ~ label{outline:2px solid var(--ink);outline-offset:1px}

/* Puzzle hub — colour from the edition scheme, per puzzle type */
.eplisten{color:var(--ok);text-decoration:none;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase}
.eplisten:hover{text-decoration:underline}

/* Podcast episode page — inline <audio> player (replaces the old broken
   image-of-an-mp3 fallback). */
.rpod{display:flex;gap:16px;align-items:center;margin:14px 0 4px;padding:14px 16px;border:1px solid var(--rule2);background:color-mix(in srgb,var(--ink) 3%,transparent)}
.rpodart{flex:0 0 84px;width:84px;height:84px;object-fit:cover;border:1px solid var(--rule)}
.rpodmain{flex:1;min-width:0}
.rpodkick{display:block;font:700 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin-bottom:8px}
/* The article's hand-off to the persistent bar. It is a button in everything
   but markup — a real <a> to the audio file, so it still works with no JS —
   and it mirrors the playing state through .epplay, the same hook a river row
   uses. */
.rpodplay{display:inline-flex;align-items:center;gap:8px;padding:9px 16px 9px 13px;
  border:1px solid var(--rule2);border-radius:2px;font:600 calc(11px*var(--rs)) var(--sans);
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink);text-decoration:none}
.rpodplay:hover{border-color:var(--ink);background:var(--paper2)}
.rpodplay .ic{width:15px;height:15px;flex:0 0 auto}
.rpod.playing .rpodplay,.rpod.cued .rpodplay{border-color:var(--act);color:var(--act)}
.rpodhint{display:block;margin-top:7px;font:400 calc(10px*var(--rs)) var(--sans);
  letter-spacing:.06em;color:var(--faint)}

/* /listen playing-row highlight: the row cued/playing in the mini-player.
   GOLD, not the accent — the keyboard ring (.kfocus) is already a blue box,
   and two blue boxes read as two selections. Gold is the paper's "marked"
   colour (stars, tracked edges), which is what a playing row is.
   Padding/negative-margin pair keeps the text column aligned with its
   neighbours when the box appears. */
.eprow.cued{background:color-mix(in srgb,var(--gold) 8%,transparent);
  box-shadow:inset 0 0 0 1.5px var(--gold);
  padding-left:10px;padding-right:10px;margin-left:-10px;margin-right:-10px}
.eprow.cued .epplay{background:var(--gold);border-color:var(--gold);color:var(--paper)}
.eprow.cued .epplay:hover{background:var(--gold);color:var(--paper)}

/* ---- Listen: the ring rides the scroll (app.js: lnav) ----
   On a podcast page the blue keyboard ring is a playhead: app.js walks .kfocus
   to the row on the reading line — the middle of what the sticky bar and the
   mini-player leave visible — as the reader scrolls, and writes --near on the
   rows around it (1 on the line, 0 a few hundred px away). Everything here is
   spent out of that one number, so the list moves under a fixed needle the way
   a music app's does instead of being a flat wall of episodes with a box drawn
   on one of them.

   Depth, never legibility: the floor is .8, not a fade to nothing, and the
   drift is 5px. Reduced motion and e-ink kill every transition and animation
   site-wide at the top of this file; the overrides at the end of this block
   flatten the static half too, so neither ends up with a permanently dimmed
   list and no movement to explain it. */
body.lnav .eprow{--near:0;
  opacity:calc(.8 + .2*var(--near));
  transform:translateX(calc(5px*var(--near)));
  transition:opacity .22s ease,transform .22s ease,background-color .2s ease}
body.lnav .eprow .epplay{transform:scale(calc(1 + .07*var(--near)));
  transition:transform .22s ease,background-color .2s ease,border-color .2s ease,color .2s ease}
/* The row on the line: accent wash and a filled glyph, so it is visibly the row
   o/Enter opens and p plays. .cued (gold — the episode actually in the bar)
   keeps its own glyph: a playing row must not lose its colour to the ring
   passing over it. The ring LANDS rather than blinking on — it drops in from a
   wider offset as the class arrives, which reads as one highlight travelling
   down the list rather than a box teleporting between rows. It retriggers on
   every move because the class arrives on a fresh element each time. */
body.lnav .eprow.kfocus{background:color-mix(in srgb,var(--act) 7%,transparent);
  animation:lnavland .2s ease-out}
body.lnav .eprow.kfocus:not(.cued) .epplay{background:var(--act);color:var(--paper)}
/* An episode row rings a pixel inside itself now (see "the ring on a river
   row"), so the landing settles IN to -1px rather than out to 4px. */
@keyframes lnavland{from{outline-offset:8px;outline-color:transparent}to{outline-offset:-1px}}
@media (prefers-reduced-motion:reduce){
  body.lnav .eprow{opacity:1;transform:none}
  body.lnav .eprow .epplay{transform:none}
  /* The landing is a box shrinking into place, which is motion — it animates
     outline-offset and had slipped through this block. */
  body.lnav .eprow.kfocus{animation:none}}
[data-theme="eink"] body.lnav .eprow{opacity:1;transform:none}
[data-theme="eink"] body.lnav .eprow .epplay{transform:none}
[data-theme="eink"] body.lnav .eprow.kfocus{animation:none}

/* ---- News: the same needle over the river's cards (app.js: nnav) ----
   .ride is put on every card the engine measured, so this does not have to
   spell out the eight selectors the rail is made of and cannot drift from them.

   The one difference from Listen is that the desk is MULTICOL: the line crosses
   all three columns at once, so cards either side of it take the depth in EVERY
   column while exactly one — the one in the column the ring is riding — is lit.
   That is what makes it read as a needle sweeping the page rather than a box
   moving down a list. ←/→ change column; the scroll never does.

   Depth, never legibility: the floor is .82, not a fade to nothing, and the
   drift is 4px. Reduced motion and e-ink kill every transition and animation
   site-wide at the top of this file; the overrides at the end of this block
   flatten the static half too, so neither ends up with a permanently dimmed
   page and no movement to explain it. */
body.nnav .ride{--near:0;
  opacity:calc(.82 + .18*var(--near));
  transform:translateX(calc(4px*var(--near)));
  transition:opacity .22s ease,transform .22s ease,background-color .2s ease}
/* The card on the line: accent wash and a slight lift, so it is visibly the one
   o/Enter opens.

   Scale is a TRANSFORM and never a font-size. These cards sit in a multicol
   whose column breaks are computed from layout, so growing the type would
   re-break every panel below the ring on the frame it moved — the whole page
   reflowing under a highlight is the opposite of a needle. 1.2% is the most
   that reads as lift rather than as a wobble at this card size.

   The ring LANDS rather than blinking on: it settles in from a slightly wider
   scale with the outline transparent, which reads as one highlight travelling
   down the page. Only a `from` keyframe, deliberately — the implicit `to` is
   each element's own computed style, and outline-offset differs by card
   (4px on a .vcard, -1px on every river row), so a spelt-out `to` would flatten
   several different rings into one wrong one. */
body.nnav .ride.kfocus{background:color-mix(in srgb,var(--act) 6%,transparent);
  transform:translateX(4px) scale(1.012);
  animation:nnavland .2s ease-out}
@keyframes nnavland{from{outline-color:transparent;transform:translateX(4px) scale(1.03)}}
@media (prefers-reduced-motion:reduce){
  body.nnav .ride{opacity:1;transform:none}
  body.nnav .ride.kfocus{transform:none;animation:none}}
[data-theme="eink"] body.nnav .ride{opacity:1;transform:none}
[data-theme="eink"] body.nnav .ride.kfocus{transform:none;animation:none}

/* Sticky podcast mini-player bar */
body.lp-on{padding-bottom:78px}
.lplayer{position:fixed;left:0;right:0;bottom:0;z-index:60;display:flex;align-items:center;gap:12px;
  padding:10px max(16px,calc((100% - 1180px)/2));border-top:2px solid var(--ink);
  background:color-mix(in srgb,var(--paper) 94%,var(--ink));backdrop-filter:saturate(1.2) blur(6px)}
.lplayer[hidden]{display:none}
.lpbtn{flex:0 0 auto;display:flex;align-items:center;justify-content:center;background:none;border:1px solid var(--rule2);color:var(--ink);cursor:pointer}
.lptoggle{width:42px;height:42px;border-color:var(--act);color:var(--act)}
.lptoggle:hover{background:var(--act);color:var(--paper)}
.lptoggle .ic{width:18px;height:18px;fill:currentColor;stroke:none}
.lpmeta{flex:0 1 auto;min-width:0;max-width:34%;display:flex;flex-direction:column;gap:1px}
.lptitle{font:600 calc(14px*var(--rs))/1.25 var(--serif);color:var(--ink);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lptitle:hover{text-decoration:underline}
.lpshow{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:uppercase;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lptime{flex:0 0 auto;font-size:calc(11px*var(--rs));color:var(--dim);font-variant-numeric:tabular-nums}
.lpseek{flex:1 1 auto;min-width:80px;height:4px;-webkit-appearance:none;appearance:none;background:var(--rule2);border-radius:2px;cursor:pointer}
.lpseek::-webkit-slider-thumb{-webkit-appearance:none;width:13px;height:13px;border-radius:50%;background:var(--act);cursor:pointer}
.lpseek::-moz-range-thumb{width:13px;height:13px;border:0;border-radius:50%;background:var(--act);cursor:pointer}
.lpseek:disabled{opacity:.5;cursor:default}
/* Skip pair — borderless circular arrows flanking the toggle, the seconds
   count sitting inside the arc, the way Apple draws them. */
.lpskip{position:relative;width:38px;height:38px;border:none;color:var(--ink)}
.lpskip .ic{width:27px;height:27px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.lpskip .skipn{position:absolute;top:50%;left:50%;transform:translate(-50%,-38%);font:700 9.5px var(--sans);pointer-events:none}
.lpskip:hover{color:var(--act);background:none}
.lpdiscuss{height:30px;padding:0 9px;gap:6px;font:700 calc(10px*var(--rs)) var(--sans);letter-spacing:.07em;text-transform:uppercase;color:var(--ink);text-decoration:none}
.lpdiscuss .ic{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2}
@media (max-width:700px){.lpdiscuss{width:30px;padding:0}.lpdiscuss .lpdlbl{display:none}}
.lprate{min-width:40px;height:30px;padding:0 8px;font:700 calc(11px*var(--rs)) var(--sans);letter-spacing:.04em}
.lprate:hover,.lpclose:hover,.lpdiscuss:hover{border-color:var(--ink)}
.lpclose{width:30px;height:30px}
.lpclose .ic{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2}
@media (max-width:640px){
  .lptime{display:none}
  .lpmeta{max-width:none;flex:1 1 auto}
  .lpseek{flex:0 0 60px;min-width:60px}
}

/* History — starred list with the star in the left margin */
.starlist{margin-top:6px}
.starrow{display:flex;gap:12px;align-items:baseline;padding:9px 0;border-bottom:1px solid var(--rule);text-decoration:none;color:inherit}
.starrow:hover .starhl{text-decoration:underline;text-underline-offset:3px}
.starmark{flex:0 0 16px;color:var(--gold);line-height:0;align-self:center}
.starmark .ic{width:14px;height:14px;fill:currentColor}
.starbody{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.starhl{font:500 calc(15px*var(--rs))/1.35 var(--serif)}
.starmeta{font:400 calc(10px*var(--rs)) var(--sans);letter-spacing:.04em;color:var(--faint);text-transform:uppercase}
/* filled star button state */
[data-a="star"].on .ic{color:var(--gold)}

/* ===== Watch — inline video player stage + related carousel ===== */
.vstage{margin:14px 0 26px;padding:14px;border:1px solid var(--rule);background:var(--panel,var(--paper));border-radius:4px}
.vstitle h3{margin:2px 0 0;font:700 calc(19px*var(--rs))/1.25 var(--serif);color:var(--ink)}
.vclip{flex:0 0 176px;scroll-snap-align:start;display:flex;flex-direction:column;gap:6px;padding:0;border:0;background:transparent;text-align:left;cursor:pointer;text-decoration:none}
.vclipthumb{position:relative;width:100%;aspect-ratio:16/9;border-radius:3px;overflow:hidden;background:#111;display:flex;align-items:center;justify-content:center}
.vclipthumb img{width:100%;height:100%;object-fit:cover}
.vclipthumb.art{background:linear-gradient(135deg,var(--mark),var(--ink))}
.vclipthumb .ic{position:absolute;width:26px;height:26px;fill:#fff;opacity:.92;filter:drop-shadow(0 1px 3px rgba(0,0,0,.5))}
.vclip:hover .vclipthumb .ic{opacity:1;transform:scale(1.12);transition:transform .12s}
.vclipthumb .vdur{position:absolute;right:5px;bottom:5px;background:rgba(0,0,0,.8);color:#fff;font:600 calc(10px*var(--rs)) var(--sans);padding:1px 4px;border-radius:2px}
.vcliptx{display:flex;flex-direction:column;gap:2px}
.vclipsrc{font:600 calc(9px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.vcliphl{font:500 calc(12px*var(--rs))/1.3 var(--serif);color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.vclipage{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));color:var(--faint)}
.vclip:hover .vcliphl{text-decoration:underline;text-underline-offset:2px}
.vstitle{min-width:0;display:flex;justify-content:space-between;align-items:flex-start;gap:12px;margin-top:10px}
.vssrc{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--faint);margin-top:3px}
/* padding/margin pair: see .bandrow — headroom for the keyboard ring. */
.vrelated{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;padding:7px 7px 6px;margin:-7px -7px 0;-webkit-overflow-scrolling:touch}
.vrelated::-webkit-scrollbar{height:7px}
.vrelated::-webkit-scrollbar-thumb{background:var(--rule);border-radius:4px}
.vstagebar{display:flex;justify-content:flex-end;margin-bottom:8px}
.vupnext{margin-top:16px}
.vupnexthd{margin:0 0 8px}
.vdiscuss{margin-top:20px;border-top:1px solid var(--rule);padding-top:14px;max-width:760px}
.vdiscuss .rcompose{margin:8px 0 14px}
.vdiscuss:empty{display:none}
/* video card thumb-as-button: match the anchor styling */
.vcard button.vthumb{padding:0;border:0;background:transparent;cursor:pointer;width:100%;display:block}

/* ===== Jobs — career profile editor ===== */
.profform{max-width:640px;margin-top:8px}
.pfs{border:1px solid var(--rule);border-radius:4px;padding:12px 14px 14px;margin:0 0 14px}
.pfs legend{font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--faint);padding:0 6px}
.profform label{display:block;font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.03em;color:var(--faint);margin-bottom:8px}
.profform input,.profform select,.profform textarea{width:100%;margin-top:3px;background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);padding:7px;font:400 calc(14px*var(--rs)) var(--serif);border-radius:3px}
.profform textarea{resize:vertical}
.prow{display:flex;gap:12px}
/* The identity-mode group. A <fieldset> for the screen reader (the question
   was never announced when it was a div + span), styled back to what it was. */
.idmode{border:0;border-left:3px solid var(--gold);margin:10px 0;padding:2px 0 2px 11px}
.idmode legend{padding:0;float:left;width:100%}
.dnote.tight{margin:4px 0 8px}
/* Touch targets: the proof rows were ~22px, under even the 24px of 2.5.8. */
.pcheck{display:flex;align-items:center;gap:8px;min-height:44px}
/* The label's prose is wrapped in one <span> in the template. A bare text
   run beside a <strong> is two flex items that cannot be told to shrink, and
   at 375px that pushed the identity radios 57px past the screen. */
.pcheck>span{min-width:0}
/* The board card, previewed on the member's own editor. Dimmed while they are
   not actually listed, so "this is what employers see" is not a lie. */
.wgrid.preview{margin:0 0 18px}
.wgrid.preview.off{opacity:.62}
@media(max-width:520px){.prow{flex-wrap:wrap}.prow>label{flex:1 1 100%}}
.prow>label{flex:1;min-width:0}
.pcheck{display:flex!important;align-items:center;gap:7px;font-size:calc(13px*var(--rs))!important;text-transform:none!important;letter-spacing:0!important;color:var(--ink)!important;margin-bottom:8px}
.pcheck input{width:auto!important;margin:0!important}

/* ===== Jobs — lane map (people + roles on the same tracks) ===== */
.lanemap{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin:6px 0 4px}
.lmcol{border:1px solid var(--rule);border-radius:4px;padding:11px 12px;background:var(--panel,var(--paper))}
.lmhd b{font:700 calc(14px*var(--rs)) var(--disp);color:var(--ink)}
.lmhd .lmsub{display:block;font:400 calc(10px*var(--rs)) var(--sans);letter-spacing:.04em;text-transform:uppercase;color:var(--faint);margin-top:1px}
.lmcount{display:flex;justify-content:space-between;font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.05em;text-transform:uppercase;color:var(--faint);margin:8px 0;padding:5px 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.lmpeople,.lmjobs{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}
.lmchip{font:600 calc(11px*var(--rs)) var(--sans);padding:3px 7px;border-radius:3px;text-decoration:none}
.lmchip.person{background:color-mix(in srgb,var(--hi) 18%,transparent);color:var(--ink);border:1px solid color-mix(in srgb,var(--hi) 40%,transparent)}
.lmchip.role{display:flex;flex-direction:column;background:var(--paper2);border:1px solid var(--rule2);color:var(--ink);line-height:1.2}
.lmchip.role em{font:400 calc(9px*var(--rs)) var(--sans);color:var(--faint);font-style:normal;text-transform:uppercase;letter-spacing:.04em}
.lmempty{font:400 calc(11px*var(--rs)) var(--sans);color:var(--faint);font-style:italic}

/* ===== Jobs — situations-wanted external proof stamps ===== */
.wsen{font:600 calc(9px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--faint);border:1px solid var(--rule2);padding:1px 5px;border-radius:2px;vertical-align:middle}
.cpill.onp{background:color-mix(in srgb,var(--gold) 16%,transparent);border-color:color-mix(in srgb,var(--gold) 40%,transparent)}
.cpill.rank{background:var(--ink);color:var(--paper);border-color:var(--ink);font-weight:700}
.cvelist{display:flex;flex-direction:column;gap:6px;margin:8px 0}
.cverow{display:flex;align-items:center;gap:10px;padding:6px 10px;border:1px solid var(--rule2);font-size:calc(12.5px*var(--rs))}
.cvestate{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.cvestate.s-verified{color:var(--ok)}
.cvestate.s-rejected{color:var(--act)}
.cvestate.s-email_verified{color:var(--gold)}
.cpill.ext{text-decoration:none;color:var(--mark);border-color:color-mix(in srgb,var(--mark) 40%,transparent)}
.cpill.ext:hover{background:color-mix(in srgb,var(--mark) 12%,transparent)}
.cpill.cve{background:color-mix(in srgb,var(--act) 12%,transparent);border-color:color-mix(in srgb,var(--act) 40%,transparent)}
.cpill.cert{background:color-mix(in srgb,var(--ok) 12%,transparent);border-color:color-mix(in srgb,var(--ok) 38%,transparent)}
/* admin feeds — media-type tabs (News / Podcasts / Videos) */
.mtabs{display:flex;width:fit-content;border:1px solid var(--rule2);margin:0 0 12px;font-family:var(--sans);font-size:calc(12px*var(--rs))}
.mtab{padding:7px 14px;border-right:1px solid var(--rule2);color:var(--dim);white-space:nowrap}
.mtab:last-child{border-right:0}
.mtab:hover{color:var(--ink)}
.mtab.on{background:var(--ink);color:var(--paper);font-weight:600}

/* admin feeds — the roster table.
   Four hundred rows with eleven cells each is 4,800 style attributes when the
   padding lives inline, and the browser recomputes every one of them. Here it
   is four rules and one shared computed style, and the page ships ~35% less
   HTML for the same table. */
.aft{width:100%;border-collapse:collapse;font-size:calc(13px*var(--rs))}
.aft th{padding:6px;text-align:left;color:var(--faint)}
.aft thead tr{border-bottom:2px solid var(--rule2)}
.aft th a{color:inherit}
.aft td{padding:5px 6px;vertical-align:top}
.aft tbody tr{border-top:1px solid var(--rule)}
.aft .nw{white-space:nowrap}
.aft .dim{color:var(--faint)}
.aft .ttl{color:var(--ink)}
.aft .sm{font-size:calc(11px*var(--rs))}
.aft .xs{font-size:calc(10px*var(--rs))}
.aft .bad{color:var(--act)}
.aft .warn{color:var(--gold)}
.aft .good{color:var(--ok)}
.aft select,.aft input{background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);font-size:calc(10px*var(--rs));padding:1px}
.aft .vend{width:56px}
.aft form.blk{display:block;margin-top:2px}
.aft form.inl{display:inline}
.aft .g{font-size:calc(10px*var(--rs))}
/* Pager for the roster: the table used to render every feed on one page. */
.apager{display:flex;gap:10px;align-items:center;font-family:var(--sans);font-size:calc(12px*var(--rs));margin:12px 0;color:var(--faint)}
.apager a{color:var(--ink)}
.apager .cur{font-weight:600;color:var(--ink)}

/* ============================================================
   Wiki page — navigation layer (breadcrumb, jump nav, snapshot,
   typed pills, counts, capped panels). See handlers_wiki.go.
   ============================================================ */

/* Typed pills — a small colour dot keyed to the kind of page the pill points
   to, so relation/child/backlink panels read as a legible map (kd families in
   handlers_wiki.go::kindFamily). Hues are mid-tone, legible on every theme. */
.pill[class*="kd-"]{padding-left:16px}
.pill[class*="kd-"]::before{content:"";position:absolute;left:6px;top:50%;
  width:6px;height:6px;margin-top:-3px;border-radius:50%;
  background:var(--kd,#8a938c)}
.kd-threat{--kd:#e0654e}
.kd-malware{--kd:#c86fd0}
.kd-vuln{--kd:#e39a3c}
.kd-technique{--kd:#8891e6}
.kd-tool{--kd:#52c2a8}
.kd-product{--kd:#4f9bd9}
.kd-defence{--kd:#79c98e}
.kd-policy{--kd:#d3b85e}
.kd-science{--kd:#a7ce5a}
.kd-place{--kd:#8fa39a}
.kd-person{--kd:#c9a66b}
.kd-event{--kd:#e0885a}
.kd-concept{--kd:#8a938c}

/* Breadcrumb — parent chain, root → immediate parent. */
.wcrumb{font-size:calc(10px*var(--rs));letter-spacing:.04em;color:var(--faint);
  margin:2px 0 6px;display:flex;flex-wrap:wrap;align-items:center;gap:2px}
.wcrumb a{color:var(--dim);text-decoration:none;padding-left:12px;position:relative}
.wcrumb a::before{content:"";position:absolute;left:3px;top:50%;width:5px;height:5px;
  margin-top:-2px;border-radius:50%;background:var(--kd,#8a938c)}
.wcrumb a:hover{color:var(--ink)}
.wcrumb .sep{color:var(--rule2);margin:0 2px}
.wcrumb .cur{color:var(--ink);font-weight:600}

/* Sticky in-page jump nav. */
.wjump{position:sticky;top:0;z-index:20;display:flex;flex-wrap:wrap;gap:2px;
  margin:10px -4px 4px;padding:6px 4px;background:var(--paper);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
  font-size:calc(10px*var(--rs));letter-spacing:.08em;text-transform:uppercase}
.wjump a{color:var(--dim);text-decoration:none;padding:3px 8px;white-space:nowrap}
.wjump a:hover{color:var(--ink);background:var(--paper2)}
.wjump a b{color:var(--faint);font-weight:600;margin-left:3px}

/* The single-vendor category tail on the "By category" browse list. Set apart
   from the categories that group, and quieter than them. */
.catsingles{margin:10px 0 4px}
.catsingles-hd{font-size:calc(9.5px*var(--rs));letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);margin:0 0 5px}

/* Compiled prose. The body is written in the wiki's authoring dialect and
   rendered by renderWikiBody: [[links]] become ordinary links, and source refs
   become the only #i… links on the page — superscript reference numbers that
   jump to the matching entry in the Sources list. */
.wbody h3,.wbody h4,.wbody h5,.wbody h6{font-family:var(--disp);font-weight:700;margin:14px 0 4px}
.wbody h3{font-size:calc(16px*var(--rs))}
.wbody h4,.wbody h5,.wbody h6{font-size:calc(14px*var(--rs))}
.wbody p,.wbody li{line-height:1.55}
.wbody ul,.wbody ol{margin:6px 0 6px 20px}
.wbody li{margin:3px 0}
.wbody hr{border:0;border-top:1px solid var(--rule);margin:14px 0}
.wbody a[href^="#i"],.wcite{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));
  vertical-align:super;line-height:0;color:var(--act);text-decoration:none;padding:0 1px}
.wbody a[href^="#i"]::before,.wcite::before{content:"["}
.wbody a[href^="#i"]::after,.wcite::after{content:"]"}
.wbody a[href^="#i"]:hover,.wcite:hover{text-decoration:underline}
/* A cited entry lands clear of the sticky jump nav, and lights up on arrival. */
#sources li{scroll-margin-top:64px}
#sources li:target{background:var(--paper2)}

/* Snapshot card — key attributes above the fold ("Janes move"). */
.wsnap{border:1px solid var(--rule2);border-left:3px solid var(--act);
  background:var(--paper2);margin:12px 0 4px;padding:10px 12px 4px}
.wsnap-hd{margin:0 0 6px;color:var(--faint)}
.wsnap dl{display:grid;grid-template-columns:1fr 1fr;gap:2px 20px;margin:0}
@media (max-width:520px){.wsnap dl{grid-template-columns:1fr}}
.wsnap dl>div{display:flex;justify-content:space-between;gap:10px;
  padding:4px 0;border-bottom:1px solid var(--rule)}
.wsnap dt{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.04em;text-transform:uppercase;color:var(--faint);flex:0 0 auto}
.wsnap dd{margin:0;font-size:calc(13px*var(--rs));color:var(--ink);text-align:right}

/* Competing values for one single-valued attribute, inside its Facts row. */
.claimalt{margin-top:2px;font-size:calc(11px*var(--rs));color:var(--faint);line-height:1.4}
.claimalt a{color:var(--dim)}
.claimalt i{font-style:normal;font-size:calc(10px*var(--rs))}

/* Count badges on section headings. */
.ncount{display:inline-block;font-family:"Archivo",sans-serif;
  font-size:calc(9px*var(--rs));color:var(--faint);border:1px solid var(--rule2);
  border-radius:9px;padding:0 6px;margin-left:4px;vertical-align:1px;letter-spacing:.02em}

/* Pill rows + capped/"show all" overflow control. */
.pills{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.pills.capped>.pill:nth-child(n+13){display:none}
/* A longer cap for the category singleton tail, where the pills are the whole
   point of the block rather than an overflow of one. */
.pills.capped24>.pill:nth-child(n+25){display:none}
/* The same facet when its values are clauses rather than tags — a technique's
   detection notes read as a list; as pills they were paragraph-wide lozenges. */
.claimvals{margin:0 0 4px 18px;padding:0;font-size:calc(13px*var(--rs));line-height:1.5}
.claimvals li{margin:3px 0}
.claimvals a{color:var(--ink);text-decoration:underline;text-underline-offset:2px;
  text-decoration-color:var(--rule2)}
.claimvals.capped>li:nth-child(n+13){display:none}
.morelink{display:inline-block;margin:6px 0 2px;font-size:calc(10px*var(--rs));
  letter-spacing:.06em;text-transform:uppercase;color:var(--act);
  background:none;border:0;cursor:pointer;padding:2px 0}
.morelink:hover{text-decoration:underline;text-underline-offset:2px}

/* Section anchors clear the sticky jump nav. */
.wiki section[id]{scroll-margin-top:64px}

/* Intelligence Coverage strip — content-type mix citing the entity (§16). */
.wcov{margin:12px 0 4px}
.wcov-hd{color:var(--faint);margin:0 0 6px}
.wcov-row{display:flex;flex-wrap:wrap;gap:8px}
.wcov-stat{display:flex;flex-direction:column;align-items:center;min-width:64px;
  border:1px solid var(--rule2);border-radius:3px;padding:6px 12px;text-decoration:none;
  background:var(--paper2);transition:border-color .12s,color .12s}
.wcov-stat:hover{border-color:var(--act)}
.wcov-stat b{font-family:var(--disp);font-size:calc(17px*var(--rs));color:var(--ink);line-height:1}
.wcov-stat span{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint);margin-top:3px}

/* Source media-kind tag in the Sources list. */
.srckind{font-family:"Archivo",sans-serif;font-size:calc(8.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;border:1px solid var(--rule2);border-radius:2px;padding:0 4px;
  color:var(--dim);vertical-align:1px}
.srckind.video,.srckind.short{color:var(--act);border-color:var(--act)}
.srckind.podcast{color:var(--gold);border-color:var(--gold)}

/* Faceted browse on the kind listing. */
.wfacets{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin:12px 0 4px;
  padding:10px 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.wfilter{border:1px solid var(--rule2);background:var(--paper);color:var(--ink);
  padding:5px 9px;font-size:calc(12px*var(--rs));width:170px;max-width:100%}
.wfgroup{display:flex;flex-wrap:wrap;align-items:center;gap:5px}
.wflabel{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);margin-right:1px}
.wchip{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.04em;
  border:1px solid var(--rule2);border-radius:2px;color:var(--dim);background:none;
  padding:2px 8px;cursor:pointer;transition:color .12s,border-color .12s,background .12s}
.wchip:hover{color:var(--ink);border-color:var(--ink)}
.wchip.on{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.wchip .wcn{color:var(--faint);margin-left:2px}
.wchip.on .wcn{color:var(--paper)}
.wclear{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--act);background:none;border:0;cursor:pointer;padding:2px 4px}
.wclear:hover{text-decoration:underline;text-underline-offset:2px}
.wikicard.whide{display:none}
.wnomatch{color:var(--faint);padding:18px 0}
.wnomatch[hidden]{display:none}

/* Neighbourhood graph — compact 1-hop node-link SVG (doc item 9). Node/centre
   fill reuses the kd-* colour families via --kd. */
.wgraph{overflow-x:auto}
.wgraph-svg{width:100%;height:auto;max-width:640px;display:block;margin:0 auto}
.wg-edge{stroke:var(--rule2);stroke-width:1}
.wg-rel{fill:var(--faint);font-family:"Archivo",sans-serif;font-size:9px;letter-spacing:.03em}
.wg-node{cursor:pointer}
.wg-dot{fill:var(--kd,#8a938c);stroke:var(--paper);stroke-width:1.5;transition:r .1s}
.wg-node:hover .wg-dot{r:7}
.wg-label{fill:var(--dim);font-family:"Archivo",sans-serif;font-size:11px}
.wg-node:hover .wg-label{fill:var(--ink)}
.wg-center{fill:var(--kd,var(--act));stroke:var(--paper);stroke-width:2}
.wg-clabel{fill:var(--ink);font-family:"Archivo",sans-serif;font-size:12px;font-weight:700}

/* Timeline axis — year-anchored spine, coloured by event type (doc item 10). */
.wtl{margin:4px 0 0;border-left:2px solid var(--rule)}
.wtl-year{padding:2px 0 6px 12px}
.wtl-yr{font-size:calc(13px*var(--rs));font-weight:700;color:var(--dim);letter-spacing:.05em;margin:6px 0 3px}
.wtl-events{list-style:none;margin:0;padding:0}
.wtl-ev{display:flex;align-items:center;gap:8px;font-size:calc(13px*var(--rs));padding:2px 0}
.wtl-dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;background:var(--ev,var(--ok))}
.wtl-type{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));text-transform:uppercase;
  letter-spacing:.06em;color:var(--faint);min-width:66px;flex:0 0 auto}
.wtl-date{color:var(--faint);flex:0 0 auto}
.wtl-title{color:var(--dim);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wtl-syn{opacity:.72}
.wtl-syn .wtl-title{font-style:italic}
.ev-contract{--ev:#d3b85e}
.ev-test{--ev:#4f9bd9}
.ev-deploy{--ev:#79c98e}
.ev-retire{--ev:#e0654e}
.ev-mile{--ev:#c86fd0}
.ev-seen{--ev:#8fa39a}

/* Novelty bands on the media rivers: the clusters river's own header, plus a
   link through to the full clustered river. */
.sechd.novelband .lnk{font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));
  letter-spacing:.04em;color:var(--dim);white-space:nowrap}
.sechd.novelband .lnk:hover{color:var(--ink)}
/* Band cards on Watch are the river's own cards (vmasonry), so they need no
   layout of their own; the tellings fold sits inside the card body, above the
   star row. Listen's bands are .eplist — the same .eprow the rest of the page
   uses, so the strip needs only the rule that closes the list, not a grid.
   (It was a square-artwork grid, which reserved a 1:1 thumb whether or not the
   episode had art.) */
.eplist .eprow:last-child{border-bottom:0}
/* An episode row sitting in a prose river (a podcast lead on the Clusters
   page) takes the river's own gutters, so the two card shapes share one left
   edge instead of the episode hanging into the rule. */
.wirelist .eprow{padding:9px 11px}
.wirelist .eprow:last-child{border-bottom:0}
.eplist .clmore{margin:6px 0 0}
.vcard .clmore{margin:4px 0 0}
.vcard .clmore ul{margin:5px 0 2px 14px}

/* A withdrawn item still renders its page so links resolve; the notice has to
   read as a warning about the copy below it, not as another dim caption. */
.dnote.retracted{color:var(--act);border-left:2px solid var(--act);padding-left:9px;font-style:normal}

/* ============================================================
   /membership — the tier explainer. One prose column; sections
   carry anchors (#pro is the wiki interstitial's landing) with
   the same focus-on-arrival treatment as /profile's .acct.
   ============================================================ */
.mship{max-width:680px;padding-bottom:34px}
.mship p,.mship ul{font-size:calc(14px*var(--rs))}
/* Running prose gets a paragraph gap. The global reset zeroes p margins and
   only .reading put one back, so every other page on this wrapper — terms,
   privacy, editorial, security, membership — set consecutive paragraphs with
   no gap at all and read as one wall: the four paragraphs under Terms'
   "Copyright, aggregation and takedowns" were indistinguishable from one.
   
   :not([class]) is what keeps this surgical. Every classed paragraph in here
   (.tiercta, .tierlede, .tiernote, .acctnote, .kindlab) already carries a
   margin tuned to its block, and a bare `.mship p` would outrank most of them
   on specificity and undo that tuning. Only unclassed prose is claimed. */
.mship p:not([class]){margin:0 0 .8em}
.mship h3.kick{margin:26px 0 8px}
/* Sub-headings had no rule of their own, so h4.kick fell back to the reset's
   zero and sat flush against the paragraph above it — Terms' "Pictures members
   upload" looked like a caption on the sentence it followed rather than the
   head of the section it opens. Less air than h3 above, because it is a step
   down, not a peer. */
.mship h4.kick{margin:18px 0 6px}
/* A paragraph that is just a button is an action row, not prose. .b is a
   40px-tall inline-flex box, so in a 14px line box it crowds whatever sits
   either side of it: on /write the two calls to action were wedged between
   the lines that explain them. Same treatment .tiercta already gives the
   membership tiers, and the gap lets two buttons sit side by side. */
.mship p:not([class]):has(>.b){display:flex;gap:8px;flex-wrap:wrap;align-items:center;
  margin:16px 0 20px}
.mship ul{line-height:1.55;margin:6px 0 10px;padding-left:20px}
.mship ul.kinds{columns:2;column-gap:22px;font-size:calc(12.5px*var(--rs));color:var(--dim);
  margin:2px 0 0;padding-left:18px;list-style:"› "}
.mship .kindlab{font-size:calc(10px*var(--rs));letter-spacing:.11em;text-transform:uppercase;
  color:var(--faint);margin:12px 0 0}
/* .reading — the essay setting, opted into by /about. The .mship default is
   14px with NO paragraph margin at all (the global reset zeroes it and
   nothing put it back), so About's era section arrived as one unbroken wall
   at a ~95-character measure: legible, unreadable. Here the type goes up to
   17px on 1.5 leading, paragraphs get a real gap, and the measure comes in to
   about 70 characters. The container keeps its 680px so the section rules and
   headings still span the column they always did. */
.mship.reading p,.mship.reading ul{font-size:calc(17px*var(--rs));line-height:1.5;max-width:600px}
.mship.reading p{margin:0 0 .85em}
.mship.reading ul{margin:8px 0 .85em;padding-left:22px}
.mship.reading li{margin:0 0 .5em}

.mship section{scroll-margin-top:52px}
.mship section:focus{outline:none}
.mship section:focus-visible{outline:2px solid var(--act);outline-offset:6px}

/* The tier blocks. Each one reads price → promise → list → one action, and
   every qualifier that does not help someone choose a tier is folded into the
   .mfine disclosure beneath it. */
.tier{border:1px solid var(--rule2);padding:16px 18px;margin:20px 0}
.tier.pro{border-left:2px solid var(--act)}
.tier.flat{border:none;border-top:1px solid var(--rule);padding:16px 0 0}
.tierhd{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:6px}
.tierhd h3{font-family:var(--disp);font-size:calc(19px*var(--rs));font-weight:800;margin:0;letter-spacing:-.005em}
.tierprice{margin-left:auto;font-family:var(--mono);font-size:calc(11px*var(--rs));
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.tier.pro .tierprice{color:var(--act)}
.mship .tierlede{font-size:calc(14.5px*var(--rs));color:var(--dim);margin:0 0 10px}
.mship ul.tierlist{margin:0;padding-left:18px;list-style:"› "}
.mship ul.tierlist li{margin:0 0 4px}
.mship .tiernote{font-size:calc(13px*var(--rs));color:var(--dim);margin:12px 0 0}
.mship .tieralt{font-size:calc(12.5px*var(--rs));color:var(--faint);margin:8px 0 0}
.tiercta{margin:14px 0 0;display:flex;gap:8px;flex-wrap:wrap;align-items:center}

/* /partners. The listing is the .edgrid two-column card set; the card's own
   heading and blurb sizes live here rather than inline on the template. */
.partnergrid{columns:2;column-gap:22px;margin:18px 0 6px}
.partnercard{break-inside:avoid;padding:14px 16px}
.partnercard h3{font-family:var(--disp);font-size:calc(18px*var(--rs));margin:0 0 4px}
.mship .partnerblurb{font-size:calc(14px*var(--rs));color:var(--dim);margin:0}
@media(max-width:620px){.partnergrid{columns:1}}

.mfine{border-top:1px solid var(--rule);margin-top:14px}
.mfine summary{cursor:pointer;padding:10px 0;min-height:40px;color:var(--faint);
  font-size:calc(12.5px*var(--rs))}
.mfine summary:hover{color:var(--ink)}
.mship .mfine>p{font-size:calc(13px*var(--rs));color:var(--dim);margin:0 0 10px;max-width:62ch}
.mfine+.mfine{margin-top:0}

/* ============================================================
   /profile — the reader's own account. Sections are stacked
   cards; the last one is the delete-account door and is marked
   as dangerous by an accent edge rather than by colour alone.
   ============================================================ */
.acct{margin:26px 0 0;padding-top:16px;border-top:1px solid var(--rule)}
/* Jumped to from .acctnav. tabindex=-1 moves focus with the scroll (without it
   the next Tab returns to the top of the page); scroll-margin keeps the
   heading off the viewport edge on arrival. */
.acct{scroll-margin-top:52px}
.acct:focus{outline:none}
.acct:focus-visible{outline:2px solid var(--act);outline-offset:6px}
/* The section headings are real h2/h3 now — for a screen reader, which had no
   way to navigate this page. They must not move a pixel. */
.sechd2solo h2,.sechd2solo h3{font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;margin:0}
.sechd2solo.spaced{margin-top:22px}
.acctnote.lead{margin-bottom:10px}
.acctnote b{color:var(--ink)}
.acctnote.bad{color:var(--act)}
.acctnote .aside{color:var(--faint)}
.acctbody{font-size:calc(13px*var(--rs))}
.acct .acctform{display:flex;flex-wrap:wrap;align-items:flex-end;gap:12px 18px;margin:12px 0 6px;
  /* Capped, because a single-line username field stretched to the full width
     of a desktop window reads as a text area, and the eye has to travel the
     whole page to find the button at the end of it. */
  max-width:900px}
.acct .acctnote{max-width:66ch}
.acctsub{font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;
  color:var(--dim);margin:18px 0 6px}
.profhandle{font:400 .55em var(--sans);color:var(--faint);letter-spacing:.04em}
/* the reader's own reactions on /profile — a private list, so plain rows */
.rxlist{list-style:none;margin:10px 0 0;padding:0;max-width:66ch}
.rxlist li{display:flex;gap:9px;align-items:baseline;padding:7px 0;border-top:1px solid var(--rule);
  font:400 calc(12.5px*var(--rs)) var(--sans)}
.rxlist .rem{flex:none;line-height:1}
.rxlist a{color:var(--ink);text-decoration:none}
.rxlist a:hover{color:var(--act)}
.rxlist time{margin-left:auto;white-space:nowrap;color:var(--faint);font-size:calc(11px*var(--rs));font-feature-settings:"tnum" 1}
.acctfield{display:flex;flex-direction:column;gap:5px;flex:1 1 240px;min-width:0}
.acctfield .acctlabel{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.acctfield .acctlabel em{font-style:normal;text-transform:none;letter-spacing:.02em}
/* The three controls share one skin — the account forms only ever needed
   inputs, but the author console has a standfirst, an article body and a
   section picker, and a field that looks like a different design system
   because it happens to be a textarea is the kind of seam readers read as
   "unfinished". 16px on the body is the iOS zoom floor. */
.acctfield input,.acctfield textarea,.acctfield select{background:var(--paper2);
  border:1px solid var(--rule2);padding:7px 9px;color:var(--ink);
  font-size:calc(13px*var(--rs));min-width:0;width:100%}
.acctfield textarea{font-family:var(--sans);line-height:1.5;resize:vertical}
.acctfield input:focus,.acctfield textarea:focus,.acctfield select:focus{outline:2px solid var(--act);outline-offset:-1px}
/* A field that should take the whole row rather than share it. */
.acctfield.wide{flex:1 1 100%}

/* The desk shelf (0247). */
.shelf{margin-bottom:26px}
.shelfband{margin:6px 0 14px;padding-left:11px;border-left:3px solid var(--gold)}
.shelfbandhd{display:block;color:var(--gold);margin-bottom:4px}
.shelfrows{display:flex;flex-direction:column}
.bookrow{display:flex;gap:13px;align-items:flex-start;padding:12px 0;border-bottom:1px solid var(--rule)}
/* A typographic cover: the title and byline set in the paper's own type over a
   tint. No third-party image request, which is the point — a member-supplied
   image URL on a public page is an open tracker. */
.bookcover{flex:0 0 58px;width:58px;height:82px;display:flex;flex-direction:column;
  justify-content:space-between;padding:7px 6px;background:var(--paper2);
  border:1px solid var(--rule2);border-left:3px solid var(--rule2);overflow:hidden}
.bct{font-family:var(--disp);font-size:calc(9px*var(--rs));line-height:1.15;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
.bca{font-family:"Archivo",sans-serif;font-size:calc(6.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bookmeta{flex:1;min-width:0}
.booktitle{font-family:var(--disp);font-size:calc(16px*var(--rs));line-height:1.25;margin:0;font-weight:600}
.booktitle a{color:var(--ink)}
.booksub{font-family:var(--disp);font-size:calc(13px*var(--rs));color:var(--dim);margin-top:1px}
.bookby{font-family:var(--sans);font-size:calc(11.5px*var(--rs));color:var(--faint);margin-top:4px}
.bookclaim{margin-left:6px;font-family:"Archivo",sans-serif;font-size:calc(8.5px*var(--rs));
  letter-spacing:.1em;color:var(--paper);background:var(--gold);border-radius:2px;padding:1px 4px;cursor:help}
.bookblurb{margin:6px 0 0;font-family:var(--sans);font-size:calc(12.5px*var(--rs));
  line-height:1.55;color:var(--dim);max-width:60ch}
.bookvote{flex:0 0 auto}
.bookmine{flex-direction:row;align-items:baseline;gap:8px}
.bookmine input{flex:0 0 auto;accent-color:var(--act)}
@media(max-width:640px){
  .bookcover{flex-basis:46px;width:46px;height:66px}
  .bookrow{gap:10px}
}

/* The river's second exit — quieter than the thread link beside it. */
.morelink.alt{color:var(--faint)}
.morelink.alt:hover{color:var(--ink)}

/* A single thread on its own page. */
.thtitle{font-size:calc(21px*var(--rs));line-height:1.2;margin:2px 0 10px}
.thtitle a{color:var(--ink)}
.thcrumbs{display:flex;flex-direction:column;gap:2px;margin:0 0 14px;
  border-left:2px solid var(--rule2);padding-left:10px}
.thcrumb{display:flex;gap:8px;align-items:baseline;padding:3px 0;color:var(--dim);
  font-family:var(--sans);font-size:calc(12px*var(--rs))}
.thcrumb:hover .thex{color:var(--ink)}
.thwho{flex:0 0 auto;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.thex{color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.thfoot{margin-top:16px;font-family:var(--sans);font-size:calc(12.5px*var(--rs))}

/* Comment sort: three links that read as one control. */
.csort{display:inline-flex;gap:2px;margin-left:auto;font-family:"Archivo",sans-serif;
  font-size:calc(10px*var(--rs));letter-spacing:.08em;text-transform:uppercase}
.csort a{padding:2px 7px;color:var(--faint);border:1px solid transparent}
.csort a:hover{color:var(--ink)}
.csort a.on{color:var(--ink);border-color:var(--rule2);background:var(--paper2)}
.csort a:focus-visible{outline:2px solid var(--act);outline-offset:2px}

/* Collapse. Open by default; the glyph swap is CSS so it works with no JS. */
.ckids{display:block}
.ckids>summary{list-style:none;cursor:pointer;font-family:"Archivo",sans-serif;
  font-size:calc(10px*var(--rs));letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);padding:2px 0;user-select:none}
.ckids>summary::-webkit-details-marker{display:none}
.ckids>summary:hover{color:var(--ink)}
.ckids .cfoldplus{display:none}
.ckids:not([open]) .cfoldminus{display:none}
.ckids:not([open]) .cfoldplus{display:inline}
/* The pre-existing gap this closes on the way past: .creply summary had no
   focus ring at all, so the reply disclosure was invisible to a keyboard. */
.creply summary:focus-visible,.ckids>summary:focus-visible{outline:2px solid var(--act);outline-offset:2px}
@media(pointer:coarse){.ckids>summary{padding:7px 0}}

/* ---------- the composer's topic autocomplete ----------
   The list opens DOWNWARD. It opened upward while the composer was the last
   thing on the standalone discussion page, where there was nothing below it to
   open into. The composer sits above the comments now, so up would have meant
   a listbox covering the sort row and the AI disclosure, and down also matches
   the DOM order the combobox announces. */
.tawrap{position:relative}
.tac{position:absolute;left:10px;right:10px;top:calc(100% - 6px);z-index:3;
  list-style:none;margin:0;padding:0;background:var(--paper2);
  border:1px solid var(--faint);max-height:200px;overflow-y:auto;
  font:400 calc(12px*var(--rs)) var(--sans)}
.tac li{display:flex;justify-content:space-between;gap:16px;align-items:baseline;
  padding:8px 10px;color:var(--dim);cursor:pointer;min-height:40px}
.tac li[aria-selected="true"],.tac li:hover{background:var(--paper);color:var(--ink)}
.tac li b{font-weight:600;color:var(--ink)}
.tac li span{color:var(--faint);font-size:calc(10.5px*var(--rs))}
.rbfoot{display:flex;justify-content:space-between;align-items:center;gap:12px;
  flex-wrap:wrap;margin-top:8px}
.rbhint{font:400 calc(11px*var(--rs)) var(--sans);color:var(--faint)}
.rbhint code{font:500 calc(11px*var(--rs)) var(--sans);color:var(--dim);
  border:1px solid var(--rule2);padding:1px 5px}
/* A topic a reader filed under, inside a comment body. */
.ctopic{color:var(--dim);text-decoration:none;border-bottom:1px solid var(--rule2)}
.ctopic:hover{color:var(--ink);border-color:var(--ink)}

/* ---------- the discussion page: one article's conversation ---------- */
.dcrumb{margin:18px 0 14px;font:500 calc(11px*var(--rs)) var(--sans);
  letter-spacing:.14em;text-transform:uppercase;color:var(--dim);
  display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.dcrumb a{text-decoration:none}
/* .rback is shaped for the top of the reader page. Here it is one item in a
   breadcrumb row and takes the row's metrics. */
.dcrumb .rback{display:inline;padding:0;font:inherit;letter-spacing:inherit;
  text-transform:inherit;color:inherit}
.dcrumb a:hover{color:var(--ink)}
.dcrumb .sep{color:var(--rule2)}
/* The room furniture. Its own page is gone (handlers_discussion_page.go): the
   story header these rules used to sit under is the article's own, so what
   survives here is the tally, the two columns and the rail. */
.thtally{display:flex;flex-wrap:wrap;gap:6px 22px;margin-top:14px;
  font:400 calc(11.5px*var(--rs)) var(--sans);color:var(--dim)}
.thtally b{color:var(--ink);font-weight:600}
.thtally .live{color:var(--ok)}
.thpage{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:44px;align-items:start;margin-top:18px}
@media(max-width:960px){.thpage{grid-template-columns:1fr;gap:28px}}
.thmain{min-width:0}
.thrail{position:sticky;top:20px;display:flex;flex-direction:column;gap:22px;font-family:var(--sans)}
@media(max-width:960px){.thrail{position:static}}
.thbox h3{font:600 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim);padding-bottom:8px;
  border-bottom:1px solid var(--rule);margin-bottom:10px}
.thbox h3 span{font-weight:400;letter-spacing:.06em;text-transform:none;color:var(--faint);margin-left:6px}
.throster{display:flex;flex-direction:column;gap:6px}
.throster a,.throster .thalias{display:flex;align-items:center;gap:8px;text-decoration:none;
  font-size:calc(12px*var(--rs));color:var(--dim);min-height:40px}
.throster a:hover{color:var(--ink)}
.thwhere{margin-left:auto;font-size:calc(9.5px*var(--rs));letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint)}
.thcarry{list-style:none;display:flex;flex-direction:column}
.thcarry li{padding:8px 0;border-bottom:1px dashed var(--rule)}
.thcarry li:last-child{border-bottom:0}
.thcarry a{text-decoration:none;display:block}
.thcarry .t{color:var(--ink);display:block;font-family:var(--serif);
  font-size:calc(13.5px*var(--rs));line-height:1.35}
.thcarry .m{color:var(--faint);font-size:calc(10.5px*var(--rs));letter-spacing:.06em;
  display:block;margin-top:3px}
.thcarry a:hover .t{text-decoration:underline;text-underline-offset:2px}
/* The pulse. Bars carry no meaning on their own, so the figure is labelled and
   the caption beside it does the actual telling. */
.thpulse{display:flex;align-items:flex-end;gap:3px;height:28px;margin:4px 0 6px}
.thpulse i{flex:1;background:var(--rule2);display:block;min-height:2px}
.thpulse i.on{background:var(--dim)}
.thpulse i.now{background:var(--ok)}

/* ---------- the discussion river, one row per conversation ----------
   The row keeps class="thr" alongside .drow: app.js registers .thr in
   LIST_SEL, and the comment there records that the last time this row changed
   shape the whole page dropped out of the keyboard ring and j/k did nothing.
   .drow carries the new layout; .thr keeps the ring. */
/* Four columns, and the header has four cells. It had four all along while the
   grid had three, so "Activity" wrapped onto a second row of its own and every
   number below it sat under a heading naming something else. .dmeta is
   display:contents so the count and the age are grid items in their own right
   rather than a stack crammed into one cell. */
/* The row's own padding is set on .thr further down — the later rule, and the
   one that wins — including the 14px at the sides that holds the keyboard ring
   off the headline. .dhdr below takes the same 14px so the column headings stay
   over their columns. */
.drow{display:grid;grid-template-columns:minmax(0,1fr) 112px 88px 58px;gap:0 18px;
  align-items:start;border-bottom:1px solid var(--rule)}
.dhdr{display:grid;grid-template-columns:minmax(0,1fr) 112px 88px 58px;gap:0 18px;
  padding:8px 14px;border-bottom:1px solid var(--rule);
  font:500 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}
.dhdr span:nth-child(n+3){text-align:right}
.dmain{min-width:0}
/* One line, clipped. It was a wrapping flex row, so a story carrying three
   long topic names pushed the headline down and gave the row a second line of
   furniture above its own title. Block rather than flex because that is what
   text-overflow needs; the children space themselves on margins. */
.dkick{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  font:500 calc(10px*var(--rs)) var(--sans);letter-spacing:.12em;
  text-transform:uppercase;color:var(--dim)}
.dkick i{width:7px;height:7px;border-radius:50%;display:inline-block;
  vertical-align:1px;margin-right:7px}
.dkick>*+*{margin-left:8px}
/* The desk owns the row and is named in full beside its dot. Brighter than the
   section, because four colours a reader can learn are worth more than forty
   they cannot. */
.ddesk{color:var(--ink);font-weight:700}
/* The section keeps its own hue, on a left edge rather than a second dot: two
   dots side by side read as one two-part signal, and these are two different
   taxonomies. */
.dsec{display:inline-block;color:var(--dim);text-decoration:none;
  border-left:2px solid var(--sechue,var(--rule2));padding-left:7px}
.dsec:hover{color:var(--ink)}
/* A newsroom that came here to argue but does not own the story. Its own dot,
   its own name, lower-case against the kicker's caps so it reads as an aside
   rather than a second headline. */
.dalso{color:var(--faint);text-transform:none;letter-spacing:.03em;
  font-size:calc(10.5px*var(--rs))}
.dalso i{background:var(--rule2);margin-right:5px}
.dtag{font:400 calc(10.5px*var(--rs)) var(--sans);letter-spacing:.03em;
  text-transform:none;color:var(--faint);text-decoration:none}
/* --faint, not --rule2. The hash is not punctuation between two things, it is
   the first character of the token: the rail above writes the same topic as one
   uniform "#ai", and this row wrote the # in the hairline colour and the word
   in --faint. Two treatments of one string on one page, and the hairline half
   measures 1.8:1 against paper in phosphor and 2.2:1 in slate, under the 3:1
   floor for a glyph that carries meaning. --rule2 draws rules. */
.dtag::before{content:"#";color:var(--faint)}
.dtag:hover{color:var(--ink)}
.dtitle{font:400 calc(17px*var(--rs))/1.3 var(--serif);color:var(--ink);margin:3px 0 2px;max-width:60ch}
.dtitle a{text-decoration:none;color:inherit}
/* The whole row is clickable without a line of JS. The design does it with a
   click handler that calls .click() on the title link, which throws away
   modifier keys (no cmd-click, no middle-click, and middle-click never fires
   `click` at all) and navigates when a reader finishes selecting the snippet.
   A pseudo-element over the row is the house answer and is already used by
   .srow .hl, .chip, .b and .deskbar .dk. */
.drow{position:relative}
.dtitle a::after{content:"";position:absolute;inset:0}
.drow:hover .dtitle,.drow:focus-within .dtitle{text-decoration:underline;
  text-underline-offset:3px;text-decoration-color:var(--rule2)}
.dsnip{font:400 calc(13.5px*var(--rs))/1.45 var(--serif);color:var(--dim);
  max-width:62ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dspeaker{font-weight:600;color:var(--ink)}
.dsnip .aibadge{margin-left:.3em}
/* The monogram. .cav, never .mono: .mono is the global Archivo utility and
   the comment byline wears it. A reader is a circle, a Cast character a
   square, but the shape is never the only signal: each carries its name as
   its accessible label and says so in the title, because e-ink flattens every
   desk hue to one grey and a border-radius announces nothing at all. */
.dwho{display:flex;gap:3px;flex-wrap:wrap;justify-content:flex-start;align-content:flex-start}
/* The face first, the initials after it. Two letters derived from a username
   identify nobody — Monogram's own comment records that hullnumber comes out
   HU — while the mark beside a name is the same mark that name wears on every
   comment it has ever posted, which is a thing a regular reader learns. The
   initials stay because the face is a picture and pictures are slow to scan:
   the pair reads faster than either alone. */
.cav{display:inline-flex;align-items:center;gap:3px;padding:1px 4px 1px 2px;min-height:20px;
  border:1px solid var(--cavhue,var(--faint));color:var(--cavhue,var(--dim));
  background:var(--paper);text-decoration:none}
.cavm{font:600 calc(8.5px*var(--rs)) var(--sans);letter-spacing:.06em}
.cav:hover{border-color:var(--ink);color:var(--ink)}
/* A reader. Round ends, and deliberately without a --cavhue: readers wear no
   desk, and borrowing one would dress the only voice that is not staff as
   staff. A pill rather than a circle now that the box holds two things, with
   an extra pixel of gutter so the mark clears the curve instead of being
   clipped by it. */
.cav.h{border-radius:11px;padding-left:4px;padding-right:6px}
/* In the light editions a --d-* at its dark-theme weight is too pale against
   paper for a 8.5px monogram, so the hue is mixed toward the ink for the text
   and left alone on the border. Matches what .tierdot already does. */
[data-theme="slate"] .cav,[data-theme="paper"] .cav,
[data-theme="dawn"] .cav,[data-theme="heritage"] .cav{
  color:color-mix(in srgb,var(--cavhue,var(--dim)) 70%,#000)}

/* The face itself. .cmark is the byline's 18px version and this is the same
   art at the sizes these rosters have room for; the rules are kept apart
   because the byline sits in running prose and these sit in boxes.

   A drawn mark is currentColor, so it inherits the desk hue from .cav and the
   rail box for free. An uploaded portrait cannot, so it is held to greyscale
   here exactly as .cmark holds it: a photograph at full saturation beside
   eleven line drawings owns the column. */
.uface{width:14px;height:14px;flex:0 0 auto;display:block}
img.uface{object-fit:cover;filter:grayscale(1);opacity:.9}
.hface{opacity:.8}
/* The rail's face box. Borrows .cav's frame so the two rosters read as one
   system, at the rail's slightly larger size. */
.rface{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  width:20px;height:20px;border:1px solid var(--cavhue,var(--faint));
  color:var(--cavhue,var(--dim));background:var(--paper)}
.rface .uface{width:14px;height:14px}
.dtlist a[href]:hover .rface{border-color:var(--ink);color:var(--ink)}
[data-theme="slate"] .rface,[data-theme="paper"] .rface,
[data-theme="dawn"] .rface,[data-theme="heritage"] .rface{
  color:color-mix(in srgb,var(--cavhue,var(--dim)) 70%,#000)}
.dmore{align-self:center;font:400 calc(10px*var(--rs)) var(--sans);color:var(--faint)}
/* .dtitle a::after covers the whole row so the title is clickable anywhere.
   Anything else in the row that is itself a control has to come back up
   through it, or the overlay eats the click: the monograms have been
   unreachable since the row was drawn, and the reaction rail would have
   shipped the same way. */
.dwho a,.drow .rreact{position:relative;z-index:1}
.drow .rreact{margin-top:6px}
.dmeta{display:contents}
.dnum{font:600 calc(13px*var(--rs)) var(--sans);color:var(--ink);text-align:right}
.dnum small{display:block;font-weight:400;font-size:calc(9.5px*var(--rs));
  letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
/* Muting says which rooms are house-only in words. The design dims them to
   45% opacity, which fails 4.5:1 in every edition (2.36 in slate, 3.70 even
   in phosphor) and, on this corpus, would fire on 260 rows out of 266: an
   effect that lands on 98% of a page is a stylesheet, not a signal. */
.dcastonly{color:var(--gold)}
.dage{font:400 calc(12px*var(--rs)) var(--sans);color:var(--faint);text-align:right}
/* Moved in the last day. The one place on this page where a colour is the
   whole message, so it is also the one place it is allowed to be quiet: green
   against grey in six editions, and in `eink` an age like every other. */
.dage.live{color:var(--ok)}
.dmute{margin-left:auto;font:500 calc(10.5px*var(--rs)) var(--sans);
  letter-spacing:.1em;text-transform:uppercase;color:var(--dim);
  text-decoration:none;border:1px solid var(--faint);padding:6px 11px;
  display:inline-flex;align-items:center;min-height:40px}
.dmute:hover{color:var(--ink);border-color:var(--ink)}
.dmute.on{background:var(--paper2);color:var(--ink);border-color:var(--ink)}
.comsort{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
@media(max-width:700px){
  .dhdr{display:none}
  .drow{grid-template-columns:1fr;gap:6px}
  .dwho{justify-content:flex-start}
  /* One line of metadata instead of three stacked cells, which is why .dmeta
     stops being display:contents here and becomes a row again. */
  .dmeta{display:flex;flex-direction:row;align-items:baseline;gap:8px}
  .dnum,.dage{text-align:left}
  .dnum small{display:inline;margin-left:4px}
  .dsnip{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
}

/* ---------- one comment node ----------
   The c* family is the comment's own furniture, beside .creply / .ckids /
   .cfold* / .cthread / .cmark / .csort which were already here. The redesign
   arrived proposing .c, .mono, .who, .body, .thread and .acts for these; every one
   of those is taken. .mono is the global Archivo utility at the top of this
   file and this partial WORE it, so redefining it as a 20px avatar box would
   have reshaped every byline on four surfaces. .who is a global that paints
   text --d-policy. .thread is the archive rail's and the Jobs talk list's,
   which is the .comrow collision at the bottom of this file happening a third
   time. Renaming was the cheaper half of this work. .c itself is not used either:
   a one-character class in a 4,152-line global sheet is free today and a
   collision the first time anyone else wants it.

   Indent is a custom property set server-side, because Indent is not Depth:
   the thread page re-bases its own root to zero, and a .cnode .cnode nesting rule
   counts DOM depth instead. The step lives here so it is one number. */
.cnode{padding-left:calc(var(--ci,0)*18px);margin-top:10px}
.cnode>.cwho,.cnode>.cbody,.cnode>.cacts,.cnode>.ckids{border-left:2px solid var(--rule);padding-left:10px}
.cwho{display:flex;flex-wrap:wrap;align-items:center;gap:0 6px;
  font:400 calc(10px*var(--rs)) var(--sans);letter-spacing:.05em;
  text-transform:uppercase;color:var(--faint);padding-top:2px}
.cname{color:var(--dim);text-decoration:none}
.cname:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
span.cname:hover{color:var(--dim);text-decoration:none}
/* An alias byline (0298): a name the reader chose, with no card behind it. */
.calias{border:1px solid var(--rule2);padding:0 4px;font-size:.85em;letter-spacing:.08em;color:var(--faint)}
/* The "post as" choice above a compose or reply box. */
.postas{display:flex;align-items:center;gap:6px;margin:0 0 6px;
  font:400 calc(11px*var(--rs)) var(--sans);color:var(--dim)}
.postas select{background:var(--paper);color:var(--ink);border:1px solid var(--rule2);
  padding:3px 6px;font:inherit;min-height:28px}
.postas-k{letter-spacing:.06em;text-transform:uppercase;font-size:.9em;color:var(--faint)}
/* The persona's home desk, and whether this comment is away from it. Text
   rather than a hue dot: desks carry no hue_var (sections do), so a coloured
   desk pip would mean four new tokens in each of the four token blocks, and a
   signal carried only by colour is one e-ink flattens to nothing anyway. */
.chome{color:var(--faint);text-transform:none;letter-spacing:.02em}
.chome::before,.cago::before,.cpts::before{content:"·";color:var(--rule2);margin-right:6px}
.cvisit{margin-left:5px;color:var(--faint);font-style:italic}
.cvisit::before{content:"·";color:var(--rule2);margin-right:5px}
.cago,.cpts{color:var(--faint)}
.cbody{font:400 calc(14px*var(--rs))/1.6 var(--serif);color:var(--ink);padding-top:3px;padding-bottom:3px}
.cbody p+p{margin-top:8px}
.cacts{display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding-bottom:2px}
.cacts form{display:inline}
/* Deep threads run to depth 8. At the full step that is 144px of gutter on a
   360px phone, so the step halves where the screen cannot afford it. */
@media(max-width:620px){.cnode{padding-left:calc(var(--ci,0)*9px)}}

/* The pending-confirmation notice on step 2. Loud, because under double opt-in
   an unconfirmed address receives nothing at all, and this is now the only
   place in the flow that says so. */
.nlpending{margin:0 0 16px;padding:11px 13px;border:1px solid var(--rule2);
  border-left:3px solid var(--act);background:var(--paper2);
  font-family:var(--sans);font-size:calc(13px*var(--rs));line-height:1.55;color:var(--dim)}
.nlpending b{color:var(--ink)}

/* The reveal (0246). Built on the .cxmodal skin the puzzle already ships, so
   the paper has one dialog look rather than two. */
.cxmodal.rev{width:min(560px,calc(100vw - 28px))}
.rev .cxmodalhd{display:flex;gap:14px;align-items:flex-start}
.revwho{flex:0 0 auto;font-size:30px;line-height:1}
.rev .cxclose{margin-left:auto}
.revbody{padding:16px 18px}
/* The award sits above the cloud: the badge is the finding and the cloud is
   the evidence for it. Rules top and bottom rather than a tinted card, because
   a coloured panel here reads as a promotion and the shapes are not a ladder. */
.revaward{display:flex;gap:12px;align-items:flex-start;margin:0 0 16px;padding:12px 0;
  border-top:1px solid var(--rule2);border-bottom:1px solid var(--rule2)}
.ramark{font-size:calc(26px*var(--rs));line-height:1.1;flex:none}
.ratitle{display:block;font-family:var(--sans);font-size:calc(14px*var(--rs));
  letter-spacing:.01em;color:var(--ink)}
.raline{margin:3px 0 0;font-family:var(--sans);font-size:calc(12px*var(--rs));
  line-height:1.5;color:var(--dim)}
.rafoot{margin:6px 0 0;font-family:var(--sans);font-size:calc(10px*var(--rs));
  line-height:1.4;color:var(--faint)}
.ravlbl{margin:0 0 8px;font-family:var(--sans);font-size:calc(10px*var(--rs));
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
/* The cloud. Weight tracks DISTINCT items read, not read-days — the same
   number the floor is drawn on, so what looks biggest is what is best
   evidenced rather than what was refreshed most. */
.revcloud{display:flex;flex-wrap:wrap;gap:8px}
.revchip{display:inline-flex;align-items:center;gap:5px;border:1px solid var(--rule2);
  background:var(--paper2);padding:5px 9px;border-radius:2px;color:var(--ink);
  font-family:var(--sans);font-size:calc((12 + min(var(--w,1),8)) * 1px * var(--rs));line-height:1.2}
.revchip.sm{font-size:calc(11px*var(--rs));padding:3px 7px;color:var(--dim)}
.revhide{display:inline}
.revhide button{background:none;border:0;padding:0 0 0 2px;color:var(--faint);cursor:pointer;
  font-size:calc(11px*var(--rs));line-height:1}
.revhide button:hover{color:var(--bad)}
.revhide button:focus-visible{outline:2px solid var(--act);outline-offset:2px}
.revnote{margin:14px 0 0;font-family:var(--sans);font-size:calc(11.5px*var(--rs));
  line-height:1.55;color:var(--faint)}
.revjobs{display:flex;flex-direction:column;gap:2px}
.revjob{padding:9px 0;border-bottom:1px solid var(--rule)}
.revjobt{font-family:var(--disp);font-size:calc(15px*var(--rs));font-weight:600;line-height:1.25}
.revjobm{font-family:var(--sans);font-size:calc(12px*var(--rs));color:var(--dim);margin:2px 0 5px}
.revjobs2{display:flex;flex-wrap:wrap;gap:5px}
.revacts{padding:0 18px 18px;display:flex;gap:8px;align-items:center}
@media(max-width:640px){.revwho{font-size:24px}}

/* The reader's own last-20 reads. Their history, shown only to them. */
.rdlist{margin-top:10px;border-top:1px solid var(--rule)}
.rdrow{display:flex;gap:10px;justify-content:space-between;align-items:baseline;
  padding:5px 0;border-bottom:1px solid var(--rule);font-size:calc(13px*var(--rs))}
.rdrow a{color:var(--ink)}
.rdwhen{flex:0 0 auto;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}

/* The cross-listing checkbox grid. Columns rather than a list because the
   taxonomy runs to ~30 sections and a single column would push the save
   button off the fold. */
.afsecs{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  gap:4px 14px;flex:1 1 100%;margin:2px 0 4px}
.afsec{display:flex;gap:7px;align-items:baseline;font-family:var(--sans);
  font-size:calc(12.5px*var(--rs));color:var(--dim);cursor:pointer;min-height:26px}
.afsec input{flex:0 0 auto;accent-color:var(--act)}
.afsec:hover span{color:var(--ink)}
@media(pointer:coarse){.afsec{min-height:34px}}
/* The lookup's running commentary. A live region rather than a toast: the
   reader is mid-form and the answer belongs beside the field they are
   filling, not floating at the bottom of the window. */
.aflook{font-family:var(--sans);font-size:calc(11.5px*var(--rs));line-height:1.5;
  color:var(--faint);flex:1 1 100%;min-height:1em;margin:-4px 0 0}
.acctnote{font-family:var(--sans);font-size:calc(11.5px*var(--rs));line-height:1.55;color:var(--faint);margin:6px 0 0}

/* The author console's item list (0204). A list rather than a table: every
   row carries a thumbnail, a state line and two disclosures, which a table
   cell handles badly and a phone handles worse. The reader here is the writer
   looking at their own shelf. */
.aflist{margin:10px 0 0;border-top:1px solid var(--rule)}
.afrow{display:flex;gap:12px;align-items:flex-start;padding:12px 0;border-bottom:1px solid var(--rule)}
.afthumb{flex:0 0 64px;width:64px;height:48px;object-fit:cover;background:var(--paper2);
  border:1px solid var(--rule2)}
/* The imageless case is worth SEEING, not hiding: a pasted article could
   never acquire a lead image until this pass, and a writer should be able to
   tell at a glance which of their pieces will render blank in the river. */
.afthumb.none{display:flex;align-items:center;justify-content:center;font-family:"Archivo",sans-serif;
  font-size:calc(8.5px*var(--rs));letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
.afmeta{flex:1;min-width:0}
.afmeta h4{font-family:var(--disp);font-size:calc(15px*var(--rs));line-height:1.25;margin:0 0 3px;font-weight:600}
.afmeta h4 a{color:var(--ink)}
.afstate{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.afstate .on{color:var(--ok)}
.afstate .off{color:var(--mark)}
.afstate .warn{color:var(--bad)}
.afacts{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:7px}
.afacts form{display:inline}
.afedit,.afprev{margin-top:8px}
.afedit>summary,.afprev>summary{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint);cursor:pointer;list-style:none}
.afedit>summary::-webkit-details-marker,.afprev>summary::-webkit-details-marker{display:none}
.afedit>summary::before{content:"▸ "}
.afedit[open]>summary::before{content:"▾ "}
.afprev>summary::before{content:"▸ "}
.afprev[open]>summary::before{content:"▾ "}
.afedit>summary:hover,.afprev>summary:hover{color:var(--ink)}
.afedit>summary:focus-visible,.afprev>summary:focus-visible{outline:2px solid var(--act);outline-offset:2px}
.afedit .acctform{margin-top:10px;padding:12px;background:var(--paper2);border:1px solid var(--rule2)}
.afprev>div{margin-top:10px;padding:12px;border:1px dashed var(--rule2)}
@media(max-width:640px){
  .afrow{flex-wrap:wrap}
  .afthumb{flex-basis:52px;width:52px;height:40px}
}
/* The tag editor is a shared partial; on /profile it sits inside a card and
   does not need the desk page's separating rules. */
.acct .tagtrack{border:0;margin:10px 0 0;padding:0}
.acct .tagtrack .tthd{margin-bottom:8px}
/* The partial carries its own "Your tags" heading, which on /profile sits
   directly under the section heading of the same name. The dek keeps the
   explanation; only the repeated title goes. */
.acct .tagtrack .tthd>.kick{display:none}
.acct.danger{border:1px solid var(--act);border-left-width:3px;border-top-width:3px;
  border-top-style:double;padding:2px 14px 14px;margin-top:34px}
.acct.danger .kick{color:var(--act)}
.b.danger{border-color:var(--act);color:var(--act)}
.b.danger:hover{background:var(--act);color:var(--paper)}

/* ============================================================
   /history — one timeline over two sources (localStorage reads
   and account stars), grouped by day, with a stars-only filter.
   ============================================================ */
.histbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:16px 0 4px;
  padding-bottom:8px;border-bottom:2px solid var(--rule2)}
.histcount{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin-left:auto}
.histfilter button{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint);padding:3px 9px}
.histfilter button.on{color:var(--act)}

/* Indented to match the rows under it (see .histrow). */
.histday{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);margin:20px 0 2px;padding:0 11px 4px;
  border-bottom:1px solid var(--rule)}
.histday:first-child{margin-top:12px}
.histrow{display:flex;align-items:baseline;gap:10px;padding:9px 11px;
  border-bottom:1px solid var(--rule);font-size:calc(14px*var(--rs))}
.histrow:hover{background:var(--paper2)}
.histmark{flex:0 0 14px;line-height:1}
.histmark .ic{width:12px;height:12px;fill:var(--gold);stroke:none;vertical-align:-1px}
.histtime{flex:0 0 auto;font-family:var(--mono,"Archivo",sans-serif);font-size:calc(11px*var(--rs));color:var(--faint)}
.histtt{flex:1 1 auto;min-width:0;font-family:"Newsreader",serif;line-height:1.35;color:var(--ink)}
.histrow.starred .histtt{color:var(--ink)}
.histsrc{flex:0 0 auto;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.05em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
@media(max-width:600px){.histsrc{display:none}}

/* The no-JS fallback list. app.js removes it once the timeline is built, so
   this only ever renders for a reader without scripting. */
.starseedlist{display:flex;flex-direction:column}
.starseed{padding:7px 2px;border-bottom:1px solid var(--rule);font-family:"Newsreader",serif;
  font-size:calc(14px*var(--rs))}
.starseedsrc{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.05em;
  text-transform:uppercase;color:var(--faint);margin-left:8px}

/* A pro member's comment mark. Greyscale on the server, but the filter is kept
   here too so a mark uploaded before that conversion existed, or one served
   from a cache, still can't shout in colour.

   The frame takes the author's desk from --cavhue, set on .cnode. This is the
   same move .cav and .rface make in the discussion river, and it is on the
   FRAME rather than on the name for a reason worth writing down: a hue on this
   paper names a desk, and painting a username with it would say the colour is
   a property of the person. It is a property of the beat they are writing
   from, and the same character wears a different one when it visits. The
   fallback is the neutral rule colour, which is what a reader gets: readers
   wear no desk (see Participant.DeskHue). */
.cmark{width:18px;height:18px;object-fit:cover;vertical-align:-4px;margin-right:5px;
  border:1px solid var(--cavhue,var(--rule2));filter:grayscale(1);opacity:.85}
/* A house mark is drawn art, not a photograph, so there is no grayscale filter
   to apply — the sprite is currentColor already, which is how it picks the
   desk up. It keeps the frame the photograph has: the pair sit in one column
   and a mark that is framed beside one that is not reads as two states of
   something rather than as two kinds of picture. */
.cmark.hmark{filter:none;opacity:.85;color:var(--cavhue,var(--dim))}
/* Same correction .cav carries: a --d-* at its dark-edition weight is too pale
   against paper for an 18px line drawing, so the hue is mixed toward the ink
   for the art and left alone on the frame. */
[data-theme="slate"] .cmark.hmark,[data-theme="paper"] .cmark.hmark,
[data-theme="dawn"] .cmark.hmark,[data-theme="heritage"] .cmark.hmark{
  color:color-mix(in srgb,var(--cavhue,var(--dim)) 70%,#000)}
/* The picker. Twelve buttons that look like the marks they set, because a list
   of names would make the reader click one to find out what it is. */
.markpick{display:grid;grid-template-columns:repeat(auto-fill,minmax(76px,1fr));
  gap:6px;margin:10px 0 14px;max-width:520px}
.hmopt{display:flex;flex-direction:column;align-items:center;gap:4px;padding:8px 4px;
  background:var(--paper2);border:1px solid var(--rule2);color:var(--dim);cursor:pointer;
  font:inherit;font-size:11px;line-height:1.2}
.hmopt:hover{border-color:var(--ink);color:var(--ink)}
.hmopt.on{border-color:var(--ink);color:var(--ink);background:var(--paper)}
.hmopt.on span{font-weight:700}
.hmbig{display:block}
.markrow{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;margin:12px 0 6px}
.markprev{width:48px;height:48px;object-fit:cover;border:1px solid var(--rule2);
  background:var(--paper2);filter:grayscale(1)}
.markprev.empty{display:flex;align-items:center;justify-content:center;color:var(--faint);
  font-family:"Archivo",sans-serif}
.markform{flex:1 1 260px;margin:0}
.markform input[type=file]{font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));color:var(--dim)}

/* ============================================================
   /admin/feed-health — triage for the ways a feed goes quietly
   wrong. Verdict text carries the meaning; colour only echoes it,
   so the page still reads without it.
   ============================================================ */
.fhnote{font-family:var(--sans);font-size:calc(12px*var(--rs));line-height:1.6;color:var(--dim);
  max-width:78ch;margin:4px 0 12px}
.fhnote code{font-size:calc(11px*var(--rs));background:var(--paper2);padding:1px 4px;border:1px solid var(--rule)}
.fhsub{font-family:"Newsreader",serif;font-style:italic;text-transform:none;letter-spacing:0;
  color:var(--faint);font-weight:400}
.fhtable{width:100%;border-collapse:collapse;font-size:calc(13px*var(--rs))}
.fhtable th{text-align:left;font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint);
  border-bottom:1px solid var(--rule2);padding:4px 8px 4px 0}
.fhtable td{padding:6px 8px 6px 0;border-bottom:1px solid var(--rule);vertical-align:baseline}
.fhtable .n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.fhtable td.n.bad,.fhtable .bad{color:var(--act)}
.fhok{color:var(--ok,var(--dim))}
.fhwarn{color:var(--gold)}
.fhbad{color:var(--act);font-weight:600}
/* Identity drift is a deploy alarm, not a feed fault — it gets its own box so
   it cannot be mistaken for one more row in a triage table. */
.fhalarm{border:1px solid var(--act);border-left-width:3px;padding:11px 14px;margin:14px 0 18px;
  font-family:var(--sans);font-size:calc(12px*var(--rs));line-height:1.6;color:var(--dim);max-width:78ch}
.fhalarm b{color:var(--ink)}
.fhalarm code{font-size:calc(11px*var(--rs));background:var(--paper2);padding:1px 4px;border:1px solid var(--rule)}

/* ============================================================
   /admin — the portal's shared furniture.
   Thirty-six admin templates each carried their own inline
   styles for the same four things: a page heading, a table, a
   form and a row of buttons. They had drifted apart — different
   paddings, different rule weights, three sizes of the same
   heading — so the portal read as thirty-six pages rather than
   one tool. These classes are that furniture, named once.
   ============================================================ */
/* The portal's tab bar. .tab is flex:1 on the public site, which divides the
   width evenly between six river tabs; eight admin tabs under the same rule
   squeeze "Newsletter" to a column of letters. In here they size to their
   words and the bar scrolls sideways if it must, so a tab is never truncated
   to something a reader has to decode. */
.adm .tabs{overflow-x:auto;scrollbar-width:thin;scrollbar-color:var(--rule2) transparent}
.adm .tabs::-webkit-scrollbar{height:3px}
.adm .tabs::-webkit-scrollbar-thumb{background:var(--rule2)}
.adm .tab{flex:0 0 auto;padding-inline:14px;scroll-margin-inline:14px}
/* overflow-x:auto makes overflow-y compute to auto as well, which amputates the
   top and bottom of the global focus ring (2px outline, +2px offset). Inside
   the scroller the ring goes on the inside instead, where nothing clips it. */
.adm .tabs .tab:focus-visible{outline-offset:-2px}

/* ---- the portal's second level: the pages inside the tab you are on ----
   The tab bar names the eight parts of the portal; this names the pages of the
   one you are in, so the dashboard index stops being the only route to
   twenty-four of them.

   It is the public site's two-level nav inverted. Out there a light, widely
   tracked row of desks sits ABOVE the tabs and marks itself with 2px of --hi
   while the tab below takes 3px; in here the light row sits BELOW and keeps
   that same 2px/3px pair — one mark, one step down, so the two rows can never
   be read as one row of equals. Subordination is carried on five axes at once
   (size, weight, case, tracking, colour) because on the e-ink edition --hi,
   --act, --gold and --ink all collapse to one black and colour carries nothing.

   It WRAPS, it does not scroll. The tab bar above may scroll because its eight
   short words are fixed and an operator learns them; a row whose whole job is
   to reveal pages you did not know existed must never hide one off-screen. The
   widest group (wiki, eight labels) holds one line down to about a 990px
   window and costs one 27px line below that — cheaper than a hidden overflow.
   Sentence case rather than the tabs' uppercase is worth ~150px of that width. */
.admsub{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 2px;
  padding:7px 0 0 4px;margin:0 0 2px}
.admsub a{font-family:var(--sans);font-size:calc(11.5px*var(--rs));font-weight:400;
  letter-spacing:.04em;color:var(--faint);white-space:nowrap;
  padding:6px 10px 7px;border-bottom:2px solid transparent}
.admsub a:hover{color:var(--ink)}
.admsub a.on{color:var(--ink);font-weight:600;border-bottom-color:var(--hi)}
@media(max-width:560px){
  .admsub{gap:0;padding-left:0}
  .admsub a{font-size:calc(11px*var(--rs));padding:6px 7px 7px;letter-spacing:.02em}
}

.admhd{display:flex;align-items:flex-end;gap:14px;flex-wrap:wrap;
  border-bottom:2px solid var(--rule2);padding-bottom:8px;margin:18px 0 4px}
.admhd h2{font:800 calc(19px*var(--rs)) var(--disp);letter-spacing:-.01em;margin:0;line-height:1.1}
.admhd .admkick{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));
  letter-spacing:.14em;text-transform:uppercase;color:var(--faint);display:block;margin-bottom:2px}
.admhd .admacts{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
/* The standfirst. Every admin page should be able to say, in one sentence,
   what pressing things here does to the public site. */
.admnote{font-family:var(--sans);font-size:calc(12px*var(--rs));line-height:1.6;
  color:var(--dim);max-width:80ch;margin:8px 0 16px}
.admnote a{color:var(--act)}
.admnote code{font-size:calc(11px*var(--rs));background:var(--paper2);padding:1px 4px;border:1px solid var(--rule)}

/* Filter chips: the counted views of one queue. The count is part of the
   control because "Pending" with no number tells an operator nothing. */
.admfilters{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin:0 0 14px}
.admfilter{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint);border:1px solid var(--rule);padding:5px 10px;
  text-decoration:none;display:inline-flex;gap:6px;align-items:center;white-space:nowrap}
.admfilter:hover{color:var(--ink);border-color:var(--rule2)}
.admfilter.on{color:var(--ink);border-color:var(--ink)}
.admfilter b{font-weight:700;font-variant-numeric:tabular-nums}
.admfilter.warn.n b{color:var(--gold)}
/* A search box belongs in the filter row, not above it: filtering and
   searching narrow the same list, and split apart an operator sets one and
   loses track of the other. It sits right so the counted chips stay adjacent. */
.admsearch{display:flex;gap:6px;align-items:center;margin-left:auto}
.admsearch input[type=search]{background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);
  padding:5px 8px;font-family:var(--sans);font-size:calc(11.5px*var(--rs));min-width:16ch}
.admsearch .b{padding:5px 10px;font-size:calc(10px*var(--rs))}
/* A search that is the page's only control, rather than the tail of a filter
   row: no auto margin, and its own space above the table. */
.admsearch.lead{margin-left:0;margin-bottom:14px}

/* Tiles. A number, what it counts, and — where it means work — a link to the
   queue it belongs to. The accent edge is a second signal beside the colour. */
.admtiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px;margin:14px 0}
.admtile{background:var(--paper);border:1px solid var(--rule);padding:11px 12px;
  display:flex;flex-direction:column;gap:2px}
/* The sans face, not the display one: the display zero is slashed, so a tile
   reading "0 jobs to approve" rendered its number as a symbol. */
.admtile b{font:700 calc(23px*var(--rs)) "Archivo",sans-serif;line-height:1.1;
  font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.admtile span{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));color:var(--dim)}
.admtile em{font-style:normal;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));
  color:var(--faint);letter-spacing:.03em}
.admtile a{color:inherit}
.admtile.due{box-shadow:inset 3px 0 0 var(--gold)}
.admtile.due b{color:var(--gold)}
.admtile.bad{box-shadow:inset 3px 0 0 var(--act)}
.admtile.bad b{color:var(--act)}
.admtile.good{box-shadow:inset 3px 0 0 var(--ok)}

/* Tables. One weight of rule, one alignment rule for numbers, and rows that
   respond to the pointer so a wide row can be tracked across. */
.admtable{width:100%;border-collapse:collapse;font-size:calc(13px*var(--rs))}
.admtable th{text-align:left;font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));
  letter-spacing:.1em;text-transform:uppercase;color:var(--faint);font-weight:600;
  border-bottom:1px solid var(--rule2);padding:0 10px 5px 0;white-space:nowrap}
.admtable td{padding:9px 10px 9px 0;border-bottom:1px solid var(--rule);vertical-align:top}
.admtable tbody tr:hover{background:var(--paper2)}
.admtable .n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.admtable .nw{white-space:nowrap}
.admtable .acts{text-align:right;white-space:nowrap}
.admtable .acts form{display:inline-flex;gap:5px;align-items:center;flex-wrap:wrap;justify-content:flex-end}
/* Deciding an application takes two dates and two verbs. Side by side they
   squeezed the sentence-that-runs column — the one thing on this page an
   operator has to READ — down to four words a line. Two rows instead. */
.admtable .acts form.decide{flex-direction:column;align-items:flex-end;gap:6px}
.admtable .acts .dates{display:flex;gap:6px;align-items:center}
.admtable .acts .dates label{display:inline-flex;gap:4px;align-items:center;
  font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint)}
.admtable .acts .btns{display:flex;gap:5px;align-items:center}
.admtable input[type=date]{background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);
  padding:4px 5px;font-family:var(--sans);font-size:calc(11px*var(--rs))}
.admtable .acts .b{padding:6px 10px;font-size:calc(10px*var(--rs))}
.admsub{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));color:var(--faint);
  display:block;margin-top:2px;word-break:break-word}
.admsub a{color:var(--faint)}
.admsub a:hover{color:var(--act)}
.admempty{padding:26px 4px;color:var(--faint);font-family:var(--sans);
  font-size:calc(12.5px*var(--rs));text-align:center}
.admempty b{color:var(--ink);display:block;font-size:calc(14px*var(--rs));margin-bottom:4px}
/* The empty state INSIDE a cell. .admempty is a whole-table row and is centred
   and padded for it; a cell that holds nothing needs one quiet line, not a
   26px void in the middle of a table that has other rows. */
.admnone{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));color:var(--faint);
  margin-bottom:6px}

/* A heading BETWEEN tables. A page carrying two unrelated tables (the partner
   roster and the slot-demand instrument) had only a paragraph between them, so
   the second table's header row read as a continuation of the first. */
.admsec{display:flex;align-items:center;gap:10px;margin:8px 0 12px}
.admsec.spaced{margin-top:34px}
.admsec h3{font:700 calc(15px*var(--rs)) "Archivo",sans-serif;letter-spacing:.04em;
  color:var(--ink);margin:0;white-space:nowrap}
.admsec i{flex:1;height:1px;background:var(--rule2);order:3}
.admsecnote{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));color:var(--faint);
  white-space:nowrap;order:2}
@media(max-width:620px){.admsecnote{display:none}}

/* The manage column. Three buttons and a date pair is more than "nowrap" can
   honestly hold at a laptop width: it was pushing the sentence-that-runs
   column, the one thing on the page an operator has to read, off the screen.
   Let it wrap, right-aligned, and keep each button whole. */
.admtable .acts.wrap{white-space:normal}
.admtable .acts.wrap .b{white-space:nowrap}
@media(max-width:900px){
  .admtable .acts form{flex-direction:column;align-items:flex-end;gap:6px}
  .admtable .acts .dates{flex-wrap:wrap;justify-content:flex-end}
}

/* A row of verbs. Each is its own POST form (no verb is a GET), so a plain
   flex row of forms is what an action bar is here; without this they stack,
   one button per line, because a form is block-level. */
.admbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:0 0 6px}
.admbar form{display:flex;gap:6px;align-items:center;margin:0}
.admbar input[type=email],.admbar input[type=text],.admbar select{background:var(--paper2);
  color:var(--ink);border:1px solid var(--rule2);padding:6px 8px;
  font-family:var(--sans);font-size:calc(12px*var(--rs))}
.admbar input[type=email]{min-width:200px}

/* The page-level warning. A standing condition that changes what every action
   on the page will do — sending switched off, a provider paused — rather than
   the outcome of the last one, which is what a flash is. Marked by an edge as
   well as a colour: the portal is themed and one of the themes is greyscale. */
.admalert{font-family:var(--sans);font-size:calc(12.5px*var(--rs));line-height:1.5;
  border:1px solid var(--act);border-left-width:3px;background:var(--paper2);
  padding:10px 12px;margin:0 0 14px;color:var(--dim);max-width:900px}
.admalert b{color:var(--act)}
.admalert.ok{border-color:var(--ok)}
.admalert.ok b{color:var(--ok)}

/* State pills carry a word as well as a colour, because the portal is themed
   and one of the themes is greyscale. */
.admstate{font-family:"Archivo",sans-serif;font-size:calc(9.5px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);padding:3px 7px;
  display:inline-block;white-space:nowrap}
.admstate.live{border-color:var(--ok);color:var(--ok)}
.admstate.due{border-color:var(--gold);color:var(--gold)}
.admstate.off{border-color:var(--rule2);color:var(--faint)}
.admstate.bad{border-color:var(--act);color:var(--act)}

/* Forms. A single column of labelled fields, each with room for the sentence
   explaining what the field does to the public site. */
.admform{display:flex;flex-direction:column;gap:16px;max-width:620px;margin:16px 0 8px}
.admform.wide{max-width:none}
.admfield{display:flex;flex-direction:column;gap:5px}
.admfield>span{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint)}
.admfield>span em{font-style:normal;text-transform:none;letter-spacing:.02em;opacity:.9}
.admfield input[type=text],.admfield input[type=url],.admfield input[type=email],
.admfield input[type=date],.admfield select,.admfield textarea{
  background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);padding:8px 9px;
  font-family:var(--sans);font-size:calc(13px*var(--rs));width:100%;min-width:0}
.admfield textarea{resize:vertical;line-height:1.5}
.admfield input:focus,.admfield select:focus,.admfield textarea:focus{
  outline:2px solid var(--act);outline-offset:-1px}
.admfield>span.admhint,.admhint{font-family:var(--sans);font-size:calc(11px*var(--rs));
  color:var(--faint);line-height:1.5;text-transform:none;letter-spacing:normal;max-width:74ch}
.admhint b{color:var(--dim)}
.admrow{display:flex;gap:14px;flex-wrap:wrap;align-items:flex-start}
.admrow>.admfield{flex:1 1 200px}
/* The label sits on one line or two depending on how long it is; without this
   a two-line label pushes its input below the neighbouring one. */
.admrow>.admfield>span:first-child{min-height:2.1em;display:flex;align-items:flex-end}
.admactions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  border-top:1px solid var(--rule);padding-top:14px;margin-top:2px}
.admactions .admright{margin-left:auto;font-family:var(--sans);
  font-size:calc(11px*var(--rs));color:var(--faint)}

/* A checkbox set that reads as a set: one bordered strip, each option a tick
   plus its label, wrapping rather than overflowing on a narrow screen. */
.admchecks{display:flex;gap:8px;flex-wrap:wrap}
.admchecks label{display:inline-flex;gap:7px;align-items:center;border:1px solid var(--rule2);
  padding:7px 11px;font-family:"Archivo",sans-serif;font-size:calc(11.5px*var(--rs));
  color:var(--dim);cursor:pointer}
.admchecks label:hover{border-color:var(--ink);color:var(--ink)}
.admchecks input{accent-color:var(--act);margin:0}
.admchecks input:checked+span{color:var(--ink);font-weight:600}

/* The dashboard's index of the portal. Every page, grouped, with a line
   saying what it is for — replacing a run-on of twenty-eight bare links. */
.admindex{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:18px 24px;margin:16px 0 4px}
.admgroup h3{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint);margin:0 0 6px;padding-bottom:5px;
  border-bottom:1px solid var(--rule2);font-weight:600}
.admgroup a{display:block;padding:6px 0;border-bottom:1px solid var(--rule);
  text-decoration:none;color:var(--ink);font-family:var(--sans);font-size:calc(13px*var(--rs))}
.admgroup a:hover{background:var(--paper2)}
.admgroup a i{display:block;font-style:normal;font-size:calc(11px*var(--rs));color:var(--faint);margin-top:1px}
.admgroup a:hover i{color:var(--dim)}

/* ============================================================
   /profile additions — the index to the page, the unconfirmed-
   email notice, the invitation cards and the referral bar.
   ============================================================ */
/* The index. Twenty sections in one column is a long scroll to reach the last
   one; these are the anchors that already existed, made visible.

   Grouped since the page was, into the same five runs the sections are filed
   under. A flat row of twenty uppercase chips is a wall — the eye has no way
   in — and the group label is what turns it into five short lists. The rows
   come from profileNav in Go, so a section can never be missing from here. */
.acctnav{position:sticky;top:0;z-index:20;background:var(--paper);
  display:flex;flex-direction:column;gap:2px;margin:18px 0 4px;
  padding-bottom:10px;border-bottom:2px solid var(--rule2)}
.acctnavg{display:flex;gap:4px;align-items:baseline;flex-wrap:wrap}
.acctnavh{font-family:"Archivo",sans-serif;font-size:calc(9px*var(--rs));letter-spacing:.14em;
  text-transform:uppercase;color:var(--dim);font-weight:700;white-space:nowrap;
  padding:6px 8px 6px 0;flex:0 0 auto;min-width:8.5em}
.acctnavl{display:flex;gap:2px;flex-wrap:wrap;align-items:center;flex:1 1 auto;min-width:0}
.acctnav a{font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint);text-decoration:none;padding:5px 9px;
  border:1px solid transparent;display:inline-flex;gap:6px;align-items:center;white-space:nowrap}
.acctnav a:hover{color:var(--ink);border-color:var(--rule2);background:var(--paper2)}
.acctnav a:focus-visible{outline:2px solid var(--act);outline-offset:-1px}
.acctnav a b{font-weight:700;color:var(--gold);font-variant-numeric:tabular-nums}
.acctnav a.danger{color:var(--act)}
.acctnav a.danger:hover{border-color:var(--act)}
/* On a phone the label sits above its row rather than stealing half the width. */
@media(max-width:560px){
  .acctnavg{flex-direction:column;gap:0}
  .acctnavh{min-width:0;padding:8px 0 2px}
}

/* The group heading between sections. The five runs the index names, said
   again in the page itself, so a reader who scrolled here rather than clicked
   knows which run they are in. Louder than a section heading on purpose:
   .sechd2solo is a rule with a word on it, this is the word alone. */
.acctgroup{font:800 calc(20px*var(--rs)) var(--disp);color:var(--ink);
  margin:40px 0 -6px;padding-bottom:0;letter-spacing:.01em}
.acctgroup:first-of-type{margin-top:30px}

/* The tier chip beside the username. Says which tier in one word; #membership
   says the rest. Free is deliberately not a warning colour — most readers are
   on it and it is not a fault state. */
.planchip{display:inline-block;vertical-align:middle;margin-left:10px;
  font:700 calc(10px*var(--rs)) "Archivo",sans-serif;letter-spacing:.12em;text-transform:uppercase;
  padding:3px 8px;border:1px solid var(--rule2);color:var(--dim)}
.planchip.pro{border-color:var(--act);color:var(--act)}
.planchip.lapsed{border-color:var(--rule2);color:var(--faint)}

/* A form that opens with a question. Same mechanics as the danger zone one
   block down, in ink rather than the alert colour: sending an invitation is
   not destructive. */
.askform{margin:4px 0 14px}
.askform>summary{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs));font-weight:600;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink);
  border:1px solid var(--ink);padding:10px 14px;min-height:40px;
  list-style:none;user-select:none}
.askform>summary::-webkit-details-marker{display:none}
.askform>summary::before{content:"\25B8";font-size:.9em;line-height:1}
.askform[open]>summary::before{content:"\25BE"}
.askform>summary:hover{background:color-mix(in srgb,var(--ink) 8%,transparent)}
.askform>summary:focus-visible{outline:2px solid var(--act);outline-offset:2px}
.askform .acctform{margin-top:14px;padding:12px;background:var(--paper2);
  border:1px solid var(--rule2)}

/* The danger zone. Deleting an account happens once in an account's life and
   was holding an open block, explanation and all, at the foot of every visit.
   Closed by default, and the summary is the button: a destructive action that
   takes two deliberate gestures (open, then confirm the password) before the
   browser's own confirm() gets a say. */
.dangerzone{margin-top:4px}
.dangerzone>summary{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--act);border:1px solid var(--act);padding:7px 14px;
  list-style:none;user-select:none}
.dangerzone>summary::-webkit-details-marker{display:none}
.dangerzone>summary::before{content:"▸";font-size:.9em;line-height:1}
.dangerzone[open]>summary::before{content:"▾"}
.dangerzone>summary:hover{background:color-mix(in srgb,var(--act) 10%,transparent)}
.dangerzone>summary:focus-visible{outline:2px solid var(--act);outline-offset:2px}
.dangerzone .dzbody{margin-top:14px;padding-left:12px;border-left:2px solid var(--act)}

/* The unconfirmed-email notice. It gates invitations and referral credit, so
   it sits above everything and says so in one line with the fix beside it. */
.acctalert{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin:16px 0 0;
  border:1px solid var(--gold);border-left-width:3px;padding:12px 14px;background:var(--paper2)}
.acctalert b{font-family:"Archivo",sans-serif;font-size:calc(12px*var(--rs));
  letter-spacing:.04em;color:var(--ink)}
.acctalert span{font-family:var(--sans);font-size:calc(12px*var(--rs));line-height:1.55;
  color:var(--dim);flex:1 1 300px}
.acctalert form{margin:0}

/* Invitation codes. A code you still hold is something you hand to a person,
   so it gets a card and a copy button; a spent one recedes into a register of
   who took what. */
.invgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:1px;
  background:var(--rule);border:1px solid var(--rule);margin:4px 0 10px}
.invcard{background:var(--paper);padding:11px 12px;display:flex;flex-direction:column;
  gap:7px;align-items:flex-start}
.invcode{font-family:var(--mono,"Archivo",sans-serif);font-size:calc(17px*var(--rs));
  letter-spacing:.09em;color:var(--ink);word-break:break-all;line-height:1.2}
.invstate{font-family:"Archivo",sans-serif;font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.invstate b{color:var(--dim);font-weight:600;text-transform:none;letter-spacing:.02em}
.invcard.used,.invcard.gone{background:var(--paper2)}
.invcard:not(.used):not(.gone){border-left:3px solid var(--gold)}
.invcard.used .invcode{color:var(--dim)}
.invcard.gone .invcode{color:var(--faint);text-decoration:line-through}
.invcard .b{padding:6px 11px;font-size:calc(10px*var(--rs))}

/* Progress toward the free year of pro. Same shape as the karma card's bar,
   so the two read as one system rather than two inventions. */
.refbar{height:6px;background:color-mix(in srgb,var(--ink) 10%,transparent);
  position:relative;overflow:hidden;margin:2px 0 9px;max-width:340px}
.refbar i{position:absolute;inset:0 auto 0 0;background:var(--gold)}


/* --- Advisor notes (work-lenses-plan A2) --- */
.advnotes{margin:16px 0}
.advnote{border:1px solid var(--rule2);border-left:3px solid var(--gold);padding:10px 14px;margin:10px 0}
.advhead{font-size:12px;color:var(--faint);margin-bottom:6px}
.advhead a{border-bottom:1px solid var(--rule2)}
.advbody p{margin:0 0 8px;font-size:14.5px;line-height:1.55}
.advbody p:last-child{margin-bottom:0}



/* --- The Watch page door: portrait tile strip under the shortsband rule --- */
.shortsband i{flex:1}
.shortsband .shplayall{font-family:"Archivo",sans-serif;font-weight:700;font-size:calc(12.5px*var(--rs));
  color:var(--act);text-decoration:none;white-space:nowrap}
.shortsband .shplayall:hover{text-decoration:underline;text-underline-offset:3px}
/* The 7px inline padding (and its negative margin) is ring headroom — see
   .bandrow. The 12px top already cleared the ring vertically. */
.shtiles{display:flex;gap:12px;overflow-x:auto;scroll-snap-type:x mandatory;
  padding:12px 7px 7px;margin:0 -7px;-webkit-overflow-scrolling:touch}
.shtiles::-webkit-scrollbar{height:7px}
.shtiles::-webkit-scrollbar-thumb{background:var(--rule);border-radius:4px}
.shtile{flex:0 0 148px;scroll-snap-align:start;text-decoration:none;color:inherit}
.shtilecrop{position:relative;display:flex;align-items:center;justify-content:center;
  aspect-ratio:9/16;overflow:hidden;border:1px solid var(--rule2);background:#000}
.shtilecrop img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transition:transform .35s ease}
.shtilecrop.art{background:color-mix(in srgb,var(--ink) 12%,var(--paper2))}
.shtilecrop .ic{position:relative;z-index:1;width:28px;height:28px;color:#fff;background:rgba(0,0,0,.55);
  border-radius:50%;padding:8px;transition:transform .2s ease,background .2s ease}
.shtilecrop .vdur{position:absolute;right:5px;bottom:5px;z-index:1;background:rgba(0,0,0,.8);color:#fff;
  font:600 calc(10px*var(--rs)) var(--sans);padding:1px 4px;border-radius:2px}
.shtile:hover .shtilecrop img{transform:scale(1.06)}
.shtile:hover .shtilecrop .ic{transform:scale(1.12);background:#c8281e}
.shtilecap{display:block;padding:5px 2px 0}
.shtilet{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  font:500 calc(12px*var(--rs))/1.3 var(--serif);color:var(--ink)}
.shtile:hover .shtilet{text-decoration:underline;text-underline-offset:2px}
.shtilesrc{display:block;margin-top:2px;font:600 calc(9px*var(--rs)) var(--sans);
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}

/* --- The reel page, document flow (the no-JS truth) --- */
.shortstage{max-width:520px;margin:0 auto}
.shbar{display:flex;align-items:center;gap:10px;min-height:48px;padding:2px 0;
  border-bottom:1px solid var(--rule)}
.shbar .shkick{font-family:"Archivo",sans-serif;font-weight:700;font-size:calc(13px*var(--rs));
  letter-spacing:.04em;color:var(--act);white-space:nowrap}
.shbar .shcount{font-family:"Archivo",sans-serif;font-size:calc(11px*var(--rs));color:var(--faint)}
.shbar .spacer{flex:1}
.shbar .shsound{min-width:44px;min-height:44px;font-size:17px;background:none;border:0;cursor:pointer;color:var(--ink)}
.shbar .shclose{white-space:nowrap;align-self:center}
.shh1{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
.shfeed{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:28px}
.shslide{padding:18px 0 0;display:flex;flex-direction:column;gap:10px;align-items:center}
.shslide:focus-visible{outline:2px solid var(--act);outline-offset:4px}
.shmedia{position:relative;width:100%;max-width:min(94vw,480px);aspect-ratio:9/16;
  overflow:hidden;border:1px solid var(--rule2);background:#000}
.shmedia img,.shmedia iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
.shmedia img{object-fit:cover}
.shmedia .shplay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:1}
.shmedia .shplay .ic{width:44px;height:44px;color:#fff;background:rgba(0,0,0,.55);border-radius:50%;padding:12px;
  transition:transform .2s ease,background .2s ease}
.shmedia .shplay:hover .ic,.shmedia .shplay:focus-visible .ic{transform:scale(1.1);background:#c8281e}
/* While an embed is live the house Play overlay steps aside — it must never
   sit over the iframe and swallow taps meant for YouTube's own controls. */
.shslide.playing .shplay{display:none}
/* A slide whose embed died: keep its place in flow (deleting under mandatory
   snap jumps the feed), dim the poster, say why. */
.shslide.dead .shposter{opacity:.35}
.shdead{position:absolute;inset:auto 0 0;z-index:2;padding:10px;text-align:center;
  background:color-mix(in srgb,var(--paper) 88%,transparent);color:var(--ink);
  font:500 calc(12.5px*var(--rs)) var(--sans)}
.shdead a{color:var(--act)}
.shbody{width:100%;max-width:min(94vw,480px)}
.shbody .vbody{padding:0}
.shextra{display:flex;gap:14px;margin-top:6px;font-family:"Archivo",sans-serif;font-size:calc(10.5px*var(--rs))}
.shextra .g{color:var(--faint);text-decoration:none}
.shextra .g:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
.shnewer,.shsentinel{display:flex;align-items:center;justify-content:center;padding:10px 0}
.shterminal{display:flex;align-items:center;justify-content:center}
.shdone{max-width:min(94vw,480px);text-align:center;padding:28px 12px;border:1px solid var(--rule)}
.shdone h3{margin:0 0 8px;font:700 calc(20px*var(--rs)) var(--serif);color:var(--ink)}
.shdone p{margin:6px 0;font:400 calc(13px*var(--rs))/1.5 var(--serif);color:var(--dim)}
.shdone a{color:var(--act)}
.shlive{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}
.shhint{display:none}

/* --- The live overlay (JS adds .shorts-live to #shortstage and .shorts-on
       to <body>; scroll lock is belt-and-suspenders with the stored scrollY
       app.js restores on exit, because iOS never reliably honoured it) --- */
body.shorts-on{overflow:hidden}
.shortstage.shorts-live{position:fixed;inset:0;z-index:150;max-width:none;margin:0;
  background:var(--paper);display:flex;flex-direction:column}
.shorts-live .shbar{flex:0 0 48px;padding:0 12px;background:var(--paper)}
.shorts-live .shfeed{flex:1;min-height:0;overflow-y:auto;scroll-snap-type:y mandatory;
  overscroll-behavior:contain;-webkit-overflow-scrolling:touch;gap:0;scroll-padding:4px}
/* min-height, never height: at 200% text zoom a slide may legitimately be
   taller than the viewport, and the interior must stay reachable (WCAG
   1.4.4/1.4.10). */
.shorts-live .shslide{min-height:100%;box-sizing:border-box;scroll-snap-align:start;
  justify-content:center;padding:12px;gap:8px;overflow-y:auto}
.shorts-live .shmedia{width:auto;height:min(78vh,calc((94vw)*16/9));max-height:78vh;max-width:min(94vw,480px)}
.shorts-live .shnewer,.shorts-live .shsentinel,.shorts-live .shterminal{
  min-height:100%;box-sizing:border-box;scroll-snap-align:start}
/* Desktop: the 9:16 column centred, metadata as a right-hand rail in the
   space letterboxing wastes. No phone-frame skeuomorphism. */
@media (min-width:960px){
  .shorts-live .shslide{flex-direction:row;align-items:center;gap:28px}
  .shorts-live .shbody{width:300px;max-width:300px}
  .shorts-live .shhint{display:block;position:absolute;bottom:10px;left:0;right:0;margin:0;
    text-align:center;font:400 calc(10.5px*var(--rs)) var(--sans);color:var(--faint);pointer-events:none}
}
@media (prefers-reduced-motion:reduce){
  .shorts-live .shfeed{scroll-snap-type:y proximity}
}


/* ---- Encrypted article chat (0233-0236) ---------------------------------
   The room says what it is doing: a visible handshake, a roster whose key
   counts can be eyeballed, and identity chips that carry the same three
   facts the sender bound into the message. */
.chatsetup {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--faint);
  border: 1px solid var(--rule2); border-left: 2px solid var(--act);
  padding: 4px 8px; margin: 6px 0;
  transition: opacity .6s ease;
}
.chatsetup-done { opacity: .45; }
.chatsetup-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--act); flex: none;
  animation: chatpulse 1.1s ease-in-out infinite;
}
.chatsetup[data-step="ready"] .chatsetup-dot { animation: none; }
@keyframes chatpulse { 0%, 100% { opacity: .25 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) {
  .chatsetup-dot { animation: none }
}
.chatsetup-fp { font-size: 10px; letter-spacing: .04em; color: var(--ink); opacity: .8; }

.chatroster { font-size: 11px; color: var(--faint); margin: 2px 0 6px; }
.chatrosterp sup { font-size: 9px; color: var(--act); }
.chatme { font-size: 11px; color: var(--faint); margin: 4px 0 0; }

/* The identity chips. Deliberately quiet: they are context, not the message. */
.chatbadge { font-size: 8px; color: var(--act); vertical-align: middle; }
.chatcc, .chatkarma {
  font-size: 9px; letter-spacing: .05em; color: var(--faint);
  border: 1px solid var(--rule2); border-radius: 2px; padding: 0 3px;
}
.chatlocked { color: var(--faint); font-style: italic; }

/* A key changing is the one notice that must not be missable, and must not
   be dismissible by scrolling past it either — so it sits in the transcript. */
.chatsys { color: var(--faint); font-size: 11px; font-style: italic; margin: 4px 0; }
.chatsys-loud {
  color: var(--ink); font-style: normal;
  border-left: 2px solid var(--act); padding: 4px 8px;
  background: var(--paper2);
}

/* ---- /early-access -------------------------------------------------------
   The offer page. Its whole job is to get a code out of someone's clipboard
   and into the box, so the box comes first and every promise that does not
   help them decide that is folded into a <details>. The page used to be five
   dense paragraphs of terms with the form buried in the middle. */
.ea { max-width: 44rem; }
.eaform {
  display: flex; gap: 10px; align-items: end; flex-wrap: wrap;
  margin: 20px 0 4px; padding: 16px; background: var(--paper2);
  border: 1px solid var(--rule2); border-left: 2px solid var(--act);
}
.eaform label {
  display: block; font-size: calc(10.5px*var(--rs)); letter-spacing: .09em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 5px;
}
.eaform input {
  background: var(--paper); color: var(--ink); border: 1px solid var(--rule2);
  padding: 10px; font-family: var(--mono); letter-spacing: .1em;
  font-size: calc(14px*var(--rs)); width: 15rem; max-width: 100%;
}
.eaform input:focus { outline: 2px solid var(--act); outline-offset: 1px; }
.eaform .b.solid { min-height: 42px; }

.eawhat { list-style: none; margin: 18px 0 0; padding: 0; }
.eawhat li {
  position: relative; padding-left: 20px; margin: 0 0 7px;
  font-size: calc(14.5px*var(--rs)); color: var(--ink);
}
/* A rule, not a tick glyph — the paper draws with rules and never with emoji. */
.eawhat li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 11px; height: 2px; background: var(--act);
}
.eaworth { margin: 2px 0 0; font-size: calc(12.5px*var(--rs)); color: var(--faint); }

.eafound {
  margin: 20px 0 4px; padding: 11px 13px; font-size: calc(13.5px*var(--rs));
  color: var(--dim); background: var(--paper2); border: 1px solid var(--rule2);
}
.eafound b { color: var(--ink); }

.eamore { border-bottom: 1px solid var(--rule); }
.ea .eamore:first-of-type { border-top: 1px solid var(--rule); margin-top: 22px; }
.eamore summary {
  cursor: pointer; padding: 11px 2px; min-height: 42px;
  font-size: calc(13.5px*var(--rs)); color: var(--dim);
}
.eamore summary:hover { color: var(--ink); }
.eamore > p { margin: 0 2px 11px; font-size: calc(13.5px*var(--rs)); color: var(--dim); max-width: 62ch; }

.eafoot { margin: 22px 0 0; font-size: calc(12.5px*var(--rs)); color: var(--faint); line-height: 1.7; }

/* ---- /discussions --------------------------------------------------------
   A rail on the left and conversations down the page. The chip rails that used
   to sit across the top are gone: 23 sections and 190 topics as chips is a
   navigation surface taller than the river it navigates, and on a phone it
   pushed every conversation below the fold. The rail carries the eight topics
   that are live, the voices in the building, and the sections folded shut; the
   field above takes everything else.

   This page is where the paper's four desks first wear colours of their own
   (0285). A section hue answers "which room"; forty of them cannot answer
   "whose newsroom", which is the question a cross-desk river raises on every
   row. Both are here, doing different jobs. */

/* The masthead of the room. .pzhead is the river head and says "here is a
   list"; this says "here is a place". */
.dhead { padding: 24px 0 12px; }
.dhead h2 {
  font-family: var(--disp); font-weight: 400;
  font-size: calc(30px*var(--rs)); letter-spacing: .01em; line-height: 1.1; margin: 0;
}
.dhead p {
  font-family: var(--serif); font-style: italic; font-size: calc(14.5px*var(--rs));
  color: var(--dim); margin: 5px 0 0;
}

/* The two filter bars, ruled off from each other so the field reads as one
   control and the state below it as another. */
.dbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 0; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  font-family: var(--sans);
}
.dbar.row2 { border-top: 0; }
.dsp { flex: 1 1 auto; }
.dsearch {
  display: inline-flex; align-items: center; gap: 7px; flex: 1 1 320px; max-width: 420px;
  border: 1px solid var(--rule2); padding: 0 11px; min-height: 38px;
}
.dsearch:focus-within { border-color: var(--act); }
.dsearch b { color: var(--faint); font-weight: 600; font-size: calc(13px*var(--rs)); }
.dsearch input {
  flex: 1; min-width: 0; background: none; border: 0; outline: 0; color: var(--ink);
  font-family: var(--sans); font-size: calc(12.5px*var(--rs)); padding: 8px 0;
}
.dsearch input::placeholder { color: var(--faint); }

/* The filter that is on, and the way off it. */
.dactive { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 30px; }
.dchip {
  display: inline-flex; align-items: center; border: 1px solid var(--act);
  color: var(--act); padding: 4px 9px; font-size: calc(11px*var(--rs));
  font-weight: 600; letter-spacing: .04em;
}
.dclear { font-size: calc(11px*var(--rs)); color: var(--faint); text-decoration: none; }
.dclear:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }

/* ---- the rail ---- */
.dpage {
  display: grid; grid-template-columns: 212px minmax(0, 1fr); gap: 0 40px;
  align-items: start; margin-top: 10px;
}
.dtree {
  position: sticky; top: 14px; font-family: var(--sans);
  display: flex; flex-direction: column; gap: 20px; padding-top: 14px;
}
.dtb h3, .dsecs summary {
  font-size: calc(10.5px*var(--rs)); font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim); margin: 0 0 8px;
  padding-bottom: 8px; border-bottom: 1px solid var(--rule);
}
.dtb h3 span {
  font-weight: 400; letter-spacing: .04em; text-transform: none;
  color: var(--faint); margin-left: 6px;
}
.dtlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
/* Eight is the rail; the rest are in the DOM and one press away, the same
   "show all N" the wiki facets and the old chip row used. nth-of-type on the
   <li> because the list has no label element in front of it here. */
.dtlist.capped > li:nth-of-type(n+9) { display: none; }
/* a[href], not a bare a. These are list items, and `li a[href]` at :80 puts a
   body-copy underline on every one of them at a specificity a plain .dtlist a
   loses to. The rail is navigation and does not want one. */
.dtlist a[href] {
  display: flex; align-items: center; gap: 7px; text-decoration: none;
  color: var(--dim); font-size: calc(12.5px*var(--rs)); min-height: 26px;
}
.dtlist a:hover { color: var(--ink); }
.dtlist a.on { color: var(--ink); font-weight: 700; }
.dtlist a i { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.dtn { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dtc { margin-left: auto; flex: none; font-size: calc(10.5px*var(--rs)); color: var(--faint); }
/* Conversations opened this week. The all-time count says a room exists; this
   one says somebody is in it, which is the only number worth clicking on. */
.dtc b { color: var(--ok); font-weight: 700; }
.dcast .aibadge { margin-left: 5px; }
.dtall {
  display: inline-block; margin-top: 9px; padding: 0; border: 0; background: none;
  font-family: var(--sans); font-size: calc(10.5px*var(--rs)); letter-spacing: .06em;
  color: var(--faint); text-decoration: none; cursor: pointer;
}
.dtall:hover { color: var(--ink); }
.dsecs { padding-top: 2px; border-top: 1px solid var(--rule); }
.dsecs summary { list-style: none; cursor: pointer; color: var(--faint); border-bottom: 0; }
.dsecs summary::-webkit-details-marker { display: none; }
/* The disclosure marker is the affordance — it is what says this rail folds —
   so it takes the summary's own colour rather than the hairline one it sat in
   at 1.8:1. */
.dsecs summary::before { content: "+ "; color: var(--faint); }
.dsecs[open] summary::before { content: "\2013 "; }
.dsecs summary:hover { color: var(--ink); }
@media (pointer: coarse) { .dtlist a, .dsecs summary { min-height: 40px } }
/* Below the rail's own width there is no rail, and it goes UNDER the river
   rather than over it. Stacked above, the three lists ran 700px before the
   first conversation: the navigation eating the thing it navigates, which is
   the exact failure that killed the chip rows this replaced. The field above
   the river is the filter a phone actually reaches for; the rail becomes the
   index at the end. */
@media (max-width: 860px) {
  .dpage { grid-template-columns: 1fr; gap: 0; }
  .driver { order: 1; }
  .dtree { order: 2; position: static; padding-top: 18px; margin-bottom: 0;
           border-top: 1px solid var(--rule); }
}
@media (max-width: 620px) {
  /* The spacer forces the sort and the mute onto two lines of their own on a
     narrow screen, with the filter state stranded above them. */
  .dsp { display: none; }
  .dsearch { flex: 1 1 150px; }
}

/* The order control. Mirrors the .tophd .seg a variant — .seg is the frame,
   its anchors are the cells, and .on is what makes the choice legible. It sits
   in .dbar now rather than in a .comsort of its own: the sort, the active
   filter and the mute are one bar, so they are one element. */
.dbar .seg { display: inline-flex; border: 1px solid var(--rule2); }
.dbar .seg a {
  font-family: var(--sans); font-size: calc(9.5px*var(--rs)); font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; padding: 8px 12px;
  color: var(--faint); text-decoration: none; border-right: 1px solid var(--rule2);
  display: inline-flex; align-items: center; min-height: 34px;
}
.dbar .seg a:last-child { border-right: 0; }
.dbar .seg a:hover { color: var(--ink); }
.dbar .seg a.on { background: var(--ink); color: var(--paper); }
@media (pointer: coarse) { .dbar .seg a { min-height: 44px } }

/* The 14px at the sides is the keyboard ring's gutter, not a margin. The ring
   is drawn a pixel inside this row (app.css, "the ring on a river row"), and with
   no side padding at all it ran against the first letter of the headline and
   the last digit of the age — the one row on the paper where the blue touched
   the words. 14px rather than the 11px a story card carries, because this row
   is the tallest on the paper (18/15) and a ring 10px off the sides of a row
   that deep still reads as a squeeze. */
.thr { border-bottom: 1px solid var(--rule); padding: 18px 14px 15px; }

/* .rreact carries a 3px double rule at :1710, which is the divider under the
   reaction rail on the STORY page, where it separates the rail from the
   comments below it. On a river row there is nothing below it to separate, and
   it drew a second, shorter line inside every row above the row's own border. */
.thr .rreact.rmini { padding: 8px 0 0; border-bottom: 0; }
/* A seed chip is an offer, not a reading. It carries no number and sits at the
   weight of the furniture around it, so forty rows of invitation do not shout
   louder than the seven conversations above them. It brightens on hover
   because it IS a control — quiet is not the same as disabled. */
.thr .rchip.seed { border-color: var(--rule); color: var(--faint); padding: 3px 8px; min-height: 26px; }
.thr .rchip.seed .rem { font-size: calc(12px*var(--rs)); opacity: .72; }
.thr .rchip.seed:hover { border-color: var(--ink); opacity: 1; }
.thr .rchip.seed:hover .rem { opacity: 1; }
@media (pointer: coarse) { .thr .rchip.seed { min-height: 44px; padding: 8px 12px } }



.thrnone { color: var(--faint); padding: 20px 0; }

/* The per-comment thread link on the item page. Quiet — it is a way through,
   not a call to action, and it sits beside the vote and reply controls. */
.cthread { text-decoration: none; }
.cthread:hover { color: var(--act); }

/* ===== The jobs deck =====
   The stack is a stack in CSS, not in JS: only the top card is in flow and the
   two behind it are absolutely positioned edges. That is what makes the whole
   desk work with JS off, because every swipe is a real form post and the next
   card is simply the one that is now first. */
.deckbar2{display:flex;align-items:center;gap:10px;margin:0 0 12px}
.deckbar2 i{flex:1;height:1px;background:var(--rule)}
.deckbar2 .dkcount{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.dkview a{font-family:var(--sans);font-size:calc(9.5px*var(--rs));font-weight:600;letter-spacing:.04em;
  padding:5px 9px;color:var(--dim);text-decoration:none;border-right:1px solid var(--rule2)}
.dkview a:last-child{border-right:0}
.dkview a.on{background:var(--ink);color:var(--paper)}

.deck{position:relative;margin:0 0 20px}
.deck>.dcard{display:none}
.deck>.dcard:first-child{display:block;position:relative;z-index:3}
.deck>.dcard:nth-child(2),.deck>.dcard:nth-child(3){display:block;position:absolute;
  left:0;right:0;top:0;height:100%;z-index:1;pointer-events:none}
.deck>.dcard:nth-child(2){transform:translate(6px,7px);z-index:2}
.deck>.dcard:nth-child(3){transform:translate(12px,14px)}
/* The cards behind show as edges only. Hiding their contents also takes their
   controls out of the tab order, which visibility:hidden does and opacity
   does not. */
.deck>.dcard:nth-child(2)>*,.deck>.dcard:nth-child(3)>*{visibility:hidden}
/* One height for every card in the stack. The two behind the top one are
   absolutely positioned at height:100% of the deck, so without a floor here
   they take their size from whichever card is currently first and the whole
   pile jumps on every swipe. */
.dcard{border:1px solid var(--rule2);background:var(--paper);padding:15px 16px 13px;
  touch-action:pan-y;display:flex;flex-direction:column;min-height:392px}
/* The floor is for the LISTING stack. A candidate card's height is driven by
   its portrait, which is an aspect ratio of the column and therefore already
   identical on every card in one deck; what varied was the body underneath,
   and that is fixed row by row below rather than by a pixel floor that would
   fight the portrait on a narrow screen. */
.deck[data-deck="job"]{min-height:392px}
/* ...but only while there is a card in it. The reservation stops the page
   jumping as the stack is swiped down; with the stack empty it is 392px of
   nothing above a one-line sentence explaining why the stack is empty, which
   reads as a page that failed to load the thing it just told you is not there.
   A browser without :has() keeps the old spacing and loses nothing else. */
.deck[data-deck="job"]:not(:has(> .dcard)){min-height:0}
.deck[data-deck="candidate"]>.dcard{min-height:0}
.dcard.micro{border-left:3px solid var(--gold)}
.dcard.swiping{transition:none;cursor:grabbing}
.dcard.settling{transition:transform .18s ease-out,opacity .18s ease-out}
.dcard.choosing{border-color:var(--ok)}
.deckend{color:var(--faint);padding:18px 0;font-size:calc(13.5px*var(--rs))}

/* One listing's face.

   EVERY SLOT IS ALWAYS DRAWN, empty or not, and each one has a fixed height.
   That is what makes the cards the same size: a stack whose top card resizes
   on every swipe reads as a page that keeps flinching, and in the grid a
   ragged bottom edge makes forty ads look like forty different things. Rows
   that can overflow (stats, skills, proofs) clip rather than wrap to a second
   line, because the card is a summary and the grid is where the whole board
   is read. */
.jf{display:flex;flex-direction:column;height:100%}
.jf .jftop{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-height:18px;
  font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.08em;text-transform:uppercase}
.jf .mtag{background:var(--gold);color:var(--paper);font-weight:700;padding:1px 7px}
.jf .jtrack{color:var(--dim);border:1px solid var(--rule2);padding:1px 7px}
.jf .jleft{margin-left:auto;color:var(--faint)}
.jf .jleft.soon{color:var(--act)}
/* Two lines of title, clamped. A four-line title on one card and a one-line
   title on the next is the single biggest source of a ragged stack. */
.jf .jft{font-family:var(--disp);font-weight:700;font-size:calc(19px*var(--rs));margin:6px 0 2px;
  line-height:1.22;height:calc(19px*var(--rs)*1.22*2);overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.jf .jft a{color:inherit;text-decoration:none}
.jf .jfc{font-family:var(--sans);font-size:calc(10.5px*var(--rs));letter-spacing:.05em;color:var(--dim);
  height:calc(10.5px*var(--rs)*1.6);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
/* The flag is a glyph, not an image: two regional-indicator code points from
   the ISO country code. Sized up a touch because emoji render small beside
   uppercase sans at this size. */
.jf .jflag{font-size:calc(13px*var(--rs));line-height:1;margin-right:2px;vertical-align:-1px}

/* The disclosure row: who is behind the ad, and their own domain. Both slots
   are drawn even when the answer is "they did not say", greyed rather than
   absent, so a listing cannot decline to answer without the reader seeing it. */
.jf .jdisc{display:flex;align-items:center;gap:8px;margin:7px 0 0;min-height:20px;
  font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.07em}
.jf .pk{text-transform:uppercase;font-weight:700;padding:2px 7px;
  border:1px solid var(--ink);color:var(--ink)}
.jf .pk.un{border-color:var(--rule2);color:var(--faint);font-weight:400}
.jf .jdom{color:var(--mark);text-decoration:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.jf .jdom:hover{text-decoration:underline}
.jf .jdom.un{color:var(--faint)}

.jf .jstats{display:flex;flex-wrap:wrap;gap:5px;margin:9px 0 0;height:26px;overflow:hidden}

/* The skills row. A lit chip is a skill this reader already claims: the
   overlap is the whole reason both sides of the desk were given the same
   column, and a reader with an empty profile lights none, which is the
   strongest argument the desk can make for filling it in. */
.jf .jskills,.ccard .cskills{display:flex;flex-wrap:wrap;gap:5px;margin:8px 0 0;height:24px;overflow:hidden}
.skill{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.03em;
  padding:3px 8px;border:1px solid var(--rule2);color:var(--dim);white-space:nowrap}
.skill.on{border-color:var(--ok);color:var(--ok);font-weight:600;
  background:color-mix(in srgb,var(--ok) 10%,transparent)}

/* .skill.near — a DIFFERENT spelling of a skill the reader claims, resolved
   through the skills vocabulary (0278). Deliberately quieter than .on: an
   exact hit is the word they actually typed and a related one is an inference,
   and drawing them the same would tell somebody they wrote a word they did
   not. Dotted rather than solid so the difference survives a monochrome
   theme, where a colour change alone would not. */
.skill.near{border-style:dotted;border-color:var(--rule2);color:var(--dim)}

/* .ptbl — the passport. Three columns, and the two that matter are side by
   side on purpose: what the member says, and what the bank measured. They are
   never summed, so they are never adjacent to a total. */
.ptbl{width:100%;border-collapse:collapse;margin-top:10px;font-size:calc(13px*var(--rs))}
.ptbl th{text-align:left;font:600 calc(11px*var(--rs))/1.3 var(--sans);letter-spacing:.04em;
  text-transform:uppercase;color:var(--faint);padding:6px 10px 6px 0;border-bottom:1px solid var(--rule)}
.ptbl td{padding:10px 10px 10px 0;border-bottom:1px solid var(--rule);vertical-align:top}
.ptbl td:first-child{font-weight:600}
@media (max-width:640px){
  .ptbl,.ptbl tbody,.ptbl tr,.ptbl td{display:block;width:100%}
  /* The head is dropped because a three-column header over stacked cells is
     meaningless — but dropping it took the labels with it, and on the passport
     those labels ARE the design: "what you say" beside "what the bank
     measured", never merged. A reader at phone width was getting a skill, a
     row of buttons and a sentence with nothing saying which was which. Each
     cell carries its own label here instead. */
  .ptbl thead{display:none}
  .ptbl td{border-bottom:0;padding:2px 0}
  .ptbl td[data-label]::before{content:attr(data-label);display:block;
    font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.08em;
    text-transform:uppercase;color:var(--faint);margin:6px 0 2px}
  .ptbl tr{border-bottom:1px solid var(--rule);padding:10px 0}
}
.ccard .ccves{display:flex;flex-wrap:wrap;gap:5px;padding:0 12px 2px}
.cpill.cve.more{opacity:.75}

.jf .wants{margin-top:auto;padding-top:8px;height:auto;min-height:0}

/* The action row. TWO FORMS, because pass and send are two different acts
   with two different bodies; one form with a hidden `decision` would put two
   of that field on the wire and let the hidden one win.

   There was a <select> here for the opener, which is to say the most important
   choice on the desk was collapsed behind one click and defaulted to whichever
   line happened to be first in the list. Every opener is its own button now. */
.dact{display:flex;align-items:stretch;gap:8px;flex-wrap:wrap;margin-top:13px;
  border-top:1px solid var(--rule);padding-top:11px}
.dact .dopen{display:flex;gap:8px;flex:1;flex-wrap:wrap}
.dact .dopen .b{flex:1;min-width:170px;justify-content:center;text-align:center}
.dact .b.sw{padding:11px 14px;font-size:calc(11px*var(--rs));line-height:1.3}
.dact .b.yes:first-of-type{background:var(--act);border-color:var(--act);color:var(--paper)}
.dact .b.yes:first-of-type:hover{opacity:.9}
.dact .b.pass{color:var(--faint)}
.dxtra{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:11px}
.dxtra .inlf{display:inline}
.askstate{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint)}

/* The grid: every opportunity at once, on stats and location. */
.jgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;margin-bottom:18px}
/* grid-auto-rows over a min-height: the rows are equal AND the cells stretch
   to fill them, so a short ad and a long one are the same box rather than two
   boxes that merely start at the same top edge. */
.jgrid{grid-auto-rows:1fr}
.jgcell{border:1px solid var(--rule);padding:13px;display:flex;flex-direction:column}
/* The people grid gets the same equal-row treatment, scoped to this page so
   the world-pages .wgrid keeps its own sizing. */
.wgrid.jgpeople{grid-auto-rows:1fr}
.wgrid.jgpeople .jgcell{padding:0}
.wgrid.jgpeople .ccard{border:0;flex:1}
.wgrid.jgpeople .jgfoot{padding:8px 12px;margin:0}
.jgcell .jf{flex:1}
.jgcell.micro{border-left:3px solid var(--gold)}
.jgcell.decided{opacity:.55}
.jgcell.d-yes{opacity:1;border-color:var(--ok)}
.jgfoot{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:9px;
  padding-top:8px;border-top:1px solid var(--rule)}
.jgfoot .inlf{display:inline}

/* ===== The card that opens =====
   <details>, so the whole thing works with scripting off and the open card is
   a real keyboard stop. The summary is the card face, which means the hit area
   is the card: the marker is removed on both engines, and the face keeps its
   own flex behaviour so a closed card is exactly the card it was.
   list-style:none alone is not enough on WebKit, hence the pseudo-element. */
.jgx{display:flex;flex-direction:column;flex:1;min-width:0}
.jgsum{display:flex;flex-direction:column;list-style:none;cursor:pointer;position:relative}
.jgsum::-webkit-details-marker{display:none}
.jgsum::marker{content:""}
.jgx:not([open])>.jgsum{flex:1}
.jgsum .jf,.jgsum .ccard{flex:1}
.jgsum:focus-visible{outline:2px solid var(--act);outline-offset:2px}
.jgsum:hover .jgcue{color:var(--ink);border-color:var(--dim)}
/* The affordance. A card that opens has to say so before it is clicked, and a
   cursor change says it only to a mouse. */
.jgcue{align-self:flex-start;margin-top:8px;padding:3px 8px;border:1px solid var(--rule2);
  font:600 calc(9px*var(--rs)) var(--sans);letter-spacing:.11em;text-transform:uppercase;
  color:var(--faint)}
.jgcue::after{content:" ▾"}
.jgx[open] .jgcue::after{content:" ▴"}
.jgx .jgc2,.jgx[open] .jgc1{display:none}
.jgx[open] .jgc2{display:inline}
.jgbody{border-top:1px solid var(--rule);margin-top:9px;padding-top:10px}
.wgrid.jgpeople .jgbody{margin:0;padding:10px 12px 4px}
.wgrid.jgpeople .jgsum{padding-bottom:0}
.wgrid.jgpeople .jgcue{margin:0 12px 10px}
.jgbrief{margin:0 0 8px;font:400 calc(13px*var(--rs))/1.55 var(--serif);color:var(--dim)}
.jgul{margin:0 0 8px}
.jgul .kick.k2{display:block;margin-bottom:3px}
.jgul ul{margin:0;padding:0;list-style:none}
.jgul li{position:relative;padding-left:12px;margin:2px 0;
  font:400 calc(12.5px*var(--rs))/1.45 var(--serif);color:var(--dim)}
.jgul li::before{content:"·";position:absolute;left:2px;color:var(--rule2)}
.jgline{margin:2px 0;font:400 calc(12.5px*var(--rs)) var(--serif);color:var(--dim)}
.jgline b{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.1em;
  text-transform:uppercase;color:var(--faint);margin-right:6px}
/* Unclipped, unlike the row on the face: showing the rest of a list the card
   cut off is most of the reason to open one. */
.jgskills{display:flex;flex-wrap:wrap;gap:5px;margin:8px 0 0}
.jglinks{display:flex;flex-wrap:wrap;gap:12px;margin:10px 0 2px}
.jgbody .dact{margin-top:10px}

/* The persona bar, at last on the page. It was written into partials and
   rendered by nothing, so the three sides of the table were a URL parameter
   with no control anywhere. The note under it is not decoration: a switch
   whose effect is invisible is one nobody will touch twice. */
.jdmodewrap{margin:0 0 14px}
.jdmode{display:inline-flex;flex-wrap:wrap;border:1px solid var(--rule2)}
.jdmode a{display:inline-flex;align-items:center;min-height:36px;padding:7px 13px;
  font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);text-decoration:none;border-right:1px solid var(--rule2)}
.jdmode a:last-child{border-right:0}
.jdmode a:hover{color:var(--ink)}
.jdmode a.on{background:var(--ink);color:var(--paper)}
.jdmoden{margin:6px 0 0;font:400 calc(12.5px*var(--rs))/1.5 var(--serif);color:var(--faint)}
@media (pointer:coarse){.jdmode a{min-height:44px}}
@media (max-width:640px){.jdmode{display:flex}.jdmode a{flex:1 1 auto;justify-content:center}}

/* The two inbox lists. */
.asklist{display:flex;flex-direction:column;margin-bottom:16px}
.askrow{display:flex;gap:10px;align-items:center;flex-wrap:wrap;
  padding:10px 0;border-bottom:1px solid var(--rule)}
.askrow .askjob{font-family:var(--sans);font-size:calc(10px*var(--rs));color:var(--dim)}
.askrow .talkline{font-size:calc(13.5px*var(--rs));color:var(--ink)}
.askrow .askacts{display:flex;gap:6px;margin-left:auto}
.askrow .talknote{flex-basis:100%;font-size:calc(13px*var(--rs));color:var(--dim);line-height:1.5}
@media(max-width:640px){
  .dact{flex-direction:column}
  .dact .dopen{flex-direction:column}
  .dact .dopen .b{min-width:0}
  .dcard{min-height:0}
  .deck{min-height:0}
}

/* ===== Big buttons instead of dropdowns =====
   Radios under the hood, so a picker posts exactly what a <select> posted and
   still works with scripting off. The label is the control; the input stays
   focusable (never display:none) so the keyboard ring lands on it and the
   arrow keys move through the group the way a radio group should. */
.bpick{display:flex;flex-wrap:wrap;gap:8px;margin:4px 0 2px}
.pflab{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.07em;
  text-transform:uppercase;color:var(--faint);margin:12px 0 2px}
.bp{position:relative;display:inline-flex}
.bp input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.bp span{font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:600;letter-spacing:.05em;
  padding:11px 16px;border:1px solid var(--rule2);color:var(--dim);background:var(--paper);
  display:inline-flex;align-items:center;min-height:22px;transition:none}
.bp input:checked+span{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.bp input:hover+span{border-color:var(--ink);color:var(--ink)}
.bp input:checked:hover+span{color:var(--paper)}
.bp input:focus-visible+span{outline:2px solid var(--mark);outline-offset:2px}
/* .profform label sets margin-bottom:8px at (0,1,1), and every picker button
   IS a <label>, so each wrapped row carried 8px it was never meant to on top
   of .bpick's own gap. The city grid paid for it ten rows over. */
.profform .bp{margin-bottom:0}

/* The city picker. Grouped rather than listed: a timezone has too many
   answers for one row of buttons and too few to justify a search box, and it
   is the field that must not become a dropdown.

   The four regions are <details> now. Open at all times they ran to ten
   wrapped rows, which on a phone was taller than the screen reading them;
   shut, the whole question costs four summary rows. The <fieldset> is what
   gives the 48 radios a name, which the .pflab div outside them never did.
   .bp.wide went with the change: "Not stated" is the same size as every other
   answer, because it is one. */
.tzpick{margin:4px 0 2px;border:0;padding:0;min-inline-size:0}
.tzpick>legend{padding:0;margin:12px 0 4px}
.tzgroup{margin:5px 0 0}
/* list-item, not block: a <summary> loses its disclosure triangle the moment
   its display is anything else, and that triangle is the only thing saying
   there is more behind the heading. */
.tzreg{display:list-item;list-style-position:inside;cursor:pointer;
  font-family:var(--sans);font-size:calc(9.5px*var(--rs));
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint);
  padding:2px 0;margin:0}
.tzreg:hover{color:var(--ink)}
.tzreg:focus-visible{outline:2px solid var(--mark);outline-offset:2px}
.tzgroup[open]>.tzreg{margin:0 0 4px}
.tzpick .bpick{gap:5px;margin:2px 0 0}
/* min-height:0 is load-bearing: .bp span floors at 22px, which pins the button
   at 32px however far the padding comes down. */
.tzpick .bp span{padding:4px 8px;font-size:calc(10.5px*var(--rs));
  min-height:0;line-height:1.5;font-weight:400}
.tzpick .bp input:checked+span{font-weight:700}

/* The reading animal. The badge sits on the portrait, small and quiet: it is
   a true thing about somebody, not a score, and nothing here ranks. */
/* #fff, not var(--paper): the badge sits on the scrim over a portrait, which
   is dark in every edition. --paper is white only in the light ones, and the
   default edition is phosphor (#0B0E11) — so the animal was painted near-black
   on near-black and no card has shown it since. Its sibling .cfid had this
   right already. */
.ccard .cfanimal{display:inline-block;margin-top:3px;font-family:var(--sans);
  font-size:calc(10px*var(--rs));letter-spacing:.04em;color:#fff;opacity:.92}
.animalbox{display:flex;gap:14px;align-items:flex-start}
.animalbox .anmark{font-size:calc(34px*var(--rs));line-height:1.1}
.animalbox .antitle{font-family:var(--disp);font-weight:700;font-size:calc(19px*var(--rs))}

/* The hours row, and the cost of them. The slot is always drawn so the cards
   stay the same height whether or not a listing states a window. */
.jf .jhours{min-height:18px;margin:8px 0 0;display:flex;flex-wrap:wrap;gap:4px 10px;
  align-items:baseline;font-family:var(--sans);font-size:calc(10.5px*var(--rs));color:var(--dim)}
.jf .jhy{color:var(--ink);font-weight:600}
.jf .jhtag{text-transform:uppercase;letter-spacing:.07em;font-size:calc(9.5px*var(--rs));
  font-weight:700;color:var(--paper);background:var(--act);padding:2px 7px}
.jf .jhnote{flex-basis:100%;color:var(--faint);font-size:calc(10px*var(--rs))}
.jf .jhours.clash .jhy{color:var(--act)}
/* A clashing window is the one thing on the card allowed two lines: the whole
   feature is the sentence naming what it costs. */
.jf .jhours.clash{min-height:34px}
.cpill.on{border-color:var(--ok);color:var(--ok);font-weight:600;
  background:color-mix(in srgb,var(--ok) 10%,transparent)}

.b.big{padding:13px 20px;font-size:calc(11.5px*var(--rs))}
.b.quiet{border-color:var(--rule2);color:var(--faint)}
.b.quiet:hover{color:var(--act);border-color:var(--act)}
.b.warn{border-color:var(--gold);color:var(--gold)}
.b.pay{background:var(--ok);border-color:var(--ok);color:var(--paper)}

/* ===== The thread list =====
   Both directions. A desk that shows what was sent to you and not what you
   sent is a desk where half your conversations do not exist. */
.thlist{display:flex;flex-direction:column;margin:0 0 18px}
.throw{display:grid;grid-template-columns:minmax(90px,auto) 1fr auto;gap:4px 12px;align-items:baseline;
  padding:12px 2px;border-bottom:1px solid var(--rule);text-decoration:none;color:inherit}
.throw:hover{background:var(--paper2)}
.throw.wait{border-left:3px solid var(--act);padding-left:9px}
.throw .thwho{font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:700;letter-spacing:.04em}
.throw .thline{font-size:calc(13.5px*var(--rs));color:var(--ink)}
.throw .thgo{font-family:var(--sans);font-size:calc(10px*var(--rs));color:var(--mark);white-space:nowrap}
.throw .thmeta{grid-column:2;font-family:var(--sans);font-size:calc(9.5px*var(--rs));
  letter-spacing:.05em;color:var(--faint);text-transform:uppercase}
.throw .askjob{grid-column:2}

/* ===== One conversation ===== */
.thead{display:flex;align-items:center;gap:10px;margin:0 0 14px}
.thead i{flex:1;height:1px;background:var(--rule)}
.thstate{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);border:1px solid var(--rule2);padding:2px 8px}
.thstate.s-agreed{color:var(--gold);border-color:var(--gold)}
.thstate.s-paid{color:var(--ok);border-color:var(--ok)}
.thstate.s-declined{opacity:.6}
.thread{display:flex;flex-direction:column;gap:10px;margin:0 0 16px}
.msg{max-width:80%;border:1px solid var(--rule2);background:var(--paper);padding:11px 13px}
.msg.mine{align-self:flex-end;background:var(--paper2)}
.msg.opener{border-left:3px solid var(--act)}
.msg .mwho{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint)}
.msg .mbody{margin:4px 0 0;font-size:calc(14px*var(--rs));line-height:1.55;white-space:pre-wrap}
.msg .mbody.note{color:var(--dim);font-size:calc(13px*var(--rs))}
.msg .mago{display:block;margin-top:6px;font-family:var(--sans);font-size:calc(9px*var(--rs));
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.replybox{display:flex;flex-direction:column;gap:10px;align-items:flex-start;margin:0 0 22px}
.replybox textarea{width:100%;font-family:var(--body);font-size:calc(14px*var(--rs));
  padding:11px 12px;border:1px solid var(--rule2);background:var(--paper);color:var(--ink)}

/* ===== The money on a thread ===== */
.dealbox{border:1px solid var(--rule2);padding:15px 16px;margin:0 0 12px}
.dealbox.live{border-left:3px solid var(--gold)}
.dealbox.paid{border-left:3px solid var(--ok)}
.dealbox.warn{border-left:3px solid var(--act)}
.dealbox.off{opacity:.7}
.dlk{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint)}
.dlnum{font-family:var(--disp);font-weight:700;font-size:calc(30px*var(--rs));margin:4px 0 6px}
.dlnote{margin:4px 0 0;font-size:calc(13.5px*var(--rs));color:var(--dim);line-height:1.55}
.dlsplit{margin:8px 0 0;font-size:calc(13.5px*var(--rs));line-height:1.55}
.dealbox .b{margin-top:12px}
.dealacts{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 18px}
.feeform{display:flex;flex-direction:column;gap:8px;align-items:flex-start;
  border:1px solid var(--rule2);padding:15px 16px;margin:0 0 18px}
.feeform label{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint)}
.feeform input{font-family:var(--body);font-size:calc(15px*var(--rs));padding:10px 12px;
  border:1px solid var(--rule2);background:var(--paper);color:var(--ink);min-width:260px;text-transform:none}
.closebox{margin:22px 0 0;padding-top:14px;border-top:1px solid var(--rule)}

/* ===== Getting paid ===== */
.kycbox{border:1px solid var(--rule2);border-left:3px solid var(--rule2);padding:16px 18px;margin:0 0 18px}
.kycbox.s-verified{border-left-color:var(--ok)}
.kycbox.s-pending{border-left-color:var(--gold)}
.kycbox.s-restricted{border-left-color:var(--act)}
.needlist{margin:10px 0 0;padding-left:20px;font-size:calc(13.5px*var(--rs));color:var(--dim);line-height:1.7}
/* Was .comrow, which collided with the Discussion chip rail and won, silently
   turning those chip rows into a 240px grid. The chip rail itself is gone —
   /discussions carries a .dtree rail now — but the rename stands: the lesson
   was that a generic name in a global sheet is a collision waiting, not that
   one particular page needed protecting. */
.payrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin:0 0 14px}
.comcell{border:1px solid var(--rule2);padding:14px 16px}
.comcell .dlk{font-family:var(--disp);font-size:calc(26px*var(--rs));font-weight:700;
  letter-spacing:0;text-transform:none;color:var(--ink);display:block}

.admnote .warn{color:var(--gold)}

/* ===== The candidate card =====
   A face, then four numbers, then the pills. The face is the identity mode
   drawn: sharp for a member who chose to be named, an 8-pixel derivative for
   everybody else, and a drawn portrait for the many with no uploaded mark.

   NOTHING HERE IS THE PRIVACY BOUNDARY. `filter: blur()` below is applied to
   an image that is ALREADY eight pixels wide when it leaves the server; it
   smooths the upscale, it does not hide anything. If a full-resolution photo
   ever reaches this rule the promise on the card is broken and no stylesheet
   can put it back. See internal/app/candidate_face.go. */
.ccard{border:1px solid var(--rule2);background:var(--paper);display:flex;flex-direction:column}
.ccard .cface{position:relative;aspect-ratio:4/5;overflow:hidden;background:var(--paper2)}
.ccard .cfimg,.ccard .cfart{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* The upscale of an 8px image is nearest-neighbour by default, which reads as
   a QR code rather than as a person. Smoothing it is what makes it a face. */
.ccard .cfimg.blur{filter:blur(6px) grayscale(1);transform:scale(1.12)}
.ccard .cfimg{filter:grayscale(1)}
.ccard .cfscrim{position:absolute;inset:0;
  background:linear-gradient(to top,rgba(0,0,0,.82) 0%,rgba(0,0,0,.45) 34%,rgba(0,0,0,0) 62%)}
.ccard .cfid{position:absolute;left:0;right:0;bottom:0;padding:11px 12px 10px;color:#fff}
.ccard .cfname{display:block;font-family:"Archivo",sans-serif;font-weight:700;
  font-size:calc(17px*var(--rs));letter-spacing:.06em;line-height:1.15}
.ccard .cfrole{display:block;font-family:var(--disp);font-size:calc(14px*var(--rs));
  margin-top:2px;line-height:1.25}
.ccard .cfmeta{display:block;font-family:var(--sans);font-size:calc(9.5px*var(--rs));
  letter-spacing:.07em;text-transform:uppercase;opacity:.82;margin-top:4px}
/* The three questions in cfmeta are separate elements now, for the labels
   inside them; inline-block keeps a wrapped set of levels from breaking mid
   phrase. */
.ccard .cfpart{display:inline-block}
.ccard .cfrank{position:absolute;top:9px;left:9px;font-family:"Archivo",sans-serif;font-weight:700;
  font-size:calc(10px*var(--rs));color:var(--paper);background:var(--ink);padding:2px 7px}

/* The numbers strip: the figures the paper stands behind, then the ones the
   member typed. `said` is not a warning, it is a different kind of fact, so it
   is quieter rather than coloured. */
.ccard .cnums{display:flex;border-top:1px solid var(--rule)}
.ccard .cnum{flex:1;text-align:center;padding:9px 4px 8px;border-right:1px solid var(--rule)}
.ccard .cnum:last-child{border-right:0}
.ccard .cnum b{display:block;font-family:var(--disp);font-weight:700;
  font-size:calc(17px*var(--rs));line-height:1.1}
.ccard .cnum i{display:block;font-family:var(--sans);font-style:normal;
  font-size:calc(8.5px*var(--rs));letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);margin-top:2px}
.ccard .cnum.said b{color:var(--dim);font-weight:400}
/* Every row under the portrait has a fixed height, so two candidates are the
   same card whatever they filled in. Two lines of pitch: one candidate's
   paragraph and another's half-sentence is what made the people grid ragged. */
.ccard .cblurb{font-size:calc(13.5px*var(--rs));color:var(--dim);line-height:1.5;
  margin:0;padding:10px 12px 2px;
  height:calc(13.5px*var(--rs)*1.5*2 + 12px);overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.ccard .cskills{padding:2px 12px 0}
.ccard .ccves{min-height:0}
/* Two rows of pills, clipped. The card is a summary; the profile page is
   where the whole list is read.
   Clipped ON a row boundary, which the old calc was not: it assumed a 24px
   pill and left the 20px of padding out altogether, so a cap of 53px showed
   one row and the top half of the next. Half-height letters read as a
   rendering fault, not as "there is more of this on the profile". The pill's
   line box is pinned here so the row height is a number this rule knows rather
   than whatever the font returns for `normal`, and it scales with --rs. */
.ccard .wchips{padding:8px 12px 12px;overflow:hidden;
  --rowh:calc(17px*var(--rs) + 10px);
  /* padTop + two rows + the gap after the second, and NOT padBottom: clipping
     happens at the border box, so counting the 12px bottom padding into the
     cap left 7px of the third row showing as a sliver of its top border. The
     box ends exactly where row three begins. */
  max-height:calc(var(--rowh)*2 + 5px + 5px + 8px)}
.ccard .wchips>*{line-height:calc(17px*var(--rs))}

/* In the swipe stack the card is the whole hand, so it loses its own border
   and the face gets more of the height.
   Capped, because "more of the height" was unbounded: the stack is one card at
   the full width of the page, so 3/4 of 1126px is a 1501px portrait on a
   1400px screen, and every fact about the candidate — the counts, the blurb,
   the skills, the credential chips, all 175px of it — sat below a full screen
   of a face that is deliberately blurred. A deck asks for a decision, so the
   thing being decided about has to fit in front of the reader. The face keeps
   its ratio until the cap and then crops, which cfimg's object-fit already
   does. */
.dcard .ccard{border:0;background:transparent}
.dcard .ccard .cface{aspect-ratio:3/4;max-height:min(58vh,520px)}
.dcard .ccard .cblurb,.dcard .ccard .wchips,
.dcard .ccard .cskills,.dcard .ccard .ccves{padding-left:0;padding-right:0}
.dcard .ccard{border:0}
@media(max-width:520px){.ccard .cnum b{font-size:calc(15px*var(--rs))}}

/* ===== The portrait builder =====
   A stage and five sliders. The stage is a fixed 4:5 so the portrait is
   composed in the frame it will actually appear in on the card, rather than
   looking right here and cropped there. */
.avbuilder .avrow{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap}
.avbuilder .avstage{flex:0 0 168px;width:168px;aspect-ratio:4/5;overflow:hidden;
  border:1px solid var(--rule2);background:var(--paper2)}
.avbuilder .avstage svg{display:block;width:100%;height:100%}
.avbuilder .avctl{flex:1;min-width:220px;display:flex;flex-direction:column;gap:11px}
.avbuilder .avsl{display:block}
.avbuilder .avlbl{display:flex;justify-content:space-between;align-items:baseline;
  font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);margin-bottom:3px}
.avbuilder .avlbl b{font-family:var(--sans);font-size:calc(10.5px*var(--rs));
  letter-spacing:.04em;text-transform:none;color:var(--ink);font-weight:600}
.avbuilder input[type="range"]{width:100%;accent-color:var(--ink);margin:0}
@media(max-width:520px){.avbuilder .avstage{flex-basis:132px;width:132px}}


/* ===== The CVE claims table =====
   A list of a hundred claims was a stack of bare <div>s: no count, no row
   boundary, and N buttons all named "Remove". A table gives the reader a
   caption with the total, real row semantics, and somewhere for the state to
   sit that is not the only thing in the row. */
.cvesec{max-width:640px}
.cvesec:focus{outline:none}
.cvetab{width:100%;border-collapse:collapse;margin:10px 0 0}
.cvetab th{text-align:left;font-family:var(--sans);font-size:calc(9.5px*var(--rs));
  letter-spacing:.09em;text-transform:uppercase;color:var(--faint);
  padding:0 8px 4px 0;border-bottom:1px solid var(--rule)}
.cvetab td{padding:6px 8px 6px 0;border-bottom:1px solid var(--rule);vertical-align:middle}
.cvetab td:last-child{text-align:right;padding-right:0}
.cvetab .inlf{display:inline}
.cvesec textarea{width:100%;font-family:var(--mono,monospace);font-size:calc(12px*var(--rs))}

/* ═══ The work market and the listing page ═══════════════════════════════
   Classes introduced by 0276-0279. The card face is shared with the deck, so
   everything here either sits beside it or relaxes it. */

/* The kind, and how certain the money is. Both live in .jftop beside MICRO,
   and the certainty is the one that lets this board carry work that is not
   contracted yet without letting it read as funded, so it is drawn in its own
   colour per rung rather than as one neutral chip. */
.jf .mtag.work{background:var(--act);color:var(--paper)}
.jf .ctag{border:1px solid var(--rule2);color:var(--dim);padding:1px 7px;font-weight:600}
.jf .ctag.c-funded{border-color:var(--ok);color:var(--ok)}
.jf .ctag.c-negotiating{border-color:var(--gold);color:var(--gold)}
.jf .ctag.c-bidding{border-color:var(--gold);color:var(--gold)}
.jf .ctag.c-pipeline{border-color:var(--rule2);color:var(--faint)}

/* On its own page the card is not in a stack, so every fixed height the deck
   needs becomes a black gap. The clamps exist to stop a ragged pile; there is
   no pile here. */
/* The deck's 392px floor keeps a pile of cards from jumping as it is swiped.
   A single card on its own page is not a pile, and the floor is just a black
   gap under it. Same override the candidate deck already makes. */
.jpage{max-width:none;margin:14px 0;min-height:0}
.jpage .jf .jft,.jpage .jf .jfc,.jpage .jf .jstats,
.jpage .jf .jskills,.jpage .jf .jdisc{height:auto;overflow:visible;white-space:normal}
.jpage .jf .jft{-webkit-line-clamp:none;display:block}

/* The terms. A definition list in all but name: the label is the question and
   the value is the listing's answer, and only the lines it actually answered
   are drawn. */
/* The rules are drawn by the CELLS, not by a gap over a coloured container.
   The gap trick paints the container through any UNFILLED cell in the last
   row, which on a five-column grid holding six terms is a solid block the
   width of four columns — barely visible on the dark theme and conspicuous on
   paper and e-ink. Borders on the cells leave an empty cell empty. */
.jterms{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:0;border-top:1px solid var(--rule);border-left:1px solid var(--rule);margin:16px 0}
.jterm{background:var(--paper);padding:10px 12px;
  border-right:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.jterm span{display:block;font-family:var(--sans);font-size:calc(9.5px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin-bottom:3px}
.jterm b{font-size:calc(14px*var(--rs));line-height:1.3}

.jsub{font-family:var(--sans);font-size:calc(10.5px*var(--rs));letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim);margin:18px 0 6px;font-weight:700}
.jlist{margin:0 0 6px;padding-left:20px}
.jlist li{margin:4px 0;line-height:1.5}
.jprose{margin:14px 0;line-height:1.62;max-width:64ch}
.jprose p{margin:0 0 0.8em}
.jpageact{margin:18px 0 6px}

/* The list view: one line per listing, for reading a board rather than
   deciding about it. No swipe controls, so the row can be this dense. */
.jlistv{list-style:none;margin:12px 0;padding:0;border-top:1px solid var(--rule)}
.jlrow{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1.4fr) minmax(0,1.2fr) auto;
  gap:12px;align-items:baseline;padding:11px 2px;border-bottom:1px solid var(--rule)}
.jlrow.decided{opacity:.5}
.jlt{font-family:var(--disp);font-weight:700;font-size:calc(15px*var(--rs));
  color:var(--ink);text-decoration:none}
.jlt:hover{color:var(--mark)}
.jlc,.jlm,.jla{font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim);
  overflow:hidden;text-overflow:ellipsis}
.jla{color:var(--faint);white-space:nowrap;text-align:right}
.jlk{border:1px solid var(--rule2);padding:1px 6px;margin-right:5px;
  font-size:calc(9.5px*var(--rs));letter-spacing:.06em;text-transform:uppercase}
@media (max-width:720px){
  .jlrow{grid-template-columns:1fr;gap:3px}
  .jla{text-align:left}
}

/* The sprint. One question at a time, and the options are submit buttons
   because the answer IS the submission: there is no separate confirm step to
   change your mind in, which is the point of a timed assessment. */
.qcard{border:1px solid var(--rule2);background:var(--paper);padding:18px 20px;margin:14px 0}
.qstem{font-size:calc(17px*var(--rs));line-height:1.45;margin:6px 0 16px;max-width:62ch}
.qopts{display:flex;flex-direction:column;gap:8px;align-items:stretch}
.qopt{text-align:left;padding:12px 14px;font-size:calc(14px*var(--rs))}

/* The joint bid's thread. Plainer than the article conversation on purpose:
   this is a working channel between people who already agreed to work
   together, not a public discussion that needs a reply ladder. */
.tmsg{border-left:2px solid var(--rule2);padding:2px 0 2px 12px;margin:10px 0}
.tmsg.mine{border-left-color:var(--mark)}
.tmwho{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;
  text-transform:uppercase;color:var(--faint)}
.tmsg p{margin:3px 0 0;line-height:1.55}

/* The engagement ladder. Each rung is a fact rather than a claim: funded is
   written by the payment webhook and delivery is derived from the deliverables,
   so the drawing and the data cannot disagree. */
.engladder{display:flex;flex-wrap:wrap;gap:0;list-style:none;margin:16px 0;padding:0;
  border:1px solid var(--rule);background:var(--rule)}
.engrung{flex:1 1 0;min-width:92px;background:var(--paper);padding:9px 10px;
  font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint);text-align:center}
.engrung.done{color:var(--ok)}
.engrung.now{color:var(--ink);font-weight:700;box-shadow:inset 0 -3px 0 var(--mark)}

/* The sprint's review list. Stems only: which ones were right is deliberately
   absent, because that is the answer key arriving one bit at a time. */
.qrev{border-top:1px solid var(--rule);padding:11px 0}
.qrevstem{margin:0 0 7px;line-height:1.5}

/* One person asking for a place on a team. Borrowed classes from the prototype
   (.listcard, .row) rendered as bare divs here, because this codebase never had
   them — the mockup's stylesheet is not this one. */
.askcard{border:1px solid var(--rule2);background:var(--paper);padding:12px 14px;margin:10px 0}
.askwho{display:flex;justify-content:space-between;align-items:baseline;gap:12px;margin-bottom:6px}
.askcard p{margin:4px 0;line-height:1.5}

/* A closed listing's card still carries jobCardFace's countdown chip, which is
   a decision-support number for a live board and reads as nonsense on an ad
   that already ran out ("closing today", about something closed last month).
   The page states the closure in full above; the chip is hidden rather than
   left to contradict it. */
.jpage.closed .jf .jleft{visibility:hidden}

/* The premium TL;DR block on the reader page (0286).
   Keyed off the rule, not a box: a bordered card would read as an ad slot
   beside the article's own measure. The gold kicker is the same one the
   envelope headers use, so "In short" is recognisably the house speaking
   rather than the publisher. */
.rtldr{margin:0 0 14px;padding:10px 0 12px;border-top:1px solid var(--rule2);border-bottom:1px solid var(--rule2)}
.rtldrk{display:block;font:600 11px/1.2 var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--gold);margin:0 0 5px}
.rtldrd{margin:0;font:400 calc(16px*var(--rs))/1.55 var(--serif);color:var(--ink)}
[data-density="compact"] .rtldr{margin-bottom:10px;padding:8px 0 9px}

/* ===== The jobs desk's sections (v13 prototype) =====
   The prototype is dark-only hardcoded hex; everything below is tokens, so the
   seven editions keep working. Its bottom tab bar is deliberately absent: this
   site already fixes a bar to the bottom of the viewport for background
   listening, and two stacked is one too many. The sections read as a rail. */
.jdnav{display:flex;flex-wrap:wrap;gap:2px;margin:10px 0 16px;
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:4px 0}
.jdn{display:inline-flex;align-items:center;gap:7px;padding:9px 12px;min-height:40px;
  font:600 calc(11px*var(--rs)) var(--sans);letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);text-decoration:none}
.jdn .pip{width:calc(13px*var(--rs));height:calc(13px*var(--rs));color:var(--rule2)}
.jdn:hover{color:var(--ink)}
.jdn:hover .pip{color:var(--dim)}
.jdn.on{color:var(--ink);box-shadow:inset 0 -2px 0 var(--act)}
.jdn.on .pip{color:var(--act)}

/* The pips themselves. Drawn in one box at one weight so the rail reads as a
   set; see the jdpip template for why they are not characters. currentColor
   throughout, which is what lets the nav colour them by state and the quick
   cards colour them by their own accent without a second copy of each shape.
   Deliberately monochrome: a red heart and a red diamond would be the only
   red on a desk where red already means something went wrong. */
.pip{width:1.15em;height:1.15em;flex:none;display:inline-block;vertical-align:-.16em;
  fill:currentColor}
.pip.stroke{fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linejoin:round;stroke-linecap:round}
@media (pointer:coarse){.jdn{min-height:44px}}

/* The overview's quick actions: the prototype's argument is that nothing
   important should need the footer, so these are the sections again, as cards
   with their reason attached. */
.jdquick{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:10px;margin:8px 0 18px}
.jdq{display:block;border:1px solid var(--rule2);padding:13px 14px;text-decoration:none;
  background:var(--paper)}
.jdq:hover{border-color:var(--dim)}
.jdq .pip{display:block;width:calc(20px*var(--rs));height:calc(20px*var(--rs));
  color:var(--act);margin-bottom:7px}
.jdqt{display:block;font:700 calc(14px*var(--rs)) var(--disp);color:var(--ink)}
.jdqn{display:block;font:400 calc(11.5px*var(--rs)) var(--sans);color:var(--faint);margin-top:3px;line-height:1.45}

.jdplace{margin:0 0 14px}
.jdplace .row{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.jdplace b{font:700 calc(15px*var(--rs)) var(--disp);color:var(--ink)}

/* The market, counted. Links, not figures: a number a reader cannot act on is
   decoration, and every one of these has a page behind it. */
.jdkpi{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin:0 0 18px}
.jdkpi .kpi{display:block;border:1px solid var(--rule2);padding:12px 14px;text-decoration:none;text-align:center}
.jdkpi .kpi:hover{border-color:var(--dim)}
.jdkpi .kpi b{display:block;font:700 calc(26px*var(--rs)) var(--disp);color:var(--ink);line-height:1.1}
.jdkpi .kpi span{display:block;font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.11em;
  text-transform:uppercase;color:var(--faint);margin-top:4px}

.jdlede{display:block;font:700 calc(17px*var(--rs)) var(--disp);color:var(--ink);margin:2px 0 6px;line-height:1.25}
.jdreader .row{display:flex;gap:12px;align-items:flex-start}
.jdmark{font-size:calc(30px*var(--rs));line-height:1}

/* The bar carrying counts, tabs and the view switch. One bar, so the three
   controls read as one set rather than three stacked decisions. */
.jdbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 12px;
  padding:8px 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.jdbar>i{flex:1 1 auto}
.jdcount{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.12em;
  text-transform:uppercase;color:var(--faint)}
.jdview,.jdtabs{display:inline-flex;border:1px solid var(--rule2)}
.jdview a,.jdtabs a{display:inline-flex;align-items:center;min-height:34px;padding:7px 12px;
  font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.1em;text-transform:uppercase;
  color:var(--faint);text-decoration:none;border-right:1px solid var(--rule2)}
.jdview a:last-child,.jdtabs a:last-child{border-right:0}
.jdview a:hover,.jdtabs a:hover{color:var(--ink)}
.jdview a.on,.jdtabs a.on{background:var(--ink);color:var(--paper)}
@media (pointer:coarse){.jdview a,.jdtabs a{min-height:44px}}

/* Real selects with a submit beside them. Without scripting a filter cannot
   apply itself, and a control that looks live but is not is worse than one
   that plainly asks to be pressed. */
.jdfilters{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin:0 0 6px}
.jdfilters label{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.08em;
  text-transform:uppercase;color:var(--faint);display:flex;flex-direction:column;gap:4px}
.jdfilters select{background:var(--paper2);color:var(--ink);border:1px solid var(--rule2);
  padding:7px 8px;font:400 calc(13px*var(--rs)) var(--serif);min-height:38px}

.jdpassed{display:flex;flex-direction:column;gap:0}
.jdprow{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:10px 0;border-bottom:1px solid var(--rule)}
.jdpt{font:700 calc(14px*var(--rs)) var(--disp);color:var(--ink);text-decoration:none}
.jdpt:hover{text-decoration:underline;text-underline-offset:2px}
.jdpc{font:400 calc(11.5px*var(--rs)) var(--sans);color:var(--faint);margin-right:auto}

.jdlessons{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;margin:8px 0 12px}
.jdl{border:1px solid var(--rule2);padding:13px 14px}
.jdl.gold{border-left:3px solid var(--gold)}
.jdl b{display:block;font:700 calc(13.5px*var(--rs)) var(--disp);color:var(--ink);margin-bottom:5px}
.jdl p{margin:0;font:400 calc(12.5px*var(--rs)) var(--serif);color:var(--dim);line-height:1.55}

.jdctx{margin:0 0 14px}
.jdctxpick{display:flex;flex-wrap:wrap;gap:6px;margin-top:9px}
.jdsignals{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:10px;margin:8px 0 10px}
.jds{border:1px solid var(--rule2);padding:11px 12px}
.jds b{display:block;font:700 calc(12.5px*var(--rs)) var(--disp);color:var(--ink)}
.jds span{display:block;font:400 calc(11px*var(--rs)) var(--sans);color:var(--faint);margin-top:3px}

.jdmatch{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}
/* The prototype calls this .card. A bare .card in a global sheet is the
   collision that cost this codebase once already (.comrow, renamed .payrow
   after it silently reshaped the discussion chip rail), so it is prefixed
   like everything else on these screens. */
.jdcard{border:1px solid var(--rule2);background:var(--paper);padding:14px 16px;margin:0 0 12px}
.jdcard>.kick.k2{display:block;margin-bottom:4px}
.jdmatch .jdcard{display:flex;flex-direction:column;align-items:flex-start;gap:2px;margin:0}
.jdmatch .jdcard .b{margin-top:10px}
/* The ladder. A rung that has happened is inked, the one in progress is the
   accent, and the rest stay faint: the shape says how far along this is
   without needing a percentage nobody could defend. */
.jdtimeline{display:flex;flex-wrap:wrap;gap:6px;margin:9px 0 2px}
.jdtimeline span{font:600 calc(9.5px*var(--rs)) var(--sans);letter-spacing:.09em;
  text-transform:uppercase;color:var(--faint);border:1px solid var(--rule2);padding:4px 8px}
.jdtimeline .done{color:var(--dim);border-color:var(--rule)}
.jdtimeline .now{color:var(--act);border-color:var(--act);font-weight:700}

@media (max-width:640px){
  .jdnav{gap:0}
  .jdn{padding:9px 10px;font-size:calc(10px*var(--rs))}
  .jdbar{gap:8px}
  .jdfilters{gap:8px}
  .jdfilters label{flex:1 1 100%}
  .jdfilters select{width:100%}
}

/* Static labels, wrapping. Distinct from .chips, which is a scroll rail. */
.jdchips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}

/* ── The market's pictures (0287) ──────────────────────────────────────────
   Every colour here is a token. A literal hex would look right in phosphor
   and break the other six editions, which is the one rule this stylesheet
   cannot bend.

   A photograph does not theme. The frame, the caption and the spacing do, so
   those hang off the tokens and the picture is left alone: the grayscale(1)
   the house puts on .uface and img.fav exists so a member's mark stays
   subordinate to the writing beside it, and a gallery somebody deliberately
   opened is not that. eink is the exception, because eink is a screen that
   cannot show the difference anyway. */

/* The lead picture on a card. A FIXED 3:2 box, never height:auto: .jgrid sets
   grid-auto-rows:1fr, so one tall photograph would grow every row in the grid
   to match it. object-fit does the rest, and the server has already cropped
   to this ratio, so it is a belt on a listing whose card render predates a
   change to these numbers. */
.jflead{position:relative;aspect-ratio:3/2;overflow:hidden;border-bottom:1px solid var(--rule);background:var(--paper2)}
.jflead img{display:block;width:100%;height:100%;object-fit:cover}
.jfmore{position:absolute;right:8px;bottom:8px;background:var(--paper);color:var(--dim);
  border:1px solid var(--rule2);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:700}

/* The strip under a candidate's facts. Deliberately not in .cface: that slot
   is FaceKind's, and a second thing drawing into it would be the way round the
   guarantee that a codename cannot show a sharp photograph. */
.cshots{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.cshot{display:block;width:84px;height:56px;overflow:hidden;border:1px solid var(--rule2);border-radius:4px}
.cshot img{display:block;width:100%;height:100%;object-fit:cover}

/* The listing page's gallery. */
.jgal{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin:10px 0 18px}
.jshot{margin:0}
/* The border is not decoration: without it a light picture dissolves into
   --paper in slate, paper and dawn, and a dark one dissolves into phosphor
   and ember. */
.jshot img{display:block;width:100%;aspect-ratio:3/2;object-fit:cover;
  border:1px solid var(--rule2);border-radius:4px;background:var(--paper2)}
/* Captions sit OUTSIDE the picture, never over it. Text over an arbitrary
   photograph cannot be contrast-tested, so it cannot be signed off. */
.jshot figcaption{font-size:12px;line-height:1.45;color:var(--dim);margin-top:6px}

/* One picture on its own page. */
.shotpage{margin:14px 0}
.shotpage img{display:block;max-width:100%;height:auto;border:1px solid var(--rule2);background:var(--paper2)}
.shotpage figcaption{font-size:13px;line-height:1.5;color:var(--dim);margin-top:8px;max-width:60ch}
.shotnav{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0 18px}
/* A disabled end-of-gallery step, drawn as a step rather than removed, so the
   row does not shuffle sideways on the first and last picture. */
.b.off{opacity:.45;pointer-events:none}

/* The owner's management list. */
.shotmgr{display:grid;gap:12px;margin:10px 0 6px}
.shotrow{display:grid;grid-template-columns:160px 1fr;gap:12px;align-items:start;
  border:1px solid var(--rule);border-radius:6px;padding:10px;background:var(--paper2)}
.shotrow.pending{border-style:dashed}
.shotthumb{display:block;width:100%;aspect-ratio:3/2;object-fit:cover;
  border:1px solid var(--rule2);border-radius:4px;background:var(--paper)}
.shotform{display:grid;gap:8px}
.shotacts{display:flex;gap:6px;flex-wrap:wrap}
.shotadd{display:grid;gap:8px;border:1px dashed var(--rule2);border-radius:6px;padding:12px;background:var(--paper2)}
@media(max-width:560px){.shotrow{grid-template-columns:1fr}}

/* eink shows one ink. Greyscaling the pictures there is honesty about the
   display rather than a judgement about the pictures, and the edition already
   does exactly this to the reaction chips. */
[data-theme="eink"] .jflead img,
[data-theme="eink"] .cshot img,
[data-theme="eink"] .jshot img,
[data-theme="eink"] .shotpage img,
[data-theme="eink"] .shotthumb{filter:grayscale(1)}

/* The picture queue, inside the ad queue rather than beside it. */
.admshots > td{background:var(--paper2);border-top:0}
.admshot{display:grid;grid-template-columns:150px 1fr;gap:10px;align-items:start;
  padding:8px 0;border-top:1px solid var(--rule)}
.admshot:first-child{border-top:0}
.admshot img{display:block;width:100%;aspect-ratio:3/2;object-fit:cover;
  border:1px solid var(--rule2);border-radius:4px;background:var(--paper)}
.admshotbody{display:grid;gap:6px}
@media(max-width:640px){.admshot{grid-template-columns:1fr}}
.admshotthumb{display:block;width:100%;max-width:150px;aspect-ratio:3/2;object-fit:cover;
  border:1px solid var(--rule2);border-radius:4px;background:var(--paper2)}

/* ===== FRONT PAGE (.fp-) =====
   The frozen daily edition at / (front.html), and the weekly and monthly at
   /edition/{kind}/{no}. Namespaced .fp- because .edlede/.eddesks belong to the
   editorial strip and .flash to the session message. Reuses .newsrow, .sechd,
   .wireflash, .aibadge, .hl and .kfocus; everything else is here. Metadata
   sits at --dim, not --faint: --faint under 12px fails contrast on --paper2 in
   four of the seven themes. */
.fp-band{margin-top:6px}
.fp-main,.fp-rail{min-width:0}
.fp-row .hl{overflow-wrap:anywhere}
.fp-row.kfocus{outline:2px solid var(--act);outline-offset:-1px}
.fp-empty{font-family:var(--serif);font-size:calc(15px*var(--rs));color:var(--dim);margin:18px 0}
.fp-empty a{color:var(--act);text-decoration:underline;text-underline-offset:2px}

/* section heads: the house .sechd with a real h2 in it */
.fp-sechd{margin:22px 0 8px}
.fp-sechd h2{font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;margin:0}
.fp-sechd .n{color:var(--dim);font-variant-numeric:tabular-nums}
.fp-sechd .lnk{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;text-decoration:none;color:var(--act);white-space:nowrap;padding:0}
.fp-sechd .lnk:hover{color:var(--ink)}
.fp-rail .fp-sechd{margin-top:0;padding-top:12px}

/* the edition line */
.fp-edition{display:flex;justify-content:space-between;align-items:center;gap:6px 12px;flex-wrap:wrap;padding:9px 0 6px;font-family:var(--sans);font-size:calc(12px*var(--rs));color:var(--dim)}
.fp-edition b{color:var(--ink);font-weight:600}
.fp-edno{font-family:var(--disp);font-size:calc(15px*var(--rs));color:var(--mark);letter-spacing:.02em;margin-right:6px}
.fp-frozen{color:var(--dim)}
.fp-formats{font-size:calc(10.5px*var(--rs));letter-spacing:.04em}
.fp-formats a{color:var(--dim);border-bottom:1px solid var(--rule2)}
.fp-formats a:hover{color:var(--act);border-color:var(--act)}
.fp-formats .fp-towire{color:var(--act);border:0;letter-spacing:.06em;text-transform:uppercase;font-weight:600}

/* the wire flash, on top of the house .wireflash */
.fp-flash{margin-top:10px;border-left-width:4px}
.fp-flash .hl{font-family:var(--serif);font-weight:700;font-size:calc(15.5px*var(--rs));line-height:1.3;letter-spacing:0}
.fp-flash-st{font-size:calc(11px*var(--rs));color:var(--dim)}
.fp-flash-st b{color:var(--ink);font-weight:600}

/* jump row: phones only, scrolls in its own box */
.fp-jump{display:none;gap:6px;overflow-x:auto;scrollbar-width:none;padding:0 0 10px;font-family:var(--sans);max-width:100%}
.fp-jump a{font-size:calc(10px*var(--rs));letter-spacing:.07em;text-transform:uppercase;border:1px solid var(--rule2);color:var(--dim);padding:6px 10px;border-radius:2px;white-space:nowrap;flex:0 0 auto}
.fp-jump a:hover{color:var(--ink);border-color:var(--ink)}
.fp-jump a i{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--sh,var(--rule2));margin-right:6px;vertical-align:1px}
@media (max-width:760px){.fp-jump{display:flex}}

/* story rows: the shared pieces */
.fp-sum{font-family:var(--serif);font-size:calc(13.5px*var(--rs));line-height:1.45;color:var(--dim);margin:3px 0 0;max-width:62ch;text-wrap:pretty}
.fp-withheld{display:inline-block;font-family:var(--sans);font-size:calc(10.5px*var(--rs));color:var(--dim);letter-spacing:.04em;margin-top:3px;cursor:help;border-bottom:1px dotted var(--rule2)}
/* The persona column is bounded and may wrap: a beat like "learning in
   public, the audience participates" in small caps is wider than a phone,
   and with an auto column the quote beside it was squeezed off the page. */
.fp-line{display:grid;grid-template-columns:minmax(0,max-content) minmax(0,1fr);gap:0 10px;align-items:baseline;margin:6px 0 0;padding:6px 0 0;border-top:1px dotted var(--rule2)}
.fp-who{font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:700;color:var(--gold);max-width:11em}
@media (max-width:520px){.fp-line{grid-template-columns:minmax(0,1fr)}.fp-who{max-width:none}}
.fp-who a{color:inherit}
.fp-who small{display:block;font-weight:500;color:var(--dim);font-size:calc(9.5px*var(--rs));letter-spacing:.06em;text-transform:uppercase}
.fp-say{font-family:var(--serif);font-style:italic;font-size:calc(14px*var(--rs));line-height:1.45;color:var(--ink);text-wrap:pretty}
.fp-meta{display:flex;gap:2px 8px;align-items:center;flex-wrap:wrap;font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim);margin-top:4px}
.fp-meta .fp-src{color:var(--ink);font-weight:500}
.fp-meta .fp-src:hover{color:var(--act)}
.fp-age{color:var(--dim)}
.fp-desk{color:var(--sh,var(--dim));letter-spacing:.1em;text-transform:uppercase;font-size:calc(10px*var(--rs));font-weight:600;font-family:var(--sans)}
.fp-desk i{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--sh,var(--rule2));margin-right:6px;vertical-align:1px}
.fp-why{color:var(--dim);margin-left:auto;display:flex;gap:0 8px}
.fp-why b{color:var(--gold);font-weight:600}
.fp-growth{color:var(--mark);white-space:nowrap}
.fp-deal{color:var(--gold);font-weight:600;font-size:calc(10.5px*var(--rs))}
.fp-dur{color:var(--act);font-weight:600}
.fp-picked a{color:var(--ink)}

/* the lede */
.fp-lede{padding:14px 0 16px;border-bottom:1px solid var(--rule)}
/* The size sits on the heading, not the link, so 22ch is 22 of THESE
   characters: on the link it measured the heading's default size and the
   headline came out one word per line. */
.fp-lede-hl{margin:0 0 8px;max-width:22ch;font-family:var(--serif);font-weight:700;font-size:clamp(26px,3.4vw,38px);line-height:1.12;letter-spacing:-.012em;text-wrap:pretty;font-variation-settings:"opsz" 36}
.fp-lede-hl .hl{font:inherit;letter-spacing:inherit}
.fp-lede .fp-sum{font-size:calc(17px*var(--rs));line-height:1.5;max-width:60ch;margin:0 0 6px}

/* the water cooler */
.fp-cooler{margin:0;padding:0;list-style:none;border-bottom:2px solid var(--rule2)}
.fp-cooler li{display:grid;grid-template-columns:2.2em 1fr;gap:0 10px;padding:10px 0;border-bottom:1px solid var(--rule)}
.fp-cooler li:last-child{border-bottom:0}
.fp-cooler li>div{min-width:0}
.fp-cooler-n{font-family:var(--disp);font-size:calc(18px*var(--rs));color:var(--mark);line-height:1.2}
.fp-cooler .hl{font-family:var(--serif);font-weight:700;font-size:calc(17px*var(--rs));line-height:1.25;text-wrap:pretty}
.fp-cooler .fp-line{margin-top:5px}

/* developing */
.fp-dev{margin:0;padding:0;list-style:none}
.fp-dev li{padding:8px 0;border-bottom:1px solid var(--rule)}
.fp-dev li:last-child{border-bottom:0}
.fp-dev li>div{min-width:0}
.fp-dev-n{font-family:var(--sans);font-weight:700;font-size:calc(15px*var(--rs));color:var(--ink);min-width:2.2em;text-align:right;font-variant-numeric:tabular-nums}
.fp-dev-n small{display:block;font-size:calc(9.5px*var(--rs));letter-spacing:.06em;color:var(--dim);font-weight:500;text-transform:uppercase}
.fp-dev .hl{font-family:var(--serif);font-weight:600;font-size:calc(16px*var(--rs));line-height:1.3}

/* desk strips: two columns, one on phones */
.fp-desks{display:grid;grid-template-columns:1fr 1fr;gap:0 26px}
.fp-desks>*{min-width:0}
.fp-strip{margin-top:4px}
/* A desk's head on the front page: one rule above, the name in serif. */
.fp-deskhd{border-top:1px solid var(--ink);margin:22px 0 8px;padding-top:6px}
.fp-deskhd h2{font:500 calc(19px*var(--rs))/1.2 var(--serif);color:var(--ink);margin:0;letter-spacing:0;text-transform:none}
.fp-desks .fp-strip:nth-child(even){border-left:1px solid var(--rule);padding-left:20px}
@media (max-width:640px){.fp-desks{grid-template-columns:1fr}.fp-desks .fp-strip:nth-child(even){border-left:0;padding-left:0}}
.fp-strip-list{margin:0;padding:0;list-style:none}
.fp-strip-list li{padding:8px 0;border-bottom:1px solid var(--rule)}
.fp-strip-list li.feat{padding:9px 0}
.fp-strip-list li:last-child{border-bottom:0}
.fp-strip-list .hl{font-family:var(--serif);font-weight:600;font-size:calc(15.5px*var(--rs));line-height:1.3;letter-spacing:-.004em}
.fp-strip-list .fp-meta{margin-top:2px;font-size:calc(10.5px*var(--rs))}

/* listen & watch: three cells, then two, then one */
.fp-media{display:grid;grid-template-columns:1.25fr 1fr 1fr;gap:0 22px}
.fp-media>*{min-width:0}
.fp-media-cell+.fp-media-cell{border-left:1px solid var(--rule);padding-left:18px}
.fp-media-kind{display:block;font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.1em;text-transform:uppercase;color:var(--dim);margin-bottom:6px}
.fp-media-hl{margin:0 0 5px}
.fp-media-hl .hl{font-family:var(--serif);font-weight:600;font-size:calc(15.5px*var(--rs));line-height:1.3;text-wrap:pretty}
@media (max-width:760px){.fp-media{grid-template-columns:1fr 1fr}.fp-media-cell{padding:8px 0}.fp-media-cell+.fp-media-cell{border-left:0;padding-left:0}.fp-media-cell:nth-child(even){border-left:1px solid var(--rule);padding-left:16px}.fp-media-cell:nth-child(n+3){border-top:1px solid var(--rule)}}
@media (max-width:520px){.fp-media{grid-template-columns:1fr}.fp-media-cell:nth-child(even){border-left:0;padding-left:0}.fp-media-cell+.fp-media-cell{border-top:1px solid var(--rule)}}

/* rail modules */
.fp-mod{padding:0 0 14px;margin-bottom:8px;border-bottom:1px solid var(--rule)}
.fp-mod:last-child{border-bottom:0}
.fp-rail .nlrail{padding:12px 0 14px;margin-bottom:8px;border-bottom:1px solid var(--rule)}
.fp-rail-list{margin:0;padding:0;list-style:none}
.fp-rail-list li{padding:7px 0;border-bottom:1px solid var(--rule)}
.fp-rail-list li:last-child{border-bottom:0}
.fp-rail-list .hl{font-family:var(--serif);font-weight:600;font-size:calc(14.5px*var(--rs));line-height:1.3}
.fp-rail-list .fp-meta{font-size:calc(10.5px*var(--rs))}

/* the puzzle */
.fp-pz-num{font-family:var(--disp);font-size:calc(22px*var(--rs));line-height:1;color:var(--mark);margin:0 0 6px}
.fp-pz-t{font-family:var(--serif);font-weight:700;font-size:calc(18px*var(--rs));line-height:1.15;margin:0 0 3px}
.fp-pz-s{font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim);line-height:1.5;margin:6px 0 0}
.fp-pz-s a{color:var(--act);text-decoration:underline;text-underline-offset:2px}
.fp-btn{display:inline-block;margin-top:8px;font-family:var(--sans);font-size:calc(11px*var(--rs));letter-spacing:.08em;text-transform:uppercase;font-weight:700;color:var(--paper);background:var(--mark);padding:7px 12px;border-radius:2px}
.fp-btn:hover{color:var(--paper);background:var(--ink)}
.fp-btn.alt{background:none;color:var(--ink);border:1px solid var(--rule2)}
.fp-btn.alt:hover{border-color:var(--ink);background:none;color:var(--ink)}

/* the review module: the week or the month, on the day it froze */
.fp-review-lede{padding:4px 0 8px;border-bottom:1px solid var(--rule)}
.fp-review-lede .hl{font-family:var(--serif);font-weight:700;font-size:calc(16px*var(--rs));line-height:1.25}
.fp-review-desk{margin-top:8px}
.fp-review-desk .fp-desk{display:block;margin-bottom:2px}
.fp-review-more{font-family:var(--sans);font-size:calc(10.5px*var(--rs));letter-spacing:.06em;text-transform:uppercase;font-weight:600;margin:10px 0 0}
.fp-review-more a{color:var(--act)}

/* jobs */
.fp-job{display:grid;grid-template-columns:1fr auto;gap:0 10px;align-items:baseline}
.fp-job>div{min-width:0}
.fp-jobs .hl{font-size:calc(14px*var(--rs))}
.fp-job-co{font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim)}
.fp-job-loc{font-family:var(--sans);font-size:calc(10.5px*var(--rs));color:var(--dim);white-space:nowrap}

/* the wire band */
.fp-wire{margin-top:22px;border-top:3px double var(--rule2);border-bottom:1px solid var(--rule);padding:18px 0;display:flex;justify-content:space-between;align-items:baseline;gap:8px 12px;flex-wrap:wrap;color:inherit}
.fp-wire:hover .fp-wire-t,.fp-wire:hover .fp-wire-go{color:var(--act)}
.fp-wire-t{font-family:var(--serif);font-size:calc(19px*var(--rs));font-weight:700}
.fp-wire-n{font-family:var(--sans);font-size:calc(11px*var(--rs));color:var(--dim)}
.fp-wire-n b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
.fp-wire-go{font-family:var(--sans);font-size:calc(10.5px*var(--rs));letter-spacing:.1em;text-transform:uppercase;font-weight:700;color:var(--act)}
.fp-wire.kfocus{outline:2px solid var(--act);outline-offset:2px}

/* Phase 3 modules: corrections, long-running stories, top comments, the puzzle
   standing, and the media band's play link. */
.fp-corr{font-family:var(--serif);font-size:calc(13px*var(--rs));line-height:1.5;color:var(--dim);margin:18px 0 0;padding-top:10px;border-top:1px solid var(--rule2)}
.fp-corr b{color:var(--ink);font-weight:600}
.fp-corr p{margin:4px 0}
.fp-corr .lnk{margin-left:6px}
.fp-arcs{margin:0;padding:0;list-style:none}
.fp-arcs li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 10px;padding:7px 0;border-bottom:1px solid var(--rule);align-items:start}
.fp-arcs li:last-child{border-bottom:0}
.fp-arc-t{font-family:var(--serif);font-weight:600;font-size:calc(14px*var(--rs));line-height:1.3}
.fp-arc-m{font-family:var(--sans);font-size:calc(10.5px*var(--rs));color:var(--dim);grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:2px 6px}
.fp-arc-m b{color:var(--ink);font-weight:600}
.fp-state{font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.1em;text-transform:uppercase;font-weight:700;padding:2px 6px;border-radius:2px;white-space:nowrap}
.fp-state.quiet{color:var(--dim);border:1px solid var(--rule2)}
.fp-state.warming{color:var(--gold);border:1px solid var(--gold)}
.fp-state.boiling{color:var(--paper);background:var(--bad)}
.fp-spark{display:inline-flex;align-items:flex-end;gap:2px;height:14px;margin:0 4px}
.fp-spark i{display:inline-block;width:6px;border-radius:1px 1px 0 0;background:var(--rule2)}
.fp-spark i.now{background:var(--gold)}
.fp-follow{font:600 calc(10px*var(--rs)) var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--act);background:none;border:0;padding:0;cursor:pointer}
.fp-follow.on{color:var(--gold);cursor:default}
.fp-cm{font-family:var(--serif);font-size:calc(13px*var(--rs));line-height:1.45;color:var(--dim);margin:3px 0 2px;text-wrap:pretty}
/* The result grid. Each Rows entry is a WHOLE ROW of tiles ("🟦🟦🟦🟦"), so each
   one is a line of its own.
   
   It used to be a CSS grid of four 14px columns with display:contents on the
   spans, which cannot work: display:contents promotes an element's CHILDREN to
   grid items, and a span's child here is a single text node — so each row
   became one anonymous item in one 14px cell, and four rows laid out as four
   cells on a single line with the tiles spilling out of them. Sixteen tiles in
   a row instead of four rows of four. A block per row needs no column count and
   cannot disagree with how many tiles a board has. */
.fp-pz-res{margin:8px 0;font-size:calc(13px*var(--rs));line-height:1.22;letter-spacing:.06em}
.fp-pz-res span{display:block;white-space:nowrap}
/* The fallback when a solved board has no stored grid: says the word, at the
   weight the achievement deserves rather than the weight of a footnote. */
.fp-pz-done{font-family:var(--sans);font-weight:700;font-size:calc(11px*var(--rs));
  letter-spacing:.14em;text-transform:uppercase;color:var(--ok);margin:6px 0 2px}
.fp-pz-streak{font-family:var(--sans);font-size:calc(10.5px*var(--rs));color:var(--dim);margin:6px 0 8px}
.fp-pz-streak b{font-size:calc(18px*var(--rs));color:var(--ink);font-weight:700;font-variant-numeric:tabular-nums;margin-right:4px}
.fp-play{display:inline-flex;align-items:center;gap:6px;font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:600;color:var(--act)}
.fp-play-b{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:50%;background:var(--act);color:var(--paper);font-size:10px;padding-left:2px}
.fp-play:hover .fp-play-b{background:var(--ink)}

/* ===== Watch · Today (handlers_today.go, today.html) =====
   Every class here is tw- prefixed: .coms, .comrow and .card have each cost a
   collision in this sheet already. Programme and wire rows are article.story,
   so the keyboard ring and the ride reach them with no selector change and
   the 11px ring gutter comes from the base rule. */
.twnav{display:flex;gap:2px;margin:12px 0 0;border-bottom:1px solid var(--rule)}
.twn{font-family:var(--sans);font-size:calc(10.5px*var(--rs));font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:var(--dim);padding:10px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;text-decoration:none}
.twn:hover{color:var(--ink)}
.twn.on{color:var(--ink);border-bottom-color:var(--act)}
.twdial{padding:22px 0 8px}
.twdial h3{font-family:var(--serif);font-weight:400;font-size:calc(24px*var(--rs));line-height:1.15;margin:0 0 6px}
.twdial .sub{font-family:var(--serif);color:var(--dim);max-width:62ch;margin:0 0 16px;font-size:calc(15px*var(--rs))}
.twmoods{display:grid;grid-template-columns:repeat(4,1fr);border-top:3px double var(--rule2);border-bottom:1px solid var(--rule)}
.twmood{position:relative;display:flex;flex-direction:column;gap:6px;min-height:112px;padding:16px 14px 14px;
  border-right:1px solid var(--rule);text-decoration:none;color:inherit}
.twmood:last-child{border-right:0}
.twmood:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}
.twmood.on{background:var(--paper2)}
.twmood.on::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:3px;background:var(--hi)}
.twmn{font-family:var(--sans);font-size:calc(12px*var(--rs));font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.twmood.on .twmn,.twmood.on .twml{color:var(--ink)}
.twml{font-family:var(--serif);font-style:italic;font-size:calc(14px*var(--rs));line-height:1.3;color:var(--dim)}
.twmc{margin-top:auto;font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.twnow{position:absolute;top:8px;right:10px;font-family:var(--sans);font-size:calc(9px*var(--rs));font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--act)}
.twprog,.twguide{padding:22px 0 30px;border-bottom:1px solid var(--rule)}
.twhd{display:flex;align-items:baseline;gap:14px 18px;flex-wrap:wrap;margin-bottom:14px}
.twhd h3{font-family:var(--sans);font-size:calc(12px*var(--rs));font-weight:700;letter-spacing:.14em;text-transform:uppercase;margin:0}
.twhd h3 span{font:italic 400 calc(13px*var(--rs)) var(--serif);letter-spacing:0;text-transform:none;color:var(--dim);margin-left:10px}
.twhd .twm{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.twhd .twr{margin-left:auto;display:flex;gap:16px;align-items:center;font-family:var(--sans);font-size:calc(10.5px*var(--rs));
  font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--dim)}
.twhd .twr a{text-decoration:none;color:inherit}
.twhd .twr a:hover,.twhd .twr button:hover{color:var(--ink)}
.twsched{border-top:1px solid var(--rule2)}
.tw-prow{display:grid;grid-template-columns:56px 150px 1fr;gap:16px;align-items:start}
.tw-prow.lead{grid-template-columns:56px 320px 1fr}
.tw-time{font-family:var(--sans);font-size:calc(11px*var(--rs));letter-spacing:.06em;color:var(--faint);font-variant-numeric:tabular-nums;padding-top:4px}
.tw-prow.lead .tw-time{color:var(--act);font-weight:700}
.tw-thumb{position:relative;display:flex;aspect-ratio:16/9;width:100%;align-items:center;justify-content:center;
  background:var(--paper2);border:1px solid var(--rule2);color:var(--act);overflow:hidden}
.tw-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.tw-thumb .ic{position:absolute;width:30px;height:30px;fill:currentColor;stroke:none;opacity:0;transition:opacity .15s;
  filter:drop-shadow(0 1px 4px rgba(0,0,0,.6))}
.tw-thumb:hover .ic,.tw-thumb:focus-visible .ic,.tw-thumb:not(:has(img)) .ic{opacity:1}
.tw-meta{display:flex;gap:4px 10px;flex-wrap:wrap;align-items:center;font-family:var(--sans);font-size:calc(10px*var(--rs));
  letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin-bottom:4px}
.tw-meta .sec{color:var(--dim)}
.tw-meta a{color:inherit;text-decoration:none}
.tw-meta a:hover{color:var(--ink)}
.tw-prow .hl{font-size:calc(17px*var(--rs));font-weight:500;line-height:1.25}
.tw-prow.lead .hl{font-size:calc(24px*var(--rs));line-height:1.18}
.tw-why{font-family:var(--serif);font-size:calc(14.5px*var(--rs));line-height:1.5;color:var(--dim);max-width:60ch;margin:6px 0 0}
.tw-also{margin-top:8px;font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.tw-also b{color:var(--dim);font-weight:600}
.twfoot{display:flex;gap:12px;align-items:center;padding-top:16px;flex-wrap:wrap;font-family:var(--sans);font-size:calc(12px*var(--rs));color:var(--faint)}
.twnote{font-family:var(--sans);font-size:calc(12.5px*var(--rs));color:var(--faint);padding:10px 0 0}
.twempty{padding:26px 0;font:italic 400 calc(16px*var(--rs)) var(--serif);color:var(--dim)}
.twempty a{color:var(--act);text-decoration:underline;text-underline-offset:2px}
.twdate{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.08em;text-transform:uppercase;color:var(--faint);display:flex;gap:10px;flex-wrap:wrap;margin:14px 0}
.twdate a{color:var(--dim);text-decoration:none}
/* the guide */
.twg{display:grid;grid-template-columns:150px 1fr;border-top:3px double var(--rule2)}
.twg .gl{padding:14px 12px 14px 0;border-bottom:1px solid var(--rule);display:flex;flex-direction:column;gap:4px;align-items:flex-start}
.twg .gl .wn{font-family:var(--sans);font-size:calc(11px*var(--rs));font-weight:700;letter-spacing:.12em;text-transform:uppercase}
.twg .gl .wc{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.twg .gh{display:grid;grid-template-columns:repeat(240,1fr);border-left:1px solid var(--rule);padding:6px 0;
  font-family:var(--sans);font-size:calc(10px*var(--rs));color:var(--faint);font-variant-numeric:tabular-nums}
.twg .gh span{grid-column:span 30;padding-left:4px}
.twg .gr{display:grid;grid-template-columns:repeat(240,1fr);border-bottom:1px solid var(--rule);border-left:1px solid var(--rule);min-height:64px;align-items:center}
.tw-gbar{display:block;margin:4px 2px 4px 0;padding:6px 8px;background:var(--paper2);border:1px solid var(--rule);border-left:3px solid var(--act);
  font-family:var(--serif);font-size:calc(13px*var(--rs));line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink);text-decoration:none;min-width:0}
.tw-gbar small{display:block;font-family:var(--sans);font-size:calc(9.5px*var(--rs));letter-spacing:.05em;text-transform:uppercase;color:var(--faint);margin-top:2px}
.tw-gbar:hover{border-color:var(--ink)}
.tw-gempty{grid-column:1/span 80;font-family:var(--sans);font-size:calc(12px*var(--rs));color:var(--faint);padding:0 8px}
/* the wire */
.twwire{padding:26px 0 40px}
.twfilters{display:flex;gap:6px;flex-wrap:wrap;margin:6px 0 14px}
.tw-row{display:grid;grid-template-columns:118px 1fr;gap:16px;align-items:start}
.tw-row .hl{font-size:calc(16px*var(--rs));font-weight:500;line-height:1.3}
.tw-row .smeta{margin-top:5px}
.tw-hideseen .tw-seen{display:none}
.tw-seen .hl{color:var(--dim);font-weight:400}
.tw-seen .tw-thumb{opacity:.6}
.tw-more{padding:18px 0;text-align:center}
.tw-hand{border-top:3px double var(--rule2);margin-top:8px;padding:22px 0 0;font:400 calc(16px*var(--rs)) var(--serif);color:var(--dim)}
@media (max-width:980px){
  .tw-prow.lead{grid-template-columns:56px 1fr}
  .tw-prow.lead .tw-thumb,.tw-prow.lead .tw-body{grid-column:2}
}
@media (max-width:640px){
  .twmoods{grid-template-columns:repeat(2,1fr)}
  .twmood{border-right:0;border-top:1px solid var(--rule)}.twmood:nth-child(odd){border-right:1px solid var(--rule)}.twmood:nth-child(-n+2){border-top:0}
  .tw-prow,.tw-prow.lead{grid-template-columns:44px 1fr;gap:10px}
  .tw-prow .tw-thumb,.tw-prow .tw-body{grid-column:2}
  .tw-row{grid-template-columns:92px 1fr;gap:10px}
  .twg{grid-template-columns:90px 1fr}
  .twnav{overflow:auto;scrollbar-width:none;white-space:nowrap}
}
/* A section with nothing under the current mood: present, dimmed, still a
   link. Hiding it instead would reshape the bar every time the dial moves. */
.chip.thin{opacity:.5}
/* the archive's facet lists */
.twarch{padding:22px 0 10px}
.twfacets{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));border-top:3px double var(--rule2)}
.twfacet{display:flex;flex-direction:column;gap:3px;padding:14px 12px 14px 0;border-bottom:1px solid var(--rule);text-decoration:none;color:inherit}
.twfacet .wn{font-family:var(--serif);font-size:calc(16px*var(--rs));color:var(--ink)}
.twfacet .wc{font-family:var(--sans);font-size:calc(10px*var(--rs));letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.twfacet:hover .wn{text-decoration:underline;text-underline-offset:3px;color:var(--act)}
