/*
 * Salt on-site forms. Replaces WS Form PRO's rendered markup and its client runtime.
 *
 * Brand tokens are the Salt design system, not eyeballed: Marine #022938, Silica #F5EFE4,
 * Sea Spray #BFF6E5, Scrub #65654B, Kodchasan. Scoped under .salt-form so nothing here can
 * reach the rest of the ported Bricks CSS, which uses very generic class names.
 *
 * Breakpoint: one, at 640px. Both sides of it are checked in the browser gate.
 */
.salt-form{
  --sf-marine:#022938; --sf-silica:#F5EFE4; --sf-spray:#BFF6E5; --sf-scrub:#65654B;
  --sf-line:rgba(2,41,56,.22); --sf-err:#a4262c;
  font-family:'Kodchasan',system-ui,-apple-system,sans-serif;
  color:var(--sf-marine); max-width:520px;
}
.salt-form *{box-sizing:border-box}

.salt-form__head{margin-bottom:18px}
.salt-form__title{font-size:22px;font-weight:600;letter-spacing:-.01em;margin:0 0 6px}
.salt-form__blurb{font-size:14px;line-height:1.55;color:var(--sf-scrub);margin:0}

/* Step counter for the multi-step VIP form. */
.salt-form__steps{display:flex;gap:6px;margin-bottom:18px}
.salt-form__step{flex:1;height:3px;border-radius:2px;background:var(--sf-line)}
.salt-form__step.is-done,.salt-form__step.is-current{background:var(--sf-marine)}
.salt-form__stepnote{font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--sf-scrub);margin-bottom:14px}

.salt-form__row{margin-bottom:14px}
.salt-form__grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:640px){.salt-form__grid{grid-template-columns:1fr}}

.salt-form label{display:block;font-size:12.5px;font-weight:600;margin-bottom:5px}
.salt-form label .opt{font-weight:400;color:var(--sf-scrub)}
/*
 * ⛔ 16px is a FLOOR, not a size. iOS Safari zooms the whole page when a focused input computes
 * under 16px, which throws the layout mid-typing and reads as the form breaking.
 *
 * This rule used to read `font:15px/1.3 inherit`, and that declaration NEVER APPLIED. A
 * CSS-wide keyword is only legal as an entire property value, never as one component of a
 * shorthand, so `inherit` in the font-family slot makes the whole shorthand invalid and the
 * browser drops it. Measured, not assumed: `el.style.font = '16px/1.3 inherit'` returns ''
 * where '16px/1.3 Kodchasan' is accepted, and the input's computed size tracked the body's
 * fluid ramp exactly (16.008px at 320 to 18px at 1440) rather than sitting at 15px.
 *
 * So the fields were never 15px and iOS was never zooming. What they actually inherit is the
 * theme's fluid body size via its own `input:not([type=submit]){font-size:inherit}`, and that
 * is what shipped and what was signed off — including the 64px control height, which comes
 * from the theme's inherited line-height, not from anything here.
 *
 * `max(16px, 1em)` therefore keeps today's rendering to the pixel (1em is the parent's size,
 * which is >= 16.008px at every viewport in the gate) and adds the floor the defect report
 * asked for, so a future change to the theme's fluid ramp cannot drop a field under 16px and
 * reintroduce the zoom. Do not "simplify" it to a flat 16px: that shrinks every field from
 * 64px to ~45px, which is a redesign of a signed-off live form, not a defect fix.
 *
 * line-height is deliberately NOT set here for the same reason. The 1.3 the old shorthand
 * meant to apply is a separate, deliberate design decision. See the handover note.
 */
.salt-form input[type=text],.salt-form input[type=email],.salt-form input[type=tel],.salt-form select{
  width:100%;padding:11px 13px;font-family:inherit;font-size:max(16px,1em);color:var(--sf-marine);
  background:#fff;border:1px solid var(--sf-line);border-radius:8px;appearance:none;
}
.salt-form select{background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' fill='none' stroke='%23022938' stroke-width='1.6'/></svg>");
  background-repeat:no-repeat;background-position:right 13px center;padding-right:34px}
.salt-form input:focus,.salt-form select:focus{outline:2px solid var(--sf-spray);outline-offset:1px;border-color:var(--sf-marine)}
.salt-form input[aria-invalid=true],.salt-form select[aria-invalid=true]{border-color:var(--sf-err)}
.salt-form .salt-form__err{display:none;font-size:12px;color:var(--sf-err);margin-top:5px}
.salt-form input[aria-invalid=true]~.salt-form__err,
.salt-form select[aria-invalid=true]~.salt-form__err{display:block}

/* The honeypot. Off-screen rather than display:none — some bots skip hidden inputs. */
.salt-form__hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.salt-form__block{display:flex;gap:12px;align-items:center;padding:12px 14px;margin-bottom:16px;
  background:var(--sf-marine);color:var(--sf-silica);border-radius:8px}
.salt-form__block .lot{font-size:19px;font-weight:600;color:var(--sf-spray);line-height:1}
.salt-form__block .det{font-size:12.5px;line-height:1.45;opacity:.9}

/*
 * The actions row WRAPS. Three attempts to control button labels on this site each fixed one
 * thing and broke another — a label clipped mid-word, a header CTA on three lines, and a 319px
 * page scrolling sideways — and the rule that finally held states the requirement instead of
 * picking a width: take one line where there is room (max-content), never push the page
 * sideways (max-width:100%), never crush an unbreakable word (min-width:min-content), and let
 * the ROW wrap when neither button can shrink further. The VIP form is the case that needs it:
 * "Save my answers" beside "Skip, you are on the list either way" does not fit a 320px phone on
 * one line, and the skip must stay legible rather than shrink to a stack of single words.
 */
