/* ==========================================================================
   VelosifyCredit design system
   One stylesheet for the marketing site, the order flow and the portal, so
   the whole product reads as one thing. Tokens first, then primitives, then
   the handful of page-specific blocks.
   ========================================================================== */

:root {
  /* Canvas: light, with a faint blue cast so white cards read as raised */
  --bg:            #f6f9fd;
  --bg-soft:       #eaf1fa;
  --surface:       #ffffff;
  --surface-2:     #f2f6fc;
  --surface-3:     #e2ebf6;
  --border:        rgba(13, 27, 47, 0.12);
  --border-strong: rgba(13, 27, 47, 0.20);

  /* Ink: every value below clears WCAG AA (4.5:1) on both --surface and
     --bg-soft, which are the two backgrounds body copy ever sits on. */
  --text:          #0d1b2f;
  --text-muted:    #495c78;
  --text-faint:    #5a6b84;

  /* Accents */
  --accent:        #1364df;
  --accent-deep:   #0c4bb0;
  /* The light stop is capped so WHITE TEXT clears 4.5:1 across the whole
     gradient, not just at the dark end. Anything lighter than this fails on
     the right-hand side of a filled button. */
  --accent-light:  #2370d4;
  --gradient:      linear-gradient(120deg, #1364df 0%, #2370d4 100%);
  --gradient-soft: linear-gradient(120deg, rgba(19,100,223,.10), rgba(63,151,246,.10));

  /* Semantic: each is used as text ON its own tint (badges, alerts), which
     composites lighter than plain white. These values clear 4.5:1 in that
     worst case with headroom, not just against a white card. */
  --success:       #065c3b;
  --success-bg:    rgba(6, 92, 59, 0.10);
  --warn:          #7a4700;
  --warn-bg:       rgba(122, 71, 0, 0.10);
  --danger:        #9e1b32;
  --danger-bg:     rgba(158, 27, 50, 0.10);

  /* Shape */
  /* Squared throughout. Kept as tokens so a radius can be reintroduced in
     one place if that ever changes. */
  --radius-sm:  0;
  --radius:     0;
  --radius-lg:  0;
  --radius-xl:  0;

  --shadow:     0 18px 42px rgba(13, 27, 47, 0.10);
  --shadow-sm:  0 3px 12px rgba(13, 27, 47, 0.06);
  --glow:       0 8px 22px rgba(19, 100, 223, 0.26);

  --container: 1140px;
  --nav-h: 72px;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          Helvetica, Arial, sans-serif;
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

/* An email address or URL is a single unbreakable token. Printed at phone
   width it cannot wrap, so it widens the page and the whole document scrolls
   sideways. These are allowed to break mid-token instead. */
body { overflow-wrap: break-word; }
a[href^="mailto:"], a[href^="http"] { overflow-wrap: anywhere; }
button, input, select, textarea { font: inherit; color: inherit; }

h1, h2, h3, h4 {
  margin: 0 0 .5em;
  line-height: 1.15;
  letter-spacing: -0.022em;
  font-weight: 700;
}
h1 { font-size: clamp(2.2rem, 5vw, 3.6rem); }
h2 { font-size: clamp(1.7rem, 3.4vw, 2.5rem); }
h3 { font-size: 1.25rem; }
p  { margin: 0 0 1em; }

::selection { background: rgba(19, 100, 223, .18); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 0;
}

/* --- Layout ------------------------------------------------------------ */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: 24px;
}
.container-narrow { max-width: 760px; }
.container-form   { max-width: 560px; }

.section { padding: clamp(56px, 7.4vw, 94px) 0; }
.section-tight { padding: clamp(40px, 5.4vw, 66px) 0; }
/* One measure for the space under a section's heading block, so five
   sections cannot drift to five different values. */
.section-head {
  text-align: center;
  margin: 0 auto clamp(28px, 3.2vw, 42px);
}
.section-head > :last-child { margin-bottom: 0; }
.section-alt { background: var(--bg-soft); }

.stack { min-width: 0; }
.stack > * + * { margin-top: 16px; }
.row { display: flex; gap: 16px; flex-wrap: wrap; }
.row-between { display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.grid { display: grid; gap: 24px; }
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.text-center { text-align: center; }
.muted  { color: var(--text-muted); }
.faint  { color: var(--text-faint); }
.small  { font-size: .875rem; }
.tiny   { font-size: .78rem; }
.strong { font-weight: 650; }
.nowrap { white-space: nowrap; }
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; }
.mt-2 { margin-top: 24px; } .mt-3 { margin-top: 36px; }
.hide { display: none !important; }
/* Present for assistive tech and search engines, absent visually. Clip
   rather than display:none, which would remove it from the accessibility
   tree entirely. */
.visually-hidden {
  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;
}

.gradient-text {
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.lead {
  font-size: clamp(1.02rem, 1.6vw, 1.18rem);
  color: var(--text-muted);
  max-width: 62ch;
}
.lead-center { margin-inline: auto; }

/* --- Buttons ----------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 26px;
  border: 1px solid transparent;
  border-radius: 0;
  background: var(--surface-2);
  color: var(--text);
  font-weight: 650;
  font-size: .95rem;
  cursor: pointer;
  transition: transform .16s ease, box-shadow .16s ease,
              background .16s ease, border-color .16s ease;
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn-primary {
  background: var(--gradient);
  color: #fff;
  box-shadow: var(--glow);
}
.btn-primary:hover { box-shadow: 0 12px 30px rgba(19, 100, 223, .34); }

.btn-ghost {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--text);
}
.btn-ghost:hover { background: var(--surface-2); }

.btn-quiet {
  background: transparent;
  color: var(--text-muted);
  padding: 8px 14px;
}
.btn-quiet:hover { color: var(--text); background: var(--surface-2); }

.btn-danger { background: var(--danger-bg); color: var(--danger); border-color: rgba(158,27,50,.28); }
.btn-danger:hover { background: rgba(158,27,50,.16); }

.btn-sm { padding: 9px 16px; font-size: .85rem; }
.btn-lg { padding: 17px 34px; font-size: 1.02rem; }
/* A full-width button already has its width decided, so nowrap can only push
   the page wider than the viewport. Long labels wrap instead. */
.btn-block { width: 100%; white-space: normal; }
.btn[disabled] { opacity: .5; cursor: not-allowed; transform: none; }

/* --- Cards ------------------------------------------------------------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 28px;
  box-shadow: var(--shadow-sm);
}
.card-pad-sm { padding: 20px; }
.card-glass {
  background: linear-gradient(180deg, #ffffff, #f7fafe);
  box-shadow: var(--shadow);
}
.card-hover { transition: border-color .18s ease, transform .18s ease; }
.card-hover:hover { border-color: var(--border-strong); transform: translateY(-3px); }

.card-title { font-size: 1.05rem; font-weight: 680; margin-bottom: 6px; }

/* --- Badges & pills ---------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: 0;
  font-size: .74rem;
  font-weight: 650;
  letter-spacing: .02em;
  background: var(--surface-3);
  color: var(--text-muted);
  border: 1px solid var(--border);
}
.badge-success { background: var(--success-bg); color: var(--success); border-color: rgba(6,92,59,.26); }
.badge-warn    { background: var(--warn-bg);    color: var(--warn);    border-color: rgba(122,71,0,.26); }
.badge-danger  { background: var(--danger-bg);  color: var(--danger);  border-color: rgba(158,27,50,.26); }
.badge-accent  { background: rgba(19,100,223,.10); color: var(--accent-deep); border-color: rgba(19,100,223,.24); }


/* --- Forms ------------------------------------------------------------- */
.field { margin-bottom: 18px; }
.field label {
  display: block;
  font-size: .84rem;
  font-weight: 620;
  margin-bottom: 7px;
  color: var(--text);
}
.field .hint { font-size: .8rem; color: var(--text-faint); margin-top: 6px; }

.input, .select, .textarea {
  width: 100%;
  padding: 13px 15px;
  background: var(--bg-soft);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  color: var(--text);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(19, 100, 223, .16);
}
.textarea { min-height: 110px; resize: vertical; }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%235a6b84' d='M6 8 0 0h12z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 40px;
}

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.check {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 16px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 0;
  cursor: pointer;
  /* Sits inside .field, which bolds its label, so reset that for body copy. */
  font-weight: 400;
}
.check input { margin-top: 3px; width: 17px; height: 17px; accent-color: var(--accent); flex: none; }
.check span { font-size: .87rem; color: var(--text-muted); line-height: 1.55; }

/* --- Alerts & flashes -------------------------------------------------- */
.alert {
  padding: 14px 18px;
  border-radius: 0;
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: .9rem;
  margin-bottom: 18px;
}
.alert-success { background: var(--success-bg); border-color: rgba(6,92,59,.28); color: #054d31; }
.alert-error   { background: var(--danger-bg);  border-color: rgba(158,27,50,.28); color: #7d1224; }
.alert-info    { background: rgba(19,100,223,.07); border-color: rgba(19,100,223,.22); color: #0c4bb0; }
.alert-warn    { background: var(--warn-bg);     border-color: rgba(122,71,0,.28);   color: #653b00; }

/* --- Site nav ---------------------------------------------------------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border);
}
.nav .container { display: flex; align-items: center; justify-content: space-between; gap: 20px; }

.brand { display: inline-flex; align-items: center; gap: 11px; font-weight: 750; letter-spacing: -.02em; font-size: 1.06rem; }
.brand-mark {
  width: 30px; height: 30px;
  border-radius: 0;
  background: var(--gradient);
  display: grid; place-items: center;
  font-size: .9rem; font-weight: 800; color: #fff;
  box-shadow: 0 3px 10px rgba(19,100,223,.32);
}
.brand small { color: var(--text-faint); font-weight: 500; }

.nav-links { display: flex; align-items: center; gap: 6px; }
/* :not(.btn) matters, because `.nav-links a` is more specific than `.btn-primary`,
   so without it this rule wins and paints the header CTA's label muted grey
   on top of its blue gradient. */
.nav-links a:not(.btn) {
  padding: 9px 14px;
  border-radius: 0;
  font-size: .9rem;
  color: var(--text-muted);
  transition: color .15s ease, background .15s ease;
}
.nav-links a:not(.btn):hover { color: var(--text); background: var(--surface-2); }
.nav-toggle { display: none; background: none; border: 0; padding: 8px; cursor: pointer; color: var(--text); }

/* --- Hero --------------------------------------------------------------
   Logo, two buttons, nothing else. The photograph carries a dark mask
   because the mark is white; every scrim value here is verified against the
   real image pixels rather than chosen by eye, since a photo's dark and
   light regions both sit under the same text.
   ---------------------------------------------------------------------- */
.hero {
  position: relative;
  overflow: hidden;
  min-height: min(74vh, 660px);
  display: grid;
  place-items: center;
  text-align: center;
  padding: clamp(64px, 10vw, 120px) 0;
  background-image: url("../img/hero-home.jpg");
  background-size: cover;
  background-position: center 38%;
  background-repeat: no-repeat;
}
/* The mask is its own layer rather than a background-image stop, so the
   mobile video can sit under it and be darkened by exactly the same wash
   the photograph gets. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg,
    rgba(9, 18, 42, 0.72) 0%,
    rgba(9, 18, 42, 0.64) 45%,
    rgba(9, 18, 42, 0.76) 100%);
}
.hero .container { position: relative; z-index: 2; }

.hero-logo {
  width: min(560px, 84vw);
  height: auto;
  margin: 0 auto;
}

/* Equal-width buttons. A grid with 1fr columns sizes both to the wider
   label, so they stay matched if either string changes, which a fixed
   min-width would not. */
.hero-cta {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 14px;
  width: min(520px, 100%);
  margin: clamp(32px, 5vw, 46px) auto 0;
}
.hero-cta .btn { width: 100%; }

/* Outline button for use on the masked photograph. */
.btn-on-dark {
  background: rgba(255, 255, 255, .10);
  border-color: rgba(255, 255, 255, .55);
  color: #fff;
  backdrop-filter: blur(4px);
}
.btn-on-dark:hover {
  background: rgba(255, 255, 255, .2);
  border-color: #fff;
}

/* --- Three-bureau gauges -------------------------------------------------
   A semicircular dial per bureau, spanning 300 to 850. Script animates the
   arc, the bubble and the number together; the band class on .gauge drives
   the colour so all three stay in step through one transition.

   Colours here are the vivid pair of the semantic tokens: the arc is a
   graphic (needs 3:1) and the number is large text, and every value clears
   its threshold on the white card with room to spare.
   ------------------------------------------------------------------------ */
.gauge-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
}

.gauge {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  padding: 26px 20px 24px;
  text-align: center;
  /* Set by script during the reset beat so the dial can fade out and back
     in rather than snapping from 806 to 512 in one frame. */
  --gauge-fade: 1;
}

.gauge-dial {
  width: min(280px, 100%);
  margin: 0 auto;
  opacity: var(--gauge-fade);
}
.gauge-dial > svg { display: block; width: 100%; height: auto; }

.gauge-track,
.gauge-arc {
  fill: none;
  stroke-width: 13;
  stroke-linecap: round;
}
.gauge-track { stroke: var(--surface-3); }
.gauge-arc   { transition: stroke .45s ease; }

.gauge-bubble {
  fill: var(--surface);
  stroke-width: 4.5;
  transition: stroke .45s ease;
}
.gauge-halo { opacity: .16; transition: fill .45s ease; }

/* An SVG <text> node, so the reading scales with the dial and stays centred
   in the arc at any card width. Size and placement are in the viewBox. */
.gauge-score {
  font-weight: 800;
  letter-spacing: -.6px;
  font-variant-numeric: tabular-nums;
  transition: fill .45s ease;
}

/* All three files share one canvas with the lettering already sat on a
   common baseline at a common x-height, so one width lines the row up. */
.gauge-logo {
  display: block;
  width: min(170px, 100%);
  height: auto;
  margin: 16px auto 0;
}

/* Bands. Under 620 poor, 620 to 739 fair, 740 and up good. */
.gauge.is-poor .gauge-arc,
.gauge.is-poor .gauge-bubble { stroke: #d92d4e; }
.gauge.is-poor .gauge-halo   { fill:   #d92d4e; }
.gauge.is-poor .gauge-score  { fill:   #c11f3c; }

.gauge.is-fair .gauge-arc,
.gauge.is-fair .gauge-bubble { stroke: #c07600; }
.gauge.is-fair .gauge-halo   { fill:   #c07600; }
.gauge.is-fair .gauge-score  { fill:   #9a5a00; }

.gauge.is-good .gauge-arc,
.gauge.is-good .gauge-bubble { stroke: #0f9f68; }
.gauge.is-good .gauge-halo   { fill:   #0f9f68; }
.gauge.is-good .gauge-score  { fill:   #07734a; }

/* Held to one line at every width. The heading measures 14.6em wide, and the
   container keeps 24px of padding each side, so the largest size that still
   fits is (100vw - 48px) / 14.6. The vw term below sits under that with a
   little slack for whatever font actually loads. */
.bureau-head {
  white-space: nowrap;
  font-size: min(2.5rem, calc(6.2vw - 3px));
}

.gauge-note {
  margin: 22px 0 0;
  text-align: center;
  font-size: .76rem;
  color: var(--text-faint);
}

/* --- How it works: connected flow ---------------------------------------
   Four markers sitting on a single track, so the steps read as one process
   rather than four unrelated cards. The track is a static rule with a filled
   span on top; script grows the fill and lights each marker as it arrives.
   ------------------------------------------------------------------------ */
.flow {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 24px;
}

/* Runs between the centres of the first and last markers: with four equal
   columns those sit at 12.5% and 87.5%. */
.flow-track {
  position: absolute;
  top: 23px;
  left: 12.5%;
  right: 12.5%;
  height: 2px;
  background: var(--surface-3);
}
.flow-fill {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--gradient);
  transform-origin: left center;
}

.flow-step {
  position: relative;
  text-align: center;
  display: flex;
  flex-direction: column;
}

.flow-num {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin: 0 auto 20px;
  background: var(--surface);
  border: 2px solid var(--surface-3);
  color: var(--text-faint);
  font-weight: 750;
  font-size: 1.02rem;
  transition: background .45s ease, border-color .45s ease,
              color .45s ease, box-shadow .45s ease, transform .45s ease;
}
.flow-step.is-on .flow-num {
  background: var(--gradient);
  border-color: transparent;
  color: #fff;
  box-shadow: var(--glow);
}

.flow-card {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  padding: 24px 22px;
  text-align: left;
  flex: 1;
  transition: opacity .5s ease, transform .5s cubic-bezier(.22, .8, .25, 1);
}
.flow-card h3 { font-size: 1.08rem; margin-bottom: 8px; }

/* Animated state, applied by script, so nothing is hidden without JS. */
.flow.is-armed .flow-fill { transform: scaleX(0); }
.flow.is-armed .flow-step:not(.is-on) .flow-num { transform: scale(.86); }
.flow.is-armed .flow-step:not(.is-on) .flow-card { opacity: 0; transform: translateY(14px); }
.flow.is-running .flow-fill {
  transform: scaleX(1);
  transition: transform 2s cubic-bezier(.35, 0, .2, 1);
}


/* --- What we challenge -------------------------------------------------- */
.challenge-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  /* Equal rows, so every card is the same height at every width. Without
     this each row sizes to its own tallest card and the grid steps. */
  grid-auto-rows: 1fr;
  gap: 14px;
}

.challenge-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  padding: 20px 18px;
  overflow: hidden;
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}
/* A rule that draws across the top on hover, rather than the whole card
   changing colour. Keeps the grid calm when the pointer moves over it. */
.challenge-card::after {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  height: 2px;
  width: 0;
  background: var(--gradient);
  transition: width .3s cubic-bezier(.22, .8, .25, 1);
}
.challenge-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: rgba(19, 100, 223, .28);
}
.challenge-card:hover::after { width: 100%; }

.challenge-ico {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  background: var(--gradient-soft);
  border: 1px solid rgba(19, 100, 223, .16);
  color: var(--accent-deep);
  transition: background .22s ease, color .22s ease;
}
.challenge-ico svg { width: 21px; height: 21px; }
.challenge-card:hover .challenge-ico {
  background: var(--gradient);
  color: #fff;
  border-color: transparent;
}

.challenge-card h3 {
  font-size: .98rem;
  line-height: 1.25;
  margin-bottom: 6px;
  /* Two-word titles like "Credit card debt" have to be able to break, or a
     narrow column pushes the whole grid wider than its container. */
  overflow-wrap: break-word;
}

.challenge-note {
  max-width: 66ch;
  margin: 36px auto 0;
  padding-left: 18px;
  border-left: 2px solid rgba(19, 100, 223, .3);
  font-size: .88rem;
  color: var(--text-muted);
  text-align: left;
}

/* --- Feature list ------------------------------------------------------ */
.feature-list { list-style: none; margin: 0; padding: 0; }
.feature-list li {
  display: flex; gap: 13px; align-items: flex-start;
  padding: 11px 0;
  color: var(--text-muted);
  font-size: .93rem;
}
.feature-list li + li { border-top: 1px solid var(--border); }
.feature-list .tick { flex: none; margin-top: 1px; }

/* --- Pricing ----------------------------------------------------------- */
.price-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 40px;
  overflow: hidden;
}
.price-card::before {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(520px 300px at 50% -60px, rgba(19,100,223,.13), transparent 68%);
  pointer-events: none;
}
/* A rule across the top so the card announces itself rather than sitting
   there as one more white rectangle. */
.price-card::after {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--gradient);
}
.price-card > * { position: relative; }

.price-head {
  text-align: center;
  padding-bottom: 28px;
  margin-bottom: 26px;
  border-bottom: 1px solid var(--border);
}
.price-head .badge { margin-bottom: 20px; }

.price-figure { display: block; margin-bottom: 12px; }
.price-num {
  display: block;
  font-size: clamp(3.6rem, 8vw, 5rem);
  font-weight: 800;
  letter-spacing: -.045em;
  line-height: .95;
  background: var(--gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.price-once {
  display: inline-block;
  margin-top: 12px;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.price-includes { margin: 0 0 28px; }
.price-includes li { padding: 12px 0; }

/* --- FAQ --------------------------------------------------------------- */
.faq-item {
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  margin-bottom: 12px;
  overflow: hidden;
}
.faq-item summary {
  list-style: none;
  cursor: pointer;
  padding: 20px 24px;
  font-weight: 620;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  font-size: 1.3rem;
  color: var(--accent);
  transition: transform .2s ease;
  flex: none;
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-body { padding: 0 24px 22px; color: var(--text-muted); font-size: .93rem; }
.faq-body p:last-child { margin-bottom: 0; }

/* --- Footer ------------------------------------------------------------ */
.footer {
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
  padding: 56px 0 40px;
  font-size: .88rem;
  color: var(--text-muted);
}
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr; gap: 40px; margin-bottom: 40px; }
.footer h4 { font-size: .78rem; text-transform: uppercase; letter-spacing: .14em; color: var(--text-faint); margin-bottom: 14px; }
.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { margin-bottom: 9px; }
.footer a:hover { color: var(--text); }
.footer-legal {
  border-top: 1px solid var(--border);
  padding-top: 26px;
  font-size: .78rem;
  color: var(--text-faint);
  line-height: 1.7;
}

/* --- Portal shell ------------------------------------------------------ */
.app-shell {
  display: grid;
  grid-template-columns: 250px 1fr;
  min-height: 100vh;
  background: linear-gradient(to right,
    var(--bg-soft) 0 250px,
    var(--border) 250px 251px,
    transparent 251px);
}

.sidebar {
  background: var(--bg-soft);
  /* Same guard as .app-main: on mobile this becomes a horizontally scrolling
     bar, and without min-width:0 its min-content would size the grid column
     and scroll the whole document instead of just the bar. */
  min-width: 0;
  padding: 22px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sidebar .brand { padding: 4px 10px 22px; }
.side-link {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 14px;
  border-radius: 0;
  color: var(--text-muted);
  font-size: .92rem;
  font-weight: 550;
  transition: background .15s ease, color .15s ease;
}
.side-link:hover { background: var(--surface-2); color: var(--text); }
.side-link.active { background: var(--gradient-soft); color: var(--accent-deep); border: 1px solid rgba(19,100,223,.22); }
.side-link .ico { width: 18px; text-align: center; flex: none; opacity: .9; }
.side-foot { margin-top: auto; padding-top: 18px; border-top: 1px solid var(--border); }

/* min-width:0 matters: a grid item's automatic minimum is its min-content
   width, so a wide table or a long filename would widen the whole column and
   scroll the document sideways instead of scrolling inside .table-wrap. */
.app-main { padding: 34px 38px 70px; max-width: 1080px; min-width: 0; }
.page-head { margin-bottom: 30px; }
.page-head h1 { font-size: clamp(1.6rem, 3vw, 2.1rem); margin-bottom: 6px; }

/* A main column beside a rail of smaller cards. Was written as an inline
   style on both pages, which no media query can override, so on a phone it
   stayed two-up at 200px and 133px and everything inside it wrapped to
   ribbons. As a class it can collapse. */
.portal-split {
  grid-template-columns: 1.5fr 1fr;
  gap: 24px;
}

/* Used where an operation half-succeeded and the operator has to act: the
   account exists, the email did not go. Loud enough not to be scrolled past.
   ------------------------------------------------------------------------ */
.notice-strong {
  border: 1px solid var(--border);
  border-left: 4px solid var(--warn);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  padding: 22px 24px;
  margin-bottom: 22px;
}
.notice-strong h3 { font-size: 1.05rem; margin-bottom: 10px; }
.notice-strong p { font-size: .9rem; color: var(--text-muted); }
.token-box {
  background: var(--surface-2);
  border: 1px solid var(--border);
  padding: 12px 14px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82rem;
  /* A URL is one unbreakable token; without this it widens the page. */
  overflow-wrap: anywhere;
  user-select: all;
  margin-bottom: 10px;
}

/* --- Portal chrome on a phone -------------------------------------------
   Both of these are display:none until the mobile breakpoint switches them
   on, so a wide screen keeps the sidebar and never renders either.
   ------------------------------------------------------------------------ */
.app-topbar {
  display: none;
  position: sticky;
  top: 0;
  z-index: 40;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  padding-top: calc(10px + env(safe-area-inset-top));
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.app-topbar .brand { padding: 0; font-size: 1.02rem; }
.app-topbar-actions { display: flex; gap: 6px; }
.topbar-btn {
  display: grid;
  place-items: center;
  /* 44px is the smallest target most people can hit reliably. */
  width: 44px;
  height: 44px;
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1;
}
.topbar-btn:hover, .topbar-btn:focus-visible {
  background: var(--surface-2);
  color: var(--text);
}

.app-tabs {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: rgba(255, 255, 255, .95);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
  /* Clears the home indicator on a notched phone. Falls back to plain
     padding where the inset is unsupported or zero. */
  padding-bottom: env(safe-area-inset-bottom);
}
.app-tab {
  display: grid;
  justify-items: center;
  gap: 3px;
  padding: 9px 4px 8px;
  color: var(--text-faint);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .01em;
  min-height: 52px;
  border-top: 2px solid transparent;
  transition: color .15s ease, border-color .15s ease;
}
.app-tab-ico { font-size: 1.12rem; line-height: 1; }
.app-tab-label { line-height: 1.1; }
.app-tab.active {
  color: var(--accent-deep);
  border-top-color: var(--accent);
  background: var(--gradient-soft);
}
.app-tab:active { background: var(--surface-2); }

/* --- Progress tracker -------------------------------------------------- */
.tracker { display: flex; gap: 0; margin: 6px 0 4px; }
/* min-width:0 lets the six steps share a narrow viewport; without it each
   label's min-content sets a floor and the tracker widens the page. */
.tracker-step { flex: 1; min-width: 0; text-align: center; position: relative; padding-top: 30px; }
.tracker-step::before {
  content: "";
  position: absolute; top: 9px; left: 0; right: 0;
  height: 3px; background: var(--surface-3);
}
.tracker-step:first-child::before { left: 50%; border-radius: 0; }
.tracker-step:last-child::before  { right: 50%; border-radius: 0; }
.tracker-step .knob {
  position: absolute; top: 3px; left: 50%; transform: translateX(-50%);
  width: 15px; height: 15px; border-radius: 0;
  background: var(--surface-3);
  border: 3px solid var(--bg);
  z-index: 1;
}
.tracker-step.done::before { background: var(--accent); }
.tracker-step.done .knob   { background: var(--accent); }
.tracker-step.current .knob {
  background: var(--gradient);
  box-shadow: 0 0 0 4px rgba(19,100,223,.18);
  width: 17px; height: 17px; top: 2px;
}
.tracker-step .name {
  font-size: .76rem;
  color: var(--text-faint);
  display: block;
  padding: 0 2px;
  overflow-wrap: anywhere;
  hyphens: auto;
}
.tracker-step.done .name, .tracker-step.current .name { color: var(--text); font-weight: 600; }

/* Only rendered on a phone, where the per-step labels are hidden. */
.tracker-caption {
  display: none;
  margin: 12px 0 0;
  font-size: .82rem;
  color: var(--text-muted);
}
.tracker-caption strong { color: var(--text); }

/* --- Checklist / documents -------------------------------------------- */
/* Two up on a wide screen. Six full-width rows was a page you scrolled
   rather than a page you scanned, and each row was mostly empty to its
   right. */
.doc-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
  margin-bottom: 30px;
}
.doc-group-head { margin: 0 0 14px; }
.doc-group-head h2 { font-size: 1.08rem; margin-bottom: 2px; }

.doc-item {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  padding: 22px 24px;
  overflow: hidden;
  transition: transform .22s cubic-bezier(.22, .8, .25, 1),
              box-shadow .22s ease, border-color .22s ease;
}
/* The same rule-sweep the landing page cards use, so the portal and the
   marketing site behave like one product. */
.doc-item::after {
  content: "";
  position: absolute;
  inset: 0 auto auto 0;
  height: 2px;
  width: 0;
  background: var(--gradient);
  transition: width .35s cubic-bezier(.22, .8, .25, 1);
}
.doc-item:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: rgba(19, 100, 223, .28);
}
.doc-item:hover::after { width: 100%; }
/* Keyboard users get the same signal as the pointer does. */
.doc-item:focus-within {
  border-color: rgba(19, 100, 223, .28);
  box-shadow: var(--shadow-sm);
}
.doc-item:focus-within::after { width: 100%; }

.doc-item.satisfied { border-color: rgba(6, 92, 59, .30); }
.doc-item.satisfied::after { background: var(--success); }
.doc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.doc-check {
  width: 26px; height: 26px; border-radius: 0; flex: none;
  display: grid; place-items: center;
  border: 2px solid var(--border-strong);
  color: transparent;
  font-size: .8rem; font-weight: 800;
  margin-right: 4px;
  transition: transform .22s cubic-bezier(.34, 1.56, .64, 1),
              background .22s ease, border-color .22s ease;
}
.doc-item:hover .doc-check { transform: scale(1.08); }
.doc-item:hover .doc-check:not(.doc-item.satisfied .doc-check) {
  border-color: var(--accent);
}
.doc-item.satisfied .doc-check { background: var(--success); border-color: var(--success); color: #ffffff; }
.doc-title-wrap { display: flex; gap: 12px; align-items: flex-start; }

.file-row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 14px;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: 0;
  margin-top: 10px;
  font-size: .87rem;
}
/* Was flex-wrap, which on a phone dropped the delete control onto its own
   line under the filename and left a stray x floating in the row. A grid
   pins each part: icon, name that truncates, status, control. */
.file-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  flex-wrap: nowrap;
}
.file-row .name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-row form { display: flex; }
/* A 36px square is the smallest comfortable target for a destructive control
   that sits next to text. */
.file-row .btn-quiet { padding: 0; width: 36px; height: 36px; display: grid; place-items: center; }
.file-ico { width: 30px; height: 30px; border-radius: 0; background: var(--surface-3); display: grid; place-items: center; font-size: .68rem; font-weight: 700; color: var(--text-muted); flex: none; }

.dropzone {
  display: grid;
  justify-items: center;
  gap: 8px;
  border: 1.5px dashed var(--border-strong);
  border-radius: 0;
  padding: 20px 18px;
  text-align: center;
  color: var(--text-muted);
  font-size: .88rem;
  cursor: pointer;
  background: var(--bg-soft);
  transition: border-color .2s ease, background .2s ease,
              transform .2s cubic-bezier(.22, .8, .25, 1);
}
.dropzone-ico {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  color: var(--accent);
  opacity: .55;
  transition: transform .28s cubic-bezier(.34, 1.4, .64, 1), opacity .2s ease;
}
.dropzone-ico svg { width: 20px; height: 20px; }
.dropzone:hover, .dropzone:focus-visible {
  border-color: var(--accent);
  background: rgba(19, 100, 223, .05);
}
/* The arrow lifts on hover and further while a file is actually over the
   target, so the zone reads as somewhere to drop rather than a grey box. */
.dropzone:hover .dropzone-ico,
.dropzone:focus-visible .dropzone-ico { transform: translateY(-3px); opacity: 1; }
.dropzone.dragover {
  border-style: solid;
  border-color: var(--accent);
  background: rgba(19, 100, 223, .09);
  transform: scale(1.01);
}
.dropzone.dragover .dropzone-ico { transform: translateY(-6px); opacity: 1; }
.dropzone input { display: none; }
.dropzone .picked { color: var(--accent-deep); font-weight: 600; }

/* --- Timeline ---------------------------------------------------------- */
.timeline { list-style: none; margin: 0; padding: 0 0 0 26px; position: relative; }
.timeline::before {
  content: ""; position: absolute; left: 5px; top: 8px; bottom: 8px;
  width: 2px; background: var(--surface-3);
}
.timeline li { position: relative; padding-bottom: 24px; }
.timeline li:last-child { padding-bottom: 0; }
.timeline li::before {
  content: ""; position: absolute; left: -26px; top: 6px;
  width: 12px; height: 12px; border-radius: 0;
  background: var(--surface-3); border: 2px solid var(--bg);
}
.timeline li:first-child::before { background: var(--gradient); }
.timeline .t-title { font-weight: 620; font-size: .95rem; }
.timeline .t-meta  { font-size: .76rem; color: var(--text-faint); margin-bottom: 4px; }
.timeline .t-body  { font-size: .89rem; color: var(--text-muted); }

/* --- Tables ------------------------------------------------------------ */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 0; }
table.data { width: 100%; border-collapse: collapse; font-size: .9rem; }
table.data th {
  text-align: left;
  padding: 14px 18px;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-faint);
  background: var(--bg-soft);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
table.data td { padding: 15px 18px; border-bottom: 1px solid var(--border); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: rgba(19, 100, 223, .04); }

/* --- Stat tiles -------------------------------------------------------- */
.stat { background: var(--surface); border: 1px solid var(--border); border-radius: 0; padding: 20px 22px; }
.stat .k { font-size: .74rem; text-transform: uppercase; letter-spacing: .1em; color: var(--text-faint); margin-bottom: 8px; }
.stat .v { font-size: 1.65rem; font-weight: 750; letter-spacing: -.02em; line-height: 1.1; }

/* --- Order page --------------------------------------------------------- */
.order-head { margin-bottom: 32px; max-width: 44ch; }

/* Was borrowing .hero-grid, which the hero no longer needs now that it is
   just a logo and two buttons. Its own class, so the two cannot drift. */
.order-layout {
  display: grid;
  grid-template-columns: 1.05fr .95fr;
  gap: 48px;
  align-items: flex-start;
}
.order-summary-card { position: sticky; top: 96px; }

.agreement-scroll {
  max-height: 210px;
  overflow-y: auto;
  background: var(--bg-soft);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  font-size: .83rem;
  line-height: 1.65;
  color: var(--text-muted);
  /* Momentum scrolling, and keep the gesture inside the box rather than
     dragging the page once it bottoms out. */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

@media (max-width: 980px) {
  /* Price and inclusions before the form. Reading order on a phone is
     top-to-bottom, so a summary in the second column lands after six fields
     of typing, which is exactly backwards for a page that has to sell. */
  .order-layout { display: flex; flex-direction: column; }
  .order-layout .order-summary-col { order: -1; }
  /* Sticky is meaningless once the columns stack. */
  .order-summary-card { position: static; }
  .agreement-scroll { max-height: 260px; }
}

/* --- Legal documents --------------------------------------------------- */
.legal-doc { font-size: .95rem; color: var(--text-muted); }
.legal-doc h2 { font-size: 1.2rem; color: var(--text); margin-top: 34px; }
.legal-doc h3 { font-size: 1rem; color: var(--text); margin-top: 24px; }
.legal-doc ul { padding-left: 20px; }
.legal-doc li { margin-bottom: 8px; }
.legal-doc .notice {
  border-left: 3px solid var(--accent);
  background: var(--surface);
  padding: 18px 22px;
  border-radius: 0;
  margin: 22px 0;
  color: var(--text);
}

/* Statutory text. Set apart from our own prose so a reader can see at a
   glance where our words stop and the law's begin, and so it prints as a
   discrete document. */
.croa-statute,
.croa-form {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  padding: clamp(22px, 3.4vw, 34px);
  margin: 26px 0;
  color: var(--text);
}
.croa-title {
  font-size: 1.05rem;
  margin: 0 0 18px;
  text-align: center;
  letter-spacing: .01em;
}
.legal-doc .croa-statute h2.croa-title,
.legal-doc .croa-form h2.croa-title { margin-top: 0; }
.croa-address { margin-bottom: 0; }
/* The statute requires this wording in bold face type. */
.croa-bold { font-weight: 700; }
.croa-blank {
  display: inline-block;
  min-width: 160px;
  border-bottom: 1px solid var(--text-faint);
  vertical-align: baseline;
}
.croa-copy-label { margin: 30px 0 -14px; }
.croa-sign {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: 22px;
  margin-top: 34px;
}
.croa-rule {
  display: block;
  border-bottom: 1px solid var(--text-faint);
  height: 26px;
}
.croa-caption {
  display: block;
  margin-top: 6px;
  font-size: .78rem;
  color: var(--text-faint);
}

/* The statutory cancellation notice on the order form. It has to be bold and
   immediately next to the signature field, so it gets a little emphasis of
   its own rather than reading as one more paragraph of fine print. */
.sign-notice {
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
  padding: 14px 18px;
  margin: 0 0 20px;
  font-size: .88rem;
  color: var(--text);
}
.sign-notice a { text-decoration: underline; }

.agreement-rule { border: 0; border-top: 1px solid var(--border); margin: 30px 0; }

/* --- Legal index -------------------------------------------------------- */
.legal-index {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
}
.legal-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  padding: 20px 22px;
  transition: border-color .16s ease, box-shadow .16s ease, transform .16s ease;
}
.legal-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}
.legal-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.legal-card-title { font-weight: 700; color: var(--text); }

/* --- Auth / centered pages --------------------------------------------- */
.centered-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background:
    radial-gradient(700px 420px at 50% -10%, rgba(19,100,223,.10), transparent 70%),
    var(--bg);
}
.centered-card { width: 100%; max-width: 440px; }
/* The radial wash behind these pages is darker than --bg, which is what
   --text-faint was validated against: on this background it lands at 4.47:1,
   just under AA. The muted tone clears it at 5.6:1, so small print here uses
   that rather than getting a one-off colour of its own. */
.centered-page .faint { color: var(--text-muted); }

/* Address block in the footer. <address> is italic by default in every
   browser, which is not what anyone wants for a postal address. */
.footer-contact {
  font-style: normal;
  display: grid;
  gap: 4px;
  margin-top: 16px;
  font-size: .85rem;
}
.footer-contact a:hover { color: var(--accent); }

/* --- Scroll reveal ------------------------------------------------------
   Markup ships in its finished state. Script adds .reveal-init only once it
   has established that it can animate, then .is-visible as each element
   arrives, so a JS failure or reduce-motion leaves an ordinary, fully
   visible page rather than a blank one.
   ------------------------------------------------------------------------ */
[data-reveal].reveal-init {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .6s cubic-bezier(.22, .8, .25, 1),
              transform .6s cubic-bezier(.22, .8, .25, 1);
  will-change: opacity, transform;
}
[data-reveal="scale"].reveal-init { transform: scale(.955); }
[data-reveal].reveal-init.is-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Sideways entrances are a phone treatment. On a wide screen the same cards
   arrive four abreast, and sliding them in from opposite edges reads as
   noise rather than motion, so above this width they just rise like the
   rest of the page. */
@media (max-width: 720px) {
  [data-reveal="left"].reveal-init  { transform: translateX(-38px); }
  [data-reveal="right"].reveal-init { transform: translateX(38px); }
}

/* An element travelling sideways passes beyond the viewport edge on its way
   in, which would hand the document a horizontal scrollbar for the length of
   the transition. Clipped rather than hidden, so this never becomes a scroll
   container and never traps a sticky child. */
.reveal-clip { overflow-x: clip; }
@supports not (overflow-x: clip) {
  .reveal-clip { overflow-x: hidden; }
}

/* --- Responsive -------------------------------------------------------- */
@media (max-width: 980px) {
  /* Stacked: each step draws its own connector down to the next marker.
     A single absolute track can't work here, because its end would have to
     know the height of the last card, so it overshoots past the final
     marker to the bottom of the list. */
  .flow { grid-template-columns: 1fr; gap: 18px; }
  .flow-track { display: none; }
  .flow-step {
    display: grid;
    grid-template-columns: 48px 1fr;
    gap: 18px;
    align-items: start;
    text-align: left;
  }
  .flow-num { margin: 0; }

  .flow-step::before,
  .flow-step::after {
    content: "";
    position: absolute;
    left: 23px;
    top: 48px;
    bottom: -18px;
    width: 2px;
  }
  .flow-step::before { background: var(--surface-3); }
  .flow-step::after {
    background: var(--gradient);
    transform-origin: center top;
  }
  .flow-step:last-child::before,
  .flow-step:last-child::after { display: none; }

  /* The connector fills as its own step lights up. */
  .flow.is-armed .flow-step::after { transform: scaleY(0); }
  .flow.is-armed .flow-step.is-on::after {
    transform: scaleY(1);
    transition: transform .6s cubic-bezier(.35, 0, .2, 1);
  }

  .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .challenge-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
  .gauge-grid { gap: 14px; }
  .gauge { padding: 22px 14px 20px; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  /* The sidebar used to become a horizontally scrolling strip here: 849px of
     links crammed into a 390px viewport, so Sign out and half the sections
     were off screen behind a scrollbar nobody looks for. On a phone the nav
     moves to a fixed bottom bar instead, which is where a thumb already is. */
  .app-shell { grid-template-columns: 1fr; background: none; }
  .sidebar { display: none; }
  .app-topbar { display: flex; }
  .app-tabs { display: grid; }
  .app-main {
    padding: 20px 16px calc(78px + env(safe-area-inset-bottom));
  }
  .portal-split { grid-template-columns: 1fr; gap: 16px; }
  .doc-grid { grid-template-columns: 1fr; gap: 12px; margin-bottom: 24px; }
  /* Hover lift is meaningless on a touch screen and only causes a stuck
     hover state after a tap. */
  .doc-item:hover { transform: none; box-shadow: none; }

  /* Six labels across a phone is about 50px each, which turned "Collecting
     documents" into four broken lines. The dots still show progress; the
     stage name is already spelled out in the badge above them, and the
     caption below says which number it is. */
  .tracker-step .name { display: none; }
  .tracker-caption { display: block; }
  .tracker { margin-bottom: 0; }
}

@media (max-width: 700px) {
  .legal-index { grid-template-columns: 1fr; }
  .croa-sign { grid-template-columns: 1fr; gap: 14px; }
}

@media (max-width: 620px) {
  /* The three stay side by side on a phone, so the comparison still reads as
     one row. Everything inside sizes off the card, so tightening the padding
     and the gap is all that is needed to keep them legible. */
  .gauge-grid { gap: 8px; }
  .gauge { padding: 14px 8px 12px; }
  .gauge-dial { width: 100%; }
  .gauge-logo { margin-top: 10px; }
}

@media (max-width: 400px) {
  /* Buy the cards back a few pixels each rather than shrinking the dials. */
  .gauge-grid { gap: 6px; }
  .gauge { padding: 12px 6px 10px; }
}

@media (max-width: 560px) {
  /* Side by side they get too narrow to hold their labels on one line. */
  .hero-cta { grid-auto-flow: row; width: min(340px, 100%); }
}

@media (max-width: 420px) {
  /* On the narrowest phones any button label may need to wrap rather than
     force horizontal scrolling on the whole document. */
  .btn { white-space: normal; }
}

@media (max-width: 720px) {
  .nav-links { display: none; }
  .nav-links.open {
    display: flex;
    position: absolute; top: var(--nav-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--bg-soft);
    border-bottom: 1px solid var(--border);
    padding: 12px 16px 18px;
  }
  .nav-links.open a { padding: 12px 14px; }
  .nav-links.open .btn { margin-top: 8px; }
  .nav-toggle { display: block; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  /* These cards are a heading plus one line, so they stay two-up on a phone
     rather than turning eight items into eight screens of scrolling. */
  .challenge-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .challenge-card { padding: 18px 16px; }
  .challenge-ico { width: 34px; height: 34px; margin-bottom: 12px; }
  .challenge-ico svg { width: 18px; height: 18px; }
  .challenge-card h3 { font-size: .95rem; }
  .field-row { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  .price-card { padding: 28px 22px; }
  .card { padding: 22px; }
  .tracker-step .name { font-size: .66rem; }
  .app-main { padding: 22px 16px 56px; }
}

/* Printing never scrolls, so anything waiting on an intersection would come
   out blank. Force every deferred element to its finished state on paper. */
@media print {
  [data-reveal].reveal-init {
    opacity: 1 !important;
    transform: none !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

@media (max-width: 350px) {
  /* Measured, not guessed. Two columns leave (100vw - 48 - 12) / 2 minus the
     card's own padding as usable width: about 113px at 350, which still fits
     the longest title, and about 98px at 320, which does not, so
     "Repossessions" breaks mid-word. One column below that.

     This has to come after the 720px block: same selector, same specificity,
     so the later one wins. */
  .challenge-grid { grid-template-columns: 1fr; gap: 10px; }
  .challenge-card { padding: 16px 18px; }
}
