
/* Space Grotesk is one of the faces yikyak.com itself ships. Self-hosted rather than
   pulled from Google so the dashboard makes zero third-party requests, and because the
   CSP here is default-src 'none' — a CDN font would be blocked outright. */
@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("/font/space-grotesk.woff2") format("woff2")}
:root{
  /* Palette lifted from yikyak.com: theme-color #0DD5B2, ink #01151C, slate #344449.
     The dark surfaces are tinted toward that ink instead of neutral grey, so the whole
     UI sits in the brand hue rather than being grey with a teal button dropped on top. */
  --teal:#0dd5b2;--teal-hi:#00dac0;--teal-lo:#0ac7a6;--teal-ink:#01151c;
  --teal-glow:rgba(13,213,178,.16);--teal-wash:rgba(13,213,178,.08);
  --bg:#01151c;--panel:#06212a;--panel2:#0b2c37;--line:#123642;--line2:#1d4a58;
  --fg:#eaf7f5;--fg2:#b6cdd0;--mut:#7c979d;
  /* accents are semantic only: each one means something, none are decorative */
  --red:#ee5050;--amber:#fdc46e;--blue:#62b6ff;--violet:#a78dda;--blue2:#2f5f8a;
  --green:#0dd5b2;
  --sans:"Space Grotesk",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SF Mono","Cascadia Code","JetBrains Mono",Menlo,Consolas,monospace;
  --r:16px;--r-sm:11px;--nav-h:62px;
  --shadow:0 1px 2px rgba(0,0,0,.4),0 8px 24px -12px rgba(0,0,0,.6);
}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--bg);color:var(--fg);font:15.5px/1.6 var(--sans);
  -webkit-font-smoothing:antialiased;
  padding-bottom:calc(var(--nav-h) + env(safe-area-inset-bottom))}
/* one soft teal bloom behind the header keeps the brand hue present without decoration */
body::before{content:"";position:fixed;inset:0 0 auto 0;height:280px;pointer-events:none;z-index:0;
  background:radial-gradient(70% 100% at 50% 0%,rgba(13,213,178,.10),transparent 72%)}
body>*{position:relative;z-index:1}
a{color:var(--teal);text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:3px}
h1,h2,h3{margin:0 0 .4em;font-weight:700;letter-spacing:-.02em}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.mut{color:var(--mut)}.sm{font-size:12.5px}.xs{font-size:11.5px}
.wrap{max-width:1080px;margin:0 auto;padding:20px 16px 32px}
.row{display:flex;gap:12px;flex-wrap:wrap}
.between{display:flex;align-items:center;justify-content:space-between;gap:10px}
:focus-visible{outline:2px solid var(--teal);outline-offset:2px;border-radius:8px}

/* top bar (desktop) */
header.top{position:sticky;top:0;z-index:40;display:flex;align-items:center;gap:16px;
  padding:11px 18px;background:rgba(1,21,28,.82);backdrop-filter:saturate(160%) blur(14px);
  border-bottom:1px solid var(--line)}
header.top .brand{display:inline-flex;align-items:center;gap:10px;color:var(--fg)}
header.top .brand:hover{text-decoration:none}
header.top .brand .bt{font-weight:700;font-size:16.5px;letter-spacing:-.02em}
header.top nav.mainnav{display:none;align-items:center;gap:2px;flex:0 0 auto}
.ngroup{display:flex;align-items:center;gap:2px}
header.top nav.mainnav a.nl{position:relative;display:inline-flex;align-items:center;gap:8px;
  padding:9px 13px;white-space:nowrap;border-radius:var(--r-sm);color:var(--fg2);
  font-size:14.5px;font-weight:500;transition:background .16s ease,color .16s ease}
header.top nav.mainnav a.nl:hover{background:var(--panel2);color:var(--fg);text-decoration:none}
header.top nav.mainnav a.nl.active{background:var(--teal-wash);color:var(--teal)}
header.top nav.mainnav .navsep{align-self:center;width:1px;height:19px;margin:0 7px;background:var(--line)}

/* ── overflow, not truncation ──────────────────────────────────────────────
   The previous bar dropped the WORDS when it ran out of room, which left a row of
   unlabelled glyphs and no way to tell Events from Review without hovering. A label is
   the thing being clicked, so it never leaves: when the destinations no longer fit side
   by side the secondary group moves whole into a "More" menu, where every row still
   reads as icon + word. Nothing is ever half-visible, and nothing is ever anonymous.

   Two thresholds because the bar has two lengths: an admin carries Glossary, Campaign and
   Admin as well, and 11 labelled links need appreciably more room than 8. The class on <nav>
   says which bar this is, so the breakpoint matches the content instead of the worst case.
   The admin threshold moved from 1460 to 1580 when Campaign was added, because the number in
   that threshold is a measurement of the labels and not a round number to leave alone. */
.ngroup.sec{display:none}
header.top .menu.more{display:block}
@media (min-width:1240px){
  header.top nav.mainnav:not(.adm) .ngroup.sec{display:flex}
  header.top nav.mainnav:not(.adm) .menu.more{display:none}
}
@media (min-width:1580px){
  header.top nav.mainnav.adm .ngroup.sec{display:flex}
  header.top nav.mainnav.adm .menu.more{display:none}
}
@media (min-width:760px) and (max-width:1023px){
  header.top{gap:10px;padding-left:12px;padding-right:12px}
  header.top nav.mainnav a.nl{padding:9px 11px}
  header.top .brand .bt{display:none}
  /* The name shortens, it does not vanish: an account button with no name on it is the
     same guessing game as the icon-only links this replaced. */
  header.top .acct .anm{max-width:8ch}
}

/* ── dropdown menus (More, account) ────────────────────────────────────────
   <details> rather than a scripted popover: it opens, closes and takes keyboard focus
   with no JS at all, so the nav still works if the script fails. client.js only adds the
   conveniences a native details cannot do — click-outside and Escape. */
.menu{position:relative}
.menu>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
  padding:9px 12px;border-radius:var(--r-sm);color:var(--fg2);font-size:14.5px;font-weight:500;
  white-space:nowrap;min-height:40px;transition:background .16s ease,color .16s ease}
.menu>summary::-webkit-details-marker{display:none}
.menu>summary::marker{content:""}
.menu>summary:hover{background:var(--panel2);color:var(--fg)}
.menu[open]>summary{background:var(--panel2);color:var(--fg)}
.menu .chev{display:inline-flex;opacity:.7;transition:transform .16s ease}
.menu[open] .chev{transform:rotate(180deg)}
.menu.on>summary{color:var(--teal)}
.mpanel{position:absolute;top:calc(100% + 8px);right:0;z-index:60;min-width:224px;padding:7px;
  border:1px solid var(--line2);border-radius:14px;background:var(--panel);
  box-shadow:var(--shadow);display:flex;flex-direction:column;gap:2px}
.mpanel a,.mpanel button{display:flex;align-items:center;gap:11px;width:100%;min-height:44px;
  padding:10px 12px;border-radius:10px;border:0;background:none;color:var(--fg2);
  font-size:14.5px;font-weight:500;text-align:left;letter-spacing:0}
