/* =========================================================
   Wish-Birthday.com — stylesheet
   Light, professional, mobile-first. No framework, no build
   step, no webfont. Restyle by editing :root only.

   Written mobile-first: base styles are the phone layout, and
   every media query is min-width. Nothing is undone later.

   Breakpoint ladder
     base      320–479   small phone
     30rem     480+      large phone
     48rem     768+      tablet portrait
     64rem     1024+     tablet landscape / laptop
     80rem     1280+     desktop

   Rules marked [PERF] exist for speed, [TOUCH] for finger
   targets, [MOBILE] for phone-specific correctness.
   Don't "tidy" those away.

   Contents
   1. Tokens
   2. Reset + base
   3. Layout: sections, wrap, prose
   4. Header + navigation
   5. Hero
   6. Wish list
   7. Tiles, chips, breadcrumbs, FAQ
   8. Footer
   9. Utilities
   10. Breakpoints
   11. Capability + preference queries
   ========================================================= */

/* ---------- 1. Tokens ---------------------------------- */
:root {
  /* colour */
  /* Warm neutrals. A stark #FFFFFF page cannot read as romantic
     whatever accent sits on it, so the whole base is shifted warm.
     Every value below was contrast-checked, not eyeballed: ink is
     16.6:1 on paper, body 8.6:1, muted 5.3:1 — all pass AA for
     normal text. An earlier muted (#8A7A84) looked right and came
     out at 3.95:1, so it was darkened. */
  --page:        #FFFCFA;
  --wash:        #FDF6F4;
  --ink:         #221A20;
  --body:        #55474F;
  --muted:       #786470;
  --line:        #EFE4E7;
  --line-soft:   #F7F0F2;
  --accent:      #2E5F5B;
  --accent-dark: #234A47;
  --accent-wash: #EDF3F2;

  /* type — system stacks only, nothing to download */
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
           Georgia, "Times New Roman", serif;

  /* scale — phone values, stepped up at breakpoints */
  --t-xs:   0.8125rem;
  --t-sm:   0.9375rem;
  --t-base: 1rem;
  --t-md:   1.0625rem;
  --t-lg:   1.25rem;
  --t-xl:   1.5rem;
  --t-2xl:  1.875rem;
  --t-3xl:  clamp(2rem, 7vw, 3.5rem);

  /* [MOBILE] Form controls must never go below 16px. iOS Safari
     auto-zooms the page on focus for anything smaller, which throws
     the layout sideways when someone taps the name field. */
  --t-input: 1rem;

  /* space — phone values */
  --s-1: 0.375rem;
  --s-2: 0.75rem;
  --s-3: 1.125rem;
  --s-4: 1.5rem;
  --s-5: 2.25rem;
  --s-6: 3rem;

  /* layout */
  --rail: 1280px;
  --gutter: 1.25rem;
  --measure: 66ch;
  --radius: 10px;   /* softened: sharp corners read as clinical */

  /* [MOBILE] Header height as a variable, not a magic number.
     The mobile panel offset and anchor scroll padding both read it,
     so changing the header can't desync them. */
  --head-h: 56px;

  /* [TOUCH] Minimum comfortable finger target */
  --tap: 44px;
}

/* ---------- 2. Reset + base ---------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* [MOBILE] stops anchor targets hiding under the sticky header */
  scroll-padding-top: calc(var(--head-h) + 1rem);
}

body {
  margin: 0;
  background: var(--page);
  color: var(--body);
  font: 400 var(--t-base)/1.68 var(--sans);
  /* [PERF] clip, not hidden — hidden makes body a scroll container,
     which can break position:sticky and adds layout work */
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  /* [MOBILE] long unbroken strings — email addresses, URLs,
     Devanagari compounds — must not force horizontal scroll */
  overflow-wrap: break-word;
}

h1, h2, h3 {
  font-family: var(--sans);
  color: var(--ink);
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: -0.018em;
  margin: 0 0 var(--s-3);
}

h1 { font-size: var(--t-2xl); line-height: 1.15; letter-spacing: -0.024em; }
h2 { font-size: var(--t-xl); margin-top: var(--s-5); }
h3 { font-size: var(--t-base); margin-top: var(--s-4); letter-spacing: -0.008em; }

p, ul, ol { margin: 0 0 var(--s-3); }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  /* [MOBILE] kill the grey flash box on tap in mobile Safari/Chrome */
  -webkit-tap-highlight-color: rgba(46, 95, 91, 0.12);
}
a:hover { color: var(--accent-dark); }

strong { color: var(--ink); font-weight: 600; }

/* [PERF] always set width and height attributes on <img> in the HTML
   too, so space is reserved before the file arrives */
img { max-width: 100%; height: auto; display: block; }

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

.skip {
  position: absolute; left: -9999px;
  background: var(--ink); color: #fff;
  padding: var(--s-2) var(--s-3); z-index: 100;
}
.skip:focus { left: var(--s-3); top: var(--s-3); }

/* ---------- 3. Layout ---------------------------------- */
.band {
  width: 100%;
  padding: var(--s-5) 0;
  border-bottom: 1px solid var(--line-soft);
}
.band:last-of-type { border-bottom: 0; }
.band--tint { background: var(--wash); }
.band--ink { background: var(--wash); }   /* kept for compatibility */
.band--tight { padding: var(--s-4) 0; }

/* [PERF] Skips layout, style and paint for off-screen sections.
   Add class="defer" to any band below the fold. */
