/* Suppress slotPop's scale bounce on drawer pills. In the tight
   3-up grid the 1 → 1.08 → 1 bounce makes the card edges and the
   calendar appear to flex left/right whenever the customer clicks
   between slots. The dark fill + ring shadow already give clear
   selection feedback. Specificity (3 classes) edges out
   .slot-btn.selected so this wins on tie-break. */
.slot-btn.slot-btn-drawer.selected {
  animation: none;
}
.slot-btn-drawer.selected .slot-time { color: #FFFFFF; }
.slot-btn-drawer.selected .slot-badge-inline {
  color: var(--selected-ink);
  background: #FFFFFF;
  border-color: rgba(255, 255, 255, 0.5);
}
.cal-drawer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.cal-drawer-back-placeholder { width: 1px; }
.cal-drawer-continue {
  margin-left: auto;
}

/* Dim non-active weeks: only the available (eye-catching, full-opacity)
   cells in non-active weeks fade to 0.32. Unavailable cells across
   all weeks keep their natural ~0.5 grey, so the dim effect doesn't
   produce two competing levels of grey in the same row. */
.cal-grid[data-active-week] .cal-day.cal-available {
  opacity: 0.32;
  transition: opacity 0.32s ease;
}
.cal-grid[data-active-week="0"] .cal-day.cal-available[data-week="0"],
.cal-grid[data-active-week="1"] .cal-day.cal-available[data-week="1"],
.cal-grid[data-active-week="2"] .cal-day.cal-available[data-week="2"],
.cal-grid[data-active-week="3"] .cal-day.cal-available[data-week="3"],
.cal-grid[data-active-week="4"] .cal-day.cal-available[data-week="4"],
.cal-grid[data-active-week="5"] .cal-day.cal-available[data-week="5"] {
  opacity: 1;
}
/* Hover on a dimmed cell partially restores opacity so the user
   knows it's still clickable. */
.cal-grid[data-active-week] .cal-day.cal-available:hover {
  opacity: 0.85;
}

/* Inline skeleton loader (single-column variant): sits below the grid
   skeleton in the centric layout instead of in a separate right
   column. */
.slots-loading-inline {
  margin-top: 24px;
  padding: 16px 0 0;
  border-top: 1px solid var(--border-soft);
  text-align: center;
  color: var(--text-muted);
}

/* When the calendar is showing, hide the legacy bottom .actions row
   on step 2 — the drawer's own Back/Continue take over. The :has()
   check keeps it visible during the address-only / commercial paths
   when #availability isn't rendered. */
.step-panel[data-step="2"]:has(#availability:not([hidden]) .calendar-wrap[data-mode="centric"]) > .actions {
  display: none;
}

/* Mobile tightening — the drawer becomes a stacked tray so long slot
   labels never force the booking card wider than the phone. */
@media (max-width: 600px) {
  .step-card,
  .step-panel,
  .step-panel[data-step="2"],
  .step-panel[data-step="2"] > #availability,
  .calendar-wrap[data-mode="centric"],
  .calendar-wrap[data-mode="centric"] .cal-pane,
  .calendar-wrap[data-mode="centric"] .cal-grid,
  .cal-drawer,
  .cal-drawer-tray {
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
  }
  .step-panel[data-step="2"] > #availability,
  .calendar-wrap[data-mode="centric"],
  .calendar-wrap[data-mode="centric"] .cal-pane,
  .calendar-wrap[data-mode="centric"] .cal-grid,
  .cal-drawer,
  .cal-drawer-tray {
    overflow-x: hidden;
    overflow-x: clip;
  }
  .calendar-wrap[data-mode="centric"],
  .calendar-wrap[data-mode="centric"] .cal-pane,
  .calendar-wrap[data-mode="centric"] .cal-grid {
    width: 100%;
  }
  .booking-page {
    padding-left: 10px;
    padding-right: 10px;
  }
  .step-card {
    width: min(100%, calc(100vw - 20px));
    max-width: calc(100vw - 20px);
    margin-left: auto;
    margin-right: auto;
    border-radius: 20px;
  }
  .step-panel[data-step="2"] {
    overflow-x: hidden;
    padding-top: 22px;
    padding-bottom: 30px;
  }
  .step-panel[data-step="2"] > *:not(#availability) {
    margin-left: 16px;
    margin-right: 16px;
  }
  .step-panel[data-step="2"] > .step2-summary {
    margin-left: 16px;
    margin-right: 16px;
    padding: 10px 12px;
    gap: 10px;
  }
  .step-panel[data-step="2"] > #availability {
    width: 100%;
    overflow: hidden;
  }
  .calendar-wrap[data-mode="centric"] .cal-pane {
    width: 100%;
    /* Top bumped 14 → 24 so the date header isn't crammed under the
       tabs/summary. Centering is inherited from the centric rule above. */
    padding: 24px 10px 18px;
    overflow: hidden;
  }
  .calendar-wrap[data-mode="centric"] .cal-grid {
    width: 100%;
    gap: 2px;
  }
  .cal-header {
    margin-bottom: 4px;
  }
  .cal-title {
    font-size: 1rem;
  }
  .cal-nav {
    width: 30px;
    height: 30px;
  }
  .cal-dow {
    font-size: 0.64rem;
    letter-spacing: 0.12em;
    padding-bottom: 4px;
    overflow: hidden;
  }
  .cal-cell {
    min-height: 40px;
  }
  .cal-day.cal-available::before {
    transform: translate(-50%, calc(-50% + 15px));
  }
  .cal-day.cal-available:hover::before {
    transform: translate(-50%, calc(-50% + 15px)) scale(1.2);
  }
  .cal-day.cal-available.cal-selected::before,
  .cal-day.cal-selected::before {
    width: 44px;
    height: 44px;
    border-radius: 14px;
  }
  .cal-drawer.is-open {
    max-height: 560px;
    margin: 8px 0 12px;
  }
  .cal-drawer-tray {
    width: min(100%, 360px);
    margin: 12px auto 0;
    padding: 20px 16px 22px;
    gap: 14px;
    border-radius: 18px;
  }
  .cal-drawer-slots,
  .cal-drawer-slots[data-slot-count="1"],
  .cal-drawer-slots[data-slot-count="2"] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    justify-content: stretch;
    width: 100%;
  }
  .cal-drawer-slots > *,
  .cal-drawer-slots[data-slot-count="1"] .slot-btn-drawer,
  .cal-drawer-slots[data-slot-count="2"] .slot-btn-drawer,
  .cal-drawer-slots[data-slot-count="3"] > *,
  .cal-drawer-slots:not([data-slot-count]) > * {
    width: 100%;
    max-width: none;
    min-width: 0;
    flex: none;
  }
  .slot-btn-drawer {
    min-height: 58px;
    padding: 15px 12px;
    font-size: 0.98rem;
    border-radius: 13px;
  }
  .slot-time {
    white-space: nowrap;
  }
  .slot-badge-inline {
    /* Smaller badge so it doesn't overflow the compact pill on mobile. */
    font-size: 0.54rem;
    padding: 2px 7px;
    top: -7px;
    left: 10px;
  }
  .cal-drawer-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.18fr);
    gap: 14px;
    align-items: stretch;
    margin-top: 2px;
  }
  .cal-drawer-back-placeholder { display: none; }
  .cal-drawer-continue { margin-left: 0; }
  .cal-drawer-actions .btn {
    width: 100%;
    min-width: 0;
    flex: 1 1 0;
    min-height: 56px;
    padding: 12px 14px;
    font-size: 0.96rem;
    justify-content: center;
  }
}