.mpanel a:hover,.mpanel button:hover{background:var(--panel2);color:var(--fg);
  text-decoration:none;box-shadow:none;transform:none}
.mpanel a.active{background:var(--teal-wash);color:var(--teal)}
.mpanel form{margin:0}
.mpanel .msep{height:1px;margin:6px 4px;background:var(--line)}
.mpanel .mhead{padding:9px 12px 5px;font-size:11px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--mut)}
/* The caption is uppercased; the username inside it is not. "VIEWER1" is not their name.
   The role reads as a badge rather than as more caption, so "admin · admin" stops looking
   like the same word twice. */
.mhead .mname{text-transform:none;letter-spacing:0;font-size:12.5px;color:var(--fg2);font-weight:600}
.mhead .mrole{margin-left:7px;padding:2px 8px;border-radius:999px;background:var(--teal-wash);
  color:var(--teal);font-size:10px;font-weight:600;letter-spacing:.07em}
.mpanel button.signout{color:var(--red)}
.mpanel button.signout:hover{background:rgba(238,80,80,.12);color:#ffb0b0}

/* The account control. It used to be a bare shield and a bare door icon with the username
   sitting loose between them; now it is one named button that says who you are, and the
   things you can do about it live under it. */
header.top .acct>summary{padding:6px 11px 6px 6px;border:1px solid var(--line);background:var(--panel2)}
header.top .acct .ava{display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:9px;background:var(--teal-wash);color:var(--teal);
  font-size:13px;font-weight:700;text-transform:uppercase}
header.top .acct .anm{max-width:10ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
header.top .sp{flex:1}
.btn,button{font:inherit;font-family:var(--sans);cursor:pointer;border-radius:var(--r-sm);
  padding:10px 16px;border:1px solid transparent;background:var(--teal);color:var(--teal-ink);
  font-weight:600;font-size:14.5px;min-height:44px;letter-spacing:-.01em;
  transition:background .16s ease,box-shadow .16s ease,transform .1s ease}
.btn:hover,button:hover{background:var(--teal-hi);box-shadow:0 0 0 4px var(--teal-glow);text-decoration:none}
.btn:active,button:active{transform:translateY(1px)}
.btn.ghost,button.ghost{background:var(--panel2);color:var(--fg);border-color:var(--line)}
.btn.ghost:hover,button.ghost:hover{background:var(--panel2);border-color:var(--teal);box-shadow:none}
.btn.sm{padding:6px 12px;min-height:36px;font-size:13px}
button.danger{background:var(--red);color:#1c0403}
button.danger:hover{background:#ff6a6a;box-shadow:0 0 0 4px rgba(238,80,80,.16)}
.hicon{display:inline-flex;align-items:center;justify-content:center;width:42px;height:42px;
  border-radius:var(--r-sm);color:var(--fg2);border:1px solid var(--line);background:var(--panel2);
  transition:color .16s ease,border-color .16s ease}
.hicon:hover{color:var(--teal);border-color:var(--teal);text-decoration:none}
.hicon.active{color:var(--teal);border-color:var(--teal-lo);background:var(--teal-wash)}
input,select{font:inherit;font-family:var(--sans);color:var(--fg);background:var(--panel2);
  border:1px solid var(--line);border-radius:var(--r-sm);padding:10px 12px;min-height:44px;
  font-size:16px;transition:border-color .16s ease,box-shadow .16s ease}
input::placeholder{color:var(--mut)}
input:hover,select:hover{border-color:var(--line2)}
input:focus,select:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px var(--teal-glow)}
label.fld{display:flex;flex-direction:column;gap:5px;font-size:12.5px;color:var(--mut)}

/* cards and tiles */
.card{position:relative;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  padding:16px 18px;margin-bottom:12px;box-shadow:var(--shadow)}
.card h3{font-size:12.5px;color:var(--mut);text-transform:uppercase;letter-spacing:.07em;font-weight:600}
.tiles{display:grid;grid-template-columns:repeat(2,1fr);gap:11px;margin-bottom:14px}
.tile{position:relative;background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  padding:15px 16px;box-shadow:var(--shadow);transition:border-color .16s ease}
.tile:hover{border-color:var(--line2)}
.tile .k{color:var(--mut);font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;font-weight:600}
.tile .v{font-size:29px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.1;
  margin-top:6px;letter-spacing:-.03em}
.tile .sub{font-size:12px;color:var(--mut);margin-top:3px;line-height:1.35;word-break:break-word}
.cmprow{display:inline-flex;align-items:center;gap:7px;flex-wrap:wrap}
.grid2{display:grid;grid-template-columns:1fr;gap:12px}

/* entries */
.entry{transition:border-color .16s ease}
/* the whole card is a target; the explicit Open icon stays for keyboard and screen readers */
.entry.nav{cursor:pointer}
.entry.nav:hover{border-color:var(--teal-lo)}
.entry.nav a,.entry.nav button,.entry.nav img.asset{cursor:pointer}
.entry:hover{border-color:var(--line2)}
.entry .meta{color:var(--mut);font-size:12.5px;display:flex;gap:10px;flex-wrap:wrap;
  align-items:center;margin-bottom:8px}
.entry .text{white-space:pre-wrap;word-break:break-word;font-size:15.5px;line-height:1.62}
.chip{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border:1px solid var(--line);
  border-radius:999px;font-size:11.5px;color:var(--fg2);background:var(--panel2)}
.chip.house{border-color:var(--blue2);color:#9bccff}
.chip.frat{border-color:var(--blue2);color:#9bccff}
.chip.sorority{border-color:#6b52a8;color:#c9b6ee}
/* review flags. Only severity 3 gets the red treatment; a level-1 tag is information, not
   an alarm, and colouring them all red would make the page unreadable. */
.chip.flag{border-color:var(--line2);color:var(--fg2)}
.chip.flag.s2{border-color:#7a6320;color:#e6c369}
.chip.flag.s3{border-color:#8c3b3b;color:#ff9b9b}
.chip.flag:hover{text-decoration:none;filter:brightness(1.2)}
/* The type mark is an icon, not text, so it gets a fixed square badge instead of the
   text pill's padding -- otherwise the glyph sits hard against the border on one side and
   hard against the house name on the other. Sized so every row's badge is identical. */
.typechip{gap:6px;padding-left:8px}
.tmark{width:27px;height:27px;padding:0;flex:0 0 27px;justify-content:center;gap:0}
.gmark{display:inline-flex;align-items:center;justify-content:center}
.gmark svg{display:block;shape-rendering:geometricPrecision}
.score{color:var(--teal);font-variant-numeric:tabular-nums;font-weight:600}
.score.neg{color:var(--red)}
/* sentiment is a filled pill with +/-/·; votes are bare text with an arrow. Different
   shape AND different glyph set, so neither is mistaken for the other at a glance. */
.sent{letter-spacing:.01em}
.sent{font-size:11.5px;padding:3px 9px;border-radius:999px;font-variant-numeric:tabular-nums;font-weight:600}
.sent.pos{background:rgba(13,213,178,.15);color:#5fe7cd}
.sent.neg{background:rgba(238,80,80,.15);color:#ff9090}
.sent.neu{background:rgba(124,151,157,.15);color:var(--fg2)}
.thumbs{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.thumbs a{display:inline-block;line-height:0}
img.asset{max-width:100%;max-height:240px;width:auto;height:auto;object-fit:contain;
  border:1px solid var(--line);border-radius:var(--r-sm);cursor:zoom-in;background:var(--panel2)}
.entry img.asset{max-height:220px}

/* filter bar */
.filters{display:grid;grid-template-columns:1fr;gap:9px;margin-bottom:16px}
.filters .line{display:flex;gap:9px;flex-wrap:wrap}
.filters .line>*{flex:1;min-width:130px}
.filters .full{width:100%}

/* save control. .sp was only ever styled under header.top, so the Thread link was never
   actually right-aligned — this fixes that too. icon() sets fill="none" as a presentation
   attribute, and any CSS rule outranks one, so the filled state costs a single rule. */
.entry .meta .sp{flex:1}
.savef{display:inline-flex;margin:0}
.savesearch{margin:-6px 0 14px}
.itemwrap.unread{border-left-color:var(--teal)}
a.savebtn{text-decoration:none}
/* line chart points: subtle until hovered, with a generous invisible hit area */
/* Donut. Nothing changes geometry on hover — only brightness and the centre readout —
   so the ring never lurches. Non-hovered slices are left alone rather than dimmed. */
.donut{width:100%;max-width:118px;height:auto;display:block;flex:0 0 auto;overflow:visible}
.donut .seg{cursor:pointer;outline:none}
.donut .seg circle{transition:filter .18s ease}
.donut .seg:hover circle,.donut .seg:focus-visible circle{filter:brightness(1.28) saturate(1.1)}
.donut .seg:focus-visible circle{stroke-dasharray:inherit;filter:brightness(1.35)}
.donut .cdef,.donut .cseg{transition:opacity .16s ease}
.donut .cseg{opacity:0}
.donut .seg:nth-of-type(1):hover ~ .ctr .cdef,.donut .seg:nth-of-type(1):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(1):hover ~ .ctr .cseg-0,.donut .seg:nth-of-type(1):focus-visible ~ .ctr .cseg-0{opacity:1}
.donut .seg:nth-of-type(2):hover ~ .ctr .cdef,.donut .seg:nth-of-type(2):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(2):hover ~ .ctr .cseg-1,.donut .seg:nth-of-type(2):focus-visible ~ .ctr .cseg-1{opacity:1}
.donut .seg:nth-of-type(3):hover ~ .ctr .cdef,.donut .seg:nth-of-type(3):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(3):hover ~ .ctr .cseg-2,.donut .seg:nth-of-type(3):focus-visible ~ .ctr .cseg-2{opacity:1}
.donut .seg:nth-of-type(4):hover ~ .ctr .cdef,.donut .seg:nth-of-type(4):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(4):hover ~ .ctr .cseg-3,.donut .seg:nth-of-type(4):focus-visible ~ .ctr .cseg-3{opacity:1}
.donut .seg:nth-of-type(5):hover ~ .ctr .cdef,.donut .seg:nth-of-type(5):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(5):hover ~ .ctr .cseg-4,.donut .seg:nth-of-type(5):focus-visible ~ .ctr .cseg-4{opacity:1}
.donut .seg:nth-of-type(6):hover ~ .ctr .cdef,.donut .seg:nth-of-type(6):focus-visible ~ .ctr .cdef{opacity:0}
.donut .seg:nth-of-type(6):hover ~ .ctr .cseg-5,.donut .seg:nth-of-type(6):focus-visible ~ .ctr .cseg-5{opacity:1}
/* let each card be its natural height instead of stretching to its tallest sibling */
.grid2{align-items:start}
/* Cards in an .even pair share a height instead of one dangling short. The children are
   flex columns so a block marked margin-top:auto pins to the bottom and fills the gap
   rather than leaving dead space under it. */
.grid2.even{align-items:stretch}
.grid2.even>.card{display:flex;flex-direction:column}
.sentwrap{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
.sentwrap .legend{flex:1 1 130px;flex-direction:column;gap:6px;margin:0}
.sentwrap .legend a{display:flex;align-items:center;gap:2px;color:var(--fg2);padding:5px 6px;border-radius:8px;
  transition:background .16s ease,color .16s ease}
.sentwrap .legend a:hover{color:var(--fg);background:var(--panel2);text-decoration:none}
.sentwrap .legend .pctv{margin-left:auto;color:var(--mut);font-variant-numeric:tabular-nums;font-size:11.5px}
@media (prefers-reduced-motion:reduce){ .donut .seg circle,.donut .cdef,.donut .cseg{transition:none} }
@media (max-width:459px){ .sentwrap{gap:12px} .donut{max-width:104px} }
/* chart tooltip: positioned with transform only, so it never triggers layout */
.heatcal{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap;margin:0}
.heatcal .fld{gap:3px;font-size:11px}
.heatcal input[type=date]{min-height:36px;padding:6px 9px;font-size:13.5px;color-scheme:dark}
.heatcal .btn{min-height:36px}
@media (max-width:559px){ .heatcal{width:100%} .heatcal .fld{flex:1 1 42%} .heatcal .btn{flex:1 1 100%;justify-content:center} }
.charttip{position:fixed;top:0;left:0;z-index:9998;pointer-events:none;
  background:rgba(11,44,55,.97);border:1px solid var(--teal-lo);color:var(--fg);
  padding:7px 11px;border-radius:9px;font-size:12.5px;font-weight:600;white-space:nowrap;
  font-variant-numeric:tabular-nums;box-shadow:0 6px 22px -6px rgba(0,0,0,.75);
  opacity:0;transform:translate3d(-9999px,-9999px,0);
  transition:opacity .13s ease}
.charttip.on{opacity:1}
@media (prefers-reduced-motion:reduce){ .charttip{transition:none} }
.linechart .pt{cursor:crosshair}
.linechart .dot{transition:r .12s ease,stroke-width .12s ease}
.linechart .pt:hover .dot{r:5;stroke:var(--fg);stroke-width:1.5}
/* The strip is short and its dots are hidden until you point at one, so the hit target is
   a full-height band per day and the guide line is what tells you which day you are on. */
.trendstrip .pt{cursor:crosshair}
.trendstrip .gl{opacity:0;transition:opacity .12s ease}
.trendstrip .dot{transition:r .12s ease}
.trendstrip .pt:hover .gl{opacity:.45}
.trendstrip .pt:hover .dot{r:3.2;stroke:var(--fg);stroke-width:1.2}
@media (prefers-reduced-motion:reduce){ .trendstrip .gl,.trendstrip .dot{transition:none} }

/* ---- glossary ----
   Two treatments on purpose: the pending queue is a task list (amber, expanded, evidence
   in full), the defined list is a reference (collapsed to one line, filterable). Showing
   two dozen open editors was the wall that made this section unusable. */
.gchips{display:flex;gap:6px;flex-wrap:wrap}
.gchip{font-size:11.5px;padding:3px 9px;border-radius:999px;border:1px solid var(--line);
  color:var(--mut);white-space:nowrap}
.gchip.act{border-color:#7a6320;color:var(--amber);background:rgba(253,196,110,.09)}
.gchip.ok{border-color:var(--teal-lo);color:var(--teal)}
.ghead{font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;color:var(--mut);
  margin:18px 0 9px;font-weight:600}
.gcard{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel2);
  margin-bottom:8px}
.gpend{padding:13px 14px;border-left:3px solid var(--amber)}
.gtop{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:9px}
.gterm{font-family:var(--mono);font-size:14px;font-weight:600;color:var(--fg)}
.gcount{font-size:11.5px;color:var(--amber)}
/* the entry the analysis was reading, in full — a clipped quote cannot answer "what does
   this word mean", which is the only question being asked here */
.gquote{margin:0 0 11px;padding:9px 12px;border-left:2px solid var(--line2);
  background:rgba(1,21,28,.45);border-radius:0 8px 8px 0;color:var(--fg2);
  font-size:13.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word}
.gopen{display:inline-block;margin-top:6px;font-size:11.5px;color:var(--mut)}
.gopen:hover{color:var(--teal)}
.gfield{display:block}
.glabel{display:block;font-size:12px;font-weight:600;color:var(--fg2);margin-bottom:5px}
.gfield textarea{width:100%;min-height:62px;resize:vertical;line-height:1.5}
.ghelp{display:block;font-size:11.5px;color:var(--mut);margin-top:5px;line-height:1.5}
.gactions{display:flex;gap:8px;flex-wrap:wrap;margin-top:11px}
.gactions button,.gactions .btn{min-height:44px}
.gactions .danger{color:var(--red);border-color:rgba(238,80,80,.35)}
.gactions .danger:hover{background:rgba(238,80,80,.1)}
/* defined rows: collapsed, one scannable line each */
.gdone summary{display:flex;align-items:baseline;gap:10px;padding:11px 14px;cursor:pointer;
  list-style:none;border-radius:var(--r-sm);min-height:44px;box-sizing:border-box}
.gdone summary::-webkit-details-marker{display:none}
.gdone summary::before{content:"";flex:0 0 6px;height:6px;margin-top:6px;border-right:1.5px solid var(--mut);
  border-bottom:1.5px solid var(--mut);transform:rotate(-45deg);transition:transform .16s ease}
.gdone[open] summary::before{transform:rotate(45deg)}
.gdone summary:hover{background:var(--panel)}
.gdone summary:focus-visible{outline:2px solid var(--teal);outline-offset:-2px}
.gprev{color:var(--mut);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;min-width:0}
.gdone form{padding:0 14px 13px}
/* a whole card that folds: the summary carries the heading and enough of the newest row to
   answer the usual question without opening it */
.card.fold{padding:0}
.card.fold>summary{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;cursor:pointer;
  padding:15px 17px;list-style:none;border-radius:var(--r);min-height:44px;box-sizing:border-box}
.card.fold>summary::-webkit-details-marker{display:none}
.card.fold>summary::before{content:"";flex:0 0 6px;height:6px;align-self:center;
  border-right:1.5px solid var(--mut);border-bottom:1.5px solid var(--mut);
  transform:rotate(-45deg);transition:transform .16s ease}
.card.fold[open]>summary::before{transform:rotate(45deg)}
.card.fold>summary h3{margin:0;display:inline}
.card.fold>summary .mut{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card.fold>summary:hover{background:var(--panel2)}
.card.fold>summary:focus-visible{outline:2px solid var(--teal);outline-offset:-2px}
.card.fold>.logbox,.card.fold>.empty{margin:0 17px 16px}
.gempty{color:var(--mut);font-size:13px;padding:12px 14px;border:1px dashed var(--line);
  border-radius:var(--r-sm);line-height:1.55}
.gimpact{display:block;font-size:11.5px;color:var(--mut);margin-top:9px;line-height:1.5}
.gimpact b{color:var(--fg2);font-variant-numeric:tabular-nums}
/* The two reference rows under the defined list. Dashed and unfilled so they do not read as
   two more glossary entries with the odd names "not slang" and "never asked about" — which is
   exactly how they read when they borrowed the mono term styling of the rows above them. */
.gref{margin-top:15px;background:transparent;border-style:dashed}
.gref + .gref{margin-top:8px}
.greflabel{font-weight:600;font-size:13px;color:var(--fg2);white-space:nowrap}
/* the shipped stoplist, laid out as a reference and not as controls: no hover, no pointer */
.gwords{display:flex;flex-wrap:wrap;gap:5px 6px;padding:0 14px 13px}
.gwords code{font-family:var(--mono);font-size:11.5px;color:var(--mut);padding:2px 7px;
  border:1px solid var(--line);border-radius:999px;white-space:nowrap}
/* reader reports: a task list like the pending queue, but the evidence is a person's words
   rather than a post, so the note leads and the drafted term follows it */
.grep{padding:13px 14px;border-left:3px solid var(--blue)}
.gnote{margin:0 0 11px;padding:9px 12px;border-left:2px solid var(--blue2);
  background:rgba(1,21,28,.45);border-radius:0 8px 8px 0;color:var(--fg2);
  font-size:13.5px;line-height:1.55;white-space:pre-wrap;word-break:break-word}
.gwho{font-size:11.5px;color:var(--mut)}
.gflip{border-color:#7a2f2f;color:var(--red);background:rgba(238,80,80,.09)}
.gfilter{display:flex;gap:6px;align-items:center}
.gfilter input{min-height:38px;padding:7px 11px;font-size:13px;min-width:0;width:190px}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
@media (prefers-reduced-motion:reduce){ .gdone summary::before{transition:none} }
@media (max-width:559px){
  .gfilter{width:100%}
  .gfilter input{flex:1}
  .gactions button,.gactions .btn{flex:1 1 100%;justify-content:center}
}

/* ---- campaign log ----
   Built out of the glossary's vocabulary on purpose rather than a new one: both pages are a
   queue of things waiting on a person to decide something, and they should not look like two
   different products. Only what the glossary has no equivalent for is defined here - a draft
   shown as the exact characters that will be typed, its length against the limit, the
   precondition line that decides whether the buttons under it do anything, and a row that has
   to read as one row of buttons while actually being several separate forms. */
.ctext{margin:0 0 10px;padding:10px 12px;border-left:2px solid var(--line2);
  background:rgba(1,21,28,.45);border-radius:0 8px 8px 0;color:var(--fg);font-family:var(--mono);
  font-size:13.5px;line-height:1.6;white-space:pre-wrap;word-break:break-word}
/* Advisory only: db.mjs is what enforces the limit. This exists so being over it is something
   you see while writing rather than something the server tells you afterwards. */
.cchars{font-size:11.5px;color:var(--mut);font-variant-numeric:tabular-nums;white-space:nowrap}
.cchars.over{color:var(--red);font-weight:600}
/* The precondition, in the three states it has. Amber while it is still a claim about the
   world that has not happened, because that is the state where the controls above are dead
   and the page owes the reader an explanation for it. */
.cgate{display:block;font-size:12.5px;line-height:1.55;margin:0 0 9px;padding:8px 11px;
  border-radius:var(--r-sm);border:1px solid var(--line);color:var(--fg2)}
.cgate b{color:var(--fg)}
.cgate.off{border-color:#7a6320;background:rgba(253,196,110,.09)}
.cgate.ok{border-color:var(--teal-lo)}
.cgate.none{border-style:dashed;color:var(--mut)}
/* Each control posts to a different route, so this row cannot be the glossary's .gactions,
   where the buttons are siblings inside one form. The forms are the flex items here and the
   buttons fill them, which is also what lets a phone give each one its own full-width line. */
.cacts{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:11px}
.cacts form{margin:0;display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap}
.cacts button,.cacts .btn{min-height:44px}
.cacts .fld{flex:1 1 190px;min-width:0}
.cacts .danger{color:var(--red);border-color:rgba(238,80,80,.35)}
.cacts .danger:hover{background:rgba(238,80,80,.1);border-color:rgba(238,80,80,.55)}
/* A disabled control has to READ disabled. The default button is filled teal, and dimming
   that alone still looks like something worth pressing. */
.cacts button[disabled],.cacts button[aria-disabled="true"]{background:var(--panel2);color:var(--mut);border-color:var(--line);
  cursor:not-allowed;font-weight:500}
.cacts button[disabled]:hover{background:var(--panel2);border-color:var(--line);box-shadow:none}
.cacts button[disabled]:active{transform:none}
.cnote{font-size:11.5px;color:var(--mut);line-height:1.5;flex:1 1 210px;min-width:0}
/* The scoreboard: rows, not tiles. Every number on it is small, some of them are averages of
   a handful of posts, and a 29px figure would claim a confidence none of them have. */
.cscore{display:flex;flex-direction:column}
.crow{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;font-size:13.5px;line-height:1.6;
  padding:9px 0;border-bottom:1px solid var(--line)}
.crow:last-child{border-bottom:0;padding-bottom:0}
.crow b{color:var(--fg);font-variant-numeric:tabular-nums}
.ck{flex:0 0 136px;color:var(--mut);font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;
  font-weight:600}
.cv{flex:1 1 210px;min-width:0;color:var(--fg2)}
/* Ready and skipped rows are one line each, but the words still wrap in full instead of
   truncating: the exact words are the thing this page exists to get right. That is also why
   this carries the same white-space and word-break as .ctext above. A ready row is the one a
   person actually retypes from, and without pre-wrap a draft written over two paragraphs was
   shown there as a single run of text while the draft card above showed it correctly, so the
   two views of the same words disagreed about what was going to be typed. break-word is the
   other half of it: at 390px an unbroken run of characters in a draft has nowhere to wrap and
   pushed the row out past the edge of the card. */
.cone{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;padding:12px 14px}
.cwhen{font-size:12px;color:var(--mut);white-space:nowrap}
.ctx{flex:1 1 60%;min-width:0;font-size:13.5px;line-height:1.55;color:var(--fg);
  white-space:pre-wrap;word-break:break-word}
.cpad{padding:13px 14px}
.cbody{padding:0 14px 13px}
.card.fold>.cgroup{margin:0 17px 16px}
/* The editor behind a summary. Closed, its inputs still submit with the rest of the form,
   which is why the fields that are rarely touched can live in it without being lost. */
.cfold{border-top:1px solid var(--line)}
.cfold>summary{cursor:pointer;list-style:none;font-size:12.5px;color:var(--mut);
  padding:11px 14px;min-height:44px;display:flex;align-items:center;gap:8px}
.cfold>summary::-webkit-details-marker{display:none}
.cfold>summary:hover{color:var(--fg)}
.cfold>summary:focus-visible{outline:2px solid var(--teal);outline-offset:-2px}
/* the same fold used inside a form, where it is a group of fields rather than the lid of a
   card and therefore carries neither the rule above it nor the card's side padding */
.cfold.cinner{border-top:0}
.cfold.cinner>summary{padding:9px 0;min-height:38px}
.cwarn{display:block;font-size:12px;line-height:1.5;color:var(--amber);margin:0 0 10px}
/* The one control that opens the editor, and the block it opens into.

   The editor used to ship inside a closed <details> on every card. A closed <details> still
   sends every byte inside it, so folding it away saved nothing at all on the wire: it was
   48 KB a page, 47% of the whole response and 680 of its 1,462 elements, to show an editor
   zero times. This is an <a> instead — with script off it is a link to the same row with
   ?edit= on it, and with script it fetches the editor and drops it into the div below.

   The negative margins pull it out to the card's own edges, since it sits inside .cbody,
   which carries the side padding. 44px tall, because it is a tap target at 390px. */
.cedge{display:flex;align-items:center;gap:8px;border-top:1px solid var(--line);
  margin:11px -14px -13px;padding:11px 14px;min-height:44px;font-size:12.5px;color:var(--mut)}
.cedge:hover{color:var(--fg);text-decoration:none;background:var(--panel2)}
.cedit{padding:12px 0 0;margin-top:11px;border-top:1px solid var(--line)}
/* A row that has just been decided, in place. Deciding one no longer reloads the whole queue,
   so what comes back is this: the same row, what happened to it, and the way back. It replaces
   the card rather than removing it, because removing a row would shift every card below it up
   under a thumb at 390px, which is how you skip the wrong draft. */
.cdone{opacity:.7}
.cdone .cone{align-items:center}
.cdone form{margin:0}
.cdone.bad{opacity:1;border-color:rgba(238,80,80,.4)}
.cdone.bad .cwhen{color:var(--red);white-space:normal}
/* The reason a draft was written, which is three times the length of the draft.

   The rationale averages 201 characters against a 71-character post, so at 390px it was four
   or five lines under every card and taller than the thing being decided. Two lines, and a tap
   opens the rest — clamped rather than truncated on the server, so the whole of it is still on
   the page and nothing is hidden that a press cannot reach. The handler is in client.js, since
   the CSP refuses an inline script and innerHTML would never run one anyway. */
.gimpact.clampable{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;cursor:pointer}
.gimpact.clampable:hover{color:var(--fg2)}
.gimpact.clampable.open{display:block;overflow:visible;-webkit-line-clamp:none}
@media (max-width:559px){
  .ck{flex:1 1 100%}
  .cacts form{flex:1 1 100%}
  .cacts button,.cacts .btn{flex:1 1 100%;justify-content:center}
  /* ...except the row where the decision is actually made.

     Full-width stacking is right for the editor's Save, for the datetime forms on the Ready tab
     and for the add-a-draft form, and it was wrong here: four or five 44px bars, one per line,
     is 250px of controls wrapped around 70 characters of post, and twenty of those made the
     queue nineteen phone screens long for twenty decisions. Ready and Skip share a line; Copy
     and the precondition share the next. Both keep the 44px tap target, which is the part that
     was never the problem. Higher specificity than the rule above, deliberately. */
  .cacts.cdecide button,.cacts.cdecide .btn{flex:1 1 calc(50% - 4px);min-width:120px}
}

/* ---- events tracker ---- */
.evday{margin-bottom:12px}
.evhd{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:2px}
.evdate{font-weight:600;font-size:15px;color:var(--fg)}
a.evdate:hover{color:var(--teal);text-decoration:none}
.evhead{font-size:16px;font-weight:600;color:var(--fg);margin:6px 0 10px}
.evlist{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:9px}
.evlist li{display:flex;gap:9px;align-items:baseline;line-height:1.55;flex-wrap:wrap}
.evlist li::before{content:"";flex:0 0 5px;height:5px;border-radius:50%;background:var(--teal-lo);
  transform:translateY(-3px)}
/* the sentence and its receipts are separate boxes: run together in one span they copied and
   read out as "...(68 upvotes).> 681", one story counted twice with no gap between */
.evtext{min-width:0;flex:1 1 60%}
.evmeta{display:inline-flex;align-items:baseline;gap:6px;flex:0 0 auto;white-space:nowrap;
  margin-left:auto}
/* citations: the digest is a claim, these are the receipts for it */
.ref{display:inline-flex;align-items:center;justify-content:center;min-width:17px;height:17px;
  padding:0 4px;margin-left:4px;border:1px solid var(--line);border-radius:5px;
  font-size:10.5px;font-variant-numeric:tabular-nums;color:var(--mut);vertical-align:1px}
.ref:hover{border-color:var(--teal-lo);color:var(--teal);text-decoration:none}
.evsc{font-size:11.5px;font-weight:600;font-variant-numeric:tabular-nums;
  color:var(--teal);white-space:nowrap}
.evsc.neg{color:var(--red)}
/* a digest written before a word was defined is still the best account of that day, so it
   keeps its place and says what it is rather than vanishing until the next pass */
.evstale{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;border-radius:999px;
  border:1px solid #7a6320;background:rgba(253,196,110,.09);color:var(--amber);
  font-size:11px;font-weight:500;white-space:nowrap;vertical-align:2px;margin-left:8px}
.evquiet,.evthin{display:flex;align-items:baseline;gap:10px;color:var(--mut);font-size:12.5px;
  padding:9px 0;border-bottom:1px solid var(--line)}
.evthin .evdate,.evquiet .evdate{font-weight:500;color:var(--fg2);font-size:13px}
/* overview teaser */
.evteaser .evlist{gap:7px;font-size:13.5px}
/* The timeline band is a flexbox of bars, NOT an svg. The svg version was authored at a
   fixed viewBox and stretched with preserveAspectRatio="none", which looked right at one
   width and distorted at every other - fine on a phone, wrong on a desktop. Flex items size
   themselves to whatever width they are given, so there is no design width to get wrong. */
/* the date fields are label+input stacks and therefore taller than a bare button, which
   stretched Apply into a full-height slab. Bottom-align the row instead. */
.evfilters .line{align-items:flex-end}
.evfilters .line>button,.evfilters .line>.btn{min-height:38px}
.evbandwrap{display:grid;grid-template-columns:auto minmax(0,1fr);gap:9px;align-items:stretch}
.evyaxis{display:flex;flex-direction:column;justify-content:space-between;height:104px;
  font-size:10px;color:var(--mut);text-align:right;font-variant-numeric:tabular-nums;
  transform:translateY(-.4em)}
.evscroll{overflow-x:auto;overflow-y:hidden;padding-bottom:2px}
.evband{position:relative;display:flex;align-items:flex-end;gap:2px;height:104px;
  border-bottom:1px solid var(--line)}
/* Gridlines at the top and midpoint. ::after is generated as the LAST child, so without an
   explicit order it painted straight across the bars; both sit at z-index 0 and the bars at
   1 so the lines read behind them, visible only in the empty space above each bar. */
.evband::before,.evband::after{content:"";position:absolute;left:0;right:0;height:1px;
  background:var(--line);opacity:.5;pointer-events:none;z-index:0}
.evband::before{top:0}
.evband::after{top:50%}
/* cap the width so a two-day range renders two bars rather than two slabs; flex:1 still
   spreads them evenly across everything wider than that */
.ebar{flex:1 1 0;min-width:4px;max-width:52px;height:100%;display:flex;align-items:flex-end;position:relative;z-index:1}
.ebar i{display:block;width:100%;height:var(--h);min-height:2px;border-radius:2px 2px 0 0;
  background:var(--line2);transition:filter .12s ease}
.ebar.has i{background:var(--teal)}
.ebar.quiet i{background:var(--mut);opacity:.42}
.ebar.none i{background:var(--line2);opacity:.6}
.ebar.match i{background:var(--amber)}
.ebar:hover i{filter:brightness(1.4)}
.ebar.dim i{opacity:.18}
.evbandx{display:flex;justify-content:space-between;font-size:10.5px;color:var(--mut);
  margin-top:5px;font-variant-numeric:tabular-nums}
/* the sticky header would otherwise cover a day jumped to from elsewhere */
.evday,.evquiet,.evthin{scroll-margin-top:78px}
a.ts{color:var(--mut)}
a.ts:hover{color:var(--teal);text-decoration:none}
.savebtn.cnt{gap:5px;padding:6px 9px;min-width:auto}
.savebtn.cnt b{font-size:12.5px;font-variant-numeric:tabular-nums;font-weight:700}
/* bars: label / track / value. On phones the label gets its own line above the bar,
   which is the only way a house name stays readable at 390px. */
.bars{display:flex;flex-direction:column;gap:1px}
.bar{display:grid;grid-template-columns:minmax(96px,150px) 1fr auto;gap:11px;align-items:center;
  padding:8px 6px;border-radius:8px;color:var(--fg)}
a.bar:hover{background:var(--panel2);text-decoration:none}
a.bar .barlabel{color:var(--teal)}
.barlabel{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bartrack{background:var(--panel2);border-radius:999px;height:13px;overflow:hidden}
.barfill{display:block;height:100%;border-radius:999px}
.barval{font-variant-numeric:tabular-nums;font-size:12.5px;color:var(--mut);min-width:36px;text-align:right}
@media (max-width:559px){
  .bar{grid-template-columns:1fr auto;grid-template-areas:"label value" "track track";gap:5px 10px;padding:10px 6px;min-height:44px}
  .barlabel{grid-area:label;white-space:normal;font-size:15px}
  .barval{grid-area:value}
  .bartrack{grid-area:track;height:10px}
}
.relrow{display:block;padding:9px 0;border-bottom:1px solid var(--line);color:var(--fg)}
.relrow:last-child{border-bottom:0}
.relrow:hover{text-decoration:none;background:var(--panel2)}
.relmeta{display:flex;gap:9px;align-items:center;font-size:12px;color:var(--mut);margin-bottom:3px}
.reltext{display:block;font-size:14.5px;line-height:1.5;color:var(--fg2)}
.savebtn{background:none;border:0;padding:6px;min-height:36px;min-width:36px;color:var(--mut);
  display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-sm);box-shadow:none}
.savebtn:hover{background:var(--panel2);color:var(--fg);box-shadow:none}
.savebtn[aria-pressed="true"]{color:var(--teal)}
.savebtn[aria-pressed="true"] svg{fill:currentColor}
.savebtn[disabled]{opacity:.45}
/* collection cards */
.collgrid{display:grid;grid-template-columns:1fr;gap:11px}
.collcard{display:block;color:inherit}
.collcard:hover{text-decoration:none;border-color:var(--teal)}
.collname{font-weight:700;font-size:16px;display:flex;align-items:center;gap:8px}
.collmeta{color:var(--mut);font-size:12.5px;margin-top:4px}
.kindchip{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;padding:2px 8px;border-radius:999px;
  border:1px solid var(--line);color:var(--mut)}
.kindchip.smart{border-color:var(--violet);color:#c9b6ee}
.kindchip.shared{border-color:var(--amber);color:var(--amber)}
.itemwrap{border-left:2px solid var(--line);padding-left:10px;margin-bottom:12px}
.itemctl{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:6px}
.itemctl form{margin:0}
textarea{font:inherit;font-family:var(--sans);color:var(--fg);background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:10px 12px;font-size:15px;width:100%;min-height:60px;resize:vertical}
textarea:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px var(--teal-glow)}

/* heatmap drill-down banner */
.drill{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  background:linear-gradient(90deg,var(--teal-wash),transparent 70%);border-color:var(--teal-lo)}

/* tables and leaderboard */
.lb{display:flex;flex-direction:column;gap:3px}
.lb .hd,.lb .rw{display:grid;grid-template-columns:32px 1fr 84px 96px;gap:9px;align-items:center;padding:11px 7px}
.lb .hd{color:var(--mut);font-size:11.5px;text-transform:uppercase;letter-spacing:.07em;
  font-weight:600;border-bottom:1px solid var(--line)}
.lb .rw{border-radius:10px;transition:background .15s ease}
.lb .rw:hover{background:var(--panel2)}
.lb .rk{color:var(--mut);text-align:center;font-variant-numeric:tabular-nums}
.lb .nm{font-weight:600;display:flex;align-items:center;gap:12px;min-width:0}
.lb a.rw:hover,.relrow:hover,.collcard:hover,a.savebtn:hover{text-decoration:none}
.lb .nm small{color:var(--mut);font-weight:400}
.lb .mono{text-align:right}
/* sits under the donut, which otherwise left the card half-empty next to the taller
   houses card. Separated by a rule so it reads as a second fact, not donut chrome. */
.moodblk{margin-top:18px;padding-top:12px;border-top:1px solid var(--line)}
/* inside a stretched pair the auto margin eats the slack, pinning the strip to the card
   floor so the extra height goes between the donut and the strip rather than below it */
.grid2.even .moodblk{margin-top:auto}
.moodfoot{display:flex;justify-content:space-between;align-items:baseline;margin-top:2px}
.trend{font-size:12.5px;font-variant-numeric:tabular-nums;font-weight:600}
.trend.up{color:var(--teal)}.trend.down{color:var(--red)}.trend.flat{color:var(--mut)}
table.data{width:100%;border-collapse:collapse;font-size:13.5px}
table.data th,table.data td{text-align:left;padding:10px 9px;border-bottom:1px solid var(--line);vertical-align:top}
table.data th{color:var(--mut);font-weight:600;font-size:11.5px;text-transform:uppercase;letter-spacing:.06em}
table.data tr:hover td{background:var(--panel2)}

/* term and tag pills */
.pills{display:flex;flex-wrap:wrap;gap:8px}
.pill{padding:7px 13px;border:1px solid var(--line);border-radius:999px;background:var(--panel2);
  font-size:13.5px;transition:border-color .16s ease,background .16s ease}
.pill:hover{text-decoration:none;border-color:var(--teal);background:var(--teal-wash)}
.pill b{color:var(--teal);margin-left:7px;font-variant-numeric:tabular-nums}
.pill.sm{padding:5px 10px;font-size:12.5px;min-height:0}

/* bottom nav (mobile) */
/* No backdrop-filter here, deliberately. A filtered element becomes the containing block
   for position:fixed descendants, which would trap the More sheet inside the 62px bar. The
   bar was 96% opaque anyway, so the blur was doing almost nothing; it is a flat surface now
   and the sheet can cover the screen. */
nav.bottom{position:fixed;left:0;right:0;bottom:0;z-index:50;display:flex;
  background:#02181f;border-top:1px solid var(--line);padding-bottom:env(safe-area-inset-bottom);
  height:calc(var(--nav-h) + env(safe-area-inset-bottom))}
/* Direct children only. The sheet below hangs inside this bar in the DOM, and an
   unscoped nav.bottom a selector turned its rows into little stacked tab buttons. */
nav.bottom>a,nav.bottom .bmore>summary{position:relative;flex:1;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:4px;color:var(--mut);font-size:10.5px;font-weight:500;
  min-height:44px;text-decoration:none;padding:7px 2px 0;transition:color .16s ease}
nav.bottom>a.active,nav.bottom .bmore.on>summary,nav.bottom .bmore[open]>summary{color:var(--teal)}
nav.bottom>a.active::before,nav.bottom .bmore.on>summary::before{content:"";position:absolute;top:0;
  left:50%;transform:translateX(-50%);width:26px;height:3px;border-radius:0 0 3px 3px;background:var(--teal)}
/* ── the phone's "everything else" ────────────────────────────────────────
   Five slots and nine destinations. Events, Review, Compare and (for an admin) Glossary and
   Admin had no route at all on a phone — they were top-bar-only, and the top bar is hidden
   here. The fifth slot is a sheet that lists them by name, so every page in the archive is
   two taps away and each row says what it is. Sign out moved in with them: it was a bare
   door icon in the header, which is exactly the "guess what this does" problem. */
nav.bottom .bmore{flex:1;display:flex;position:static}
nav.bottom .bmore>summary,nav.bottom .bmore[open]>summary,
nav.bottom .bmore>summary:hover{border-radius:0;background:none;font-weight:500}
.bpanel{position:fixed;left:0;right:0;top:0;z-index:55;display:flex;align-items:flex-end;
  bottom:calc(var(--nav-h) + env(safe-area-inset-bottom));
  background:rgba(1,10,14,.62);animation:bfade .16s ease-out}
.bsheet{width:100%;max-height:72vh;overflow:auto;padding:8px 8px 14px;background:var(--panel);
  border-top:1px solid var(--line2);border-radius:18px 18px 0 0;
  box-shadow:0 -14px 34px -18px rgba(0,0,0,.85);animation:brise .2s cubic-bezier(.2,.8,.3,1)}
.bsheet .mhead{padding:10px 12px 6px;font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--mut)}
.bsheet .msep{height:1px;margin:6px 4px;background:var(--line)}
.bsheet form{margin:0}
.bsheet a,.bsheet button{display:flex;align-items:center;gap:13px;width:100%;min-height:50px;
  padding:12px 12px;border-radius:12px;border:0;background:none;color:var(--fg2);
  font-size:15.5px;font-weight:500;text-align:left;letter-spacing:0}
.bsheet a:hover,.bsheet button:hover{background:var(--panel2);color:var(--fg);
  text-decoration:none;box-shadow:none;transform:none}
.bsheet a.active{background:var(--teal-wash);color:var(--teal)}
.bsheet button.signout{color:var(--red)}
@keyframes bfade{from{opacity:0}to{opacity:1}}
@keyframes brise{from{transform:translateY(16px)}to{transform:none}}
.notice{background:rgba(13,213,178,.12);border:1px solid var(--teal-lo);color:#7fecd7;
  padding:11px 13px;border-radius:var(--r-sm);margin-bottom:12px}
.err{background:rgba(238,80,80,.12);border:1px solid #7a2f2f;color:#ffb0b0;
  padding:11px 13px;border-radius:var(--r-sm);margin-bottom:12px}
/* A signpost, not a card: where something that used to be on this page went. */
.crosslink{display:flex;align-items:center;gap:11px;margin-bottom:14px;padding:12px 14px;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel);
  color:var(--fg2);font-size:13.5px;line-height:1.5;
  transition:border-color .16s ease,background .16s ease}
.crosslink:hover{border-color:var(--teal-lo);background:var(--panel2);text-decoration:none}
.crosslink b{color:var(--fg)}
.crosslink svg{flex:0 0 auto;color:var(--teal)}
.crosslink svg:last-child{margin-left:auto;color:var(--mut);transform:rotate(-90deg)}
.pager{display:flex;justify-content:space-between;margin-top:16px;gap:10px}
.back{display:inline-flex;align-items:center;gap:6px;color:var(--mut);font-size:13.5px;margin-bottom:12px}
.back:hover{color:var(--teal)}
.legend{display:flex;gap:15px;flex-wrap:wrap;font-size:12px;color:var(--mut);margin-top:10px}
.legend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:6px;vertical-align:middle}
.empty{color:var(--mut);text-align:center;padding:32px 12px}
/* The summary's 32px tap target hides ~7px of leading under its text, so the bottom
   padding is short of the top one on purpose — the two read as equal. */
.replygap{text-align:center;padding:22px 14px 28px;color:var(--mut)}
.replygap .rgh{font-size:14.5px;color:var(--fg);opacity:.75}
.replygap .rgs{font-size:12.5px;margin-top:5px}
.rgw{margin-top:9px;font-size:12.5px}
.rgw summary{cursor:pointer;display:inline-block;min-height:32px;line-height:32px;padding:0 4px;
  color:var(--mut);list-style:none;text-decoration:underline dotted;text-underline-offset:4px;
  transition:color .16s ease}
.rgw summary:hover{color:var(--fg)}
.rgw summary::-webkit-details-marker{display:none}
.rgw p{max-width:52ch;margin:2px auto 0;line-height:1.55}
.logbox{max-height:360px;overflow:auto;background:var(--bg);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:12px 14px;font-size:12px;line-height:1.7}
.logline{white-space:pre-wrap;word-break:break-word}
.logline .mut{margin-right:5px}
.hdacts{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
.rpills{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}
.rpill{display:inline-block;padding:4px 11px;border:1px solid var(--line);border-radius:999px;
  font-size:11.5px;color:var(--mut);margin-left:6px;min-height:0;
  transition:color .16s ease,border-color .16s ease}
.rpill:hover{color:var(--fg);text-decoration:none}
.rpill.on{background:var(--teal-wash);color:var(--teal);border-color:var(--teal-lo)}

@media (min-width:760px){
  .tiles{grid-template-columns:repeat(4,1fr)}
  .collgrid{grid-template-columns:1fr 1fr}
  .grid2{grid-template-columns:1fr 1fr}
  header.top nav.mainnav{display:flex}
  nav.bottom{display:none}
  body{padding-bottom:0}
  .filters{grid-template-columns:1fr;}
}
/* Keep the root pseudo-element alive. Removing it resets Firefox's viewport to 0. */
html::after{content:"";position:fixed;top:0;left:0;height:3px;width:40%;opacity:0;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--teal),transparent);z-index:9999;
  animation:navbar 1.1s ease-in-out infinite;animation-play-state:paused}
html.navigating::after{opacity:1;animation-play-state:running}
@keyframes navbar{0%{transform:translateX(-100%)}100%{transform:translateX(350%)}}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}
  body::before{display:none}
  html.navigating::after{animation:none;width:100%;opacity:.6}}

/* ───────────────────────── phone layer ─────────────────────────
   The dashboard doubles as a private read-only Yik Yak on a phone, so below the
   breakpoint the feed is treated as the primary surface: bigger body copy, real
   tap targets on every inline link, and charts that scroll instead of squashing. */
@media (max-width:759px){
  .wrap{padding:14px 12px 26px}
  h1{font-size:22px}
  .card{border-radius:14px;padding:14px 14px}
  .tiles{gap:9px}
  .tile{padding:13px 14px}
  .tile .v{font-size:24px}
  header.top{padding:9px 12px;gap:10px}
  header.top .brand{min-height:44px}

  /* yak-like reading: roomier text, tighter chrome */
  .entry .text{font-size:16.5px;line-height:1.62}
  .entry .meta{gap:8px 10px;font-size:12.5px}

  /* every inline link is a finger target, not a mouse target */
  .chip{min-height:34px;padding:7px 12px;font-size:12px}
  .savebtn{min-height:44px;min-width:44px;padding:10px}
  .entry .meta a,.entry .meta .chip{min-height:38px}

  /* the score and citations drop under the sentence rather than squeezing it into a column
     two words wide -- at 390px there is no room to sit them side by side */
  .evlist li{gap:4px 9px}
  .evtext{flex:1 1 100%}
  .evmeta{margin-left:14px}
  a.sm{min-height:40px;display:inline-flex;align-items:center;padding:0 4px}
  .btn.sm{min-height:40px;padding:8px 14px;display:inline-flex;align-items:center;justify-content:center}
  .rpill{min-height:36px;padding:8px 13px;display:inline-flex;align-items:center;margin-left:0}
  .pill{min-height:40px;padding:9px 14px}
  .lb .hd,.lb .rw{padding:12px 6px}
  /* 32+84+96px of fixed columns plus gaps left the name ~130px on a 390px phone, so
     "Sigma Alpha Epsilon" broke onto three lines and its meta line onto four. Drop to
     rank | name | activity and move sentiment to its own row beneath, where it has the
     full width and can sit the bar and its number side by side. */
  .lb .hd{display:none}
  .lb .rw{grid-template-columns:26px minmax(0,1fr) auto;gap:9px;row-gap:5px;align-items:start}
  .lb .rw>span:nth-child(3){align-self:center}
  .lb .rw>span:nth-child(4){grid-column:2 / -1;display:flex;align-items:center;gap:9px;text-align:left!important}
  .lb .rw>span:nth-child(4)>div{margin:0}
  .lb .nm small{display:block;margin-top:2px;font-size:11px;letter-spacing:-.01em}
  .lb .nm{gap:9px}
  .pager{gap:10px}
  .pager .btn{flex:1;display:inline-flex;align-items:center;justify-content:center}

  /* filters: two controls per row, full-width search, thumb-reachable submit */
  .filters{gap:8px}
  .filters .line>*{min-width:calc(50% - 5px)}
  .filters button[type=submit]{min-width:100%}

  /* wide charts scroll horizontally rather than compressing into mush */
  .card>div[style*="overflow-x"]{-webkit-overflow-scrolling:touch;scrollbar-width:thin}
  /* 24 columns cannot fit a phone: give the heatmap room so cells stay tappable */
  svg.heatmap{min-width:620px}
  table.data{min-width:520px}
  .logbox{font-size:11.5px;max-height:300px}
}
@media (max-width:389px){
  .tiles{grid-template-columns:1fr}
  .filters .line>*{min-width:100%}
}
