/* ============================================================
   Interview Questions Bank — layout, components, cards, tabs,
   search, sidebar, responsive, print. (Consolidated for a
   reliable first deploy; split further later if desired.)
   ============================================================ */

/* ---------- app shell ---------- */
/* Scroll anchoring rewrites scrollY whenever something above the viewport resizes.
   The collapsing header and the expanding answer cards both do that constantly, so
   the browser's correction fights our own scroll handling instead of helping it.
   We position those two cases explicitly (see initCollapsibleHeader / toggleCard). */
html { overflow-anchor: none; }
.app { display: flex; flex-direction: column; min-height: 100vh; }

/* ---------- header ---------- */
.header-shell {
  position: sticky; top: 0; z-index: 40;
  overflow: hidden;
  --header-shell-h: calc(var(--header-h) + var(--tabs-h));
  --header-shell-h-collapsed: var(--header-h);
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(12px);
  box-shadow: none;
  transition: box-shadow .24s ease;
  will-change: transform;
}
.header-shell.is-collapsed {
  box-shadow: 0 8px 24px rgba(32, 26, 28, 0.06);
}
.site-header {
  position: relative; z-index: 2;
  min-height: var(--header-h);
  display: flex; align-items: center; gap: 16px;
  padding: 0 clamp(14px, 3vw, 28px);
  border-bottom: 1px solid var(--border);
  transition: padding .24s ease, gap .24s ease;
}
.header-shell.is-collapsed .site-header {
  padding-top: 9px;
  padding-bottom: 9px;
  gap: 10px;
}
.header-shell.is-collapsed .brand-name {
  font-size: 20px;
}
.header-shell.is-collapsed .header-search {
  max-width: 280px;
}
.header-shell.is-collapsed .header-search input {
  height: 34px;
}
.header-shell.is-collapsed .auth-btn,
.header-shell.is-collapsed .icon-btn {
  transform: scale(0.96);
}
.brand { display: flex; align-items: center; gap: 10px; font-weight: 800; letter-spacing: -.02em; }
/* The app mark — carries the brand alone on mobile (see the responsive block).
   Drawn as a mask rather than an <img> so the glyph takes var(--accent): the
   source art is a fixed crimson authored for white paper, which would sit at
   ~3:1 on the dark theme while every other accent around it lifted to #FF6B80.
   The mask is the artwork's alpha only, so the colour is ours to choose. */
.brand-mark {
  display: none; flex: 0 0 auto;
  width: 30px; height: 30px;
  background: var(--accent);
  -webkit-mask: url("../assets/logo-mask.png") center / contain no-repeat;
  mask: url("../assets/logo-mask.png") center / contain no-repeat;
}
/* desktop shows the wordmark with the mark beside it */
@media (min-width: 901px) { .brand-mark { display: block; } }
/* the count rides on the corner of the funnel icon */
.filter-btn { position: relative; overflow: visible; }
.filter-count {
  position: absolute; top: -5px; right: -5px;
  display: grid; place-items: center; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: var(--r-pill); background: var(--accent); color: var(--accent-contrast);
  font-family: var(--mono); font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums;
  border: 2px solid var(--bg);
}
.filter-count[hidden] { display: none; }
.filter-btn.has-active { border-color: var(--accent); color: var(--accent); }
.brand-logo {
    width: 30px;
    height: 30px;
    display: block;
    flex-shrink: 0;
}
.brand-name { font-size: 26px; }
.brand-name span { color: var(--accent); }
.header-count {
  font-family: var(--mono); font-size: var(--step--1); color: var(--muted);
  padding: 3px 9px; border: 1px solid var(--border); border-radius: var(--r-pill);
  white-space: nowrap;
}
.header-spacer { flex: 1; }
.header-search { position: relative; flex: 1 1 240px; max-width: 340px; min-width: 0; }
.header-search input {
  width: 100%; height: 38px; padding: 0 34px 0 34px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface); color: var(--ink); font-size: var(--step--1);
  transition: border-color var(--trans), box-shadow var(--trans);
}
.header-search input:focus { border-color: var(--accent); box-shadow: var(--focus); }
.header-search .s-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 14px; }
.header-search .s-kbd {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  border: 1px solid var(--border); border-radius: 5px; padding: 1px 5px; background: var(--surface-2);
}
.icon-btn {
  width: 38px; height: 38px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); display: grid; place-items: center;
  font-size: 16px; transition: var(--trans); flex: 0 0 auto;
}
.icon-btn:hover { border-color: var(--accent); color: var(--accent); }
.icon-btn.signed-in { border-color: var(--accent); color: var(--accent); font-weight: 800; }

/* Mobile-only header controls — off by default, switched on in the <=900px
   block. Declared AFTER .icon-btn: they carry that class too, so an earlier
   `display: none` would just lose to `.icon-btn { display: grid }`. */
.search-close, .search-toggle, .filter-btn { display: none; }

/* ---------- auth pill button ---------- */
.auth-btn {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  height: 36px; padding: 0 16px;
  border-radius: var(--r-pill);
  border: none;
  background: #2563eb; color: #fff;
  font-size: var(--step--1); font-weight: 700;
  cursor: pointer; transition: background var(--trans), box-shadow var(--trans), transform var(--trans);
  box-shadow: 0 1px 4px rgba(37,99,235,.35);
}
.auth-btn:hover { background: #1d4ed8; box-shadow: 0 3px 10px rgba(37,99,235,.45); transform: translateY(-1px); }
.auth-btn:active { transform: translateY(0); box-shadow: 0 1px 3px rgba(37,99,235,.3); }

/* signed-out: show label, hide avatar */
.auth-btn .auth-btn-label { display: inline-flex; align-items: center; gap: 6px; }
.auth-btn .auth-btn-user  { display: none; }

/* signed-in: shrink to icon-only square, show avatar, hide label */
.auth-btn.signed-in {
  width: 36px; padding: 0;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--accent);
  color: var(--accent);
  box-shadow: none;
  overflow: hidden;
}
.auth-btn.signed-in:hover { background: var(--accent-soft); box-shadow: none; transform: none; }
.auth-btn.signed-in .auth-btn-label { display: none; }
.auth-btn.signed-in .auth-btn-user  { display: inline-flex; }

/* ---------- account dropdown ---------- */
.auth-menu {
  position: fixed; z-index: 80; width: min(320px, calc(100vw - 24px));
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  /* x hidden keeps the rounded corners clipping; y auto lets a long progress
     list scroll rather than run off the bottom (positionMenu caps the height) */
  overflow: hidden auto;
  overscroll-behavior: contain;
}
.auth-head {
  display: flex; align-items: center; gap: 12px; padding: 16px 16px 14px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 16%, var(--surface)), var(--surface));
  border-bottom: 1px solid var(--border);
}
.auth-avatar {
  width: 44px; height: 44px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; overflow: hidden;
  background: var(--accent); color: #fff; font-weight: 800; font-size: 18px;
}
.auth-avatar img { width: 100%; height: 100%; object-fit: cover; }
.auth-id { min-width: 0; }
.auth-name { font-weight: 700; color: var(--ink); font-size: var(--step-0); line-height: 1.2; }
.auth-email { font-size: var(--step--1); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.auth-progress { padding: 14px 16px 4px; }
.auth-progress-title {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--beginner); font-weight: 700; margin-bottom: 10px;
}
.auth-total {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; margin-bottom: 12px; border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--beginner) 35%, var(--border));
  background: color-mix(in srgb, var(--beginner) 8%, var(--surface));
  font-weight: 700; color: var(--beginner);
}
.auth-total span { color: var(--ink); font-weight: 600; font-size: var(--step--1); }
.auth-total strong { font-size: var(--step-2); font-variant-numeric: tabular-nums; }

.auth-row { margin-bottom: 12px; }
.auth-row-top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.auth-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.auth-row-label { font-size: var(--step--1); color: var(--ink); font-weight: 600; }
.auth-row-n { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.auth-bar { height: 6px; border-radius: 100px; background: var(--surface-3); overflow: hidden; }
.auth-bar-fill { height: 100%; border-radius: 100px; transition: width .4s ease; }

/* entries into the reports panels (js/reports.js) — "My reported issues" for
   any signed-in reader, "Issue reports" for an allowlisted admin */
.auth-link {
  width: 100%; padding: 13px; border: none; border-top: 1px solid var(--border);
  background: none; color: var(--ink); font-weight: 700; font-size: var(--step--1);
  text-align: left;
  cursor: pointer; transition: var(--trans);
}
.auth-link:hover { background: var(--surface-2); color: var(--accent); }
.auth-link[hidden] { display: none; }

.auth-signout {
  width: 100%; padding: 13px; border: none; border-top: 1px solid var(--border);
  background: none; color: var(--advanced); font-weight: 700; font-size: var(--step--1);
  cursor: pointer; transition: var(--trans);
}
.auth-signout:hover { background: color-mix(in srgb, var(--advanced) 8%, transparent); }

/* ---------- sign-in prompt (dismissible) ---------- */
.login-prompt {
  position: fixed; right: 64px; bottom: 18px; z-index: 75;
  width: min(340px, calc(100vw - 28px));
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg); padding: 16px 16px 14px;
  opacity: 0; transform: translateY(16px); transition: opacity .25s, transform .25s;
}
.login-prompt.show { opacity: 1; transform: translateY(0); }
.lp-close {
  position: absolute; top: 8px; right: 8px; width: 26px; height: 26px; border: none;
  background: none; color: var(--muted); font-size: 13px; cursor: pointer; border-radius: 6px;
}
.lp-close:hover { background: var(--surface-2); color: var(--ink); }
.lp-title { font-weight: 800; color: var(--ink); font-size: var(--step-0); margin-bottom: 6px; }
.lp-text { font-size: var(--step--1); color: var(--ink-2); line-height: 1.5; margin-bottom: 14px; }
.lp-actions { display: flex; gap: 8px; align-items: center; }
.lp-signin {
  flex: 1 1 auto; padding: 9px 12px; border-radius: var(--r-pill); border: none;
  background: var(--accent); color: #fff; font-weight: 700; font-size: var(--step--1); cursor: pointer;
  transition: var(--trans);
}
.lp-signin:hover { filter: brightness(1.08); }
.lp-signin.is-busy, .auth-btn.is-busy {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  cursor: default; filter: none; opacity: .85;
}
.btn-spinner {
  width: 13px; height: 13px; flex: 0 0 auto; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: btn-spin .7s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .btn-spinner { animation-duration: 2s; } }
