/* =========================================================================
   ClearHarbor — LANDING PAGE ("/")  ·  GROUND-UP REDESIGN
   =========================================================================

   WHAT CHANGED, AND WHY THIS FILE IS A REWRITE RATHER THAN ANOTHER PASS
   --------------------------------------------------------------------
   The previous landing page was assembled by loading coming-soon.css (for the
   hero shell) and download.css (for the CTA cluster) and then overriding both
   from here. That is the root cause of "cookie cutter": the front door was
   literally wearing the coming-soon teaser's chassis — a full-bleed photo with
   text scrimmed over it, one column, one rhythm — and every attempt to make it
   feel composed was an override fighting a layout designed for a different job.

   FULL-REDESIGN PASS loaded tokens.css + site.css + this file, nothing else.
   MARKETING-SHELL PASS inserted marketing.css between site.css and this file
   (tokens.css → site.css → marketing.css → landing.css) and moved the nav,
   the dark-band footer, .ch-wrap/.ch-band/.ch-grain/.ch-band-dark, and the
   four hoisted local custom properties OUT of this file and into that one —
   see marketing.css's own header comment for the full reasoning. What's left
   here is genuinely Landing-only: hero grid, evidence card, story band,
   how-it-works, art slots, motion system. coming-soon.css and download.css
   are untouched and still serve /coming-soon and /download.

   THE COMPOSITION (the gap was structural, not craft — see .claude/otto-goal.md)
   ----------------------------------------------------------------------------
   Section shapes alternate deliberately, dense against empty:

     1. HERO          flat --bg   · editorial copy block, then a wide art plate
                                    with a bento side column. Text is NEVER over
                                    the painting.
     2. PROOF BAND    --panel     · hard colour cut (Anthropic's device, not a
                                    fade). The before/after demo, then the
                                    asymmetric "What it does" split. Dense.
     3. HOW IT WORKS  flat --bg   · wide, sparse, oversized display numerals.
                                    The page's breath.
     4. WHAT TO EXPECT flat --bg  · a 5/7/12 bento; the third card is a dark
                                    plate, which sets up the foot.
     5. CLOSING BAND  --band      · full-bleed dark, the single repeated CTA.
     6. FOOTER        --band      · continues the dark foot, oversized wordmark.

   Both reference screenshots are answered here as one language rather than one
   motif each: Xurya's big rounded hero plate + tidy feature grid + dark closing
   band; Ascone's bento hero, dark plate inside a light band, full-bleed dark
   stats band and oversized footer wordmark.

   HARD CONSTRAINTS HONOURED
   -------------------------
   · No raw hex colours in this file (LandingStyleContractTests invariant 3).
     Every colour is a token or a color-mix over one.
   · No new custom property names beyond the four already allowlisted in
     LandingStyleContractTests (--ink-a08, --ink-a16, --shadow-soft-1,
     --shadow-soft-2) plus --landing-nav-h and --i. New *tokens* went into
     tokens.css instead, where the contract test can see them.
   · /app is untouched: nothing here is in site.css, and the new tokens.css
     entries (--r-xl/--r-2xl, --space-8/9, --fs-section, --band*) have no
     consumer outside this file.
   · The nav's clearance is measured at runtime from the real element
     (Landing.cshtml) and read here as --landing-nav-h. It is never hand-
     computed — that regression has happened twice.
   · There is no hero scrim any more, and that is the point. The scrim existed
     because copy sat on top of the painting; the copy no longer does, so the
     failure mode the scrim was patching (and the .hero-copy-vs-.hero anchoring
     bug underneath it) is designed out rather than re-tuned.
   ========================================================================= */


/* =========================================================================
   1. PAGE FRAME
   .landing-shell/.landing-below/.landing-footer's four hoisted custom
   properties (--ink-a08/--ink-a16/--shadow-soft-1/--shadow-soft-2), the
   `body { background }` reset, the `html { scroll-behavior/scroll-padding }`
   rule and .ch-wrap/.ch-band all MOVED to marketing.css — every marketing
   page needs them, not just this one. See that file's header comment.
   These are the ones LandingStyleContractTests already knows as
   element-local; the selector they're declared on grew to include
   .marketing-shell/.marketing-footer, not renamed, so the contract test
   keeps passing.
   ========================================================================= */


/* =========================================================================
   1b. ATMOSPHERE — the "do we need a shader?" answer
   =========================================================================
   No. Not a shader, not WebGL, not canvas — and not because those are hard, but
   because they would be off-brand. This product's entire visual identity is
   watercolour: soft wet bleed everywhere, dry ink only on the subject. A
   procedural gradient mesh is the wrong material. What the page actually needed
   was the thing our own artwork already does — large, soft, low-contrast washes
   and paper grain — which CSS does natively and for free.

   Grounded in what I could actually verify: I did NOT fetch the three reference
   sites for this (fetching returns their text, not their background treatment,
   so it would have been guesswork dressed up as research). I worked from the two
   screenshots Andrew supplied, where the treatment IS visible — and the useful
   observation is in the fintech one: its full-bleed dark band is not flat, it
   carries a soft draped-silk gradient. That is the note our dark bands were
   missing, and it is the same note our watercolour already plays.

   Two ingredients, both fail-open and both static (a drifting background is the
   fastest way to make a calm page feel cheap, and it would fight the
   one-time-entrance discipline everywhere else on this page):

     · a soft directional wash, from existing halo/accent tokens
     · paper grain, reusing site.css's own noise — body::before is
       position:fixed at z-index:-1, so it never reaches a band that paints its
       own background. This puts the same grain back on top of those bands.

   The grain (.ch-grain::after) and dark-band wash (.ch-band-dark::before)
   rules themselves moved to marketing.css alongside .ch-band/.ch-wrap — every
   marketing page's dark footer band uses them now, not just this page's
   closing band. Only the hero-specific wash below (.landing-shell::before,
   under the copy column) stays here.
   ========================================================================= */

/* Hero atmosphere. Sits under the copy column, where round 1 left a large flat
   pale void — it gives that space depth without putting anything IN it. */
