/* Lumen — "Lucid Field" visual system.
   Signal drawn out of noise: a calm near-white field, ink for trust,
   one luminous gold accent, semantic colours used only as punctuation. */

:root {
  --ink: #15171c;
  --mist: #f4f5f2;
  --paper: #ffffff;
  --paper-top: #ffffff;         /* topmost radial in body gradient */
  --lumen: #f4b400;
  --lumen-hi: #ffd363;
  --lumen-warm: rgba(255, 211, 99, 0.20);   /* accent halo for body bg */
  --wordmark-glow: rgba(244, 180, 0, 0.38); /* soft aura around Lumen wordmark + brand rules */
  --sunrise: #ff7a59;   /* good news */
  --sunrise-bg: #fff4ee;
  --tide: #10b5a4;      /* events / insider */
  --tide-bg: #e9faf7;
  --clear: #3fa7d6;     /* weather */
  --clear-bg: #e7f5fc;
  --history: #6b5bd2;
  --history-bg: #f1effb;
  --markets: #2f9e6f;
  --markets-bg: #ecf8f2;
  --fact: #c2185b;      /* did you know */
  --fact-bg: #fdeef4;
  --word: #96600f;      /* word of the day — darkened: #b06f14 gave the white
                           badge text only 4.09:1, under the 4.5:1 AA floor */
  --word-bg: #fdf3e4;
  --blurb-ink: #3b3f47;
  --track-bg: #e8e9e4;   /* poll bar track */
  --wday-bg: rgba(255, 255, 255, 0.6);
  --wday-border: #d4eaf6;
  --muted: #8a8f98;
  --hair: #d3d6d1;
  --soft: #eeefec;   /* subtle gray fill for tabs + form inputs */
  --emphasis-bg: #15171c;  /* pressed/active fill — inverts to warm amber on dark */
  --emphasis-fg: #ffffff;
  --shadow: 0 14px 34px rgba(20, 24, 30, 0.10);
  --radius: 24px;

  /* Type scale multiplier — overridden by [data-font-scale="l"|"xl"]. Base
     ratcheted up so the default UI already reads comfortably; l/xl grow from
     the same shifted baseline. */
  --scale: 1.08;

  --f-display: "Italiana", Georgia, serif;
  --f-head: "Bricolage Grotesque", system-ui, sans-serif;
  --f-body: "Instrument Sans", system-ui, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, monospace;
}

/* Dark mode — "Auric Hush": deep ink base, warm off-white text, gold kept as
   the sole warm accent. Palette matches design/lumen-styleguide-dark.png. */
:root[data-theme="dark"] {
  --ink: #eaecea;
  --mist: #0f1116;
  --paper: #191c22;
  --paper-top: #171a20;
  --lumen: #d9a01f;                          /* muted amber — less butter-neon on dark */
  --lumen-hi: #e6b449;
  --lumen-warm: rgba(217, 160, 31, 0.05);
  --wordmark-glow: rgba(217, 160, 31, 0.22); /* dimmed aura around the wordmark on dark */
  --sunrise: #dd8570;                        /* dusty coral instead of bright orange */
  --sunrise-bg: #2b2117;   /* lifted from #1f1a15 — warm brown, still deep */
  --tide: #2bc6b6;
  --tide-bg: #12201e;
  --clear: #57b8e0;
  --clear-bg: #141a20;
  --history: #8e7be0;
  --history-bg: #1b1622;
  --markets: #4cb884;
  --markets-bg: #12201a;
  --fact: #ee7fa8;
  --fact-bg: #23161c;
  --word: #dda94f;
  --word-bg: #221a10;
  --blurb-ink: #c8cacc;
  --track-bg: #2a2f36;
  --wday-bg: rgba(255, 255, 255, 0.03);
  --wday-border: #1e2a34;
  --muted: #8b8e90;
  --hair: #2a2d35;
  --soft: #12151b;   /* darker than --paper — inputs + inactive tabs feel inset */
  --emphasis-bg: #a4956f;  /* Parchment dimmed 30% — warm off-white for Pin/Add/1D/toggles */
  --emphasis-fg: #15171c;  /* dark ink text still contrasts cleanly on the parchment fill */
  --shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
}

:root[data-font-scale="l"]  { --scale: 1.21; }
:root[data-font-scale="xl"] { --scale: 1.35; }

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--f-body);
  color: var(--ink);
  min-height: 100vh;
  background:
    radial-gradient(900px 420px at 82% -8%, var(--lumen-warm), transparent 60%),
    radial-gradient(1100px 600px at 50% -12%, var(--paper-top), var(--mist));
  display: flex;
  flex-direction: column;
  align-items: center;
  -webkit-tap-highlight-color: transparent;
}

/* ---- responsive layout: single column (mobile) / 3 columns (desktop) ----
   On desktop the layout becomes a grid: stats | feed | pins, with the brand
   banner centered over the feed and the sidebars occupying the SAME row as the
   cards (so they match the deck's height and y-position). */
.layout {
  width: 100%; max-width: 600px; margin: 0 auto; padding: 0 20px 24px;
  display: flex; flex-direction: column;
}
.sidebar { display: none; }          /* hidden on phones */
.tracked-col { display: none; }      /* hidden until wide desktop */
.hint-desk { display: none; }
.hint-touch { display: inline; }

/* ---- top bar ---- */
.topbar {
  width: 100%;
  display: flex; flex-direction: column; align-items: stretch;
  gap: 10px;
  padding: 16px 0 8px;
}
.brand {
  text-align: center;
}
/* Wordmark row: one thin horizontal line on each side, running through the
   vertical center of "✦ Lumen" and stopping short of it with a breath of
   space. The rules share the ink color and carry the same soft gold aura the
   .logo has, so the whole row reads as one luminous mark. Full container
   width, matching the select-row below. */
.logo-row {
  display: flex; align-items: center; gap: 20px;
  width: 100%;
}
.brand-rule {
  flex: 1;
  height: 1px;
  background: var(--ink);
  border-radius: 1px;
  box-shadow: 0 0 12px var(--wordmark-glow);
  opacity: 0.7;
}
.brand .logo {
  font-family: var(--f-display);
  font-size: calc(40px * var(--scale)); line-height: 1; color: var(--ink); letter-spacing: 0.5px;
  text-shadow: 0 0 24px var(--wordmark-glow);
}
/* The wordmark is a link home (News). It wraps the .logo, which keeps all its
   own styling; this just makes the anchor an invisible, clickable shell. */
.logo-link { text-decoration: none; color: inherit; display: inline-flex; cursor: pointer; }
.logo-link:focus-visible { outline: 2px solid var(--lumen); outline-offset: 4px; border-radius: 6px; }
.brand .tagline {
  font-family: var(--f-mono);
  font-size: calc(11px * var(--scale)); font-weight: 400; color: var(--muted);
  letter-spacing: 2.5px; text-transform: uppercase; margin-top: 6px;
}
.topbar-controls {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 8px;
}

/* ---- user chip + dropdown in the header ---- */
.user-menu { position: relative; margin-right: 8px; display: inline-flex; align-items: center; gap: 6px; }
.user-chip {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 0.3px;
  padding: 6px 11px 6px 8px; border-radius: 999px; cursor: pointer;
  /* A display name may be up to 40 characters (users.MAX_DISPLAY), and this chip
     is an inline-flex that sizes to its text — so a long name grew it past the
     screen edge and gave the whole page a horizontal scrollbar on mobile. Cap it
     and ellipsise instead: the name is an identifier here, not a thing to read. */
  max-width: min(46vw, 260px); min-width: 0;
}
.user-chip #userName {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* The dot and caret must keep their size while the NAME is what gives way. */
.user-chip .user-dot, .user-chip .user-caret { flex: none; }
.user-chip:hover { border-color: var(--lumen); }
/* Used on both <button> (Options) and <a> (Friends, back-links). An anchor picks
   up the UA's underline unless we kill it here — that made the Friends chip read
   as a link instead of a peer of Options and the account chip. */
.chip-btn {
  display: inline-flex; align-items: center;
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 0.3px;
  padding: 6px 12px; border-radius: 999px; cursor: pointer;
  text-decoration: none; white-space: nowrap;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.chip-btn:hover { border-color: var(--lumen); color: var(--lumen); }
.theme-btn {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--hair); background: var(--paper); color: var(--muted);
  width: 36px; height: 36px; padding: 0; border-radius: 999px; cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.theme-btn:hover { border-color: var(--lumen); color: var(--lumen); }
.theme-btn:active { transform: scale(0.94); }
.theme-icon { width: 18px; height: 18px; display: block; }
/* Light mode shows the moon (target = dark); dark mode shows the sun. */
.theme-icon-sun  { display: none; }
:root[data-theme="dark"] .theme-icon-sun  { display: block; color: var(--lumen); }
:root[data-theme="dark"] .theme-icon-moon { display: none; }
.user-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--lumen); box-shadow: 0 0 6px rgba(244,180,0,0.55); }
.user-caret { font-size: calc(9px * var(--scale)); color: var(--muted); margin-left: 1px; }
.user-drop {
  position: absolute; right: 0; top: calc(100% + 6px);
  background: var(--paper); border-radius: 12px; box-shadow: var(--shadow);
  min-width: 120px; padding: 6px; z-index: 40;
  animation: pop 0.18s ease;
}
/* ---- avatars ------------------------------------------------------------
   Every person-slot draws either an SVG from static/img/avatars.svg or the old
   letter initial. `.av` sizes to its container so the same markup works at 72px
   on a profile and 36px on a post. The sprite's colours are baked in (a <use>
   from an external file inherits no cascade), so no theming here. */
.av { display: block; width: 100%; height: 100%; border-radius: 50%; }
.av-initial { display: grid; place-items: center; width: 100%; height: 100%; }

.av-picker {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 10px; margin-top: 4px;
}
.av-tile {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 10px 6px; border-radius: 14px; cursor: pointer;
  border: 2px solid transparent; background: transparent;
}
.av-tile > .av, .av-tile > .av-initial {
  width: 56px; height: 56px; flex: none;
  background: var(--soft); color: var(--ink);
  font-family: var(--f-head); font-weight: 800; font-size: 22px;
  border-radius: 50%;
}
.av-tile:hover { background: var(--soft); }
.av-tile.on { border-color: var(--lumen); background: var(--soft); }
.av-tile.on .av-cap { color: var(--ink); font-weight: 700; }
.av-cap {
  font-family: var(--f-mono); font-size: calc(9.5px * var(--scale));
  line-height: 1.3; text-align: center; color: var(--blurb-ink);
}
.av-tile:focus-visible { outline: 2px solid var(--lumen); outline-offset: 2px; }

/* Incoming friend requests, pinned under the account chip. Default display:none
   plus a .show class — NOT the `hidden` attribute, which loses the cascade tie
   to any later `display:` rule (see CLAUDE.md; that trap has bitten four times). */
.req-notice {
  display: none;
  position: absolute; right: 0; top: calc(100% + 6px);
  width: 250px; padding: 11px 12px; z-index: 39;
  background: var(--paper); border-radius: 12px; box-shadow: var(--shadow);
  border-left: 4px solid var(--lumen);
  text-align: left; animation: pop 0.18s ease;
}
.req-notice.show { display: block; }
/* --blurb-ink, not --muted: both measured 3.25:1 on --paper, under the 4.5:1 AA
   floor for text this small. This notice is the only place a friend request is
   surfaced, so it has to be readable. */
