/* ============================================================
   menneske.no
   Konsept: rutenettet ER identiteten. Nordisk minimalisme,
   papir + blekk + én aksentfarge. Schibsted Grotesk (norsk)
   for tekst, IBM Plex Mono for alt som er tall og data.
   ============================================================ */

:root {
  --bg: #faf9f6;
  --surface: #ffffff;
  --ink: #1b1b18;
  /* Slightly darkened for contrast margin over the old #6e6d66 ~4.93:1 (#163). */
  --ink-2: #67665f;
  /* Nudged darker so cell-to-cell lines aren't quite as faint (#163). */
  --line: #dcdacf;
  --line-strong: #1b1b18;
  --bad: #c03b2e;
  --accent: #2b5bb7;
  /* Accent used as TEXT on the page background. Same blue in light mode (passes
     AA on the paper bg); a lighter tint in dark mode where #2b5bb7 is only
     ~2.8:1 (#155). Button backgrounds keep --accent. */
  --accent-text: #2b5bb7;
  --radius: 10px;
  --radius-pill: 999px;
  --maxw: 1120px;
}
[data-theme="dark"] {
  --bg: #161614;
  --surface: #1e1e1b;
  --ink: #edece6;
  --ink-2: #99978d;
  --line: #2e2d29;
  --line-strong: #edece6;
  --bad: #e0604f;
  /* ≥6.8:1 against both --bg and --surface (#155). */
  --accent-text: #7ea6ec;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: 'Schibsted Grotesk', sans-serif;
  -webkit-font-smoothing: antialiased;
  transition: background 0.25s ease, color 0.25s ease;
}
body[data-gridbg="on"]::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 38px 38px;
  opacity: 0.35;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.9), transparent 70%);
  mask-image: linear-gradient(to bottom, rgba(0,0,0,0.9), transparent 70%);
  z-index: 0;
}
.app { position: relative; z-index: 1; min-height: 100vh; display: flex; flex-direction: column; }
/* Skip-to-content link (WCAG 2.4.1): off-screen until focused (#154). */
.skip-link {
  position: absolute; left: 8px; top: -48px; z-index: 200;
  background: var(--accent); color: #fff; padding: 8px 14px;
  border-radius: var(--radius); font-weight: 600; text-decoration: none;
  transition: top 0.15s ease;
}
.skip-link:focus { top: 8px; outline: 2px solid var(--ink); outline-offset: 2px; }
/* Thin top progress bar shown while an auto-submit navigation is in flight. */
.nav-loading-bar {
  position: fixed; top: 0; left: 0; height: 3px; z-index: 100;
  background: var(--accent); animation: nav-load 1.1s ease-in-out infinite;
}
@keyframes nav-load { 0% { left: -40%; width: 40%; } 50% { width: 55%; } 100% { left: 100%; width: 40%; } }
main { flex: 1; }
/* Visible to screen readers, removed from the visual layout (headings, labels). */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
.mono { font-family: 'IBM Plex Mono', monospace; }
button { font-family: inherit; color: inherit; cursor: pointer; }
h1, h2, h3, p { margin: 0; }
a.btn-primary, a.chip, a.action-btn, a.back-btn, a.nav-link, a.mobile-menu-link, a.puzzle-card, a.more-link, a.num-btn, a.lang-btn, a.icon-btn, a.miniboard {
  text-decoration: none;
}

/* ---------- Header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  /* viewport-fit=cover lets content run under the status bar / Dynamic Island
     (app-skallene + iPhone-Safari); the safe-area inset keeps the header clear. */
  padding: calc(14px + env(safe-area-inset-top)) 24px 14px;
  background: color-mix(in oklab, var(--bg) 86%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.header-left { display: flex; align-items: center; gap: 18px; min-width: 0; }
/* FLOGVIT.puzzles-wordmark (#334): FLOGVIT i grotesk 800, produktnavnet i mono,
   prikken i aksent. To lenker — FLOGVIT → flogvit.com, .puzzles → forsiden. */
.wordmark {
  display: inline-flex; align-items: baseline; white-space: nowrap;
  padding: 4px; font-size: 17px; color: var(--ink);
}
.wordmark a { color: var(--ink); text-decoration: none; }
.wordmark a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.wm-brand { font-weight: 800; letter-spacing: -0.02em; }
.wm-product { font-family: 'IBM Plex Mono', monospace; font-weight: 500; font-size: 0.95em; }
.wordmark-dot { color: var(--accent); font-family: 'Schibsted Grotesk', sans-serif; font-weight: 800; }
/* lab-flaten (#340) har fiolett prikk (STYLE.md-aksenten), uavhengig av sidens --accent */
.wordmark-dot.lab { color: #6743b8; }
[data-theme="dark"] .wordmark-dot.lab { color: #b39df0; }
.logo-mark { width: 22px; height: 22px; color: var(--ink); }
.site-nav { display: flex; gap: 2px; }
.nav-link {
  border: none; background: none; padding: 8px 12px;
  font-size: 14px; font-weight: 500; color: var(--ink-2);
  border-radius: var(--radius-pill);
  transition: color 0.12s ease, background 0.12s ease;
  white-space: nowrap;
}
.nav-link:hover { color: var(--ink); }
.nav-link.active { color: var(--ink); background: color-mix(in oklab, var(--ink) 7%, transparent); }
.header-controls { display: flex; align-items: center; gap: 10px; flex-shrink: 0; margin-left: auto; }
.header-toggles { display: flex; align-items: center; gap: 10px; }
.lang-toggle {
  display: flex; border: 1px solid var(--line); border-radius: var(--radius-pill);
  overflow: hidden; background: var(--surface);
}
.lang-btn {
  border: none; background: none; padding: 6px 12px;
  min-height: 40px; display: inline-flex; align-items: center;
  font-size: 12px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--ink-2);
}
.lang-btn.active { background: var(--ink); color: var(--bg); }
.icon-btn {
  width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink);
  transition: border-color 0.15s ease;
}
.icon-btn:hover { border-color: var(--ink-2); }
.icon-btn.menu-btn { display: none; }
.account-link { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- Auth pages ---------- */
.auth-page { max-width: 440px; margin: 0 auto; padding: 56px 24px 80px; width: 100%; }
.auth-title { font-size: clamp(28px, 4vw, 40px); font-weight: 700; letter-spacing: -0.03em; margin-bottom: 24px; }
.auth-card { gap: 16px; }
.auth-card .btn-primary { text-align: center; }
.auth-error { color: var(--bad); font-size: 13px; }
.auth-alt { font-size: 13.5px; color: var(--ink-2); }
.auth-alt a { color: var(--accent-text); }
/* OIDC login (#317): "or" divider + the continue-with-Google button. */
.auth-divider {
  display: flex; align-items: center; gap: 12px;
  color: var(--ink-2); font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em;
}
.auth-divider::before, .auth-divider::after { content: ''; flex: 1; border-top: 1px solid var(--line); }
.oidc-btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; text-align: center; }
.auth-benefits {
  background: color-mix(in oklab, var(--accent) 6%, var(--surface));
  border: 1px solid color-mix(in oklab, var(--accent-text) 28%, var(--line));
  border-radius: calc(var(--radius) + 4px);
  padding: 18px 20px; margin-bottom: 20px;
}
.auth-benefits-lead { margin: 0 0 10px; font-size: 14px; line-height: 1.5; color: var(--ink); text-wrap: pretty; }
.auth-benefits-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; }
.auth-benefits-list li { font-size: 13.5px; line-height: 1.45; color: var(--ink-2); text-wrap: pretty; }
.theme-icon-light, [data-theme="dark"] .theme-icon-dark { display: none; }
.theme-icon-dark, [data-theme="dark"] .theme-icon-light { display: block; }
.mobile-menu {
  position: absolute; top: 100%; left: 0; right: 0;
  background: var(--bg); border-bottom: 1px solid var(--line);
  display: none; flex-direction: column; padding: 8px 12px 14px;
  box-shadow: 0 18px 30px -18px color-mix(in oklab, var(--ink) 25%, transparent);
}
.mobile-menu.open { display: flex; }
/* Backdrop behind the open menu (#288). z-index 40 sits below the header (sticky,
   z 50 — so the menu panel and the logo/hamburger stay lit and tappable) and above
   the page content, dimming everything else. toggles.js toggles .open in step with
   the menu. */
.menu-scrim {
  position: fixed; inset: 0; z-index: 40;
  background: color-mix(in oklab, var(--ink) 32%, transparent);
  opacity: 0; visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}
.menu-scrim.open { opacity: 1; visibility: visible; }
.mobile-menu-link {
  display: flex; justify-content: space-between; align-items: center;
  border: none; background: none; padding: 13px 10px;
  font-size: 16px; font-weight: 500; color: var(--ink);
  border-bottom: 1px solid var(--line); text-align: left;
}
.mobile-menu-link:last-child { border-bottom: none; }
.mobile-menu-count { font-size: 11px; color: var(--ink-2); }
/* Settings block at the foot of the mobile menu: account, language, theme —
   moved here so the header bar can shrink to just wordmark + hamburger. */
.mobile-menu-settings { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); }
.mobile-menu-settings .mobile-menu-link:last-child { border-bottom: 1px solid var(--line); }
.mobile-menu-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px; gap: 12px;
}
.mobile-menu-row-label { font-size: 16px; font-weight: 500; color: var(--ink); }
@media (max-width: 880px) {
  .site-nav { display: none; }
  .header-toggles, .account-link, .nav-pluss { display: none; }
  .icon-btn.menu-btn { display: grid; }
}

/* ---------- Pluss sales page (#306) ---------- */
.nav-pluss { color: var(--accent-text); font-weight: 600; }
.pluss-page { max-width: 520px; }
.pluss-lead { font-size: 15.5px; line-height: 1.6; color: var(--ink-2); margin-bottom: 24px; text-wrap: pretty; }
.pluss-card { gap: 20px; }
.pluss-price-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.pluss-price { font-size: 30px; font-weight: 600; letter-spacing: -0.02em; }
.pluss-price-note { font-size: 13px; color: var(--ink-2); }
.pluss-features { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 10px; }
.pluss-feature { display: flex; align-items: baseline; gap: 10px; font-size: 14.5px; line-height: 1.5; }
.pluss-tick { color: var(--accent-text); font-weight: 700; flex: none; }
.pluss-tag {
  font-size: 10.5px; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-text); border: 1px solid color-mix(in oklab, var(--accent) 40%, var(--line));
  border-radius: var(--radius-pill); padding: 1px 8px; margin-left: 4px; white-space: nowrap;
}
.pluss-cta { display: flex; flex-direction: column; gap: 12px; }
.pluss-smallprint, .pluss-footnote { font-size: 12.5px; line-height: 1.55; color: var(--ink-2); text-wrap: pretty; }
.pluss-footnote { margin-top: 18px; }

/* ---------- Buttons & chips ---------- */
.btn-primary {
  display: inline-block;
  background: var(--accent); color: #fff;
  white-space: nowrap;
  border: none; border-radius: var(--radius);
  padding: 15px 30px; font-size: 17px; font-weight: 600;
  letter-spacing: -0.01em;
  transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px color-mix(in oklab, var(--accent) 35%, transparent);
  filter: brightness(1.05);
}
.btn-primary:active { transform: translateY(0); }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink-2);
  padding: 8px 15px; font-size: 13.5px; font-weight: 500;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
  min-height: 36px;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* Pluss lock chip (#302): marks a gated feature and links to /pluss. */
.pluss-lock {
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid color-mix(in oklab, var(--accent) 45%, var(--line));
  border-radius: var(--radius-pill);
  padding: 3px 10px; font-size: 11.5px; font-weight: 500;
  color: var(--accent-text); background: color-mix(in oklab, var(--accent) 7%, transparent);
  white-space: nowrap; vertical-align: middle;
}
.pluss-lock:hover { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 14%, transparent); }

/* ---------- Hero ---------- */
.home { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; width: 100%; }
.hero {
  display: grid; grid-template-columns: 1.1fr 0.9fr;
  gap: 56px; align-items: center;
  padding: 80px 0 64px;
}
.hero h1 {
  font-size: clamp(46px, 6.4vw, 86px);
  font-weight: 700; line-height: 1.02; letter-spacing: -0.035em;
  display: flex; flex-direction: column;
}
.hero-l2 { color: var(--accent-text); }
.hero-sub {
  margin-top: 22px; max-width: 46ch;
  font-size: 18px; line-height: 1.6; color: var(--ink-2);
  text-wrap: pretty;
}
.hero-cta { margin-top: 32px; }
.hero-diffs { margin-top: 26px; display: flex; flex-direction: column; gap: 10px; }
.hero-diffs-label { font-size: 13px; color: var(--ink-2); }
.hero-board { display: flex; justify-content: center; }
/* Collapse the two-column hero to a single column on phones; the mini-board
   stacks under the text instead of being squeezed alongside it. */
@media (max-width: 720px) {
  .hero { grid-template-columns: 1fr; gap: 28px; padding: 40px 0 28px; }
}

@media (prefers-reduced-motion: no-preference) {
  .hero-text > *, .hero-board { animation: rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
  .hero-text > :nth-child(2) { animation-delay: 0.07s; }
  .hero-text > :nth-child(3) { animation-delay: 0.14s; }
  .hero-text > :nth-child(4) { animation-delay: 0.2s; }
  .hero-board { animation-delay: 0.15s; }
  @keyframes rise {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: translateY(0); }
  }
}