.landing-shell::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(70% 55% at 12% 18%, var(--halo-1), transparent 62%),
    radial-gradient(55% 45% at 38% 88%, var(--halo-2), transparent 60%),
    radial-gradient(40% 40% at 0% 60%, var(--accent-soft), transparent 70%);
  opacity: 0.9;
}
/* THE NAV-SHIFT BUG, ROOT CAUSE (found, not guessed): this selector used to
   read `.landing-shell > *` — every direct child, no exclusions. Two of this
   div's direct children are `#landing-nav-sentinel` and `<header class="site-
   nav">`, and marketing.css gives THOSE their own explicit position values
   (`position: absolute` and `position: fixed` respectively) at the exact same
   specificity (one class selector, 0-1-0) as this rule. With a specificity
   tie the cascade falls back to source order, and this file loads AFTER
   marketing.css — so on Landing (and only on Landing, since Privacy/Terms/
   Getting Started's nav sits in `.marketing-shell`, which no such wildcard
   rule touches) this line was silently overwriting `position: fixed` with
   `position: relative`. A `position: relative` .site-nav with no offsets
   behaves as an in-flow, full-width block (position:relative doesn't get the
   abs/fixed sizing algorithm's shrink-to-fit-then-center treatment) instead
   of the centered, 1200px-capped floating capsule every other marketing page
   renders — a completely different box on the one page a visitor is likely
   to land on first, which is exactly the "shift" moving between pages.
   Only `<main class="hero">` genuinely needs the lift-above-the-wash
   treatment this rule exists for (it has its own explicit `position:
   relative` in the ".landing-shell .hero" rule below anyway, so it does not
   depend on this line) — narrowed to exclude the two elements that already
   declare their own position and must not have it silently reassigned. */
.landing-shell > *:not(.site-nav):not(.landing-nav-sentinel) { position: relative; z-index: 1; }


/* =========================================================================
   3. HERO
   The single biggest compositional change on the page. Previously: one column
   of copy floated over a full-bleed photograph behind a scrim. Now: an
   editorial copy block on flat paper, then a wide art plate with a bento side
   column beneath it.

   Why this arrangement and not Ascone's literal right-column bento — the fork,
   recorded: our hero art is a WIDE watercolour whose whole subject (the dry-ink
   sailboats against extreme negative space) needs horizontal room. Cropping it
   into a portrait right-hand column would have destroyed the one asset the
   brief says must survive. So the bento runs full width UNDER the copy instead
   of beside it: the painting gets a 16/10 plate, the side column gets the two
   supporting cards. Alternative if this reads wrong: swap to copy-left /
   art-right at 6/6 and accept a much tighter crop on the painting.
   ========================================================================= */
.landing-shell {
  position: relative;
  background: var(--bg);
}

.landing-shell .hero {
  position: relative;
  /* Runtime-measured nav clearance. The fallback is deliberately OVERSIZED:
     extra clearance is invisible, a fraction-short one slices the top of the
     copy column — the exact bug this replaced, twice. Never hand-compute this.
     ROUND 4 raised the stakes: the eyebrow that used to absorb a short fallback
     is gone, so a fraction-short value now clips the HEADLINE. */
  padding-block-start: calc(var(--landing-nav-h, 6rem) + clamp(1.5rem, 4vw, 3rem));
  padding-block-end: clamp(2.5rem, 6vw, 4.5rem);
}

/* ---- THE HERO GRID ------------------------------------------------------
   ROUND 2, Andrew's call: the painting comes back into the main landing area.
   Round 1 put it on a contained plate BELOW the copy, which left a large pale
   void above it and made the page read as "space waiting for an image".

   The reconciliation with the no-text-over-imagery discipline: copy column on
   the left, painting on the right BLEEDING OFF the viewport edge at hero scale.
   Nothing is ever laid over the art, but the art is now large, load-bearing and
   in the first screen — and the bleed is what makes it read as a window rather
   than a card floating in space. Rounded on the left only; the right side runs
   out of the frame.

   The fork, recorded: the alternative was a hard-cut full-width art band
   directly under a tight copy block (more Anthropic, less Cursor). It is a
   calmer composition but it puts the painting BELOW the fold again at common
   laptop heights, which is the exact complaint this round is fixing. Say the
   word and it is a small change.

   The left column deliberately reuses the SAME measure the rest of the page is
   aligned to. `max(gutter, 50vw - half-measure + gutter)` puts the eyebrow, the
   headline and every section title below on one continuous vertical line, while
   the right column is free to run to the viewport edge. That single alignment
   is doing more for "composed" than any component here. */
.ch-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.88fr);
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  min-height: clamp(24rem, 44vw, 36rem);
}

.ch-hero-copy {
  padding-inline-start: max(
    clamp(1.25rem, 4vw, 3rem),
    calc(50vw - 600px + clamp(1.25rem, 4vw, 3rem))
  );
  padding-inline-end: clamp(1rem, 2vw, 2rem);
}

/* ROUND 4: .hero-eyebrow's rule is REMOVED with the element — it was
   `margin: 0 0 var(--space-4); color: var(--accent)` and served this one <p>.
   ⚠️ Do not go looking for it in site.css or coming-soon.css and "restore" it:
   .mono-eyebrow (site.css) is a shared utility and coming-soon.css has its own
   .hero-eyebrow with a halo. Both are still in use by other pages. Only the
   landing-scoped rule went.

   Side effect worth knowing: --accent no longer appears anywhere in the hero
   copy column, which leaves the download button as the single accent in the
   first screen. That is an improvement, not a gap — one accent, one action. */

/* The headline, now the FIRST voice on the page rather than the second.
   Capped at 72px, raised from 64px in round 4. The token's own ceiling (84px)
   is tuned for a short teaser line and two full sentences at that size wrap to
   a wall — but with the eyebrow gone the headline has to carry the entry on its
   own, and the copy column lost roughly four lines this round (eyebrow, two
   subhead lines, one meta line) and needed the weight back to hold its side of
   the grid against a full-height painting. 20ch keeps it to the same wrap.
   min() keeps the token consumed and the cap honest. If it reads too loud,
   this is one number: put it back to 4rem. */
.hero-heading {
  margin: 0;
  max-width: 20ch;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: min(var(--fs-hero), 4.5rem);
  line-height: 1.06;
  letter-spacing: -0.022em;
  color: var(--ink);
  text-wrap: balance;
}

/* Two-tone headline (chosen over serif-italic mixing — this is two full
   sentences, and italicising a whole sentence reads as emphasis, not accent).
   color-mix rather than var(--muted): --muted is tuned for small secondary UI
   text and goes chalky at 64px.

   ROUND 3: stepped from 52% to 64%. In the old headline the second sentence was
   explanatory and could afford to recede hard. In the privacy-play headline
   BOTH sentences are hooks — "The new tools don't need them." is the line doing
   the strategic work, the one that says you can have the tool and keep the
   name. Muting the resolution as hard as an explanation threw it away. */
.hero-heading-soft {
  color: color-mix(in srgb, var(--ink) 64%, transparent);
}

