/* =============================================================================
   The three-step lead funnel  ::  LL-F129.T05, amended by T09 (ADR-0094),
   light-locked by LL-F130.T01
   -----------------------------------------------------------------------------
   HAND-WRITTEN. No build step, no preprocessor, no bundler: this file is the
   deployed artefact, exactly as it sits on disk.

   VERSIONED FILENAME (.v3.css, bumped from .v2 by LL-F130.T01), and a SEPARATE
   file from site.v2.css on purpose. site.v2.css is the design system and is
   cached immutably under its own key; a funnel edit must not force a new key
   for it. It introduces no second palette, no second radius system and no
   second accent: every colour, radius, measure and font below is one of
   site.v2.css's :root custom properties, so the funnel inherits the one coral
   accent and the 20px/pill radius pair for free.

   ★ ONE SCHEME. The site is LIGHT-ONLY (LL-F130.T01): site.v2.css's header
   explains why, and this file no longer carries a dark override of its own.

   ONE TOKEN IS DECLARED HERE, AND IT IS AN ACCESSIBILITY FIX.
   --ll-control-line, on .funnel in section 3. site.v2.css's --ll-rule is
   rgba(26,26,26,0.14), which composites to #DFDFDF on white and measures
   1.33:1 - right for a DECORATIVE hairline between paragraphs, and not enough
   for the BOUNDARY OF A FORM CONTROL. WCAG 1.4.11 asks 3:1 of "visual
   information required to identify user interface components and states", and
   the edge of a text field, a checkbox, an age chip and a season card is
   exactly that. So the funnel's interactive boundaries carry their own token,
   measured 3.79:1 on paper / 3.66:1 on the input ground / 3.06:1 on the
   selected tint. The decorative hairlines - the shell, the plates, the rule
   under the step counter - still use --ll-rule. The two tokens are NOT
   interchangeable and neither is an alias of the other.

   ACCENT DISCIPLINE CARRIES OVER. --ll-accent (#F5546A) measures 3.30:1
   against white and therefore never carries text here either.
   It appears as the binding keyline on the notebook-family plates, the
   selected-option rule and the error marker glyph. The selected state of an
   option is NEVER colour alone: it also gains a 2px ink border and a check
   marker. The one place the coral is deliberately NOT used is the error alert's
   keyline: coral on the cream plate measures 2.96:1, under the 3:1 bar, and an
   ink edge is the more serious signal for the surface that says something went
   wrong.
   ============================================================================= */


/* -----------------------------------------------------------------------------
   1. UTILITIES
   ----------------------------------------------------------------------------- */

/* The standard off-canvas-but-announced pattern, for the live region. Not
   display:none, which removes it from the accessibility tree and takes the
   announcement with it. */
.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* -----------------------------------------------------------------------------
   2. ★ THE HONEYPOT'S HIDING TECHNIQUE
   -----------------------------------------------------------------------------
   Three requirements pull against each other and this rule is the resolution:

     1. A HUMAN MUST NEVER SEE IT OR REACH IT. Clipped to a 1px box with
        clip-path, out of flow, out of the tab order (tabindex="-1" in the
        markup) and out of the accessibility tree (aria-hidden on the wrapper).
     2. A BOT MUST NOT CHEAPLY DETECT IT. Which is why this is NOT
        `display: none`, NOT `visibility: hidden`, NOT `type="hidden"` and NOT
        `opacity: 0` on its own: those are the four checks a form-stuffing
        script can run in one line each. A clipped, positioned, 1px element
        with a real computed display is the expensive case to detect.
     3. IT MUST NOT DISTURB THE LAYOUT. position: absolute against the
        positioned .funnel (section 3), so it contributes nothing to flow and
        cannot introduce a scrollable overflow at any width. Verified at 320,
        390, 768 and 1440.
   ----------------------------------------------------------------------------- */

.ll-hp {
  position: absolute;
  inset-inline-start: -9999px;
  inset-block-start: auto;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  pointer-events: none;
}


/* -----------------------------------------------------------------------------
   3. THE FUNNEL SHELL
   -----------------------------------------------------------------------------
   An inset sheet on the notebook plate: one shade lighter than the cream it
   sits on, so the funnel reads as a card laid on the page rather than as a
   form bolted to the bottom of a marketing section.
   ----------------------------------------------------------------------------- */