.req-title {
  font-family: var(--f-mono); font-size: calc(9.5px * var(--scale));
  letter-spacing: 1.2px; text-transform: uppercase; color: var(--blurb-ink); margin-bottom: 9px;
}
.req-row { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.req-row:last-child { margin-bottom: 0; }
.req-av {
  width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center;
  background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-head); font-weight: 800; font-size: 11px;
}
.req-who { flex: 1; min-width: 0; }
.req-name {
  font-family: var(--f-head); font-weight: 700; font-size: calc(12px * var(--scale));
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.req-sub { font-family: var(--f-body); font-size: calc(10.5px * var(--scale)); color: var(--blurb-ink); }
.req-acts { display: flex; gap: 5px; flex-shrink: 0; }
.req-btn {
  border: 1px solid var(--hair); background: var(--soft); color: var(--ink); cursor: pointer;
  font-family: var(--f-mono); font-weight: 700; font-size: calc(9.5px * var(--scale));
  padding: 5px 8px; border-radius: 999px;
}
.req-btn.primary { background: var(--emphasis-bg); color: var(--emphasis-fg); border-color: var(--emphasis-bg); }
.req-btn:disabled { opacity: 0.5; cursor: default; }

/* On desktop the account chip sits in the fixed top-right corner, so a 250px
   panel hangs into empty space — fine. On the wrapped mobile topbar the chip is
   mid-row, and that same panel (anchored right:0 to the chip) spilled left under
   the Feed chip. Narrowing it pulls its left edge right, clear of Feed; the
   actions drop to their own line so the narrower width still holds two buttons.
   Scoped to .req-notice so the feed's Friends-column request rows are untouched. */
@media (max-width: 1023.98px) {
  .req-notice { width: 168px; padding: 9px 10px; }
  .req-notice .req-title { margin-bottom: 7px; }
  .req-notice .req-row { flex-wrap: wrap; gap: 7px; }
  .req-notice .req-acts { width: 100%; margin-top: 2px; }
  .req-notice .req-btn { flex: 1; padding: 6px 4px; }
}

.user-drop-item {
  display: block; width: 100%; text-align: left; border: none; background: transparent;
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
  font-family: var(--f-body); font-weight: 600; font-size: calc(13px * var(--scale)); color: var(--ink);
  text-decoration: none;
}
.user-drop-item:hover { background: var(--mist); }
.user-drop-item.danger { color: #d6483b; }
.user-drop-item.danger:hover { background: rgba(214, 72, 59, 0.09); }

/* ---- auth (login/register) page ---- */
.auth-body {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 32px 20px;
}
.auth-shell { width: 100%; max-width: 380px; }
.auth-brand { text-align: center; margin-bottom: 22px; position: relative; }
.auth-spark {
  width: 42px; height: 42px; margin: 0 auto 8px; color: var(--lumen);
  filter: drop-shadow(0 0 18px rgba(244,180,0,0.55));
}
.auth-spark svg { width: 100%; height: 100%; display: block; }
.auth-logo {
  font-family: var(--f-display); font-size: calc(44px * var(--scale)); line-height: 1; color: var(--ink);
  letter-spacing: 0.3px;
  text-shadow: 0 0 22px rgba(244, 180, 0, 0.28);
}
.auth-tagline {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale)); letter-spacing: 2.5px;
  text-transform: uppercase; color: var(--muted); margin-top: 8px;
}
.auth-card {
  background: var(--paper); border-radius: 22px; padding: 22px 22px 18px;
  box-shadow: var(--shadow);
}
.auth-tabs {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
  background: var(--mist); border-radius: 12px; padding: 4px; margin-bottom: 16px;
}
.auth-tab {
  border: none; background: transparent; color: var(--muted);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 1px;
  text-transform: uppercase; padding: 8px 0; border-radius: 9px; cursor: pointer;
}
.auth-tab.active { background: var(--paper); color: var(--ink); box-shadow: 0 1px 4px rgba(20,24,30,0.08); }
.auth-form { display: flex; flex-direction: column; gap: 12px; }
.auth-form.hidden { display: none; }
.auth-field { display: flex; flex-direction: column; gap: 5px; }
.auth-field > span {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale)); letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--muted);
}
.auth-field input {
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  font-family: var(--f-body); font-size: calc(14px * var(--scale)); padding: 10px 12px; border-radius: 10px;
  outline: none;
}
.auth-field input:focus { border-color: var(--lumen); box-shadow: 0 0 0 3px rgba(244,180,0,0.18); }
.auth-error { min-height: 14px; font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: #d6483b; }
.auth-error:empty { min-height: 0; }
.auth-remember {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--f-body); font-size: calc(12.5px * var(--scale)); color: var(--muted);
  margin-top: 2px; cursor: pointer; user-select: none;
}
.auth-remember input { accent-color: var(--lumen); width: 15px; height: 15px; cursor: pointer; }
.auth-remember:hover { color: var(--ink); }
.auth-lang {
  border: 1px solid var(--hair); border-radius: 10px; padding: 8px 12px 10px;
  display: flex; flex-wrap: wrap; gap: 16px;
  font-family: var(--f-body); font-size: calc(12.5px * var(--scale)); color: var(--muted);
  margin-top: 2px;
}
.auth-lang legend {
  padding: 0 6px; font-family: var(--f-mono); font-size: calc(10.5px * var(--scale));
  letter-spacing: 1.5px; text-transform: uppercase; color: var(--muted);
}
.auth-lang label {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  color: var(--ink);
}
.auth-lang input { accent-color: var(--lumen); cursor: pointer; }
.auth-submit {
  border: none; background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(13px * var(--scale)); letter-spacing: 1px;
  text-transform: uppercase; padding: 12px; border-radius: 12px; cursor: pointer;
  margin-top: 4px; transition: transform 0.08s ease;
}
.auth-submit:hover { background: var(--lumen); color: var(--ink); }
.auth-submit:active { transform: scale(0.98); }
/* Error pages (404 / 500). Calm, not alarming: a broken page is our problem, not
   something to shout at the reader about. The big number is --hair, almost a
   watermark — the apology is the message, the code is just a fact. */
.error-page { text-align: center; padding: 40px 20px 60px; }
.error-code {
  font-family: var(--f-head); font-weight: 800; line-height: 1;
  font-size: calc(84px * var(--scale)); color: var(--hair);
  letter-spacing: 2px;
}
.error-title {
  font-family: var(--f-head); font-weight: 800; color: var(--ink);
  font-size: calc(24px * var(--scale)); margin: 10px 0 8px;
}
.error-msg {
  font-family: var(--f-body); font-size: calc(14px * var(--scale));
  color: var(--blurb-ink); line-height: 1.6; margin: 0 auto 22px;
  max-width: 34em;
}
.error-incident {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale));
  color: var(--muted); margin: 0 0 22px;
}
.error-incident code {
  background: var(--soft); border: 1px solid var(--hair); border-radius: 6px;
  padding: 2px 7px; color: var(--blurb-ink); user-select: all;
}
.error-back { display: inline-block; }

/* Headings on the forgot / reset pages, which have no tab strip to title them. */
.auth-heading {
  font-family: var(--f-head); font-weight: 800; font-size: calc(20px * var(--scale));
  color: var(--ink); margin: 0 0 8px; text-align: center; line-height: 1.25;
}
.auth-intro {
  font-family: var(--f-body); font-size: calc(12.5px * var(--scale));
  color: var(--blurb-ink); line-height: 1.55; margin: 0 0 18px; text-align: center;
}
.auth-foot {
  font-family: var(--f-body); font-size: calc(11.5px * var(--scale)); color: var(--muted);
  text-align: center; margin-top: 16px; line-height: 1.6;
}
.auth-forgot {
  color: var(--muted); text-decoration: none; border-bottom: 1px dotted var(--hair);
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); letter-spacing: 0.5px;
}
.auth-forgot:hover { color: var(--ink); border-bottom-color: var(--lumen); }
.auth-help {
  margin-top: 12px; padding: 10px 12px; background: var(--mist); border-radius: 10px;
  font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--ink); line-height: 1.5;
  text-align: left;
}
.auth-help code {
  display: inline-block; margin: 4px 0; padding: 2px 6px; background: var(--paper);
  border: 1px solid var(--hair); border-radius: 6px; font-family: var(--f-mono); font-size: calc(11.5px * var(--scale));
}

/* ---- post-login greeting overlay (philosophy: First Light) ---- */
.greeting {
  position: fixed; inset: 0; display: none; align-items: center; justify-content: center;
  background: rgba(244, 245, 242, 0.92);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  z-index: 100; cursor: pointer;
}
.greeting.show { display: flex; animation: greetFade 0.6s ease; }
.greeting.show.leaving { animation: greetFadeOut 0.5s ease forwards; }
@keyframes greetFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes greetFadeOut { to { opacity: 0; } }

.greet-frame {
  width: 100%; max-width: 480px; padding: 40px 30px; text-align: center;
  position: relative;
}
/* radial warm halo from the top */
.greet-frame::before {
  content: ""; position: absolute; left: 50%; top: -10%;
  width: 480px; height: 320px; transform: translateX(-50%);
  background: radial-gradient(closest-side, rgba(255,211,99,0.55), rgba(255,211,99,0) 70%);
  pointer-events: none; z-index: 0;
}
.greet-spark {
  position: relative; z-index: 1;
  width: 56px; height: 56px; margin: 0 auto 10px; color: var(--lumen);
  filter: drop-shadow(0 0 22px rgba(244,180,0,0.65));
  opacity: 0; animation: greetSpark 0.7s ease 0.05s forwards;
}
.greet-spark svg { width: 100%; height: 100%; display: block; }
@keyframes greetSpark {
  from { opacity: 0; transform: scale(0.6); }
  to   { opacity: 1; transform: scale(1); }
}

.greet-cue {
  position: relative; z-index: 1;
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); letter-spacing: 4px;
  text-transform: uppercase; color: var(--muted); margin-bottom: 10px;
  opacity: 0; animation: greetSpark 0.6s ease 0.3s forwards;
}

.greet-hello {
  position: relative; z-index: 1;
  font-family: var(--f-display); font-size: calc(46px * var(--scale)); line-height: 1.1; color: var(--ink);
  letter-spacing: 0.2px; margin: 4px 0 14px;
  opacity: 0; transform: translateY(14px);
  animation: greetRise 0.7s ease 0.35s forwards;
}
.greet-hello span { color: var(--ink); }
@keyframes greetRise {
  to { opacity: 1; transform: none; }
}

.greet-whisper {
  position: relative; z-index: 1;
  font-family: "Caveat", "Brush Script MT", Georgia, serif; font-size: calc(26px * var(--scale)); line-height: 1.2; color: var(--blurb-ink);
  opacity: 0; animation: greetSpark 0.9s ease 0.85s forwards;
}

.greet-tap {
  position: relative; z-index: 1;
  margin-top: 26px;
  font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); letter-spacing: 2.5px;
  text-transform: uppercase; color: var(--muted);
  opacity: 0; animation: greetSpark 0.9s ease 1.5s forwards;
}

@media (max-width: 480px) {
  .greet-hello { font-size: calc(36px * var(--scale)); }
  .greet-whisper { font-size: calc(22px * var(--scale)); }
}

/* App controls (theme + options): inline on mobile, fixed top-LEFT on
   desktop — mirror of the user chip's top-right position. Separated from
   .user-menu so the user-drop dropdown no longer opens under Options. */
.app-controls {
  display: inline-flex; align-items: center; gap: 6px;
}

/* On desktop the streak lives in the sidebar; the user chip is fixed to the
   top-right of the viewport and the app controls to the top-left, so
   neither ever crowds the centered brand + tagline. */
@media (min-width: 1024px) {
  .topbar .user-menu {
    position: fixed;
    top: 20px;
    right: 24px;
    margin: 0;
    z-index: 30;
  }
  .topbar .app-controls {
    position: fixed;
    top: 20px;
    left: 24px;
    z-index: 30;
  }
}