/* ROUND 4, two adjustments for the shorter string and the bigger headline:

   margin-top --space-5 (a flat 24px) -> a clamp. 24px was already snug under a
   64px headline and is too tight under a 72px one; optical spacing has to scale
   with the type it separates, and a flat token cannot. The floor keeps the
   narrow-width value exactly where it was.

   max-width 44ch -> 48ch. The headline block is ~720px at full size and a 44ch
   subhead was ~475px, so the column read as a wide slab sitting on a narrow
   one. 48ch closes that gap and the column reads as one shape. Still well
   inside the comfortable measure, and the sentence sets in ~3 lines. */
.hero-subhead {
  margin: clamp(1.5rem, 2vw, 2rem) 0 0;
  max-width: 48ch;
  font-size: clamp(1.1rem, 1rem + 0.5vw, 1.35rem);
  line-height: 1.5;
  color: var(--text);
}

/* ---- Hero CTA cluster ----------------------------------------------------
   Rebuilt here (download.css is no longer loaded on this page). One primary
   action, generous, with its build metadata directly beneath it, and the macOS
   status parked to the side behind a hairline so it reads as information
   rather than a second, weaker choice. */
.download-panel {
  margin-block-start: clamp(1.75rem, 4vw, 2.5rem);
}

/* ROUND 4: align-items was flex-start, which left the cluster with a ragged
   bottom edge — the button+meta column runs ~78px tall, the macOS column ~36px,
   and the hairline stopped short of both. Stretching makes the hairline the full
   height of the cluster and lets the macOS lines sit centred against it, so the
   two halves read as ONE bounded object at the foot of the hero instead of two
   stacks of different lengths. With the tile gone this is the copy column's
   closing edge, so it is the edge that has to be clean. */
.download-cta-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: clamp(1.25rem, 3vw, 2.25rem);
}

.download-primary {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* The one unmissable action on the page. site.css's .primary-button is
   width:100% for the app's stacked controls — overridden to an inline pill at
   landing scale. */
.landing-shell .primary-button,
.landing-below .primary-button {
  width: auto;
  padding: 0.95rem 1.75rem;
  border-radius: var(--r-pill);
  font-size: var(--fs-lg);
  box-shadow: var(--shadow-soft-1);
}
/* Bug fix: these are <a class="primary-button"> (real download links, not
   <button>s like the app uses), so site.css's global `a:hover { color:
   var(--accent-hover) }` (0,1,1 specificity) was beating .primary-button's own
   `color: var(--cta-ink)` (0,1,0) — the label silently swapped to the accent
   colour on hover, which sits close to the button's own fill in both themes
   and read as disabled. Restated here at higher specificity so the label stays
   at full strength; the fill lifting is what should read as "more committed",
   not the text dimming. Plain tab-focus (no hover) was already fine — the
   global rule only fires on :hover — but :focus-visible is restated too as
   insurance against the same collision, kept to color-only so it doesn't pick
   up the hover lift/shadow as a side effect. */
.landing-shell .primary-button:hover,
.landing-below .primary-button:hover {
  color: var(--cta-ink);
  transform: translateY(-1px);
  box-shadow: var(--shadow-soft-2);
}
.landing-shell .primary-button:focus-visible,
.landing-below .primary-button:focus-visible {
  color: var(--cta-ink);
}
.landing-shell .primary-button:active,
.landing-below .primary-button:active {
  transform: scale(0.98);
  box-shadow: var(--shadow-soft-1);
}

.download-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.01em;
  color: var(--muted);
}

.download-secondary {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-2);
  padding-inline-start: clamp(1.25rem, 3vw, 2.25rem);
  border-inline-start: 1px solid var(--ink-a16);
}

.download-secondary-status {
  margin: 0;
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--ink);
}
.status-dot { padding-inline: 0.4rem; color: var(--muted); }
.status-pending { font-weight: 500; color: var(--muted); }

@media (max-width: 560px) {
  .download-secondary {
    padding-inline-start: 0;
    padding-block-start: var(--space-4);
    border-inline-start: none;
    border-block-start: 1px solid var(--ink-a16);
    width: 100%;
  }
  .landing-shell .primary-button { width: 100%; }
}

/* ---- Download confirmation panel -----------------------------------------
   Beneath the button, inside .download-primary's own flex column — extends it
   by one row rather than reflowing the hero. Authored [hidden] in the markup;
   this is a browser that genuinely cannot tell a download started, so the
   fail-safe default is "say nothing" and JS (foot of Landing.cshtml) reveals
   it only once the button has actually been clicked. Never re-hidden: a
   second click is a no-op, not a reset.

   [hidden]'s UA-stylesheet `display: none` is normal-priority, and any
   author rule beats a normal UA rule regardless of specificity — so the base
   selector below being unconditional would win over [hidden] and defeat it.
   `:not([hidden])` is what actually gates the reveal; the base rule only
   carries the transition + the pre-reveal opacity/transform starting point.

   Styled as the success pill here — --success/--success-bg are exactly the
   "pale wash + dark ink" pair tokens.css ships for an all-clear banner (see
   its own comment). The closing-band instance overrides this to plain text
   further down (CLOSING BAND section) — a filled green pill would fight that
   band's own near-black ground. */
.download-confirm {
  display: none;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--dur-3) var(--ease-out), transform var(--dur-3) var(--ease-out);
}
.download-confirm:not([hidden]) {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}
.download-confirm.is-visible {
  opacity: 1;
  transform: none;
}

.download-primary .download-confirm {
  padding: var(--space-4);
  border-radius: var(--r-lg);
  background: var(--success-bg);
}

.download-confirm-message {
  margin: 0;
  max-width: 40ch;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--success);
}

.download-confirm-cta {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--success);
}
.download-confirm-cta:hover,
.download-confirm-cta:focus-visible {
  color: var(--ink);
}


/* =========================================================================
   4. HERO ART — the bleeding watercolour plate + the interlocking tile
   ========================================================================= */
/* The painting. Full height of the hero grid, rounded on the leading edge only,
   running off the trailing edge of the viewport. Nothing is laid on top of it. */
.ch-hero-art {
  position: relative;
  align-self: stretch;
  overflow: visible;
  margin: 0;
  min-height: clamp(20rem, 40vw, 34rem);
}

.ch-hero-art-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-start-start-radius: var(--r-2xl);
  border-end-start-radius: var(--r-2xl);
  background: var(--panel-2);
  box-shadow: var(--shadow-soft-2);
}