@media (max-width: 380px) {
  .cal-drawer-tray {
    padding: 18px 14px 20px;
  }
  .cal-drawer-actions {
    grid-template-columns: minmax(0, 1fr);
  }
  .step-panel[data-step="3"] > .actions,
  .step-panel[data-step="4"] > .actions {
    gap: 14px;
  }
  .step-panel[data-step="3"] > .actions .btn,
  .step-panel[data-step="4"] > .actions .btn {
    padding-left: 14px;
    padding-right: 14px;
  }
}

/* (Legacy `.slots-pane` / `.slots-list` / `.slot-btn-block` / `.slots-prompt`
    rules removed — replaced by Centric Expansion drawer that lives inside
    the calendar grid. Drawer markup is `.cal-drawer` + `.slot-btn-drawer`
    further down.) */
.slot-time {
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}
/* Floating EARLIEST badge — overlaps top-left of the slot button.
   Charcoal text + neutral border so it reads as a quiet annotation,
   not a second accent competing with the orange Continue button. */
.slot-badge-inline {
  position: absolute;
  top: -8px;
  left: 12px;
  font-size: 0.58rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--selected-ink);
  background: #fff;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid rgba(39, 41, 54, 0.20);
  z-index: 2;
  transition: color 0.2s, background 0.2s, border-color 0.2s;
}
.slot-btn.selected .slot-badge-inline {
  color: var(--selected-ink);
  background: #fff;
  border-color: rgba(255, 255, 255, 0.5);
}
/* Legacy date-list (still used by tests / fallback) */
.date-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 8px; }
.date-row {
  display: grid;
  grid-template-columns: 200px 1fr;
  align-items: center;
  gap: 16px;
  padding: 14px;
  background: var(--cream);
  border-radius: var(--radius-sm);
  @media (max-width: 600px) { grid-template-columns: 1fr; }
}
.date-label { font-weight: 600; color: var(--navy); }
.slots { display: flex; gap: 8px; flex-wrap: wrap; }
.slot-btn {
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid #E5E7EB;
  background: var(--card);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s, background 0.2s, box-shadow 0.2s;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.slot-btn:hover:not(.selected) {
  /* Soft halo on hover — neutral charcoal, not orange. The user feels
     the response without the page screaming "primary action!" The
     ring (box-shadow) extends slightly beyond the button as a halo. */
  border-color: rgba(39, 41, 54, 0.30);
  background: #FBFAF7;
  color: var(--text);
  box-shadow: 0 0 0 4px rgba(39, 41, 54, 0.04), 0 1px 2px rgba(39, 41, 54, 0.04);
}
.slot-btn:active:not(:disabled) { transform: scale(0.98); }
/* Selected slot — charcoal, not orange. The selected slot is a
   confirmation of choice, not the primary action; the orange
   Continue button below is the call to action. Was a solid orange
   gradient with orange glow — now charcoal-on-charcoal with a
   subtle ring so it reads as "locked in" but not as "click me". */
.slot-btn.selected {
  background: linear-gradient(180deg, var(--selected-ink), var(--selected-ink));
  color: #fff;
  border: 1.5px solid var(--selected-ink);
  box-shadow:
    0 0 0 4px rgba(39, 41, 54, 0.10),
    0 6px 18px rgba(39, 41, 54, 0.16),
    inset 0 1px 0 rgba(255, 255, 255, 0.10);
  animation: slotPop 0.4s cubic-bezier(0.34, 1.4, 0.64, 1);
}
@keyframes slotPop {
  0% { transform: scale(1); }
  40% { transform: scale(1.08); }
  100% { transform: scale(1); }
}

.date-row { animation: rowIn 0.4s ease-out backwards; }
.date-row:nth-child(1) { animation-delay: 0.02s; }
.date-row:nth-child(2) { animation-delay: 0.06s; }
.date-row:nth-child(3) { animation-delay: 0.10s; }
.date-row:nth-child(4) { animation-delay: 0.14s; }
.date-row:nth-child(5) { animation-delay: 0.18s; }
.date-row:nth-child(n+6) { animation-delay: 0.22s; }
@keyframes rowIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ═══ 31. Calendar skeleton ═══ */
/* Calendar-shaped loading state — feels alive while data fetches */
.calendar-loading { animation: fadeIn 0.18s ease-out; }
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.cal-title-skeleton {
  width: 130px;
  height: 16px;
  border-radius: 6px;
  background: rgba(39, 41, 54, 0.06);
  animation: titleBreathe 1.6s ease-in-out infinite;
}
@keyframes titleBreathe {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

/* Skeleton cells — quiet, no orange pulse. Just a soft fade-in that matches
   the look of unavailable date cells, so the swap to the real calendar
   feels like numbers being drawn over the same grid. */
.cal-skeleton-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: skeletonDot 1.6s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
}
.cal-skeleton-cell::before {
  content: '';
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(39, 41, 54, 0.16);
}
@keyframes skeletonDot {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}

.slots-loading {
  display: flex;
  flex-direction: column;
  gap: 10px;
  animation: fadeIn 0.18s ease-out;
}
.slots-loading-label {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--navy);
  margin-bottom: 6px;
  font-variant-numeric: tabular-nums;
}
.ld-dot {
  display: inline-block;
  margin-left: 1px;
  opacity: 0.2;
  animation: dotFade 1.4s ease-in-out infinite;
  animation-delay: var(--delay, 0s);
}
@keyframes dotFade {
  0%, 60%, 100% { opacity: 0.2; }
  30% { opacity: 1; }
}
.slots-skeleton-bar {
  height: 50px;
  border-radius: 12px;
  background: rgba(39, 41, 54, 0.05);
  animation: barEnter 0.4s cubic-bezier(0.18, 0.89, 0.32, 1.28) backwards,
             barWave 1.6s ease-in-out infinite;
  animation-delay: var(--delay, 0s), calc(var(--delay, 0s) + 0.4s);
}
@keyframes barEnter {
  from { opacity: 0; transform: translateX(-8px); }
  to { opacity: 1; transform: translateX(0); }
}
@keyframes barWave {
  0%, 100% { background: rgba(39, 41, 54, 0.05); }
  50% { background: rgba(224, 122, 42, 0.14); }
}