/* ---- streak emblem: progress ring + lumen spark + number ---- */
.streak { position: relative; width: 50px; height: 50px; }
.ring { position: absolute; inset: 0; transform: rotate(-90deg); }
.ring-bg { fill: none; stroke: #e4e6e1; stroke-width: 4; }
.ring-fg {
  fill: none; stroke: var(--lumen); stroke-width: 4; stroke-linecap: round;
  stroke-dasharray: 131.9; stroke-dashoffset: 131.9;
  transition: stroke-dashoffset 0.6s ease;
  filter: drop-shadow(0 0 4px rgba(244, 180, 0, 0.55));
}
.spark { position: absolute; inset: 0; }
.spark path {
  fill: var(--lumen);
  filter: drop-shadow(0 0 7px rgba(244, 180, 0, 0.5));
}
.streak-num {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--f-head); font-weight: 800; font-size: calc(15px * var(--scale));
  color: var(--ink); padding-bottom: 1px;
}

/* ---- scope selectors ---- */
.scopes {
  width: 100%; padding: 6px 0 2px;
  display: flex; flex-direction: column; gap: 12px;
}
.select-row { display: flex; align-items: center; gap: 10px; }
.select { display: flex; align-items: center; gap: 10px; flex: 1; }
.gear {
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); font-weight: 700; letter-spacing: 0.5px;
  padding: 9px 13px; border-radius: 12px; cursor: pointer; white-space: nowrap;
}
.gear:active { transform: scale(0.96); }
.select span {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale)); letter-spacing: 2px;
  text-transform: uppercase; color: var(--muted);
}
.select select {
  flex: 1; padding: 9px 12px; border-radius: 12px; border: 1px solid var(--hair);
  background: var(--paper); color: var(--ink);
  font-family: var(--f-body); font-size: calc(14px * var(--scale)); font-weight: 600;
}
.tabs { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; justify-content: center; }
.tab {
  white-space: nowrap; border: 1px solid var(--hair); background: var(--soft);
  color: var(--muted); font-family: var(--f-mono); font-weight: 500;
  font-size: calc(12px * var(--scale)); letter-spacing: 0.5px; padding: 8px 14px; border-radius: 999px;
  cursor: pointer; transition: transform 0.08s ease, background 0.15s ease;
}
.tab:active { transform: scale(0.95); }
.tab.active { background: var(--emphasis-bg); color: var(--emphasis-fg); border-color: var(--emphasis-bg); }

/* ---- feed ---- */
.daily { width: 100%; margin: 8px 0 16px; }
.vac-badge {
  display: block; text-align: center; margin: 0 auto 10px;
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); font-weight: 700;
  letter-spacing: 0.6px; color: var(--ink);
  background: var(--lumen-warm, rgba(244, 180, 0, 0.16)); border: 1px solid var(--lumen);
  border-radius: 999px; padding: 5px 14px; width: fit-content;
}
.vac-badge[hidden] { display: none; }
.daily-bar { position: relative; height: 9px; background: var(--hair); border-radius: 999px; overflow: hidden; }
/* Both fills anchor to the left of the track and stack; the orange is second in
   the DOM so it paints over the gray. --muted reads as a distinct mid-gray
   against both the pale track and the orange, in light and dark. */
.daily-max, .daily-fill {
  position: absolute; left: 0; top: 0; height: 100%; width: 0%;
  border-radius: 999px; transition: width 0.45s ease;
}
.daily-max { background: var(--muted); }
.daily-fill { background: linear-gradient(90deg, var(--lumen), var(--sunrise)); }
.daily-label {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); font-weight: 500; letter-spacing: 1px;
  text-transform: uppercase; color: var(--muted); margin-top: 8px; text-align: center;
}

.deck { position: relative; flex: 1; min-height: 400px; }

.card {
  position: absolute; inset: 0; background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 24px; display: flex; flex-direction: column;
  overflow: hidden; animation: pop 0.3s ease; will-change: transform, opacity;
}
@keyframes pop { from { transform: scale(0.97) translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
.card.leaving { animation: leave 0.25s ease forwards; }
@keyframes leave { to { transform: translateX(-42px) rotate(-3deg); opacity: 0; } }
.card.placeholder { align-items: center; justify-content: center; color: var(--muted); font-family: var(--f-mono); font-size: calc(13px * var(--scale)); }

/* Each card's own accent — the colour its category badge is painted in, and the
   colour of its left rail. Exposed as a variable so the foot's Save/Share
   buttons can take their font colour from it and blend into the card instead of
   sitting in a grey frame. A plain news card's badge (.badge.cat) prints in
   --ink, so that's this card's accent too. */
.card { --card-accent: var(--ink); }
.card.good { --card-accent: var(--sunrise); }
.card.event { --card-accent: var(--tide); }
.card.weather { --card-accent: var(--clear); }
.card.history { --card-accent: var(--history); }
.card.fact { --card-accent: var(--fact); }
.card.word { --card-accent: var(--word); }
.card.markets { --card-accent: var(--markets); }

/* tinted special cards + left accent rail */
.card.good { background: var(--sunrise-bg); border-left: 8px solid var(--sunrise); }
.card.event { background: var(--tide-bg); border-left: 8px solid var(--tide); }
.card.weather { background: var(--clear-bg); border-left: 8px solid var(--clear); }
.card.weather h2 { font-size: calc(46px * var(--scale)); }
.card.history { background: var(--history-bg); border-left: 8px solid var(--history); }
.card.fact { background: var(--fact-bg); border-left: 8px solid var(--fact); }
.card.word { background: var(--word-bg); border-left: 8px solid var(--word); }
.card.word h2 { font-size: calc(34px * var(--scale)); }   /* the word itself carries the card */
.card.markets { background: var(--markets-bg); border-left: 8px solid var(--markets); }
.card.clickable { cursor: pointer; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.card.clickable:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(90, 59, 156, 0.15); }

.card-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.datetag {
  margin-left: auto; font-family: var(--f-mono); font-size: calc(10px * var(--scale)); font-weight: 500;
  letter-spacing: 1px; color: var(--muted); border: 1px solid var(--hair);
  padding: 3px 8px; border-radius: 999px;
}
.datetag.today { color: var(--lumen); border-color: var(--lumen); }
.badge {
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; padding: 5px 10px; border-radius: 999px;
  background: var(--mist); color: var(--ink);
}
.badge.cat { background: var(--mist); color: var(--ink); }
.badge.good { background: var(--sunrise); color: #fff; }
.badge.insider { background: transparent; color: var(--tide); box-shadow: inset 0 0 0 1.4px var(--tide); }
.badge.event { background: var(--tide); color: #fff; }
.badge.weather { background: var(--clear); color: #fff; }
.badge.history { background: var(--history); color: #fff; }
.badge.fact { background: var(--fact); color: #fff; }
.badge.word { background: var(--word); color: #fff; }
.badge.markets { background: var(--markets); color: #fff; }

.card h2 {
  font-family: var(--f-head); font-weight: 800; font-size: calc(21px * var(--scale)); line-height: 1.2;
  margin: 4px 0 8px; letter-spacing: -0.3px;
}
.card .blurb { color: var(--blurb-ink); font-size: calc(13.5px * var(--scale)); line-height: 1.5; overflow-y: auto; flex: 1; }
/* Image height scales with the viewport instead of a fixed 152px. On a short
   window the deck card is only ~330px, and a 152px image left the blurb with ~0
   room, so the first sentences never showed. 13vh tracks how tall the card can be
   (both shrink on a short window), and clamp bounds it 92–150px so the image is
   never tiny and never hogs the card. On a tall window it hits 150 and the blurb
   still has plenty (the card grew too). */
.card .thumb {
  width: 100%; height: clamp(92px, 13vh, 150px);
  object-fit: cover; border-radius: 14px; margin-bottom: 14px;
}
.event-meta { font-family: var(--f-body); font-weight: 600; font-size: calc(14px * var(--scale)); color: var(--tide); margin: 4px 0 0; }
.card.weather .event-meta { color: var(--clear); }

/* weather week strip */
.week { display: flex; gap: 6px; margin-top: 16px; }
.wday {
  flex: 1; text-align: center; background: var(--wday-bg);
  border: 1px solid var(--wday-border); border-radius: 12px; padding: 9px 2px;
}
.wname { font-family: var(--f-mono); font-size: calc(10px * var(--scale)); letter-spacing: 0.5px; color: var(--muted); text-transform: uppercase; }
.wemoji { font-size: calc(18px * var(--scale)); margin: 5px 0 3px; }
.whi { font-family: var(--f-head); font-weight: 800; font-size: calc(15px * var(--scale)); color: var(--ink); }
.wlo { font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--muted); }

.card-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; gap: 10px; }
.src { font-family: var(--f-mono); font-size: calc(11px * var(--scale)); letter-spacing: 0.6px; color: var(--muted); }
/* The weather source is a real link (Open-Meteo's CC BY 4.0 requires one), but
   it should read as the same quiet credit as the plain-text version, not a
   default blue link. Dotted underline signals it's clickable on hover. */
a.src { text-decoration: none; border-bottom: 1px dotted var(--hair); }
a.src:hover { color: var(--ink); border-bottom-color: currentColor; }
/* Licence notice for CC BY-SA cards. Quiet, but it's a legal obligation, so it
   must actually be readable: --muted measured 2.9:1 on the tinted fact/word
   backgrounds, under the 4.5:1 WCAG AA floor for text this small. --blurb-ink
   clears it comfortably while still reading as secondary. */
.lic {
  font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); letter-spacing: 0.4px;
  color: var(--blurb-ink); text-decoration: none; margin-left: 8px; white-space: nowrap;
  border-bottom: 1px dotted var(--hair);
}
.lic:hover { color: var(--ink); border-bottom-color: currentColor; }
.readmore {
  font-family: var(--f-mono); font-size: calc(12px * var(--scale)); font-weight: 700; letter-spacing: 0.5px;
  color: var(--ink); text-decoration: none; background: var(--mist);
  padding: 8px 13px; border-radius: 10px; white-space: nowrap;
}

/* ---- controls ---- */
.controls { display: flex; align-items: center; justify-content: space-between; margin-top: 18px; gap: 12px; }
.hint { font-family: var(--f-mono); font-size: calc(11px * var(--scale)); letter-spacing: 1px; color: var(--muted); text-align: center; line-height: 1.5; }
.hint b { color: var(--ink); }
.hint small { font-size: calc(10px * var(--scale)); }
.ctrl {
  width: 54px; height: 54px; border-radius: 50%; border: none; cursor: pointer;
  font-size: calc(26px * var(--scale)); background: var(--paper); color: var(--ink); box-shadow: var(--shadow);
  transition: transform 0.08s ease;
}
.ctrl.primary {
  background: var(--lumen); color: var(--ink);
  box-shadow: 0 10px 24px rgba(244, 180, 0, 0.4);
}
.ctrl:active { transform: scale(0.92); }
.ctrl:disabled { opacity: 0.4; cursor: default; }

/* ---- mobile layout: card first ----
   On phones the page is a single flex column (the desktop grid only applies at
   >=1024px). By DOM order the city/topic/region pickers and the progress bar sat
   ABOVE the deck, which pushed the card down the screen and dropped the prev/next
   controls below the fold. Reorder so the card sits right under the header, then
   the nav controls, then the progress bar + pickers, then the quote.

   And let the card size to its content. On desktop the card is position:absolute
   (inset:0) to fill the fixed-height deck; on mobile the deck has no fixed height,
   so an absolute card collapsed the deck to its 400px min-height and clipped any
   card taller than that (the blurb scrolled inside instead of showing in full).
   Flowing the card lets the deck grow to it, and the blurb shows whole. */
@media (max-width: 1023.98px) {
  .layout-deck > .topbar      { order: 0; }
  .layout-deck > .deck        { order: 1; }
  .layout-deck > .controls    { order: 2; }
  .layout-deck > .more-row    { order: 3; }
  .layout-deck > .daily       { order: 4; }
  .layout-deck > .scopes      { order: 5; }
  .layout-deck > .quote       { order: 6; }
  .layout-deck > .sidebar,
  .layout-deck > .tracked-col { order: 7; }

  .deck { flex: none; min-height: 0; }
  .card { position: relative; inset: auto; }
  .card .blurb { flex: none; overflow: visible; }   /* show the whole blurb, no inner scroll */
}

/* ---- celebration ---- */
.celebrate {
  position: fixed; inset: 0; display: none; align-items: center; justify-content: center;
  background: rgba(21, 23, 28, 0.5); z-index: 30; animation: fade 0.3s ease; cursor: pointer;
}
.celebrate.show { display: flex; }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.celebrate-inner {
  background: var(--paper); border-radius: 26px; padding: 36px 44px; text-align: center;
  box-shadow: var(--shadow); animation: pop 0.35s ease;
}
.celebrate-emblem { position: relative; width: 92px; height: 92px; margin: 0 auto 6px; }
.spark-lg { width: 92px; height: 92px; filter: drop-shadow(0 0 18px rgba(244, 180, 0, 0.55)); }
.spark-lg path { fill: var(--lumen); }
.celebrate-num {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--f-head); font-weight: 800; font-size: calc(32px * var(--scale)); color: var(--ink); padding-bottom: 4px;
}
.celebrate-title {
  font-family: var(--f-mono); font-size: calc(14px * var(--scale)); font-weight: 700; letter-spacing: 4px;
  text-transform: uppercase; color: var(--ink); margin-top: 2px;
}
.celebrate-inner small { display: block; margin-top: 10px; font-family: var(--f-body); color: var(--muted); font-size: calc(13px * var(--scale)); }
.celebrate-inner small.tap { margin-top: 16px; font-family: var(--f-mono); font-size: calc(10px * var(--scale)); letter-spacing: 1.5px; text-transform: uppercase; opacity: 0.6; }

/* ---- personalization panel ---- */
.panel {
  position: fixed; inset: 0; display: none; align-items: flex-end; justify-content: center;
  background: rgba(21, 23, 28, 0.5); z-index: 40; animation: fade 0.25s ease;
}
.panel.show { display: flex; }
.panel-inner {
  background: var(--paper); width: 100%; max-width: 560px;
  border-radius: 24px 24px 0 0; padding: 22px 22px 30px; box-shadow: var(--shadow);
  animation: rise 0.28s ease; max-height: 80vh; overflow-y: auto;
}
/* Desktop: the Options panel is a settings surface, not a reach-with-your-thumb
   bottom sheet. Anchor it just under the wordmark instead of rising from the
   bottom — its top used to land down by the city selector, which has nothing to
   do with Options. Scoped to #optionsPanel; the Personalize sheet is untouched,
   and mobile keeps the bottom sheet (correct there). */
@media (min-width: 1024px) {
  #optionsPanel { align-items: flex-start; }
  #optionsPanel .panel-inner {
    margin-top: 30px;                       /* a couple of points under the tagline */
    max-height: calc(100vh - 60px);         /* 30px breathing room top and bottom */
    border-radius: 24px;                    /* not a bottom sheet any more → round all corners */
  }
}
@keyframes rise { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.panel-title { font-family: var(--f-head); font-weight: 800; font-size: calc(20px * var(--scale)); }
.panel-close {
  border: none; background: var(--lumen); color: var(--ink);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(13px * var(--scale)); letter-spacing: 1px;
  padding: 9px 18px; border-radius: 12px; cursor: pointer;
}
.panel-close:active { transform: scale(0.96); }
.panel-actions { display: flex; align-items: center; gap: 8px; }
.panel-cancel {
  border: 1px solid var(--hair); background: transparent; color: var(--muted);
  font-family: var(--f-mono); font-weight: 600; font-size: calc(13px * var(--scale)); letter-spacing: 1px;
  padding: 8px 14px; border-radius: 12px; cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.panel-cancel:hover { color: var(--ink); border-color: var(--ink); }
.panel-cancel:active { transform: scale(0.96); }
.panel-scope { margin-bottom: 16px; }
.panel-scope-label {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); letter-spacing: 2px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 9px;
}
.toggle-row { display: flex; flex-wrap: wrap; gap: 8px; }
.toggle {
  border: 1.4px solid var(--emphasis-bg); background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-mono); font-size: calc(12px * var(--scale)); font-weight: 500; letter-spacing: 0.4px;
  padding: 8px 14px; border-radius: 999px; cursor: pointer; transition: all 0.12s ease;
}
.toggle::before { content: "✓ "; opacity: 0.9; }
.toggle.off {
  background: transparent; color: var(--muted); border-color: var(--hair);
  text-decoration: line-through;
}
.toggle.off::before { content: ""; }
.toggle:active { transform: scale(0.95); }

/* Boost topics — an unfoldable menu to pick up to 3 topics to over-weight. */
.panel-boost { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--hair); }
.panel-boost-head {
  width: 100%; display: flex; align-items: center; gap: 10px;
  background: none; border: none; cursor: pointer; padding: 0; text-align: left;
}
.panel-boost-title {
  font-family: var(--f-head); font-weight: 700; font-size: calc(15px * var(--scale)); color: var(--ink); flex: 1;
}
.panel-boost-count {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); color: var(--muted);
  padding: 2px 8px; border-radius: 999px; background: var(--soft); transition: color 0.15s ease;
}
.panel-boost-count.bump { animation: boostBump 0.3s ease; color: var(--lumen); }
@keyframes boostBump { 0%,100% { transform: scale(1); } 40% { transform: scale(1.25); } }
.panel-boost-caret { color: var(--muted); font-size: calc(11px * var(--scale)); transition: transform 0.15s ease; }
.panel-boost.open .panel-boost-caret { transform: rotate(90deg); }
.panel-boost-body { margin-top: 12px; }
.panel-boost-hint {
  font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--muted); margin-bottom: 12px;
}
.boost-group { margin-bottom: 10px; }
.boost-group:last-child { margin-bottom: 0; }
.boost-group-label {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale));
  letter-spacing: 1.6px; text-transform: uppercase; color: var(--muted); margin-bottom: 7px;
}
.boost-group-row { display: flex; flex-wrap: wrap; gap: 6px; }
.boost-chip {
  border: 1px solid var(--hair); background: var(--soft); color: var(--ink);
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); font-weight: 500;
  padding: 5px 12px; border-radius: 999px; cursor: pointer; transition: all 0.12s ease;
}
.boost-chip.on { background: var(--lumen); border-color: var(--lumen); color: var(--ink); font-weight: 700; }
.boost-chip.on::before { content: "★ "; }
.boost-chip:active { transform: scale(0.95); }