@media (max-width: 900px) {
  .ch-hero-grid {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    gap: clamp(2rem, 6vw, 3rem);
  }
  .ch-hero-copy {
    padding-inline: clamp(1.25rem, 4vw, 3rem);
  }
  /* Stacked: the art becomes a full-bleed band rather than a card, so it still
     reads as a window and not as another rounded rectangle in a column of
     rounded rectangles.

     The aspect-ratio moves from the figure to the FRAME here, and the frame
     returns to normal flow. This originally existed to stop the stacked tile
     spilling into the next section out of an aspect-ratio'd figure; round 4 cut
     the tile, but the arrangement stays — a figure whose only child is absolute
     has no intrinsic height, and normal flow is the correct shape for a stacked
     band regardless. */
  .ch-hero-art {
    position: relative;
    display: block;
    min-height: 0;
    aspect-ratio: auto;
  }
  .ch-hero-art-frame {
    position: relative;
    inset: auto;
    aspect-ratio: 16 / 10;
    border-radius: 0;
    box-shadow: none;
  }
}
@media (max-width: 560px) {
  .ch-hero-art-frame { aspect-ratio: 4 / 3; }
}

/* ---- The overlapping tile — REMOVED IN ROUND 4 ---------------------------
   Xurya's interlocking tile lived here: a dark card straddling the seam between
   the copy column and the painting, carrying the trust sentence.

   Cut for two reasons at once. It was the fourth statement of a single idea in
   one screen (eyebrow, headline, subhead, tile), and it was the element
   physically chopping the hero in half — the composition device had become the
   fragmentation. The sentence survives verbatim in the closing band's
   microline, so no copy was lost and none was reworded.

   .ch-hero-art keeps overflow:visible (harmless now that nothing crosses the
   boundary) and .ch-hero-art-frame keeps the clipping, so the art's asymmetric
   rounding and the load wipe are untouched. */

/* The artwork itself. Moved here from coming-soon.css unchanged in behaviour:
   two stacked layers, one per theme, crossfaded — so the theme toggle swaps the
   painting without a flash of the wrong one. */
.hero-media-stack {
  position: absolute;
  inset: 0;
}

.hero-media {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: var(--panel-2);
}

/* Token-driven gradient wash painted BEHIND the real image. A failed
   background-image url() layer simply drops out (no broken-image glyph, unlike
   <img>), so the plate still reads as a deliberate on-brand wash if the webp
   has not landed in wwwroot/img/ yet. */
.hero-media-light {
  background-image:
    url("/img/hero-light.webp"),
    radial-gradient(circle at 32% 22%, var(--accent-soft), transparent 60%),
    linear-gradient(155deg, var(--halo-1), transparent 55%),
    linear-gradient(-15deg, var(--halo-2), transparent 60%);
}
.hero-media-dark {
  background-image:
    url("/img/hero-dark.webp"),
    radial-gradient(circle at 32% 22%, var(--accent-soft), transparent 60%),
    linear-gradient(155deg, var(--halo-1), transparent 55%),
    linear-gradient(-15deg, var(--halo-2), transparent 60%);
}

.hero-media-dark { opacity: 0; transition: opacity var(--dur-3) var(--ease); }
:root[data-theme="dark"] .hero-media-light { opacity: 0; }
:root[data-theme="dark"] .hero-media-dark  { opacity: 1; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .hero-media-light { opacity: 0; }
  :root:not([data-theme="light"]) .hero-media-dark  { opacity: 1; }
}

/* Narrow viewports: the plate's crop window tightens toward portrait, which
   walks the sailboats off the right edge. Same correction coming-soon.css
   arrived at by measuring against the real image — reused, not re-derived. */
@media (max-width: 720px) {
  .hero-media-light { background-position-x: 68%; }
  .hero-media-dark  { background-position-x: 72%; }
}

/* The round-1 bento side column (a chips tile + a trust tile stacked beside a
   contained art plate) is gone with the plate it hung off. The chips already
   appear twice below — in the demo card and on the "What it does" plate — so a
   third hero instance was repetition, not reinforcement; the trust sentence
   they shared the column with is now the overlapping tile above, which is a
   stronger home for it than a stacked card was. */


/* =========================================================================
   5. SLOT 2 (the evidence) + the story band that carries slots 3 and 4
   One full-bleed --panel band. The transition into it is a hard colour cut
   against the flat --bg above, with no gradient: a soft bleed between a plate
   and a flat colour reads as an unfinished transition no matter how many stops
   you add. The contrast IS the transition.
   ========================================================================= */
.landing-below {
  position: relative;
  background: var(--bg);
}

/* ---- SLOT 2: the evidence ------------------------------------------------
   ROUND 3. The demo card used to open a full-bleed --panel band of its own,
   under an eyebrow and a heading. It now sits on the hero's own ground with no
   furniture at all, close enough that it reads as the hero's last paragraph.

   The spacing above it is the whole trick: large enough to breathe, small
   enough that it is clearly still part of the opening statement rather than the
   page's second stop. That is roughly half a section's rhythm — deliberately
   NOT var(--space-9), which is what every real section boundary uses. */
.landing-evidence {
  padding-block-start: clamp(1.5rem, 4vw, 3rem);
  padding-block-end: clamp(3.5rem, 9vw, var(--space-9));
}

/* ---- SLOTS 3 + 4: one band ----------------------------------------------
   "The moment" and "How it works" share this ground on purpose — the turn line
   at the end of the moment describes a sequence, and how-it-works IS that
   sequence. See Landing.cshtml for the full reasoning. The hard colour cut
   against the flat --bg above is the transition; no gradient, because a soft
   bleed between a plate and a flat colour reads as unfinished no matter how
   many stops you add. */
.landing-story-band {
  background: var(--panel);
  padding-block: clamp(3.5rem, 9vw, var(--space-9));
}

.landing-story-inner {
  display: flex;
  flex-direction: column;
  gap: clamp(3.5rem, 8vw, 6rem);
}

/* ---- The before/after demo ----------------------------------------------
   The strongest asset on the page and the "moment of value": a reader sees the
   product work before being asked to believe anything. Built in HTML/CSS, not
   a screenshot — theme-aware for free, no binary asset, and zero risk of
   anything resembling real patient data landing on a public page. The content
   is obviously synthetic on purpose. */
.landing-demo {
  margin: 0;
}

/* ROUND 3 — ground flipped from --bg to --panel. The card used to sit inside a
   --panel band and was tinted --bg to read as a recessed plate. Now that it sits
   directly on the hero's --bg ground, that same value made the card and the page
   the identical colour: an invisible card with a shadow around it. It lifts off
   the page instead, which is also the correct read now that it is evidence
   attached to the hero rather than the contents of a section. */
.landing-demo-card {
  border-radius: var(--r-2xl);
  background: var(--panel);
  box-shadow: var(--shadow-soft-2);
  overflow: hidden;
}

