/* ============================================================
   Doorbelle — Component Vocabulary ("Lacquer v1")
   Owned by: frontend-brand-guardian
   Source of truth: docs/design-system.md §5–§6, docs/mockup.html

   A vocabulary, not a zoo (§6): three button voices, one card, one pill
   base, one form-field recipe, one swatch, one avatar, one star rating,
   one badge, one eyebrow. Every screen composes these — nothing gets
   restyled locally. Flat class selectors, spacing owned by parent
   containers, everything references tokens.css.

   Two shadow TOKENS exist system-wide (--shadow-card / --shadow-pop, both
   elevation shadows). The inset "wet lacquer" gloss/inner-shadow technique
   used on swatch dots, the avatar, and the wordmark dot is a distinct,
   single, reused MATERIAL recipe (design §5.1/§5.4) — not a competing
   elevation shadow, so it isn't tokenized as a third --shadow-*; it's
   written once per component below and never varied.
   ============================================================ */

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

/* The hidden attribute must win over component display rules
   (e.g. .field's flex) — templates rely on it for conditional fields. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  font-family: var(--font-ui);
  background: var(--porcelain);
  color: var(--ink);
  font-size: var(--fs-14-5);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

button { font-family: inherit; cursor: pointer; }
a { color: inherit; }
img { max-width: 100%; display: block; }
fieldset { border: none; margin: 0; padding: 0; min-width: 0; }

/* Visible keyboard focus everywhere (design §10) — 3px lacquer, 2px offset. */
:focus-visible {
  outline: 3px solid var(--lacquer);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Screen-reader-only helper — used by the aria-live region in base.html. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* prefers-reduced-motion (design §7/§10): kills all CSS animation/transition.
   chime.js still plays audio regardless — sound is information, not
   decoration; bell.js's swing is purely this CSS keyframe, so it is
   silenced here automatically. */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- Layout ---------- */
.wrap {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* ---------- Eyebrow — the ONLY uppercase in the product ---------- */
.eyebrow {
  display: inline-block;
  font-family: var(--font-ui);
  font-size: var(--fs-11);
  font-weight: var(--fw-bold);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--plum);
}

/* ---------- Gloss highlight — swatch dots + primary buttons ONLY ---------- */
.gloss { position: relative; overflow: hidden; }
.gloss::after {
  content: "";
  position: absolute; left: 12%; top: 8%; width: 38%; height: 26%;
  background: radial-gradient(ellipse at center, rgba(255, 255, 255, .85), rgba(255, 255, 255, 0) 70%);
  border-radius: 50%;
  pointer-events: none;
}

/* ---------- Type ---------- */
.display-headline {
  font-family: var(--font-display);
  font-weight: var(--fw-regular);
  font-size: var(--fs-display);
  line-height: var(--lh-display-tight);
  letter-spacing: -.015em;
  color: var(--ink);
}
.display-headline em {
  font-style: italic;
  font-weight: var(--fw-medium);
  color: var(--lacquer);
}
.tabular-nums { font-variant-numeric: tabular-nums; }

/* ---------- Buttons — three voices, nothing else ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  font-family: var(--font-ui);
  font-weight: var(--fw-bold);
  font-size: var(--fs-13-5);
  line-height: 1;
  border: none;
  cursor: pointer;
  border-radius: var(--r-pill);
  min-height: var(--space-44);
  padding: 0 22px;
  text-decoration: none;
  transition: transform var(--dur-hover) var(--ease-standard),
              background var(--dur-hover) var(--ease-standard),
              color var(--dur-hover) var(--ease-standard);
}
.btn:disabled { cursor: not-allowed; opacity: .55; }

.btn-primary { background: var(--lacquer); color: var(--surface); box-shadow: var(--shadow-pop); }
.btn-primary:hover:not(:disabled) { background: var(--lacquer-deep); transform: translateY(-1px); }
.btn-primary:active:not(:disabled) { transform: translateY(0); }

.btn-quiet {
  background: none; box-shadow: none;
  color: var(--ink-soft); font-weight: var(--fw-semibold);
  padding: 0; min-height: auto;
}
.btn-quiet:hover { color: var(--ink); }
.btn-quiet.is-destructive { text-decoration: underline; }

.chip-dark {
  display: inline-flex; align-items: center; gap: var(--space-8);
  background: var(--ink); color: var(--surface);
  font-weight: var(--fw-semibold); font-size: var(--fs-13-5);
  border-radius: var(--r-pill); padding: 7px 15px; border: none;
}

/* Live/pulsing status dot — used inside chip-dark, mode cards, etc. */
.pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--lacquer); display: inline-block; animation: pulse 1.6s infinite; }
.pulse--on-lacquer { background: var(--surface); } /* white dot on a lacquer-filled surface */
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ---------- Cards ---------- */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  border: 1px solid transparent; /* no border at rest */
  padding: var(--space-20);
  transition: transform var(--dur-hover) var(--ease-standard),
              border-color var(--dur-hover) var(--ease-standard);
}
.card:hover, .card.is-selected { border-color: var(--lacquer); transform: translateY(-2px); }

