:root {
  color-scheme: only light;
  --ink: #2B211B;
  --ink-strong: #241A14;
  --ink-soft: #4A3B31;
  --ink-mute: #6B5748;
  --paper: #FAF4EA;
  --runway: 88px; /* the faded strip along the bottom where Scout runs: its top is about two of his head-heights above him */
  --rule: #E5D6C0;
  --edge: #DBC8AE;
  --accent: #A8571F;
  --accent-dark: #8F4818;
  --serif: Newsreader, Georgia, "Times New Roman", serif;
  --sans: Karla, system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; }
html {
  margin: 0; padding: 0;
  background-color: var(--paper) !important;
  scroll-behavior: smooth;
}
body {
  margin: 0; padding: 0;
  min-height: 100vh;
  background-color: var(--paper) !important;
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
a { color: var(--accent); text-decoration-color: rgba(168,87,31,.35); text-underline-offset: 3px; }
a:hover { color: #7C3D12; text-decoration-color: rgba(124,61,18,.8); }
img, video { display: block; max-width: 100%; }

section, header, footer, .hero-bg, .band { background-color: var(--paper); }

.wrap { max-width: 1120px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 56px); }
.narrow { max-width: 720px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 40px); }
.mid { max-width: 1000px; margin: 0 auto; padding: 0 clamp(20px, 5vw, 48px); }

.eyebrow {
  font-family: var(--sans);
  font-size: 12px; font-weight: 600;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--accent-dark);
}
.label {
  font-family: var(--sans);
  font-size: 13px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-mute);
}

/* ---- navigation ----
   .topnav sits in normal flow and scrolls away with the hero. Once it's
   scrolled out of view (tracked in script.js via IntersectionObserver),
   .sidenav takes over: the same bar, rotated — writing-mode flips its
   layout axis and its text together, so it becomes a full-height strip
   docked on the left instead of a full-width strip docked on top. It
   slides back out once .topnav is back on screen. */
.topnav, .sidenav {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: clamp(20px, 4vw, 48px);
  background: var(--paper);
}
.topnav {
  padding: 18px clamp(20px, 5vw, 48px);
  border-bottom: 1px solid var(--rule);
}
.topnav a, .sidenav a {
  font-family: var(--sans);
  font-size: 12px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-mute); text-decoration: none; white-space: nowrap;
  transition: color 200ms ease;
}
.topnav a:hover, .sidenav a:hover { color: var(--accent); }
/* The section on screen (script.js, markNav()). */
.topnav a[aria-current], .sidenav a[aria-current] {
  color: var(--accent);
  text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 6px;
}

.sidenav {
  position: fixed; top: 0; left: 0; height: 100vh; z-index: 500;
  align-items: center;
  padding: clamp(20px, 5vw, 48px) 18px;
  writing-mode: vertical-rl;
  border-right: 1px solid var(--rule);
  transform: translateX(-100%);
  transition: transform 320ms cubic-bezier(.2,.7,.3,1), box-shadow 320ms;
}
/* Keep the "Top" arrow pointing up inside the sideways text. */
.sidenav .to-top span { text-orientation: upright; }
/* Shadow only while shown, so it doesn't peek in from off-screen. */
.sidenav.visible { transform: translateX(0); box-shadow: 8px 0 28px rgba(64,42,25,.15); }
@media (max-width: 640px) {
  .sidenav { padding: 14px 12px; gap: clamp(14px, 4vw, 28px); }
  .sidenav a { font-size: 11px; letter-spacing: .14em; }
}

/* ---- scenes ----
   script.js adds .scenes to <html>; without it the page scrolls as one
   normal document. With it, each .scene is a full-screen layer and only
   the active one is shown. A scene taller than the screen scrolls inside
   itself; scrolling past either end fades to the neighbouring scene. The
   outgoing scene fades first, then the incoming one fades in after a delay,
   so the two never overlap. --shift (set in script.js) is the direction of
   the slight drift. */
html.scenes, html.scenes body { height: 100%; overflow: hidden; overscroll-behavior: none; }
.scenes .hairline { display: none; }
.scenes .scene {
  position: fixed; inset: 0; z-index: 1;
  display: flex; flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
  background-color: var(--paper);
  opacity: 0; visibility: hidden;
  transform: translateY(var(--shift, 24px));
  transition: opacity 450ms ease, transform 450ms ease, visibility 0s linear 450ms;
}
.scenes .scene.is-active {
  z-index: 2; opacity: 1; visibility: visible; transform: none;
  transition: opacity 650ms ease 380ms, transform 650ms cubic-bezier(.2,.7,.3,1) 380ms, visibility 0s;
}
.scenes .scene > * { flex: none; }
/* Room at the bottom for the running Scout (.runway, .runner). */
.scenes .scene { padding-bottom: var(--runway); }
.scenes .scene > :last-child { margin-block: auto; }
.scenes .band { border-top: 0; }
/* Keep the whole slideshow (header, photo, caption, controls) on one screen. */
.scenes #slideshow-stage { max-height: calc(100vh - 428px - var(--runway)); min-height: 200px; } /* 48px of it for the stack peeking out */
@media (prefers-reduced-motion: reduce) {
  .scenes .scene { transform: none !important; }
}
/* One dot per scene down the right edge (added by script.js). */
.scene-dots {
  position: fixed; right: clamp(8px, 1.6vw, 22px); top: 50%; z-index: 600;
  transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 2px;
}
.scene-dots button {
  width: 22px; height: 22px; padding: 0;
  display: grid; place-items: center;
  border: none; background: none; cursor: pointer;
}
.scene-dots button::after {
  content: ""; width: 7px; height: 7px; border-radius: 999px;
  border: 1px solid #B89D7C; background: transparent;
  transition: background 300ms, border-color 300ms, transform 300ms;
}
.scene-dots button:hover::after { border-color: var(--accent); }
.scene-dots button[aria-current="true"]::after { background: var(--accent); border-color: var(--accent); transform: scale(1.3); }