.landing-demo-panes {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.landing-demo-pane {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: clamp(1.5rem, 3.5vw, 2.5rem);
}

/* The seam between the two states — a single hairline, not a border on each
   pane, so the card reads as one object showing two moments. */
.landing-demo-panes .landing-demo-pane + .landing-demo-pane {
  border-inline-start: 1px solid var(--ink-a08);
}

@media (max-width: 760px) {
  .landing-demo-panes { grid-template-columns: minmax(0, 1fr); }
  .landing-demo-panes .landing-demo-pane + .landing-demo-pane {
    border-inline-start: none;
    border-block-start: 1px solid var(--ink-a08);
  }
}

.landing-demo-label {
  margin: 0;
}
/* The "After" label carries the accent — the only place on the demo card where
   colour is used to say "this is the outcome". */
.landing-demo-pane + .landing-demo-pane .landing-demo-label { color: var(--accent); }

.landing-demo-note {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: clamp(1rem, 2.5vw, 1.4rem);
  border-radius: var(--r);
  background: var(--panel-2);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--text);
}

.demo-line { display: block; }

/* The "before" state: identifiers marked as exposed. A warm underline rather
   than a highlight block — a highlight would compete with the chips opposite,
   which are the thing the eye is meant to end on. */
.demo-sensitive {
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 0 -0.55em 0 color-mix(in srgb, var(--danger-ink) 18%, transparent);
}


/* =========================================================================
   6. SLOT 3 — "THE MOMENT"
   =========================================================================
   The narrative core, and the only section on the page built purely out of
   type. No card, no plate, no art — the tension is in the words and putting a
   graphic beside them would give the eye somewhere else to go at the exact
   moment it should be reading.

   The design idea is the size difference between the two paragraphs. The body
   sets up a problem the reader already has, at reading size; the turn resolves
   it at display scale. Marketing called the turn billboard copy, so it is set
   like a billboard.
   ========================================================================= */
.landing-moment {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: 62rem;
}

.landing-moment-eyebrow {
  margin: 0;
  color: var(--accent);
}

.landing-moment-body {
  margin: 0;
  max-width: 54ch;
  font-size: clamp(1.1rem, 1rem + 0.45vw, 1.35rem);
  line-height: 1.6;
  color: var(--text);
}

/* The turn. Display serif at section scale, and the only place on the page
   where a paragraph is set as large as a heading — because it is one, in
   everything but markup. It stays a <p> so the document outline still reads
   hero → moment → how it works → what stays, without a heading that is really
   a sentence wedged into it. */
.landing-moment-turn {
  margin: 0;
  max-width: 22ch;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.75rem, 1.1rem + 2.2vw, 2.85rem);
  line-height: 1.14;
  letter-spacing: -0.018em;
  color: var(--ink);
  text-wrap: balance;
}

@media (max-width: 860px) {
  .landing-moment-turn { max-width: none; }
}



/* =========================================================================
   7. SHARED SECTION TYPE
   ========================================================================= */
.landing-section {
  padding-block: clamp(3.5rem, 9vw, var(--space-9));
}
/* Sections that live inside the proof band get their rhythm from the band. */
/* (Nothing inside the story band carries .landing-section — the band's own flex
   gap sets the rhythm for both sections it holds. The guard rule that used to
   zero their padding is removed rather than left as a defensive no-op.) */

.landing-section-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--fs-section);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}

/* .landing-body is gone with the "What it does" section that was its only user.
   Every remaining body-copy style on this page is section-specific
   (.landing-moment-body, .landing-step-text, .landing-stays-list li,
   .landing-honest p) because each one is set at a deliberately different size —
   a shared body class would have flattened exactly the type hierarchy this
   round is building. */


/* =========================================================================
   8. SLOT 4 — "HOW IT WORKS", now three ROWS
   =========================================================================
   ROUND 3: this was three equal columns with oversized numerals. The revised
   copy broke that layout, and not marginally — step 1 is 33 characters while
   steps 2 and 3 are around 150 each. Three equal columns would have produced
   one nearly-empty column beside two walls of text, which is exactly the
   ragged, chopped-up read this round exists to kill.

   Rows absorb uneven length by construction, give the art vignettes somewhere
   to sit at real size, and read as a sequence rather than a comparison — which
   is what a numbered list of steps actually is. It also lets the section share
   a band with "The moment" above without the two reading as one soup: the rows
   have their own internal rhythm.
   ========================================================================= */
.landing-how {
  display: flex;
  flex-direction: column;
  gap: clamp(1.75rem, 4vw, 2.75rem);
}

.landing-how-eyebrow {
  margin: 0;
  color: var(--accent);
}

.landing-steps {
  margin: 0;
  padding: 0;
  list-style: none;
}

.landing-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-block: clamp(1.5rem, 3vw, 2.25rem);
  border-block-start: 1px solid var(--ink-a08);
}
.landing-step:first-child { border-block-start: none; padding-block-start: 0; }
.landing-step:last-child { padding-block-end: 0; }

/* The rail: artwork and numeral together, at a fixed width so all three rows'
   text starts on the same vertical line no matter how long the sentence is. */
.landing-step-rail {
  display: flex;
  align-items: center;
  gap: clamp(0.6rem, 1.4vw, 1rem);
}

.landing-step-num {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.5rem, 1.6rem + 2.6vw, 4rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  color: var(--ink-a16);
}

.landing-step-text {
  margin: 0;
  max-width: 54ch;
  font-size: clamp(1.05rem, 1rem + 0.25vw, 1.2rem);
  line-height: var(--lh);
  color: var(--ink);
}

@media (max-width: 620px) {
  .landing-step {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
  }
}


/* =========================================================================
   9. SLOT 5 — "WHAT STAYS ON YOUR COMPUTER"
   =========================================================================
   Replaces "What to expect". Round 2 put three dense card paragraphs here and
   that is where most of the "verbose" complaint actually lived; two of the
   three are cut by the deck and the survivor is reframed.

   Four scannable rows, then the honest line on the dark plate. The plate is
   kept from round 2's .ch-card-c — same device, better tenant: instead of a
   third reassurance paragraph it now carries the one line that names the
   product's limit, which is simultaneously the biggest legal exposure in this
   pivot and the thing that makes everything above it believable.
   ========================================================================= */
.landing-stays {
  background: var(--bg);
}

.landing-stays-inner {
  display: flex;
  flex-direction: column;
  gap: clamp(2rem, 5vw, 3rem);
}

.landing-stays-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1.5rem, 4vw, 3rem);
}