/* ---------- Pills / chips — radius 999 everywhere a pill appears ---------- */
.pill {
  display: inline-flex; align-items: center; gap: var(--space-8);
  border-radius: var(--r-pill);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-13-5);
  padding: 9px 16px;
}
.pill--surface { background: var(--surface); border: 1px solid var(--blush-line); box-shadow: var(--shadow-card); color: var(--ink); }
.pill--dark { background: var(--ink); color: var(--surface); }
.pill--status { background: var(--blush); color: var(--lacquer-deep); font-size: var(--fs-12-5); padding: 4px 10px; }
/* Pending semantic (design §3: "pending = --plum") — same shape as
   pill--status, plum instead of lacquer-deep so it never reads as an error. */
.pill--pending { background: var(--blush); color: var(--plum); font-size: var(--fs-12-5); padding: 4px 10px; margin-bottom: var(--space-12); }

.addr-pill svg { color: var(--lacquer); flex: none; }

/* ---------- Forms — white fields, blush-line borders, labels above ---------- */
.field { display: flex; flex-direction: column; gap: var(--space-8); }
.field label,
.field legend { font-size: var(--fs-13-5); font-weight: var(--fw-semibold); color: var(--ink); }
.field legend { padding: 0; } /* legend only — the UA stylesheet's default inline legend padding,
                                  not .role-option (also a `.field label`), which owns its own padding */
.field input,
.field select,
.field textarea {
  font-family: var(--font-ui);
  font-size: var(--fs-14-5);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--blush-line);
  border-radius: var(--r-md);
  padding: 12px 14px;
  min-height: var(--space-44);
  width: 100%;
  transition: border-color var(--dur-hover-fast) var(--ease-standard);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--ink-soft); } /* placeholders are hints, never labels */
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { border-color: var(--lacquer); }
/* .field input's width:100%/min-height:44px/padding/border (0,1,1) out-
   specifies .sr-only (0,1,0) on any visually-hidden input nested in a
   .field (e.g. .role-option's radio) — with no positioned ancestor that
   blows the absolutely-positioned .sr-only box out to ~100vw and causes
   horizontal overflow. Re-assert the 1px sr-only box at (0,2,0). */
.field .sr-only { width: 1px; min-height: 0; padding: 0; border: 0; }
.field .hint { font-size: var(--fs-12-5); color: var(--ink-soft); }
.field .error-text { font-size: var(--fs-12-5); color: var(--lacquer-deep); } /* error = lacquer-deep on blush */

/* File inputs (vendor KYC, design §6 "on-brand file inputs, real inputs,
   keyboard accessible") — the element itself keeps the standard .field
   input treatment above; only the native "choose file" trigger is themed,
   via the ::file-selector-button pseudo (widely supported) with the older
   -webkit- pseudo as a fallback. Themed as a quiet ink/plum pill, not
   lacquer — the form's one submit button stays the single lacquer moment
   per viewport. The browser's own selected-filename text stays native. */
.field input[type="file"] { padding: 6px 14px 6px 6px; cursor: pointer; }
.field input[type="file"]::file-selector-button,
.field input[type="file"]::-webkit-file-upload-button {
  font-family: var(--font-ui);
  font-weight: var(--fw-bold);
  font-size: var(--fs-13-5);
  color: var(--surface);
  background: var(--plum);
  border: none;
  border-radius: var(--r-pill);
  padding: 9px 16px;
  margin-right: var(--space-12);
  cursor: pointer;
  transition: background var(--dur-hover-fast) var(--ease-standard);
}
.field input[type="file"]:hover::file-selector-button,
.field input[type="file"]:hover::-webkit-file-upload-button { background: var(--ink); }

