@import url('https://fonts.googleapis.com/css2?family=Bitter:wght@400;600;700&family=Nunito:ital,wght@0,400;0,600;0,700;0,800;1,400&display=swap');

/* ------------------------------------------------------------------ */
/* One theme, on purpose: a soft daytime look. "Hedgerow" greens from */
/* the Malvern Hills, Annie's coat gold on the waypoints, and her     */
/* collar pink kept rare — the badge bow, the ribbon, the pup's bow.  */
/* Dark mode was removed deliberately (owner decision, Aug 2026).     */
/* ------------------------------------------------------------------ */

:root {
  --paper: #f4f3e7;
  --card: #fbfaf1;
  --stamp-face: #fffef6;
  --ink: #3d4032;
  --ink-muted: #767a68;
  --line: #e3e2d2;
  --moss: #4c7a50;
  --moss-soft: #e0eadb;
  --moss-deep: #3a3b28;
  --gold: #e8c07d;
  --gold-ink: #5a4520;
  --bow: #d98fa0;
  --bow-knot: #c17a8e;
  --bow-ink: #4a2530;
  /* Third pastel, added Aug 2026 (owner decision): a soft periwinkle read as
     "the sky above the hills". Sits in a triad with moss and bow. Atmosphere
     and accents only — like bow, it is never body text or a link colour. */
  --sky: #a8bfe0;
  /* The home panel. Deliberately a few shades down from --paper and tinted
     sage: --card (#fbfaf1) sits so close to the page ground that the panel
     read as no panel at all. Keeps ink at ~8.9:1. */
  --cover-panel: #e7ecda;
  --cover-panel-line: #d3dcc2;
  --mist: rgba(255, 254, 246, 0.6);
  --hills-back-img: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 240' preserveAspectRatio='none'%3E%3Cpath d='M0,240L0,150Q80,90,170,130Q240,160,320,120Q400,78,480,125Q560,165,660,135Q740,110,830,140Q920,168,1010,130Q1090,96,1200,145L1200,240Z' fill='%23d3dab9'/%3E%3C/svg%3E");
  --hills-front-img: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 160' preserveAspectRatio='none'%3E%3Cpath d='M0,160L0,110Q120,70,260,105Q380,135,520,108Q660,82,800,112Q930,138,1060,110Q1130,96,1200,115L1200,160Z' fill='%23b8c49c'/%3E%3C/svg%3E");
  --font-display: 'Bitter', Georgia, 'Times New Roman', serif;
  --font-body: 'Nunito', -apple-system, 'Segoe UI', Roboto, sans-serif;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --shadow-soft: 0 4px 16px rgba(90, 96, 74, 0.12);
  --radius-card: 16px;
  /* Home medallion: a circle with 16 near-semicircular scallops, generated
     rather than hand-drawn (see DESIGN.md). Used as a CSS mask so the fill
     stays in CSS and the shape scales with the element. */
  --scallop-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cpath d='M100 14A17.45 17.45 0 0 1 132.91 20.55A17.45 17.45 0 0 1 160.81 39.19A17.45 17.45 0 0 1 179.45 67.09A17.45 17.45 0 0 1 186 100A17.45 17.45 0 0 1 179.45 132.91A17.45 17.45 0 0 1 160.81 160.81A17.45 17.45 0 0 1 132.91 179.45A17.45 17.45 0 0 1 100 186A17.45 17.45 0 0 1 67.09 179.45A17.45 17.45 0 0 1 39.19 160.81A17.45 17.45 0 0 1 20.55 132.91A17.45 17.45 0 0 1 14 100A17.45 17.45 0 0 1 20.55 67.09A17.45 17.45 0 0 1 39.19 39.19A17.45 17.45 0 0 1 67.09 20.55A17.45 17.45 0 0 1 100 14Z' fill='%23000'/%3E%3C/svg%3E");
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.65;
  font-size: 18px;
}

a { color: var(--moss); }

/* ------------------------------------------------------------------ */
/* Backdrop: the Malvern ridge with slowly drifting mist.             */
/* transform/opacity only; 90s+ cycles (far below the 0.2Hz           */
/* vestibular band); stilled entirely under prefers-reduced-motion.   */
/* ------------------------------------------------------------------ */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.55;
}

.page-home .backdrop { opacity: 1; }
.page-journey .backdrop, .page-album .backdrop { opacity: 0.75; }

.hills {
  position: absolute;
  left: -3%;
  right: -3%;
  bottom: 0;
  background-repeat: repeat-x;
  background-position: bottom;
  background-size: 1200px 100%;
  will-change: transform;
}

.hills-back { height: 200px; background-image: var(--hills-back-img); z-index: 1; }
.hills-front { height: 120px; background-image: var(--hills-front-img); z-index: 3; }

.mist {
  position: absolute;
  bottom: 0;
  height: 45%;
  width: 160%;
  left: -30%;
  z-index: 2;
}

.mist-a {
  background:
    radial-gradient(55% 90% at 30% 100%, var(--mist) 0%, transparent 70%),
    radial-gradient(35% 70% at 75% 100%, var(--mist) 0%, transparent 70%);
  animation: drift-a 90s var(--ease-in-out) infinite alternate;
}

.mist-b {
  background:
    radial-gradient(45% 80% at 55% 100%, var(--mist) 0%, transparent 70%),
    radial-gradient(30% 60% at 10% 100%, var(--mist) 0%, transparent 70%);
  animation: drift-b 130s var(--ease-in-out) infinite alternate;
}

@keyframes drift-a { from { transform: translateX(0); } to { transform: translateX(5%); } }
@keyframes drift-b { from { transform: translateX(0); } to { transform: translateX(-6%); } }

/* ------------------------------------------------------------------ */
/* Header / brand                                                     */
/* ------------------------------------------------------------------ */

.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: 860px;
  margin: 0 auto;
  padding: 22px 20px;
}

.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }

.brand-word {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.15rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.badge { overflow: visible; }
.badge-ring { stroke: var(--moss-deep); }
.badge-text {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 28px;
  fill: var(--moss-deep);
}
.bow path { fill: var(--bow); }
.bow .bow-knot { fill: var(--bow-knot); }

/* The bow ties itself once per visit — most of the delight budget. */
.bow {
  transform-box: fill-box;
  transform-origin: 50% 25%;
  animation: bow-settle 600ms var(--ease-out) 150ms both;
}

@keyframes bow-settle {
  from { opacity: 0; transform: rotate(-12deg) scale(0.85); }
  to { opacity: 1; transform: none; }
}

.site-nav { display: flex; gap: 4px; flex-wrap: wrap; }

.site-nav a {
  text-decoration: none;
  color: var(--ink-muted);
  font-size: 0.88rem;
  font-weight: 700;
  padding: 7px 13px;
  border-radius: 999px;
  transition: color 160ms var(--ease-out), background-color 160ms var(--ease-out), transform 120ms var(--ease-out);
}

.site-nav a.active { color: var(--moss-deep); background: var(--moss-soft); }
.site-nav a:active { transform: scale(0.96); }

@media (hover: hover) and (pointer: fine) {
  .site-nav a:hover { color: var(--moss-deep); background: var(--moss-soft); }
}

/* ------------------------------------------------------------------ */
/* Main / article                                                     */
/* ------------------------------------------------------------------ */

main {
  max-width: 860px;
  margin: 0 auto;
  padding: 28px 20px 170px;
}

main article { animation: article-in 300ms var(--ease-out) both; }

@keyframes article-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 2.1rem;
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}

h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-top: 2em;
}

.post-date {
  font-size: 0.85rem;
  color: var(--ink-muted);
  margin: 0 0 28px;
}
.post-date:empty { display: none; }

/* Reading pages sit on a soft paper card. */
.page-quiet main article {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 34px 36px 46px;
  max-width: 700px;
  margin: 0 auto;
}

article img {
  max-width: 100%;
  height: auto;
  border-radius: 12px;
  margin: 20px 0;
}

/* Deliberately scoped to the Drive video embed this was written for,
   NOT every iframe. Social embeds (Instagram) size their own iframe from
   their script, and a reel is vertical, not 16:9. Forcing this ratio on
   one crops it, and "resetting" it with width/height auto is worse: an
   iframe with no ratio and no dimensions falls back to the CSS default
   replaced-element size of 300x150 and collapses to a sliver. Leave
   social iframes alone entirely and let their own script size them. */
article iframe[src*="drive.google.com"] {
  max-width: 100%;
  border-radius: 12px;
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;
  border: none;
}

/* This wrapper supplies the vertical rhythm the surrounding prose
   expects. embed.js swaps the blockquote for an iframe carrying an
   inline "margin: 0 0 12px", which left-aligns it in the column, so the
   horizontal margins are overridden to centre it. Only those two
   properties: touching width, height or aspect-ratio is what collapsed
   the embed to a 300x150 sliver before. */
.ig-embed {
  margin: 20px 0;
}

.ig-embed iframe,
.ig-embed .instagram-media {
  margin-left: auto !important;
  margin-right: auto !important;
}

/* ------------------------------------------------------------------ */
/* Photos: printed "snaps" taped into the page. Two or more together  */
/* become a picture-book spread — tilted slightly, like a real album. */
/* Tilt lives in --tilt so the scroll reveal can compose with it       */
/* instead of fighting it.                                            */
/* ------------------------------------------------------------------ */

.snap {
  --tilt: 0deg;
  position: relative;
  margin: 30px 0;
  padding: 12px 12px 0;
  background: var(--stamp-face);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow-soft);
  transform: rotate(var(--tilt));
}

.snap img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0;
  border-radius: 5px;
  cursor: zoom-in;
}

.snap figcaption {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--ink-muted);
  text-align: center;
  padding: 11px 6px 13px;
}

.photo-book {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 18px;
  align-items: start;
  margin: 34px 0;
}

.photo-book .snap { margin: 0; }
.photo-book .snap:nth-child(odd) { --tilt: -1.1deg; }
.photo-book .snap:nth-child(even) { --tilt: 1.3deg; }
.photo-book .snap:nth-child(3n) { --tilt: 0.5deg; }

/* Reveal: JS-enhanced. Without JS the snaps are simply visible. */
.js .snap { opacity: 0; transform: rotate(var(--tilt)) translateY(12px); }
.js .snap.seen {
  opacity: 1;
  transform: rotate(var(--tilt));
  transition: opacity 400ms var(--ease-out), transform 400ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .snap { transition: transform 200ms var(--ease-out), box-shadow 200ms var(--ease-out); }
  .snap:hover {
    transform: rotate(0deg) translateY(-3px);
    box-shadow: 0 10px 26px rgba(90, 96, 74, 0.22);
    z-index: 2;
  }
}

/* Tap or click a snap to see it properly. Built by journey.js. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  padding: 22px;
  background: rgba(38, 42, 31, 0.92);
  animation: lightbox-fade 200ms var(--ease-out) both;
}

.lightbox[hidden] { display: none; }

.lightbox img {
  max-width: 100%;
  max-height: 86vh;
  border-radius: 6px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
  animation: lightbox-in 220ms var(--ease-out) both;
}

.lightbox-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 16px;
  padding: 0 26px;
  text-align: center;
  /* Light-on-scrim: the lightbox is the one dark surface on the site. */
  color: var(--paper);
  opacity: 0.82;
  font-size: 0.88rem;
}

.lightbox-close {
  position: absolute;
  top: 14px;
  right: 16px;
  width: 42px;
  height: 42px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
  font-family: var(--font-body);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 160ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .lightbox-close:hover { background: rgba(255, 255, 255, 0.28); }
}

