/* =========================================================
   LANDING PAGE (issue #184)
   =========================================================
   The pre-auth marketing view. Its job is not to explain Lancong — it is to
   make you want to go somewhere, then hand you the button.

   The design brief: departure fever. The page is built out of the physical
   debris of actually going — rubber passport stamps, taped-down instant
   prints, boarding-pass stubs, a dashed route line — printed the way a cheap
   travel zine is printed, in flat riso inks that sit slightly out of
   register. Nothing here is centred and reverent; things are taped down at an
   angle because that is how a suitcase looks after a trip.

   THE SIGNATURE is .lp-stampword: the hero headline reads "Let's go to —"
   and the destination lands under it as a rubber stamp, rotated and
   over-printed, cycling through places. It is the product's actual question
   ("where?") made kinetic, and it is the ONE loud thing. Everything around it
   is deliberately disciplined — quiet body copy, one type scale, no second
   marquee competing for the same job.

   The page's own component rules stay scoped under #landing-page so they
   can't leak into the auth gate (#auth-panel) that shares #auth-screen with
   it — the two are converted, not merged, per DESIGN.md's "convert a screen
   whole" migration note. What IS shared, deliberately, is the --lp-* token
   block on bare .landing-screen below: both surfaces read the identical
   ground/stock/ink/type values, which is what lets a visitor cross from one
   to the other (landingStart('register') et al.) without the colours
   disagreeing. Every ink a CONTROL is painted in is in turn an alias of
   css/style.css's own --vn-* and --surface-* tokens, not a colour re-picked
   here — the poster owns ground/stock/type/geometry, the product owns the
   brand. See the Brand section of the token block below before changing any
   of it, and see the "Auth gate" section further down for #auth-panel's own
   component set.

   ⚠️ Never write a token pair as `--vn-*` immediately followed by a slash
   inside a comment. That spells a comment terminator, and it silently ends
   the comment early: the parser then discards everything up to and including
   the next {…} block. That exact typo shipped with the first version of this
   file and cost the page its `padding: 0` for months — every "full-bleed"
   band was quietly inset 16px, and the sticky nav floated 32px down from the
   top of the viewport. Nothing warns you; the rest of the file parses fine.

   Mobile-first; the wide layout starts at the app's own 1024px breakpoint.
*/

/* ---------- Tokens ---------- */
/* Declared on bare .landing-screen (#auth-screen's only class, see
   css/style.css) so both states it can be in — the marketing page
   (.lp-mode) and the auth gate beneath it — read the identical values. The
   auth gate used to be exempt from this block entirely, sitting on the
   retired Field Journal look (system font stack, 10px radii, a blurred
   focus glow) while the page one tap away from it was fully converted.
   DESIGN.md is explicit that this system is normative everywhere with no
   exceptions and names the auth gate as the outstanding surface — see the
   "Auth gate" component section below for that conversion. Only the ground
   painting and these tokens are shared; #auth-panel keeps its own component
   rules separate from #landing-page's, per the migration note's "convert a
   screen whole" rule — the two are converted, not merged. */
.landing-screen {
  /* Noon. A saturated apricot, not a neutral paper stock — the ground itself
     should read as sunlight, which is the whole argument against the cream
     this page used to sit on. */
  --lp-ground: #ffe3c4;
  --lp-panel: #fff7ec;      /* the stock a card or a stub is cut from */
  --lp-ink: #101a14;        /* green-black, the brand green run to its floor */
  --lp-ink-soft: #4a5851;   /* 5.9:1 on --lp-ground */

  /* Instant-film stock, and the only pair that does NOT invert with the
     theme. A photo print is white at midnight too — keeping it light is what
     makes the prints the brightest thing on the night version of the page,
     which is right for photographs of somewhere sunny. Anything set on the
     stock therefore has to stay dark in both themes. */
  --lp-stock: #fdf6ea;
  --lp-stock-ink: #5f6b63;

  /* ---------- Brand ---------- */
  /* Every ink below is the app's OWN --vn-* token, aliased rather than
     re-picked, so this page cannot drift off-brand and follows the palette if
     it ever moves. The page still owns its ground, stock, type and geometry —
     that is what keeps it a poster rather than app chrome — but the colour a
     control is painted in belongs to the product, not to the poster.
     ⚠️ --vn-teal is a DEEP GREEN in light mode and a BRIGHT MINT in dark
     (issue #432). Anything filled with --lp-brand must therefore take
     --lp-brand-fg as its foreground, never a literal white — that is the
     exact bug #432 fixed across 13 rules in the app. */
  --lp-brand: var(--vn-teal);
  --lp-brand-fg: var(--surface-2);

  /* The pinned brand green, mirroring --topbar-bg. The app's top bar is the
     literal #10702A in both themes because a full-bleed field of the
     dark-mode mint would be unreadable; the footer poster is this page's
     equivalent field, so it follows the same rule and pins its foreground
     with it. Also the one green safe to set on --lp-stock, which never
     darkens. */
  --lp-brand-field: #10702a;
  --lp-brand-field-fg: #ffffff;

  /* The two supporting brand inks. Coral is the stamp: a rubber stamp is a
     red-orange before it is anything else, and coral is the palette's own
     terracotta rather than a hue invented for this page. Amber is the route
     and the tape. Green is reserved for things you can press. */
  --lp-coral: var(--vn-coral);
  --lp-amber: var(--vn-amber);
  /* ⚠️ --lp-amber is 1.9:1 on --lp-ground: a fill and a mark, never text.
     Copy set ON an amber fill takes --lp-amber-fg, pinned dark because the
     token lightens in dark mode (4.8:1 light, 8.4:1 dark). */
  --lp-amber-fg: #1c1509;

  /* The out-of-register second pass — the green landing a hair off the coral,
     which is what a two-ink riso job actually does. */
  --lp-ghost: color-mix(in srgb, var(--lp-brand) 30%, transparent);
  --lp-hair: rgba(16, 26, 20, 0.16);
  --lp-tape: rgba(186, 117, 23, 0.42);

  /* An inverted band: one hard cut in the middle of the page so the scroll
     has a floor to it. Flips with the theme rather than being pinned dark.
     A deep step of the brand green rather than a neutral black — the page
     then has three green fields (nav mark, this band, the footer poster) with
     the apricot ground and the coral stamp between them, instead of one black
     slab reading as a different design. */
  --lp-invert-bg: #0d4a21;
  --lp-invert-fg: #ffe3c4;
  --lp-invert-soft: #b9cbb6;

  --lp-focus: var(--lp-brand);

  /* --error (#e53935) is a fixed, non-theme-aware app token (css/style.css)
     built for a keyline/fill, not for 0.75rem body text — 3.98:1 on
     --lp-panel here, under the 4.5:1 small-text floor. This is that same
     alert red darkened for legibility as TEXT specifically (5.29:1); the
     keyline/border stays on --error itself, which clears the lower 3:1 a
     non-text UI component needs. */
  --lp-error-ink: #c62828;

  --lp-max: 1120px;
  --lp-gutter: clamp(20px, 5vw, 56px);

  /* Bricolage carries the shout: a grotesque with genuinely odd joints, so a
     headline set in it reads as drawn rather than as a system font at 800.
     Figtree gets out of the way underneath it. Martian Mono is the utility
     voice — wide and stencil-like, the way a stamp or a stub is printed. */
  --lp-display: 'Bricolage Grotesque Variable', 'Arial Black', Impact, sans-serif;
  --lp-sans: 'Figtree Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --lp-mono: 'Martian Mono Variable', ui-monospace, 'SF Mono', Menlo, monospace;

  --lp-ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* Overshoot-then-settle, for anything that is meant to land rather than
     arrive — a deliberately different character from the app-wide --ease-out,
     same reasoning css/style.css gives --trace-tick. */
  --lp-ease-slam: cubic-bezier(0.34, 1.56, 0.64, 1);

  background-color: var(--lp-ground);
  /* Printed, not lit: a coarse dot screen over two broad ink washes. The dots
     are a real halftone grid rather than noise, because that is the artefact
     this page's whole visual argument is borrowed from. */
  background-image:
    radial-gradient(rgba(16, 26, 20, 0.16) 0.9px, transparent 1px),
    radial-gradient(85% 60% at 15% -5%, rgba(239, 159, 39, 0.34), transparent 60%),
    radial-gradient(75% 55% at 92% 4%, rgba(153, 60, 29, 0.13), transparent 62%);
  background-size: 6px 6px, auto, auto;
  color: var(--lp-ink);
}

