/* ============================================================
   FIELD NOTES — a walkable hand-drawn map portfolio
   paper + ink + one crimson accent; photographs carry all color
   ============================================================ */

:root {
  /* palette (OKLCH) */
  --paper:         oklch(0.972 0.002 250);
  --paper-warm:    oklch(0.955 0.004 250);
  --ink:           oklch(0.25 0.012 270);
  --ink-muted:     oklch(0.45 0.012 270);
  --pencil:        oklch(0.70 0.008 270);
  --crimson:       oklch(0.55 0.185 28);
  --crimson-deep:  oklch(0.47 0.17 28);

  /* type */
  --hand: "Kalam", "Bradley Hand", "Segoe Print", cursive;
  --type: "Special Elite", "American Typewriter", "Courier New", monospace;

  /* motion */
  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);
  --t-fast: 150ms;
  --t-med:  260ms;
  --t-slow: 480ms;

  /* space */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  /* surfaces that must flip with the day/night egg */
  --btn-shadow: oklch(0.25 0.012 270 / 0.85);
  --kbd-bg: oklch(1 0 0 / 0.55);
  --star: oklch(0.9 0.03 250);

  /* z scale (dialogs live in the top layer; this is for in-page chrome) */
  --z-hud: 10;
}

/* ---------- night: the sun doodle is the switch ---------- */
body.night {
  --paper:      oklch(0.225 0.018 265);
  --paper-warm: oklch(0.26 0.02 265);
  --ink:        oklch(0.93 0.006 250);
  --ink-muted:  oklch(0.72 0.011 258);
  --pencil:     oklch(0.5 0.014 262);
  --crimson:    oklch(0.68 0.155 30);
  --btn-shadow: oklch(0.08 0.014 265 / 0.9);
  --kbd-bg:     oklch(1 0 0 / 0.07);
}
body.night #grainRect { opacity: 0.5; }
body.night .dot { opacity: 0.09; }

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--type);
  font-size: 1.0625rem;
  line-height: 1.6;
  overflow: hidden;
  height: 100vh;
  height: 100dvh;
  /* the page never scrolls, so give the browser no reason to rubber-band
     or pull-to-refresh — a down-swipe in the runner is a sploot, not a reload */
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
}

main { height: 100vh; height: 100dvh; }

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

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }

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

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ---------- accessible nav / skip / noscript ---------- */

.skip-link {
  position: fixed; top: 10px; left: 10px; z-index: calc(var(--z-hud) + 1);
  background: var(--ink); color: var(--paper);
  font-family: var(--hand); font-size: 1rem;
  padding: 10px 16px; border-radius: 10px;
  transform: translateY(-64px);
  transition: transform var(--t-fast) var(--ease-out-quint);
}
.skip-link:focus { transform: translateY(0); }

.site-nav {
  position: fixed; inset: auto auto 0 0; z-index: var(--z-hud);
  transform: translateY(110%);
}
.site-nav:focus-within {
  transform: none;
  background: var(--paper);
  padding: var(--sp-4) var(--sp-5);
  border-radius: 0 12px 0 0;
  box-shadow: 0 -4px 24px oklch(0.25 0.012 270 / 0.22);
}
.site-nav ul { list-style: none; display: grid; gap: var(--sp-2); }
.site-nav a { color: var(--ink); font-family: var(--hand); font-size: 1.05rem; }

.noscript-note {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-hud);
  background: var(--paper); color: var(--ink);
  padding: var(--sp-4) var(--sp-5); text-align: center;
  border-bottom: 2px solid var(--ink);
}

/* ============================================================
   HUD — written onto the map, not floating above it
   ============================================================ */

.hud > * { position: fixed; z-index: var(--z-hud); }

.brand-chip {
  top: max(18px, env(safe-area-inset-top));
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand);
  line-height: 1.05;
  text-align: left;
  padding: 4px 6px 6px 2px;
  rotate: -1.2deg;
  transition: rotate var(--t-med) var(--ease-out-quint);
}
.brand-chip strong {
  display: block;
  font-size: clamp(1.3rem, 1.1rem + 1vw, 1.7rem);
  font-weight: 700;
  color: var(--ink);
}
.brand-chip span {
  display: block;
  font-size: clamp(1rem, 0.9rem + 0.5vw, 1.15rem);
  color: var(--crimson);
  margin-top: 2px;
}
.brand-chip::after {
  content: "";
  display: block;
  width: 62px; height: 4px;
  margin-top: 5px;
  background: var(--crimson);
  border-radius: 999px 2px 999px 3px;
  rotate: -0.8deg;
  transition: width var(--t-med) var(--ease-out-quint);
}
.brand-chip:hover { rotate: 0.2deg; }
.brand-chip:hover::after { width: 92px; }

/* clicking the name on the bare map re-inks it: the whole chip shakes,
   the tagline settles in fresh and the underline redraws itself with a
   little overshoot — then it wears off (.fade) back to the real line */
.brand-chip.reink { animation: brand-shake 0.5s ease; }
.brand-chip.reink span { animation: brand-ink 0.55s ease both; }
.brand-chip.reink::after { animation: brand-rule 0.6s var(--ease-out-quint); }

.brand-chip span { transition: opacity 0.4s ease; }
.brand-chip.fade span { opacity: 0; }

@keyframes brand-shake {
  0%   { translate: 0 0;      rotate: -1.2deg; }
  18%  { translate: -2px 1px; rotate: 1.6deg; }
  36%  { translate: 3px -1px; rotate: -2.4deg; }
  54%  { translate: -2px 0;   rotate: 1.2deg; }
  72%  { translate: 1px 1px;  rotate: -1.8deg; }
  100% { translate: 0 0;      rotate: -1.2deg; }
}

@keyframes brand-ink {
  0%   { opacity: 0; translate: 0 4px; }
  35%  { opacity: 0; translate: 0 4px; }
  100% { opacity: 1; translate: 0 0; }
}
@keyframes brand-rule {
  0%   { width: 0;     rotate: -0.8deg; }
  62%  { width: 98px;  rotate: 0.9deg; }
  100% { width: 62px;  rotate: -0.8deg; }
}

.hint-chip {
  left: max(20px, env(safe-area-inset-left));
  bottom: max(20px, env(safe-area-inset-bottom));
  color: var(--ink-muted);
  font-family: var(--type); font-size: 0.85rem;
  max-width: min(70vw, 430px);
  transition: opacity var(--t-slow) ease, translate var(--t-slow) var(--ease-out-quint);
}
.hint-chip span { display: inline-block; white-space: nowrap; margin-right: 10px; }
.hint-chip kbd {
  font-family: inherit;
  color: var(--ink);
  border: 1.5px solid var(--pencil);
  border-radius: 5px;
  padding: 0 4px;
  margin: 0 1px;
  background: var(--kbd-bg);
}
.hint-chip.hidden { opacity: 0; translate: 0 10px; pointer-events: none; }

/* the one real control on the map: drawn like a hand-inked button */
.compass-chip {
  right: max(18px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  display: flex; align-items: center; gap: 9px;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--hand); font-weight: 700; font-size: 1.1rem;
  padding: 9px 20px 10px 13px;
  border: 2.5px solid var(--ink);
  border-radius: 999px;
  box-shadow: 3px 3px 0 var(--btn-shadow);
  transition: translate var(--t-fast) var(--ease-out-quint), box-shadow var(--t-fast) ease;
}
.compass-chip svg { width: 30px; height: 30px; }
.compass-chip .needle { fill: var(--crimson); }
/* points at whatever you haven't visited yet, then settles back to north */
.needle-spin {
  transform-box: fill-box; transform-origin: 50% 50%;
  transition: rotate 700ms var(--ease-out-quint);
}
.compass-chip:hover { translate: -1px -1px; box-shadow: 5px 5px 0 var(--btn-shadow); }
.compass-chip:active { translate: 2px 2px; box-shadow: 1px 1px 0 var(--btn-shadow); }
.compass-chip:hover svg { animation: needle-wiggle 600ms var(--ease-out-quint); }
@keyframes needle-wiggle {
  0% { rotate: 0deg; } 30% { rotate: 14deg; } 65% { rotate: -8deg; } 100% { rotate: 0deg; }
}

/* ============================================================
   THE MAP
   ============================================================ */

.stage {
  position: relative;
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  background: var(--paper);
}
/* worn edges — suggests old paper without framing it as a page */
.stage::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  box-shadow: inset 0 0 clamp(70px, 14vw, 190px) oklch(0.25 0.012 270 / 0.06);
}

#world, #actors {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
#world {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
#actors { pointer-events: none; }

.paper-fill { fill: var(--paper); }
.dot { fill: var(--ink); opacity: 0.05; }

/* stars only exist after dark */
#starsRect { display: none; }
body.night #starsRect { display: block; }
.star { fill: var(--star); opacity: 0.55; }

/* the sun / moon toggle */
.egg { cursor: pointer; }
.egg-halo { fill: transparent; }
.egg:focus-visible { outline: none; }
.egg:focus-visible .egg-halo {
  fill: none; stroke: var(--crimson); stroke-width: 2.5; stroke-dasharray: 2 9; stroke-linecap: round;
}
.egg:hover { opacity: 1; }
.moon-only { display: none; }
body.night .sun-only { display: none; }
body.night .moon-only { display: block; }

/* the campfire catches the dark */
body.night .ember {
  stroke: oklch(0.75 0.16 62);
  filter: drop-shadow(0 0 5px oklch(0.7 0.17 55 / 0.75));
}

/* birds startle when you walk up on them, and actually flap away */
.wing {
  fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round;
}
.wl { transform-box: fill-box; transform-origin: 100% 100%; }
.wr { transform-box: fill-box; transform-origin: 0 100%; }

/* eleven beats, not infinite: the fly-off is 2.2 s, and after it they flapped
   on off-screen (inside a roughened group) for as long as you stayed near */
#decorBirds.scattered .wl { animation: flap-l 220ms ease-in-out 11; }
#decorBirds.scattered .wr { animation: flap-r 220ms ease-in-out 11; }
#decorBirds.scattered .b2 .wl,
#decorBirds.scattered .b2 .wr { animation-delay: 70ms; }
#decorBirds.scattered .b3 .wl,
#decorBirds.scattered .b3 .wr { animation-delay: 130ms; }
@keyframes flap-l {
  0%, 100% { transform: rotate(17deg); }
  50%      { transform: rotate(-21deg); }
}
@keyframes flap-r {
  0%, 100% { transform: rotate(-17deg); }
  50%      { transform: rotate(21deg); }
}

/* --fly-x / --fly-y are measured in JS so they always clear the viewport */
#decorBirds.scattered { animation: fly-off 2200ms cubic-bezier(0.4, 0, 0.75, 0.5) forwards; }
@keyframes fly-off {
  0%   { translate: 0 0; }
  14%  { translate: -4px -16px; }        /* the startled jump */
  100% { translate: var(--fly-x, 500px) var(--fly-y, -700px); }
}

.pencil { color: var(--pencil); }

.pencil-route {
  fill: none;
  stroke: var(--pencil);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-dasharray: 0.1 11;
  opacity: 0.85;
}

/* Where you've walked, in your own colour and in real dashes — the route
   between the landmarks is grey dots, and the two were reading as the same
   line. */
#trail {
  fill: none;
  stroke: var(--crimson);
  stroke-width: 2.6;
  stroke-linecap: butt;
  stroke-dasharray: 9 7;
  opacity: 0.42;
}
/* the same 0.42 against dark paper is barely there */
body.night #trail { opacity: 0.62; }

.ink-decor { color: var(--ink); opacity: 0.8; }
.ink-decor .ink,
#startNote path {
  fill: none; stroke: currentColor;
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
.ink-decor .ink.thin { stroke-width: 2.2; }
.crimson-fill { fill: var(--crimson); }

/* paper-colored knockout so hand text stays legible over dots and route lines */
.knock {
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 7;
  stroke-linejoin: round;
}

.hand-sm  { font-family: var(--hand); font-size: 17px; fill: var(--ink); }
.hand-sig { font-family: var(--hand); font-size: 22px; font-weight: 700; fill: var(--ink); opacity: 0.8; }
.hand-note{ font-family: var(--hand); font-size: 19px; fill: var(--crimson); }

#startNote { color: var(--crimson); transition: opacity 700ms ease; }
#startNote path { stroke-width: 2.6; }
#startNote.gone { opacity: 0; }

/* the little × where you clicked */
#clickMark { opacity: 0; transform-box: fill-box; transform-origin: center; }
#clickMark path { fill: none; stroke: var(--crimson); stroke-width: 3; stroke-linecap: round; }
#clickMark.ping { animation: ping 560ms var(--ease-out-quint); }
@keyframes ping {
  0%   { opacity: 0.95; scale: 0.5; }
  60%  { opacity: 0.85; scale: 1.06; }
  100% { opacity: 0;    scale: 1; }
}

/* ---------- landmarks ---------- */

.landmark { cursor: pointer; color: var(--ink); }
.landmark .lm-halo { fill: transparent; }

.landmark .ink {
  fill: none; stroke: currentColor;
  stroke-width: 4.4; stroke-linecap: round; stroke-linejoin: round;
}
.landmark .ink.thin { stroke-width: 3; }
.landmark .ink.hairline { stroke-width: 1.9; fill: none; }
.landmark .smoke { opacity: 0.55; }

.landmark .lm-art { transition: translate var(--t-med) var(--ease-out-quint); }

.landmark .lm-label {
  font-family: var(--hand); font-weight: 700; font-size: 32px;
  fill: var(--ink); text-anchor: middle;
  transition: fill var(--t-fast) ease;
}

.landmark .lm-underline {
  fill: none; stroke: var(--crimson); stroke-width: 3.4; stroke-linecap: round;
  stroke-dasharray: 150; stroke-dashoffset: 150;
  transition: stroke-dashoffset 360ms var(--ease-out-quint);
}

.landmark:hover .lm-art,
.landmark:focus-visible .lm-art,
.landmark.in-range .lm-art { translate: 0 -5px; }

