/*
 * RideXpress Design System — Patterns v1
 *
 * Compositions of two or more components.css primitives into a
 * recurring, reusable unit -- per docs/engineering/
 * FRONTEND_ARCHITECTURE.md section 3. A page template should rarely
 * need a new one-off CSS rule; if a rule is needed more than twice,
 * promote it here instead of repeating it (same duplication-avoidance
 * discipline as docs/engineering/FOUNDATION_COMPLETION_REPORT.md's
 * backend consolidation program).
 *
 * Load order: tokens.css, components.css, then this file.
 *
 * v1 scope: Portal nav shell (sidebar + topbar + mobile collapse).
 * Open/close state for the mobile nav is wired with Alpine.js, not CSS
 * classes toggled server-side -- see the shell template's x-data usage.
 */

/* ============================== Portal shell ============================== */
.rx-shell {
  display: flex;
  min-height: 100vh;
  background: var(--rx-color-surface-alt);
}

.rx-shell__sidebar {
  width: 260px;
  flex-shrink: 0;
  background: var(--rx-color-dark-surface);
  color: var(--rx-color-text-on-dark);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  z-index: var(--rx-z-sticky);
  transition: transform var(--rx-motion-base) var(--rx-ease-decelerate);
}
.rx-shell__sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--rx-space-3);
  padding: var(--rx-space-5) var(--rx-space-5);
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-lg);
  color: var(--rx-color-white);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.rx-shell__sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: var(--rx-space-1);
  padding: var(--rx-space-4);
  flex: 1;
}
/* Sidebar sits on a dark surface, so its rx-nav-item needs the on-dark
   text tokens rather than the default (light-surface) muted tone. */
.rx-shell__sidebar-nav .rx-nav-item {
  color: var(--rx-color-text-on-dark-muted);
}
.rx-shell__sidebar-nav .rx-nav-item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--rx-color-text-on-dark);
}
.rx-shell__sidebar-nav .rx-nav-item.is-active {
  background: var(--rx-color-brand);
  color: var(--rx-color-ink);
}
.rx-shell__sidebar-group-label {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-xs);
  font-weight: var(--rx-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--rx-color-text-on-dark-muted);
  padding: var(--rx-space-4) var(--rx-space-4) var(--rx-space-2);
}

.rx-shell__main {
  flex: 1;
  min-width: 0; /* prevents flex overflow from wide tables/content */
  display: flex;
  flex-direction: column;
}

.rx-shell__topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-4);
  padding: var(--rx-space-4) var(--rx-space-6);
  background: var(--rx-color-surface);
  border-bottom: 1px solid var(--rx-color-border);
  position: sticky;
  top: 0;
  z-index: var(--rx-z-sticky);
}
.rx-shell__topbar-toggle {
  display: none; /* shown only below --rx-bp-md, see media query */
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--rx-space-2);
  color: var(--rx-color-text);
  /* 44px is the WCAG 2.5.5 / Apple HIG minimum touch target -- this
     is the sole trigger for the entire mobile sidebar, so it must be
     reliably tappable (docs/engineering/MOBILE_RESPONSIVENESS_REVIEW.md
     finding 1: the glyph+padding alone computed to ~32-35px). */
  min-width: 44px;
  min-height: 44px;
  font-size: var(--rx-text-lg);
}

.rx-shell__content {
  padding: var(--rx-space-6);
  flex: 1;
}

/* ---------- Topbar search ---------- */
.rx-shell__search {
  flex: 1;
  max-width: 360px;
  margin: 0 var(--rx-space-4);
}
.rx-shell__search .rx-input {
  padding-top: 8px;
  padding-bottom: 8px;
}
@media (max-width: 1023px) { /* == --rx-bp-lg - 1px: staff portals are desktop-first, see FRONTEND_ARCHITECTURE.md section 4 */
  .rx-shell__search { display: none; }
}

/* ---------- Topbar icon buttons (theme toggle, notifications) ---------- */
.rx-shell__icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--rx-radius-md);
  border: 1px solid transparent;
  background: none;
  color: var(--rx-color-text-muted);
  font-size: var(--rx-text-base);
  cursor: pointer;
  transition: background-color var(--rx-motion-instant) var(--rx-ease-standard), color var(--rx-motion-instant) var(--rx-ease-standard);
}
.rx-shell__icon-btn:hover, .rx-shell__icon-btn.is-open {
  background: var(--rx-color-surface-sunken);
  color: var(--rx-color-text);
}
.rx-shell__icon-btn-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  font-size: 10px;
  line-height: 16px;
}

