/* ==========================================================================
   Stick Math site stylesheet.
   One file for every page: index, privacy, terms, 404.
   Light theme, locked on purpose. The world is warm: ivory paper, wood and
   flame, with the playable board on a burnt-wood full-bleed band. The
   seven segment matchstick equations are the site's display face; the type
   itself (Nunito Sans, the app's brand face) stays quiet around them.
   Radius system: pills for interactive controls, 20 to 28px for containers.
   ========================================================================== */

:root {
  /* Paper and ink, all warm */
  --bg: #faf6ef;
  --surface: #ffffff;
  --sunken: #f1eae0;
  --ink: #221b14;
  --ink-2: #5d5347;
  --ink-3: #6e6455;
  --line: #e7dfd2;

  /* Flame */
  --accent: #c93a12;
  --accent-deep: #a82e0c;
  --accent-dark: #7a2108;
  --gold: #f2a31c;

  /* The burnt-wood table the game is played on */
  --band-a: #2a1c12;
  --band-b: #140c07;
  --band-ink: #f6efe6;
  --band-ink-2: #cbbba9;

  /* Matchstick material */
  --wood-1: #f4cf9d;
  --wood-2: #d99c57;
  --head-1: #ff7a50;
  --head-2: #d63920;
  /* The same sticks on a pale card. The hero wood is tuned for the dark
     band and falls to 1.5:1 on white, so the paper rows get their own. */
  --wood-paper-1: #cf9247;
  --wood-paper-2: #a4661f;

  --ok: #0f7a34;
  --ok-bright: #5cd98c;

  /* Tier colors: the app's design tokens darkened 16% so white text on the
     tile gradients meets WCAG AA. */
  --easy-a: #1d8a3f;   --easy-b: #0d662c;
  --medium-a: #2162c3; --medium-b: #0f3598;
  --master-a: #6731b4; --master-b: #3e1885;
  --legend-a: #ca6025; --legend-b: #a12c16;

  /* Shape */
  --r-card: 28px;
  --r-tile: 20px;
  --r-pill: 999px;

  /* Motion */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);

  /* Layers */
  --z-nav: 30;
  --z-drag: 40;

  --wrap: 1140px;

  /* Nunito Sans stays the reading face; the marker is for display headings only. */
  --font-display: "Luckiest Guy", "Nunito Sans", "Avenir Next", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  /* The sticky header covers the top of the viewport; keep anchor targets
     (the download band, legal TOCs) clear of it. */
  scroll-padding-top: 84px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Nunito Sans", "Avenir Next", "Segoe UI", system-ui, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.6;
  font-weight: 600;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}

/* The flame focus ring vanishes on the dark band; use warm white there. */
.board-band :focus-visible,
.dl-band :focus-visible {
  outline-color: #fff;
  outline-offset: 2px;
}

::selection { background: #f6dcc4; color: var(--accent-dark); }

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(20px, 4vw, 40px);
}

/* --------------------------------------------------------------------------
   Skip link
   -------------------------------------------------------------------------- */

.skip-link {
  position: fixed;
  top: 14px;
  left: 14px;
  z-index: 60;
  padding: 10px 18px;
  background: var(--ink);
  color: #fff;
  border-radius: var(--r-pill);
  font-weight: 800;
  font-size: 0.9rem;
  transform: translateY(-64px);
  transition: transform 240ms var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); text-decoration: none; }

/* --------------------------------------------------------------------------
   Header: one bar, one action
   -------------------------------------------------------------------------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-nav);
  background: rgba(250, 246, 239, 0.82);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
  border-bottom: 1px solid rgba(34, 27, 20, 0.07);
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
  transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out);
}
/* The game fills the first screen on its own; the header only earns its
   place once the visitor starts scrolling past it, then it sticks. */