/* Alerts */
/* ═══ 32. Alerts ═══ */
.alert {
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  margin: 12px 0;
  font-size: 0.92rem;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1px solid transparent;
  animation: alertIn 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Step 2's #zone-message uses .alert-warning when the address falls
   outside the service area (or for "online booking not live" etc.).
   Two layouts share this element:
   1. Plain-text message variants (hcp_not_configured, no online
      booking yet, etc.) — get the tucked-in cream banner skin.
   2. Admin cross-loc warning — the inner .cross-loc-warn element
      brings its own card chrome (bg, border, radius, shadow, accent
      stripe). When it's present we strip ALL the outer alert chrome
      via :has() so we don't render two stacked cards.
   The :has() check is safe to leave standalone: any browser that
   doesn't support it just gets the original cream skin, which still
   reads acceptably (just doubled chrome). */
#zone-message.alert.alert-warning {
  margin: 14px auto 0;
  /* Narrower than the booking card so it reads as a tucked-in note,
     not a full-width banner. The hcp_not_configured copy uses an
     explicit <br> to force a two-line layout; this width keeps the
     second line from running too long even without the break. */
  max-width: 520px;
  background: #FAF7F1;
  border: 1px solid rgba(39, 41, 54, 0.10);
  border-left: 3px solid var(--orange, #E07A2A);
  border-radius: 14px;
  color: var(--text);
  padding: 14px 18px;
  font-size: 0.94rem;
  line-height: 1.5;
  text-align: center;
}
#zone-message.alert.alert-warning::before {
  /* Drop the warning glyph inside this skin — the orange accent stripe
     already carries the "heads up" weight, and a triangle on a soft
     cream surface reads as decoration rather than signal. */
  content: none;
}
/* When the alert contains the cross-loc-warn admin warning, strip
   the outer chrome — that block has its own card chrome and the
   stacked layers were reading as a card-in-a-card. Forces left-
   align too since the inner block uses a flex head + body that
   shouldn't inherit the parent's text-align:center. */
