/* ===========================================================
   Kim Johnston — author website  ·  v3 "say it straight"
   Hitchhiker's Guide to the Apocalypse
   Palette: Kim's gum tree photo — sage leaves, purple-brown bark,
   warm cream. Voice-led design: big statements, one clear CTA.
   =========================================================== */

:root {
  /* The eight approved values — Kim's gum tree. Nothing else. */
  --cream: #F5F2EC;
  --cream-deep: #EDE9E0;

  --bark: #7A4A52;
  --bark-deep: #5E3942;
  --leaf: #5B6E66;
  --leaf-deep: #46584F;
  --leaf-tint: #E4EAE5;
  --bark-tint: #EFE3E5;

  /* WCAG 2.2 AA control tokens (all drawn from the eight) */
  --focus: #5E3942;        /* focus ring on light backgrounds */
  --focus-on-dark: #F5F2EC;/* focus ring on leaf-deep / bark-deep bands */
  --border-strong: #46584F;/* form and control borders — clears 3:1 */

  /* Spectral, single serif. Kim approved 20 July. */
  --font-display: "Spectral", Georgia, "Times New Roman", serif;
  --font-body: "Spectral", Georgia, "Times New Roman", serif;
  --font-type: "Spectral", Georgia, "Times New Roman", serif;

  --radius-lg: 22px;
  --radius: 14px;
  --radius-sm: 9px;
  /* Shadows are --bark-deep (#5E3942) at low alpha, so every colour on the
     site — shadows included — traces back to the approved eight. Bark-deep
     is lighter than the near-black these were built from, so the alphas are
     nudged up (0.10 -> 0.11, 0.16 -> 0.18) to land on the same composite
     lightness over cream. Measured, not guessed. */
  --shadow: 0 10px 30px rgba(94, 57, 66, 0.11);
  --shadow-big: 0 26px 60px rgba(94, 57, 66, 0.18);
  --container: 1120px;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* The browser's own [hidden] rule is display:none at user-agent level, which any
   author rule beats. .btn sets display:inline-flex, so a hidden .btn stayed on
   screen. This makes hidden mean hidden, whatever else the element is. */
[hidden] { display: none !important; }
html {
  scroll-behavior: smooth;
  /* Sticky header measures ~78px (16px + 45px nav row + 16px + 1px rule).
     +1rem clearance so anchor targets never land underneath it. */
  scroll-padding-top: 6rem;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Kill the lift/scale/rotate hover moves as well — a killed transition
     still leaves the transform itself jumping. */
  .btn:hover, .card:hover, .waypoint-card:hover, .postcard:hover {
    transform: none !important;
  }
  .cover-frame:hover { transform: rotate(-1.6deg) !important; }
  .waypoint.is-visible .marker { transform: translate(-50%, -50%) !important; }
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--bark-deep);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1.0625rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

::selection { background: var(--bark-deep); color: var(--cream); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--bark-deep);
  line-height: 1.05;
  margin: 0 0 0.45em;
  font-weight: 700;
  letter-spacing: -0.015em;
}
h1 { font-size: clamp(2.6rem, 7vw, 5.2rem); }
/* Floor lowered 2.3rem -> 1.9rem. One change, not two: the base h1 above is
   left alone. The problem being fixed is the home page's first screen, and
   once index.html carries class="h1-tight" (agreed in the work list) this is
   the rule that governs that headline — so this is the line that moves it.
   Measured at 375x667 with Safari's chrome (about 553px of usable height):
   header 78 + hero padding 67 + eyebrow 67 + headline 4 lines at 1.9rem
   (142) + pitch 104 + button row 30 puts the top of "Read chapter one free"
   at about 488px, so the headline AND the button are both on the first
   screen. At the old 2.3rem floor the button started around 520px and was
   cut off. Lowering the base h1 instead would not have touched the home
   page at all, and would have shrunk about.html's short heading, which is
   not the one causing trouble. 1.9rem is still 1.8x body text. */
h1.h1-tight { font-size: clamp(1.9rem, 5.5vw, 4rem); }
h2 { font-size: clamp(1.9rem, 4.2vw, 3.2rem); }
h3 { font-size: clamp(1.15rem, 2vw, 1.5rem); }

p { margin: 0 0 1em; }
a { color: var(--bark-deep); }
img { max-width: 100%; display: block; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 26px; }
.narrow { max-width: 740px; }

/* ---------- Skip link ---------- */
.skip-link {
  position: absolute;
  left: -999px; top: 0;
  background: var(--bark-deep);
  color: var(--cream);
  padding: 12px 22px;
  z-index: 200;
  border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 700;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

/* ---------- Focus ---------- */
a:focus-visible, button:focus-visible, summary:focus-visible,
input:focus-visible, textarea:focus-visible, select:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}
/* On the dark bands, the footer and the CTA panel the ring must not share a
   colour with what sits behind it — outline-offset means the ring lands on the
   band, not on the control. */
.band-dark a:focus-visible,
.band-dark button:focus-visible,
.cta-panel a:focus-visible,
.cta-panel button:focus-visible,
.site-footer a:focus-visible,
.site-footer button:focus-visible {
  outline-color: var(--focus-on-dark);
}
/* Skip-link target. Programmatic focus on <main> gets no visible ring, but
   real keyboard focus still would — this is not a blanket outline:none. */
main[tabindex="-1"]:focus:not(:focus-visible) { outline: none; }