/* "Where my news are coming from" — the comprehensive source list at the
   bottom of the Personalize panel. Every source across every scope, so the
   reader can see (and toggle) exactly which outlets are behind their feed.
   Grouped by scope so a long list stays scannable. */
.panel-sources {
  margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--hair);
}
.panel-sources-head {
  font-family: var(--f-head); font-weight: 700;
  font-size: calc(15px * var(--scale)); color: var(--ink);
  margin-bottom: 3px;
}
.panel-sources-sub {
  font-family: var(--f-body); font-size: calc(12px * var(--scale));
  color: var(--muted); margin-bottom: 14px;
}
.sources-group { margin-bottom: 12px; }
.sources-group:last-child { margin-bottom: 0; }
.sources-group-label {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale));
  letter-spacing: 1.6px; text-transform: uppercase; color: var(--muted);
  margin-bottom: 7px;
}
.sources-group-row {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.source-chip {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--hair); background: var(--soft); color: var(--ink);
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); font-weight: 500;
  padding: 4px 10px; border-radius: 999px; cursor: pointer;
  transition: all 0.12s ease;
}
.source-chip .source-name { line-height: 1; }
.source-chip.off { opacity: 0.55; text-decoration: line-through; }
.source-chip:active { transform: scale(0.95); }
.source-lang-badge {
  font-family: var(--f-mono); font-size: calc(8.5px * var(--scale)); letter-spacing: 0.3px;
  text-transform: uppercase; padding: 2px 6px; border-radius: 999px;
  background: rgba(244,180,0,0.16); color: var(--ink);
  border: 1px solid rgba(244,180,0,0.35);
  line-height: 1;
}

.panel-hint {
  font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--muted); margin-top: 6px;
}

/* ---- Options panel ---- */
.option { margin-bottom: 22px; padding-bottom: 22px; border-bottom: 1px solid var(--hair); }
.option:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
.option-label { font-family: var(--f-head); font-weight: 700; font-size: calc(15px * var(--scale)); color: var(--ink); margin-bottom: 12px; }
.option-hero {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin-bottom: 10px;
}
.option-tier {
  font-family: var(--f-head); font-weight: 800; font-size: calc(30px * var(--scale));
  color: var(--lumen);
  transition: transform 0.15s ease;
}
.option-count {
  font-family: var(--f-mono); font-size: calc(13px * var(--scale)); color: var(--muted);
}
.option-count b { color: var(--lumen); font-weight: 700; font-size: calc(16px * var(--scale)); }
.option-scale {
  display: flex; justify-content: space-between;
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); color: var(--muted); margin-top: 4px;
}
.goal-slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 6px; border-radius: 999px;
  background: linear-gradient(to right, var(--lumen) 0%, var(--lumen) var(--fill, 27.5%), var(--hair) var(--fill, 27.5%), var(--hair) 100%);
  outline: none; cursor: pointer;
}
.goal-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--lumen); border: 3px solid var(--paper);
  box-shadow: 0 2px 6px rgba(244, 180, 0, 0.4);
  cursor: pointer; transition: transform 0.1s ease;
}
.goal-slider::-webkit-slider-thumb:active { transform: scale(1.15); }
.goal-slider::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--lumen); border: 3px solid var(--paper);
  box-shadow: 0 2px 6px rgba(244, 180, 0, 0.4);
  cursor: pointer;
}
.option-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.option-row .option-label { margin-bottom: 0; }
.option-sub { font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--muted); margin-top: 7px; line-height: 1.4; }
/* Privacy block. The state line is --blurb-ink, not --muted: "your profile is
   public" is the single most consequential sentence in this panel, so it must
   not read as fine print. .is-public gets the gold rail — a nudge, not an alarm;
   public is a legitimate choice, it just shouldn't be a silent one. */
.privacy-state {
  color: var(--blurb-ink); padding: 8px 10px; border-radius: 9px;
  background: var(--soft); margin-top: 9px;
}
.privacy-state.is-public { border-left: 3px solid var(--lumen); }
.privacy-list { margin: 5px 0 0; padding-left: 18px; }
.privacy-list li { margin-bottom: 2px; }
.privacy-more {
  display: inline-block; margin-top: 9px;
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale));
  color: var(--blurb-ink); text-decoration: none;
  border-bottom: 1px dotted var(--hair);
}
.privacy-more:hover { color: var(--ink); border-bottom-color: var(--lumen); }

/* ---- Share panel: ice-breaker prompts + optional text/picture ---- */
.share-card {
  background: var(--soft); border-radius: 14px; padding: 12px 14px; margin-bottom: 18px;
}
.share-card .sc-head {
  font-family: var(--f-head); font-weight: 700; font-size: calc(14px * var(--scale));
  color: var(--ink); line-height: 1.35;
}
.share-card .sc-src {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale)); color: var(--muted); margin-top: 5px;
}
/* A dropdown, not a chip wall: the prompt list is long, and chips would push the
   text box and picture picker below the fold. */