#zone-message.alert.alert-warning:has(.cross-loc-warn) {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  max-width: none;
  text-align: left;
  margin: 14px 0 0;
}
.alert::before {
  font-weight: 700;
  flex-shrink: 0;
  font-size: 0.95rem;
  line-height: 1.4;
}
.alert:empty { display: none; }
.alert[hidden] { display: none; }
.alert-error {
  background: var(--error-bg);
  color: var(--error);
  border-color: #FECACA;
}
.alert-error::before { content: '⚠'; }
.alert-warning {
  background: var(--warning-bg);
  color: var(--warning-text);
  border-color: #FCD34D;
}
.alert-warning::before { content: '⚠'; }
.alert-success {
  background: var(--success-bg);
  color: var(--success);
  border-color: #BBF7D0;
}
.alert-success::before { content: '✓'; }
@keyframes alertIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Success */
/* ═══ 33. Step 5 success ═══ */
/* Step 5 (success): the panel inherits flex column + min-height 720
   from .step-panel, but the success content is short. Center it
   vertically inside the card rather than top-aligning + auto-pushing
   the Finish button to the bottom (which would leave the success
   mark/headline floating in the upper portion). */
.step-panel[data-step="5"] {
  justify-content: center;
}
.step-panel[data-step="5"] .actions { margin-top: 28px; }
.success-card {
  text-align: center;
  padding: 24px 8px;
  @media (max-width: 600px) { padding: 24px 4px; }
}
.success-mark {
  width: 84px;
  height: 84px;
  margin: 0 auto 24px;
  position: relative;
}
.success-mark svg { width: 100%; height: 100%; }
.success-mark .ring {
  fill: none;
  stroke: var(--success);
  stroke-width: 4;
  stroke-dasharray: 240;
  stroke-dashoffset: 240;
  animation: drawRing 0.6s cubic-bezier(0.65, 0, 0.45, 1) 0.1s forwards;
}
.success-mark .check {
  fill: none;
  stroke: var(--success);
  stroke-width: 6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 50;
  stroke-dashoffset: 50;
  animation: drawCheck 0.4s cubic-bezier(0.65, 0, 0.45, 1) 0.55s forwards;
}
@keyframes drawRing {
  to { stroke-dashoffset: 0; }
}
@keyframes drawCheck {
  to { stroke-dashoffset: 0; }
}
.success-card h2 {
  color: var(--navy);
  margin-bottom: 12px;
  animation: fadeUp 0.5s ease-out 0.7s backwards;
  @media (max-width: 600px) { font-size: 1.5rem; margin-bottom: 8px; }
}
.success-card p {
  animation: fadeUp 0.5s ease-out 0.85s backwards;
}
/* Confirmation-text callout — sits between the headline summary and
   the recap card. Orange-soft pill skin makes it the most visually
   prominent element on the success page after the green checkmark,
   so customers immediately understand they should also be watching
   for an SMS. Inherits the same fade-up cadence as the headline +
   summary so it lands in sequence. */