/* ---------- Notification panel ---------- */
.rx-shell__notif-panel {
  width: 320px;
  max-height: 400px;
  overflow-y: auto;
  padding: var(--rx-space-2);
}
.rx-shell__notif-item {
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  white-space: normal;
}
.rx-shell__notif-body {
  display: block;
  font-weight: var(--rx-weight-regular);
  color: var(--rx-color-text-muted);
  font-size: var(--rx-text-xs);
}
.rx-shell__notif-when {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
}

/* ---------- Global loading progress bar ---------- */
.rx-shell__progress {
  height: 3px;
  background: transparent;
}
.rx-shell__progress.is-active {
  background: linear-gradient(90deg, var(--rx-color-brand) 0%, var(--rx-color-brand-strong) 50%, var(--rx-color-brand) 100%);
  background-size: 200% 100%;
  animation: rx-progress-sweep 1.2s ease-in-out infinite;
}
@keyframes rx-progress-sweep {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ---------- Mobile collapse (< 768px == --rx-bp-md) ---------- */
@media (max-width: 767px) {
  .rx-shell__sidebar {
    position: fixed;
    left: 0;
    top: 0;
    transform: translateX(-100%);
    box-shadow: var(--rx-shadow-lg);
  }
  .rx-shell__sidebar.is-open {
    transform: translateX(0);
  }
  .rx-shell__topbar-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  /* Pages with page_actions buttons (CRUD list/detail) arithmetically
     overflow a 375px viewport without this -- the topbar has no wrap
     behavior otherwise (docs/engineering/MOBILE_RESPONSIVENESS_REVIEW.md
     finding 2). Actions wrap to their own row rather than clipping. */
  .rx-shell__topbar {
    flex-wrap: wrap;
    row-gap: var(--rx-space-2);
  }
  .rx-shell__content {
    padding: var(--rx-space-4);
  }
  /* Backdrop shown behind the open mobile sidebar; markup-provided,
     visibility toggled the same way as .rx-modal-backdrop. */
  .rx-shell__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 20, 25, 0.55);
    z-index: calc(var(--rx-z-sticky) - 1);
  }
}

/* ============================== Page header ============================== */
.rx-page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--rx-space-4);
  flex-wrap: wrap;
  margin-bottom: var(--rx-space-6);
}
.rx-page-header__title {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-2xl);
  color: var(--rx-color-text);
  margin: var(--rx-space-2) 0 0;
}
.rx-page-header__actions {
  display: flex;
  gap: var(--rx-space-3);
  flex-wrap: wrap;
}

/* ============================== Stat card ============================== */
.rx-stat-card {
  background: var(--rx-color-surface);
  border: 1px solid var(--rx-color-border);
  border-radius: var(--rx-radius-lg);
  box-shadow: var(--rx-shadow-sm);
  padding: var(--rx-space-5);
  display: flex;
  flex-direction: column;
  gap: var(--rx-space-2);
}
.rx-stat-card__label {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
}
.rx-stat-card__value {
  font-family: var(--rx-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--rx-text-3xl);
  font-weight: var(--rx-weight-semibold);
  color: var(--rx-color-text);
}
.rx-stat-card__trend {
  display: inline-flex;
  align-items: center;
  gap: var(--rx-space-1);
  font-size: var(--rx-text-xs);
  font-weight: var(--rx-weight-medium);
}
.rx-stat-card__trend--up { color: var(--rx-color-success); }
.rx-stat-card__trend--down { color: var(--rx-color-danger); }

/* ============================== Data table toolbar ============================== */
.rx-table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-4);
  flex-wrap: wrap;
  margin-bottom: var(--rx-space-4);
}
.rx-table-toolbar__filters {
  display: flex;
  align-items: center;
  gap: var(--rx-space-3);
  flex-wrap: wrap;
}
.rx-table-toolbar__search {
  min-width: 240px;
}

/* ============================== CRUD list loading state ============================== */
/* Stripe-style skeleton overlay for the generic CRUD list's search-driven
   HTMX refetch (taxipro/crud_views.py CrudListView + crud/list.html,
   templates/design_system/crud/_list_results.html) -- closes the gap
   named in MOTION_LANGUAGE.md 2.5 ("skeleton primitive exists, not yet
   wired into a real HTMX loading state"). The skeleton mirrors the
   .rx-card/.rx-table shell it covers (same column count as the real
   table) rather than a generic spinner, per Stripe's "shape of what's
   coming" pattern cited in that doc's research section.

   Positioned as an inset overlay over the results region (not swapped
   in as a layout replacement), so the previous results stay in the DOM
   and the overlay just fades over them via the existing .htmx-indicator
   opacity convention (components.css) -- avoids a blank flash between
   the old and new result sets while the request is in flight. */