/* Night market. Only the tokens change — no rule below is duplicated. */
@media (prefers-color-scheme: dark) {
  .landing-screen {
    --lp-ground: #0c1410;
    --lp-panel: #151d17;
    --lp-ink: #ffeeda;
    --lp-ink-soft: #9aa79f;

    /* No brand ink is redefined here. --lp-brand/--lp-coral/--lp-amber are
       aliases of --vn-teal/--vn-coral/--vn-amber, which css/style.css already
       flips in its own dark block — restating them would be a second source
       of truth for the same colour. --lp-brand-field/-fg, --lp-amber-fg,
       --lp-stock and --lp-stock-ink are all pinned by design (see above). */
    --lp-hair: rgba(255, 238, 218, 0.18);
    --lp-tape: rgba(232, 163, 61, 0.28);

    --lp-invert-bg: #ffeeda;
    --lp-invert-fg: #0c1410;
    --lp-invert-soft: #3e4a42;

    /* Lightened rather than darkened here — the same alert red at a
       brightness that clears 4.5:1 on --lp-panel's near-black dark value
       (6.20:1); see the light-mode definition above for why this isn't
       just --error. */
    --lp-error-ink: #ff6b6b;

    background-image:
      radial-gradient(rgba(255, 238, 218, 0.09) 0.9px, transparent 1px),
      radial-gradient(85% 60% at 15% -5%, rgba(232, 163, 61, 0.13), transparent 60%),
      radial-gradient(75% 55% at 92% 4%, rgba(226, 114, 74, 0.15), transparent 62%);
  }
}

/* .landing-screen centers one narrow column in its auth-gate state; in
   landing mode #auth-screen is instead a normal, full-width scrolling
   document. Only these two blocks differ between the states — everything
   else (tokens, ground) is shared above. */
.landing-screen.lp-mode {
  display: block;
  padding: 0;
  min-height: 100vh;
}
.landing-screen:not(.lp-mode) {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100vh;
  padding: clamp(32px, 7vw, 64px) var(--lp-gutter) clamp(48px, 8vw, 88px);
}

#landing-page {
  font-family: var(--lp-sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--lp-ink);
  overflow-x: clip;
}

#landing-page :focus-visible,
#auth-panel :focus-visible {
  outline: 3px solid var(--lp-focus);
  outline-offset: 3px;
  border-radius: 2px;
}
/* The band's ground flips with the theme, and no single accent clears both
   sides of that flip — so the ring takes the band's own foreground, which is
   guaranteed to contrast with whatever the band is currently painted in. */
.lp-invert :focus-visible { --lp-focus: var(--lp-invert-fg); }

/* =========================================================
   AUTH GATE (#auth-panel)
   =========================================================
   The account gate a visitor lands on one tap after the landing page's own
   CTAs — "Start a trip" here is landingStart('register'), which routes
   straight past the button group below into #auth-register-form. DESIGN.md
   is explicit that Departure Fever is normative everywhere with no
   exceptions and names this surface as the one still on the retired Field
   Journal look (system font stack, 10px radii, a blurred focus glow, near-
   white cards). Converted whole, per the migration note: shape, type and
   shadow change together, and the --lp-* tokens above already make this
   colour-compatible with the poster one tap away.

   Structure: one persistent .ap-card shell around every child #auth-*-form
   / #auth-main-buttons / #auth-retry-btn — js/auth.js only ever toggles
   which child's display is 'block'/'none' (_hideAllAuthForms and friends),
   so the card itself must never remount between Log In / Register / Guest,
   or that toggle has nothing stable to sit inside. */

#auth-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 380px;
  margin: 0 auto;
}

.ap-mark {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  margin-bottom: 18px;
  border: 2px solid var(--lp-ink);
  border-radius: 999px;
  background: var(--lp-panel);
  color: var(--lp-brand);
  font-size: 24px;
  box-shadow: 3px 3px 0 var(--lp-ink);
}

/* The one shout on this screen (The One Shout Rule) — Bricolage at headline
   scale, standing in for the stamp word the marketing page spends its own
   shout on one tap earlier. Everything below it is deliberately quiet. */
#auth-panel .landing-title {
  margin: 0;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 7vw, 2.125rem);
  line-height: 0.98;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  text-align: center;
  color: var(--lp-ink);
}

#auth-panel .landing-subtitle {
  margin: 10px 0 28px;
  max-width: 34ch;
  font-size: 0.9375rem;
  line-height: 1.6;
  text-align: center;
  color: var(--lp-ink-soft);
}

/* DESIGN.md's Card component. One instance, persistent — see the structure
   note above. */
