/* A & F Auto Repair LLC — public site styles.
   Mobile-first, plain CSS, no framework, no external fonts.
   Design-system tokens at the top; shared layout and page styles below.
   The palette is a restrained neutral automotive baseline (deep slate +
   burnt orange accent) chosen to accept the real logo/branding later. */

/* ---------- Design tokens ---------- */
:root {
  color-scheme: light;

  /* Color */
  --color-ink: #14232e;            /* header / footer / dark surfaces */
  --color-ink-soft: #1c2f3d;       /* hero / CTA band */
  --color-bg: #f4f6f7;             /* page background */
  --color-surface: #ffffff;        /* panels */
  --color-text: #26333d;           /* body text */
  --color-heading: #14232e;
  --color-muted: #5b6b76;          /* secondary text */
  --color-border: #d7dee3;
  --color-accent: #b3561c;         /* burnt orange — CTAs */
  --color-accent-strong: #97471a;  /* accent hover */
  --color-accent-soft: #f0a35e;    /* accent text on dark surfaces */
  --color-link: #155a8a;
  --color-focus: #155a8a;
  --color-error: #b3261e;          /* invalid-field guidance (W02-H03) */
  --color-error-soft: #fbeae9;     /* invalid-field card tint (W02-H03) */
  --color-on-ink: #ffffff;
  --color-on-ink-muted: #c3d0d8;

  /* Spacing */
  --space-xs: 0.375rem;
  --space-sm: 0.75rem;
  --space-md: 1.25rem;
  --space-lg: 2rem;
  --space-xl: 3rem;
  --space-2xl: 4rem;

  /* Layout */
  --content-width: 68rem;
  --content-narrow: 44rem;

  /* Typography */
  --text-xs: 0.875rem;
  --text-sm: 0.9375rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.375rem;
  --text-2xl: 1.75rem;
  --text-3xl: 2.5rem;

  /* Radii */
  --radius-sm: 4px;
  --radius: 8px;
}

/* ---------- Reset / base ---------- */
* { box-sizing: border-box; }

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

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-bg);
}

img, svg { max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  line-height: 1.25;
  color: var(--color-heading);
  margin: 0 0 var(--space-sm);
  overflow-wrap: break-word;
}
p { margin: 0 0 var(--space-md); }

a {
  color: var(--color-link);
  text-underline-offset: 2px;
}
a:hover { color: var(--color-accent); }

ul { padding-left: 1.25rem; }

.container {
  width: 100%;
  max-width: var(--content-width);
  margin-inline: auto;
  padding-inline: var(--space-md);
}

/* ---------- Accessibility ---------- */
/* Skip link: visually hidden until focused. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-ink);
  color: var(--color-on-ink);
  padding: 0.6rem 1rem;
  z-index: 100;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; color: var(--color-on-ink); }

:focus-visible { outline: 3px solid var(--color-focus); outline-offset: 2px; }

::selection { background: var(--color-accent); color: var(--color-on-ink); }

/* ---------- Header / navigation ---------- */
.site-header {
  background: var(--color-ink);
  color: var(--color-on-ink);
  border-bottom: 3px solid var(--color-accent);
}
.header-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-top: var(--space-sm);
  padding-bottom: var(--space-sm);
}
.brand {
  color: var(--color-on-ink);
  text-decoration: none;
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.3;
}
.brand:hover { color: var(--color-on-ink); }

.site-nav { display: flex; flex-wrap: wrap; gap: var(--space-md); }
.site-nav a {
  color: var(--color-on-ink-muted);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 0.65rem 0; /* >= 44px tap target on mobile */
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--color-on-ink); }
.site-nav a[aria-current="page"] {
  color: var(--color-on-ink);
  border-bottom-color: var(--color-accent-soft);
}

/* Header CTA: keeps the primary nav to three links so 320px stays usable;
   the estimate path is a distinct button instead of a fourth nav link. */
.header-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
}
.header-cta { text-align: center; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  min-height: 2.75rem;
  padding: 0.7rem 1.4rem;
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-weight: 600;
  font-size: var(--text-base);
  line-height: 1.3;
  border: 2px solid transparent;
}
.btn-primary { background: var(--color-accent); color: var(--color-on-ink); }
.btn-primary:hover { background: var(--color-accent-strong); color: var(--color-on-ink); }
button.btn {
  font: inherit;
  cursor: pointer;
}
.estimate-submit {
  margin-top: var(--space-md);
}
/* W04-B05: indeterminate submission status. Text is authoritative; the
   ::before spinner is decoration only and is disabled under
   prefers-reduced-motion. */
.estimate-status {
  margin: 0 0 var(--space-sm);
  color: var(--color-heading);
  font-weight: 600;
  font-size: var(--text-sm);
  min-height: 1.4em; /* reserves one line so the button does not jump */
}
.estimate-status.is-busy::before {
  content: "";
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-right: 0.45em;
  vertical-align: -0.15em;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: estimate-spin 0.8s linear infinite;
}
@keyframes estimate-spin {
  to { transform: rotate(360deg); }
}
/* Mobile: the primary submit action spans the form width — a larger, easier
   tap target that also prevents the button text from wrapping awkwardly on
   narrow screens. Restored to an inline right-aligned control at tablet+. */
.estimate-submit .btn {
  width: 100%;
  padding-inline: 1rem; /* keeps "Submit estimate request" on one line at 320px */
}
.btn-secondary { background: transparent; color: var(--color-on-ink); border-color: var(--color-on-ink); }
.btn-secondary:hover { background: rgba(255, 255, 255, 0.12); color: var(--color-on-ink); }
.btn-outline { background: transparent; color: var(--color-link); border-color: var(--color-link); }
.btn-outline:hover { background: var(--color-link); color: var(--color-on-ink); }

/* ---------- Shared button + utility primitives (W06-P01) ----------
   The composer needed a couple of missing sizes/roles; they are defined once
   here (not inline per control) so every admin surface can reuse them. */
.btn:focus-visible { outline: 3px solid var(--color-focus); outline-offset: 2px; }
.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}
.btn-lg {
  min-height: 2.875rem;
  padding: 0.7rem 1.6rem;
  font-size: 1.0625rem;
}
.btn-sm {
  min-height: 2.25rem;
  padding: 0.4rem 0.9rem;
  font-size: var(--text-sm);
}
/* Square icon control: compact, secondary, still a 36px+ target. */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  font: inherit;
  font-size: var(--text-base);
  line-height: 1;
  color: var(--color-muted);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.btn-icon:hover { color: var(--color-heading); border-color: var(--color-muted); }
.btn-icon[disabled] { opacity: 0.4; cursor: not-allowed; }
.btn-icon--danger { font-size: 1.15rem; }
.btn-icon--danger:hover {
  color: var(--color-error);
  border-color: var(--color-error);
  background: var(--color-error-soft);
}
/* Quiet destructive text action: never louder than Save Draft. */
.btn-quiet-danger {
  padding: 0.4rem 0.6rem;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-muted);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.btn-quiet-danger:hover { color: var(--color-error); border-color: var(--color-error); }
.btn-quiet-danger[disabled] { opacity: 0.4; cursor: not-allowed; color: var(--color-muted); border-color: transparent; }
/* Finalize is deliberately NOT the same filled-orange control as Save Draft:
   it locks a revision, so it reads as a deliberate, different decision. */
.btn-finalize {
  background: var(--color-ink);
  border-color: var(--color-ink);
  color: var(--color-on-ink);
}
.btn-finalize:hover { background: var(--color-ink-soft); border-color: var(--color-ink-soft); color: var(--color-on-ink); }

/* Standard visually-hidden helper (used by the composer's table labels and
   section legends). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Hero ---------- */
.hero { background: var(--color-ink-soft); color: var(--color-on-ink); padding: var(--space-2xl) 0; }
.hero h1 { color: var(--color-on-ink); font-size: var(--text-2xl); margin-bottom: var(--space-sm); }
.hero-kicker {
  margin-bottom: var(--space-xs);
  color: var(--color-accent-soft);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.hero-lede {
  color: var(--color-on-ink-muted);
  max-width: var(--content-narrow);
  font-size: var(--text-lg);
}
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-top: var(--space-md); }

/* ---------- Sections ---------- */
.section { padding: var(--space-2xl) 0; border-bottom: 1px solid var(--color-border); }
.page-head { padding-bottom: var(--space-sm); }

.section-head { margin-bottom: var(--space-lg); }
.section-head h2 { margin-bottom: var(--space-xs); }
.section-intro-text { color: var(--color-muted); max-width: var(--content-narrow); margin-bottom: 0; }
.section-cta { margin-top: var(--space-md); }

/* Homepage: trust / value introduction */
.intro-inner { max-width: var(--content-narrow); }
.intro-inner p { margin-bottom: var(--space-md); }
.value-list { margin: 0 0 var(--space-sm); padding-left: 0; list-style: none; }
.value-list li {
  position: relative;
  padding-left: 1.5rem;
  margin-bottom: var(--space-sm);
}
.value-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 0.75rem;
  height: 2px;
  background: var(--color-accent);
}