.landmark:hover .lm-label,
.landmark:focus-visible .lm-label,
.landmark.in-range .lm-label { fill: var(--crimson); }

.landmark:hover .lm-underline,
.landmark:focus-visible .lm-underline,
.landmark.in-range .lm-underline { stroke-dashoffset: 0; }

.landmark:focus-visible { outline: none; }
.landmark:focus-visible .lm-halo {
  fill: none; stroke: var(--crimson); stroke-width: 3; stroke-dasharray: 2 10; stroke-linecap: round;
}

.enter-tip {
  opacity: 0; scale: 0.7;
  transform-box: fill-box; transform-origin: center;
  transition: opacity var(--t-fast) ease, scale var(--t-med) var(--ease-out-quint);
  pointer-events: none;
}
.enter-tip rect { fill: var(--ink); }
.enter-tip text { font-family: var(--hand); font-weight: 700; font-size: 17px; fill: var(--paper); text-anchor: middle; }
.landmark.in-range .enter-tip { opacity: 1; scale: 1; }
/* just-closed landmark: the prompt goes quiet until you step away */
.landmark.suppressed .enter-tip { opacity: 0; scale: 0.7; }

/* visited stamps */
.stamp { opacity: 0; scale: 0.3; rotate: 14deg; transform-box: fill-box; transform-origin: center; color: var(--crimson); }
.stamp circle { fill: var(--paper); stroke: currentColor; stroke-width: 2.6; stroke-dasharray: 4 5; }
.stamp text { font-family: var(--type); font-size: 13px; fill: currentColor; text-anchor: middle; text-transform: uppercase; letter-spacing: 0.08em; }
.stamp.stamped { opacity: 0.85; scale: 1; rotate: 0deg; transition: opacity var(--t-med) ease, scale 340ms var(--ease-out-quint), rotate 340ms var(--ease-out-quint); }

/* ---------- hiker ---------- */

#hiker { pointer-events: none; }
.hiker-shadow { fill: var(--ink); opacity: 0.14; }
.hiker-line, .leg path {
  fill: none; stroke: var(--ink); stroke-width: 2.7; stroke-linecap: round; stroke-linejoin: round;
}
.hiker-line.thin { stroke-width: 2; }
.hiker-head { fill: var(--paper); stroke: var(--ink); stroke-width: 2.7; }
.hiker-pack { fill: var(--crimson); stroke: var(--ink); stroke-width: 2; }

/* legs rotate from the hip: fill-box corners land exactly on (0,-17) */
.leg { transform-box: fill-box; }
.leg-l { transform-origin: 100% 0; }
.leg-r { transform-origin: 0 0; }
#hiker.walking .leg-l { animation: stride 360ms ease-in-out infinite; }
#hiker.walking .leg-r { animation: stride 360ms ease-in-out infinite reverse; }
@keyframes stride {
  0%, 100% { transform: rotate(24deg); }
  50%      { transform: rotate(-24deg); }
}
#hiker.walking #hikerInner { animation: jog 360ms ease-in-out infinite; }
@keyframes jog {
  0%, 100% { translate: 0 0; }
  25%      { translate: 0 -1.6px; }
  75%      { translate: 0 -1.6px; }
}

/* ---------- the dog ---------- */

#dog { pointer-events: auto; cursor: pointer; }
.dog-hit { fill: transparent; }        /* generous tap target around a small drawing */
.dog-shadow { fill: var(--ink); opacity: 0.12; }

/* paper fill keeps the far legs hidden where the body overlaps them */
.dog-body {
  fill: var(--paper); stroke: var(--ink);
  stroke-width: 1.9; stroke-linejoin: round;
}
.dog-mantle { fill: var(--ink); }
.dog-tail-shape { fill: var(--ink); }
.dog-eye { fill: var(--ink); }
.dog-collar { fill: none; stroke: var(--crimson); stroke-width: 1.8; stroke-linecap: round; }
.dog-leg path { fill: none; stroke: var(--ink); stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round; }
.dog-leg-ff path, .dog-leg-bf path { opacity: 0.45; }   /* far side sits back */

#dog:focus-visible { outline: none; }
#dog:focus-visible .dog-hit {
  fill: none; stroke: var(--crimson); stroke-width: 2.5; stroke-dasharray: 2 8; stroke-linecap: round;
}

/* every moving part pivots from its top-right point, which is where each
   path starts — hip, shoulder, tail base */
.dog-leg, .dog-tail { transform-box: fill-box; transform-origin: 100% 0; }

/* diagonal trot: near-front swings with far-back */
#dog.walking .dog-leg-fn, #dog.walking .dog-leg-bf { animation: dog-stride 300ms ease-in-out infinite; }
#dog.walking .dog-leg-ff, #dog.walking .dog-leg-bn { animation: dog-stride 300ms ease-in-out infinite reverse; }
@keyframes dog-stride {
  0%, 100% { transform: rotate(21deg); }
  50%      { transform: rotate(-21deg); }
}
#dog.walking.running .dog-leg-fn, #dog.walking.running .dog-leg-bf,
#dog.walking.running .dog-leg-ff, #dog.walking.running .dog-leg-bn { animation-duration: 225ms; }
#dog.walking.running #dogInner { animation-duration: 225ms; }

#dog.walking #dogInner { animation: dog-bob 300ms ease-in-out infinite; }
@keyframes dog-bob {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -1.1px; }
}

.dog-tail { transform: rotate(0deg); }
#dog.walking .dog-tail { animation: tail-sway 600ms ease-in-out infinite; }
@keyframes tail-sway {
  0%, 100% { transform: rotate(-9deg); }
  50%      { transform: rotate(9deg); }
}
#dog.idle .dog-tail { animation: tail-wag 460ms ease-in-out infinite; }
@keyframes tail-wag {
  0%, 100% { transform: rotate(-16deg); }
  50%      { transform: rotate(20deg); }
}
/* petted: the whole point of having a dog */
#dog.happy .dog-tail { animation: tail-wag 150ms ease-in-out infinite; }
#dog.happy #dogInner { animation: dog-hop 400ms var(--ease-out-quint) 2; }
@keyframes dog-hop {
  0%, 100% { translate: 0 0; }
  35%      { translate: 0 -4px; }
}

#dogSpeak { opacity: 0; pointer-events: none; }
#dogSpeak.say { animation: say-pop 1400ms var(--ease-out-quint); }
.dog-say { font-family: var(--hand); font-weight: 700; font-size: 17px; fill: var(--crimson); }
@keyframes say-pop {
  0%   { opacity: 0; translate: 0 4px;   scale: 0.6; }
  18%  { opacity: 1; translate: 0 -6px;  scale: 1; }
  70%  { opacity: 1; translate: 0 -12px; }
  100% { opacity: 0; translate: 0 -20px; }
}

/* ---------- fetch ---------- */

#stickWrap { display: none; }
#stickWrap.live { display: block; }
#stickShadow { fill: var(--ink); opacity: 0.13; }
.stick-art {
  fill: none; stroke: var(--ink);
  stroke-width: 2.4; stroke-linecap: round;
}

.arm-r { transform-box: fill-box; transform-origin: 0 0; }
#hiker.throwing .arm-r { animation: throw-arm 560ms var(--ease-out-quint); }
@keyframes throw-arm {
  0%   { transform: rotate(0deg); }
  28%  { transform: rotate(-74deg); }
  52%  { transform: rotate(38deg); }
  100% { transform: rotate(0deg); }
}

/* ---------- making camp (15s idle) ---------- */

#hikerSit, #dogCurl, #zzz { display: none; }
body.camping #hikerWalk, body.camping #dogWalk { display: none; }
body.camping #hikerSit, body.camping #dogCurl, body.camping #zzz { display: block; }

.dog-stroke {
  fill: none; stroke: var(--ink);
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.dog-eye-shut {
  fill: none; stroke: var(--ink); stroke-width: 1.4;
  stroke-dasharray: 2.4 20; stroke-dashoffset: 1.2;   /* a closed, contented eye */
}

.z {
  font-family: var(--hand); font-weight: 700; font-size: 11px;
  fill: var(--ink-muted);
}
.z1 { animation: drift 3s ease-in-out infinite; }
.z2 { animation: drift 3s ease-in-out 0.55s infinite; }
.z3 { animation: drift 3s ease-in-out 1.1s infinite; }
@keyframes drift {
  0%   { opacity: 0;   translate: 0 3px;   scale: 0.8; }
  30%  { opacity: 0.9; }
  100% { opacity: 0;   translate: 5px -12px; scale: 1.15; }
}

/* ---------- field trip complete ---------- */

.done-stamp {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
  z-index: calc(var(--z-hud) + 2);
  color: var(--crimson);
  opacity: 0;
  visibility: hidden;
}
.done-stamp svg { width: min(46vw, 300px); height: auto; }
.done-stamp text {
  font-family: var(--type); font-size: 27px; letter-spacing: 0.04em;
  fill: currentColor;
}
.done-stamp .ds-sub { font-family: var(--hand); font-size: 22px; letter-spacing: 0; opacity: 0.85; }
.done-stamp.thump {
  visibility: visible;
  animation: stamp-thump 2900ms var(--ease-out-quint) forwards;
}
@keyframes stamp-thump {
  0%   { opacity: 0; scale: 2.4; rotate: -16deg; }
  12%  { opacity: 0.92; scale: 1; rotate: -7deg; }
  16%  { scale: 1.05; }
  22%  { scale: 1; }
  78%  { opacity: 0.92; scale: 1; rotate: -7deg; }
  100% { opacity: 0; scale: 1.06; rotate: -7deg; }
}

/* ---------- entrance ---------- */

.stage { opacity: 0; scale: 0.992; transition: opacity 620ms ease, scale 700ms var(--ease-out-quint); }
body.loaded .stage { opacity: 1; scale: 1; }

#hikerInner { scale: 0; transform-box: fill-box; transform-origin: 50% 100%; transition: scale 380ms var(--ease-out-quint) 300ms; }
body.loaded #hikerInner { scale: 1; }

#dogInner { scale: 0; transform-box: fill-box; transform-origin: 50% 100%; transition: scale 380ms var(--ease-out-quint) 430ms; }
body.loaded #dogInner { scale: 1; }   /* trots in just after the hiker */

.hud > * { opacity: 0; translate: 0 10px; }
body.loaded .hud > * { opacity: 1; translate: 0 0; }
body.loaded .brand-chip   { transition: opacity var(--t-slow) ease 250ms, translate var(--t-slow) var(--ease-out-quint) 250ms, rotate var(--t-med) var(--ease-out-quint); }
body.loaded .hint-chip    { transition: opacity var(--t-slow) ease 420ms, translate var(--t-slow) var(--ease-out-quint) 420ms; }
body.loaded .compass-chip { transition: opacity var(--t-slow) ease 560ms, translate var(--t-slow) var(--ease-out-quint) 560ms, box-shadow var(--t-fast) ease; }
body.loaded .hint-chip.hidden {
  opacity: 0; translate: 0 10px;
  transition: opacity var(--t-slow) ease, translate var(--t-slow) var(--ease-out-quint);
}

/* ============================================================
   OVERLAY PAGES (dialogs)
   ============================================================ */

dialog { border: none; padding: 0; color: var(--ink); }
dialog::backdrop { background: oklch(0.16 0.01 285 / 0.5); backdrop-filter: blur(3px); }
/* the full-screen ones cover the map entirely: a blur nobody can see, still
   being recomputed under them every frame the map animates */
.gallery-dlg::backdrop, .lightbox::backdrop { backdrop-filter: none; }

.page-dlg[open] { animation: dlg-in var(--t-med) var(--ease-out-quint); }
@keyframes dlg-in {
  from { opacity: 0; transform: scale(0.965) rotate(-0.6deg); }
  to   { opacity: 1; transform: none; }
}

.dlg-close {
  position: absolute;
  top: 14px; right: 14px;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: inherit;
  transition: rotate var(--t-med) var(--ease-out-quint), background var(--t-fast) ease;
  z-index: 2;
}
.dlg-close svg { width: 24px; height: 24px; }
.dlg-close svg path { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; }
.dlg-close:hover { rotate: 90deg; background: oklch(0.5 0.01 270 / 0.14); }

/* ---- paper sheets (about / contact / contents) ---- */

.paper-dlg {
  background: transparent;
  max-width: none; max-height: none;
  width: 100vw; height: 100dvh;
  display: grid;
  overflow: auto;
  padding: clamp(12px, 4vw, 40px);
}
.paper-dlg:not([open]) { display: none; }

.sheet {
  margin: auto;   /* centers, but stays scrollable when taller than the screen */
  position: relative;
  background:
    radial-gradient(oklch(0.25 0.012 270 / 0.05) 1.1px, transparent 1.2px) 0 0 / 26px 26px,
    var(--paper);
  border-radius: 14px 10px 16px 9px / 10px 15px 9px 14px;
  box-shadow: 0 24px 70px oklch(0 0 0 / 0.45);
  rotate: -0.5deg;
  width: min(660px, 94vw);
  padding: clamp(28px, 6vw, 56px);
}

.sheet h2 {
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.9rem, 5vw, 2.5rem);
  line-height: 1.15;
  margin-bottom: var(--sp-5);
  text-wrap: balance;
}
.sheet h2::after {
  content: "";
  display: block;
  width: 96px; height: 5px;
  margin-top: 6px;
  background: var(--crimson);
  border-radius: 999px 3px 999px 4px;
  rotate: -1deg;
}

.sheet p { max-width: 58ch; margin-bottom: var(--sp-4); }
.sheet .sig { font-family: var(--hand); font-weight: 700; font-size: 1.5rem; color: var(--crimson); rotate: -2deg; width: max-content; margin-top: var(--sp-5); }

.scribble { list-style: none; margin: var(--sp-5) 0; display: grid; gap: var(--sp-2); }
.scribble li { padding-left: 28px; position: relative; }
.scribble li::before {
  content: "—"; position: absolute; left: 0;
  color: var(--crimson); font-family: var(--hand); font-weight: 700;
}

