/* Azuvyn — website intake form.
   MIDNIGHT SLATE: Azuvyn's artifact package, projected from the approved Midnight
   Marble system. Exact values arrive first from /_generated/midnight-slate.css;
   flagship-site/src/data/brand-tokens.json owns them. This file maps only local
   component roles. Mobile-first, one column, big tap targets.

   Fonts are SELF-HOSTED. `_headers` ships `default-src 'self'` with no font-src, so a
   Google Fonts URL is blocked outright and the page would quietly fall back to system
   UI — looking like a different company on the first screen a stranger sees. Files are
   the same four woff2 the client cockpit serves (analytics-reporting/public/fonts). */

@font-face {
  font-family: "Sora";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("/fonts/sora-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Sora";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("/fonts/sora-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Surfaces, darkest to lightest. --field is DARKER than the card it sits on: a
     recessed input reads as "type here" on dark, where a raised one reads as a chip. */
  --bg: var(--azuvyn-midnight-slate-background);
  --card: var(--azuvyn-midnight-slate-surface-raised);
  --slate-2: var(--azuvyn-midnight-slate-surface-highest);
  --slate-3: var(--azuvyn-midnight-slate-surface-highest);
  --field: var(--azuvyn-midnight-slate-surface);

  /* Text */
  --ink: var(--azuvyn-midnight-slate-text-display);
  --text: var(--azuvyn-midnight-slate-text-body);
  --ink-soft: var(--azuvyn-midnight-slate-text-muted);
  --placeholder: var(--azuvyn-midnight-slate-text-muted);

  --line: var(--azuvyn-midnight-slate-border);
  --line-strong: var(--azuvyn-midnight-slate-border-strong);

  --brand: var(--azuvyn-midnight-slate-action);
  --brand-deep: var(--azuvyn-midnight-slate-action-hover);
  --brand-text: var(--azuvyn-midnight-slate-action);
  --brand-wash: color-mix(in srgb, var(--brand) 18%, transparent);
  --brand-edge: color-mix(in srgb, var(--brand) 32%, transparent);

  --accent: var(--azuvyn-midnight-slate-guide);
  --accent-wash: color-mix(in srgb, var(--accent) 13%, transparent);
  --accent-edge: color-mix(in srgb, var(--accent) 32%, transparent);

  --ok: var(--azuvyn-midnight-slate-positive);
  --err: #ff8f8f;
  --err-wash: rgba(255, 143, 143, 0.10);

  --radius: 16px;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 18px 44px rgba(0, 0, 0, 0.34);
  --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Sora", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Tells the browser to render its OWN controls dark — the time picker, the select
     popup, the scrollbar. Without it those arrive white on a black page. */
  color-scheme: dark;
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: var(--font);
  color: var(--text);
  background: var(--bg);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: 20px 16px 64px;
}

.hidden { display: none !important; }
.center { text-align: center; }

/* ── Automated-marketing intake additions (marketing.html) ──────────── */
.row { display: flex; gap: 12px; flex-wrap: wrap; }
.row .field { flex: 1 1 120px; }
.agreement {
  white-space: pre-wrap;
  font-family: var(--font);
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 14px;
  max-height: 280px;
  overflow: auto;
  margin: 0 0 14px;
}
.muted { color: var(--ink-soft); }
.small { font-size: 14px; }

/* ── Hero ──────────────────────────────────────────────────────────── */
.hero {
  padding: 28px 22px 22px;
  margin-bottom: 16px;
  border-radius: var(--radius);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background:
    radial-gradient(ellipse 62% 88% at 14% 18%, rgba(43, 92, 230, 0.20), transparent 72%),
    radial-gradient(ellipse 52% 70% at 92% 88%, rgba(243, 168, 27, 0.055), transparent 76%),
    linear-gradient(150deg, #1b2732 0%, #131d26 62%, #0f1922 100%);
  border: 1px solid var(--line);
}
/* The dot field from the homepage hero, at the same 46px pitch, faded out before
   it reaches the headline — decoration must never compete with the question. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.5;
  background-image: radial-gradient(rgba(174, 185, 184, 0.22) 0.6px, transparent 0.6px);
  background-size: 46px 46px;
  -webkit-mask-image: linear-gradient(to bottom, black, transparent 86%);
  mask-image: linear-gradient(to bottom, black, transparent 86%);
  pointer-events: none;
}
/* The lockup: the AV monogram beside the wordmark, at the homepage's proportions
   (the mark's cap height matches the wordmark's, which is why the two font sizes
   and the mark height are tied to the same number rather than set apart). */
.brand {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}
.brand-mark {
  height: 22px;
  width: auto;
  flex: 0 0 auto;
  display: block;
}
.brand-word {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 20px;
  letter-spacing: -0.02em;
  color: var(--ink);
  line-height: 1;
}
.h1 {
  position: relative;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 30px;
  line-height: 1.12;
  margin: 0 0 12px;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-wrap: balance;
}
.h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.015em;
  color: var(--ink);
  margin: 0 0 14px;
}
.lead { position: relative; font-size: 16px; color: var(--ink-soft); margin: 0 0 12px; }
.privacy {
  display: inline-block;
  font-size: 13px;
  color: var(--brand-text);
  background: var(--brand-wash);
  border-radius: 999px;
  padding: 6px 12px;
  margin: 4px 0 0;
}

/* ── Cards ─────────────────────────────────────────────────────────── */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px;
  margin-bottom: 16px;
}

/* ── Fields ────────────────────────────────────────────────────────── */
.field { display: block; margin-bottom: 18px; }
.field:last-child { margin-bottom: 0; }
/* Separates a field from the grid/table above it (the receptionist form's hours
   note and price range). It exists as a class because the predecessor carried this
   spacing as `style="margin-top:16px"`, and this app serves `style-src 'self'` with
   no `'unsafe-inline'` — which blocks style ATTRIBUTES too, not just <style> blocks.
   Ported verbatim would have rendered flush against the grid on every deployment,
   visible only to someone looking at the page. */
.field.spaced { margin-top: 16px; }
.q { display: block; font-weight: 600; font-size: 15px; color: var(--ink); margin-bottom: 7px; }
.q em { font-style: normal; font-weight: 400; color: var(--ink-soft); }
/* One labelled line inside a grouped question (the social pages: one input per
   network under one legend). Lighter than `.q` so the legend still reads as the
   question and these as its parts. */
.field.sub { margin-bottom: 12px; }
.q-sub { display: block; font-weight: 500; font-size: 14px; color: var(--ink); margin-bottom: 5px; }
.q-sub em { font-style: normal; font-weight: 400; color: var(--ink-soft); }

/* ⚠️ EVERY TEXTUAL INPUT TYPE MUST BE LISTED HERE.
   `input[type=email]` was missing until 2026-08-27, so the public entry's email
   box — the ONE field the whole feature turns on, because it is what joins a
   submission to a lead — rendered as a narrow browser-default control beside
   full-width neighbours, with no focus ring and, on iOS, at a size that zooms the
   page on focus. Nothing looked broken; it just looked cheap on the first screen a
   stranger sees. A type-attribute selector silently ignores what it does not name,
   so `test/ui-surface.test.ts` now derives the set of input types index.html
   actually uses and fails if any textual one is absent from this rule. */
input[type=text], input[type=tel], input[type=email], input[type=url], textarea {
  width: 100%;
  font-family: inherit;
  font-size: 16px; /* 16px stops iOS zoom-on-focus */
  color: var(--ink);
  background: var(--field);
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 12px 14px;
  transition: border-color .15s, box-shadow .15s;
}
textarea { resize: vertical; min-height: 64px; }
input:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(95, 174, 244, 0.20);
  background: var(--slate-2);
}
::placeholder { color: var(--placeholder); }