/* Homepage: services preview (light list, not cards) */
.service-preview-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
}
.service-preview-item {
  border-left: 3px solid var(--color-accent);
  padding-left: var(--space-md);
}
.service-preview-item h3 { margin-bottom: var(--space-xs); font-size: var(--text-lg); }
.service-preview-item p { margin-bottom: 0; color: var(--color-muted); }

/* Homepage: visit the shop (spec-sheet style) */
.visit-grid {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}
.visit-block { padding: var(--space-md); }
.visit-block + .visit-block { border-top: 1px solid var(--color-border); }
.visit-block h3 { font-size: var(--text-base); margin-bottom: var(--space-xs); }
.visit-block p { margin-bottom: var(--space-xs); }
.visit-block p:last-child { margin-bottom: 0; }

/* Homepage: shop photograph (real Client photo; placeholder styles removed) */
.shop-photo { margin: 0; }
.shop-photo img {
  display: block;
  width: 100%;
  max-width: 48rem;
  height: auto;
  margin-inline: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}

/* Homepage: final CTA band */
.section-final-cta {
  background: var(--color-ink-soft);
  border-bottom: 0;
  color: var(--color-on-ink);
  margin-bottom: var(--space-lg);
}
.cta-inner { max-width: var(--content-narrow); text-align: left; }
.cta-inner h2 { color: var(--color-on-ink); font-size: var(--text-2xl); }
.cta-inner p { color: var(--color-on-ink-muted); }

/* ---------- Services page ---------- */
/* Page intro: light band with an accent rule, distinct from the dark hero. */
.page-intro {
  background: var(--color-surface);
  border-bottom: 3px solid var(--color-accent);
  padding: var(--space-2xl) 0;
}
.page-intro h1 { font-size: var(--text-3xl); margin-bottom: var(--space-xs); }
.page-intro-lede {
  color: var(--color-muted);
  max-width: var(--content-narrow);
  font-size: var(--text-lg);
  margin-bottom: var(--space-md);
}

/* Service catalog: numbered spec-style list, not generic cards. */
.service-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--color-border);
}
.service-item {
  display: flex;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  border-bottom: 1px solid var(--color-border);
}
.service-index {
  flex: 0 0 2.25rem;
  padding-top: 0.15rem;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}
.service-body h3 { margin-bottom: var(--space-xs); font-size: var(--text-lg); }
.service-body p { margin-bottom: 0; color: var(--color-muted); }
.pending-note {
  font-size: var(--text-xs);
  color: var(--color-muted);
  font-style: italic;
  margin-bottom: 0;
}

/* ---------- Contact list ---------- */
.contact-panel {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  padding: var(--space-xs) var(--space-md);
}
.contact-list { margin: 0; }
.contact-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xs);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--color-border);
}
.contact-row:last-child { border-bottom: 0; }
.contact-row dt { font-weight: 600; color: var(--color-heading); }
.contact-row dd { margin: 0; }
.contact-row dd address { font-style: normal; }
.note { color: var(--color-muted); }
.note-on-dark { color: var(--color-on-ink-muted); margin-top: var(--space-md); }

/* ---------- Estimate page ---------- */
/* Intake shell: the structural panel that will host the W02 contact, vehicle,
   and repair-description fields. Plain spec-sheet styling — no fake inputs. */
/* Intake steps stay stacked top-down at every width (single column). */
.estimate-process {
  display: grid;
  grid-template-columns: 1fr;
}
.intake-shell {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  padding: var(--space-xs) var(--space-md);
  max-width: var(--content-narrow);
}
.intake-section {
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--color-border);
}
.intake-section:last-of-type { border-bottom: 0; }
.intake-section h3 { font-size: var(--text-base); margin-bottom: var(--space-xs); }
.intake-section p { margin-bottom: 0; color: var(--color-muted); font-size: var(--text-sm); }
.intake-pending {
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
}
.estimate-note { margin-top: var(--space-md); max-width: var(--content-narrow); }

/* Estimate form controls: reusable vocabulary for the W02 intake fields
   (customer name now; phone/email/vehicle/damage fields follow in B03+). */
.form-group { margin-bottom: var(--space-md); }
.form-group:last-child { margin-bottom: 0; }
.form-label {
  display: block;
  font-weight: 600;
  color: var(--color-heading);
  margin-bottom: var(--space-xs);
}
.form-control {
  width: 100%;
  min-height: 2.75rem; /* matches .btn tap-target size on mobile */
  padding: 0.6rem 0.75rem;
  font: inherit;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.form-control:focus-visible { border-color: var(--color-focus); }

/* Paired fields (e.g. first/last name): stacked on mobile, side-by-side
   from tablet up so each control keeps a comfortable width. */
.form-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  margin-bottom: var(--space-md);
}
.form-row .form-group { margin-bottom: 0; }

/* Preferred-contact method: semantic radio group (B03). The three choices
   stack on narrow screens and sit side-by-side from tablet up. */
fieldset.form-group {
  border: 0;
  margin: 0 0 var(--space-md);
  padding: 0;
  min-width: 0;
}
fieldset.form-group > legend.form-label { padding: 0; }
.preference-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xs);
}
.form-choice {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 2.75rem; /* matches .form-control tap-target size on mobile */
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  cursor: pointer;
}
.form-choice input { accent-color: var(--color-accent); }
/* Desktop pointer affordance for the tappable cards; the checked state
   below wins by specificity, so hover never masks the selection. */
.form-choice:hover { border-color: var(--color-accent-strong); background: var(--color-bg); }
.form-choice:has(input:checked) { border-color: var(--color-accent); }

/* ---------- Damage-photo selection (W04-B01/B02) ----------
   The photo input is a native multi-file control; the application-controlled
   selected-photo area below it is filled by static/js/app.js. Long
   filenames wrap inside the list (overflow-wrap: anywhere) so no item can
   push the page horizontally at 320px/375px. The clear action is a plain
   text button so it never competes with the primary submit control.
   B02: the same single native input is the mobile picker surface — the
   browser/OS owns the photo-library/camera chooser, so no custom camera
   UI or MediaDevices code exists here. font-size is pinned to 1rem (>=16px)
   so iOS Safari does not auto-zoom when the control is focused. */
.form-helper {
  display: block;
  margin-top: var(--space-xs);
  color: var(--color-muted);
  font-size: var(--text-xs);
  line-height: 1.4;
}
.photo-input {
  display: block;
  width: 100%;
  max-width: 24rem;
  min-height: 2.75rem; /* tap-target size parity with .form-control */
  font: inherit;
  font-size: 1rem; /* >=16px: prevents iOS focus auto-zoom */
  color: var(--color-text);
}
.photo-input:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
.photo-summary {
  margin-top: var(--space-sm);
  margin-bottom: 0;
  font-weight: 600;
  color: var(--color-heading);
}
.photo-list {
  margin: var(--space-xs) 0 0;
  padding-left: 1.25rem;
  display: grid;
  gap: var(--space-xs);
}
.photo-list li { overflow-wrap: anywhere; }
/* W04-B04: client-side size precheck warning (UX only — the server
   enforces the 10 MiB per-photo rule on actual bytes). Visible whenever
   the customer has selected an oversized file; also carried as the native
   constraint-validation message on the input. */
.photo-warning {
  display: none;
  margin-top: var(--space-xs);
  color: var(--color-error);
  font-size: var(--text-xs);
  line-height: 1.4;
}
.photo-warning.show { display: block; }
.photo-clear {
  margin-top: var(--space-xs);
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-link);
  font: inherit;
  font-size: var(--text-xs);
  text-decoration: underline;
  cursor: pointer;
}
.photo-clear:hover { color: var(--color-accent-strong); }

/* ---------- Invalid-field handling (W02-H03) ----------
   Progressive enhancement on top of native constraint validation: the
   browser still blocks submission, focuses the first invalid control, and
   shows its own tooltip. This block adds a persistent visual/guidance layer
   only after a submit attempt has actually failed (.was-validated is added
   by static/js/app.js). Before any attempt, invalid controls are not
   decorated, so an empty required field is never "angry red" on page load. */
.form-error {
  display: none;
  margin-top: var(--space-xs);
  color: var(--color-error);
  font-size: var(--text-xs);
  line-height: 1.4;
}

.estimate-form.was-validated .form-control:invalid {
  border-color: var(--color-error);
}

.estimate-form.was-validated .form-control:invalid ~ .form-error {
  display: block;
}

/* Preferred-contact method is one required radio group: when no choice is
   selected after an attempt, treat the whole group as the problem — a
   colored legend, a shared message, and a shared card tint. Individual
   radios are never labeled as separate errors. */