.hero {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
  padding-top: clamp(28px, 6vw, 72px);
  padding-bottom: clamp(40px, 7vw, 88px);
}
.hero h1 {
  margin: clamp(14px, 2.5vw, 26px) 0 0;
  font-size: clamp(76px, 15vw, 168px);
  line-height: .82; font-weight: 300;
  letter-spacing: -.035em; color: var(--ink-strong);
}
.hero-rule { margin-top: clamp(20px, 3vw, 34px); display: flex; align-items: center; gap: 16px; }
.hero-rule span.line { height: 1px; width: clamp(36px, 8vw, 72px); background: var(--edge); }
.hero blockquote {
  margin: clamp(22px, 3.5vw, 36px) 0 0;
  font-size: clamp(21px, 2.4vw, 27px); line-height: 1.5;
  font-style: italic; font-weight: 300; color: var(--ink-soft);
  max-width: 30ch; text-wrap: pretty;
}
.frame { position: relative; width: min(100%, 300px); margin-inline: auto; }
.frame::before {
  content: ""; position: absolute;
  inset: clamp(14px,3vw,26px) calc(-1 * clamp(10px,2vw,20px)) calc(-1 * clamp(14px,3vw,26px)) clamp(10px,2vw,20px);
  border: 1px solid #E2D2BC; border-radius: 4px;
}
.frame img {
  position: relative; width: 100%;
  aspect-ratio: 499 / 934; object-fit: cover; object-position: center center;
  background-color: var(--paper); border-radius: 3px;
}
/* Hidden until script.js knows which photo it is (media/page_files.txt). */
.scenes #top .frame img { opacity: 0; transition: opacity 500ms ease; }
.scenes #top .frame img.is-ready { opacity: 1; }