.defer {
  content-visibility: auto;
  contain-intrinsic-size: auto 700px;
}

.wrap {
  width: 100%;
  max-width: var(--rail);
  margin-inline: auto;
  /* [MOBILE] safe-area insets keep content clear of the notch and
     the rounded corners in landscape on iPhone */
  padding-inline: max(var(--gutter), env(safe-area-inset-left))
                  max(var(--gutter), env(safe-area-inset-right));
}

.prose { max-width: var(--measure); }
.prose--wide { max-width: 82ch; }

/* stacked on phones; becomes two columns at 64rem */
.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-5); }

/* ---------- 4. Header + navigation --------------------- */
.site-head {
  position: sticky; top: 0; z-index: 50;
  /* [PERF] solid background, no backdrop-filter — a blur on a sticky
     element repaints the full page width on every scroll frame */
  background: var(--page);
  border-bottom: 1px solid var(--line);
  /* layout only, NOT paint. Paint containment clips descendants to
     the box, which would have silently swallowed the scroll shadow
     drawn by .site-head::after below it. */
  contain: layout;
}

.head-row {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--head-h);
}

.brand {
  font-size: var(--t-base);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  margin-right: auto;
  /* [TOUCH] full-height tap area for the home link */
  display: flex; align-items: center;
  min-height: var(--tap);
}

/* primary links are hidden on phones — they live in the panel
   instead, so there is one menu rather than two */
.nav { display: none; }

.allpages { position: relative; }
.allpages > summary {
  list-style: none; cursor: pointer;
  font: 450 var(--t-sm)/1 var(--sans);
  color: var(--ink);
  background: var(--page);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* [TOUCH] 44px target, not a 30px pill */
  min-height: var(--tap);
  padding-inline: 0.9rem;
  display: flex; align-items: center;
}
.allpages > summary::-webkit-details-marker { display: none; }
.allpages > summary:hover { border-color: var(--muted); }
.allpages[open] > summary { border-color: var(--accent); color: var(--accent); }

/* [MOBILE] full-screen sheet on phones */
.allpages__panel {
  position: fixed; left: 0; right: 0; top: var(--head-h);
  width: 100%;
  /* dvh accounts for the collapsing browser toolbar; vh is the
     fallback for older engines and overshoots slightly */
  max-height: calc(100vh - var(--head-h));
  max-height: calc(100dvh - var(--head-h));
  overflow-y: auto;
  overscroll-behavior: contain;   /* [PERF] no scroll chaining to body */
  -webkit-overflow-scrolling: touch;
  background: var(--page);
  border-top: 1px solid var(--line);
  padding: var(--s-4) var(--gutter)
           calc(var(--s-5) + env(safe-area-inset-bottom));
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  contain: layout paint;
}
.allpages__panel h4 {
  font: 600 var(--t-xs)/1.3 var(--sans);
  color: var(--ink);
  margin: 0 0 var(--s-1);
  padding-bottom: var(--s-1);
  border-bottom: 1px solid var(--line-soft);
}
.allpages__panel ul { list-style: none; margin: 0; padding: 0; }
.allpages__panel li { margin: 0; }
.allpages__panel a {
  display: flex; align-items: center;
  font-size: var(--t-sm);
  text-decoration: none;
  color: var(--body);
  border-radius: 2px;
  /* [TOUCH] rows in a 50-item menu are the most-tapped thing on the
     site — they get a full target each */
  min-height: 40px;
  padding: 2px 6px;
}
.allpages__panel a:hover { background: var(--accent-wash); color: var(--accent-dark); }

/* ---------- 5. Hero ------------------------------------ */
.hero { padding: var(--s-5) 0; border-bottom: 1px solid var(--line-soft); }
.hero h1 {
  font-size: var(--t-3xl);
  line-height: 1.1;
  letter-spacing: -0.026em;
  max-width: 20ch;
  margin-bottom: var(--s-3);
}
.hero__lede { font-size: var(--t-md); line-height: 1.6; color: var(--muted); max-width: 48ch; }

.demo {
  margin-top: var(--s-4);
  border: 1px solid var(--line);
  border-top: 2px solid var(--accent);
  border-radius: var(--radius);
  background: var(--page);
  padding: var(--s-4) var(--s-3);
}
/* [PERF] min-height reserves space so rotating the wish text never
   shifts what's below it. Taller on phones because text wraps more. */
.demo__out {
  font-family: var(--serif);
  font-size: var(--t-lg);
  line-height: 1.45;
  color: var(--ink);
  min-height: 4.4em;
  margin: 0 0 var(--s-3);
}
.demo__row { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }

.field {
  /* [MOBILE] 16px minimum — see --t-input */
  font: 400 var(--t-input) var(--sans);
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--page);
  color: var(--ink);
  min-height: var(--tap);
  /* [MOBILE] full width on phones, share the row from 30rem up */
  flex: 1 1 100%;
  min-width: 0;
}
.field::placeholder { color: var(--muted); }
.field:focus { border-color: var(--accent); }

