/* Wishing Well landing styles — ARCHITECTURE §4.
 *
 * Hard constraints (D11): body >= 18px · CTA >= 44x44px · WCAG 2.1 AA contrast ·
 * usable at 200% zoom · one image on the page · < 350 KB total transfer (D11 as
 * amended 14 Oct 2026; was 300 KB).
 *
 * D11's *system-font* clause is deliberately superseded (operator, 13 Sep 2026): the
 * pack's own face is Fraunces, self-hosted below rather than the earlier system-stack
 * stand-in. Two static weights only (400/600, Latin subset, ~18 KB WOFF2 each) —
 * ~36 KB against a budget that was running at 9% with none. `ops/check_weight.sh` was
 * updated the same day to weigh a font's bytes into the total without counting it
 * against the "exactly one image" gate, which was never about fonts. The script
 * signature and line-art marks stay inline SVG path data (worker/src/icons.ts) — not
 * a font, since they're fixed art, not text a registry row can vary. */

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/fraunces-400.woff2') format('woff2');
}

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/fraunces-600.woff2') format('woff2');
}

/* tokens:begin */
/* Generated from design/tokens.py — edit THERE, then run design/sync_tokens.py. */
:root {
  --bg: #f9f5f2;           /* warm cream (operator-approved design pack, 13 Sep 2026) */
  --ink: #211f1b;          /* body text        — 15.2:1 on --bg */
  --muted: #55524c;        /* secondary text   —  7.2:1 on --bg */
  --accent: #c4e5fb;       /* CTA ground       — --ink on it is 12.5:1 */
  --accent-dark: #1f4f73;  /* active / focus / secondary-CTA label — 8.0:1 on --bg */
  --rule: #ece3d5;         /* hairlines, photo placeholder ground */
  --cta-ink: #211f1b;      /* CTA label — dark ink on the pale-blue ground, not white (D11 pack) */
  --note-bg: #ecae51;      /* cancel-note ground (amber, operator-approved pack) */
  --icon-tag: #132c33;     /* the tag+plane mark's own fill in the source pack — a deliberate dark teal distinct from --ink, restored 14 Sep 2026 after a visual-drift pass mistook it for a stray hex */
  --confetti-1: var(--accent);
  --confetti-2: #d8a25c;
  --confetti-3: #c76f6f;
  --confetti-4: #7fa08f;
  --confetti-5: #e0c9a6;
  --confetti-6: var(--accent-dark);
  color-scheme: light; /* the palette is print-warm; don't let UAs invert it */
}
/* tokens:end */

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 1.5rem 1.25rem 3rem;
  padding-left: max(1.25rem, env(safe-area-inset-left));
  padding-right: max(1.25rem, env(safe-area-inset-right));
  background: var(--bg);
  color: var(--ink);
  /* Design pack, 13 Sep 2026: the pack's own face, self-hosted above. System-serif
     fallback covers the render between font-display: swap and the file landing.
     Line-height tightened 14 Sep 2026 (visual-drift pass): the pack's own text sets
     noticeably denser than 1.6. */
  font: 1.125rem/1.5 'Fraunces', ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;  /* 18px floor */
  -webkit-text-size-adjust: 100%;
}

main {
  max-width: 30rem;
  margin: 0 auto;
}

/* 1. the one-line lede ---------------------------------------------------- */
/* Note: not `.lead` (message-page sub-line, further down) — near-homonym, different rules. */
.lede {
  margin: 0 0 1rem;
  font-size: 1.125rem;
  letter-spacing: 0.01em;
  color: var(--ink); /* the pack runs this line at full ink, not a softer tone (visual-drift pass, 14 Sep 2026) */
}

/* 2. the item photo — the only image on the page (D11) -------------------- */
.photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 0.5rem; /* the pack's own rounding reads much subtler than a full 1rem */
  background: var(--rule);
  margin-top: 0.75rem; /* breathing room from .reassure above (visual-drift pass) */
}

