/* Katachi-Go game — retro-futuristic synthwave theme (neon sunset, grid horizon) */
:root {
  --navy: #1c0a30;      /* deep purple panel bg */
  --offwhite: #150821;  /* page background base (body uses a gradient over this) */
  --white: #f5edff;     /* light lavender-white text */
  --blue: #38d0ff;      /* neon cyan-blue */
  --orange: #ff9d4d;    /* neon orange (also the ball "gold rim" color in render.js) */
  --yellow: #ffd76a;    /* sun-top gold */
  --mauve: #ff4fa0;     /* neon pink */
  --board: #170821;     /* board frame background */
  --dimple: #2a1240;    /* empty-cell accent */
  --font-script: 'Orbitron', sans-serif;
  --font-body: 'Raleway', sans-serif;
  --font-logo: 'Yesteryear', cursive; /* "Hoffmakers" wordmark only, matches the main site's script font */
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-body);
  background: var(--offwhite) radial-gradient(ellipse at 50% 15%, #4a1a66 0%, #23103a 45%, #150821 80%);
  color: var(--white);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ── Nav (matches site, dark synthwave variant) ── */
nav {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.9rem 2rem; background: rgba(21,8,33,0.9); backdrop-filter: blur(6px);
  box-shadow: 0 2px 16px rgba(255,79,160,0.15), 0 1px 0 rgba(255,255,255,0.06);
  position: sticky; top: 0; z-index: 50;
}
.nav-logo { text-decoration: none; }
.nav-logo-main { font-family: var(--font-logo); font-size: 1.7rem; font-weight: 400; color: var(--white); letter-spacing: 0.03em; }
.nav-links { display: flex; gap: 1.4rem; list-style: none; align-items: center; }
.nav-links a { color: var(--white); text-decoration: none; font-weight: 700; font-size: 0.85rem; letter-spacing: 0.06em; text-transform: uppercase; }
.nav-links a:hover { color: var(--blue); }
.nav-link-btn {
  background: none; border: none; color: var(--white); font-family: var(--font-body);
  font-weight: 700; font-size: 0.85rem; letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer;
}
.nav-link-btn:hover { color: var(--blue); }
.nav-right { display: flex; align-items: center; gap: 1.4rem; }
.nav-cta {
  background: linear-gradient(90deg, var(--orange), var(--mauve)); color: var(--white); border: none; border-radius: 99px;
  padding: 0.55rem 1.2rem; font-family: var(--font-body); font-weight: 800; font-size: 0.8rem;
  letter-spacing: 0.05em; text-transform: uppercase; cursor: pointer; text-decoration: none;
  display: inline-block; transition: transform 0.15s, box-shadow 0.15s;
}
.nav-cta:hover { box-shadow: 0 0 18px rgba(255,79,160,0.55); transform: translateY(-1px); }
.nav-search { position: relative; }
.nav-search input[type="search"] { width: 130px; padding: 0.45rem 0.9rem; border: 1px solid rgba(255,255,255,0.15); border-radius: 20px; font-family: var(--font-body); font-size: 0.8rem; background: rgba(255,255,255,0.06); color: var(--white); transition: width 0.2s ease, background 0.2s ease; }
.nav-search input[type="search"]::placeholder { color: rgba(245,237,255,0.45); }
.nav-search input[type="search"]:focus { outline: none; width: 190px; background: rgba(255,255,255,0.1); border-color: var(--blue); }
.nav-search-results { position: absolute; top: calc(100% + 0.6rem); right: 0; width: 280px; background: var(--navy); border: 1px solid rgba(255,255,255,0.1); border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); max-height: 20rem; overflow-y: auto; display: none; z-index: 300; }
.nav-search-results.open { display: block; }
.nsr-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.8rem; text-decoration: none; color: inherit; border-bottom: 1px solid rgba(255,255,255,0.08); }
.nsr-item:last-child { border-bottom: none; }
.nsr-item:hover { background: rgba(255,255,255,0.06); }
.nsr-item img { width: 36px; height: 36px; border-radius: 6px; object-fit: cover; flex-shrink: 0; }
.nsr-item-name { font-size: 0.85rem; font-weight: 700; color: var(--white); }
.nsr-empty { padding: 0.8rem; font-size: 0.82rem; color: #9d8cc2; text-align: center; }
.nav-account-badge {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: rgba(56,208,255,0.12); color: var(--blue); border: 2px solid rgba(56,208,255,0.35); border-radius: 99px;
  padding: 0.4rem 0.85rem; font-family: var(--font-body); font-weight: 700; font-size: 0.75rem;
  letter-spacing: 0.03em; cursor: pointer;
}
.nav-account-badge::before { content: ''; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--blue); }
.nav-account-badge:hover { border-color: var(--blue); }
.nav-account-badge[hidden] { display: none; }
.hamburger { display: none; background: none; border: none; cursor: pointer; padding: 0.4rem; flex-direction: column; gap: 5px; }
.hamburger span { display: block; width: 22px; height: 2px; background: var(--white); border-radius: 2px; transition: all 0.25s; }
.hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger.open span:nth-child(2) { opacity: 0; }
.hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

main { flex: 1; width: 100%; max-width: 860px; margin: 0 auto; padding: 1.5rem 1rem 3rem; }