.lp-later {
  flex: 0 0 auto; padding: 9px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--muted);
  font-size: var(--step--1); cursor: pointer; transition: var(--trans);
}
.lp-later:hover { border-color: var(--border-strong); color: var(--ink); }

/* Below 900px the sidebar and tabs bar are gone and the bottom-right corner this
   card was anchored to belongs to .reading-mode-toggle (z-index 120) and
   .scroll-to-top (z-index 90) — which is what the earlier right/bottom nudges were
   dodging. Centering clears them outright and gives the one-and-only sign-in ask
   the weight it deserves. The dim is a box-shadow spread (the same trick
   .tour-spotlight uses) rather than a z-index:-1 pseudo-element, because negative
   children paint over their parent's background and would tint the card itself.
   It deliberately does NOT block the page: the copy calls itself optional. */
@media (max-width: 900px) {
  .login-prompt {
    top: 50%; left: 50%; right: auto; bottom: auto;
    z-index: 130; /* over .reading-mode-toggle so the dim covers it too */
    width: min(360px, calc(100vw - 32px));
    padding: 20px 18px 16px;
    border-color: var(--accent);
    transform: translate(-50%, calc(-50% + 14px)) scale(.97);
    box-shadow:
      0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent),
      var(--shadow-lg),
      0 0 0 9999px rgba(15, 23, 42, .55);
  }
  .login-prompt.show { transform: translate(-50%, -50%) scale(1); }
  .lp-title { font-size: var(--step-1); }
  /* stack full-width so the primary action leads and nothing is cramped */
  .lp-actions { flex-direction: column; align-items: stretch; gap: 9px; }
  .lp-signin, .lp-later { width: 100%; padding: 12px; }
}

/* ---------- hero ---------- */
.hero { padding: clamp(30px, 6vw, 60px) clamp(14px, 3vw, 28px) clamp(22px, 3vw, 34px); border-bottom: 1px solid var(--border); }
.hero-inner { max-width: var(--maxw); margin: 0 auto; }
.hero-eyebrow {
  font-family: var(--mono); font-size: var(--step--1); text-transform: uppercase;
  letter-spacing: .16em; color: var(--accent); font-weight: 600; margin-bottom: 14px;
}
.hero h1 { font-size: var(--step-4); font-weight: 850; }
.hero h1 span { color: var(--accent); }
.hero-sub { color: var(--muted); font-size: var(--step-1); margin-top: 14px; max-width: 60ch; }
.hero-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.hero-pill {
  font-family: var(--mono); font-size: var(--step--1); padding: 6px 13px;
  border-radius: var(--r-pill); background: var(--surface); border: 1px solid var(--border);
  color: var(--ink-2); transition: var(--trans);
}
.hero-pill:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); }
.hero-stats { display: flex; flex-wrap: wrap; gap: 26px; margin-top: 26px; }
.hero-stat b { display: block; font-size: var(--step-2); font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.hero-stat span { font-size: var(--step--1); color: var(--muted); font-family: var(--mono); }

/* ---------- category tabs ---------- */
.tabs-wrap {
  position: relative;
  z-index: 1;
  max-height: 64px;
  overflow: hidden;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--border);
  opacity: 1;
  transform: translateY(0);
  transition: max-height .24s ease, opacity .24s ease, transform .24s ease, border-color .24s ease;
}
.header-shell.is-collapsed .tabs-wrap {
  max-height: 0;
  opacity: 0;
  transform: translateY(-6px);
  border-bottom-color: transparent;
  pointer-events: none;
}
.tabs {
  max-width: var(--maxw); margin: 0 auto; display: flex; gap: 6px;
  padding: 9px clamp(14px, 3vw, 28px); overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; border-radius: var(--r-pill); font-size: var(--step--1); font-weight: 600;
  color: var(--ink-2); border: 1px solid transparent; white-space: nowrap; transition: var(--trans);
}
.tab .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--tab-c, var(--muted)); }
.tab .tab-n { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.tab:hover { background: var(--surface-2); }
.tab.active { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }
.tab.active .tab-n { color: color-mix(in srgb, var(--accent-contrast) 80%, transparent); }
.tab.active .dot { background: var(--accent-contrast); }

/* ---------- main grid: sidebar + content ---------- */
.main { max-width: var(--maxw); margin: 0 auto; width: 100%; display: grid; grid-template-columns: var(--sidebar-w) 1fr; gap: 30px; padding: 26px clamp(14px, 3vw, 28px) 80px; }
/* grid/flex items default to min-width:auto (= min-content), which lets a card's
   content refuse to shrink and blow the 1fr column past the viewport — that
   horizontal overflow makes phones zoom out and render the desktop layout.
   Forcing min-width:0 lets the column shrink; long code blocks scroll inside <pre>. */
.main > * { min-width: 0; }

.sidebar { position: sticky; top: calc(var(--header-offset) + 6px); align-self: start; height: fit-content; }
.sidebar-title { font-family: var(--mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin: 4px 0 10px 12px; }
.side-link {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; padding: 8px 12px; border-radius: var(--r-sm);
  color: var(--ink-2); font-size: var(--step--1); font-weight: 600; transition: var(--trans);
  border-left: 3px solid transparent;
}
.side-link .s-count { font-family: var(--mono); font-size: 11px; color: var(--muted); }
/* label + mark share a row; min-width:0 lets a long category name ellipsis
   rather than shove the count out of the sidebar */
.side-link-label { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.side-link-label > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-link .cat-ic { width: 15px; height: 15px; }
.side-link:hover { background: var(--surface-2); }
/* Selected has to differ from :hover by HUE, not lightness. These two used to share
   --surface-2 outright, so a merely hovered row looked exactly as selected as the
   real one — and neither separated from the page behind it (1.08:1). Lightness
   can't fix that (even a 22% tint only reaches 1.20:1 on this palette), and a solid
   fill like .tab.active uses isn't available either: white on the lighter category
   accents (JavaScript #B4820A) is 3.4:1, under AA for label-sized text. A tint of
   the row's own category colour reads unmistakably as "this one" while keeping the
   label above 13:1, and hover stays neutral so the two can never be confused. */
.side-link.active {
  background: color-mix(in srgb, var(--side-c, var(--accent)) 14%, var(--surface));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--side-c, var(--accent)) 28%, transparent);
  color: var(--ink);
  font-weight: 700;
  border-left-color: var(--side-c, var(--accent));
}
/* The raw accent is only 2.96:1 here at 11px (JavaScript's #B4820A is the floor),
   and this is a real number, not decoration. Mixing toward --ink keeps the category
   hue and self-corrects per theme — --ink darkens the accent on light, lightens it
   on dark. 55% clears AA on both (6.16 light / 4.97 dark); 60% lands exactly on 4.50
   with no headroom for a future category, and 65% fails dark outright at 4.13. */
.side-link.active .s-count {
  color: color-mix(in srgb, var(--side-c, var(--accent)) 55%, var(--ink));
  font-weight: 700;
}
.side-group {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; text-align: left; padding: 10px 12px 5px; margin-top: 8px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; transition: var(--trans);
}
.side-group:hover,
.side-group.active { color: var(--side-c, var(--accent)); }
.side-group .s-count { font-family: var(--mono); font-size: 10px; letter-spacing: 0; }
.side-sub { padding-left: 22px; font-weight: 500; }
.side-sep { height: 1px; background: var(--border); margin: 12px 8px; }

/* ---------- category tech marks (js/icons.js) ----------
   One solid 24x24 path per category, tinted with that category's manifest accent
   via the --ic custom property app.js sets inline. Sized in em so each mark tracks
   whatever text it sits beside. Groups never get one. */
.cat-ic {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; color: var(--ic, currentColor);
}
.cat-ic svg { width: 100%; height: 100%; display: block; }

/* ---------- content ---------- */
.content-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-bottom: 16px; }
.content-title { font-size: var(--step-2); }
.content-title .cat-ic { width: 1em; height: 1em; margin-right: .38em; vertical-align: -.1em; }
.content-title small { font-family: var(--mono); font-size: var(--step--1); color: var(--muted); font-weight: 500; margin-left: 6px; }
.content-tools { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.tool {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: var(--step--1); padding: 6px 11px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-2);
  transition: background var(--trans), border-color var(--trans), color var(--trans), transform var(--trans), box-shadow var(--trans);
}
.tool svg {
  transition: transform var(--trans);
}
.tool:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  transform: translateY(-1.5px);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 8%, transparent);
}
.tool:active {
  transform: translateY(0) scale(0.96);
  box-shadow: none;
}
.tool.on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-ink);
}
/* The theme switch only joins the tools on mobile, where it is evicted from the
   header for space. Declared after .tool so it beats that rule's inline-flex. */
.tool-theme { display: none; }
.tool.on:hover {
  background: color-mix(in srgb, var(--accent) 15%, var(--surface));
}
.tool:hover svg {
  transform: scale(1.1);
}
.tool#random-btn:hover svg {
  transform: scale(1.1) rotate(15deg);
}
.tool#expand-all:hover svg {
  transform: scale(1.1) translateY(1px);
}
.tool#collapse-all:hover svg {
  transform: scale(1.1) translateY(-1px);
}

/* ---------- filters & tools sheet ----------
   Desktop: the panel is `display: contents`, so .content-tools stays a direct
   child of .content-head and lays out exactly as it did before this existed.
   The trigger, sheet chrome and backdrop are all off. Under 900px the panel
   becomes a bottom sheet — see the responsive block. */
.tools-panel { display: contents; }
.filter-btn,
.tools-sheet-head,
.tools-sheet-foot,
.tools-backdrop { display: none; }

/* mobile/tablet topic selector — hidden on desktop (sidebar covers it) */
.subnav { display: none; }
.subchip {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: var(--step--1); padding: 7px 13px;
  border-radius: var(--r-pill); border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-2); white-space: nowrap; transition: var(--trans);
}
.subchip .cat-ic { width: 13px; height: 13px; }
.subchip:hover { border-color: var(--sc, var(--accent)); color: var(--sc, var(--accent)); }
/* --on-cat, not #fff: the category colours invert per theme (dark on light, light
   on dark), so a pinned white label drops to 2.7:1 in the dark theme. */