.estimate-form.was-validated fieldset:has(input:invalid) legend.form-label {
  color: var(--color-error);
}
.estimate-form.was-validated fieldset:has(input:invalid) .form-choice {
  border-color: var(--color-error);
  background: var(--color-error-soft);
}
.estimate-form.was-validated fieldset:has(input:invalid) .form-error {
  display: block;
}

/* ---------- Server validation failures (W02-H06) ----------
   The server is the authority: a handcrafted POST that bypasses browser
   validation is checked server-side, and the 422 re-render marks rejected
   fields with the same visual language as H03 (.server-invalid on the
   control, .show on the message). Some server-invalid values (e.g.
   whitespace-only required fields) are browser-valid, so this state is
   independent of :invalid and of the H03 .was-validated gating. */
.estimate-form.server-validated .form-control.server-invalid {
  border-color: var(--color-error);
}

.estimate-form.server-validated .form-control.server-invalid ~ .form-error,
.estimate-form.server-validated .form-error.show {
  display: block;
}

/* Preferred-contact group: one shared server-error treatment. */
.estimate-form.server-validated fieldset.server-invalid legend.form-label {
  color: var(--color-error);
}
.estimate-form.server-validated fieldset.server-invalid .form-choice {
  border-color: var(--color-error);
  background: var(--color-error-soft);
}
.estimate-form.server-validated fieldset.server-invalid .form-error.show {
  display: block;
}

/* ---------- Request-level failure (W02-H07) ----------
   A pre-commit infrastructure failure re-renders the form with a
   form-level banner. It is deliberately NOT field-error styling: no field
   is marked, and the copy does not blame any customer input. The banner is
   a restrained neutral panel with a strong left accent so it reads as
   "the request did not go through" without shouting. */
.form-alert {
  margin-bottom: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-accent-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.form-alert p {
  margin: 0;
}

/* ---------- Administrator pipeline status (W06-B04) ----------
   The one place an administrator moves a lead along the shop workflow.
   The current status is the persisted (localized) state and the buttons are
   generated server-side from the centralized transition policy, so this
   panel is a control surface, never a rule book. Terminal statuses render no
   buttons at all. */
.admin-status-current {
  margin: 0 0 var(--space-sm);
}
.admin-status-label {
  color: var(--color-muted);
}
.admin-status-change {
  margin: 0 0 var(--space-xs);
  color: var(--color-muted);
  font-size: var(--text-sm);
}
.admin-status-choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}
.admin-status-choices .btn {
  width: auto;
}
/* A confirmed status change reads as success without shouting: the same
   panel as a form alert, accent moved to the confirm colour. */
.form-alert-success {
  border-left-color: #1d7a4b;
  background: #eef7f1;
}

/* ---------- Footer ---------- */
.site-footer {
  background: var(--color-ink);
  color: var(--color-on-ink-muted);
  border-top: 3px solid var(--color-accent);
  padding: var(--space-xl) 0 var(--space-2xl);
}
.site-footer h2 { color: var(--color-on-ink); font-size: var(--text-base); margin-bottom: var(--space-xs); }
.site-footer a { color: var(--color-accent-soft); }
.footer-col p { margin-bottom: 0; color: var(--color-on-ink-muted); }
.footer-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-lg); }

/* ---------- Administrator area (W02-P02) ----------
   The admin shell shares the public design tokens and button vocabulary but
   owns its own chrome (header, navigation, content frame). Every admin
   class is admin-* prefixed and the body is scoped to .page-admin, so no
   public customer page is affected. Mobile-first; the header and nav wrap
   instead of requiring a horizontal scroll. */
body.page-admin {
  background: var(--color-bg);
}

.admin-header {
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.admin-header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
  padding-block: var(--space-sm);
}

.admin-brand {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.admin-brand-name {
  color: var(--color-heading);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.25;
  text-decoration: none;
}
.admin-brand-name:hover { color: var(--color-link); }

.admin-brand-role {
  color: var(--color-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.admin-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
}

.admin-logout-form { margin: 0; }

.admin-nav {
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.admin-nav-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-nav-list a {
  display: inline-block;
  padding: var(--space-sm) var(--space-xs);
  color: var(--color-text);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px; /* sit on the nav's bottom border */
}
.admin-nav-list a:hover { color: var(--color-link); }
/* Active item: color PLUS an underline, so it is not conveyed by color
   alone (aria-current is also present in the markup). */
.admin-nav-list a[aria-current="page"] {
  color: var(--color-link);
  border-bottom-color: var(--color-accent);
}

.admin-main {
  padding: var(--space-lg) 0 var(--space-2xl);
}

.admin-intro {
  max-width: var(--content-narrow);
  margin-bottom: var(--space-lg);
}
/* The public .hero-kicker is tuned for dark hero surfaces; on the white
   admin surface it uses the brand accent for readable contrast. */
.admin-intro .hero-kicker { color: var(--color-accent); }

.admin-lede {
  font-size: var(--text-lg);
  margin-bottom: var(--space-xs);
}
.admin-lede-sub {
  color: var(--color-muted);
  margin-bottom: 0;
}

.admin-panel {
  max-width: var(--content-narrow);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-md);
}
.admin-panel h2 { font-size: var(--text-lg); }

/* W08-B02: compact, stacked Repair Job queue cards. Current work stays the
   first section; completed records are quieter but remain easy to find. */
.admin-repair-job-section { margin-top: var(--space-xl); }
.admin-repair-job-section > h2 { margin-bottom: var(--space-sm); }
.admin-repair-job-list {
  display: grid;
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}
.admin-repair-job-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.admin-repair-job-identity { min-width: 0; }
.admin-repair-job-identity h3,
.admin-repair-job-identity p { margin: 0; overflow-wrap: anywhere; }
.admin-repair-job-identity p { margin-top: var(--space-xs); color: var(--color-muted); }
.admin-repair-job-status { margin: 0; white-space: nowrap; }
.admin-repair-job-status span { color: var(--color-muted); }
.admin-repair-job-open { white-space: normal; }
.admin-repair-job-completed { margin-top: var(--space-2xl); }
.admin-repair-job-completed .admin-repair-job-card { background: var(--color-bg); }
.admin-repair-job-warning {
  padding: var(--space-sm) var(--space-md);
  border-left: 3px solid var(--color-border);
  background: var(--color-surface);
}
.admin-repair-job-detail { display: grid; gap: var(--space-md); }
.admin-repair-job-detail-summary h2 { margin-top: 0; }
.admin-repair-job-detail-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-md);
}
.admin-repair-job-detail-grid .admin-panel { min-width: 0; }
.admin-repair-job-detail-grid h2 { font-size: var(--text-base); margin-top: 0; }
.admin-repair-job-detail-value { margin-bottom: 0; overflow-wrap: anywhere; }
.admin-repair-job-original p { overflow-wrap: anywhere; }
.admin-repair-job-approved-work { min-width: 0; }
.admin-repair-job-notes-activity h2 { margin-top: 0; }
.admin-repair-job-notes-activity nav { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.admin-repair-job-approved-revision { display: grid; gap: var(--space-xs); overflow-wrap: anywhere; }
.admin-repair-job-revision-note { color: var(--color-muted); }
.admin-repair-job-work-section { margin-top: var(--space-lg); min-width: 0; }
.admin-repair-job-work-section h3 { font-size: var(--text-base); }
.admin-repair-job-work-list { display: grid; gap: var(--space-sm); list-style: none; padding: 0; }
.admin-repair-job-work-item { min-width: 0; border-top: 1px solid var(--color-border); padding-top: var(--space-sm); overflow-wrap: anywhere; }
.admin-repair-job-work-item h4 { margin: 0; font-size: var(--text-base); }
.admin-repair-job-work-item p { overflow-wrap: anywhere; }
.admin-repair-job-work-photos { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.admin-repair-job-work-photos a { display: grid; gap: var(--space-xs); width: min(9rem, 100%); color: inherit; overflow-wrap: anywhere; }
.admin-repair-job-work-photos img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-sm); }
.admin-repair-job-work-photos span { font-size: var(--text-sm); }
@media (max-width: 47.99rem) {
  .admin-repair-job-card {
    grid-template-columns: minmax(0, 1fr);
    justify-items: start;
    gap: var(--space-sm);
  }
  .admin-repair-job-status { white-space: normal; }
  .admin-repair-job-open { width: 100%; text-align: center; }
  .admin-repair-job-detail-grid { grid-template-columns: minmax(0, 1fr); }
}

/* W05-B05: caption under the "Submitted customer/vehicle information"
   headings on the lead-detail page — a muted one-liner that makes the
   historical (estimate-submission) nature of the panel unmistakable
   without developer terms or internal ids. */
/* W05-B09: compact current-vehicle list on the customer-detail page.
   Plain stacked entries (no table), each wrapping safely at 320px. */