.hairline { height: 1px; background: linear-gradient(90deg, rgba(217,196,166,0) 0%, #D9C4A6 50%, rgba(217,196,166,0) 100%); }

.dedication { padding: clamp(52px, 9vw, 112px) 0; }
.dedication p { white-space: pre-line; } /* keep single line breaks from media/memorial.txt */
/* The first paragraph (whatever comes before it in memorial.txt) is the
   large one with the drop letter. */
.dedication p:first-of-type {
  margin: 0; font-size: clamp(22px, 2.6vw, 29px); line-height: 1.62;
  font-weight: 300; color: #33271F; text-wrap: pretty;
}
.dedication p:first-of-type::first-letter {
  font-size: 3.4em; line-height: .8; float: left;
  padding: .06em .18em 0 0; color: var(--accent); font-weight: 300;
}
.dedication p:not(:first-of-type) {
  margin: clamp(26px, 4vw, 40px) 0 0;
  font-size: clamp(19px, 2.1vw, 23px); line-height: 1.72;
  font-weight: 300; color: var(--ink-soft); text-wrap: pretty;
}
.dedication > :not(:first-child) { margin-top: clamp(26px, 4vw, 40px); }
.dedication strong { font-weight: 600; color: #33271F; }
.dedication h3 {
  margin: 0; clear: both;
  font-size: clamp(24px, 2.8vw, 31px); line-height: 1.3; font-weight: 400;
  font-style: italic; color: #33271F; text-wrap: balance;
}
.dedication > h3:not(:first-child) { margin-top: clamp(40px, 6vw, 60px); }
/* Blank (just the paper colour) until script.js has the scenes set up. */
html.is-loading body > * { visibility: hidden !important; }
/* The photos in it are styled in memorial.css. */
@media (min-width: 700px) {
  .dedication::after { content: ""; display: block; clear: both; }
}

.band { border-top: 1px solid var(--rule); }
.band-inner, .section { padding-top: clamp(44px, 7vw, 88px); padding-bottom: clamp(44px, 7vw, 88px); }
.head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 12px 20px;
  margin-bottom: clamp(22px, 3.5vw, 34px);
}
.head h2 {
  margin: 0; font-size: clamp(30px, 4.4vw, 46px);
  font-weight: 300; letter-spacing: -.02em; color: var(--ink-strong);
}

/* ---- Moments (#moments; once the slideshow, so its parts are still ss-*;
   markup in index.html, behaviour in script.js) ---- */
/* A stack of cards: script.js (cardStack) places each
   card with an inline transform and z-index, the top one .is-top and the
   ones below peeking out under it. .is-out is the top card's slide out to
   the left, before it tucks under the bottom. Only the top card takes
   clicks. */
.card-stack { position: relative; margin-bottom: 48px; }
.card-stack .stack-card {
  position: absolute; inset: 0; transform-origin: 50% 100%;
  transition: transform 600ms cubic-bezier(.2,.7,.2,1), opacity 600ms ease;
}
.card-stack .stack-card.is-out { transition: transform 450ms cubic-bezier(.4,0,.7,.2), opacity 450ms ease; }
.card-stack .stack-card:not(.is-top) { pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .card-stack .stack-card, .card-stack .stack-card.is-out { transition: none; } }

#slideshow-stage { position: relative; width: 100%; aspect-ratio: 3 / 2; touch-action: pan-y; }
.ss-card {
  background-color: var(--paper); border: 1px solid var(--rule); border-radius: 3px;
  overflow: hidden; box-shadow: 0 10px 28px rgba(64,42,25,.14);
}
.ss-card img, .ss-card video { display: block; width: 100%; height: 100%; object-fit: contain; background-color: var(--paper); }
/* Only the top card's sound button shows; a card that failed to load shows .ss-missing. */
.ss-card:not(.is-top) .sound, .ss-card.is-empty .sound { display: none; }
.ss-missing {
  position: absolute; inset: 0; display: none; place-items: center; padding: 24px;
  text-align: center; font-size: clamp(18px,2vw,22px); font-weight: 300; color: var(--ink-soft);
  background-color: var(--paper);
}
.ss-card.is-empty .ss-missing { display: grid; }
/* Fills over each photo's time on screen; script.js drives it. */
.ss-progress {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 10000; height: 3px; /* over every card */
  background: rgba(219,200,174,.45); pointer-events: none;
}
.ss-progress[hidden] { display: none; }
.ss-progress i { display: block; height: 100%; width: 0; background: var(--accent); opacity: .75; transition: width 260ms linear; }
@keyframes ss-fill { from { width: 0; } to { width: 100%; } }
.ss-caption {
  margin: 12px 0 0; min-height: 1.5em; text-align: center;
  font-size: clamp(17px, 1.9vw, 20px); line-height: 1.5;
  font-style: italic; font-weight: 300; color: var(--ink-soft);
}
#slideshow-stage .ss-empty {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: 24px; text-align: center;
  font-family: var(--sans); font-size: 14px; color: var(--ink-mute);
}
.ss-controls {
  margin-top: clamp(18px, 2.6vw, 26px);
  display: flex; align-items: center; justify-content: center;
  gap: clamp(12px, 3vw, 28px);
}
.ss-arrow {
  width: 48px; height: 48px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--edge); border-radius: 999px;
  background: var(--paper); color: var(--ink-mute); cursor: pointer;
  transition: background 200ms, color 200ms, border-color 200ms, transform 200ms;
}
.ss-arrow:hover { background: var(--accent); border-color: var(--accent); color: #FFF8EC; }
.ss-arrow:first-child:hover { transform: translateX(-2px); }
.ss-arrow:last-child:hover { transform: translateX(2px); }
/* Arrows stay on one row; with many slides the dots wrap between them. */
.ss-dots { display: flex; align-items: center; gap: 4px 12px; flex-wrap: wrap; justify-content: center; min-width: 0; }
@media (max-width: 640px) { .ss-dots { gap: 4px 6px; } }
/* Lots of dots: script.js shows a sliding window of them (windowDots). */
.ss-dots.is-windowed { display: block; overflow: hidden; flex: none; }
.dot-strip { display: flex; gap: inherit; transition: transform 300ms ease; }
.dot-strip button { transition: transform 300ms ease; }
.ss-dots button {
  width: 22px; height: 22px; padding: 0; flex: none;
  display: grid; place-items: center;
  border: none; background: none; cursor: pointer;
}
.ss-dots button i {
  display: block; width: 8px; height: 8px; border-radius: 999px;
  background: #CDB79A; transition: background 300ms, transform 300ms;
}
.ss-dots button[aria-current="true"] i { background: var(--accent); transform: scale(1.35); }
.ss-count { font-family: var(--sans); font-size: 12px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-mute); }
.ss-hint { margin: clamp(14px,2vw,20px) 0 0; text-align: center; font-family: var(--sans); font-size: 13px; color: var(--ink-mute); }

/* ---- video ---- */
/* Sound toggle over the corner of a video in Moments (script.js). */
.sound {
  position: absolute; top: 12px; right: 12px; z-index: 1;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px 8px 12px; border: 0; border-radius: 999px;
  background: rgba(20,15,10,.55); color: #FFF8EC; cursor: pointer;
  font: 600 12px var(--sans); letter-spacing: .08em;
  transition: background 200ms;
}
.sound:hover { background: rgba(20,15,10,.75); }
.sound .waves { display: none; }
.sound[aria-pressed="true"] .waves { display: inline; }
.sound[aria-pressed="true"] .cross { display: none; }
.caption {
  margin: clamp(16px,2.4vw,22px) 0 0;
  font-size: clamp(18px,2vw,21px); line-height: 1.7;
  font-weight: 300; font-style: italic; color: var(--ink-soft);
  max-width: 52ch; text-wrap: pretty;
}