.ap-card {
  width: 100%;
  padding: 26px 22px 28px;
  background: var(--lp-panel);
  border: 2px solid var(--lp-ink);
  border-radius: 14px;
  box-shadow: 5px 5px 0 var(--lp-ink);
}

#auth-panel h3 {
  margin: 0 0 20px;
  font-family: var(--lp-display);
  font-size: 1.0625rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  text-align: center;
  color: var(--lp-ink);
}

/* ---------- Buttons ---------- */
/* .btn-large/.btn-create/.btn-join/.btn-primary/.btn-secondary are shared,
   app-wide classes (modals, My Trips action cards, every form's submit/
   cancel pair) — everything here is scoped to #auth-panel specifically so
   this conversion cannot leak into any other screen that also uses them. */

#auth-panel .btn-large,
#auth-panel .btn-primary,
#auth-panel .btn-secondary {
  min-height: var(--tap-min);
  padding: 0 22px;
  border: 2px solid var(--lp-ink);
  border-radius: 999px;
  font-family: var(--lp-sans);
  font-size: 0.9375rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  box-shadow: 4px 4px 0 var(--lp-ink);
  transition:
    transform var(--dur-feedback) var(--lp-ease),
    box-shadow var(--dur-feedback) var(--lp-ease);
}
#auth-panel .btn-large:hover,
#auth-panel .btn-primary:hover,
#auth-panel .btn-secondary:hover {
  transform: translate(2px, 2px);
  box-shadow: 2px 2px 0 var(--lp-ink);
}
#auth-panel .btn-large:active,
#auth-panel .btn-primary:active,
#auth-panel .btn-secondary:active {
  transform: translate(4px, 4px);
  box-shadow: 0 0 0 var(--lp-ink);
}
#auth-panel .btn-primary:disabled,
#auth-panel .btn-secondary:disabled,
#auth-panel .btn-large:disabled {
  cursor: default;
  opacity: 0.55;
  transform: none;
  box-shadow: 4px 4px 0 var(--lp-ink);
}

/* Primary: Trail Green, --lp-brand-fg on top rather than a literal white —
   The Mint Flip Rule (issue #432). */
#auth-panel .btn-primary,
#auth-panel .btn-create {
  background: var(--lp-brand);
  color: var(--lp-brand-fg);
}
#auth-panel .btn-primary:hover,
#auth-panel .btn-create:hover { background: var(--lp-brand); }

/* Secondary: Cut Stock, ink text — same keyline and offset as primary. */
#auth-panel .btn-secondary,
#auth-panel .btn-join {
  background: var(--lp-panel);
  color: var(--lp-ink);
}

/* Continue as Guest — the one button that isn't fully committing yet, which
   is also the only place on this screen a dashed edge is honest: it reads
   as provisional the way a taped, not-yet-stamped ticket does (The
   Crooked-On-Purpose Rule's spirit, applied to a border instead of a tilt). */
#auth-panel .ap-btn-ghost {
  background: transparent;
  border: 2px dashed var(--lp-ink-soft);
  color: var(--lp-ink-soft);
  box-shadow: none;
}
#auth-panel .ap-btn-ghost:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--lp-ink);
  color: var(--lp-ink);
}
#auth-panel .ap-btn-ghost:active { transform: none; }

.auth-back-link {
  margin-top: 18px;
  background: none;
  border: none;
  color: var(--lp-ink-soft);
  font-family: var(--lp-sans);
  font-size: 0.875rem;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  min-height: var(--tap-min);
  padding: 0 var(--space-sm);
}
.auth-back-link:hover { color: var(--lp-brand); text-decoration: underline; text-underline-offset: 3px; }

/* ---------- Fields ---------- */
/* DESIGN.md Inputs spec: Cut Stock fill, 2px keyline, 12px radius (cut, not
   pressed — no pill), 44px floor, no glow anywhere. font-size is
   deliberately left to the shared app-wide input rule (css/style.css) —
   14px desktop / 16px below 680px — rather than pinned here, so the auth
   gate keeps the same iOS-zoom fix as every other form in the app. */
#auth-panel .form-group { margin-bottom: 20px; }

#auth-panel .form-label {
  font-family: var(--lp-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--lp-ink-soft);
  margin-bottom: var(--space-md);
}

#auth-panel input[type="text"],
#auth-panel input[type="email"],
#auth-panel input[type="password"],
#auth-panel select {
  height: 44px;
  border: 2px solid var(--lp-ink);
  border-radius: 12px;
  background: var(--lp-panel);
  color: var(--lp-ink);
}
#auth-panel input::placeholder { color: var(--lp-ink-soft); opacity: 1; }

/* No glow anywhere in this system (The Hard Shadow Rule's sibling for
   fields) — a 3px solid ring at a 3px offset instead of the app-wide
   shared rule's box-shadow blur. */
#auth-panel input:focus,
#auth-panel select:focus {
  outline: 3px solid var(--lp-brand);
  outline-offset: 3px;
  border-color: var(--lp-brand);
  box-shadow: none;
}

/* A field currently failing validation — Alert Red keyline, never Stamp
   Coral, which is branded and must not read as danger (DESIGN.md's Inputs
   error spec). */
#auth-panel input[aria-invalid="true"] {
  border-color: var(--error);
}
#auth-panel input[aria-invalid="true"]:focus {
  outline-color: var(--error);
  border-color: var(--error);
}

.ap-field-hint {
  margin: var(--space-sm) 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--lp-ink-soft);
}

.ap-field-error {
  display: none;
  margin: var(--space-sm) 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  font-weight: 600;
  color: var(--lp-error-ink);
}
.ap-field-error.is-visible { display: block; }

/* Invite deferred-claim banner (issue #674 PR3) — a sibling of every
   #auth-*-form inside the one persistent .ap-card (see the structure note
   at the top of this section), so it sits above whichever form
   showLoginForm()/showRegisterForm() (js/auth.js) reveals rather than
   living inside either one. */
.auth-invite-banner {
  margin: 0 0 20px;
  padding: var(--space-md) var(--space-lg);
  background: color-mix(in srgb, var(--lp-brand) 12%, var(--lp-panel));
  border: 2px solid var(--lp-ink);
  border-radius: 10px;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  color: var(--lp-ink);
}

/* "Where are you from?" searchable country combobox — a real listbox, not
   a native <select> (see the markup comment in index.html for why). */