.admin-vehicle-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.admin-vehicle-entry {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-sm) var(--space-md);
  overflow-wrap: anywhere;
}
.admin-vehicle-link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.admin-vehicle-view {
  color: var(--color-muted);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.admin-snapshot-note {
  color: var(--color-muted);
  font-size: var(--text-sm);
  margin: calc(var(--space-xs) * -0.5) 0 var(--space-sm);
}

.admin-area-list { margin: 0; padding-left: 1.25rem; }
.admin-area-list li { margin-bottom: var(--space-xs); }
.admin-area-list li:last-child { margin-bottom: 0; }

/* ---------- Administrator lead list (W02-P03) ---------- */
/* Desktop: a semantic summary table. Mobile (< 48rem): rows transform into
   stacked labelled cards (thead visually hidden, td::before shows the
   data-label), so Felix can inspect the list on a phone without horizontal
   scrolling. */
.admin-table-wrap { overflow-x: auto; }

.admin-leads-table {
  width: 100%;
  border-collapse: collapse;
}

.admin-leads-table th,
.admin-leads-table td {
  text-align: left;
  padding: var(--space-sm) var(--space-xs);
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
}

.admin-leads-table thead th {
  color: var(--color-muted);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.admin-leads-table tbody tr:last-child td { border-bottom: 0; }

.admin-leads-table td span {
  display: block;
  overflow-wrap: anywhere; /* long emails/names wrap; nothing is clipped */
  min-width: 0;
}

.admin-lead-customer {
  font-weight: 600;
  color: var(--color-heading);
}

/* W02-P04: the one real list → detail link. The customer name is the link;
   the accessible name says "View request for …" while the URL stays a bare
   numeric ID. */
.admin-lead-link {
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.admin-lead-link:hover { color: var(--color-accent-strong); }
.admin-lead-link:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.admin-lead-muted { color: var(--color-muted); }

/* W03-B09: read-only lead-state badge. Display-only — never interactive. */
.admin-state-badge {
  display: inline-block;
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--color-text);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  white-space: nowrap;
}

/* W03-B10: basic search/sort controls. Plain GET form — no JavaScript.
   Compact row on desktop, stacked labels on narrow viewports. */
.admin-leads-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
  margin-bottom: 1rem;
  padding: 0.75rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
}

.admin-leads-controls label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text);
}

.admin-leads-controls input[type="search"],
.admin-leads-controls select {
  padding: 0.45rem 0.6rem;
  font: inherit;
  color: var(--color-text);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 0.35rem;
}

.admin-search-field input[type="search"] {
  min-width: 16rem;
  width: 100%;
}

/* W05-B13: directory searches share the existing admin list controls but
   keep the action group together when a query is present. */
.admin-directory-search .admin-search-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* W03-P02: the preferred-contact filter select matches the sort select's
   footprint so the controls row stays balanced on desktop. */
.admin-contact-field select,
.admin-sort-field select {
  min-width: 8.5rem;
}

@media (max-width: 47.99rem) {
  .admin-leads-controls {
    align-items: stretch;
    flex-direction: column;
  }
  .admin-search-field input[type="search"] {
    min-width: 0;
  }
  .admin-directory-search .admin-search-actions {
    align-items: stretch;
    flex-direction: column;
  }
}

.admin-empty { margin: 0; color: var(--color-muted); }

.admin-lead-count {
  margin: 0 0 var(--space-sm);
  color: var(--color-muted);
  font-size: var(--text-sm);
}

.admin-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}
.admin-pagination .btn {
  min-height: 2.25rem;
  padding: 0.4rem 0.9rem;
  font-size: var(--text-sm);
}
.admin-pagination-info,
.admin-pagination-disabled {
  color: var(--color-muted);
  font-size: var(--text-sm);
}
.admin-pagination-disabled { opacity: 0.6; }

/* ---------- Administrator lead detail (W02-P04) ---------- */
/* Mobile-first: one column. Desktop: a restrained two-column grid — the
   admin panel's --content-narrow cap keeps text from stretching across a
   wide monitor. */
.admin-detail-back {
  margin: 0 0 var(--space-md);
}

.admin-detail-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm) var(--space-lg);
  margin: 0;
}

.admin-detail-grid > div {
  min-width: 0; /* long emails/vehicles wrap instead of overflowing */
}

.admin-detail-grid dt {
  color: var(--color-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.admin-detail-grid dd {
  margin: var(--space-xs) 0 0;
  overflow-wrap: anywhere;
}

/* The complete Damage Description: full value, no truncation, line breaks
   preserved (pre-wrap), and very long unbroken text wrapped (anywhere) so
   nothing can blow out the page horizontally. */
.admin-damage-description {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  min-width: 0;
}

/* W03-P01: internal lead notes — immutable plain-text administrative
   memory. Mobile-first block list; bodies pre-wrap + wrap-anywhere so
   multiline and long unbroken note text stays inside the panel. */
.admin-note-list {
  margin: 0 0 var(--space-md);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-sm);
}
.admin-note {
  margin: 0;
  padding: var(--space-sm);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: 0.25rem solid var(--color-accent-strong);
  border-radius: var(--radius-sm);
  min-width: 0;
}
.admin-note-body {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.admin-note-meta {
  margin: var(--space-xs) 0 0;
  color: var(--color-muted);
  font-size: var(--text-xs);
}
.admin-note-form {
  margin: 0;
}
.admin-note-form textarea {
  min-height: 5rem;
  resize: vertical;
}

/* W06-P03: accessible chronological projection of existing Lead, status, and
   Note records. Every event carries a visible type label as well as its
   marker so meaning does not depend on color. */
.lead-activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-sm);
}
.lead-activity-item {
  display: grid;
  grid-template-columns: minmax(8rem, 10rem) minmax(0, 1fr);
  gap: var(--space-sm);
  min-width: 0;
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-left: 0.25rem solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}
.lead-activity-item[data-activity-kind="status"] {
  border-left-color: var(--color-link);
}
.lead-activity-item[data-activity-kind="note"] {
  border-left-color: var(--color-accent-strong);
}
.lead-activity-meta,
.lead-activity-content {
  min-width: 0;
}
.lead-activity-meta {
  display: grid;
  align-content: start;
  gap: 0.25rem;
}
.lead-activity-kind {
  font-weight: 700;
}
.lead-activity-meta time {
  color: var(--color-muted);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}
.lead-activity-summary,
.lead-activity-note {
  margin: 0;
  overflow-wrap: anywhere;
}
.lead-activity-note {
  white-space: pre-wrap;
}
.lead-activity-context {
  display: inline-block;
  margin-top: var(--space-xs);
  overflow-wrap: anywhere;
}

@media (max-width: 47.99rem) {
  .lead-activity-item {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.25rem;
  }
  .lead-activity-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--space-sm);
  }
}

/* ---------- W04-B08: administrator damage-photo gallery ----------
   Photos are private files served only through the protected lead-scoped
   PREVIEW route (/admin/leads/{id}/photos/{photo_id}/preview) — small
   W04-H08 derived previews, never the multi-megabyte originals (the B09
   viewer loads the original byte route only when the administrator opens
   a photo for inspection). Mobile-first: one full-width column per card;
   from 40rem a three-column grid keeps the whole set visible on
   tablets/desktop. Cards use aspect-ratio + object-fit: cover so image
   dimensions never dictate layout.
   W04-H08 decode-failure state: when a stored original cannot be
   preview-decoded (corrupt accepted object) the gallery image error is
   caught in app.js, the card gets .preview-unavailable, the broken <img>
   is hidden, and the card shows a controlled "Preview unavailable" note —
   the viewer link to the ORIGINAL still works.
   W04-B09: each card is a plain anchor to the protected viewer page
   (/admin/leads/{id}/photos/{photo_id}/view); the byte <img> and the
   visible "View" caption stay inside it. */
.admin-photo-gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-photo-card {
  margin: 0;
  min-width: 0;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.admin-photo-card:hover { border-color: var(--color-accent); }

/* The whole card is the viewer link; focus is drawn on the card itself so
   the outline is never clipped by overflow: hidden. */
.admin-photo-card:focus-within {
  border-color: var(--color-focus);
  outline: 2px solid var(--color-focus);
  outline-offset: -2px;
}

.admin-photo-link {
  display: flex;
  flex-direction: column;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
.admin-photo-link:hover { color: inherit; }

.admin-photo-thumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.admin-photo-view {
  display: block;
  padding: var(--space-xs);
  text-align: center;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-accent-strong);
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
}

/* W04-H08: controlled preview-unavailable state (see gallery comment). */
.admin-photo-card.preview-unavailable .admin-photo-thumb { display: none; }
.admin-photo-card.preview-unavailable .admin-photo-view {
  border-top: none;
}
.admin-photo-card.preview-unavailable .admin-photo-note {
  display: block;
  padding: var(--space-md) var(--space-sm);
  text-align: center;
  font-size: var(--text-xs);
  color: var(--color-muted);
}

@media (min-width: 40rem) {
  .admin-photo-gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- W04-B09: image viewer ----------
   One photograph at inspectable size inside the admin shell. The image is
   constrained by max-width/max-height with auto width/height (natural
   aspect ratio preserved — no crop, no cover, no stretch), so portrait and
   landscape originals both fit the page/viewport without horizontal
   overflow. Navigation is plain wrapping links; no JavaScript. */
.admin-viewer {
  margin: 0 0 var(--space-md);
  padding: var(--space-sm);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  min-width: 0;
}

.admin-viewer-image {
  display: block;
  margin-inline: auto;
  max-width: 100%;
  max-height: 72vh;
  width: auto;
  height: auto;
}

.admin-viewer-count {
  margin: 0 0 var(--space-sm);
  color: var(--color-muted);
  font-size: var(--text-sm);
}

.admin-viewer-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin: 0;
}

.admin-viewer-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin: var(--space-md) 0 0;
}