.btn {
  font: 450 var(--t-input) var(--sans);
  border-radius: var(--radius);
  border: 1px solid var(--ink);
  background: var(--ink);
  color: #fff;
  cursor: pointer;
  min-height: var(--tap);
  padding-inline: 1.05rem;
  flex: 1 1 auto;
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { background: #000; border-color: #000; }
.btn--ghost { background: var(--page); color: var(--body); border-color: var(--line); }
.btn--ghost:hover { color: var(--ink); border-color: var(--muted); }

/* ---------- 6. Wish list ------------------------------- */
.wishlist {
  list-style: none; margin: 0; padding: 0;
  counter-reset: wish;
  display: grid; gap: 0;
  grid-template-columns: minmax(0, 1fr);
  border-top: 1px solid var(--line);
}

/* phone: number and text on one row, Copy beneath */
.wish {
  counter-increment: wish;
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: 0 var(--s-2);
  align-items: start;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--line-soft);
}

/* numerals are functional — the guidance text refers to wishes by
   number — so they stay, but quietly */
.wish::before {
  content: counter(wish);
  font: 400 var(--t-sm)/1.7 var(--sans);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.wish__text {
  font-family: var(--serif);
  font-size: var(--t-md);
  line-height: 1.58;
  color: var(--ink);
  margin: 0;
}

.copy {
  font: 450 var(--t-sm) var(--sans);
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  white-space: nowrap;
  /* [TOUCH] 44px, and a fixed width so swapping the label to
     "Copied" can't reflow the row */
  min-height: var(--tap);
  min-width: 5.5rem;
  padding-inline: 0.8rem;
  -webkit-tap-highlight-color: transparent;
  /* phone: sits under the text, aligned with it */
  grid-column: 2;
  justify-self: start;
  margin-top: var(--s-2);
}
.copy:hover { border-color: var(--accent); color: var(--accent); }
.copy[data-done="true"] {
  background: var(--accent-wash);
  border-color: var(--accent);
  color: var(--accent-dark);
}

.personalise {
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  align-items: center;
  padding: 0 0 var(--s-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s-2);
}
.personalise label {
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--ink);
  flex: 1 1 100%;
}

/* ---------- 7. Tiles, chips, crumbs, FAQ --------------- */
.grid {
  display: grid; gap: var(--s-4);
  grid-template-columns: minmax(0, 1fr);
}
.tile { border-top: 1px solid var(--line); padding-top: var(--s-2); }
.tile h3 { margin: 0 0 var(--s-1); font-size: var(--t-sm); }
.tile h3 a { text-decoration: none; color: var(--ink); display: inline-block; padding: 2px 0; }
.tile h3 a:hover { color: var(--accent); }
.tile p { font-size: var(--t-sm); color: var(--muted); margin: 0; line-height: 1.6; }

.chips {
  display: flex; flex-wrap: wrap; gap: var(--s-1);
  list-style: none; padding: 0; margin: 0 0 var(--s-4);
}
.chips a {
  display: flex; align-items: center;
  font-size: var(--t-sm);
  text-decoration: none;
  color: var(--body);
  background: var(--page);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* [TOUCH] chips are a primary navigation surface on phones */
  min-height: var(--tap);
  padding-inline: 0.85rem;
}
.chips a:hover { border-color: var(--accent); color: var(--accent-dark); }

.crumbs { font-size: var(--t-xs); color: var(--muted); padding: var(--s-3) 0 0; }
.crumbs ol {
  list-style: none; display: flex; flex-wrap: wrap;
  gap: 0.35rem; margin: 0; padding: 0;
}
.crumbs li:not(:last-child)::after { content: "/"; margin-left: 0.35rem; color: var(--line); }
.crumbs a { color: var(--muted); text-decoration: none; }
.crumbs a:hover { color: var(--ink); }

.faq { border-top: 1px solid var(--line); max-width: 82ch; }
.faq details { border-bottom: 1px solid var(--line-soft); }
.faq summary {
  cursor: pointer;
  font-weight: 500;
  color: var(--ink);
  position: relative;
  list-style: none;
  /* [TOUCH] whole row is the target, with room for the +/- */
  min-height: var(--tap);
  display: flex; align-items: center;
  padding: var(--s-3) 2rem var(--s-3) 0;
  -webkit-tap-highlight-color: transparent;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--accent); }
.faq summary::after {
  content: "+"; position: absolute; right: 0; top: 50%;
  transform: translateY(-50%);
  color: var(--muted); font-weight: 400;
}
.faq details[open] summary::after { content: "\2212"; }
.faq details > p { max-width: var(--measure); padding-bottom: var(--s-3); margin: 0; }

/* ---------- 8. Footer ---------------------------------- */
.site-foot {
  background: var(--page);
  color: var(--body);
  border-top: 1px solid var(--line);
  padding: var(--s-5) 0 var(--s-4);
  /* [MOBILE] clear the iOS home indicator */
  padding-bottom: calc(var(--s-4) + env(safe-area-inset-bottom));
  /* [PERF] ~60 links, always below the fold */
  content-visibility: auto;
  contain-intrinsic-size: auto 900px;
}
/* [MOBILE] two columns on phones. Six stacked groups would make the
   footer taller than the page content. */