.reg-country-group { position: relative; }
.reg-country-listbox {
  position: absolute;
  left: 0; right: 0; top: 100%;
  z-index: 20;
  margin: var(--space-sm) 0 0;
  padding: var(--space-sm) 0;
  max-height: 260px;
  overflow-y: auto;
  list-style: none;
  background: var(--lp-panel);
  border: 2px solid var(--lp-ink);
  border-radius: 12px;
}
.reg-country-option {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: var(--space-sm) var(--space-lg);
  color: var(--lp-ink);
  cursor: pointer;
}
.reg-country-option.active,
.reg-country-option:hover { background: var(--lp-brand); color: var(--lp-panel); }
.reg-country-empty {
  padding: var(--space-sm) var(--space-lg);
  color: var(--lp-ink-soft);
}

/* ---------- Desktop ---------- */
/* A real wide-viewport treatment — the card was previously the identical
   320px column at every width. Widens the card and its type rather than
   introducing a second layout; a four-field form doesn't earn a multi-
   column composition. */
@media (min-width: 1024px) {
  #auth-panel { max-width: 440px; }

  .ap-mark { width: 60px; height: 60px; font-size: 28px; }

  #auth-panel .landing-title { font-size: 2.5rem; }
  #auth-panel .landing-subtitle { margin-bottom: 34px; font-size: 1rem; }

  .ap-card { padding: 34px 32px 36px; }

  #auth-panel h3 { font-size: 1.25rem; margin-bottom: 24px; }
}

/* ---------- Shared primitives ---------- */

/* The stencil voice: eyebrows, stub metadata, footer meta. Tracked out and
   uppercase, the way anything printed onto a ticket is. */
.lp-eyebrow,
.lp-kicker,
.lp-credit,
.lp-footer-links,
.lp-footer-brand,
.lp-dcard-dest {
  font-family: var(--lp-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Buttons are stickers: a flat block of ink with a hard offset shadow, which
   presses into the page when you push it. No gradients, no blur — the whole
   page is printed in flat inks. */
.lp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap-min);
  padding: 0 22px;
  border: 2px solid var(--lp-ink);
  border-radius: 999px;
  background: var(--lp-panel);
  color: var(--lp-ink);
  font-family: var(--lp-sans);
  font-size: 0.9375rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 4px 4px 0 var(--lp-ink);
  transition:
    transform var(--dur-feedback) var(--lp-ease),
    box-shadow var(--dur-feedback) var(--lp-ease),
    background var(--dur-feedback) var(--lp-ease),
    color var(--dur-feedback) var(--lp-ease);
}
.lp-btn:hover { transform: translate(2px, 2px); box-shadow: 2px 2px 0 var(--lp-ink); }
.lp-btn:active { transform: translate(4px, 4px); box-shadow: 0 0 0 var(--lp-ink); }

.lp-btn-lg {
  min-height: 56px;
  padding: 0 30px;
  font-size: 1.0625rem;
  box-shadow: 5px 5px 0 var(--lp-ink);
}
.lp-btn-lg:hover { box-shadow: 3px 3px 0 var(--lp-ink); }
.lp-btn-lg:active { box-shadow: 0 0 0 var(--lp-ink); }

/* The brand green, and --lp-brand-fg on top of it rather than a literal
   white — see the #432 warning in the token block. */
.lp-btn-primary { background: var(--lp-brand); color: var(--lp-brand-fg); }
.lp-btn-secondary { background: var(--lp-panel); color: var(--lp-ink); }

/* Inside the inverted band the ink that draws the border and the shadow is
   the band's foreground, not the page's. */
.lp-invert .lp-btn {
  border-color: var(--lp-invert-fg);
  box-shadow: 4px 4px 0 var(--lp-invert-fg);
}
.lp-invert .lp-btn:hover { box-shadow: 2px 2px 0 var(--lp-invert-fg); }
.lp-invert .lp-btn:active { box-shadow: 0 0 0 var(--lp-invert-fg); }

.lp-btn-quiet {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  font-weight: 700;
}
.lp-btn-quiet:hover { background: transparent; box-shadow: none; transform: none; color: var(--lp-brand); }
.lp-btn-quiet:active { transform: none; box-shadow: none; }

.lp-textlink {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--lp-ink);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--lp-brand);
  text-underline-offset: 5px;
  cursor: pointer;
}
.lp-textlink:hover { color: var(--lp-brand); }

/* ---------- Nav ---------- */

/* The bar is the app's own top bar, not a tint of the poster behind it: the
   same pinned --topbar-bg green, carrying pale pills the way that bar carries
   its logo and profile pills. A visitor therefore meets the product's chrome
   before they have logged into it, and the landing → app transition has no
   seam at the top of the window.

   Opaque, and with no backdrop-filter. The old bar was a 90% tint of
   --lp-ground over a ground that carries a halftone screen and two radial
   washes, so at the top of the page it read as a ghost band: the dots blurred
   inside it and crisp immediately below, with the washes discontinuous at its
   edges. A solid field cannot do that, and it is also the only way the bar is
   unmistakable against BOTH the apricot ground and the cream panel band it
   sticks over. No keyline either — chrome does not need the poster's 2px ink
   vocabulary to separate from anything, and the app's own bar has none. */
.lp-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--lp-brand-field);
  color: var(--lp-brand-field-fg);
}

/* White on green: --lp-brand is a deep green in light mode and would vanish
   into the bar. Same reasoning as the footer poster. */
.lp-nav :focus-visible { --lp-focus: var(--lp-brand-field-fg); }

.lp-nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: 10px var(--lp-gutter);
}

.lp-nav-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
}

/* The product's own mark, not an abstract dot: the same ti-map-2 glyph the
   auth gate's .landing-logo uses, on a pale pill carrying the brand green —
   which is exactly what --topbar-accent is for in the app's own bar. The
   pill, not the glyph, is the light half now that the bar itself is green. */
.lp-nav-brand i {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  background: var(--lp-brand-field-fg);
  color: var(--lp-brand-field);
  font-size: 17px;
}

.lp-nav-actions { display: flex; align-items: center; gap: 8px; }

/* On the green field the two controls swap roles with the rest of the page:
   the quiet one is pale text at the app's own --topbar-fg-muted weight
   (4.98:1 on this green), and the CTA is the pale pill. */
.lp-nav .lp-btn-quiet {
  color: color-mix(in srgb, var(--lp-brand-field-fg) 85%, transparent);
}
.lp-nav .lp-btn-quiet:hover { color: var(--lp-brand-field-fg); }

.lp-nav .lp-btn-primary {
  border-color: var(--lp-brand-field-fg);
  background: var(--lp-brand-field-fg);
  color: var(--lp-brand-field);
  box-shadow: 3px 3px 0 rgba(6, 40, 17, 0.45);
}
.lp-nav .lp-btn-primary:hover { box-shadow: 1px 1px 0 rgba(6, 40, 17, 0.45); }
.lp-nav .lp-btn-primary:active { box-shadow: 0 0 0 rgba(6, 40, 17, 0.45); }

