/* ============================================================
   AM Design System — COMPONENTS
   ------------------------------------------------------------
   One block per component. Each block states its PURPOSE, the
   MARKUP contract, its STATES, and the variant JSON FIELD that
   feeds it (see README.md for the full mapping table).

   Rules:
   - No page-specific CSS. If a page needs something new it
     becomes a component or a modifier here.
   - Everything reads from tokens.css. No literal colours or
     sizes below except where a value is structural (a border
     width, a 50% translate).
   - Mobile-first: the base rules ARE the 390px design. The one
     media query at the foot lets it breathe on desktop.
   - Touch targets are never below --tap-min (48px).

   Depends on: tokens.css (required), motion.css (optional —
   components are fully usable without it, they just do not
   animate), fonts/fonts.css.

   CONTENTS
     0. Reset and base
     1. Layout      .page .bg-image .split .brand-stripe .topbar .footer
     2. Content     .tag .hero .proof .trust .notice .divider
     3. Actions     .btn .cta-note
     4. Forms       .field .input .choice .choice-group .progress .stepper
     5. Utilities   .icon .stack .sr-only
     6. Desktop
   ============================================================ */


/* ============================================================
   0. RESET AND BASE
   Minimal. Just the things that would otherwise bite us in a
   webview.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html {
  /* Stop iOS inflating text in landscape. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-regular);
  -webkit-font-smoothing: antialiased;
  /* Belt and braces against a long word forcing a sideways
     scroll on a 390px screen. */
  overflow-wrap: break-word;
}

/* No browser focus rings or tap flashes anywhere. Focus is shown by
   the control itself: an input's border turns green, a choice card
   fills. */
:focus, :focus-visible { outline: none; }
html { -webkit-tap-highlight-color: transparent; }


/* ============================================================
   1. LAYOUT
   ============================================================ */

/* ------------------------------------------------------------
   .page
   PURPOSE  The full-height screen every landing page is built
            in. A column: things flow from the top, and an
            optional .page__bottom group pins to the foot so the
            CTA lands in the same place no matter how long the
            headline runs.
   MARKUP   <div class="page">
              <div class="brand-stripe"></div>
              <header class="topbar">…</header>
              <div class="page__body">
                …flowing content…
                <div class="page__bottom">…CTA, note, footer…</div>
              </div>
            </div>
   STATES   —
   JSON     none directly; `theme` may be set as data-theme on it.

   HEIGHT   100dvh with a 100vh fallback declared first. dvh is
            what makes this behave when a Facebook or Instagram
            webview shows and hides its toolbar mid-scroll; the
            older units either leave a gap or push content under
            the chrome. We deliberately do NOT use position:fixed
            for the bottom group — a fixed bar fights the webview
            toolbar and can end up unreachable.
   ------------------------------------------------------------ */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;          /* fallback, must come first */
  min-height: 100dvh;
  background: var(--ground);
  color: var(--ink);
  /* Respect notches and home indicators. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* The gutter column that fills the remaining height. */
.page__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding-inline: var(--gutter);
}

/* Pins its group to the bottom of the flex column. The slack
   cap stops a short page from stranding the CTA miles below the
   content on a tall screen. */
.page__bottom {
  margin-top: auto;
  padding-top: var(--sp-proof-cta);
}
.page__bottom--capped { margin-top: min(auto, var(--slack-max)); }

/* A content layer that must sit above a background photo. */
.page__screen {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}


/* ------------------------------------------------------------
   .bg-image
   PURPOSE  Full-bleed cover photograph with the scrim that makes
            cream text legible over it. Layout A.
   MARKUP   <div class="bg-image" style="--img:url(…)"></div>
            …as the first child of a position:relative .page,
            with the content in a sibling .page__screen.on-image.
   STATES   .bg-image--top  photo across the top band only (used
            by .split); height driven by --photo-h.
   JSON     image_url  ->  --img

   The scrim stops are tokens, measured against the reference
   photograph so cream clears AA in every band including a
   bright sky. If the imagery treatment changes, re-measure.
   ------------------------------------------------------------ */
.bg-image {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: var(--z-photo);
  background-color: var(--ink-fixed);   /* shows while the photo loads */
  background-image: var(--img, none);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

.bg-image::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    var(--scrim-0)     0%,
    var(--scrim-34)   34%,
    var(--scrim-62)   62%,
    var(--scrim-78)   78%,
    var(--scrim-100) 100%
  );
}

/* Top band only. Height comes from --photo-h so the split
   layout can size it against its header content rather than a
   fixed percentage, which clips the last line of a long
   headline. */
.bg-image--top {
  inset: 0 0 auto 0;
  height: var(--photo-h, 40%);
  min-height: 300px;
}
.bg-image--top::after {
  background: linear-gradient(
    to bottom,
    var(--scrim-top-0)     0%,
    var(--scrim-top-55)   55%,
    var(--scrim-top-100) 100%
  );
}


/* ------------------------------------------------------------
   .split
   PURPOSE  Layout C. Photo across the top carrying the topbar
            and headline; a cream card overlaps upward into it
            with the rest of the content.
   MARKUP   <div class="page split">
              <div class="bg-image bg-image--top" style="--img:…"></div>
              <div class="page__screen">
                <div class="brand-stripe"></div>
                <div class="on-image split__header">
                  <header class="topbar topbar--over-image">…</header>
                  <section class="hero">…h1 only…</section>
                </div>
                <div class="split__card">…subhead, proof, bottom…</div>
              </div>
            </div>
   STATES   —
   JSON     layout: "split"
   ------------------------------------------------------------ */

/* Padding below the headline sets how much photo shows before
   the card starts. */
.split__header {
  padding-bottom: 56px;
}
.split__header .hero { padding-inline: var(--gutter); }

.split__card {
  position: relative;
  z-index: var(--z-content);
  margin-top: -24px;                        /* the overlap */
  padding: var(--sp-5) var(--gutter) 0;
  background: var(--ground);
  border-radius: var(--radius) var(--radius) 0 0;
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}


/* ------------------------------------------------------------
   .brand-stripe
   PURPOSE  4px of --primary at the very top of every page. The
            single most consistent brand cue across all cells.
   MARKUP   <div class="brand-stripe"></div>
   STATES   —
   JSON     colour follows `theme`
   ------------------------------------------------------------ */
.brand-stripe {
  flex: 0 0 var(--stripe-h);
  height: var(--stripe-h);
  background: var(--primary);
}


/* ------------------------------------------------------------
   .topbar
   PURPOSE  The header: AM tile, service name, highlight tag on
            the right. One sticky green row on every page.
   MARKUP   <header class="topbar">
              <span class="topbar__service">Find a Family Solicitor · Leeds</span>
              <span class="tag">Free to enquire</span>
            </header>
   STATES   .topbar--over-image  transparent, cream text, for use
                                 inside an .on-image scope
   JSON     service_name -> .topbar__service
            tag          -> .tag  (omit the element if empty)
   ------------------------------------------------------------ */
.topbar {
  flex: 0 0 var(--band-h);
  min-height: var(--band-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-inline: var(--gutter);
  background: var(--primary);
  color: var(--on-primary);
}

/* Sticks under the top edge for the whole scroll. It is a direct
   child of .page on every layout, because a sticky box cannot outlive its
   parent: inside the hero it vanished as soon as the hero scrolled away. */
.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
}
/* Once the page has scrolled, lg.js adds .is-stuck: the row goes a
   touch translucent with a blur so the content shows through. */