.share-select {
  width: 100%; box-sizing: border-box; margin-bottom: 20px;
  padding: 11px 13px; border-radius: 12px; border: 1px solid var(--hair);
  background: var(--soft); color: var(--ink); cursor: pointer;
  font-family: var(--f-body); font-size: calc(13px * var(--scale));
}
.share-select:focus { outline: none; border-color: var(--lumen); }
.share-text {
  width: 100%; box-sizing: border-box; resize: vertical;
  padding: 10px 12px; border-radius: 12px; border: 1px solid var(--hair);
  background: var(--soft); color: var(--ink);
  font-family: var(--f-body); font-size: calc(13px * var(--scale)); line-height: 1.45;
}
.share-text:focus { outline: none; border-color: var(--lumen); }
.share-count {
  font-family: var(--f-mono); font-size: calc(10px * var(--scale)); color: var(--muted);
  text-align: right; margin: 4px 0 18px;
}
.share-file { font-family: var(--f-mono); font-size: calc(11px * var(--scale)); color: var(--muted); }
.share-img-preview { margin-top: 10px; display: flex; align-items: center; gap: 10px; }
.share-img-preview[hidden] { display: none; }
.share-img-preview img { max-width: 110px; max-height: 110px; border-radius: 10px; border: 1px solid var(--hair); }
.share-img-clear {
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  font-family: var(--f-mono); font-size: calc(11px * var(--scale)); font-weight: 700;
  padding: 6px 12px; border-radius: 999px; cursor: pointer;
}
.share-status { margin-top: 12px; }
.share-status.ok { color: var(--tide); }

/* Save + Share in the card foot, siblings of "Read full →". Frameless on
   purpose: no border, no fill — they print in the card's own accent colour (the
   same colour as its category badge) so they sit inside the card's palette
   rather than fighting it.

   Deliberately NOT dimmed at rest. The accents are mid-tone on their own tinted
   backgrounds (tide on --tide-bg is ~2.4:1), so lowering opacity would push
   already-marginal text further down. Saved vs unsaved is carried by the label
   ("Save" → "✓ Saved"), not by a fill; hover gets an underline. */
.card-foot .sharebtn, .card-foot .savebtn {
  border: none; background: transparent; color: var(--card-accent);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(11px * var(--scale));
  padding: 8px 4px; cursor: pointer; margin-right: 12px;
  text-decoration: none; transition: text-decoration-color 0.15s ease;
}
.card-foot .savebtn { margin-left: auto; }         /* first of the two, pushes right */
.card-foot .sharebtn:hover, .card-foot .savebtn:hover { text-decoration: underline; text-underline-offset: 3px; }
.card-foot .sharebtn:focus-visible, .card-foot .savebtn:focus-visible {
  outline: 1.5px solid var(--card-accent); outline-offset: 2px; border-radius: 4px;
}

/* ---- Save panel: pick / create a collection ---- */
.save-list { display: flex; flex-direction: column; gap: 7px; margin-bottom: 20px; }
.save-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 1px solid var(--hair); background: var(--soft); color: var(--ink);
  padding: 10px 13px; border-radius: 12px; cursor: pointer; text-align: left;
  font-family: var(--f-body); font-size: calc(13px * var(--scale)); transition: all 0.12s ease;
}
.save-row.on { border-color: var(--lumen); background: rgba(244, 180, 0, 0.14); }
.save-row .tick {
  width: 20px; height: 20px; flex-shrink: 0; border-radius: 6px; display: grid; place-items: center;
  border: 1.5px solid var(--hair); background: var(--paper);
  font-size: 12px; font-weight: 700; color: transparent;
}
.save-row.on .tick { background: var(--lumen); border-color: var(--lumen); color: var(--ink); }
.save-row .nm { flex: 1; font-weight: 600; }
.save-row .ct { font-family: var(--f-mono); font-size: calc(10px * var(--scale)); color: var(--muted); }
.save-row:active { transform: scale(0.99); }
.save-empty { font-family: var(--f-body); font-style: italic; color: var(--muted); font-size: calc(12.5px * var(--scale)); margin-bottom: 20px; }
.save-new { display: flex; gap: 8px; }
.save-status { margin-top: 12px; }
.save-status.ok { color: var(--tide); }

/* "Share your streak" CTA inside the celebration overlay */
.celebrate-share {
  margin-top: 14px; border: none; background: var(--lumen); color: var(--ink);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale));
  letter-spacing: 0.6px; padding: 10px 20px; border-radius: 999px; cursor: pointer;
}
.celebrate-share[hidden] { display: none; }

.switch { border: none; background: none; cursor: pointer; padding: 4px; }
.switch-track {
  display: block; width: 40px; height: 22px; border-radius: 999px;
  background: var(--hair); position: relative;
  transition: background 0.15s ease;
}
.switch-thumb {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 50%; background: var(--paper);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
  transition: transform 0.15s ease;
}
.switch[aria-checked="true"] .switch-track { background: var(--lumen); }
.switch[aria-checked="true"] .switch-thumb { transform: translateX(18px); }

.lang-toggle { display: inline-flex; border: 1px solid var(--hair); border-radius: 999px; padding: 3px; gap: 2px; }
.lang-opt {
  border: none; background-color: transparent; color: var(--muted);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 0.5px;
  padding: 6px 14px; border-radius: 999px; cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.lang-opt[aria-pressed="true"] { background-color: var(--lumen); color: var(--ink); }
.lang-opt:hover:not([aria-pressed="true"]) { color: var(--ink); }
.fs-opt {
  border: none; background-color: transparent; color: var(--muted);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 0.5px;
  padding: 6px 14px; border-radius: 999px; cursor: pointer;
  /* Transition the LONG-HAND background-color (not the shorthand). Chrome
     can leave a shorthand background transition mid-way when the panel is
     display:none and then re-shown, so the highlight fills the wrong tier
     after Fertig + reopen. Explicit long-hand + no transition on aria-pressed
     changes triggered by JS avoids the visual desync. */
  transition: background-color 0.15s ease, color 0.15s ease;
}
.fs-opt[aria-pressed="true"] { background-color: var(--lumen); color: var(--ink); }
.fs-opt:hover:not([aria-pressed="true"]) { color: var(--ink); }
/* Applied to .panel while it's mid-seed so leftover CSS transitions from the
   previous close don't paint the wrong tier gold on reopen. Removed after
   two rAFs so normal user-driven clicks still transition smoothly. */
.panel.no-transition,
.panel.no-transition * { transition: none !important; }

/* ---- desktop stats sidebar ---- */
.sidebar {
  grid-area: stats; background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 18px; overflow-y: auto;
  /* Fixed height (not align-self: stretch) — grid can't renegotiate this
     mid-load when polls/stocks/topics content arrives asynchronously, which
     is what caused the earlier "grows too far" and "randomly loses its
     extension" bugs. min-height: 0 keeps the item from pushing the auto rows
     outward; content beyond this scrolls inside via overflow-y: auto. Height
     is deck-row clamp + auto-row estimates + 20px visual extension toward
     the shiny line above the quote. */
  height: calc(clamp(330px, 44vh, 480px) + 235px);
  min-height: 0;
}
.side-head { margin-bottom: 8px; }
.side-title { font-family: var(--f-head); font-weight: 800; font-size: calc(18px * var(--scale)); display: flex; align-items: center; gap: 8px; }
.refresh-btn {
  background: transparent; border: 1px solid transparent; border-radius: 999px;
  width: 24px; height: 24px; padding: 0; font-size: calc(16px * var(--scale)); line-height: 1;
  color: var(--muted); cursor: pointer; transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
  display: inline-flex; align-items: center; justify-content: center;
}
.refresh-btn:hover { color: var(--lumen); border-color: var(--lumen); background: rgba(244, 180, 0, 0.08); }
.refresh-btn.spinning { animation: refresh-spin 0.8s linear infinite; color: var(--lumen); pointer-events: none; }
@keyframes refresh-spin { to { transform: rotate(360deg); } }
.side-sub { font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); letter-spacing: 1.5px; text-transform: uppercase; color: var(--muted); margin-top: 2px; }

.stat-hero {
  text-align: center; padding: 2px 0 8px; border-bottom: 1px solid var(--hair); margin-bottom: 8px;
}
.hero-emblem { position: relative; width: 72px; height: 72px; margin: 0 auto 4px; }
.hero-emblem .spark-lg { width: 72px; height: 72px; filter: drop-shadow(0 0 16px rgba(244,180,0,0.5)); }
.hero-emblem .spark-lg path { fill: var(--lumen); }
.hero-num {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-family: var(--f-head); font-weight: 800; font-size: calc(27px * var(--scale)); color: var(--ink); padding-bottom: 2px;
}
.hero-label { font-family: var(--f-mono); font-size: calc(11px * var(--scale)); letter-spacing: 2.5px; text-transform: uppercase; color: var(--ink); }
.hero-sub { font-family: var(--f-body); font-size: calc(11.5px * var(--scale)); color: var(--muted); margin-top: 2px; }

.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-bottom: 8px; }
.stat-grid.three { grid-template-columns: 1fr 1fr 1fr; }
.stat-tile { background: var(--mist); border-radius: 12px; padding: 9px 4px; text-align: center; }
.stat-num { font-family: var(--f-head); font-weight: 800; font-size: calc(20px * var(--scale)); color: var(--ink); line-height: 1.05; }
.stat-cap { font-family: var(--f-mono); font-size: calc(8.5px * var(--scale)); letter-spacing: 0.4px; text-transform: uppercase; color: var(--muted); margin-top: 3px; }

.stat-highlight {
  background: linear-gradient(135deg, rgba(244,180,0,0.14), rgba(255,122,89,0.12));
  border: 1px solid rgba(244,180,0,0.35); border-radius: 12px; padding: 7px 11px; margin-bottom: 8px;
}
.hl-cap { font-family: var(--f-mono); font-size: calc(9px * var(--scale)); letter-spacing: 1.1px; text-transform: uppercase; color: var(--muted); }
.hl-val { font-family: var(--f-head); font-weight: 800; font-size: calc(16px * var(--scale)); color: var(--ink); margin-top: 2px; }

.stat-section-title { font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); letter-spacing: 2px; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
/* Consecutive dashboard sections (Topics → Friend streaks) sat flush against
   each other while the highlight box above Topics had an 8px gap below it. Give
   section-to-section the same 8px so the rhythm is even down the column. The
   first section (Topics) is preceded by .stat-highlight, not another section, so
   it keeps its existing gap and isn't double-spaced. */
.stat-section + .stat-section { margin-top: 8px; }

/* ---- weekly recap section ---- */
.stat-section.recap { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--hair); }
.recap-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 7px; margin-bottom: 8px; }
.recap-suggest {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-family: var(--f-body); font-size: calc(11.5px * var(--scale)); color: var(--muted);
}
.recap-suggest-val { color: var(--lumen); font-weight: 700; }