.landing-stays-headings {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.landing-stays-headings .mono-eyebrow { margin: 0; color: var(--accent); }

@media (max-width: 620px) {
  .landing-stays-head .ch-art-stays { display: none; }
}

/* Four rows, not four cards. A rule above each line rather than a box around
   it: the deck asked for short items, and boxing four one-liners would have
   rebuilt the grid of paragraphs this section was created to replace. */
.landing-stays-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 clamp(1.5rem, 4vw, 3.5rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.landing-stays-list li {
  padding-block: clamp(1rem, 2.2vw, 1.5rem);
  border-block-start: 1px solid var(--ink-a16);
  font-size: clamp(1rem, 0.97rem + 0.2vw, 1.1rem);
  line-height: var(--lh);
  color: var(--ink);
}

/* REMOVED (Revision 9 accuracy fold-in): li:last-child { grid-column: 1 / -1; }
   That rule existed only because the ACCOUNTS PASS cut left three items in a
   two-column grid — an odd count leaves an empty cell beside the last row, so
   the third item was stretched full-width to close the gap. Row 4 is back
   (see Landing.cshtml), and with four items the grid fills both columns
   evenly on its own; the stretch rule would now wrongly pull row 4 full-width
   again for no layout reason. Pure layout fix, no copy involved either time. */

@media (max-width: 720px) {
  .landing-stays-list { grid-template-columns: minmax(0, 1fr); }
}

/* Itemisation disclosure — sits between the four-row list and the honest
   plate below it. Uses the shared .trust-disclosure component (site.css);
   this is just the spacing/measure that fits it into the section's own
   rhythm rather than the component's generic defaults. */
.landing-stays-disclosure {
  margin: clamp(0.25rem, 1vw, 0.75rem) 0 clamp(1.5rem, 3vw, 2.25rem);
}
.landing-stays-disclosure .trust-disclosure-body p {
  max-width: 62ch;
}

/* The honest line, on the dark plate. Set larger than the rows above it — it is
   a statement, not a fifth item. */
.landing-honest {
  padding: clamp(1.75rem, 4vw, 2.75rem);
  border-radius: var(--r-2xl);
  background: var(--band);
  box-shadow: var(--shadow-soft-2);
}
.landing-honest p {
  margin: 0;
  max-width: 62ch;
  font-size: clamp(1.1rem, 1rem + 0.5vw, 1.35rem);
  line-height: var(--lh);
  color: var(--band-ink);
}

/* Same dark-theme collision the round-2 plates had: --band and --panel resolve
   to the same value in dark theme. This plate sits on --bg rather than --panel
   so it survives, but it steps to --panel-3 anyway to stay the emphasised
   surface rather than merely a different one. */
:root[data-theme="dark"] .landing-honest { background: var(--panel-3); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .landing-honest { background: var(--panel-3); }
}


/* =========================================================================
   10. CLOSING BAND — the dark foot
   Full bleed, --band in both themes (see tokens.css for why this is not
   --accent). One repeated CTA, nothing else competing.

   ROUND 3 — the art slot below went from a rounded card in a two-column
   split to the section's own full-bleed background (see the note on
   .ch-art-closing). position:relative + z-index stacking here is what makes
   that legible: .ch-art-closing paints the section edge to edge (z-index 0),
   ::before scrims the left edge where the copy sits (z-index 1), and .ch-wrap
   carries the real content above both (z-index 2, set below). */
.landing-closing {
  position: relative;
  background: var(--band);
  padding-block: clamp(3.5rem, 8vw, 6rem);
  overflow: hidden;
}

/* The band's own colour, left to right, fading over the painting. Andrew's
   call: the rounded card cropped a wide horizontal composition into a
   portrait-ish box, losing the sky and compressing the horizon. Full bleed
   fixes the crop but puts pale watercolour behind the headline/button/meta —
   so the copy column gets a solid-to-transparent scrim of the band's own
   colour, strong enough to be indistinguishable from flat --band anywhere
   text can land, fading out once the measure is clear.

   0%–40% is fully opaque: at every width this section is still shown at
   (see the max-width:860px cutover below), the copy column — bound by
   .landing-closing-inner's max-width:34ch, ~300px at the fixed 16px root —
   never exceeds ~37% of the section's width (worst case is the narrowest
   kept width, 900px: (36px padding + 300px)/900px ≈ 37.3%). 40% carries a
   margin over that measured worst case, so this is flat --band under the
   copy, not a partial blend of it — the same 13.1:1 / 7.46:1 (light) and
   13.13:1 / 5.07:1 (dark) the band already had before there was a painting
   behind it, unconditionally, because it's opaque rather than blended.
   40–66% fades so the boat (roughly the frame's 47–72%) reads through a
   morning-mist veil rather than vanishing under a hard edge; clear by 78%. */
.landing-closing::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to right,
    var(--band) 0%,
    var(--band) 40%,
    color-mix(in srgb, var(--band) 45%, transparent) 66%,
    transparent 78%
  );
}

.landing-closing > .ch-wrap {
  position: relative;
  z-index: 2;
}

.landing-closing-grid {
  display: flex;
  align-items: center;
}

.landing-closing-inner {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-5);
  max-width: 34ch;
}

/* =========================================================================
   ART SLOTS — a component with a deliberate empty state
   =========================================================================
   None of the commissioned artwork exists yet (see docs/design/landing-art-prompts.md).
   So every slot is built to look INTENTIONAL while empty, not like a broken
   image: the container paints a soft on-brand watercolour wash from tokens, and
   the artwork is layered on top as a background-image. A missing url() layer
   simply drops out — no broken-image glyph, unlike <img> — leaving the wash.

   That means Andrew can drop the .webp files into wwwroot/img/ and they light
   up with ZERO code changes. No wiring pass, no follow-up ticket.
   ========================================================================= */
.ch-art {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(62% 62% at 50% 42%, var(--accent-soft), transparent 68%),
    radial-gradient(45% 45% at 68% 78%, var(--halo-2), transparent 70%);
}

.ch-art-layer {
  position: absolute;
  inset: 0;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
}

/* Same theme crossfade the hero artwork uses — one layer per theme, so the
   toggle swaps the art without a flash of the wrong one. */
.ch-art-dark { opacity: 0; transition: opacity var(--dur-3) var(--ease); }
:root[data-theme="dark"] .ch-art-light { opacity: 0; }
:root[data-theme="dark"] .ch-art-dark  { opacity: 1; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ch-art-light { opacity: 0; }
  :root:not([data-theme="light"]) .ch-art-dark  { opacity: 1; }
}