.topbar.is-stuck {
  background: rgb(31 77 58 / 0.86);                      /* --primary at 86% */
  background: color-mix(in srgb, var(--primary) 86%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 1px 0 rgb(0 0 0 / 0.08);
}

.topbar__service {
  font-size: 16px;
  line-height: 1.3;
  font-weight: var(--fw-semi);
  letter-spacing: .005em;
  /* Long service names truncate rather than push the tag off. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ------------------------------------------------------------
   .footer
   PURPOSE  The compliance floor of the page. Every disclosure the
            body is forbidden to carry lives here: who we are, the
            introduction fee, what happens to the reader's details.
            docs/family-law-page.md keeps that copy out of the
            argument above the rule, so it has to be sound below it.
   MARKUP   <footer class="footer">
              <p class="footer__service">Find a Family Solicitor</p>
              <p class="footer__by"><svg class="footer__mark">…</svg>
                 <span>A service by Awakened Media.</span></p>
              <p class="footer__disclosure">…not a law firm… SRA… fee…</p>
              <p class="footer__data">…one firm… delete… <a>Privacy notice</a></p>
              <p class="footer__links"><a>Privacy</a>·<a>Terms</a>·<a>How it works</a></p>
              <p class="footer__legal">© 2026 Awakened Media.</p>
            </footer>
   STATES   inherits .on-image (rule and text go translucent cream)
   JSON     none — fixed on every page; service name from the page

   A stack, not a row: at 13px the disclosure runs to several lines
   and a flex row would try to sit it beside the monogram. The
   monogram is the FLAT variant, inline at cap height on the "A
   service by" line. Links are inline text, below the 48px target
   floor by design; they are secondary legal links, not actions,
   and carry generous line spacing instead.
   ------------------------------------------------------------ */
.footer {
  margin-top: var(--sp-6);
  padding: var(--sp-5) 0 var(--sp-foot-pad);
  border-top: 1px solid var(--rule);
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  max-width: var(--card-max);
}
.footer > p { margin: 0; }
.footer > p + p { margin-top: var(--sp-3); }

/* One-line lockup: tile, service name, owner. */
.footer__by {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
.footer__mark {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  display: block;
  border-radius: 4px;
}
.footer__service {
  font-weight: 600;
  color: var(--ink);
}
.footer__owner { color: var(--muted); }

.footer__disclosure { max-width: 60ch; }

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}
.footer a {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.footer a:hover { color: var(--ink); }
.footer__legal { font-size: 12px; }
/* ------------------------------------------------------------
   .tag
   PURPOSE  The one highlight-coloured element on the page. Used
            once, in the topbar, for a no-cost reassurance.
   MARKUP   <span class="tag">Free to enquire</span>
   STATES   —
   JSON     tag  (omit the element entirely when null)

   --highlight is never a text colour. Ink on amber, always.
   ------------------------------------------------------------ */
.tag {
  flex: 0 0 auto;
  display: inline-block;
  padding: 3px var(--sp-2);
  border-radius: var(--radius-chip);
  background: var(--highlight);
  color: var(--on-highlight);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: var(--fw-semi);
  white-space: nowrap;
}


/* ------------------------------------------------------------
   .hero
   PURPOSE  Headline and subhead. The whole proposition.
   MARKUP   <section class="hero">
              <h1 class="hero__h1">…</h1>
              <p  class="hero__sub">…</p>
            </section>
   STATES   inherits .on-image
   JSON     headline -> .hero__h1
            subhead  -> .hero__sub

   H1 is capped at three lines at 390px by the copy, not by CSS —
   truncating a headline mid-sentence is worse than a fourth line.
   text-wrap: balance evens the rag.
   ------------------------------------------------------------ */
.hero { padding-top: var(--sp-above-h1); }

.hero__h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  font-weight: var(--fw-semi);
  letter-spacing: 0;
  color: var(--ink);
  text-wrap: balance;
}

.hero__sub {
  margin: var(--sp-h1-sub) 0 0;
  font-size: var(--fs-sub);
  line-height: var(--lh-sub);
  color: var(--subhead);
  text-wrap: pretty;
}

/* When the subhead sits alone in a split card it has no H1
   above it, so it supplies its own top margin. */
.split__card > .hero__sub { margin-top: 0; }


/* ------------------------------------------------------------
   .proof
   PURPOSE  Three short reasons to trust the page. Stacked on a
            sage surface with 01/02/03 numerals by default.
   MARKUP   <ul class="proof">
              <li class="proof__item">
                <span class="proof__num" aria-hidden="true">01</span>
                SRA-regulated firms only
              </li>
              …
            </ul>
   STATES   .proof--inline  one line, three columns, check icons,
                            no surface. For use over a photo where
                            a sage panel would fight the image.
   JSON     proof_points[]  (expects exactly 3; 2 also works)

   The numerals are decorative and aria-hidden — a screen reader
   announcing "zero one" before every item is noise, and the list
   semantics already convey the count.
   ------------------------------------------------------------ */
.proof {
  list-style: none;
  margin: var(--sp-sub-proof) 0 0;
  padding: var(--sp-proof-pad);
  background: var(--tint);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  gap: var(--sp-proof-gap);
}

.proof__item {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink);
}

.proof__num {
  flex: 0 0 auto;
  min-width: 18px;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: var(--fw-semi);
  font-variant-numeric: tabular-nums lining;
  color: var(--primary);
}

/* Inline modifier: three equal columns, no wrap, so the items
   always sit on ONE line at 390px. Copy must be short — three
   words at most per item. */
.proof--inline {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin: 0;
  padding: 0;
  background: none;
  border-radius: 0;
}
.proof--inline .proof__item {
  align-items: center;
  gap: 5px;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.proof--inline .icon {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
}
/* Over a photo the numerals lose their green against the scrim. */
.on-image .proof__num { color: var(--on-image); }


/* ------------------------------------------------------------
   .trust
   PURPOSE  A row of small regulator or membership badges. A
            placeholder until real marks are cleared for use —
            do not invent logos for firms we do not represent.
   MARKUP   <ul class="trust">
              <li class="trust__item">
                <svg class="icon"><use href="icons.svg#shield-check"/></svg>
                SRA regulated
              </li>
              …
            </ul>
   STATES   inherits .on-image
   JSON     not yet a field. Add trust_badges[] when real marks exist.
   ------------------------------------------------------------ */
.trust {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
  margin: var(--sp-4) 0 0;
  padding: 0;
}

.trust__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  font-weight: var(--fw-medium);
  color: var(--muted);
}
.trust__item .icon {
  width: 14px;
  height: 14px;
  color: var(--primary);
}
.on-image .trust__item .icon { color: var(--on-image); }

/* Placeholder box for a real logo we have not licensed yet. */
.trust__logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  padding-inline: var(--sp-2);
  border: 1px dashed var(--rule);
  border-radius: var(--radius);
  font-size: 10px;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
}


/* ------------------------------------------------------------
   .notice
   PURPOSE  A short banner: information, a confirmed submission,
            or a failure.
   MARKUP   <div class="notice notice--success" role="status">
              <svg class="icon"><use href="icons.svg#check-circle"/></svg>
              <p class="notice__text">…</p>
            </div>
   STATES   --info (default) --success --error
   JSON     none — driven by the Worker's response

   role="status" for info and success (polite), role="alert" for
   error (assertive). Colour is never the only signal: each
   variant carries its own icon.
   ------------------------------------------------------------ */
.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  border-left: 3px solid var(--muted);
  background: var(--info-tint);
  color: var(--ink);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.notice__text { margin: 0; }
.notice .icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  /* Optically centre the icon on the first line of text. */
  margin-top: 2px;
}

.notice--info    { background: var(--info-tint);    border-left-color: var(--muted); }
.notice--info    .icon { color: var(--muted); }