.ss-controls[hidden] { display: none; }

/* ---- gallery grid (filled by script.js from media/) ----
   .masonry: rows are 1px and script.js makes each tile span its own height
   in rows, so photos keep their shape; the gap below a tile is its margin. */
.photo-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(6px, 1.4vw, 14px);
}
.photo-grid.masonry { grid-auto-rows: 1px; row-gap: 0; align-items: start; }
.photo-grid.masonry button { aspect-ratio: auto; height: auto; margin-bottom: clamp(6px, 1.4vw, 14px); }
.photo-grid.masonry img, .photo-grid.masonry video { height: auto; }
@media (max-width: 640px) { .photo-grid { grid-template-columns: repeat(2, 1fr); } }
.photo-grid button {
  display: block; padding: 0; aspect-ratio: 1;
  border: 1px solid var(--rule); border-radius: 3px; overflow: hidden;
  background: var(--paper); cursor: zoom-in;
  position: relative;
}
.photo-grid img, .photo-grid video {
  display: block; width: 100%; height: 100%; object-fit: cover;
  transition: transform 400ms cubic-bezier(.2,.7,.3,1);
}
.photo-grid button:hover img, .photo-grid button:hover video { transform: scale(1.04); }
/* Play badge on video tiles: a soft dark circle with a white triangle. */
.photo-grid .play, .memory-card-photo .play {
  position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; margin: -22px 0 0 -22px;
  border-radius: 999px; background: rgba(20,15,10,.55);
  pointer-events: none;
}
.photo-grid .play::after, .memory-card-photo .play::after {
  content: ""; position: absolute; left: 17px; top: 13px;
  border-style: solid; border-width: 9px 0 9px 15px;
  border-color: transparent transparent transparent #fff;
}
.photo-grid .ss-empty {
  grid-column: 1 / -1; padding: 24px; text-align: center;
  font-family: var(--sans); font-size: 14px; color: var(--ink-mute);
}

/* ---- "More of Scout": the sign-in for guests, in place of Memories,
   Moments and the Gallery (guestPage() in script.js) ---- */
.access p { max-width: 34em; }
.access .access-form input { max-width: 420px; }
.access .access-form input:first-of-type { margin-top: 0; }
.access-hint { margin: 0 0 12px; font-family: var(--sans); font-size: 15px; color: var(--ink-soft); }
.access-tabs { display: flex; flex-wrap: wrap; gap: 10px; margin: 22px 0 18px; }
.access-tabs button {
  padding: 9px 18px; border: 1px solid var(--edge); border-radius: 999px;
  background: #FFFDF8; color: var(--ink-soft); cursor: pointer;
  font: 600 12px var(--sans); letter-spacing: .12em; text-transform: uppercase;
}
.access-tabs button:hover { border-color: var(--accent); color: var(--accent); }
.access-tabs button[aria-pressed="true"] { border-color: var(--accent); background: var(--accent); color: #FFF8EC; }
.access-form input[hidden] { display: none; }

/* ---- the goodbye at the very end (#goodbye; goodbye() in script.js) ---- */
.goodbye { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; min-height: 100%; box-sizing: border-box; }
.goodbye-photo { position: relative; margin: 0 0 clamp(28px, 5vh, 48px); }
.goodbye-photo[hidden] { display: none; }
.goodbye-photo::before { content: ""; position: absolute; inset: 0; transform: translate(14px, 14px); border: 1px solid var(--edge); pointer-events: none; }
.goodbye-photo img { position: relative; display: block; max-width: min(100%, 520px); max-height: 58vh; width: auto; height: auto; cursor: zoom-in; }
.goodbye-words { margin: 0; max-width: 28em; font: 300 italic clamp(22px, 3vw, 32px)/1.45 var(--serif); color: var(--ink); }

/* ---- messages (form + approved list; backend in server/messages.js) ---- */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.message-form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.message-form textarea, .message-form #message-name, .access-form input {
  display: block; width: 100%; resize: vertical; min-height: 120px;
  padding: 14px 16px; border: 1px solid var(--edge); border-radius: 3px;
  background: #FFFDF8; color: var(--ink);
  font: 300 clamp(17px, 1.9vw, 19px)/1.6 var(--serif);
}
.message-form #message-name, .access-form input { min-height: 0; margin-top: 10px; padding: 10px 16px; font-size: clamp(16px, 1.7vw, 17px); }
.message-form textarea:focus, .message-form #message-name:focus, .access-form input:focus { outline: 2px solid rgba(168,87,31,.35); outline-offset: 1px; border-color: var(--accent); }
/* "Add photos or videos": the file input sits invisibly over its label;
   script.js shows each picked file in .attach-list with a remove button. */