.success-confirm-note {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 18px auto 4px;
  padding: 12px 20px;
  background: var(--orange-soft);
  color: var(--orange-dark);
  border: 1px solid rgba(184, 96, 18, 0.18);
  border-radius: 999px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: left;
  max-width: 100%;
  animation: fadeUp 0.5s ease-out 0.95s backwards;
}
.success-confirm-note-icon {
  display: inline-flex;
  flex-shrink: 0;
}
@media (max-width: 600px) {
  .success-confirm-note {
    font-size: 0.95rem;
    padding: 11px 16px;
    gap: 8px;
  }
}
/* Post-booking auto-redirect countdown. Sits just above the Finish button
   on the customer success card when the admin configured a "redirect after
   they book" link. Quiet/secondary (the headline + confirm note are the
   stars); the live second-count is tabular so it doesn't jiggle as it ticks. */
.success-redirect-note {
  margin: 16px auto 0;
  color: var(--ink-soft, #6B7280);
  font-size: 0.95rem;
  line-height: 1.4;
  text-align: center;
  animation: fadeUp 0.5s ease-out 1.1s backwards;
}
.success-redirect-count {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--orange-dark, #B86012);
}
/* CSR-facing reminder on the admin success state. Same anatomy as
   .success-confirm-note (icon + text inline) but a quieter sage
   wash instead of the customer-facing orange pill — this is a
   gentle nudge to the admin, not a status announcement to the
   customer. Sits between the recap and the action row, centered. */
.success-admin-reminder {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 14px auto 4px;
  padding: 10px 16px;
  background: #EDF4EE;
  color: #2F5C42;
  border: 1px solid #CFE2D4;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: left;
  max-width: 100%;
  animation: fadeUp 0.5s ease-out 1.0s backwards;
}
.success-admin-reminder-icon {
  display: inline-flex;
  flex-shrink: 0;
  color: #4BAA78;
}
@media (max-width: 600px) {
  .success-admin-reminder {
    font-size: 0.85rem;
    padding: 9px 14px;
    gap: 8px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .success-admin-reminder { animation: none; }
}
/* Finish button on the success card. The default .actions row sets
   justify-content: space-between (sensible for a Back/Continue pair),
   but here we have a single CTA on a centered card — override to
   center it and give it the same fade-in cadence as the headline. */
.success-actions {
  justify-content: center;
  margin-top: 28px;
}
.success-actions .btn-primary { margin-left: 0; }
.success-finish {
  text-decoration: none; /* it's an <a>, not a <button> */
  animation: fadeUp 0.5s ease-out 1s backwards;
  min-width: 180px;
  /* Mobile: full-width since it's the only CTA on the screen. */
  @media (max-width: 600px) { width: 100%; max-width: 320px; }
}
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Auto-redirect countdown — appears 15s after the success page lands
   and counts down the final 5 seconds before navigating to the
   marketing thank-you URL. Sits under the Finish button as a quiet
   secondary signal; the Stop button lets the customer hold the page.
   Animated entrance so it never just snaps in. */
.success-redirect-countdown {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 14px auto 0;
  padding: 8px 16px;
  font-size: 0.86rem;
  color: var(--text-muted);
  animation: fadeUp 0.32s ease-out both;
}
.success-redirect-countdown[hidden] { display: none !important; }
.success-redirect-text { letter-spacing: 0.01em; }
.success-redirect-seconds {
  display: inline-block;
  min-width: 1ch;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--orange-dark, #B86012);
}
.success-redirect-stop {
  appearance: none;
  background: transparent;
  border: 1px solid rgba(39, 41, 54, 0.18);
  color: var(--ink);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.success-redirect-stop:hover {
  border-color: var(--orange);
  background: #FFF6EC;
  color: var(--orange-dark, #B86012);
}
.success-redirect-stop:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(243, 138, 63, 0.28);
}

/* Auth pages — match the booking page warmth, premium card */
.auth-card .btn { margin-top: 12px; }

/* Admin service-area picker */
.sa-search-wrap {
  position: relative;
  margin-bottom: 14px;
}

#sa-search {
  width: 100%;
  padding: 12px 14px 12px 38px;
  font-size: var(--t-md);
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2386868B' stroke-width='1.6'><circle cx='7' cy='7' r='5'/><path d='M11 11l3 3' stroke-linecap='round'/></svg>");
  background-repeat: no-repeat;
  background-position: 12px center;
  background-size: 16px;
}

#sa-search:focus {
  outline: none;
  border-color: transparent;
  box-shadow: var(--sw-focus-shadow, 0 0 0 1px #F38A3F, 0 6px 13px rgba(243, 138, 63, 0.30));
}

.sa-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 4px 0;
}

.sa-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 12px;
  border-radius: 999px;
  background: var(--orange-soft);
  border: 1px solid rgba(224,122,42,0.22);
  color: var(--text);
  font-size: var(--t-sm);
  font-weight: 600;
}