/* The nav's own CTA is redundant beside the hero's on a small screen, where
   both are on the first viewport anyway. */
.lp-nav-cta { display: none; }

/* ---------- Hero ---------- */

.lp-hero {
  display: grid;
  gap: clamp(48px, 8vw, 64px);
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: clamp(40px, 7vw, 76px) var(--lp-gutter) clamp(56px, 9vw, 104px);
}

.lp-eyebrow {
  display: block;
  max-width: 34ch;
  color: var(--lp-ink-soft);
  line-height: 1.7;
}

.lp-hero-title {
  margin: 20px 0 0;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: clamp(2.5rem, 10vw, 4.5rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: uppercase;
}

/* The destination sits on its own line, always. A stamp that shared a line
   with "Let's go to" would re-wrap the whole headline every time the word
   changed length — the cycle has to be able to run without the page moving
   underneath it. */
.lp-stamp-slot {
  display: block;
  margin-top: 0.14em;
  padding-left: 0.14em;   /* the rotation swings the left edge outward */
}

/* ★ THE SIGNATURE — a rubber stamp, struck slightly crooked and printed
   twice: the double keyline is the stamp's die, the text-shadow is the
   second ink pass landing out of register. */
.lp-stampword {
  display: inline-block;
  padding: 0.04em 0.2em 0.09em;
  border: 0.055em solid currentColor;
  border-radius: 0.14em;
  box-shadow: inset 0 0 0 0.022em currentColor;
  color: var(--lp-coral);
  text-shadow: 0.028em 0.036em 0 var(--lp-ghost);
  transform: rotate(-2.6deg);
  transform-origin: 30% 60%;
}

.lp-hero-sub {
  margin: 26px 0 0;
  max-width: 44ch;
  font-size: 1.0625rem;
  color: var(--lp-ink-soft);
}

.lp-hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
}

.lp-hero-note {
  margin: 22px 0 0;
  font-size: 0.875rem;
  color: var(--lp-ink-soft);
}
/* Inline inside a sentence, so it cannot be a 44px-tall box without wrecking
   the line. Expand the tappable area instead, per the --tap-min note in
   CLAUDE.md — the glyphs stay on the baseline, the hit box does not. */
.lp-hero-note .lp-textlink {
  position: relative;
  font-size: inherit;
  min-height: 0;
}
.lp-hero-note .lp-textlink::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: max(var(--tap-min), 100%);
  transform: translate(-50%, -50%);
}

/* ---------- Prints ---------- */
/* Every photograph on this page is an instant print: a stock border wider at
   the foot than at the head, taped down at an angle. No captions — a print
   stuck to a wall does not carry a label, and the picture is doing the
   arguing on its own. */

.lp-print {
  position: relative;
  display: block;
  padding: 10px 10px 34px;
  background: var(--lp-stock);
  border-radius: 3px;
  box-shadow:
    0 1px 0 rgba(9, 20, 13, 0.1),
    0 22px 40px -26px rgba(9, 20, 13, 0.75);
}

/* Tape. Two torn strips holding the print to the page — the reason anything
   on this page is allowed to sit at an angle. */
.lp-print::before,
.lp-print::after {
  content: '';
  position: absolute;
  top: -13px;
  width: 74px;
  height: 26px;
  background: var(--lp-tape);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.lp-print::before { left: 9%; transform: rotate(-4deg); }
.lp-print::after { right: 9%; transform: rotate(3.4deg); }

.lp-print-window {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: 1px;
  background: color-mix(in srgb, var(--lp-ink) 12%, var(--lp-stock));
}

.lp-print-window img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The hero print is revealed by .lp-hero-has-photo, which js/landing.js sets
   only once photos actually render. With none, the hero is a single centred
   column of copy — a deliberate state, not a broken one. */
.lp-hero-photo { display: none; }
.lp-hero-has-photo .lp-hero-photo { display: block; }

.lp-hero-photo {
  position: relative;
  justify-self: center;
  width: min(100%, 440px);
  margin: 0;
  transform: rotate(-2deg);
}

/* Two more prints under the top one, just their edges showing. A stack, not
   a single specimen — you took more than one photo. */
.lp-hero-photo .lp-stack-a,
.lp-hero-photo .lp-stack-b {
  position: absolute;
  inset: 0;
  border-radius: 3px;
  background: var(--lp-stock);
  box-shadow: 0 10px 24px -18px rgba(9, 20, 13, 0.8);
}
.lp-hero-photo .lp-stack-a { transform: rotate(4.5deg) translate(6px, 4px); }
.lp-hero-photo .lp-stack-b { transform: rotate(-6deg) translate(-8px, 8px); }
.lp-hero-photo .lp-print { position: relative; }

.lp-hero-bg {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: color-mix(in srgb, var(--lp-ink) 12%, var(--lp-stock));
}

.lp-hero-bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 900ms var(--lp-ease);
}
.lp-hero-bg img.is-active { opacity: 1; }

/* Photograph attribution, printed as fine print on the stock's bottom margin
   rather than as a label under the work. The captions this page used to carry
   are gone on purpose — a print taped to a wall does not explain itself — but
   the photographer is still owed a credit, so this is the one line that
   stays. A photo with no credit on the row prints nothing at all. */
.lp-credit {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 11px;
  height: 1.2em;
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  color: var(--lp-stock-ink);
}