/* W04-B10: the destructive action. A clear red surface for permanent
   deletion (the confirmation page's Delete button and the viewer's entry
   link); the action still requires the deliberate confirmation POST with a
   CSRF token — styling is never authorization. */
.btn-danger {
  background: var(--color-error);
  border-color: var(--color-error);
  color: #ffffff;
}
.btn-danger:hover {
  background: #8f1d16;
  border-color: #8f1d16;
  color: #ffffff;
}
a.btn-danger:focus-visible,
button.btn-danger:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.admin-delete-form {
  margin: 0;
}

@media (min-width: 40rem) {
  .admin-detail-grid { grid-template-columns: repeat(2, 1fr); }
}

/* W03-B11: convenient contact actions — plain intent links that wrap
   cleanly and stay tap-friendly on narrow viewports. */
.admin-contact-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

@media (max-width: 47.99rem) {
  .admin-leads-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .admin-leads-table,
  .admin-leads-table tbody,
  .admin-leads-table tr,
  .admin-leads-table td {
    display: block;
    width: 100%;
  }
  .admin-leads-table tbody tr {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    margin-bottom: var(--space-sm);
    padding: var(--space-xs) var(--space-sm);
  }
  .admin-leads-table tbody tr:last-child { margin-bottom: 0; }
  .admin-leads-table td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-md);
    border: 0;
    padding: var(--space-xs) 0;
  }
  .admin-leads-table td::before {
    content: attr(data-label);
    flex-shrink: 0;
    color: var(--color-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
}

/* ---------- Tablet (>= 640px) ---------- */
@media (min-width: 40rem) {
  .header-inner { flex-direction: row; align-items: center; justify-content: space-between; }
  .header-actions { flex-direction: row; align-items: center; }
  .form-row { grid-template-columns: 1fr 1fr; }
  .preference-row { grid-template-columns: repeat(3, 1fr); }
  .site-nav a { padding: 0.65rem 0.25rem; }
  .estimate-submit { text-align: right; }
  .estimate-submit .btn { width: auto; padding-inline: 1.4rem; }
  .service-preview-grid { grid-template-columns: repeat(2, 1fr); }
  .visit-grid { display: grid; grid-template-columns: repeat(3, 1fr); }
  .visit-block + .visit-block { border-top: 0; border-left: 1px solid var(--color-border); }
  .visit-block:first-child { border-left: 0; }
  .contact-row { grid-template-columns: minmax(7rem, 10rem) 1fr; gap: 0.5rem 1rem; }
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
  .hero h1 { font-size: var(--text-3xl); }
}

/* ---------- Desktop (>= 992px) ---------- */
@media (min-width: 62rem) {
  .service-preview-grid { grid-template-columns: repeat(3, 1fr); }
  .service-list { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--space-2xl); }
  .estimate-process { grid-template-columns: 1fr; }
  .hero { padding: var(--space-2xl) 0; }
  .section { padding: var(--space-2xl) 0; }
  .cta-inner { text-align: center; }
  .cta-inner .hero-actions { justify-content: center; }
}

/* ---------- W06-P01: repair-estimate composer (admin work area) ----------
   The composer is the live service-counter tool: one wide work area, white
   cards, labels above their fields, presentation-specific line-item columns,
   and a summary panel showing the server's money. It reuses the shared
   tokens, .btn* vocabulary, .form-control and .form-label primitives — this
   block is layout and hierarchy, never a second design system. */

/* The composer needs the full desktop width; the narrow reading measure used
   by the other admin pages would leave most of a shop monitor empty. */
.container-wide { max-width: 76rem; }

.composer-head { max-width: 48rem; margin-bottom: var(--space-lg); }
/* The public .hero-kicker color is tuned for dark hero surfaces; on this
   white work area the eyebrow uses the brand accent for readable contrast
   (same rule the other admin pages use). */
.composer-head .hero-kicker { color: var(--color-accent); }
.composer-title {
  font-size: clamp(1.75rem, 1.5rem + 1vw, 2rem);
  margin-bottom: var(--space-xs);
}
.composer-sub { margin: 0; color: var(--color-muted); font-size: var(--text-base); }

/* ---- Server rejection / client validation summary ---- */
.composer-alert {
  margin: 0 0 var(--space-lg);
  padding: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-error);
  border-radius: var(--radius);
}
.composer-alert--fields { border-left-color: var(--color-accent-strong); }
.composer-alert-title { margin: 0 0 var(--space-xs); font-size: var(--text-base); }
.composer-alert-body { margin: 0 0 var(--space-xs); }
.composer-alert-hint { margin: 0; color: var(--color-muted); font-size: var(--text-sm); }
.composer-alert-list { margin: 0; padding-left: 1.1rem; font-size: var(--text-sm); }
.composer-alert-list li { margin-bottom: var(--space-xs); }
.composer-alert-list li:last-child { margin-bottom: 0; }

/* ---- Cards (customer / vehicle / details / notes / summary) ---- */
.card {
  min-width: 0;
  padding: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(20, 35, 46, 0.04);
}
.card--compact { padding: var(--space-sm) var(--space-md); }
/* Meta bar: a wide card that holds only a couple of fields keeps its copy on
   the left and the fields at the right edge, so the width reads as intent
   instead of empty space. */
.card--meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
}
.card--meta .card-head { margin-bottom: 0; flex: 1 1 22rem; }
.card-head { margin-bottom: var(--space-sm); }
.card-title { margin: 0; font-size: var(--text-base); }
.card-sub { margin: var(--space-xs) 0 0; color: var(--color-muted); font-size: var(--text-sm); }

/* ---- Fields ---- */
.form-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-sm) var(--space-md); }
.field { display: flex; flex-direction: column; min-width: 0; }
.field--wide { grid-column: 1 / -1; }

.estimate-composer .form-label {
  margin-bottom: var(--space-xs);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-muted);
}
/* One consistent control height for every input and select (42px), so the
   customer, vehicle, details, and line-item controls all line up. */
.estimate-composer .form-control {
  height: 2.625rem;
  min-height: 2.625rem;
  padding: 0.5rem 0.75rem;
  line-height: 1.4;
  font-size: var(--text-base);
}
.estimate-composer textarea.form-control {
  height: auto;
  min-height: 9rem;
  line-height: 1.5;
}
.estimate-composer .form-control:focus-visible {
  border-color: var(--color-focus);
  box-shadow: 0 0 0 3px rgba(21, 90, 138, 0.16);
}
/* Native constraint validation stays authoritative; this only paints the
   rejected control once a submit attempt has actually failed. */
.estimate-composer.was-validated .form-control:invalid {
  border-color: var(--color-error);
  background: var(--color-error-soft);
}
.field-hint { margin: var(--space-xs) 0 0; color: var(--color-muted); font-size: var(--text-xs); }

/* Money: Felix types dollars, the server stores exact cents. */
.money-input { position: relative; display: block; }
.money-prefix {
  position: absolute;
  left: 0.65rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-muted);
  font-size: var(--text-sm);
  pointer-events: none;
}
.money-input .form-control { padding-left: 1.6rem; }

/* ---- Top blocks ---- */
.estimate-composer { display: block; }
.composer-top { display: grid; grid-template-columns: 1fr; gap: var(--space-md); margin-bottom: var(--space-md); }
.composer-bottom { display: grid; grid-template-columns: 1fr; gap: var(--space-md); margin-bottom: var(--space-lg); }
.composer-bottom textarea { min-height: 9rem; resize: vertical; }

/* ---- Sections ---- */
.composer-lines { margin-bottom: var(--space-lg); }
.composer-lines-head { margin-bottom: var(--space-md); }

.estimate-section {
  margin: 0 0 var(--space-md);
  padding: var(--space-md);
  min-width: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(20, 35, 46, 0.04);
}
.estimate-section:last-of-type { margin-bottom: 0; }

.estimate-section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
}
.estimate-section-ident { flex: 1 1 24rem; min-width: 0; }
.estimate-section-number {
  margin: 0 0 var(--space-xs);
  color: var(--color-accent);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.estimate-section-fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11rem;
  gap: var(--space-sm) var(--space-md);
  align-items: end;
}
.estimate-section-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-xs); }