.rx-crud-list__body { position: relative; }
.rx-crud-list__skeleton {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none; /* never intercepts clicks, active or not */
  background: var(--rx-color-surface-alt);
  /* min-height, not a height override: `inset:0` still stretches this box
     to match .rx-crud-list__body's real-content height for the common
     case, so a long previous result set stays fully covered by the
     overlay during the refetch. min-height only takes over when that
     stretched height would be shorter than the skeleton's own content --
     e.g. a search that had just narrowed to 1-2 real rows -- which
     previously let the skeleton's 5-row table silently render past the
     bottom of its own forced (too-short) box
     (MOBILE_RESPONSIVENESS_REVIEW_2.md finding 6). ~260px = count line
     (space-3 skeleton height + space-2 margin) + flush card wrapping a
     5-row table (~44px header row + 5 * ~37px body rows, from this
     file's own td/th padding and .rx-skeleton--text height) -- an
     approximation like the fixed 5-row count itself, not a
     pixel-measured value. */
  min-height: 260px;
}
.rx-crud-list__count {
  color: var(--rx-color-text-muted);
  font-size: var(--rx-text-sm);
  margin-bottom: var(--rx-space-2);
}

/* ============================== Staggered list-item reveal ============================== */
/* Material's list/grid entrance choreography: items in a freshly-
   rendered list fade/slide in with a small incremental per-item delay
   instead of popping in as one flat block -- reads as a *sequence*
   rendering, not a single blob of content appearing at once. Applied to
   the CRUD list's table rows (.rx-table--stagger, on the table in
   crud/_list_results.html) and the recent-activity feed pattern
   (.rx-activity-feed__item -- used in preview.html today; ready for the
   first real dashboard feed that adopts the pattern).

   Capped at the first 10 items, and the per-item delay step is derived
   from --rx-motion-fast (150ms / 5 = 30ms) rather than an invented
   number: 10 x 30ms = 270ms max stagger delay, so the whole cascade
   (last item's delay + its own fade-in) finishes in ~420ms -- subtle,
   not a light show. Rows past the 10th share that same 270ms delay via
   the base rule below instead of restarting at 0ms or getting no delay
   at all, so a 200-row table never produces a multi-second cascade AND
   the un-staggered tail doesn't read as a separate, disconnected pop-in
   from the staggered head -- everything settles together. */
.rx-stagger-item,
.rx-table--stagger tbody tr,
.rx-activity-feed__item {
  animation: rx-stagger-in var(--rx-motion-fast) var(--rx-ease-decelerate) both;
  animation-delay: calc(var(--rx-motion-fast) / 5 * 9);
}
.rx-stagger-item:nth-child(1), .rx-table--stagger tbody tr:nth-child(1), .rx-activity-feed__item:nth-child(1) { animation-delay: 0ms; }
.rx-stagger-item:nth-child(2), .rx-table--stagger tbody tr:nth-child(2), .rx-activity-feed__item:nth-child(2) { animation-delay: calc(var(--rx-motion-fast) / 5 * 1); }
.rx-stagger-item:nth-child(3), .rx-table--stagger tbody tr:nth-child(3), .rx-activity-feed__item:nth-child(3) { animation-delay: calc(var(--rx-motion-fast) / 5 * 2); }
.rx-stagger-item:nth-child(4), .rx-table--stagger tbody tr:nth-child(4), .rx-activity-feed__item:nth-child(4) { animation-delay: calc(var(--rx-motion-fast) / 5 * 3); }
.rx-stagger-item:nth-child(5), .rx-table--stagger tbody tr:nth-child(5), .rx-activity-feed__item:nth-child(5) { animation-delay: calc(var(--rx-motion-fast) / 5 * 4); }
.rx-stagger-item:nth-child(6), .rx-table--stagger tbody tr:nth-child(6), .rx-activity-feed__item:nth-child(6) { animation-delay: calc(var(--rx-motion-fast) / 5 * 5); }
.rx-stagger-item:nth-child(7), .rx-table--stagger tbody tr:nth-child(7), .rx-activity-feed__item:nth-child(7) { animation-delay: calc(var(--rx-motion-fast) / 5 * 6); }
.rx-stagger-item:nth-child(8), .rx-table--stagger tbody tr:nth-child(8), .rx-activity-feed__item:nth-child(8) { animation-delay: calc(var(--rx-motion-fast) / 5 * 7); }
.rx-stagger-item:nth-child(9), .rx-table--stagger tbody tr:nth-child(9), .rx-activity-feed__item:nth-child(9) { animation-delay: calc(var(--rx-motion-fast) / 5 * 8); }
.rx-stagger-item:nth-child(10), .rx-table--stagger tbody tr:nth-child(10), .rx-activity-feed__item:nth-child(10) { animation-delay: calc(var(--rx-motion-fast) / 5 * 9); }
@keyframes rx-stagger-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