.funnel {
  /* ★ The interactive-boundary token. See the accessibility note at the top of
     this file: --ll-rule is a decorative hairline at 1.49:1 and cannot carry a
     control's edge. Measured 3.79:1 on --ll-paper and 3.66:1 on --ll-ground. */
  --ll-control-line: #89827C;

  position: relative;          /* the honeypot's containing block */
  background: var(--ll-paper);
  border: 1px solid var(--ll-rule);
  border-radius: var(--ll-radius-plate);
  padding: clamp(1.5rem, 4vw, 2.5rem);
}

.funnel__count {
  font-family: var(--ll-ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ll-ink-soft);
  padding-block-end: 1.25rem;
  margin-block-end: 1.5rem;
  border-block-end: 1px solid var(--ll-rule-faint);
}

.funnel__head {
  font-family: var(--ll-display);
  font-weight: 400;
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem);
  line-height: 1.1;
  letter-spacing: -0.012em;
  margin: 0;
  padding-block-end: 0.06em;   /* descender clearance */
}

/* ★ Step 3 is the hook, and it is the one place on this page that gets the
   display italic at a size the headline would recognise. Everything else in
   the funnel is quieter than this on purpose. */
.funnel__question {
  font-family: var(--ll-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.625rem, 1.1rem + 2.2vw, 2.625rem);
  line-height: 1.22;
  letter-spacing: -0.008em;
  margin: 0;
  padding-block-end: 0.1em;    /* the italic y, g and j all dip here */
  max-width: 26ch;
}

.funnel__lede {
  margin-block-start: 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ll-ink-soft);
  max-width: 40ch;
}

.funnel__form {
  margin-block-start: 1.75rem;
}

.funnel__actions {
  margin-block-start: clamp(1.5rem, 3vw, 2rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
}

/* The Back control: a third treatment, quieter than site.v2.css's secondary,
   because it must never compete with Continue for the eye. Still a 54px
   target, still contrast-measured (ink-soft on paper, 5.81:1). */
.btn--ghost {
  background: transparent;
  color: var(--ll-ink-soft);
  border-color: transparent;
  padding-inline: clamp(0.75rem, 3vw, 1rem);
}

.btn--ghost:hover {
  color: var(--ll-ink);
  border-color: var(--ll-rule);
}

.btn--ghost:active {
  transform: translateY(1px);
}

/* The pending state of the submit control. `disabled` is the real mechanism
   (the attribute, not a class) and this is only what it looks like. */
.btn[disabled] {
  cursor: default;
  opacity: 1;                  /* no faded label: contrast must survive */
  background: var(--ll-ink-soft);
  border-color: transparent;
  color: var(--ll-on-ink);     /* paper on #6B6460 = 5.81:1 */
  transform: none;
}

.btn--ghost[disabled] {
  background: transparent;
  color: var(--ll-ink-soft);
}

.funnel__noscript {
  margin-block-start: 1.5rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ll-ink-soft);
  border-inline-start: 2px solid var(--ll-accent);
  padding-inline-start: 1rem;
  max-width: 44ch;
}

.funnel__noscript a {
  color: var(--ll-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}


/* -----------------------------------------------------------------------------
   4. TEXT FIELDS
   -----------------------------------------------------------------------------
   Label ABOVE the input, always. No placeholder-as-label anywhere: a
   placeholder disappears the moment it is needed and is announced
   inconsistently. There are no placeholders in this funnel at all.
   ----------------------------------------------------------------------------- */

.field + .field {
  margin-block-start: 1.25rem;
}

.field__label {
  display: block;
  font-family: var(--ll-ui);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ll-ink);
  margin-block-end: 0.5rem;
}

.field__input {
  display: block;
  inline-size: 100%;
  min-block-size: var(--ll-tap);
  padding: 0.625rem 0.875rem;
  font-family: var(--ll-ui);
  font-size: 1rem;             /* 16px: anything smaller makes iOS Safari zoom
                                  the viewport on focus */
  line-height: 1.4;
  color: var(--ll-ink);
  background: var(--ll-ground);
  border: 1px solid var(--ll-control-line);
  border-radius: 10px;
  appearance: none;
  transition: border-color 160ms ease;
}