.subchip.active { background: var(--sc, var(--accent)); border-color: var(--sc, var(--accent)); color: var(--on-cat); }
/* the active chip paints --sc as its background, so the mark has to stop being --sc */
.subchip.active .cat-ic { color: var(--on-cat); }
.subchip-n { font-size: 10px; opacity: .7; font-variant-numeric: tabular-nums; }
@media (max-width: 900px) {
  .subnav {
    display: flex; gap: 7px; overflow-x: auto; scrollbar-width: none;
    padding: 2px 0 6px; margin-bottom: 14px;
  }
  .subnav::-webkit-scrollbar { display: none; }
}

.filter-row { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 18px; }
.chip-filter {
  font-family: var(--mono); font-size: var(--step--1); padding: 5px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); transition: var(--trans);
}
.chip-filter:hover { border-color: var(--border-strong); }
.chip-filter.active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* The AI Tutor's curated view chip — accented so it reads as distinct from the
   plain level filters, and carries a count badge of how many questions matched. */
.chip-filter.ai-suggested { border-color: var(--accent); color: var(--accent); }
.chip-filter.ai-suggested:hover { border-color: var(--accent); }
.chip-filter.ai-suggested.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.chip-filter.ai-suggested .chip-n {
  margin-left: 6px; padding: 0 6px; border-radius: var(--r-pill);
  background: color-mix(in srgb, currentColor 18%, transparent); font-size: .85em;
}

/* ---------- JS playground ----------
   One Dark, fixed in both app themes. An editor theme is a thing in its own
   right — people recognise these exact colours — so it deliberately does NOT
   follow the page's light/dark tokens the way .code-block does. */
.playground {
  --od-bg: #282C34;
  --od-gutter: #21252B;
  --od-line: #3E4451;
  --od-fg: #ABB2BF;
  --od-dim: #5C6370;
  --od-comment: #5C6370;
  --od-string: #98C379;
  --od-number: #D19A66;
  --od-keyword: #C678DD;
  --od-builtin: #E5C07B;
  --od-fn: #61AFEF;
  --od-red: #E06C75;

  grid-column: 1 / -1; display: flex; flex-direction: column; min-height: 0;
}
/* Every `display` below would otherwise beat the UA's `[hidden] { display: none }`
   and leave the element on screen — the playground stranded at the foot of every
   question page, or both output and input panels stacked in the same pane. */
.playground[hidden], .pg-panel[hidden] { display: none; }

/* pg-mode hands the whole viewport to the playground: the sidebar, tools and
   mobile filter dropdowns are all question-shaped and mean nothing here, and
   the page itself must not scroll — the editor and output are the scrollers. */
body.pg-mode { overflow: hidden; }
body.pg-mode :is(.sidebar, .content, .mobile-controls, .scroll-to-top) { display: none; }
body.pg-mode .main {
  max-width: none; grid-template-columns: 1fr; gap: 0;
  padding: 0 clamp(10px, 2vw, 18px) clamp(10px, 2vw, 18px);
  height: calc(100vh - var(--header-shell-h));
}
/* a drag must not select the page text it sweeps over */
body.pg-dragging { user-select: none; -webkit-user-select: none; cursor: col-resize; }

.pg-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 14px; padding: 10px 2px; flex: 0 0 auto; }
.pg-title { font-size: var(--step-1); display: flex; align-items: center; gap: 9px; }
.pg-lang {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  padding: 3px 7px; border-radius: 5px;
  background: color-mix(in srgb, var(--playground) 14%, var(--surface));
  color: var(--playground); border: 1px solid color-mix(in srgb, var(--playground) 30%, transparent);
}
.pg-sub { font-size: var(--step--1); color: var(--muted); }
.pg-bar-actions { display: flex; gap: 8px; margin-left: auto; }

/* --pg-split is the editor's width; the splitter is a fixed 8px rail. */
.pg-grid {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: var(--pg-split, 50%) 8px 1fr;
}
.pg-pane {
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  background: var(--od-bg); border: 1px solid var(--od-line);
  border-radius: var(--r-md); overflow: hidden;
}
.pg-pane-head {
  display: flex; align-items: center; gap: 10px; padding: 7px 8px 7px 14px;
  border-bottom: 1px solid var(--od-line); background: var(--od-gutter); flex: 0 0 auto;
}
.pg-pane-name {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--od-dim);
}
.pg-hint { font-family: var(--mono); font-size: 10.5px; color: var(--od-dim); margin-left: auto; }
.pg-status { font-family: var(--mono); font-size: 11px; color: var(--od-dim); margin-left: auto; }
.pg-status.ok { color: var(--od-string); }
.pg-status.err { color: var(--od-red); }
.pg-status.warn { color: var(--od-builtin); }

/* ----- splitter ----- */
.pg-split {
  cursor: col-resize; position: relative; background: none; border: 0;
  touch-action: none; /* pointermove must not be eaten by scroll gestures */
}
.pg-split::before {
  content: ""; position: absolute; inset: 0; margin: auto; width: 2px; height: 34px;
  border-radius: 2px; background: var(--border-strong); transition: var(--trans);
}
.pg-split:hover::before, .pg-split:focus-visible::before { background: var(--playground); height: 60px; }
.pg-split:focus-visible { outline: none; }

/* ----- editor ----- */
.pg-body { flex: 1; min-height: 0; display: flex; overflow: hidden; }
.pg-gutter {
  flex: 0 0 auto; overflow: hidden; background: var(--od-gutter);
  border-right: 1px solid var(--od-line); padding: 12px 0;
  user-select: none; -webkit-user-select: none;
}
.pg-ln {
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  color: var(--od-dim); text-align: right; padding: 0 10px 0 14px; min-width: 46px;
}
.pg-layers { position: relative; flex: 1; min-width: 0; }
/* The two layers must agree on every metric that affects glyph position, or the
   caret drifts away from the text it is supposedly sitting in. */
.pg-hl, .pg-editor {
  position: absolute; inset: 0; margin: 0;
  font-family: var(--mono); font-size: 13px; line-height: 1.7; tab-size: 2;
  padding: 12px 16px; border: 0; white-space: pre; overflow-wrap: normal;
  -webkit-font-smoothing: antialiased;
}
.pg-hl { pointer-events: none; overflow: hidden; color: var(--od-fg); background: none; }
.pg-editor {
  color: transparent; caret-color: var(--od-fg); background: none;
  resize: none; outline: none; overflow: auto;
}
.pg-editor::selection { background: rgba(97, 175, 239, .3); }

/* One Dark token colours */
.pg-hl .t-c { color: var(--od-comment); font-style: italic; }
.pg-hl .t-s { color: var(--od-string); }
.pg-hl .t-n, .pg-hl .t-l { color: var(--od-number); }
.pg-hl .t-k { color: var(--od-keyword); }
.pg-hl .t-b, .pg-hl .t-y { color: var(--od-builtin); }
.pg-hl .t-f { color: var(--od-fn); }

/* ----- output / input ----- */
.pg-out, .pg-stdin {
  flex: 1; min-height: 0; overflow: auto;
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  padding: 12px 16px; color: var(--od-fg); background: none; border: 0;
  -webkit-font-smoothing: antialiased;
}
.pg-out { white-space: pre-wrap; word-break: break-word; }
.pg-line { padding: 1px 0; }
.pg-line.error { color: var(--od-red); }
.pg-line.warn { color: var(--od-builtin); }
.pg-line.info { color: var(--od-fn); }
.pg-line.muted { color: var(--od-dim); font-style: italic; }

.pg-stdin-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.pg-stdin-note {
  font-size: var(--step--1); color: var(--od-dim); padding: 10px 16px; flex: 0 0 auto;
  border-bottom: 1px solid var(--od-line);
}
.pg-stdin { resize: none; outline: none; white-space: pre; }
.pg-stdin::placeholder { color: var(--od-dim); }

.pg-tabs { display: flex; gap: 4px; }
.pg-tab {
  font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 5px; color: var(--od-dim); background: none;
  border: 1px solid transparent; transition: var(--trans);
}
.pg-tab:hover { color: var(--od-fg); }
.pg-tab.active { color: #fff; background: var(--playground); border-color: transparent; }

.pg-run, .pg-clear {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--mono); font-size: 11px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  padding: 6px 12px; border-radius: 6px; transition: var(--trans);
}
.pg-run { background: var(--playground); color: #fff; border: 1px solid transparent; margin-left: 10px; }
.pg-run:hover { background: color-mix(in srgb, var(--playground) 85%, #000); }
.playground.running .pg-run { background: var(--advanced); }
.pg-clear { background: rgba(255, 255, 255, .07); border: 1px solid rgba(255, 255, 255, .16); color: var(--od-dim); }
.pg-clear:hover { color: #fff; border-color: rgba(255, 255, 255, .3); }

/* Under 900px a side-by-side split is unusable and a drag rail is worse — stack
   the panes and let the page scroll normally again. */
@media (max-width: 900px) {
  body.pg-mode { overflow: auto; }
  body.pg-mode .main { height: auto; padding: 0 10px 40px; }
  .pg-grid { grid-template-columns: 1fr; grid-template-rows: minmax(320px, 60vh) minmax(200px, auto); gap: 12px; }
  .pg-split { display: none; }
  .pg-bar-actions { margin-left: 0; }
}

/* ---------- question cards ---------- */
.q-list { display: flex; flex-direction: column; gap: 12px; }
.qa-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md);
  box-shadow: var(--shadow-sm); overflow: hidden; transition: border-color var(--trans), box-shadow var(--trans), transform var(--trans);
  /* keeps scrollIntoView from parking a card underneath the sticky header */
  scroll-margin-top: calc(var(--header-offset) + 12px);
  scroll-margin-bottom: 12px;
}
.qa-card:hover { box-shadow: var(--shadow-md); }
.qa-card.open { border-color: color-mix(in srgb, var(--cat, var(--accent)) 45%, var(--border)); }
.qa-card.done { border-left: 3px solid var(--beginner); }

.qa-head { width: 100%; text-align: left; padding: 15px 16px; display: grid; gap: 9px; cursor: pointer; user-select: text; -webkit-user-select: text; }
/* numbered cards get a left gutter for the .qa-qnum medallion; everything else
   stacks in column 2 so the badges, question and tags stay flush with each other */
.qa-head.numbered { grid-template-columns: 36px 1fr; column-gap: 14px; }
.qa-head.numbered > :not(.qa-qnum) { grid-column: 2; }
.qa-qnum {
  grid-column: 1; grid-row: 1 / -1; align-self: center;
  width: 36px; height: 36px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--muted);
  user-select: none; -webkit-user-select: none;
  transition: background var(--trans), border-color var(--trans), color var(--trans);
}
.qa-card.open .qa-qnum {
  background: color-mix(in srgb, var(--cat, var(--accent)) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--cat, var(--accent)) 40%, transparent);
  color: var(--cat, var(--accent));
}
.qa-card.done .qa-qnum {
  background: var(--beginner-soft);
  border-color: color-mix(in srgb, var(--beginner) 40%, transparent);
  color: var(--beginner);
}
.qa-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.badge {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; padding: 3px 8px; border-radius: 6px; white-space: nowrap;
}
.badge.cat { background: color-mix(in srgb, var(--cat) 14%, var(--surface)); color: var(--cat); }
.badge.diff-beginner { background: var(--beginner-soft); color: var(--beginner); }
.badge.diff-intermediate { background: var(--intermediate-soft); color: var(--intermediate); }
.badge.diff-advanced { background: var(--advanced-soft); color: var(--advanced); }
.qa-star { margin-left: auto; font-size: 17px; line-height: 1; color: var(--muted); transition: var(--trans); background: none; }
.qa-star:hover { color: var(--intermediate); transform: scale(1.12); }
.qa-star.on { color: var(--intermediate); }