/* ============================== Form section ============================== */
.rx-form-section {
  padding-bottom: var(--rx-space-6);
  margin-bottom: var(--rx-space-6);
  border-bottom: 1px solid var(--rx-color-border);
}
.rx-form-section:last-of-type {
  border-bottom: none;
  margin-bottom: 0;
}
.rx-form-section__title {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-lg);
  color: var(--rx-color-text);
  margin: 0 0 var(--rx-space-1);
}
.rx-form-section__description {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
  margin: 0 0 var(--rx-space-4);
}
.rx-form-section__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--rx-space-4);
}

/* ============================== Info cards (booking/vehicle/driver/customer) ==============================
 * Shared base for the 4 static "info card" patterns below -- all four
 * repeated components.css's .rx-card primitive (background/border/
 * border-radius/box-shadow) plus an identical --rx-space-5 padding
 * verbatim, violating this file's own "promote after 2 uses" rule
 * (DEAD_CODE_AUDIT.md finding 3). .rx-fare-card (further down) is
 * legitimately excluded -- it's an interactive selectable button (2px
 * border, no shadow, hover/selected states), not a static info card.
 */
.rx-booking-card,
.rx-vehicle-card,
.rx-driver-card,
.rx-customer-card {
  background: var(--rx-color-surface);
  border: 1px solid var(--rx-color-border);
  border-radius: var(--rx-radius-lg);
  box-shadow: var(--rx-shadow-sm);
  padding: var(--rx-space-5);
}
/* Shared by driver/customer cards -- byte-identical name style, meta
   style differing only in bottom margin (kept as a modifier below). */
.rx-driver-card__name,
.rx-customer-card__name {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-lg);
  color: var(--rx-color-text);
  margin: 0;
}
.rx-driver-card__meta,
.rx-customer-card__meta {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
}
.rx-driver-card__meta { margin: 2px 0 var(--rx-space-2); }
.rx-customer-card__meta { margin: 2px 0 0; }

/* ============================== Booking summary card ============================== */
.rx-booking-card__route {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
  margin: 2px 0 var(--rx-space-3);
}
.rx-booking-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rx-space-4);
  margin-top: var(--rx-space-4);
  padding-top: var(--rx-space-4);
  border-top: 1px solid var(--rx-color-border);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
}
.rx-booking-card__meta strong {
  color: var(--rx-color-text);
  font-weight: var(--rx-weight-medium);
}

/* ============================== Vehicle card ============================== */
.rx-vehicle-card {
  display: flex;
  flex-direction: column;
  gap: var(--rx-space-3);
}
.rx-vehicle-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-3);
}
.rx-vehicle-card__number {
  font-family: var(--rx-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--rx-text-lg);
  font-weight: var(--rx-weight-semibold);
  color: var(--rx-color-text);
}
.rx-vehicle-card__meta {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
}
.rx-vehicle-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: var(--rx-space-3);
  border-top: 1px solid var(--rx-color-border);
  font-size: var(--rx-text-sm);
}

/* ============================== Driver card ============================== */
.rx-driver-card {
  display: flex;
  align-items: flex-start;
  gap: var(--rx-space-4);
}
.rx-driver-card__body { flex: 1; min-width: 0; }
.rx-driver-card__badges {
  display: flex;
  gap: var(--rx-space-2);
  flex-wrap: wrap;
}

/* ============================== Customer card ============================== */
.rx-customer-card {
  display: flex;
  align-items: center;
  gap: var(--rx-space-4);
}
.rx-customer-card__body { flex: 1; min-width: 0; }
.rx-customer-card__stat {
  text-align: right;
  flex-shrink: 0;
}
.rx-customer-card__stat-value {
  display: block;
  font-family: var(--rx-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--rx-text-xl);
  font-weight: var(--rx-weight-semibold);
  color: var(--rx-color-text);
}
.rx-customer-card__stat-label {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
}