.salt-form__actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:18px}
/*
 * ⛔ LONGHANDS, AND THEY HAVE TO BE. This rule read `font:600 14px/1 inherit` and that
 * declaration NEVER APPLIED — the same trap as the input rule above: a CSS-wide keyword is legal
 * only as an entire property value, never as one component of a shorthand, so `inherit` in the
 * font-family slot voids the whole thing and the browser drops it. `letter-spacing` and
 * `text-transform` are separate declarations, so the buttons still came out uppercase and
 * letter-spaced, which is exactly why nobody caught it: they looked deliberate.
 *
 * MEASURED on the live deployed page, not assumed. `/offer/` "Request offer form" rendered
 * 17.832px/400 against the design system's 14px/600 — the button inherited the theme's fluid
 * body ramp because nothing here was setting a size at all.
 *
 * ⛔ THE LINE-HEIGHT IS DELIBERATELY NOT RESTORED, and that is the whole care in this fix.
 * The dead shorthand carried `/1`. Putting it back reaches FOUR SIGNED-OFF FORMS: nothing else
 * sets a line-height on these buttons, so `.salt-form button` (0,1,1) would win on register, VIP,
 * brochure and both skip buttons and take every one of them from 51px to 48px. MEASURED:
 * gate-forms-css.mjs reported 162 computed differences across 14 screens with `line-height:1`
 * present, and 0 outside `/offer/` and design guidelines without it. The defect Keith approved
 * fixing is the TYPE — 17.8px/400 where the system says 14px/600 — not the button's height, so
 * only the three properties that carry the type are restored and the cascade is left as it was.
 * A future change that wants `/1` back owns re-approving those four forms.
 *
 * ⚠ gate-forms-css.mjs reports `font-family: Arial -> Kodchasan` on the skip buttons and on
 * `/offer/`. IGNORE THOSE ROWS — they are a harness artefact, not a live change. The gate renders
 * this stylesheet ALONE, and the ported theme carries
 * `button,input,optgroup,select,textarea{font-family:inherit}`, so on the real page every one of
 * these buttons already inherited Kodchasan. Confirmed by measuring the deployed page directly:
 * `/offer/` read 17.832px/400/KODCHASAN, never Arial. Live, this fix changes the SIZE and the
 * WEIGHT of one button and nothing else.
 */
.salt-form button{font-weight:600;font-size:14px;font-family:inherit;
  letter-spacing:.04em;text-transform:uppercase;
  width:max-content;max-width:100%;min-width:min-content;
  padding:14px 22px;border-radius:8px;border:1px solid var(--sf-marine);cursor:pointer;
  background:var(--sf-marine);color:var(--sf-silica);transition:opacity .15s}
.salt-form button:hover{opacity:.88}
.salt-form button[disabled]{opacity:.5;cursor:not-allowed}
.salt-form button.ghost{background:transparent;color:var(--sf-marine)}
.salt-form__skip{font-size:13px;color:var(--sf-scrub);text-decoration:underline;
  background:none;border:none;padding:0;cursor:pointer;text-transform:none;letter-spacing:0;font-weight:400}

/* ⛔ NO RESERVED SPACE — the widget is INVISIBLE mode as of 15/08 (Keith).
   This carried margin-top:16px;min-height:65px to stop the MANAGED widget shifting the layout
   while it loaded. An invisible widget paints nothing at all, so that reservation became an 81px
   hole above the submit button on all five forms. The box still exists and still receives the
   load-failure message, which sizes itself.
   ⚠ Reverting the widget to Managed in Cloudflare means restoring these two values, or the
   widget will shift the form as it loads. The mode and this rule move together. */
.salt-form__turnstile:not(:empty){margin-top:16px}
.salt-form__legal{font-size:11.5px;line-height:1.5;color:var(--sf-scrub);margin-top:14px}