.site-header.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
@media (prefers-reduced-transparency: reduce) {
  .site-header { background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.site-header .bar {
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 10px clamp(20px, 4vw, 40px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-weight: 900;
  font-size: 1.05rem;
  color: var(--ink);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.brand:hover { text-decoration: none; }

/* The logo is the game: a not-equals sign laid from three matchsticks */
.logo-mark {
  position: relative;
  width: 32px;
  height: 32px;
  flex: none;
}
.logo-mark span {
  position: absolute;
  left: 3px;
  width: 26px;
  height: 4.5px;
  border-radius: 3px;
  background: linear-gradient(180deg, var(--wood-1), var(--wood-2));
  box-shadow: 0 1px 2px rgba(60, 30, 5, 0.35);
}
.logo-mark span::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 6px;
  height: 7.5px;
  transform: translateY(-50%);
  background: radial-gradient(120% 120% at 32% 26%, var(--head-1), var(--head-2) 72%);
  border-radius: 46% 46% 50% 50% / 42% 42% 58% 58%;
}
.logo-mark .l1 { top: 9px; }
.logo-mark .l1::after { right: -2px; transform: translateY(-50%) rotate(90deg); }
.logo-mark .l2 { top: 19px; }
.logo-mark .l2::after { left: -2px; transform: translateY(-50%) rotate(-90deg); }
.logo-mark .l3 {
  top: 14px;
  left: 2px;
  width: 29px;
  transform: rotate(-58deg);
}
.logo-mark .l3::after {
  right: -2px;
  transform: translateY(-50%) rotate(90deg);
  transition: box-shadow 300ms var(--ease-out);
}
.brand:hover .logo-mark .l3::after {
  box-shadow: 0 0 10px 3px rgba(255, 122, 80, 0.75);
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 13px 24px;
  border: 0;
  border-radius: var(--r-pill);
  font: inherit;
  font-weight: 800;
  font-size: 1rem;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 140ms var(--ease-out), box-shadow 240ms var(--ease-out),
    background-color 200ms var(--ease-out), color 200ms var(--ease-out);
}
.btn:hover { text-decoration: none; }

.btn-primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 10px 22px -12px rgba(201, 58, 18, 0.7);
}
.btn-primary:hover {
  background: var(--accent-deep);
  transform: translateY(-1px);
  box-shadow: 0 14px 28px -12px rgba(201, 58, 18, 0.75);
}
.btn-primary:active { transform: translateY(0) scale(0.97); }

.btn-small { padding: 10px 20px; font-size: 0.94rem; }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.btn-ghost:hover { box-shadow: inset 0 0 0 1.5px var(--ink-3); }
.btn-ghost:active { transform: scale(0.97); }

/* Ghost buttons sitting on the dark band */
.board-band .btn-ghost {
  color: var(--band-ink);
  box-shadow: inset 0 0 0 1.5px rgba(246, 239, 230, 0.35);
}
.board-band .btn-ghost:hover { box-shadow: inset 0 0 0 1.5px rgba(246, 239, 230, 0.7); }

/* --------------------------------------------------------------------------
   Scroll reveals (JS adds .in when the element enters the viewport).
   A finished animation must release the transform property, so this is a
   fill-backwards animation, not a transition to a pinned end state.
   -------------------------------------------------------------------------- */

.js .reveal { opacity: 0; }
.js .reveal.in {
  opacity: 1;
  animation: reveal-in 700ms var(--ease-out) backwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(20px); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal, .js .reveal.in { opacity: 1; animation: none; }
  .skip-link { transition: none; }
  .ember { animation: none; opacity: 0; }
  .board-band::after, .dl-band::after { animation: none; }
  .scroll-cue { animation: none; }
  /* Show the rail already drawn. Killing the animation alone would leave it
     stuck behind its own dash offset, so the line would never appear. */
  .js .tier-rail line { animation: none; stroke-dashoffset: 0; }
  .js .tier-rail circle { animation: none; opacity: 1; }
  /* Hold the highlight still rather than dropping it: without motion this
     is the only thing pointing at the move. */
  .stick.hint-glow .wood {
    animation: none;
    filter: brightness(1.3) drop-shadow(0 0 10px rgba(255, 59, 48, 0.9));
  }
  .slot.hint-target {
    animation: none;
    box-shadow: inset 0 0 0 2px rgba(255, 59, 48, 0.85);
    background-color: rgba(255, 59, 48, 0.14);
  }
  .feature-phone-frame { animation: none; }
}

/* --------------------------------------------------------------------------
   Hero: the game itself, filling the first screen. One background (the
   burnt wood the sticks already sit on), everything centered: the board,
   then its prompt, then the drag instruction and progress dots below it.
   Everything else waits below the fold.
   -------------------------------------------------------------------------- */

.hero-board {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(64px, 10vh, 96px) clamp(48px, 7vh, 72px);
}
.hero-board-wrap {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 18px;
}
.hero-board-wrap h1 {
  margin: 0;
  font-size: 1rem;
  color: var(--band-ink-2);
  text-wrap: balance;
}
@media (max-width: 720px) {
  .hero-board { min-height: 92svh; }
  /* The puzzle is the point, let it take up more of a tall phone screen
     instead of sitting small in the middle of empty background. */
  .hero-board-wrap .ms-row { --u: clamp(34px, 11vw, 46px); }
}

/* A quiet cue that there is more below, nothing more is said */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: clamp(16px, 3vh, 32px);
  transform: translateX(-50%);
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: var(--band-ink-2);
  animation: scroll-cue-bob 1.8s ease-in-out infinite;
}
.scroll-cue:hover { color: var(--band-ink); text-decoration: none; }
.scroll-cue svg { width: 18px; height: 18px; }
@keyframes scroll-cue-bob {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50% { transform: translateX(-50%) translateY(5px); }
}

/* --------------------------------------------------------------------------
   The board band: a full-bleed strip of burnt wood the puzzle sits on
   -------------------------------------------------------------------------- */

.board-band {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 170% at 50% -40%, var(--band-a), var(--band-b) 74%);
  padding-block: clamp(40px, 6vw, 72px) clamp(26px, 4vw, 40px);
}
.board-band::after {
  /* ember light pooling at the bottom of the table, breathing slowly */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(55% 42% at 50% 108%, rgba(242, 163, 28, 0.16), transparent 70%);
  animation: glow-breathe 7s ease-in-out infinite;
}
@keyframes glow-breathe {
  0%, 100% { opacity: 0.7; }
  50% { opacity: 1; }
}
/* Solve wash: gradients cannot transition, so crossfade an overlay */
.board-band::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 170% at 50% -40%, #17683a, #062e17 74%);
  opacity: 0;
  transition: opacity 600ms var(--ease-inout);
}
.board-band.solved::before { opacity: 1; }

.board-band .wrap { position: relative; z-index: 1; }

.board {
  display: flex;
  justify-content: center;
  padding-block: clamp(6px, 1vw, 14px);
  /* Fast taps on sticks and slots must not become double-tap zoom on iOS */
  touch-action: manipulation;
}

/* Rising embers. Pure CSS, pointer-events free, killed by reduced motion. */
.ember {
  position: absolute;
  bottom: -8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold);
  opacity: 0;
  pointer-events: none;
  animation: ember-rise 7s linear infinite;
  animation-delay: var(--d, 0s);
  left: var(--x, 50%);
}
.ember.red { background: var(--head-1); width: 4px; height: 4px; }
@keyframes ember-rise {
  0% { opacity: 0; transform: translateY(0) scale(1); }
  12% { opacity: 0.85; }
  60% { opacity: 0.4; }
  100% { opacity: 0; transform: translateY(-46vh) translateX(18px) scale(0.4); }
}

/* Status row lives on the band */
.board-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px 18px;
}
.board-status .msg {
  margin: 0;
  font-size: 1rem;
  color: var(--band-ink-2);
  min-height: 1.6em;
}
.board-status .msg strong { color: var(--band-ink); }
.board-status .msg.ok strong { color: var(--ok-bright); }
.board-actions { display: flex; align-items: center; gap: 14px; }
.board-actions .btn { padding: 10px 20px; font-size: 0.94rem; }
.board-actions .btn[hidden] { display: none; }

/* Warm-up puzzles: four dots, one per demo puzzle, marking which is current */
.dots { display: inline-flex; gap: 7px; }
.dots span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(246, 239, 230, 0.22);
  transition: background-color 300ms var(--ease-out), transform 300ms var(--ease-out);
}
.dots span.active { background: var(--gold); transform: scale(1.15); }