.polaroid {
  float: right;
  width: min(215px, 42%);
  margin: 4px 0 var(--sp-4) var(--sp-5);
  background: white;
  padding: 9px 9px 30px;
  box-shadow: 0 8px 22px oklch(0 0 0 / 0.25);
  rotate: 2.2deg;
  transition: rotate var(--t-med) var(--ease-out-quint), scale var(--t-med) var(--ease-out-quint);
}
.polaroid:hover { rotate: 0.6deg; scale: 1.02; }
.polaroid img { aspect-ratio: 1 / 1; object-fit: cover; }
.polaroid figcaption { font-family: var(--hand); text-align: center; font-size: 0.95rem; margin-top: 7px; color: var(--ink-muted); }

/* ---- postcard (contact) ---- */

.postcard { display: grid; grid-template-columns: 1.15fr 1fr; gap: clamp(20px, 4vw, 40px); width: min(820px, 94vw); }
.pc-write > p { margin-bottom: var(--sp-4); }

.btn-mail {
  display: inline-block;
  background: var(--crimson);
  color: var(--paper);
  font-family: var(--hand); font-weight: 700; font-size: 1.15rem;
  text-decoration: none;
  padding: 12px 26px 14px;
  border-radius: 6px 16px 7px 14px / 14px 7px 16px 6px;
  box-shadow: 0 4px 0 var(--crimson-deep);
  transition: translate var(--t-fast) var(--ease-out-quint), box-shadow var(--t-fast) ease;
}
.btn-mail:hover { translate: 0 -2px; box-shadow: 0 6px 0 var(--crimson-deep); }
.btn-mail:active { translate: 0 2px; box-shadow: 0 2px 0 var(--crimson-deep); }

/* ---- contact form ---- */

#contactForm .field { margin-bottom: var(--sp-3); }
#contactForm label {
  display: block;
  font-family: var(--hand); font-size: 1.05rem;
  color: var(--ink-muted);
  margin-bottom: 3px;
}
#contactForm input,
#contactForm textarea {
  width: 100%;
  font: inherit; font-size: 0.95rem;
  color: var(--ink);
  background: oklch(1 0 0 / 0.5);
  border: none;
  border-bottom: 2px solid var(--pencil);
  border-radius: 2px 2px 0 0;
  padding: 7px 9px;
  transition: border-color var(--t-fast) ease, background var(--t-fast) ease;
}
body.night #contactForm input,
body.night #contactForm textarea { background: oklch(1 0 0 / 0.07); }
#contactForm textarea { resize: vertical; min-height: 84px; }
#contactForm input:focus,
#contactForm textarea:focus {
  outline: none;
  border-bottom-color: var(--crimson);
  background: oklch(1 0 0 / 0.75);
}
body.night #contactForm input:focus,
body.night #contactForm textarea:focus { background: oklch(1 0 0 / 0.12); }
#contactForm .btn-mail { margin-top: var(--sp-2); border: none; cursor: pointer; }
#contactForm .btn-mail:disabled { opacity: 0.6; cursor: progress; }

.form-status { font-size: 0.9rem; margin-top: var(--sp-3); min-height: 1.2em; color: var(--ink-muted); }
.form-status.good { color: var(--crimson); font-family: var(--hand); font-size: 1.05rem; }
.form-status.bad  { color: var(--crimson); }

/* ---- email reveal ---- */

.reveal { margin-top: auto; }
.btn-reveal {
  font-family: var(--hand); font-weight: 700; font-size: 1.05rem;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 7px 18px;
  box-shadow: 2px 2px 0 var(--btn-shadow);
  transition: translate var(--t-fast) var(--ease-out-quint), box-shadow var(--t-fast) ease;
}
.btn-reveal:hover { translate: -1px -1px; box-shadow: 4px 4px 0 var(--btn-shadow); }
.btn-reveal:active { translate: 1px 1px; box-shadow: 1px 1px 0 var(--btn-shadow); }

.captcha label { display: block; font-size: 0.85rem; color: var(--ink-muted); margin-bottom: 5px; }
.captcha-row { display: flex; gap: 7px; align-items: center; }
.captcha-row input {
  font: inherit; font-size: 0.95rem; width: 4.5em;
  color: var(--ink);
  background: oklch(1 0 0 / 0.5);
  border: none; border-bottom: 2px solid var(--pencil);
  padding: 5px 7px;
}
body.night .captcha-row input { background: oklch(1 0 0 / 0.07); }
.captcha-row input:focus { outline: none; border-bottom-color: var(--crimson); }
.captcha-row button {
  font-family: var(--hand); font-weight: 700;
  color: var(--paper); background: var(--ink);
  border-radius: 999px; padding: 5px 14px;
}
.captcha-error { color: var(--crimson); font-size: 0.85rem; margin-top: 5px; }
.revealed a {
  font-family: var(--hand); font-weight: 700; font-size: 1.1rem;
  color: var(--crimson);
  word-break: break-all;
}

.pc-side {
  border-left: 2px dashed var(--pencil);
  padding-left: clamp(20px, 3vw, 34px);
  display: flex; flex-direction: column; gap: var(--sp-6);
}
.pc-stamp { color: var(--crimson); width: 74px; align-self: flex-end; rotate: 3deg; }
.addr { display: grid; gap: 14px; font-family: var(--hand); font-size: 1.1rem; color: var(--ink-muted); rotate: -0.6deg; }
.addr span { border-bottom: 2px solid var(--pencil); padding-bottom: 4px; }
.addr .addr-name { color: var(--ink); font-weight: 700; }

/* ---- contents (fast travel) ---- */

.contents-dlg .sheet { width: min(560px, 94vw); }

#contentsList { list-style: none; display: grid; gap: 6px; margin: var(--sp-4) 0 var(--sp-5); }
#contentsList button {
  display: flex; align-items: baseline; gap: 12px;
  width: 100%;
  padding: 12px 8px;
  border-radius: 8px;
  text-align: left;
  transition: translate var(--t-fast) var(--ease-out-quint), background var(--t-fast) ease;
}
#contentsList button:hover { translate: 6px 0; background: oklch(0.5 0.01 270 / 0.08); }
#contentsList .c-name { font-family: var(--hand); font-weight: 700; font-size: 1.35rem; transition: color var(--t-fast) ease; }
#contentsList button:hover .c-name { color: var(--crimson); }
#contentsList .c-count { color: var(--ink-muted); font-size: 0.8rem; white-space: nowrap; }
#contentsList .c-leader { flex: 1; border-bottom: 2.5px dotted var(--pencil); translate: 0 -6px; min-width: 24px; }
#contentsList .c-seen {
  font-family: var(--type); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--crimson);
  border: 1.5px dashed var(--crimson);
  border-radius: 999px;
  padding: 2px 9px;
  rotate: -4deg;
  white-space: nowrap;
}
.contents-note { color: var(--ink-muted); font-size: 0.95rem; }
.contents-tip { margin-top: var(--sp-3); }
.colophon {
  margin-top: var(--sp-5);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--pencil);
  color: var(--ink-muted);
  font-size: 0.78rem;
}
.contents-tip kbd {
  font-family: var(--type); color: var(--ink);
  border: 1.5px solid var(--pencil); border-radius: 5px;
  padding: 1px 6px; margin: 0 2px;
}

/* ============================================================
   GALLERIES — the map's own paper, after dark
   ============================================================ */

/* Always the night paper, whatever the map is doing: dark suits the
   photographs, and it's still the same notebook — same dot grid, grain,
   pen and lettering as the map, with the lights down. */
.gallery-dlg, .lightbox, .gal-return {
  --paper:      oklch(0.225 0.018 265);
  --paper-warm: oklch(0.26 0.02 265);
  --paper-deep: oklch(0.185 0.017 265);
  --ink:        oklch(0.93 0.006 250);
  --ink-muted:  oklch(0.72 0.011 258);
  --pencil:     oklch(0.5 0.014 262);
  --crimson:    oklch(0.68 0.155 30);
  --gutter: clamp(18px, 4.5vw, 72px);
  --gal-max: 1640px;
  --bar-h: 64px;
  color: var(--ink);
}

.gallery-dlg {
  width: 100vw; height: 100dvh;
  max-width: none; max-height: none;
  overflow-x: hidden; overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-color: var(--pencil) transparent;
  scrollbar-width: thin;
  background-color: var(--paper);
  /* the grain is a tile game.js draws once (--grain); the dots are the map's */
  background-image:
    var(--grain, none),
    radial-gradient(circle at 15px 15px, oklch(0.93 0.006 250 / 0.085) 1.15px, transparent 1.7px);
  background-size: 220px 220px, 30px 30px;
  background-attachment: local;
}
.gallery-dlg:not([open]) { display: none; }
.defs-only { position: absolute; }

/* ---------- the way in from the map ----------
   game.js (playIn / playOut): the section's own scene plays — for Peaks &
   Wilds, ridgelines rising — and then the page fades up around it. */
.page-dlg.fx[open] { animation: none; }
.gallery-dlg.fx::backdrop { background: transparent; }
.gallery-dlg.dusk { background: transparent; }

/* the night paper as its own layer, so a scene can fade it in behind itself */
.gal-night {
  display: none; opacity: 0;
  position: fixed; inset: 0;
  background-color: var(--paper);
  background-image:
    var(--grain, none),
    radial-gradient(circle at 15px 15px, oklch(0.93 0.006 250 / 0.085) 1.15px, transparent 1.7px);
  background-size: 220px 220px, 30px 30px;
}
.gallery-dlg.dusk .gal-night, .gal-return .gal-night { display: block; }
.gal-return { position: fixed; inset: 0; z-index: 40; pointer-events: none; }

.scene { position: fixed; inset: 0; overflow: hidden; pointer-events: none; }
.scene > svg { position: absolute; left: 0; top: 0; overflow: visible; will-change: transform; }
.scene .sky-star { fill: var(--ink); opacity: 0.7; }
/* the ski game's ranges, at night and a little bolder than behind the run */
.scene .sk-alp-far  { --k: 0.5; }
.scene .sk-alp-mid  { --k: 0.72; }
.scene .sk-alp-near { --k: 0.9; }
/* each peak's paper hides the lines of the one behind, as on the landmark */
.ridge { fill: var(--paper); stroke: var(--ink); stroke-linecap: round; stroke-linejoin: round; }
.ridge-ground { fill: var(--paper); }
.ridge-mark { fill: none; stroke: var(--ink); stroke-linecap: round; stroke-linejoin: round; }
.ridge-trees { fill: var(--paper); }

/* Journeys: postcards of the first photos, and the landmark's plane */
.postcard-fly {
  position: absolute;
  translate: -50% -50%;
  padding: 7px 7px 20px;
  background: oklch(0.95 0.004 250);
  border-radius: 2px;
  box-shadow: 0 18px 40px -16px oklch(0.05 0.02 265 / 0.9);
  will-change: transform;
}
.postcard-fly img { display: block; width: 100%; height: auto; object-fit: cover; background: oklch(0.85 0.005 250); }
.plane-fly {
  width: clamp(150px, 17vw, 240px); height: clamp(150px, 17vw, 240px);
  offset-rotate: auto 90deg; offset-anchor: center;
  color: var(--ink);
}
.plane-fly .ink { fill: var(--paper); }
.trail-dots { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; stroke-dasharray: 0.1 13; opacity: 0.65; }
.trail-mask { fill: none; stroke: #fff; stroke-width: 12; stroke-dasharray: 1; stroke-dashoffset: 1; }

/* Good Company: the viewfinder */
.vf-corner { width: 70px; height: 70px; overflow: visible; }
.vf-corner path, .vf-focus circle, .vf-focus path {
  fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
.scene > .vf-focus { left: 50%; top: 50%; width: 120px; height: 120px; margin: -60px 0 0 -60px; }
.vf-focus circle { stroke-width: 2.2; stroke-dasharray: 3 7; }
.vf-focus .vf-lock { fill: var(--crimson); stroke: none; }
.vf-read {
  position: absolute; left: 0; right: 0; bottom: max(30px, 7vh);
  text-align: center; font-family: var(--type); font-size: 0.9rem; letter-spacing: 0.1em;
  color: var(--ink);
}
.vf-flash { position: absolute; inset: 0; background: oklch(0.99 0.01 95); opacity: 0; }
.vf-subject {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  height: min(58vh, 520px); max-width: 70vw; object-fit: cover;
  box-shadow: 0 30px 70px -24px oklch(0.05 0.02 265 / 0.9);
}

/* The Whole Lot: pages of the notebook, in the map's own daylight paper */
.page-fly {
  position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  width: min(380px, 68vw); height: min(500px, 64vh);
  display: grid; place-items: center;
  background:
    radial-gradient(oklch(0.25 0.012 270 / 0.05) 1.1px, transparent 1.2px) 0 0 / 26px 26px,
    oklch(0.972 0.002 250);
  color: oklch(0.25 0.012 270);
  --crimson: oklch(0.55 0.185 28);
  border-radius: 14px 10px 16px 9px / 10px 15px 9px 14px;
  box-shadow: 0 30px 70px -20px oklch(0.05 0.02 265 / 0.9);
  will-change: transform;
}
.page-fly svg { width: 64%; overflow: visible; }
.page-fly .page-no { position: absolute; right: 20px; bottom: 14px; font-family: var(--type); font-size: 0.85rem; opacity: 0.7; }
.needle-fly { transform-box: fill-box; transform-origin: 50% 50%; }

/* About me: smoke and sparks off the campfire */
/* soft puffs of smoke: a blurred-edge disc each, which costs nothing to move */
.puff-fly {
  position: absolute; border-radius: 50%;
  background: radial-gradient(closest-side, oklch(0.98 0.004 250 / 0.85), oklch(0.98 0.004 250 / 0.35) 55%, transparent);
  will-change: transform, opacity;
}
.spark-fly { fill: oklch(0.78 0.16 60); }

/* Say hello: the envelope off the signpost */
/* both halves sit in the same place; they scale about the envelope's middle */
.scene > .env-fly {
  position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  width: min(300px, 70vw); overflow: visible;
  transform-origin: 50% 69%;
}
.env-fly path { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.env-fly .env-pocket { fill: var(--paper); }
.env-fly .env-inside { fill: var(--paper-warm); }
.env-fly .env-flap { transform-box: fill-box; transform-origin: 50% 0; }
.env-fly .env-flap path, .env-fly .env-open { fill: var(--paper); }
.env-fly .env-open { transform-box: fill-box; transform-origin: 50% 100%; }
.env-fly .env-seal { fill: var(--crimson); stroke: none; }

/* while the drawing is carried in, the rest of the page waits */
.gal-bar, .gal-words, .gal-grid, .gal-end { transition: opacity 500ms ease; }
.gal-open::before { transition: opacity 700ms ease; }
.gallery-dlg.arriving :is(.gal-bar, .gal-words, .gal-grid, .gal-end),
.gallery-dlg.arriving .gal-open::before { opacity: 0; }
.gallery-dlg.arriving .gal-art { opacity: 0; }

/* ---------- the pen ----------
   The map's three line weights. --k converts them into each drawing's own
   units, so a drawing shown bigger or smaller is still drawn with one pen. */
.gal-pen .ink {
  fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round;
  stroke-width: calc(4.4 * var(--k, 1));
}
.gal-pen .ink.thin     { stroke-width: calc(3 * var(--k, 1)); }
.gal-pen .ink.hairline { stroke-width: calc(1.9 * var(--k, 1)); }
.gal-pen .smoke { opacity: 0.55; }
.gal-pen .crimson-fill { fill: var(--crimson); }
/* small marks: arrows, rules, underlines */
.mark { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }

/* drawn in, stroke by stroke (game.js sets pathLength and the delays) */
.gallery-dlg.shown .draw .ink:not([stroke-dasharray]) {
  stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: pen-draw 900ms var(--ease-out-quint) var(--d, 0ms) forwards;
}
@keyframes pen-draw { to { stroke-dashoffset: 0; } }

/* ---------- the bar ---------- */
.gal-bar {
  position: sticky; top: 0; z-index: 3;
  height: var(--bar-h);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 0 var(--gutter);
}
.gal-bar::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--paper);
  opacity: 0; transition: opacity 420ms ease;
}
.gal-bar.solid::before { opacity: 0.97; }
.gal-rule {
  position: absolute; left: var(--gutter); bottom: -4px;
  width: calc(100% - 2 * var(--gutter)); height: 8px;
  overflow: visible; opacity: 0.55;
}
.gal-rule path {
  stroke-width: 1.9;
  stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0;
  transition: stroke-dashoffset 900ms var(--ease-out-quint), opacity 0s 900ms;
}
.gal-bar.solid .gal-rule path {
  stroke-dashoffset: 0; opacity: 1;
  transition: stroke-dashoffset 900ms var(--ease-out-quint);
}

.gal-back {
  justify-self: start;
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--hand); font-weight: 700; font-size: 1.15rem;
  white-space: nowrap;
  padding: 8px 4px;
  transition: color var(--t-fast) ease;
}
.gal-back svg { width: 34px; height: 20px; overflow: visible; transition: translate var(--t-med) var(--ease-out-quint); }
.gal-back:hover { color: var(--crimson); }
.gal-back:hover svg { translate: -4px 0; }