.sa-tag-icon,
.address-suggestions .sugg-icon {
  width: 14px;
  height: 14px;
  color: var(--orange-dark);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 14px;
}

.sa-tag-icon svg,
.address-suggestions .sugg-icon svg {
  width: 14px;
  height: 14px;
  display: block;
  flex: 0 0 14px;
}

.sa-tag-type {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: 999px;
  background: rgba(255,255,255,0.7);
  color: var(--orange-dark);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.06em;
}

.sa-tag-remove {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: inherit;
}

.sa-tag-remove:hover {
  background: rgba(0,0,0,0.06);
  color: var(--st-danger-fg);
}

.sa-empty { width: 100%; }

/* The keyboard-shortcuts reference card (.kbd-card) was removed
   2026-06-14 along with its markup and the booking-stats strip. The
   shortcuts themselves still work (keydown handlers in booking.js);
   only the on-screen cheat sheet is gone, so its styles were deleted. */

/* ═══════════════════════════════════════════════════════════════
   Animation pass — admin booking polish
   ═══════════════════════════════════════════════════════════════
   A coherent set of micro-interactions that make the admin flow
   feel smoother. Every animation respects prefers-reduced-motion
   by way of the global guard at the bottom of this block. */

/* Auto-fill flash — when applyCustomer or the cross-loc address
   stash dispatches synthetic 'input' events on the form fields,
   each field gets briefly highlighted with an amber wash so the
   admin's eye can follow the cascade of fills. The class is added
   by booking.js right before the value is set, then removed after
   the keyframe ends. */
@keyframes fieldAutofillFlash {
  0%   { background: rgba(243, 138, 63, 0.22); box-shadow: 0 0 0 2px rgba(243, 138, 63, 0.35); }
  60%  { background: rgba(243, 138, 63, 0.10); box-shadow: 0 0 0 1px rgba(243, 138, 63, 0.18); }
  100% { background: transparent; box-shadow: 0 0 0 0 rgba(243, 138, 63, 0); }
}
.field input.is-autofill-flash,
.field textarea.is-autofill-flash,
input.is-autofill-flash,
textarea.is-autofill-flash {
  animation: fieldAutofillFlash 0.85s cubic-bezier(0.2, 0.85, 0.4, 1) forwards;
}