/* ============================== Timeline ============================== */
/* Vertical event history -- ride status changes, audit trail entries.
   Semantic status color drives the dot; the connecting line is purely
   structural (border-color), same "semantic color separate from
   accent" rule as everywhere else in this system. */
.rx-timeline {
  display: flex;
  flex-direction: column;
}
.rx-timeline__item {
  position: relative;
  padding-left: var(--rx-space-8);
  padding-bottom: var(--rx-space-6);
}
.rx-timeline__item:last-child { padding-bottom: 0; }
.rx-timeline__item::before {
  /* connecting line */
  content: '';
  position: absolute;
  left: 5px;
  top: 18px;
  bottom: 0;
  width: 2px;
  background: var(--rx-color-border);
}
.rx-timeline__item:last-child::before { display: none; }
.rx-timeline__dot {
  position: absolute;
  left: 0;
  top: 4px;
  width: 12px;
  height: 12px;
  border-radius: var(--rx-radius-full);
  background: var(--rx-color-border);
  border: 2px solid var(--rx-color-surface);
  box-shadow: 0 0 0 2px var(--rx-color-border);
}
.rx-timeline__dot--success { background: var(--rx-color-success); box-shadow: 0 0 0 2px var(--rx-color-success); }
.rx-timeline__dot--info { background: var(--rx-color-info); box-shadow: 0 0 0 2px var(--rx-color-info); }
.rx-timeline__dot--danger { background: var(--rx-color-danger); box-shadow: 0 0 0 2px var(--rx-color-danger); }
.rx-timeline__title {
  font-size: var(--rx-text-sm);
  font-weight: var(--rx-weight-medium);
  color: var(--rx-color-text);
  margin: 0;
}
.rx-timeline__meta {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
  margin-top: 2px;
}

/* ============================== Recent activity feed ============================== */
/* Page-embedded activity list (dashboard "recent activity" panel) --
   distinct from the topbar notification panel (portal_base.html),
   which is a dropdown, not page content. */
.rx-activity-feed {
  display: flex;
  flex-direction: column;
}
.rx-activity-feed__item {
  display: flex;
  gap: var(--rx-space-3);
  padding: var(--rx-space-3) 0;
  border-bottom: 1px solid var(--rx-color-border);
}
.rx-activity-feed__item:last-child { border-bottom: none; }
.rx-activity-feed__body { flex: 1; min-width: 0; }
.rx-activity-feed__text {
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text);
  margin: 0;
}
.rx-activity-feed__text strong { font-weight: var(--rx-weight-semibold); }
.rx-activity-feed__when {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
  margin-top: 2px;
}

/* ============================== Date range ============================== */
/* Two native <input type="date"> fields rather than a custom calendar
   widget -- fully functional/accessible/keyboard-operable with zero
   JS and zero new dependency, consistent with this project's "no
   build step" constraint (FRONTEND_ARCHITECTURE.md section 1). A
   richer JS calendar can replace this later if the product genuinely
   needs range-preview/shortcut-buttons UX that native inputs can't do
   -- not built preemptively here. */
.rx-date-range {
  display: flex;
  align-items: center;
  gap: var(--rx-space-2);
}
.rx-date-range .rx-input { width: auto; }
.rx-date-range__sep {
  color: var(--rx-color-text-muted);
  font-size: var(--rx-text-sm);
}

/* ============================== Auth shell (pre-login pages) ============================== */
.rx-auth-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--rx-space-8);
  padding: var(--rx-space-6);
  background: var(--rx-color-dark-surface);
}
.rx-auth-shell__brand {
  text-align: center;
  color: var(--rx-color-text-on-dark);
}
.rx-auth-shell__brand-mark {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-3xl);
  color: var(--rx-color-white);
}
.rx-auth-shell__brand-tagline {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-on-dark-muted);
  margin: var(--rx-space-2) 0 0;
}
.rx-auth-card {
  background: var(--rx-color-surface);
  border-radius: var(--rx-radius-lg);
  box-shadow: var(--rx-shadow-lg);
  padding: var(--rx-space-8);
  width: 100%;
  max-width: 400px;
}
.rx-auth-card__title {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-2xl);
  color: var(--rx-color-text);
  margin: 0 0 var(--rx-space-1);
}
.rx-auth-card__subtitle {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
  margin: 0 0 var(--rx-space-6);
}
.rx-auth-card__footer {
  margin-top: var(--rx-space-6);
  padding-top: var(--rx-space-4);
  border-top: 1px solid var(--rx-color-border);
  text-align: center;
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
}
.rx-auth-card__footer a { color: var(--rx-color-brand-strong); text-decoration: none; }
.rx-auth-card__footer a:hover { text-decoration: underline; }