.gal-bar-title {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--hand); font-weight: 700; font-size: 1.2rem;
  white-space: nowrap;
  opacity: 0; translate: 0 6px;
  transition: opacity 320ms ease, translate 420ms var(--ease-out-quint);
}
.gal-bar.solid .gal-bar-title { opacity: 1; translate: 0 0; }
/* the section's own landmark, shrunk to an icon */
.gal-icon { width: 36px; height: 26px; margin-top: -3px; overflow: visible; }

.gal-bar-meta { justify-self: end; display: grid; font-size: 0.85rem; color: var(--ink-muted); }
.gal-bar-meta > span { grid-area: 1 / 1; text-align: right; white-space: nowrap; transition: opacity 320ms ease; }
.gal-bar-meta .m-prog { opacity: 0; }
.gal-bar.solid .m-page { opacity: 0; }
.gal-bar.solid .m-prog { opacity: 1; }

/* ---------- the opening ---------- */
.gal-open {
  position: relative;
  max-width: var(--gal-max); margin: 0 auto;
  padding: clamp(12px, 3vh, 28px) var(--gutter) clamp(30px, 5vh, 52px);
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  align-items: center; gap: clamp(24px, 4vw, 64px);
}
/* the night map's stars, only up where the sky is */
.gal-open::before {
  content: ""; position: absolute; z-index: -1;
  top: calc(-1 * var(--bar-h)); bottom: 0; left: calc(50% - 50vw); right: calc(50% - 50vw);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='197' height='173'%3E%3Cg fill='%23d7e0f0' fill-opacity='.55'%3E%3Ccircle cx='24' cy='38' r='1.4'/%3E%3Ccircle cx='121' cy='17' r='1'/%3E%3Ccircle cx='163' cy='97' r='1.7'/%3E%3Ccircle cx='71' cy='121' r='1.1'/%3E%3Ccircle cx='14' cy='151' r='.9'/%3E%3Ccircle cx='99' cy='70' r='.8'/%3E%3Ccircle cx='186' cy='140' r='1.2'/%3E%3Ccircle cx='48' cy='88' r='.85'/%3E%3C/g%3E%3C/svg%3E");
  -webkit-mask-image: linear-gradient(#000 20%, transparent 90%);
          mask-image: linear-gradient(#000 20%, transparent 90%);
}

.gal-title {
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(3rem, 7.4vw, 5.75rem);
  line-height: 0.95; letter-spacing: -0.01em;
  text-wrap: balance;
}
.gal-uline { display: block; width: min(360px, 70%); height: 22px; margin: 10px 0 0 -4px; overflow: visible; color: var(--crimson); }
.gal-uline .mark { stroke-width: 4.4; }
.gal-blurb {
  margin-top: clamp(18px, 3vh, 28px);
  max-width: 40ch;
  font-size: clamp(1rem, 1.2vw, 1.15rem);
  color: var(--ink-muted);
  text-wrap: pretty;
}
.gal-meta { margin-top: 14px; font-size: 0.85rem; color: var(--ink-muted); opacity: 0.8; }

.gal-art { --k: 0.75; justify-self: center; width: min(340px, 100%); overflow: visible; }

/* the map's "been here" stamp, pressed on as you arrive */
.gal-stamp { color: var(--crimson); transform-box: fill-box; transform-origin: center; opacity: 0; scale: 0.3; rotate: 14deg; }
.gal-stamp circle { fill: var(--paper); stroke: currentColor; stroke-width: 2.6; stroke-dasharray: 4 5; }
.gal-stamp text { font-family: var(--type); font-size: 11px; fill: currentColor; text-anchor: middle; text-transform: uppercase; letter-spacing: 0.08em; }
.gallery-dlg.shown .gal-stamp { animation: gal-stamp 380ms var(--ease-out-quint) 1250ms forwards; }
@keyframes gal-stamp { to { opacity: 0.85; scale: 1; rotate: 0deg; } }

/* the map's "you are here" note, pointing you in */
.gal-here {
  position: absolute; left: var(--gutter); bottom: -14px;
  display: flex; align-items: flex-start; gap: 6px;
  color: var(--crimson); font-family: var(--hand); font-size: 1.2rem;
  pointer-events: none;
  opacity: 0;
}
.gallery-dlg.shown .gal-here { animation: gal-fade 600ms ease 1700ms forwards; }
.gal-here svg { width: 40px; height: 44px; margin-top: 10px; overflow: visible; }
@keyframes gal-fade { to { opacity: 1; } }

/* ---------- the photographs ----------
   Placed by game.js (placeMasonry): shortest-column masonry, with the
   featured photos across two columns. */
.gal-grid {
  position: relative;
  width: calc(min(100%, var(--gal-max)) - 2 * var(--gutter));
  margin: 28px auto 0;
}

.ph {
  position: absolute;
  padding: 0;
  overflow: hidden;
  background: var(--paper-warm);   /* holds the shape while the jpeg streams in */
  box-shadow: 0 1px 0 oklch(0.1 0.02 265 / 0.5), 0 18px 34px -22px oklch(0.05 0.02 265 / 0.9);
  cursor: zoom-in;

  /* the reveal carried over from the old site: each photo rises and fades
     in, and buildGallery() spaces them 80ms apart */
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 0.7s cubic-bezier(.4, 0, .2, 1),
              transform 0.7s cubic-bezier(.4, 0, .2, 1);
}
.ph.visible { opacity: 1; transform: none; }
.ph img {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: opacity 380ms ease, filter 380ms ease;
}
.ph:focus-visible { outline: 2px dashed var(--crimson); outline-offset: 4px; }
/* look at one and the rest step back — no zoom */
@media (hover: hover) {
  .gal-grid:has(.ph:hover) .ph:not(:hover) img { opacity: 0.5; filter: saturate(0.7); }
}

/* ---------- the end of the page ---------- */
.gal-end {
  max-width: var(--gal-max); margin: 0 auto;
  padding: clamp(24px, 4vw, 40px) var(--gutter) clamp(64px, 10vw, 120px);
  display: grid; justify-items: center; text-align: center;
}
/* your trail, in the map's crimson dashes, leading on */
.gal-trail { width: min(420px, 90%); height: 190px; overflow: visible; }
.gal-trail .walked { fill: none; stroke: var(--crimson); stroke-width: 2.6; stroke-dasharray: 9 7; opacity: 0.62; }
.gal-trail mask path { fill: none; stroke: #fff; stroke-width: 10; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 1600ms var(--ease-out-quint); }
.gal-end.seen .gal-trail mask path { stroke-dashoffset: 0; }

.gal-next { display: grid; justify-items: center; margin-top: 6px; }
.gal-next-art { --k: 1; width: 220px; height: 120px; overflow: visible; transition: translate var(--t-med) var(--ease-out-quint); }
.gal-next-kick { margin-top: 10px; font-size: 0.85rem; color: var(--ink-muted); }
.gal-next-name { font-family: var(--hand); font-weight: 700; font-size: clamp(2.2rem, 4.2vw, 3rem); line-height: 1.1; transition: color var(--t-fast) ease; }
.gal-next .gal-uline { width: 150px; height: 14px; margin: 0; }
.gal-next .gal-uline .mark {
  stroke-width: 3;
  stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0;
  transition: stroke-dashoffset 360ms var(--ease-out-quint), opacity 0s 360ms;
}
.gal-next-blurb { margin-top: 12px; max-width: 38ch; font-size: 0.95rem; color: var(--ink-muted); }
.gal-next:hover .gal-next-art, .gal-next:focus-visible .gal-next-art { translate: 0 -5px; }
.gal-next:hover .gal-next-name, .gal-next:focus-visible .gal-next-name { color: var(--crimson); }
.gal-next:hover .gal-uline .mark, .gal-next:focus-visible .gal-uline .mark {
  stroke-dashoffset: 0; opacity: 1;
  transition: stroke-dashoffset 360ms var(--ease-out-quint);
}
.gal-next:focus-visible { outline: none; }

.gal-links {
  margin-top: clamp(40px, 6vw, 64px);
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 40px;
  font-family: var(--hand); font-weight: 700; font-size: 1.1rem;
}
.gal-links button { color: var(--ink-muted); padding: 6px 2px; transition: color var(--t-fast) ease; }
.gal-links button:hover, .gal-links button:focus-visible { color: var(--crimson); }

/* ---------- the photo viewer ---------- */

.lightbox {
  width: 100vw; height: 100dvh;
  max-width: none; max-height: none;
  background-color: var(--paper-deep);
  background-image: var(--grain, none);
  background-size: 220px 220px;
  display: grid; grid-template: minmax(0, 1fr) / minmax(0, 1fr); place-items: center;
  padding: 64px clamp(16px, 9vw, 130px) 28px;
  /* sideways drags are ours (the swipe to the next photo); left to the
     browser, it can claim them as a pan and cancel the pointer mid-swipe */
  touch-action: pan-y pinch-zoom;
}
.lightbox:not([open]) { display: none; }
.lightbox[open] { animation: lb-in 320ms ease; }
@keyframes lb-in { from { opacity: 0; } }

.lightbox figure { grid-area: 1 / 1; display: grid; gap: 14px; }
.lightbox img {
  display: block;
  max-width: 100%;
  max-height: calc(100dvh - 170px);
  width: auto; height: auto;
  background: var(--paper-warm);
  box-shadow: 0 30px 80px -30px oklch(0.05 0.02 265 / 0.9);
}
.lightbox[open] img { animation: lb-photo 520ms var(--ease-out-expo); }
@keyframes lb-photo { from { scale: 0.975; } }
/* Sized from the photo's known shape, not from whichever file is loaded:
   at natural size the 700 px thumb showed small, then the 1600 px original
   swapped in bigger and the photo (and its caption) jumped. Viewport units,
   not %, because the figure shrink-wraps the image. */
.lightbox img.lb-sized {
  width: min(1500px, calc(100vw - 2 * clamp(16px, 9vw, 130px)), calc((100dvh - 170px) * var(--ar)));
  max-height: none;
  aspect-ratio: var(--ar);
}

/* your note in your hand, the place in type; a photo with neither shows the
   gallery's name there quietly instead */
.lightbox figcaption {
  display: flex; justify-content: space-between; align-items: baseline; gap: 24px;
  min-height: 1.7em;
}
.lb-words { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 16px; }
.lb-cap { font-family: var(--hand); font-size: 1.3rem; line-height: 1.3; }
.lb-cap:empty::before { content: attr(data-gallery); font-size: 1.1rem; color: var(--ink-muted); }
.lb-place { font-size: 0.85rem; color: var(--ink-muted); }
.lb-place:empty { display: none; }
.lb-count { font-size: 0.9rem; color: var(--ink-muted); white-space: nowrap; }

.lb-close {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  right: max(clamp(12px, 3vw, 36px), env(safe-area-inset-right));
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--hand); font-weight: 700; font-size: 1.1rem;
  color: var(--ink-muted);
  padding: 10px 6px;
  transition: color var(--t-fast) ease;
}
.lb-close svg { width: 22px; height: 22px; overflow: visible; transition: rotate 300ms var(--ease-out-quint); }
.lb-close svg path, .lb-nav svg path { fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.lb-close:hover { color: var(--ink); }
.lb-close:hover svg { rotate: 90deg; }

.lb-nav {
  position: fixed; top: 50%; translate: 0 -50%;
  width: clamp(48px, 6vw, 84px); height: 64px;
  display: grid; place-items: center;
  opacity: 0.6;
  transition: opacity var(--t-fast) ease, color var(--t-fast) ease;
}
.lb-nav svg { width: 100%; height: 40px; overflow: visible; transition: translate var(--t-med) var(--ease-out-quint); }
.lb-prev { left: max(clamp(8px, 2vw, 28px), env(safe-area-inset-left)); }
.lb-next { right: max(clamp(8px, 2vw, 28px), env(safe-area-inset-right)); }
.lb-prev svg { scale: -1 1; }
.lb-nav:hover { opacity: 1; color: var(--crimson); }
.lb-next:hover svg { translate: 5px 0; }
.lb-prev:hover svg { translate: -5px 0; }

/* ---------- galleries on smaller screens ---------- */
@media (max-width: 860px) {
  .gal-open { grid-template-columns: 1fr; align-items: start; gap: 8px; }
  .gal-art { order: -1; justify-self: start; width: min(260px, 64%); margin-left: -10px; }
  .gal-here { display: none; }
}
@media (max-width: 720px) {
  .gal-bar { grid-template-columns: 1fr auto; }
  .gal-bar-title { display: none; }
  .lightbox { padding-inline: 12px; }
  .lightbox img.lb-sized { width: min(calc(100vw - 24px), calc((100dvh - 170px) * var(--ar))); }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 720px) {
  .hint-chip { font-size: 0.8rem; max-width: min(56vw, 300px); }
  .compass-chip span { display: none; }
  .compass-chip { padding: 11px; }

  .lb-nav { display: none; }            /* swipe / tap zones take over */

  .postcard { grid-template-columns: 1fr; }
  .pc-side { border-left: none; border-top: 2px dashed var(--pencil); padding-left: 0; padding-top: var(--sp-5); flex-direction: row; align-items: center; justify-content: space-between; }
  .pc-stamp { align-self: flex-start; }
  .addr { font-size: 1rem; flex: 1; margin-right: var(--sp-5); }
}

@media (max-width: 480px) {
  .polaroid { float: none; width: 70%; margin: 0 auto var(--sp-5); rotate: 1.6deg; }
  #contentsList .c-count { display: none; }
}

@media (pointer: coarse) {
  .dlg-close { width: 50px; height: 50px; }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .stage { opacity: 1; scale: 1; }
  #hikerInner, #dogInner { scale: 1; }
  .hud > * { opacity: 1; translate: 0 0; }
  #trail { display: none; }
  /* the dog still answers a pet, just without the bouncing */
  #dogSpeak.say { animation: none; opacity: 1; }
  /* photos still shuffle, they just arrive all at once instead of cascading */
  .ph { opacity: 1; transform: none; }
}
/* ============================================================
   OFF THE MAP — the runner
   ============================================================ */

#runner {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-hud) + 5);
  background: var(--paper);
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
#runner[hidden] { display: none; }
body.running-game { overflow: hidden; }

#rkSvg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* the sky is #runner's own paper, so the skyline strip behind can show */
#rkSky { fill: none; }
#rkBack { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.rk-band { position: absolute; left: 0; top: 0; overflow: visible; will-change: transform; }
/* a touch bolder than behind the ski run: here they're the whole horizon */
.rk-band .sk-alp-far { --k: 0.5; }
.rk-band .sk-alp-mid { --k: 0.72; }
.rk-band .sk-alp-near { --k: 0.6; }

/* ---------- the world ---------- */

.rk-ink {
  fill: none; stroke: var(--ink);
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
.rk-ink.rk-thick { stroke-width: 4.5; }
.rk-ink.rk-thin  { stroke-width: 2.4; }
.rk-ink.rk-hair  { stroke-width: 2; opacity: 0.65; }

.rk-trail { fill: var(--ink); opacity: 0.045; stroke: none; }
.rk-edge {
  fill: none; stroke: var(--ink);
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.55;
}
/* grass along the edges and pebbles on the trail, streaming past */
.rk-tuft { fill: none; stroke: var(--ink); stroke-width: 1.8; stroke-linecap: round; opacity: 0.6; }
.rk-tuft-far { stroke-width: 1.2; opacity: 0.38; }
.rk-pebble { fill: var(--ink); opacity: 0.26; }
/* the woods: the ski game's brush firs without the snow, aspens, bushes, rocks */
.rk-tw-base { fill: var(--paper); }
.rk-tw-wash { fill: var(--ink); fill-opacity: 0.2; }
.rk-tw-wash.rk-tw-dark { fill-opacity: 0.32; }
.rk-tw-line { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.rk-tw-line.rk-tw-heavy { stroke-width: 2.6; }
.rk-tw-ink { fill: var(--ink); fill-opacity: 0.75; }
.rk-tw-tick { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linecap: round; stroke-opacity: 0.6; }
/* Felix's shadow, and the puff when he lands a jump */
.rk-shadow { fill: var(--ink); fill-opacity: 0.14; }
.rk-dust circle { fill: var(--paper); stroke: var(--ink); stroke-width: 1.6; opacity: 0;
                  transform-box: fill-box; transform-origin: 50% 50%; }
.rk-dust.on circle { animation: rk-poof 440ms var(--ease-out-quint) forwards; }
@keyframes rk-poof {
  0%   { opacity: 0.9; transform: translate(0, 0) scale(0.4); }
  100% { opacity: 0;   transform: translate(var(--dx), var(--dy)) scale(1.3); }
}
.rk-lane {
  fill: none; stroke: var(--pencil);
  stroke-width: 2.2; stroke-linecap: round;
  stroke-dasharray: 2 22;
  opacity: 0.8;
}
.rk-horizon { fill: none; stroke: var(--ink); stroke-width: 2.4; opacity: 0.4; }
.rk-far { fill: none; stroke: var(--pencil); stroke-width: 2.4; stroke-linejoin: round; opacity: 0.75; }
.rk-far-back { opacity: 0.4; stroke-width: 2; }

.rk-tree, .rk-ob { color: var(--ink); }

/* ---------- the runners ---------- */

.rk-pack { fill: var(--crimson); stroke: var(--ink); stroke-width: 3.5; }
.rk-head { fill: var(--paper); stroke: var(--ink); stroke-width: 3.5; }
.rk-dog-body { fill: var(--paper); stroke: var(--ink); stroke-width: 2.6; }
.rk-dog-head { fill: var(--paper); stroke: var(--ink); stroke-width: 3; }
.rk-dog-mantle { fill: var(--ink); }

#rkHiker, #rkDog { transition: none; }

/* legs and arms pump; --rk-cadence tightens as you speed up */
.rk-leg, .rk-arm { transform-box: fill-box; transform-origin: 50% 0; }
.rk-l1 { animation: rk-stride var(--rk-cadence, 320ms) ease-in-out infinite; }
.rk-l2 { animation: rk-stride var(--rk-cadence, 320ms) ease-in-out infinite reverse; }
.rk-a1 { animation: rk-swing var(--rk-cadence, 320ms) ease-in-out infinite reverse; }
.rk-a2 { animation: rk-swing var(--rk-cadence, 320ms) ease-in-out infinite; }
@keyframes rk-stride {
  0%, 100% { transform: rotate(19deg); }
  50%      { transform: rotate(-19deg); }
}
@keyframes rk-swing {
  0%, 100% { transform: rotate(11deg); }
  50%      { transform: rotate(-11deg); }
}
/* the tail hangs from his rump and swings from there */
.rk-tail { transform-box: fill-box; transform-origin: 50% 0; animation: rk-wag 300ms ease-in-out infinite; }
@keyframes rk-wag {
  0%, 100% { transform: rotate(-11deg); }
  50%      { transform: rotate(11deg); }
}

/* sliding: squash down and tuck the legs */
#rkHiker.rk-sliding { scale: 1 0.46; }
#rkDog.rk-sliding   { scale: 1 0.62; }
#rkHiker, #rkDog { transform-box: view-box; }

.rk-crashed #rkHiker { rotate: -74deg; translate: -14px 26px; transition: rotate 380ms var(--ease-out-quint), translate 380ms var(--ease-out-quint); }
.rk-crashed .rk-l1, .rk-crashed .rk-l2,
.rk-crashed .rk-a1, .rk-crashed .rk-a2,
.rk-crashed .rk-tail { animation-play-state: paused; }
/* and at the end of a run — he's stopped, so his legs should be too (and
   they were animating under the card's backdrop blur the whole time) */
#runner.rk-done .rk-bob, #runner.rk-done .rk-ear-l, #runner.rk-done .rk-ear-r,
#runner.rk-done .rk-lf1, #runner.rk-done .rk-lf2,
#runner.rk-done .rk-lb1, #runner.rk-done .rk-lb2,
#runner.rk-done .rk-l1, #runner.rk-done .rk-l2,
#runner.rk-done .rk-a1, #runner.rk-done .rk-a2,
#runner.rk-done .rk-tail { animation-play-state: paused; }

/* ---------- night falls ---------- */

#runner.rk-night {
  --paper:  oklch(0.225 0.018 265);
  --ink:    oklch(0.93 0.006 250);
  --pencil: oklch(0.5 0.014 262);
  --crimson: oklch(0.68 0.155 30);
}
#runner { transition: background-color 2.4s ease; }
#runner.rk-night #rkSky { transition: fill 2.4s ease; }

/* ---------- hud ---------- */

.rk-dist {
  position: absolute;
  top: max(18px, env(safe-area-inset-top));
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.2rem);
  color: var(--ink);
  pointer-events: none;
}
.rk-dist span { color: var(--crimson); }

/* The mark to beat, kept under the live distance for the whole run. Muted so
   it reads as a reference rather than as your score, and it turns crimson the
   moment you pass it — at which point it stops being a target and starts
   being the news. */
.rk-pb {
  position: absolute;
  top: calc(max(18px, env(safe-area-inset-top)) + 2.5rem);
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(0.95rem, 0.8rem + 0.7vw, 1.3rem);
  color: var(--ink-muted);
  pointer-events: none;
}
.rk-pb[hidden] { display: none; }
.rk-pb-hit { color: var(--crimson); }

.rk-quit {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  font-family: var(--type); font-size: 0.85rem;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 5px 15px;
  box-shadow: 2px 2px 0 var(--btn-shadow);
}
.rk-quit:hover { translate: -1px -1px; box-shadow: 3px 3px 0 var(--btn-shadow); }

.rk-help {
  position: absolute;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: 50%; translate: -50% 0;
  font-family: var(--type); font-size: 0.8rem;
  color: var(--ink-muted);
  pointer-events: none;
  white-space: nowrap;
}
@media (pointer: coarse) { .rk-help { font-size: 0.75rem; } }

/* ---------- the stamp when you stop ---------- */

.rk-over {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: oklch(0.16 0.01 285 / 0.42);
  backdrop-filter: blur(2px);
}
.rk-over[hidden] { display: none; }

.rk-stamp {
  background: var(--paper);
  border-radius: 14px 10px 16px 9px / 10px 15px 9px 14px;
  box-shadow: 0 22px 60px oklch(0 0 0 / 0.42);
  rotate: -1.2deg;
  padding: clamp(24px, 5vw, 40px) clamp(28px, 6vw, 52px);
  text-align: center;
  animation: rk-thump 460ms var(--ease-out-quint);
}
@keyframes rk-thump {
  0%   { opacity: 0; scale: 1.6; rotate: -9deg; }
  60%  { opacity: 1; scale: 0.98; }
  100% { opacity: 1; scale: 1; rotate: -1.2deg; }
}
.rk-over-dist {
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(2.4rem, 1.6rem + 4vw, 3.6rem);
  line-height: 1;
  color: var(--ink);
}
.rk-over-dist span { color: var(--crimson); }
.rk-over-best {
  font-family: var(--type); font-size: 0.9rem;
  color: var(--ink-muted);
  margin-top: 8px;
}
.rk-over-btns { display: flex; gap: 10px; justify-content: center; margin-top: var(--sp-5); flex-wrap: wrap; }
.rk-over-btns button {
  font-family: var(--hand); font-weight: 700; font-size: 1.05rem;
  padding: 8px 20px;
  border-radius: 999px;
  border: 2px solid var(--ink);
  background: var(--paper); color: var(--ink);
  box-shadow: 2px 2px 0 var(--btn-shadow);
}
.rk-over-btns button:first-child { background: var(--crimson); color: var(--paper); border-color: var(--crimson); }
.rk-over-btns button:hover { translate: -1px -1px; box-shadow: 4px 4px 0 var(--btn-shadow); }

/* ---------- the way out ---------- */

#edgeHint { opacity: 0; transition: opacity 220ms ease; pointer-events: none; }
#edgeHint.on { opacity: 1; }
#edgeHint text { font-family: var(--hand); font-weight: 700; font-size: 19px; fill: var(--crimson); }
#edgeHint path { fill: none; stroke: var(--crimson); stroke-width: 2.6; stroke-linecap: round; stroke-dasharray: 3 8; }

#signpost, #skiSign { cursor: pointer; }
/* Lighter as well as smaller. These are a keepsake in the margin, not a
   landmark — at landmark weight a half-size sign just reads as a heavy blot. */
#signpost .sp-ink, #skiSign .sp-ink { fill: none; stroke: var(--ink); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
#signpost .sp-board, #skiSign .sp-board { fill: var(--paper); stroke: var(--ink); stroke-width: 2.6; stroke-linejoin: round; }
#signpost text, #skiSign text { font-family: var(--hand); font-weight: 700; font-size: 12.5px; fill: var(--ink); text-anchor: middle; }
#signpost:hover .sp-board, #skiSign:hover .sp-board { fill: var(--paper-warm); }
#signpost:focus-visible, #skiSign:focus-visible { outline: none; }
#signpost:focus-visible .sp-board, #skiSign:focus-visible .sp-board { stroke: var(--crimson); }

@media (prefers-reduced-motion: reduce) {
  .rk-l1, .rk-l2, .rk-a1, .rk-a2, .rk-tail,
  .rk-bob, .rk-lf1, .rk-lf2, .rk-lb1, .rk-lb2, .rk-ear-l, .rk-ear-r, .rk-dust.on circle { animation: none; }
  .rk-stamp { animation: none; }
}

/* ---------- runner prototype: signs + message ---------- */
.rk-sign-board { fill: var(--paper); stroke: var(--ink); stroke-width: 4; stroke-linejoin: round; }
.rk-sign-arrow { fill: none; stroke: var(--ink); stroke-width: 5; stroke-linecap: round; stroke-linejoin: round;
                 transform-box: fill-box; transform-origin: 50% 50%; }
.rk-sign { color: var(--ink); }
.rk-sign-live .rk-sign-board { stroke: var(--crimson); }
.rk-sign-live .rk-sign-arrow { stroke: var(--crimson); }

.rk-msg {
  position: absolute; left: 50%; top: 44%; translate: -50% -50%;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.6rem, 1.1rem + 2.4vw, 2.6rem);
  color: var(--crimson);
  pointer-events: none;
}
.rk-msg[hidden] { display: none; }

/* ---------- runner: Felix is the one running ---------- */
.rk-tail-thick { fill: none; stroke: var(--ink); stroke-width: 4; stroke-linecap: round; }
.rk-collar { fill: none; stroke: var(--crimson); stroke-width: 3.4; stroke-linecap: round; }
/* The gallop, seen from behind and above: the whole dog rises and falls once
   a stride, the hind legs tuck up under him (from here a kick reads as the
   leg shortening, not swinging) a beat apart, the front legs reach on the
   opposite beat, and the ears bounce. All on one clock, so runner.js speeds
   the lot up together by playback rate. */
.rk-bob { animation: rk-bob var(--rk-cadence, 320ms) ease-in-out infinite; }
@keyframes rk-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
.rk-lf1, .rk-lf2, .rk-lb1, .rk-lb2 { transform-box: fill-box; transform-origin: 50% 0; }
.rk-lb1, .rk-lb2 { animation: rk-kick var(--rk-cadence, 320ms) ease-in-out infinite; }
.rk-lf1, .rk-lf2 { animation: rk-reach var(--rk-cadence, 320ms) ease-in-out infinite; }
.rk-lb2, .rk-lf2 { animation-delay: -50ms; }
@keyframes rk-kick  { 0%, 100% { transform: none; } 50% { transform: translateY(-2.6px) scaleY(0.68); } }
@keyframes rk-reach { 0%, 100% { transform: translateY(1px) scaleY(1.12); } 50% { transform: translateY(-2px) scaleY(0.8); } }
.rk-ear-l, .rk-ear-r { transform-box: fill-box; transform-origin: 50% 100%;
                       animation: rk-flap-l var(--rk-cadence, 320ms) ease-in-out infinite; }
.rk-ear-r { animation-name: rk-flap-r; }
@keyframes rk-flap-l { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(4deg); } }
@keyframes rk-flap-r { 0%, 100% { transform: rotate(3deg); } 50% { transform: rotate(-4deg); } }
/* his coat: pale legs inside an ink line, black saddle and head, pads */
.rk-limb-o { fill: none; stroke: var(--ink); stroke-width: 7; stroke-linecap: round; stroke-linejoin: round; }
.rk-limb-i { fill: none; stroke: var(--paper); stroke-width: 3.4; stroke-linecap: round; stroke-linejoin: round; }
.rk-pad { fill: var(--ink); }
.rk-head, .rk-ear { fill: var(--ink); stroke: var(--paper); stroke-width: 2; stroke-linejoin: round; }
.rk-ear-in { fill: var(--paper); }
.rk-face { fill: var(--paper); stroke: var(--ink); stroke-width: 2.6; stroke-linejoin: round; }
.rk-tongue { fill: var(--crimson); stroke: var(--ink); stroke-width: 1.4; }
.rk-nose-hi { fill: var(--paper); opacity: 0.8; }
.rk-eye-dk { fill: var(--ink); }
.rk-mask { fill: var(--ink); fill-opacity: 0.55; }

/* the back of Felix's head — dark like his back, ringed in paper so the
   two dark masses don't merge into one blob */
.rk-nape { fill: var(--ink); stroke: var(--paper); stroke-width: 2.6; stroke-linejoin: round; }

/* ---------- runner: obstacles, ducking, the end of a run ---------- */

#rkDog { transform-box: view-box; }

.rk-over-why {
  font-family: var(--type); font-size: 0.85rem;
  color: var(--ink-muted); margin-bottom: 6px;
}
.rk-call {
  margin-top: var(--sp-5);
  font-family: var(--hand); font-weight: 700; font-size: 1.15rem;
  background: var(--crimson); color: var(--paper);
  border: none; border-radius: 999px;
  padding: 10px 26px;
  box-shadow: 0 4px 0 var(--crimson-deep);
  transition: translate var(--t-fast) var(--ease-out-quint), box-shadow var(--t-fast) ease;
}
.rk-call:hover { translate: 0 -2px; box-shadow: 0 6px 0 var(--crimson-deep); }
.rk-call:active { translate: 0 2px; box-shadow: 0 2px 0 var(--crimson-deep); }

/* ---------- obstacles: a round-capped stroke reads as a trunk or a limb,
     which a rectangle never did ---------- */

/* ---------- the squirrel, and Felix coming home ---------- */
.rk-sq-body { fill: var(--paper); stroke: var(--ink); stroke-width: 2.6; }
.rk-sq-tail { fill: var(--ink); }
.rk-sq-ear  { fill: var(--ink); }
#rkSquirrel { animation: rk-scamper 190ms ease-in-out infinite; transform-box: fill-box; transform-origin: 50% 100%; }
@keyframes rk-scamper { 0%,100% { scale: 1 1; } 50% { scale: 1.06 0.93; } }

.rk-muzzle { fill: var(--paper); stroke: var(--ink); stroke-width: 2.2; }
.rk-eye { fill: var(--paper); }
.rk-front-tail { transform-box: fill-box; transform-origin: 0% 100%;
                 animation: rk-wag2 200ms ease-in-out infinite; }
@keyframes rk-wag2 { 0%,100% { rotate: -14deg; } 50% { rotate: 14deg; } }

/* ---------- shouts ---------- */
.rk-shout {
  position: absolute; left: 50%; top: 26%; translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.4rem, 1rem + 2vw, 2.2rem);
  color: var(--crimson); pointer-events: none;
  animation: rk-shout-in 400ms var(--ease-out-quint);
}
.rk-shout[hidden] { display: none; }
@keyframes rk-shout-in { from { opacity: 0; scale: 0.7; } to { opacity: 1; scale: 1; } }