/* A rejected submit is always visible. reCAPTCHA's silent 200 is the failure this replaces. */
.salt-form__alert{display:none;margin-top:14px;padding:12px 14px;border-radius:8px;font-size:13.5px;line-height:1.5}
.salt-form__alert.is-shown{display:block}
.salt-form__alert.err{background:#fbecec;color:var(--sf-err);border-left:3px solid var(--sf-err)}

.salt-form__done{text-align:left;padding:6px 0}
.salt-form__done h3{font-size:20px;font-weight:600;margin:0 0 8px}
.salt-form__done p{font-size:14.5px;line-height:1.6;color:var(--sf-scrub);margin:0 0 14px}
.salt-form__done .doc{display:inline-block;padding:13px 20px;background:var(--sf-marine);
  color:var(--sf-silica);border-radius:8px;text-decoration:none;font-size:14px;font-weight:600;
  letter-spacing:.04em;text-transform:uppercase}

/* ==========================================================================================
 * DESIGN PACK, job 2648 — ingested 14/08/26. MERGED, not replaced.
 *
 * Source: layer-1-context/clients/salt/jobs/2648/20260814_design-handoff-forms/salt-forms.css
 *
 * WHY MERGE. The pack styles a BEM vocabulary — .salt-form__input, .salt-form__label,
 * .salt-form__btn — and the live engine (public/js/salt-forms.js) emits bare <input>, <select>,
 * <label> and <button>. Dropping the pack in as a replacement would have:
 *
 *   1. unstyled every field, label and button on all five live forms, because no rule in the
 *      pack matches the markup that actually renders;
 *   2. left the rejected-submit alert PERMANENTLY VISIBLE. The engine renders
 *      <div class="salt-form__alert err"> on every screen of every form and reveals it by
 *      toggling .is-shown. The pack has no .is-shown and no display:none, so every form would
 *      have carried an empty red box, and the toggle would have become a no-op — the exact
 *      silent-failure class this component was built to end;
 *   3. dropped .lot / .det (the block summary card on /offer/) and .doc (the brochure and
 *      design-guidelines download button), all three emitted by the engine today;
 *   4. dropped the six --sf-* tokens the rules above depend on;
 *   5. flattened the input floor from max(16px,1em) to 16px, which per commit 77abb70 shrinks
 *      the signed-off live fields from 64px to ~45px. That is a redesign, not a defect fix.
 *
 * So everything above this line is the live base and is untouched. Everything below is
 * ADDITIVE and cannot reach a live form today: each rule either targets a class the engine
 * never emits, or sits under the .v-spray modifier, which nothing carries yet. Proven, not
 * assumed — scripts/gate-forms-css.mjs diffs the computed styles of every element of all five
 * forms at both sides of the 640px breakpoint, before and after.
 *
 * NOT CARRIED OVER, deliberately:
 *   .v-marine, .v-frame  the two rejected treatments. The pack keeps them for the record;
 *                        Sea Spray (.v-spray) is the chosen one. Shipping two dead treatments
 *                        on a production stylesheet is how dead CSS becomes permanent.
 *   .salt-modal*         the site's real modal is position:fixed with a backdrop, in
 *                        BaseLayout.astro. The pack's .salt-modal is position:relative — the
 *                        specimen-sheet rendering of a modal shown inline on a page. Shipping
 *                        it would fight the working overlay.
 *   .opt-badge/.opt-head specimen-sheet furniture: the "Option 1a / 1b / 1c" headers.
 * ========================================================================================== */

/* ---------- the BEM field vocabulary the design pack introduces ----------
 * Scoped .salt-form .salt-form__* rather than bare .salt-form__*, so these beat the ported
 * theme's own input:not([type=submit]){font-size:inherit} — (0,2,0) over (0,1,1). Class
 * selectors only: element ids are regenerated per page and :where() has zero specificity, so a
 * ported Bricks rule would win against it. */
.salt-form .salt-form__label{display:block;font-size:12.5px;font-weight:600;margin-bottom:5px;color:var(--sf-marine)}
.salt-form .salt-form__label .opt{font-weight:400;color:var(--sf-scrub)}
.salt-form .salt-form__hint{font-size:12.5px;line-height:1.5;color:var(--sf-scrub);margin:4px 0 0}

/* 16px flat here, max(16px,1em) on the live element rule above. Both satisfy the iOS floor —
 * under 16px Safari zooms the page on focus — and they are deliberately different values: the
 * element rule carries a signed-off 64px control height that comes from the theme's inherited
 * line-height, and flattening it would resize live fields. These BEM classes are new design
 * surfaces (the appointment form, the block chooser), so they take the design's own number. */
.salt-form .salt-form__input,
.salt-form .salt-form__select,
.salt-form .salt-form__textarea{
  font-family:inherit;font-size:16px;line-height:1.4;color:var(--sf-marine);background:#fff;
  border:1px solid var(--sf-line);border-radius:8px;padding:12px 14px;width:100%;
  box-sizing:border-box;min-height:46px;appearance:none}
.salt-form .salt-form__textarea{min-height:96px;resize:vertical}
.salt-form .salt-form__select{
  background-image:linear-gradient(45deg,transparent 50%,var(--sf-marine) 50%),
    linear-gradient(135deg,var(--sf-marine) 50%,transparent 50%);
  background-position:calc(100% - 19px) 21px,calc(100% - 14px) 21px;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:40px}
.salt-form .salt-form__input:hover,
.salt-form .salt-form__select:hover,
.salt-form .salt-form__textarea:hover{border-color:rgba(2,41,56,.45)}
.salt-form .salt-form__input:focus,
.salt-form .salt-form__select:focus,
.salt-form .salt-form__textarea:focus{
  border-color:var(--sf-marine);outline:2px solid var(--sf-spray);outline-offset:1px}
.salt-form .salt-form__input[aria-invalid=true],
.salt-form .salt-form__select[aria-invalid=true],
.salt-form .salt-form__textarea[aria-invalid=true]{border-color:var(--sf-err);border-width:2px}

.salt-form .salt-form__btn{
  font-family:inherit;font-size:13px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--sf-silica);background:var(--sf-marine);border:1px solid var(--sf-marine);
  border-radius:8px;padding:15px 30px;cursor:pointer;
  width:max-content;max-width:100%;min-width:min-content;min-height:48px}