/* ---------- Mini board (hero) ---------- */
.miniboard-wrap { display: flex; flex-direction: column; gap: 12px; align-items: center; }
.miniboard {
  display: grid; grid-template-columns: repeat(9, 1fr); grid-auto-rows: 1fr;
  width: min(420px, 86vw); aspect-ratio: 1;
  padding: 0; background: var(--surface);
  border: 2px solid var(--line-strong); border-radius: var(--radius);
  overflow: hidden;
  font-family: 'IBM Plex Mono', monospace;
  box-shadow: 0 18px 50px -18px color-mix(in oklab, var(--ink) 28%, transparent);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.miniboard:hover {
  transform: translateY(-3px) rotate(-0.4deg);
  box-shadow: 0 26px 60px -18px color-mix(in oklab, var(--ink) 34%, transparent);
}
.mb-cell {
  display: grid; place-items: center;
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: clamp(13px, 2.4vw, 19px); font-weight: 500; color: var(--ink);
}
.mb-cell:nth-child(9n) { border-right: none; }
.mb-cell:nth-child(n+73) { border-bottom: none; }
.mb-cell.bdr { border-right: 2px solid var(--line-strong); }
.mb-cell.bdb { border-bottom: 2px solid var(--line-strong); }
.miniboard-hint { font-size: 11.5px; color: var(--ink-2); letter-spacing: 0.04em; }

/* ---------- Stats ---------- */
.stats {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 30px 0;
}
.stat { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.stat-num { font-size: clamp(22px, 3vw, 30px); font-weight: 500; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 13px; color: var(--ink-2); }
/* Reflow at narrow widths (WCAG 1.4.10): the long served-count never fits in
   three columns at 320px, so drop to two and shrink the number (#160). */
@media (max-width: 400px) {
  .stats { grid-template-columns: 1fr 1fr; gap: 16px 12px; }
  .stat-num { font-size: clamp(18px, 5.5vw, 24px); }
}

/* ---------- Catalog ---------- */
.catalog { padding: 72px 0 8px; }
.section-head { margin-bottom: 28px; }
.section-head h2 { font-size: 28px; font-weight: 700; letter-spacing: -0.02em; }
.section-head p { margin-top: 6px; color: var(--ink-2); font-size: 15px; }
.catalog-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.puzzle-card {
  background: var(--bg);
  /* Hairline separators drawn per-card so a ragged last row leaves no
     grey filler cell (an empty grid cell would otherwise show through). */
  box-shadow: 0 0 0 0.5px var(--line);
  padding: 26px 24px;
  display: flex; flex-direction: column; gap: 16px;
  transition: background 0.15s ease;
  border: none; text-align: left; cursor: pointer;
  font-family: inherit; color: inherit;
}
.puzzle-card:hover { background: var(--surface); }
.puzzle-card:hover .glyph { color: var(--accent); }
.glyph { width: 30px; height: 30px; color: var(--ink); transition: color 0.15s ease; }
.puzzle-card h3 { font-size: 16px; font-weight: 600; }
.puzzle-card p { margin-top: 4px; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); text-wrap: pretty; }
.live-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); margin-left: 8px; vertical-align: 2px;
}

/* ---------- More-than-puzzles section (home) ---------- */
.more { padding: 64px 0 0; }
.more-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
@media (max-width: 880px) { .more-grid { grid-template-columns: 1fr; } }
.more-card {
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  background: var(--surface); padding: 24px; display: flex; flex-direction: column; gap: 8px;
}
.more-card h3 { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; }
.more-card > p { margin: 0 0 8px; font-size: 13.5px; color: var(--ink-2); }
.more-links { display: flex; flex-direction: column; }
.more-link {
  display: flex; align-items: center; gap: 12px;
  border: none; background: none; padding: 9px 4px;
  font-size: 14.5px; font-weight: 500; color: var(--ink);
  border-top: 1px solid var(--line); text-align: left; cursor: pointer;
}
.more-link:hover { color: var(--accent); }
.more-link .glyph { width: 20px; height: 20px; color: var(--ink-2); }
.more-link:hover .glyph { color: var(--accent); }