/* ---- profile settings page ---- */
.profile-page {
  width: 100%; max-width: 640px; margin: 0 auto;
  padding: 8px 4px 40px;
}
.profile-title {
  font-family: var(--f-head); font-weight: 800;
  font-size: calc(28px * var(--scale)); color: var(--ink);
  margin: 8px 0 20px; letter-spacing: -0.3px;
}
.profile-section {
  background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px; margin-bottom: 16px;
}
.profile-section h2 {
  font-family: var(--f-head); font-weight: 800;
  font-size: calc(17px * var(--scale)); color: var(--ink);
  margin: 0 0 14px; letter-spacing: -0.2px;
}
.profile-section p {
  font-family: var(--f-body); font-size: calc(13.5px * var(--scale));
  color: var(--muted); margin: 0 0 12px; line-height: 1.5;
}
.profile-form { display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap; }
.profile-form.column { flex-direction: column; align-items: stretch; }
.profile-form.column label { display: flex; flex-direction: column; gap: 5px; }
.profile-form.column label > span {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale));
  letter-spacing: 1px; text-transform: uppercase; color: var(--muted);
}
.profile-form input {
  flex: 1; min-width: 0;
  border: 1px solid var(--hair); background: var(--soft); color: var(--ink);
  font-family: var(--f-body); font-size: calc(14px * var(--scale));
  padding: 10px 12px; border-radius: 10px;
}
.profile-form input:focus {
  outline: none;
  border-color: var(--lumen); box-shadow: 0 0 0 3px rgba(244,180,0,0.18);
}
.profile-save {
  border: none; background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12.5px * var(--scale));
  letter-spacing: 1px; text-transform: uppercase;
  padding: 10px 18px; border-radius: 10px; cursor: pointer;
  transition: transform 0.08s ease, background 0.15s ease;
}
.profile-save:hover { background: var(--lumen); color: var(--ink); }
.profile-save:active { transform: scale(0.97); }
.profile-status {
  font-family: var(--f-body); font-size: calc(12.5px * var(--scale));
  margin-top: 10px; min-height: 18px; line-height: 1.4;
}
.profile-status.ok { color: var(--tide); }
.profile-status.err { color: #d6483b; }
.profile-hint {
  font-family: var(--f-body); font-size: calc(13.5px * var(--scale));
  color: var(--muted); font-style: italic; line-height: 1.5;
}
.profile-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.profile-section.danger-zone {
  border: 1px solid rgba(214, 72, 59, 0.35);
}
.profile-section.danger-zone h2 { color: #d6483b; }
.profile-danger {
  border: 1px solid #d6483b; background: transparent; color: #d6483b;
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12.5px * var(--scale));
  letter-spacing: 1px; text-transform: uppercase;
  padding: 10px 18px; border-radius: 10px; cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.profile-danger:hover { background: #d6483b; color: #fff; }
.topic-bars { display: flex; flex-direction: column; gap: 6px; }
/* Shared friend streaks. A dead streak is dimmed, not hidden — seeing that you
   and Chris are at zero is the nudge; hiding the row removes it. */
.fs-list { display: flex; flex-direction: column; gap: 2px; }
.fs-row {
  display: grid; grid-template-columns: 18px 26px 1fr 26px; align-items: center; gap: 8px;
  padding: 6px 4px; border-radius: 10px; text-decoration: none;
}
.fs-row:hover { background: var(--soft); }
/* 0.55 opacity dropped the dimmed row's text under AA once the colour was fixed —
   opacity multiplies into the rendered contrast. 0.75 still reads as "spent". */
.fs-row.dim { opacity: 0.75; }
.fs-flame { font-size: calc(12px * var(--scale)); line-height: 1; text-align: center; }
/* The name column is 1fr, which bounds the BOX but not the text inside it — a
   40-char display name would run out of the row and widen the page. Same clamp
   .req-name already uses; min-width:0 is what lets the 1fr track actually shrink. */
.fs-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Ink, not gold: gold on --mist is 1.85:1. The app's own .hero-num already sets
   the precedent — the flame carries the colour, the number carries the meaning. */
.fs-num {
  font-family: var(--f-head); font-weight: 800; font-size: calc(14px * var(--scale));
  color: var(--ink); text-align: right;
}
.fs-row.dim .fs-num { color: var(--blurb-ink); }
.fs-name {
  font-family: var(--f-body); font-weight: 600; font-size: calc(11.5px * var(--scale));
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fs-av {
  width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  overflow: hidden;
  background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-head); font-weight: 800; font-size: 10px;
}
/* When an actual avatar is drawn, the sprite tile is its own full-bleed
   background — drop the placeholder fill so no coloured ring peeks past the
   circle. Initial-only rows keep the emphasis chip. */
.fs-av:has(.av) { background: none; }

.topic-row { display: grid; grid-template-columns: 72px 1fr 22px; align-items: center; gap: 8px; }
.topic-name { font-family: var(--f-body); font-weight: 600; font-size: calc(11.5px * var(--scale)); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topic-track { height: 7px; background: var(--mist); border-radius: 999px; overflow: hidden; }
.topic-fill { height: 100%; background: linear-gradient(90deg, var(--lumen), var(--sunrise)); border-radius: 999px; }
.topic-count { font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); color: var(--muted); text-align: right; }
.topic-empty { font-family: var(--f-body); font-size: calc(11.5px * var(--scale)); color: var(--muted); line-height: 1.4; }
.side-foot { margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--hair); font-family: var(--f-body); font-size: calc(10.5px * var(--scale)); color: var(--muted); line-height: 1.35; }

/* ---- tracked column (wide desktop) ---- */
.tracked-col {
  grid-area: pins; background: var(--paper); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 18px; overflow-y: auto;
  /* Same fixed-height recipe as .sidebar — stable under async content loads
     (polls, stocks). See the note on .sidebar. */
  height: calc(clamp(330px, 44vh, 480px) + 235px);
  min-height: 0;
}
/* Dark-mode accent rails — brighter palette colors give the side panels a
   distinct edge against the deep ink base. Left rail matches the news-card
   pattern (border-left). Different colors per panel for visual identity. */
:root[data-theme="dark"] .sidebar,
:root[data-theme="dark"] .tracked-col { border-left: 4px solid rgba(237, 225, 200, 0.7); }

/* ---- Dark-mode desaturation of dashboard chrome ----
   Everything above the news cards used to pick up a rainbow of accents
   (coral topic bars, purple mood dots, cyan events highlight) that competed
   with the deck. In dark mode we now hold to a monochrome grayscale for
   sidebar chrome — the only saturated things left are the news card
   variants themselves (.card.good / .event / .weather / .history / .markets
   and their badges) and the streak spark's gold (still `--lumen`,
   because that IS the streak count symbol Elena wanted to keep). */

/* Streak progress bar — Parchment at full brightness. Kept at 1.0 alpha
   at the right edge (streak still reads as "the bright thing") while
   sharing the same warm-off-white base as the surrounding rails/borders,
   so it stops looking like the one grey survivor. */
:root[data-theme="dark"] .daily-fill {
  background: linear-gradient(90deg, rgba(237, 225, 200, 0.35), #ede1c8);
}

/* Topic bars ("Topics you opened today"). Not streak — Parchment at
   dim alphas so the bars carry the same warmth as the panel edges. */
:root[data-theme="dark"] .topic-fill {
  background: linear-gradient(90deg, rgba(237, 225, 200, 0.24), rgba(237, 225, 200, 0.7));
}

/* "Most interesting to you" highlight box. Original had a warm gold-to-coral
   diagonal + gold border; now a dimmed colorless glass panel. */
:root[data-theme="dark"] .stat-highlight {
  background: rgba(255, 255, 255, 0.035);
  border-color: rgba(255, 255, 255, 0.126);
}

/* Profile-page "saved OK" status was cyan (--tide); go neutral instead. */
:root[data-theme="dark"] .profile-status.ok { color: var(--ink); }

/* Stocks + polls at 50% saturation — the party colours and the up/down
   tickers still carry meaning (colourblind readers still need to tell red
   from teal), so we can't grayscale them, but pulling saturation halfway
   lets the news deck lead visually. `filter` covers both the SVG spark
   polylines (inline color attr) AND the CSS-styled poll-fill bars in one
   rule, without hunting individual hex values. */
:root[data-theme="dark"] .tracked-list,
:root[data-theme="dark"] .polls-list {
  filter: saturate(0.5);
}

/* Dark-mode edges on the cards *inside* the tracked column (DAX, Microsoft,
   poll cards) and on the location dropdown. Base tone is Parchment
   (#ede1c8 — a warm off-white) at 70% alpha, so the chrome carries a whisper
   of amber instead of feeling clinical. Full-brightness ink (#eaecea) is
   still reserved for the wordmark, streak count, and streak progress bar. */
:root[data-theme="dark"] .track-card,
:root[data-theme="dark"] .poll-card {
  border: 1px solid rgba(237, 225, 200, 0.7);
}
:root[data-theme="dark"] .select select {
  border-color: rgba(237, 225, 200, 0.7);
}

/* News cards a touch brighter in dark mode so they read as lifted tiles
   like in bright mode, instead of blending into the paper bg. Explicitly
   :not() every special variant — because our selector picks up the extra
   specificity of the [data-theme] attribute, it would otherwise beat
   `.card.good` / `.card.event` / etc. in the cascade and flatten their
   coloured backgrounds. */
:root[data-theme="dark"] .card:not(.good):not(.event):not(.weather):not(.history):not(.markets):not(.placeholder) {
  background: #24272e;
}
.tracked-list { display: flex; flex-direction: column; gap: 7px; margin-bottom: 8px; }
.tracked-empty { font-family: var(--f-body); font-size: calc(12px * var(--scale)); color: var(--muted); line-height: 1.5; }
.track-card { background: var(--mist); border-radius: 14px; padding: 7px 11px; }
.track-top { display: flex; align-items: center; justify-content: space-between; }
.track-name { font-family: var(--f-body); font-weight: 700; font-size: calc(14px * var(--scale)); color: var(--ink); }
.track-x { border: none; background: none; color: var(--muted); font-size: calc(16px * var(--scale)); cursor: pointer; line-height: 1; padding: 0 2px; }
.track-row { display: flex; align-items: baseline; justify-content: space-between; margin: 3px 0 6px; }
.track-price { font-family: var(--f-head); font-weight: 800; font-size: calc(17px * var(--scale)); color: var(--ink); }
.track-chg { font-family: var(--f-mono); font-size: calc(12px * var(--scale)); font-weight: 700; }
.track-chg.up { color: #10b5a4; }
.track-chg.down { color: #ef5b5b; }
.spark-svg { width: 100%; height: 48px; display: block; }
.spark-axis { position: relative; height: 12px; margin-top: 2px; }
.spark-tick {
  position: absolute; top: 0; font-family: var(--f-mono); font-size: calc(8.5px * var(--scale));
  color: var(--muted); white-space: nowrap; line-height: 1;
}
.pin-form { display: flex; gap: 8px; margin-bottom: 6px; }
.pin-status { font-family: var(--f-body); font-size: calc(11px * var(--scale)); color: var(--muted); margin-bottom: 6px; line-height: 1.4; }
.pin-status:empty { margin-bottom: 0; }
.pin-status.error { color: #d6483b; }
.pin-input {
  flex: 1; min-width: 0; padding: 9px 11px; border-radius: 11px; border: 1px solid var(--hair);
  background: var(--soft);
  font-family: var(--f-mono); font-size: calc(12px * var(--scale)); color: var(--ink);
}
.pin-add {
  border: none; background: var(--emphasis-bg); color: var(--emphasis-fg); font-family: var(--f-mono);
  font-weight: 700; font-size: calc(12px * var(--scale)); padding: 0 14px; border-radius: 11px; cursor: pointer;
}
.tracked-section { margin-bottom: 6px; }
.polls-section { margin-top: 4px; }
.polls-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.polls-list { display: flex; flex-direction: column; gap: 7px; margin-top: 4px; }
.poll-card { background: var(--mist); border-radius: 12px; padding: 8px 11px; cursor: grab; }
.poll-card.dragging { opacity: 0.4; cursor: grabbing; }
.poll-card.drag-over { box-shadow: inset 0 2.5px 0 var(--lumen); }
.poll-head { display: flex; align-items: center; gap: 7px; }
.poll-flag { font-size: calc(16px * var(--scale)); line-height: 1; }
.poll-country { font-family: var(--f-body); font-weight: 700; font-size: calc(13px * var(--scale)); color: var(--ink); flex: 1; }
/* Share a poll. Same borderless, accent-coloured treatment as the deck cards'
   Save/Share, so it blends into the sidebar instead of sitting in a frame. */
.poll-share {
  border: none; background: none; cursor: pointer; padding: 2px 4px;
  font-family: var(--f-mono); font-weight: 700; font-size: calc(10px * var(--scale));
  color: var(--muted); letter-spacing: 0.4px;
}
.poll-share:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.poll-meta { font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); color: var(--muted); margin: 3px 0 6px; line-height: 1.3; }
.poll-meta a { color: var(--muted); text-decoration: none; border-bottom: 1px dotted var(--hair); }
/* What the number actually measures. Body font, not mono: this is a sentence to
   read once, not a data label. Sits above the bars so it's read before them. */
.poll-measure {
  font-family: var(--f-body); font-size: calc(10px * var(--scale));
  color: var(--muted); line-height: 1.35; margin: 0 0 7px;
  padding-left: 7px; border-left: 2px solid var(--hair);
}
.poll-bars { display: flex; flex-direction: column; gap: 3px; }
.poll-cc {
  font-family: var(--f-mono); font-size: calc(8.5px * var(--scale)); letter-spacing: 0.3px;
  color: var(--muted); margin-top: 6px; opacity: 0.7;
}
.poll-bar { display: grid; grid-template-columns: 62px 1fr 30px; align-items: center; gap: 6px; }
.poll-name { font-family: var(--f-body); font-weight: 600; font-size: calc(10.5px * var(--scale)); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Tokenised: this was a hard-coded light grey, which read as a bright bar on a
   dark card once shared polls started rendering in the feed. */
.poll-track { height: 6px; background: var(--track-bg); border-radius: 999px; overflow: hidden; }
.poll-fill { display: block; height: 100%; border-radius: 999px; }
.poll-pct { font-family: var(--f-mono); font-size: calc(10px * var(--scale)); color: var(--muted); text-align: right; }

/* "Add another deck" button — shown at the end of the daily feed */
.more-row { text-align: center; margin-top: 14px; }
.more-btn {
  border: none; background: var(--lumen); color: var(--ink);
  font-family: var(--f-mono); font-weight: 700; font-size: calc(12px * var(--scale)); letter-spacing: 1.2px;
  text-transform: uppercase; padding: 12px 22px; border-radius: 12px; cursor: pointer;
  box-shadow: 0 8px 20px rgba(244, 180, 0, 0.35);
  transition: transform 0.08s ease, box-shadow 0.15s ease;
}
.more-btn:hover { box-shadow: 0 10px 24px rgba(244, 180, 0, 0.5); }
.more-btn:active { transform: scale(0.97); }
.more-btn:disabled { opacity: 0.6; cursor: default; box-shadow: none; }
.more-hint {
  font-family: var(--f-body); font-size: calc(11.5px * var(--scale)); color: var(--muted);
  margin-top: 8px; line-height: 1.4;
}

/* motivational quote of the day (centred, below the cards) */
.quote { text-align: center; margin-top: 24px; padding: 0 8px; }
/* Shiny divider above the quote — same ink+glow recipe as .brand-rule so the
   page reads as one visual family (wordmark at the top, quote at the bottom). */
.quote::before {
  content: "";
  display: block;
  height: 1px;
  background: var(--ink);
  border-radius: 1px;
  box-shadow: 0 0 12px var(--wordmark-glow);
  opacity: 0.7;
  margin: 0 0 14px;
}
.quote-label { font-family: var(--f-mono); font-size: calc(9px * var(--scale)); letter-spacing: 2px; text-transform: uppercase; color: var(--muted); margin-bottom: 2px; }
.quote-text { font-family: "Caveat", "Brush Script MT", Georgia, serif; font-size: calc(27px * var(--scale)); color: var(--ink); line-height: 1.2; }
.quote-author { font-family: var(--f-body); font-style: normal; font-size: calc(12px * var(--scale)); color: var(--muted); margin-top: 2px; }

/* good-news half-sun watermark */
.card.good > *:not(.goodsun) { position: relative; z-index: 1; }
.goodsun { position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%); width: 150px; height: auto; color: var(--sunrise); opacity: 0.16; pointer-events: none; z-index: 0; }

/* On a phone the card is ~325px, so the centred sun sits squarely behind the
   Save/Share buttons (measured: 100% overlap on both axes) and swallows them —
   they're sunrise-coloured too. Lift it so it rests on the foot's top edge:
   the divider line becomes the horizon. Roomy cards (>640px) push the buttons
   out past the sun on their own, so leave those alone. */
@media (max-width: 640px) {
  .goodsun { bottom: calc(58px * var(--scale) + 12px); width: 130px; }
}

/* The card that just got shared, flying into the Feed chip. Cloned, so the deck
   underneath never moves. `animation: none` kills the .card pop-in it inherits. */
.fly-clone {
  animation: none !important;
  transition: transform 620ms cubic-bezier(0.35, 0.9, 0.3, 1), opacity 620ms ease-in;
  will-change: transform, opacity;
}
/* …and the chip acknowledges the landing. */
.feed-pulse { animation: feedPulse 0.5s ease; }
@keyframes feedPulse {
  0% { transform: scale(1); }
  35% { transform: scale(1.18); border-color: var(--lumen); color: var(--lumen); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .fly-clone { transition: none; }
  .feed-pulse { animation: none; }
}

/* tracked: range switcher */
.range-row { display: flex; gap: 6px; margin-bottom: 8px; }
.range-btn { flex: 1; border: 1px solid var(--hair); background: transparent; color: var(--muted); font-family: var(--f-mono); font-size: calc(11px * var(--scale)); font-weight: 700; padding: 6px 0; border-radius: 9px; cursor: pointer; }
.range-btn.active { background: var(--emphasis-bg); color: var(--emphasis-fg); border-color: var(--emphasis-bg); }
.range-btn:active { transform: scale(0.95); }

/* tracked: chart with y-axis */
.chart { display: flex; gap: 5px; align-items: stretch; }
.y-axis { display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; min-width: 30px; padding: 1px 0; }
.y-lab { font-family: var(--f-mono); font-size: calc(8px * var(--scale)); color: var(--muted); line-height: 1; }
.chart-main { flex: 1; min-width: 0; }

/* tracked: drag-and-drop reorder */
.track-card { cursor: grab; }
.track-card.dragging { opacity: 0.4; cursor: grabbing; }
.track-card.drag-over { box-shadow: inset 0 2.5px 0 var(--lumen); }

/* Friends column on /feed — same slot the stocks/polls column takes on the deck,
   so it inherits .sidebar's card treatment via the shared .side-head styles. */
.friends-col { display: none; }          /* shown by the grid rules below */
.fc-section { margin-bottom: 20px; }
.fc-section:last-of-type { margin-bottom: 12px; }
.fc-section[hidden] { display: none; }   /* `hidden` alone loses to any display rule */
.fc-row {
  display: grid; grid-template-columns: 28px 1fr auto; align-items: center; gap: 9px;
  padding: 7px 4px; border-radius: 10px; text-decoration: none;
}
.fc-row:hover { background: var(--soft); }
.fc-av {
  width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
  background: var(--emphasis-bg); color: var(--emphasis-fg);
  font-family: var(--f-head); font-weight: 800; font-size: 11px;
}
.fc-who { min-width: 0; }
.fc-name {
  font-family: var(--f-head); font-weight: 700; font-size: calc(12.5px * var(--scale));
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fc-sub { font-family: var(--f-mono); font-size: calc(9.5px * var(--scale)); color: var(--blurb-ink); }
.fc-streak {
  font-family: var(--f-mono); font-size: calc(10.5px * var(--scale)); font-weight: 700;
  color: var(--ink); white-space: nowrap;   /* gold is 2.97:1 here — the flame is the colour */
}
.fc-streak.dim { color: var(--blurb-ink); }
.fc-acts { display: flex; gap: 5px; }
.fc-more {
  display: inline-block; font-family: var(--f-mono); font-size: calc(10.5px * var(--scale));
  color: var(--muted); text-decoration: none; border-bottom: 1px dotted var(--hair);
}
.fc-more:hover { color: var(--ink); }

@media (min-width: 1024px) {
  /* 2-column grid: stats sidebar flanks the cards; brand centered above them.
     Columns are flexible (minmax) so they widen to fill the screen.

     Scoped to `.layout-deck` on purpose. The grid-template-areas below only name
     children that index.html has. Any other page dropped into a bare `.layout`
     would have its <header> yanked into the `brand` cell (a huge centred logo in
     the middle of the screen) and its <main> auto-placed into the narrow `stats`
     column. Sub-pages want the plain centred column instead. */
  .layout-deck {
    max-width: 1280px; padding: 14px 24px 18px;
    display: grid; justify-content: center; column-gap: 26px; row-gap: 10px;
    grid-template-columns: minmax(300px, 1fr) minmax(520px, 680px);
    /* The whole deck page is meant to be readable at one glance, with only the
       legal footer (which lives OUTSIDE this grid) below the fold. min-height
       pins the grid to exactly one screen; the deck row is `1fr` so it absorbs
       whatever the furniture above and below doesn't use.

       It used to be clamp(330px, 44vh, 480px) — a height that cannot shrink, so
       it had no way to give room back when the furniture grew (the PLACE/Topics
       select-row turned `.scopes` into two stacked rows, +61px). The grid then
       simply grew past the viewport and pushed the quote off the bottom. A
       flexible row can't drift out of sync with the furniture the way a fixed
       one silently did.

       The 300px floor is the point below which a card clips badly; past that the
       page scrolls, which is the honest outcome on a genuinely short window. */
    min-height: 100vh;
    grid-template-rows: auto auto auto minmax(300px, 1fr) auto auto;
    grid-template-areas:
      ".     brand"
      "stats scopes"
      "stats daily"
      "stats deck"
      "stats controls"
      ".     quote";
  }
  /* Tighter than the mobile default: on desktop the quote is the last thing
     between the controls and the fold, and 24px was part of what pushed it off. */
  .layout-deck .quote { grid-area: quote; margin-top: 12px; }
  .layout-deck .topbar { grid-area: brand; justify-content: center; text-align: center; padding-top: 2px; }
  .layout-deck .topbar .streak { display: none; }   /* the sidebar shows the streak on desktop */
  .layout-deck .brand { text-align: center; }
  .layout-deck .brand .logo { font-size: calc(52px * var(--scale)); }
  .layout-deck .brand .tagline { font-size: calc(12px * var(--scale)); margin-top: 8px; }
  .layout-deck .scopes { grid-area: scopes; }
  .layout-deck .daily { grid-area: daily; }
  /* The card fills its 1fr row up to this ceiling. 480px used to leave a chunk of
     the row empty on taller screens (the card stopped growing while the row kept
     going), which read as wasted space and squeezed the blurb. 600px lets the
     card take the slack and show a couple more sentences; past ~600 a short blurb
     would just leave a gap inside the card, so it's still bounded. On a short
     screen the row is under 600 and this doesn't bind, so the quote still fits. */
  .layout-deck .deck { grid-area: deck; min-height: 0; height: 100%; max-height: 600px; }
  .layout-deck .controls { grid-area: controls; }
  .layout-deck .sidebar { display: block; }
  .hint-desk { display: inline; }
  .hint-touch { display: none; }

  /* Feed is the other "main" page, so it wears the deck's shell exactly: same
     centred wordmark, same stats sidebar on the left, same content column
     width. It's the deck grid minus the scopes/daily/controls/quote rows. */
  .layout-side {
    max-width: 1280px; padding: 18px 24px 26px;
    display: grid; justify-content: center; column-gap: 26px; row-gap: 12px;
    grid-template-columns: minmax(300px, 1fr) minmax(520px, 680px);
    grid-template-areas:
      ".     brand"
      "stats feed";
  }
  .layout-side .topbar { grid-area: brand; justify-content: center; text-align: center; padding-top: 2px; }
  .layout-side .brand { text-align: center; }
  .layout-side .brand .logo { font-size: calc(52px * var(--scale)); }
  .layout-side .brand .tagline { font-size: calc(12px * var(--scale)); margin-top: 8px; }
  .layout-side .sidebar { grid-area: stats; display: block; align-self: start; }
  .layout-side .feed-wrap { grid-area: feed; }
}

@media (min-width: 1340px) {
  /* 3-column grid: stats | cards | pins, all sharing the cards' row.
     Scoped to .layout-deck for the same reason as the 1024px block — a bare
     `.layout` here would re-break every sub-page above 1340px. */
  .layout-deck {
    max-width: 1640px;
    grid-template-columns: minmax(300px, 1fr) minmax(560px, 720px) minmax(300px, 1fr);
    grid-template-areas:
      ".     brand    ."
      "stats scopes   pins"
      "stats daily    pins"
      "stats deck     pins"
      "stats controls pins"
      ".     quote    .";
  }
  .layout-deck .tracked-col { display: block; }

  /* Feed's third column carries the friends list, where the deck puts stocks and
     polls. Keeping all three columns is also what lands the wordmark on the
     page's centre line and holds the stats column at ~300px. */
  .layout-side {
    max-width: 1640px;
    grid-template-columns: minmax(300px, 1fr) minmax(560px, 720px) minmax(300px, 1fr);
    grid-template-areas:
      ".     brand ."
      "stats feed  pins";
  }
  .layout-side .friends-col { display: block; grid-area: pins; align-self: start; }
}

@media (max-width: 420px) {
  .card.weather h2 { font-size: calc(40px * var(--scale)); }
}

/* Legal footer — Impressum + Datenschutz. Bottom of the page, horizontally
   centred, never over content. Same on every page that has it: the deck and
   /login carry it outside .layout (full page width), the legal + error pages
   inside a centred .layout — text-align:center lands it on the page's centre
   line either way. */
.site-foot {
  padding: 20px 24px 32px;
  font-family: var(--f-mono);
  font-size: calc(11px * var(--scale));
  letter-spacing: 0.4px;
  color: var(--muted);
  text-align: center;
}
.site-foot a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px dotted var(--hair);
  padding-bottom: 1px;
}
.site-foot a:hover { color: var(--ink); border-bottom-color: var(--ink); }
.site-foot-links { display: inline-flex; gap: 10px; align-items: center; }
.site-foot-sep { opacity: 0.5; }

/* Legal pages (Impressum, Datenschutz) — narrow reading column with
   typographic hierarchy that matches the rest of the app. */
.legal-page {
  max-width: 720px;
  margin: 0 auto;
  padding: 8px 4px 40px;
  font-family: var(--f-body);
  color: var(--ink);
  line-height: 1.65;
}
.legal-title {
  font-family: var(--f-head); font-weight: 800;
  font-size: calc(30px * var(--scale)); line-height: 1.15;
  margin: 4px 0 20px;
}
.legal-placeholder {
  padding: 12px 14px; border-radius: 10px;
  background: rgba(244, 180, 0, 0.08);
  border: 1px solid rgba(244, 180, 0, 0.25);
  color: var(--ink); margin-bottom: 24px;
  font-size: calc(13.5px * var(--scale));
}
.legal-placeholder strong { color: var(--ink); }
/* "Stand: <date>" on a legal page. --muted measured 2.97:1 on --mist, under the
   4.5:1 AA floor for text this small — and the date a policy last changed is not
   decoration. --blurb-ink clears it while still reading as secondary. */
.legal-stand {
  font-family: var(--f-mono); font-size: calc(11px * var(--scale));
  letter-spacing: 0.6px; color: var(--blurb-ink); margin: 0 0 22px;
}
.legal-section { margin-bottom: 22px; }
/* The privacy policy has sub-headings and lists; the Impressum didn't. */
.legal-section h3 {
  font-family: var(--f-head); font-weight: 700;
  font-size: calc(15px * var(--scale)); margin: 18px 0 8px;
}
.legal-section ul { margin: 8px 0 0; padding-left: 20px; }
.legal-section li { margin-bottom: 8px; line-height: 1.55; }
.legal-section h2 {
  font-family: var(--f-head); font-weight: 700;
  font-size: calc(16px * var(--scale)); letter-spacing: 0;
  margin: 0 0 8px; color: var(--ink);
}
.legal-section p {
  margin: 0 0 10px;
  font-size: calc(14px * var(--scale));
  color: var(--ink);
}
.legal-section a { color: var(--ink); border-bottom: 1px dotted var(--hair); text-decoration: none; }
.legal-section a:hover { border-bottom-color: var(--ink); }

/* Below the desktop grid breakpoint (phones, tablets, split-screen windows)
   the sidebar (streak + stats) and tracked column (stocks + polls) stack
   below the deck instead of being hidden. They grow naturally here — no
   fixed height, no internal scroll — so the page just gets longer and the
   reader scrolls to reach them. The desktop grid rules above win at ≥1024px
   because they come later in cascade order for those widths (and also set
   height back to the fixed clamp). */
@media (max-width: 1023.98px) {
  .sidebar, .tracked-col, .friends-col {
    display: block;
    height: auto;
    overflow-y: visible;
    margin-top: 16px;
  }
  /* Below the grid breakpoint nothing is pinned to a viewport corner any more —
     the controls collapse into one row under the centred wordmark, so centre
     them with it instead of leaving them hugging the right edge. They wrap on
     the narrowest phones rather than overflowing. */
  .topbar-controls { justify-content: center; flex-wrap: wrap; row-gap: 8px; }
  .user-menu { margin-right: 0; }
}

/* ---- intro tour overlay ----
   Full-screen dim with a radial-gradient mask that punches a soft "spotlight"
   hole at (--x, --y) of radius --r around whatever element the current step
   highlights. The bubble carries the caption + progress dots; a Skip button
   sits top-right. When the tour reaches the Options-panel demo step, the JS
   adds .no-mask so the whole overlay just dims uniformly (no spotlight) and
   the panel opens on top of that. */
.intro-overlay {
  position: fixed; inset: 0; z-index: 1200;
  background: rgba(0, 0, 0, 0.62);
  display: none; pointer-events: auto;
  --x: 50%; --y: 50%; --r: 0px;
  -webkit-mask-image: radial-gradient(circle at var(--x) var(--y), transparent var(--r), rgba(0,0,0,0.05) calc(var(--r) + 12px), black calc(var(--r) + 56px));
          mask-image: radial-gradient(circle at var(--x) var(--y), transparent var(--r), rgba(0,0,0,0.05) calc(var(--r) + 12px), black calc(var(--r) + 56px));
  transition: -webkit-mask-image 0.28s ease, mask-image 0.28s ease, background 0.2s ease;
}
.intro-overlay.show { display: block; }
.intro-overlay.no-mask {
  -webkit-mask-image: none; mask-image: none;
  background: rgba(0, 0, 0, 0.45);
}
/* Dark mode dims MORE aggressively — the underlying page is already deep ink,
   so the default rgba(0,0,0,0.62) barely registers and the spotlight doesn't
   stand out. Bumping toward 0.9 gives the highlighted element clear visual
   separation from the rest of the screen. */
:root[data-theme="dark"] .intro-overlay          { background: rgba(0, 0, 0, 0.9); }
:root[data-theme="dark"] .intro-overlay.no-mask  { background: rgba(0, 0, 0, 0.75); }
/* Panel steps: the tour adds NO dim of its own, because the panel underneath
   brings its own scrim (see .panel.intro-lifted). Without this the screen would
   be dimmed twice and the panel would read muddy.
   The dark-mode variant is listed explicitly: `:root[data-theme=dark]
   .intro-overlay.no-mask` is more specific than a bare `.intro-overlay.clear`,
   so a single-selector rule here would silently lose in dark mode. */
.intro-overlay.clear,
:root[data-theme="dark"] .intro-overlay.clear { background: transparent; }
/* Panel demo steps. This used to read `z-index: 1210; background: transparent`,
   to lift the panel above the intro's dim — with the note "Bubble + Skip button
   already sit above this via higher z-index". They do NOT, and that was the bug:
   the bubble is a CHILD of .intro-overlay, which has z-index 1200 and therefore
   forms a stacking context. Its 1221 only ranks it INSIDE that context; against a
   sibling at 1210 the whole overlay competes as 1200 — and loses. The panel then
   painted straight over the caption (on a phone, where the panel sheet is tall,
   the text was half-covered and unreadable).

   The fix is to stop fighting the order: the panel keeps its natural z-index and
   its OWN scrim (.panel's rgba(21,23,28,0.5)), and the intro overlay goes
   transparent for these steps (.intro-overlay.clear, just above). The screen is
   still dimmed — by the panel rather than the tour — the panel reads at full
   brightness, and the bubble is above it because 1200 > 40. No child ever has to
   escape its parent. TestIntroStacking guards this; don't give .panel a z-index
   above .intro-overlay's again.

   `background: transparent` is gone too: that existed to stop the panel's scrim
   stacking on the tour's, but now the panel's scrim IS the dim. .intro-lifted
   survives purely as the hook for the entrance animation below. */
/* Smoother, longer entrance for the panel-inner during the intro — a gentle
   fade-and-lift so the panel doesn't feel like it slams into view. */
.panel.intro-lifted .panel-inner {
  animation: intro-panel-in 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes intro-panel-in {
  from { transform: translateY(24px) scale(0.985); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.intro-quit {
  position: fixed; bottom: 22px; right: 26px; z-index: 1222;
  border: 1px solid rgba(255,255,255,0.35); background: rgba(0,0,0,0.35);
  color: #fff; font-family: var(--f-mono); font-weight: 700;
  font-size: calc(12px * var(--scale)); letter-spacing: 1px; text-transform: uppercase;
  padding: 9px 16px; border-radius: 999px; cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.intro-quit:hover { border-color: var(--lumen); color: var(--lumen); }
/* In-panel intro highlight: draws the eye to ONE part of an open panel — the
   topic toggles, then the boost menu — rather than lighting the whole panel,
   which read as "everything" and told the reader nothing. A gold ring + gentle
   pulse; the element keeps its place in the panel. Used by the topics/boost
   steps (focusTopics / focusBoost) and by the cross-page guided stops. */
.intro-focus {
  position: relative; z-index: 1; border-radius: 14px;
  box-shadow: 0 0 0 3px var(--lumen), 0 0 0 9px rgba(244,180,0,0.22);
  animation: introFocusPulse 1.5s ease-in-out infinite;
}
@keyframes introFocusPulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--lumen), 0 0 0 9px rgba(244,180,0,0.22); }
  50%      { box-shadow: 0 0 0 3px var(--lumen), 0 0 0 14px rgba(244,180,0,0.08); }
}

.intro-bubble {
  position: fixed; z-index: 1221; left: 50%; transform: translateX(-50%);
  background: var(--paper); color: var(--ink); border-radius: 18px;
  box-shadow: 0 12px 30px rgba(0,0,0,0.35), 0 0 0 1px rgba(244,180,0,0.15);
  padding: 14px 20px 12px; max-width: min(420px, calc(100vw - 40px));
  text-align: center;
  transition: top 0.28s ease, opacity 0.2s ease;
  opacity: 0;
}
.intro-overlay.show .intro-bubble { opacity: 1; }
.intro-caption {
  font-family: var(--f-body); font-weight: 600;
  font-size: calc(14px * var(--scale)); color: var(--ink); line-height: 1.4;
  margin-bottom: 10px;
}
.intro-dots { display: flex; justify-content: center; gap: 6px; }
.intro-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--hair); transition: background 0.2s ease, transform 0.2s ease;
}
.intro-dot.active { background: var(--lumen); transform: scale(1.4); }
@media (max-width: 480px) {
  .intro-quit { bottom: 14px; right: 14px; padding: 7px 12px; font-size: calc(10.5px * var(--scale)); }
  .intro-bubble { padding: 12px 16px 10px; }
}