/* --------------------------------------------------------------------------
   Seven segment equations
   -------------------------------------------------------------------------- */

.ms-row {
  --u: clamp(30px, 7.5vw, 70px);
  --t: calc(var(--u) * 0.15);
  display: flex;
  align-items: center;
  gap: clamp(10px, 2.6vw, 28px);
  touch-action: pan-y;
}

.ms-row.mini { --u: 15px; gap: 9px; }

/* Paper variant: wood sticks on light surfaces. Viewport-responsive so the
   row never overflows tiny phones. */
.ms-row.paper { --u: clamp(28px, 8.5vw, 40px); gap: clamp(10px, 3vw, 16px); }
.ms-row.paper .stick { cursor: default; }
.ms-row.paper .stick .wood {
  background: linear-gradient(180deg, var(--wood-paper-1), var(--wood-paper-2));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 calc(var(--t) * -0.22) 0 rgba(120, 74, 26, 0.28),
    0 2px 5px rgba(90, 45, 8, 0.25);
}
.ms-row.paper .stick.locked .wood {
  background: #e6dac6;
  box-shadow: inset 0 0 0 1px #c9b599;
}
/* Decorative rows show only the sticks that are there; ghost slots would
   make every digit read like a broken 8. */
.ms-row.paper .slot.open { box-shadow: none; background: none; }

.glyph { position: relative; flex: none; }
.glyph.digit {
  width: calc(var(--u) + 2 * var(--t));
  height: calc(2 * var(--u) + 3 * var(--t));
}
.glyph.op { width: var(--u); height: var(--u); }

.slot { position: absolute; border-radius: var(--t); }
.slot.h { width: var(--u); height: var(--t); }
.slot.v { width: var(--t); height: var(--u); }

.slot.seg-a { left: var(--t); top: 0; }
.slot.seg-f { left: 0; top: var(--t); }
.slot.seg-b { left: calc(var(--u) + var(--t)); top: var(--t); }
.slot.seg-g { left: var(--t); top: calc(var(--u) + var(--t)); }
.slot.seg-e { left: 0; top: calc(var(--u) + 2 * var(--t)); }
.slot.seg-c { left: calc(var(--u) + var(--t)); top: calc(var(--u) + 2 * var(--t)); }
.slot.seg-d { left: var(--t); top: calc(2 * var(--u) + 2 * var(--t)); }

.slot.op-h { left: 0; top: calc((var(--u) - var(--t)) / 2); }
.slot.op-v { left: calc((var(--u) - var(--t)) / 2); top: 0; }
.slot.eq-1 { left: 0; top: calc(var(--u) * 0.32 - var(--t) / 2); }
.slot.eq-2 { left: 0; top: calc(var(--u) * 0.68 - var(--t) / 2); }

.slot.open {
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0);
  transition: box-shadow 260ms var(--ease-out), background-color 260ms var(--ease-out);
}
/* On the playable board the empty spots stay faintly visible at rest, so
   the player can see where a stick could go before touching anything. */
.board .slot.open {
  box-shadow: inset 0 0 0 1.5px rgba(255, 244, 230, 0.14);
  background: rgba(255, 244, 230, 0.035);
}
.board.picking .slot.open {
  box-shadow: inset 0 0 0 1.5px rgba(255, 244, 230, 0.3);
  background: rgba(255, 244, 230, 0.07);
}
.board.picking .slot.open.target {
  box-shadow: inset 0 0 0 2px rgba(255, 244, 230, 0.9);
  background: rgba(255, 244, 230, 0.18);
}

/* --------------------------------------------------------------------------
   The matchstick itself
   -------------------------------------------------------------------------- */

.stick {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--t);
  cursor: grab;
  -webkit-tap-highlight-color: transparent;
}
.stick:active { cursor: grabbing; }
.stick:disabled, .stick.locked { cursor: default; }

/* A stick's box is only --t thick. Invisible pseudo-elements widen the thin
   axis to a guaranteed 24px touch target without changing visuals or the
   drop targeting, which measures the stick's own box. */
.stick.movable::before { content: ""; position: absolute; }
.slot.h .stick.movable::before { inset: calc((var(--t) - 24px) / 2) 6px; }
.slot.v .stick.movable::before { inset: 6px calc((var(--t) - 24px) / 2); }
.board.picking .slot.open::after { content: ""; position: absolute; }
.board.picking .slot.h.open::after { inset: calc((var(--t) - 24px) / 2) 6px; }
.board.picking .slot.v.open::after { inset: 6px calc((var(--t) - 24px) / 2); }

.stick .wood {
  position: absolute;
  border-radius: calc(var(--t) * 0.5);
  background: linear-gradient(180deg, var(--wood-1), var(--wood-2));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 calc(var(--t) * -0.22) 0 rgba(120, 74, 26, 0.28),
    0 2px 6px rgba(12, 6, 2, 0.5);
}
.slot.h .stick .wood { inset: calc(var(--t) * 0.06) calc(var(--t) * 0.28); }
.slot.v .stick .wood {
  inset: calc(var(--t) * 0.28) calc(var(--t) * 0.06);
  background: linear-gradient(90deg, var(--wood-1), var(--wood-2));
}

.stick .head {
  position: absolute;
  width: calc(var(--t) * 1.28);
  height: calc(var(--t) * 1.62);
  background: radial-gradient(120% 120% at 32% 26%, var(--head-1), var(--head-2) 72%);
  border-radius: 46% 46% 50% 50% / 42% 42% 58% 58%;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.4),
    0 2px 5px rgba(12, 6, 2, 0.55);
}
.slot.v .stick .head { left: 50%; transform: translateX(-50%); }
.slot.v .stick[data-head="up"] .head { top: calc(var(--t) * -0.18); }
.slot.v .stick[data-head="down"] .head { bottom: calc(var(--t) * -0.18); transform: translateX(-50%) rotate(180deg); }
.slot.h .stick .head { top: 50%; transform: translateY(-50%) rotate(-90deg); }
.slot.h .stick[data-head="right"] .head { right: calc(var(--t) * -0.18); transform: translateY(-50%) rotate(90deg); }
.slot.h .stick[data-head="left"] .head { left: calc(var(--t) * -0.18); }