/* ---- Line items: a real table on desktop, stacked cards on phones ---- */
.estimate-lines { margin-top: var(--space-md); }
.estimate-items-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
.estimate-items-table thead th {
  padding: 0 var(--space-xs) var(--space-xs);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
}
.estimate-items-table thead th[data-col="total"] { text-align: right; }
.estimate-items-table tbody td { padding: var(--space-xs) var(--space-xs); vertical-align: middle; }
.estimate-items-table tbody tr + tr td { padding-top: 0; }
.estimate-cell--total { text-align: right; }
.estimate-cell--remove { width: 2.5rem; text-align: right; }
.estimate-line-total {
  display: block;
  padding: 0 0.35rem;
  color: var(--color-heading);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.estimate-line-total.is-unreadable { color: var(--color-muted); font-weight: 400; }

/* Columns per kind of work: hourly labor bills hours x rate, flat service has
   one cost, and a part line is part + unit price (exactly what the printed
   estimate shows). Hidden columns keep their values, so switching kinds of
   work never destroys typed data. These widths are a desktop-table concern
   only; on phones the rows become stacked cards with full-width controls. */
@media (min-width: 48rem) {
  .estimate-section[data-presentation="hourly_labor"] th[data-col="part"],
  .estimate-section[data-presentation="hourly_labor"] td[data-col="part"] { width: 21%; }
  .estimate-section[data-presentation="hourly_labor"] th[data-col="description"],
  .estimate-section[data-presentation="hourly_labor"] td[data-col="description"] { width: 31%; }
  .estimate-section[data-presentation="hourly_labor"] th[data-col="hours"],
  .estimate-section[data-presentation="hourly_labor"] td[data-col="hours"] { width: 10%; }
  .estimate-section[data-presentation="hourly_labor"] th[data-col="rate"],
  .estimate-section[data-presentation="hourly_labor"] td[data-col="rate"] { width: 16%; }
  .estimate-section[data-presentation="hourly_labor"] th[data-col="total"],
  .estimate-section[data-presentation="hourly_labor"] td[data-col="total"] { width: 16%; }

  .estimate-section[data-presentation="flat_service"] th[data-col="part"],
  .estimate-section[data-presentation="flat_service"] td[data-col="part"] { width: 26%; }
  .estimate-section[data-presentation="flat_service"] th[data-col="description"],
  .estimate-section[data-presentation="flat_service"] td[data-col="description"] { width: 36%; }
  .estimate-section[data-presentation="flat_service"] th[data-col="unit_price"],
  .estimate-section[data-presentation="flat_service"] td[data-col="unit_price"] { width: 18%; }
  .estimate-section[data-presentation="flat_service"] th[data-col="total"],
  .estimate-section[data-presentation="flat_service"] td[data-col="total"] { width: 14%; }

  .estimate-section[data-presentation="parts"] th[data-col="part"],
  .estimate-section[data-presentation="parts"] td[data-col="part"] { width: 46%; }
  .estimate-section[data-presentation="parts"] th[data-col="unit_price"],
  .estimate-section[data-presentation="parts"] td[data-col="unit_price"] { width: 50%; }
}

/* An empty section is an intentional state, never blank table chrome. */
.estimate-empty {
  display: none;
  margin: var(--space-sm) 0 0;
  padding: var(--space-md);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius);
  color: var(--color-muted);
  font-size: var(--text-sm);
  text-align: center;
}
.estimate-lines.is-empty .estimate-items-table { display: none; }
.estimate-lines.is-empty .estimate-empty { display: block; }

.estimate-section-foot { margin-top: var(--space-sm); }

.estimate-photo-panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
  min-width: 0;
}
.estimate-photo-panel--item {
  padding: var(--space-sm) 0 0;
  border-top: 1px dashed var(--color-border);
}
.estimate-photo-panel-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-xs) var(--space-sm); min-width: 0; }
.estimate-photo-count { color: var(--color-muted); font-size: var(--text-sm); }
.estimate-photo-actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.estimate-photo-actions .btn { min-height: 2.75rem; }
.estimate-photo-error { flex: 1 1 100%; margin: 0; color: var(--color-error); font-size: var(--text-sm); }
.estimate-line-photo-row td { padding: 0 var(--space-xs) var(--space-sm); }
.estimate-line-photo-row .estimate-photo-panel { color: var(--color-text); }
.estimate-photo-group + .estimate-photo-group { margin-top: var(--space-lg); }
.estimate-photo-group-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-sm); margin-bottom: var(--space-sm); }
.estimate-photo-group-head h3 { margin: 0; font-size: var(--text-base); }
.estimate-photo-group-head span { color: var(--color-muted); font-size: var(--text-sm); }
.estimate-photo-assign { margin-top: var(--space-xs); }
.estimate-photo-assign .form-control { min-height: 2.5rem; font-size: var(--text-sm); }
.photo-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-md);
  min-width: 0;
}
.photo-card {
  min-width: 0;
  margin: 0;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
}
.photo-card > a {
  display: block;
  aspect-ratio: 4 / 3;
  background: var(--color-surface);
}
.photo-card img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.photo-card figcaption {
  padding: var(--space-xs) var(--space-sm);
  color: var(--color-muted);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}
@media (min-width: 40rem) {
  .photo-gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.composer-add-section {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-md);
  margin-top: var(--space-md);
}
.composer-add-hint { margin: 0; color: var(--color-muted); font-size: var(--text-sm); }

/* ---- Summary ---- */
.estimate-summary { align-self: start; }
.estimate-summary-list { margin: 0; }
.estimate-summary-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-xs) 0;
  border-bottom: 1px solid var(--color-border);
}
.estimate-summary-row[hidden] { display: none; }
.estimate-summary-row dt { font-size: var(--text-sm); }
.estimate-summary-row dd {
  margin: 0;
  color: var(--color-heading);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.estimate-summary-grand {
  margin-top: var(--space-xs);
  padding-top: var(--space-sm);
  border-bottom: 0;
  border-top: 2px solid var(--color-ink);
}
.estimate-summary-grand dt { color: var(--color-heading); font-size: var(--text-base); font-weight: 700; }
.estimate-summary-grand dd { font-size: var(--text-xl); font-weight: 700; }
.estimate-summary-note { margin: var(--space-sm) 0 0; color: var(--color-muted); font-size: var(--text-xs); }

/* ---- Actions ---- */
.composer-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
  padding: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.composer-actions-hint { margin: 0; color: var(--color-muted); font-size: var(--text-sm); }
.composer-actions-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-left: auto;
}

/* Finalize is a deliberately different decision from Save Draft. */
.estimate-finalize {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
  margin-top: var(--space-md);
  border-left: 4px solid var(--color-ink);
}
.estimate-finalize-copy { max-width: 42rem; }
.estimate-finalize-form { margin: 0; }

/* The hidden-column rule must outrank the stacked-card display rules. */
.estimate-items-table [data-col][hidden] { display: none; }

/* ---- Estimate detail page bits ---- */
.estimate-section-sub {
  margin: 0 0 var(--space-xs);
  color: var(--color-muted);
  font-size: var(--text-sm);
}
.estimate-detail-table td:last-child,
.estimate-detail-table th:last-child { text-align: right; }
.estimate-detail-table td:last-child span { font-variant-numeric: tabular-nums; font-weight: 600; }
.estimate-pdf-actions { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }

/* Estimate list cards: the list is an information hierarchy, not a dense
   spreadsheet. Keep these admin records explicitly LTR without changing the
   document direction policy for future RTL locales elsewhere. */
.estimate-list-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: var(--space-md);
  margin-top: var(--space-md);
}
.estimate-list-card {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  text-align: left;
  direction: ltr;
}
.estimate-list-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-sm);
}
.estimate-list-number { color: var(--color-heading); font-size: var(--text-lg); font-weight: 700; }
.estimate-list-primary h3 { margin: 0; font-size: var(--text-lg); overflow-wrap: anywhere; }
.estimate-list-primary p { margin: var(--space-xs) 0 0; color: var(--color-muted); overflow-wrap: anywhere; }
.estimate-list-total { margin: 0; color: var(--color-heading); font-size: var(--text-2xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.estimate-list-meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-sm); margin: 0; padding-top: var(--space-sm); border-top: 1px solid var(--color-border); }
.estimate-list-meta dt { color: var(--color-muted); font-size: var(--text-xs); }
.estimate-list-meta dd { margin: 0.15rem 0 0; font-size: var(--text-sm); overflow-wrap: anywhere; }
.estimate-list-open { width: 100%; margin-top: auto; text-align: center; }

