/* =========================================================
   Snowed In: the look
   Time of day colors: app.js sets --tod-bg and --tod-ink as you scroll
   (night blue, firelight, storm white, bluebird).
   ========================================================= */
:root {
  --serif: "Instrument Serif", Georgia, serif;
  --sans: "Jost", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --pen: "Nanum Pen Script", "Bradley Hand", cursive;
  --tod-bg: #121a26;
  --tod-ink: #eef2f7;
  --amber: #e9a85c;
  --paper: #faf7f0;
  --ink: #23324a;
  --stake-w: 56px;
}
* { box-sizing: border-box; }
html { background: #121a26; }
body {
  margin: 0;
  background: var(--tod-bg);
  color: var(--tod-ink);
  font: 300 17px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, video { display: block; }

/* ---------- the stage (fixed photos behind everything) ---------- */
.stage { position: fixed; inset: 0; z-index: 0; background: var(--tod-bg); overflow: hidden; }
.layer { position: absolute; inset: 0; opacity: 0; }
.layer[data-id="open"] { opacity: 1; }
.layer > img, .layer > video { width: 100%; height: 100%; object-fit: cover; }
.layer.night { background: radial-gradient(ellipse at 50% 42%, #1c2a3e 0%, #121a26 55%, #0c121b 100%); }
.layer.day { background: radial-gradient(ellipse at 50% 42%, #fbfcfd 0%, #eef2f5 55%, #dfe5ea 100%); }

/* soft shade on the left and bottom so the words always read over a photo */
.scrim {
  position: fixed; inset: 0; z-index: 1; pointer-events: none; opacity: 0;
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--tod-bg) 72%, transparent) 0%, color-mix(in srgb, var(--tod-bg) 30%, transparent) 42%, transparent 70%),
    linear-gradient(0deg, color-mix(in srgb, var(--tod-bg) 55%, transparent) 0%, transparent 40%);
}
canvas.snow { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 2; pointer-events: none; }

/* film grain: a faint moving texture over everything (part of the "film look"). It only jiggles a few pixels, so
   it's just 40px bigger than the screen on each side (it was twice the screen's size, which phones paid for in memory) */
.grain {
  position: fixed; inset: -40px; z-index: 3; pointer-events: none; opacity: .09; mix-blend-mode: overlay;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.05' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  animation: grain .6s steps(4) infinite;
}
@keyframes grain {
  0% { transform: translate(0, 0); } 25% { transform: translate(-24px, 16px); }
  50% { transform: translate(16px, -24px); } 75% { transform: translate(-16px, -16px); }
}

/* ---------- the opening drawing ---------- */
.paper { position: fixed; inset: 0; z-index: 4; background: var(--paper); pointer-events: none; }
/* a soft wash of paper where the title sits, so the words read cleanly over the pencil lines */
.paper::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 62% 48% at 22% 78%, var(--paper) 0%, color-mix(in srgb, var(--paper) 85%, transparent) 45%, transparent 100%),
    linear-gradient(0deg, var(--paper) 0%, color-mix(in srgb, var(--paper) 70%, transparent) 14%, transparent 30%);
}
/* the drawing itself: an SVG of pen strokes, sized to cover the screen exactly like the photo below it */
.paper .sketch { position: absolute; inset: 0; }
.paper .sketch svg { display: block; width: 100%; height: 100%; }

/* chapter openers further down the page: the paper (wrapper) fades in with the scroll, then the sheet
   (drawing + paper) dissolves away by itself to show the photo */
.paper.mid { opacity: 0; background: none; }
.paper.mid::after { display: none; }
.paper.mid .sheet { position: absolute; inset: 0; background: var(--paper); }
.paper.mid .sheet::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(ellipse 60% 46% at 22% 18%, var(--paper) 0%, color-mix(in srgb, var(--paper) 80%, transparent) 45%, transparent 100%);
}

main { position: relative; z-index: 5; }