/* Locked equals sticks read as chalk, not wood */
.stick.locked .wood {
  background: rgba(246, 239, 230, 0.92);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 1px 4px rgba(12, 6, 2, 0.4);
}
.stick.locked .head { display: none; }

.ms-row.mini .stick { cursor: default; }
.ms-row.mini .stick .wood {
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 1px 3px rgba(12, 6, 2, 0.35);
}
.ms-row.mini .stick .head { display: none; }

.stick.movable:hover .wood { filter: brightness(1.08); }
.stick.flying {
  z-index: var(--z-drag);
  cursor: grabbing;
  will-change: transform;
}
.stick.flying .wood { box-shadow:
  inset 0 1px 0 rgba(255, 255, 255, 0.55),
  inset 0 calc(var(--t) * -0.22) 0 rgba(120, 74, 26, 0.28),
  0 14px 28px rgba(8, 4, 1, 0.6);
}
/* First-time nudge: the correct stick glows on its own after a few
   seconds of no interaction, once, for a first-time visitor only. */
/* The nudge reads in red rather than gold. Every other thing on this band
   is warm wood, flame and ember, so a gold halo just looked like more of
   the same; red is the one signal here that means "this one". */
.stick.hint-glow .wood {
  animation: hint-glow 1.3s ease-in-out infinite;
}
@keyframes hint-glow {
  0%, 100% { filter: brightness(1); }
  50% {
    filter: brightness(1.4)
      drop-shadow(0 0 10px rgba(255, 59, 48, 0.95))
      drop-shadow(0 0 22px rgba(255, 59, 48, 0.55));
  }
}

/* Where that stick belongs. Lighting the destination too turns the nudge
   from "this one is wrong" into "carry it over there". */
.slot.hint-target {
  animation: hint-target 1.3s ease-in-out infinite;
}
@keyframes hint-target {
  0%, 100% {
    box-shadow: inset 0 0 0 1.5px rgba(255, 59, 48, 0.4);
    background-color: rgba(255, 59, 48, 0.06);
  }
  50% {
    box-shadow: inset 0 0 0 2px rgba(255, 59, 48, 0.95), 0 0 14px rgba(255, 59, 48, 0.45);
    background-color: rgba(255, 59, 48, 0.18);
  }
}

.stick.picked .wood {
  filter: brightness(1.16);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 calc(var(--t) * -0.22) 0 rgba(120, 74, 26, 0.28),
    0 0 0 3px rgba(255, 244, 230, 0.55),
    0 6px 16px rgba(8, 4, 1, 0.55);
}

.board.solved .stick .wood { filter: brightness(1.1) saturate(1.05); }

/* Solve celebration */
.spark {
  position: fixed;
  z-index: var(--z-drag);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  pointer-events: none;
  background: var(--wood-1);
}
.spark.hot { background: var(--head-1); }

/* An inviting pulse ring that lands on a stick while the page is idle, so
   nobody mistakes the board for a picture. Removed on first interaction. */
.touch-cue {
  position: fixed;
  z-index: var(--z-drag);
  width: 46px;
  height: 46px;
  margin: -23px 0 0 -23px;
  border-radius: 50%;
  border: 2.5px solid rgba(255, 244, 230, 0.85);
  background: rgba(255, 244, 230, 0.12);
  pointer-events: none;
  opacity: 0;
  animation: cue-pulse 1.35s var(--ease-out) 2;
}
@keyframes cue-pulse {
  0% { opacity: 0; transform: scale(0.45); }
  30% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.4); }
}

/* The hero board's fingertip: a touch point that demonstrates the drag */
.demo-finger {
  position: absolute;
  z-index: 3;
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.16) 72%);
  border: 2px solid rgba(255, 255, 255, 0.75);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  opacity: 0;
  will-change: transform;
}

.flame {
  position: fixed;
  z-index: var(--z-drag);
  width: 26px;
  height: 34px;
  pointer-events: none;
  transform: translate(-50%, -85%);
  background:
    radial-gradient(50% 42% at 50% 78%, #fff3c9 0%, transparent 60%),
    radial-gradient(50% 62% at 50% 66%, var(--gold) 0%, rgba(242, 163, 28, 0) 70%),
    radial-gradient(58% 78% at 50% 60%, var(--head-1) 0%, rgba(255, 122, 80, 0) 72%);
  border-radius: 50% 50% 46% 46% / 62% 62% 38% 38%;
  filter: blur(0.4px);
  animation: flame-pop 900ms var(--ease-out) forwards;
}
@keyframes flame-pop {
  0% { opacity: 0; transform: translate(-50%, -60%) scale(0.3); }
  22% { opacity: 1; transform: translate(-50%, -88%) scale(1.12) rotate(-4deg); }
  45% { transform: translate(-50%, -96%) scale(0.94) rotate(4deg); }
  70% { opacity: 0.85; transform: translate(-50%, -104%) scale(1.05) rotate(-3deg); }
  100% { opacity: 0; transform: translate(-50%, -125%) scale(0.5); }
}

/* --------------------------------------------------------------------------
   Sections
   -------------------------------------------------------------------------- */

section { padding-block: clamp(60px, 9vw, 120px); }
section.tight { padding-block: clamp(40px, 6vw, 76px); }

.section-head { margin-bottom: clamp(26px, 4vw, 48px); }
.section-head h2 {
  margin: 0 0 10px;
  font-size: clamp(1.9rem, 3.8vw, 2.8rem);
  font-weight: 1000;
  line-height: 1.06;
  letter-spacing: -0.028em;
  text-wrap: balance;
}
.section-head p {
  margin: 0;
  color: var(--ink-2);
  font-size: 1.07rem;
  max-width: 52ch;
}

/* --------------------------------------------------------------------------
   Know your game: the numbered feature list beside a real screenshot
   -------------------------------------------------------------------------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.feature-split {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}

/* The heading sits over the list column, underlined with a flame-gold stroke. */
.feature-copy .section-head { margin-bottom: clamp(28px, 3.5vw, 46px); }
.feature-copy .section-head h2 {
  position: relative;
  display: inline-block;
  margin-bottom: 0;
  transform: rotate(-1.5deg);
}
.feature-copy .section-head h2::after {
  content: "";
  position: absolute;
  left: 3px;
  bottom: -12px;
  width: 64%;
  height: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--gold), #f7c869);
  transform: rotate(0deg);
}