.salt-form .salt-form__btn:hover{background:#04384c}
.salt-form .salt-form__btn:focus-visible{outline:2px solid var(--sf-spray);outline-offset:1px}
.salt-form .salt-form__btn.ghost{background:#fff;color:var(--sf-marine)}
.salt-form .salt-form__btn.ghost:hover{background:var(--sf-silica)}
/* Disabled is a real visual state, not just an attribute: the appointment submit sits disabled
 * until a day AND a time are chosen, and the summary line above it says what is missing. */
.salt-form .salt-form__btn.is-disabled,
.salt-form .salt-form__btn[disabled]{
  background:#E5DFD3;border-color:#E5DFD3;color:var(--sf-scrub);cursor:not-allowed;opacity:1}
.salt-form .salt-form__btn.is-busy{background:#04384c;border-color:#04384c;cursor:progress}

/*
 * ---------- the rejected alert, and the error line under a field ----------
 *
 * Both take the pack's own values, and both are scoped to the treatment. The four forms that
 * carry it (vip, appointment, register, brochure) draw the pack's alert; offer and
 * design-guidelines keep the red-on-pink panel they shipped with, because they are out of the
 * pack's scope and nothing here may move them.
 *
 * ⛔ NO display RULE IN EITHER. The base .salt-form__alert is display:none until the engine adds
 * .is-shown, and the pack has neither — dropped in unscoped, the pack's alert block would have
 * left a red box permanently on every screen of every form and turned the toggle into a no-op.
 * That is the silent-failure shape this component exists to end, so the switch stays above and
 * only the paint is restated here.
 *
 * (0,3,0) against the base rule's (0,2,0), so the border shorthand wins outright rather than by
 * source order — it has to reset the base's 3px left border, not sit beside it.
 */
.salt-form.v-spray .salt-form__alert{font-size:13.5px;line-height:1.6;padding:14px 16px}
.salt-form.v-spray .salt-form__alert.err{background:#fdf3f3;border:1px solid var(--sf-err);
  color:var(--sf-marine)}
/* The lead sentence is the Error colour; the instruction under it is Marine, because it is the
 * part a person has to read and act on. 4.5:1 on both is asserted, not assumed. */
.salt-form .salt-form__alert.err strong{color:var(--sf-err);font-weight:600}
/* The fallback address is a real mailto and has to look like one. `color:inherit` so it stays
 * legible on either alert ground — Marine on the treated forms, Error on the two live ones. */
.salt-form .salt-form__alert.err a{color:inherit;text-decoration:underline}
/* The pack's error type: 12.5px 500, against the base's 12px 400. Scoped to the treatment so
 * offer and design-guidelines keep the error line they render today. */
.salt-form.v-spray .salt-form__err{font-size:12.5px;font-weight:500;line-height:1.5;margin-top:6px}

/* The quiet "Saved. You are on the VIP list." acknowledgement under the band on page 2. */
.salt-form__saved{display:flex;align-items:center;gap:9px;font-size:12.5px;color:var(--sf-scrub);margin:0 0 18px}
.salt-form__saved .dot{width:8px;height:8px;border-radius:8px;background:var(--sf-spray);
  border:1px solid rgba(2,41,56,.3);flex:none}

/* ---------- appointment: day and time chips ----------
 * No fixed widths anywhere. Three earlier attempts at fixed widths on this site each broke
 * something — a clipped label, a three-line header CTA, a page scrolling sideways — so these
 * state the requirement instead: one line where there is room, never past the viewport, never
 * crushed, and the ROW wraps. The strip wraps and never scrolls horizontally. */
.salt-form__fieldset{border:0;padding:0;margin:0 0 22px;min-width:0}
.salt-form__legend{font-size:15px;font-weight:600;padding:0;margin:0 0 12px;color:var(--sf-marine)}
.salt-form__chips{display:flex;flex-wrap:wrap;gap:8px}
/*
 * ⚠ SCOPED ONE LEVEL DEEPER THAN THE PACK WRITES IT, and it has to be. `.salt-form button` above
 * scores (0,1,1) and paints every button in a form card uppercase, 600 14px, 14px/22px padding,
 * Marine on Marine. The pack's `.salt-form__chip` scores (0,1,0) and LOSES to it outright —
 * source order does not enter into it.
 *
 * MEASURED on the appointment form before this changed: every time chip rendered
 * `text-transform: uppercase`, so the strip read 9:00 AM / 10:00 AM while the summary line
 * directly under it read "Requesting Wednesday 19 Aug at 11:00 am" and the confirmation read it
 * back the same way — the same value in two cases on one screen. Padding came out 16px 34px
 * against the pack's 10px 14px, and the chip stood 53px tall instead of 44.
 *
 * Restated here at (0,2,0) so the pack's own values win. The three rules that qualify this one
 * (:hover, :focus-visible, .is-selected) are bumped to match, so none of them is left losing to
 * the base it is supposed to override.
 */
.salt-form .salt-form__chip{font-family:inherit;font-size:14.5px;font-weight:500;line-height:1.2;
  letter-spacing:normal;text-transform:none;background:#fff;color:var(--sf-marine);
  border:1px solid var(--sf-line);border-radius:8px;cursor:pointer;
  width:max-content;max-width:100%;min-width:min-content;min-height:44px;
  padding:10px 14px}
.salt-form .salt-form__chip:hover{border-color:var(--sf-scrub);background:var(--sf-silica);opacity:1}
.salt-form .salt-form__chip:focus-visible{outline:2px solid var(--sf-spray);outline-offset:1px}
/* Selected INVERTS as well as recolouring, so the state never depends on colour alone. */
.salt-form .salt-form__chip.is-selected,
.salt-form .salt-form__chip[aria-pressed=true]{background:var(--sf-marine);border-color:var(--sf-marine);color:var(--sf-silica)}
.salt-form__chip.day{display:grid;gap:2px;justify-items:center;padding:8px 10px;min-width:62px;line-height:1.15}
.salt-form__chip.day .dow,.salt-form__chip.day .mon{
  font-size:10.5px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--sf-scrub)}
.salt-form__chip.day .dat{font-size:19px;font-weight:600;color:var(--sf-marine)}
.salt-form__chip.day.is-selected .dow,.salt-form__chip.day.is-selected .mon,
.salt-form__chip.day[aria-pressed=true] .dow,.salt-form__chip.day[aria-pressed=true] .mon{color:#9FD9C6}
.salt-form__chip.day.is-selected .dat,
.salt-form__chip.day[aria-pressed=true] .dat{color:var(--sf-silica)}

/* The line directly above the appointment submit. It carries what is missing, so the disabled
 * submit needs no error state of its own. */
.salt-form__summary{font-size:15px;line-height:1.6;color:var(--sf-scrub);background:var(--sf-silica);
  border-radius:8px;padding:14px 16px;margin:0 0 16px}
.salt-form__summary strong{color:var(--sf-marine);font-weight:600}

/* The hint under the time strip sits further off it than a hint under a field does — 12px on
 * the sheet, against the 4px above. Scoped to a fieldset, which only the appointment renders. */
.salt-form .salt-form__fieldset>.salt-form__hint{margin-top:12px}

/*
 * TWO COLUMNS ABOVE THE BREAKPOINT, ONE BELOW IT, CHIPS FIRST EITHER WAY.
 *
 * The media query below is the design's own rule and is absolute: under a 640px VIEWPORT this is
 * always one column. `auto-fit` handles the case a viewport breakpoint cannot see — the same form
 * inside the 560px pop-up panel on a 1440px screen. A flat `1fr 1fr` there gives two 230px
 * columns and a 105px "First name" field, on a desktop wide enough that the media query is
 * nowhere near firing, because the constraint is the PANEL and the query measures the WINDOW.
 *
 * ⚠ It follows that between roughly 641px and 690px this renders one column where the design's
 * breakpoint alone would have drawn two. That is deliberate: at that width the two columns would
 * be under 260px each, which is the layout this rule exists to prevent, not a version of it.
 *
 * Chips lead in the DOM, so "chips first" is the source order rather than a CSS reorder — the
 * phone reading order and the tab order stay the same order.
 */
.salt-form__cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:36px}
.salt-form__colhead{font-size:15px;font-weight:600;margin:0 0 14px;color:var(--sf-marine)}

/* ---------- offer: the block chooser ---------- */
/*
 * ⚠ SPECIFICITY, AND IT WAS MEASURED, NOT REASONED ABOUT.
 *
 * `.salt-form button` above scores (0,1,1) and paints EVERY button inside a form card: Marine
 * ground, Silica text, uppercase, 600 14px, 14px/22px padding, a Marine border and
 * width:max-content. The design pack's own card and chip rules score (0,1,0) and lose to it
 * outright — source order is irrelevant, specificity decides first.
 *
 * Left alone, that is not a cosmetic drift. Driven in a browser, every block card rendered as a
 * solid Marine button: `.sz` and `.pr` are Marine text, so the SIZE AND THE PRICE OF EVERY BLOCK
 * measured 1.00:1 against their own ground and were invisible. The sort row was worse — an
 * unselected chip and the selected one both painted Marine, so the row showed no selection at
 * all and "Size, selected by default" was drawn nowhere on screen.
 *
 * The pack's own stylesheet does not carry a generic `button` rule, so the pack is not wrong;
 * the merge is where the two meet. Fixed by scoping the pack's values one level deeper — class
 * selectors only, no ids, no :where() (it scores zero and would lose again). The selected chip
 * is restated at (0,3,0) rather than left to tie on source order.
 *
 * Same shape of defect as the select arrow and the selected day chip, both found the same way on
 * this job: read the cascade, do not trust it.
 */
.salt-form__sortrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 18px}
.salt-form__sortrow .lab{font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--sf-scrub);margin-right:4px}
.salt-form__blocks{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px}
/* The whole card is the tap target, so it is a <button>, not a card with a link inside it. */
.salt-form .salt-form__blockcard{font-family:inherit;font-size:14px;font-weight:400;line-height:1.4;letter-spacing:normal;text-transform:none;
  text-align:left;background:#fff;color:var(--sf-marine);border:1px solid var(--sf-line);
  border-radius:8px;padding:14px 16px;cursor:pointer;display:grid;gap:4px;min-height:44px;
  width:auto;max-width:100%;min-width:0}
.salt-form .salt-form__blockcard:hover{border-color:var(--sf-marine);background:var(--sf-silica);opacity:1}
.salt-form .salt-form__blockcard:focus-visible{outline:2px solid var(--sf-spray);outline-offset:1px;
  box-shadow:0 0 0 4px var(--sf-marine)}
.salt-form__blockcard .no{font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--sf-scrub)}
.salt-form__blockcard .sz{font-size:20px;font-weight:600;color:var(--sf-marine)}
.salt-form__blockcard .pr{font-size:14px;color:var(--sf-marine)}
/* Sold and not-yet-released. Named, never priced — .pr is deliberately absent from this markup. */
.salt-form__notice{background:var(--sf-marine);border-radius:8px;padding:22px 24px;margin:0 0 22px}
.salt-form__notice h3{font-size:20px;font-weight:600;color:var(--sf-silica);margin:0 0 8px}
.salt-form__notice p{font-size:14px;line-height:1.7;color:var(--sf-spray);margin:0;max-width:52ch}
.salt-form__links{display:flex;flex-wrap:wrap;gap:10px 22px;align-items:center;margin:18px 0 0}
.salt-form__links a{font-size:13.5px;font-weight:500;color:var(--sf-marine)}
/*
 * Two of the chooser's exits are ACTIONS, not destinations: "Register your interest instead"
 * opens the register modal every other page of this site opens, and "Choose from all blocks now
 * selling" swaps the screen in place — the chooser is not a page, so linking to one would reload
 * /offer/ to reach a state it is already in. A <button> is the honest element for both. It reads
 * as the pack's link and keeps a 44px target: the underline is the affordance, the padding is
 * the tap area, and the negative margin keeps it optically on the same baseline as the anchor
 * beside it.
 */