.message-photo { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 12px; }
.photo-pick {
  position: relative; display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px; border: 1px solid var(--edge); border-radius: 999px;
  background: #FFFDF8; color: var(--ink-soft); cursor: pointer;
  font: 600 12px var(--sans); letter-spacing: .12em; text-transform: uppercase;
}
.photo-pick:hover { border-color: var(--accent); color: var(--accent); }
.photo-pick input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.photo-pick:focus-within { outline: 2px solid rgba(168,87,31,.35); outline-offset: 2px; }
.attach-list { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; }
.attach-list[hidden] { display: none; }
.attach { position: relative; }
.attach img, .attach video { display: block; width: 96px; height: 96px; object-fit: cover; border-radius: 3px; border: 1px solid var(--rule); background: #F1E6D4; }
.attach-badge {
  position: absolute; left: 6px; bottom: 6px; padding: 1px 6px; border-radius: 999px;
  background: rgba(43,33,27,.7); color: #FFF8EC; font-family: var(--sans); font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
}
.attach button {
  position: absolute; top: -10px; right: -10px; width: 26px; height: 26px; padding: 0;
  border: 1px solid var(--edge); border-radius: 999px; background: var(--paper); color: var(--ink-soft);
  font-size: 16px; line-height: 1; cursor: pointer;
}
.message-note { margin: 14px 0 0; font-family: var(--sans); font-size: 13px; color: var(--ink-mute); }
.message-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; margin-top: 14px; }
.message-send {
  padding: 12px 22px; border: 1px solid var(--accent); border-radius: 999px;
  background: var(--accent); color: #FFF8EC; cursor: pointer;
  font: 600 12px var(--sans); letter-spacing: .16em; text-transform: uppercase;
  transition: background 200ms, border-color 200ms;
}
.message-send:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.message-send:disabled { opacity: .6; cursor: default; }
.message-status { margin: 0; font-family: var(--sans); font-size: 14px; color: var(--ink-mute); }
.message-list { margin-top: clamp(32px, 5vw, 48px); }
.message-list .msg { padding: 20px 0; border-top: 1px solid var(--rule); }
.message-list .msg p {
  margin: 0; white-space: pre-line; overflow-wrap: anywhere;
  font-size: clamp(18px, 2vw, 21px); line-height: 1.65; font-weight: 300; color: var(--ink-soft);
}
/* A message's photos and videos: one shows at its own shape; several make
   a grid of squares (a photo opens full size in a new tab). */