/* ---- Slot: closing band (wide) ----------------------------------------
   ROUND 3 — full-bleed background rather than a right-half card. A sibling
   of .ch-wrap in the markup (not a grid child of it), so it isn't clipped to
   the 1200px measure: inset:0 on the section itself, edge to edge of the
   viewport. No background-colour of its own any more — the empty/failed-
   image state was a wash painted from tokens while no art existed yet; now
   the art is real, and the honest fallback if a .webp 404s is the section's
   own solid --band showing through this (now transparent) layer, not a
   separate placeholder that would only ever be seen on a broken image. */
.ch-art-closing {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.ch-art-closing .ch-art-layer {
  background-size: cover;
  /* 65% 30%: the horizon/boat sit right-of-centre and slightly low in
     frame, leaving calm dark sky — already within a couple of RGB levels of
     --band itself (see the prompt file) — under the scrim and the copy. */
  background-position: 65% 30%;
}
.ch-art-closing .ch-art-light { background-image: url("/img/closing-band-light.webp"); }
.ch-art-closing .ch-art-dark  { background-image: url("/img/closing-band-dark.webp"); }

/* Below ~860px the copy column stops being a minority share of the section
   width (a 34ch/~300px column is already 82% of a 390px viewport) and no
   position/scrim can hold: the painting would sit directly behind the type
   with no calm margin left to fade into. Andrew's call — drop to the flat
   --band wash rather than force it; a mobile reader over a pale horizon is
   worse than no painting. Reuses this section's own pre-existing 860px
   single-column cutover rather than inventing a new one. */
@media (max-width: 860px) {
  .ch-art-closing { display: none; }
}

/* ---- Slot: how-it-works step vignettes -------------------------------- */
/* These sit on open paper, so no plate behind them — just a very soft bloom as
   the empty state. With no artwork it reads as a stray watercolour mark, which
   is on-style; with artwork it is invisible underneath it. */
.ch-art-step {
  flex: 0 0 auto;
  width: clamp(3.5rem, 6vw, 5rem);
  aspect-ratio: 1;
  background: radial-gradient(58% 58% at 45% 45%, var(--halo-1), transparent 70%);
}
.ch-art-step1 .ch-art-light { background-image: url("/img/how-it-works-step1-light.webp"); }
.ch-art-step1 .ch-art-dark  { background-image: url("/img/how-it-works-step1-dark.webp"); }
.ch-art-step2 .ch-art-light { background-image: url("/img/how-it-works-step2-light.webp"); }
.ch-art-step2 .ch-art-dark  { background-image: url("/img/how-it-works-step2-dark.webp"); }
.ch-art-step3 .ch-art-light { background-image: url("/img/how-it-works-step3-light.webp"); }
.ch-art-step3 .ch-art-dark  { background-image: url("/img/how-it-works-step3-dark.webp"); }

/* ---- Slot: "what stays on your computer" spot mark ---------------------
   ROUND 3: renamed from .ch-art-expect, and the MOTIF changed with it — the
   section it sits beside is no longer "what to expect". Filenames changed to
   match. Nothing has been generated yet, so this rename costs nothing; see
   docs/design/landing-art-prompts.md for the new prompt. */
.ch-art-stays {
  flex: 0 0 auto;
  width: clamp(4.5rem, 8vw, 6.5rem);
  aspect-ratio: 3 / 4;
  background: radial-gradient(52% 46% at 50% 42%, var(--halo-1), transparent 72%);
}
.ch-art-stays .ch-art-light { background-image: url("/img/stays-on-computer-mark-light.webp"); }
.ch-art-stays .ch-art-dark  { background-image: url("/img/stays-on-computer-mark-dark.webp"); }


.landing-closing-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--fs-section);
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--band-ink);
  text-wrap: balance;
}

/* Inverted button: --cta-bg is a near-black in light theme and would vanish on
   the band. Painted from the band's own ink/ground pair so it inverts correctly
   in both themes without a dark-mode counterpart rule. */
.landing-closing .primary-button {
  margin-block-start: var(--space-2);
  background: var(--band-ink);
  color: var(--band);
}
.landing-closing .primary-button:hover {
  background: color-mix(in srgb, var(--band-ink) 88%, var(--band));
}
.landing-closing .primary-button:active {
  background: color-mix(in srgb, var(--band-ink) 78%, var(--band));
}

.landing-closing-meta {
  max-width: 46ch;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: 1.6;
  letter-spacing: 0;
  color: var(--band-muted);
}

/* The closing instance of .download-confirm — same markup, same copy, but
   this band already speaks in plain --band-ink/--band-muted text (see
   .landing-closing-meta above), not a filled pill. --success/--success-bg
   are tuned for light paper; a pale green pill on this near-black ground
   would fight the language every other line in this band already uses. */
.landing-closing .download-confirm {
  padding: 0;
  border-radius: 0;
  background: none;
}
.landing-closing .download-confirm-message {
  color: var(--band-muted);
}
.landing-closing .download-confirm-cta {
  color: var(--band-ink);
}
.landing-closing .download-confirm-cta:hover,
.landing-closing .download-confirm-cta:focus-visible {
  color: var(--band-muted);
}


/* =========================================================================
   11. FOOTER — the dark foot continues, signed off with the wordmark
   The closing band and the footer share one ground on purpose, so the page ends
   on a single confident dark block rather than a dark band followed by a
   separate pale strip.
   ========================================================================= */
.landing-footer {
  background: var(--band);
  border-block-start: 1px solid var(--band-line);
  padding-block: clamp(2.5rem, 6vw, var(--space-8));
  margin: 0;
}

.landing-footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 3vw, 2.5rem);
  padding-block-end: clamp(2rem, 5vw, 3rem);
}

.landing-footer-nav a {
  font-size: var(--fs-base);
  font-weight: 500;
  color: var(--band-muted);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}
.landing-footer-nav a:hover { color: var(--band-ink); text-decoration: none; }

/* The sign-off wordmark — Ascone's device, brought down to size in round 4.

   It was clamp(3.5rem, 1rem + 11vw, 10rem): 160px on a laptop, filling ~80% of
   the 1200px measure. That scale works on the fintech reference because the
   wordmark IS the payoff of that page; here it followed a closing band that had
   already made the ask, so it read as shouting the name after the conversation
   had ended.

   The new scale is pinned by RELATION, not by taste: the ceiling sits about 1.2x
   --fs-section (the closing title directly above it). Above the headings, so it
   still reads as a mark rather than one more heading — but nowhere near the
   hero, so it cannot compete with the thing being said. It now occupies roughly
   a third of the measure instead of four fifths.

   Tracking relaxed from -0.035em to -0.02em with the size drop: tight tracking
   reads as confidence at 160px and as cramped at 60px. Floor is 2.25rem so
   "ClearHarbor" still sets on one line inside the gutters at 320px — it has
   never wrapped and it must not start now. aria-hidden stands: the nav's brand
   announces the name once per page and this is a decorative repeat. */