.salt-form .salt-form__links button{font-family:inherit;font-size:13.5px;font-weight:500;line-height:1.4;letter-spacing:normal;
  text-transform:none;color:var(--sf-marine);background:none;border:0;
  border-bottom:1px solid rgba(2,41,56,.35);border-radius:0;cursor:pointer;
  padding:12px 0;margin:-12px 0;width:auto;min-height:44px;text-align:left}
.salt-form .salt-form__links button:hover{border-bottom-color:var(--sf-marine);opacity:1}
.salt-form .salt-form__links button:focus-visible{outline:2px solid var(--sf-spray);outline-offset:2px}
/*
 * The not-yet-released screen's only button is a LINK to /vip/ — an <a> carrying the button
 * class, exactly as the design pack draws it. Without these three declarations it renders as
 * underlined inline text at the button's letter-spacing, and its 48px min-height does nothing.
 */
a.salt-form__btn{display:inline-flex;align-items:center;justify-content:center;text-decoration:none}
/*
 * The not-yet-released screen's rhythm, from the pack: 18px under the VIP button, 16px under the
 * blurb. Adjacent-sibling rules rather than a wrapper class — no other screen in this stylesheet
 * puts a blurb after an action row or a block grid after a blurb, so these can only ever match
 * the screen they were written for.
 */
.salt-form__actions + .salt-form__blurb{margin:18px 0 0}
.salt-form__blurb + .salt-form__blocks{margin-top:16px}

/* The pack's block summary card uses .num/.meta where the live engine emits .lot/.det. Both are
 * carried: .lot/.det above keep /offer/ rendering today, these serve the pack's markup. */
.salt-form__block .num{font-size:34px;font-weight:600;line-height:1;color:var(--sf-spray)}
.salt-form__block .meta{font-size:13.5px;line-height:1.65;color:var(--sf-silica)}
.salt-form__block .meta strong{font-weight:600}

/* ---------- confirmation panel additions ---------- */
.salt-form__done .eyebrow{font-size:12px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;
  color:var(--sf-scrub);margin:0 0 12px}