.foot-grid {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.site-foot h4 { font: 600 var(--t-xs)/1.3 var(--sans); color: var(--ink); margin: 0 0 var(--s-1); }
.site-foot ul { list-style: none; margin: 0; padding: 0; }
.site-foot li { margin: 0; }
.site-foot a {
  color: var(--muted);
  font-size: var(--t-sm);
  text-decoration: none;
  /* [TOUCH] footer links are notoriously hard to hit on phones */
  display: flex; align-items: center;
  min-height: 38px;
}
.site-foot a:hover { color: var(--ink); }
.foot-base {
  margin-top: var(--s-4); padding-top: var(--s-3);
  border-top: 1px solid var(--line-soft);
  font-size: var(--t-xs); color: var(--muted);
  display: flex; flex-wrap: wrap; gap: var(--s-2);
}

/* ---------- 9. Utilities ------------------------------- */
.lede { font-size: var(--t-md); color: var(--muted); line-height: 1.6; }
.muted { color: var(--muted); }
.small { font-size: var(--t-sm); }
.stack > * + * { margin-top: var(--s-3); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.note {
  border-left: 2px solid var(--accent);
  padding: var(--s-1) 0 var(--s-1) var(--s-3);
  margin: var(--s-4) 0;
  font-size: var(--t-sm);
  color: var(--muted);
}
.note p:last-child { margin-bottom: 0; }

/* =========================================================
   10. Breakpoints — all min-width, nothing undone
   ========================================================= */

/* ---------- 30rem / 480px — large phone ---------------- */
@media (min-width: 30rem) {
  :root { --gutter: 1.5rem; --t-2xl: 2rem; }

  /* input and buttons share one row once there's space */
  .field { flex: 1 1 12rem; }
  .btn { flex: 0 0 auto; }

  .personalise label { flex: 0 0 auto; }
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot-base { justify-content: space-between; }
}

/* ---------- 48rem / 768px — tablet portrait ------------ */
@media (min-width: 48rem) {
  :root {
    --head-h: 64px;
    --gutter: 2.5rem;
    --t-md:  1.125rem;
    --t-lg:  1.375rem;
    --t-xl:  1.625rem;
    --t-2xl: 2.125rem;
    --s-3: 1.25rem;
    --s-4: 2rem;
    --s-5: 3rem;
    --s-6: 4.5rem;
  }

  .band { padding: var(--s-6) 0; }
  .band--tight { padding: var(--s-5) 0; }
  .hero { padding: var(--s-6) 0; }
  .demo { padding: var(--s-4); }
  .demo__out { min-height: 3.6em; }

  /* primary nav appears — there's room for it from tablet up */
  .nav { display: flex; gap: var(--s-4); align-items: center; }
  .nav a {
    color: var(--body);
    font-size: var(--t-sm);
    font-weight: 450;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    display: flex; align-items: center;
    min-height: var(--tap);
  }
  .nav a:hover { color: var(--ink); border-bottom-color: var(--line); }
  .nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

  /* panel becomes a dropdown rather than a sheet */
  .allpages__panel {
    position: absolute; left: auto; right: 0; top: calc(100% + 8px);
    width: min(92vw, 880px);
    max-height: none;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: 0 12px 28px -16px rgba(25, 28, 33, 0.18);
    padding: var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
  }

  /* Copy button moves back onto the wish row */
  .wish {
    grid-template-columns: 1.75rem minmax(0, 1fr) auto;
    gap: var(--s-3);
    padding: var(--s-4) 0;
  }
  .copy {
    grid-column: 3;
    margin-top: 0;
    min-height: 36px;
    min-width: 5rem;
    font-size: var(--t-xs);
  }

  .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .foot-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .site-foot a { min-height: 32px; }
  .allpages__panel a { min-height: 32px; }
}

/* ---------- 64rem / 1024px — tablet landscape / laptop -- */
@media (min-width: 64rem) {
  :root {
    --head-h: 68px;
    --gutter: 3rem;
    --t-lg:  1.4375rem;
    --t-xl:  1.75rem;
    --t-2xl: 2.375rem;
    --s-5: 3.25rem;
    --s-6: 5.5rem;
  }

  h1, h2 { text-wrap: balance; }   /* enough width for it to help */

  .split { grid-template-columns: 1.2fr 0.8fr; gap: var(--s-6); }

  /* two columns of wishes — this is where the full width gets used */
  .wishlist { grid-template-columns: 1fr 1fr; column-gap: var(--s-6); }

  .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .foot-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); }
}

/* ---------- 80rem / 1280px — desktop ------------------- */
@media (min-width: 80rem) {
  :root { --gutter: 4rem; }
}

/* =========================================================
   11. Capability + preference queries
   ========================================================= */

/* [TOUCH] Touch tablets can be wide enough to hit the desktop rules
   while still being finger-driven. Restore full targets by input
   type rather than by width. */
@media (pointer: coarse) {
  .copy, .chips a, .allpages > summary, .btn, .field { min-height: var(--tap); }
  .site-foot a, .allpages__panel a { min-height: 40px; }
  .faq summary { min-height: var(--tap); }
}

/* [MOBILE] Landscape phones have very little vertical room —
   claw some back from the section padding. */
@media (max-height: 30rem) and (orientation: landscape) {
  :root { --s-5: 1.5rem; --s-6: 2rem; }
  .hero { padding: var(--s-5) 0; }
  .demo__out { min-height: 2.6em; }
}

@media print {
  .site-head, .site-foot, .copy, .personalise, .demo__row { display: none; }
  .band, .hero { padding: 0; border: 0; }
  .defer, .site-foot { content-visibility: visible; }
  .wishlist { grid-template-columns: 1fr; }
}

/* [PERF] smooth scrolling opt-in rather than global — it delays
   anchor navigation and is a motion trigger for some people */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* =========================================================
   12. Cookie consent (consent.js)
   Bottom sheet on phones, bottom-right card from tablet up.
   Deliberately not a full-screen overlay: blocking all content
   behind a wall is what EU regulators have objected to.
   ========================================================= */