/* ---------- three seconds to go again ---------- */
.rk-timer { margin-top: var(--sp-4); height: 3px; background: var(--pencil);
            border-radius: 2px; overflow: hidden; opacity: 0.5; }
.rk-timer i { display: block; height: 100%; width: 100%; background: var(--crimson); }
@keyframes rk-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.rk-timer i { transform-origin: left center; }

/* ---------- logs and branches ----------
   A log needs its sawn ends showing; that reads as a felled trunk in a way
   a plain bar never does. A branch needs to come off a tree at one side and
   fork, rather than hang like a fringe. */
.log-body  { fill: var(--paper); stroke: var(--ink); stroke-width: 3; stroke-linejoin: round; }
.log-cap-l, .log-cap-r { fill: var(--paper); stroke: var(--ink); stroke-width: 3; }
.log-ring-l, .log-ring-r { fill: none; stroke: var(--ink); stroke-width: 1.6; opacity: 0.7; }
.log-grain { fill: none; stroke: var(--ink); stroke-linecap: round; opacity: 0.42; }

.br-trunk  { fill: var(--ink); fill-opacity: 0.9; }
.br-limb   { fill: var(--ink); }
.br-twigs  { fill: none; stroke: var(--ink); stroke-linecap: round; }
/* leaf clumps in the woods' own wash, so the limb reads as part of a tree */
.br-fol-base { fill: var(--paper); }
.br-fol    { fill: var(--ink); fill-opacity: 0.34; stroke: var(--ink); stroke-width: 1.6; stroke-linejoin: round; }