/* ---------- About ---------- */
.about { padding: 72px 0 88px; }
.about-inner { max-width: 600px; }
.about h2 { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
.about p { margin-top: 12px; font-size: 16px; line-height: 1.65; color: var(--ink-2); text-wrap: pretty; }

/* ---------- Footer ---------- */
.site-footer { border-top: 1px solid var(--line); }
.footer-inner {
  max-width: var(--maxw); margin: 0 auto; padding: 26px 24px;
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 14px;
  font-size: 12.5px; color: var(--ink-2);
}
.footer-links { display: flex; align-items: center; flex-wrap: wrap; gap: 18px; }
.footer-links a { color: var(--ink-2); text-decoration: none; }
.footer-links a:hover { color: var(--accent); }
.footer-copy { color: var(--ink-2); text-decoration: none; }
.footer-copy:hover { color: var(--accent); }

/* ---------- Play ---------- */
.play { max-width: var(--maxw); margin: 0 auto; padding: 24px 24px 64px; width: 100%; }
.play-top {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 14px; margin-bottom: 26px;
}
.back-btn {
  display: flex; align-items: center; gap: 7px;
  border: none; background: none; padding: 8px 10px 8px 0;
  font-size: 14px; font-weight: 500; color: var(--ink-2);
}
.back-btn:hover { color: var(--accent); }
/* Number-first: the sticky active number / erase brush on the pad (#184). */
.num-btn.active-num, .action-btn.active-num {
  background: var(--accent); color: var(--bg);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.num-btn.active-num .num, .num-btn.active-num .num-left { color: var(--bg); }
/* Breadcrumb trail (#171): Puslespill → Wordoku → Statistikk */
.breadcrumb { margin: 0 0 22px; }
.breadcrumb ol {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0;
  list-style: none; margin: 0; padding: 0;
  font-size: 14px; color: var(--ink-2);
}
.breadcrumb li { display: flex; align-items: center; }
/* separator before every crumb except the first */
.breadcrumb li + li::before {
  content: '›'; margin: 0 9px; color: var(--ink-2); opacity: 0.6;
}
.breadcrumb a { color: var(--ink-2); font-weight: 500; }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb [aria-current='page'] { color: var(--ink); font-weight: 600; }
/* when the breadcrumb shares the tool-top row with the sketch badge */
.tool-top .breadcrumb { margin-bottom: 0; }
.play-layout { display: flex; flex-direction: column; gap: 28px; }
.board-col { display: flex; flex-direction: column; gap: 10px; }
.board-meta {
  display: flex; justify-content: space-between; gap: 16px;
  font-size: 12px; color: var(--ink-2); letter-spacing: 0.02em;
  width: min(540px, 100%); margin: 0 auto;
}
.board-meta span { white-space: nowrap; }
.timer { font-variant-numeric: tabular-nums; }
.board-wrap { position: relative; width: min(540px, 100%); margin: 0 auto; }
.board {
  /* minmax(0, 1fr) — not plain 1fr — so a big digit's min-content height can't
     grow the row past its 1/9 share (which would make cells non-square and
     push rows past the square board). Tracks stay fixed; overflow is clipped. */
  display: grid; grid-template-columns: repeat(9, minmax(0, 1fr)); grid-auto-rows: minmax(0, 1fr);
  width: 100%; aspect-ratio: 1;
  background: var(--surface);
  border: 2px solid var(--line-strong); border-radius: var(--radius);
  overflow: hidden;
  transition: opacity 0.2s ease;
}
.board.is-generating { opacity: 0.35; }
/* Large boards (n ≥ 10, #172): two-digit values need real cell width, and the
   square 1fr grid clipped them. Drop the square aspect, give cells a fixed
   minimum size and let the wrapper scroll instead of cramming/clipping. */
/* Large boards live in a bounded, scrollable viewport so an 81×81 doesn't
   tower off-screen or slide under the control column (#181). */
.board-wrap.large {
  width: 100%; max-width: 100%; max-height: min(78vh, 86vw);
  overflow: auto; overscroll-behavior: contain;
  border: 1px solid var(--line); border-radius: var(--radius);
}
.board.large {
  /* inline-grid + fixed-size columns/rows (set inline) give the board a
     deterministic intrinsic size that the scroll wrapper sees in BOTH axes;
     a block grid stretches to the container and kills horizontal scroll (#181). */
  display: inline-grid;
  width: max-content; max-width: none; aspect-ratio: auto;
  grid-auto-rows: 2.2em; overflow: visible; margin: 0;
  vertical-align: top;
}
/* Compact numpad for large boards — 81 full-size buttons would dwarf the board (#181). */
.numpad.large { grid-template-columns: repeat(var(--np-m, 6), 1fr); gap: 4px; }
.numpad.large .num-btn { min-height: 34px; }
.numpad.large .num { font-size: 14px; }
.numpad.large .num-left { display: none; }
/* ── Shared board cell sizing (#265) ──────────────────────────────────────
   One scale for every interactive/printed grid. A cell's glyph is sized from
   the cell's own width — board width ÷ columns — tracked live with container
   units (100cqi = the grid's rendered width). That makes a narrow Sudoku-3D
   sub-board and a full-width Sudoku board land on the same *relative* size,
   so every variant inherits consistent sizing instead of carrying its own
   clamp. Each grid sets --cols inline (its column count); a few variants tune
   --cell-fill (the glyph's share of the cell) where the digit should be
   denser or larger. Large (scrollable, n≥10) sudoku boards opt out — they use
   fixed cells, handled separately below. */
.board:not(.large), .mgrid { container-type: inline-size; }
.cell, .mg-cell {
  font-size: clamp(11px, calc(var(--cell-fill, 0.64) * 100cqi / var(--cols, 9)), 30px);
}
.cell {
  position: relative;
  display: grid; place-items: center;
  padding: 0; border: none; background: transparent;
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-family: 'IBM Plex Mono', monospace;
  transition: background 0.08s ease;
}
/* Large boards (n≥10) are scrollable with fixed-width cells, so cqi sizing
   doesn't apply — give their digits an explicit readable size (#172, #265). */
.board.large .cell { font-size: clamp(13px, 1.6vw, 17px); }
.cell.last-col { border-right: none; }
.cell.last-row { border-bottom: none; }
.cell.bdr { border-right: 2px solid var(--line-strong); }
.cell.bdb { border-bottom: 2px solid var(--line-strong); }
.cell.given { font-weight: 600; color: var(--ink); }
.cell.user { font-weight: 500; color: var(--accent); }
.cell.peer { background: color-mix(in oklab, var(--ink) 5%, transparent); }
.cell.same { background: color-mix(in oklab, var(--accent) 16%, transparent); }
.cell.sel {
  background: color-mix(in oklab, var(--accent) 22%, transparent);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.cell.bad { color: var(--bad); text-decoration: underline wavy var(--bad); text-underline-offset: 2px; }
.cell.bad.user { background: color-mix(in oklab, var(--bad) 12%, transparent); }
.cell.rbr { border-right: 2px solid var(--line-strong); }
.cell.rbb { border-bottom: 2px solid var(--line-strong); }
.cell.diag-cell::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: color-mix(in oklab, var(--accent) 7%, transparent);
}
/* #168: on the diagonal the uniform accent tint washed out the given/user
   distinction — user glyphs are the accent colour, so they blended into the
   accent tint. Keep the diagonal marked, but separate the two: user cells get
   a neutral tint so the accent glyph stands out, and are italic so the cue
   isn't colour alone (WCAG). Given cells keep their bold ink glyph. */
.cell.diag-cell.user::after { background: color-mix(in oklab, var(--ink) 7%, transparent); }
.cell.diag-cell.user { font-style: italic; }
.cell.oe-even {
  background-image: repeating-linear-gradient(45deg,
    color-mix(in oklab, var(--ink) 9%, transparent) 0 2px, transparent 2px 6px);
}
.dg-dot {
  position: absolute; top: 9%; left: 9%; width: 6px; height: 6px;
  border-radius: 50%; opacity: 0.75; pointer-events: none;
}
.pencil-grid {
  position: absolute; inset: 0;
  display: grid;
  place-items: center;
}
.pm { font-size: clamp(8px, 1.7vw, 11px); color: var(--ink-2); font-weight: 500; line-height: 1; }
.board-overlay[hidden] { display: none; }
.board-overlay {
  position: absolute; inset: 0; z-index: 5;
  display: grid; place-items: center;
  background: color-mix(in oklab, var(--bg) 55%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  border-radius: var(--radius);
}
.generating-label { font-size: 13px; color: var(--ink-2); animation: pulse 1.2s ease infinite; }
@keyframes pulse { 50% { opacity: 0.4; } }
.won-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  padding: 34px 44px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  box-shadow: 0 24px 70px -20px color-mix(in oklab, var(--ink) 40%, transparent);
  animation: pop 0.35s cubic-bezier(0.2, 1.2, 0.3, 1) both;
}
.won-card h2 { font-size: 30px; font-weight: 700; letter-spacing: -0.02em; }
.won-card p { color: var(--ink-2); font-size: 14px; }
.won-card .btn-primary { margin-top: 10px; font-size: 15px; padding: 12px 24px; }
.won-card .logo-mark { width: 28px; height: 28px; }
@keyframes pop { from { opacity: 0; transform: scale(0.92); } }
.check-msg {
  text-align: center; font-size: 12px; color: var(--ink-2);
  min-height: 20px; opacity: 0; transition: opacity 0.2s ease;
}
.check-msg.visible { opacity: 1; }
.board-legend { text-align: center; font-size: 11.5px; color: var(--ink-2); }

/* ---------- Play: variant picker ---------- */
.variant-bar {
  display: flex; flex-wrap: wrap; gap: 14px 28px;
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  background: var(--surface);
  padding: 14px 18px; margin-top: 30px;
}
.play-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
/* Sudoku variant bar: always open on desktop, collapsible on phones so the
   chips don't form a wall above the board (toggles.js closes it on load). */
.variant-collapse > summary { display: none; list-style: none; }
.variant-collapse > summary::-webkit-details-marker { display: none; }
@media (max-width: 820px) {
  .variant-collapse > summary {
    display: flex; align-items: center; gap: 7px; margin-top: 22px;
    font-size: 12.5px; font-weight: 500; color: var(--ink-2);
    cursor: pointer; user-select: none;
  }
  .variant-collapse > summary::before { content: '▸'; font-size: 10px; transition: transform 0.15s ease; }
  .variant-collapse[open] > summary::before { transform: rotate(90deg); }
  .variant-collapse > summary:hover { color: var(--ink); }
  .variant-collapse[open] .variant-bar { margin-top: 12px; }
}
/* Rules preamble: full text on desktop/no-JS, a "How to play" disclosure on
   phones so the long intro stops pushing the board below the fold (#285). */
.rules-collapse > summary { display: none; list-style: none; }
.rules-collapse > summary::-webkit-details-marker { display: none; }
@media (max-width: 820px) {
  .rules-collapse > summary {
    display: flex; align-items: center; gap: 7px;
    font-size: 12.5px; font-weight: 500; color: var(--ink-2);
    cursor: pointer; user-select: none;
  }
  .rules-collapse > summary::before { content: '▸'; font-size: 10px; transition: transform 0.15s ease; }
  .rules-collapse[open] > summary::before { transform: rotate(90deg); }
  .rules-collapse > summary:hover { color: var(--ink); }
  .rules-collapse[open] .tool-text { margin-top: 10px; }
}

.variant-group { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.variant-label { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.select {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
  padding: 9px 12px; font-size: 16px; font-family: inherit; font-weight: 500;
  cursor: pointer; max-width: 100%;
}
.chip-size { padding: 6px 11px; font-size: 12.5px; font-family: 'IBM Plex Mono', monospace; }
.top-chip { border-style: dashed; }
a.list-row { text-decoration: none; color: inherit; }
a.list-row:hover { background: color-mix(in oklab, var(--accent) 6%, transparent); }
.chip.disabled { opacity: 0.38; cursor: not-allowed; }
.chip.disabled:hover { border-color: var(--line); color: var(--ink-2); }

/* ---------- Controls ---------- */
.control-col { display: flex; flex-direction: column; gap: 16px; width: min(540px, 100%); margin: 0 auto; }
/* Single horizontal row of square keys (scales to 12×12); the row wraps only
   if a big variant's keys would drop below a tappable size. */
.numpad { display: grid; grid-template-columns: repeat(var(--np-m, 9), minmax(0, 1fr)); gap: 6px; }
.num-btn {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  aspect-ratio: 1; min-height: 0; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
  transition: border-color 0.12s ease, transform 0.12s ease, background 0.12s ease;
}
.num-btn:hover { border-color: var(--accent); transform: translateY(-1px); }
.num-btn .num { font-family: 'IBM Plex Mono', monospace; font-size: 19px; font-weight: 600; line-height: 1; }
.num-btn .num-left { position: absolute; bottom: 3px; font-size: 10px; color: var(--ink-2); }
.num-btn.spent { opacity: 0.35; }
.numpad.large .num-btn { aspect-ratio: auto; }
/* Sticky active brush (number-first input): a chosen number/erase stays lit. */
.num-btn.active-num, .action-btn.active-num {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in oklab, var(--accent) 14%, transparent);
}
.primary-actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.more-actions { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.action-label { white-space: nowrap; }

/* Secondary controls: a bottom-sheet on phones, inline in the sidebar on desktop. */
.more-sheet {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: flex-end;
  background: color-mix(in oklab, var(--ink) 38%, transparent);
  opacity: 0; visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}
.more-sheet.open { opacity: 1; visibility: visible; }
.more-sheet-panel {
  width: 100%;
  display: flex; flex-direction: column; gap: 12px;
  background: var(--bg); color: var(--ink);
  border-top: 1px solid var(--line); border-radius: 18px 18px 0 0;
  padding: 14px 18px calc(20px + env(safe-area-inset-bottom));
  box-shadow: 0 -24px 70px -20px color-mix(in oklab, var(--ink) 45%, transparent);
  transform: translateY(100%);
  transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.more-sheet.open .more-sheet-panel { transform: translateY(0); }
.more-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.more-sheet-title { flex: 1; font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-2); }
.action-btn {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 44px; padding: 10px 8px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
  font-size: 13px; font-weight: 500; color: var(--ink);
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
  /* Suppress the iOS long-press callout on the anchor-styled action buttons. */
  -webkit-touch-callout: none;
}
.action-btn:hover { border-color: var(--ink-2); }
.action-btn.active {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in oklab, var(--accent) 9%, transparent);
}
/* Only shown on phones (see the mobile media query). Must come after the
   .action-btn base rule — equal specificity, so source order decides. */
.more-btn { display: none; }
.new-btn { width: 100%; font-size: 16px; padding: 14px 0; text-align: center; }
.keys-hint { font-size: 12px; color: var(--ink-2); text-align: center; }

/* Mobile play: keep board + pad + primary row within one screen so you never
   have to scroll to reach the controls. The square board is height-capped (it
   stays width-bound on normal portrait phones); the keyboard hint is dropped
   on touch. */
@media (max-width: 899px) {
  .play-layout { gap: 12px; }
  .board-wrap:not(.large), .board-meta, .control-col {
    width: min(540px, 100%, calc(100svh - 300px));
  }
  /* Tighten the chrome above the board so the control row (check / more) clears
     the fold on short screens (iPhone 13 portrait etc., #283) without shrinking
     the board: less top padding, a calmer breadcrumb gap, snug board↔meta. */
  .play { padding-top: 14px; }
  .breadcrumb { margin-bottom: 12px; }
  .board-col { gap: 8px; }
  .more-btn { display: flex; }
  .keys-hint { display: none; }
  /* Square keys, as large as a single row of 9 allows (≈ width/9). A tight gap
     maximises the key size. */
  .numpad { gap: 2px; }
  .numpad:not(.large) .num-btn { aspect-ratio: 1; min-height: 0; }
  /* Primary controls as one compact icon-only row — the slots (six for entry
     types, four for drawing types, fewer when a type hides unbound ones) share
     the row evenly; text labels drop on phones (the buttons keep their
     aria-labels for screen readers). */
  .primary-actions { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 4px; }
  .primary-actions .action-label { display: none; }
  .primary-actions .action-btn { padding: 11px 0; gap: 0; }
  /* Drop the small remaining-count badge on phones — it crowds the digit; the
     spent (dimmed) state already signals when a number is used up. */
  .num-btn .num-left { display: none; }
  /* Bigger, bolder digits on phones — in the keypad. The board cells now scale
     from their own width via container units (#265), so no phone override. */
  .numpad:not(.large) .num-btn .num { font-size: 26px; }
}

/* Focus mode (#182): hide page chrome so only the board + input remain. The
   focus button stays (in .actions) so there's always a way out, plus Esc. */
body.focus .site-header,
body.focus .site-footer,
body.focus .breadcrumb,
body.focus .board-meta,
body.focus .variant-collapse,
body.focus .play-links,
body.focus .keys-hint,
body.focus .ad-slot,
body.focus .new-btn { display: none !important; }
body.focus .app { padding-top: 18px; }

/* ---------- Uniform play chrome (mobil-design + desktop-design) ---------- */
/* One vocabulary at every width: on phones a fixed frame (topbar · toolbar ·
   menu bottom-sheet); on desktop the same DOM renders as board-left +
   control-panel-right, with the sheet shown inline as the panel. Pages are
   scoped with :has(.play-topbar) — only interactive play views render it. */
.play-topbar { display: none; }
.action-btn[hidden] { display: none !important; }
.hint-pill-row { display: none; }
.hint-pill {
  border: 1px dashed var(--line); border-radius: var(--radius-pill);
  padding: 9px 18px; background: var(--surface);
  font-size: 12.5px; color: var(--ink-2); text-align: center;
}
/* Picker sections shared by the desktop panel and the mobile menu sheet. */
.sheet-sections { display: flex; flex-direction: column; gap: 14px; border-top: 1px solid var(--line); padding-top: 14px; }
.sheet-group { display: flex; flex-direction: column; gap: 8px; }
.sheet-group-label { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
/* Board title in the meta row (desktop title row; hidden with the meta on phones). */
.board-title { font-family: 'Schibsted Grotesk', sans-serif; font-size: 21px; font-weight: 700; letter-spacing: -0.02em; color: var(--ink); }

/* Interactive play pages hide the page chrome the frame/panel now carries, at
   EVERY width (desktop-design): breadcrumb + h1 + lede (the title lives in the
   board meta row), the rules text (behind «?» — kept in the DOM for search
   engines) and the legacy action rows whose functions live in the toolbar. */
main:has(.play-topbar) .breadcrumb,
main:has(.play-topbar) .tool-top,
main:has(.play-topbar) .tool-title,
main:has(.play-topbar) .tool-lede,
main:has(.play-topbar) .rules-collapse,
main:has(.play-topbar) .actions { display: none; }

@media (max-width: 899px) {
  /* The topbar replaces the site header + board meta on play pages; the
     footer + ad slot go too, so the play view is a scroll-free app frame. */
  body:has(.play-topbar) .site-header,
  body:has(.play-topbar) .site-footer,
  body:has(.play-topbar) .ad-slot { display: none; }
  main:has(.play-topbar) .board-meta,
  main:has(.play-topbar) .board-legend { display: none; }

  /* Both play containers pad 24px inline; the bar bleeds to the page edge so
     it reads as an app bar, and the container's top padding goes to the bar. */
  main:has(> .play-topbar), main:has(.tool-body > .play-topbar) { padding-top: 0; }
  .play-topbar {
    display: flex; align-items: center; gap: 8px;
    position: sticky; top: 0; z-index: 40;
    margin: 0 -24px; padding: calc(6px + env(safe-area-inset-top)) 14px 8px 10px;
    background: var(--bg); border-bottom: 1px solid var(--line);
  }
  .topbar-back { width: 36px; height: 36px; display: grid; place-items: center; color: var(--ink); flex: none; }
  .topbar-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
  .topbar-title { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.1; }
  .topbar-sub { font-size: 10.5px; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .topbar-timer { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--ink-2); flex: none; }
  .topbar-help {
    width: 30px; height: 30px; flex: none; padding: 0;
    border: 1px solid var(--line); border-radius: 50%;
    background: var(--surface); color: var(--ink-2);
    display: grid; place-items: center;
    font-size: 13px; font-weight: 600;
  }

  /* Focus mode keeps only board + input; the frame goes too (Esc/Meny exits). */
  body.focus .play-topbar { display: none; }

  /* Menu sheet: picker sections (difficulty/variant/size), links, CTA. The
     sheet can outgrow short phones — the panel scrolls, the page doesn't. */
  .more-sheet-panel { max-height: 88svh; overflow-y: auto; }

  /* Drawing types (Hitori, Arukone, Hashi, Wordsearch): a hint pill where the
     keypad would sit — input happens directly on the board. */
  .hint-pill-row { display: flex; justify-content: center; padding: 2px 0; }

  /* The keypad lives in the board column now; give it the same width budget
     as the board so the pair reads as one unit on phones. */
  .board-col .numpad { width: min(540px, 100%, calc(100svh - 300px)); }
  .puzzle-mock-board .numpad { width: 100%; max-width: 420px; }

  /* One-screen fit for the generic pages, mirroring the sudoku cap: board +
     pad + toolbar stay inside one viewport (the svh term wins on short,
     wide-ish phones; width wins on tall ones). */
  main:has(.play-topbar) .puzzle-mock-board .mgrid {
    width: min(420px, 100%, calc(100svh - 330px));
  }
  main:has(.play-topbar) .board-wrap.large { max-height: calc(100svh - 320px); }
  /* Fit-first zoom (mobil-design): the max-height is the height budget zoom.ts
     reads to compute the fit scale — the whole board renders scaled to fit,
     pinch/double-tap zooms in. Applies to tall generic boards (samurai) and,
     via the JS wrap, to large sudoku boards on phones. */
  main:has(.play-topbar) .puzzle-mock-board .zoom-viewport,
  main:has(.play-topbar) .board-wrap.large .zoom-viewport {
    max-height: calc(100svh - 320px);
  }
  /* Once zoom.ts has wrapped a large sudoku board, the wrap's own scroll box
     must stand down — the zoom viewport bounds and pans instead. */
  main:has(.play-topbar) .board-wrap.large:has(.zoom-viewport) {
    max-height: none; overflow: hidden;
  }
}

/* ---------- Desktop play layout (desktop-design) ---------- */
/* Board left, control panel right: the panel holds exactly the mobile menu's
   groups — toolbar, (hint), difficulty/variant/size, links, «Ny oppgave» —
   nothing hidden behind a menu. The keypad sits under the board. */
@media (min-width: 900px) {
  .play-layout {
    display: grid; grid-template-columns: minmax(0, 1fr) 300px;
    gap: 40px; align-items: start;
  }
  .control-col { width: auto; margin: 0; }
  .puzzle-mock { grid-template-columns: minmax(0, 1fr) 300px; gap: 40px; }
  /* Panel card look for both page kinds; sticky so it follows long boards. */
  .control-col, .puzzle-mock-side {
    position: sticky; top: 92px;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 16px;
    display: flex; flex-direction: column; gap: 16px;
  }
  /* Keypad row under the board: board-cell-sized square keys. */
  .numpad { grid-template-columns: repeat(var(--np-m, 9), minmax(0, 1fr)); gap: 5px; width: min(560px, 100%); margin: 0 auto; }
  .num-btn { aspect-ratio: 1; min-height: 0; }
  .num-btn .num { font-size: 20px; }
  /* Title row above the board: title + № left, timer + «?» + star right. */
  .board-col .board-meta, .puzzle-mock-board .board-meta { align-items: baseline; justify-content: flex-start; }
  .puzzle-mock-board .board-meta { width: 100%; max-width: 560px; }
  .board-col .board-meta .timer, .puzzle-mock-board .board-meta .timer { margin-left: auto; }
  /* Same toolbar as mobile: icon + small stacked label, one even row. */
  .primary-actions { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 5px; }
  .primary-actions .action-btn { flex-direction: column; gap: 3px; padding: 6px 0; }
  .primary-actions .action-label { font-size: 10px; color: var(--ink-2); }
  .more-actions { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  /* Panel order on generic pages: toolbar first, then the drawing hint. */
  .puzzle-mock-side .primary-actions { order: -2; }
  .puzzle-mock-side .hint-pill-row { order: -1; }
  .hint-pill-row { display: flex; }
  .hint-pill-row .hint-pill { width: 100%; text-align: left; border-radius: var(--radius); }
  /* On desktop the "more" sheet is just inline content in the panel. */
  .more-sheet {
    position: static; inset: auto; display: block; z-index: auto;
    background: none; opacity: 1; visibility: visible;
  }
  .more-sheet-panel {
    transform: none; box-shadow: none; border: none; border-radius: 0;
    background: none; padding: 0; gap: 16px;
  }
  .more-sheet-head { display: none; }
  .keys-hint { text-align: left; }
}

/* ---------- Ad slot (#307) ---------- */
/* One discreet unit at the foot of play pages, free users only. The height is
   reserved up front so the late ad can't shift the layout underneath it. */
.ad-slot {
  width: min(728px, calc(100% - 48px));
  margin: 28px auto 0;
  display: flex; flex-direction: column; gap: 4px;
}
.ad-slot .adsbygoogle { min-height: 90px; }
.ad-marker { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.ad-remove { font-size: 11.5px; color: var(--ink-2); align-self: flex-end; }
.ad-remove:hover { color: var(--accent-text); }

/* ---------- Category & tool pages ---------- */
.cat-page, .tool-page { max-width: var(--maxw); margin: 0 auto; padding: 40px 24px 72px; width: 100%; }
.cat-title, .tool-title {
  font-size: clamp(34px, 4.5vw, 52px); font-weight: 700; letter-spacing: -0.03em;
  /* Long single-word Norwegian compounds (Tilgjengelighetserklæring,
     Personvernerklæring) must be allowed to break, or they push the document
     wider than a narrow phone viewport and cause horizontal scroll (#279). */
  overflow-wrap: anywhere; hyphens: auto;
}
.cat-lede, .tool-lede { margin: 12px 0 32px; font-size: 17px; color: var(--ink-2); max-width: 56ch; text-wrap: pretty; }
.tool-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.mock-badge {
  font-size: 11px; letter-spacing: 0.05em; color: var(--ink-2);
  border: 1px dashed var(--ink-2); border-radius: var(--radius-pill);
  padding: 4px 12px;
  white-space: nowrap;
}
/* min-width:0 throughout: a flex column's default min-width:auto is the widest
   non-shrinkable child, so a wide field (e.g. the QR result) pushed the whole
   column past the viewport and caused horizontal scroll on phones (#286). */
.tool-body { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.tool-text { font-size: 15.5px; line-height: 1.6; color: var(--ink-2); max-width: 56ch; text-wrap: pretty; margin: 0; }
.tool-text a, .result-note a { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; transition: color 0.15s ease; }
.tool-text a:hover, .result-note a:hover { color: var(--ink); }

/* ---------- Generic cards & forms ---------- */
.duo { display: grid; grid-template-columns: minmax(240px, 360px) minmax(0, 1fr); gap: 20px; align-items: start; }
@media (max-width: 720px) { .duo { grid-template-columns: 1fr; } }
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px); padding: 22px;
  display: flex; flex-direction: column; gap: 14px; min-width: 0;
}
.card-title { font-size: 17px; font-weight: 600; margin: 0; }
.form-card { gap: 16px; }
.field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.field label { font-size: 12.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-2); }
.input {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
  padding: 11px 14px; font-size: 16px; font-family: inherit;
}
.input:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
/* Custom dropdown styling: drop the raw native arrow (which sits far out to the
   right with a gap) for a chevron tucked neatly beside the text. */
select.input, .select {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5l4 4 4-4' fill='none' stroke='%236e6d66' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
[data-theme="dark"] select.input, [data-theme="dark"] .select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5l4 4 4-4' fill='none' stroke='%2399978d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* ---------- Searchable combobox (enhances native <select>) ---------- */
.cbx { position: relative; display: inline-block; max-width: 100%; }
.cbx-native { display: none; }
.cbx-trigger {
  display: inline-flex; align-items: center; max-width: 100%; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--ink);
  padding: 9px 34px 9px 12px; font-family: inherit; font-size: 16px; font-weight: 500;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5l4 4 4-4' fill='none' stroke='%236e6d66' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}
[data-theme="dark"] .cbx-trigger {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5l4 4 4-4' fill='none' stroke='%2399978d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.cbx-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.cbx-open .cbx-trigger { border-color: var(--accent); }
.cbx-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cbx-panel {
  position: absolute; z-index: 50; top: calc(100% + 4px); left: 0;
  min-width: 100%; width: max-content; max-width: min(340px, 86vw);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 10px 30px color-mix(in oklab, var(--ink) 20%, transparent);
  padding: 6px; display: flex; flex-direction: column; gap: 6px;
}
.cbx-panel[hidden] { display: none; }
.cbx-search {
  width: 100%; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--ink); padding: 9px 11px;
  font-family: inherit; font-size: 16px;
}
.cbx-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.cbx-list { list-style: none; margin: 0; padding: 0; max-height: 264px; overflow-y: auto; }
.cbx-option {
  padding: 9px 11px; border-radius: 8px; cursor: pointer; color: var(--ink);
  font-size: 15px; white-space: nowrap;
}
.cbx-option.sel { font-weight: 600; }
.cbx-option.active { background: color-mix(in oklab, var(--accent) 14%, transparent); }
.cbx-option.cbx-custom { color: var(--accent); font-style: italic; }
.cbx-backdrop { display: none; }
@media (max-width: 560px) {
  .cbx-open .cbx-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; min-width: 0; max-width: none; z-index: 1001;
    border-radius: 16px 16px 0 0; padding: 12px; gap: 10px;
  }
  .cbx-open .cbx-list { max-height: 50vh; }
  .cbx-open .cbx-option { padding: 13px 12px; font-size: 16px; }
  .cbx-open .cbx-backdrop {
    display: block; position: fixed; inset: 0; z-index: 1000;
    background: color-mix(in oklab, var(--ink) 45%, transparent);
  }
}

.check-row { display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--ink-2); }
.checkbox {
  width: 16px; height: 16px; border: 1px solid var(--line); border-radius: 4px;
  background: var(--bg); display: inline-block;
}
.result-card { gap: 12px; }
.result-label { font-size: 12.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-2); }
.result-rows { list-style: none; margin: 0; padding: 0; }
.result-rows li { padding: 9px 2px; border-bottom: 1px solid var(--line); font-size: 15px; }
.result-rows li:last-child { border-bottom: none; }
.result-note { font-size: 12.5px; color: var(--ink-2); margin: 0; }
.inert-btn { display: inline-flex; justify-content: center; cursor: default; align-self: flex-start; }
/* The class is reused as a layout helper on real submit buttons — those must
   still look clickable. Only genuinely inert (non-button) uses keep default. */
button.inert-btn, a.inert-btn { cursor: pointer; }
.chip.inert { cursor: default; }
.inert-actions .action-btn { cursor: default; }
.mock-note { font-size: 12.5px; color: var(--ink-2); font-style: italic; margin: 4px 0 0; }

/* ---------- Specific tool bits ---------- */
.bp-digits { font-size: clamp(28px, 5vw, 44px); font-weight: 500; letter-spacing: 0.06em; }
.prime-chip { color: var(--accent-text); border-color: color-mix(in oklab, var(--accent) 40%, transparent); }
.phoneword { font-size: clamp(30px, 5vw, 46px); font-weight: 600; letter-spacing: 0.14em; color: var(--accent-text); }
.textified { font-size: 19px; line-height: 1.55; margin: 0; font-style: italic; }
.textout { font-size: 14px; line-height: 1.5; margin: 0; white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow: auto; }
.word-cols { columns: 3 110px; max-height: 360px; overflow: auto; }
.word-cols li { break-inside: avoid; }
textarea.input { resize: vertical; min-height: 80px; }
.calc-row { display: flex; align-items: center; gap: 12px; color: var(--ink-2); }
.calc-in { font-size: 14px; }
.calc-out { font-size: 26px; font-weight: 600; color: var(--accent-text); }
.dm-grid {
  display: grid; width: min(190px, 60%); aspect-ratio: 1;
  border: 1px solid var(--line); padding: 8px; background: #fff; border-radius: 4px;
}
.dm { background: transparent; }
.dm.on { background: #111; }
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 18px; }
.gallery-item { margin: 0; display: flex; flex-direction: column; gap: 8px; }
.gallery-item figcaption { font-size: 11px; color: var(--ink-2); letter-spacing: 0.05em; }
.gallery-slot {
  width: 100%; aspect-ratio: 1; border-radius: 10px;
  border: 1px dashed var(--line); background: var(--surface);
  display: grid; place-items: center;
  color: var(--ink-2); font-size: 12px;
}
.list-rows { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px); overflow: hidden; background: var(--surface); }
.list-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 16px 20px; border-bottom: 1px solid var(--line);
}
.list-row:last-child { border-bottom: none; }
.list-row-main { display: flex; flex-direction: column; gap: 6px; }
.list-row-main h3 { margin: 0; font-size: 15.5px; font-weight: 600; }
.list-row-main p { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.tag {
  font-size: 10.5px; letter-spacing: 0.06em; color: var(--ink-2);
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 3px 10px;
}
.skel { display: inline-block; height: 13px; border-radius: 6px; background: color-mix(in oklab, var(--ink) 9%, transparent); }
.skel-sub { height: 10px; opacity: 0.7; }
.link-card { flex-direction: row; align-items: center; gap: 16px; text-decoration: none; color: inherit; max-width: 420px; }
.link-card:hover { border-color: var(--ink-2); }
.link-card .glyph { width: 26px; height: 26px; flex: none; }
.sheet-preview {
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
  width: min(230px, 80%); aspect-ratio: 210 / 297; padding: 9%;
  display: grid; grid-template-columns: 1fr 1fr; gap: 12%;
  align-content: center;
  box-shadow: 0 8px 22px -12px color-mix(in oklab, var(--ink) 30%, transparent);
}
.sheet-grid { display: grid; grid-template-columns: repeat(9, 1fr); aspect-ratio: 1; border: 1px solid #333; }
.sheet-cell { border-right: 0.5px solid #ddd; border-bottom: 0.5px solid #ddd; }
.sheet-cell.tbr { border-right: 1px solid #333; }
.sheet-cell.tbb { border-bottom: 1px solid #333; }
/* ---------- Lærer print sheet ---------- */
.print-sheet { margin-top: 28px; }
.print-title { font-size: 16px; font-weight: 600; margin-bottom: 16px; }
.print-answers-title { font-size: 13px; font-weight: 600; margin: 24px 0 12px; color: var(--ink-2); }
.print-grid { display: grid; gap: 18px; }
.print-grid.cols-1 { grid-template-columns: minmax(0, 360px); }
.print-grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 760px; }
.print-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: 760px; }
.print-sudoku { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.print-sudoku figcaption { font-size: 10px; color: var(--ink-2); }
.sheet-grid.big { width: 100%; aspect-ratio: 1; background: #fff; }
.sheet-grid.big .sheet-cell { display: grid; place-items: center; font-family: 'IBM Plex Mono', monospace; font-size: clamp(10px, 2vw, 16px); color: #111; }
.answers .sheet-grid.big .sheet-cell { font-size: clamp(7px, 1.4vw, 11px); color: #444; }
@media print {
  .print-grid.answers { page-break-before: always; }
  .sheet-grid.big .sheet-cell { color: #000; }
}
.store-pills { display: flex; gap: 10px; margin-top: 18px; }
.store-pill {
  border: 1px solid var(--ink); border-radius: var(--radius-pill);
  padding: 9px 20px; font-size: 13px; font-weight: 500;
}
/* Sudokuto end-state overlay (#193): a clear win/loss banner over the board
   with the reason and a new-game button, instead of just a status line. */
.sto-overlay {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 24px; text-align: center;
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  backdrop-filter: blur(2px); border-radius: var(--radius);
}
.sto-overlay-text { font-size: clamp(18px, 3vw, 24px); font-weight: 700; margin: 0; max-width: 22ch; }
.sto-overlay.won { box-shadow: inset 0 0 0 3px var(--good, #2a9d8f); }
.sto-overlay.lost { box-shadow: inset 0 0 0 3px var(--bad); }
.sto-overlay.won .sto-overlay-text { color: var(--good, #2a9d8f); }
.sto-overlay.lost .sto-overlay-text { color: var(--bad); }

/* Settings page (#185) */
.settings-list { display: flex; flex-direction: column; gap: 2px; margin: 0 0 12px; }
/* The whole row is the <label>, so it's the tap target — guarantee it clears the
   44px minimum even on hint-less rows, and give the box a little more size (#289). */
.settings-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: 44px;
  padding: 12px 0; border-bottom: 1px solid var(--line); cursor: pointer;
}
.settings-label { display: flex; flex-direction: column; gap: 3px; font-size: 15px; }
.settings-hint { font-size: 13px; color: var(--ink-2); }
.settings-toggle { width: 22px; height: 22px; flex: none; accent-color: var(--accent); cursor: pointer; }

/* ---------- Mock puzzle boards ---------- */
.puzzle-mock { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 40px; align-items: start; }
/* Breakpoint unified with the sudoku play page (mobil-design): 899px. */
@media (max-width: 899px) { .puzzle-mock { grid-template-columns: 1fr; } }
.puzzle-mock-board { position: relative; display: flex; flex-direction: column; align-items: center; gap: 16px; }
/* Win celebration overlaid on the board for non-sudoku puzzles. */
.puzzle-won {
  position: absolute; inset: 0; z-index: 6;
  display: grid; place-items: center;
  background: color-mix(in oklab, var(--bg) 55%, transparent);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  border-radius: var(--radius);
}
/* Boggle injects its play UI (timer, word input, found words) as a second
   child; stack it under the grid instead of squeezing it alongside. */
.puzzle-mock-board:has(.boggle-play) { flex-direction: column; align-items: center; }
.puzzle-mock-side { display: flex; flex-direction: column; gap: 16px; }
.puzzle-mock-side .board-meta { width: auto; margin: 0; flex-direction: column; gap: 6px; align-items: flex-start; }
/* The generic puzzle player's action buttons (New/Fasit/Print/Notat/undo/…):
   a tidy grid instead of default block flow; a touch more compact on phones. */
.actions { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
@media (max-width: 899px) {
  .actions { grid-template-columns: repeat(3, 1fr); }
  /* Pull the action buttons up directly under the pad/feedback so the core
     tools (Sjekk/Angre/Notat …) sit within reach instead of below the puzzle
     meta + archive form (#284). The side is a flex column, so reorder with
     `order`: pad → feedback → hint pill → toolbar → (hidden desktop rows). */
  .puzzle-mock-side .numpad { order: 0; }
  .puzzle-mock-side .combo-help,
  .puzzle-mock-side .check-msg { order: 1; }
  .puzzle-mock-side .hint-pill-row { order: 2; }
  .puzzle-mock-side .primary-actions { order: 3; }
  .puzzle-mock-side .actions { order: 4; }
  .puzzle-mock-side .board-meta { order: 5; }
  .puzzle-mock-side .nr-form { order: 6; }
  /* Tighten the chrome above the board on phones so more of the control row
     clears the fold (#284): less top padding, a snugger lede, tighter stacks. */
  .cat-page, .tool-page { padding-top: 16px; }
  .tool-lede { margin: 8px 0 16px; }
  .puzzle-mock { gap: 12px; }
  .puzzle-mock-board { gap: 10px; }
  .puzzle-mock-side { gap: 10px; }
}
.dim-row { display: flex; flex-direction: column; gap: 8px; }
/* Size/variant pickers are secondary while solving — tucked into a collapsible. */
.variant-picker { border-top: 1px solid var(--line); padding-top: 14px; }
.variant-picker > summary {
  font-size: 12.5px; font-weight: 500; color: var(--ink-2); cursor: pointer;
  list-style: none; display: flex; align-items: center; gap: 7px; user-select: none;
}
.variant-picker > summary::-webkit-details-marker { display: none; }
.variant-picker > summary::before { content: '▸'; font-size: 10px; transition: transform 0.15s ease; }
.variant-picker[open] > summary::before { transform: rotate(90deg); }
.variant-picker > summary:hover { color: var(--ink); }
.variant-picker .dim-row { margin-top: 14px; }
.mgrid {
  display: grid; grid-auto-rows: 1fr; width: min(420px, 100%); aspect-ratio: 1;
  background: var(--surface); border: 2px solid var(--line-strong);
  border-radius: var(--radius); overflow: hidden;
  font-family: 'IBM Plex Mono', monospace;
}
.mg-cell {
  position: relative; display: grid; place-items: center;
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-weight: 500; color: var(--ink);
  /* font-size: shared cqi scale (#265). Dragging (Hashi bridges, Arukone lines,
     Hitori shading, Wordsearch select) must not select the cell text. */
  user-select: none; -webkit-user-select: none;
}
.mg-cell.tbr { border-right: 2px solid var(--line-strong); }
.mg-cell.tbb { border-bottom: 2px solid var(--line-strong); }
.mg-cell.blk { background: var(--line-strong); }
[data-theme="dark"] .mg-cell.blk { background: #000; }
.mg-cell.sh { background: color-mix(in oklab, var(--ink) 78%, transparent); color: var(--bg); }
/* Boggle dice etc.: deliberately large glyphs, so they get their own (higher)
   bounds rather than the shared cell cap (#265). */
.mg-cell.big { font-size: clamp(20px, calc(0.5 * 100cqi / var(--cols, 4)), 38px); font-weight: 600; }
/* Hashi has no grid or frame — islands are circles, bridges are the lines that
   connect them (matching the original puzzles). */
.mgrid-hashi { background: transparent; border: none; border-radius: 0; overflow: visible; }
.mgrid-hashi .mg-cell { border: none; }
.mg-cell.island {
  font-weight: 700; color: var(--ink); --cell-fill: 0.42;
  background: radial-gradient(circle at center, var(--surface) 0 43%, var(--ink) 44% 47%, transparent 48%);
  transition: opacity 0.15s ease, color 0.15s ease;
  /* The island is a disc drawn in a square cell. Round the cell so the keyboard
     :focus-visible outline (and any ring) hugs the circle instead of drawing a
     square box around it — the odd "firkant rundt cellen" from #291. */
  border-radius: 50%;
}
/* Dim an island once it has exactly enough bridges; flag it red if too many. */
.mg-cell.island.done { opacity: 0.35; }
.mg-cell.island.over { color: #c0392b; background: radial-gradient(circle at center, var(--surface) 0 43%, #c0392b 44% 47%, transparent 48%); }
/* Selecting an island during a drag tints its circle instead of a square box. */
.mgrid-hashi .mg-cell.sel { box-shadow: none; }
.mgrid-hashi .mg-cell.island.sel { background: radial-gradient(circle at center, color-mix(in oklab, var(--accent) 16%, var(--surface)) 0 43%, var(--accent) 44% 47%, transparent 48%); }
/* Live preview of the span while dragging a bridge. */
.mg-cell.bridge-prev { background: color-mix(in oklab, var(--accent) 22%, transparent); }
/* Shift+hover: islands currently linked to the hovered one (one-group check). */
.mgrid-hashi .mg-cell.island.group { color: var(--accent); background: radial-gradient(circle at center, color-mix(in oklab, var(--accent) 22%, var(--surface)) 0 43%, var(--accent) 44% 47%, transparent 48%); }
/* Islands the selected one can still reach (faint helper, #139). */
.mgrid-hashi .mg-cell.island.hint { box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--accent) 40%, transparent); border-radius: 50%; }
/* "Needs N more bridges" badge on the selected island (#139). */
.mg-cell.island.needs::after {
  content: attr(data-needs);
  position: absolute; top: 0; right: 0;
  min-width: 14px; height: 14px; padding: 0 3px;
  font-size: 9px; line-height: 14px; font-weight: 700;
  color: #fff; background: var(--accent); border-radius: 8px;
  text-align: center; pointer-events: none; z-index: 4;
}
/* A closed, fully-satisfied group that can never join the rest (#139). The red
   ring is a shape cue; colour rides var(--bad) for the colourblind palette. */
.mgrid-hashi .mg-cell.island.iso {
  color: var(--bad);
  background: radial-gradient(circle at center, color-mix(in oklab, var(--bad) 14%, var(--surface)) 0 43%, var(--bad) 44% 47%, transparent 48%);
}
.mg-cell.endpoint {
  font-weight: 700; color: #fff;
  background: radial-gradient(circle at center, var(--pair, var(--accent)) 0 40%, transparent 41%);
}
/* Interactive Arukone: endpoints start as hollow rings and fill solid once the
   pair is connected end-to-end (#141). ring↔disc is a shape cue, not just hue.
   The pair colour comes from --pair (set per cell) so it matches the line. */
.mg-cell.endpoint.ep-live {
  color: var(--pair, var(--accent));
  background: radial-gradient(circle at center, transparent 0 36%, var(--pair, var(--accent)) 38% 46%, transparent 48%);
}
.mg-cell.endpoint.ep-live.connected {
  color: #fff;
  background: radial-gradient(circle at center, var(--pair, var(--accent)) 0 46%, transparent 48%);
}
/* Persistent progress line above an interactive board (#141). */
.puzzle-status { text-align: center; font-size: 13px; color: var(--ink-2); margin-bottom: 8px; min-height: 1.2em; }
/* Pinch-zoom container (#135): clip to the board, transform an inner layer.
   touch-action:none lets our two-finger handler run instead of native zoom.
   Widths stay 100% so the wrapped board keeps its original responsive size;
   the puzzle grid is re-centred since it is no longer a flex child. */
.zoom-viewport { position: relative; overflow: hidden; touch-action: none; width: 100%; }
.zoom-layer { transform-origin: 0 0; width: 100%; }
.zoom-layer > .mgrid { margin-inline: auto; }
.mg-cell.tile { background: color-mix(in oklab, var(--ink) 5%, transparent); box-shadow: inset 0 -2px 0 color-mix(in oklab, var(--ink) 14%, transparent); }
.mg-cell.hole { background: color-mix(in oklab, var(--ink) 12%, transparent); }
.mgrid-gap { gap: 5px; padding: 5px; background: var(--surface); }
.mgrid-gap .mg-cell { border: none; border-radius: 6px; }
.mg-cell.kclue { background: color-mix(in oklab, var(--ink) 88%, transparent); }
/* Main diagonal (top-left → bottom-right) splits the clue cell into a lower-
   left triangle (the down / vertical sum) and an upper-right triangle (the
   across / horizontal sum); each number sits centred in its own triangle so it
   is clear which run the sum belongs to. */
.kclue-in { position: absolute; inset: 0; background: linear-gradient(to bottom left, transparent calc(50% - 0.5px), var(--bg) calc(50% - 0.5px), var(--bg) calc(50% + 0.5px), transparent calc(50% + 0.5px)); }
.kclue-in .kd, .kclue-in .kr { position: absolute; transform: translate(-50%, -50%); font-style: normal; font-size: clamp(11px, 1.6vw, 12px); line-height: 1; letter-spacing: -0.02em; color: var(--bg); font-weight: 600; white-space: nowrap; }
.kclue-in .kd { left: 28%; top: 72%; }   /* down / vertical sum — lower-left triangle */
.kclue-in .kr { left: 72%; top: 28%; }   /* across / horizontal sum — upper-right triangle */
/* Remaining-sum feedback as digits are placed in a run. */
.kr.kremain, .kd.kremain { color: #f0c674; }
.kr.kdone, .kd.kdone { color: #7bd88f; }
.kr.kover, .kd.kover { color: #ff6b6b; }
/* The across/down clues governing the selected cell. */
.mg-cell.kclue.kactive { outline: 2px solid var(--accent); outline-offset: -2px; z-index: 3; }
.cage-cell { display: grid; place-items: center; position: absolute; inset: 0; }
.cage-op { position: absolute; top: 5%; left: 7%; font-style: normal; font-size: clamp(11px, 1.7vw, 13px); color: var(--ink-2); font-weight: 600; }
.mock-svg { width: min(420px, 100%); aspect-ratio: 1; display: block; }

/* ---------- DB-backed puzzle pages ---------- */
.cage-v { position: relative; z-index: 1; }
.mgrid-gt { overflow: visible; }
.mgrid-gt .mg-cell { overflow: visible; }
.gt-r, .gt-b {
  position: absolute; z-index: 2; font-style: normal; font-weight: 700;
  color: var(--accent); font-size: clamp(10px, 1.8vw, 15px); line-height: 1;
  pointer-events: none;
}
.gt-r { right: 0; top: 50%; transform: translate(55%, -50%); }
.gt-b { bottom: 0; left: 50%; transform: translate(-50%, 60%); }
.mg-cell.bridge { color: var(--ink-2); font-weight: 400; }
/* Drawn arukone line numbers: bigger than before and coloured per pair via an
   inline style set in the client (#198). */
.mg-cell.path { color: var(--ink-2); --cell-fill: 0.5; font-weight: 600; }
/* Arukone draws connecting strokes (#263): a path cell shows coloured line
   segments toward its same-pair neighbours instead of repeating the number.
   currentColor = the cell's pair colour (set inline), so the line is coloured. */
.mg-cell .ar-line { position: absolute; inset: 0; pointer-events: none; color: inherit; z-index: 1; }
.ar-line i { position: absolute; background: var(--pair, currentColor); }
.ar-line .up { left: calc(50% - 3px); top: 0; width: 6px; height: 52%; }
.ar-line .down { left: calc(50% - 3px); bottom: 0; width: 6px; height: 52%; }
.ar-line .left { top: calc(50% - 3px); left: 0; height: 6px; width: 52%; }
.ar-line .right { top: calc(50% - 3px); right: 0; height: 6px; width: 52%; }
.ar-line .dot { left: calc(50% - 4px); top: calc(50% - 4px); width: 8px; height: 8px; border-radius: 50%; }
/* Endpoint number/disc sit above the line stub. */
.mg-cell.endpoint { z-index: 2; }
/* The line being dragged right now, distinct from already-committed paths (#188). */
.mg-cell.ar-active { background: color-mix(in oklab, var(--accent) 26%, transparent); box-shadow: inset 0 0 0 2px var(--accent); color: var(--ink); }
.mg-cell.sum {
  border: none; background: transparent; color: var(--ink-2);
  --cell-fill: 0.4; font-weight: 600;
}
/* Shuffle (#190): a line that hits its sum, or already overshoots it. A ✓/!
   glyph backs up the colour so it isn't colour-only (WCAG). */
.mg-cell.sum.sum-ok { color: var(--good, #2a9d8f); }
.mg-cell.sum.sum-ok::after { content: '✓'; font-size: 0.8em; margin-left: 2px; }
.mg-cell.sum.sum-over { color: var(--bad); }
.mg-cell.sum.sum-over::after { content: '!'; font-size: 0.85em; margin-left: 1px; }
.mg-cell.diag-sh { background: color-mix(in oklab, var(--ink) 9%, transparent); }
.mg-cell.diag-sh.sh { background: color-mix(in oklab, var(--ink) 78%, transparent); }

/* cursor:pointer signals the cells are draggable/selectable (#186).
   line-height:1 keeps the letter's line box from spilling past its row: on
   phones the font is bumped to ~18px but a 15×15 row is only ~24px tall, and the
   inherited ~1.3 line-height pushed the bottom rows' glyphs against/through the
   grid edge. A single centred glyph never needs the extra leading. */
.mgrid-ws .mg-cell { --cell-fill: 0.5; line-height: 1; border-color: color-mix(in oklab, var(--line) 50%, transparent); cursor: pointer; }
.mgrid-ws .mg-cell:hover { background: color-mix(in oklab, var(--accent) 9%, transparent); }
/* Live highlight while dragging a wordsearch selection — strong, with a ring so
   the in-progress word is obvious (#186). */
.mg-cell.ws-prev { background: color-mix(in oklab, var(--accent) 32%, transparent); box-shadow: inset 0 0 0 2px var(--accent); }
/* Fillomino: outline equal-number regions; tint blocks by size correctness. */
.mg-cell.rb-t { border-top: 2px solid var(--ink); }
.mg-cell.rb-r { border-right: 2px solid var(--ink); }
.mg-cell.rb-b { border-bottom: 2px solid var(--ink); }
.mg-cell.rb-l { border-left: 2px solid var(--ink); }
.mg-cell.fm-ok { background: color-mix(in oklab, #1e8e3e 16%, transparent); }
.mg-cell.fm-bad { background: color-mix(in oklab, #c0392b 18%, transparent); }
/* Shared edge between two equal-size Fillomino regions (the rule players break
   most). Colour rides the colourblind palette via var(--bad). */
.mg-cell.fmc-t { border-top: 3px solid var(--bad); }
.mg-cell.fmc-r { border-right: 3px solid var(--bad); }
.mg-cell.fmc-b { border-bottom: 3px solid var(--bad); }
.mg-cell.fmc-l { border-left: 3px solid var(--bad); }
.ws-words { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 14px; max-width: 420px; }
.ws-word { font-size: 12.5px; color: var(--ink); }
.ws-word.found { color: var(--ink-2); text-decoration: line-through; text-decoration-thickness: 2px; }

.s3d-set { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; width: min(560px, 100%); }
.s3d-board { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.s3d-board .mgrid { width: 100%; border-width: 1.5px; }
.s3d-board figcaption { font-size: 10.5px; color: var(--ink-2); }
/* Digit size follows each sub-board's own width via the shared cqi scale
   (#265): three boards across desktop shrink the cells, so the digits shrink
   to match; one per row on phones (#258) lets each board — and its digits —
   grow. No per-breakpoint font override needed any more. */
@media (max-width: 560px) {
  .s3d-set { grid-template-columns: 1fr; }
}

.dm-svg { width: min(260px, 100%); height: auto; border-radius: 6px; }
/* QR tool (#176) */
.qr-svg { width: min(280px, 100%); height: auto; border-radius: 6px; }
.qr-logo-controls { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.qr-logo-row { display: flex; align-items: center; gap: 10px; }
.qr-logo-row input[type="range"] { flex: 1; }
.qr-ecc-warn { font-size: 12px; color: var(--accent-text); margin: 0; }
.qr-downloads { display: flex; gap: 10px; flex-wrap: wrap; }

.fractal-app { display: flex; flex-direction: column; gap: 14px; }
.fractal-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.fractal-controls label { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.fractal-controls .input { width: auto; }
.fractal-stage { position: relative; width: 100%; max-width: 960px; }
.fractal-stage canvas {
  width: 100%; height: auto; display: block; border-radius: var(--radius);
  border: 1px solid var(--line); cursor: crosshair; background: #000;
  touch-action: none;
}
.fractal-marker {
  position: absolute; border: 1.5px dashed #fff; mix-blend-mode: difference;
  pointer-events: none;
}

/* ---------- Interactive puzzle play ---------- */
.mg-cell.editable { cursor: pointer; }
.mg-cell.editable:hover { background: color-mix(in oklab, var(--accent) 10%, transparent); }
/* Soft selection (matches the main sudoku) instead of a hard outline box. */
.mg-cell.sel {
  background: color-mix(in oklab, var(--accent) 18%, transparent);
  box-shadow: inset 0 0 0 2px var(--accent);
}
/* Hide the browser focus ring on tap, keep it for keyboard navigation. */
.mg-cell:focus { outline: none; }
.mg-cell:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Live conflict highlighting for the Latin-square variants. */
.mg-cell.peer { background: color-mix(in oklab, var(--ink) 5%, transparent); }
.mg-cell.same { background: color-mix(in oklab, var(--accent) 16%, transparent); }
.mg-cell.bad { color: var(--bad); background: color-mix(in oklab, var(--bad) 14%, transparent); text-decoration: underline wavy var(--bad); text-underline-offset: 2px; }
.mg-cell.shaded { background: color-mix(in oklab, var(--ink) 78%, transparent); color: var(--bg); }
/* Hitori: a number still repeated in its row/column among the white cells. */
.mg-cell.dup:not(.shaded) {
  background: color-mix(in oklab, #e8a13a 26%, transparent);
  outline: 2px dotted #b25e00; outline-offset: -2px;
}
/* Hitori "kept white" mark: a ring around the number (solver aid only). */
.mg-cell.ringed::after {
  content: ''; position: absolute; inset: 16%;
  border: 2px solid var(--ink-2); border-radius: 50%; pointer-events: none;
}
.mg-cell.err { background: color-mix(in oklab, #d33 28%, transparent); }
.mg-cell .cage-v { font-size: inherit; }
.check-msg.ok { color: #2c7a3d; }
.check-msg.bad { color: #c0392b; }
/* On-screen number/letter pad for the entry puzzles, sized to sit right under
   the board so the pointer barely travels between a cell and a number. */
.puzzle-pad { display: grid; grid-template-columns: repeat(auto-fit, minmax(38px, 1fr)); gap: 6px; width: min(420px, 100%); }
.pad-btn {
  min-height: 44px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--ink); cursor: pointer;
  font-family: 'IBM Plex Mono', monospace; font-size: 18px; font-weight: 600;
  transition: border-color 0.12s ease;
}
.pad-btn:hover { border-color: var(--accent); }
.pad-btn:disabled, .pad-btn.spent { opacity: 0.3; cursor: default; border-color: var(--line); }
.pad-del { font-size: 16px; }
/* Kakuro combination helper shown for the selected cell's runs. */
.combo-help { white-space: pre-wrap; font-size: 11.5px; line-height: 1.55; color: var(--ink-2); width: min(420px, 100%); text-align: center; min-height: 1em; }
.mg-cell.bg-path { background: color-mix(in oklab, var(--accent) 30%, transparent); }
.boggle-play { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; max-width: 420px; }
.boggle-bar { display: flex; justify-content: space-between; font-weight: 600; }
.bg-preview { min-height: 1.3em; font-weight: 600; letter-spacing: 0.08em; color: var(--ink-2); }
.bg-preview.valid { color: #2c7a3d; }
/* Pointer-up verdict flash: distinct colour + shape so already-found reads
   apart from never-a-word even without relying on hue alone. */
.bg-preview.dupe { color: #b25e00; text-decoration: underline; }
.bg-preview.invalid { color: var(--bad); text-decoration: line-through; }
/* Post-submit confirmation, sized up so it reads at a glance with the phone
   keyboard up (#267); paired with the input flash below. */
.bg-preview.bg-flash { font-size: 1.05rem; font-weight: 700; letter-spacing: 0.04em; }
#bg-word.bg-ok { animation: bg-flash-ok 0.6s ease; }
#bg-word.bg-bad { animation: bg-flash-bad 0.6s ease; }
@keyframes bg-flash-ok { 0%, 100% { background: var(--surface); } 30% { background: color-mix(in oklab, #2c7a3d 24%, var(--surface)); } }
@keyframes bg-flash-bad { 0%, 100% { background: var(--surface); } 30% { background: color-mix(in oklab, var(--bad) 24%, var(--surface)); } }
@media (prefers-reduced-motion: reduce) { #bg-word.bg-ok, #bg-word.bg-bad { animation: none; } }

/* ---------- Content pages ---------- */
.content-body { max-width: 680px; display: flex; flex-direction: column; gap: 14px; }
.content-body .textout { white-space: pre-wrap; overflow-x: auto; }
.lyrics {
  font-family: inherit; font-size: 15.5px; line-height: 1.65; white-space: pre-wrap;
  color: var(--ink); margin: 8px 0;
}
.faq-item { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.combo-section { margin-bottom: 18px; }
.combo-rows { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; }
.combo-row { display: flex; gap: 10px; }
.combo-sum { min-width: 28px; font-weight: 600; color: var(--ink-2); text-align: right; }
.essays { max-width: 680px; margin-top: 8px; }
.essay-card { margin-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.essay-table { border-collapse: collapse; font-size: 12.5px; margin: 6px 0; }
.essay-table th, .essay-table td {
  border: 1px solid var(--line); padding: 5px 12px; text-align: left;
}
.essay-table th { color: var(--ink-2); font-weight: 600; }
.field-row { display: flex; flex-wrap: wrap; gap: 12px; }
.field-row .field { flex: 1 1 140px; min-width: 0; }

.nr-form { display: flex; flex-direction: column; gap: 8px; }
.nr-form label { font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-2); }
.nr-row { display: flex; gap: 8px; }
.nr-row .input { max-width: 140px; }

.print-puzzle { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.print-puzzle figcaption { font-size: 10px; color: var(--ink-2); }
.print-puzzle .mgrid { width: 100%; }
.print-puzzle .s3d-set { width: 100%; }
.answers .print-puzzle .mg-cell { font-size: clamp(8px, 1.4vw, 12px); color: var(--ink-2); }

/* ---------- Colour-blind-safe error palette ---------- */
/* Replace red error cues with a blue (Wong palette) tone plus a dashed outline,
   so errors are distinguishable without relying on red/green perception. */
body.cb { --bad: #0072b2; }
body.cb .cell.bad, body.cb .mg-cell.bad {
  outline: 2px dashed currentColor; outline-offset: -2px;
}
body.cb .mg-cell.err {
  background: color-mix(in oklab, #0072b2 30%, transparent);
  outline: 2px dashed #0072b2; outline-offset: -2px;
}
body.cb .check-msg.bad, body.cb .sto-msg.bad { color: #0072b2; }
body.cb .mg-cell.island.over { color: #0072b2; background: radial-gradient(circle at center, var(--surface) 0 43%, #0072b2 44% 47%, transparent 48%); }
.footer-toggle { border: none; background: none; padding: 6px 0; font-size: 12.5px; color: var(--ink-2); cursor: pointer; }
.footer-toggle:hover { color: var(--accent); }
.footer-toggle[aria-pressed="true"] { color: var(--accent); font-weight: 600; }

/* ---------- Print: just the board ---------- */
@media print {
  .no-print, .site-header, .site-footer, .check-msg, body::before,
  .variant-bar, .board-legend, .mock-badge { display: none !important; }
  body { background: #fff; color: #000; }
  .play { padding: 0; }
  .board-wrap, .board-meta { width: 16cm; margin: 0 auto; }
  .board-meta { margin-bottom: 4mm; color: #000; }
  .board { border-color: #000; border-radius: 0; box-shadow: none; }
  .cell { border-color: #bbb; background: #fff !important; box-shadow: none !important; }
  .cell.bdr, .cell.bdb, .cell.rbr, .cell.rbb { border-color: #000; }
  .cell.given { color: #000; }
  .cell.user { color: #555; }
  .cell.oe-even { background-image: repeating-linear-gradient(45deg, #ccc 0 2px, transparent 2px 6px) !important; }
  .mgrid, .mock-svg { width: 14cm; }
  .puzzle-mock { grid-template-columns: 1fr; }
  .print-puzzle .mgrid, .print-grid .mgrid { width: 100%; }
  .mgrid { background: #fff; border-color: #000; border-radius: 0; }
  .mg-cell { color: #000; border-color: #ccc; }
  .mg-cell.tbr { border-right-color: #000; }
  .mg-cell.tbb { border-bottom-color: #000; }
  .mg-cell.sh { background: #777 !important; color: #fff; }
  .gt-r, .gt-b { color: #000; }
  .tool-lede, .tool-title { color: #000; }
}

/* ---------- Samurai: cross-shaped board with void cells ---------- */
.mgrid-samurai {
  background: transparent; border: none; border-radius: 0; overflow: visible;
  width: min(560px, 100%);
}
.mgrid-samurai .mg-cell { background: var(--surface); font-size: clamp(9px, 1.6vw, 15px); }
.mg-cell.tbl { border-left: 2px solid var(--line-strong); }
.mg-cell.tbt { border-top: 2px solid var(--line-strong); }
.mg-cell.void, .mgrid-samurai .mg-cell.void { background: transparent; border: none; }

/* ---------- Solving table (pencil-mark grid) ---------- */
.solgrid {
  display: grid; grid-template-columns: repeat(9, 1fr);
  width: min(560px, 100%); aspect-ratio: 1;
  background: var(--surface); border: 2px solid var(--line-strong);
  border-radius: var(--radius); overflow: hidden;
}
.solgrid-cell {
  display: grid; grid-template-columns: repeat(3, 1fr); padding: 3px;
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.solgrid-cell i {
  font-style: normal; display: grid; place-items: center;
  font-size: clamp(6px, 1.1vw, 11px); color: var(--ink-2);
}
.solgrid-cell.tbr { border-right: 2px solid var(--line-strong); }
.solgrid-cell.tbb { border-bottom: 2px solid var(--line-strong); }

/* ---------- Statistics tables ---------- */
.stat-table { border-collapse: collapse; margin: 8px 0 24px; }
.stat-table th, .stat-table td {
  text-align: left; padding: 6px 28px 6px 0;
  border-bottom: 1px solid var(--line);
}
.stat-table th { color: var(--ink-2); font-weight: 500; }
.stat-table tr.stat-sum td { font-weight: 600; border-top: 2px solid var(--line-strong); }

/* Mobile: scroll wide tables instead of overflowing, and roomier footer taps. */
@media (max-width: 600px) {
  .stat-table, .essay-table { display: block; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .stat-table th, .stat-table td { padding-right: 18px; }
  /* Roomier tap targets on touch (#281): footer nav, auth links and breadcrumbs
     all sat well under 44px. Padding + inline-block gives a real tap box. */
  .footer-links { gap: 4px 18px; }
  .footer-links a { display: inline-block; padding: 12px 0; }
  .auth-alt { line-height: 1.4; }
  .auth-alt a { display: inline-block; padding: 11px 2px; }
  .breadcrumb a { display: inline-block; padding: 5px 0; }
  /* Let the meta line wrap instead of overflowing a narrow viewport. */
  .board-meta span { white-space: normal; }
  /* Pair each fractal control label with its select instead of wrapping apart. */
  .fractal-controls { display: grid; grid-template-columns: auto 1fr; gap: 8px 10px; align-items: center; }
  .fractal-controls .input { width: 100%; }
}
/* Keep the wordsearch clue list compact and scrollable so it doesn't push the
   board far below the fold on small screens. */
@media (max-width: 820px) {
  .ws-words { max-height: 108px; overflow: auto; -webkit-overflow-scrolling: touch; }
}

/* ---------- Lærer / stasjoner ---------- */
.st-rounds { display: flex; flex-direction: column; gap: 12px; }
.st-round h3 { margin: 0 0 6px; }
.st-round p { margin: 2px 0; }

/* ---------- Sudokuto game ---------- */
.sto-bar { display: flex; gap: 12px; align-items: center; margin-top: 12px; flex-wrap: wrap; }
.sto-msg { min-height: 1.4em; }
.sto-msg.bad { color: var(--err, #c0392b); }
.sto-msg.ok { color: var(--ok, #1e8e3e); }

@media print {
  .solgrid { width: 16cm; background: #fff; border-color: #000; border-radius: 0; }
  .solgrid-cell { border-color: #ccc; }
  .solgrid-cell.tbr { border-right-color: #000; }
  .solgrid-cell.tbb { border-bottom-color: #000; }
  .solgrid-cell i { color: #333; }
  .mgrid-samurai { background: transparent; }
  .mgrid-samurai .mg-cell { background: #fff; }
  .mgrid-samurai .mg-cell.void { background: transparent; border: none; }
  .mg-cell.tbl { border-left-color: #000; }
  .mg-cell.tbt { border-top-color: #000; }
}
#sto-board .mg-cell { padding: 0; }
.sto-in {
  width: 100%; height: 100%; border: none; background: transparent;
  /* Keep at least 16px so iOS Safari doesn't zoom in on focus. */
  text-align: center; font-size: max(16px, 1em); font-weight: 500; color: var(--ink);
  font-family: inherit; outline: none;
}
.sto-in:focus { background: color-mix(in oklab, var(--accent) 12%, transparent); }
.sto-in.sto-cpu { color: var(--ink-2); }
.sto-in.sto-new { background: color-mix(in oklab, var(--accent) 18%, transparent); }
/* Sudokuto level picker: the native 13px radio is a poor touch target (#281).
   Hide it and make the whole chip the tap area with a clear selected state. The
   input stays in the DOM (focusable, still read/checked by sudokuto.ts). */
.sto-bar label.chip { display: inline-flex; align-items: center; cursor: pointer; min-height: 42px; }
.sto-bar label.chip input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.sto-bar label.chip:has(input:checked) { background: var(--accent); border-color: var(--accent); color: #fff; }
.sto-bar label.chip:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============ Photo / Foto section (#167) ============ */
.photo-page { width: 100%; }
.photo-page .section-head { max-width: var(--maxw); margin: 0 auto 28px; padding: 0 24px; }
/* Foto nav item — camera glyph in the accent colour. */
.nav-foto { display: inline-flex; align-items: center; gap: 6px; }
.nav-foto svg { color: var(--accent); opacity: 0.85; }

/* Calm hero — mirrors the home hero (text left, framed feature right, on paper). */
.photo-hero-calm {
  max-width: var(--maxw); margin: 0 auto;
  padding: 56px 24px 8px;
  display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 52px; align-items: center;
}
.photo-kicker-light {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent-text);
}
.phc-text h1 {
  margin: 18px 0 0; max-width: 15ch;
  font-size: clamp(40px, 5.5vw, 74px); font-weight: 700;
  line-height: 1.02; letter-spacing: -0.035em;
}
.phc-text p {
  margin: 18px 0 0; max-width: 46ch;
  font-size: 17px; line-height: 1.6; color: var(--ink-2); text-wrap: pretty;
}
.photo-hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
.btn-ghost-ink {
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  border-radius: var(--radius); padding: 14px 26px; font-size: 16px; font-weight: 600;
  text-decoration: none; cursor: pointer; transition: border-color 0.15s ease;
  display: inline-flex; align-items: center;
}
.btn-ghost-ink:hover { border-color: var(--ink-2); }
/* Feature image keeps its own aspect ratio — no cropping. */
.phc-feature {
  margin: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  box-shadow: 0 24px 60px -28px color-mix(in oklab, var(--ink) 40%, transparent);
}
.phc-feature-slot { display: block; width: 100%; height: auto; }
@media (max-width: 820px) {
  .photo-hero-calm { grid-template-columns: 1fr; gap: 30px; padding-top: 40px; }
}

/* Selected works — masonry columns so every photo keeps its native proportions
   (landscape, portrait, panorama) instead of being squeezed into a fixed box. */
.photo-works { max-width: var(--maxw); margin: 0 auto; padding: 64px 24px 0; }
.photo-grid { columns: 3; column-gap: 14px; }
@media (max-width: 820px) { .photo-grid { columns: 2; } }
@media (max-width: 520px) { .photo-grid { columns: 1; } }
.photo-tile {
  position: relative; margin: 0 0 14px; break-inside: avoid;
  overflow: hidden; border-radius: var(--radius); border: 1px solid var(--line);
}
.photo-tile-link { display: block; }
.photo-tile-slot { display: block; width: 100%; height: auto; transition: transform 0.45s ease; }
.photo-tile-link:hover .photo-tile-slot { transform: scale(1.045); }
.photo-tile-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.photo-tile figcaption {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: flex; flex-direction: column; gap: 2px;
  padding: 30px 16px 14px;
  background: linear-gradient(to top, rgba(12,12,11,0.8), transparent);
  color: #f5f4ef; pointer-events: none;
}
.photo-tile .pt-title { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.photo-tile .pt-meta { font-size: 11.5px; color: rgba(245,244,239,0.82); letter-spacing: 0.02em; }

/* Three series. */
.photo-series { max-width: var(--maxw); margin: 0 auto; padding: 72px 24px 0; }
.series-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; align-items: start; }
@media (max-width: 820px) { .series-grid { grid-template-columns: 1fr; } }
.series-card {
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  overflow: hidden; background: var(--surface);
  display: flex; flex-direction: column;
  text-decoration: none; color: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.series-card:hover { border-color: var(--ink-2); box-shadow: 0 12px 30px -22px color-mix(in oklab, var(--ink) 50%, transparent); }
.series-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.series-slot { display: block; width: 100%; height: auto; transition: transform 0.45s ease; }
.series-card:hover .series-slot { transform: scale(1.04); }
.series-body { padding: 20px 22px 24px; display: flex; flex-direction: column; gap: 8px; }
.series-body h3 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -0.01em; }
.series-body p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-2); text-wrap: pretty; }
.series-note {
  margin-top: 4px; align-self: flex-start;
  font-size: 11.5px; color: var(--accent-text);
  border: 1px solid color-mix(in oklab, var(--accent) 38%, transparent);
  border-radius: var(--radius-pill); padding: 4px 12px;
}

/* CTA band out to flogvit.no. */
.photo-cta { max-width: var(--maxw); margin: 0 auto; padding: 72px 24px 88px; }
.photo-cta-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 28px; flex-wrap: wrap;
  background: var(--ink); color: var(--bg);
  border-radius: calc(var(--radius) + 8px); padding: 40px 44px;
}
.photo-cta-inner h2 { margin: 0; font-size: clamp(24px, 3vw, 34px); font-weight: 700; letter-spacing: -0.02em; }
.photo-cta-inner p { margin: 10px 0 0; font-size: 15px; color: color-mix(in oklab, var(--bg) 78%, var(--ink)); max-width: 48ch; }
.photo-cta-inner .btn-primary { flex: none; }

@media print { .photo-page { display: none !important; } }

/* Sudokuto renders the .mgrid board (#sto-board), not .board, so it missed the
   larger mobile sudoku digits. Match them on phones (#252) — the .sto-in inputs
   size off the cell's 1em, so bumping the cell flows through to the inputs. */
@media (max-width: 899px) {
  #sto-board .mg-cell { font-size: clamp(22px, 6.5vw, 30px); }
}

/* Generic boards whose readable symbols should match the sudoku size on phones
   (.mgrid-entry, set by puzzle.ts): keypad-entry variants had ~12px symbols vs
   ~26px keypad glyphs (#257), and Hitori's numbers were small too (#259). Bump
   the symbol font to sudoku scale. Values (and cage-v, which inherits) grow;
   pencil marks (.pm) and kakuro clue numbers keep their own smaller fonts. */
@media (max-width: 899px) {
  .puzzle-mock-board .mgrid-entry .mg-cell { font-size: clamp(22px, 6.5vw, 30px); }
}

/* Wordsearch's 15×15 grid had ~10px letters on phones (#268) — too small to
   read. Bump the mobile font; the denser grid keeps a smaller scale than the
   number boards above. */
@media (max-width: 899px) {
  .mgrid-ws .mg-cell { font-size: clamp(14px, 4vw, 18px); }
}

/* ---------- Gamification: statistics / profile (#249) ---------- */
.stats-page { max-width: 760px; margin: 0 auto; }
.stats-h1 { font-size: clamp(26px, 4vw, 38px); font-weight: 800; letter-spacing: -0.02em; margin: 0 0 6px; }
.stats-intro { margin: 0 0 22px; color: var(--ink-2); }
.stats-root { display: flex; flex-direction: column; gap: 22px; }

/* Rank hero */
.rank-hero { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.rank-title { font-size: clamp(24px, 4vw, 34px); font-weight: 800; letter-spacing: -0.02em; color: var(--accent-text, var(--accent)); }
.rank-sub { color: var(--ink-2); font-size: 14px; }
.rank-bar { width: 100%; height: 8px; border-radius: 999px; background: color-mix(in oklab, var(--ink) 10%, transparent); overflow: hidden; margin-top: 4px; }
.rank-bar-fill { height: 100%; border-radius: 999px; background: var(--accent); transition: width 0.4s ease; }
.rank-next { font-size: 12px; color: var(--ink-2); }

/* Summary cards */
.stat-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
/* Pluss teaser on the free stats page (#305). */
.stats-teaser {
  display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
  border: 1px dashed color-mix(in oklab, var(--accent) 40%, var(--line));
  margin-top: 4px;
}
/* min-width:0 lets a card shrink below its content so the value/label wrap
   inside the card instead of overflowing the narrow column (#274). */
.stat-card { min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; text-align: center; }
.stat-value { font-size: clamp(18px, 4vw, 30px); font-weight: 700; color: var(--ink); line-height: 1.1; overflow-wrap: anywhere; }
.stat-label { font-size: 12px; color: var(--ink-2); letter-spacing: 0.06em; text-transform: uppercase; margin-top: 4px; overflow-wrap: anywhere; }

/* Sections with bar rows */
.stats-section { display: flex; flex-direction: column; gap: 10px; }
.stats-h2 { font-size: 14px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-2); margin: 6px 0 2px; }
.bar-row { display: grid; grid-template-columns: minmax(6em, 9em) 1fr auto; align-items: center; gap: 10px; }
/* Let long type names wrap to a second line rather than ellipsis-cut (#274). */
.bar-label { min-width: 0; font-size: 14px; line-height: 1.2; color: var(--ink); overflow-wrap: anywhere; }
.bar-track { height: 14px; border-radius: 999px; background: color-mix(in oklab, var(--ink) 8%, transparent); overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 999px; background: var(--accent); min-width: 2px; transition: width 0.4s ease; }
.bar-easy { background: var(--good, #2a9d8f); }
.bar-medium { background: var(--accent); }
.bar-hard { background: var(--bad, #c0392b); }
.bar-type { background: color-mix(in oklab, var(--accent) 75%, var(--ink)); }
.bar-count { font-size: 13px; color: var(--ink-2); min-width: 3.5em; text-align: right; }

.stats-empty { text-align: center; padding: 32px; }

@media (max-width: 560px) {
  .bar-row { grid-template-columns: 6em 1fr auto; }
}

/* ---------- Gamification: badges + unlock toast (#247) ---------- */
.badges-section { gap: 6px; }
.badge-cat { font-size: 13px; font-weight: 600; color: var(--ink); margin: 14px 0 4px; }
.badge-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.badge-tile {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 12px;
}
.badge-tile.locked { opacity: 0.55; }
.badge-tile.locked .badge-icon { filter: grayscale(1); opacity: 0.7; }
.badge-tile.earned { border-color: color-mix(in oklab, var(--accent) 45%, var(--line)); background: color-mix(in oklab, var(--accent) 7%, var(--surface)); }
.badge-icon { font-size: 22px; line-height: 1; flex: none; }
.badge-name { min-width: 0; font-size: 13px; font-weight: 500; color: var(--ink); flex: 1; line-height: 1.2; overflow-wrap: anywhere; }
.badge-pts { font-size: 11px; color: var(--accent-text, var(--accent)); font-weight: 600; flex: none; }
.badge-prog { font-size: 11px; color: var(--ink-2); flex: none; }

.badge-toasts {
  position: fixed; right: 16px; bottom: 16px; z-index: 60;
  display: flex; flex-direction: column; gap: 10px; pointer-events: none;
}
.badge-toast {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid color-mix(in oklab, var(--accent) 45%, var(--line));
  border-radius: var(--radius); padding: 12px 16px;
  box-shadow: 0 8px 24px color-mix(in oklab, var(--ink) 18%, transparent);
  transform: translateX(120%); opacity: 0; transition: transform 0.35s ease, opacity 0.35s ease;
  max-width: 280px;
}
.badge-toast.in { transform: translateX(0); opacity: 1; }
.badge-toast-icon { font-size: 28px; line-height: 1; }
.badge-toast-lead { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-text, var(--accent)); font-weight: 600; }
.badge-toast-name { font-size: 15px; font-weight: 600; color: var(--ink); }
@media (max-width: 560px) {
  .badge-toasts { left: 12px; right: 12px; bottom: 12px; }
  .badge-toast { max-width: none; }
}
@media (prefers-reduced-motion: reduce) {
  .badge-toast { transition: opacity 0.2s ease; transform: none; }
}

/* ---------- Guided "how to play" intro modal (#291) ---------- */
/* Auto-opens on first unsolved visit; also reachable from the board's help
   button (.intro-help, added next to .board-meta by src/client/intro.ts). */
.intro-help { width: 26px; height: 26px; flex: none; color: var(--ink-2); }
.intro-help:hover { color: var(--accent); border-color: var(--accent); }

.intro-modal {
  position: fixed; inset: 0; z-index: 1100;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: color-mix(in oklab, var(--ink) 42%, transparent);
  opacity: 0; visibility: hidden; transition: opacity 0.2s ease, visibility 0.2s ease;
}
.intro-modal.open { opacity: 1; visibility: visible; }
.intro-card {
  position: relative;
  width: min(440px, 100%); max-height: calc(100vh - 40px); overflow-y: auto;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: 18px;
  padding: 26px 24px 22px;
  box-shadow: 0 24px 70px -20px color-mix(in oklab, var(--ink) 50%, transparent);
  transform: translateY(12px) scale(0.98); transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.intro-modal.open .intro-card { transform: translateY(0) scale(1); }
.intro-close { position: absolute; top: 12px; right: 12px; width: 34px; height: 34px; color: var(--ink-2); }
.intro-title { font-size: 20px; font-weight: 700; margin: 0 28px 16px 0; line-height: 1.25; }
.intro-steps { margin: 0 0 22px; padding: 0; list-style: none; counter-reset: intro; display: flex; flex-direction: column; gap: 12px; }
.intro-steps li {
  position: relative; counter-increment: intro;
  padding-left: 40px; font-size: 15px; line-height: 1.45; color: var(--ink);
}
.intro-steps li::before {
  content: counter(intro);
  position: absolute; left: 0; top: -1px;
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in oklab, var(--accent) 12%, transparent);
  color: var(--accent-text, var(--accent)); border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--line));
  font-family: 'IBM Plex Mono', monospace; font-size: 13px; font-weight: 600;
}
.intro-start { width: 100%; font-size: 16px; padding: 13px 0; text-align: center; }
/* Link from the quick intro to the full /{type}/hjelp walkthrough (#262). */
.intro-more { display: block; margin-top: 12px; text-align: center; font-size: 14px; color: var(--accent); text-decoration: none; }
.intro-more:hover { text-decoration: underline; }
/* Solving-technique nudge, set apart from the numbered rules (#295): a soft
   accent card with a left rule, so it reads as "here's how to get started"
   rather than another rule. */
.intro-tip {
  margin: 0 0 22px; padding: 12px 14px;
  background: color-mix(in oklab, var(--accent) 8%, transparent);
  border-left: 3px solid var(--accent); border-radius: 8px;
  font-size: 14px; line-height: 1.5; color: var(--ink); text-wrap: pretty;
}
.intro-tip strong { color: var(--accent); font-weight: 700; }
/* Example board illustration on the per-type help page (#262). Capped and
   centred so it reads as an inline illustration, not a full playable board. */
.help-example { align-self: center; width: 100%; max-width: 320px; margin: 2px 0 4px; }
.help-example figcaption { margin-top: 8px; text-align: center; font-size: 12px; color: var(--ink-2); }
/* Step-by-step walkthrough (#262): a sequence of real board snapshots, each
   paired with a caption explaining the move. */
.wt { display: flex; flex-direction: column; gap: 12px; }
.wt-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.wt-step { display: grid; grid-template-columns: minmax(0, 220px) 1fr; gap: 16px; align-items: center; }
.wt-step-board .wt-grid { width: 100%; max-width: 220px; }
.wt-step-text { margin: 0; font-size: 15px; line-height: 1.5; color: var(--ink); display: flex; gap: 10px; align-items: baseline; }
.wt-num {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: inline-grid; place-items: center; font-size: 12px; font-weight: 600;
  background: color-mix(in oklab, var(--accent) 12%, transparent);
  color: var(--accent-text, var(--accent)); border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--line));
}
.wt-grid .mg-cell { color: var(--ink); }
.wt-grid .mg-cell.wt-given { font-weight: 700; }
.wt-grid .mg-cell.wt-hi { background: color-mix(in oklab, var(--accent) 24%, transparent); font-weight: 600; }
.wt-grid .mg-cell.wt-found { background: color-mix(in oklab, var(--accent) 18%, transparent); }
.wt-grid .mg-cell.wt-path { color: var(--accent); font-weight: 600; }
.wt-grid .mg-cell.wt-sum { color: var(--ink-2); font-size: 0.8em; }
.wt-grid .mg-cell.kclue-wt { font-size: clamp(6px, 1.7cqi, 10px); line-height: 1.05; color: var(--bg); font-weight: 600; }
.wt-src { font-size: 12px; color: var(--ink-2); }
/* Locked tail of a walkthrough for non-Pluss visitors (#303). */
.wt-locked {
  font-size: 13.5px; line-height: 1.55; color: var(--ink-2);
  border: 1px dashed color-mix(in oklab, var(--accent) 40%, var(--line));
  border-radius: var(--radius); padding: 12px 16px; margin: 4px 0 12px;
}
.wt-locked a { color: var(--accent-text); font-weight: 500; }
@media (max-width: 560px) {
  .wt-step { grid-template-columns: 1fr; justify-items: center; text-align: left; }
  .wt-step-board .wt-grid { max-width: 260px; }
}
@media (prefers-reduced-motion: reduce) {
  .intro-modal, .intro-card { transition: opacity 0.2s ease; transform: none; }
}

/* ---------- Gamification: daily challenge + streak (#248) ---------- */
.stat-cards-4 { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }

.daily-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  background: linear-gradient(135deg, color-mix(in oklab, var(--accent) 14%, var(--surface)), var(--surface));
  border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--line));
}
.daily-head { display: flex; align-items: center; gap: 8px; width: 100%; }
.daily-kicker { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-text, var(--accent)); font-weight: 600; }
.daily-check { margin-left: auto; color: #2a9d8f; font-weight: 800; font-size: 18px; }
.daily-type { font-size: clamp(20px, 3vw, 26px); font-weight: 800; letter-spacing: -0.01em; color: var(--ink); }
.daily-cta { flex: none; }
.daily-note { font-size: 12px; color: var(--ink-2); }
.daily-card.done { background: linear-gradient(135deg, color-mix(in oklab, #2a9d8f 12%, var(--surface)), var(--surface)); border-color: color-mix(in oklab, #2a9d8f 30%, var(--line)); }

.week-strip { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.week-day { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 0; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--line); }
.week-day.on { background: color-mix(in oklab, var(--accent) 10%, var(--surface)); border-color: color-mix(in oklab, var(--accent) 30%, var(--line)); }
.week-day.today { box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--accent) 45%, transparent); }
.week-dot { font-size: 16px; line-height: 1; min-height: 16px; }
.week-day:not(.on) .week-dot::before { content: '·'; color: var(--ink-2); opacity: 0.5; }
.week-name { font-size: 11px; color: var(--ink-2); text-transform: uppercase; }

@media (max-width: 560px) {
  .stat-cards-4 { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Gamification: favourites (#250) ---------- */
.fav-star {
  background: none; border: none; cursor: pointer; padding: 0 2px;
  font-size: 18px; line-height: 1; color: var(--ink-2);
  transition: color 0.15s ease, transform 0.1s ease;
}
.fav-star:hover { color: var(--accent); transform: scale(1.12); }
.fav-star.on { color: #e8b500; }
.board-meta .fav-star { margin-left: auto; }

.fav-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.fav-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: 14px; color: var(--ink); text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.fav-chip:hover { border-color: color-mix(in oklab, var(--accent) 45%, var(--line)); background: color-mix(in oklab, var(--accent) 7%, var(--surface)); }
.fav-chip-star { color: #e8b500; font-size: 13px; }

/* ---------- Gamification: seasonal events (#251) ---------- */
.season-card {
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  border: 1px solid color-mix(in oklab, var(--season, var(--accent)) 45%, var(--line));
  background: linear-gradient(135deg, color-mix(in oklab, var(--season, var(--accent)) 14%, var(--surface)), var(--surface));
}
.season-head { display: flex; align-items: center; gap: 8px; width: 100%; }
.season-kicker { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: color-mix(in oklab, var(--season, var(--accent)) 75%, var(--ink)); font-weight: 700; }
.season-days { margin-left: auto; font-size: 12px; color: var(--ink-2); }
.season-title { display: flex; align-items: center; gap: 10px; font-size: clamp(20px, 3vw, 26px); font-weight: 800; letter-spacing: -0.01em; color: var(--ink); }
.season-emoji { font-size: 1.3em; line-height: 1; }
.season-count { font-size: 13px; color: var(--ink-2); }
.season-tiers { display: flex; gap: 8px; margin-top: 2px; }
.season-tier {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line);
  opacity: 0.5; filter: grayscale(0.7);
}
.season-tier.reached { opacity: 1; filter: none; border-color: color-mix(in oklab, var(--season, var(--accent)) 40%, var(--line)); font-weight: 600; }
.season-medal { font-size: 18px; line-height: 1; }
.season-target { font-size: 12px; color: var(--ink-2); }
.season-tier.reached .season-target { color: var(--ink); }
.season-next { font-size: 12px; color: var(--ink-2); }

/* ---------- Account management (#272) ---------- */
.account-page { max-width: 560px; margin: 0 auto; }
.account-section { margin-bottom: 16px; }
.account-h2 { font-size: 15px; font-weight: 600; margin: 0 0 12px; color: var(--ink); }
.account-notice { color: var(--good, #2a9d8f); background: color-mix(in oklab, #2a9d8f 10%, transparent); border-radius: var(--radius); padding: 10px 14px; margin: 0 0 14px; }

.account-id { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.account-id-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.account-id-main strong { font-size: 16px; }
.status-badge { flex: none; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px; white-space: nowrap; }
.status-badge.ok { color: var(--good, #2a9d8f); background: color-mix(in oklab, #2a9d8f 14%, transparent); }
.status-badge.warn { color: var(--accent-text, var(--accent)); background: color-mix(in oklab, var(--accent) 14%, transparent); }

.account-form { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
.account-form .field { width: 100%; }
.inline-form { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; margin-top: 12px; }
.account-links { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.logout-form { margin-top: 4px; }

.danger-zone { border-color: color-mix(in oklab, var(--bad, #c0392b) 35%, var(--line)); }
.danger-zone .account-h2 { color: var(--bad, #c0392b); }
.btn-danger {
  appearance: none; cursor: pointer; font: inherit; font-weight: 600;
  padding: 10px 18px; border-radius: var(--radius);
  color: #fff; background: var(--bad, #c0392b); border: 1px solid transparent;
}
.btn-danger:hover { background: color-mix(in oklab, var(--bad, #c0392b) 88%, #000); }

/* ============================================================
   Home v2 — breadth-first workshop layout. The front page leads
   with identity (puzzles · numbers · words · photo) rather than
   sudoku alone; sudoku stays the flagship in the live section.
   ============================================================ */

.hero-kicker {
  display: inline-block; font-size: 12px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--ink-2); margin-bottom: 18px;
}
.hero-v2 { align-items: center; }
.hero-v2 h1 { font-size: clamp(40px, 5.6vw, 78px); }
.hero-v2 .hero-cta { display: flex; flex-wrap: wrap; gap: 12px; }

/* Glyph mosaic — echoes the 3×3 dot logo */
.hero-mosaic {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
  width: min(380px, 86vw); margin: 0 auto; aspect-ratio: 1;
}
.mosaic-cell {
  display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  text-decoration: none;
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.mosaic-cell .glyph { width: 40%; height: 40%; }
.mosaic-cell:hover { transform: translateY(-3px); border-color: var(--accent-text); color: var(--accent-text); }
.mosaic-cell.center { background: var(--accent); color: #fff; border-color: var(--accent); }
.mosaic-cell.center:hover { transform: translateY(-3px) scale(1.02); color: #fff; }
@media (prefers-reduced-motion: no-preference) {
  .hero-mosaic .mosaic-cell { animation: mzoom 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
  .mosaic-cell:nth-child(1) { animation-delay: .04s } .mosaic-cell:nth-child(2) { animation-delay: .08s }
  .mosaic-cell:nth-child(3) { animation-delay: .12s } .mosaic-cell:nth-child(4) { animation-delay: .10s }
  .mosaic-cell:nth-child(5) { animation-delay: .16s } .mosaic-cell:nth-child(6) { animation-delay: .14s }
  .mosaic-cell:nth-child(7) { animation-delay: .20s } .mosaic-cell:nth-child(8) { animation-delay: .18s }
  .mosaic-cell:nth-child(9) { animation-delay: .22s }
  @keyframes mzoom { from { opacity: 0; transform: scale(0.86); } }
}

/* Browse lanes — one card per visible category */
.browse { padding: 76px 0 0; }
.lane-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; }
.lane-card {
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  background: var(--surface); padding: 24px 24px 22px;
  display: flex; flex-direction: column; gap: 12px;
}
.lane-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.lane-title {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 22px; font-weight: 700; letter-spacing: -0.02em; color: var(--ink);
  text-decoration: none;
}
.lane-title svg { color: var(--ink-2); transition: transform 0.15s ease, color 0.15s ease; }
.lane-title:hover { color: var(--accent-text); }
.lane-title:hover svg { color: var(--accent-text); transform: translateX(3px); }
.lane-count {
  font-size: 12px; color: var(--ink-2);
  border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 2px 10px;
}
.lane-lede { margin: 0; font-size: 14px; color: var(--ink-2); text-wrap: pretty; }
.lane-tools { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.tool-chip {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--bg); color: var(--ink);
  padding: 7px 13px 7px 10px; font-size: 13px; font-weight: 500;
  text-decoration: none;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.tool-chip .glyph { width: 16px; height: 16px; color: var(--ink-2); }
.tool-chip:hover { border-color: var(--accent-text); color: var(--accent-text); }
.tool-chip:hover .glyph { color: var(--accent-text); }
.tool-chip .live-dot { width: 6px; height: 6px; margin-left: 1px; }
.more-chip { color: var(--ink-2); font-family: 'IBM Plex Mono', monospace; font-size: 12px; }

/* Foto band — distinct because the photography is */
.foto-band { padding: 64px 0 0; }
.foto-band-inner {
  display: grid; grid-template-columns: 1fr 1fr; gap: 32px; align-items: center;
  background: color-mix(in oklab, var(--accent) 6%, var(--surface));
  border: 1px solid color-mix(in oklab, var(--accent-text) 30%, var(--line));
  border-radius: calc(var(--radius) + 8px); padding: 34px 36px;
}
@media (max-width: 760px) { .foto-band-inner { grid-template-columns: 1fr; gap: 24px; } }
.foto-band-kicker {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-text);
}
.foto-band-text h2 { margin: 12px 0 0; font-size: clamp(24px, 3vw, 34px); font-weight: 700; letter-spacing: -0.02em; }
.foto-band-text p { margin: 8px 0 18px; font-size: 15px; color: var(--ink-2); max-width: 40ch; text-wrap: pretty; }
.foto-band-thumbs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.fb-thumb { width: 100%; aspect-ratio: 3 / 4; border-radius: var(--radius); overflow: hidden; display: block; object-fit: cover; }

/* Live tools — sudoku flagship + browser-playable tools */
.live { padding: 72px 0 0; }
.live-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 16px; align-items: stretch; }
@media (max-width: 820px) { .live-grid { grid-template-columns: 1fr; } }
.live-card {
  display: flex; align-items: center; gap: 18px; text-align: left;
  border: 1px solid var(--line); border-radius: calc(var(--radius) + 4px);
  background: var(--surface); padding: 20px; text-decoration: none; color: var(--ink);
  transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.live-card:hover { transform: translateY(-3px); border-color: var(--accent-text); box-shadow: 0 14px 34px -20px color-mix(in oklab, var(--ink) 40%, transparent); }
.live-board {
  display: grid; grid-template-columns: repeat(9, 1fr);
  width: 104px; height: 104px; flex: none;
  border: 1.5px solid var(--line-strong); border-radius: 6px; overflow: hidden;
  font-family: 'IBM Plex Mono', monospace;
}
.lb-cell { display: grid; place-items: center; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); font-size: 9px; color: var(--ink); }
.lb-cell:nth-child(9n) { border-right: none; } .lb-cell:nth-child(n+73) { border-bottom: none; }
.lb-cell.bdr { border-right: 1.5px solid var(--line-strong); } .lb-cell.bdb { border-bottom: 1.5px solid var(--line-strong); }
.live-glyph { width: 56px; height: 56px; flex: none; display: grid; place-items: center; color: var(--ink); }
.live-glyph .glyph { width: 100%; height: 100%; }
.live-card-text h3 { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; display: flex; align-items: center; gap: 8px; }
.live-card-text p { margin: 5px 0 0; font-size: 13.5px; color: var(--ink-2); text-wrap: pretty; }
.live-card-text .live-dot { width: 7px; height: 7px; }

/* Account value band (home, logged-out only) */
.acct-cta { padding: 64px 0 0; }
.acct-cta-inner {
  display: grid; grid-template-columns: 1fr auto; gap: 28px; align-items: center;
  background: color-mix(in oklab, var(--accent) 6%, var(--surface));
  border: 1px solid color-mix(in oklab, var(--accent-text) 28%, var(--line));
  border-radius: calc(var(--radius) + 8px); padding: 30px 34px;
}
@media (max-width: 760px) { .acct-cta-inner { grid-template-columns: 1fr; gap: 20px; } }
.acct-cta-text h2 { margin: 0; font-size: clamp(22px, 2.6vw, 30px); font-weight: 700; letter-spacing: -0.02em; }
.acct-cta-lead { margin: 8px 0 0; font-size: 14.5px; line-height: 1.5; color: var(--ink-2); max-width: 54ch; text-wrap: pretty; }
.acct-cta-list { margin: 12px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.acct-cta-list li { font-size: 13.5px; line-height: 1.45; color: var(--ink-2); text-wrap: pretty; }
.acct-cta-actions { display: flex; flex-direction: column; gap: 10px; }
.acct-cta-actions .btn-primary, .acct-cta-actions .btn-ghost-ink { text-align: center; white-space: nowrap; }
@media (max-width: 760px) { .acct-cta-actions { flex-direction: row; } }

/* Tighten the home page on phones (#278): the section rhythm is generous on
   desktop but wastes vertical space on small screens, pushing the actual
   content (browse lanes, live tools) far below the fold. Trim the gaps, calm
   the hero spacing and shrink the decorative mosaic so the page reads compact
   and the user reaches real content sooner. */
@media (max-width: 720px) {
  .hero-v2 .hero-kicker { margin-bottom: 12px; }
  .hero-v2 .hero-sub { margin-top: 14px; font-size: 16px; }
  .hero-v2 .hero-cta { margin-top: 22px; }
  /* Smaller mosaic — it's decorative (the same tools live in the lanes below),
     so it shouldn't own a full screen-width square above the fold. */
  .hero-mosaic { width: min(260px, 64vw); gap: 9px; }
  .browse { padding: 40px 0 0; }
  .lane-grid { gap: 14px; }
  .lane-card { padding: 20px 18px 18px; gap: 10px; }
  .foto-band { padding: 44px 0 0; }
  .live { padding: 44px 0 0; }
  .acct-cta { padding: 44px 0 0; }
  .about { padding: 48px 0 56px; }
}