.notice--success { background: var(--success-tint); border-left-color: var(--success); }
.notice--success .icon { color: var(--success); }

.notice--error   { background: var(--error-tint);   border-left-color: var(--error); }
.notice--error   .icon { color: var(--error); }


/* ------------------------------------------------------------
   .divider
   PURPOSE  A hairline between sections. Used sparingly; the
            language leans on space, not lines.
   MARKUP   <hr class="divider">
   ------------------------------------------------------------ */
.divider {
  height: 0;
  margin: var(--sp-5) 0;
  border: 0;
  border-top: 1px solid var(--rule);
}


/* ============================================================
   3. ACTIONS
   ============================================================ */

/* ------------------------------------------------------------
   .btn
   PURPOSE  Every action. Full width by default because that is
            what a 390px screen and a 40-to-60 audience want.
   MARKUP   <a class="btn btn--primary" href="#start">Start</a>
            <button class="btn btn--secondary" type="button">Back</button>
   VARIANTS --primary    solid green, the one main action
            --secondary  outlined, a real but lesser action
            --ghost      text only, for a back link
            --on-image   cream fill with FIXED ink text, for use
                         over a photo where green loses separation
   SIZES    default 48px, --lg 56px
   STATES   :hover :active(scale .98) :focus-visible
            [disabled] / .is-disabled
            .is-loading (shows the spinner, blocks pointer events)
   JSON     cta_label -> the label

   Height is min-height, not height, so a two-line label on a
   narrow screen grows the button rather than spilling out.
   ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--cta-h);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-cta);
  font-weight: var(--fw-semi);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  /* Stops a double-tap from zooming the page in a webview. */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Press feedback. Timed in motion.css. */
.btn:active { transform: scale(.98); }

.btn--lg { min-height: var(--cta-h-lg); }

/* Only where a button genuinely sits beside others in a row. */
.btn--auto { width: auto; }

/* --- primary --- */
.btn--primary {
  background: var(--primary);
  color: var(--on-primary);
  border-color: var(--primary);
}
.btn--primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

/* --- secondary --- */
.btn--secondary {
  background: transparent;
  color: var(--primary);
  border-color: var(--primary);
}
.btn--secondary:hover {
  background: var(--tint);
}

/* --- ghost --- */
.btn--ghost {
  background: transparent;
  color: var(--muted);
  border-color: transparent;
  font-weight: var(--fw-medium);
}
.btn--ghost:hover { color: var(--ink); }

/* --- on-image ---
   Cream fill over the scrim. The label uses --ink-fixed rather
   than --ink so it stays dark when .on-image remaps --ink to
   cream, which would otherwise make it cream-on-cream. */
.btn--on-image {
  background: var(--ground);
  color: var(--ink-fixed);
  border-color: var(--ground);
}
.btn--on-image:hover {
  background: #FFFFFF;
  border-color: #FFFFFF;
}

/* --- disabled --- */
.btn[disabled],
.btn.is-disabled {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}

/* --- loading ---
   The label stays in place so the button does not change width;
   the spinner is appended by markup, not generated content, so
   it is available to assistive tech via aria-busy on the button. */
.btn.is-loading {
  pointer-events: none;
  cursor: progress;
}

.spinner {
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  /* Rotation is in motion.css. */
}

/* Icons inside buttons match the label's optical weight. */
.btn .icon { width: 18px; height: 18px; }


/* ------------------------------------------------------------
   .cta-note
   PURPOSE  The reassurance micro-line under the main action.
            Says what will and will not happen. Carries a lot of
            weight with a cautious audience — do not drop it.
   MARKUP   <p class="cta-note">No obligation. No spam.</p>
   STATES   inherits .on-image (slightly softened)
   JSON     cta_note
   ------------------------------------------------------------ */
.cta-note {
  margin: var(--sp-cta-note) 0 0;
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  color: var(--muted);
}
.on-image .cta-note { opacity: .88; }


/* ============================================================
   4. FORMS — the qualifier
   Four questions maximum per vertical, then a contact step.
   ============================================================ */

/* ------------------------------------------------------------
   .field
   PURPOSE  Wraps one input with its label, optional hint and
            error. The label is always visible: placeholder-only
            labels fail this audience the moment they type.
   MARKUP   <div class="field">
              <label class="field__label" for="phone">Phone number</label>
              <p class="field__hint" id="phone-hint">So a solicitor can call you.</p>
              <input class="input" id="phone" type="tel"
                     aria-describedby="phone-hint" required>
              <p class="field__error" id="phone-err" hidden>Enter a phone number.</p>
            </div>
   STATES   .field.has-error   reveals the error, reddens the input
   JSON     questions[].label / .hint

   When an error shows, stepper.js also sets aria-invalid on the
   input and points aria-describedby at the error node.
   ------------------------------------------------------------ */
.field { display: block; }
.field + .field { margin-top: var(--sp-4); }

.field__label {
  display: block;
  margin-bottom: var(--sp-1);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-semi);
  color: var(--ink);
}

.field__hint {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  color: var(--muted);
}

.field__error {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  font-weight: var(--fw-medium);
  color: var(--error);
}
.field__error .icon { width: 14px; height: 14px; flex: 0 0 auto; }
/* [hidden] needs help against the display above. */
.field__error[hidden] { display: none; }


/* ------------------------------------------------------------
   .input
   PURPOSE  Text, tel, email and postcode entry.
   MARKUP   <input class="input" type="tel" inputmode="tel"
                   autocomplete="tel">
   STATES   :hover :focus-visible [disabled] .has-error

   16px font is NOT a style choice: iOS Safari and every webview
   built on it zoom the viewport when a field under 16px takes
   focus, and the user is then stranded on a page they cannot
   scroll back. Never lower --fs-input.

   Always pair with inputmode and autocomplete so the right
   keyboard appears and the browser can fill it:
     tel      inputmode="tel"   autocomplete="tel"
     email    inputmode="email" autocomplete="email"
     postcode inputmode="text"  autocomplete="postal-code"
     name     inputmode="text"  autocomplete="name"
   ------------------------------------------------------------ */
.input {
  display: block;
  width: 100%;
  min-height: var(--cta-h);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #FFFFFF;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-input);       /* 16px floor — see above */
  line-height: var(--lh-body);
  /* iOS otherwise rounds and shades the field itself. */
  -webkit-appearance: none;
  appearance: none;
}
.input::placeholder { color: var(--muted); opacity: .7; }
.input:hover { border-color: var(--primary); }
.input:focus { border-color: var(--primary); }
.input[disabled] { opacity: .5; cursor: not-allowed; background: var(--tint); }

.field.has-error .input,
.input[aria-invalid="true"] {
  border-color: var(--error);
  border-width: 2px;
}

/* Textarea, on the rare page that needs free text. */
textarea.input {
  min-height: 96px;
  padding-top: var(--sp-3);
  resize: vertical;
}


/* ------------------------------------------------------------
   .choice
   PURPOSE  Tap-to-answer card for a radio or checkbox. The WHOLE
            card is the target — the native control is visually
            hidden but still focusable and still announced, so
            keyboard and screen-reader users get real semantics.
   MARKUP   <label class="choice">
              <input class="choice__input" type="radio"
                     name="stage" value="considering">
              <span class="choice__label">Just starting to consider it</span>
              <svg class="choice__check icon" aria-hidden="true">
                <use href="icons.svg#check"/>
              </svg>
            </label>
   STATES   :hover, :has(:checked) selected, :focus-within ring,
            [disabled]
   JSON     questions[].options[]  -> one .choice each

   56px minimum, well over the 48px floor, because a mis-tap here
   costs us the lead. :has() is supported everywhere we care
   about (iOS 15.4+, Chrome 105+); the :checked sibling rules
   below cover the label and tick without it, so the only thing
   an ancient browser loses is the card's background tint.
   ------------------------------------------------------------ */