.field__input:hover {
  border-color: var(--ll-ink-soft);
}

.field__input:focus {
  border-color: var(--ll-ink);
}

/* The error state is NEVER colour alone: the border thickens to 2px and the
   message below carries a leading marker glyph. */
.field__input[aria-invalid='true'] {
  border-width: 2px;
  border-color: var(--ll-ink);
}

.field__error {
  margin-block-start: 0.5rem;
  font-family: var(--ll-ui);
  font-size: 0.8125rem;
  line-height: 1.5;
  font-weight: 500;
  color: var(--ll-ink);        /* ink on paper, 17.40:1. Error text is not red
                                  on white here: that pair measures badly and
                                  carries the meaning in hue alone. */
  display: flex;
  gap: 0.4375rem;
}

.field__error::before {
  content: '\2715';            /* multiplication X, not an emoji and not an
                                  icon request */
  color: var(--ll-accent);
  font-weight: 600;
  flex: none;
}

.field__error--consent {
  margin-block-start: 0.625rem;
}


/* -----------------------------------------------------------------------------
   5. THE CONSENT ROW
   ----------------------------------------------------------------------------- */

.consent {
  margin-block-start: 1.5rem;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
}

/* A real checkbox, restyled rather than replaced: appearance:none keeps the
   element, its focus behaviour, its Space activation and its announcement, and
   only takes the native paint away. */
.consent__box {
  appearance: none;
  flex: none;
  inline-size: 1.375rem;
  block-size: 1.375rem;
  margin: 0;
  /* Optical alignment with the first line of the label, which is 0.9375rem at
     1.6 line-height. */
  margin-block-start: 0.125rem;
  border: 1px solid var(--ll-ink-soft);
  border-radius: 6px;
  background: var(--ll-ground);
  cursor: pointer;
  position: relative;
  transition: background-color 140ms ease, border-color 140ms ease;
}

.consent__box:checked {
  background: var(--ll-ink);
  border-color: var(--ll-ink);
}

.consent__box:checked::after {
  content: '';
  position: absolute;
  inset-inline-start: 0.3125rem;
  inset-block-start: 0.1875rem;
  inline-size: 0.4375rem;
  block-size: 0.6875rem;
  border-inline-end: 2px solid var(--ll-on-ink);
  border-block-end: 2px solid var(--ll-on-ink);
  transform: rotate(40deg);
}

/* The tap target is the 22px box plus the label, which together clear 44px of
   height; the label is clickable because of its `for`. */
.consent__label {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ll-ink);
  cursor: pointer;
}

.consent__label a {
  color: var(--ll-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
}

.consent__note {
  margin-block-start: 0.875rem;
  padding-inline-start: 2.125rem;   /* aligns under the label, not the box */
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ll-ink-soft);
  max-width: 48ch;
}

.consent__note a {
  color: var(--ll-ink);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  overflow-wrap: anywhere;          /* the support address is one long token */
}

@media (max-width: 29.99em) {
  .consent__note {
    padding-inline-start: 0;
  }
}


/* -----------------------------------------------------------------------------
   6. RADIO GROUPS  ::  age range and the four seasons
   -----------------------------------------------------------------------------
   Both are native <fieldset>/<legend> + input[type=radio]. Not clickable divs:
   the platform gives one tab stop per group, arrow-key navigation in both
   axes, Space to select, and "Autumn, radio button, 4 of 4, selected" for
   free. Nothing below removes a single one of those behaviours - every rule
   here is paint.
   ----------------------------------------------------------------------------- */