@keyframes lightbox-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lightbox-in {
  from { opacity: 0; transform: scale(0.96); }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ */
/* Home: the scalloped medallion                                       */
/*                                                                     */
/* Replaced the deep-moss "box lid" (owner decision, Aug 2026 — it     */
/* read too dark and too boxy). The page is now light throughout: a    */
/* scalloped photo medallion floats over a soft, heavily-rounded card. */
/* The medallion is the page's contrast anchor now that nothing is     */
/* dark — it earns that by being the only photograph on the page.      */
/* ------------------------------------------------------------------ */

/* The hero: four beats (owner brief, 29 Aug 2026). Beat 1 is the wow —
   no card any more, the hills are the stage. Annie's medallion, her
   name on a pink ribbon, one line of intro, real snaps fanned around
   her. build.js emits its own <h1> inside .hero-ribbon (the template's
   h1 slot renders empty on home). */
.home-hero {
  position: relative;
  text-align: center;
  padding: 44px 0 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

main article > h1:empty { display: none; }

.home-hero .cover-hero {
  width: clamp(210px, 46vw, 300px);
  margin-top: 0;
}

/* The fan: real snaps from the newest entries, tilted like photos
   spilling out of an album. Positions hug the medallion on narrow
   screens (the max() keeps them on-page) and spread out on desktop. */
.hero-snap {
  position: absolute;
  z-index: 1;
  padding: 7px 7px 9px;
  background: var(--stamp-face);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow-soft);
  width: clamp(96px, 13vw, 150px);
  transform: rotate(var(--tilt, 0deg));
}
.hero-snap img { display: block; width: 100%; height: auto; border-radius: 4px; margin: 0; }
.hero-snap--1 { --tilt: -7deg; top: 4%; left: max(2%, calc(50% - 350px)); }
.hero-snap--2 { --tilt: 5deg; top: 44%; left: max(0%, calc(50% - 395px)); }
.hero-snap--3 { --tilt: 6deg; top: 6%; right: max(2%, calc(50% - 350px)); }
.hero-snap--4 { --tilt: -5deg; top: 46%; right: max(0%, calc(50% - 395px)); }

@media (hover: hover) and (pointer: fine) {
  .hero-snap { transition: transform 200ms var(--ease-out), box-shadow 200ms var(--ease-out); }
  .hero-snap:hover {
    transform: rotate(0deg) translateY(-3px);
    box-shadow: 0 10px 26px rgba(90, 96, 74, 0.22);
    z-index: 3;
  }
}

/* Whimsy layer: thin curled ribbon streamers in the pastel accents.
   Decoration only — pink stays reserved for the title ribbon. */
.hero-streamer {
  position: absolute;
  width: 34px;
  height: 130px;
  pointer-events: none;
  opacity: 0.5;
}
.hero-streamer path { fill: none; stroke-width: 5; stroke-linecap: round; }
.hero-streamer--a { left: 13%; top: 2%; transform: rotate(14deg); }
.hero-streamer--a path { stroke: var(--sky); }
.hero-streamer--b { right: 12%; top: 34%; transform: rotate(-18deg); }
.hero-streamer--b path { stroke: var(--gold); }
.hero-streamer--c { left: 17%; top: 60%; transform: rotate(-8deg); width: 26px; height: 100px; opacity: 0.35; }
.hero-streamer--c path { stroke: var(--moss); }

/* Her name on the ribbon. The svg stretches to the text's box. */
.hero-ribbon {
  position: relative;
  z-index: 2;
  width: min(330px, 82vw);
  margin: 22px auto 2px;
  display: grid;
  place-items: center;
}
.hero-ribbon .ribbon { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero-ribbon h1 {
  position: relative;
  margin: 0;
  padding: 8px 0 18px;
  font-size: clamp(2.2rem, 9vw, 2.9rem);
  letter-spacing: -0.02em;
  color: var(--bow-ink);
}

.hero-intro {
  font-size: 1.06rem;
  max-width: min(46ch, 92vw);
  margin: 12px auto 6px;
  text-wrap: pretty;
}

/* Beat 2: the launch. The pup sets off from the left and the path hooks
   back down into the first waypoint.

   Sized to .trail's own 700px column, and drawn as ONE uniformly-scaled
   SVG with the pup embedded in it. Both of those are load-bearing, and
   both were learned the hard way (29 Aug 2026):
   - The first version was full-bleed 100vw with preserveAspectRatio
     "none". On a wide monitor that stretched a 1000-unit viewBox across
     2500px, flattening the curve to a near-straight line and squashing
     every paw print and dash into an oval. NEVER non-uniformly scale a
     drawing that contains round shapes.
   - The pup was separately positioned in px while the path scaled with
     the viewport, so the two drifted apart and the pup floated off the
     line. Anything that must stay glued to the path belongs INSIDE the
     path's SVG, in its coordinate space.
   - Sharing the trail's width means the path's descent lands on the
     first waypoint badge at every screen size, so it hands the eye over
     instead of trailing off into the right margin. */
.hero-launch {
  position: relative;
  max-width: 700px;
  margin: -8px auto 0;
}
.launch-path { display: block; width: 100%; height: auto; }
.launch-path .launch-line {
  fill: none;
  stroke: var(--moss);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-dasharray: 1 16;
  opacity: 0.6;
}
.launch-path .paw { fill: var(--moss); opacity: 0.45; }

/* The walk draws itself once, the first time the path comes into view.
   The dotted line has its own dasharray for the dots, so it cannot be
   drawn by animating its own dashoffset. Instead a fat solid stroke of
   the same path lives in a <mask> over the line and the paws, with
   pathLength normalised to 1000; its dashoffset runs 1000 to 0 and the
   dots and paws appear along the route behind it. The pup is outside the
   mask, already stood at the start. Without JS the html has no .js class
   and the path is simply visible; under reduced motion the same. */
.launch-reveal-line {
  fill: none;
  stroke: #fff;
  stroke-width: 48;
  stroke-linecap: round;
  stroke-dasharray: 1000;
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1300ms var(--ease-out);
}
.js .hero-launch:not(.seen) .launch-reveal-line { stroke-dashoffset: 1000; }

/* Beat 2a: the trailhead sign (owner brief, 13 Sep 2026). "Come on my
   walk" alone read as a heading floating in whitespace: a voice with no
   promise behind it and no threshold. An invitation needs who is asking,
   what they are offering, and where to step. So: a signpost, in Annie's
   voice, with one line under the title saying what the walk actually is
   (the job the muted "journey so far" sentence used to do at the FOOT of
   the path, i.e. after the reader had already been asked). Cream board,
   gold edge, a slight album tilt, and a post planted beside the path.
   Deliberately not a dark or boxy panel: same lineage as the medallion. */
.trailhead {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 64px auto 0;
}
.trailhead-board {
  max-width: min(34rem, 92vw);
  padding: 18px 30px 20px;
  background: var(--stamp-face);
  border: 2px solid var(--gold);
  border-radius: 18px;
  box-shadow: var(--shadow-soft);
  text-align: center;
  transform: rotate(-1.2deg);
}
.trailhead-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.05rem);
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: var(--moss-deep);
  margin: 0 0 8px;
}
.trailhead-note {
  font-size: 0.98rem;
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
  text-wrap: pretty;
}
.trailhead-post {
  width: 8px;
  height: 52px;
  background: var(--gold);
  border-radius: 0 0 4px 4px;
  margin-top: -3px;
}

/* Entrance: one-time stagger, ease-out, everything under 300ms.
   fill-mode backwards (not both) so the final state is the element's
   own — hover transforms keep working once the entrance is done. */