/* Calendar grid stagger — each tech-head + cell fades up after the
   grid renders. delay is set per-element by booking/calendar.js
   via inline style.animationDelay so the cascade reads as one
   sweep across the grid. */
@keyframes gridCellAppear {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.admin-cal-grid .admin-cal-tech-head,
.admin-cal-grid .admin-cal-cell,
.admin-cal-grid .admin-cal-corner,
.admin-cal-grid .admin-cal-time-head {
  animation: gridCellAppear 0.32s cubic-bezier(0.2, 0.85, 0.4, 1) backwards;
}

/* Cell selection pulse — a brief halo ring expands and fades when
   the admin clicks a cell so the selection feels physically clicked
   rather than instantly swapped. Pure CSS via an ::after pseudo. */
@keyframes cellSelectPulse {
  0%   { opacity: 0.45; transform: scale(0.98); }
  60%  { opacity: 0.18; transform: scale(1.08); }
  100% { opacity: 0;    transform: scale(1.14); }
}
.admin-cal-cell.is-pulsing::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 12px;
  background: rgba(243, 138, 63, 0.35);
  pointer-events: none;
  animation: cellSelectPulse 0.55s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.admin-cal-cell { position: relative; }

/* Tag-chip pop-in — newly added pending tags spring into view with
   a small overshoot. .is-popping is added by booking.js when a tag
   is first inserted into the editor, removed after the keyframe. */
@keyframes tagChipPop {
  0%   { opacity: 0; transform: scale(0.6); }
  60%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}
.acc-tag.is-popping {
  animation: tagChipPop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

/* Number ticker — the bento stats (12 jobs on file, $7,292 billed,
   etc.) count up from 0 on first render. JS handles the value
   transition; CSS just gives the digits a slight breathing tabular
   width so the layout doesn't reflow as digits change. */
.acc-stat-num,
.success-admin-recap-row dd {
  font-variant-numeric: tabular-nums;
}

/* Skeleton → real content crossfade — the customer rail's two-phase
   load swaps from pulsing skeleton blocks to the real timeline /
   stats. Without a transition the swap snaps. .acc-section,
   .acc-stat, and .acc-timeline get a quick fade-in when they're
   replaced with real content. */
@keyframes contentFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.acc-section:not(.acc-section--loading),
.acc-stats > .acc-stat:not(.acc-stat--skeleton) {
  animation: contentFadeIn 0.35s ease-out;
}

/* Submit button hover lift — gentle vertical nudge + shadow growth
   so the primary CTA reads as physical. Only fires on hover, not
   focus, so keyboard navigation doesn't get jiggling. */
.admin-flow-submit-row .btn-primary,
#submit-booking {
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}
.admin-flow-submit-row .btn-primary:hover:not(:disabled),
#submit-booking:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(243, 138, 63, 0.30);
}

/* Cross-location warning banner slide-down — the in-calendar
   "address belongs to {other franchise}" banner pops in with a
   subtle drop instead of just appearing. */