/* ============================== Booking wizard ==============================
 * docs/engineering/BOOKING_UX_RESEARCH.md's prioritized build list, §7.
 * Three-step flow (Route+Schedule / Vehicle / Confirm) over the existing
 * customerPricingDetails / cusvalidate-coupon / customer_addbooking
 * endpoints -- presentation-only, no new backend contract.
 */

/* ---------- Stepper ---------- */
.rx-stepper {
  display: flex;
  align-items: center;
  gap: var(--rx-space-2);
  list-style: none;
  margin: 0 0 var(--rx-space-6);
  padding: 0;
}
.rx-stepper__step {
  display: flex;
  align-items: center;
  gap: var(--rx-space-2);
  color: var(--rx-color-text-muted);
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
}
.rx-stepper__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--rx-radius-full);
  border: 2px solid var(--rx-color-border);
  color: var(--rx-color-text-muted);
  font-weight: var(--rx-weight-semibold);
  font-size: var(--rx-text-xs);
  flex-shrink: 0;
  background: var(--rx-color-surface);
  transition: background-color var(--rx-motion-fast) var(--rx-ease-standard),
              border-color var(--rx-motion-fast) var(--rx-ease-standard),
              color var(--rx-motion-fast) var(--rx-ease-standard);
}
.rx-stepper__step.is-active .rx-stepper__dot {
  border-color: var(--rx-color-brand);
  background: var(--rx-color-brand);
  color: var(--rx-color-ink);
}
.rx-stepper__step.is-complete .rx-stepper__dot {
  border-color: var(--rx-color-success);
  background: var(--rx-color-success);
  color: var(--rx-color-white);
}
.rx-stepper__step.is-active .rx-stepper__label { color: var(--rx-color-text); font-weight: var(--rx-weight-medium); }
.rx-stepper__step.is-complete .rx-stepper__label { color: var(--rx-color-text); }
.rx-stepper__connector {
  width: 32px;
  height: 2px;
  background: var(--rx-color-border);
  flex-shrink: 0;
}
.rx-stepper__step.is-complete + .rx-stepper__connector { background: var(--rx-color-success); }
@media (max-width: 479px) { /* == --rx-bp-sm - 1px */
  .rx-stepper__label { display: none; }
  .rx-stepper__connector { width: 20px; }
}

/* ---------- Persistent route summary (the shared-element between steps) ---------- */
.rx-route-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-3);
  background: var(--rx-color-surface);
  border: 1px solid var(--rx-color-border);
  border-radius: var(--rx-radius-lg);
  padding: var(--rx-space-4);
  margin-bottom: var(--rx-space-5);
}
.rx-route-summary__route {
  font-family: var(--rx-font-body);
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text);
  min-width: 0;
}
.rx-route-summary__route strong { font-weight: var(--rx-weight-medium); }
.rx-route-summary__meta {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
  margin-top: 2px;
}
.rx-route-summary__edit {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--rx-color-brand-strong);
  font-size: var(--rx-text-sm);
  font-weight: var(--rx-weight-medium);
  cursor: pointer;
  padding: var(--rx-space-2) var(--rx-space-3);
  min-height: 44px;
}

/* ---------- Fare card (selectable vehicle/category option, step 2) ----------
 * A <div>, not a <button>: it contains two independent actions (select
 * vehicle, toggle fare breakdown) plus a <dl>, and interactive content
 * must not nest inside a <button> -- browsers silently split a nested
 * button out of its parent at parse time, which broke the card's visual
 * grouping (MOBILE_RESPONSIVENESS_REVIEW_2.md finding 1, confirmed live
 * in Chrome). .rx-fare-card__select below is the actual clickable
 * "pick this vehicle" control; .rx-fare-card__toggle is its own sibling
 * button, not nested inside it.
 */