@keyframes hero-pop {
  from { opacity: 0; transform: translateY(12px) rotate(var(--tilt, 0deg)); }
  to { opacity: 1; transform: rotate(var(--tilt, 0deg)); }
}
.home-hero .cover-hero { animation: hero-pop 260ms var(--ease-out) backwards; }
.hero-ribbon { animation: hero-pop 260ms var(--ease-out) 70ms backwards; }
.hero-intro, .home-hero .cover-subtitle { animation: hero-pop 260ms var(--ease-out) 130ms backwards; }
.trailhead { animation: hero-pop 260ms var(--ease-out) 200ms backwards; }
.hero-snap { animation: hero-pop 280ms var(--ease-out) backwards; }
.hero-snap--1 { animation-delay: 160ms; }
.hero-snap--2 { animation-delay: 210ms; }
.hero-snap--3 { animation-delay: 260ms; }
.hero-snap--4 { animation-delay: 310ms; }

/* --- The medallion ------------------------------------------------- */

.cover-hero {
  position: relative;
  width: clamp(190px, 46vw, 244px);
  aspect-ratio: 1;
  margin-top: -92px;
  display: grid;
  place-items: center;
}

/* Soft periwinkle-and-blush halo. Sits behind everything, blurred, and
   breathes very slowly — the one motion added to the delight budget. */
/* Kept tight to the medallion on purpose: at a wider spread the blur bled
   down over the <h1> and subtitle and muddied the first thing you read. */
.cover-glow {
  position: absolute;
  inset: -10%;
  border-radius: 50%;
  background:
    radial-gradient(circle at 30% 26%, var(--sky) 0%, transparent 55%),
    radial-gradient(circle at 74% 74%, var(--bow) 0%, transparent 54%),
    radial-gradient(circle at 52% 52%, var(--moss-soft) 0%, transparent 68%);
  opacity: 0.45;
  filter: blur(16px);
  animation: medallion-breathe 7s var(--ease-in-out) infinite alternate;
}

@keyframes medallion-breathe {
  from { transform: scale(1); opacity: 0.38; }
  to { transform: scale(1.06); opacity: 0.54; }
}

.cover-medallion {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
}

/* Two stacked scallops: the rim is a touch larger, so a cream scalloped
   border shows around the photo — a pressed-flower/sticker edge. */
.medallion-rim,
.cover-medallion img {
  -webkit-mask-image: var(--scallop-mask);
  mask-image: var(--scallop-mask);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

.medallion-rim {
  position: absolute;
  inset: 0;
  background: var(--stamp-face);
}

.cover-medallion img {
  position: relative;
  width: 88%;
  height: 88%;
  object-fit: cover;
  display: block;
}

/* Little pastel companions. Abstract dots, not illustration — they add
   the soft "sticker sheet" feel without pretending to be drawings. */
.cover-dot {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}

.cover-dot--a { width: 15px; height: 15px; background: var(--sky); opacity: 0.65; top: 5%; right: 6%; }
.cover-dot--b { width: 10px; height: 10px; background: var(--bow); opacity: 0.6; bottom: 12%; left: 2%; }
.cover-dot--c { width: 7px; height: 7px; background: var(--gold); opacity: 0.75; top: 22%; left: 8%; }

/* --- Cover typography ---------------------------------------------- */

/* Scoped through .home-hero so it outranks the 44ch prose measure above —
   at this font-size `ch` is narrow enough to wrap the line mid-phrase. */
.home-hero .cover-subtitle {
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-ink);
  opacity: 0.75;
  margin: 0 0 16px;
  max-width: 100%;
}

/* Beat 3: the newest week's "you are here" pin — gold, like the
   waypoint badges, so it reads as part of the map language. */
.you-are-here {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: rgba(232, 192, 125, 0.38);
  color: var(--gold-ink);
  font-size: 0.82rem;
  font-weight: 800;
  border-radius: 999px;
  padding: 6px 14px;
}

/* Beat 4: the sign-off. The pup that set off with you at the top is
   asleep at the end of the trail, beside the site's name. */
.trail-end { text-align: center; margin: 60px 0 8px; }
.trail-end-pup { position: relative; width: 96px; margin: 0 auto; }
.trail-end-pup svg { display: block; width: 100%; height: auto; opacity: 0.95; }
.pup-zzz {
  position: absolute;
  top: -14px;
  right: -18px;
  font-family: var(--font-display);
  font-style: italic;
  color: var(--ink-muted);
  font-size: 0.95rem;
}
.trail-end-brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  margin: 10px 0 2px;
}
.trail-end-links { font-size: 0.95rem; color: var(--ink-muted); margin: 4px 0 0; }
.trail-end-links a { color: var(--moss); font-weight: 700; }

/* (The blush CTA pill is gone: the journal starts right under the cover
   now, so there's nothing to link to. Pink stays rare — the badge bow,
   the pup's bow, and favourite marks.) */
@media (hover: hover) and (pointer: fine) {
  .cover-medallion:hover { transform: rotate(-1.5deg) scale(1.02); }
}

.cover-medallion { transition: transform 220ms var(--ease-out); }

/* The medallion on a quiet page (About Annie's hello photo): same
   scallop, rim and halo as home, just sitting inside the card under the
   title instead of overhanging it. */
.page-quiet .cover-hero { margin: 10px auto 30px; }

/* ------------------------------------------------------------------ */
/* The journey trail: the last few weeks as a winding path            */
/* ------------------------------------------------------------------ */

.trail { max-width: 700px; margin: 0 auto; }

.trail-intro {
  color: var(--ink-muted);
  font-size: 0.95rem;
  margin: 0 0 6px;
}

.random-day {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  /* Lives at the bottom of the trail now (owner request, 29 Aug 2026),
     as a little parting invitation rather than a toolbar up top. */
  margin: 44px 0 10px;
}

.random-day-hint {
  flex-basis: 100%;
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.9rem;
}

.random-day select {
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 14px;
}

.random-day button {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 800;
  color: #fff;
  background: var(--moss);
  border: none;
  border-radius: 999px;
  padding: 10px 20px;
  cursor: pointer;
  box-shadow: var(--shadow-soft);
  transition: transform 120ms var(--ease-out);
}

.random-day button:active { transform: scale(0.96); }

@media (hover: hover) and (pointer: fine) {
  .random-day button:hover { transform: translateY(-2px); }
}

.week-stop { position: relative; }

/* JS-enhanced reveal: base state (no JS) is fully visible. */
.js .week-stop { opacity: 0; transform: translateY(14px); }
.js .week-stop.seen {
  opacity: 1;
  transform: none;
  transition: opacity 400ms var(--ease-out), transform 400ms var(--ease-out);
}