.cc {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 200;
  background: var(--page);
  border-top: 1px solid var(--line);
  box-shadow: 0 -8px 28px -18px rgba(25, 28, 33, 0.25);
  padding: var(--s-4) 0 calc(var(--s-4) + env(safe-area-inset-bottom));
  /* [MOBILE] a long consent list must not trap the page scroll */
  max-height: 85dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.cc__inner {
  width: 100%;
  max-width: var(--rail);
  margin-inline: auto;
  padding-inline: max(var(--gutter), env(safe-area-inset-left))
                  max(var(--gutter), env(safe-area-inset-right));
  display: grid;
  gap: var(--s-3);
}

.cc__title {
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.012em;
  margin: 0 0 var(--s-1);
}

.cc__text p { font-size: var(--t-sm); color: var(--body); margin: 0; max-width: 62ch; }

/* All three buttons the same visual weight. Making "reject" quieter
   than "accept" is a dark pattern and has drawn fines in the EU. */
.cc__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.cc__actions .btn { flex: 1 1 auto; }

.cc__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.cc__list li { border-top: 1px solid var(--line-soft); padding-top: var(--s-2); }
.cc__list label {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-sm); color: var(--ink);
  min-height: var(--tap);   /* [TOUCH] */
  cursor: pointer;
}
.cc__list input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); }
.cc__list input:disabled { opacity: 0.55; cursor: not-allowed; }
.cc__list p {
  font-size: var(--t-xs); color: var(--muted);
  margin: 0; padding-left: calc(18px + var(--s-2)); max-width: 60ch;
}

@media (min-width: 48rem) {
  .cc {
    left: auto; right: var(--s-4); bottom: var(--s-4);
    max-width: 33rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: var(--s-4);
  }
  .cc__inner { padding-inline: 0; max-width: none; }
  .cc__actions .btn { flex: 0 0 auto; }
}

/* inline affiliate disclosure — FTC wants this NEAR the links,
   not only on a policy page */
.affiliate-note {
  font-size: var(--t-xs);
  color: var(--muted);
  border-left: 2px solid var(--line);
  padding: var(--s-1) 0 var(--s-1) var(--s-2);
  margin: 0 0 var(--s-4);
}
.affiliate-note a { color: var(--muted); }

@media print { .cc { display: none; } }

/* footer button styled as a link — it must be a <button> because it
   performs an action rather than navigating, but it should look like
   its neighbours in the footer list */
.foot-btn {
  background: none; border: 0; padding: 0;
  font: inherit; font-size: var(--t-sm);
  color: var(--muted); cursor: pointer;
  text-align: left;
  display: flex; align-items: center;
  min-height: 38px;
}
.foot-btn:hover { color: var(--ink); text-decoration: underline; }
@media (min-width: 48rem) { .foot-btn { min-height: 32px; } }
@media (pointer: coarse) { .foot-btn { min-height: 40px; } }

/* related-collections block (links.py) — descriptive links with a
   reason, so the list is useful to a reader rather than plumbing */
.related { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.related li {
  border-top: 1px solid var(--line-soft);
  padding: var(--s-3) 0;
  font-size: var(--t-sm);
  color: var(--muted);
  line-height: 1.6;
}
.related li:last-child { border-bottom: 1px solid var(--line-soft); }
.related a {
  color: var(--ink);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.related a:hover { color: var(--accent); border-bottom-color: var(--accent); }

@media (min-width: 48rem) {
  .related { grid-template-columns: 1fr 1fr; column-gap: var(--s-6); }
  .related li:nth-last-child(2):nth-child(odd) { border-bottom: 1px solid var(--line-soft); }
}
@media (pointer: coarse) { .related a { display: inline-block; padding: 2px 0; } }

/* answer capsule (ai-ready.py) — a self-contained 40-60 word direct
   answer. Retrieval pipelines quote short blocks, not sections, so
   this is deliberately set apart and kept quotable. */
.answer {
  margin: var(--s-4) 0 0;
  padding: var(--s-3) 0 var(--s-3) var(--s-3);
  border-left: 2px solid var(--accent);
  font-size: var(--t-md);
  line-height: 1.6;
  color: var(--ink);
  max-width: 72ch;
}
.answer + * { margin-top: var(--s-4); }

/* =========================================================
   13. Motion

   Rewritten as one section. Three earlier passes had layered
   transitions on top of each other, leaving nine selectors with
   two competing `transition` declarations where the later one
   silently dropped properties the earlier one animated. That is
   why the feel was uneven.

   TOKENS: one curve and three durations for the whole site.
   Consistent easing is most of what reads as "polished" — mixed
   curves feel unfinished even when each is fine alone.

   COMPOSITOR: transform and opacity only, with one deliberate
   exception noted below. Nothing animates box-shadow,
   background-color during scroll, or any layout property.
   ========================================================= */

:root {
  --ease:      cubic-bezier(0.16, 0.8, 0.3, 1);   /* decelerating */
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);      /* symmetrical  */
  --dur-fast:  0.16s;
  --dur:       0.24s;
  --dur-slow:  0.44s;
}

/* Stops the page jumping sideways when opening an FAQ changes the
   height enough to add or remove the scrollbar. */
html { scrollbar-gutter: stable; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes draw   { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fade   { from { opacity: 0; } to { opacity: 1; } }
@keyframes lift   { from { opacity: 0; transform: translateY(12px) scale(0.995); }
                    to   { opacity: 1; transform: none; } }
@keyframes copied { 0% { transform: scale(1); } 40% { transform: scale(1.05); }
                    100% { transform: scale(1); } }

/* ---------- 13.1 hero entrance ----------
   Stagger tightened to 60ms steps. The previous 80ms steps put the
   demo card 840ms behind first paint, which reads as sluggish
   rather than considered. */
.band--tight h1, .hero h1        { animation: rise var(--dur-slow) var(--ease) both; }
.lede.prose                      { animation: rise var(--dur-slow) 0.06s var(--ease) both; }
.answer                          { animation: rise var(--dur-slow) 0.12s var(--ease) both; }
.demo                            { animation: lift var(--dur-slow) 0.18s var(--ease) both; }

.band--tight > .wrap > h1::after,
.hero h1::after {
  content: "";
  display: block;
  width: 3.5rem; height: 2px;
  margin-top: var(--s-3);
  background: var(--accent);
  transform-origin: left center;
  animation: draw var(--dur-slow) 0.2s var(--ease) both;
}

/* ---------- 13.2 shadows via opacity, not box-shadow ----------
   Animating box-shadow repaints the element every frame. A
   pseudo-element carrying a static shadow, faded with opacity,
   is composited and visually identical.
   .site-head is already position: sticky in section 5, which is
   what these absolutely-positioned overlays anchor to. */
.site-head::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 100%;
  height: 12px;
  pointer-events: none;
  box-shadow: 0 1px 12px -4px rgba(25, 28, 33, 0.3);
  opacity: 0;
}

.demo { position: relative; }
.demo::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  box-shadow: 0 12px 32px -22px rgba(25, 28, 33, 0.45);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-soft);
  pointer-events: none;
}
.demo:hover::after, .demo:focus-within::after { opacity: 1; }
.demo:focus-within { border-color: var(--accent); }