.message-list .msg-media { margin-top: 12px; }
.message-list .msg p + .msg-media { margin-top: 14px; }
.message-list .msg-photo { display: block; }
.message-list .msg-photo img, .message-list .msg-video { display: block; max-width: 100%; max-height: 420px; border-radius: 3px; border: 1px solid var(--rule); }
.message-list .msg-video { width: 100%; background: #000; }
.message-list .msg-media.is-multi { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.message-list .is-multi .msg-photo img, .message-list .is-multi .msg-video { width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; }
.message-list .is-multi .msg-video { object-fit: contain; }
.message-list .msg .msg-name { margin-top: 10px; font-size: clamp(16px, 1.7vw, 18px); font-style: italic; color: var(--ink-soft); }
.message-list .msg .msg-name + time { margin-top: 4px; }
.message-list .msg time { display: block; margin-top: 8px; font-family: var(--sans); font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-mute); }

/* ---- lightbox ----
   .lightbox always scrolls; .lightbox-viewport always centers its
   content via flex. When the image is smaller than the viewport, the
   viewport (min-width/height: 100%) matches the screen and the flex
   centering puts it in the middle. When zoomed past that, the viewport
   grows to fit the image and .lightbox scrolls to reveal it — the image
   never has to jump between "centered" and "pinned to a corner" layouts. */
/* ---- Memories: a grid of cards (script.js builds them from
   media/memories.json); tapping one opens it in #memory-view ---- */
#memories { width: 100%; }
.memory-grid {
  display: grid; gap: clamp(16px, 2.4vw, 26px);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr));
}
.memory-card {
  display: flex; flex-direction: column; text-align: left; padding: 10px 10px 14px;
  border: 1px solid var(--rule); border-radius: 3px; background: #FFFDF8; color: var(--ink);
  box-shadow: 0 1px 2px rgba(64,42,25,.06), 0 8px 22px rgba(64,42,25,.1);
  font: inherit; cursor: pointer;
  transition: transform 300ms cubic-bezier(.2,.7,.3,1), box-shadow 300ms;
}
.memory-card:hover, .memory-card:focus-visible { transform: translateY(-3px); box-shadow: 0 2px 4px rgba(64,42,25,.08), 0 14px 32px rgba(64,42,25,.16); }
.memory-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.memory-card-photo { position: relative; display: block; aspect-ratio: 4 / 3; overflow: hidden; border-radius: 2px; background: #EFE3D2; }
.memory-card-photo img, .memory-card-photo canvas, .memory-card-photo video { display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
/* A GIF or video as the main picture is a still of it, marked; and how many
   pictures there are, when there's more than one. */
.memory-card-gif, .memory-card-count {
  position: absolute; display: flex; align-items: center; gap: 5px;
  padding: 3px 8px; border-radius: 999px; background: rgba(20,15,10,.6); color: #FFF8EC;
  font: 600 11px/1.4 var(--sans); letter-spacing: .08em; pointer-events: none;
}
.memory-card-gif { left: 8px; top: 8px; }
.memory-card-count { right: 8px; bottom: 8px; }
.memory-card-title {
  display: block; margin-top: 12px; padding: 0 4px;
  font-size: clamp(19px, 2vw, 22px); line-height: 1.3; font-style: italic; font-weight: 400; color: var(--ink-strong);
}
.memory-card.no-photo { justify-content: flex-start; min-height: 180px; padding: 18px 16px; }
.memory-card.no-photo .memory-card-title { margin-top: 0; }
.memory-card-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 5; line-clamp: 5; overflow: hidden;
  margin-top: 10px; padding: 0 4px; font-size: 16px; line-height: 1.55; font-weight: 300; color: var(--ink-soft);
}
.memory-empty { margin: 0; font-style: italic; font-weight: 300; font-size: clamp(18px, 2vw, 21px); color: var(--ink-mute); }
@media (max-width: 640px) {
  .memory-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .memory-card { padding: 7px 7px 10px; }
  .memory-card-title { font-size: 16px; margin-top: 8px; padding: 0 2px; }
  .memory-card-text { font-size: 14px; -webkit-line-clamp: 4; line-clamp: 4; }
  .memory-card.no-photo { min-height: 0; padding: 12px 10px; }
}

/* One memory, over the page. */
.memory-view {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center; padding: clamp(12px, 4vw, 40px);
  background: rgba(36,26,20,.55);
}
.memory-view[hidden] { display: none; }
.mv-card {
  position: relative; width: min(100%, 680px); max-height: 100%; overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper); border-radius: 4px; box-shadow: 0 30px 80px rgba(0,0,0,.35);
}
.mv-close {
  position: sticky; top: 10px; float: right; z-index: 1; margin: 10px 10px -50px 0;
  width: 40px; height: 40px; border: 0; border-radius: 999px;
  background: rgba(250,244,234,.92); color: var(--ink); font-size: 26px; line-height: 1; cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
/* A memory's pictures: one on its own, or (.is-slides) a slideshow of
   them, one on top at a time, with arrows and dots under it. */
.mv-stage { position: relative; isolation: isolate; }
.mv-photo:not(.is-slides) .mv-slide img { display: block; width: 100%; max-height: 60vh; object-fit: cover; }
.mv-photo:not(.is-slides) .mv-slide video { display: block; width: 100%; max-height: 60vh; background: #1C1612; }
.mv-photo.is-slides .mv-stage { aspect-ratio: 4 / 3; max-height: 62vh; width: 100%; overflow: hidden; background: #2A211B; }
.mv-photo.is-slides .mv-slide {
  position: absolute; inset: 0; opacity: 0; visibility: hidden;
  transition: opacity 600ms ease, visibility 0s 600ms;
}
.mv-photo.is-slides .mv-slide.is-on { opacity: 1; visibility: visible; z-index: 1; transition: opacity 600ms ease; }
.mv-photo.is-slides .mv-slide img, .mv-photo.is-slides .mv-slide video { display: block; width: 100%; height: 100%; object-fit: contain; }
.mv-slide { position: relative; }
.mv-slide .sound { right: auto; left: 12px; } /* the × is in the top right */
.mv-slide .ss-missing { display: none; position: absolute; inset: 0; place-items: center; padding: 20px; text-align: center; background: var(--paper); font-family: var(--sans); font-size: 14px; color: var(--ink-mute); }
.mv-slide.is-missing .ss-missing { display: grid; }
.mv-photo .ss-progress { z-index: 2; }
.mv-strip {
  display: flex; align-items: center; justify-content: center; gap: clamp(10px, 3vw, 22px);
  padding: 12px clamp(12px, 4vw, 30px) 0;
}
.mv-strip .ss-arrow { width: 40px; height: 40px; }
.mv-photo[hidden] { display: none; }
.mv-body { padding: clamp(22px, 4vw, 36px) clamp(20px, 5vw, 44px) 8px; }
.mv-title { margin: 0 44px 14px 0; font-size: clamp(26px, 3.4vw, 34px); line-height: 1.2; font-weight: 400; font-style: italic; color: var(--ink-strong); }
.mv-photo:not([hidden]) + .mv-body .mv-title { margin-right: 0; }
.mv-text p { margin: 0 0 1em; font-size: clamp(18px, 2vw, 21px); line-height: 1.7; font-weight: 300; color: var(--ink-soft); text-wrap: pretty; }
.mv-text strong { font-weight: 600; color: #33271F; }
.mv-nav {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px clamp(20px, 5vw, 44px) 22px; border-top: 1px solid var(--rule); margin-top: 8px;
  font-family: var(--sans); font-size: 13px; color: var(--ink-mute);
}
.mv-nav button {
  padding: 8px 14px; border: 1px solid var(--edge); border-radius: 999px; background: var(--paper);
  font: 600 13px var(--sans); color: var(--ink-soft); cursor: pointer;
}
.mv-nav button:hover { background: var(--accent); border-color: var(--accent); color: #FFF8EC; }
.mv-nav button[hidden] { visibility: hidden; display: inline-block; }

.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(20,15,10,.92);
  overflow: auto;
}
.lightbox[hidden] { display: none; }
.lightbox-viewport {
  min-width: 100%; min-height: 100%;
  box-sizing: border-box; padding: 32px;
  display: flex; align-items: center; justify-content: center;
}
.lightbox img {
  max-width: 100%; max-height: calc(100vh - 64px);
  display: block;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
  border-radius: 2px;
  -webkit-user-drag: none; user-select: none;
}
.lightbox.zoomed img {
  max-width: none; max-height: none;
  height: auto;
  box-shadow: none; border-radius: 0;
  cursor: grab;
}
.lightbox.zoomed img.dragging { cursor: grabbing; }
.lightbox video {
  max-width: 100%; max-height: calc(100vh - 64px);
  display: block; background: #000;
  box-shadow: 0 30px 80px rgba(0,0,0,.5);
  border-radius: 2px;
}
.lightbox-close {
  position: fixed; top: 16px; right: 16px; z-index: 1001;
  width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid rgba(250,244,234,.35); border-radius: 999px;
  background: rgba(0,0,0,.4); color: var(--paper);
  font-size: 22px; line-height: 1; cursor: pointer;
}
.lightbox-close:hover { background: rgba(0,0,0,.6); }
.lightbox-caption {
  position: fixed; left: 50%; bottom: 14px; z-index: 1001;
  transform: translateX(-50%); max-width: min(90vw, 640px); margin: 0;
  padding: 8px 16px; border-radius: 999px; background: rgba(0,0,0,.5);
  color: var(--paper); text-align: center;
  font-size: 17px; line-height: 1.45; font-style: italic; font-weight: 300;
}
.lightbox-caption[hidden] { display: none; }

/* ---- scene clips ----
   The transition into a scene with data-clip (script.js): it plays on the
   bare paper background between the old scene fading out and the new one
   fading in, in the middle at 75% opacity, cropped to a square and faded
   out toward the edges in a circle. */
.scene-clip {
  position: fixed; inset: 0; z-index: 700;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 700ms ease, visibility 0s linear 700ms;
}
.scene-clip.show { opacity: 1; visibility: visible; transition: opacity 600ms ease, visibility 0s; }
.scene-clip { display: grid; place-items: center; }
.scene-clip img, .scene-clip video {
  display: block; width: min(80vmin, 560px); aspect-ratio: 1;
  object-fit: cover; opacity: .75;
  transform: scale(.94); transition: transform 700ms ease;
  -webkit-mask-image: radial-gradient(circle closest-side, #000 45%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 45%, transparent 100%);
}
/* On a wide enough screen (desktop, a tablet sideways) the clip is an oval,
   wider than high, with the same soft edge; phones keep the circle. */
@media (min-width: 700px) and (min-aspect-ratio: 5/4) {
  .scene-clip img, .scene-clip video {
    width: min(88vw, 960px, 128vh); aspect-ratio: 16 / 10;
    -webkit-mask-image: radial-gradient(ellipse closest-side, #000 45%, transparent 100%);
    mask-image: radial-gradient(ellipse closest-side, #000 45%, transparent 100%);
  }
}
.scene-clip.show img, .scene-clip.show video { transform: scale(1); transition: transform 1800ms cubic-bezier(.2,.7,.3,1); }

footer { border-top: 1px solid var(--rule); }
footer .mid { padding: clamp(40px,6vw,72px) clamp(20px,5vw,48px); text-align: center; }
footer .name { font-size: clamp(26px,3.6vw,38px); font-weight: 300; font-style: italic; color: #33271F; }
footer .eyebrow { margin-top: 14px; }

/* On a narrow computer window, the docked .sidenav (--nav-w wide, from
   script.js) would cover the left of the content: each container's left
   padding grows to keep it clear (by the width it's short of, given the
   container's centring), and the right padding to keep clear of the dots.
   Wide windows, where the centring leaves room anyway, are unchanged. */
@media (min-width: 641px) {
  .scenes .scene:not(#top) .wrap {
    padding-left: max(clamp(20px, 5vw, 56px), calc(var(--nav-w, 0px) + 24px - max(0px, (100vw - 1120px) / 2)));
    padding-right: max(clamp(20px, 5vw, 56px), calc(52px - max(0px, (100vw - 1120px) / 2)));
  }
  .scenes .scene:not(#top) .narrow {
    padding-left: max(clamp(20px, 5vw, 40px), calc(var(--nav-w, 0px) + 24px - max(0px, (100vw - 720px) / 2)));
    padding-right: max(clamp(20px, 5vw, 40px), calc(52px - max(0px, (100vw - 720px) / 2)));
  }
  .scenes .scene:not(#top) .mid {
    padding-left: max(clamp(20px, 5vw, 48px), calc(var(--nav-w, 0px) + 24px - max(0px, (100vw - 1000px) / 2)));
    padding-right: max(clamp(20px, 5vw, 48px), calc(52px - max(0px, (100vw - 1000px) / 2)));
  }
}

@media (max-width: 640px) {
  /* Wider side padding keeps text clear of the docked .sidenav. */
  .wrap, .narrow, .mid { padding-left: 50px; padding-right: 50px; }
  /* Every scene after the intro has the sidebar docked on the left. */
  .scenes .scene:not(#top) :is(.wrap, .narrow, .mid) { padding-left: 60px; padding-right: 34px; }
  .frame img { aspect-ratio: 499 / 934; }
  .frame { width: min(100%, 260px); }
}

/* Re-assert the light palette if anything tries to serve a dark treatment. */
@media (prefers-color-scheme: dark) {
  html, body { background-color: var(--paper); color: #2B211B; }
  .band, footer, .hero-bg, #slideshow-stage { background-color: var(--paper); }
  .dedication p:first-of-type { color: #33271F; }
  .dedication p:not(:first-of-type), .caption, .hero blockquote { color: #4A3B31; }
  .head h2, .hero h1 { color: #241A14; }
  .label, .ss-count, .ss-hint { color: #6B5748; }
  .eyebrow { color: #8F4818; }
}

/* ---- Scout chasing a frisbee along the bottom (script.js, scoutRun()):
   .runner-go is moved across; .is-running while it moves, .is-left when
   he's heading back, .is-caught from catching it on the last section
   until he's back at the start. Under the clips,
   the memory reader and the lightbox. ---- */
.runner {
  position: fixed; left: 0; right: 0; bottom: max(6px, env(safe-area-inset-bottom)); z-index: 400;
  height: 40px; pointer-events: none;
}
.runner-go { position: absolute; left: 0; bottom: 0; will-change: transform; }
.runner.is-ready .runner-go { transition: transform 1200ms cubic-bezier(.45,.05,.35,1); }
.runner-dog { display: block; width: 58px; height: auto; overflow: visible; transition: transform 250ms ease; }
.runner.is-left .runner-dog { transform: scaleX(-1); }
.runner .leg, .runner .tail { transform-box: fill-box; }
.runner .leg { transform-origin: 50% 0; }
.runner .tail { transform-origin: 100% 100%; animation: runner-wag 1.1s ease-in-out infinite alternate; }
.runner.is-running .leg-a { animation: runner-leg 300ms ease-in-out infinite alternate; }
.runner.is-running .leg-b { animation: runner-leg 300ms ease-in-out infinite alternate-reverse; }
.runner.is-running .dog { animation: runner-bob 300ms ease-in-out infinite alternate; }
.runner.is-caught .tail { animation-duration: 260ms; }
/* The catch: a leap (on .dog, so .is-left's flip still holds), the
   frisbee coming down to his mouth, as long as JUMP_MS in script.js. */
.runner .dog { transform-box: fill-box; transform-origin: 50% 100%; }
.runner.is-jumping .dog { animation: runner-jump 700ms cubic-bezier(.3,.7,.4,1); }
.runner.is-jumping .runner-frisbee { animation: runner-catch 300ms ease-in forwards; }
@keyframes runner-jump {
  0% { transform: translateY(0) rotate(0); }
  20% { transform: translateY(2px) rotate(0); }
  45% { transform: translateY(-24px) rotate(-14deg); }
  80% { transform: translateY(0) rotate(4deg); }
  100% { transform: translateY(0) rotate(0); }
}
@keyframes runner-catch { to { transform: translate(-22px, -20px) rotate(-10deg); } }
/* The bottom of every section fades into the paper, under Scout. */
.runway {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 300; height: var(--runway);
  background: linear-gradient(to bottom, rgba(250,244,234,0), var(--paper) 62%);
  pointer-events: none;
}
.runner .disc { display: none; }
.runner.is-caught .disc { display: inline; }
.runner-frisbee {
  position: absolute; left: calc(100% + 10px); bottom: 24px;
  width: 20px; height: 6px; border-radius: 50%;
  /* his favourite frisbee: neon yellow */
  background: radial-gradient(ellipse closest-side, #E4FF3C 0 55%, #C4E01E 58% 64%, #DDFB2A 67%);
  box-shadow: inset 0 -1.5px 0 rgba(90,100,0,.3), 0 0 0 .5px rgba(64,42,25,.18), 0 6px 6px -4px rgba(64,42,25,.25);
  animation: runner-float 1400ms ease-in-out infinite alternate;
  transition: opacity 300ms;
}
.runner.is-left .runner-frisbee { left: auto; right: calc(100% + 10px); }
.runner.is-caught .runner-frisbee { opacity: 0; transition-duration: 60ms; }
@keyframes runner-leg { from { transform: rotate(30deg); } to { transform: rotate(-30deg); } }
@keyframes runner-bob { from { transform: translateY(0); } to { transform: translateY(-2.5px); } }
@keyframes runner-wag { from { transform: rotate(-10deg); } to { transform: rotate(12deg); } }
@keyframes runner-float { from { transform: translateY(0) rotate(-8deg); } to { transform: translateY(-8px) rotate(6deg); } }
@media (max-width: 640px) {
  .runner-dog { width: 46px; }
  .runner-frisbee { width: 16px; height: 5px; bottom: 19px; }
}
@media (prefers-reduced-motion: reduce) {
  .runner.is-ready .runner-go { transition: none; }
  .runner *, .runner .tail { animation: none !important; }
}