/* The cards alternate left and right by a fixed offset, so the dotted
   connector between the badges can be drawn at exact coordinates. */
.feature-list {
  --f-offset: 64px;
  /* The border tone is too pale to read as a trail on the ivory page. */
  --f-rail: #dfb877;
  list-style: none;
  margin: 0;
  padding: 0 0 0 17px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  position: relative;
}
.feature-zigzag {
  position: absolute;
  top: 44px;
  left: 0;
  width: calc(var(--f-offset) + 34px);
  height: calc(100% - 88px);
  pointer-events: none;
}
.feature-zigzag path {
  stroke: var(--f-rail);
  stroke-width: 2.5;
  /* Was "1 7" — a 1px dash with round caps renders as tiny dots. */
  stroke-dasharray: 9 7;
}

.feature-item {
  position: relative;
  width: calc(100% - var(--f-offset));
}
.feature-item:nth-of-type(even) { margin-left: var(--f-offset); }

/* The numbered badge straddles the card's left edge. */
.feature-num {
  position: absolute;
  left: -17px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--f-accent);
  color: #fff;
  font-weight: 900;
  font-size: 0.92rem;
  box-shadow: 0 3px 8px -2px rgba(30, 14, 4, 0.4);
}

.feature-card {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 96px;
  padding: 16px 20px 16px 30px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  box-shadow: 0 16px 34px -20px rgba(30, 14, 4, 0.45), 0 2px 6px -3px rgba(30, 14, 4, 0.16);
}

.feature-icon {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--f-tint);
  /* The glyph usually takes the card's accent. Streak opts out via --f-icon
     so its flame can be the app's own orange, which is too light to carry
     the white numeral on the badge. */
  color: var(--f-icon, var(--f-accent));
}
.feature-icon svg { width: 26px; height: 26px; }

.feature-text h3 {
  margin: 0 0 4px;
  font-size: 1.08rem;
  font-weight: 900;
  letter-spacing: -0.01em;
}
.feature-text p { margin: 0; color: var(--ink-2); font-size: 0.95rem; line-height: 1.4; }

/* The phone: a titanium rail, a black bezel, and two side buttons. */
.feature-phone { display: flex; justify-content: center; align-items: center; }
.feature-phone-frame {
  position: relative;
  width: min(100%, 360px);
  padding: 10px;
  border-radius: 44px;
  background: linear-gradient(145deg, #e4e0da 0%, #fbf9f6 22%, #cec8c0 50%, #fbf9f6 78%, #dad5ce 100%);
  box-shadow: 0 34px 64px -30px rgba(30, 14, 4, 0.5), inset 0 0 0 1px rgba(30, 14, 4, 0.07);
  animation: phone-float 5s ease-in-out infinite;
}
/* Volume rocker on the left rail, power button on the right. */
.feature-phone-frame::before {
  content: "";
  position: absolute;
  left: -2px;
  top: 17%;
  width: 3px;
  height: 26%;
  border-radius: 2px 0 0 2px;
  background:
    linear-gradient(#cdc7be, #a9a39b) left top / 100% 30% no-repeat,
    linear-gradient(#cdc7be, #a9a39b) left bottom / 100% 55% no-repeat;
}
.feature-phone-frame::after {
  content: "";
  position: absolute;
  right: -2px;
  top: 23%;
  width: 3px;
  height: 11%;
  border-radius: 0 2px 2px 0;
  background: linear-gradient(#cdc7be, #a9a39b);
}
.feature-phone-screen {
  padding: 3px;
  border-radius: 35px;
  background: #08090c;
  overflow: hidden;
}
.feature-phone-screen img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 32px;
}

@keyframes phone-float {
  0%, 100% {
    transform: translateY(0);
    box-shadow: 0 34px 64px -30px rgba(30, 14, 4, 0.5), inset 0 0 0 1px rgba(30, 14, 4, 0.07);
  }
  50% {
    transform: translateY(-26px);
    box-shadow: 0 58px 80px -24px rgba(30, 14, 4, 0.28), inset 0 0 0 1px rgba(30, 14, 4, 0.07);
  }
}

@media (max-width: 820px) {
  .feature-split { grid-template-columns: 1fr; }
  .feature-phone { order: -1; }
  .feature-copy .section-head { text-align: center; }
}

/* On a phone the offset only steals width from the copy, and the uneven
   card heights it causes pull the curve off its badges. Straighten the
   column out and swap the serpentine for a plain dotted rail. */
@media (max-width: 560px) {
  .feature-list { --f-offset: 0px; }
  .feature-zigzag { display: none; }
  .feature-list::before {
    content: "";
    position: absolute;
    top: 44px;
    bottom: 44px;
    left: 16px;
    width: 2px;
    background: repeating-linear-gradient(to bottom, var(--f-rail) 0 9px, transparent 9px 16px);
  }
}

/* --------------------------------------------------------------------------
   Tiers: four light cards, each carrying its own tier colour.
   --t-color and --t-tint are set per card in the markup, so one rule set
   dresses all four.
   -------------------------------------------------------------------------- */

.tiers-head { text-align: center; }
.tiers-head h2 { margin-bottom: 12px; }
.tiers-head p { margin-inline: auto; }
/* The line is 288 user units long (x1 6 to x2 294), so that is the dash it
   hides behind until the section arrives. Held back to .js only: without
   the script .in never lands and the rail would stay blank forever. */
.js .tier-rail line { stroke-dasharray: 288; stroke-dashoffset: 288; }
.js .tier-rail circle { opacity: 0; }
.js .tiers-head.in .tier-rail line {
  animation: rail-draw 900ms var(--ease-out) 150ms forwards;
}
.js .tiers-head.in .tier-rail circle {
  animation: rail-dot 320ms var(--ease-out) forwards;
}
.js .tiers-head.in .tier-rail circle:nth-of-type(1) { animation-delay: 200ms; }
.js .tiers-head.in .tier-rail circle:nth-of-type(2) { animation-delay: 450ms; }
.js .tiers-head.in .tier-rail circle:nth-of-type(3) { animation-delay: 700ms; }
.js .tiers-head.in .tier-rail circle:nth-of-type(4) { animation-delay: 950ms; }
@keyframes rail-draw { to { stroke-dashoffset: 0; } }
@keyframes rail-dot { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: scale(1); } }

.tier-rail {
  display: block;
  width: min(300px, 62%);
  height: 12px;
  margin: 26px auto 0;
}

.tier-grid {
  list-style: none;
  margin: 54px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 26px;
}

.tier-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
  padding: 46px 18px 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
  box-shadow: 0 14px 30px -24px rgba(30, 14, 4, 0.45);
  transition: transform 300ms var(--ease-out), box-shadow 300ms var(--ease-out);
}
.tier-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 24px 44px -26px rgba(30, 14, 4, 0.5);
}
/* The tier colour signs off the foot of its own card. Drawn as a pseudo
   element with matching corners, since the card cannot clip its overflow
   without cutting the badge that straddles the top edge. */