.qa-question { font-size: var(--step-1); font-weight: 650; color: var(--ink); }
.qa-question :is(code, .inline) { font-family: var(--mono); font-size: .82em; background: var(--surface-2); padding: 1px 5px; border-radius: 5px; border: 1px solid var(--border); color: var(--accent-ink); }
.qa-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.qa-tags { display: flex; flex-wrap: wrap; gap: 6px; }
/* Tags earn their place in the list, where they help you scan; once a card is
   open you've already chosen the question and they're just a line between it and
   the answer. So they go on open cards — every width, reading mode or not.

   The whole foot goes, not only .qa-tags: the +/✕ toggle shares that row and
   holds it at full height, so hiding the tags alone would reclaim 0px. Nothing
   becomes unreachable — .qa-head is itself the click target that closes the
   card, and an open card is self-evident from its answer being visible. */
.qa-card.open .qa-foot { display: none; }
.qa-tag { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.qa-tag::before { content: "#"; opacity: .55; }
/* "V.Imp" flag — a high-priority marker that stands out from ordinary #tags */
.qa-tag.vimp {
  color: var(--accent); font-weight: 700; letter-spacing: .02em;
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  padding: 1px 7px; border-radius: var(--r-pill);
}
.qa-tag.vimp::before { content: "★ "; opacity: 1; }
.qa-toggle { margin-left: auto; font-family: var(--mono); font-size: 19px; line-height: 1; color: var(--muted); transition: transform var(--trans), color var(--trans); }
.qa-card.open .qa-toggle { transform: rotate(45deg); color: var(--cat, var(--accent)); }

/* body — collapsed by default, shown when the card is open */
.qa-body { display: none; padding: 0 16px 16px; border-top: 1px solid var(--border); }
.qa-card.open .qa-body { display: block; }
.qa-body-inner { padding-top: 14px; font-size: var(--step-0); color: var(--ink-2); }
.qa-body p { margin-bottom: 10px; }
.qa-body p:last-child { margin-bottom: 0; }
.qa-body ul { padding-left: 20px; margin-bottom: 10px; }
.qa-body li { margin: 5px 0; list-style: disc; }
.qa-body strong { color: var(--ink); }
/* :not(pre code) matters — `code` inside a `pre` is still inline, so the chip's
   background+border used to paint once per wrapped line, which read as a stray
   rule under every line of the block. Block code is styled by .code-block pre. */
.qa-body :is(code, .inline):not(pre code) { font-family: var(--mono); font-size: .85em; background: var(--surface-2); padding: 1px 6px; border-radius: 5px; border: 1px solid var(--border); color: var(--accent-ink); }

.code-block { position: relative; margin: 14px 0; }
.code-block pre, .qa-deep pre {
  font-family: var(--mono); font-size: 13px; line-height: 1.7; tab-size: 2;
  background: var(--code-bg); border: 1px solid var(--code-border); border-radius: var(--r-md);
  padding: 16px 18px; padding-right: 74px; overflow-x: auto;
  -webkit-font-smoothing: antialiased; text-decoration: none;
}
.code-block pre code, .qa-deep pre code {
  background: none; border: none; padding: 0; color: var(--code-ink); text-decoration: none;
}
.copy-btn {
  position: absolute; top: 9px; right: 9px; z-index: 1;
  font-family: var(--mono); font-size: 10.5px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase;
  padding: 5px 10px; border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, .16); background: rgba(255, 255, 255, .07);
  color: var(--code-muted); opacity: .75; transition: var(--trans);
}
.code-block:hover .copy-btn, .copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { border-color: rgba(255, 255, 255, .3); background: rgba(255, 255, 255, .13); color: #fff; }
.copy-btn.done { color: #7EE2A8; border-color: rgba(126, 226, 168, .45); opacity: 1; }

.qa-tip { font-size: var(--step--1); color: var(--ink-2); border-left: 3px solid var(--cat, var(--accent)); padding: 4px 0 4px 12px; margin-top: 12px; background: color-mix(in srgb, var(--cat, var(--accent)) 5%, transparent); border-radius: 0 6px 6px 0; }
.qa-tip b { color: var(--cat, var(--accent)); font-family: var(--mono); font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; margin-right: 6px; }

/* Small gap when a Study-in-depth button immediately follows a tip */
.qa-tip + .deep-btn { margin-top: 10px; display: inline-flex; }

/* ---------- reading mode toggle button ---------- */
.reading-mode-toggle, .scroll-to-top { transition: transform .18s ease, opacity .18s ease; }
.reading-mode-toggle {
  position: fixed; right: 18px; bottom: 20px; /* above scroll-to-top */
  width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); display: grid; place-items: center;
  box-shadow: var(--shadow-lg); z-index: 120; cursor: pointer; font-size: 18px;
}
.reading-mode-toggle:hover { transform: translateY(-4px); border-color: var(--accent); color: var(--accent); }
.reading-mode-toggle.on { background: var(--accent); color: var(--accent-contrast); border-color: var(--accent); }

/* reading-mode layout rules */
body.reading-mode .header-shell,
body.reading-mode .mobile-controls,
body.reading-mode .sidebar,
body.reading-mode .content-head { display: none !important; }
body.reading-mode .main { grid-template-columns: 1fr !important; gap: 0 !important; padding-top: 12px; }
body.reading-mode .content { padding-left: 0; padding-right: 0; }
body.reading-mode .q-list { max-width: 960px; margin: 0 auto; }
/* ensure cards have larger reading-friendly spacing */
body.reading-mode .qa-card { border-radius: 10px; margin-bottom: 18px; }

/* Compact brand on small screens: show a shorter title to save header space */
@media (max-width: 900px) {
  .brand { gap: 4px; }
  .brand-logo { width: 20px; height: 24px; }
  .brand-name { font-size: 12px; font-weight: 800; }
  /* hide the second word ('Helper') so the title is shorter on mobile */
}

/* in-depth study panel */
.deep-btn { border-color: color-mix(in srgb, var(--cat, var(--accent)) 45%, var(--border)) !important; color: var(--cat, var(--accent)) !important; }
.qa-deep {
  margin-top: 12px; padding: 16px 18px; border-radius: var(--r-sm);
  border: 1px solid var(--border); border-left: 3px solid var(--cat, var(--accent));
  background: color-mix(in srgb, var(--cat, var(--accent)) 5%, var(--surface));
  font-size: var(--step-0); color: var(--ink-2);
}
.qa-deep h4 {
  font-family: var(--mono); font-size: 15px; letter-spacing: normal;
  color: var(--cat, var(--accent)); margin: 16px 0 8px; font-weight: 800;
}
.qa-deep h4:first-child { margin-top: 0; }
.qa-deep p { margin-bottom: 10px; }
.qa-deep ul, .qa-deep ol { padding-left: 20px; margin-bottom: 10px; }
.qa-deep li { margin: 5px 0; list-style: disc; }
.qa-deep ol li { list-style: decimal; }
.qa-deep strong { color: var(--ink); }
/* INLINE code only. The :not() is load-bearing: `:is(code, .inline)` takes the
   specificity of its most specific argument (.inline, a class), so this rule
   scored higher than `.qa-deep pre code` above and was repainting every line of
   a deep-dive code BLOCK with the pale inline-code chip styling. Excluding
   code inside <pre> lets the block rule win, rather than fighting it with
   !important. */
.qa-deep :is(code, .inline):not(pre code) { font-family: var(--mono); font-size: .85em; background: var(--surface-2); padding: 1px 6px; border-radius: 5px; border: 1px solid var(--border); color: var(--accent-ink); }
/* the shared dark block is defined up with .code-block pre — only the flow
   margin differs inside a deep dive */
.qa-deep pre { margin: 4px 0 12px; padding-right: 18px; }
/* ---------- tables (answer + deep-dive) ----------
   Each table is wrapped in a .table-scroll port by wrapTables() in js/app.js.
   The port is what scrolls; the table keeps its natural column widths. These
   rules used to be scoped to .qa-deep only, which left the one <table> that
   lives in an answer completely unstyled. */
:is(.qa-deep, .answer) table { width: 100%; border-collapse: collapse; margin: 0; font-size: var(--step--1); }
:is(.qa-deep, .answer) :is(th, td) { border: 1px solid var(--border); padding: 7px 10px; text-align: left; }
:is(.qa-deep, .answer) th {
  background: var(--surface-2); font-family: var(--mono); font-size: 11px;
  text-transform: uppercase; letter-spacing: .04em; color: var(--ink);
}

.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  margin: 4px 0 12px;
  border-radius: var(--r-sm);
}
/* width:100% alone let the table auto-layout squeeze every column down to
   min-content on a phone — a 542px table crammed into 273px, one word per line.
   max-content lets each table take the width it actually needs (these run
   411-896px) and pan; min-width:100% still makes it fill a roomy desktop card. */