.lp-credit-line {
  display: block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The hero credit turns over with the photo it belongs to. Stacked rather
   than swapped so the line never collapses mid-transition. */
#lp-hero-label .lp-credit-line { transition: opacity 400ms var(--lp-ease); }
#lp-hero-label .lp-credit-line:not(.is-active) {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}

.lp-credit a { color: inherit; text-underline-offset: 3px; }
/* --lp-brand-field, not --lp-brand: this line sits on --lp-stock, which is
   pinned light, and the dark-mode mint would drop to 1.2:1 on it. */
.lp-credit a:hover { color: var(--lp-brand-field); }

/* ---------- Bands and sections ---------- */

.lp-section {
  max-width: var(--lp-max);
  margin: 0 auto;
  padding: clamp(56px, 8vw, 96px) var(--lp-gutter);
}

/* A full-bleed band of flat ink. .lp-band is the wrapper; the section inside
   keeps the page's measure. */
.lp-band {
  border-block: 2px solid var(--lp-ink);
}
.lp-band-panel { background: var(--lp-panel); }

.lp-invert {
  background: var(--lp-invert-bg);
  color: var(--lp-invert-fg);
  border-block-color: var(--lp-invert-bg);
}
.lp-invert .lp-section-sub,
.lp-invert .lp-step p,
.lp-invert .lp-eyebrow { color: var(--lp-invert-soft); }

.lp-section-head { max-width: 52ch; margin-bottom: clamp(34px, 5vw, 54px); }

.lp-kicker {
  display: inline-block;
  margin-bottom: 14px;
  padding: 5px 11px 4px;
  border: 2px solid currentColor;
  border-radius: 999px;
  color: var(--lp-brand);
}
/* ⚠️ The band's ground flips with the theme, so no fixed ink is legible on
   both. The pill takes the band's own foreground instead — the outline is
   what carries it, not the colour. */
.lp-invert .lp-kicker { color: inherit; }

.lp-section-title {
  margin: 0;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: clamp(2rem, 5.4vw, 3.125rem);
  line-height: 0.98;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  text-wrap: balance;
}

.lp-section-sub {
  margin: 16px 0 0;
  font-size: 1rem;
  color: var(--lp-ink-soft);
}

.lp-section-foot { margin-top: 38px; }

/* ---------- The wall of prints ---------- */

/* Breaks the page's measure and runs the full width of the viewport, because
   a wall of photos does not stop where the body copy stops. */
.lp-wall { max-width: none; padding-inline: 0; }

.lp-wall-head {
  max-width: var(--lp-max);
  margin-inline: auto;
  padding-inline: var(--lp-gutter);
}

.lp-wall-row {
  display: flex;
  align-items: center;
  gap: clamp(22px, 4vw, 52px);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* Vertical: room for the tape, the rotation and the focus ring, which the
     scroll container would otherwise clip. Horizontal: line the first print
     up with the page's measure while still letting the row run off the right
     edge — 100% here is the viewport width, since .lp-wall is full-bleed. */
  padding-block: 30px;
  padding-inline: max(
    var(--lp-gutter),
    calc((100% - var(--lp-max)) / 2 + var(--lp-gutter))
  );
  margin-block: -30px;
  scrollbar-width: thin;
}

.lp-photo-card {
  position: relative;
  flex: 0 0 auto;
  width: clamp(210px, 58vw, 258px);
  margin: 0;
  scroll-snap-align: center;
  transition: transform var(--dur-state) var(--lp-ease);
}

/* A wall is a rhythm of formats and angles, not a row of identical tiles.
   Cycling four tilts and four ratios by position gives that rhythm without
   needing dimensions the LandingPhoto row does not store. */
.lp-photo-card:nth-child(4n + 1) { transform: rotate(-2.4deg); }
.lp-photo-card:nth-child(4n + 2) { transform: rotate(1.8deg) translateY(-14px); }
.lp-photo-card:nth-child(4n + 3) { transform: rotate(-1.2deg) translateY(10px); }
.lp-photo-card:nth-child(4n + 4) { transform: rotate(3deg); }

.lp-photo-card:nth-child(4n + 1) .lp-print-window { aspect-ratio: 4 / 5; }
.lp-photo-card:nth-child(4n + 2) .lp-print-window { aspect-ratio: 1 / 1; }
.lp-photo-card:nth-child(4n + 3) .lp-print-window { aspect-ratio: 5 / 4; }
.lp-photo-card:nth-child(4n + 4) .lp-print-window { aspect-ratio: 3 / 4; }

/* Straightening a crooked print is what you actually do when you look at one
   on a wall. */
.lp-photo-card:hover,
.lp-photo-card:focus-within { transform: rotate(0deg) scale(1.03); }

/* ---------- What it does ---------- */

.lp-cards {
  display: grid;
  gap: 18px;
}

.lp-card {
  position: relative;
  padding: 24px 22px 26px;
  background: var(--lp-panel);
  border: 2px solid var(--lp-ink);
  border-radius: 14px;
  box-shadow: 5px 5px 0 var(--lp-ink);
}

/* Each card is over-printed with a different ink, cycled by position — a
   sheet of stickers comes off the press in colour order. */
.lp-card:nth-child(3n + 1) { --lp-card-ink: var(--lp-brand); }
.lp-card:nth-child(3n + 2) { --lp-card-ink: var(--lp-coral); }
.lp-card:nth-child(3n + 3) { --lp-card-ink: var(--lp-amber); }

.lp-card::before {
  content: '';
  position: absolute;
  top: -2px;
  left: 22px;
  width: 46px;
  height: 8px;
  border-radius: 0 0 6px 6px;
  background: var(--lp-card-ink);
}

.lp-card h3 {
  margin: 0;
  font-family: var(--lp-display);
  font-size: 1.1875rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--lp-ink);
}

.lp-card p {
  margin: 10px 0 0;
  font-size: 0.9375rem;
  color: var(--lp-ink-soft);
}

/* ---------- Three steps, as a route ---------- */

.lp-steps {
  position: relative;
  display: grid;
  gap: 26px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.lp-step {
  position: relative;
  display: grid;
  grid-template-columns: 52px 1fr;
  align-items: start;
  gap: 4px 18px;
}

/* The pin. Numbered because the content genuinely is a sequence — you cannot
   share a code before there is a trip to share. */
.lp-step-num {
  grid-row: 1 / 3;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 2px solid var(--lp-invert-fg);
  border-radius: 50%;
  /* A waypoint takes the colour of the route it sits on, so the pin is amber
     like the dashes rather than green. Green stays reserved for controls —
     and a green pin would in any case near-vanish into this band's own
     green-black ground in light mode. */
  background: var(--lp-amber);
  color: var(--lp-amber-fg);
  font-family: var(--lp-display);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1;
}

.lp-step h3 {
  margin: 4px 0 0;
  font-family: var(--lp-display);
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.lp-step p {
  margin: 8px 0 0;
  max-width: 46ch;
  font-size: 0.9375rem;
  color: var(--lp-invert-soft);
}

/* The route between the pins: a dashed line, the one on every map that shows
   a journey rather than a road. Vertical while the steps stack. */
.lp-steps::before {
  content: '';
  position: absolute;
  top: 26px;
  bottom: 26px;
  left: 25px;
  width: 3px;
  background: repeating-linear-gradient(
    to bottom,
    var(--lp-amber) 0 9px,
    transparent 9px 18px
  );
}

/* ---------- Discover stubs ---------- */

.lp-discover-row {
  display: flex;
  gap: 18px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding: 6px 2px 10px;
  scrollbar-width: thin;
}

/* A boarding-pass stub: the notch on the left is where it was torn off. */
.lp-dcard {
  position: relative;
  flex: 0 0 auto;
  width: clamp(210px, 32vw, 252px);
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
  background: var(--lp-panel);
  border: 2px solid var(--lp-ink);
  border-radius: 12px;
  box-shadow: 4px 4px 0 var(--lp-ink);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    transform var(--dur-feedback) var(--lp-ease),
    box-shadow var(--dur-feedback) var(--lp-ease);
}
.lp-dcard:hover { transform: translate(2px, 2px); box-shadow: 2px 2px 0 var(--lp-ink); }
.lp-dcard:active { transform: translate(4px, 4px); box-shadow: 0 0 0 var(--lp-ink); }

.lp-dcard-img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-bottom: 2px dashed var(--lp-hair);
  background: color-mix(in srgb, var(--lp-ink) 12%, var(--lp-panel));
}

.lp-dcard-img-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.75rem;
}