/* ── Level select ── */
.hero-title { font-family: var(--font-script); font-weight: 700; font-size: 2.4rem; text-align: center; margin-top: 0.5rem; color: var(--white); text-shadow: 0 0 22px rgba(255,79,160,0.55), 0 0 46px rgba(56,208,255,0.25); letter-spacing: 0.02em; }
/* Punchy one-line hook, between the title and the descriptive hero-sub paragraph --
   reuses the same bold-orange treatment already established for subtitle lines
   elsewhere on this page (e.g. solutions.html's inline hero-sub2 style). */
.hero-tagline { text-align: center; color: var(--orange); font-weight: 700; font-size: 1.1rem; letter-spacing: 0.01em; margin: 0.3rem auto 0.2rem; }
.hero-sub { text-align: center; color: #cbb8ec; font-size: 0.95rem; max-width: 34rem; margin: 0.4rem auto 1.6rem; line-height: 1.5; }

/* Today's Trial — full-width featured banner (was a side-by-side card next to the
   tier list; now its own highlighted strip so it reads as a distinct "no setup
   needed" shortcut rather than competing with the tier/difficulty picker). */
.daily-banner {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  background: linear-gradient(135deg, #3a1256, #1c0a30); color: var(--white); border-radius: 16px;
  border: 1px solid rgba(255,157,77,0.35);
  padding: 1rem 1.3rem; margin-bottom: 1rem; flex-wrap: wrap;
}
.daily-banner h2 { font-family: var(--font-script); font-size: 1.15rem; font-weight: 700; color: var(--orange); }
.daily-banner p { font-size: 0.85rem; color: #c9c9e0; margin-top: 0.15rem; }

/* My Stats — a small secondary link, not a primary CTA, since the banner above and
   the tier picker below are the real focal points on this page. */
.quick-bar { text-align: center; margin-bottom: 1.6rem; }
.stats-link-btn {
  background: none; border: none; color: #cbb8ec; font-family: var(--font-body);
  font-weight: 700; font-size: 0.8rem; letter-spacing: 0.03em; cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.stats-link-btn:hover { color: var(--blue); }

.picker-heading { font-size: 0.95rem; font-weight: 800; color: var(--white); text-align: center; text-transform: uppercase; letter-spacing: 0.04em; margin: 1.4rem 0 0.5rem; }
.btn {
  background: linear-gradient(90deg, var(--orange), var(--mauve)); color: var(--white); border: none; border-radius: 99px;
  padding: 0.6rem 1.4rem; font-family: var(--font-body); font-weight: 800; font-size: 0.85rem;
  letter-spacing: 0.04em; cursor: pointer; transition: transform 0.15s, box-shadow 0.15s;
}
.btn:hover { box-shadow: 0 0 18px rgba(255,79,160,0.5); transform: translateY(-1px); }
.btn:disabled { opacity: 0.4; cursor: default; transform: none; box-shadow: none; }
.btn-ghost { background: transparent; color: var(--white); border: 2px solid rgba(255,255,255,0.2); }
.btn-ghost:hover { background: rgba(56,208,255,0.1); border-color: var(--blue); }

/* ── Difficulty picker (solutions page only — the interactive game uses the world
   carousel below instead). Stacked as its own column to the left of the tier list via
   .diff-tier-layout/.diff-tabs-col, Easy on top / Extreme on the bottom (DOM order is
   already easiest-first, so no JS change was needed to reorder it). ── */
.diff-tier-layout { display: flex; gap: 1.6rem; align-items: flex-start; justify-content: center; flex-wrap: wrap; }
.diff-tabs-col { display: flex; flex-direction: column; align-items: center; }
.diff-label { text-align: center; font-size: 0.78rem; color: #cbb8ec; letter-spacing: 0.03em; margin: 0.2rem 0 0.5rem; max-width: 12rem; }
.diff-tabs { display: flex; flex-direction: column; gap: 0.4rem; align-items: stretch; }
.diff-btn {
  background: rgba(255,255,255,0.04); border: 2px solid rgba(255,255,255,0.14); border-radius: 99px; padding: 0.6rem 1rem;
  font-family: var(--font-body); font-weight: 700; font-size: 0.75rem; letter-spacing: 0.05em;
  text-transform: uppercase; cursor: pointer; color: var(--white); transition: all 0.2s;
  min-height: 2.75rem; /* ~44px tap target */
}
.diff-btn:hover { border-color: var(--orange); color: var(--orange); }
.diff-btn.active { background: linear-gradient(90deg, var(--orange), var(--mauve)); border-color: transparent; color: var(--white); box-shadow: 0 0 16px rgba(255,157,77,0.4); }
.diff-btn.locked { opacity: 0.45; cursor: not-allowed; color: #cbb8ec; }
.diff-btn.locked:hover { border-color: rgba(255,255,255,0.14); color: #cbb8ec; }

/* ── World picker (carousel replacement for .diff-tabs, game page only —
   /katachi-go/solutions keeps the plain .diff-tabs pill row above, untouched) ── */
.world-carousel-wrap { display: flex; align-items: center; gap: 0.5rem; }
.world-carousel {
  display: flex; gap: 1.4rem; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; padding: 0.3rem 0.6rem 0.6rem; flex: 1;
  scrollbar-width: none; cursor: grab; user-select: none;
}
.world-carousel::-webkit-scrollbar { display: none; }
.world-carousel.dragging { cursor: grabbing; scroll-snap-type: none; }
/* No transition while actively dragging -- the coverflow scale/fade should track the
   mouse 1:1, not lag behind it with the resting-state transition applied. */
.world-carousel.dragging .world-card { transition: none; }
.world-card {
  /* transform/opacity/z-index are driven live by updateCoverflow() in game.js (a
     continuous "distance from center" falloff), not by CSS state -- the transition here
     is what makes that JS-driven scale/fade feel smooth instead of jumpy. */
  flex: 0 0 auto; scroll-snap-align: center; width: 20rem; height: 20rem;
  border-radius: 18px; border: 2px solid rgba(255,255,255,0.14); cursor: pointer;
  position: relative;
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.15s ease-out, opacity 0.15s ease-out;
  box-shadow: 0 6px 22px rgba(0,0,0,0.35);
  background-size: cover; background-position: center;
}
.world-card:hover { border-color: var(--orange); }
.world-card.active { border-color: var(--orange); box-shadow: 0 0 28px rgba(255,157,77,0.5); }
.world-card.locked { cursor: not-allowed; filter: grayscale(0.6); }
.world-card-lock {
  position: absolute; top: 0.7rem; right: 0.7rem; font-size: 1.3rem;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.85));
}
.world-card.locked:hover { border-color: rgba(255,255,255,0.14); }
.world-arrow {
  flex: 0 0 auto; width: 2.2rem; height: 2.2rem; border-radius: 50%;
  background: rgba(255,255,255,0.06); border: 2px solid rgba(255,255,255,0.14);
  color: var(--white); font-size: 1.3rem; cursor: pointer; line-height: 1;
  display: flex; align-items: center; justify-content: center; transition: all 0.2s;
}
.world-arrow:hover { border-color: var(--orange); color: var(--orange); }
.carousel-dots { display: flex; justify-content: center; gap: 0.5rem; margin: 0.4rem 0 1.2rem; }
/* Shows the world's number (2026-08, was a plain unlabeled dot) so it's obvious at a
   glance how many worlds there are and which one is centered, not just a position marker. */
.carousel-dot {
  width: 22px; height: 22px; border-radius: 50%; background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.25); color: rgba(245,237,255,0.7);
  font-family: var(--font-body); font-weight: 800; font-size: 0.68rem; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background 0.2s, transform 0.2s, color 0.2s, border-color 0.2s; padding: 0;
}
.carousel-dot.active { background: var(--orange); border-color: transparent; color: var(--white); transform: scale(1.15); }
@media (max-width: 600px) {
  .world-card { width: 78vw; height: 78vw; }
  .world-card-name { font-size: 1.2rem; }
}

.tier-tabs { display: flex; flex-direction: column; flex-wrap: nowrap; gap: 0.4rem; align-items: center; justify-content: flex-start; margin-bottom: 1.6rem; }
.tier-btn {
  background: rgba(255,255,255,0.04); border: 2px solid rgba(255,255,255,0.14); border-radius: 99px; padding: 0.6rem 1.1rem;
  font-family: var(--font-body); font-weight: 700; font-size: 0.78rem; letter-spacing: 0.07em;
  text-transform: uppercase; cursor: pointer; color: var(--white); transition: all 0.2s;
  white-space: nowrap; max-width: 92vw; min-height: 2.75rem; /* ~44px tap target */
}
.tier-btn:hover { border-color: var(--blue); color: var(--blue); }
/* Two-line tier button content (title + missing-piece-count subtitle) -- only tier
   buttons render these spans (mode-tabs/diff-tabs buttons are plain text and are
   unaffected). Block-level so they stack regardless of the parent's white-space:nowrap. */
#tierTabs .tier-btn { padding-top: 0.65rem; padding-bottom: 0.65rem; }
.tier-btn-title { display: block; font-size: 0.85rem; }
.tier-btn-sub {
  display: block; font-size: 0.66rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: none; color: rgba(245,237,255,0.6); margin-top: 0.2rem;
}
/* Katachi-Go's own level list only (scoped via .tier-row, a wrapper that doesn't exist on
   /katachi-go/solutions or the mode-tabs toggle -- both keep plain .tier-btn direct
   children, untouched) -- shows the player's best score for that tier to the right of the
   button itself, not inside it. */
/* Row max-width = the button's original 22rem budget + the fixed score column + gap, so
   the button keeps the same ~22rem it always had (long labels like "(Hardest)"/"(Easiest)"
   still fit on one line) while the score column's fixed width keeps every row's score
   flush at the same x position -- true vertical alignment down the column. */
.tier-row { display: flex; align-items: center; gap: 0.7rem; width: 100%; max-width: 27.9rem; }
.tier-row .tier-btn { flex: 1; }
.tier-score-outside {
  font-weight: 800; font-size: 0.78rem; color: var(--orange); white-space: nowrap;
  flex: 0 0 auto; width: 5.2rem; text-align: right;
}

/* ── Mode picker (flat tray / giant pyramid) — solutions page only ── */
/* .mode-row holds the mode tabs + the How to Play "?" button on the same row, to the
   tabs' right -- .mode-tabs keeps its own flex/wrap/gap for the two mode buttons
   themselves, .mode-row just adds the "?" as a trailing sibling and owns the row's
   bottom margin (moved off .mode-tabs so it isn't doubled). */
.mode-row { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; justify-content: center; margin: 0 0 1.2rem; }
.mode-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; justify-content: center; }
.mode-tabs .tier-btn {
  background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.14); color: var(--white);
  padding: 0.6rem 1.4rem; font-size: 0.88rem;
}
.mode-tabs .tier-btn:hover { border-color: var(--blue); color: var(--blue); }
.tier-btn.active { background: linear-gradient(90deg, var(--blue), var(--mauve)); border-color: transparent; color: var(--white); box-shadow: 0 0 16px rgba(56,208,255,0.4); }
/* Default pyramid/wedge widths — still used by /spheramids/solutions.html, which
   shares this file. Katachi-Go's own level-select overrides this to a uniform
   width below (.tier-tabs-uniform), that page only. */
.tier-tabs .tier-btn:nth-child(1) { width: 22rem; }
.tier-tabs .tier-btn:nth-child(2) { width: 20.4rem; }
.tier-tabs .tier-btn:nth-child(3) { width: 18.8rem; }
.tier-tabs .tier-btn:nth-child(4) { width: 17.2rem; }
.tier-tabs .tier-btn:nth-child(5) { width: 15.6rem; }
.tier-tabs .tier-btn:nth-child(6) { width: 14rem; }
.tier-tabs .tier-btn:nth-child(7) { width: 12.5rem; }

/* Katachi-Go level-select only: uniform width for all 7 tiers (was the decreasing
   "pyramid" above), easiest at top / hardest at the bottom, just resized evenly.
   /katachi-go/solutions keeps plain .tier-btn direct children (no .tier-row), so this
   targets .tier-row there instead and simply doesn't match on that page. */
.tier-tabs-uniform .tier-btn:nth-child(n) { width: 100%; max-width: 22rem; }
.tier-tabs-uniform .tier-row:nth-child(n) { width: 100%; max-width: 27.9rem; }
.tier-tabs-uniform .tier-row .tier-btn { max-width: none; width: auto; }

.generate-row { display: flex; justify-content: center; margin: 0.4rem 0 0.8rem; }
.generate-row .btn { font-size: 0.95rem; padding: 0.75rem 1.8rem; }

/* ── SEO blurb / FAQ (level-select page only) ── */
.seo-blurb { margin-top: 2.2rem; padding-top: 1.4rem; border-top: 1px solid rgba(255,255,255,0.12); }
.seo-blurb > p { color: #cbb8ec; font-size: 0.9rem; line-height: 1.6; max-width: 40rem; margin: 0.6rem auto 1.2rem; text-align: center; }
.faq-item {
  max-width: 40rem; margin: 0 auto 0.6rem; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px;
  padding: 0.7rem 1rem;
}
.faq-item summary { font-weight: 700; font-size: 0.88rem; color: var(--white); cursor: pointer; }
.faq-item p { font-size: 0.85rem; color: #cbb8ec; line-height: 1.55; margin-top: 0.5rem; }

.leaderboard-section { margin-top: 2rem; padding-top: 1.4rem; border-top: 1px solid rgba(255,255,255,0.12); }
.leaderboard-list { max-width: 30rem; margin: 0 auto; }

/* ── Game view ── */
.game-topbar { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.6rem; margin-bottom: 0.8rem; flex-wrap: wrap; }
.game-topbar > span { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.6rem; }
.game-title { font-weight: 800; font-size: 1rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--white); text-align: center; }
.game-title .tier { color: var(--orange); }

/* Landing-screen difficulty row -- difficulty picking lives right below the world
   carousel on the landing screen. Reuses .tier-btn's pill look, just laid out as a
   horizontal wrap instead of the vertical .tier-tabs column the old picker/solutions
   page use. Picking a pill only selects that difficulty (see game.js's
   renderGameDiffRow()) -- picking a world card is what starts a puzzle. */
.game-diff-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem; margin: 0 0 0.8rem; }
.game-diff-row .tier-btn { flex: 1 1 auto; min-width: 5.4rem; padding: 0.5rem 0.7rem; }

/* Narrow screens: the toolbar (5 buttons) and difficulty row (5 pills) previously wrapped
   as ragged flex rows -- different-width buttons per row meant each row's left/right
   edges didn't line up with the row above/below it, reading as "not centered" even
   though each row was centered as its own group. Switching both to a fixed 3-column grid
   (all cells the same width) fixes that misalignment and, with smaller
   font-size/padding, meaningfully shrinks the vertical space the controls eat before the
   board even starts. Desktop keeps the original flex-wrap layout, which already reads
   fine at that width. */
@media (max-width: 600px) {
  .game-topbar > span {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.4rem; width: 100%;
  }
  .game-topbar > span .btn { width: 100%; padding: 0.45rem 0.3rem; font-size: 0.72rem; }
  .game-diff-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.4rem; }
  .game-diff-row .tier-btn { width: 100%; min-width: 0; padding: 0.45rem 0.3rem; }
}

.board-wrap { background: var(--board); border: 1px solid rgba(56,208,255,0.25); border-radius: 18px; padding: 10px; box-shadow: 0 0 30px rgba(255,79,160,0.15), 0 6px 24px rgba(0,0,0,0.45); }
#boardWrap { max-width: 90%; margin: 0 auto; }
#boardSvg { display: block; width: 100%; height: auto; touch-action: none; }
#boardSvg .cell-hit { cursor: pointer; }

.controls-hint { text-align: center; font-size: 1rem; color: #cbb8ec; margin: 0.7rem 0 0.3rem; }
.tray {
  display: flex; gap: 0.45rem; flex-wrap: wrap; justify-content: center;
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 0.8rem; margin-top: 0.8rem;
  min-height: 72px;
}
.tray-piece {
  background: rgba(255,255,255,0.05); border: 2px solid transparent; border-radius: 12px; padding: 9px;
  cursor: pointer; transition: border-color 0.12s, background 0.12s, opacity 0.12s; line-height: 0;
  touch-action: none; -webkit-user-select: none; user-select: none;
}
.tray-piece:hover { border-color: var(--blue); }
.tray-piece.selected { border-color: var(--orange); background: rgba(255,157,77,0.14); }
.tray-piece.dragging-source { opacity: 0.35; }
/* Brief glow while a second quick tap would still count as "flip" rather than a new
   rotate (2026-08 audit fix) -- makes the previously-invisible ~350ms double-tap window
   visible, so a late second tap reads as "the window closed" instead of "flip is broken". */
.tray-piece.flip-window { box-shadow: 0 0 12px 3px rgba(56,208,255,0.65); transition: box-shadow 0.15s ease-out; }
.tray-empty { color: #9d8cc2; font-size: 1.6rem; align-self: center; }

/* ── Drag ghost (follows pointer while dragging a piece from the tray) ── */
.drag-ghost {
  position: fixed; top: 0; left: 0; z-index: 150; pointer-events: none;
  opacity: 0.88; filter: drop-shadow(0 6px 14px rgba(0,0,0,0.5));
}

@keyframes shake { 0%,100%{transform:translateX(0)} 25%{transform:translateX(-5px)} 75%{transform:translateX(5px)} }
.board-wrap.shake { animation: shake 0.25s; }

/* ── Win modal ── */
.overlay {
  position: fixed; inset: 0; background: rgba(10,4,18,0.75); z-index: 100;
  display: none; align-items: center; justify-content: center; padding: 1rem;
}
.overlay.open { display: flex; }
.win-modal {
  background: linear-gradient(160deg, #2b1140, #170821); border: 1px solid rgba(255,79,160,0.35); border-radius: 20px; padding: 2rem 1.8rem; max-width: 24rem;
  text-align: center; box-shadow: 0 0 60px rgba(255,79,160,0.25), 0 20px 60px rgba(0,0,0,0.5);
}
.win-modal h2 { font-family: var(--font-script); font-size: 2rem; color: var(--orange); text-shadow: 0 0 18px rgba(255,157,77,0.6); }
.win-modal p { color: #cbb8ec; font-size: 0.92rem; line-height: 1.55; margin: 0.7rem 0 1.2rem; }
.win-actions { display: flex; flex-direction: column; gap: 0.55rem; }

/* How to Play trigger -- same .btn-ghost look as Hint/Reset. Sizes to its own label
   naturally (no fixed width) -- both host layouts (.mode-row, .game-topbar > span) are
   flex-wrap/grid-based already, so a text label reflows safely without further changes. */
.howto-btn { min-height: 2.75rem; padding: 0.55rem 1rem; text-align: center; font-size: 0.85rem; font-weight: 700; }
.howto-modal { max-width: 26rem; text-align: left; }
.howto-modal h2 { text-align: center; }
.howto-list { color: #cbb8ec; font-size: 0.9rem; line-height: 1.6; margin: 0.8rem 0 1.2rem; padding-left: 1.2rem; }
.howto-list li { margin-bottom: 0.5rem; }
.howto-list strong { color: var(--white); }

.win-thrust { font-weight: 800; font-size: 1.3rem; color: var(--white); margin-top: 0.2rem; }
.win-best { color: var(--orange); font-weight: 700; font-size: 0.85rem; margin-top: 0.3rem; }
.win-streak { color: var(--mauve); font-weight: 700; font-size: 0.85rem; margin-top: 0.2rem; }
.win-badges { margin-top: 0.4rem; }
.win-physical-cta {
  display: block; margin: 0.8rem 0; padding: 0.6rem 1rem; border-radius: 10px;
  background: rgba(255,157,77,0.12); border: 1px solid rgba(255,157,77,0.4);
  color: var(--orange); font-weight: 700; font-size: 0.85rem; text-decoration: none;
}
.win-physical-cta:hover { background: rgba(255,157,77,0.22); }

/* ── Win celebration: neon flash + particle burst ── */
.celebrate-flash {
  position: absolute; inset: 0; pointer-events: none; display: flex; align-items: center; justify-content: center;
}
.celebrate-flash::after {
  content: ''; width: 60vmax; height: 60vmax; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,79,160,0.85) 0%, rgba(56,208,255,0.4) 35%, rgba(56,208,255,0) 70%);
  opacity: 0; transform: scale(0.3);
}
.celebrate-flash.play::after { animation: sunFlash 0.9s ease-out; }
@keyframes sunFlash {
  0% { opacity: 0; transform: scale(0.3) rotate(0deg); }
  30% { opacity: 1; transform: scale(1) rotate(20deg); }
  100% { opacity: 0; transform: scale(1.5) rotate(35deg); }
}

.celebrate-burst { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.particle {
  position: absolute; left: 50%; top: 42%; width: 1.5rem; height: 1.5rem; margin: -0.75rem;
  opacity: 0; animation: burstFall 1.5s ease-out forwards; animation-delay: var(--delay, 0s);
}
.particle.coin {
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff2c9, var(--mauve) 60%, #7a1e5c 100%);
  box-shadow: inset 0 0 0 2px rgba(255,255,255,0.4), 0 0 10px rgba(255,79,160,0.6);
}
.particle.scarab svg { width: 100%; height: 100%; display: block; }
@keyframes burstFall {
  0% { transform: translate(0, 0) rotate(0deg) scale(0.4); opacity: 0; }
  14% { opacity: 1; transform: translate(calc(var(--dx) * 0.3), calc(var(--dy) * 0.3)) rotate(90deg) scale(1); }
  100% { transform: translate(var(--dx), calc(var(--dy) + 230px)) rotate(var(--rot)) scale(0.85); opacity: 0; }
}
.badge-unlock, .badge-earned {
  background: rgba(255,157,77,0.12); border: 1px solid rgba(255,157,77,0.4); border-radius: 10px; padding: 0.4rem 0.7rem;
  font-size: 0.82rem; font-weight: 600; color: var(--orange); margin-top: 0.4rem;
}


/* ── Stats view ── */
.stats-link-row { text-align: center; margin: -0.4rem 0 1.2rem; }
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.7rem; margin: 1rem 0 1.4rem; }
.stat-card { background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); border-radius: 14px; padding: 1rem 0.6rem; text-align: center; }
.stat-num { font-weight: 800; font-size: 1.6rem; color: var(--white); }
.stat-label { font-size: 0.72rem; color: #9d8cc2; text-transform: uppercase; letter-spacing: 0.05em; margin-top: 0.2rem; }
.stats-subhead { font-size: 0.95rem; font-weight: 800; color: var(--white); margin: 1.2rem 0 0.5rem; text-transform: uppercase; letter-spacing: 0.04em; }
.stat-row {
  display: flex; justify-content: space-between; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); border-radius: 10px;
  padding: 0.55rem 0.9rem; margin-bottom: 0.4rem; font-size: 0.88rem; color: var(--white);
}
.stats-empty { color: #9d8cc2; font-size: 0.85rem; }

/* ── Account / sync block ── */
.account-block {
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); border-radius: 14px; padding: 1rem 1.1rem; margin: 1rem 0 1.4rem;
  text-align: center;
}
.account-label { font-size: 0.85rem; font-weight: 700; color: var(--white); margin-bottom: 0.6rem; }
.account-form { display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; }
.account-form input {
  flex: 1 1 12rem; border: 2px solid rgba(255,255,255,0.16); border-radius: 99px; padding: 0.5rem 1rem;
  font-family: var(--font-body); font-size: 0.85rem; color: var(--white); background: rgba(255,255,255,0.05);
}
.account-form input:focus { outline: none; border-color: var(--blue); }
.account-msg { font-size: 0.78rem; color: #9d8cc2; margin-top: 0.5rem; min-height: 1.1em; }

/* ── Install prompt ── */
#installBtn { display: none; color: var(--white); align-items: center; gap: 0.4rem; }
#installBtn img { width: 20px; height: 20px; border-radius: 5px; object-fit: cover; vertical-align: middle; }

/* ── Footer (matches site) ── */
footer { background: var(--navy); color: var(--white); text-align: center; padding: 1.6rem 1rem; margin-top: 2rem; border-top: 1px solid rgba(255,79,160,0.2); }
.footer-logo { font-family: var(--font-logo); font-size: 1.3rem; font-weight: 400; display: block; margin-bottom: 0.3rem; }
footer p { font-size: 0.8rem; color: #c9c9e0; }
footer a { color: var(--blue); }

.toast {
  position: fixed; bottom: 1.4rem; left: 50%; transform: translateX(-50%) translateY(20px);
  background: linear-gradient(90deg, var(--mauve), var(--blue)); color: var(--white); border-radius: 99px; padding: 0.6rem 1.3rem;
  font-size: 0.85rem; font-weight: 600; opacity: 0; pointer-events: none;
  transition: opacity 0.25s, transform 0.25s; z-index: 200; box-shadow: 0 4px 20px rgba(255,79,160,0.4);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ── Physical puzzle (coming soon) page ── */
.physical-badge-row { text-align: center; margin: -0.6rem 0 1.2rem; }
.coming-soon-badge {
  display: inline-block; background: var(--yellow); color: var(--navy);
  font-weight: 800; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 0.35rem 0.9rem; border-radius: 99px;
}

/* ── Puzzle Solutions page ── */
.solutions-detail { margin: 0.5rem 0 1.8rem; }
/* Single merged board (starting layout, swapped in place for the solution on reveal) --
   sized to match the interactive game's own #boardWrap (max-width:90%, centered) instead
   of the old narrow two-board-column cap, so the two pages' boards read as the same size
   even though they already render at the identical U=60 SVG cell scale. */
.single-board-col {
  display: flex; flex-direction: column; align-items: center; gap: 1rem;
}
.single-board-col .board-wrap { max-width: 90%; margin: 0 auto; }
/* boardSvg functions emit real width/height attributes (not just viewBox), so the
   flat-tray board stretches to fill the column while the pyramid board renders at its own
   native pixel scale instead of being stretched (that stretching inflated its spheres far
   past the flat board's sphere size). max-width:100% only guards narrow-screen overflow. */
#puzzleBoard svg {
  display: block; margin: 0 auto; max-width: 100%; height: auto;
}
.legend-section { margin-top: 2rem; }
.legend-grid { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; }
.legend-item {
  display: flex; flex-direction: column; align-items: center; gap: 0.3rem;
  background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; padding: 0.6rem 0.8rem;
  font-size: 0.72rem; font-weight: 700; color: #cbb8ec;
  min-width: 4.5rem;
}
.legend-letter {
  display: flex; align-items: center; justify-content: center;
  width: 1.4rem; height: 1.4rem; border-radius: 50%;
  background: #0D0D2B; color: #fff; font-size: 0.78rem; font-weight: 800;
}

@media (max-width: 560px) {
  nav { padding: 0.7rem 1rem; flex-wrap: wrap; }
  .nav-links {
    display: none; width: 100%; flex-direction: column; gap: 0;
    order: 3; border-top: 1px solid rgba(255,255,255,0.1); margin-top: 0.7rem; padding-top: 0.5rem;
  }
  .nav-links.open { display: flex; }
  .nav-links a { display: block; padding: 0.6rem 0; }
  .hamburger { display: flex; }
  .nav-search { display: none; }
  .hero-title { font-size: 2.1rem; }
  .tier-world-name { font-size: 1.25rem; }
  main { padding: 1rem 0.7rem 2.5rem; }
  .top-row { flex-direction: column; }
  .daily-card { max-width: none; }
}

/* ── Printable puzzle booklet (solutions.js: buildBooklet()) ──
   #printBooklet is built on demand and only ever meant to be seen via the browser's
   print/print-preview pass -- hidden on screen, revealed (and everything else on the
   page hidden) only under @media print. Fixed light colors regardless of the site's
   dark theme, since this surface never appears on screen either theme. */
#printBooklet { display: none; }

@media print {
  /* Fixes the printable page box so .booklet-group's height below can be sized
     against a known quantity -- letter, 0.5in margins -> 7.5in x 10in content box. */
  @page {
    size: letter portrait; margin: 0.5in;
    /* Best-effort running footer on every physical printed page (2026-08) -- CSS Paged
       Media margin-box content support is real in Chrome but genuinely inconsistent
       across browsers/print pipelines, and this repo's automated verification tools
       can't open a real print preview to confirm it renders. If it doesn't show up in
       your own print/PDF check, this @bottom-center rule is the only place to remove --
       the back-cover .booklet-legal-page below carries the same "official" notice as a
       guaranteed-to-work fallback. */
    @bottom-center {
      content: "Katachi-Go — An Official Hoffmakers Game · Page " counter(page);
      font-family: var(--font-body); font-size: 8pt; color: #666;
    }
  }

  nav, .hero-title, .hero-sub, .hero-sub2, .mode-row, .generate-row,
  #detailPane, .diff-tier-layout, .legend-section, .seo-blurb, footer,
  .toast, .overlay {
    display: none !important;
  }
  body { background: #ffffff; }
  /* Browsers default to NOT printing background-like fills (gradients, images) to
     save ink unless told otherwise -- each sphere's colored radial-gradient fill
     was getting silently dropped, leaving only its stroke ring and letter behind
     (a plain white circle with a colored outline). Force exact color printing for
     every element in the booklet so sphere fills actually print. */
  #printBooklet, #printBooklet * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    color-adjust: exact;
  }
  #printBooklet {
    display: block;
    color: #1a1a2e;
    font-family: var(--font-body);
    max-width: none;
    padding: 0;
  }
  /* Real Hoffmakers logo artwork (2026-08) -- IMG_9091.PNG (script wordmark + starburst
     on a dark starfield square, not transparent). Kept small and rounded so it reads as
     a logo badge above the title rather than a large dark ink block dominating the
     cover. */
  .booklet-logo {
    display: block; width: 1.4in; height: 1.4in; margin: 0 auto 0.5rem;
    border-radius: 10px; object-fit: cover;
  }
  /* Booklet cover title -- the very first thing on the printed page, above even the
     instructions section's own heading. */
  .booklet-main-title {
    font-family: var(--font-script);
    font-size: 2.2rem;
    color: #1a1a2e;
    text-align: center;
    margin: 0 0 0.1rem;
  }
  /* Subtitle under the main title -- same font-size as .booklet-section-title ("How to
     Use This Booklet" etc.) so it doesn't compete with "Katachi-Go" for top billing, just
     centered and without that class's underline/left-alignment (not right for a cover). */
  .booklet-subtitle {
    font-family: var(--font-script);
    font-size: 1.6rem;
    color: #1a1a2e;
    text-align: center;
    margin: 0 0 0.3rem;
  }
  /* Cover branding block (2026-08) -- byline, short "official product" positioning
     blurb, copyright line matching the exact wording used in every other footer on the
     site, and a printing/edition note. Sized well below the main title so the cover's
     hierarchy stays title-first, not a competing headline. */
  .booklet-byline {
    text-align: center; font-weight: 800; font-size: 0.95rem;
    letter-spacing: 0.04em; text-transform: uppercase; color: #7a3fd4;
    margin: 0 0 0.9rem;
  }
  .booklet-about {
    text-align: center; font-size: 0.85rem; color: #444;
    max-width: 32rem; margin: 0 auto 0.6rem;
  }
  .booklet-copyright {
    text-align: center; font-size: 0.75rem; color: #777;
    margin: 0 0 0.3rem;
  }
  .booklet-edition {
    text-align: center; font-size: 0.7rem; font-style: italic; color: #999;
    margin: 0 0 1.4rem;
  }
  /* Back-cover legal page -- forced onto its own page at the very end of the booklet, a
     guaranteed-to-render fallback for the @page running-footer above (see its comment). */
  .booklet-legal-page {
    page-break-before: always;
    display: flex; flex-direction: column; justify-content: center; align-items: center;
    min-height: 8in; text-align: center; color: #555; font-size: 0.8rem; line-height: 1.7;
  }
  .booklet-legal-page p { max-width: 28rem; margin: 0 0 0.5rem; }
  .booklet-section-title {
    font-family: var(--font-script);
    font-size: 1.6rem;
    color: #1a1a2e;
    border-bottom: 2px solid #1a1a2e;
    padding-bottom: 0.3rem;
    margin: 0 0 1rem;
  }
  /* Each world/tier's puzzles are wrapped in .booklet-group (heading + its 5-puzzle
     grid). Groups no longer force their own page -- height is auto/intrinsic to their
     actual content, and `page-break-inside: avoid` just keeps a single group from being
     split across two pages; the print engine's normal flow packs multiple groups onto
     one page (or starts a group mid-page) wherever they fit. Puzzle sizing is fixed,
     explicit inches per board type (see .booklet-puzzle svg below), so a group's real
     height is small and predictable -- a flat-tray group is well under half a page,
     letting several share a page; a pyramid group is close to a full page on its own,
     so it'll typically start fresh even without being forced to. */
  .booklet-group {
    margin-top: 0.35in;
    page-break-inside: avoid;
  }
  .booklet-group-title {
    font-size: 1.15rem;
    color: #1a1a2e;
    margin: 0 0 0.15in;
  }
  /* 2 columns, 3 rows (5 puzzles). Plain grid, no flex-fill -- row/column
     tracks just need to hold each puzzle's fixed, precomputed size (below),
     not stretch anything to match them. */
  .booklet-group-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.15in 0.25in;
  }
  .booklet-puzzle {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .booklet-puzzle-label {
    font-size: 0.85rem;
    font-weight: 700;
    color: #333;
    margin-bottom: 0.1in;
    text-align: center;
  }
  /* Each board type's pixel dimensions are constant regardless of which puzzle is
     shown (flat: 660x300 always; pyramid: 222x596 always -- neither depends on
     givens/solution), so a fixed size per type is hardcoded here rather than left to
     the print engine's flex/grid auto-fill (proved unreliable in real paginated
     output in an earlier pass -- see git history). Flat and pyramid are sized very
     differently on purpose (2026-08, page-count-reduction pass): a flat-tray group's
     3-row/2-col grid at 3.4in-wide boards took ~6.1in, already more than half a page,
     so no two worlds could ever actually share a page even with page-break-inside
     relaxed to allow it -- shrinking flat boards to 2.25in wide brings a group down to
     ~4.5in, letting two flat worlds reliably land on one page together (see
     .booklet-group's comment). Pyramid boards stay large (2.5in) -- their tall aspect
     ratio means even a fairly aggressive shrink still can't fit two worlds on one page
     (each pyramid group is dominated by row height, not column width), so there's no
     real page-count win available there to trade legibility for; pyramid worlds keep
     the same size as before and mostly still land one per page, same as always. */
  .booklet-puzzle.is-flat svg {
    width: 2.25in;
    height: auto;
    display: block;
  }
  .booklet-puzzle.is-pyramid svg {
    height: 2.5in;
    width: auto;
    display: block;
  }
  /* Instructions section (renderBookletInstructions() in solutions.js), the
     booklet's first page -- explains the two sections plus the sphere-ring/no-ring/
     empty-cell convention, and a print-safe (solid-fill, non-gradient) piece legend.
     Deliberately its own light/print-themed rules rather than reusing the on-screen
     .legend-grid/.legend-item classes -- those are dark-theme and hidden entirely
     under print (.legend-section { display:none }), so reusing their names here
     would fight that rule. */
  .booklet-instructions p, .booklet-instructions li {
    color: #333;
    font-size: 0.92rem;
    line-height: 1.5;
  }
  .booklet-instructions ul {
    margin: 0.6rem 0 1rem;
    padding-left: 1.3rem;
  }
  .booklet-instructions li {
    margin-bottom: 0.35rem;
  }
  .booklet-legend-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.25in 0.3in;
    margin-top: 0.5rem;
  }
  .booklet-legend-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: #1a1a2e;
  }
}