.table-scroll > table { width: max-content; min-width: 100%; }
/* the port is focusable so it can be scrolled by keyboard — it must show that */
.table-scroll:focus-visible { outline: none; box-shadow: var(--focus); }
/* a scroll port would clip columns off the sheet; let print fall back to squeezing */
@media print { .table-scroll { overflow-x: visible; } .table-scroll > table { width: 100%; min-width: 0; } }

/* personal note (collapsible, per-question) */
.pn-section { margin-top: 12px; }
.pn-toggle.has-note { border-color: color-mix(in srgb, var(--cat, var(--accent)) 45%, var(--border)); color: var(--cat, var(--accent)); }
.pn-toggle.has-note::after { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--cat, var(--accent)); margin-left: 2px; }
.pn-body {
  margin-top: 10px; padding: 14px 16px; border-radius: var(--r-sm);
  border: 1px solid var(--border); border-left: 3px solid var(--cat, var(--accent));
  background: color-mix(in srgb, var(--cat, var(--accent)) 4%, var(--surface));
}
.pn-add {
  font-family: var(--mono); font-size: var(--step--1); padding: 7px 13px;
  border-radius: var(--r-sm); border: 1px dashed var(--border-strong);
  background: var(--surface); color: var(--ink-2); transition: var(--trans);
}
.pn-add:hover { border-color: var(--cat, var(--accent)); color: var(--cat, var(--accent)); }
.pn-text { font-size: var(--step-0); color: var(--ink); line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
.pn-input {
  width: 100%; font-family: inherit; font-size: var(--step-0); line-height: 1.55;
  color: var(--ink); background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); padding: 10px 12px; resize: vertical; min-height: 80px;
}
.pn-input:focus { outline: none; border-color: var(--cat, var(--accent)); box-shadow: var(--focus); }
.pn-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.pn-btn {
  font-family: var(--mono); font-size: var(--step--1); padding: 6px 12px;
  border-radius: var(--r-sm); border: 1px solid var(--border); background: var(--surface);
  color: var(--ink-2); transition: var(--trans);
}
.pn-btn:hover { border-color: var(--accent); color: var(--accent); }
.pn-primary { border-color: var(--cat, var(--accent)); color: var(--cat, var(--accent)); }
.pn-primary:hover { background: color-mix(in srgb, var(--cat, var(--accent)) 10%, var(--surface)); }
.pn-danger:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* ===== text highlights ===== */
/* solid swatch colors (used by the header palette + per-mark toolbar) */
:root {
  --hlp-yellow: hsl(48 95% 62%);
  --hlp-green:  hsl(140 60% 58%);
  --hlp-blue:   hsl(210 90% 62%);
  --hlp-pink:   hsl(330 85% 66%);
  /* presenter pen — the single ink color, matched to INK in js/inkpen.js */
  --ink-pen:    #ef2b2b;
}
/* applied marks — tint sits under the body text (see dark-theme.css for --hl-l) */
.answer, .qa-deep, .qa-qtext { --hl-l: 78%; }
mark.hl { background: transparent; color: inherit; border-radius: 3px; padding: 0 1px; cursor: pointer; box-decoration-break: clone; -webkit-box-decoration-break: clone; transition: filter var(--trans); }
mark.hl:hover { filter: brightness(0.94); }
mark.hl-yellow { background: hsl(48 95% var(--hl-l)); }
mark.hl-green  { background: hsl(140 60% var(--hl-l)); }
mark.hl-blue   { background: hsl(210 90% var(--hl-l)); }
mark.hl-pink   { background: hsl(330 85% var(--hl-l)); }

/* header highlighter button: active state shows the current pen color */
#hl-toggle.pen-on { color: var(--ink); border-color: color-mix(in srgb, var(--pen, var(--accent)) 60%, var(--border)); box-shadow: inset 0 -3px 0 var(--pen, var(--accent)); }
/* when underline style is active, show a neutral underline icon instead of color fill */
#hl-toggle.hl-btn-style-underline.pen-on { color: var(--ink); border-color: var(--border); box-shadow: none; }
/* show a small color dot on the toggle indicating selected color only when using the highlighter (not underline) */
#hl-toggle.pen-on:not(.hl-btn-style-underline) { position: relative; }
#hl-toggle.pen-on:not(.hl-btn-style-underline)::after {
  content: ""; position: absolute; width: 10px; height: 10px; border-radius: 50%; right: 6px; bottom: 6px;
  background: var(--pen, var(--hlp-yellow)); border: 1px solid rgba(0,0,0,0.06);
}
body.hl-pen .answer, body.hl-pen .qa-deep, body.hl-pen .qa-qtext { cursor: text; }

/* generic round swatch (header palette) */
.hl-swatch { width: 22px; height: 22px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: transform var(--trans), border-color var(--trans); box-shadow: inset 0 0 0 1px rgba(0,0,0,.14); }
.hl-swatch:hover { transform: scale(1.12); }
.hl-swatch[data-active="true"] { border-color: var(--ink); }
.hl-swatch.hl-yellow { background: var(--hlp-yellow); }
.hl-swatch.hl-green  { background: var(--hlp-green); }
.hl-swatch.hl-blue   { background: var(--hlp-blue); }
.hl-swatch.hl-pink   { background: var(--hlp-pink); }

/* header palette dropdown */
.hl-palette { position: absolute; z-index: 70; width: max-content; padding: 12px; border-radius: 12px; background: var(--surface); border: 1px solid var(--border-strong); box-shadow: var(--shadow-lg, 0 12px 40px rgba(0,0,0,.2)); }
.hl-palette-title { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin-bottom: 9px; }
.hl-swatch-row { display: flex; gap: 10px; margin-bottom: 11px; align-items: center; }
.hl-swatch-row--with-style { gap: 12px; }
.hl-style-row { display: flex; justify-content: center; margin-bottom: 10px; }
.hl-style-option, .hl-menu-style {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink); cursor: pointer; transition: var(--trans);
}
.hl-style-option:hover, .hl-menu-style:hover { border-color: var(--accent); color: var(--accent); }
.hl-style-option.active, .hl-menu-style.active { border-color: var(--accent); background: var(--accent-soft); }
.hl-style-line { display: block; width: 16px; height: 2px; border-radius: 999px; background: currentColor; }
.hl-off { width: 100%; font-family: var(--mono); font-size: var(--step--1); padding: 7px 10px; border-radius: var(--r-sm); border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); transition: var(--trans); cursor: pointer; }
.hl-off:hover { border-color: var(--accent); color: var(--accent); }

/* presenter pen (js/inkpen.js) — desktop-only, sits inline with the swatches */
.hl-ink-option {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink-pen); cursor: pointer; transition: var(--trans);
}
.hl-ink-option:hover { border-color: var(--ink-pen); }
.hl-ink-option.active { border-color: var(--ink-pen); background: color-mix(in srgb, var(--ink-pen) 12%, transparent); }
/* the pen is a mode on the highlighter button — it wears the pen icon (set in
   js/highlights.js) and rings itself in the pen's own red. Both rules must outrank
   the .pen-on pair above, which is why they carry the doubled class. */
#hl-toggle.ink-active.ink-active { border-color: var(--ink-pen); color: var(--ink-pen); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ink-pen) 26%, transparent); }
/* the color dot belongs to the highlighter — this pen has exactly one color, so
   the dot says nothing here and only reads as the wrong one */
#hl-toggle.ink-active.ink-active::after { content: none; }

/* the drawing surface itself: over the content, under the sticky header (40) and
   the palette (70) so the control that turns it off stays reachable */
.ink-layer { position: fixed; inset: 0; cursor: crosshair; touch-action: none; }
.ink-layer[hidden] { display: none; }
/* while drawing, a drag must not select the text underneath */
body.ink-on { user-select: none; -webkit-user-select: none; }
@media (max-width: 899px) { .ink-layer { display: none; } }

/* the display:flex/absolute below would otherwise override the [hidden]
   attribute (same specificity, author beats UA) and the menu/palette would
   never hide — restore hiding explicitly. */
.hl-menu[hidden], .hl-palette[hidden] { display: none; }

/* active highlight being edited: a ring so you see exactly which span the
   toolbar acts on (cleared when the toolbar closes) */
mark.hl.hl-editing { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 2px; }
mark.hl-underline {
  background: transparent;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-decoration-skip-ink: auto;
}
/* Underline style: keep neutral underline (no per-color underline) */
mark.hl-underline {
  background: transparent;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-decoration-skip-ink: auto;
  /* Always render underline in solid black per product request */
  text-decoration-color: #000 !important;
}

/* per-mark toolbar (recolor + delete) — dark floating pill */
.hl-menu { position: absolute; z-index: 70; display: flex; align-items: center; gap: 8px; padding: 7px 9px; border-radius: 999px; background: #2a2731; border: 1px solid rgba(255,255,255,.12); box-shadow: 0 8px 26px rgba(0,0,0,.35); }
.hl-menu-swatch { width: 18px; height: 18px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; transition: transform var(--trans); box-shadow: inset 0 0 0 1px rgba(0,0,0,.2); }
.hl-menu-swatch:hover { transform: scale(1.15); }
.hl-menu-swatch.hl-yellow { background: var(--hlp-yellow); }
.hl-menu-swatch.hl-green  { background: var(--hlp-green); }
.hl-menu-swatch.hl-blue   { background: var(--hlp-blue); }
.hl-menu-swatch.hl-pink   { background: var(--hlp-pink); }
.hl-menu-swatch[data-active="true"] { border-color: var(--ink); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 8%, transparent); }
.hl-menu-del { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; margin-left: 4px; border-left: 1px solid rgba(255,255,255,.16); border-top: none; border-right: none; border-bottom: none; padding-left: 8px; border-radius: 0; background: none; color: #f3b4b4; cursor: pointer; transition: var(--trans); }
.hl-menu-del:hover { color: #ff7a7a; }

.qa-body-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; user-select: none; -webkit-user-select: none; }
.qa-act { display: inline-flex; align-items: center; gap: 6px; font-family: var(--mono); font-size: var(--step--1); padding: 6px 11px; border-radius: var(--r-sm); border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); transition: var(--trans); }
.qa-act:hover { border-color: var(--accent); color: var(--accent); }
/* Responsive labels — the short form only exists below 560px (see that block). */
.qa-act-short { display: none; }
.qa-act.on { background: var(--beginner-soft); border-color: var(--beginner); color: var(--beginner); }