.trail-connector { display: block; width: 100%; height: 84px; }
.trail-connector path {
  fill: none;
  stroke: var(--moss);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 1 16;
  opacity: 0.7;
}

.paw { fill: var(--moss-deep); opacity: 0.35; }
.js .week-stop .paw { opacity: 0; }
.js .week-stop.seen .paw { opacity: 0.35; transition: opacity 300ms var(--ease-out); }
.js .week-stop.seen .paw:nth-of-type(2) { transition-delay: 120ms; }
.js .week-stop.seen .paw:nth-of-type(3) { transition-delay: 240ms; }

.waypoint { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

.waypoint-badge {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--gold-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  display: grid;
  place-items: center;
  border: 3px solid var(--stamp-face);
  box-shadow: var(--shadow-soft);
  flex-shrink: 0;
}

.waypoint-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  margin: 0;
}

.waypoint-dates {
  display: block;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.signpost {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--moss-soft);
  color: var(--moss-deep);
  font-size: 0.82rem;
  font-weight: 800;
  border-radius: 999px;
  padding: 6px 14px;
}

/* ------------------------------------------------------------------ */
/* "How Claude helps": long clickable bars, one per integration.       */
/* Native <details>, so they open and close with JS off. The door on   */
/* About This Project reuses the same bar shape as a link.             */
/* ------------------------------------------------------------------ */

.claude-bar {
  margin: 14px 0;
  background: var(--stamp-face);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: var(--shadow-soft);
}

.claude-bar summary,
.claude-door {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  cursor: pointer;
  list-style: none;
  border-radius: 16px;
}

.claude-bar summary::-webkit-details-marker { display: none; }

.bar-icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 1.3rem;
  background: var(--moss-soft);
}

.bar-label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.bar-label strong { font-family: var(--font-display); font-size: 1.02rem; }
.bar-label small { color: var(--ink-muted); font-size: 0.85rem; }

.claude-bar summary::after {
  content: '▾';
  margin-left: auto;
  color: var(--moss);
  font-size: 1.1rem;
  transition: transform 200ms var(--ease-out);
}
.claude-bar[open] summary::after { transform: rotate(180deg); }

.claude-bar .bar-body { padding: 0 20px 16px 76px; }
.claude-bar .bar-body p, .claude-bar .bar-body ol { margin: 8px 0; }

/* The door: same bar, but a link (lives inside a <p>, so it must stay
   an inline element made flex). */
.claude-door {
  margin: 6px 0;
  background: var(--stamp-face);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-soft);
  text-decoration: none;
  color: var(--ink);
  transition: transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
}
.claude-door::after {
  content: '→';
  margin-left: auto;
  color: var(--moss);
  font-weight: 800;
}
.claude-door:active { transform: scale(0.98); }

@media (hover: hover) and (pointer: fine) {
  .claude-bar summary:hover { background: var(--moss-soft); }
  .claude-door:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 26px rgba(90, 96, 74, 0.2);
  }
}

/* Lessons: a post that produced a lesson signposts it at the bottom;
   the lesson links back to its days. */
.lesson-signpost {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  background: var(--moss-soft);
  color: var(--moss-deep);
  border-radius: 999px;
  padding: 9px 18px;
  font-size: 0.88rem;
  font-weight: 700;
  margin-top: 26px;
}
.lesson-signpost a { color: var(--moss); }

.lesson-from {
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.lesson-from a { color: var(--moss); font-weight: 700; }

/* Milestones page: a mini stamp-timeline, oldest first — a condensed
   echo of the journey trail. Gold badge = a week-anniversary (calendar
   rule), pink bow = a "first" quoted from a post (text rule), moss paw
   = hand-written in milestones.md. */
/* Chapters: one <details> per month, newest open, older ones folded.
   Native disclosure — collapsing works with JS off. */
.milestone-chapter { margin: 20px 0; }

.milestone-chapter summary {
  display: flex;
  align-items: center;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.2rem;
  padding: 4px 2px;
}
.milestone-chapter summary::-webkit-details-marker { display: none; }
.milestone-chapter summary::after {
  content: '▾';
  margin-left: auto;
  color: var(--moss);
  font-size: 1rem;
  transition: transform 200ms var(--ease-out);
}
.milestone-chapter[open] summary::after { transform: rotate(180deg); }

.milestone-timeline {
  list-style: none;
  margin: 14px 0 0;
  padding: 0 0 0 2px;
  position: relative;
}

.milestone-timeline::before {
  content: '';
  position: absolute;
  left: 19px;
  top: 8px;
  bottom: 8px;
  border-left: 3px dashed var(--moss);
  opacity: 0.3;
}

.milestone-item {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 14px 0;
  position: relative;
}

.milestone-item:first-child { padding-top: 0; }
.milestone-item:last-child { padding-bottom: 0; }

.milestone-mark {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--gold);
  border: 3px solid var(--paper);
  box-shadow: var(--shadow-soft);
}

.milestone-item--first .milestone-mark { background: var(--bow); }
.milestone-item--manual .milestone-mark { background: var(--moss); }