.tier-card::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--t-color);
  border-radius: 0 0 var(--r-tile) var(--r-tile);
}

.tier-badge {
  position: absolute;
  top: -30px;
  left: 50%;
  transform: translateX(-50%);
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--t-tint);
  border: 5px solid var(--bg);
  color: var(--t-color);
}
.tier-badge svg { width: 28px; height: 28px; }

.tier-card h3 {
  margin: 0;
  font-size: 1.32rem;
  font-weight: 1000;
  letter-spacing: -0.01em;
  color: var(--t-color);
}

.tier-moves {
  font-size: 0.82rem;
  font-weight: 800;
  padding: 5px 14px;
  border-radius: var(--r-pill);
  background: var(--t-tint);
  color: var(--t-color);
  white-space: nowrap;
}

.tier-eq { display: flex; justify-content: center; padding: 10px 0 2px; }
.tier-eq .ms-row.paper { --u: clamp(13px, 1.35vw, 16px); gap: clamp(5px, 0.6vw, 7px); }

.tier-desc {
  width: 100%;
  margin: 0;
  padding: 15px 4px 22px;
  border-top: 1px solid var(--line);
  color: var(--ink-2);
  font-size: 0.97rem;
  line-height: 1.45;
}

/* The chevrons carrying the eye from one tier to the next. */
.tier-link {
  position: absolute;
  left: -30px;
  top: 54%;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  border: 1.5px solid var(--t-color);
  color: var(--t-color);
}
.tier-link svg { width: 13px; height: 13px; }

@media (max-width: 980px) {
  .tier-grid { grid-template-columns: repeat(2, 1fr); row-gap: 64px; }
  /* Only a chevron sitting between two cards on the same row still means
     anything; the one starting a row would point at the page edge. */
  .tier-card:nth-child(odd) .tier-link { display: none; }
}
@media (max-width: 620px) {
  .tier-grid { grid-template-columns: 1fr; row-gap: 64px; }
  .tier-link { display: none; }
}

/* --------------------------------------------------------------------------
   Multiplayer
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: 1.02fr 1fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
}
.split-copy h2 {
  margin: 0 0 16px;
  font-size: clamp(1.9rem, 3.8vw, 2.8rem);
  font-weight: 1000;
  line-height: 1.02;
  letter-spacing: -0.028em;
}
/* The second word drops to its own line and takes the flame, the way the
   reference stacks it. */
.mp-hl {
  display: block;
  color: var(--accent);
  font-size: 1.2em;
}
.mp-zip {
  display: inline-block;
  width: 0.62em;
  height: 0.66em;
  margin-left: 0.24em;
  vertical-align: 0.04em;
  color: var(--accent);
}
.split-copy p { margin: 0; color: var(--ink-2); font-size: 1.07rem; max-width: 42ch; }

/* Five chips, one row. A flex row let the last one stretch alone on a
   second line, so this is an explicit five column grid. */
.mp-chips {
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
}
.mp-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  padding: 14px 6px;
  border-radius: var(--r-tile);
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 0.82rem;
  font-weight: 800;
  color: var(--ink-2);
  line-height: 1.25;
}
.mp-chip > span {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ct);
  color: var(--c);
}
.mp-chip svg { width: 20px; height: 20px; }