.choice {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--cta-h-lg);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: #FFFFFF;
  color: var(--ink);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* The real control: hidden from sight, present for everything
   else. Not display:none — that would remove it from the tab
   order and from the accessibility tree. */
.choice__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.choice__label { flex: 1 1 auto; }

/* The tick, revealed on selection. */
.choice__check {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  color: var(--primary);
  opacity: 0;
}

.choice:hover { border-color: var(--primary); }
.choice:active { transform: scale(.99); }

/* Focus on the card, driven by the hidden input: same as hover. */
.choice:focus-within { border-color: var(--primary); }

/* Selected. Two signals: a 2px primary border and the tick. */
.choice:has(.choice__input:checked) {
  border-color: var(--primary);
  border-width: 2px;
  background: var(--tint);
  /* Keep the text where it was despite the thicker border. */
  padding: calc(var(--sp-3) - 1px) calc(var(--sp-4) - 1px);
}
.choice__input:checked ~ .choice__check { opacity: 1; }
.choice__input:checked ~ .choice__label { font-weight: var(--fw-medium); }

.choice:has(.choice__input[disabled]) {
  opacity: .45;
  cursor: not-allowed;
}

/* ------------------------------------------------------------
   .choice-group
   PURPOSE  The set of choices for one question. A fieldset, so
            the question text is the legend and is announced with
            each option.
   MARKUP   <fieldset class="choice-group">
              <legend class="choice-group__legend">Where are you up to?</legend>
              <label class="choice">…</label>
              …
            </fieldset>
   JSON     questions[].label -> legend; .options[] -> children
   ------------------------------------------------------------ */
.choice-group {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  border: 0;
}

.choice-group__legend {
  /* A legend does not respect flex, so take it out of flow and
     give it its own bottom margin. */
  float: left;
  width: 100%;
  margin: 0 0 var(--sp-3);
  padding: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-semi);
  color: var(--ink);
}
.choice-group__legend + * { clear: left; }


/* ------------------------------------------------------------
   .progress
   PURPOSE  A thin bar showing how far through the questions the
            user is. Telling someone there are two steps left is
            most of what stops them abandoning.
   MARKUP   <div class="progress" role="progressbar"
                 aria-valuemin="0" aria-valuemax="5" aria-valuenow="1"
                 aria-label="Question 1 of 5">
              <div class="progress__fill" style="width:20%"></div>
            </div>
   STATES   width is set inline by stepper.js; the transition is
            in motion.css
   JSON     length of questions[] + 1 contact step
   ------------------------------------------------------------ */
.progress {
  width: 100%;
  height: 4px;
  border-radius: 2px;
  background: var(--rule);
  overflow: hidden;
}

.progress__fill {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--primary);
}

.progress__label {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  font-weight: var(--fw-medium);
  color: var(--muted);
}


/* ------------------------------------------------------------
   .stepper
   PURPOSE  One question per screen. Panels show and hide; the
            progress bar fills; back and next sit in a row.
   MARKUP   <form class="stepper" data-stepper>
              <div class="stepper__progress">
                <div class="progress" role="progressbar">…</div>
                <p class="progress__label" data-stepper-label>Question 1 of 5</p>
              </div>
              <div class="stepper__panels">
                <section class="stepper__panel" data-panel data-active>…</section>
                <section class="stepper__panel" data-panel>…</section>
              </div>
              <div class="stepper__nav">
                <button class="btn btn--ghost btn--auto" data-stepper-back>…</button>
                <button class="btn btn--primary" data-stepper-next>Continue</button>
              </div>
            </form>
   STATES   data-dir="next"|"prev" on the form drives the slide;
            data-active marks the visible panel
   JSON     questions[] -> one panel each, plus a contact panel

   PROGRESSIVE ENHANCEMENT: without JavaScript every panel is
   visible and the form is a single long page that still submits.
   stepper.js adds the `is-enhanced` class, and only then do the
   hiding rules apply. Nothing is hidden by CSS alone.
   ------------------------------------------------------------ */
.stepper {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.stepper__progress { margin-bottom: var(--sp-5); }

.stepper__panels {
  flex: 1;
  min-height: 0;
}

/* Unenhanced: panels stack, separated by a rule. */
.stepper__panel + .stepper__panel {
  margin-top: var(--sp-6);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--rule);
}

/* Enhanced: only the active panel shows, and the separators go. */
.stepper.is-enhanced .stepper__panel { display: none; }
.stepper.is-enhanced .stepper__panel[data-active] { display: block; }
.stepper.is-enhanced .stepper__panel + .stepper__panel {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Back sits left and only takes the width it needs; next fills
   the rest, keeping the primary action big and central. */
.stepper__nav {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-5);
}
.stepper__nav .btn--ghost { flex: 0 0 auto; }
.stepper__nav .btn--primary { flex: 1 1 auto; }

/* On the first step there is nowhere to go back to. Kept in the
   DOM so the row does not reflow when it appears. */
.stepper__nav [data-stepper-back][hidden] { display: none; }

/* Question text inside a panel that is not a fieldset. */
.stepper__question {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-semi);
  color: var(--ink);
}


/* ============================================================
   5. UTILITIES
   ============================================================ */

/* ------------------------------------------------------------
   .icon
   PURPOSE  Every inline SVG icon. 1em square so it scales with
            its text, currentColor so it takes the surrounding
            colour. See icons.svg for the sprite and the two
            reference patterns.
   ------------------------------------------------------------ */
.icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  vertical-align: -.125em;   /* sits on the text baseline */
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}


/* ------------------------------------------------------------
   .stack
   PURPOSE  Consistent vertical rhythm without every component
            carrying its own margins. Puts a gap between children;
            the modifier picks the step from the space scale.
   MARKUP   <div class="stack stack--4">…</div>
   ------------------------------------------------------------ */
.stack { display: flex; flex-direction: column; }
.stack--1 { gap: var(--sp-1); }
.stack--2 { gap: var(--sp-2); }
.stack--3 { gap: var(--sp-3); }
.stack--4 { gap: var(--sp-4); }
.stack--5 { gap: var(--sp-5); }
.stack--6 { gap: var(--sp-6); }
.stack--7 { gap: var(--sp-7); }


/* ------------------------------------------------------------
   .sr-only
   PURPOSE  Visible to assistive technology, not to the eye. For
            labels a sighted user gets from context.
   ------------------------------------------------------------ */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ============================================================
   6. DESKTOP
   The design is mobile. On a wider screen we do not redesign it;
   we centre the same column on the warm ground and let the type
   and spacing breathe a little. One breakpoint, deliberately.
   ============================================================ */
@media (min-width: 600px) {

  .page {
    max-width: var(--card-max);
    margin-inline: auto;
    box-shadow: var(--shadow-soft);
  }

  .page__body {
    padding-inline: var(--sp-6);
  }

  .topbar { padding-inline: var(--sp-6); }
  .split__header .hero { padding-inline: var(--sp-6); }
  .split__card { padding-inline: var(--sp-6); }

  /* A touch more room above the headline now there is space. */
  .hero { padding-top: var(--sp-7); }

  /* The main action no longer needs to span a 480px card. */
  .page__bottom .btn--primary,
  .page__bottom .btn--on-image { min-height: var(--cta-h-lg); }
}