.milestone-mark .fav-bow { width: 18px; height: 13px; }
.milestone-mark .fav-bow path { fill: #fff; }
.milestone-mark .fav-bow circle { fill: rgba(255, 255, 255, 0.75); }
.milestone-paw { width: 16px; height: 16px; }
.milestone-paw .paw { fill: #fff; opacity: 0.9; }

.milestone-body {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding-top: 7px;
  min-width: 0;
}

.milestone-date {
  flex-shrink: 0;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--ink-muted);
  min-width: 9em;
}

.milestone-label { font-weight: 700; }
.milestone-timeline a { font-weight: 700; color: var(--moss); }

/* Two tiers: landmarks (owner-written) large, everything else compact
   texture. Compact marks shrink but stay centred on the dashed line. */
.milestone-item--compact { padding: 8px 0; }
.milestone-item--compact .milestone-mark { width: 26px; height: 26px; margin-left: 7px; }
.milestone-item--compact .milestone-mark .fav-bow { width: 14px; height: 10px; }
.milestone-item--compact .milestone-paw { width: 12px; height: 12px; }
.milestone-item--compact .milestone-body { padding-top: 2px; font-size: 0.9rem; }
.milestone-item--compact .milestone-label { font-weight: 600; }
.milestone-item--compact .milestone-date { min-width: 8em; }

.milestone-item--landmark .milestone-label {
  font-family: var(--font-display);
  font-size: 1.12rem;
}

/* Gold calendar rows keep their compact size but carry a photo; shrink
   the thumb so it sits comfortably on a single-line row. */
.milestone-item--compact .milestone-photo { width: 48px; height: 48px; }
.milestone-star { color: var(--gold-ink); }

.milestone-photo {
  flex-shrink: 0;
  width: 60px;
  height: 60px;
  border-radius: 10px;
  object-fit: cover;
  border: 3px solid var(--stamp-face);
  box-shadow: var(--shadow-soft);
  transform: rotate(-2.5deg);
}

/* Same mismatched-scrapbook feel as the trail's patches: alternate the
   tilt and the mount tint down the timeline. */
.milestone-item:nth-child(even) .milestone-photo { transform: rotate(2.5deg); }
.milestone-item:nth-child(3n) .milestone-photo { border-color: var(--moss-soft); }
.milestone-item:nth-child(3n+1) .milestone-photo { border-color: #f7e6ea; }

/* The skipping-stone rule: a week's patches are laid ONE PER STEP down a
   winding path — never a wrapped row or grid. Each patch offsets further
   across the trail then wanders back, and overlaps the previous one a
   little vertically, so a week reads as stones skipped across a stream.
   If you ever see the trail as neat rows of pictures, this is what
   regressed. */
.week-entries {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 26px 0 10px;
}

.week-entries .patch { margin-top: -30px; }
.week-entries .patch:first-child { margin-top: 0; }

/* An 8-step meander: out to the far bank and back. Cycle repeats for
   longer weeks. Offsets fit 700px trail minus the widest patch. */
.week-entries .patch:nth-child(8n+1) { margin-left: 0; }
.week-entries .patch:nth-child(8n+2) { margin-left: 165px; }
.week-entries .patch:nth-child(8n+3) { margin-left: 330px; }
.week-entries .patch:nth-child(8n+4) { margin-left: 480px; }
.week-entries .patch:nth-child(8n+5) { margin-left: 340px; }
.week-entries .patch:nth-child(8n+6) { margin-left: 175px; }
.week-entries .patch:nth-child(8n+7) { margin-left: 20px; }
.week-entries .patch:nth-child(8n)   { margin-left: 210px; }

/* Right-side weeks skip the same path mirrored, so the meander picks up
   where the connector hands it over. */
.week-stop.side-right .week-entries .patch:nth-child(8n+1) { margin-left: 480px; }
.week-stop.side-right .week-entries .patch:nth-child(8n+2) { margin-left: 315px; }
.week-stop.side-right .week-entries .patch:nth-child(8n+3) { margin-left: 150px; }
.week-stop.side-right .week-entries .patch:nth-child(8n+4) { margin-left: 0; }
.week-stop.side-right .week-entries .patch:nth-child(8n+5) { margin-left: 140px; }
.week-stop.side-right .week-entries .patch:nth-child(8n+6) { margin-left: 305px; }
.week-stop.side-right .week-entries .patch:nth-child(8n+7) { margin-left: 460px; }
.week-stop.side-right .week-entries .patch:nth-child(8n)   { margin-left: 270px; }

.week-stop.side-right .waypoint { justify-content: flex-end; }
.week-stop.side-right .waypoint { flex-direction: row-reverse; text-align: right; }

.quiet-week {
  color: var(--ink-muted);
  font-size: 0.9rem;
  font-style: italic;
  margin: 10px 0 4px;
}

.archive-link {
  display: inline-block;
  font-weight: 800;
  font-size: 0.92rem;
  text-decoration: none;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 11px 22px;
  margin-top: 28px;
  box-shadow: var(--shadow-soft);
  transition: transform 120ms var(--ease-out);
}

.archive-link:active { transform: scale(0.96); }

@media (hover: hover) and (pointer: fine) {
  .archive-link:hover { transform: translateY(-2px); }
}

/* Favourite spots — entries the owner has flagged featured: true */
.fav-shelf {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 20px 26px 26px;
  margin-top: 44px;
}

.fav-shelf h2 {
  margin: 0 0 4px;
  font-size: 1.2rem;
  display: flex;
  align-items: center;
  gap: 10px;
}

.fav-shelf .fav-note { color: var(--ink-muted); font-size: 0.85rem; margin: 0 0 10px; }

.fav-bow { width: 22px; height: 16px; flex-shrink: 0; }
.fav-bow path { fill: var(--bow); }
.fav-bow circle { fill: var(--bow-knot); }

/* ------------------------------------------------------------------ */
/* The postcard — the newsletter sign-up at the foot of the trail      */
/*                                                                     */
/* A card, a perforated stamp with the pup sitting in it, one line and  */
/* one button. build.js only emits it once NEWSLETTER is wired to a     */
/* publication, so until then it is absent from the page entirely.      */
/* ------------------------------------------------------------------ */

.postcard {
  display: flex;
  gap: 22px;
  align-items: flex-start;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 22px 26px 24px;
  margin-top: 44px;
}

/* The stamp edge is a dotted border sitting just outside the face, so it
   reads as perforation without needing an image or a gradient trick. */
.postcard-stamp {
  position: relative;
  flex: 0 0 96px;
  width: 96px;
  height: 96px;
  background: var(--stamp-face);
  border-radius: 6px;
  transform: rotate(-3deg);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 6px;
}

.postcard-perf {
  position: absolute;
  inset: -5px;
  border: 2px dotted var(--line);
  border-radius: 9px;
}

.postcard-pup { width: 78px; height: auto; }

.postcard-body { flex: 1 1 auto; min-width: 0; }
.postcard-body h2 { margin: 0 0 4px; font-size: 1.2rem; }

.postcard-note {
  color: var(--ink-muted);
  font-size: 0.9rem;
  margin: 0 0 14px;
  max-width: 46ch;
}

.postcard-go {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 800;
  color: #fff;
  background: var(--moss);
  border-radius: 999px;
  padding: 10px 20px;
  text-decoration: none;
  box-shadow: var(--shadow-soft);
  transition: transform 120ms var(--ease-out);
}

.postcard-go:active { transform: scale(0.96); }

@media (hover: hover) and (pointer: fine) {
  .postcard-go:hover { transform: translateY(-2px); }
}

/* Beehiiv's embed is an iframe with no intrinsic size, and an iframe
   with no width, height or aspect-ratio falls back to the CSS default
   replaced-element size of 300x150 and collapses — the same trap the
   Instagram embed hit. Both dimensions are therefore set explicitly.
   The height is generous on purpose: their form is one row on desktop
   and wraps to two on narrow screens, and a short iframe clips the
   button instead of scrolling to it. */
.postcard-embed { max-width: 420px; }

.postcard-embed iframe {
  display: block;
  width: 100%;
  height: 82px;
  border: 0;
  margin: 0;
  color-scheme: light;
}

.postcard-small {
  color: var(--ink-muted);
  font-size: 0.78rem;
  margin: 12px 0 0;
}

@media (max-width: 560px) {
  .postcard {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 16px;
  }
  .postcard-note { max-width: none; }
  .postcard-embed { max-width: none; width: 100%; }
}

/* ------------------------------------------------------------------ */
/* Patches — journal entries as scrapbook cuttings                     */
/* (trail, favourites shelf, archive)                                  */
/*                                                                     */
/* Every patch carries a paper tint, an edge treatment, a tilt and a    */
/* size, mixed deliberately so no two neighbours match. build.js picks  */
/* those from a hash of the slug, so the mismatch is stable across      */
/* builds rather than reshuffling on every deploy.                      */
/* ------------------------------------------------------------------ */

.patch-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(152px, 1fr));
  gap: 26px 20px;
  margin: 0;
  padding: 26px 0 0;
}

