/* WILLXO, service intake wizard styling.
   THE PALETTE IS THE SITE'S. It is not restated here.

   This file used to open with a fifteen-token :root and the note "palette source of
   truth is web/index.html, keep these in step". They had not been kept in step, and
   nothing could have noticed: --petrol was #0C305C against the site's #002862 and
   --gold was #B68A35 against #D29835, so the order flow drew the brand in colours the
   brand does not use. That is a large part of why it read as a different website even
   where the layout happened to match.

   add-flow-chrome.py now grafts the site's own stylesheet in ahead of this one, which
   brings all 28 tokens AND their dark-mode values, so a second declaration here could
   only ever drift again. What remains below are the three the site does not define,
   and their dark counterparts.

   `--sans` carries a literal fallback rather than assuming --font-body: this file is
   also linked by /start/index.html, and a token that resolves to nothing would drop the
   page to the browser's default serif. */
:root {
  --good: #1B7F4B; --crit: #B3352F;
  --sans: var(--font-body, "Inter", "Segoe UI", system-ui, sans-serif);
  /* THE TICK BOX'S FILL, and it is deliberately NOT --teal. That token is the site's
     brand blue in light and a NEAR-WHITE (#F4F6F8) in dark, because there it is a text
     and border accent -- as a checkbox fill it would be a white square, and the tick
     glyph is the browser's to colour. A mid-tone in both themes keeps that glyph
     readable whichever way the browser draws it. */
  --tick: #1265B0;
}
/* Written twice, once for the switch and once for the device, the same doubled pattern
   the site's own palette uses. Without this a filename confirming an upload, and the
   message confirming a promo code, are dark green on a dark card. */
:root[data-theme="dark"] { --good: #5FD79A; --crit: #F09A93; --tick: #3B8FD9; }
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) { --good: #5FD79A; --crit: #F09A93; --tick: #3B8FD9; }
}
/* No `*`, `body` or `a` reset here either, the grafted stylesheet brings the site's,
   and a second copy loading afterwards would quietly win on any rule that differed. */

/* ---------- service picker (index.html) ----------
   Headings, prices and picked options take --brand-ink, never --petrol. --petrol is the
   site's deep panel colour and stays dark in dark mode; --brand-ink is its text ink and
   inverts. They are the same #3D1A11 in light, so this costs nothing there.
   (This line used to say #002862 and "panel navy", which was the donor platform's colour
   and had simply been left behind - the rule it describes is right, the colour it named
   belonged to the other brand. The name itself is not written here because
   apply-wx-name.py sweeps this file and refuses it, which is the point of that sweep.) */
.picker-wrap { max-width: 980px; margin: 0 auto; padding: 48px 24px 80px; }
.picker-wrap h1 { font-family: var(--font-head); font-size: 30px; font-weight: 800; color: var(--brand-ink); }
.picker-wrap p.lede { color: var(--muted); max-width: 62ch; margin-top: 10px; }
.svc-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 14px; margin-top: 28px;
}
.svc-card {
  display: block; background: var(--paper); border: 1px solid var(--line); border-top: 3px solid var(--gold);
  border-radius: 14px; padding: 18px; cursor: pointer; transition: transform .12s, border-color .12s;
}
.svc-card:hover { border-top-color: var(--teal); transform: translateY(-2px); }
.svc-card .cat { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--gold-ink); }
.svc-card h3 { font-family: var(--font-head); font-size: 15.5px; margin-top: 6px; }
.svc-card .price { font-family: ui-monospace, monospace; color: var(--brand-ink); font-weight: 700; margin-top: 10px; font-size: 14px; }

/* ---------- the wizard (flow.html) ----------
   No `#app` rule and no `header.flow-top`. #app used to be a 100vh flex column that
   owned the whole viewport, and the flow drew its own one-line header inside it. Both
   were right when this page had no chrome; with the site's real header above and its
   real footer below, a 100vh middle would push the footer off the screen and a second
   brand line would print "WillXO" directly beneath the logo. The service being
   ordered is now named by the breadcrumb, which add-flow-chrome.py puts outside #app so
   a re-render cannot wipe it. */