/* ---------- the squirrel on the map, and the seam into the trail ---------- */
#mapSquirrel { animation: rk-scamper 150ms ease-in-out infinite;
               transform-box: fill-box; transform-origin: 50% 100%; }

.chase-fade {
  position: fixed; inset: 0;
  background: var(--paper);
  opacity: 0; pointer-events: none;
  z-index: calc(var(--z-hud) + 4);
  transition: opacity 380ms ease;
}
.chase-fade.on { opacity: 1; }
/* Coming back from a run the cover has to go up in the same frame the game's
   own white is torn down, or the map flashes through the seam. */
.chase-fade.instant { transition: none; }

/* ---------- skiing back onto the map ---------- */
#hikerSki { display: none; }
body.arriving #hikerSki { display: inline; }
body.arriving #hikerWalk, body.arriving #hikerSit, body.arriving #zzz { display: none; }

/* ---------- the control prompts, shown as each one is needed ---------- */
/* up in the sky, clear of Felix — down by his feet it sat right over his legs */
.rk-tip {
  position: absolute; left: 50%; top: 13%; translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.05rem, 0.9rem + 1vw, 1.5rem);
  color: var(--ink);
  background: var(--paper);
  border: 2.5px solid var(--ink);
  /* a pill on one line; on a phone the longest tips wrap to two, and a
     fixed radius keeps that a rounded card instead of a lozenge */
  border-radius: 1.4em;
  padding: 7px 20px;
  box-shadow: 3px 3px 0 var(--btn-shadow);
  pointer-events: none;
  width: max-content; max-width: calc(100% - 24px);
  text-align: center; text-wrap: balance;
  animation: rk-tip-in 320ms var(--ease-out-quint);
}
.rk-tip[hidden] { display: none; }
@keyframes rk-tip-in { from { opacity: 0; translate: -50% 10px; } to { opacity: 1; translate: -50% 0; } }

@media (prefers-reduced-motion: reduce) {
  .chase-fade { transition: none; }
  #mapSquirrel { animation: none; }
}

/* ---------- the squirrel ---------- */
.sq-body, .rk-sq-body { fill: var(--paper); stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }
.sq-tail, .rk-sq-tail { fill: var(--ink); }
.sq-ear,  .rk-sq-ear  { fill: var(--ink); }
.sq-eye,  .rk-sq-eye  { fill: var(--ink); }
.sq-leg { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linecap: round; }
#mapSquirrel .sq-flip { transform: scaleX(var(--sq-flip, 1)); transform-box: fill-box; transform-origin: 50% 50%; }

/* ---------- the way in, for anyone using a mouse ---------- */
.trail-prompt {
  position: fixed;
  transform: translate(-50%, -50%);
  z-index: calc(var(--z-hud) + 3);
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1rem, 0.9rem + 0.5vw, 1.2rem);
  color: var(--paper); background: var(--crimson);
  border: none; border-radius: 999px;
  padding: 9px 20px 10px;
  box-shadow: 0 4px 0 var(--crimson-deep);
  white-space: nowrap; cursor: pointer;
  opacity: 0; scale: 0.8;
  transition: opacity 200ms ease, scale 240ms var(--ease-out-quint), translate 150ms var(--ease-out-quint);
}
.trail-prompt.on { opacity: 1; scale: 1; }
.trail-prompt[hidden] { display: none; }
.trail-prompt:hover { translate: 0 -2px; box-shadow: 0 6px 0 var(--crimson-deep); }
.trail-prompt:active { translate: 0 2px; box-shadow: 0 2px 0 var(--crimson-deep); }

/* ---------- Felix in the run ---------- */
.rk-tail-fill { fill: var(--ink); }
.rk-nose { fill: var(--ink); stroke: var(--paper); stroke-width: 1.6; }

/* ============================================================
   THE SKI RUN — off the left edge
   Snow is unfilled paper: the slope is one ink line with nothing
   beneath it, and depth comes from line weight rather than colour.
   ============================================================ */

#skirun {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-hud) + 5);
  background: var(--paper);
  overflow: hidden;
  touch-action: none;
  overscroll-behavior: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
#skirun[hidden] { display: none; }
#skSvg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
#skSky { fill: var(--paper); }

/* ---------- the mountain ---------- */