/* ---------- Header / nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(245, 242, 236, 0.9);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(94, 57, 66, 0.2);
}
.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 26px;
  max-width: var(--container);
  margin: 0 auto;
}
.brand {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--bark-deep);
  text-decoration: none;
}
.brand:hover { color: var(--leaf-deep); }

.nav-toggle {
  display: none;
  background: none;
  border: 2px solid var(--bark-deep);
  border-radius: 999px;
  padding: 9px 16px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1rem;
  color: var(--bark-deep);
  cursor: pointer;
  min-height: 44px;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  margin: 0; padding: 0;
}
.nav-links a {
  text-decoration: none;
  color: var(--bark-deep);
  font-weight: 600;
  font-size: 1rem;
  padding: 9px 16px;
  border-radius: 999px;
  transition: background 0.15s ease, color 0.15s ease;
}
.nav-links a:hover { background: var(--leaf-tint); color: var(--leaf-deep); }
.nav-links a[aria-current="page"] { background: var(--bark-deep); color: var(--cream); }
.nav-links .nav-cta a {
  background: var(--leaf-deep);
  color: var(--cream);
  margin-left: 8px;
}
.nav-links .nav-cta a:hover { background: var(--bark-deep); color: var(--cream); }

@media (max-width: 760px) {
  /* The drawer belongs to JavaScript. Everything below is gated on .js so that
     with scripts off the reader keeps a plain wrapped menu instead of losing
     the whole navigation behind a button that cannot open. */
  .js .nav-toggle { display: block; }
  .nav-links { flex-wrap: wrap; justify-content: center; row-gap: 2px; }
  /* Without scripts the menu wraps to about three rows, so the sticky header
     is roughly 190px tall instead of 78px. scroll-padding-top is sized for the
     scripted header, so anchor targets would land underneath it. Give the
     no-script case its own clearance. */
  html:not(.js) { scroll-padding-top: 13rem; }
  .js .nav-links {
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    position: absolute;
    left: 0; right: 0; top: 100%;
    background: var(--cream);
    border-bottom: 1px solid rgba(94, 57, 66, 0.2);
    padding: 14px 26px 22px;
  }
  .js .nav-links.is-open { display: flex; }
  .nav-links a { display: block; padding: 13px 16px; }
  .nav-links .nav-cta a { margin-left: 0; text-align: center; }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 16px 32px;
  border-radius: 999px;
  font-weight: 600;
  text-decoration: none;
  font-family: var(--font-body);
  font-size: 1.05rem;
  border: 2px solid transparent;
  cursor: pointer;
  min-height: 48px;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.btn:hover:not(:disabled) { transform: translateY(-2px); }
.btn-primary {
  background: var(--leaf-deep);
  color: var(--cream);
  box-shadow: var(--shadow);
}
.btn-primary:hover:not(:disabled) { background: var(--bark-deep); }
.btn-outline {
  background: transparent;
  border-color: var(--bark-deep);
  color: var(--bark-deep);
}
.btn-outline:hover:not(:disabled) { background: var(--bark-tint); }
/* Unavailable controls: announced by the disabled attribute, and visibly muted.
   Text stays on --bark-deep for legibility; WCAG exempts inactive controls from
   contrast minimums, but this pairing clears AA anyway. */
.btn:disabled,
.btn[disabled] {
  background: var(--cream-deep);
  border-color: var(--leaf-deep);
  color: var(--bark-deep);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
.band-dark .btn-primary { background: var(--cream); color: var(--bark-deep); }
.band-dark .btn-primary:hover { background: var(--cream-deep); }
.band-dark .btn-outline { border-color: var(--cream); color: var(--cream); }
.band-dark .btn-outline:hover { background: rgba(245,242,236,0.12); }
.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 30px; }
.link-quiet {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--bark);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  padding: 12px 4px;
  min-height: 44px;
}
.link-quiet:hover { color: var(--bark-deep); }

/* The three rules that used to sit here dressed up href-less anchors as
   switched-off buttons. A link with no href is skipped by the Tab key and
   read as plain text, so they propped up a pattern that shut keyboard
   readers out. book.html has been rebuilt without them; an unavailable
   control is now a real <button disabled>, styled at .btn:disabled above. */

/* ---------- Eyebrow ---------- */
.eyebrow {
  display: inline-block;
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 1rem;
  color: var(--leaf-deep);
  background: var(--leaf-tint);
  padding: 8px 16px;
  border-radius: 999px;
  margin-bottom: 24px;
}
.band-dark .eyebrow { background: rgba(245,242,236,0.14); color: var(--cream); }

/* ---------- Hero ---------- */
/* position:relative here only establishes a positioning context for an
   optional .hero-background layer (see below) — harmless on pages that
   don't use one. */