/* ============================================================
   added for runtime
   ------------------------------------------------------------
   Classes the Worker emits that the original sheet had no
   specimen for, plus the fixes that the CSP forces.

   The CSP is `style-src 'self'`, so a `style="…"` attribute is
   dropped by the browser. Anything that used to be an inline
   style is a class here, or is set through the CSSOM by JS.
   ============================================================ */

/* ---------- background photo as an <img> --------------------
   `background-image: var(--img)` needed an inline custom
   property, which the CSP drops, so the photo never painted.
   The image is a real <img> covering the layer instead;
   img-src allows https:. The scrim ::after still sits on top. */
.bg-image__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
}
.bg-image::after { z-index: 1; }

/* ---------- header ------------------------------------------
   The monogram sits beside the service name at every breakpoint. */
.topbar__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;                 /* lets the service name truncate */
}
.topbar__mark {
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 5px;
  display: block;
}
/* Over a photo the tile would disappear into the scrim; a hairline
   keeps its edge readable without introducing a new colour. */

/* ---------- hero screen (layout: image) ---------------------
   The photo screen is exactly one viewport; the rest of the page
   scrolls beneath it on the normal ground, so choice cards and
   sections keep ink-on-cream contrast instead of inheriting
   .on-image's cream text. */
.hero-screen {
  position: relative;
  display: flex;
  flex-direction: column;
  /* One full screen, less the sticky header row above it, so everything after
     the ask starts below the fold. No overflow: hidden, or the header's sticky
     would stop here; the photo is clipped by .bg-image's own box. */
  min-height: calc(100vh - var(--band-h));   /* fallback, must come first */
  min-height: calc(100dvh - var(--band-h));
  background: var(--ink-fixed); /* shows while the photo loads */
}
.hero-screen__content {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
.hero-screen__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  justify-content: flex-end;    /* the block sits low, over the deep scrim */
  gap: var(--sp-3);
  padding: var(--sp-5) var(--gutter);
  padding-bottom: var(--sp-5);
  overflow: hidden;
}
/* The headline at 1.5x on the photo screen: it has a full screen to itself. */
.hero-screen__body .hero__h1 {
  font-size: var(--fs-h1-xl);
  line-height: var(--lh-h1-xl);
  letter-spacing: -0.015em;
}
/* Tighten the stack so headline + subhead + proof + CTA clear 844px at 390 wide. */
.hero-screen__body > * + * { margin-top: 0; }
.hero-screen__body .hero { margin: 0; }
.hero-screen__body .proof--inline { margin: var(--sp-2) 0 0; }
.hero-screen__body .btn { margin-top: var(--sp-3); }
.hero-screen__body .cta-note { margin-top: var(--sp-2); }
/* The subhead can lose a line before the CTA does; clamp it rather than scroll. */
.hero-screen__body .hero__sub {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}
.page--image .page__body { padding-block: var(--sp-6) var(--sp-5); }

/* ---------- split header ------------------------------------ */
.page--split { position: relative; }
.split__header { position: relative; }
.split__header-content {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
}
.split__header-body { padding: var(--sp-5) var(--gutter) var(--sp-6); }
/* The photo band sizes to the headline over it. The original rule added 56px of
   padding under a hero that no longer lives there, which opened a gap between the
   photo and the card. */
.page--split .split__header { padding-bottom: 0; }
/* The band must be at least as tall as the photo, or the card floats below it. */
.page--split .split__header-content { min-height: 300px; justify-content: flex-end; }

/* ---------- vertical rhythm ---------------------------------
   The pinned-group markup is gone, so the body column spaces its
   own children on the 8px scale rather than relying on inline
   margins. */
.page__body > * + * { margin-top: var(--sp-5); }
.split__card > * + * { margin-top: var(--sp-5); }
/* The qualifier overlay is position:fixed; a rhythm margin would push it off the
   top of the viewport, so it is exempt from the column's spacing. */
.page__body > .qualifier--screen,
.split__card > .qualifier--screen,
.hero-screen__body > .qualifier--screen { margin: 0; }
.page__body > .footer,
.split__card > .footer { margin-top: auto; padding-top: var(--sp-6); }
/* margin-top:auto collapses to 0 when the content already fills the column, so the
   padding above is what actually guarantees the gap. */
.page__body > .cta-note,
.split__card > .cta-note { margin-top: var(--sp-3); }
.hero-screen__body > * + * { margin-top: var(--sp-3); }

/* ---------- inline proof row --------------------------------
   Three items on one line overflowed a 390px column. It wraps now
   and the items centre, so two-up and three-up both read. */
/* The base rule is a rigid 3-column grid with nowrap items, which overflowed a
   390px column the moment the copy ran past three words. Flex + wrap lets the row
   reflow to two lines instead of pushing the page sideways. */
.proof--inline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  /* Items are their own flex lines when the copy is long; start-align them so a
     wrapped row reads as a left-aligned list rather than a centred stack. */
  align-items: flex-start;
  justify-content: flex-start;
  grid-template-columns: none;
  /* The base .proof pads for its tint surface; the inline row has none, so it must
     not inherit that inset or it sits proud of the CTA beneath it. */
  padding: 0;
  background: none;
}
.proof--inline .proof__item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  /* nowrap is what forced the overflow; let a long item break instead. */
  white-space: normal;
  padding: 0;
  border: 0;
}
/* Centred only over the photo, where the row is a caption under the headline. */
.on-image .proof--inline { justify-content: center; }
.on-image .proof--inline .proof__item { justify-content: center; }

/* ---------- sections (the reasons) --------------------------
   Three quiet reasons, one under another. No card, no icon
   circle, no capitals: a serif heading and one or two lines of
   prose, separated by hairlines and air. The photo above has
   done the emotional work; this is where the reader calms down. */
.sections {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
}
/* The first reason carries no top padding: the block above it
   (usually the trust row) already sets the gap. */
.sections > .section:first-child { padding-top: 0; }
/* The trust row sits centred between the band above and the
   reasons below: the body's sibling gap on both sides, and the
   first reason has no top padding of its own. .page__body is a
   flex column, so margins do not collapse; keep the bottom at 0. */
.page__body > .trust { margin: var(--sp-5) 0 0; }
.section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-6) 0;
}
.section + .section {
  border-top: 1px solid var(--rule);
}
.section__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
.section__icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--tint);
  color: var(--primary);
}
.section__eyebrow {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 500;
  color: var(--muted);
}
.section__heading {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.section__text {
  margin: var(--sp-1) 0 0;
  font-size: 17px;
  line-height: 1.55;
  color: var(--ink-soft, var(--ink));
}

/* ---------- .check ------------------------------------------
   A visible checkbox: a 24px box that fills green with a tick.
   The native input is hidden but keeps keyboard and label
   behaviour. Used for the contact step's consent line. */
.check {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  cursor: pointer;
  font-size: 16px;
  line-height: 1.4;
  color: var(--ink);
}
.check--consent { margin-top: var(--sp-4); }
.check__input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.check__box {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-top: 1px;
  border: 2px solid var(--muted);
  border-radius: 6px;
  background: #FFFFFF;
  transition: background .15s ease, border-color .15s ease;
}
.check__tick {
  width: 16px;
  height: 16px;
  stroke-width: 3;
  color: var(--on-primary);
  opacity: 0;
  transition: opacity .15s ease;
}
.check__input:checked + .check__box {
  background: var(--primary);
  border-color: var(--primary);
}
.check__input:checked + .check__box .check__tick { opacity: 1; }
.check__input:focus + .check__box { border-color: var(--primary); }

/* ---------- the image block --------------------------------- */
.block-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

/* ---------- qualifier as its own screen ---------------------
   Hidden on landing once lg.js enhances; revealed by a CTA with
   href="#qualifier". Without JavaScript it stays in the flow and
   submits natively, so `hidden` is only ever set by script. */
.qualifier { scroll-margin-top: var(--sp-4); }

.qualifier--screen {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  /* The panel scrolls inside, not the screen, so the nav stays put. */
  overflow: hidden;
  overscroll-behavior: contain;
}
/* One compact band: monogram + service name left, Close right. No brand stripe —
   this is a modal surface, not a page header. */
.qualifier__head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: 44px;
  padding-inline: var(--gutter);
  background: var(--primary);
  color: var(--on-primary);
  border-radius: 0;
}
.qualifier__head .topbar__service { color: var(--on-primary); }
.qualifier__head .topbar__mark { box-shadow: 0 0 0 1px rgb(247 244 238 / 0.28); }
.qualifier__close {
  flex: none;
  color: var(--on-primary);
  min-height: 36px;
  padding-inline: var(--sp-2);
}
.qualifier__close .icon { width: 15px; height: 15px; }
.qualifier__close:hover { color: var(--on-primary); opacity: 0.82; }
.qualifier--screen .stepper {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  width: 100%;
  max-width: 560px;
  margin-inline: auto;
  padding: var(--sp-5) var(--gutter) var(--sp-6);
}