/* Vertical rhythm between stacked fields inside a form — owned by the form
   (parent), not by .field itself (design §11 spacing discipline). */
.form-stack { display: flex; flex-direction: column; gap: var(--space-16); text-align: left; }

/* Error banner — the §6/§3 error treatment: --lacquer-deep text on --blush,
   as a real surface (not just colored text) so it reads at a glance. */
.form-error {
  background: var(--blush);
  color: var(--lacquer-deep);
  border-radius: var(--r-md);
  padding: var(--space-12) var(--space-16);
  margin-bottom: var(--space-16);
  font-size: var(--fs-13-5);
  font-weight: var(--fw-semibold);
  text-align: left;
}

/* Role toggle (auth/signup.html) — elegant segmented choice, real
   <input type="radio"> underneath (visually hidden via .sr-only, not
   display:none, so it stays keyboard-focusable); the label wraps the
   input so a click/tap anywhere on the card toggles it natively. */
.role-options { display: flex; gap: var(--space-12); }
.role-option {
  flex: 1;
  display: flex; flex-direction: column; gap: 2px;
  background: var(--surface);
  border: 1px solid var(--blush-line);
  border-radius: var(--r-md);
  padding: var(--space-12) var(--space-16);
  cursor: pointer;
  transition: border-color var(--dur-hover-fast) var(--ease-standard),
              background var(--dur-hover-fast) var(--ease-standard);
}
.role-option:hover { border-color: var(--lacquer); }
.role-option:has(:checked) { border-color: var(--lacquer); background: var(--blush); }
.role-option:has(:focus-visible) { outline: 3px solid var(--lacquer); outline-offset: 2px; }
.role-option input[type="radio"]:focus-visible { outline: none; } /* ring lives on the card via :has() above */
.role-option-title { font-weight: var(--fw-bold); font-size: var(--fs-14-5); color: var(--ink); }
.role-option:has(:checked) .role-option-title { color: var(--lacquer-deep); }
.role-option-sub { font-size: var(--fs-12-5); color: var(--ink-soft); }
@media (max-width: 380px) {
  .role-options { flex-direction: column; } /* two cramped columns → one comfortable stack at 360px */
}

/* ---------- Polish swatch — the #1 identity carrier (design §5.1) ---------- */
/* Color comes from data via the --swatch-color custom property, set inline
   per instance (e.g. style="--swatch-color: {{ service.swatch_color }}").
   Implemented once here; never restyled locally. */
.swatch-dot {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--swatch-color, var(--nude));
  box-shadow: inset -2px -3px 5px rgba(0, 0, 0, .18), inset 2px 2px 4px rgba(255, 255, 255, .35);
  flex: none;
}
.swatch-dot--sm { width: 20px; height: 20px; }
/* Sheer/dashed variant (e.g. gel removal) — no fill gloss, dashed outline. */
.swatch-dot--sheer {
  background: var(--sheer);
  border: 1px dashed #D9BFB0; /* mockup-exact dashed tone for the sheer variant only */
  box-shadow: none;
}

.swatch {
  display: inline-flex; align-items: center; gap: var(--space-8);
  background: var(--surface);
  border: 1px solid var(--blush-line);
  border-radius: var(--r-pill);
  padding: 8px 16px 8px 9px;
  font-size: var(--fs-13-5);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  transition: border-color var(--dur-hover-fast) var(--ease-standard),
              transform var(--dur-hover-fast) var(--ease-standard);
}
.swatch:hover { border-color: var(--lacquer); transform: translateY(-1px); }
.swatch.is-active { border-color: var(--lacquer); background: var(--blush); }

/* ---------- Avatar — photo, or polish-colored circle + Fraunces initial ---------- */
/* NEVER a gray silhouette. Color via --avatar-color, same data pattern as swatches. */
.avatar {
  width: 56px; height: 56px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  overflow: hidden;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-20);
  color: var(--surface);
  background: var(--avatar-color, var(--plum));
  box-shadow: inset -3px -4px 8px rgba(0, 0, 0, .2);
  flex: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 44px; height: 44px; font-size: var(--fs-16); }