.group {
  /* A fieldset's UA defaults (border, padding, and min-inline-size:min-content)
     are all wrong here, and the last one is the dangerous one: it stops the
     element shrinking below its widest child and is a classic source of
     horizontal overflow on a phone. */
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

/* display:block takes the legend out of its special UA layout mode and lets it
   behave as an ordinary block, which is what makes the heading inside it set
   like the heading it is. */
.group__legend {
  display: block;
  inline-size: 100%;
  padding: 0;
  margin: 0;
}

/* -- age range: compact, typographic chips ----------------------------------- */

.ages {
  margin-block-start: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
}

.age {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-block-size: var(--ll-tap);
  padding: 0 1.125rem;
  border: 1px solid var(--ll-control-line);
  border-radius: var(--ll-radius-pill);
  background: var(--ll-ground);
  cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease;
}

.age:hover {
  border-color: var(--ll-ink-soft);
}

/* ★ THE INPUT IS THE WHOLE CHIP, TRANSPARENT, ON TOP - rather than a clipped
   1x1 input that the label forwards clicks to.

   HONEST PROVENANCE: both shapes were A/B-tested by tapping the chip in mobile
   Safari on a real iOS 18.1 simulator, and BOTH select correctly. The 1x1
   version is not broken. This shape is kept because it removes a dependency
   rather than because it fixed a defect: with the input stretched over the
   chip, the tap target, the hit-tested element, the accessibility frame and
   the visible control are all ONE rectangle, and selection never relies on
   label-activation forwarding - a behaviour that is correct per spec and
   historically patchy in mobile browsers when the control is zero-sized.
   z-index: 1 keeps it above the ::before binding rule and the ::after check
   marker, which are pseudo-elements of the label and would otherwise hit-test
   in front of it. */
.age input {
  position: absolute;
  inset: 0;
  z-index: 1;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  appearance: none;
  background: transparent;
  opacity: 0;
  cursor: pointer;
}

.age__text {
  font-family: var(--ll-ui);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ll-ink);
  white-space: nowrap;
}

.age:has(input:checked) {
  border-color: var(--ll-ink);
  border-width: 2px;
  background: var(--ll-tint);
  padding: 0 calc(1.125rem - 1px);   /* hold the width as the border thickens */
}

.age:has(input:checked) .age__text {
  font-weight: 600;
}

.age:has(input:focus-visible) {
  outline: 2px solid var(--ll-ink);
  outline-offset: 3px;
}

/* -- the four seasons: the editorial treatment ------------------------------- */

.seasons-choice {
  margin-block-start: 1.75rem;
  display: grid;
  gap: 0.75rem;
}

@media (min-width: 30em) {
  .seasons-choice {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Each season is a considered option set in the display serif, with one line
   of plain description underneath, and a coral keyline that only completes
   when it is chosen. Four grey radio pills would have been a decision about
   the brand, not just about a form. */
.season {
  position: relative;
  display: block;
  padding: clamp(1rem, 3vw, 1.25rem) clamp(1rem, 3vw, 1.375rem);
  background: var(--ll-ground);
  border: 1px solid var(--ll-control-line);
  border-radius: 12px;
  cursor: pointer;
  overflow: hidden;
  transition: border-color 160ms ease, background-color 160ms ease;
}

/* Same as .age input above, and for the same reason. */
.season input {
  position: absolute;
  inset: 0;
  z-index: 1;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  appearance: none;
  background: transparent;
  opacity: 0;
  cursor: pointer;
}

/* The binding edge, as on the notebook cover in the section above: a 3px rule
   that is faint until the season is the chosen one. */
.season::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 3px;
  background: var(--ll-rule-faint);
  transition: background-color 160ms ease;
}

.season__body {
  display: block;
}

.season__name {
  display: block;
  font-family: var(--ll-display);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 1.875rem);
  line-height: 1.15;
  letter-spacing: -0.008em;
  color: var(--ll-ink);
  padding-block-end: 0.04em;
}

.season__gloss {
  display: block;
  margin-block-start: 0.3125rem;
  font-family: var(--ll-ui);
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--ll-ink-soft);
}

.season:hover {
  border-color: var(--ll-ink-soft);
}

.season:hover::before {
  background: var(--ll-accent);
}

/* The selected state carries THREE signals, none of them colour on its own:
   a 2px ink border, the full-strength coral binding rule, and a check marker
   in the top-right corner. */
.season:has(input:checked) {
  border-color: var(--ll-ink);
  border-width: 2px;
  padding: calc(clamp(1rem, 3vw, 1.25rem) - 1px) calc(clamp(1rem, 3vw, 1.375rem) - 1px);
  background: var(--ll-paper);
}

.season:has(input:checked)::before {
  background: var(--ll-accent);
}

.season:has(input:checked)::after {
  content: '\2713';
  position: absolute;
  inset-block-start: 0.625rem;
  inset-inline-end: 0.75rem;
  font-family: var(--ll-ui);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ll-ink);
}