.sk-ink {
  fill: none; stroke: var(--ink);
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
.sk-ink.sk-thick { stroke-width: 4.5; }
.sk-ink.sk-thin  { stroke-width: 2.4; }

.sk-slope {
  fill: none; stroke: var(--ink);
  stroke-width: 4.2; stroke-linecap: round; stroke-linejoin: round;
}
/* the ranges behind: depth by weight, which is how a pencil does depth */
.sk-ridge { fill: none; stroke: var(--pencil); stroke-linejoin: round; stroke-linecap: round; }
.sk-ridge-1 { stroke-width: 2.6; opacity: 0.62; }
.sk-ridge-2 { stroke-width: 2.2; opacity: 0.40; }
.sk-ridge-3 { stroke-width: 1.8; opacity: 0.24; }

/* ---------- the ranges behind (alps.js) ----------
   Their own layers, under the game's svg, each moved only by a compositor
   transform. Depth is how much ink each layer is allowed (--k), never layer
   opacity: the paper fills have to stay fully opaque or the farther ranges
   ghost through the nearer ones. Capped well under full strength so the
   rocks stay the darkest thing on screen. Fills and strokes fade on the same
   3s clock as the sky when night comes. */
#skAlpsWrap { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
#skAlpsWrap > svg { position: absolute; display: block; overflow: visible; }
#skAlpsSky { left: 0; top: 0; width: 100%; height: 100%; }
.sk-alp { left: -50%; top: -50%; width: 200%; height: 200%; will-change: transform; }
.sk-alp-far  { --k: 0.38; }
.sk-alp-mid  { --k: 0.55; }
.sk-alp-near { --k: 0.56; }
.sk-alp-fill  { fill: var(--paper); }
/* fill-/stroke-opacity, not opacity: each of these paints one thing, so it
   looks identical, and opacity made the browser composite every one of these
   (hundreds of paths) through its own offscreen layer each redraw */
.sk-alp-wash  { fill: var(--ink); fill-opacity: calc(var(--k) * 0.16); }
.sk-alp-hatch { fill: none; stroke: var(--ink); stroke-width: 1.3; stroke-linecap: round;
                stroke-opacity: calc(var(--k) * 0.55); }
.sk-alp-ink   { fill: var(--ink); fill-opacity: calc(var(--k) * 0.85); }
.sk-alp-tree  { fill: var(--ink); fill-opacity: calc(var(--k) * 0.6); }
.sk-alp-line  { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round;
                stroke-linejoin: round; stroke-opacity: calc(var(--k) * 0.7); }
.sk-alp-fill, .sk-alp-wash, .sk-alp-ink, .sk-alp-tree { transition: fill 3s ease; }
/* The forest falling away under the chair is for the ride UP. Skiing, the
   same band sits right above the slope — exactly where you read the rocks
   coming — so it melts into mist as you leave the chair. */
.sk-alp-forest { transition: fill 3s ease, opacity 2.4s ease; }
#skirun:not(.sk-riding-up) .sk-alp-forest { opacity: 0; }
.sk-alp-hatch, .sk-alp-line { transition: stroke 3s ease; }
/* a whiteout is mist rolling over the ranges, not the ranges going translucent */
.sk-alp-mist { position: absolute; inset: 0; background: var(--paper); opacity: 0;
               transition: background-color 3s ease; }
/* with the ranges in, the sky is the root's own paper (which fades at night),
   and the snow under the run is opaque so they stop at the slope */
#skirun.sk-alps #skSky { fill: none; }
.sk-snow { fill: none; stroke: none; }
#skirun.sk-alps .sk-snow { fill: var(--paper); transition: fill 3s ease; }

.sk-tree { color: var(--ink); }
.sk-rock { fill: var(--ink); stroke: var(--ink); stroke-width: 3; stroke-linejoin: round; }
.sk-rock-face { fill: var(--paper); opacity: 0.30; stroke: none; }
.sk-rock-shadow { fill: var(--ink); opacity: 0.16; }
.sk-cable { fill: none; stroke: var(--ink); stroke-width: 2.4; opacity: 0.75; }
/* ramp starts where the palette actually inverts, or they appear as dark
   specks on white paper for the 700 m before nightfall */
.sk-star { fill: var(--ink); opacity: calc(max(var(--sk-dusk, 0) - 0.55, 0) * 1.9); }

/* ---------- the rider ---------- */

.sk-ski { fill: none; stroke: var(--ink); stroke-width: 4; stroke-linecap: round; }
.sk-pack { fill: var(--crimson); stroke: var(--ink); stroke-width: 3; }
.sk-head { fill: var(--paper); stroke: var(--ink); stroke-width: 3.4; }
.sk-pole { opacity: 0.9; }
#skRider, #skRiderCrash, #skFelix { transition: none; }

/* The ski chatter is gone: at 220ms it was adding its own sub-pixel jitter
   on top of the camera's, and two small shakes read as one big one. */
.sk-rider-in { transform-box: fill-box; transform-origin: 50% 100%; }

/* ---------- Felix ---------- */

.sk-f-body { fill: var(--paper); stroke: var(--ink); stroke-width: 2.4; stroke-linejoin: round; }
.sk-f-mantle { fill: var(--ink); }
.sk-f-fur { fill: var(--ink); }
.sk-f-eye { fill: var(--paper); }
/* the ride up */
.sk-cable { fill: none; stroke: var(--ink); stroke-width: 2.2; }
.sk-tower { fill: none; stroke: var(--ink); stroke-width: 3.4;
            stroke-linecap: round; opacity: 0.85; }
.sk-wheel { fill: var(--paper); stroke: var(--ink); stroke-width: 3; }
/* the HUD has nothing to say until the run starts */
.sk-riding-up .sk-dist, .sk-riding-up .sk-pb, .sk-riding-up .sk-score { opacity: 0; }
/* only out at the top of a lick */
.sk-f-tongue { fill: var(--crimson); display: none; }
.sk-licking .sk-f-tongue { display: inline; }
.sk-f-leg path { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
/* rotate about the shoulder, which is the top of each leg's own box */
.sk-f-leg { transform-box: fill-box; transform-origin: 50% 0; }
.sk-f-lf, .sk-f-lb2 { animation: sk-bound var(--sk-cadence, 260ms) ease-in-out infinite; }
.sk-f-lb, .sk-f-lf2 { animation: sk-bound var(--sk-cadence, 260ms) ease-in-out infinite reverse; }
@keyframes sk-bound {
  0%, 100% { transform: rotate(24deg); }
  50%      { transform: rotate(-24deg); }
}
.sk-f-tail { transform-box: fill-box; transform-origin: 100% 0; animation: sk-wag 300ms ease-in-out infinite; }
@keyframes sk-wag {
  0%, 100% { transform: rotate(-8deg); }
  50%      { transform: rotate(8deg); }
}

/* ---------- night falls as you descend ---------- */

#skirun.sk-night, #skirun.sk-night-start {
  --paper:      oklch(0.225 0.018 265);
  --ink:        oklch(0.93 0.006 250);
  --ink-muted:  oklch(0.72 0.011 258);   /* without these the tip and the death */
  --btn-shadow: oklch(0.14 0.01 265);    /* reason drop to about 2.4:1 at night */
  --pencil:     oklch(0.5 0.014 262);
  --crimson:    oklch(0.68 0.155 30);
}
#skirun { transition: background-color 3s ease; }
#skirun.sk-night #skSky, #skirun.sk-night-start #skSky { transition: fill 3s ease; }

/* ---------- hud ---------- */

.sk-dist {
  position: absolute;
  top: max(18px, env(safe-area-inset-top));
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.2rem);
  color: var(--ink);
  pointer-events: none;
}
.sk-dist span { color: var(--crimson); }

/* The mark to beat, kept under the live distance for the whole run. Muted so
   it reads as a reference rather than as your score, and it turns crimson the
   moment you pass it — at which point it stops being a target and starts
   being the news. */
.sk-pb {
  position: absolute;
  top: calc(max(18px, env(safe-area-inset-top)) + 2.5rem);
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(0.95rem, 0.8rem + 0.7vw, 1.3rem);
  color: var(--ink-muted);
  pointer-events: none;
}
.sk-pb[hidden] { display: none; }
.sk-pb-hit { color: var(--crimson); }

.sk-score {
  position: absolute;
  top: calc(max(18px, env(safe-area-inset-top)) + 4.2rem);
  left: max(20px, env(safe-area-inset-left));
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1rem, 0.8rem + 0.9vw, 1.4rem);
  color: var(--ink-muted);
  pointer-events: none;
}
/* how much of the combo window is left — the thing you're racing */
.sk-meter {
  position: absolute; left: 50%; top: calc(22% + 2.6rem); translate: -50% 0;
  width: min(180px, 40vw); height: 4px;
  background: oklch(0.5 0 0 / 0.2); border-radius: 2px; overflow: hidden;
}
.sk-meter[hidden] { display: none; }
.sk-meter i {
  display: block; height: 100%; background: var(--crimson);
  transform-origin: left center;
}

.sk-combo {
  position: absolute; left: 50%; top: 22%; translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.4rem, 1rem + 2vw, 2.2rem);
  color: var(--crimson);
  pointer-events: none;
}
.sk-combo[hidden] { display: none; }
.sk-combo b { font-size: 1.35em; }
.sk-combo.sk-pop { animation: sk-pop 420ms var(--ease-out-quint); }
@keyframes sk-pop {
  0%   { opacity: 0; scale: 0.7; }
  60%  { opacity: 1; scale: 1.08; }
  100% { opacity: 1; scale: 1; }
}

.sk-quit {
  position: absolute;
  z-index: 1;                    /* above .sk-over's full-bleed scrim */
  top: max(16px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  font-family: var(--type); font-size: 0.85rem;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 5px 15px;
  box-shadow: 2px 2px 0 var(--btn-shadow);
}
.sk-quit:hover { translate: -1px -1px; box-shadow: 3px 3px 0 var(--btn-shadow); }

.sk-shout, .sk-tip {
  position: absolute; left: 50%; translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  color: var(--crimson);
  pointer-events: none;
  /* one line where it fits, wrapped (and balanced) on a phone rather than
     running off both edges — max-content, because a box at left: 50% would
     otherwise only get half the screen to shrink into */
  width: max-content; max-width: calc(100% - 32px);
  text-align: center; text-wrap: balance;
}
.sk-shout { top: 12%; font-size: clamp(1.5rem, 1.1rem + 2vw, 2.4rem); }
.sk-tip   { bottom: 16%; font-size: clamp(1rem, 0.9rem + 0.8vw, 1.3rem); color: var(--ink-muted); }
.sk-shout[hidden], .sk-tip[hidden] { display: none; }

.sk-help {
  position: absolute;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: 50%; translate: -50% 0;
  font-family: var(--type); font-size: 0.8rem;
  color: var(--ink-muted);
  pointer-events: none;
  white-space: nowrap;
}
@media (pointer: coarse) { .sk-help { font-size: 0.75rem; } }

/* ---------- the stamp when you stop ---------- */

.sk-over {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: oklch(0.16 0.01 285 / 0.42);
  backdrop-filter: blur(2px);
}
.sk-over[hidden] { display: none; }
.sk-stamp {
  background: var(--paper);
  border-radius: 14px 10px 16px 9px / 10px 15px 9px 14px;
  box-shadow: 0 22px 60px oklch(0 0 0 / 0.42);
  rotate: -1.2deg;
  padding: clamp(24px, 5vw, 40px) clamp(28px, 6vw, 52px);
  text-align: center;
  animation: rk-thump 460ms var(--ease-out-quint);
}
.sk-over-why { font-family: var(--type); font-size: 0.9rem; color: var(--ink-muted); margin-bottom: 6px; }
.sk-over-dist {
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(2.4rem, 1.6rem + 4vw, 3.6rem);
  line-height: 1; color: var(--ink);
}
.sk-over-dist span { color: var(--crimson); }
.sk-over-best { font-family: var(--type); font-size: 0.9rem; color: var(--ink-muted); margin-top: 8px; }
.sk-call {
  font-family: var(--hand); font-weight: 700; font-size: 1.05rem;
  margin-top: var(--sp-5);
  padding: 8px 22px;
  border-radius: 999px;
  border: 2px solid var(--crimson);
  background: var(--crimson); color: var(--paper);
  box-shadow: 2px 2px 0 var(--btn-shadow);
}
.sk-call:hover { translate: -1px -1px; box-shadow: 4px 4px 0 var(--btn-shadow); }
.sk-timer { height: 3px; margin-top: 14px; background: oklch(0.5 0 0 / 0.18); border-radius: 2px; overflow: hidden; }
.sk-timer i { display: block; height: 100%; background: var(--crimson); transform-origin: left center; }

@media (prefers-reduced-motion: reduce) {
  .sk-f-leg, .sk-f-tail { animation: none; }
  /* has to out-specify #skirun:not(.sk-airborne) .sk-rider-in, or the chatter
     keeps running for exactly the people who asked it not to */
  #skirun:not(.sk-airborne) .sk-rider-in { animation: none; }
  .sk-stamp, .sk-combo { animation: none; }
}

/* fall-line ticks under the piste — a pencil map's way of showing steepness,
   and what stops the paper below the ride line reading as nothing at all */
.sk-hach {
  fill: none; stroke: var(--pencil);
  stroke-width: 2; stroke-linecap: round;
  opacity: 0.30;
}

/* the 500 m markers down the piste, in the map's own fingerpost idiom */
.sk-marker { color: var(--ink); }
.sk-board { fill: var(--paper); stroke: var(--ink); stroke-width: 3.4; stroke-linejoin: round; }
.sk-marker text {
  font-family: var(--hand); font-weight: 700; font-size: 17px;
  fill: var(--ink); text-anchor: middle;
}

/* the go-again countdown */
@keyframes sk-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ---------- ski: hazards read louder than scenery ---------- */

/* the trees are BACKGROUND — they were nearly as dark as the rocks, and wider,
   so the thing that kills you was the smaller, quieter shape on screen */

/* and one crimson tick at the base of every rock: the only saturated thing
   on the page, so the eye goes straight to it */
.sk-rock-mark {
  fill: none; stroke: var(--crimson);
  stroke-width: 4; stroke-linecap: round;
}

/* a piste warns you before it hurts you */
.sk-warn { color: var(--ink); }
.sk-warn-plate { fill: var(--paper); stroke: var(--crimson); stroke-width: 4.5; stroke-linejoin: round; }
.sk-warn-bang { fill: none; stroke: var(--crimson); stroke-width: 5; stroke-linecap: round; }
.sk-warn-bang-dot { fill: var(--crimson); }
.sk-warn text {
  font-family: var(--hand); font-weight: 700; font-size: 15px;
  fill: var(--crimson); text-anchor: middle;
}

/* a gap has to look like a hole, not like the ink stopping */
.sk-face { fill: none; stroke: var(--ink); stroke-width: 4; stroke-linecap: round; }
.sk-chasm-shade { fill: var(--ink); opacity: 0.12; stroke: none; }

/* ---------- the trick chain, made legible ---------- */

.sk-mult {
  position: absolute; left: 50%; top: calc(22% + 3.4rem); translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.6rem, 1.2rem + 2vw, 2.6rem);
  color: var(--crimson);
  pointer-events: none;
}
.sk-mult[hidden] { display: none; }