.landing-footer-wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.25rem, 1.5rem + 3vw, 3.75rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--band-ink);
  user-select: none;
}
/* 45% -> 55%, matching the nav's own over-dark suffix exactly. 45% was tuned
   for a 160px mark, where a very light second word still has plenty of ink on
   the page; at 60px it went chalky and "Harbor" started to disappear against
   the band in light theme's dark footer. One value for the suffix everywhere on
   dark ground is also simply correct. */
.landing-footer-wordmark .brand-suffix {
  color: color-mix(in srgb, var(--band-ink) 55%, transparent);
}

/* ROUND 4: padding-top cut from clamp(2rem,5vw,3rem) to roughly half. The old
   value was balancing a 160px wordmark; against a 60px one it left the mark
   stranded in the middle of the footer with a large void on either side. Pulling
   the rule up under it makes wordmark + hairline + copyright read as one
   signature block, and leaves the generous air where it belongs — between the
   link row above and the signature. */
.landing-footer .site-footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  max-width: none;
  margin: 0;
  padding: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
  border-block-start: 1px solid var(--band-line);
}

.landing-footer .site-footer-copy { color: var(--band-muted); font-size: var(--fs-sm); }
/* The .site-footer-links overrides are gone with the duplicate footer nav the
   deck cut — Privacy/Terms appeared twice, forty pixels apart. The full link set
   lives in .landing-footer-nav above; this row is the copyright only. */


/* =========================================================================
   12. MOTION
   Every effect below is one-time, additive, and gated behind html.js-anim —
   which Landing.cshtml stamps synchronously before first paint. With JS blocked
   the class never lands, so no starting state is ever applied and every element
   renders at full opacity from the start. Reduced motion needs no branch here:
   site.css's global "*, *::before, *::after" override forces every duration to
   0.01ms. Reimplemented by hand in plain CSS — no library, no framework.
   ========================================================================= */

/* Scroll-in reveal. One IntersectionObserver in Landing.cshtml drives this and
   every effect below it off the same .is-visible class. */
html.js-anim .landing-reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-4) var(--ease-out), transform var(--dur-4) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms);
}
html.js-anim .landing-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ---- Line reveal on headlines -------------------------------------------
   ⚠️ BUG FIXED HERE — read before changing this back.

   These spans used to be handed to the IntersectionObserver directly, exactly
   like .landing-reveal. That shipped FIVE INVISIBLE HEADINGS: the hero h1, all
   three section titles and the closing title never appeared at all.

   The cause is a genuine chicken-and-egg, not a typo. An observer's intersection
   rectangle is computed AFTER the target's own clip is applied, so an element
   hiding itself with `clip-path: inset(0 100% 0 0)` has a zero-area
   intersection box — intersectionRatio is permanently 0, the 0.15 threshold can
   never be met, `is-visible` is never added, and the clip is never released.
   The element hides itself from the very observer that would reveal it.
   .landing-reveal survived because opacity:0 does not shrink the box.

   THE RULE THIS ESTABLISHES: never observe an element that clips itself. The
   trigger goes on an un-clipped ancestor and the wipe cascades down from it.
   Landing.cshtml's watchdog is the second belt — see it for why one is not
   enough. */
html.js-anim .landing-reveal .reveal-line {
  display: block;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 700ms var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms);
}
html.js-anim .landing-reveal.is-visible .reveal-line {
  clip-path: inset(0 0 0 0);
}

/* The art plate sweeps open on load — a wipe from the bottom plus a slow settle
   out of a slight over-scale, so the painting arrives rather than appearing.
   Not scroll-linked: it is in the first viewport and plays once.

   The wipe runs on .hero-media-stack, NOT on .ch-hero-art-frame. clip-path
   replaces an element's own visual rounding while it is applied, so animating
   the frame would square off its rounded leading edge for the whole wipe and
   snap it back at the end. Clipping the stack INSIDE the frame leaves the
   frame's overflow:hidden to do the rounding, which it does for free and at
   whatever asymmetric radius the breakpoint happens to be using. */
html.js-anim .ch-hero-art-frame .hero-media-stack {
  animation: ch-art-in 1400ms var(--ease-out) 200ms both;
}
@keyframes ch-art-in {
  from { clip-path: inset(0 0 100% 0); transform: scale(1.06); }
  to   { clip-path: inset(0 0 0 0);    transform: scale(1); }
}

/* ROUND 4: the tile's staggered arrival (ch-tile-in) went with the tile. The
   painting's wipe above is now the hero's only entrance, which is the right
   number for a screen meant to read as one statement. */

/* The demo's payoff: each [TOKEN] chip wipes in left-to-right, staggered, the
   moment the card scrolls into view. This is the one animation on the page that
   is doing marketing work rather than polish — it is the product's whole value
   proposition, animated. */
html.js-anim .landing-demo .redaction-chip {
  clip-path: inset(0 100% 0 0);
}
html.js-anim .landing-demo.is-visible .redaction-chip {
  animation: demo-chip-sweep 520ms var(--ease-out) both;
  animation-delay: calc(240ms + var(--i, 0) * 140ms);
}
@keyframes demo-chip-sweep {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Oversized numerals rise as their section arrives. */
html.js-anim .landing-how .landing-step-num {
  opacity: 0;
}
/* The stagger now comes from the row's position rather than an inline --i,
   because the rows are a fixed set of three and :nth-child is one less thing
   for the markup to carry. */
html.js-anim .landing-how.is-visible .landing-step-num {
  animation: step-num-rise 700ms var(--ease-out) both;
}
html.js-anim .landing-how.is-visible .landing-step:nth-child(2) .landing-step-num { animation-delay: 120ms; }
html.js-anim .landing-how.is-visible .landing-step:nth-child(3) .landing-step-num { animation-delay: 240ms; }
@keyframes step-num-rise {
  from { opacity: 0; transform: translateY(0.25em); }
  to   { opacity: 1; transform: none; }
}

/* Cross-document crossfade on navigation to /download, /privacy, /terms and
   /getting-started. Six lines, zero JS; unsupported browsers simply navigate.
   Wrapped in prefers-reduced-motion directly because site.css's global override
   targets element transitions, not this at-rule. */
@media not (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: auto;
  }
  ::view-transition-old(root), ::view-transition-new(root) {
    animation-duration: 240ms;
    animation-timing-function: ease-out;
  }
}