/* practice mode: hide answers behind a reveal */
.practice .qa-card:not(.revealed) .qa-body { display: none; }
.reveal-btn { display: none; }
.practice .qa-card:not(.revealed) .reveal-btn { display: inline-flex; margin: 0 16px 16px; }
.reveal-btn { align-items: center; gap: 6px; font-family: var(--mono); font-size: var(--step--1); padding: 7px 13px; border-radius: var(--r-sm); border: 1px dashed var(--accent); background: var(--accent-soft); color: var(--accent-ink); }

/* empty / no-results */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 60px 20px;
  color: var(--muted);
}
.empty .big { font-size: 40px; margin-bottom: 10px; }
.empty h3 { color: var(--ink); margin-bottom: 6px; }

/* progress bar in sidebar */
.progress-box { margin: 4px 12px 14px; }
.progress-box .pl { display: flex; justify-content: space-between; gap: 8px; font-family: var(--mono); font-size: 11px; color: var(--muted); margin-bottom: 6px; }
.progress-box .prog-num { color: var(--side-c, var(--ink)); font-variant-numeric: tabular-nums; white-space: nowrap; flex-shrink: 0; }
.progress-track { height: 7px; border-radius: 100px; background: var(--surface-3); overflow: hidden; }
.progress-fill { height: 100%; width: 0; background: var(--beginner); border-radius: 100px; transition: width .4s ease; }

/* footer */
.site-footer { border-top: 1px solid var(--border); padding: 26px clamp(14px, 3vw, 28px); color: var(--muted); font-size: var(--step--1); }
.site-footer .fi { max-width: var(--maxw); margin: 0 auto; display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; }
.site-footer a { color: var(--accent); }

/* toast */
.toast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%) translateY(20px); background: var(--ink); color: var(--bg); padding: 10px 18px; border-radius: var(--r-pill); font-size: var(--step--1); font-family: var(--mono); box-shadow: var(--shadow-lg); opacity: 0; pointer-events: none; transition: .25s; z-index: 60; }
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- sidebar rail (AI Helper open) ----------
   With the chat panel docked, the 210px sidebar is the most expendable column
   on screen — but hiding it outright would strand the reader with no category
   navigation for as long as the chat is open. It collapses to an icon rail
   instead: ~154px back, navigation intact, `title` carries the name.

   Scoped above 900px because below that the sidebar is already display:none. */
@media (min-width: 901px) {
  body.tutor-open { --sidebar-w: 56px; }
  body.tutor-open .main { gap: 16px; }

  /* Animating grid-template-columns is not reliably interpolatable across
     browsers, so the transition rides on the parts that are. */
  /* The progress tracker goes entirely, rather than being squeezed: its label
     ("Frontend progress") and its "12 / 395" count are both text that cannot
     survive a 56px column, and a bar with no numbers beside it says nothing.
     It is back the moment the chat closes. */
  body.tutor-open .progress-box,
  body.tutor-open .sidebar-title,
  body.tutor-open .side-link-label > span:not(.cat-ic),
  body.tutor-open .side-link .s-count { display: none; }

  body.tutor-open .side-link {
    justify-content: center; padding: 9px 0; border-left-width: 0;
    border-radius: var(--r-sm);
  }
  /* The active marker was a left border, which has nowhere to sit once the
     row is centred — a tinted pill reads better at this width anyway. */
  body.tutor-open .side-link.active { background: var(--accent-soft); }
  body.tutor-open .side-link-label { justify-content: center; gap: 0; }
  body.tutor-open .side-link .cat-ic { width: 19px; height: 19px; }

  .sidebar, .main { transition: gap .25s ease; }
  .side-link { transition: padding .25s ease, background var(--trans), border-color var(--trans), color var(--trans); }
}

/* ---------- responsive ---------- */
@media (max-width: 900px) {
  :root { --sidebar-w: 0px; }
  .main { grid-template-columns: 1fr; }
  .sidebar { display: none; }
  /* replace the space-hungry chip rows with compact dropdowns */
  .mobile-controls { display: flex; }
  .tabs-wrap { display: none; }
  .subnav { display: none; }
  #difficulty-filter { display: none; }

  /* ---------- mobile header ----------
     One sticky row: [mark] ......... [profile] [filter] [highlighter] [search].
     The wordmark goes (the mark says it in 30px), the search collapses to an
     icon, and the theme switch moves into the tools sheet — which is what buys
     four comfortable 38px targets at 375px. Everything the reader needs stays
     in the sticky header, so none of it scrolls away. */
  .brand-name { display: none; }
  .brand-mark { display: grid; }
  #filter-btn, #search-toggle { display: grid; }
  /* the moon lives in the tools sheet down here (#theme-toggle-m) */
  #theme-toggle { display: none; }
  .site-header { gap: 8px; }
  .header-search { display: none; } /* until the search icon expands it */
  .header-search .s-kbd { display: none; } /* no "/" shortcut on a phone */

  /* expanded search: the row becomes the search bar. Plain flex swaps, no
     absolute overlay — nothing to mis-position over the sticky header. */
  .site-header.search-open > :is(.brand, .header-spacer, #auth-btn, #filter-btn, #hl-toggle, #search-toggle) { display: none; }
  .site-header.search-open .header-search { display: block; flex: 1 1 auto; max-width: none; }
  .site-header.search-open .search-close { display: grid; }

  /* Ten tools ragged-wrapping into five rows was the messiest block on a phone.
     A horizontal scroller fixed the mess but hid the tools behind a swipe with
     no affordance — you cannot tell a row scrolls until you try. So they move
     into a bottom sheet behind an explicit "Filters & tools" button that shows
     how many are active. Everything stays visible at once inside the sheet, and
     it is a normal multi-select: tap several, then Show results. */
  .content-head { gap: 10px; }

  .tools-backdrop {
    display: block; position: fixed; inset: 0; z-index: 110;
    background: rgba(15, 23, 42, .5);
    opacity: 0; transition: opacity .22s ease;
  }
  .tools-backdrop.show { opacity: 1; }
  .tools-backdrop[hidden] { display: none; }

  .tools-panel {
    display: flex; flex-direction: column;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 111;
    max-height: min(78vh, 560px);
    background: var(--surface);
    border-top: 1px solid var(--border);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: var(--shadow-lg);
    /* Parked below the fold and hidden. `visibility` is what keeps the closed
       sheet's eleven tools out of the tab order and the a11y tree — transform
       alone would leave them reachable, just invisible.

       It lives HERE, in the media query, on purpose. The same buttons ARE the
       desktop toolbar (the panel is display:contents above 900px), so whatever
       hides them must switch itself off at the breakpoint with no help from
       JavaScript. A JS-driven equivalent (the `inert` attribute) depends on a
       matchMedia change event firing, and when that event is missed the failure
       is a silently dead desktop toolbar. A media query cannot miss.

       No transition on either property: the idle state must not animate, or
       crossing the breakpoint — which swaps this between display:contents (no
       box) and a fixed sheet — tries to animate that swap. */
    transform: translateY(100%);
    visibility: hidden;
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  /* the motion belongs to the two gestures, the only times it should move */
  .tools-panel.show { transform: translateY(0); visibility: visible; }
  /* .closing holds it visible through the slide-out; app.js drops the class once
     the sheet is parked, which is what returns it to hidden — no timed
     visibility transition to unwind. */
  .tools-panel.closing { visibility: visible; }
  .tools-panel.show,
  .tools-panel.closing { transition: transform .28s cubic-bezier(.4, 0, .2, 1); }

  .tools-sheet-head {
    position: relative; /* anchors the grab handle below */
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 16px 16px 10px; border-bottom: 1px solid var(--border); flex: 0 0 auto;
  }
  /* the grab handle above the title, so the sheet reads as a sheet */
  .tools-sheet-head::before {
    content: ""; position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
    width: 36px; height: 4px; border-radius: var(--r-pill); background: var(--border-strong);
  }
  .tools-sheet-title { font-weight: 700; color: var(--ink); font-size: var(--step-0); }
  .tools-close {
    display: grid; place-items: center; width: 30px; height: 30px; flex: 0 0 auto;
    border: none; background: none; color: var(--muted); border-radius: 8px; cursor: pointer;
  }
  .tools-close:hover { background: var(--surface-2); color: var(--ink); }

  /* two even columns — every tool visible at once, nothing ragged, no scroll
     affordance to miss. The sheet itself scrolls only if the viewport is tiny. */
  .content-tools {
    width: auto; margin: 0;
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    padding: 14px 16px; overflow-y: auto; flex: 1 1 auto; min-height: 0;
  }
  .tool { justify-content: flex-start; padding: 10px 12px; }
  /* the hover lift makes buttons jitter under a thumb in a grid */
  .tool:hover { transform: none; box-shadow: none; }
  /* the relocated theme switch spans the grid so it reads as its own thing
     rather than an eleventh filter */
  .tool-theme { display: inline-flex; grid-column: 1 / -1; }

  .tools-sheet-foot {
    display: flex; gap: 8px; flex: 0 0 auto;
    padding: 12px 16px; border-top: 1px solid var(--border);
  }
  .tools-clear, .tools-done {
    flex: 1 1 0; padding: 11px 12px; border-radius: var(--r-pill);
    font-family: var(--mono); font-size: var(--step--1); font-weight: 700; cursor: pointer;
    transition: var(--trans);
  }
  .tools-clear { border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); }
  .tools-clear:hover { border-color: var(--accent); color: var(--accent); }
  .tools-done { border: 1px solid var(--accent); background: var(--accent); color: var(--accent-contrast); }
  .tools-done:hover { filter: brightness(1.06); }

  body.tools-open { overflow: hidden; }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
  .tools-panel, .tools-backdrop { transition-duration: .01ms; }
}
@media (min-width: 901px) { .mobile-controls { display: none; } }

/* ---------- mobile controls ---------- */
.mobile-controls {
  flex-wrap: wrap; gap: 8px;
  padding: 12px clamp(14px, 3vw, 28px) 2px; max-width: var(--maxw); margin: 0 auto;
}
.m-field { position: relative; flex: 1 1 30%; min-width: 0; }
/* the caret and the bare-select styling below are the no-JS fallback; once
   js/select.js enhances a field it draws its own trigger and the <select> goes
   display:none (see .sel-host) */
.m-field::after {
  content: "▾"; position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
  pointer-events: none; color: var(--muted); font-size: 12px;
}
.m-select {
  width: 100%; appearance: none; -webkit-appearance: none;
  font-family: var(--mono); font-size: var(--step--1); line-height: 1.2;
  padding: 9px 26px 9px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  text-overflow: ellipsis; cursor: pointer; transition: var(--trans);
}
.m-select:hover { border-color: var(--border-strong); }
.m-select:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus); }