.hero { position: relative; padding: clamp(64px, 10vh, 120px) 0 clamp(48px, 7vh, 90px); }
.hero-grid {
  position: relative;
  z-index: 1; /* keeps hero copy/CTAs above any .hero-background layer */
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(40px, 6vw, 80px);
  align-items: center;
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* Cover follows the headline on small screens. With order:-1 the art
     pushed the h1 and the main call to action below the fold on a 375px
     phone — measured, not assumed. Desktop layout is unchanged. */
  /* 300px -> 220px. The cover now follows the headline, so its job here is
     to confirm the book exists, not to fill the screen. The 80px saved is
     80px less scrolling between the pitch and the journey. */
  .hero-art { max-width: 220px; margin: 24px auto 0; }
  /* The generated-visual-slot's left/right mask assumes the desktop
     two-column split (text-safe left, art-safe right). Below this
     breakpoint the grid stacks into one column, so that same split
     would let the image bleed under the headline instead — caught by
     testing at mobile width, not assumed safe. Drop the image here;
     .ambient-light-layer + .motion-overlay (verified safe at any
     layout, see contrast comments above) carry the atmosphere alone. */
  .generated-visual-slot { display: none; }
}
.pitch {
  font-size: clamp(1.15rem, 2vw, 1.35rem);
  color: var(--bark);
  max-width: 44ch;
  line-height: 1.55;
}
.lede { font-size: 1.12rem; color: var(--bark); max-width: 56ch; }

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  margin-top: 34px;
  padding-top: 26px;
  border-top: 2px solid var(--cream-deep);
}
.hero-meta .k {
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bark);
  margin-bottom: 4px;
}
.hero-meta .v { font-weight: 700; color: var(--bark-deep); }

/* ==========================================================
   Ambient background system — hero / section atmosphere
   ==========================================================
   Reusable, hero-only for now (see index.html). Re-interprets
   MotionSites-style layered glow/depth/motion using the site's
   OWN palette and its OWN motif (the dawn road + gum tree from
   the cover art) rather than importing a generic cosmic/neon
   look. Every layer here is decorative: aria-hidden, non-
   interactive, and safely paints behind hero-grid (z-index:1
   above, set alongside .hero position:relative, above).

   Layer order (back to front):
     1. .generated-visual-slot  — empty Higgsfield hook (see below)
     2. .ambient-light-layer    — the soft glow, in Kim's palette
     3. .motion-overlay         — faint static grain for depth
   ========================================================== */

/* Positioning wrapper for .hero's decorative background layers. */
.hero-background {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}

/* ---- Higgsfield asset hook ------------------------------------------
   HIGGSFIELD SWAP-IN POINT — now a looping video (index.html), animated
   7 Aug 2026 from the still below: images/hero-visual.jpg (Recraft
   V4.1, vector mode) → images/hero-video.mp4 (Kling 3.0 Turbo, image-
   to-video, ~2MB, 5s loop). The still is used as the <video poster>
   (the first frame shown before the clip loads/plays). On mobile,
   .generated-visual-slot is hidden entirely below 900px (see that
   rule further down) — there's no separate still shown in its place;
   .ambient-light-layer + .motion-overlay carry the mobile hero alone,
   same as before the video existed. Kept
   BEHIND .ambient-light-layer in the DOM (painted first) so the glow
   still washes over it and hero text keeps its contrast regardless of
   the asset's own content.

   object-position + the mask below deliberately anchor the asset's
   detail (sun, road, gum tree) to the RIGHT side, under the cover-art
   column, and fade it to nothing under the text column on the left —
   the same contrast-safety principle used for .ambient-light-layer
   above. Verified by contrast check + screenshot before shipping,
   not assumed (the source image's tree silhouette sits close enough
   to the left edge that an un-masked full-bleed crop could land
   right under the headline). The video motion itself is deliberately
   subtle — ambient light drift and parallax only, no new scene, no
   camera movement — so it reads as atmosphere, not a second animation
   competing with the site's other motion (the road/hiker/passport).

   prefers-reduced-motion is handled in script.js ("Hero background
   video"), which pauses the video outright rather than relying on
   CSS — video playback isn't something a CSS media query can stop on
   its own. The poster frame stays visible either way, so reduced-
   motion users see the same still image as everyone else, just static.

   To swap in a different asset: keep this object-position + mask-image
   pair, or re-verify contrast if the new asset's detail sits
   elsewhere. ---------------------------------------------------------- */
.generated-visual-slot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 95% 15%;
  opacity: 0.22;
  /* Text column's box extends to ~53% of hero width (measured); the
     cover-art column starts at ~59%. Fade stays fully transparent
     well past the text box (50%) and doesn't reach full strength
     until inside the art column (78%) — a wide margin either side,
     not a tight fit against the measured numbers. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, transparent 50%, black 78%);
  mask-image: linear-gradient(90deg, transparent 0%, transparent 50%, black 78%);
}

.ambient-light-layer {
  position: absolute;
  inset: -10%;
}
.ambient-light-layer::before,
.ambient-light-layer::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
}
/* Both glows are deliberately biased toward the cover-art (right) side
   of the hero, never the text column on the left — the opaque white
   polaroid card blocks any visual bleed there anyway, and it keeps
   contrast under the headline/pitch/meta text unaffected regardless
   of blur spread. Alpha values are chosen so contrast still clears
   WCAG AA (4.5:1) even at full peak strength with zero blur falloff
   discount — verified by direct calculation, not assumed. */

/* Warm glow — echoes the sun in the cover art */
.ambient-light-layer::before {
  top: -12%;
  right: -6%;
  width: 40%;
  height: 40%;
  background: radial-gradient(circle, rgba(122, 74, 82, 0.13) 0%, rgba(122, 74, 82, 0) 70%);
}
/* Cool glow — echoes the gum tree canopy, kept on the same (right) side */
.ambient-light-layer::after {
  bottom: -12%;
  right: 8%;
  left: auto;
  width: 44%;
  height: 44%;
  background: radial-gradient(circle, rgba(91, 110, 102, 0.11) 0%, rgba(91, 110, 102, 0) 70%);
}
@media (prefers-reduced-motion: no-preference) {
  .ambient-light-layer::before { animation: ambient-drift-a 26s ease-in-out infinite alternate; }
  .ambient-light-layer::after { animation: ambient-drift-b 32s ease-in-out infinite alternate; }
}
@keyframes ambient-drift-a {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-3%, 4%) scale(1.08); }
}
@keyframes ambient-drift-b {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(4%, -3%) scale(1.06); }
}