.patch-grid li { opacity: 0; animation: patch-in 320ms var(--ease-out) forwards; }
.patch-grid li:nth-child(1) { animation-delay: 0ms; }
.patch-grid li:nth-child(2) { animation-delay: 50ms; }
.patch-grid li:nth-child(3) { animation-delay: 100ms; }
.patch-grid li:nth-child(4) { animation-delay: 150ms; }
.patch-grid li:nth-child(5) { animation-delay: 200ms; }
.patch-grid li:nth-child(6) { animation-delay: 250ms; }
.patch-grid li:nth-child(7) { animation-delay: 300ms; }
.patch-grid li:nth-child(n+8) { animation-delay: 350ms; }

@keyframes patch-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

.patch {
  --patch-face: var(--stamp-face);
  --patch-edge: var(--line);
  position: relative;
  width: 140px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 11px 11px 14px;
  text-decoration: none;
  color: var(--ink);
  background-color: var(--patch-face);
  border-radius: 10px;
  box-shadow: 0 3px 10px rgba(90, 96, 74, 0.13);
  /* The tilt is the patchwork. Kept on a custom property so hover can
     compose with it instead of overwriting it. */
  transform: rotate(var(--rot, 0deg));
  transition: transform 180ms var(--ease-out), box-shadow 180ms var(--ease-out);
}

.patch--big { width: 168px; }
.patch-grid .patch { width: auto; }
/* In grids (archive, favourites) the stagger is the grid's job, not the
   patch's — margins would fight the cells. */
.patch-grid .patch { margin-top: 0 !important; }