.rx-fare-card {
  background: var(--rx-color-surface);
  border: 2px solid var(--rx-color-border);
  border-radius: var(--rx-radius-lg);
  transition: border-color var(--rx-motion-fast) var(--rx-ease-standard),
              box-shadow var(--rx-motion-fast) var(--rx-ease-standard);
}
.rx-fare-card:hover { border-color: var(--rx-color-brand); }
.rx-fare-card.is-selected {
  border-color: var(--rx-color-brand);
  box-shadow: 0 0 0 3px var(--rx-color-brand-soft);
}
.rx-fare-card__select {
  display: block;
  width: 100%;
  padding: var(--rx-space-4);
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
}
.rx-fare-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-3);
}
.rx-fare-card__identity {
  display: flex;
  align-items: center;
  gap: var(--rx-space-3);
  min-width: 0;
}
.rx-fare-card__image {
  width: 48px;
  height: 48px;
  border-radius: var(--rx-radius-md);
  object-fit: cover;
  background: var(--rx-color-surface-sunken);
  flex-shrink: 0;
}
.rx-fare-card__name {
  font-family: var(--rx-font-display);
  font-size: var(--rx-text-base);
  color: var(--rx-color-text);
  margin: 0;
}
.rx-fare-card__seats {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
  margin-top: 2px;
}
.rx-fare-card__price {
  font-family: var(--rx-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--rx-text-lg);
  font-weight: var(--rx-weight-semibold);
  color: var(--rx-color-text);
  flex-shrink: 0;
  text-align: right;
}
.rx-fare-card__toggle {
  /* No top margin -- .rx-fare-card__select above already carries its own
     bottom padding, so the border-top divider sits directly under it. */
  padding: var(--rx-space-3) var(--rx-space-4) 0;
  border-top: 1px solid var(--rx-color-border);
  font-size: var(--rx-text-xs);
  color: var(--rx-color-brand-strong);
  background: none;
  border-left: none;
  border-right: none;
  border-bottom: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
  /* Every vehicle option has one -- the 2nd-most-tapped control on step 2
     after the card itself, so it gets the same 44px baseline as the
     route-summary edit button and pill-radio labels on this same page
     (MOBILE_RESPONSIVENESS_REVIEW_2.md finding 3), not the smaller
     32px this previously had. */
  min-height: 44px;
}
.rx-fare-card__breakdown {
  margin-top: var(--rx-space-3);
  padding: var(--rx-space-3) var(--rx-space-4) var(--rx-space-4);
  border-top: 1px dashed var(--rx-color-border);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--rx-space-1) var(--rx-space-3);
  font-size: var(--rx-text-sm);
}
.rx-fare-card__breakdown dt { color: var(--rx-color-text-muted); }
.rx-fare-card__breakdown dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; color: var(--rx-color-text); }
.rx-fare-card__breakdown dd.rx-fare-card__total { font-weight: var(--rx-weight-semibold); }

/* ---------- Pill radio group (trip type / toll option) ---------- */
.rx-pill-group {
  display: flex;
  gap: var(--rx-space-2);
  flex-wrap: wrap;
}
.rx-pill-option {
  position: relative;
}
.rx-pill-option input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.rx-pill-option label {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--rx-space-4);
  border-radius: var(--rx-radius-full);
  border: 1.5px solid var(--rx-color-border);
  color: var(--rx-color-text);
  font-size: var(--rx-text-sm);
  cursor: pointer;
  transition: border-color var(--rx-motion-fast) var(--rx-ease-standard),
              background-color var(--rx-motion-fast) var(--rx-ease-standard);
}
.rx-pill-option input:checked + label {
  border-color: var(--rx-color-brand);
  background: var(--rx-color-brand-soft);
  font-weight: var(--rx-weight-medium);
}
.rx-pill-option input:focus-visible + label {
  outline: 2px solid var(--rx-color-brand-strong);
  outline-offset: 2px;
}

/* ---------- Sticky fare/action bar (steps 2-3, mobile bottom / desktop inline) ---------- */
.rx-fare-bar {
  position: sticky;
  bottom: 0;
  z-index: var(--rx-z-sticky);
  background: var(--rx-color-surface);
  border-top: 1px solid var(--rx-color-border);
  box-shadow: var(--rx-shadow-lg);
  padding: var(--rx-space-4);
  margin: var(--rx-space-6) calc(var(--rx-space-4) * -1) calc(var(--rx-space-4) * -1);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--rx-space-4);
}
.rx-fare-bar__price {
  font-family: var(--rx-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--rx-text-xl);
  font-weight: var(--rx-weight-semibold);
  color: var(--rx-color-text);
}
.rx-fare-bar__price-original {
  font-size: var(--rx-text-sm);
  color: var(--rx-color-text-muted);
  text-decoration: line-through;
  margin-right: var(--rx-space-2);
}
.rx-fare-bar__label {
  font-size: var(--rx-text-xs);
  color: var(--rx-color-text-muted);
}