/* ---------- 13.3 one transition declaration per element ----------
   Consolidated. Every interactive element animates the same
   properties on the same curve. */
.btn, .copy, .chips a, .allpages > summary, .field,
.nav a, .allpages__panel a, .site-foot a, .crumbs a,
.prose a, .tile h3 a, .related a, .foot-btn {
  transition: color var(--dur-fast) var(--ease-soft),
              background-color var(--dur-fast) var(--ease-soft),
              border-color var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease);
}

.btn:active, .copy:active         { transform: translateY(1px); }
.chips a:hover                    { transform: translateY(-1px); }
.copy[data-done="true"]           { animation: copied 0.3s var(--ease-soft); }

/* The one paint exception: a row highlight on hover. Hover is a
   discrete event, not a per-frame one, and the repaint is confined
   to a single row by `contain`. */
.wish {
  transition: background-color var(--dur-fast) var(--ease-soft);
  contain: paint;
}
@media (hover: hover) { .wish:hover { background: var(--wash); } }

/* ---------- 13.4 navigation underline ---------- */
.nav a { position: relative; }
.nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--dur) var(--ease);
}
.nav a:hover::after,
.nav a[aria-current="page"]::after { transform: scaleX(1); transform-origin: left center; }

/* ---------- 13.5 disclosure widgets ---------- */
.faq summary::after { transition: transform var(--dur) var(--ease); }
.faq details[open] summary::after { transform: translateY(-50%) rotate(180deg); }
.faq details[open] > p            { animation: rise var(--dur) var(--ease) both; }
.allpages[open] .allpages__panel  { animation: rise 0.18s var(--ease) both; }

/* ---------- 13.6 consent banner ---------- */
@keyframes cc-in      { from { opacity: 0; transform: translateY(100%); } to { opacity: 1; transform: none; } }
@keyframes cc-in-card { from { opacity: 0; transform: translateY(14px) scale(0.98); } to { opacity: 1; transform: none; } }
.cc { animation: cc-in 0.32s 0.35s var(--ease) both; }
@media (min-width: 48rem) { .cc { animation-name: cc-in-card; } }

/* ---------- 13.7 desktop two-column hero ----------
   :has() keeps this CSS-only. Applies to any page carrying a
   .demo; every other page keeps one column, no markup change. */
@media (min-width: 64rem) {
  .band--tight > .wrap:has(.demo) {
    display: grid;
    grid-template-columns: 1.05fr 0.95fr;
    grid-template-areas: "head demo" "lede demo" "answer demo";
    column-gap: var(--s-6);
    align-items: start;
  }
  .wrap:has(.demo) > h1      { grid-area: head; margin-bottom: var(--s-3); }
  .wrap:has(.demo) > .lede   { grid-area: lede; }
  .wrap:has(.demo) > .answer { grid-area: answer; }
  .wrap:has(.demo) > .demo   { grid-area: demo; align-self: center; }
}

/* ---------- 13.8 lede hierarchy and answer capsule ---------- */
.lede.prose > p:first-child      { font-size: var(--t-md); line-height: 1.55; color: var(--body); max-width: 54ch; }
.lede.prose > p:first-child + p  { font-size: var(--t-base); color: var(--muted); max-width: 58ch; }
.lede.prose > p:last-child       { margin-bottom: 0; }

.answer {
  position: relative;
  background: var(--wash);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--s-4) var(--s-4) var(--s-3);
  font-size: var(--t-base);
  color: var(--body);
}
.answer::before {
  content: "In short";
  display: block;
  font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s-2);
}

/* ---------- 13.9 cross-document page transitions ---------- */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) {
  animation-duration: 0.2s;
  animation-timing-function: var(--ease);
}
.site-head { view-transition-name: site-header; }