.lp-dcard-body { display: block; padding: 14px 16px 16px; }

.lp-dcard-dest { display: block; color: var(--lp-brand); }

.lp-dcard-name {
  display: block;
  margin-top: 7px;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: 1.125rem;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--lp-ink);
}

.lp-dcard-meta {
  display: block;
  margin-top: 7px;
  font-size: 0.8125rem;
  color: var(--lp-ink-soft);
}

/* ---------- FAQ ---------- */

.lp-faq {
  display: grid;
  gap: 12px;
}

.lp-faq-item {
  background: var(--lp-panel);
  border: 2px solid var(--lp-ink);
  border-radius: 12px;
  overflow: hidden;
}

.lp-faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: var(--tap-min);
  padding: 16px 18px;
  font-family: var(--lp-display);
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  cursor: pointer;
  list-style: none;
}
.lp-faq-item summary::-webkit-details-marker { display: none; }

.lp-faq-item summary::after {
  content: '';
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background:
    linear-gradient(var(--lp-brand-fg), var(--lp-brand-fg)) 50% 50% / 11px 2.5px no-repeat,
    linear-gradient(var(--lp-brand-fg), var(--lp-brand-fg)) 50% 50% / 2.5px 11px no-repeat,
    var(--lp-brand);
  transition: transform var(--dur-state) var(--lp-ease);
}
.lp-faq-item[open] summary::after { transform: rotate(135deg); }

.lp-faq-item p {
  margin: 0;
  padding: 0 18px 18px;
  max-width: 62ch;
  font-size: 0.9375rem;
  color: var(--lp-ink-soft);
}
.lp-faq-item p a { color: var(--lp-brand); text-underline-offset: 3px; }

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

/* The last thing on the page is a poster, not a section: one full-bleed
   field of brand green with the instruction set as large as it will go. It
   uses --lp-brand-field rather than --lp-brand for the same reason the app
   pins --topbar-bg — a full-bleed field of the dark-mode mint would be
   unreadable — which also means the page opens and closes on the same green
   the app's own top bar is painted in. */
.lp-footer {
  max-width: none;
  padding: clamp(64px, 10vw, 116px) var(--lp-gutter) 36px;
  background: var(--lp-brand-field);
  border-top: 2px solid var(--lp-ink);
  color: var(--lp-brand-field-fg);
}

.lp-footer-cta {
  max-width: var(--lp-max);
  margin: 0 auto;
  text-align: center;
}

.lp-footer-cta h2 {
  margin: 0 0 8px;
  font-family: var(--lp-display);
  font-weight: 800;
  font-size: clamp(4rem, 22vw, 11rem);
  line-height: 0.82;
  letter-spacing: -0.06em;
  text-transform: uppercase;
}

.lp-footer-cta p {
  margin: 0 auto 30px;
  max-width: 34ch;
  font-size: 1.0625rem;
}

.lp-footer .lp-btn {
  /* A pale pill carrying the green, the way the app's top bar holds its
     white logo/profile pills against the same field. */
  border-color: var(--lp-brand-field-fg);
  background: var(--lp-brand-field-fg);
  color: var(--lp-brand-field);
  box-shadow: 5px 5px 0 rgba(6, 40, 17, 0.45);
}
.lp-footer .lp-btn:hover { box-shadow: 3px 3px 0 rgba(6, 40, 17, 0.45); }
.lp-footer .lp-btn:active { box-shadow: 0 0 0 rgba(6, 40, 17, 0.45); }
.lp-footer :focus-visible { --lp-focus: var(--lp-brand-field-fg); }

.lp-footer-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  max-width: var(--lp-max);
  margin: clamp(56px, 9vw, 92px) auto 0;
  padding-top: 20px;
  border-top: 2px solid color-mix(in srgb, var(--lp-brand-field-fg) 45%, transparent);
}

.lp-footer-brand { font-size: 0.75rem; letter-spacing: 0.2em; }

.lp-footer-links { display: flex; align-items: center; gap: 12px; }
.lp-footer-links a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  color: inherit;
  text-underline-offset: 3px;
}
.lp-footer-links a:hover { text-decoration-thickness: 2px; }

/* ---------- Entrances ---------- */
/* .is-visible is added by initLandingReveal()'s IntersectionObserver
   (js/landing.js), which falls back to marking everything visible when there
   is no observer — so no rule here may leave content hidden by default in a
   way JS is required to undo. Guarding the whole block on
   prefers-reduced-motion: no-preference means the hidden start state simply
   never exists for a reader who opted out. */