/* Faint static grain for depth/texture — never animated, keeps the
   "premium" feel without adding a second moving layer. */
.motion-overlay {
  position: absolute;
  inset: 0;
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- Cover art ---------- */
.cover-frame {
  margin: 0;
  border-radius: var(--radius-lg);
  padding: 16px;
  background: var(--cream-deep);
  box-shadow: var(--shadow-big);
  transform: rotate(-1.6deg);
  transition: transform 0.3s ease;
}
.cover-frame:hover { transform: rotate(0deg) scale(1.01); }
.cover-frame svg { border-radius: var(--radius); display: block; }

/* ---------- Sections & bands ---------- */
section { padding: clamp(56px, 8vh, 100px) 0; }
/* Named for the band it used to be; white is not in the palette, so this is
   the second cream — the one that tells itself apart from the page. */
.section-white { background: var(--cream-deep); }
.band-dark {
  background: var(--bark-deep);
  color: var(--cream);
}
.band-dark h1, .band-dark h2, .band-dark h3 { color: var(--cream); }
.band-dark p { color: var(--cream); }
.band-dark .muted { color: rgba(245, 242, 236, 0.82); }
.band-dark a { color: var(--cream); }

.statement {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 4vw, 3rem);
  line-height: 1.2;
  letter-spacing: -0.015em;
  font-weight: 600;
  max-width: 24ch;
  margin: 0 auto;
  text-align: center;
}
/* Was an off-palette mint. --leaf is the palette's mark green but only
   reaches 1.81:1 on --bark-deep, so this emphasis text takes --leaf-tint
   (8.04:1) instead. */
.statement em { font-style: normal; color: var(--leaf-tint); }

.section-head { max-width: 700px; margin-bottom: clamp(28px, 4vh, 48px); }

/* ---------- Cards ---------- */
.card {
  background: var(--cream-deep);
  border-radius: var(--radius);
  padding: 30px 28px;
  box-shadow: var(--shadow);
  transition: transform 0.18s ease;
}
.card:hover { transform: translateY(-4px); }
.card h2, .card h3 { font-size: clamp(1.15rem, 2vw, 1.5rem); margin-bottom: 8px; color: var(--leaf-deep); }
.card p { margin: 0; color: var(--bark); font-size: 1rem; }
/* The Readers card on the contact page carries a second paragraph — the line
   telling under-16s to check with a parent. Without this the two run together. */
.card p + p { margin-top: 12px; }
/* ---------- Split rows ---------- */
.split {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(36px, 6vw, 72px);
  align-items: center;
}
@media (max-width: 880px) { .split { grid-template-columns: 1fr; } }

/* ---------- Author photo ---------- */
.avatar-photo {
  width: 250px; height: 250px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
  margin: 0 auto 28px;
  box-shadow: var(--shadow-big);
  border: 4px solid var(--cream);
}
.about-hero .avatar-photo { width: 220px; height: 220px; margin-bottom: 24px; }

/* ---------- Buy panel ---------- */
/* Sits inside .section-white (--cream-deep), so this panel takes --cream to
   stay distinguishable from the band behind it. */
.format-panel {
  background: var(--cream);
  border-radius: var(--radius-lg);
  padding: clamp(28px, 4vw, 44px);
  display: grid;
  gap: 22px;
  box-shadow: var(--shadow-big);
}
.price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  border-bottom: 2px solid var(--cream-deep);
  padding-bottom: 18px;
}
.price-row .price {
  font-family: var(--font-display);
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--bark-deep);
}
.support-note {
  background: var(--leaf-tint);
  border-radius: var(--radius-sm);
  padding: 18px 20px;
  font-size: 1rem;
  color: var(--bark-deep);
}

/* ---------- Chapter reader ---------- */
.extract-panel {
  margin-top: 28px;
  background: var(--cream-deep);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-big);
}
.extract-panel[hidden] { display: none; }
/* Chapter one flows in the page. It is not trapped in an inner scroller —
   a fixed-height scroll box hides most of the text and traps the wheel. */