.sk-bank {
  position: absolute; left: 50%; top: calc(22% + 6.6rem); translate: -50% 0;
  font-family: var(--hand); font-weight: 700;
  font-size: clamp(1.1rem, 0.9rem + 1vw, 1.6rem);
  color: var(--ink-muted);
  pointer-events: none;
  animation: sk-bank 1.3s ease-out forwards;
}
.sk-bank[hidden] { display: none; }
@keyframes sk-bank {
  0%   { opacity: 0; translate: -50% 8px; }
  22%  { opacity: 1; translate: -50% 0; }
  100% { opacity: 0; translate: -50% -22px; }
}

/* landing another trick refills the window — make that something you SEE */
.sk-meter.sk-refill { animation: sk-refill 320ms var(--ease-out-quint); }
@keyframes sk-refill {
  0%   { scale: 1.28 1.9; }
  100% { scale: 1 1; }
}

@media (prefers-reduced-motion: reduce) {
  .sk-bank, .sk-meter.sk-refill { animation: none; }
}

/* ---------- ski scenery: present, but a different register to the hazards ---------- */

/* Snow-laden firs (treeArt() in ski.js). They read as scenery, not hazard,
   by TREATMENT: a hazard is one solid black mass with a crimson tick, low on
   the snow; a tree is tall, mid-grey, and broken up by white snow. Nothing
   here is ever full-strength ink across an area. */
.sk-tree { color: var(--ink); }
.sk-tw-needles  { fill: var(--ink); opacity: 0.5; }
.sk-tw-snow     { fill: var(--paper); }
.sk-tw-edge     { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round;
                  stroke-linejoin: round; opacity: 0.5; }
.sk-tw-ink      { fill: var(--ink); opacity: 0.68; }
.sk-tw-snowline { fill: none; stroke: var(--paper); stroke-width: 2.2; stroke-linecap: round; }

/* things you pass: a cairn, a piste hut, marker flags, a frozen tarn */
.sk-prop { opacity: 0.55; }
.sk-pw {
  fill: none; stroke: var(--pencil);
  stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
}
.sk-pw-flag { fill: var(--crimson); stroke: none; opacity: 0.75; }

/* ---------- rails: the third verb ---------- */

.sk-rail-post { fill: none; stroke: var(--ink); stroke-width: 2.6; stroke-linecap: round; opacity: 0.7; }
.sk-rail-bar  { fill: none; stroke: var(--ink); stroke-width: 5; stroke-linecap: round; }
/* it lights up while you're actually on it */
.sk-rail-live .sk-rail-bar { stroke: var(--crimson); }

/* the big-air marker: a terrain-park sign, not a hazard triangle */
.sk-big { color: var(--ink); }
.sk-big-plate { fill: var(--paper); stroke: var(--ink); stroke-width: 4; stroke-linejoin: round; }
.sk-big-arc { fill: none; stroke: var(--crimson); stroke-width: 4; stroke-linecap: round; stroke-dasharray: 5 6; }
.sk-big text {
  font-family: var(--hand); font-weight: 700; font-size: 16px;
  fill: var(--ink); text-anchor: middle;
}

/* snow off the edges — the cheapest way to make speed legible */
.sk-spray {
  fill: none; stroke: var(--pencil);
  stroke-width: 2.6; stroke-linecap: round;
  opacity: 0.5;
}

/* the rooster tail off a landed trick */
.sk-spray {
  fill: none; stroke: var(--pencil);
  stroke-width: 4; stroke-linecap: round;
}
/* the ridiculous one announces itself */
.sk-big-huge .sk-big-plate { stroke: var(--crimson); stroke-width: 5; }
.sk-big-huge text { fill: var(--crimson); letter-spacing: 0.06em; }


/* boarding the ski chair: the hiker takes his seated pose, no campfire zzz */
body.boarding #hikerWalk { display: none; }
body.boarding #hikerSit  { display: block; }

/* The chair waiting off the edge. Its paths need their own rules: `.ink` is
   only styled inside .landmark / .ink-decor, so bare ink paths elsewhere
   render as zero-area fills — i.e. invisible. */
#skiChair path {
  fill: none; stroke: var(--ink);
  stroke-linecap: round; stroke-linejoin: round;
  stroke-width: 4;
}
#skiChair .thin { stroke-width: 2.6; }
#skiChair .hairline { stroke-width: 1.8; opacity: 0.5; }

/* a bridge hangs over the void: planks and a sagging handrail, no posts */
.sk-rail-bridge .sk-rail-post { stroke-width: 2.2; opacity: 0.85; }
.sk-rail-bridge .sk-rail-bar { stroke-width: 5.5; }

/* ---------- ski run extras: juice, goals, the avalanche ---------- */

/* powder off the skis, and the snowflakes worth collecting */
.sk-puff  { fill: var(--pencil); opacity: 0.55; stroke: none; }
.sk-flake { fill: none; stroke: var(--ink); stroke-width: 2.2; stroke-linecap: round; }
/* wind lines once the multiplier is cooking (opacity set from js) */
.sk-streak { fill: none; stroke: var(--pencil); stroke-width: 2; stroke-linecap: round; }
/* the goat: pencil grey, never ink — it's scenery that runs away */
.sk-goat-body { fill: var(--paper); stroke: var(--pencil); stroke-width: 2.2; stroke-linejoin: round; }
.sk-goat-line { fill: none; stroke: var(--pencil); stroke-width: 2.2; stroke-linecap: round; }

/* the avalanche: a paper-filled wall that swallows what's behind it */
.sk-av-fill { fill: var(--paper); }
.sk-av-wash { fill: var(--ink); opacity: 0.11; }
.sk-av-ink  { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; opacity: 0.75; }

/* words that pop out of the world: perfect!, close call!, +25, woof! */
.sk-float { font-family: var(--hand); font-weight: 700; font-size: 20px; fill: var(--ink-muted); pointer-events: none; }
.sk-float-big { font-size: 30px; fill: var(--crimson); }

.sk-alert, .sk-av-edge {
  position: absolute; pointer-events: none; white-space: nowrap;
  font-family: var(--hand); font-weight: 700; color: var(--crimson);
}
.sk-alert { left: 50%; top: 12%; translate: -50% 0; font-size: clamp(1.3rem, 1rem + 2vw, 2.3rem);
            white-space: normal; text-align: center; width: min(92vw, 720px); line-height: 1.15;
            animation: sk-rumble 260ms ease-in-out infinite; }
.sk-av-edge { left: max(16px, env(safe-area-inset-left)); top: 50%; translate: 0 -50%;
              font-size: clamp(0.95rem, 0.85rem + 0.6vw, 1.2rem); }
.sk-av-edge.sk-close { animation: sk-rumble 200ms ease-in-out infinite; }
.sk-alert[hidden], .sk-av-edge[hidden] { display: none; }
@keyframes sk-rumble {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(-2px, 1px); }
  75% { transform: translate(2px, -1px); }
}

/* goals: a card on the chair, a stamp when you tick one off */
.sk-goals {
  position: absolute; left: 50%; top: 13%; translate: -50% 0;
  min-width: min(340px, 86vw); padding: 12px 18px 10px;
  background: var(--paper); border: 2.5px solid var(--ink); border-radius: 12px;
  box-shadow: 4px 4px 0 var(--btn-shadow, rgba(0,0,0,0.18));
  pointer-events: none; opacity: 0; translate: -50% -8px;
  transition: opacity 400ms ease, translate 400ms ease;
}
.sk-goals.sk-show { opacity: 1; translate: -50% 0; }
.sk-goals-head { font-family: var(--type); font-size: 0.78rem; letter-spacing: 0.08em;
                 text-transform: uppercase; color: var(--ink-muted); margin: 0 0 6px; }
.sk-goal { font-family: var(--hand); font-size: 1.05rem; color: var(--ink); margin: 3px 0; white-space: nowrap; }
.sk-goal b { display: inline-block; width: 1.3em; color: var(--ink-muted); }
.sk-goal i { font-style: normal; font-family: var(--type); font-size: 0.78rem; color: var(--ink-muted); margin-left: 4px; }
.sk-goal-done { color: var(--ink-muted); text-decoration: line-through; text-decoration-color: var(--crimson); }
.sk-goal-done b { color: var(--crimson); }
.sk-goal-new { color: var(--crimson); font-weight: 700; }
.sk-over-goals { margin-top: 12px; text-align: left; }
.sk-over-goals .sk-goal { font-size: 0.98rem; white-space: normal; }

.sk-goal-toast {
  position: absolute; right: max(16px, env(safe-area-inset-right)); top: 70px;
  padding: 8px 14px; max-width: min(320px, 70vw);
  background: var(--paper); border: 2.5px solid var(--crimson); border-radius: 10px;
  font-family: var(--hand); font-size: 1.05rem; color: var(--ink); pointer-events: none;
}
.sk-goal-toast b { color: var(--crimson); margin-right: 4px; }
.sk-goal-toast span { display: block; font-size: 0.9rem; color: var(--crimson); margin-top: 2px; }
.sk-goal-toast[hidden] { display: none; }
.sk-goal-toast.sk-stamped { animation: sk-stamp-in 420ms var(--ease-out-quint, ease-out); }
@keyframes sk-stamp-in {
  0% { transform: scale(1.35) rotate(-4deg); opacity: 0; }
  60% { transform: scale(0.96) rotate(1deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .sk-alert, .sk-av-edge.sk-close, .sk-goal-toast.sk-stamped { animation: none; }
}

/* ---------- the cloud bank (map <-> ski run) ----------
   Every puff is drawn twice: a slightly larger ink disc in a layer
   underneath, and a paper disc on top. Overlapping puffs therefore only ever
   show ink on the OUTSIDE of the cloud — a cauliflower outline rather than a
   pile of bubbles. */
.cloud-wipe { position: fixed; inset: 0; z-index: calc(var(--z-hud) + 12); pointer-events: none; visibility: hidden; }
.cloud-wipe.cw-on { visibility: visible; }
.cloud-wipe svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.cw-fill { position: absolute; inset: 0; background: var(--paper); opacity: 0; transition: opacity 260ms ease; }
.cloud-wipe.cw-full .cw-fill { opacity: 1; }
.cw-ink circle { fill: var(--ink); opacity: 0.7; }
.cw-paper circle { fill: var(--paper); }
.cw-shade { fill: var(--ink); opacity: 0.07; }
.cw-base { fill: none; stroke: var(--pencil); stroke-width: 0.6; stroke-linecap: round; }
/* Each cloud travels for 55% of the total time, starting somewhere in the
   first 45% depending on where it sits — a slow wave, eased at both ends.
   A fast all-at-once wipe read as a glitch; this reads as weather. */
.cw-cloud {
  transform: translate(var(--px), var(--py));
  transition: transform calc(var(--cw-ms, 1700ms) * 0.55) cubic-bezier(0.45, 0.05, 0.25, 1);
}
.cloud-wipe.cw-cover .cw-cloud { transform: translate(0, 0); transition-delay: calc(var(--cw-ms, 1700ms) * var(--fd)); }
.cloud-wipe.cw-post .cw-cloud { transform: translate(var(--qx), var(--qy)); transition-delay: calc(var(--cw-ms, 2100ms) * var(--fq)); }
.cw-fill { transition: opacity 700ms ease; }
/* and while you're in it, the clouds keep drifting by */
.cloud-wipe.cw-on .cw-drift { animation: cw-drift 7s ease-in-out infinite alternate; }
@keyframes cw-drift { from { transform: translate(var(--dr), 0); } to { transform: translate(calc(var(--dr) * -1), 0); } }
.cloud-wipe.cw-snap .cw-cloud, .cloud-wipe.cw-snap .cw-fill { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .cw-cloud { transition-duration: 1ms !important; transition-delay: 0ms !important; }
  .cloud-wipe.cw-on .cw-drift { animation: none; }
}


/* the heap of snow you're buried under */
.sk-mound { fill: var(--paper); stroke: var(--ink); stroke-width: 3; stroke-linejoin: round; }
.sk-mound-hatch { fill: none; stroke: var(--pencil); stroke-width: 2; stroke-linecap: round; }

/* ---------- back on the map after a run ---------- */
/* Felix shakes the snow off: a fast wiggle and a spray of dots */
#dogShakeDots circle { fill: var(--pencil); opacity: 0; }
#dog.shaking #dogShakeDots circle { animation: dog-shake-dot 620ms ease-out forwards; }
@keyframes dog-shake-dot {
  0% { opacity: 0.9; transform: translate(0, 0); }
  100% { opacity: 0; transform: translate(var(--dx), var(--dy)); }
}
#dog.shaking #dogWalk { transform-box: fill-box; transform-origin: 50% 80%; animation: dog-shake 620ms ease-in-out; }
@keyframes dog-shake {
  0%, 100% { transform: rotate(0); }
  15% { transform: rotate(-11deg); } 30% { transform: rotate(11deg); }
  45% { transform: rotate(-9deg); } 60% { transform: rotate(9deg); }
  75% { transform: rotate(-5deg); } 90% { transform: rotate(4deg); }
}
/* his skis, planted against the signpost */
.sp-ski { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; }
#skiSignSkis.planting { animation: sp-plant 520ms cubic-bezier(0.3, 1.4, 0.5, 1); transform-box: fill-box; transform-origin: 50% 100%; }
@keyframes sp-plant {
  0% { transform: translate(0, -26px) rotate(-14deg); opacity: 0; }
  60% { opacity: 1; }
  100% { transform: none; }
}
/* a new best: the number gets stamped over */
/* (the text, not the sign: the sign carries its own transform attribute,
   and a CSS transform on it would replace that, not add to it) */
#skiSign.sp-stamp text { fill: var(--crimson); transform-box: fill-box; transform-origin: 50% 50%;
                         animation: sp-stamp 620ms cubic-bezier(0.3, 1.5, 0.5, 1); }
@keyframes sp-stamp {
  0% { transform: scale(1); } 30% { transform: scale(1.6) rotate(-6deg); } 60% { transform: scale(0.9); } 100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  #dog.shaking #dogWalk, #dog.shaking #dogShakeDots circle, #skiSignSkis.planting, #skiSign.sp-stamp text { animation: none; }
}

/* climbing out of a crevasse — the same ink weight as the rider at his usual size */
.sk-climb { stroke-width: 5.6; }