/* ---------- opener ---------- */
/* no fixed height here: while the opener is pinned, it needs to grow to make room for the scroll */
.opener-inner { position: relative; height: 100vh; height: 100svh; }
.title-block {
  position: absolute; left: 7vw; bottom: 16vh; max-width: 40rem; color: var(--ink);
}
.kicker, .eyebrow, .stamp {
  font: 400 .74rem/1 var(--sans); letter-spacing: .26em; text-transform: uppercase; margin: 0 0 14px;
}
h1 {
  font: 400 clamp(4.2rem, 13vw, 10rem)/.88 var(--serif); margin: 0; letter-spacing: -.01em;
}
.kicker, h1 { opacity: 0; animation: rise 2.2s ease 1.8s forwards; }
.kicker { animation-delay: 1.4s; }
@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.tagline { font: italic 400 clamp(1.3rem, 2.6vw, 1.8rem)/1.3 var(--serif); margin: 18px 0 0; opacity: 0; }
.chapter-block { position: absolute; left: 7vw; bottom: 16vh; max-width: 40rem; opacity: 0; }
.chapter-block h2 { font: 400 clamp(2.8rem, 7vw, 5.6rem)/.95 var(--serif); margin: 0; }
.chapter-sub { font: italic 400 clamp(1.3rem, 2.6vw, 1.8rem)/1.3 var(--serif); margin: 14px 0 0; }
.eyebrow { color: var(--amber); }
.cue-wrap { position: absolute; left: 0; right: 0; bottom: 4vh; text-align: center; }
.cue {
  background: none; border: 0; color: var(--ink); cursor: pointer; opacity: 0;
  font: 400 .72rem/1 var(--sans); letter-spacing: .24em; text-transform: uppercase;
}
.cue span { display: block; margin-top: 8px; font-size: 1rem; animation: breathe 3.2s ease-in-out infinite; }
@keyframes breathe { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

.chapter-inner { position: relative; height: 100vh; height: 100svh; }
.chapter-open .chapter-block { top: 11vh; bottom: auto; opacity: 1; color: var(--tod-ink); }
.chapter-open .chapter-block > * { opacity: 0; }
.chapter-open .eyebrow { color: #b5702b; }

/* ---------- scenes ---------- */
.scene { position: relative; height: 185vh; }
.card {
  position: absolute; left: 7vw; top: 72vh; max-width: 31rem;
  padding-right: calc(var(--stake-w) + 12px);
  text-shadow: 0 1px 18px color-mix(in srgb, var(--tod-bg) 55%, transparent);
}
/* amber, nudged toward the text color: lighter on night pages, deeper on snowy daytime pages */
.stamp { color: color-mix(in srgb, var(--amber) 62%, var(--tod-ink)); }
.card h3 { font: 400 clamp(2.6rem, 6vw, 4.6rem)/.95 var(--serif); margin: 0 0 14px; }
.card .line { font: italic 400 clamp(1.25rem, 2.2vw, 1.6rem)/1.35 var(--serif); margin: 0; opacity: .92; }

/* card-low: for photos with a dark subject behind the usual spot, the words sit lower, on the snow */
.scene.card-low .card { top: 118vh; }

/* prints: the smaller photos, like prints laid on a table, tucked underneath the words */
.print {
  position: relative; display: block; margin: 26px 0 0; width: clamp(200px, 21vw, 290px);
  background: #fbfaf7; padding: 10px 10px 40px; transform: rotate(-2deg);
  box-shadow: 0 18px 40px rgba(5, 9, 15, .45), 0 2px 6px rgba(5, 9, 15, .3);
  text-shadow: none;
}
.scene:nth-of-type(odd) .print { transform: rotate(2deg); }
.print img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: #dde3e9; }
.print figcaption {
  position: absolute; left: 0; right: 0; bottom: 6px; text-align: center;
  font: 400 1.45rem/1 var(--pen); color: #2a3a4c; white-space: nowrap;
}

/* ---------- the snow stake ---------- */
.stake {
  position: fixed; z-index: 6; right: 22px; top: 50%; transform: translateY(-50%);
  width: var(--stake-w); height: 42vh; opacity: 0; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; color: var(--tod-ink);
}
.stake-read { text-align: center; margin-bottom: 12px; }
.stake-num { display: block; font: 400 1.7rem/1 var(--serif); }
.stake-time { display: block; font: 400 .58rem/1.3 var(--sans); letter-spacing: .16em; text-transform: uppercase; opacity: .75; margin-top: 4px; min-height: 2.6em; }
.stake-pole {
  position: relative; flex: 1; width: 8px; border-radius: 3px; overflow: visible;
  background: repeating-linear-gradient(180deg, #f2c14e 0 18px, #2a2a2a 18px 21px);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .15);
}
.stake-fill {
  position: absolute; left: -8px; right: -8px; bottom: 0; height: 0%;
  background: linear-gradient(180deg, #ffffff, #dfe7ee); border-radius: 10px 10px 3px 3px;
  box-shadow: 0 -2px 10px rgba(255, 255, 255, .35);
}
/* a little sign bolted to the top of the pole */
.stake-sign {
  position: absolute; left: 50%; top: 3%; z-index: 1;
  transform: translateX(-50%) rotate(-4deg);
  background: #f6f1e6; color: #23324a; border-radius: 2px; padding: 4px 6px 3px; text-align: center;
  font: 500 .46rem/1.15 var(--sans); letter-spacing: .18em; text-transform: uppercase; white-space: nowrap;
  box-shadow: 0 2px 6px rgba(5, 9, 15, .35);
}
.stake-sign::before, .stake-sign::after {  /* the two bolts */
  content: ""; position: absolute; top: 50%; width: 3px; height: 3px; margin-top: -1.5px; border-radius: 50%; background: #9a8f7c;
}
.stake-sign::before { left: 2px; } .stake-sign::after { right: 2px; }
/* call-outs: pop up beside the snow line as it passes each mark */
.stake-callout {
  position: absolute; right: calc(100% + 14px); bottom: 0; transform: translateY(50%) scale(.85); transform-origin: right center;
  font: 400 1.3rem/1 var(--pen); color: var(--tod-ink); white-space: nowrap; opacity: 0;
  text-shadow: 0 1px 10px color-mix(in srgb, var(--tod-bg) 70%, transparent);
  transition: opacity .45s, transform .6s cubic-bezier(.2, 1.5, .4, 1);
}
.stake-callout::after {  /* a little line pointing at the pole */
  content: ""; position: absolute; left: calc(100% + 4px); top: 50%; width: 8px; height: 1px; background: currentColor; opacity: .6;
}
.stake-callout.on { opacity: 1; transform: translateY(50%) scale(1); }
.stake-callout.on.blocked { opacity: 0; }  /* stepping aside so it doesn't cover a scene's words (app.js) */

/* on Sunday morning the stake tops out, and says so */
.stake-gift {
  position: absolute; right: -4px; top: calc(100% + 10px); white-space: nowrap;
  font: 400 1.9rem/1 var(--pen); color: var(--amber);
  transform: rotate(-8deg) scale(.6); opacity: 0;
  transition: transform .6s cubic-bezier(.2, 1.6, .4, 1), opacity .4s;
}
.stake.gift .stake-gift { transform: rotate(-8deg) scale(1); opacity: 1; }
.stake-ticks span {
  position: absolute; left: 18px; transform: translateY(50%);
  font: 400 .62rem/1 var(--sans); opacity: .6;
}

/* ---------- the weekend rail (left edge): goes down as you scroll ---------- */
.rail {
  position: fixed; z-index: 6; left: 24px; top: 14vh; bottom: 14vh; width: 40px;
  opacity: 0; color: var(--tod-ink); pointer-events: none;
}
.rail-line {
  position: absolute; left: 0; top: 0; bottom: 0; width: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--tod-ink) 16%, transparent);
}
.rail-fill { position: absolute; left: 0; top: 0; width: 100%; height: 0; border-radius: 2px;
  background: color-mix(in srgb, var(--tod-ink) 55%, transparent); }
.rail-dot {
  position: absolute; left: 1px; top: 0; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
  background: var(--amber); box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber) 28%, transparent), 0 0 14px var(--amber);
}
.rail-stop {
  position: absolute; left: -6px; width: 30px; height: 18px; margin-top: -1px; padding: 0;
  border: 0; background: none; color: inherit; cursor: pointer; pointer-events: auto; text-align: left;
}
.rail-stop::before { content: ""; position: absolute; left: 0; top: 0; width: 14px; height: 2px; background: currentColor; opacity: .55; }
.rail-stop span {
  position: absolute; left: 14px; top: 6px; writing-mode: vertical-rl; white-space: nowrap;
  font: 400 .56rem/1 var(--sans); letter-spacing: .24em; text-transform: uppercase; opacity: 0;
  transition: opacity .6s;
}
/* only the part of the weekend you're in shows its name (the others on hover), so names never overlap */
.rail-stop.on span, .rail-stop:hover span { opacity: .9; }