.mp-cta {
  margin-top: 26px;
  padding: 16px 30px;
  font-size: 1.05rem;
  background: linear-gradient(100deg, #e8632a, var(--accent) 62%);
  box-shadow: 0 16px 30px -14px rgba(201, 58, 18, 0.6);
}
.mp-cta:hover { background: linear-gradient(100deg, #d9541d, var(--accent-deep) 62%); }
.mp-cta svg { width: 21px; height: 21px; }

/* The room: one shared clock above, the players ringed around the puzzle. */
.mp-room {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: clamp(22px, 3vw, 34px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: 0 22px 46px -32px rgba(30, 14, 4, 0.45);
}

.mp-art {
  position: relative;
  width: 100%;
  max-width: 380px;
  margin: 4px auto 0;
}
.mp-art img { width: 100%; height: auto; display: block; }

/* Chatter around the room. Decorative only, and the first thing to go on a
   phone, where four bubbles round a circle collide with the artwork. */
.mp-say {
  position: absolute;
  padding: 6px 12px;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 8px 18px -12px rgba(30, 14, 4, 0.5);
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--ink-2);
  white-space: nowrap;
}
.mp-say.s1 { left: -2%;  top: 7%; }
.mp-say.s2 { right: -2%; top: 14%; }
.mp-say.s3 { left: -2%;  bottom: 24%; }
.mp-say.s4 { right: -2%; bottom: 13%; }

.mp-more {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: 14px 0 0;
  padding: 13px 16px;
  border-radius: var(--r-tile);
  background: var(--sunken);
  font-size: 0.95rem;
  color: var(--ink);
  font-weight: 800;
}
.mp-more-mark {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  color: var(--accent);
}
.mp-more-mark svg { width: 19px; height: 19px; }

.room-clock {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--ink-2);
  font-weight: 800;
  font-size: 0.95rem;
}
.room-clock svg { width: 40px; height: 40px; }
.room-clock .ring-track { stroke: var(--line); }
.room-clock .ring-fill {
  stroke: var(--accent);
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: center;
}
.room-clock .t { font-variant-numeric: tabular-nums; color: var(--ink); }

@media (max-width: 820px) {
  .split { grid-template-columns: 1fr; }
}
/* Four bubbles around a circle need room the phone does not have; below this
   the artwork speaks for itself. */
@media (max-width: 700px) {
  .mp-say { display: none; }
}
@media (max-width: 560px) {
  .mp-chips { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 380px) {
  .mp-chips { grid-template-columns: repeat(2, 1fr); }
}

/* --------------------------------------------------------------------------
   Download band
   -------------------------------------------------------------------------- */

.dl-band {
  position: relative;
  overflow: hidden;
  background: radial-gradient(120% 170% at 50% -40%, var(--band-a), var(--band-b) 74%);
  /* Tightened once the paragraph came out: the old height was sized around
     copy that is no longer there. */
  padding-block: clamp(46px, 6vw, 74px);
  text-align: center;
  color: var(--band-ink);
}
.dl-band::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(55% 45% at 50% 112%, rgba(242, 163, 28, 0.18), transparent 70%);
  animation: glow-breathe 7s ease-in-out infinite;
}
.dl-band .wrap { position: relative; z-index: 1; }
.dl-band h2 {
  /* Nothing follows the heading now but the badges, which bring their own
     top margin. */
  margin: 0;
  font-size: clamp(2rem, 4.4vw, 3.1rem);
  font-weight: 1000;
  letter-spacing: -0.028em;
  line-height: 1.05;
}

.store-badges {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 34px;
}
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px 12px 18px;
  background: #fff;
  color: var(--ink);
  border-radius: 16px;
  text-align: left;
  transition: transform 160ms var(--ease-out), box-shadow 240ms var(--ease-out);
  box-shadow: 0 12px 26px -14px rgba(0, 0, 0, 0.55);
}
.store-badge:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 34px -14px rgba(0, 0, 0, 0.6);
  text-decoration: none;
}
.store-badge:active { transform: translateY(0) scale(0.98); }
.store-badge svg { width: 26px; height: 26px; flex: none; }
.store-badge .lines { display: flex; flex-direction: column; line-height: 1.15; }
.store-badge .small { font-size: 0.72rem; font-weight: 700; color: var(--ink-2); }
.store-badge .big { font-size: 1.08rem; font-weight: 900; letter-spacing: -0.01em; }

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid var(--line);
  padding-block: clamp(36px, 5vw, 56px);
  font-size: 0.97rem;
}
.footer-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-weight: 900;
  color: var(--ink);
}
.footer-brand:hover { text-decoration: none; }
.footer-links { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.footer-links a {
  padding: 8px 12px;
  border-radius: var(--r-pill);
  color: var(--ink-2);
  font-weight: 700;
  transition: color 160ms var(--ease-out), background-color 160ms var(--ease-out);
}
.footer-links a:hover { color: var(--ink); background: var(--sunken); text-decoration: none; }
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-weight: 600;
}
.footer-bottom a { color: var(--ink-2); font-weight: 700; }

/* --------------------------------------------------------------------------
   No-JS: hide the shells the script would have filled
   -------------------------------------------------------------------------- */

html:not(.js) .site-header {
  position: static;
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
html:not(.js) .hero-board { min-height: 0; }
html:not(.js) #board,
html:not(.js) .board-status,
html:not(.js) .scroll-cue,
html:not(.js) .tier-eq { display: none; }

/* --------------------------------------------------------------------------
   Display headings: the marker face
   Index-only classes on purpose. The legal pages keep Nunito Sans, where
   uppercase marker type would hurt readability. Placed after the four base
   heading rules so it wins on equal specificity.
   -------------------------------------------------------------------------- */

#hero-h,
.section-head h2,
.split-copy h2,
.dl-band h2 {
  font-family: var(--font-display);
  /* Luckiest Guy ships a single weight; asking for 1000 forces an ugly
     synthetic bold. */
  font-weight: 400;
  text-transform: uppercase;
  /* The sans wanted tightening at -0.028em; a marker face wants air. */
  letter-spacing: 0.012em;
  line-height: 1.14;
}

/* --------------------------------------------------------------------------
   Legal pages (privacy, terms, 404)
   -------------------------------------------------------------------------- */

.legal-hero { padding-block: clamp(48px, 7vw, 80px) clamp(24px, 4vw, 40px); }
.legal-hero:not(.notfound) .wrap {
  max-width: calc(760px + 2 * clamp(20px, 4vw, 40px));
}
.legal-hero h1 {
  margin: 10px 0 8px;
  font-size: clamp(2rem, 4.4vw, 3rem);
  font-weight: 1000;
  letter-spacing: -0.025em;
  line-height: 1.06;
}
.legal-hero .meta { color: var(--ink-3); font-weight: 700; margin: 0; }
.eyebrow-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 800;
  font-size: 0.92rem;
}

.legal-body {
  max-width: calc(760px + 2 * clamp(20px, 4vw, 40px));
  margin-inline: auto;
  padding-inline: clamp(20px, 4vw, 40px);
  padding-bottom: clamp(48px, 7vw, 88px);
}
.legal-body h2 {
  margin: 2.2em 0 0.5em;
  font-size: 1.45rem;
  font-weight: 900;
  letter-spacing: -0.01em;
}
.legal-body h3 { margin: 1.6em 0 0.4em; font-size: 1.12rem; font-weight: 800; }
.legal-body p, .legal-body li { color: var(--ink-2); }
.legal-body li { margin-block: 6px; }
.legal-body strong { color: var(--ink); }