.progress-track { height: 4px; background: var(--mist); width: 100%; }
.progress-bar { height: 100%; background: linear-gradient(90deg, var(--teal), var(--gold)); transition: width .3s ease; }

.story {
  flex: 1; display: flex; align-items: center; justify-content: center; padding: 40px 20px;
}
.step {
  width: 100%; max-width: 560px; opacity: 0; transform: translateY(14px);
  transition: opacity .32s ease, transform .32s ease;
}
.step.active { opacity: 1; transform: translateY(0); }
.step .eyebrow { font-size: 12px; font-weight: 800; letter-spacing: 2px; text-transform: uppercase; color: var(--gold-ink); }
.step h2 { font-family: var(--font-head); font-size: 26px; font-weight: 800; margin-top: 8px; color: var(--brand-ink); }
.step p.help { color: var(--muted); margin-top: 8px; font-size: 14.5px; }

.field { margin-top: 26px; }
.field label { display: block; font-size: 13px; font-weight: 700; margin-bottom: 8px; }
input[type=text], input[type=number], input[type=email], input[type=tel], select {
  width: 100%; padding: 14px 16px; font-size: 15.5px; border: 1.5px solid var(--line);
  border-radius: 12px; font-family: var(--sans); color: var(--ink); background: var(--paper);
}
input:focus, select:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 3px var(--focus-ring); }

.yesno-row { display: flex; gap: 12px; margin-top: 4px; }
.yesno-row button {
  flex: 1; padding: 14px; border-radius: 12px; border: 1.5px solid var(--line); background: var(--paper);
  font-family: var(--sans); font-weight: 700; font-size: 15px; cursor: pointer; color: var(--ink);
}
.yesno-row button.picked { border-color: var(--gold); background: var(--pick-fill); color: var(--brand-ink); }