/* --- paper tints --------------------------------------------------- */
.patch--cream { --patch-face: #fffef6; --patch-edge: #e6e4d2; }
.patch--sage  { --patch-face: #e7edda; --patch-edge: #cfdabd; }
.patch--blush { --patch-face: #f7e6ea; --patch-edge: #e8cdd4; }
.patch--sky   { --patch-face: #e4ecf7; --patch-edge: #c9d8ec; }
.patch--gold  { --patch-face: #faedd5; --patch-edge: #ecd6ab; }

/* --- edge treatments ------------------------------------------------ */
/* stamp: perforated, the classic. Holes are page-coloured. */
.patch--edge-stamp {
  border-radius: 8px;
  background-image:
    radial-gradient(circle at 6px 6px, var(--paper) 4px, transparent 4.6px),
    radial-gradient(circle at 6px 6px, var(--paper) 4px, transparent 4.6px),
    radial-gradient(circle at 6px 6px, var(--paper) 4px, transparent 4.6px),
    radial-gradient(circle at 6px 6px, var(--paper) 4px, transparent 4.6px);
  background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
  background-position: top left, bottom left, top left, top right;
  background-size: 12px 12px;
}

/* print: a photo print with a wide paper margin. */
.patch--edge-print {
  border-radius: 4px;
  padding: 13px 13px 16px;
  box-shadow: 0 4px 14px rgba(90, 96, 74, 0.18);
}

/* cut: snipped out with pinking shears — a dashed outline. */
.patch--edge-cut {
  border-radius: 16px;
  outline: 2px dashed var(--patch-edge);
  outline-offset: -6px;
}

/* --- washi tape ----------------------------------------------------- */
.patch-tape {
  position: absolute;
  top: -9px;
  left: 50%;
  width: 54px;
  height: 19px;
  transform: translateX(-50%) rotate(-4deg);
  background: rgba(168, 191, 224, 0.55);
  border-left: 2px dotted rgba(255, 255, 255, 0.6);
  border-right: 2px dotted rgba(255, 255, 255, 0.6);
  pointer-events: none;
}
.patch--blush .patch-tape { background: rgba(217, 143, 160, 0.45); }
.patch--sage .patch-tape,
.patch--gold .patch-tape { background: rgba(232, 192, 125, 0.55); }

/* --- photo ---------------------------------------------------------- */
.patch-photo {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 4px;
  background: var(--moss-soft);
}

.patch-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* A text-only post: a written note rather than an empty photo frame. It
   sits shorter than a photo patch, which suits the patchwork. */
.patch--nophoto { justify-content: center; padding-top: 18px; }
.patch--nophoto .patch-title { font-size: 0.92rem; line-height: 1.25; }

.patch-note {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--paper);
  opacity: 0.75;
}
.patch-note svg { width: 17px; height: 17px; }
.patch-note .paw { fill: var(--moss); opacity: 0.8; }

/* --- date label ----------------------------------------------------- */
.patch-date {
  align-self: flex-start;
  font-family: var(--font-body);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  color: var(--moss-deep);
  background: var(--paper);
  border-radius: 4px;
  padding: 2px 7px;
  margin-top: -2px;
  transform: rotate(var(--date-rot, 0deg));
  box-shadow: 0 1px 3px rgba(90, 96, 74, 0.16);
}

.patch-title {
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
  overflow-wrap: break-word;
  max-width: 100%;
}

.patch .fav-bow {
  position: absolute;
  top: -7px;
  right: -6px;
  width: 24px;
  height: 17px;
  filter: drop-shadow(0 1px 2px rgba(90, 96, 74, 0.3));
}

.patch:active { transform: rotate(var(--rot, 0deg)) scale(0.97); }

@media (hover: hover) and (pointer: fine) {
  .patch:hover {
    transform: rotate(0deg) translateY(-5px) scale(1.03);
    box-shadow: 0 12px 26px rgba(90, 96, 74, 0.24);
    z-index: 2;
  }
}

/* ------------------------------------------------------------------ */
/* Archive (monthly album pages)                                      */
/* ------------------------------------------------------------------ */

.month-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 20px 0 0 16px; }

.month-tab {
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  padding: 8px 18px;
  border-radius: 10px 10px 0 0;
  background: var(--line);
  color: var(--ink-muted);
  text-decoration: none;
  transition: background-color 160ms var(--ease-out), color 160ms var(--ease-out);
}

.month-tab.active { background: var(--moss); color: #fff; }

@media (hover: hover) and (pointer: fine) {
  .month-tab:not(.active):hover { color: var(--ink); }
}

.album-page {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-soft);
  padding: 10px 26px 30px;
}

/* ------------------------------------------------------------------ */
/* The pup: a stylised little friend, not a portrait.                 */
/* Hidden until journey.js reveals it; decoration only.               */
/* ------------------------------------------------------------------ */

.pup {
  position: fixed;
  bottom: 6px;
  left: 18px;
  width: 88px;
  z-index: 5;
  pointer-events: none;
}

.pup.facing-left { transform: scaleX(-1); }

.pup .legs-b { opacity: 0; }
.pup.trotting { animation: pup-bob 360ms ease-in-out infinite; }
.pup.trotting .legs-a { animation: legswap-a 360ms steps(1) infinite; }
.pup.trotting .legs-b { animation: legswap-b 360ms steps(1) infinite; }

@keyframes pup-bob {
  0%, 100% { margin-bottom: 0; }
  50% { margin-bottom: 3px; }
}
@keyframes legswap-a { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@keyframes legswap-b { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }

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

.site-footer {
  max-width: 700px;
  margin: 0 auto;
  padding: 24px 20px 60px;
  color: var(--ink-muted);
  font-size: 0.85rem;
  border-top: 1px solid var(--line);
}

/* ------------------------------------------------------------------ */
/* Reduced motion: still the mist, drop travel, keep gentle fades     */
/* ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .mist-a, .mist-b { animation: none; }
  .bow { animation: none; opacity: 1; }
  main article { animation: none; opacity: 1; }
  .patch-grid li { animation: patch-fade 200ms ease forwards; }
  .js .week-stop { opacity: 1; transform: none; }
  .pup.trotting { animation: none; }
  .pup.trotting .legs-a, .pup.trotting .legs-b { animation: none; }
  /* Photos: keep the album tilt, drop the travel. */
  .js .snap { opacity: 1; transform: rotate(var(--tilt)); }
  .snap:hover { transform: rotate(var(--tilt)); }
  .lightbox img { animation: lightbox-fade 200ms ease both; }
  /* Patches keep their tilt — that's layout, not motion — but stop
     moving on press and hover. */
  .patch:active, .patch:hover { transform: rotate(var(--rot, 0deg)); }
  .site-nav a:active, .random-day button:active,
  .archive-link:active, .postcard-go:active { transform: none; }
  .random-day button:hover, .archive-link:hover,
  .postcard-go:hover { transform: none; }
  /* The medallion stops breathing; the halo stays, just still. */
  .cover-glow { animation: none; opacity: 1; }
  .cover-medallion:hover { transform: none; }
  /* Hero entrance: no travel, everything simply present. */
  .home-hero .cover-hero, .hero-ribbon, .hero-intro,
  .home-hero .cover-subtitle, .hero-snap, .trailhead { animation: none; }
  .js .hero-launch:not(.seen) .launch-reveal-line { stroke-dashoffset: 0; transition: none; }
  .hero-snap:hover { transform: rotate(var(--tilt, 0deg)); }
}

@keyframes patch-fade { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 640px) {
  .page-quiet main article { padding: 24px 20px 32px; }
  .home-hero { padding-top: 24px; }
  /* All four snaps hug the medallion in two short rows — any lower and
     they sit on the ribbon and intro text (that bug shipped once in a
     screenshot; keep them out of the type). */
  .hero-snap { width: 88px; padding: 5px 5px 7px; }
  .hero-snap--1 { top: 2%; left: 1%; }
  .hero-snap--2 { top: 30%; left: 0; }
  .hero-snap--3 { top: 2%; right: 1%; }
  .hero-snap--4 { top: 32%; right: 0; }
  .hero-streamer { display: none; }
  /* The launch path needs no mobile overrides: it scales uniformly with
     its column, pup and all. */
  .claude-bar .bar-body { padding-left: 20px; }
  .cover-hero { margin-top: -78px; }
  .patch-grid { grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); gap: 20px 14px; }
  .patch, .patch--big { width: 132px; }
  .patch-tape { width: 44px; height: 16px; }
  /* Narrow stream, same skipping stones: the meander compresses to
     percentage offsets so no step leaves the viewport. No vertical
     overlap here — the compressed steps aren't wide enough to clear the
     previous patch, so overlap would sit on its title. */
  .week-entries .patch { margin-top: 10px; }
  .week-entries .patch:first-child { margin-top: 0; }
  .week-entries .patch:nth-child(8n+1) { margin-left: 0; }
  .week-entries .patch:nth-child(8n+2) { margin-left: 22%; }
  .week-entries .patch:nth-child(8n+3) { margin-left: 44%; }
  .week-entries .patch:nth-child(8n+4) { margin-left: 56%; }
  .week-entries .patch:nth-child(8n+5) { margin-left: 40%; }
  .week-entries .patch:nth-child(8n+6) { margin-left: 20%; }
  .week-entries .patch:nth-child(8n+7) { margin-left: 2%; }
  .week-entries .patch:nth-child(8n)   { margin-left: 28%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+1) { margin-left: 56%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+2) { margin-left: 36%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+3) { margin-left: 16%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+4) { margin-left: 0; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+5) { margin-left: 18%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+6) { margin-left: 38%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n+7) { margin-left: 54%; }
  .week-stop.side-right .week-entries .patch:nth-child(8n)   { margin-left: 30%; }
  .pup { width: 68px; left: 10px; }
  .cover-pup-peek { width: 56px; bottom: 10px; right: 12px; }
  .milestone-photo { width: 44px; height: 44px; }
  .milestone-timeline::before { left: 17px; }
  .milestone-mark { width: 34px; height: 34px; }
}