/* ---------- Star rating — always with count: "4.9 (212)" ---------- */
.stars { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-13-5); color: var(--ink-soft); }
.stars .star-icon { color: var(--lacquer); }
.stars b { color: var(--ink); font-weight: var(--fw-bold); }

/* ---------- Verified badge — lacquer check, success has no green ---------- */
.badge-verified {
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--lacquer);
  color: var(--surface);
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
}

/* ---------- Wordmark ---------- */
.wordmark {
  display: inline-flex; align-items: baseline; gap: 2px;
  font-family: var(--font-display);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-28);
  letter-spacing: -.01em;
  color: var(--ink);
  text-decoration: none;
}
.wordmark .dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--lacquer);
  display: inline-block;
  margin-left: 3px;
  box-shadow: inset -1px -1px 2px rgba(0, 0, 0, .15);
}

/* ---------- Bell + accept-moment motion (design §5.2/§7) ---------- */
.bell-glyph { display: inline-block; transform-origin: 50% 8%; color: var(--lacquer); }
@keyframes bellswing {
  0% { transform: rotate(0); }
  15% { transform: rotate(18deg); }
  30% { transform: rotate(-14deg); }
  45% { transform: rotate(9deg); }
  60% { transform: rotate(-5deg); }
  75% { transform: rotate(2deg); }
  100% { transform: rotate(0); }
}
.ringing .bell-glyph { animation: bellswing 1.1s ease-in-out both; }

/* ---------- Countdown ring (bell-stage) — design §5.2, countdown.js drives it ---------- */
.bell-stage { position: relative; width: 168px; height: 168px; margin: 0 auto var(--space-24); }
.bell-stage .ringsvg { position: absolute; inset: 0; }
.ring-track { stroke: var(--blush); stroke-width: 7; fill: none; }
.ring-prog {
  stroke: var(--lacquer); stroke-width: 7; fill: none; stroke-linecap: round;
  transform: rotate(-90deg); transform-origin: center;
  transition: stroke-dashoffset 1s linear;
}
.bell-core {
  position: absolute; inset: 22px;
  border-radius: 50%;
  background: linear-gradient(150deg, var(--blush), var(--surface));
  display: flex; align-items: center; justify-content: center;
}
.count { font-variant-numeric: tabular-nums; font-weight: var(--fw-bold); color: var(--lacquer-deep); }

/* ---------- Customer shell (base_customer.html) ---------- */
.shell-customer { display: flex; flex-direction: column; min-height: 100vh; }
.shell-main { flex: 1; }
.brandbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-16); padding: var(--space-24) 0; }

/* ---------- Focus shell (base_focus.html) — single centered card ---------- */
.shell-focus { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: var(--space-32) var(--space-16); }
/* `margin: 0 auto` is a no-op inside .shell-focus's own flex centering, but
   lets .focus-card be reused verbatim as a self-contained "one job" block
   inside a non-flex parent too — e.g. vendor/inbox.html + vendor/kyc.html
   (Phase 6 polish pass) center it inside base_vendor.html's plain .wrap,
   keeping the sidebar/tabbar chrome instead of switching to a chromeless
   shell. Same component, no new class. */
.focus-card { width: min(460px, 100%); margin: 0 auto; background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--shadow-card); padding: var(--space-32) var(--space-24) var(--space-24); text-align: center; }
.focus-card .eyebrow { display: block; margin-bottom: var(--space-12); }
.focus-card form .btn-primary { width: 100%; } /* primary submit fills the card — the lacquer moment per viewport */
.focus-card .focus-links { margin: var(--space-20) 0 0; font-size: var(--fs-14-5); color: var(--ink-soft); }
.focus-card .focus-links a { color: var(--ink-soft); font-weight: var(--fw-semibold); text-decoration: underline; text-decoration-color: var(--blush-line); text-underline-offset: 2px; }
.focus-card .focus-links a:hover, .focus-card .focus-links a:focus-visible { color: var(--ink); text-decoration-color: currentColor; }

/* Heading/lead-text base type — was scoped to `.focus-card h1/h2/.focus-sub`
   only; generalized (Phase 6 polish pass) because `.focus-sub` was already
   used unscoped on several admin/vendor screens (bare <p class="focus-sub">
   outside any .focus-card), which silently fell back to unstyled UA text —
   a real bug, not a choice. Page titles getting Fraunces everywhere is also
   exactly what design §2.4 asks for ("no display type beyond page titles"
   on admin — i.e. page titles ARE allowed it). A more specific selector
   (e.g. `.display-headline`) still wins over this base rule by specificity,
   so index.html's hero is unaffected. */