/* ── Select (native picker, styled to match inputs) ────────────────── */
select {
  width: 100%;
  font-family: inherit;
  font-size: 16px; /* 16px stops iOS zoom-on-focus */
  color: var(--ink);
  background-color: var(--field);
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 12px 40px 12px 14px;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none'%3E%3Cpath d='M5 7.5l5 5 5-5' stroke='%23a4b3b8' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 18px;
  transition: border-color .15s, box-shadow .15s;
}
select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(95, 174, 244, 0.20);
  background-color: var(--slate-2);
}

/* ── Checkbox chips (tap-to-pick groups) ───────────────────────────── */
fieldset.group { border: 0; margin: 0 0 18px; padding: 0; min-width: 0; }
fieldset.group:last-child { margin-bottom: 0; }
fieldset.group > .q { padding: 0; margin-bottom: 10px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip { position: relative; display: inline-flex; }
.chip input {
  position: absolute; opacity: 0; width: 1px; height: 1px; /* visually hidden, still focusable */
}
.chip span {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: var(--field);
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
  user-select: none;
  transition: border-color .12s, background .12s, color .12s;
}
.chip span::before {
  content: "";
  width: 16px; height: 16px;
  flex: 0 0 auto;
  border: 1.5px solid var(--line-strong);
  border-radius: 5px;
  transition: background-color .12s, border-color .12s;
}
.chip input:checked + span {
  border-color: var(--brand);
  background: var(--brand-wash);
  color: var(--brand-text);
  font-weight: 600;
}
.chip input:checked + span::before {
  border-color: var(--brand);
  background-color: var(--brand);
}
/* The ✓ is a MASK, not a background image (#412). A stroke colour inside an SVG data
   URI cannot read a custom property, so it is a palette literal: it was `white`, 2.05:1
   on the mint fill. Here the SVG gives only the SHAPE (a mask reads alpha; its stroke
   colour is irrelevant) and the ink is the generated on-action role, 9.06:1 — the ink
   the filled button's label wears since #407.
   It overlays the square by POSITION, not arithmetic: absolute with every inset left
   `auto`, a flex container's child sits at its static position — the start of the
   pill's content box, centred across it — which is exactly where the square, the first
   flex item, sits. The box is declared like the square's, so the two snap alike: a
   1.5px border is 1px at 1x density, so a margin constant would miss the centre by
   that pixel (measured locally). ⚠️ This needs a start-justified pill; `.seg` centres its pills, so a ✓ chip
   may not sit in a `.seg` row (test/button-contrast.test.ts derives that from the HTML). */
.chip input:checked + span::after {
  content: "";
  position: absolute;
  width: 16px; height: 16px;
  border: 1.5px solid transparent;
  background-color: var(--azuvyn-midnight-slate-on-action);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 8.5l3 3 6-7' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M3.5 8.5l3 3 6-7' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
}
/* Forced colours repaint a background as Canvas — the colour of the square beneath —
   which would erase a masked ✓, and there the ✓ is the ONLY thing telling a checked
   square from an empty one. The old background-image ✓ survived forcing; this keeps
   the mask one visible in the user's own text colour. */
@media (forced-colors: active) {
  .chip input:checked + span::after { forced-color-adjust: none; background-color: CanvasText; }
}
.chip input:focus-visible + span {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(95, 174, 244, 0.30);
}
.chip span:active { transform: scale(.97); }

/* ── Pick chips (single-select radio groups: palette, style, logo) ──
   Same pill as the checkbox chips, minus the checkbox square — the filled
   pill IS the selected state, and the radio underneath keeps it one-pick. */
.chip.pick span::before,
.chip.pick span::after { content: none; display: none; }

/* Palette swatches — two little color dots inside the pill. Colors live HERE
   (classes, not style attributes) because the CSP allows no inline styles. */
.swatch { display: inline-flex; gap: 3px; flex: 0 0 auto; font-style: normal; }
.sw {
  display: inline-block;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.24);
}
.sw-navy { background: #1e3a8a; }
.sw-orange { background: #f97316; }
.sw-forest { background: #166534; }
.sw-cream { background: #f5f0e1; }
.sw-black { background: #16181d; }
.sw-gold { background: #d4af37; }
.sw-sky { background: #38bdf8; }
.sw-deepblue { background: #1e40af; }
.sw-clay { background: #9a5b33; }
.sw-sand { background: #e7cfa8; }
.sw-green { background: #22c55e; }
.sw-lemon { background: #fde047; }
.sw-stone { background: #d9d4cc; }
.sw-taupe { background: #8d8a83; }

/* ── Tier fit panel (the soft recommendation above the submit card) ── */
.fit-card {
  background:
    radial-gradient(ellipse 70% 120% at 8% 0%, rgba(243, 168, 27, 0.09), transparent 68%),
    linear-gradient(150deg, var(--slate-2) 0%, var(--card) 70%);
  border-color: var(--accent-edge);
}
.fit-line { font-size: 16px; margin: 0 0 8px; }
.fit-line strong { color: var(--accent); }
.fit-price {
  font-size: 15px;
  font-weight: 600;
  color: var(--brand-text);
  margin: 0 0 6px;
}

/* ── Dropzone ──────────────────────────────────────────────────────── */
.drop {
  display: block;
  border: 2px dashed var(--line-strong);
  border-radius: 14px;
  background: var(--field);
  padding: 26px 18px;
  text-align: center;
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .05s;
  margin: 14px 0 4px;
}
.drop:hover, .drop:focus-visible { border-color: var(--brand); background: rgba(43, 92, 230, 0.10); outline: none; }
.drop.drag { border-color: var(--brand); background: var(--brand-wash); }
.drop:active { transform: scale(.997); }
.drop-icon { font-size: 30px; margin-bottom: 6px; }
.drop-text strong { font-size: 16px; }

/* ── File list ─────────────────────────────────────────────────────── */
.files { list-style: none; margin: 12px 0 0; padding: 0; }
.file {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 12px;
  margin-bottom: 8px;
  background: var(--slate-2);
}
.file-thumb {
  width: 40px; height: 40px;
  border-radius: 9px;
  object-fit: cover;
  background: var(--slate-3);
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px;
}
.file-main { flex: 1 1 auto; min-width: 0; }
.file-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-status { font-size: 12px; color: var(--ink-soft); }
.file-status.ok { color: var(--ok); }
.file-status.err { color: var(--err); }
.bar { height: 5px; border-radius: 99px; background: var(--slate-3); overflow: hidden; margin-top: 5px; }
.bar > span { display: block; height: 100%; width: 0; background: var(--brand); transition: width .2s; }
.file-x {
  flex: 0 0 auto;
  border: none; background: none; cursor: pointer;
  color: var(--ink-soft); font-size: 20px; line-height: 1; padding: 4px;
}
.file-x:hover { color: var(--err); }

/* ── Buttons ───────────────────────────────────────────────────────── */
/* The label is the brand's ink FOR the action colour, never white: white on this
   mint measured 2.05:1 (#407, WCAG 2.2 AA needs 4.5:1 at 16–17px bold). Pinned by
   test/button-contrast.test.ts, which resolves the roles and does the arithmetic. */
.btn {
  display: inline-block;
  font-family: var(--font-display); font-size: 16px; font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--azuvyn-midnight-slate-on-action); background: var(--brand);
  border: none; border-radius: 12px;
  padding: 14px 22px;
  cursor: pointer;
  text-decoration: none;
  transition: background .15s, transform .05s, opacity .15s;
}
.btn:hover { background: var(--brand-deep); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .6; cursor: default; }
.btn.big { width: 100%; padding: 16px; font-size: 17px; }

.submit-card { text-align: center; }
.submit-note { margin-top: 10px; }
.error-line { color: var(--err); font-size: 14px; margin: 10px 0 0; }

.foot {
  text-align: center;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  color: var(--ink-soft);
  line-height: 1.6;
}

/* ── Loading / done ────────────────────────────────────────────────── */
.spinner {
  width: 34px; height: 34px;
  border: 3px solid var(--line-strong); border-top-color: var(--brand);
  border-radius: 50%;
  margin: 6px auto 14px;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.check {
  width: 60px; height: 60px;
  margin: 4px auto 14px;
  border-radius: 50%;
  background: var(--ok); color: var(--azuvyn-midnight-slate-on-action);
  font-size: 32px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}

@media (min-width: 560px) {
  .wrap { padding-top: 32px; }
  .h1 { font-size: 36px; }
}

/* ── Receptionist tuning intake additions (receptionist.html) ─────────── */

/* Checkbox / radio inside a .check or .chip already styled above; add a small helper line. */
.hint { display: block; font-size: 13px; color: var(--ink-soft); margin-top: 6px; }

/* Numbered steps inside a question (the Manager-access invite path, 2026-10-17): the owner's
   phone is the device, so each step is one line with room to tap nothing. */
.field .steps { margin: 6px 0 10px; padding-left: 22px; font-size: 15px; line-height: 1.5; }
.field .steps li + li { margin-top: 4px; }

/* Secondary button (add-a-row) — outlined, not the loud primary. */
.btn.secondary {
  color: var(--brand-text);
  background: var(--brand-wash);
  border: 1.5px solid var(--brand-edge);
}
.btn.secondary:hover { background: rgba(43, 92, 230, 0.28); }
.add-row { font-size: 15px; padding: 10px 16px; margin-top: 4px; }

/* The plain checkbox rows (compliance ack + hours "Closed").
   ⚠️ NAMED `.checkrow`, NOT `.check`, and the rename is the fix for a real defect.
   `.check` is ALREADY TAKEN above by the done panel's 60px green tick circle
   (`<div class="check">✓</div>` in index.html). These rules came across with the
   original port in `92a382ef7` under that same name, so any checkbox row inherited
   the circle's `width:60px; height:60px; border-radius:50%; background:var(--ok)`
   — none of which a `display:flex` override resets — and rendered as a green disc
   with its label clipped inside it. MEASURED 2026-09-17 by rendering the ported
   receptionist form locally; the screenshot is what found it.
   It had never fired, because for eleven months neither consumer was reachable:
   this page did not exist, and the website form's only `.check` row is built by
   `loadAssetAck()` in intake.js, which returns early while INTAKE_ASSET_ACK_LIVE is
   dark AND `_lib/asset-ack.ts` has null copy. Arming that control would have been
   the first render — a compliance acknowledgement, illegible.
   Two different things must not share a class name. Keep them apart. */
.checkrow { display: flex; align-items: flex-start; gap: 10px; font-size: 15px; cursor: pointer; line-height: 1.45; }
.checkrow input[type=checkbox] { width: 20px; height: 20px; margin: 1px 0 0; flex: 0 0 auto; accent-color: var(--brand); }

/* Weekly hours grid — one row per day, stacks tight on mobile. */
.hours-grid { display: flex; flex-direction: column; gap: 10px; }
.hours-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--field);
}
.hours-day { font-weight: 600; font-size: 15px; flex: 0 0 90px; }
.hours-times { display: inline-flex; align-items: center; gap: 8px; flex: 1 1 auto; }
.hours-times input[type=time] {
  font-family: inherit; font-size: 16px; color: var(--ink);
  background: var(--field); border: 1.5px solid var(--line); border-radius: 10px; padding: 8px 10px; min-width: 0; flex: 1 1 90px;
}
.hours-times input[type=time]:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(95, 174, 244, 0.20); }
.hours-dash { color: var(--ink-soft); font-size: 14px; flex: 0 0 auto; }
.hours-closed { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: var(--ink-soft); cursor: pointer; flex: 0 0 auto; }
.hours-closed input { width: 18px; height: 18px; accent-color: var(--brand); }
.hours-row.is-closed .hours-times { opacity: .4; pointer-events: none; }

/* Repeatable service / FAQ rows. */
.rows { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.row-item {
  display: grid; gap: 8px; align-items: start;
  grid-template-columns: 1fr auto;
  padding: 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--field);
}
.row-item input, .row-item textarea { margin: 0; }
.row-item .row-x {
  grid-column: 2; grid-row: 1;
  border: none; background: none; cursor: pointer;
  color: var(--ink-soft); font-size: 22px; line-height: 1; padding: 2px 6px; align-self: start;
}
.row-item .row-x:hover { color: var(--err); }
/* Service row: name / desc / price stack on mobile, the × sits top-right. */
.row-item .s-name, .row-item .s-desc, .row-item .s-price, .row-item .q-q, .row-item .q-a { grid-column: 1; }

@media (min-width: 560px) {
  .row-item:not(.faq) {
    grid-template-columns: 1fr 1fr 120px auto;
  }
  .row-item .s-name { grid-column: 1; grid-row: 1; }
  .row-item .s-desc { grid-column: 2; grid-row: 1; }
  .row-item .s-price { grid-column: 3; grid-row: 1; }
  .row-item:not(.faq) .row-x { grid-column: 4; grid-row: 1; }
}

/* Turnstile — the bot challenge on the public entry. Reserves its own row so the
   Start button never jumps when the widget finishes loading. */
.turnstile { margin: 18px 0 6px; min-height: 65px; }
.turnstile:empty { min-height: 0; margin: 0; }

/* ══ Stepped flow ══════════════════════════════════════════════════════
   The form is one <form> with four <section class="step">; navigating only
   toggles which one is displayed. Every control stays in the DOM the whole
   time, which is what makes "preserve every answer when navigating" a
   property of the markup rather than something the script has to remember. */

.progress { margin: 0 0 16px; }
.progress-top {
  display: flex; flex-wrap: wrap; gap: 8px 14px;
  align-items: baseline; justify-content: space-between;
}
.progress-label {
  font-size: 14px; font-weight: 600; color: var(--brand-text);
  margin: 0 0 8px;
}

/* ══ Short version / full version ══════════════════════════════════════
   ONE RULE DOES THE WHOLE THING. `#form` ships with `quick`, and every
   question the short version does not ask carries `data-detail`; folding
   them away is this selector and nothing else. It out-specifies the
   `.field` / `fieldset.group` display rules (1,1,0 beats 0,1,0) and loses
   to `.hidden`, which is `!important` — so a conditional block that is
   hidden for its OWN reason stays hidden in either version.

   Nothing is detached or cleared. A value typed in the full version and
   then folded away is still collected, still saved and still sent, which
   is the same promise conditional disclosure already makes. */
#form.quick [data-detail] { display: none; }

/* ══ #839 · The basics-only form ═══════════════════════════════════════
   "Help me figure it out" with the specialist dark: the short version, less
   every DESIGN DECISION (`data-decide`) — the visitor asked us to decide the
   look, the pages and the features. `basics` always rides with `quick`, so the
   detail rule above still applies; this rule only takes more away, and the one
   line that says why shows in its place. Same promise as above: nothing is
   detached, and a value set before the fold is still collected and sent. */
#form.basics [data-decide] { display: none; }
[data-basics-only] { display: none; }
#form.basics [data-basics-only] { display: block; }
.basics-note {
  margin: 18px 0 4px; padding: 12px 14px;
  border-left: 3px solid var(--accent-edge);
  background: var(--accent-wash);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 15px; line-height: 1.5;
}

.mode-toggle {
  flex: 0 0 auto;
  font-family: inherit; font-size: 13px; font-weight: 600;
  color: var(--brand-text);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 13px;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.mode-toggle:hover { background: var(--slate-3); border-color: var(--accent-edge); }
.mode-toggle:focus-visible { outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 2px; }
.mode-note { margin: 8px 0 0; }
.progress-bar {
  height: 6px; border-radius: 99px; background: var(--slate-3); overflow: hidden;
}
.progress-bar > span {
  display: block; height: 100%; width: 25%;
  background: var(--brand);
  border-radius: 99px;
  transition: width .25s ease;
}
.progress-save { margin: 8px 0 0; min-height: 18px; }
.progress-save.saving { color: var(--ink-soft); }
.progress-save.saved { color: var(--ok); }
.progress-save.local { color: var(--accent); }

.step-h { scroll-margin-top: 12px; }
.step-h:focus-visible { outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 4px; border-radius: 6px; }
.step-sub { margin: -8px 0 18px; }
.h3 { font-family: var(--font-display); font-weight: 700; font-size: 16px; color: var(--ink); margin: 24px 0 10px; }

/* "Needed to start" vs "Helpful if you know it" — the distinction that makes a
   long form feel short. A band applies to the fields BELOW it until the next one. */
.band {
  display: inline-block;
  font-size: 12px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  border-radius: 999px; padding: 5px 11px;
  margin: 4px 0 14px;
}
.band-need { color: var(--accent); background: var(--accent-wash); border: 1px solid var(--accent-edge); }
.band-help { color: var(--ink-soft); background: var(--slate-3); border: 1px solid var(--line); }
.step .band:not(:first-of-type) { margin-top: 22px; }

/* A field the essential set names, once it has been flagged as unanswered. */
.field.invalid input,
.field.invalid textarea,
.field.invalid select { border-color: var(--err); background: var(--err-wash); }
.field.invalid .chip span { border-color: var(--err); }
.field-error { display: block; color: var(--err); font-size: 14px; margin-top: 7px; }

/* The AI-concierge block: a card inside a card, so it reads as an extra branch
   rather than as more of the same form. */
.subcard {
  border: 1.5px solid var(--brand-edge);
  background: var(--slate-2);
  border-radius: 14px;
  padding: 18px 16px;
  margin: 6px 0 18px;
}
.subcard .h3 { margin-top: 0; }

/* The acknowledgment slot. Empty and hidden unless /api/config arms it. */
.ack {
  border: 1px solid var(--line); border-radius: 12px;
  background: var(--slate-2); padding: 14px; margin: 14px 0 0;
}

/* ── Review list ──────────────────────────────────────────────────── */
.review { margin: 10px 0 0; padding: 0; }
.review-row {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.review-row:last-child { border-bottom: 0; }
.review dt { flex: 1 1 160px; font-size: 13px; color: var(--ink-soft); margin: 0; }
.review dd { flex: 2 1 220px; margin: 0; font-size: 15px; white-space: pre-wrap; overflow-wrap: anywhere; }
.review dd.blank { color: var(--ink-soft); font-style: italic; }
.review-edit {
  flex: 0 0 auto;
  border: none; background: none; padding: 4px 6px;
  color: var(--brand); font: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer; border-radius: 8px;
}
.review-edit:hover { background: var(--brand-wash); }
.review-edit:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.review-empty { color: var(--ink-soft); font-style: italic; }

/* ── Step navigation ──────────────────────────────────────────────── */
.stepnav {
  display: flex; gap: 10px; align-items: stretch;
  margin: 4px 0 10px;
}
.stepnav .btn { flex: 1 1 auto; }
.stepnav .btn.secondary { flex: 0 0 auto; }
.stepnav .btn.big { padding: 16px; }
@media (max-width: 400px) {
  .stepnav .btn.secondary { padding: 16px 14px; }
}

/* ── File rows: retry, and the "still uploading" state ────────────── */
.file-retry {
  flex: 0 0 auto;
  border: 1.5px solid var(--brand-edge); background: var(--brand-wash);
  color: var(--brand-text); font: inherit; font-size: 13px; font-weight: 600;
  border-radius: 9px; padding: 6px 10px; cursor: pointer;
}
.file-retry:hover { background: rgba(43, 92, 230, 0.28); }
.file.busy { border-color: var(--line-strong); }

/* ══ Segmented chip rows (the former <select>s) ══════════════════════════
   `.seg` is a LAYOUT variant of `.chips`, not a new control: the pill, the
   checked state and the focus ring above are unchanged, which is what keeps
   one visual language across the page. It only stops long option labels
   ("Someone else (an old web person, an agency)") from being squeezed into
   an unreadable column on a narrow phone. */
.seg { gap: 8px; }
.seg .chip { flex: 1 1 auto; }
.seg .chip span { width: 100%; justify-content: center; text-align: center; }
@media (min-width: 520px) {
  .seg .chip { flex: 0 1 auto; }
  .seg .chip span { width: auto; }
}

/* ══ The dials ══════════════════════════════════════════════════════════
   A native `input[type=range]` on purpose: it is draggable, it is arrow-key
   operable and it announces itself with no scripting at all, which a rotary
   dial or a div-with-listeners would each have to reinvent badly. WCAG 2.2 AA
   is a compliance-critical preserve (COMPLIANCE.md) and this is the cheapest
   way to keep it — the script's only accessibility job is aria-valuetext, so
   a screen reader hears "Leaning modern" rather than "3".

   ⚠️ THE UNSET STATE IS THE POINT, so it has to LOOK unset. Without
   `data-set` the track is flat, the thumb is hollow and muted, and the
   readout carries the "you decide" line — a dial that looked identical
   whether or not it had been touched would be a form inventing opinions. */
.dial .dial-row {
  display: flex; align-items: center; gap: 12px;
}
.dial input[type=range] {
  -webkit-appearance: none; appearance: none;
  flex: 1 1 auto; min-width: 0;
  height: 34px;                       /* ≥24px target — WCAG 2.5.8 */
  margin: 0; padding: 0;
  background: transparent;
  cursor: pointer;
}
.dial input[type=range]::-webkit-slider-runnable-track {
  height: 8px; border-radius: 99px;
  background: var(--slate-3);
  border: 1px solid var(--line);
}
.dial input[type=range]::-moz-range-track {
  height: 8px; border-radius: 99px;
  background: var(--slate-3);
  border: 1px solid var(--line);
}
.dial input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; margin-top: -10px;
  border-radius: 50%;
  background: var(--field);
  border: 2px solid var(--line-strong);
  box-shadow: 0 1px 3px rgba(9, 17, 33, 0.28);
  transition: background .12s, border-color .12s;
}
.dial input[type=range]::-moz-range-thumb {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--field);
  border: 2px solid var(--line-strong);
  box-shadow: 0 1px 3px rgba(9, 17, 33, 0.28);
}
/* Touched. The thumb fills with the brand, so "I have an opinion here" is
   visible from across the step rather than only in the readout. */
.dial input[type=range][data-set]::-webkit-slider-thumb {
  background: var(--brand); border-color: var(--brand);
}
.dial input[type=range][data-set]::-moz-range-thumb {
  background: var(--brand); border-color: var(--brand);
}
.dial input[type=range]:focus-visible {
  outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 4px;
  border-radius: 99px;
}
.dial-clear {
  flex: 0 0 auto;
  font-family: inherit; font-size: 13px; font-weight: 600;
  color: var(--ink-soft);
  background: transparent;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  padding: 8px 12px;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.dial-clear:hover { background: var(--slate-3); }
.dial-clear:focus-visible { outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 2px; }
/* Pressed = "we're choosing this one", i.e. the dial is untouched. It is the
   DEFAULT, which is what makes leaving every dial alone a stated answer
   instead of a gap. */
.dial-clear[aria-pressed=true] {
  color: var(--brand-text); background: var(--brand-wash); border-color: var(--brand-edge);
}
.dial-out {
  margin: 9px 0 0;
  font-size: 15px; font-weight: 600; color: var(--brand-text);
}
.dial-out.unset { font-weight: 500; color: var(--ink-soft); font-style: italic; }
.dial-ends {
  display: flex; justify-content: space-between;
  margin-top: 2px;
  font-size: 12px; color: var(--ink-soft);
}

/* ══ Audience: prospect vs paid client ══════════════════════════════════
   ONE class on the form, set from the token's signed `gen` claim. There is no
   second page and no second required set — this is the entire presentational
   difference, and it is deliberately small enough to read in one screen.

   Specificity note, the same arithmetic the `quick` rule above depends on:
   `#form.quick.client [data-client-ask]` is (1,3,0) and beats
   `#form.quick [data-detail]` at (1,2,0), so a promoted question wins; both
   still lose to `.hidden`, which is `!important`, so a conditional block
   hidden for its own reason stays hidden for every audience. */
#form.quick.client [data-client-ask] { display: block; }
/* THE SETUP INTAKE (2026-09-28): a link that names the services bought. A `data-svc`
   group none of whose services were bought is not PUT to the visitor at all — same
   `!important` weight as `.hidden`, so neither the version nor a conditional can bring
   it back; intake.js is the only writer of the class. A `data-svc-show` group whose
   service WAS bought is promoted into the short version exactly as `data-client-ask`
   is for a paid client — (1,3,0) beats `#form.quick [data-detail]`, loses to `.hidden`. */
.svc-hidden { display: none !important; }
#form.quick [data-detail].svc-show { display: block; }
#form.client [data-prospect] { display: none !important; }

/* ══ #804 · The opening choice and the design specialist (DARK) ════════════
   Rendered only while /api/config reports the interview armed; with the flag off
   none of these elements is ever shown. Every class is `iv-` (the chat) or `cv-`
   / `pal-` (a direction card) so nothing here can collide with a form class
   (test/class-collisions.test.ts). */
.iv-choose { display: grid; gap: 12px; margin: 18px 0 14px; }
.iv-choice {
  display: flex; flex-direction: column; gap: 6px;
  text-align: left; font: inherit; color: var(--ink);
  background: var(--field);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.iv-choice strong { font-family: var(--font-display); font-size: 17px; }
.iv-choice:hover { border-color: var(--brand-edge); background: var(--brand-wash); }
.iv-choice:focus-visible { outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 2px; }
@media (min-width: 560px) {
  .iv-choose { grid-template-columns: 1fr 1fr; }
}

.iv-log { list-style: none; margin: 16px 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.iv-msg { max-width: 88%; border-radius: 14px; padding: 10px 14px; line-height: 1.5; }
.iv-msg p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.iv-them { align-self: flex-start; background: var(--slate-2); border: 1px solid var(--line); }
.iv-you { align-self: flex-end; background: var(--brand-wash); border: 1px solid var(--brand-edge); }
/* The specialist's name and initial, on the first bubble of each of her runs. */
.iv-who { display: flex; align-items: center; gap: 6px; margin: 0 0 4px; font-size: 12px; font-weight: 700; color: var(--accent); }
.iv-avatar {
  display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  background: var(--accent-wash); border: 1px solid var(--accent-edge); font-size: 11px;
}
/* A live caption still typing out (#805): a steady cursor after the text. No
   blinking, no fading, so it neither distracts nor drops the bubble's contrast. */
.iv-typing p::after {
  content: ""; display: inline-block; width: 2px; height: 1em; margin-left: 2px;
  vertical-align: -0.15em; background: var(--accent);
}
.iv-notes { margin: 8px 0 0; padding-left: 18px; }
.iv-compose .field { margin-bottom: 8px; }
.iv-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.iv-out { margin: 14px 0 0; }

/* ── The direction cards ──────────────────────────────────────────────
   Each card is a BUTTON whose face is a tiny hero, coloured by a `pal-` class
   and laid out by a `cv-` class. Four custom properties per palette, and
   test/interview-cards.test.ts holds each ink to WCAG AA on its ground. */
.iv-cards { margin: 6px 0 16px; }
.iv-card-row { display: grid; gap: 12px; margin: 12px 0; }
@media (min-width: 720px) {
  .iv-card-row { grid-template-columns: repeat(3, 1fr); }
}
.iv-card {
  display: flex; flex-direction: column; gap: 8px;
  text-align: left; font: inherit; color: var(--ink);
  background: transparent; border: 1.5px solid var(--line);
  border-radius: var(--radius); padding: 8px;
  cursor: pointer;
}
.iv-card:hover { border-color: var(--brand-edge); }
.iv-card:focus-visible { outline: 3px solid rgba(95, 174, 244, 0.55); outline-offset: 2px; }
.cv-face {
  display: flex; flex-direction: column; gap: 8px;
  min-height: 190px; padding: 16px;
  border-radius: 10px;
  background: var(--cv-bg); color: var(--cv-ink);
  overflow: hidden;
}
/* The name sits beside the phone only while both fit; on a narrow card the
   phone wraps UNDER it. `break-word`, never `anywhere`: `anywhere` shrinks the
   name's min-content to one letter, so the nowrap phone squeezed it to a
   sliver and it broke letter by letter (#814). `max-width` keeps a single
   over-long word inside the centred `cv-classic` column. */
.cv-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 4px 8px; font-size: 12px; }
.cv-mark { flex: 1 1 auto; min-width: 0; max-width: 100%; font-weight: 700; font-size: 14px; overflow-wrap: break-word; }
.cv-logo { max-height: 28px; max-width: 110px; object-fit: contain; }
.cv-phone { white-space: nowrap; }
.cv-head { max-width: 100%; font-size: 19px; font-weight: 700; line-height: 1.2; overflow-wrap: break-word; }
.cv-place { font-size: 13px; }
.cv-cta {
  align-self: flex-start; margin-top: auto;
  background: var(--cv-accent); color: var(--cv-on-accent);
  font-size: 13px; font-weight: 700;
  padding: 8px 14px; border-radius: 8px;
}
.cv-label { font-size: 13px; color: var(--ink-soft); padding: 0 4px 2px; }

/* Five layouts, one per style. */
.cv-split .cv-face { border-left: 10px solid var(--cv-accent); }
.cv-split .cv-head { font-weight: 600; letter-spacing: -0.01em; }
.cv-classic .cv-face { align-items: center; text-align: center; font-family: Georgia, "Times New Roman", serif; }
.cv-classic .cv-top { justify-content: center; flex-direction: column; }
.cv-classic .cv-cta { align-self: center; border-radius: 2px; }
.cv-banner .cv-face { background: var(--cv-accent); color: var(--cv-on-accent); }
.cv-banner .cv-head { font-size: 23px; text-transform: uppercase; letter-spacing: 0.01em; }
.cv-banner .cv-cta { background: var(--cv-bg); color: var(--cv-ink); }
.cv-soft .cv-face { border-radius: 22px; }
.cv-soft .cv-cta { border-radius: 999px; }
.cv-lux .cv-face { justify-content: flex-end; }
.cv-lux .cv-head { font-weight: 400; font-size: 21px; letter-spacing: 0.04em; }
.cv-lux .cv-cta { background: transparent; color: var(--cv-ink); border: 1px solid var(--cv-accent); border-radius: 0; }

/* Palettes — each one of the form's own `palette` chips. */
.pal-navy { --cv-bg: #1e3a8a; --cv-ink: #ffffff; --cv-accent: #f97316; --cv-on-accent: #111827; }
.pal-forest { --cv-bg: #f5f0e1; --cv-ink: #1f3d2b; --cv-accent: #166534; --cv-on-accent: #ffffff; }
.pal-gold { --cv-bg: #16181d; --cv-ink: #f5f0e1; --cv-accent: #d4af37; --cv-on-accent: #16181d; }
.pal-ocean { --cv-bg: #0c3b66; --cv-ink: #ffffff; --cv-accent: #38bdf8; --cv-on-accent: #0b1b2b; }
.pal-earth { --cv-bg: #e7cfa8; --cv-ink: #3b2414; --cv-accent: #7a4524; --cv-on-accent: #ffffff; }
.pal-bright { --cv-bg: #f7fee7; --cv-ink: #14532d; --cv-accent: #22c55e; --cv-on-accent: #052e16; }
.pal-neutrals { --cv-bg: #f4f1ec; --cv-ink: #2b2a28; --cv-accent: #5f5b54; --cv-on-accent: #ffffff; }
.pal-plain { --cv-bg: #f5f5f4; --cv-ink: #1c1917; --cv-accent: #334155; --cv-on-accent: #ffffff; }

/* ── #805 · The design specialist, spoken (DARK) ──────────────────────────
   The voice option and its call screen. Shown only when /api/config reports
   `voiceInterview: true`; everything else reuses the #804 `iv-` rules above. */
@media (min-width: 760px) {
  .iv-choose.iv-three { grid-template-columns: 1fr 1fr 1fr; }
}
.vc-status { display: flex; align-items: center; gap: 8px; min-height: 1.5em; }
.vc-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--line);
  flex: none;
}
.vc-dot.vc-speaking { background: var(--brand-edge); box-shadow: 0 0 0 4px var(--brand-wash); }
@media (prefers-reduced-motion: no-preference) {
  .vc-dot.vc-speaking { transition: box-shadow .2s; }
}

/* ── The photo album walkthrough (#894 I1) ─────────────────────────────
   Drawn by public/album.js only while INTAKE_PHOTO_ALBUM_LIVE is armed; these
   rules match nothing on an inert page. */
.album-how {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--slate-2);
  padding: 4px 14px;
  margin: 12px 0 0;
}
/* `list-item`, not flex: flex drops the disclosure triangle that says it opens.
   The padding keeps the target at least 44px tall (WCAG 2.5.8). */
.album-how summary {
  cursor: pointer;
  font-weight: 600;
  display: list-item;
  padding: 12px 0;
}
.album-how-h { font-weight: 600; margin: 8px 0 2px; }
.album-how ol { margin: 0 0 6px; padding-left: 22px; }
.album-how li { margin: 2px 0; }