/* ---- Composer: tablet (>= 48rem) ---- */
@media (min-width: 48rem) {
  .composer-top { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .form-grid--customer { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--vehicle { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-grid--details { grid-template-columns: 12rem 14rem; }
  .composer-actions-buttons .btn { min-width: 9rem; text-align: center; }
}

/* ---- Composer: desktop (>= 62rem) ---- */
@media (min-width: 62rem) {
  .composer-head { margin-bottom: var(--space-lg); }
  .composer-top { gap: var(--space-lg); }
  .form-grid--vehicle { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .composer-bottom { grid-template-columns: minmax(0, 1fr) 22rem; gap: var(--space-lg); }
  .estimate-section { padding: var(--space-md) var(--space-md) var(--space-md); }
}

/* ---- Composer: phones (< 48rem) ---- */
@media (max-width: 47.99rem) {
  .estimate-list-grid { grid-template-columns: 1fr; gap: var(--space-md); }
  .estimate-list-card { padding: var(--space-md); }
  .estimate-list-card-head { align-items: center; }
  .estimate-list-total { font-size: var(--text-xl); }
  .estimate-list-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .photo-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-sm); }

  .estimate-composer .card,
  .estimate-section { padding: var(--space-md); }
  .composer-top,
  .composer-bottom { gap: var(--space-md); }
  .estimate-composer .form-grid--customer,
  .estimate-composer .form-grid--vehicle,
  .estimate-composer .form-grid--details { grid-template-columns: 1fr; }
  .estimate-composer .form-grid { gap: var(--space-md); }
  .estimate-composer .form-control { min-height: 2.875rem; }

  .estimate-section-head { align-items: stretch; }
  .estimate-section-ident { flex: 1 1 100%; }
  .estimate-section-fields { grid-template-columns: 1fr; }
  .estimate-section-actions { width: 100%; }
  .estimate-section-actions .btn-quiet-danger { margin-left: auto; }

  .estimate-items-table,
  .estimate-items-table thead,
  .estimate-items-table tbody,
  .estimate-items-table tr,
  .estimate-items-table td { display: block; width: 100%; }
  .estimate-items-table thead { display: none; }
  .estimate-items-table tbody tr.estimate-line {
    margin-bottom: var(--space-sm);
    padding: var(--space-sm);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
  }
  .estimate-items-table tbody td { padding: 0; margin-bottom: var(--space-sm); }
  .estimate-items-table tbody tr.estimate-line-photo-row { margin: -0.35rem 0 var(--space-md); padding: 0 var(--space-sm); border: 0; background: transparent; }
  .estimate-items-table tbody tr.estimate-line-photo-row td { padding: 0; }
  .estimate-line-photo-row .estimate-photo-panel { align-items: stretch; }
  .estimate-line-photo-row .estimate-photo-actions { width: 100%; }
  .estimate-line-photo-row .estimate-photo-actions .btn { flex: 1 1 10rem; }
  .estimate-items-table tbody tr.estimate-line td:last-child { margin-bottom: 0; }
  .estimate-items-table tbody tr + tr td { padding-top: 0; }
  .estimate-items-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 0.25rem;
    color: var(--color-muted);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
  }
  .estimate-cell--total { text-align: left; }
  .estimate-cell--remove { width: auto; }
  .estimate-line-total { padding: 0; text-align: left; }
}

@media (max-width: 30rem) {
  .composer-actions-buttons { width: 100%; margin-left: 0; }
  .composer-actions-buttons .btn { flex: 1 1 100%; text-align: center; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }
}

/* ---------------------------------------------------------------------------
   W06 admin data web: the linked-estimate lists on the lead, customer and
   vehicle record pages (lead → customer/vehicle/estimate across the console).
   Plain anchors, no JavaScript; wraps on narrow screens.
   --------------------------------------------------------------------------- */
.admin-linked-estimates {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.admin-linked-estimates li {
  margin: 0;
}

/* ---------------------------------------------------------------------------
   W06-B05: the administrator pipeline overview. Every estimate request,
   grouped by its CURRENT pipeline status. The six active stages render
   together; completed/lost render in a separate, visually subdued section so
   closed work never buries active work. Plain CSS grid, no JavaScript, no
   drag/drop, no filters (W06-B09) and no analytics (W06-P04).
   --------------------------------------------------------------------------- */
.admin-pipeline-section {
  margin-bottom: var(--space-xl);
}

.admin-pipeline-section > h2 {
  margin-bottom: var(--space-md);
}

/* Active work: the primary area. Closed work: separated by a rule, with
   subdued surfaces and heading color so it reads as history rather than as
   actionable work. Neither area is hidden, collapsed, or JS-dependent. */
.admin-pipeline-closed {
  margin-top: var(--space-2xl);
  padding-top: var(--space-lg);
  border-top: 2px solid var(--color-border);
}
.admin-pipeline-closed > h2,
.admin-pipeline-closed .pipeline-group-heading {
  color: var(--color-muted);
}
.admin-pipeline-closed .pipeline-group {
  background: var(--color-bg);
}
.admin-pipeline-closed .pipeline-card {
  background: var(--color-surface);
}

.pipeline-legend {
  font-size: var(--text-xs);
}

.pipeline-section-empty {
  margin-bottom: var(--space-md);
}

.pipeline-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--space-md);
  align-items: start;
}

.pipeline-group {
  min-width: 0;
  padding: var(--space-sm);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.pipeline-group-heading {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-base);
  color: var(--color-heading);
}

.pipeline-group-empty {
  font-size: var(--text-sm);
}

.pipeline-cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pipeline-card {
  min-width: 0;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}

.pipeline-card-link {
  display: block;
  padding: var(--space-xs) var(--space-sm);
  color: inherit;
  text-decoration: none;
}
.pipeline-card-link:hover {
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}
.pipeline-card-link:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.pipeline-card-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
}

.pipeline-card-name {
  font-weight: 600;
  color: var(--color-heading);
  overflow-wrap: anywhere;
}

.pipeline-card-vehicle {
  color: var(--color-text);
  overflow-wrap: anywhere;
}

.pipeline-card-received {
  display: block;
  margin-top: 0.15rem;
  color: var(--color-muted);
  font-size: var(--text-xs);
}

/* W06-B10: the primary action affordance on a card. The whole card link is
   the control (one tap target, no nested interactive elements); this is the
   VISIBLE label that says so, so the main action is never guessed at — on a
   phone it becomes a full-width, 44px-tall row. Decoration only for assistive
   technology: the link's own aria-label already names the action. */
.pipeline-card-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: var(--space-xs);
  color: var(--color-link);
  font-size: var(--text-sm);
  font-weight: 600;
}

.pipeline-card-cta::after {
  content: "›";
  font-weight: 700;
}

/* W06-B08: the one secondary action on a pipeline card — open the EXACT
   lead's existing notes section. Kept visually subordinate to the primary
   card link, and it never renders note text or a note count. */
.pipeline-card-actions {
  margin: 0;
  padding: 0 var(--space-sm) var(--space-xs);
}

.pipeline-card-notes {
  display: inline-block;
  color: var(--color-link);
  font-size: var(--text-xs);
}

.pipeline-card-notes:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* Provenance badges: make the current-record vs historical-intake
   distinction visible on every item (label text is localized; the
   `data-provenance` hook is language-neutral). Never conveyed by color
   alone — the badge text carries the meaning. */
.pipeline-badge {
  display: inline-block;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.pipeline-badge-current {
  color: var(--color-link);
  border-color: var(--color-link);
}
.pipeline-badge-intake {
  color: var(--color-accent-strong);
  border-color: var(--color-accent-soft);
}

/* W06-B09: the administrator's current-stage filter. Plain GET links, not a
   form: choosing a view cannot mutate anything, and the URL stays
   bookmarkable. The selected choice is marked by `aria-current="page"` in the
   markup AND by a filled treatment (weight + fill + border), so the current
   view is never conveyed by color alone and assistive technology announces it
   too. The row wraps, so a long localized stage name can never force the page
   wider than the viewport. */
.pipeline-filters {
  margin: var(--space-md) 0 var(--space-sm);
}

.pipeline-filters-heading {
  margin: 0 0 var(--space-xs);
  color: var(--color-muted);
  font-size: var(--text-xs);
  font-weight: 600;
}

.pipeline-filter-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pipeline-filter {
  display: inline-block;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--text-sm);
  text-decoration: none;
  white-space: nowrap;
}

.pipeline-filter:hover {
  border-color: var(--color-link);
  color: var(--color-link);
}

.pipeline-filter:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.pipeline-filter[aria-current="page"] {
  border-color: var(--color-link);
  background: var(--color-link);
  color: var(--color-on-ink);
  font-weight: 700;
}

/* W06-P05: make the operational Active view easy to spot even while All is
   selected. Closed-stage filters remain available but visually quieter. */
.pipeline-filter[data-pipeline-filter="active"] {
  border-width: 2px;
  border-color: var(--color-link);
  font-weight: 700;
}

.pipeline-filter[data-pipeline-filter="completed"],
.pipeline-filter[data-pipeline-filter="lost"] {
  color: var(--color-muted);
  background: var(--color-bg);
}