/* ---------- styled combobox (js/select.js) ----------
   Replaces the native <option> popup, which the browser draws and no CSS can
   reach. The <select> stays as the model but is hidden — and hidden from the
   a11y tree with it, so .sel-btn is the only control exposed. */
.sel-host .m-select { display: none; }
.sel-host::after { content: none; }

.sel-btn {
  width: 100%; display: flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: var(--step--1); line-height: 1.2;
  padding: 9px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  cursor: pointer; transition: var(--trans); text-align: left;
}
.sel-btn:hover { border-color: var(--border-strong); }
.sel-btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--focus); }
.sel-host.is-open .sel-btn { border-color: var(--accent); box-shadow: var(--focus); }
.sel-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sel-btn .cat-ic { width: 14px; height: 14px; }
.sel-caret { flex: 0 0 auto; width: 13px; height: 13px; color: var(--muted); transition: transform .18s ease; }
.sel-caret svg { width: 100%; height: 100%; display: block; }
.sel-host.is-open .sel-caret { transform: rotate(180deg); color: var(--accent); }

.sel-panel {
  position: absolute; z-index: 70; top: calc(100% + 6px); left: 0;
  min-width: 100%; width: max-content; max-width: min(280px, calc(100vw - 28px));
  max-height: 300px; overflow-y: auto; overscroll-behavior: contain;
  padding: 5px; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface);
  box-shadow: var(--shadow-lg);
  animation: sel-in .14s ease-out;
}
@keyframes sel-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .sel-panel { animation: none; } }
/* the Level field sits against the right edge, where a left-aligned panel wider
   than its trigger would hang off the viewport */
.mobile-controls .m-field:last-child .sel-panel { left: auto; right: 0; }

.sel-opt {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-family: var(--mono); font-size: var(--step--1); color: var(--ink-2);
  white-space: nowrap; transition: background var(--trans), color var(--trans);
}
.sel-opt .cat-ic { width: 15px; height: 15px; }
.sel-opt-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }
.sel-opt:hover { background: var(--surface-2); color: var(--ink); }
.sel-opt.is-selected { background: var(--accent-soft); color: var(--ink); font-weight: 700; }
/* Focus is real here (options are tabindex=-1 and get .focus()), so it has to be
   visible — and this must out-rank .is-selected, because opening with the keyboard
   lands focus on the selected option first. Ranked below it, the one option you
   start on would be the one with no ring. :focus-visible keeps the ring off the
   mouse path, where open() focuses an option the user never asked to see. */
.sel-opt:focus-visible { outline: none; background: var(--surface-2); color: var(--ink); box-shadow: var(--focus); }
.sel-opt-check { flex: 0 0 auto; width: 13px; height: 13px; color: var(--accent); }
.sel-opt-check svg { width: 100%; height: 100%; display: block; }

@media (max-width: 560px) {
  .hero-stats { gap: 18px; }
  /* .content-tools now goes full-width and scrolls from 900px down */
  .qa-question { font-size: var(--step-0); }
  /* The card's action row doesn't fit a phone: three labelled buttons wanted
     ~336px of a 313px row and spilled onto a second line. Shorten the labels
     ("Mark as done"→"Done", "Copy link"→"Copy", "Report Issue"→"Report") and
     trim the horizontal padding, which together bring it to ~255px — one line
     with room to spare. Icons and full desktop labels are untouched, and each
     button keeps an aria-label so the accessible name never shortens. */
  .qa-act-long { display: none; }
  .qa-act-short { display: inline; }
  .qa-act-word { display: none; }
  /* Tags are dropped outright on a phone: they wrap to their own line under
     every question and cost more room than they earn at this width. The row
     itself stays for the +/✕ expand hint, so this trades noise rather than
     height — open cards drop the whole row anyway (see .qa-card.open .qa-foot).
     NOTE this takes the ★ V.IMP marker with them — see .qa-tag.vimp. */
  .qa-tags { display: none; }

  /* The number medallion's gutter costs 50px (36px column + 14px gap) of a
     ~343px card, squeezing the question into what's left. Shrink both together:
     the column width and .qa-qnum are one measurement in two places, and
     changing only the circle would leave the gutter — and the indent — as-is. */
  .qa-head.numbered { grid-template-columns: 26px 1fr; column-gap: 10px; }
  .qa-qnum { width: 26px; height: 26px; font-size: 11px; }

  /* Only horizontal air is trimmed — tap targets stay ~35px tall. */
  .qa-body-actions { gap: 6px; }
  .qa-act { padding: 6px 8px; gap: 5px; }
}

/* ---------- scroll to top button ---------- */
.scroll-to-top {
  position: fixed;
  bottom: 70px;
  right: 18px;
  z-index: 90;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--trans), transform var(--trans), background var(--trans), border-color var(--trans);
  transform: translateY(10px);
}
.scroll-to-top.show {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}
.scroll-to-top:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  transform: scale(1.05);
}
.scroll-to-top:active {
  transform: scale(0.95);
}

/* ---------- Interactive Product Tour (Spotlight) ---------- */
.tour-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1000;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
}
.tour-overlay.show {
  opacity: 1;
  pointer-events: auto;
}
body.tour-active {
  overflow: hidden !important;
  height: 100vh !important;
}
.tour-spotlight {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  border-radius: 8px;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.75), 0 0 15px var(--accent);
  pointer-events: auto;
  transition: top .3s cubic-bezier(0.4, 0, 0.2, 1),
              left .3s cubic-bezier(0.4, 0, 0.2, 1),
              width .3s cubic-bezier(0.4, 0, 0.2, 1),
              height .3s cubic-bezier(0.4, 0, 0.2, 1),
              border-radius .3s ease;
  z-index: 1001;
}
.tour-tooltip {
  position: absolute;
  width: 320px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg), 0 10px 25px rgba(0, 0, 0, 0.2);
  padding: 16px;
  z-index: 1002;
  transition: transform .25s cubic-bezier(0.4, 0, 0.2, 1),
              opacity .25s ease,
              top .3s cubic-bezier(0.4, 0, 0.2, 1),
              left .3s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: auto;
  opacity: 0;
  transform: scale(0.95);
  transform-origin: center;
}
.tour-tooltip.show {
  opacity: 1;
  transform: scale(1);
}
.tour-step-indicator {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 6px;
}
.tour-step-title {
  font-size: 16px;
  font-weight: 800;
  margin-bottom: 8px;
  color: var(--ink);
}
.tour-step-desc {
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-2);
  margin-bottom: 16px;
}
.tour-tooltip-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.tour-tooltip-nav {
  display: flex;
  gap: 8px;
}
.tour-btn {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--ink-2);
  cursor: pointer;
  transition: var(--trans);
}
.tour-btn:hover {
  background: var(--surface-3);
  color: var(--ink);
}
.tour-btn-next {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.tour-btn-next:hover {
  background: color-mix(in srgb, var(--accent) 85%, #000);
  color: var(--accent-contrast);
}
.tour-btn-skip {
  border: none;
  background: none;
  padding: 0;
  color: var(--muted);
  font-weight: 600;
  cursor: pointer;
  transition: var(--trans);
}
.tour-btn-skip:hover {
  color: var(--advanced);
}
.tour-welcome-card {
  position: fixed;
  top: 50% !important;
  left: 50% !important;
  transform: translate(-50%, -50%) scale(0.95);
  transform-origin: center;
  transition: transform .25s ease, opacity .25s ease;
  width: 360px;
}
.tour-welcome-card.show {
  transform: translate(-50%, -50%) scale(1);
}
.tour-image-card {
  position: fixed;
  top: 50% !important;
  left: 50% !important;
  transform: translate(-50%, -50%) scale(0.95);
  transform-origin: center;
  transition: transform .25s ease, opacity .25s ease;
  width: min(650px, 95vw);
}
.tour-image-card.show {
  transform: translate(-50%, -50%) scale(1);
}
/* On phones every step is centered (its spotlight target is display:none), so the
   cards must fit the viewport and scroll rather than run off the top and bottom. */
@media (max-width: 900px) {
  .tour-welcome-card { width: min(360px, calc(100vw - 32px)); }
  .tour-welcome-card, .tour-image-card {
    max-height: calc(100vh - 32px);
    overflow-y: auto;
  }
}

/* ============================================================
   Issue reports (js/reports.js) — the report dialog, the floating
   "Report Selected Text" button, and the admin triage panel.

   Built entirely from the tokens in variables.css, so dark-theme.css
   re-themes all of it without a single override here.
   ============================================================ */

/* the page behind a dialog must not scroll under it */
body.rp-open { overflow: hidden; }

/* ---------- shared dialog chrome ---------- */
.rp-overlay, .rp-admin-overlay {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center;
  padding: 16px;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  backdrop-filter: blur(3px);
}
/* display:grid above beats the [hidden] attribute's UA display:none, so without
   this the overlays would never hide — the same trap .hl-menu fell into. */
.rp-overlay[hidden], .rp-admin-overlay[hidden] { display: none; }
.rp-panel, .rp-admin {
  width: min(520px, 100%);
  max-height: calc(100vh - 32px);
  display: flex; flex-direction: column;
  overflow: hidden auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.rp-head, .rp-admin-head {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 18px 14px;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 14%, var(--surface)), var(--surface));
  position: sticky; top: 0; z-index: 1;
}
.rp-title { font-size: var(--step-1); font-weight: 800; letter-spacing: -.02em; color: var(--ink); margin: 0; }
.rp-x {
  margin-left: auto; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--muted); font-size: 13px; line-height: 1; cursor: pointer;
  transition: var(--trans);
}
.rp-x:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- what's being reported ---------- */
.rp-context { padding: 14px 18px 0; }
.rp-ctx-q {
  font-size: var(--step--1); font-weight: 600; color: var(--ink-2);
  padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  /* a long question would otherwise push the reasons below the fold */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.rp-ctx-sel { margin-top: 10px; }
.rp-ctx-sel[hidden] { display: none; }
.rp-ctx-selhead { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.rp-ctx-label {
  font-family: var(--mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
}
/* drop a stale selection and report the whole question instead */
.rp-drop {
  margin-left: auto; padding: 0; border: none; background: none;
  color: var(--muted); font-size: 11px; font-weight: 600; cursor: pointer;
  transition: var(--trans);
}
.rp-drop:hover { color: var(--accent); }
.rp-quote {
  margin: 0; padding: 9px 12px;
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: var(--accent-soft);
  color: var(--ink); font-size: var(--step--1); line-height: 1.5;
  max-height: 7.5em; overflow-y: auto;
}
.rp-quote-region {
  display: block; margin-bottom: 4px;
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted);
}

/* ---------- reasons ---------- */
.rp-reasons { padding: 14px 18px 4px; display: flex; flex-direction: column; gap: 2px; }
.rp-reason {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 10px; border-radius: var(--r-sm);
  cursor: pointer; transition: var(--trans);
}
.rp-reason:hover { background: var(--surface-2); }
/* Drawn rather than native: the UA radio renders as a solid dark disc against
   this warm surface, so every option read as already selected. */
.rp-radio {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; margin: 2px 0 0; flex: 0 0 auto;
  border: 2px solid var(--border-strong); border-radius: 50%;
  background: var(--surface);
  cursor: pointer; transition: var(--trans);
}
.rp-radio:hover { border-color: var(--accent); }
.rp-radio:checked {
  border-color: var(--accent);
  /* the dot is painted inside the ring, so only the checked option has a fill */
  background: radial-gradient(circle at center, var(--accent) 0 4px, var(--surface) 4.5px);
}
.rp-radio:focus-visible { outline: none; box-shadow: var(--focus); }
.rp-reason-text { display: flex; flex-direction: column; gap: 1px; }
.rp-reason-label { font-size: var(--step-0); color: var(--ink); font-weight: 600; }
.rp-reason-hint { font-size: var(--step--1); color: var(--muted); }

/* ---------- details ---------- */
.rp-field { padding: 8px 18px 4px; }
.rp-label { display: block; margin-bottom: 6px; font-size: var(--step--1); font-weight: 600; color: var(--ink-2); }
.rp-input {
  width: 100%; padding: 10px 12px; resize: vertical;
  border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink);
  font-family: var(--sans); font-size: var(--step--1); line-height: 1.5;
}
.rp-input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus); }