.extract-inner {
  padding: clamp(28px, 5vw, 56px);
}
.extract-inner h3 {
  font-family: var(--font-type);
  font-weight: 700;
  color: var(--bark-deep);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 1.15rem;
  margin-bottom: 1.6em;
}
.extract-inner p { color: var(--bark-deep); }
.extract-inner .day-heading {
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  color: var(--leaf-deep);
  font-size: 1rem;
  margin: 2em 0 0.5em;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.extract-inner .guidebook {
  background: var(--leaf-tint);
  border-left: 4px solid var(--leaf);
  padding: 18px 22px;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  margin: 1.4em 0;
  font-weight: 600;
  font-style: italic;
}
.extract-inner .sign {
  display: inline-block;
  background: var(--bark-tint);
  color: var(--bark-deep);
  font-family: var(--font-type);
  font-weight: 600;
  font-style: italic;
  letter-spacing: 0.06em;
  font-size: 1rem;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  margin: 4px 8px 4px 0;
  transform: rotate(-1deg);
}
.extract-fade {
  padding: 24px clamp(28px, 5vw, 56px) 32px;
  border-top: 2px dashed var(--cream-deep);
  background: var(--cream);
  text-align: center;
}

/* ---------- Chapter disclosure (book.html) ----------
   book.html opens chapter one with a native <details class="extract-details">
   and <summary class="btn btn-primary extract-summary">. No script is
   involved, so the chapter opens on a blocked or failed script, and the
   browser opens it by itself when the address carries a fragment inside it.
   The summary carries .btn .btn-primary as well, so it is the same control
   as every other primary button on the site — the rules below only remove
   the browser's own disclosure furniture and add the open/closed cue. */
.extract-details { margin-top: 8px; }
.extract-summary {
  /* Every browser's default triangle, three ways: Firefox reads list-style,
     older Safari draws ::-webkit-details-marker, and the rest drop the
     marker as soon as the summary is not display:list-item. .btn already
     sets inline-flex; it is restated so the marker still goes if the .btn
     classes are ever dropped from the markup. */
  display: inline-flex;
  list-style: none;
  /* .btn's 48px already clears the 44px minimum. Restated for the same
     reason — the tap target must not depend on another class being there. */
  min-height: 48px;
  text-align: left;
}
.extract-summary::-webkit-details-marker { display: none; }
/* Closed and open both have to read plainly. The words stay "Read chapter
   one free" in both states — one form of words, per the build spec — so the
   arrow carries the state: down when there is something to open, up when
   the chapter is already showing below it. */
.extract-summary::after {
  content: "\2193"; /* down arrow */
  font-weight: 700;
  line-height: 1;
  transition: transform 0.2s ease;
}
.extract-details[open] .extract-summary::after { transform: rotate(180deg); }
/* The global focus rule (see "Focus" above) already covers summary on light
   backgrounds. This is the dark-surface half of that rule, which names
   a and button only — without it the ring would land plum on plum if the
   control is ever placed inside a dark band. */
.band-dark .extract-summary:focus-visible,
.cta-panel .extract-summary:focus-visible {
  outline-color: var(--focus-on-dark);
}
/* Nothing here animates the panel open. The reduced-motion block at the top
   of the file kills the arrow's transition along with every other one, and
   a <summary> that neither slides nor scrolls has nothing else to strip. */

/* ---------- Forms ---------- */
/* The contact form is followed by the privacy line and the email alternative.
   Without this they sit flush against the Send button and read as part of it. */
form { margin-bottom: 28px; }
.form-field { margin-bottom: 22px; }
label { display: block; font-weight: 700; margin-bottom: 8px; }
.hint { font-size: 1rem; font-weight: 300; color: var(--bark); margin-top: 6px; }
/* --border-strong (--leaf-deep) clears 3:1 on both the field fill (--cream,
   6.79:1) and the band behind it (--cream-deep, 6.26:1). The old
   --cream-deep border measured 1.21:1. */
input[type="text"], input[type="email"], select, textarea {
  width: 100%;
  padding: 14px 16px;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 1rem;
  min-height: 48px;
  background: var(--cream);
  color: var(--bark-deep);
}
textarea { resize: vertical; min-height: 150px; }
/* The error state, built from the eight approved colours only.
   STILL BLOCKED: none of Kim's eight is a warning colour, and a ninth needs
   his approval. Until he gives it, be honest about what actually carries the
   message here. Two signals do the work:
     1. the border doubles, 2px -> 4px — a shape change, so it reads for
        anyone, including a reader who cannot tell the two hues apart;
     2. the message below appears as a tinted block with a 4px bar down its
        left edge, so it stops looking like ordinary body text.
   Two more are reinforcement only, and must not be counted on:
     3. the border turns from the green --border-strong to the plum
        --bark-deep — measured at 1.29:1 between the two, so it is a hue
        change a reader may or may not notice, not a signal in its own right;
     4. the field fills with --bark-tint — 1.12:1 against --cream, near
        invisible on its own.
   Both were measured, not assumed. Neither can be made perceivable inside
   the approved eight, because every dark value in the palette sits at much
   the same lightness. That is the gap a real warning colour would fill, and
   it stays flagged for Kim.
   Text contrast is fine either way: --bark-deep on --bark-tint is 7.85:1,
   and the 4px plum border on --cream is 8.78:1 — both well past AA. */
.field-error input, .field-error textarea, .field-error select {
  border-width: 4px;
  border-color: var(--bark-deep);
  background: var(--bark-tint);
}
.error-text {
  color: var(--bark-deep);
  font-size: 1rem;
  margin-top: 7px;
  display: none;
  /* 600 -> 700: the same weight as a label, so the correction reads as
     loudly as the thing it is correcting. */
  font-weight: 700;
  background: var(--bark-tint);
  border-left: 4px solid var(--bark-deep);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 8px 12px;
}
.field-error .error-text { display: block; }
.honeypot-field { position: absolute; left: -9999px; }

/* ---- Chapter-one review: rating chips and the permission checkbox ----
   Built from the same eight values as everything else. No new colour is
   introduced here, and none is needed: every state below is carried by a
   shape or a fill that is already in the palette.

   The rating is five radio inputs, not a widget. With JavaScript off it
   still works, still tabs, and the browser still enforces "pick one",
   because required sits on the first radio of the group. The visible chip
   is the label; the input itself is taken out of the visual flow but left
   in the accessibility tree and still focusable, so the focus ring has to
   be drawn on the chip instead (see :focus-visible below).

   The star is decorative and aria-hidden. The accessible name of each
   radio is therefore just its number — "1" through "5" — with the legend
   "Your rating" supplying the context and #rating-hint the scale. */
.rating-set { border: 0; padding: 0; margin: 0 0 22px; }
.rating-set legend { font-weight: 700; margin-bottom: 8px; padding: 0; }
.rating-options { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.rating-option { position: relative; display: inline-flex; margin: 0; font-weight: 400; }
/* Not display:none and not visibility:hidden — either would take the radio
   out of the accessibility tree and off the tab order. */
.rating-option input {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.rating-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 48px high and comfortably past 44px wide: WCAG 2.2 target size. */
  min-width: 64px;
  min-height: 48px;
  justify-content: center;
  padding: 10px 18px;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--cream);
  color: var(--bark-deep);
  font-weight: 700;
  font-size: 1.05rem;
}
.rating-star { font-size: 1.15em; line-height: 1; }
.rating-option:hover .rating-chip { background: var(--leaf-tint); }
/* Checked state does not rely on the fill alone: the border also thickens
   from 2px to 4px, so it reads as a shape change for anyone who cannot
   separate the two hues. --cream on --leaf-deep measures 8.05:1. */
.rating-option input:checked + .rating-chip {
  background: var(--leaf-deep);
  border-color: var(--leaf-deep);
  border-width: 4px;
  color: var(--cream);
}
.rating-option input:focus-visible + .rating-chip {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* The permission checkbox. label is display:block site-wide, which would
   drop the box onto its own line above the words, so this pair is laid out
   explicitly instead. */
.checkbox-field {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0;
}
.checkbox-field input[type="checkbox"] {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  margin: 0;
  /* Draws the tick in the palette rather than the browser's blue. */
  accent-color: var(--leaf-deep);
}
.checkbox-field label {
  margin: 0;
  font-weight: 400;
  /* Lines the words up with the box rather than its top edge. */
  padding-top: 1px;
}
.checkbox-field input[type="checkbox"]:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
.form-status {
  margin-top: 20px;
  padding: 16px 20px;
  border-radius: var(--radius-sm);
  background: var(--leaf-tint);
  color: var(--leaf-deep);
  font-weight: 600;
  display: none;
}
.form-status.is-visible { display: block; }
/* The same box also carries failures ("your message hasn't been sent yet"),
   which must not arrive dressed in the success palette. script.js adds
   .is-error for those. As with the field error state, the weight and the
   left bar do the work, not the hue — a real warning colour is still
   blocked pending Kim's approval. --bark-deep on --bark-tint is 7.85:1. */
.form-status.is-error {
  background: var(--bark-tint);
  color: var(--bark-deep);
  font-weight: 700;
  border-left: 6px solid var(--bark-deep);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.contact-reasons {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin: 34px 0 10px;
}

/* ---------- CTA panel ---------- */
.cta-panel {
  background: var(--bark-deep);
  color: var(--cream);
  border-radius: var(--radius-lg);
  padding: clamp(44px, 7vw, 76px) clamp(28px, 5vw, 72px);
  text-align: center;
  box-shadow: var(--shadow-big);
}
.cta-panel h2 { color: var(--cream); margin-bottom: 12px; }
.cta-panel p { color: rgba(245, 242, 236, 0.85); max-width: 48ch; margin: 0 auto 8px; }
.cta-panel .btn-row { justify-content: center; }
.cta-panel .btn-primary { background: var(--cream); color: var(--bark-deep); }
.cta-panel .btn-primary:hover { background: var(--cream-deep); }
.cta-panel .btn-outline { border-color: var(--cream); color: var(--cream); }
.cta-panel .btn-outline:hover { background: rgba(245,242,236,0.12); }

/* ---------- Footer ---------- */
.site-footer {
  background: var(--leaf-deep);
  color: var(--cream);
  padding: 52px 0 30px;
}
/* The email address was a 24px strip with no padding of its own, which is a
   miss or a text selection on a phone. The padding goes on the link, so the
   area being tapped is the area being read. Every footer link gets it,
   including the privacy link, so they all match. */
.site-footer a {
  color: var(--cream);
  display: inline-block;
  padding: 10px 4px;
}
.footer-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
  justify-content: space-between;
  margin-bottom: 30px;
}
.footer-note { font-size: 1rem; max-width: 52ch; line-height: 1.7; color: rgba(245, 242, 236, 0.85); }
.footer-bottom {
  /* 0.25 alpha measured 1.79:1 against --leaf-deep. 0.55 clears 3:1. */
  border-top: 1px solid rgba(245, 242, 236, 0.55);
  padding-top: 22px;
  font-size: 1rem;
  font-weight: 300;
  color: rgba(245, 242, 236, 0.8);
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: space-between;
}

/* ---------- Utility ---------- */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.small { font-size: 1rem; font-weight: 300; }
.muted { color: var(--bark); }
.tag {
  display: inline-block;
  font-size: 1rem;
  font-weight: 500;
  font-style: italic;
  letter-spacing: 0.06em;
  color: var(--leaf-deep);
  background: var(--leaf-tint);
  padding: 8px 16px;
  border-radius: 999px;
  margin: 0 8px 8px 0;
}
/* The whole pill is the target, not just the words inside it — the negative
   margin pulls the link's own padding back out over the pill's padding so the
   hit area matches what is seen (well past the 24x24 minimum). */
.tag a {
  display: inline-block;
  margin: -8px -16px;
  padding: 8px 16px;
  min-height: 24px;
  border-radius: 999px;
  text-decoration: none;
  color: var(--leaf-deep);
}

/* ---------- Journey progress bar ---------- */
.journey-progress {
  position: fixed;
  top: 0; left: 0;
  height: 4px;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left;
  background: linear-gradient(90deg, var(--bark-deep), var(--leaf-deep));
  z-index: 300;
  pointer-events: none;
}

/* ---------- The road (journey timeline) ---------- */
.road {
  position: relative;
  padding: 10px 0 30px;
}
.road::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  border-left: 4px dashed var(--leaf);
  /* 0.5 measured 1.99:1 on --cream. 0.8 clears 3:1. */
  opacity: 0.8;
  transform: translateX(-2px);
}
.waypoint {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 70px;
  padding: 34px 0;
}
.waypoint-card {
  background: var(--cream-deep);
  border-radius: var(--radius);
  padding: 30px 30px 28px;
  box-shadow: var(--shadow);
  transition: transform 0.18s ease;
}
.waypoint-card:hover { transform: translateY(-4px); }
.waypoint:nth-child(odd) .waypoint-card { grid-column: 1; text-align: right; }
.waypoint:nth-child(even) .waypoint-card { grid-column: 2; text-align: left; }
.waypoint .marker {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 62px; height: 62px;
  border-radius: 50%;
  background: var(--bark-deep);
  color: var(--cream);
  border: 4px solid var(--cream);
  box-shadow: var(--shadow);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-type);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.06em;
}
.waypoint .num {
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* --leaf only reaches 4.48:1 on the card's --cream-deep. --leaf-deep is
     6.26:1. */
  color: var(--leaf-deep);
  margin: 0 0 6px;
}
.waypoint h3 { color: var(--bark-deep); margin: 0 0 6px; }
.waypoint p:last-child { color: var(--bark); margin: 0; font-size: 1rem; }