/* Breathing room between the progress bar and the first question. */
.qualifier--screen .stepper__progress { flex: none; margin-bottom: var(--sp-4); }

/* The panel scrolls, the nav does not. Without this the tallest step (contact)
   pushes the nav down over the last field's hint. The nav stays in normal flow
   at the foot of the column rather than floating above the content. */
.qualifier--screen .stepper__panels {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Clear of the nav even mid-scroll, plus the home indicator. */
  padding-bottom: var(--sp-4);
}
.qualifier--screen .stepper__nav {
  flex: none;
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  border-top: 1px solid var(--rule);
  background: var(--ground);
}
.qualifier--screen .choice { min-height: 56px; }

/* ---------- focus ------------------------------------------
   The stepper moves focus to the question heading (tabindex="-1")
   so screen readers land in the right place. That is a
   programmatic focus on a non-interactive element and must not
   draw a ring; :focus-visible rings on real controls stay. */
.stepper__question:focus,
.choice-group__legend:focus,
[tabindex="-1"]:focus { outline: none; }

/* Stops the page behind the qualifier screen from scrolling. */
.lg-locked, .lg-locked body { overflow: hidden; }

/* Body copy on the static pages (privacy, terms, how it works). */
.prose { max-width: 62ch; }
.prose__p {
  margin: 0 0 var(--sp-4);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink);
}
.prose__p:last-child { margin-bottom: 0; }

/* ---------- desktop --------------------------------------------
   Section 6 already centres `.page` at --card-max (480px) and that
   is the intended look: the same phone column on a warm ground, not
   a redesign. These only extend that decision to the parts the new
   anatomy added — the hero screen and the qualifier — so they line
   up with the card instead of spanning the window. */
@media (min-width: 600px) {
  .hero-screen {
    max-width: var(--card-max);
    margin-inline: auto;
    box-shadow: var(--shadow-soft);
  }
  .hero-screen__body { padding-inline: var(--sp-6); padding-bottom: var(--sp-7); }
  .split__header-body { padding-inline: var(--sp-6); }
  .qualifier--screen .stepper { max-width: var(--card-max); }
  /* The header band and the form share one column, so their edges line up. */
  .qualifier--screen .qualifier__head {
    width: 100%;
    max-width: var(--card-max);
    margin-inline: auto;
  }
  .qualifier--screen .qualifier__head { padding-inline: var(--sp-6); }
  .qualifier--screen .stepper { padding-inline: var(--sp-6); }
  /* The main action no longer needs to span the card. */
  .page__body > .btn,
  .split__card > .btn { width: auto; min-width: 280px; align-self: flex-start; }
  .sections { padding: var(--sp-6); }
}

/* ---------- inline proof row alignment -------------------------
   Centre the row only when it sits over the photo, where it is a
   caption under the headline. In the body flow it aligns left with
   everything else. */
.on-image .proof--inline { justify-content: center; }
.page__body > .proof--inline,
.split__card > .proof--inline { justify-content: flex-start; }

/* ---------- section icon ---------------------------------------
   The glyph needs to fill more of the 32px disc or it reads as a
   plain dot at a glance. */
.section__icon .icon { width: 20px; height: 20px; stroke-width: 2; }

/* ---------- hidden wins ----------------------------------------
   `hidden` is only `display: none` at the UA level, so any component
   that sets its own `display` (.notice is flex) keeps rendering when
   script hides it. That is what put an empty error bar on the
   qualifier. This makes the attribute authoritative everywhere. */
[hidden] { display: none !important; }

/* The inlined icon sprite: symbols only, never painted itself. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* "Optional" suffix on a field label. Muted and small so it reads as a note on the
   label, not a second label. */
.field__optional {
  margin-left: 6px;
  font-size: 13px;
  font-weight: var(--fw-regular, 400);
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
}

/* ---------- trust row --------------------------------------
   Always one row. Three equal columns on the 390px screen; a
   label that is too long wraps inside its column with the icon
   pinned to its first line, so the row never breaks into two. */
.trust {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
  padding: 0;
}
.trust__item {
  display: inline-flex;
  align-items: flex-start;
  flex: 1 1 0;
  min-width: 0;
  gap: 5px;
  white-space: normal;
  overflow-wrap: anywhere;
}
.trust__item .icon {
  width: 15px; height: 15px; color: var(--primary); flex: none;
  margin-top: calc((var(--lh-micro) * var(--fs-micro) - 15px) / 2);
}
.on-image .trust { justify-content: center; }
.on-image .trust__item { flex: 0 1 auto; }
.on-image .trust__item .icon { color: inherit; }

/* ---------- stacked proof icons ----------------------------
   Numerals stay on the typographic layout. Over a photo, and on
   the split card that sits under one, a check-circle reads faster
   than an ordinal that implies a sequence. */
.proof__check { width: 19px; height: 19px; color: var(--primary); flex: none; }
.on-image .proof__check { color: inherit; }

/* A glyph on a field label, and on the closing reassurance line. */
.field__icon { width: 14px; height: 14px; color: var(--muted); margin-right: 5px; vertical-align: -2px; }
.cta-note--lock { display: flex; align-items: flex-start; gap: 6px; }
.cta-note--lock .icon { width: 14px; height: 14px; flex: none; margin-top: 2px; }
/* The arrow on a button sits after the label with a small optical gap. */
.btn .icon { flex: none; }
.btn--primary .icon, .btn--on-image .icon { margin-left: 2px; }

/* ---------- depth ------------------------------------------
   The one soft shadow in the system, used sparingly: the things
   you touch (choice cards, the primary action) lift off the
   ground a little. No new colours, no second shadow. */
.qualifier--screen .choice { box-shadow: var(--shadow-soft); }
.qualifier--screen .choice:has(.choice__input:checked) { box-shadow: none; }
.btn--primary, .btn--on-image { box-shadow: var(--shadow-soft); }
.btn--primary:active, .btn--on-image:active { box-shadow: none; }

/* A hairline under the header on the typographic layout, where
   there is no photo to separate the band from the page. */
.page:not(.page--image):not(.page--split) > .topbar {
  border-bottom: 1px solid var(--rule);
}

/* The photo hero carries a deeper bottom scrim so the headline
   and the Start button sit confidently rather than floating. */