.season:has(input:focus-visible) {
  outline: 2px solid var(--ll-ink);
  outline-offset: 3px;
}

/* ---- :has() fallback ------------------------------------------------------
   :has() is in every browser this site targets (Safari 15.4+, Chrome 105+,
   Firefox 121+). On an engine without it the selected state above simply does
   not paint, which would leave a chosen option indistinguishable from an
   unchosen one - a real failure, not a cosmetic one. So the script also sets
   a data-ll-checked attribute on the chosen label, and these rules repeat the
   three signals off that attribute. Belt and braces, 14 lines. */

.age[data-ll-checked] {
  border-color: var(--ll-ink);
  border-width: 2px;
  background: var(--ll-tint);
  padding: 0 calc(1.125rem - 1px);
}

.age[data-ll-checked] .age__text {
  font-weight: 600;
}

.season[data-ll-checked] {
  border-color: var(--ll-ink);
  border-width: 2px;
  padding: calc(clamp(1rem, 3vw, 1.25rem) - 1px) calc(clamp(1rem, 3vw, 1.375rem) - 1px);
  background: var(--ll-paper);
}

.season[data-ll-checked]::before {
  background: var(--ll-accent);
}

.season[data-ll-checked]::after {
  content: '\2713';
  position: absolute;
  inset-block-start: 0.625rem;
  inset-inline-end: 0.75rem;
  font-family: var(--ll-ui);
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--ll-ink);
}


/* -----------------------------------------------------------------------------
   7. THE PAGE-LEVEL ALERT  ::  the three non-field error branches
   -----------------------------------------------------------------------------
   ★ THE FOUR ERROR BRANCHES MUST BE VISIBLY DISTINCT, and they are not
   distinguished by colour: each carries a different CHIP LABEL in its first
   line, written by the script from a fixed set, plus different copy.

     validation_failed  no alert at all - the message is AT the field, and the
                        funnel has navigated back to the step that owns it
     malformed_body     chip "OUR MISTAKE"
     429                chip "TOO MANY ATTEMPTS" and a named wait
     network / 5xx      chip "NO CONNECTION"
   ----------------------------------------------------------------------------- */

.funnel__alert {
  margin-block-start: 1.5rem;
  padding: 1rem 1.125rem;
  background: var(--ll-plate);
  border: 1px solid var(--ll-rule);
  /* Ink, not coral: coral on the cream plate is 2.96:1 and this edge is part of
     how the alert is identified. Ink on the plate measures 15.61:1. */
  border-inline-start: 3px solid var(--ll-ink);
  border-radius: 10px;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ll-ink);
  max-width: 48ch;
}

.funnel__alert-chip {
  display: block;
  font-family: var(--ll-ui);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ll-ink-soft);
  margin-block-end: 0.375rem;
}


/* -----------------------------------------------------------------------------
   8. THE SUCCESS SCREEN  ::  ONE SCREEN  (ADR-0094)
   -----------------------------------------------------------------------------
   Themed from the SERVER's echoed season via data-ll-season on the root of
   this section (section 9), never from the page's own form state.

   ★ FOUR RULES WERE DELETED HERE BY T09, not left unused: the full-width action
   that handed over a file, the line under it, the metadata line beside it, and
   the quiet variant of the plate that the second (apologising) block used. A
   stylesheet that still dresses a deleted element is an invitation to put the
   element back, and it ships those bytes to every visitor for ever.
   ----------------------------------------------------------------------------- */

.done__head {
  font-family: var(--ll-display);
  font-weight: 400;
  font-size: clamp(1.875rem, 1.3rem + 2.2vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: -0.012em;
  margin: 0;
  padding-block-end: 0.06em;
  max-width: 22ch;
}

.done__lede {
  margin-block-start: 1rem;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ll-ink-soft);
  max-width: 44ch;
}

.done__plate {
  margin-block-start: clamp(1.5rem, 4vw, 2rem);
  padding: clamp(1.25rem, 4vw, 1.75rem);
  background: var(--ll-ground);
  border: 1px solid var(--ll-rule);
  border-radius: 12px;
  /* The binding edge again, so the plate reads as the same family of object as
     the notebook cover and the chosen season. */
  border-inline-start: 4px solid var(--ll-accent);
}