/*
 * A legal line INSIDE a confirmation panel loses to the panel's own paragraph rule without
 * this: .salt-form__done p is (0,1,1) and .salt-form__legal is (0,1,0), so "RLA 222182." would
 * render at 14.5px body size instead of the 11.5px the type scale specifies. The pack solves it
 * with a type+class selector and that line was not carried in the first merge pass; carried now,
 * scoped under .salt-form. It cannot reach a live form: register, brochure, design-guidelines
 * and offer render no legal line inside .salt-form__done.
 */
.salt-form .salt-form__done p.salt-form__legal{font-size:11.5px;line-height:1.6;margin:18px 0 0}
/* The pack caps the confirmation body at 52ch. Scoped to .donebody, which only a banded panel
 * renders, so the four live panels keep the measure they shipped with. */
.salt-form .salt-form__done .donebody p{max-width:52ch}
.salt-form__done .readback{background:var(--sf-spray);border-radius:8px;padding:16px 18px;
  font-size:16px;line-height:1.6;color:var(--sf-marine);margin:0 0 18px}
.salt-form__done .readback strong{font-weight:600}

/* ==========================================================================================
 * SEA SPRAY (.v-spray) — the chosen treatment, option 1b on the specimen sheet.
 *
 * Scoped .salt-form.v-spray, both classes on the same element, NOT .v-spray alone. The base
 * rule .salt-form input[type=text] scores (0,2,1) because an attribute selector counts in the
 * class column; the pack's own .v-spray .salt-form__input scores (0,2,0) and would have LOST to
 * it. .salt-form.v-spray input[type=text] scores (0,3,1) and wins outright rather than by
 * source order, which is the kind of tie that breaks the next time the file is reordered.
 *
 * ⚠ WHO CARRIES IT, as of 14/08: vip, appointment, register and brochure. The last two were added
 * on Keith's word — "apply the treatment to the EXISTING register and brochure forms so the set
 * is visually coherent; do not change their fields, their copy or their behaviour: presentation
 * only" — which is why the engine splits `variant` (this card) from `packCopy` (the pack's
 * wording and its required fields). See the note above COPY in public/js/salt-forms.js.
 *
 * ⛔ offer and design-guidelines do NOT carry it, and nothing in this block may reach them. That
 * is measured every time by scripts/gate-forms-css.mjs (computed style of every node of every
 * screen, both sides of the breakpoint), not asserted.
 * ========================================================================================== */
/*
 * ⚠ box-sizing ON THE CARD ITSELF, not only its descendants.
 *
 * `.salt-form *` at the top of this file matches every element INSIDE the card and never the
 * card, so max-width applied to the CONTENT box and this rule's 32px padding was added on top of
 * it. MEASURED on the built page: getBoundingClientRect().width returned 624px against a
 * computed max-width of 560px, and 984px against the appointment's 920px.
 *
 * It also broke the two-column collapse inside the 560px pop-up panel — the panel handed the
 * card 560px of CONTENT, which is enough for two columns, so the appointment form rendered side
 * by side inside a phone-sized modal on a desktop the media query is nowhere near firing on.
 *
 * The design pack sets it on bare `.salt-form` (its line 2) and the first merge pass dropped it.
 * It is restored HERE, on the treatment, rather than there. Padding on the card is what makes
 * the two boxes differ, and only the treatment has padding — so scoped this way the four
 * signed-off live forms carry not one changed computed property, and gate-forms-css.mjs stays a
 * clean PASS on them instead of a failure everyone has to reason past. A gate people argue with
 * is a gate people stop running.
 */
.salt-form.v-spray{background:var(--sf-silica);border-radius:8px;padding:32px;overflow:hidden;
  max-width:560px;box-sizing:border-box}
/*
 * The appointment request is the one two-column screen, drawn at 920px on the specimen sheet
 * against 560px for every other form. (0,3,0) beats the 560px rule's (0,2,0) outright rather
 * than by source order. The engine toggles `is-wide` from the copy model, so the modal body —
 * one element reused by every intent — cannot be left wide behind a brochure form.
 */
.salt-form.v-spray.is-wide{max-width:920px}

/* The mint band bleeds to the card edge by cancelling the card's own padding. */
.salt-form.v-spray .salt-form__head{background:var(--sf-spray);margin:-32px -32px 26px;padding:26px 32px 22px}
.salt-form.v-spray .salt-form__head .salt-form__steps{margin:0 0 14px}
.salt-form.v-spray .salt-form__body{padding:0}
.salt-form.v-spray .salt-form__title{font-family:'Ruthfully','Kodchasan',cursive;font-weight:400;
  font-size:40px;line-height:1.15;letter-spacing:0;margin:0 0 6px}