/* ---------- 13.10 scroll-driven reveals ----------
   Runs off the main thread, so scrolling stays smooth while rows
   animate. Wrapped in @supports: a browser without scroll-driven
   animations never applies the opacity: 0 start state, so it shows
   the content instead of hiding it forever.

   Deliberately NOT applied to .band.defer or .site-foot — both
   carry content-visibility: auto, so the browser skips rendering
   them until they are near the viewport and a view() timeline on
   the same element has nothing to measure. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .wish, .tile, .related li {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 8% entry 46%;
    }
    /* header shadow driven by scroll position, no listener */
    .site-head::after {
      animation: fade linear both;
      animation-timeline: scroll();
      animation-range: 0 100px;
    }
  }
}

/* ---------- 13.11 motion off ----------
   Removed, not shortened. Vestibular triggers are not a matter of
   degree. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  .nav a[aria-current="page"]::after { transform: scaleX(1); }
  .site-head::after { opacity: 0; }
}

/* =========================================================
   14. Colour system

   One hue per section of the site, set by a class on <body>.
   Every accent on a page derives from --accent, so the colour is
   wayfinding rather than decoration: the tools section is indigo,
   milestones are amber, and you can tell where you are without
   reading the breadcrumb.

   All six hues were checked against white before use: the lowest
   is 5.35:1 and the rest exceed 6:1, so every one passes WCAG AA
   for normal text, not just large text. Body copy on the tinted
   washes stays above 7:1.
   ========================================================= */

.t-home, .t-wishes  { --accent: #B03A5F; --accent-dark: #8C2C4B; --accent-wash: #FCEFF3; --accent-2: #D4667F; }
.t-milestone        { --accent: #96600C; --accent-dark: #774C09; --accent-wash: #FBF3E6; --accent-2: #C08A2E; }
.t-tools            { --accent: #6A3D9A; --accent-dark: #54307A; --accent-wash: #F4EEFB; --accent-2: #9463C4; }
.t-situation        { --accent: #8A2F52; --accent-dark: #6E2541; --accent-wash: #FAEDF1; --accent-2: #B85578; }
.t-language         { --accent: #1F6B4A; --accent-dark: #17523A; --accent-wash: #EBF4EF; --accent-2: #3E9670; }
.t-policy           { --accent: #5A4F56; --accent-dark: #403840; --accent-wash: #F5F1F2; --accent-2: #857680; }


/* ---------- 14.1 gradient accent bar under the h1 ---------- */
.band--tight > .wrap > h1::after,
.hero h1::after {
  width: 4.5rem;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
}

/* ---------- 14.2 numbered wishes get a coloured badge ----------
   The counter was a grey numeral. As a filled badge it carries the
   section hue and gives the list a rhythm down the left edge. */
.wish::before {
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-top: 0.15rem;
  border-radius: 50%;
  background: var(--accent-wash);
  color: var(--accent-dark);
  font: 600 0.75rem/1 var(--sans);
  transition: background-color var(--dur-fast) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft);
}
@media (hover: hover) {
  .wish:hover::before { background: var(--accent); color: #fff; }
}

/* ---------- 14.3 hero wash ----------
   A very low-opacity radial tint behind the hero, so the top of the
   page is not flat white. Kept under 4% so text contrast is
   unaffected. */
.band--tight:first-of-type {
  background:
    radial-gradient(120% 90% at 12% 0%,
      color-mix(in srgb, var(--accent) 6%, transparent) 0%,
      transparent 60%);
}

/* ---------- 14.4 tiles ---------- */
.tile {
  border-top: 2px solid var(--accent);
  padding: var(--s-3) var(--s-3) var(--s-4);
  border-radius: 0 0 var(--radius) var(--radius);
  background: linear-gradient(180deg, var(--accent-wash) 0%, transparent 70%);
  transition: transform var(--dur) var(--ease);
}
@media (hover: hover) {
  .tile:hover { transform: translateY(-3px); }
}

/* ---------- 14.5 chips ---------- */
.chips a {
  border-color: color-mix(in srgb, var(--accent) 25%, var(--line));
  color: var(--accent-dark);
  background: var(--page);
}
.chips a:hover {
  background: var(--accent-wash);
  border-color: var(--accent);
}

/* ---------- 14.6 answer capsule ---------- */
.answer {
  border-left: 0;
  background: linear-gradient(135deg, var(--accent-wash) 0%, var(--page) 90%);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--line));
  border-radius: var(--radius);
  padding-left: var(--s-4);
}
.answer::after {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  border-radius: var(--radius) 0 0 var(--radius);
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
}

/* ---------- 14.7 copy button success ----------
   Green regardless of section hue: success is a status, not a
   category, so it should not change meaning between pages. */
.copy[data-done="true"] {
  background: #EAF6EF;
  border-color: #1E7A44;
  color: #146034;
}

/* ---------- 14.8 header, footer and section edges ---------- */
.site-head { border-bottom-color: color-mix(in srgb, var(--accent) 14%, var(--line)); }
.site-foot { border-top: 2px solid transparent;
             border-image: linear-gradient(90deg, var(--accent), var(--accent-2)) 1; }
.band--tint { background: linear-gradient(180deg, var(--accent-wash) 0%, var(--page) 100%); }

/* ---------- 14.9 selection and misc ---------- */
::selection { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--ink); }
.note { border-left-color: var(--accent); }
.related a:hover { border-bottom-color: var(--accent); }
.brand::before {
  content: "";
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 50%;
  margin-right: 0.5rem;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
}
.brand { display: inline-flex; align-items: center; }

/* ---------- 14.10 fallback for color-mix ----------
   Everything above degrades to a solid accent rather than to
   nothing, in engines without color-mix(). */
@supports not (color: color-mix(in srgb, red 10%, blue)) {
  .chips a { border-color: var(--line); }
  .answer  { border-color: var(--line); }
  ::selection { background: var(--accent-wash); }
  .band--tight:first-of-type { background: var(--page); }
}

/* =========================================================
   15. Romantic finish

   Softer geometry, warmer surfaces and a little ornament —
   applied through the same theme token, so every page in a
   section stays consistent with its siblings.
   ========================================================= */

/* ---------- 15.1 hero: two-layer warm gradient ---------- */
.band--tight:first-of-type {
  background:
    radial-gradient(90% 70% at 88% -10%,
      color-mix(in srgb, var(--accent-2) 14%, transparent) 0%, transparent 55%),
    radial-gradient(110% 80% at 6% 0%,
      color-mix(in srgb, var(--accent) 9%, transparent) 0%, transparent 60%);
}

/* ---------- 15.2 wishes as soft cards ----------
   Rows separated by hairlines read as a table. Rounded, tinted
   cards read as something you would send to a person. */
.wishlist { border-top: 0; gap: var(--s-2); }
.wish {
  border-bottom: 0;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--page);
  padding: var(--s-3);
  align-items: center;
}
@media (hover: hover) {
  .wish:hover {
    background: linear-gradient(135deg, var(--accent-wash) 0%, var(--page) 75%);
    border-color: color-mix(in srgb, var(--accent) 22%, var(--line));
  }
}
.wish__text { line-height: 1.6; }

/* ---------- 15.3 buttons ---------- */
.btn {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-dark) 100%);
  border-color: var(--accent-dark);
  border-radius: 999px;
  color: #fff;
}
.btn:hover { background: linear-gradient(135deg, var(--accent-2) 0%, var(--accent) 100%); }
.btn--ghost {
  background: var(--page);
  color: var(--accent-dark);
  border-color: color-mix(in srgb, var(--accent) 30%, var(--line));
}
.btn--ghost:hover { background: var(--accent-wash); color: var(--accent-dark); }
.copy, .chips a, .allpages > summary, .field { border-radius: 999px; }
.field { border-radius: var(--radius); }