/* ---------- Step panels: shared-axis slide, direction-aware ----------
 * Transform/opacity only (composite-safe, per MOTION_LANGUAGE.md /
 * BOOKING_UX_RESEARCH.md §5.1) -- never height/left. Forward = incoming
 * slides from the right, outgoing exits left; backward reverses.
 * prefers-reduced-motion collapses both to a plain opacity cross-fade.
 */
.rx-step-panel { will-change: transform, opacity; }
.rx-step-forward-enter-active,
.rx-step-forward-leave-active,
.rx-step-back-enter-active,
.rx-step-back-leave-active {
  transition: transform var(--rx-motion-base) var(--rx-ease-decelerate),
              opacity var(--rx-motion-base) var(--rx-ease-decelerate);
}
.rx-step-forward-enter-from { transform: translateX(24px); opacity: 0; }
.rx-step-forward-leave-to   { transform: translateX(-24px); opacity: 0; transition-timing-function: var(--rx-ease-accelerate); }
.rx-step-back-enter-from    { transform: translateX(-24px); opacity: 0; }
.rx-step-back-leave-to      { transform: translateX(24px); opacity: 0; transition-timing-function: var(--rx-ease-accelerate); }

@media (prefers-reduced-motion: reduce) {
  .rx-shell__sidebar { transition: none; }
  .rx-step-forward-enter-active,
  .rx-step-forward-leave-active,
  .rx-step-back-enter-active,
  .rx-step-back-leave-active { transition: opacity var(--rx-motion-fast) linear; }
  .rx-step-forward-enter-from, .rx-step-forward-leave-to,
  .rx-step-back-enter-from, .rx-step-back-leave-to { transform: none; }
  .rx-stagger-item,
  .rx-table--stagger tbody tr,
  .rx-activity-feed__item { animation: none; }
}

/* ==================================================================== */
/* == Accessibility corrections (ACCESSIBILITY_AUDIT.md)               == */
/* ==================================================================== */
/*
 * Appended rather than edited in place above, to ease merging with
 * parallel work on this file.
 *
 * Off-canvas sidebar: below 768px the closed sidebar is moved off
 * screen with transform: translateX(-100%) alone (patterns.css:205-215).
 * A transformed element is still visible to the accessibility tree and
 * still focusable, so every nav link in the closed drawer stayed in the
 * tab order -- and because the <aside> precedes .rx-shell__main in the
 * DOM, the FIRST thing a keyboard user hit on any mobile portal page
 * was a run of links they could not see (WCAG 2.4.3 / 2.4.7).
 *
 * visibility: hidden removes them from both the tab order and the a11y
 * tree. It is delayed by the drawer's own duration on close so the
 * slide-out still plays, and applied immediately on open. visibility is
 * discretely animatable, which is what lets this work without touching
 * the existing transform rules.
 */
@media (max-width: 767px) { /* == --rx-bp-md - 1px */
  .rx-shell__sidebar {
    visibility: hidden;
    transition: transform var(--rx-motion-base) var(--rx-ease-decelerate),
                visibility 0s linear var(--rx-motion-base);
  }
  .rx-shell__sidebar.is-open {
    visibility: visible;
    transition: transform var(--rx-motion-base) var(--rx-ease-decelerate),
                visibility 0s linear 0s;
  }
}
@media (prefers-reduced-motion: reduce) {
  /* No slide to wait for, so drop the visibility delay entirely. The
     rule above already sets transition: none for the sidebar; this
     restores only the (instant) visibility handling. */
  .rx-shell__sidebar,
  .rx-shell__sidebar.is-open { transition: visibility 0s linear 0s; }
}

/* Breadcrumb current-page marker. .is-current styled the last crumb but
   conveyed nothing programmatically; aria-current="page" is added in
   patterns/_breadcrumbs.html and carries the same visual weight. */
.rx-breadcrumb__item[aria-current="page"] { color: var(--rx-color-text); }

/* The booking wizard's step panels take tabindex="-1" so goStep() can
   move focus into the newly shown step (see booking_new.html). They are
   containers, not controls -- suppress the UA outline that programmatic
   focus would otherwise draw around the full-width panel on every
   Continue press. The announcement, not a ring, is the feedback here. */
.rx-step-panel:focus { outline: none; }