.salt-form.v-spray .salt-form__blurb{color:var(--sf-marine);font-size:14.5px;line-height:1.65}
.salt-form.v-spray .salt-form__stepnote{color:#0C5064;font-weight:600;letter-spacing:.14em;margin-bottom:0}
.salt-form.v-spray .salt-form__step{background:rgba(2,41,56,.18)}
.salt-form.v-spray .salt-form__step.is-done{background:var(--sf-scrub)}
.salt-form.v-spray .salt-form__step.is-current{background:var(--sf-marine)}

/* Inputs become underlines. Covers BOTH vocabularies: the bare elements the engine emits today
 * and the BEM classes the new screens use, so a form does not half-convert. */
.salt-form.v-spray input[type=text],
.salt-form.v-spray input[type=email],
.salt-form.v-spray input[type=tel],
.salt-form.v-spray select,
.salt-form.v-spray textarea,
.salt-form.v-spray .salt-form__input,
.salt-form.v-spray .salt-form__select,
.salt-form.v-spray .salt-form__textarea{
  background:transparent;border:0;border-bottom:1px solid rgba(2,41,56,.35);border-radius:0;padding:10px 2px;
  /*
   * 16px flat, and it has to be stated HERE rather than left to the rules above.
   *
   * The two vocabularies resolve to different sizes on their own: .salt-form input[type=text]
   * is (0,2,1) and wins with max(16px,1em) — 17.832px once the theme's fluid ramp is in play —
   * while .salt-form .salt-form__select is (0,2,0) and wins over .salt-form select (0,1,1) with
   * a flat 16px. MEASURED on the built page: text inputs 17.832px, the select beside them 16px,
   * in the same form. Both clear the iOS floor and they plainly disagreed with each other.
   *
   * The treatment is a new design surface — Silica card, mint band, underline fields, none of it
   * the signed-off live form — so it takes the design pack's own number for every field, and
   * these selectors score (0,3,1)/(0,3,0) so they win outright rather than by source order. The
   * 44px target is not lost with them: .salt-form .salt-form__input carries min-height:46px and
   * nothing here overrides it.
   */
  font-size:16px;line-height:1.4}
.salt-form.v-spray input[type=text]:hover,
.salt-form.v-spray input[type=email]:hover,
.salt-form.v-spray input[type=tel]:hover,
.salt-form.v-spray select:hover,
.salt-form.v-spray .salt-form__input:hover,
.salt-form.v-spray .salt-form__select:hover{border-bottom-color:var(--sf-marine)}
.salt-form.v-spray input:focus,
.salt-form.v-spray select:focus,
.salt-form.v-spray textarea:focus,
.salt-form.v-spray .salt-form__input:focus,
.salt-form.v-spray .salt-form__select:focus,
.salt-form.v-spray .salt-form__textarea:focus{
  border-bottom:2px solid var(--sf-marine);outline:2px solid var(--sf-spray);outline-offset:3px}
/*
 * ⛔ INVALID, RESTATED — AND WITHOUT THIS RULE THE ERROR BORDER DOES NOT EXIST IN THE TREATMENT.
 *
 * MEASURED, not read: an empty required Email on the VIP form computed
 * border-bottom-color rgba(2,41,56,.35) — the ordinary Line grey — while aria-invalid was "true"
 * and the message underneath it was rendered in Error red. The states table says invalid is an
 * "error border PLUS error text below"; the border half has never painted on a banded form.
 *
 * The cascade, in full: the underline rule above is `.salt-form.v-spray .salt-form__input`
 * (0,3,0) for the BEM vocabulary and `.salt-form.v-spray input[type=email]` (0,3,1) for the bare
 * one, and it sets the `border-bottom` SHORTHAND — colour included. The invalid rule it has to
 * beat, `.salt-form .salt-form__input[aria-invalid=true]`, is (0,3,0) and loses to the second and
 * ties with the first. So the treatment quietly repainted every invalid field grey.
 *
 * Restated at (0,4,0) so it wins outright. Same shape as the select arrow and the selected chip
 * on this same stylesheet: a shorthand in a more specific rule undoing a state nobody thought to
 * re-measure.
 *
 * ⚠ AFTER :focus DELIBERATELY. Both are (0,4,0), so source order decides, and a field that is
 * focused AND invalid stays Error rather than reverting to Marine — the error is the thing the
 * person has to act on, and focus is still shown by the Sea Spray outline, which this does not
 * touch. The two bare-element selectors are (0,3,1): they tie with the underline rule and are
 * ordered after it. They are unreachable today, because the engine gives every treated form the
 * BEM vocabulary, and they are here so a future form that half-converts still shows its errors.
 */
.salt-form.v-spray input[aria-invalid=true],
.salt-form.v-spray select[aria-invalid=true],
.salt-form.v-spray textarea[aria-invalid=true],
.salt-form.v-spray .salt-form__input[aria-invalid=true],
.salt-form.v-spray .salt-form__select[aria-invalid=true],
.salt-form.v-spray .salt-form__textarea[aria-invalid=true]{
  border-bottom-color:var(--sf-err);border-bottom-width:2px}

/*
 * THE ARROW HAS TO BE RESTATED HERE, and this is a departure from the pack.
 *
 * The rule above sets `background:transparent` to strip the field's box. That is the SHORTHAND,
 * so it also resets background-image to none and takes the two-gradient arrow with it. The pack
 * has the same sequence — its line 130 sets the shorthand, its line 133 sets only
 * background-position — so its own specimen renders a dropdown that looks exactly like a text
 * field. Caught by screenshotting the built page rather than by reading the CSS.
 *
 * The README lists the arrow under Assets ("The select arrow is drawn with two CSS gradients"),
 * so restoring it follows the design rather than departing from it. padding-right comes back
 * with it: the shorthand padding above also dropped the 40px that keeps a long option — "Second
 * or subsequent home" — from running underneath the arrow.
 */
.salt-form.v-spray select,
.salt-form.v-spray .salt-form__select{
  background-image:linear-gradient(45deg,transparent 50%,var(--sf-marine) 50%),
    linear-gradient(135deg,var(--sf-marine) 50%,transparent 50%);
  background-repeat:no-repeat;background-size:5px 5px,5px 5px;
  background-position:calc(100% - 12px) 21px,calc(100% - 7px) 21px;
  padding-right:28px}

/* Labels go 12px uppercase Scrub in this treatment. Both vocabularies again. */
.salt-form.v-spray label,
.salt-form.v-spray .salt-form__label{font-size:12px;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--sf-scrub)}
.salt-form.v-spray .salt-form__hp label{text-transform:none;letter-spacing:0}

.salt-form.v-spray button,
.salt-form.v-spray .salt-form__btn{border-radius:8px;padding:16px 34px}
/*
 * THE SKIP IS A QUIET TEXT BUTTON, and every one of these declarations is load-bearing.
 *
 * `.salt-form__skip` scores (0,1,0) and `.salt-form button` scores (0,1,1) — an element
 * selector in the mix beats a lone class — so the base skip rule's background:none, its colour
 * and its text-transform ALL LOSE, and the skip paints as a second solid Marine button sitting
 * beside the primary action with equal visual weight. MEASURED on the built page:
 * background rgb(2,41,56), colour rgb(245,239,228), uppercase — a form offering two identical
 * buttons, one of which means "no thanks".
 *
 * ⚠ THIS WAS ALSO TRUE ON THE LIVE REGISTER FORM, and this rule now fixes it — not as a side
 * effect, but because Keith asked for the treatment on register and brochure on 14/08 and the
 * skip is part of how the treated card looks. Scoped to .salt-form.v-spray — (0,3,0), which
 * beats (0,1,1) outright — so it reaches the four treated forms and nothing else. The skip's
 * WORDING on register is untouched: "Skip these questions" and "Skip and finish", as shipped.
 */