.opt-grid { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.opt-btn {
  text-align: left; padding: 14px 16px; border-radius: 12px; border: 1.5px solid var(--line);
  background: var(--paper); font-family: var(--sans); font-size: 15px; cursor: pointer; color: var(--ink);
  transition: border-color .12s, background .12s;
}
.opt-btn:hover { border-color: var(--teal); }
.opt-btn.picked { border-color: var(--gold); background: var(--pick-fill); color: var(--brand-ink); font-weight: 700; }

.nav-row { display: flex; gap: 10px; margin-top: 30px; align-items: center; }
/* `.btn`, `.btn-primary` and `.btn-ghost` are the SITE's, from the grafted stylesheet.
   The copies that used to live here had to go rather than merely being overridden:
   `.btn-primary{ background:var(--petrol); color:#fff }` hardcodes white ink, while the
   site's dark palette turns .btn-primary GOLD and pairs it with dark ink. Keeping both
   would have put the site's dark-ink rule over this file's navy fill and made Continue
   unreadable in dark mode -- the exact defect fixed on /admin in a60270f. Only the two
   rules the site has no equivalent for stay. */
.btn:disabled { opacity: .45; cursor: not-allowed; }
.flow-err { color: var(--crit); font-size: 13px; margin-top: 12px; min-height: 16px; }
.hint-key { font-size: 12px; color: var(--muted); margin-left: auto; }

/* quote step */
.quote-card {
  background: var(--mist); border: 1px solid var(--line); border-radius: 16px; padding: 22px;
  margin-top: 20px;
}
.quote-card .amt { font-family: var(--font-head); font-size: 34px; font-weight: 800; color: var(--brand-ink); }
.quote-card .sub { color: var(--muted); font-size: 13.5px; margin-top: 4px; }
.quote-card .row { display: flex; justify-content: space-between; padding: 8px 0; border-top: 1px solid var(--line); font-size: 13.5px; }
.quote-card .row:first-of-type { border-top: none; margin-top: 12px; }
/* Shown while the priced quote is still being fetched from the price master.
   The figure is never rendered from a local guess, see app.js. */
.quote-card.pending { opacity: .55; }
.quote-card.pending .amt { color: var(--muted); }

/* document upload step */
.doc-list { margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
.doc-item {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px; border: 1.5px dashed var(--line);
  border-radius: 12px; background: var(--paper);
}
.doc-item.done { border-style: solid; border-color: var(--good); background: rgba(27,127,75,.06); }
.doc-item.optional { border-color: #E7D3A3; }
.doc-item .num {
  width: 26px; height: 26px; border-radius: 50%; background: var(--mist); color: var(--brand-ink);
  display: grid; place-items: center; font-size: 12.5px; font-weight: 800; flex-shrink: 0;
}
/* --paper, not #fff: --good is a DARK green in light mode and a LIGHT green in dark, so
   the number sitting on it has to invert too. --paper does exactly that -- white on dark
   green, dark navy on light green -- and needs no override of its own. */
.doc-item.done .num { background: var(--good); color: var(--paper); }
.doc-item .label { flex: 1; font-size: 14px; }
.doc-item .desc { font-size: 12px; color: var(--muted); }
.doc-item .tag { font-size: 10.5px; font-weight: 700; color: var(--gold-ink); text-transform: uppercase; letter-spacing: .06em; }
.doc-item .filename { font-size: 12px; color: var(--good); font-weight: 600; word-break: break-all; }
.doc-item .filename.failed { color: var(--crit); }
.doc-item input[type=file] { max-width: 150px; font-size: 12px; }

/* currentColor, not white. The spinner only ever renders inside .btn-primary, whose ink
   is white in light mode and NEAR-BLACK in dark (the site pairs its gold fill with dark
   text) -- so a hardcoded white ring would vanish on the dark button at the one moment
   the customer is waiting to be told their order went through. A transparent quarter
   gives the same rotating-gap look without needing color-mix. */
.spinner {
  width: 22px; height: 22px; border: 3px solid currentColor; border-top-color: transparent;
  border-radius: 50%; animation: spin .7s linear infinite; display: none;
}
.btn-primary.loading .spinner { display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }

/* referral hand-off step (insurance) */
.tick {
  width: 68px; height: 68px; border-radius: 50%; background: rgba(27,127,75,.12);
  display: grid; place-items: center; margin: 0 auto 18px;
}
.tick svg { width: 34px; height: 34px; stroke: var(--good); }
.step.thankyou { text-align: center; }

@media (max-width: 480px) {
  .step h2 { font-size: 22px; }
  /* 24px, not 16px: the site's gutter, so the flow's text starts under the
     logo like every other page. See tools/fix-mobile-gutter.py. */
  .story { padding: 24px 24px; }
  .doc-item { flex-wrap: wrap; }
  .doc-item input[type=file] { max-width: 100%; }
}

/* ---------- promo code box (the quote step) ----------
   Sits above the copy under the quote card, so a customer reads the price, then the
   code box, then what happens when they accept, in that order.

   `.was` is the pre-discount figure struck through. It is rendered from the SERVER's
   `amount_before`, not from whatever the card previously said, so the two figures on
   the card always come from the same response. */
.promo { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; margin: 16px 0 4px; }
.promo label { display: block; font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
.promo-row { display: flex; gap: 8px; align-items: center; }
.promo-row input { flex: 1; min-width: 0; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 9px; font: inherit; font-size: 14.5px; text-transform: uppercase; }
.promo-row input:focus { outline: none; border-color: var(--teal); }
.promo-row .btn-sm { padding: 10px 16px; font-size: 14px; flex: none; }
.promo-msg { font-size: 13px; margin: 9px 0 0; color: var(--muted); min-height: 1px; }
.promo-msg.ok { color: var(--good); font-weight: 600; }
.promo-msg.bad { color: var(--crit); }
.quote-card .amt .was { color: var(--muted); font-weight: 600; font-size: 22px; margin-right: 8px; }

/* ---------- the two tick boxes ----------
   THE BOX HAS TO LOOK LIKE A CONTROL. Both of these used to be a bare default
   checkbox against 13px muted text, and on a phone the PDPL one -- which GATES the
   step, the customer cannot upload without it -- was reported as impossible to find:
   the eye reads muted small print as a footnote, and iOS draws an unstyled checkbox
   at about 13px. So the row is a card with its own border and padding, the box is
   22px with the brand's accent, and the words are --ink at 14px like every other
   label in the flow.

   The whole card is the target, not just the 22px square: the input sits INSIDE the
   <label>, so a tap anywhere on the row toggles it. `align-items:flex-start` keeps
   the box beside the FIRST line when the sentence wraps to three lines at 412px. */
.consent {
  display: flex; align-items: flex-start; gap: 12px; padding: 14px 16px;
  border: 1.5px solid var(--line); border-radius: 12px; background: var(--paper);
  font-size: 14px; color: var(--ink); cursor: pointer; margin-top: 4px;
}
.consent input[type=checkbox] {
  width: 22px; height: 22px; flex: none; margin: 0; cursor: pointer;
  accent-color: var(--tick);
}
.consent:hover, .consent:focus-within { border-color: var(--teal); }
/* Ticked reads like a completed .doc-item above it, the same green, so the step's
   last row confirms itself the way the four rows before it do. :has() is not
   universal; where it is missing the box is still plainly a ticked 22px box. */
.consent:has(input:checked) { border-color: var(--good); background: rgba(27,127,75,.06); }

/* The PDPL box on the documents step. It gates the step, so it carries the flow's
   own spacing above and turns red when Continue was refused for want of it --
   the error sentence names the box, and this is what makes the box findable from
   the sentence. Cleared on change, never left standing over a ticked box. */
.consent.gate { margin-top: 18px; }
.consent.gate.missed { border-color: var(--crit); background: rgba(179,53,47,.06); }
.consent.gate.missed input[type=checkbox] { outline: 2px solid var(--crit); outline-offset: 2px; }

/* ---- WX ORDER FLOW (web/tools/style-order-flow.py) --------------------------
   blanket.ae's flow, in WillXO's colours. Every number below was measured in a
   browser against blanket's own `/will/start`, at 1280 and at 390. See the tool's
   docstring for the measurements and for the one behaviour deliberately not copied.

   TWO TOKENS ARE DECLARED because the values they replace were hardcoded rgb
   triples that no palette could reach. Both are derived from --gold, so
   set-palette.py moves this page with the rest of the site. */
:root{
  --focus-ring: rgba(192,122,68,.22);
  --pick-fill: rgba(192,122,68,.10);
}
:root[data-theme="dark"]{
  --focus-ring: rgba(217,144,63,.30);
  --pick-fill: rgba(217,144,63,.14);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --focus-ring: rgba(217,144,63,.30);
    --pick-fill: rgba(217,144,63,.14);
  }
}

/* THE PICKER (/start) IS THE FLOW'S FRONT DOOR AND WAS NOT PART OF IT. It is what the
   header's "Start your will" points at on all 27 pages, so for most visitors it is the
   FIRST screen of the order, and it still carried the conventions the rest of the site
   left behind: an 800-weight heading where every other heading is Nohemi 500, a card
   with a 3px coloured top border, and - the loudest one - the PRICE SET IN A MONOSPACE
   FONT, which is a spreadsheet's idea of a number and appears nowhere else on this site.
   Its hover also moved `border-top-color` to `--teal`, which is #985434 in light and
   #F7F3F0 in dark, i.e. a near-WHITE line across the top of a card on a dark page.
   Nothing here changes a control or a link; the cards are the site's own cards now. */
.picker-wrap{ max-width: 1160px; padding: 56px 24px 80px; text-align: center; }
.picker-wrap h1{
  font-family: var(--font-head); font-weight: 500;
  font-size: clamp(26px, 1.15rem + 2.1vw, 42px);
  letter-spacing: -.01em; line-height: 1.12; color: var(--ink);
}
.picker-wrap p.lede{ max-width: 42rem; margin: 16px auto 0; font-size: 16px;
  line-height: 1.6; }
.svc-grid{ grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
  gap: 16px; margin-top: 34px; text-align: left; }
.svc-card{ border: 1px solid var(--line); border-top: 1px solid var(--line);
  border-radius: 16px; padding: 22px; background: var(--paper); }
.svc-card:hover{ border-color: var(--gold); transform: translateY(-2px); }
.svc-card h3{ font-weight: 500; font-size: 16.5px; color: var(--ink); }
/* The price in the brand's own face, at the weight every other price on the site uses. */
.svc-card .price{ font-family: var(--font-head); font-weight: 500; font-size: 18px;
  color: var(--ink); }

/* THE STAGE. #app gets a flex column for two reasons, and the second is the one that
   could not be done any other way: it lets `.story` claim the height that makes
   vertical centring mean something, AND it lets the progress indicator be REORDERED
   below the content with `order`, which is where blanket puts it. The markup emits
   the track first, and moving it in app.js would be a change to the flow's own
   rendering for a purely visual reason. */
#app{ display: flex; flex-direction: column; }
.story{
  order: 1; flex: 1 0 auto;
  display: flex; align-items: center; justify-content: center;
  min-height: min(620px, 72vh);
  padding: 56px 24px 24px;
}

/* NO CARD, AND THIS REVERSES AN EARLIER DECISION ON PURPOSE. The previous version of
   this block put the step in a bordered, shadowed 600px card, reasoning that "every
   other surface on this site puts its content in one". That is true of the MARKETING
   pages and it is the wrong instinct for a checkout: blanket's flow has no card at
   all, because a card draws a box around the one thing on screen and makes a single
   question look like a form. The screen already is the card. */
.step{
  width: 100%; max-width: 720px; text-align: center;
  background: none; border: 0; box-shadow: none; padding: 0; border-radius: 0;
}

/* The question. Regular Nohemi at blanket's own size and metrics - and NOT uppercase,
   which is the one deliberate departure from the site's heading style.

   THESE HEADINGS CARRY THE CUSTOMER'S OWN NAME. The thank-you step greets them by
   first name, so `text-transform: uppercase` renders "THANKS, RAJ" - shouting a
   person's name back at them on the screen that confirms their order. It also broke
   `tests/test-quote-mode-flow.mjs`, which drives the real wizard and reads the
   rendered text: the check looks for "Raj" and found "RAJ". A styling rule that
   changes what a name looks like is not a styling rule. */
.step h2{
  font-family: var(--font-head); font-weight: 500; text-transform: none;
  font-size: clamp(26px, 1.15rem + 2.1vw, 42px);
  letter-spacing: -.01em; line-height: 1.12;
  margin: 10px auto 0; max-width: 20ch; color: var(--ink);
}
.step .eyebrow{ font-weight: 700; letter-spacing: .14em; }
/* blanket's helper column is `max-w-2xl`, i.e. 42rem. */
.step p.help{ margin: 16px auto 0; max-width: 42rem; font-size: 16px; line-height: 1.6; }

/* THE ANSWER COLUMN IS 560px AND CENTRED, which is blanket's, and it is the measured
   fix for the complaint that keeps coming back: a control that fills 720px of a
   720px column reads as a form, and one pinned to the left of it reads as broken.
   Everything a customer touches on a step lives in this column.

   `.consent` IS IN THIS LIST AND LOOKED LIKE IT DID NOT NEED TO BE. It is the
   marketing tick box on screen one, and it is the one control with no width of its
   own - so it took the step's full 720px and sat 80px wider on each side than the
   three inputs directly above it, which is the "left and right spacing is not right"
   shape all over again, inside the checkout. */
.opt-grid, .yesno-row, .field, .flow-err, .nav-row, .consent{
  max-width: 560px; margin-left: auto; margin-right: auto;
}
/* AND IT NEEDS THE SAME AIR THE FIELDS ABOVE IT HAVE. `.field` carries
   `margin-top:26px` and this box carried nothing, so on screen one the marketing tick
   box sat flush against the bottom of the Email address input - two bordered boxes
   touching, reading as one control. The documents step's own box already had
   `.consent.gate{ margin-top:18px }`; only this one was left out. That rule is more
   specific, so it still wins where it applies. */
.consent{ text-align: left; margin-top: 26px; }
/* Labels and inputs are ranged left inside that centred column. A centred <label>
   over a full-width input floats in the middle of nothing. */
.field{ text-align: left; }
.field label{ font-size: 12.5px; letter-spacing: .04em; color: var(--ink); }
input[type=text], input[type=number], input[type=email], input[type=tel], select{
  border-radius: 10px; border-width: 1px; padding: 16px 18px;
}

/* YES / NO STACKS, it does not sit in a row. blanket renders a two-option question
   exactly like a six-option one - same width, same height, same dot - so the shape
   of the answer never depends on how many answers there are. */
.yesno-row{ flex-direction: column; gap: 10px; }
.opt-grid{ gap: 10px; }
.opt-btn, .yesno-row button{
  display: flex; align-items: center; gap: 16px; text-align: left;
  min-height: 60px; padding: 14px 20px; border-radius: 10px;
  background: var(--mist); border: 1px solid transparent;
  font-size: 16px; font-weight: 500; color: var(--ink);
  transition: background .15s ease, border-color .15s ease;
}
.opt-btn:hover, .yesno-row button:hover{ border-color: var(--gold); }
/* THE RADIO DOT IS A PSEUDO-ELEMENT, so not one line of markup changes. It is what
   makes a tall row read as a CHOICE rather than as a button, which is most of why
   blanket's options feel like a form you are filling in rather than a menu. */
.opt-btn::before, .yesno-row button::before{
  content: ""; flex: none; width: 24px; height: 24px; border-radius: 999px;
  background: var(--paper); box-shadow: inset 0 0 0 1px var(--line);
  transition: background .15s ease, box-shadow .15s ease;
}
.opt-btn.picked, .yesno-row button.picked{
  background: var(--pick-fill); border-color: var(--gold);
}
.opt-btn.picked::before, .yesno-row button.picked::before{
  background: var(--gold); box-shadow: inset 0 0 0 4px var(--paper), 0 0 0 1px var(--gold);
}

/* The controls, centred under the column. The step counter goes on its own line
   rather than being pushed to the right by an auto margin, which is what put it in
   the far corner away from everything it describes. */
.nav-row{ justify-content: center; flex-wrap: wrap; margin-top: 28px; }
.hint-key{ margin-left: 0; order: 3; width: 100%; text-align: center; margin-top: 4px; }

/* SEGMENTED PROGRESS, BELOW THE CONTENT. `--seg` is written onto the element by
   app.js because CSS cannot count our steps, and the mask cuts ONE set of gaps
   through the track and the fill together - so the dashes line up by construction
   instead of being two lists that can disagree. N segments and N-1 gaps means each
   segment is (100% - (N-1)*gap)/N, which is the expression below. */
.progress-track{
  order: 2; --seg: 6; --seg-gap: 8px;
  height: 3px; border-radius: 999px; background: var(--mist);
  width: 100%; max-width: 720px; margin: 0 auto 56px; overflow: visible;
  -webkit-mask-image: repeating-linear-gradient(90deg,
    #000 0 calc((100% + var(--seg-gap)) / var(--seg) - var(--seg-gap)),
    transparent 0 calc((100% + var(--seg-gap)) / var(--seg)));
  mask-image: repeating-linear-gradient(90deg,
    #000 0 calc((100% + var(--seg-gap)) / var(--seg) - var(--seg-gap)),
    transparent 0 calc((100% + var(--seg-gap)) / var(--seg)));
}
.progress-bar{ border-radius: 999px; background: var(--gold); }

/* THE PHONE, AND IT IS NOT AN AFTERTHOUGHT. Sir's standing rule is that a defect
   fixed on the desktop is fixed on the phone in the same change, so every number
   here was measured at 360, 390 and 430 as well. blanket's own phone layout is the
   same screen with a 20px gutter, a 30px question and 56px rows - it does not become
   a different design, which is the point. */
@media (max-width: 600px){
  .story{ padding: 28px 20px 16px; min-height: min(520px, 64vh); }
  .step h2{ max-width: none; }
  .opt-btn, .yesno-row button{ min-height: 56px; padding: 12px 18px; }
  .progress-track{ margin-bottom: 36px; }
}
/* ---- end WX ORDER FLOW ---- */