@media (max-width: 820px) {
  .road::before { left: 30px; }
  .waypoint { grid-template-columns: 1fr; padding: 26px 0; }
  .waypoint:nth-child(odd) .waypoint-card,
  .waypoint:nth-child(even) .waypoint-card {
    grid-column: 1;
    text-align: left;
    margin-left: 72px;
  }
  .waypoint .marker {
    left: 30px;
    width: 54px; height: 54px;
    font-size: 1rem;
  }
}

/* ---------- The hiker (travels the road as you scroll) ---------- */
.hiker {
  position: absolute;
  left: 50%;
  top: 0;
  width: 44px;
  transform: translate(-50%, -50%);
  z-index: 2;
  pointer-events: none;
  /* --bark-deep at 0.33 — same composite lightness over cream as the
     near-black 0.3 it replaces, and one of the approved eight. */
  filter: drop-shadow(0 4px 8px rgba(94, 57, 66, 0.33));
}
@media (max-width: 820px) {
  .hiker { left: 30px; width: 38px; }
}

/* ---------- Passport (stamp collection) ---------- */
.passport {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 150;
  background: var(--cream-deep);
  border: 2px solid var(--bark-deep);
  border-radius: var(--radius);
  box-shadow: var(--shadow-big);
  padding: 12px 16px;
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.06em;
  color: var(--bark-deep);
  display: none;
}
.passport.is-active { display: block; }
.passport .stamps {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}
.passport .stamp-dot {
  width: 16px; height: 16px;
  border-radius: 50%;
  border: 2px dashed var(--leaf);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.passport .stamp-dot.is-stamped {
  background: var(--leaf-deep);
  border: 2px solid var(--leaf-deep);
}
.passport a { display: inline-block; min-height: 24px; padding: 2px 0; color: var(--bark-deep); font-weight: 600; }
@media (max-width: 600px) {
  /* As a floating card this sat on top of the headline on a 375px screen.
     Pinned to the bottom edge as a bar, with the page reserving room for it,
     it never covers content. It stays dismissible from the keyboard. */
  .passport {
    right: 0;
    left: 0;
    bottom: 0;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    display: none;
  }
  .passport.is-active {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
  }
  .passport .stamps { margin-top: 0; }
  body:has(.passport.is-active) { padding-bottom: 96px; }
}

/* Stamped marker on visited stops */
.waypoint.is-visible .marker {
  background: var(--leaf-deep);
  transform: translate(-50%, -50%) rotate(-8deg);
}

/* ---------- Hazard line (guidebook warnings) ---------- */
.hazard {
  margin: 14px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--bark-tint);
  color: var(--bark-deep);
  font-family: var(--font-type);
  font-weight: 600;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.06em;
  transform: rotate(-0.5deg);
}
.waypoint:nth-child(odd) .hazard { transform: rotate(0.5deg); }
/* The guidebook warnings are the funniest lines on the page. A half-degree
   tilt is a wink on a wide screen; in a phone-width column it just makes a
   two-line joke look crooked. Straighten them, keep the tilt above 760px. */