.salt-form.v-spray .salt-form__skip{padding:6px 2px;min-height:44px;text-decoration:none;
  background:none;border:0;border-bottom:1px solid rgba(2,41,56,.35);
  color:var(--sf-scrub);font-size:13.5px;font-weight:500;text-transform:none;letter-spacing:0}
.salt-form.v-spray .salt-form__skip:hover{color:var(--sf-marine);border-bottom-color:var(--sf-marine);
  background:none;opacity:1}

/* Time chips are pills, day chips 12px tiles. */
/*
 * ⚠ THE PADDING IS RESTATED HERE, and it is not decoration. `.salt-form.v-spray button` below
 * scores (0,2,1) and sets `padding:16px 34px` — the primary action's padding — which beats the
 * pack's `.salt-form__chip` (0,1,0) AND the (0,2,0) restatement above it. MEASURED on the
 * appointment form: every time chip stood 53px tall with 34px of side padding, and the day tiles
 * lost the compact 62px shape the design draws them as. (0,3,0) puts the pack's own values back.
 */
.salt-form.v-spray .salt-form__chip{border-radius:24px;background:#fff;padding:10px 14px}
.salt-form.v-spray .salt-form__chip.day{border-radius:12px;padding:8px 10px}
/*
 * ⚠ SELECTED AND HOVER BOTH HAVE TO BE RESTATED HERE, and this is a departure from the pack.
 *
 * The rule above gives every chip a white ground in this treatment. `.salt-form.v-spray
 * .salt-form__chip` scores (0,3,0); `.salt-form__chip.is-selected` scores (0,2,0) and LOSES
 * outright, so a chosen chip painted white with Silica text on it — #F5EFE4 on #FFFFFF, a
 * contrast ratio of about 1.06:1. The chip a buyer had just pressed was, in practice, blank.
 *
 * ⚠ THE PACK HAS THE SAME DEFECT. Its `.v-spray .salt-form__chip` (line 136) and its
 * `.salt-form__chip.is-selected` (line 59) are BOTH (0,2,0), so the later one wins on source
 * order and its own specimen sheet draws every selected chip white. Same shape as the select
 * arrow its line 130 destroys: a shorthand or a later same-weight rule quietly undoing a state.
 * Caught by measuring the computed colours of a pressed chip, not by reading the CSS.
 *
 * :hover was lost the same way and is restored above `.is-selected`, so hovering a chosen chip
 * keeps it Marine — (0,4,0) beats the (0,3,0) hover rather than relying on order.
 */
.salt-form.v-spray .salt-form__chip:hover{border-color:var(--sf-scrub);background:var(--sf-silica)}
.salt-form.v-spray .salt-form__chip.is-selected,
.salt-form.v-spray .salt-form__chip[aria-pressed=true]{
  background:var(--sf-marine);border-color:var(--sf-marine);color:var(--sf-silica)}
.salt-form.v-spray .salt-form__summary{background:#fff}

/* The confirmation panel gets the same bleeding band. */
.salt-form.v-spray .salt-form__done{margin:-32px;padding:0}
.salt-form.v-spray .salt-form__done .doneband{background:var(--sf-spray);padding:26px 32px 22px;margin:0 0 24px}
.salt-form.v-spray .salt-form__done .donebody{padding:0 32px 32px}
.salt-form.v-spray .salt-form__done .doneband h3{font-family:'Ruthfully','Kodchasan',cursive;
  font-weight:400;font-size:40px;line-height:1.15;margin:0}

@media(max-width:640px){
  .salt-form.v-spray{padding:24px 20px}
  .salt-form.v-spray .salt-form__head{margin:-24px -20px 22px;padding:22px 20px 18px}
  .salt-form.v-spray .salt-form__done{margin:-24px -20px}
  .salt-form.v-spray .salt-form__done .doneband{padding:22px 20px 18px}
  .salt-form.v-spray .salt-form__done .donebody{padding:0 20px 24px}
  .salt-form__cols{grid-template-columns:1fr}
  .salt-form__blocks{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
}

/* ==========================================================================================
 * THE SALES-AGENT CARD on a confirmation. Keith, 18/08: every confirmation that promises a
 * call names the agent and lets the reader dial her from the screen.
 *
 * Deliberately the SAME treatment as .readback (spray ground, 8px radius, same insets) rather
 * than a new one: both are "the panel telling you a specific fact about what happens next", and
 * the appointment confirmation can show both at once. A second callout style would have made
 * that screen look like two different components stacked.
 *
 * The two links are block-level with vertical padding so each clears the 44px touch target on
 * a phone, which is where a "call the agent" link is actually used. Underlined, so the
 * affordance does not rest on colour alone (WCAG 2.2 AA, 1.4.1).
 * ========================================================================================== */
.salt-form__done .salt-form__agent{background:var(--sf-spray);border-radius:8px;
  padding:16px 18px;margin:18px 0 0}
.salt-form .salt-form__done .salt-form__agent p{margin:0;max-width:none}
.salt-form__done .salt-form__agent-lead{font-size:12px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--sf-marine);margin:0 0 8px}
.salt-form__done .salt-form__agent-name{font-size:16.5px;font-weight:600;color:var(--sf-marine)}
.salt-form__done .salt-form__agent-role{font-size:13.5px;color:var(--sf-scrub);margin:0 0 6px}
.salt-form__done .salt-form__agent-link{display:block;padding:9px 0;font-size:15px;
  font-weight:600;color:var(--sf-marine);text-decoration:underline;text-underline-offset:3px;
  min-height:44px;line-height:26px}
.salt-form__done .salt-form__agent-link:focus-visible{outline:2px solid var(--sf-marine);
  outline-offset:3px;border-radius:4px}