/* ---------- "Scroll down" cue: shows when the scene is ready, glides to the next one ----------
   No button shape: the words, and a little snowfall underneath. Same colors as the scene titles (light on
   night pages, dark on snow-day pages), with a soft halo so it stays readable over a busy photo. */
.next {
  position: fixed; z-index: 7; left: 50%;
  bottom: calc(max(18px, env(safe-area-inset-bottom) + 10px) + var(--lift, 0px));
  display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px 16px;
  background: none; border: 0; cursor: pointer; color: var(--tod-ink);
  font: 500 .8rem/1 var(--sans); letter-spacing: .24em; text-transform: uppercase;
  /* a tight halo for crisp edges plus a soft one, so it stands out over a busy photo */
  text-shadow: 0 0 3px color-mix(in srgb, var(--tod-bg) 75%, transparent), 0 1px 14px color-mix(in srgb, var(--tod-bg) 95%, transparent);
  opacity: 0; visibility: hidden; transform: translate(-50%, 10px);
  transition: opacity .9s ease, transform .9s cubic-bezier(.2, .8, .2, 1), visibility 0s .9s, bottom .5s ease, color .4s;
}
.next.on { opacity: 1; visibility: visible; transform: translate(-50%, 0); transition-delay: 0s; }
.next:hover { color: color-mix(in srgb, var(--amber) 60%, var(--tod-ink)); }
.next:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; border-radius: 8px; }
.next-label { padding-left: .24em; } /* balances the letter spacing after the last letter, so the words sit centered */
.next-icon {
  display: block; width: 54px; height: 51px; overflow: visible;
  filter: drop-shadow(0 0 2px color-mix(in srgb, var(--tod-bg) 80%, transparent)) drop-shadow(0 0 7px color-mix(in srgb, var(--tod-bg) 85%, transparent));
}
.next:not(.on) .next-icon * { animation-play-state: paused; } /* no animating while it's hidden */
.nf-spin { transform-box: fill-box; transform-origin: center; animation: nfSpin 9s linear infinite; }
.nf-fall { animation: nfFall 3.2s linear infinite; }
.nf-drop { transform-box: fill-box; transform-origin: 50% 100%; animation: nfDrop 3.2s ease-in infinite; }
.nf-vee { animation: nfNudge 3.2s ease-in-out infinite; }
.nf-vee path { stroke-dasharray: 1; animation: nfDraw 3.2s ease-out infinite; }
@keyframes nfSpin { to { transform: rotate(360deg); } }
/* the small flakes: drift down a little to the side and fade */
@keyframes nfFall { 0% { transform: translate(0, -4px); opacity: 0; } 20%, 80% { opacity: .8; } 100% { transform: translate(3px, 26px); opacity: 0; } }
/* the big flake: falls, then squashes flat and vanishes as it hits the ground... */
@keyframes nfDrop {
  0% { transform: translateY(0); opacity: 0; } 10% { opacity: 1; }
  55% { transform: translateY(20px) scale(1, 1); opacity: 1; }
  61%, 100% { transform: translateY(21px) scale(1.5, .25); opacity: 0; }
}
/* ...and the splash shoots up from that spot as the two lines of the arrow, which nudges down once, then melts */
@keyframes nfDraw { 0%, 56% { stroke-dashoffset: 1; opacity: 1; } 68%, 88% { stroke-dashoffset: 0; opacity: 1; } 100% { stroke-dashoffset: 0; opacity: 0; } }
@keyframes nfNudge { 0%, 66% { transform: translateY(0); } 76% { transform: translateY(2.5px); } 88%, 100% { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .nf-fall, .nf-drop { display: none; }
  .nf-spin, .nf-vee, .nf-vee path { animation: none; }
  .nf-vee path { stroke-dashoffset: 0; }
}

/* ---------- snow-day tip pop-out ---------- */
.tip {
  position: fixed; z-index: 7; right: calc(var(--stake-w) + 40px); bottom: 32px; width: min(330px, calc(100vw - 32px));
  background: #fbfaf7; color: #1f2b3a; border-radius: 14px; padding: 18px 20px 18px;
  box-shadow: 0 20px 50px rgba(5, 9, 15, .4); border-top: 4px solid var(--amber);
  transform: translateY(24px); opacity: 0; visibility: hidden;
  transition: transform .6s cubic-bezier(.2, .8, .2, 1), opacity .5s, visibility 0s .6s;
}
.tip.on { transform: none; opacity: 1; visibility: visible; transition-delay: 0s; }
.tip-kicker { font: 500 .66rem/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; color: #b5702b; margin: 0 0 6px; }
.tip-title { font: 400 2rem/1 var(--pen); margin: 0 0 6px; }
.tip-text { font-size: .92rem; margin: 0 0 12px; color: #4a5866; }
.tip-link { font: 500 .85rem/1 var(--sans); color: #1f2b3a; text-decoration: none; border-bottom: 1px solid var(--amber); padding-bottom: 2px; }
.tip-close { position: absolute; top: 8px; right: 10px; background: none; border: 0; font-size: 1.4rem; color: #8592a0; cursor: pointer; line-height: 1; }

/* ---------- home base map ---------- */
.mapscene { position: relative; }
.map-inner {
  position: relative; height: 100vh; height: 100svh; display: grid;
  grid-template-columns: minmax(200px, 19rem) 1fr; align-items: center; gap: 2vw;
  padding: 0 calc(var(--stake-w) + 40px) 0 7vw;
}
.map-inner .card { position: static; padding-right: 0; }
.map-inner .later { margin-top: 18px; opacity: 0; }
.map { width: 100%; max-height: 88vh; }
.m-trees use { fill: #34465f; }
.m-road { fill: none; stroke: #1d293a; stroke-width: 34; stroke-linecap: round; }
.m-label { font: 400 36px var(--pen); fill: #a9b9cc; }
.m-trail { fill: none; stroke: #8fa2ba; stroke-width: 3.5; stroke-dasharray: 1 11; stroke-linecap: round; }
.m-trail.arrive { stroke: var(--amber); }
.m-dest { opacity: 0; }
.m-mound { fill: #233247; stroke: #6f829a; stroke-width: 2; stroke-dasharray: 5 6; }
.m-ghost { fill: none; stroke: #6f829a; stroke-width: 2; stroke-dasharray: 4 6; }
.m-logs circle { fill: #3a3027; stroke: #8a6a4c; stroke-width: 2; }
.m-glow { fill: var(--amber); opacity: 0; filter: blur(14px); }
.m-aframe { fill: #2a3950; stroke: #8fa2ba; stroke-width: 2; }
.m-roofsnow { fill: none; stroke: #eef2f7; stroke-width: 7; stroke-linejoin: round; opacity: .9; }
.m-win { fill: var(--amber); opacity: .12; }
.m-mullions { stroke: #2a3950; stroke-width: 2; }
.m-home { font: 400 44px var(--pen); fill: var(--amber); }
.m-walker { fill: var(--amber); stroke: #fff; stroke-width: 2; opacity: 0; }
.m-pad { fill: #1a2536; stroke: #6f829a; stroke-width: 2; stroke-dasharray: 5 6; }
.m-van rect { fill: #cfd9e4; stroke: #8fa2ba; stroke-width: 2; }
.m-van path { fill: #3a4d66; }
.m-van circle { fill: #2a3950; }
.m-vansnow { fill: none; stroke: #ffffff; stroke-width: 7; stroke-linecap: round; opacity: 0; }
.m-built, .m-built-label { opacity: 0; }
.m-boot { fill: #3c4c5e; }
.m-paw { fill: #8a6a4c; }
.m-small { font-size: 22px; }
.m-xtree { fill: #2c5444; stroke: #7fa494; stroke-width: 1.5; }
.m-trunk { fill: #6b5340; }
/* twinkle lights: warm white, each one blinking on its own timing */
.m-lights circle { fill: #fff1c9; filter: drop-shadow(0 0 3px #ffd98a); animation: twinkle 2.2s ease-in-out infinite; }
@keyframes twinkle { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
.m-gift.a { fill: #3fa9a2; } .m-gift.b { fill: #d0453c; }
.m-ribbon { fill: none; stroke: #fdf3d8; stroke-width: 1.6; }
/* Old Greenwood: a snow-covered green with a flag */
.m-green { fill: #2a3c4c; stroke: #86a8a0; stroke-width: 2; }
.m-green-in { fill: none; stroke: #86a8a0; stroke-width: 1.2; stroke-dasharray: 3 5; opacity: .7; }
.m-hole { fill: #0e151f; }
.m-pole { stroke: #dfe6ee; stroke-width: 2; }
.m-flag { fill: #e2574c; }
.m-built circle { fill: #ffffff; stroke: #5a7088; stroke-width: 2; }
.m-nose { fill: #e58a3a; }

/* the daytime map (Saturday): ink on snow instead of chalk on night */
.map.day .m-trees use { fill: #9db0c2; }
.map.day .m-road { stroke: #d3dbe2; }
.map.day .m-label { fill: #3c4c5e; }
.map.day .m-trail { stroke: #5a7088; }
.map.day .m-mound { fill: #ffffff; stroke: #8fa2b6; stroke-dasharray: none; }
.map.day .m-ghost { stroke: #9fb0c2; }
.map.day .m-logs circle { fill: #c4976a; stroke: #7a5537; }
.map.day .m-aframe { fill: #6b4a33; stroke: #4a3222; }
.map.day .m-roofsnow { stroke: #ffffff; opacity: 1; filter: drop-shadow(0 1px 1px rgba(40, 60, 80, .35)); }
.map.day .m-mullions { stroke: #6b4a33; }
.map.day .m-home { fill: #b5502b; }
.map.day .m-glow { fill: #e9a85c; }
.map.day .m-green { fill: #f6faf7; stroke: #a9c7b5; }
.map.day .m-green-in { stroke: #a9c7b5; }
.map.day .m-hole { fill: #3c4c5e; }
.map.day .m-pole { stroke: #5a6b7c; }
.map.day .m-xtree { fill: #2f5f4c; stroke: #2a4d3e; }
.map.day .m-pad { fill: #e6ebf0; stroke: #9fb0c2; }
.map.day .m-van rect { fill: #ffffff; stroke: #5a7088; }
.map.day .m-vansnow { filter: drop-shadow(0 1px 1px rgba(40, 60, 80, .35)); }

/* ---------- teasers for the next chapter ---------- */
.scene.teaser { height: 140vh; } /* short, so its words are still on screen at the very end of the page */
.teaser .card { top: 74vh; }

/* ---------- the rest of the roll: prints spread on a dark table ---------- */
.roll { position: relative; background: var(--tod-bg); padding: 16vh 0 14vh; }
.roll-inner { padding: 0 calc(var(--stake-w) + 48px) 0 7vw; }
.roll h2 { font: 400 clamp(2.4rem, 5.5vw, 4.4rem)/1 var(--serif); margin: 0 0 6vh; }
.roll-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 40vw), 1fr));
  gap: clamp(22px, 3.5vw, 48px) clamp(18px, 3vw, 40px); align-items: start;
}
.roll .print { position: relative; right: auto; left: auto; top: auto; width: auto; margin: 0; transform: none; }
.roll .print:nth-child(3n + 2) { margin-top: 5vh; }
.roll .print:nth-child(4n) img { aspect-ratio: 3 / 4; }

/* ---------- the end ---------- */
/* the closing words sit low on their screen, just above the footer, so they never slide up under the top bar */
.finale { position: relative; min-height: 100vh; min-height: 100svh; display: grid; align-items: end; }
.finale-inner { padding: 12vh calc(var(--stake-w) + 40px) 7vh 7vw; max-width: 48rem; }  /* on the snow, below the lit windows */
.finale h2 { font: 400 clamp(2.8rem, 7vw, 5.4rem)/.95 var(--serif); margin: 0 0 28px; }
/* over the snowy cabin: a soft halo (like the scene words) and a deeper amber, so they read over the lit windows */
.finale-inner { text-shadow: 0 0 3px color-mix(in srgb, var(--tod-bg) 60%, transparent), 0 1px 22px color-mix(in srgb, var(--tod-bg) 85%, transparent); }
.finale .eyebrow { color: color-mix(in srgb, var(--amber) 55%, var(--tod-ink)); }

/* ---------- Site-wide navigation (2026-10-06, from the ourtahoecabin.com project) ---------- */
/* Top bar: the Home pill (back to ourtahoecabin.com; the cabin's sand-gold pine is the same on every site) and the
   title. Hidden until the opener's tagline arrives (app.js), so it never sits on the drawing. Clear of the weekend
   rail (14vh down) and the snow stake (right edge); stays at the footer, where Home is handy. */
.topbar {
  position: fixed; z-index: 8; top: 0; left: 0; display: flex; align-items: center;
  padding: max(14px, env(safe-area-inset-top)) 16px 14px; pointer-events: none;
  opacity: 0; visibility: hidden;
}
.topbar a { pointer-events: auto; }
.top-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.home-pill {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 9px 14px 9px 11px; border-radius: 999px;
  border: 1px solid rgba(235, 218, 166, .5); background: rgba(12, 9, 7, .55);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  font: 500 .66rem/1 var(--sans); letter-spacing: .22em; text-transform: uppercase; color: #ebdaa6;
  text-decoration: none; transition: background .3s, color .3s, border-color .3s;
}
.home-pill svg { width: 12px; height: 17px; fill: currentColor; }
.home-pill:hover, .home-pill:focus-visible { background: #ebdaa6; border-color: #ebdaa6; color: #0f0b09; }
.brand {
  font: 400 1.35rem/1 var(--serif); color: var(--tod-ink); text-decoration: none; white-space: nowrap;
  text-shadow: 0 0 3px color-mix(in srgb, var(--tod-bg) 70%, transparent), 0 1px 12px color-mix(in srgb, var(--tod-bg) 90%, transparent);
}
@media (max-width: 560px) { .top-left { gap: 8px; } .home-pill { padding: 8px 10px; } .home-pill span { display: none; } }

/* ---------- footer (same as the fire guide's, in Snowed In's fireside colors) ---------- */
.cabin-foot {
  position: relative; z-index: 5; background: #0f0b09; color: #9a8b7a;
  border-top: 1px solid rgba(201, 163, 106, .28); font: 300 .95rem/1.5 var(--sans);
  padding: 44px calc(var(--stake-w) + 40px) calc(env(safe-area-inset-bottom, 0px) + 30px) 7vw;
}
.cf-grid { max-width: 1100px; display: grid; grid-template-columns: 1.4fr 1fr 1.2fr; gap: 28px 44px; }
.cf-brand { display: grid; gap: .55rem; align-content: start; justify-items: start; }
.cf-brand p { margin: 0; }
.cf-home { display: grid; gap: .55rem; justify-items: start; text-decoration: none; }
.cf-home svg { width: 20px; height: 28px; fill: #ebdaa6; }
.cf-name { font: 400 1.9rem/1 var(--serif); color: #f3e6d0; }
.cf-col { display: grid; gap: .65rem; align-content: start; }
.cf-h { margin: 0 0 .2rem; font: 500 .66rem/1 var(--sans); letter-spacing: .22em; text-transform: uppercase; color: #c9a36a; }
.cf-col a { color: #f3e6d0; text-decoration: none; }
.cf-col a[aria-current] { color: var(--amber); }
.cf-col a:hover, .cf-home:hover .cf-name { text-decoration: underline; text-underline-offset: 4px; }
.cf-fine { max-width: 1100px; margin: 36px 0 0; font-size: .8rem; }
/* the stake and the weekend rail step out of the footer's way */
body.at-foot .stake, body.at-foot .rail { opacity: 0 !important; transition: opacity .4s; pointer-events: none; }

/* ---------- phones ---------- */
@media (max-width: 760px) {
  :root { --stake-w: 40px; }
  body { font-size: 16px; }
  .title-block, .chapter-block { left: 20px; right: calc(var(--stake-w) + 16px); bottom: 14vh; }
  .chapter-open .chapter-block { top: 9vh; bottom: auto; }
  #open .chapter-block { bottom: 19vh; }
  .layer[data-id="finale"] video { object-position: 24% 50%; }  /* a tall screen shows a narrow slice: keep the A-frame in it */  /* room between "Already blanketed in snow." and the Scroll down cue */
  .card { left: 20px; right: 0; top: 58vh; }
  .print { width: 58vw; margin-top: 20px; }
  .scene { height: 200vh; }
  .scene.card-low .card { top: 112vh; }
  .stake { right: 10px; height: 34vh; }
  .stake-num { font-size: 1.5rem; }
  .stake-time { display: none; }
  .stake-ticks { display: none; }
  .stake-gift { font-size: 1.4rem; }
  .stake-callout { font-size: 1.05rem; right: calc(100% + 10px); }
  .stake-sign { font-size: .4rem; padding: 3px 4px 2px; }
  .rail { left: 8px; top: 18vh; bottom: 18vh; }
  .rail-stop span { display: none; }
  .rail-stop::before { width: 8px; left: 3px; }
  .finale-inner { padding: 12vh calc(var(--stake-w) + 20px) 12vh 20px; }
  .tip { right: 16px; bottom: 16px; }
  .map-inner { grid-template-columns: 1fr; align-content: center; padding: 0 calc(var(--stake-w) + 16px) 0 20px; gap: 12px; }
  .map { max-height: 48vh; }
  .cabin-foot { padding: 40px 20px calc(env(safe-area-inset-bottom, 0px) + 28px); }
  .cf-grid { grid-template-columns: 1fr 1fr; }
  .cf-brand { grid-column: 1 / -1; }
}

@media (prefers-reduced-motion: reduce) {
  .m-lights circle { animation: none; opacity: .9; }
  .grain { animation: none; }
  .kicker, h1 { animation-duration: .01s; animation-delay: 0s; }
}