/* ---------- actions ---------- */
.rp-note {
  margin: 4px 18px 0; padding: 10px 12px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  border: 1px solid color-mix(in srgb, var(--intermediate) 35%, var(--border));
  background: var(--intermediate-soft);
  border-radius: var(--r-sm);
  font-size: var(--step--1); color: var(--ink-2);
}
/* display:flex above beats [hidden]'s UA display:none — without this the note
   stayed as an empty strip for signed-in readers, and survived signing in. */
.rp-note[hidden] { display: none; }
.rp-actions {
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 16px 18px; margin-top: 4px;
  border-top: 1px solid var(--border);
  position: sticky; bottom: 0; background: var(--surface);
}
.rp-btn {
  padding: 9px 18px; border-radius: var(--r-pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--ink-2); font-family: var(--sans); font-size: var(--step--1); font-weight: 700;
  cursor: pointer; transition: var(--trans);
}
.rp-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.rp-btn:disabled { opacity: .5; cursor: not-allowed; }
.rp-primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-contrast);
}
.rp-primary:hover:not(:disabled) { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--accent-contrast); }
.rp-danger { color: var(--advanced); }
.rp-danger:hover:not(:disabled) { border-color: var(--advanced); color: var(--advanced); background: var(--advanced-soft); }
.rp-sm { padding: 6px 12px; font-size: 11.5px; }

/* ---------- reports panel (admin queue + the reader's own reports) ---------- */
/* The panel is a fixed head + tabs with a scrolling list, rather than a
   scrolling panel with sticky bits: the tabs used to be pinned at a hardcoded
   top:61px, which is only the head's height while the title fits one line. On a
   phone the title wraps and the tabs then sit over it. Here the list is the only
   thing that scrolls, so nothing needs to know the head's height. */
.rp-admin { width: min(760px, 100%); overflow: hidden; }
.rp-admin-head, .rp-admin-tabs { flex: 0 0 auto; }
.rp-admin-count {
  font-family: var(--mono); font-size: 11px; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rp-refresh {
  margin-left: auto; padding: 5px 12px; border-radius: var(--r-pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--ink-2); font-size: 11.5px; font-weight: 700; cursor: pointer;
  transition: var(--trans);
}
.rp-refresh:hover { border-color: var(--accent); color: var(--accent); }
.rp-admin-head .rp-x { margin-left: 0; }

.rp-admin-tabs {
  display: flex; gap: 6px; padding: 12px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.rp-tab {
  padding: 6px 14px; border-radius: var(--r-pill);
  border: 1px solid var(--border); background: var(--surface);
  color: var(--muted); font-size: var(--step--1); font-weight: 700; cursor: pointer;
  transition: var(--trans);
}
.rp-tab:hover { color: var(--ink); }
.rp-tab.on { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }

.rp-admin-list {
  flex: 1 1 auto; min-height: 0;      /* min-height:0 or the flex item won't shrink to scroll */
  overflow-y: auto; overscroll-behavior: contain;
  padding: 14px 18px 18px; display: flex; flex-direction: column; gap: 12px;
}
.rp-empty { padding: 40px 12px; text-align: center; color: var(--muted); font-size: var(--step--1); }
.rp-empty-hint { margin-top: 8px; font-family: var(--mono); font-size: 11px; color: var(--advanced); }
.rp-empty-sub { margin-top: 6px; font-size: 11.5px; color: var(--muted); }
/* the reader's confirmation that a report of theirs was acted on */
.rp-item-note {
  padding: 8px 12px; margin-bottom: 10px; border-radius: var(--r-sm);
  background: var(--beginner-soft); color: var(--beginner);
  font-size: 11.5px; font-weight: 700;
}

.rp-item {
  padding: 14px; border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface);
  box-shadow: var(--shadow-sm);
}
/* a resolved report stays legible but stops competing for attention */
.rp-item.rp-resolved { opacity: .62; }
.rp-item-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 9px; }
.rp-badge {
  padding: 3px 9px; border-radius: var(--r-pill);
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: 10.5px; font-weight: 800; letter-spacing: .02em;
}
.rp-badge-cat { background: var(--surface-3); color: var(--ink-2); font-family: var(--mono); font-weight: 600; }
.rp-status {
  padding: 3px 9px; border-radius: var(--r-pill);
  font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: .08em; font-weight: 700;
}
.rp-status-open { background: var(--intermediate-soft); color: var(--intermediate); }
.rp-status-resolved { background: var(--beginner-soft); color: var(--beginner); }
.rp-when { margin-left: auto; font-family: var(--mono); font-size: 10.5px; color: var(--muted); }
.rp-item-q { font-size: var(--step-0); font-weight: 700; color: var(--ink); line-height: 1.4; margin-bottom: 8px; }
.rp-item .rp-quote { margin-bottom: 8px; }
.rp-item-comment {
  padding: 9px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: var(--step--1); color: var(--ink-2); line-height: 1.5;
  white-space: pre-wrap; margin-bottom: 8px;
}
.rp-item-by { font-family: var(--mono); font-size: 10.5px; color: var(--muted); margin-bottom: 10px; word-break: break-all; }
.rp-item-actions { display: flex; gap: 8px; flex-wrap: wrap; }

@media (max-width: 640px) {
  /* Centred, not pinned to the bottom edge — `align-items: end` here left the
     panel hanging off the foot of the screen instead of sitting in the middle. */
  .rp-overlay, .rp-admin-overlay { padding: 10px; place-items: center; }
  .rp-panel, .rp-admin { max-height: calc(100vh - 20px); }
  .rp-actions { flex-direction: column-reverse; }
  .rp-actions .rp-btn { width: 100%; }

  /* The head is four items on a ~340px row, so the title wrapped mid-word and
     the count broke across lines. Title + Refresh + ✕ share the first row; the
     count drops to its own line, where it has space to stay on one. */
  .rp-admin-head { flex-wrap: wrap; gap: 8px; padding: 14px 14px 12px; }
  .rp-admin-head .rp-title { font-size: var(--step-0); white-space: nowrap; }
  .rp-admin-count { order: 3; width: 100%; }
  .rp-admin-tabs { padding: 10px 14px; }
  .rp-admin-list { padding: 12px 14px 14px; }
  .rp-item-actions .rp-btn { flex: 1 1 auto; }   /* full-width taps, no stray gaps */
}

/* ---------- print ---------- */
@media print {
  .site-header, .tabs-wrap, .sidebar, .hero-pills, .content-tools, .filter-row, .qa-star, .qa-toggle, .copy-btn, .qa-body-actions, .reveal-btn, .site-footer, .icon-btn, .toast, .scroll-to-top, .tour-overlay, .tour-spotlight, .tour-tooltip, .rp-overlay, .rp-admin-overlay { display: none !important; }
  .main { display: block; }
  .qa-body { display: block !important; }
  .qa-card { break-inside: avoid; box-shadow: none; border: 1px solid #ccc; margin-bottom: 8px; }
  body { background: #fff; }
}