@media (max-width: 760px) {
  .hazard,
  .waypoint:nth-child(odd) .hazard { transform: none; }
}

/* ---------- The road on a phone (600px and below) ----------
   Above this width the road is a dashed line with the cards hanging off it,
   and the cards are pushed 72px right to leave it room. On a 375px screen
   that spends a quarter of the width on a line and a circle and squeezes the
   prose to about 24 characters a line — three or four words, which is tiring
   to read and kills the timing of the jokes.

   So below 600px the road is rebuilt rather than patched:
     1. the card takes the full width, no left margin;
     2. the numbered marker moves out of absolute positioning and sits above
        the card, in its own grid row;
     3. the dashed line moves to run through the middle of the markers, and
        the cards are lifted above it, so the line is only ever seen in the
        gaps — it threads from stop to stop like a road instead of running
        down the side of the page;
     4. the hiker stands down (see below).

   Measured at 375px: 323px of container, less 40px of card padding, leaves
   283px for the words at 16px Spectral — roughly 38 characters a line,
   rising to about 60 at the top of this breakpoint. Not the 45 the review
   hoped for at the very narrowest width; 45 characters at this type size
   needs about 360px of text, which is the whole screen with no margins at
   all. This is as close as it goes without taking the page's edges away. */
@media (max-width: 600px) {
  /* Through the middle of a 54px marker sitting at the container edge.
     The 4px dashed border is centred on that by the inherited translate. */
  .road::before { left: 27px; }
  .waypoint:nth-child(odd) .waypoint-card,
  .waypoint:nth-child(even) .waypoint-card {
    grid-column: 1;
    grid-row: 2;
    margin-left: 0;
    /* Opaque --cream-deep over the dashed line, so the road disappears
       behind each card instead of striking through the text. */
    position: relative;
    z-index: 1;
  }
  /* 30px of side padding is a luxury at this width — 20px buys back two
     characters a line and the card still does not feel cramped. */
  .waypoint-card { padding: 24px 20px 22px; }
  .waypoint .marker {
    /* Out of absolute positioning and back into the grid, above the card.
       position:relative keeps it in flow but still allows the z-index that
       lifts it over the road line. */
    position: relative;
    top: auto;
    left: auto;
    transform: none;
    z-index: 2;
    grid-row: 1;
    grid-column: 1;
    justify-self: start;
    margin: 0 0 14px;
  }
  /* The stamp on a visited stop keeps its tilt, minus the centring shift it
     no longer needs. */
  .waypoint.is-visible .marker { transform: rotate(-8deg); }
  /* The hiker travelled the gutter the cards have just taken back. With the
     cards full width he would walk across the words, and he is decorative,
     so he stands down here. The markers still stamp as each stop is reached
     and the passport bar still counts them, so the progress idea survives. */
  .hiker { display: none; }
}
/* Companion to the block above: the reduced-motion rule near the top of the
   file re-centres the stamped marker with !important, which is right for the
   absolutely positioned desktop marker and wrong for this one — it would
   shift the circle up and left by half its own size. */