.hero-screen .bg-image::after {
  background: linear-gradient(
    to bottom,
    var(--scrim-0)     0%,
    var(--scrim-34)   30%,
    var(--scrim-62)   55%,
    var(--scrim-78)   72%,
    var(--scrim-100)  92%
  );
}

/* ---------- closing CTA -------------------------------------
   A page may end with a second cta after the sections. It gets
   room above it and the footer sits 24px below. */
.page__body > .sections + .btn,
.split__card > .sections + .btn { margin-top: var(--sp-6); }
.page__body > .btn + .footer,
.page__body > .cta-note + .footer,
.split__card > .btn + .footer,
.split__card > .cta-note + .footer { padding-top: var(--sp-5); }

/* ============================================================
   7. LONG-FORM BLOCKS
   ------------------------------------------------------------
   Composition pieces for a long editorial page (variant d-star).
   Same tokens, same two families, larger rhythm: the difference
   between a leaflet and an argument is scale and whitespace, not
   a new palette.
   ============================================================ */

/* ---------- .quote ------------------------------------------
   PURPOSE  One large pull quote, the page's held breath.
   MARKUP   <figure class="quote">
              <span class="quote__mark">“</span>
              <blockquote class="quote__text">…</blockquote>
              <figcaption class="quote__cite">…<span class="quote__role">…</span></figcaption>
            </figure>
   JSON     text -> .quote__text, attribution + role -> .quote__cite

   The mark is decorative and aria-hidden; it hangs left of the
   text so the first line still aligns to the column edge.
   ------------------------------------------------------------ */
.quote {
  margin: 0;
  padding: var(--sp-5) 0 var(--sp-4);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.quote__mark {
  display: block;
  font-family: var(--font-display);
  font-size: 56px;
  line-height: .6;
  color: var(--primary);
  /* Optical: the glyph carries its own left bearing, so pull it back
     to sit flush with the text below it. */
  margin-left: -4px;
  margin-bottom: var(--sp-2);
}
.quote__text {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-regular);
  font-size: 22px;
  line-height: 1.4;
  color: var(--ink);
  text-wrap: pretty;
}
.quote__cite {
  margin-top: var(--sp-4);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: var(--fw-semi);
  color: var(--muted);
}
.quote__role {
  display: block;
  margin-top: 2px;
  font-weight: var(--fw-regular);
}

/* ---------- .statement --------------------------------------
   A line in our own voice, never attributed. Default: Lora
   italic between hairlines, right-aligned. --band: a centred
   tinted panel, used to close a page. */
.statement {
  margin: 0;
  padding: var(--sp-5) 0 var(--sp-4);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  text-align: right;
}
.statement__text {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-regular);
  font-size: 22px;
  line-height: 1.4;
  color: var(--ink);
  text-wrap: pretty;
}
.statement__note {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  font-weight: var(--fw-semi);
  color: var(--muted);
}
.statement--band {
  margin: var(--sp-4) 0;
  padding: var(--sp-6) var(--sp-5);
  border: 0;
  border-radius: var(--radius);
  background: var(--tint);
  text-align: center;
}
.statement--band .statement__text {
  font-style: normal;
  font-weight: 600;
  font-size: 24px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.statement--band .statement__note {
  font-size: 16px;
  line-height: 1.5;
  font-weight: var(--fw-regular);
}

/* ---------- .steps--row -------------------------------------
   The three steps on a full-bleed green band directly under
   the hero, shown one at a time: a step slides in, holds, flies
   out to the left and the next takes its place, on a loop. The
   reader does almost nothing, so each step is a numeral and
   three words. Reduced motion shows all three side by side. */
.steps--row {
  margin: 0 calc(-1 * var(--gutter));
  padding: var(--sp-5) var(--gutter);
  background: var(--primary);
  color: var(--on-primary);
}
/* Flush against the hero above it. */
.page__body > .steps--row:first-child { margin-top: calc(-1 * var(--sp-6)); }
.steps--row .steps__heading {
  margin: 0 0 var(--sp-3);
  text-align: center;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--on-primary);
  opacity: .7;
}
.steps--row .steps__list {
  position: relative;
  height: 60px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.steps--row .steps__item {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  padding: 0;
  border: 0;
  opacity: 0;
  animation: am-cycle 9000ms var(--ease) infinite;
}
.steps--row .steps__item:nth-child(2) { animation-delay: 3000ms; }
.steps--row .steps__item:nth-child(3) { animation-delay: 6000ms; }
.steps--row .steps__num {
  flex: none;
  box-sizing: border-box;
  width: 52px;
  height: 52px;
  margin: 0;
  padding: 0;
  border-radius: 50%;
  background: var(--on-primary);
  color: var(--primary);
  font-family: var(--font-body);
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 52px;
  text-align: center;
  display: block;
}
.steps--row .steps__body { min-width: 0; }
.steps--row .steps__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 27px;
  line-height: 1.2;
  font-weight: 600;
  color: var(--on-primary);
  white-space: nowrap;
}
/* In from below, hold, out to the left, wait for the others. */
@keyframes am-cycle {
  0%     { opacity: 0; transform: translate(0, 10px); }
  5%     { opacity: 1; transform: translate(0, 0); }
  29%    { opacity: 1; transform: translate(0, 0); }
  33.3%  { opacity: 0; transform: translate(-32px, 0); }
  100%   { opacity: 0; transform: translate(-32px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .steps--row .steps__list { display: grid; grid-template-columns: repeat(3, 1fr); height: auto; gap: var(--sp-2); }
  .steps--row .steps__item { position: static; opacity: 1; animation: none; flex-direction: column; gap: var(--sp-2); text-align: center; }
  .steps--row .steps__title { font-size: 15px; white-space: normal; }
}

/* ---------- .home -------------------------------------------
   The Awakened Media home page: one full-screen photo, a line
   of ink text over the empty part of it, one mailto button. */
.home {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;     /* the words sit bottom-left at every size */
  min-height: 100vh;
  min-height: 100dvh;
  overflow: hidden;
  background: #d9d2c6;                 /* the plaza, while the photo loads */
}
.home__bg,
.home__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.home__content {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-5);
  max-width: 68%;               /* the queue runs down the right of the phone photo */
  padding: var(--sp-6) var(--gutter);
  padding-bottom: calc(var(--sp-7) + env(safe-area-inset-bottom, 0px));
}
.home__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
}
.home__mark { width: 22px; height: 22px; border-radius: 5px; }
.home__h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(26px, 6.8vw, 34px);
  line-height: 1.12;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-wrap: balance;
}
.home__cta { min-width: 0; width: auto; padding-inline: var(--sp-6); }
@media (min-width: 700px) {
  .home__content {
    max-width: 760px;
    gap: var(--sp-6);
    padding: 0 var(--sp-7) 9vh;
  }
  .home__h1 { font-size: clamp(40px, 4.6vw, 58px); }
}
.home-page { background: var(--ground); color: var(--ink); }

/* ---------- .bullets ----------------------------------------
   PURPOSE  One idea per line, with room around each. Not a dense
            list: the point is that a worried reader can take them
            one at a time.
   MARKUP   <section class="bullets">
              <h2 class="bullets__heading">…</h2>
              <ul class="bullets__list">
                <li class="bullets__item"><svg class="bullets__icon icon"/><span>…</span></li>
              </ul>
            </section>
   JSON     heading, items[] of string | { icon, text }
   ------------------------------------------------------------ */
.bullets__heading {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-semi);
  color: var(--ink);
}
.bullets__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.bullets__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: 17px;
  line-height: 1.5;
  color: var(--ink);
}
.bullets__icon {
  flex: none;
  width: 22px;
  height: 22px;
  color: var(--primary);
  /* Optical centring against a 17/1.5 line. */
  margin-top: 2px;
}
.on-image .bullets__icon { color: inherit; }