/* ---------- 15.4 section ornament ----------
   A small gradient rule between bands instead of a hard hairline. */
.band + .band { border-top: 0; position: relative; }
.band + .band::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: min(90%, var(--rail));
  height: 1px;
  background: linear-gradient(90deg,
    transparent,
    color-mix(in srgb, var(--accent) 30%, var(--line)) 30%,
    color-mix(in srgb, var(--accent) 30%, var(--line)) 70%,
    transparent);
}

/* ---------- 15.5 answer capsule and notes ---------- */
.answer { box-shadow: 0 2px 14px -10px color-mix(in srgb, var(--accent) 55%, transparent); }
.note {
  background: var(--accent-wash);
  border-radius: var(--radius);
  border-left: 3px solid var(--accent);
  padding: var(--s-3);
}

/* ---------- 15.6 tiles and related ---------- */
.tile { border-radius: var(--radius); border-top-width: 3px; }
.related li { border-top-color: var(--line-soft); }
.related a { border-bottom-color: color-mix(in srgb, var(--accent) 30%, var(--line)); }

/* ---------- 15.7 footer ---------- */
.site-foot { background: linear-gradient(180deg, var(--accent-wash) 0%, var(--page) 55%); }

/* ---------- 15.8 consent + panel surfaces ---------- */
.cc, .allpages__panel { border-radius: var(--radius); }

/* ---------- 15.9 fallback ---------- */
@supports not (color: color-mix(in srgb, red 10%, blue)) {
  .band--tight:first-of-type { background: var(--accent-wash); }
  .band + .band::before { background: var(--line); }
  .wish:hover { background: var(--accent-wash); }
  .answer { box-shadow: none; }
}

/* ---------- 15.10 tools ---------- */
.tool {
  border: 1px solid color-mix(in srgb, var(--accent) 20%, var(--line));
  border-radius: var(--radius);
  background: linear-gradient(160deg, var(--accent-wash) 0%, var(--page) 60%);
  padding: var(--s-4);
}
.tool__form { display: grid; gap: var(--s-3); margin-bottom: var(--s-3); }
@media (min-width: 48rem) { .tool__form { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tool__field { display: grid; gap: var(--s-1); margin: 0; }
.tool__field label { font-size: var(--t-sm); font-weight: 500; color: var(--ink); }
.tool__field .field { width: 100%; }
select.field { min-height: var(--tap); font: 400 var(--t-input) var(--sans); }
.tool__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-3) 0 0; }
.tool__out { margin-top: var(--s-3); }
.tool__out:empty { display: none; }
.tool__big {
  font-family: var(--serif);
  font-size: var(--t-xl);
  color: var(--accent-dark);
  margin: 0 0 var(--s-3);
  line-height: 1.25;
}
.tool__quote { font-style: italic; }
.tool__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.tool__list li {
  display: flex; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--t-sm);
}
.tool__list li span { color: var(--muted); }
.tool__list li strong { color: var(--ink); font-variant-numeric: tabular-nums; }
.tool__note { font-size: var(--t-xs); color: var(--muted); margin: var(--s-3) 0 0; }
.tool__canvas {
  width: 100%; height: auto;
  margin-top: var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--page);
}