@media (max-width: 600px) and (prefers-reduced-motion: reduce) {
  .waypoint.is-visible .marker { transform: none !important; }
}

/* ---------- Postcards (quotes on the journey) ---------- */
.postcard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 22px;
}
.postcard {
  margin: 0;
  position: relative;
  background: var(--cream-deep);
  border: none;
  border-radius: var(--radius-sm);
  padding: 30px 28px 26px;
  box-shadow: var(--shadow);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.22rem;
  line-height: 1.45;
  color: var(--bark-deep);
  transform: rotate(-1.2deg);
}
.postcard:nth-child(2) { transform: rotate(1deg); }
.postcard:nth-child(3) { transform: rotate(-0.6deg); }
.postcard::before { display: none; }
.postcard::after {
  content: "";
  position: absolute;
  top: 14px; right: 14px;
  width: 34px; height: 42px;
  border: 2px solid var(--bark-tint);
  border-radius: 3px;
  background:
    radial-gradient(circle at 50% 42%, var(--bark-tint) 0 8px, transparent 8px);
}
.postcard footer {
  margin-top: 16px;
  font-family: var(--font-type);
  font-weight: 500;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--leaf-deep);
}

/* ---------- Reveal ---------- */
/* The hidden start state is gated behind .js on <html>, which script.js adds
   before anything else runs. No JavaScript, no .js class, nothing hidden —
   every revealed section is readable with scripting off. */
.reveal { opacity: 1; transform: none; }
@media (prefers-reduced-motion: no-preference) {
  .js .reveal[data-reveal] {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.65s cubic-bezier(0.2, 0.6, 0.2, 1), transform 0.65s cubic-bezier(0.2, 0.6, 0.2, 1);
  }
  .js .reveal[data-reveal].is-visible { opacity: 1; transform: none; }
}

/* ---------- Print (also used to render the PDF preview for Kim) ---------- */
@media print {
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
  html, body { background: var(--cream); }
  /* Interactive / fixed chrome has no place on paper */
  .journey-progress, .passport, .hiker, .nav-toggle, .skip-link { display: none !important; }
  .hero-background, .ambient-light-layer, .motion-overlay, .generated-visual-slot { display: none !important; }
  .site-header { position: static; }
  /* Force every scroll-reveal section fully visible */
  .reveal[data-reveal] { opacity: 1 !important; transform: none !important; }
  /* Keep design blocks whole across page breaks */
  .waypoint, .waypoint-card, .card, .postcard, .cover-frame,
  .format-panel, .cta-panel, .hero-art { break-inside: avoid; }
  .band-dark, .cta-panel, .site-footer { break-inside: avoid; }
}