h1, h2 { font-family: var(--font-display); font-weight: var(--fw-semibold); color: var(--ink); margin: 0; }
h1 { font-size: var(--fs-display); line-height: var(--lh-display-tight); margin: 0 0 var(--space-12); }
h2 { font-size: var(--fs-26); line-height: var(--lh-display); margin: 0 0 var(--space-8); }
.focus-sub { color: var(--ink-soft); font-size: var(--fs-14-5); line-height: var(--lh-body-loose); margin: 0 0 var(--space-24); }

/* ---------- Utility shells (base_vendor.html / base_admin.html) ---------- */
.utility-shell { display: flex; min-height: 100vh; }
.utility-sidebar {
  width: 220px; flex: none;
  background: var(--surface);
  border-right: 1px solid var(--blush-line);
  padding: var(--space-24) var(--space-16);
  display: flex; flex-direction: column; gap: var(--space-24);
}
.utility-sidebar .wordmark { font-size: var(--fs-24); }
.side-nav { display: flex; flex-direction: column; gap: var(--space-4); }
.side-nav-link {
  display: flex; align-items: center; gap: var(--space-12);
  padding: 12px 14px; border-radius: var(--r-md);
  font-weight: var(--fw-semibold); font-size: var(--fs-14-5);
  color: var(--ink-soft); text-decoration: none;
  min-height: var(--space-44);
}
.side-nav-link:hover { color: var(--ink); background: var(--porcelain); }
.side-nav-link.is-active { color: var(--lacquer); background: var(--blush); }
/* Disabled nav placeholder — route not built yet (base_vendor.html /
   base_admin.html template comments say which phase re-enables each item).
   Shared by .side-nav-link and .tabbar-link so both shell regions match;
   same slot/label as the live link, just visibly and semantically inert. */
.side-nav-link.is-disabled,
.tabbar-link.is-disabled {
  opacity: .55;
  cursor: not-allowed;
}
.side-nav-link.is-disabled:hover { color: var(--ink-soft); background: none; }
.utility-main { flex: 1; min-width: 0; padding: var(--space-32) var(--space-24); }

/* Vendor: bottom tabs replace the sidebar under 900px. Inbox is home. */
.tabbar { display: none; }
@media (max-width: 899px) {
  .utility-shell--vendor .utility-sidebar { display: none; }
  .utility-shell--vendor .tabbar {
    display: flex; position: fixed; inset: auto 0 0 0;
    background: var(--surface); border-top: 1px solid var(--blush-line);
    padding: var(--space-8); z-index: 20;
  }
  .utility-shell--vendor .tabbar-link {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; min-height: var(--space-44);
    font-size: var(--fs-11); font-weight: var(--fw-semibold);
    color: var(--ink-soft); text-decoration: none; padding: 6px 4px;
  }
  .utility-shell--vendor .tabbar-link.is-active { color: var(--lacquer); }
  .utility-shell--vendor .utility-main { padding-bottom: var(--space-64); }

  /* Admin: zero decoration — sidebar just stacks above content, no tabbar. */
  .utility-shell--admin { flex-direction: column; }
  .utility-shell--admin .utility-sidebar {
    width: 100%; border-right: none; border-bottom: 1px solid var(--blush-line);
    flex-direction: row; align-items: center; gap: var(--space-16); overflow-x: auto;
  }
  .utility-shell--admin .side-nav { flex-direction: row; }
}

/* ---------- Home placeholder (index.html, Phase 3 only — full mode-led
   home per design §2.1.1 lands in Phase 10) ---------- */
.home-placeholder { padding: var(--space-64) 0; }
.home-placeholder .display-headline { margin-top: var(--space-16); }
.home-placeholder-sub { margin-top: var(--space-16); max-width: 46ch; color: var(--ink-soft); font-size: var(--fs-17); line-height: var(--lh-body-loose); }

/* ---------- Responsive floor (design §2.2/§10): 360px → 1440px, no h-scroll ---------- */
html, body { max-width: 100%; overflow-x: hidden; }