@keyframes bannerSlideDown {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.cross-loc-warn {
  animation: bannerSlideDown 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Strip viewport — clips the strip horizontally so the ghost
   overlay can slide off-screen during the week-shift animation
   without overflowing the surrounding calendar card. The strip
   inherits flex-row layout from its existing rules; the viewport
   just provides a positioning context + clipping window. */
.admin-cal-strip-viewport {
  position: relative;
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: stretch;
}
.admin-cal-strip-viewport > .admin-cal-strip {
  flex: 1 1 auto;
  min-width: 0;
}
/* Ghost overlay — absolutely positioned copy of the previous
   strip's cells. Sits on top of the live strip during a week
   swap, slides off-screen, then gets removed. The .admin-cal-strip
   base rules give it the same flex layout so cells line up exactly
   with the live ones during the swap. */
.admin-cal-strip--ghost {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: stretch;
  justify-content: space-between;
}

/* ── Wave 2 — search dropdown, rail enter, timeline stagger,
   doc chip height, address-resolve dots, cross-loc page xition ── */

/* Search dropdown — each typeahead match slides up with a small
   stagger (set inline via animationDelay on each <li>) so the
   results read as a coordinated reveal instead of a flicker dump.
   The container itself doesn't animate (it's the parent dropdown);
   only the rows do. */
@keyframes searchRowSlideUp {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.admin-customer-search-result.is-appearing {
  animation: searchRowSlideUp 0.24s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

/* Customer rail slide-in from left — fires once when basic data
   lands on a fresh customer pick. JS adds .is-entering to the panel
   when basic data first renders; class self-removes after the
   keyframe. Re-renders triggered by history-phase or tag edits do
   NOT re-trigger this (would feel jumpy). */
@keyframes railSlideIn {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: translateX(0); }
}
#admin-customer-context.is-entering {
  animation: railSlideIn 0.42s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Visit timeline rows — stagger fade-up so the history reads as
   "this is the customer's story" instead of a wall of paperwork.
   Per-row animationDelay set by booking.js after innerHTML. */
@keyframes timelineRowAppear {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.acc-timeline:not(.acc-timeline--skeleton) > .acc-tl-visit {
  animation: timelineRowAppear 0.34s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}

/* Doc chip expand/collapse. Earlier passes tried a fancy grid-
   template-rows 1fr/0fr height animation, but that trick only
   constrains the FIRST explicit row — the detail has three children
   (description, line items, open-in-HCP link) so children 2 and 3
   landed in implicit auto-sized rows and took their natural height
   anyway, leaving the card looking permanently half-expanded.
   We removed the animation and went back to the browser default
   show/hide. Snap behavior is fine for this surface — the visual
   weight is in the timeline, not in micro-animations on the chips. */
.acc-tl-doc-caret {
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  display: inline-block;
}
.acc-tl-doc-summary[aria-expanded="true"] .acc-tl-doc-caret {
  transform: rotate(180deg);
}

/* Address-resolving hint dot trail — when setHint() runs with a
   "Resolving address…" / "Calendar loading…" message, the trailing
   ellipsis becomes three animated dots that breathe in sequence.
   JS replaces the literal "…" with a span structure; CSS handles
   the dot animation. */
@keyframes hintDotPulse {
  0%, 80%, 100% { opacity: 0.2; transform: translateY(0); }
  40%           { opacity: 1;   transform: translateY(-1.5px); }
}
.cs-hint-dots {
  display: inline-flex;
  gap: 2px;
  margin-left: 4px;
}
.cs-hint-dots span {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
  animation: hintDotPulse 1.2s ease-in-out infinite;
}
.cs-hint-dots span:nth-child(2) { animation-delay: 0.15s; }
.cs-hint-dots span:nth-child(3) { animation-delay: 0.30s; }

/* Page transition on cross-location nav — body fades to a quiet
   cream wash before navigation, then fades back in on the new
   page's load. Class toggled by JS in two places: before
   window.location nav (.is-page-leaving) and on initial DOM ready
   (.is-page-entering, removed after the keyframe). */
@keyframes pageFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes pageFadeOut {
  from { opacity: 1; }
  to   { opacity: 0; }
}
body.is-page-entering {
  animation: pageFadeIn 0.30s ease-out;
}
body.is-page-leaving {
  animation: pageFadeOut 0.22s ease-in forwards;
  pointer-events: none;
}

/* Global reduced-motion override — kill every animation in this
   block when the user has prefers-reduced-motion: reduce set. The
   underlying state (selected, pending, etc.) still updates, just
   without the motion. */
@media (prefers-reduced-motion: reduce) {
  .field input.is-autofill-flash,
  .field textarea.is-autofill-flash,
  input.is-autofill-flash,
  textarea.is-autofill-flash,
  .admin-cal-grid .admin-cal-tech-head,
  .admin-cal-grid .admin-cal-cell,
  .admin-cal-grid .admin-cal-corner,
  .admin-cal-grid .admin-cal-time-head,
  .admin-cal-cell.is-pulsing::after,
  .acc-tag.is-popping,
  .acc-section:not(.acc-section--loading),
  .acc-stats > .acc-stat:not(.acc-stat--skeleton),
  .cross-loc-warn,
  .admin-customer-search-result.is-appearing,
  .customer-search-result.is-appearing,
  #admin-customer-context.is-entering,
  .acc-timeline:not(.acc-timeline--skeleton) > .acc-tl-visit,
  .cs-hint-dots span,
  body.is-page-entering,
  body.is-page-leaving {
    animation: none !important;
  }
  .admin-flow-submit-row .btn-primary:hover:not(:disabled),
  #submit-booking:hover:not(:disabled) {
    transform: none;
  }
}