/* A 3:4 photo keeps its own shape at the same width: pages.ts adds this class and
   width/height from the photo manifest, so the box is reserved before the photo
   loads (no layout shift, D11). Operator, 14 Oct 2026. */
.photo--portrait {
  aspect-ratio: 3 / 4;
}

/* The home page photo is a button: a click or tap shows the next photo in the pool
   (pages.ts homePhoto; operator, 15 Oct 2026). Only the photo shows - no button chrome.
   /p/151 carries the same photo above its steps (operator, later on 15 Oct 2026). */
.photo-cycle {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 0.5rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.photo-cycle:focus-visible {
  outline: 2px solid var(--accent-dark);
  outline-offset: 3px;
}

/* 3. item name + one-line description ------------------------------------ */
.item {
  margin: 1.25rem 0 0.35rem;
  font-size: 1.75rem;
  line-height: 1.25;
  font-weight: 600;
}

.desc {
  margin: 0;
  color: var(--ink); /* pack shows this at full ink, not muted (visual-drift pass) */
}

/* 4. the price, large ----------------------------------------------------- */
.price {
  margin: 1.25rem 0 1.5rem;
  display: flex;
  align-items: baseline;
  gap: 0.2rem; /* the pack sits the currency code almost flush against the number */
}

.amount {
  font-size: 2.25rem; /* was 3rem — the pack sizes this close to the item name, not 1.7x it */
  line-height: 1;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.cur {
  font-size: 1.2rem;
  font-weight: 600;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.06em;
  color: var(--ink); /* visual-drift pass: pack shows this at full ink, sized ~0.5x the amount */
}

/* 5. one primary CTA ------------------------------------------------------ */
.cta-form { margin: 0; }

.cta {
  display: block;
  width: 100%;
  min-height: 3.5rem;      /* comfortably past the 44px tap-target floor */
  padding: 0.5rem 1.25rem;
  border: 0;
  /* the pack's button is a gently rounded rectangle, not a full stadium pill
     (visual-drift pass, 14 Sep 2026) */
  border-radius: 0.75rem;
  background: var(--accent);
  color: var(--cta-ink);
  font: inherit;
  font-size: 1.625rem;     /* was 1.25rem — the pack's label reads nearly as large as the item name */
  line-height: 1.15;       /* keeps the taller label from ballooning the pill's height */
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

/* color must flip too, not just background — cta-ink on accent-dark is ~1.9:1,
 * far below AA; --bg on accent-dark is 8.0:1 (audit, 13 Sep 2026; sync_tokens.py's
 * check_contrast now gates this exact pair). */
.cta:active { background: var(--accent-dark); color: var(--bg); }

.cta:focus-visible {
  outline: 3px solid var(--accent-dark);
  outline-offset: 3px;
}

.cta[disabled] { opacity: 0.65; }

/* below the fold ---------------------------------------------------------- */
/* The cancel-return note (state `cancelled return`, §5): a title + one supporting
 * line inside an amber pill, matching the pack's "No Problemo" block. Sits between
 * the price and the CTA (design pack, 13 Sep 2026). */
.note-cancel {
  margin: 0 0 1rem;
  padding: 1rem 1.25rem;
  border-radius: 1.25rem;
  background: var(--note-bg);
  color: var(--ink);
  text-align: center;
}

.note-cancel-title {
  margin: 0 0 0.25rem;
  font-size: 1.125rem;
  font-weight: 600;
}

.note-cancel p {
  margin: 0;
  font-size: 1rem;
}

.reassure {
  margin: 1rem 0 0;
  font-size: 0.9375rem;
  color: var(--ink); /* visual-drift pass: pack shows this at full ink, not muted */
  text-align: center;
}

.piece-id {
  margin: 2rem 0 0;
  font-size: 0.875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}

/* message states: claimed, closed, disabled, unknown slug (§5) ------------- */
/* No extra top padding (operator, 2 Oct 2026): the 12vh drop that sat here pushed
 * every message page's first line ~125 px down a phone. They now start at the
 * body's own 1.5rem, the same as the gift pages. */

.message { text-align: center; }

/* A line-art mark (worker/src/icons.ts) sat above a message-page title — the pack
 * places these beside/behind the title, but a fixed-width mobile column reads more
 * reliably with the mark stacked above centred text than wrapped beside it at
 * arbitrary title lengths. Purely decorative: aria-hidden in the markup. */
.icon-lede {
  display: block;
  width: 10.4rem; /* 30% larger than 8rem (operator, 30 Sep 2026); was 5.5rem before that — pack sizes this mark much larger relative to the title */
  height: auto;
  margin: 0 auto 1rem;
  color: var(--ink);
}

/* The tag+plane mark carries its own dark-teal fill in the source pack, distinct
 * from the page's ink — restored 14 Sep 2026 after an earlier pass mistook the
 * source SVG's own color for a stray hex and normalized it away. Applied via a
 * modifier rather than changing .icon-lede's default, since the suitcases mark
 * genuinely is ink-colored in the pack. */
.icon-lede--tag { color: var(--icon-tag); }

.big {
  margin: 0 0 0.75rem;
  font-size: 2.5rem; /* was 1.85rem — every message-state screen in the pack runs this notably larger */
  line-height: 1.05;  /* was 1.2 — the pack's headline sets tight, no extra leading */
  font-weight: 400;
  letter-spacing: 0.04em; /* was 0.01em — the pack tracks its all-caps headlines out more */
  text-transform: uppercase;
  text-wrap: balance;
}

/* Note: not `.lede` (opening gift-line, above) — near-homonym, different rules. */
.lead {
  margin: 0 0 1.5rem; /* was 0.75rem — the pack sets a full paragraph break before the next line */
  font-size: 1.125rem;
}

/* A second headline block, set apart from .big by a full line's gap — the pack uses
 * this for a two-part title ("MAMMA MIA!" / "SOMETHING WENT WRONG!") rather than one
 * run-on <h1> (visual-drift pass, 14 Sep 2026). Same treatment as .big, just not the
 * page's single <h1>. */
.eyebrow {
  margin: 0 0 1.75rem;
  font-size: 2.5rem;
  line-height: 1.05;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-wrap: balance;
}

/* A size step between .big and body copy — the pack gives one line ("You Legend!")
 * this extra weight, everything below it stays at .lead's own size (visual-drift
 * pass, 14 Sep 2026). */
.shout {
  margin: 0 0 0.75rem;
  font-size: 1.375rem;
}

.quiet {
  margin: 0 0 1.5rem;
  color: var(--ink); /* was --muted — every message-state screen in the pack runs body copy at full ink */
}

/* The one line on a message page that has no counterpart in the pack — a raw
 * Stripe-error diagnostic — stays genuinely de-emphasized rather than inheriting
 * .quiet's now-full-ink color (renderCheckoutError). */
.diagnostic { color: var(--muted); }

/* A second decorative mark near the foot of a message, echoing the top-of-page
 * icon-lede (renderCheckoutError's pack shows both). */
.icon-foot {
  display: block;
  width: 4.5rem;
  height: auto;
  margin: 0 auto 1rem;
  color: var(--ink);
}

/* Instructional page (/p/151, the signage code). Left-aligned inside a centred
 * .message block: numbered steps are read in order, and centred list text is harder
 * to scan than centred prose. Body stays >= 18px per ARCHITECTURE §4. */
.steps {
  margin: 0 0 1.5rem;
  /* was padding: 0 0 0 1.35rem — the pack has no list marker at all and sits the
     step text flush with "How it works:" (visual-drift pass, 14 Sep 2026) */
  padding: 0;
  list-style: none;
  text-align: left;
  font-size: 1.125rem;
  line-height: 1.5;
}

.steps li { margin: 0 0 1rem; }
.steps li:last-child { margin-bottom: 0; }

/* Step label vs. its explanation — the pack sets these as two distinct sizes, not
 * one run-on sentence (visual-drift pass, 14 Sep 2026).
 * Each step title stays on one line on a phone (operator, 2 Oct 2026): the size
 * follows the column (100vw less the body's 2.5rem padding) so that Step 2, the
 * longest title (~15.0-15.3 times its font size wide in Fraunces 400; the optical-size
 * axis widens small sizes), fits it: (100vw - 2.5rem) / 15.8 = 6.33vw - 0.16rem,
 * leaving 3-7% slack at 320-430px for Safari's metrics. The .steps-icon sits behind
 * the text, so Step 1 no longer needs room beside it. Full 1.35rem from ~380px up. */
.step-title {
  display: block;
  font-size: clamp(0.9rem, calc(6.33vw - 0.16rem), 1.35rem);
  font-weight: 400;
}

.step-desc {
  display: block;
  margin-top: 0.15rem;
  margin-left: 2rem;
  font-size: 0.9rem;
}

/* The couple's sign-off closes every page: one lockup, heart + "with love and gratitude,
 * Jordan & Eliza" (operator, 3 Oct 2026), reproduced from the operator's own vector art
 * (icons.ts), not a webfont — see the file banner. Until 3 Oct 2026 it was a separate
 * heart and signature in a wrapping flex row. */
.signoff {
  display: flex;
  justify-content: center;
  margin: 1.25rem 0 0; /* was 2rem — collapses tighter against .quiet's own larger margin now */
}

.signoff .lockup {
  flex: none;
  width: 21rem;    /* the old heart (4rem) + gap (1rem) + signature (16rem) footprint */
  max-width: 100%; /* one drawing, so it scales down at 200% zoom rather than wrapping */
  height: auto;
  color: var(--ink);
}

.message .signoff { justify-content: center; }

/* The self-serve nomination link (state `how-to`, slug 151) — an outbound Stripe
 * Payment Link the operator sets in `config.donation_link_url`. Absent until then,
 * so the section renders nothing rather than a dead link (index.ts). */
.donate {
  margin: 0 0 1.5rem;
  /* No border-top and, since 2 Oct 2026, no top padding either (operator: "reduce the
   * gap by 1 paragraph spacing"): what sits above it on every page that carries it
   * (the /p/151 steps, the already-gifted .lead, the home page's opening .lead)
   * already ends in a 1.5rem margin, and its own 1.5rem padding doubled that. */
  text-align: center;
}

.donate .lead { margin-bottom: 0.75rem; }
.donate .quiet { margin-top: 0.75rem; }

/* :not(.cta) — otherwise this beats .cta's own color rule by specificity (0,1,1 vs
 * 0,1,0) once a .cta anchor sits inside .message, quietly repainting the onward link
 * with the wrong ink token (audit, 13 Sep 2026; still >= AA either way, but wrong). */
.message a:not(.cta) { color: var(--accent-dark); font-weight: 600; }

/* The nomination invite is a solid button on both surfaces it appears on (operator,
 * 14 Sep 2026 — resolves a genuine conflict in the approved pack, which showed a
 * button here on the claimed screens but inline text on the how-to screen; the
 * operator's own prior "make the two uniform" instruction settles it as a button).
 * Narrower than the primary .cta and centred, since it is always a secondary,
 * optional ask, never the page's one primary action. */
.cta-donate {
  /* inline-flex, not inline-block (operator, 4 Oct 2026): this is an <a>, and unlike
   * the gift page's <button> an anchor does not centre its label vertically, so the
   * spare height from .cta's 3.5rem min-height all fell below the text ("Click here"
   * sat ~6 px high). Centred on both axes the way a button centres its content. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 70%;
  max-width: 20rem;
  margin: 0 auto;
}

/* The how-to page's numbered steps read left-to-right better left-aligned than
 * centred (existing .steps rule) — its little intro heading and mark follow suit,
 * rather than the centred .message default. position + isolation make the block the
 * mark's positioning box and its own stacking context, so z-index -1 puts the mark
 * behind the block's text but never behind the page. */
.steps-block { text-align: left; position: relative; isolation: isolate; }

/* On /p/151 the photo sits above the steps (operator, 15 Oct 2026). The photo has no
 * bottom margin, so this gives the steps the same 1.5rem break a .lead would, and the
 * mark's -1rem top (below) rises into that gap rather than over the photo. `~`, not `+`:
 * the photo's inline <script> sits between the button and the steps, so `+` never
 * matched (PR #25; corrected the same day). */
.photo-cycle ~ .steps-block { margin-top: 1.5rem; }

/* The mark sits top-right from "How it works:" down, BEHIND the Step 1 text (operator,
 * 2 Oct 2026, superseding the same day's float beside Step 1). Absolutely placed, so it
 * takes no room and the text runs over it; the -1rem top rises into the .lead's 1.5rem
 * bottom margin. Decorative only (aria-hidden), so it never takes a tap.
 * Later the same day (operator): mirrored left-right, so the string loop is top-left
 * and the tag points down-right as in his mockup; halved to 23% of the column, up to
 * 7rem (from 46%, up to 14rem); full strength (the 0.5 opacity is gone). */
.steps-icon {
  position: absolute;
  top: -1rem;
  right: 0;
  z-index: -1;
  width: min(7rem, calc((100vw - 2.5rem) * 0.23));
  height: auto;
  pointer-events: none;
  transform: scaleX(-1);
  color: var(--icon-tag); /* the tag+plane mark's own dark-teal fill, not ink — see .icon-lede--tag */
}

.steps-heading {
  margin: 0 0 0.75rem;
  font-size: 1.375rem;
  font-weight: 600;
}

/* D21 confetti — success page only.
 *
 * Twelve elements, one keyframe, no library and no image: the delight had to fit inside
 * the D11 budget, and a confetti package would have cost more than every other asset on
 * the site combined. Purely decorative, so aria-hidden in the markup and gone entirely
 * for anyone who asked for less motion. */
.confetti {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.confetti i {
  position: absolute;
  top: -12vh;
  width: 0.55rem;
  height: 0.85rem;
  border-radius: 1px;
  opacity: 0.85;
  animation: ww-fall 3.6s linear forwards;
}

@keyframes ww-fall {
  to { transform: translateY(118vh) rotate(600deg); }
}

/* Spread across the width, staggered, in the page's own palette (design/tokens.py). */
.confetti i:nth-child(6n + 1) { background: var(--confetti-1); }
.confetti i:nth-child(6n + 2) { background: var(--confetti-2); }
.confetti i:nth-child(6n + 3) { background: var(--confetti-3); }
.confetti i:nth-child(6n + 4) { background: var(--confetti-4); }
.confetti i:nth-child(6n + 5) { background: var(--confetti-5); }
.confetti i:nth-child(6n)     { background: var(--confetti-6); }

.confetti i:nth-child(1)  { left:  4%; animation-delay: 0s;    animation-duration: 3.2s; }
.confetti i:nth-child(2)  { left: 12%; animation-delay: 0.35s; animation-duration: 4.1s; }
.confetti i:nth-child(3)  { left: 21%; animation-delay: 0.1s;  animation-duration: 3.7s; }
.confetti i:nth-child(4)  { left: 30%; animation-delay: 0.6s;  animation-duration: 3.1s; }
.confetti i:nth-child(5)  { left: 39%; animation-delay: 0.2s;  animation-duration: 4.4s; }
.confetti i:nth-child(6)  { left: 48%; animation-delay: 0.8s;  animation-duration: 3.5s; }
.confetti i:nth-child(7)  { left: 57%; animation-delay: 0.15s; animation-duration: 4s;   }
.confetti i:nth-child(8)  { left: 66%; animation-delay: 0.5s;  animation-duration: 3.3s; }
.confetti i:nth-child(9)  { left: 75%; animation-delay: 0.25s; animation-duration: 4.2s; }
.confetti i:nth-child(10) { left: 84%; animation-delay: 0.7s;  animation-duration: 3.6s; }
.confetti i:nth-child(11) { left: 91%; animation-delay: 0.05s; animation-duration: 3.9s; }
.confetti i:nth-child(12) { left: 96%; animation-delay: 0.45s; animation-duration: 3.4s; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  .confetti { display: none; }
}