@media (prefers-reduced-motion: no-preference) {
  .lp-reveal,
  .lp-pop {
    opacity: 0;
    transform: translateY(18px);
    transition:
      opacity var(--dur-view) var(--lp-ease),
      transform var(--dur-view) var(--lp-ease);
  }
  /* .lp-pop lands rather than arrives — it is a sticker being pressed down. */
  .lp-pop { transition-timing-function: var(--lp-ease-slam); }
  .lp-reveal.is-visible,
  .lp-pop.is-visible {
    opacity: 1;
    transform: none;
  }

  /* A print's resting tilt is layout, not motion, so it fades in where the
     text cards travel — a transform entrance would have to fight the tilt
     each card already carries. The transform transition stays for hover. */
  .lp-photo-card {
    opacity: 0;
    transition:
      opacity var(--dur-view) var(--lp-ease) 200ms,
      transform var(--dur-state) var(--lp-ease);
  }
  .lp-photo-card.is-visible { opacity: 1; }

  /* The steps are revealed by their <ol>'s own .lp-timeline observation, not
     individually — one observer entry for a group that reads as one run. */
  .lp-timeline .lp-step {
    opacity: 0;
    transform: translateX(-14px);
    transition:
      opacity var(--dur-view) var(--lp-ease),
      transform var(--dur-view) var(--lp-ease);
  }
  .lp-timeline.is-visible .lp-step { opacity: 1; transform: none; }

  /* The route draws itself before the pins arrive on it — it maps to the real
     act it is named after, which is what earns it the motion. */
  .lp-timeline::before { scale: 1 0; transform-origin: top center; transition: scale 700ms var(--lp-ease); }
  .lp-timeline.is-visible::before { scale: 1 1; }

  /* Staggered so each run reads top-down rather than arriving as one block. */
  .lp-card:nth-child(2), .lp-step:nth-child(2) { transition-delay: 70ms; }
  .lp-card:nth-child(3), .lp-step:nth-child(3) { transition-delay: 140ms; }
  .lp-card:nth-child(4) { transition-delay: 210ms; }
  .lp-card:nth-child(5) { transition-delay: 280ms; }
  .lp-card:nth-child(6) { transition-delay: 350ms; }

  /* Page load: the hero settles in one short sequence rather than five
     unrelated ones. */
  .lp-hero-copy > * { animation: lp-rise var(--dur-view) var(--lp-ease) both; }
  .lp-hero-copy > *:nth-child(2) { animation-delay: 70ms; }
  .lp-hero-copy > *:nth-child(3) { animation-delay: 140ms; }
  .lp-hero-copy > *:nth-child(4) { animation-delay: 210ms; }
  .lp-hero-copy > *:nth-child(5) { animation-delay: 280ms; }
  .lp-hero-photo { animation: lp-drop 620ms var(--lp-ease) 200ms both; }

  /* ★ The stamp coming down. js/landing.js adds .is-struck for one animation
     on load and on every swap; the resting transform in .lp-stampword is what
     it lands on, so an interrupted or unsupported animation still leaves the
     word correctly placed. */
  .lp-stampword.is-struck { animation: lp-strike 420ms var(--lp-ease-slam) both; }
}

@keyframes lp-rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: none; }
}

@keyframes lp-drop {
  from { opacity: 0; transform: rotate(-9deg) translateY(-26px); }
  to { opacity: 1; transform: rotate(-2deg); }
}

@keyframes lp-strike {
  0% { opacity: 0; transform: rotate(-11deg) scale(1.85); }
  55% { opacity: 1; transform: rotate(-2.6deg) scale(0.95); }
  78% { transform: rotate(-2.6deg) scale(1.025); }
  100% { transform: rotate(-2.6deg) scale(1); }
}

/* Every travelling effect above resolves here to its FINISHED state, never a
   hidden one — same rule the app-wide block in css/style.css follows. Note
   .lp-photo-card is deliberately absent from the transform reset: its tilt is
   layout, not motion, and zeroing it here would flatten the wall. */
@media (prefers-reduced-motion: reduce) {
  .lp-reveal,
  .lp-pop,
  .lp-timeline .lp-step,
  .lp-hero-copy > *,
  .lp-hero-photo {
    opacity: 1;
    transform: none;
    animation: none;
    transition: none;
  }
  .lp-hero-photo { transform: rotate(-2deg); }
  .lp-photo-card { opacity: 1; transition: none; }
  .lp-photo-card:hover, .lp-photo-card:focus-within { transform: none; }
  .lp-timeline::before { scale: 1 1; }
  .lp-stampword { animation: none; }
  .lp-hero-bg img { transition: none; }
  .lp-btn, .lp-dcard { transition: none; }
}

/* ---------- Narrow ---------- */

/* The fanned prints under the hero shot need gutter to fan into, and at phone
   width there is only 20px of it — the back sheet's corner used to be absorbed
   by the stray 16px of page padding this file spent months accidentally
   carrying (see the ⚠️ at the top). With the page genuinely full-bleed the fan
   has to tuck in on its own. */
@media (max-width: 680px) {
  /* The fan's reach scales with the print's own width, so the print gives back
     a little of the column and the angles ease off — together that keeps the
     back sheet's corner inside a 20px gutter instead of 5px outside it. */
  .lp-hero-photo { width: min(100% - 26px, 440px); }
  .lp-hero-photo .lp-stack-a { transform: rotate(3deg) translate(4px, 3px); }
  .lp-hero-photo .lp-stack-b { transform: rotate(-3.5deg) translate(-4px, 5px); }
}

/* ---------- Wide ---------- */

@media (min-width: 700px) {
  .lp-cards { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .lp-nav-cta { display: inline-flex; }

  .lp-hero {
    grid-template-columns: 1.1fr 0.9fr;
    align-items: center;
    gap: clamp(48px, 6vw, 88px);
  }
  /* With no photo the copy is the whole hero, so it becomes a centred poster
     rather than a column of text against an empty half-page. */
  .lp-hero:not(.lp-hero-has-photo) {
    grid-template-columns: minmax(0, 760px);
    justify-content: center;
    text-align: center;
  }
  .lp-hero:not(.lp-hero-has-photo) .lp-eyebrow,
  .lp-hero:not(.lp-hero-has-photo) .lp-hero-sub { margin-inline: auto; }
  .lp-hero:not(.lp-hero-has-photo) .lp-hero-ctas { justify-content: center; }
  /* The rotation swings the stamp's left edge out; on a centred line the
     optical centre needs the compensating pad removed. */
  .lp-hero:not(.lp-hero-has-photo) .lp-stamp-slot { padding-left: 0; }

  .lp-cards { grid-template-columns: repeat(3, 1fr); gap: 22px; }

  /* Three pins along one horizontal route, which is what a journey drawn on a
     map actually looks like. */
  .lp-steps { grid-template-columns: repeat(3, 1fr); gap: 34px; }
  .lp-step { grid-template-columns: 1fr; gap: 0; }
  .lp-step-num { grid-row: auto; margin-bottom: 18px; }
  /* Ends ON the last pin rather than running past it: with three equal
     columns and a 34px gap, one column width in from the right edge is
     (100% - 68px)/3, and the pin's centre is 26px inside that. */
  .lp-steps::before {
    top: 25px;
    bottom: auto;
    left: 26px;
    right: calc((100% - 68px) / 3 - 26px);
    width: auto;
    height: 3px;
    background: repeating-linear-gradient(
      to right,
      var(--lp-amber) 0 9px,
      transparent 9px 18px
    );
  }
}

@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  .lp-timeline::before { scale: 0 1; transform-origin: left center; }
  .lp-timeline.is-visible::before { scale: 1 1; }
}