.done__kicker {
  font-family: var(--ll-ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ll-ink-soft);
  overflow-wrap: anywhere;
}

/* The plate's two lines: where the notebook is, then what Label Lens is. Both
   sit on the plate ground, and both are MEASURED on the base ground AND on all
   four seasonal tints of it (section 9 mixes 12% of the season into the ground
   and changes no text colour, which is exactly why one measurement set covers
   all five). Ink: 16.81:1 base, 15.14:1 at the worst tint (autumn). Soft:
   5.61:1 base, 5.05:1 at the worst tint. Re-measured on the now-unconditional
   light ground by LL-F130.T01. The 4.5:1 body bar holds everywhere, so neither
   line needs a per-season override. */
.done__body,
.done__note {
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--ll-ink);
  max-width: 44ch;
}

.done__body {
  margin-block-start: 0.5rem;
}

.done__note {
  margin-block-start: 0.75rem;
  color: var(--ll-ink-soft);
}

/* Iris with a phone, closing the plate (LL-F130.T03). Decorative: she repeats
   nothing and carries nothing - the two lines above say where the notebook is
   and still say it with images blocked.

   A BLOCK, PUSHED TO THE INLINE END, deliberately rather than floated or
   absolutely positioned: she can then never sit under the text, never clip
   the plate at 320px, and never collide with the App Store control below her.
   Sized on the block axis only; the 333x288 ratio on the tag reserves her
   width, so the plate does not grow when the image arrives. No animation. */
.done__iris {
  display: block;
  block-size: clamp(88px, 22vw, 112px);
  inline-size: auto;
  margin-block-start: 0.75rem;
  margin-inline: auto 0;
}

/* The App Store control, which is `hidden` until there is a URL for it. The rule
   stays because the control stays: when the href is filled in and `hidden` is
   deleted, it must already be spaced correctly rather than needing a second
   edit here. */
.done__plate .btn {
  margin-block-start: 1.25rem;
}


/* -----------------------------------------------------------------------------
   9. SEASONAL THEMING OF THE SUCCESS SCREEN
   -----------------------------------------------------------------------------
   Four variations on ONE accent, not four new palettes: the binding rule and
   the plate ground shift, nothing else, and no text colour changes, so every
   contrast pair measured for the base theme still holds. An unrecognised
   season token (a future server) simply gets the base treatment, which is why
   there is no default-case rule here to go wrong.
   ----------------------------------------------------------------------------- */

[data-ll-season='winter'] .done__plate {
  background: color-mix(in srgb, var(--ll-ground) 88%, #8FA6B8);
  border-inline-start-color: #8FA6B8;
}

[data-ll-season='spring'] .done__plate {
  background: color-mix(in srgb, var(--ll-ground) 88%, #9FBE8F);
  border-inline-start-color: #7FA86C;
}

[data-ll-season='summer'] .done__plate {
  background: color-mix(in srgb, var(--ll-ground) 88%, #E8B860);
  border-inline-start-color: #D79B33;
}

[data-ll-season='autumn'] .done__plate {
  background: color-mix(in srgb, var(--ll-ground) 88%, #D08A5C);
  border-inline-start-color: #B96E3C;
}


/* -----------------------------------------------------------------------------
   10. MOTION
   -----------------------------------------------------------------------------
   The funnel adds exactly ONE animation: a 220ms fade-and-rise on a step as it
   becomes the active one. It is motivated rather than decorative - it is the
   feedback that the Continue press did something, on a screen where the only
   other evidence is that the content changed. Everything else here is a
   160ms colour/border transition on hover and focus.

   Under prefers-reduced-motion:reduce the step change is instant. site.v2.css
   already collapses every transition and animation globally; this block states
   the funnel's own intent rather than relying on that sweep, so a future edit
   to either file cannot quietly reintroduce the movement.
   ----------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .funnel__step[data-ll-entering],
  .done[data-ll-entering] {
    animation: ll-step-in 220ms cubic-bezier(0.16, 1, 0.3, 1) both;
  }
}

@keyframes ll-step-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .funnel__step[data-ll-entering],
  .done[data-ll-entering] {
    animation: none;
  }
}