/* --------------------------------------------------------------------------
   Contact form
   The owner's address is never printed on the page. The form posts to a
   form relay that forwards the message, so there is no mailto: to scrape.
   The page is one raised card of pressed-in fields: the depth does the
   explaining, so the page needs almost no words.
   -------------------------------------------------------------------------- */

.contact-body {
  max-width: calc(580px + 2 * clamp(20px, 4vw, 40px));
  padding-top: 4px;
}
/* The heading has to share the card's left edge. The default legal hero is
   sized for 760px of prose, which left it hanging 130px outside the card. */
.legal-hero.contact-hero .wrap {
  max-width: calc(580px + 2 * clamp(20px, 4vw, 40px));
}
.legal-hero.contact-hero { padding-bottom: clamp(16px, 2.5vw, 24px); }

/* The contact page is the card and nothing else, so it centres in the
   viewport instead of sitting at the top of a scrolling column. On a normal
   screen there is then nothing to scroll to, and the header, which only
   fades in once you scroll, never needs to appear.

   min-height rather than a locked height: a short phone, a zoomed font, or
   an open keyboard all shrink the space, and the Send button has to stay
   reachable when they do. */
.page-contact #main {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(20px, 5vh, 48px);
}
.page-contact .legal-hero.contact-hero { padding-block: 0 clamp(14px, 2.4vw, 22px); }
/* width:100% matters here. .legal-body centres itself with margin-inline:auto,
   and an auto cross-axis margin stops a flex item stretching, so as a child of
   the centred #main it collapsed to its content width and the card came out
   300px wide instead of 580, no longer sharing the heading's left edge. */
.page-contact .contact-body { padding-bottom: 0; width: 100%; }
.contact-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: clamp(22px, 4vw, 34px);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.9) inset,
    0 2px 4px -2px rgba(34, 27, 20, 0.10),
    0 18px 40px -28px rgba(34, 27, 20, 0.55);
}

.contact-form {
  display: grid;
  gap: 18px;
}
.contact-form .field { display: grid; gap: 7px; }
.contact-form label {
  color: var(--ink);
  font-weight: 800;
  font-size: 0.94rem;
}

/* Fields read as pressed into the card: a hairline top shadow inside, a
   highlight along the bottom edge outside. */
.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form textarea {
  width: 100%;
  padding: 13px 15px;
  border: 1px solid #e3d9c9;
  border-radius: 13px;
  background: #fdfbf7;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  box-shadow:
    inset 0 2px 4px rgba(34, 27, 20, 0.055),
    inset 0 -1px 0 rgba(255, 255, 255, 0.8),
    0 1px 0 rgba(255, 255, 255, 0.9);
  transition: border-color 200ms var(--ease-out), box-shadow 200ms var(--ease-out),
    background-color 200ms var(--ease-out);
}
.contact-form textarea { resize: vertical; min-height: 132px; line-height: 1.55; }
/* The placeholder carries the invitation now, so it has to be readable:
   --ink-3 clears 4.5:1 on the field, a muted grey did not. */
.contact-form ::placeholder { color: var(--ink-3); opacity: 1; }

.contact-form input:hover:not(:focus),
.contact-form textarea:hover:not(:focus) { border-color: #d4c7b2; }

/* The site-wide focus ring is a 3px offset outline in flame orange. Across a
   full width field that reads as a slab of colour, so these controls get a
   ring that hugs the border instead: the field lifts off the card, the
   border warms, and a soft halo replaces the hard outline. */
.contact-form input:focus,
.contact-form textarea:focus,
.contact-form input:focus-visible,
.contact-form textarea:focus-visible {
  outline: none;
  background: var(--surface);
  border-color: #b9714a;
  box-shadow:
    inset 0 1px 2px rgba(34, 27, 20, 0.03),
    0 0 0 3px rgba(201, 58, 18, 0.10),
    0 8px 18px -12px rgba(34, 27, 20, 0.45);
}
.contact-form input.invalid,
.contact-form textarea.invalid { border-color: var(--accent-deep); }

.field-error {
  margin: 0;
  color: var(--accent-deep);
  font-size: 0.88rem;
  font-weight: 700;
}

/* The honeypot. Off-screen rather than display:none, because some bots skip
   fields they can tell are hidden. */
.contact-form .hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact-actions { margin-top: 2px; }
.contact-actions .btn { width: 100%; }
.contact-actions .btn[disabled] { opacity: 0.55; cursor: default; }
.contact-actions .btn:active { transform: translateY(1px); }

.contact-status {
  margin: 16px 0 0;
  color: var(--ink-3);
  font-weight: 700;
  font-size: 0.94rem;
}
.contact-status:empty { margin: 0; }
.contact-status.ok { color: var(--ok); }
.contact-status.err { color: var(--accent-deep); }
.contact-status:focus-visible { outline-offset: 6px; }

/* .contact-form sets display:grid, which is an author rule and so outranks
   the user agent's [hidden] { display: none }. Without this the form stays
   on screen after a successful send. */
.contact-form[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .contact-form input,
  .contact-form textarea { transition: none; }
  .contact-actions .btn:active { transform: none; }
}

.toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 24px 0 8px;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-tile);
}
.toc a {
  padding: 6px 13px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  color: var(--ink-2);
  font-weight: 800;
  font-size: 0.9rem;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out);
}
.toc a:hover { background: #f6dcc4; color: var(--accent-dark); text-decoration: none; }

.callout {
  margin: 20px 0;
  padding: 18px 22px;
  background: #f6dcc4;
  border-radius: var(--r-tile);
  color: var(--accent-dark);
}
.callout p { color: inherit; margin: 0; }

.placeholder-tag {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--r-pill);
  background: #fff3cd;
  color: #7a5b00;
  font-weight: 800;
  font-size: 0.85em;
}

.legal-hero.notfound { text-align: center; padding-block: clamp(72px, 12vw, 140px); }
.legal-hero.notfound .ms-row { justify-content: center; margin-bottom: 28px; }
.legal-hero.notfound p { color: var(--ink-2); max-width: 40ch; margin-inline: auto; }
.legal-hero.notfound .btn { margin-top: 26px; }