/* ---------- .steps ------------------------------------------
   PURPOSE  What happens, in order. The numerals are information:
            these steps are a sequence, so they are numbered.
   MARKUP   <section class="steps">
              <h2 class="steps__heading">…</h2>
              <ol class="steps__list">
                <li class="steps__item">
                  <span class="steps__num">1</span>
                  <div class="steps__body"><h3 class="steps__title">…</h3><p class="steps__text">…</p></div>
                </li>
              </ol>
            </section>
   JSON     heading, items[] of { title, text }

   A hairline runs down the left of the list, so the numerals read
   as points on one thread rather than three loose figures.
   ------------------------------------------------------------ */
.steps__heading {
  margin: 0 0 var(--sp-5);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-semi);
  color: var(--ink);
}
.steps__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
.steps__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
}
.steps__num {
  flex: none;
  width: 44px;
  font-family: var(--font-display);
  font-size: 38px;
  line-height: 1;
  font-weight: var(--fw-semi);
  color: var(--primary);
  font-variant-numeric: tabular-nums;
  /* Aligns the numeral's cap height with the title beside it. */
  margin-top: -4px;
  border-right: 1px solid var(--rule);
  padding-right: var(--sp-3);
}
.steps__body { min-width: 0; }
.steps__title {
  margin: 0 0 4px;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.35;
  font-weight: var(--fw-semi);
  color: var(--ink);
}
.steps__text {
  margin: 0;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--muted);
}

/* ---------- .band -------------------------------------------
   PURPOSE  The one loud moment: a full-bleed green band of two or
            three figures that are literal mechanics of the service.
   MARKUP   <section class="band">
              <ul class="band__list">
                <li class="band__item">
                  <span class="band__figure">3</span><span class="band__label">questions</span>
                </li>
              </ul>
            </section>
   JSON     items[] of { figure, label }

   It bleeds past the page gutter with negative inline margins, so it
   is the only element that touches both edges. That is what makes it
   read as a pivot rather than another card.
   ------------------------------------------------------------ */
.band {
  margin-inline: calc(var(--gutter) * -1);
  padding: var(--sp-6) var(--gutter);
  background: var(--primary);
  color: var(--on-primary);
}
.band__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-5) var(--sp-4);
}
.band__item {
  flex: 1 1 0;
  min-width: 88px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.band__figure {
  font-family: var(--font-display);
  font-size: 34px;
  line-height: 1.05;
  font-weight: var(--fw-semi);
  font-variant-numeric: tabular-nums;
}
.band__label {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  /* On the green ground the muted token would fail contrast, so the
     label is the same cream at reduced opacity. */
  opacity: .78;
}

/* ---------- .faq --------------------------------------------
   PURPOSE  Three or four questions, answered. Native <details>, so
            it works with lg.js blocked.
   MARKUP   <section class="faq">
              <h2 class="faq__heading">…</h2>
              <details class="faq__item">
                <summary class="faq__q">…<svg class="faq__chevron icon"/></summary>
                <p class="faq__a">…</p>
              </details>
            </section>
   JSON     heading, items[] of { q, a }

   The summary is the tap target and is held at --tap-min. The
   default disclosure triangle is removed on both engines.
   ------------------------------------------------------------ */
.faq__heading {
  margin: 0 0 var(--sp-4);
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  font-weight: var(--fw-semi);
  color: var(--ink);
}
.faq__item { border-top: 1px solid var(--rule); }
.faq__item:last-of-type { border-bottom: 1px solid var(--rule); }
.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: var(--tap-min);
  padding: var(--sp-3) 0;
  font-size: 17px;
  line-height: 1.4;
  font-weight: var(--fw-semi);
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__chevron {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--primary);
  transition: transform var(--dur-fast) var(--ease);
}
.faq__item[open] .faq__chevron { transform: rotate(180deg); }
.faq__a {
  margin: 0;
  padding: 0 0 var(--sp-4);
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--muted);
}

/* ---------- long-form rhythm --------------------------------
   These blocks are chapters, not rows, so they get more air than
   the body column's default 24px — and the band, which bleeds, gets
   more still on both sides. */
.page__body > .quote,
.split__card > .quote,
.page__body > .steps,
.split__card > .steps,
.page__body > .faq,
.split__card > .faq,
.page__body > .bullets,
.split__card > .bullets { margin-top: var(--sp-6); }
.page__body > .band,
.split__card > .band { margin-top: var(--sp-6); }
.page__body > .band + *,
.split__card > .band + * { margin-top: var(--sp-6); }


/* ============================================================
   8. STICKY CTA
   ------------------------------------------------------------
   PURPOSE  Once the whole first screen has scrolled away, the ask
            follows the reader down the page instead of making them
            scroll back for it. At every width: the page carries one
            CTA, at the top, so above 600px the bar narrows to the
            centred card rather than disappearing.
   MARKUP   <div class="sticky-cta" data-lg-sticky hidden>
              <a class="btn btn--primary btn--lg" data-lg-cta data-cta-position="sticky">…</a>
            </div>
   STATES   [hidden] until lg.js reveals it; .is-in slides it up.

   The system's rule that nothing is position:fixed was written for
   the first screen, where a fixed bar fights the in-app-browser
   toolbar over the CTA that is already visible. This bar only exists
   after that CTA has gone, and it pads for safe-area-inset-bottom,
   so it clears the toolbar rather than hiding under it.

   With JavaScript blocked the element keeps its `hidden` attribute
   and never appears, which is the correct fallback: the top CTA is
   a real link in the flow and the qualifier form stays in the page,
   so the ask is still reachable.
   ============================================================ */
.sticky-cta {
  position: fixed;
  z-index: var(--z-sticky);
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--sp-2) var(--gutter);
  padding-bottom: calc(var(--sp-2) + env(safe-area-inset-bottom, 0px));
  background: var(--ground);
  border-top: 1px solid var(--rule);
  /* Starts just below the viewport and slides up when revealed. Transform and
     opacity only, so it stays on the compositor. */
  transform: translateY(100%);
  opacity: 0;
  transition: transform var(--dur-base) var(--ease), opacity var(--dur-base) var(--ease);
}
.sticky-cta.is-in {
  transform: translateY(0);
  opacity: 1;
}
.sticky-cta .btn {
  width: 100%;
  /* 56px of button inside a 56px-tall bar's content box. */
  min-height: var(--cta-h-lg);
}

/* Room at the foot of the document so the bar never covers the last
   line of the footer. Added by lg.js only when the bar is live. */
.lg-has-sticky body,
body.lg-has-sticky {
  padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
}

@media (min-width: 600px) {
  /* Section 6 widens the body's inline padding to --sp-6, so the band's
     bleed has to widen with it or it sits proud on one edge. */
  .band { margin-inline: calc(var(--sp-6) * -1); padding-inline: var(--sp-6); }

  /* The page has one CTA now, at the top, so the bar has to carry the ask on
     desktop too. Constrained to the centred card so it reads as the foot of the
     column rather than a banner pinned across the window. Centring a fixed box
     needs both insets held at 0 with an auto margin; with left/right set to auto
     it would fall back to its static position instead. */
  .sticky-cta {
    max-width: var(--card-max);
    left: 0;
    right: 0;
    margin-inline: auto;
    border-inline: 1px solid var(--rule);
  }
}

@media (prefers-reduced-motion: reduce) {
  .sticky-cta {
    transition: none;
    /* Still arrives at its final state, just immediately. */
  }
}