/* W06-P04: a compact read-only count snapshot. The numbers are deliberately
   plain text (not faux buttons); workflow stages use the existing localized
   status names, while Completed/Lost remain in a visually distinct closed
   treatment as well as having explicit labels. */
.pipeline-summary {
  margin: var(--space-md) 0;
}

.pipeline-summary > h2 {
  margin: 0 0 var(--space-xs);
  color: var(--color-muted);
  font-size: var(--text-xs);
  font-weight: 600;
}

.pipeline-summary-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.pipeline-summary-item {
  display: flex;
  min-width: 0;
  min-height: 4rem;
  flex-direction: column;
  justify-content: space-between;
  gap: 0.25rem;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}

.pipeline-summary-total,
.pipeline-summary-active-total {
  border-width: 2px;
  font-weight: 700;
}

.pipeline-summary-active-total {
  border-color: var(--color-link);
  background: var(--color-surface);
}

.pipeline-summary-closed {
  border-color: var(--color-accent-soft);
  background: var(--color-bg);
}

.pipeline-summary-label {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: var(--text-xs);
  line-height: 1.25;
}

.pipeline-summary-count {
  font-size: var(--text-lg);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* W06-P05: the ordinary forward transition is a direct, one-tap action on
   Lead detail; exceptional Lost remains behind a labelled disclosure. */
.admin-status-primary {
  min-height: 44px;
  min-width: min(100%, 13rem);
}

.admin-status-more-actions {
  margin-top: var(--space-sm);
  max-width: 22rem;
}

.admin-status-more-actions > summary {
  display: flex;
  min-height: 44px;
  align-items: center;
  width: fit-content;
  color: var(--color-muted);
  cursor: pointer;
  font-size: var(--text-sm);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.admin-status-secondary-choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  padding-top: var(--space-xs);
}

.admin-status-secondary {
  min-height: 44px;
  border-color: var(--color-muted);
  color: var(--color-muted);
}

/* ---------------------------------------------------------------------------
   W06-B10: pipeline responsive / mobile UX.

   Three DELIBERATE layouts from one stylesheet, no JavaScript:

     >= 62rem (992px+)   the accepted W06-B05 board (auto-fill columns)
     48rem .. 61.99rem   a two-column tablet board — a real middle state, not
                         a squeezed desktop board
     <= 47.99rem (<768px) one vertical workflow: stage group, then its cards

   Everything here is scoped to the pipeline page's own classes plus the
   lead-detail status panel (the one surface where a pipeline record's status
   is actually moved). No global selector is touched, so the accepted
   estimate/invoice/photo mobile work is unaffected.
   --------------------------------------------------------------------------- */
@media (min-width: 48rem) and (max-width: 61.99rem) {
  .pipeline-groups {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Touch comfort for every non-desktop width — phones AND tablets are touch
   devices, so the filter choices, the card itself, its primary action, the
   Notes action and the status buttons all reach a practical 44px here. The
   desktop board (>= 62rem) keeps its compact, mouse-oriented sizing. */
@media (max-width: 61.99rem) {
  .pipeline-card-link {
    min-height: 2.75rem;
    padding: var(--space-sm);
  }

  .pipeline-card-row {
    min-width: 0;
  }

  .pipeline-card-cta {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 2.75rem;
    margin-top: var(--space-sm);
    border: 1px solid var(--color-link);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
  }

  /* The Lead Notes action stays subordinate to the card itself, but it is a
     real workflow action, so it becomes a practical 44px target too. */
  .pipeline-card-actions {
    padding: 0 var(--space-sm) var(--space-sm);
  }

  .pipeline-card-notes {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding-inline: 0.25rem;
    font-size: var(--text-sm);
  }

  /* The same ten GET filter choices, wrapped, each at least 44px tall so the
     chosen view stays obvious and tappable even at 320px. */
  .pipeline-filter {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: 0.3rem 0.9rem;
  }

  /* The status control surface a pipeline record links to: the buttons are
     generated by the ONE transition policy as before, and here they stack
     into full-width 44px targets (`.btn` already enforces 2.75rem). */
  .admin-status-choices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
    gap: var(--space-xs);
  }

  .admin-status-choices .btn {
    width: 100%;
  }
}

@media (max-width: 47.99rem) {
  /* One vertical workflow: every stage group owns the full width, so a stage
     never becomes a 6-8 column sliver and the board is never a shrunken
     desktop grid. */
  .pipeline-groups {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-sm);
  }

  .admin-pipeline-section {
    margin-bottom: var(--space-lg);
  }

  .admin-pipeline-section > h2 {
    margin-bottom: var(--space-sm);
  }

  /* Completed/lost stay visually separated, with phone-sized spacing. */
  .admin-pipeline-closed {
    margin-top: var(--space-lg);
    padding-top: var(--space-md);
  }

  .pipeline-group-heading {
    margin-bottom: var(--space-xs);
  }
}

/* ---------------------------------------------------------------------------
   W06-P01: the pipeline QUICK SEARCH control.

   ONE text field reusing the accepted admin directory-search layout
   (W03-B10 / W05-B13: `.admin-leads-controls.admin-directory-search`), so the
   pipeline gets the same compact row on desktop and the same stacked
   field-then-actions column on a phone as the customer and vehicle
   directories — no second search layout is introduced.

   Two deliberate details, nothing else:

     * the input itself reaches the same 44px touch height the Search and
       Clear `.btn` controls already have (the shared field is sized for
       mouse use), so a phone can tap into the field comfortably; and
     * the field never forces the page wider: it is `min-width: 0` below 48rem
       through the existing shared rule, and a long query scrolls inside the
       input instead of stretching the layout.

   No global selector is touched, and the filter row, the board and every
   other admin page are unaffected.
   --------------------------------------------------------------------------- */
.pipeline-search .admin-search-field input[type="search"] {
  min-height: 2.75rem;
}

/* W07-P01: keep the Pipeline's quick-search row lighter than the directory
   forms that share its base classes. The summary and status views provide the
   page structure; a full-width bordered panel here made the controls compete
   with the work list. Keep one comfortable search field and let Advanced
   Filters read as a secondary disclosure. The mobile rules below still stack
   the same controls at full width. */
.pipeline-search {
  max-width: 100%;
  margin-bottom: var(--space-sm);
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.pipeline-search .admin-search-field {
  flex: 0 1 28rem;
  min-width: 0;
}

.pipeline-search .admin-search-field input[type="search"] {
  min-width: 0;
}

/* W06-P02: progressive, server-rendered structured pipeline filters. Keep the
   existing P01 Quick Search row first; the disclosure gives date and exact
   current-record selectors a compact home without a client-side dependency. */
.pipeline-advanced-filters {
  flex: 1 0 100%;
  min-width: 0;
  width: 100%;
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-sm);
}

.pipeline-advanced-filters > summary {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  color: var(--color-heading);
  font-weight: 700;
  cursor: pointer;
}

.pipeline-advanced-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

.pipeline-advanced-field {
  min-width: 0;
}

.pipeline-advanced-field .form-control {
  display: block;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  min-height: 2.75rem;
  font-size: 1rem;
}

.pipeline-advanced-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

@media (max-width: 61.99rem) {
  .pipeline-advanced-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 47.99rem) {
  .pipeline-advanced-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .pipeline-advanced-actions {
    align-items: stretch;
    flex-direction: column;
  }

  .pipeline-advanced-actions .btn {
    width: 100%;
    min-height: 2.75rem;
  }
}

/* ---------------------------------------------------------------------------
   W06-B06: the shared CURRENT-pipeline-status badge vocabulary. The same
   markup (`admin-state-badge` + `data-status="<persisted code>"` + the
   canonical localized label) is used on the administrator lead list and the
   lead detail, so a status is visually recognizable as the same concept
   everywhere. Color is a secondary cue only — the text label always carries
   the meaning, so state is never communicated by color alone. The historical
   intake state keeps the neutral `.admin-state-badge` treatment.
   --------------------------------------------------------------------------- */
.admin-state-badge[data-status] {
  font-weight: 700;
  letter-spacing: 0.02em;
}
.admin-state-badge[data-status="new"] {
  color: var(--color-text);
  border-color: var(--color-border);
}
.admin-state-badge[data-status="contacted"] {
  color: var(--color-link);
  border-color: var(--color-link);
}
.admin-state-badge[data-status="estimate_scheduled"],
.admin-state-badge[data-status="estimate_sent"] {
  color: var(--color-accent-strong);
  border-color: var(--color-accent-soft);
}
.admin-state-badge[data-status="approved"] {
  color: var(--color-accent-strong);
  border-color: var(--color-accent-strong);
}
.admin-state-badge[data-status="in_repair"] {
  color: var(--color-ink);
  border-color: var(--color-ink);
}
.admin-state-badge[data-status="completed"] {
  color: var(--color-muted);
  border-color: var(--color-muted);
}
.admin-state-badge[data-status="lost"] {
  color: var(--color-error);
  border-color: var(--color-error);
}
