/* =========================================================================
   EV-TRACS — Apple-style design layer
   Sits on top of Bootstrap 5.3. Overrides Bootstrap's CSS custom properties
   so stock components (cards, forms, buttons, tables, modals) inherit the
   look automatically, then refines the components that need more than tokens.

   Load order matters:  bootstrap.css  ->  bootstrap-icons.css  ->  apple.css
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Design tokens
   ---------------------------------------------------------------------- */

:root {
  /* Type ------------------------------------------------------------- */
  --ap-font-sans:
    -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
    "Helvetica Neue", "Segoe UI", Roboto, system-ui, sans-serif;
  --ap-font-mono:
    ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", monospace;

  /* Palette — light ---------------------------------------------------- */
  --ap-blue: #0071e3;
  --ap-blue-hover: #0077ed;
  --ap-blue-active: #006edb;
  --ap-blue-soft: rgba(0, 113, 227, 0.1);
  --ap-green: #34c759;
  --ap-orange: #ff9500;
  --ap-red: #ff3b30;
  --ap-purple: #af52de;
  --ap-teal: #5ac8fa;

  --ap-bg: #f5f5f7;
  --ap-surface: #ffffff;
  --ap-surface-2: #fafafa;
  --ap-surface-raised: #ffffff;
  --ap-ink: #1d1d1f;
  --ap-ink-2: #424245;
  --ap-ink-3: #6e6e73;
  --ap-ink-4: #86868b;
  --ap-hairline: rgba(0, 0, 0, 0.08);
  --ap-hairline-strong: rgba(0, 0, 0, 0.14);
  --ap-fill: rgba(120, 120, 128, 0.12);
  --ap-fill-hover: rgba(120, 120, 128, 0.2);

  /* Frosted chrome ----------------------------------------------------- */
  --ap-chrome: rgba(255, 255, 255, 0.72);
  --ap-chrome-blur: saturate(180%) blur(20px);

  /* Radii — Apple leans generous, buttons go fully round ---------------- */
  --ap-r-sm: 8px;
  --ap-r-md: 12px;
  --ap-r-lg: 18px;
  --ap-r-xl: 24px;
  --ap-r-pill: 980px;

  /* Elevation — soft and wide, never harsh ----------------------------- */
  --ap-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 3px rgba(0, 0, 0, 0.05);
  --ap-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 6px 20px rgba(0, 0, 0, 0.06);
  --ap-shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.04), 0 18px 48px rgba(0, 0, 0, 0.12);

  /* Motion ------------------------------------------------------------- */
  --ap-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ap-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ap-dur: 0.25s;

  /* --- Bootstrap variable overrides --- */
  --bs-body-font-family: var(--ap-font-sans);
  --bs-body-font-size: 1rem;
  --bs-body-font-weight: 400;
  --bs-body-line-height: 1.47059;
  --bs-body-color: var(--ap-ink);
  --bs-body-bg: var(--ap-bg);
  --bs-emphasis-color: var(--ap-ink);
  --bs-secondary-color: var(--ap-ink-3);
  --bs-tertiary-color: var(--ap-ink-4);
  --bs-border-color: var(--ap-hairline);
  --bs-border-radius: var(--ap-r-md);
  --bs-border-radius-sm: var(--ap-r-sm);
  --bs-border-radius-lg: var(--ap-r-lg);
  --bs-border-radius-xl: var(--ap-r-xl);
  --bs-link-color: var(--ap-blue);
  --bs-link-hover-color: var(--ap-blue-hover);
  --bs-link-color-rgb: 0, 113, 227;
  --bs-primary: var(--ap-blue);
  --bs-primary-rgb: 0, 113, 227;
  --bs-success-rgb: 52, 199, 89;
  --bs-danger-rgb: 255, 59, 48;
  --bs-warning-rgb: 255, 149, 0;
  --bs-font-monospace: var(--ap-font-mono);
  --bs-code-color: var(--ap-purple);
}

/* -------------------------------------------------------------------------
   2. Dark mode — follows the OS, or an explicit data-bs-theme on <html>
   ---------------------------------------------------------------------- */

@media (prefers-color-scheme: dark) {
  :root:not([data-bs-theme="light"]) {
    color-scheme: dark;

    --ap-blue: #0a84ff;
    --ap-blue-hover: #3b9bff;
    --ap-blue-active: #0071e3;
    --ap-blue-soft: rgba(10, 132, 255, 0.16);
    --ap-green: #30d158;
    --ap-orange: #ff9f0a;
    --ap-red: #ff453a;
    --ap-purple: #bf5af2;

    --ap-bg: #000000;
    --ap-surface: #1c1c1e;
    --ap-surface-2: #161618;
    --ap-surface-raised: #2c2c2e;
    --ap-ink: #f5f5f7;
    --ap-ink-2: #d1d1d6;
    --ap-ink-3: #98989d;
    --ap-ink-4: #8e8e93;
    --ap-hairline: rgba(255, 255, 255, 0.1);
    --ap-hairline-strong: rgba(255, 255, 255, 0.18);
    --ap-fill: rgba(120, 120, 128, 0.24);
    --ap-fill-hover: rgba(120, 120, 128, 0.36);

    --ap-chrome: rgba(22, 22, 24, 0.72);

    --ap-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --ap-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 20px rgba(0, 0, 0, 0.5);
    --ap-shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.5), 0 18px 48px rgba(0, 0, 0, 0.7);

    --bs-link-color-rgb: 10, 132, 255;
    --bs-primary-rgb: 10, 132, 255;
    --bs-success-rgb: 48, 209, 88;
    --bs-danger-rgb: 255, 69, 58;
    --bs-warning-rgb: 255, 159, 10;
  }
}

:root[data-bs-theme="dark"] {
  color-scheme: dark;

  --bs-success-rgb: 48, 209, 88;
  --bs-warning-rgb: 255, 159, 10;

  --ap-blue: #0a84ff;
  --ap-blue-hover: #3b9bff;
  --ap-blue-active: #0071e3;
  --ap-blue-soft: rgba(10, 132, 255, 0.16);
  --ap-green: #30d158;
  --ap-orange: #ff9f0a;
  --ap-red: #ff453a;
  --ap-purple: #bf5af2;

  --ap-bg: #000000;
  --ap-surface: #1c1c1e;
  --ap-surface-2: #161618;
  --ap-surface-raised: #2c2c2e;
  --ap-ink: #f5f5f7;
  --ap-ink-2: #d1d1d6;
  --ap-ink-3: #98989d;
  --ap-ink-4: #8e8e93;
  --ap-hairline: rgba(255, 255, 255, 0.1);
  --ap-hairline-strong: rgba(255, 255, 255, 0.18);
  --ap-fill: rgba(120, 120, 128, 0.24);
  --ap-fill-hover: rgba(120, 120, 128, 0.36);

  --ap-chrome: rgba(22, 22, 24, 0.72);

  --ap-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --ap-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 20px rgba(0, 0, 0, 0.5);
  --ap-shadow-lg: 0 2px 4px rgba(0, 0, 0, 0.5), 0 18px 48px rgba(0, 0, 0, 0.7);

  --bs-link-color-rgb: 10, 132, 255;
  --bs-primary-rgb: 10, 132, 255;
}

/* -------------------------------------------------------------------------
   3. Base & typography
   ---------------------------------------------------------------------- */

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

body {
  background-color: var(--ap-bg);
  color: var(--ap-ink);
  font-family: var(--ap-font-sans);
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overscroll-behavior-y: none;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-weight: 600;
  letter-spacing: -0.022em;
  color: var(--ap-ink);
}

h1, .h1 { font-size: clamp(2rem, 1.4rem + 2.4vw, 3rem); line-height: 1.08; letter-spacing: -0.03em; }
h2, .h2 { font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.125rem); line-height: 1.14; letter-spacing: -0.028em; }
h3, .h3 { font-size: 1.375rem; line-height: 1.2; }
h4, .h4 { font-size: 1.1875rem; line-height: 1.25; }
h5, .h5 { font-size: 1.0625rem; line-height: 1.3; }
h6, .h6 { font-size: 0.9375rem; line-height: 1.35; }

a {
  text-decoration: none;
  transition: color var(--ap-dur) var(--ap-ease), opacity var(--ap-dur) var(--ap-ease);
}

a:hover { text-decoration: none; }

::selection {
  background: var(--ap-blue-soft);
  color: var(--ap-ink);
}

/* Apple's signature focus treatment: a soft halo, never a hard outline. */
:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px rgba(var(--bs-link-color-rgb), 0.28);
  border-radius: var(--ap-r-sm);
}

/* Text helpers ------------------------------------------------------- */

.text-muted,
.text-body-secondary { color: var(--ap-ink-3) !important; }

.ap-eyebrow {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
}

.ap-lead {
  font-size: 1.125rem;
  line-height: 1.5;
  color: var(--ap-ink-3);
  font-weight: 400;
  letter-spacing: -0.014em;
}

.ap-mono { font-family: var(--ap-font-mono); }

/* -------------------------------------------------------------------------
   4. App shell & page layout
   ---------------------------------------------------------------------- */

.ap-shell {
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
  padding: 1.75rem 1.25rem 4rem;
}

@media (min-width: 768px) {
  .ap-shell { padding: 2.5rem 2rem 5rem; }
}

.ap-shell-wide { max-width: 1600px; }

.ap-page-head {
  margin-bottom: 1.75rem;
}

.ap-page-head h1,
.ap-page-head .h1 { margin-bottom: 0.375rem; }

.ap-page-head p {
  color: var(--ap-ink-3);
  font-size: 1.0625rem;
  margin-bottom: 0;
  letter-spacing: -0.014em;
}

.ap-hairline {
  height: 1px;
  border: 0;
  background: var(--ap-hairline);
  opacity: 1;
  margin: 2rem 0;
}

/* -------------------------------------------------------------------------
   5. Navigation — frosted glass chrome
   ---------------------------------------------------------------------- */

.ap-nav {
  background: var(--ap-chrome);
  -webkit-backdrop-filter: var(--ap-chrome-blur);
  backdrop-filter: var(--ap-chrome-blur);
  border-bottom: 1px solid var(--ap-hairline);
  padding-block: 0;
  min-height: 52px;
}

/* Fallback for browsers without backdrop-filter (keeps text readable). */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ap-nav { background: var(--ap-surface); }
}

.ap-nav .navbar-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ap-ink);
  padding-block: 0.75rem;
}

.ap-nav .navbar-brand:hover { color: var(--ap-ink); opacity: 0.7; }

/* The Moving IMPACT mark is a wide wordmark, so it is sized by height and lets
   its own aspect ratio set the width. */
.ap-nav .navbar-brand img {
  height: 26px;
  width: auto;
  display: block;
}

.ap-nav .navbar-brand a {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
  transition: opacity var(--ap-dur) var(--ap-ease);
}

.ap-nav .navbar-brand a:hover { opacity: 0.7; }

/* A hairline keeps the project mark and the tool's name from reading as one
   word. */
.ap-nav .ap-brand-name {
  padding-left: 0.5rem;
  border-left: 1px solid var(--ap-hairline-strong);
}

.ap-nav .nav-link {
  font-size: 0.875rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--ap-ink-2);
  padding: 0.4rem 0.75rem;
  border-radius: var(--ap-r-sm);
  transition: color var(--ap-dur) var(--ap-ease),
              background-color var(--ap-dur) var(--ap-ease);
}

.ap-nav .nav-link:hover,
.ap-nav .nav-link:focus { color: var(--ap-ink); }

.ap-nav .nav-link.active {
  color: var(--ap-ink);
  font-weight: 500;
  background: var(--ap-fill);
}

.ap-nav .nav-link i { font-size: 0.9375rem; }

.ap-nav .navbar-toggler {
  border: 0;
  padding: 0.35rem 0.5rem;
  color: var(--ap-ink);
  font-size: 1.25rem;
  line-height: 1;
  box-shadow: none;
}

.ap-nav .navbar-toggler:focus-visible {
  box-shadow: 0 0 0 4px rgba(var(--bs-link-color-rgb), 0.28);
}

/* Menus — Apple's popovers are light, rounded, and tightly padded. */
.dropdown-menu {
  --bs-dropdown-bg: var(--ap-surface-raised);
  --bs-dropdown-border-color: var(--ap-hairline);
  --bs-dropdown-link-color: var(--ap-ink);
  --bs-dropdown-link-hover-bg: var(--ap-fill);
  --bs-dropdown-link-hover-color: var(--ap-ink);
  --bs-dropdown-link-active-bg: var(--ap-blue);
  --bs-dropdown-divider-bg: var(--ap-hairline);
  border-radius: var(--ap-r-md);
  border: 1px solid var(--ap-hairline);
  box-shadow: var(--ap-shadow-lg);
  padding: 0.375rem;
  margin-top: 0.5rem !important;
  min-width: 13rem;
}

.dropdown-item {
  font-size: 0.875rem;
  border-radius: var(--ap-r-sm);
  padding: 0.5rem 0.625rem;
  transition: background-color 0.15s var(--ap-ease);
}

.dropdown-item.text-danger { color: var(--ap-red) !important; }
.dropdown-item.text-danger:hover { background: rgba(var(--bs-danger-rgb), 0.12); }

.dropdown-header {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ap-ink-4);
  padding: 0.5rem 0.625rem 0.25rem;
}

/* Collapsed nav on small screens gets a panel rather than bare links. */
@media (max-width: 767.98px) {
  .ap-nav .navbar-collapse {
    padding: 0.5rem 0 0.75rem;
    border-top: 1px solid var(--ap-hairline);
    margin-top: 0.25rem;
  }
  .ap-nav .nav-link { padding: 0.6rem 0.5rem; font-size: 0.9375rem; }
}

/* -------------------------------------------------------------------------
   6. Surfaces & cards
   ---------------------------------------------------------------------- */

.card {
  --bs-card-bg: var(--ap-surface);
  --bs-card-border-color: transparent;
  --bs-card-border-radius: var(--ap-r-lg);
  --bs-card-inner-border-radius: calc(var(--ap-r-lg) - 1px);
  --bs-card-cap-bg: transparent;
  --bs-card-spacer-x: 1.375rem;
  --bs-card-spacer-y: 1.25rem;
  border: 1px solid var(--ap-hairline);
  box-shadow: var(--ap-shadow-sm);
  transition: box-shadow var(--ap-dur) var(--ap-ease),
              transform var(--ap-dur) var(--ap-ease);
}

.card-header {
  border-bottom: 1px solid var(--ap-hairline);
  padding: 1rem 1.375rem;
  font-weight: 600;
  letter-spacing: -0.014em;
  background: transparent;
}

.card-header .card-title { font-size: 1rem; font-weight: 600; }

.card-footer {
  background: transparent;
  border-top: 1px solid var(--ap-hairline);
  padding: 0.875rem 1.375rem;
}

/* Interactive cards lift very slightly — Apple never bounces. */
.ap-card-hover:hover {
  box-shadow: var(--ap-shadow);
  transform: translateY(-2px);
}

.ap-card-flush { box-shadow: none; }

/* Tinted card headers, replacing Bootstrap's saturated bg-* fills. */
.ap-card-head-tint {
  background: var(--ap-surface-2);
}

/* -------------------------------------------------------------------------
   7. Buttons — pill shaped, no borders, calm hovers
   ---------------------------------------------------------------------- */

.btn {
  --bs-btn-border-radius: var(--ap-r-pill);
  --bs-btn-padding-x: 1.125rem;
  --bs-btn-padding-y: 0.5rem;
  --bs-btn-font-size: 0.9375rem;
  --bs-btn-font-weight: 500;
  --bs-btn-border-width: 0;
  letter-spacing: -0.014em;
  transition: background-color var(--ap-dur) var(--ap-ease),
              color var(--ap-dur) var(--ap-ease),
              box-shadow var(--ap-dur) var(--ap-ease),
              opacity var(--ap-dur) var(--ap-ease),
              transform 0.1s var(--ap-ease);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}

.btn:active { transform: scale(0.97); }

.btn:focus-visible {
  box-shadow: 0 0 0 4px rgba(var(--bs-link-color-rgb), 0.28);
}

.btn-lg {
  --bs-btn-padding-x: 1.5rem;
  --bs-btn-padding-y: 0.75rem;
  --bs-btn-font-size: 1.0625rem;
}

.btn-sm {
  --bs-btn-padding-x: 0.875rem;
  --bs-btn-padding-y: 0.3125rem;
  --bs-btn-font-size: 0.8125rem;
}

.btn-primary {
  --bs-btn-bg: var(--ap-blue);
  --bs-btn-hover-bg: var(--ap-blue-hover);
  --bs-btn-active-bg: var(--ap-blue-active);
  --bs-btn-disabled-bg: var(--ap-blue);
  --bs-btn-color: #fff;
  --bs-btn-hover-color: #fff;
  --bs-btn-active-color: #fff;
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-opacity: 0.36;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-border-color: transparent;
}

.btn-success {
  --bs-btn-bg: var(--ap-green);
  --bs-btn-hover-bg: var(--ap-green);
  --bs-btn-active-bg: var(--ap-green);
  --bs-btn-disabled-bg: var(--ap-green);
  --bs-btn-color: #fff;
  --bs-btn-hover-color: #fff;
  --bs-btn-active-color: #fff;
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-opacity: 0.36;
}

.btn-danger {
  --bs-btn-bg: var(--ap-red);
  --bs-btn-hover-bg: var(--ap-red);
  --bs-btn-active-bg: var(--ap-red);
  --bs-btn-disabled-bg: var(--ap-red);
  --bs-btn-color: #fff;
  --bs-btn-hover-color: #fff;
  --bs-btn-active-color: #fff;
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-opacity: 0.36;
}

.btn-success:hover:not(:disabled),
.btn-danger:hover:not(:disabled) { filter: brightness(1.08); }

/* Secondary = Apple's grey fill button, not a dark slab. */
.btn-secondary {
  --bs-btn-bg: var(--ap-fill);
  --bs-btn-hover-bg: var(--ap-fill-hover);
  --bs-btn-active-bg: var(--ap-fill-hover);
  --bs-btn-disabled-bg: var(--ap-fill);
  --bs-btn-color: var(--ap-ink);
  --bs-btn-hover-color: var(--ap-ink);
  --bs-btn-active-color: var(--ap-ink);
  --bs-btn-disabled-color: var(--ap-ink-4);
}

/* Outline buttons keep a hairline instead of a 1px solid brand border. */
.btn-outline-primary,
.btn-outline-secondary,
.btn-outline-light,
.btn-outline-danger {
  --bs-btn-border-width: 1px;
  --bs-btn-bg: transparent;
}

.btn-outline-primary {
  --bs-btn-color: var(--ap-blue);
  --bs-btn-border-color: rgba(var(--bs-link-color-rgb), 0.35);
  --bs-btn-hover-bg: var(--ap-blue-soft);
  --bs-btn-hover-color: var(--ap-blue);
  --bs-btn-hover-border-color: rgba(var(--bs-link-color-rgb), 0.5);
  --bs-btn-active-bg: var(--ap-blue-soft);
  --bs-btn-active-color: var(--ap-blue);
  --bs-btn-active-border-color: var(--ap-blue);
}

.btn-outline-secondary {
  --bs-btn-color: var(--ap-ink-2);
  --bs-btn-border-color: var(--ap-hairline-strong);
  --bs-btn-hover-bg: var(--ap-fill);
  --bs-btn-hover-color: var(--ap-ink);
  --bs-btn-hover-border-color: var(--ap-hairline-strong);
  --bs-btn-active-bg: var(--ap-fill-hover);
  --bs-btn-active-color: var(--ap-ink);
}

.btn-outline-danger {
  --bs-btn-color: var(--ap-red);
  --bs-btn-border-color: rgba(var(--bs-danger-rgb), 0.35);
  --bs-btn-hover-bg: rgba(var(--bs-danger-rgb), 0.12);
  --bs-btn-hover-color: var(--ap-red);
  --bs-btn-hover-border-color: rgba(var(--bs-danger-rgb), 0.5);
}

.btn-link {
  --bs-btn-color: var(--ap-blue);
  --bs-btn-hover-color: var(--ap-blue-hover);
  --bs-btn-font-weight: 400;
  text-decoration: none;
}

.btn-link:hover { text-decoration: underline; }

/* -------------------------------------------------------------------------
   8. Forms — quiet fields, generous targets, halo focus
   ---------------------------------------------------------------------- */

.form-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--ap-ink-3);
  margin-bottom: 0.375rem;
  letter-spacing: -0.006em;
}

.form-control,
.form-select {
  background-color: var(--ap-surface);
  border: 1px solid var(--ap-hairline-strong);
  border-radius: var(--ap-r-md);
  color: var(--ap-ink);
  font-size: 1rem;
  padding: 0.625rem 0.875rem;
  box-shadow: none;
  transition: border-color var(--ap-dur) var(--ap-ease),
              box-shadow var(--ap-dur) var(--ap-ease),
              background-color var(--ap-dur) var(--ap-ease);
}

.form-control::placeholder { color: var(--ap-ink-4); }

.form-control:hover:not(:disabled):not(:focus),
.form-select:hover:not(:disabled):not(:focus) {
  border-color: var(--ap-ink-4);
}

.form-control:focus,
.form-select:focus {
  background-color: var(--ap-surface);
  border-color: var(--ap-blue);
  color: var(--ap-ink);
  box-shadow: 0 0 0 4px rgba(var(--bs-link-color-rgb), 0.22);
}

.form-control:disabled,
.form-select:disabled {
  background-color: var(--ap-fill);
  color: var(--ap-ink-4);
  opacity: 1;
}

.form-control-sm, .form-select-sm {
  font-size: 0.875rem;
  padding: 0.4rem 0.625rem;
  border-radius: var(--ap-r-sm);
}

.form-control-lg, .form-select-lg {
  font-size: 1.0625rem;
  padding: 0.75rem 1rem;
}

.form-text {
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
  margin-top: 0.375rem;
}

.form-check-input {
  border-color: var(--ap-hairline-strong);
  transition: background-color 0.2s var(--ap-ease), border-color 0.2s var(--ap-ease);
}

.form-check-input:checked {
  background-color: var(--ap-blue);
  border-color: var(--ap-blue);
}

.form-check-input:focus {
  border-color: var(--ap-blue);
  box-shadow: 0 0 0 4px rgba(var(--bs-link-color-rgb), 0.22);
}

/* Apple's switch is wider and taller than Bootstrap's default. */
.form-switch .form-check-input {
  width: 2.75em;
  height: 1.55em;
  border-radius: var(--ap-r-pill);
}

.form-switch .form-check-input:checked { background-color: var(--ap-green); border-color: var(--ap-green); }

.form-check { color: inherit; }

.input-group-text {
  background: var(--ap-surface-2);
  border-color: var(--ap-hairline-strong);
  color: var(--ap-ink-3);
  border-radius: var(--ap-r-md);
  font-size: 0.9375rem;
}

.invalid-feedback, .valid-feedback { font-size: 0.8125rem; }
.form-control.is-invalid, .form-select.is-invalid { border-color: var(--ap-red); }
.form-control.is-invalid:focus { box-shadow: 0 0 0 4px rgba(var(--bs-danger-rgb), 0.22); }

/* Django/crispy error lists ------------------------------------------- */
.errorlist {
  list-style: none;
  padding: 0;
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--ap-red);
}

.asteriskField { color: var(--ap-ink-4); margin-left: 0.15rem; }

/* Range sliders -------------------------------------------------------- */
.form-range::-webkit-slider-thumb { background: var(--ap-blue); }
.form-range::-moz-range-thumb { background: var(--ap-blue); }

/* -------------------------------------------------------------------------
   9. Stat tiles & status pills
   ---------------------------------------------------------------------- */

/* Fixed 3-up: reflowing to 2 columns would leave a visible empty cell,
   because the 1px gaps are painted by the container background. */
.ap-stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--ap-hairline);
  border-radius: var(--ap-r-md);
  overflow: hidden;
}

.ap-stat {
  background: var(--ap-surface);
  padding: 1.125rem 1rem;
  text-align: center;
}

@media (max-width: 420px) {
  .ap-stat { padding: 0.875rem 0.375rem; }
  .ap-stat-value { font-size: 1.5rem; }
  .ap-stat-label { font-size: 0.625rem; letter-spacing: 0.04em; }
}

.ap-stat-label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
  margin-bottom: 0.375rem;
}

.ap-stat-value {
  font-size: 2rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--ap-ink);
}

.ap-stat-value.is-blue { color: var(--ap-blue); }

/* A stat whose value is words rather than a number - "Demand model" at 2rem
   would wrap and tower over the figures beside it. */
.ap-stat-value.is-text {
  font-size: 1.0625rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
  /* Hold the row's baseline so all three values sit level. */
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2rem;
}

/* The unit rides small next to its figure: "24 h", not "24 H". */
.ap-stat-unit {
  font-size: 0.9375rem;
  font-weight: 500;
  margin-left: 0.15em;
  color: var(--ap-ink-4);
}
.ap-stat-value.is-green { color: var(--ap-green); }
.ap-stat-value.is-orange { color: var(--ap-orange); }

/* Status pill with a live dot ---------------------------------------- */
.ap-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  font-weight: 500;
  padding: 0.25rem 0.7rem;
  border-radius: var(--ap-r-pill);
  background: var(--ap-fill);
  color: var(--ap-ink-2);
  letter-spacing: -0.006em;
}

.ap-pill::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.65;
  flex: none;
}

.ap-pill.is-live {
  background: rgba(var(--bs-success-rgb), 0.14);
  color: var(--ap-green);
}

.ap-pill.is-live::before {
  opacity: 1;
  animation: ap-pulse 2s var(--ap-ease) infinite;
}

.ap-pill.is-warn { background: rgba(var(--bs-warning-rgb), 0.16); color: var(--ap-orange); }
.ap-pill.is-error { background: rgba(var(--bs-danger-rgb), 0.14); color: var(--ap-red); }
.ap-pill.is-info { background: var(--ap-blue-soft); color: var(--ap-blue); }

@keyframes ap-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.82); }
}

.badge {
  font-weight: 500;
  letter-spacing: -0.006em;
  border-radius: var(--ap-r-pill);
  padding: 0.3em 0.7em;
}

/* -------------------------------------------------------------------------
   10. Lists, tables, console
   ---------------------------------------------------------------------- */

.list-group {
  --bs-list-group-bg: transparent;
  --bs-list-group-border-color: var(--ap-hairline);
  --bs-list-group-color: var(--ap-ink);
  --bs-list-group-action-hover-bg: var(--ap-fill);
  --bs-list-group-action-hover-color: var(--ap-ink);
}

.list-group-item {
  padding: 0.8125rem 1.375rem;
  font-size: 0.9375rem;
  background: transparent;
}

.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--ap-ink);
  --bs-table-border-color: var(--ap-hairline);
  --bs-table-hover-bg: var(--ap-fill);
  --bs-table-hover-color: var(--ap-ink);
  font-size: 0.9375rem;
  margin-bottom: 0;
}

.table > :not(caption) > * > * { padding: 0.75rem 1rem; }

.table > thead th {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
  background-color: var(--ap-surface-2);
  border-bottom: 1px solid var(--ap-hairline);
}

.table-horizontal-lines td,
.table-horizontal-lines th {
  border-top: 1px solid var(--ap-hairline);
  border-left: none;
  border-right: none;
}

.table-horizontal-lines thead th { border-bottom: 1px solid var(--ap-hairline); }

/* Monospace log console ------------------------------------------------ */
.ap-console {
  font-family: var(--ap-font-mono);
  font-size: 0.75rem;
  line-height: 1.65;
  background: var(--ap-surface-2);
  color: var(--ap-ink-2);
  padding: 0.875rem 1.125rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  -webkit-overflow-scrolling: touch;
}

.ap-console:empty::before {
  content: "No activity yet.";
  color: var(--ap-ink-4);
}

/* Thin, unobtrusive scrollbars (WebKit) */
.ap-console::-webkit-scrollbar,
.ap-scroll::-webkit-scrollbar { width: 8px; height: 8px; }

.ap-console::-webkit-scrollbar-thumb,
.ap-scroll::-webkit-scrollbar-thumb {
  background: var(--ap-fill-hover);
  border-radius: var(--ap-r-pill);
}

.ap-console::-webkit-scrollbar-track,
.ap-scroll::-webkit-scrollbar-track { background: transparent; }

/* -------------------------------------------------------------------------
   11. Modals & alerts
   ---------------------------------------------------------------------- */

.modal-content {
  background: var(--ap-surface-raised);
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-xl);
  box-shadow: var(--ap-shadow-lg);
  color: var(--ap-ink);
}

.modal-header {
  border-bottom: 1px solid var(--ap-hairline);
  padding: 1.125rem 1.5rem;
}

.modal-title { font-size: 1.0625rem; font-weight: 600; }
.modal-body { padding: 1.5rem; }
.modal-footer { border-top: 1px solid var(--ap-hairline); padding: 1rem 1.5rem; }
.modal-backdrop.show { opacity: 0.32; }

.progress {
  --bs-progress-bg: var(--ap-fill);
  --bs-progress-bar-bg: var(--ap-blue);
  border-radius: var(--ap-r-pill);
  height: 6px;
  overflow: hidden;
}

.progress-bar { border-radius: var(--ap-r-pill); }

.alert {
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-md);
  font-size: 0.9375rem;
  padding: 0.875rem 1.125rem;
}

.alert-danger { background: rgba(var(--bs-danger-rgb), 0.1); color: var(--ap-red); }
.alert-success { background: rgba(var(--bs-success-rgb), 0.1); color: var(--ap-green); }
.alert-info { background: var(--ap-blue-soft); color: var(--ap-blue); }

/* Indeterminate activity bar for the "please wait" modal --------------- */
.ap-activity {
  height: 4px;
  border-radius: var(--ap-r-pill);
  background: var(--ap-fill);
  overflow: hidden;
  position: relative;
}

.ap-activity::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 38%;
  border-radius: var(--ap-r-pill);
  background: var(--ap-blue);
  animation: ap-slide 1.15s var(--ap-ease) infinite;
}

@keyframes ap-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(320%); }
}

/* Apple's spinner is a ring, not a dashed circle. */
.ap-spinner {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid var(--ap-fill);
  border-top-color: var(--ap-blue);
  animation: ap-spin 0.75s linear infinite;
  flex: none;
}

@keyframes ap-spin { to { transform: rotate(360deg); } }

/* -------------------------------------------------------------------------
   12. Auth screens
   ---------------------------------------------------------------------- */

.ap-auth {
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
  background: var(--ap-bg);
}

.ap-auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--ap-surface);
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-xl);
  box-shadow: var(--ap-shadow);
  padding: 2.5rem 2rem;
}

.ap-auth-mark {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  display: block;
  margin: 0 auto 1.25rem;
  box-shadow: var(--ap-shadow-sm);
}

.ap-auth-card h1,
.ap-auth-card h4 {
  text-align: center;
  font-size: 1.5rem;
  letter-spacing: -0.024em;
  margin-bottom: 0.375rem;
}

.ap-auth-sub {
  text-align: center;
  color: var(--ap-ink-3);
  font-size: 0.9375rem;
  margin-bottom: 1.75rem;
}

.ap-auth-card .btn { width: 100%; }

.ap-auth-foot {
  text-align: center;
  margin-top: 1.5rem;
  font-size: 0.875rem;
  color: var(--ap-ink-3);
}

/* -------------------------------------------------------------------------
   13. Map surfaces
   ---------------------------------------------------------------------- */

.ap-map {
  width: 100%;
  border-radius: var(--ap-r-lg);
  overflow: hidden;
  background: var(--ap-fill);
}

/* OpenLayers chrome, restyled to match the rest of the UI. */
.ol-control button {
  background: var(--ap-chrome) !important;
  -webkit-backdrop-filter: var(--ap-chrome-blur);
  backdrop-filter: var(--ap-chrome-blur);
  color: var(--ap-ink) !important;
  border-radius: var(--ap-r-sm) !important;
  font-size: 1rem !important;
  font-weight: 500;
  box-shadow: var(--ap-shadow-sm);
  transition: background-color var(--ap-dur) var(--ap-ease);
}

.ol-control button:hover { background: var(--ap-surface) !important; }

.ol-zoom { border-radius: var(--ap-r-sm); overflow: hidden; }
.ol-attribution { border-radius: var(--ap-r-sm) !important; font-size: 0.6875rem; }

/* noUiSlider, restyled as an Apple range control ---------------------- */
.noUi-target {
  background: var(--ap-fill);
  border: 0;
  box-shadow: none;
  border-radius: var(--ap-r-pill);
}

.noUi-horizontal { height: 5px; }
.noUi-connect { background: var(--ap-blue); }

.noUi-horizontal .noUi-handle {
  width: 22px;
  height: 22px;
  right: -11px;
  top: -9px;
  border-radius: 50%;
  background: #fff;
  border: 0.5px solid var(--ap-hairline-strong);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 3px 8px rgba(0, 0, 0, 0.12);
  cursor: grab;
  transition: transform 0.15s var(--ap-ease);
}

.noUi-horizontal .noUi-handle:active { cursor: grabbing; transform: scale(1.1); }
.noUi-handle::after, .noUi-handle::before { display: none; }

.noUi-tooltip {
  border: 0;
  background: var(--ap-ink);
  color: var(--ap-bg);
  padding: 0.2rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 500;
  border-radius: var(--ap-r-sm);
  bottom: 165%;
  white-space: nowrap;
  box-shadow: var(--ap-shadow-sm);
}

/* -------------------------------------------------------------------------
   14. Feature tiles (home)
   ---------------------------------------------------------------------- */

.ap-tile {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 1.5rem;
  background: var(--ap-surface);
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-lg);
  box-shadow: var(--ap-shadow-sm);
  color: inherit;
  transition: box-shadow var(--ap-dur) var(--ap-ease),
              transform var(--ap-dur) var(--ap-ease);
}

a.ap-tile:hover {
  color: inherit;
  box-shadow: var(--ap-shadow);
  transform: translateY(-2px);
}

.ap-tile-icon {
  width: 42px;
  height: 42px;
  border-radius: var(--ap-r-md);
  display: grid;
  place-items: center;
  font-size: 1.25rem;
  margin-bottom: 1rem;
  background: var(--ap-blue-soft);
  color: var(--ap-blue);
}

.ap-tile-icon.is-green { background: rgba(var(--bs-success-rgb), 0.14); color: var(--ap-green); }
.ap-tile-icon.is-orange { background: rgba(var(--bs-warning-rgb), 0.16); color: var(--ap-orange); }
.ap-tile-icon.is-purple { background: rgba(175, 82, 222, 0.14); color: var(--ap-purple); }

.ap-tile h3 { font-size: 1.0625rem; margin-bottom: 0.375rem; }

.ap-tile p {
  font-size: 0.9375rem;
  color: var(--ap-ink-3);
  margin-bottom: 0;
  line-height: 1.45;
}

/* Workflow sections on the scenario page: Define, Run, View, Analyse. The rule
   under each heading does the separating, so the sections need no card of their
   own around the tiles. */
.ap-section + .ap-section { margin-top: 2rem; }

.ap-section-title {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
  margin-bottom: 0.875rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--ap-hairline);
}

/* Screenshot at the head of the home page. Bordered rather than floating, so a
   map full of pale terrain does not bleed into the page behind it. */
.ap-hero { margin: 0; }

.ap-hero-shot {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-lg);
  box-shadow: var(--ap-shadow-sm);
}

.ap-hero figcaption {
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
  margin-top: 0.625rem;
  text-align: center;
}

/* A tile for something this scenario has not published: still legible, but
   plainly not a link. */
.ap-tile-muted {
  opacity: 0.55;
  box-shadow: none;
}

.ap-tile-more {
  margin-top: auto;
  padding-top: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--ap-blue);
}

.ap-tile-more i {
  transition: transform var(--ap-dur) var(--ap-ease);
  display: inline-block;
}

a.ap-tile:hover .ap-tile-more i { transform: translateX(3px); }

/* -------------------------------------------------------------------------
   15. Scenarios
   ---------------------------------------------------------------------- */

.ap-shell-narrow { max-width: 820px; }

.ap-messages { margin-bottom: 1.5rem; }
.ap-messages .alert:last-child { margin-bottom: 0; }

/* Name field + button sit on one line, stacking on narrow screens. */
.ap-form-row {
  display: flex;
  gap: 0.75rem;
  align-items: flex-end;
}

@media (max-width: 575.98px) {
  .ap-form-row { flex-direction: column; align-items: stretch; }
  .ap-form-row .btn { width: 100%; }
}

.ap-field-error {
  font-size: 0.8125rem;
  color: var(--ap-red);
  margin-top: 0.375rem;
}

.ap-scenario-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  transition: background-color var(--ap-dur) var(--ap-ease);
}

.ap-scenario-row.is-active { background: var(--ap-blue-soft); }

.ap-scenario-main {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
}

/* A row stacks the scenario's name over which mode it runs in. */
.ap-scenario-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.125rem;
  min-width: 0;
}

.ap-scenario-name {
  font-weight: 500;
  letter-spacing: -0.014em;
  word-break: break-word;
}

.ap-scenario-mode {
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
}

.ap-scenario-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* Light/dark switch. Sized to match the nav links beside it rather than
   drawn as a button, so it sits in the bar rather than on it. */
.ap-theme-toggle {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--ap-ink-2);
  font-size: 1rem;
  transition: background-color var(--ap-dur) var(--ap-ease),
              color var(--ap-dur) var(--ap-ease);
}

.ap-theme-toggle:hover {
  background: var(--ap-fill);
  color: var(--ap-ink);
}

/* Active scenario name in the navbar. */
.ap-nav-scenario {
  max-width: 11rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}

/* Dropdown items that are submit buttons need the anchor treatment. */
.dropdown-menu form { margin: 0; }

.dropdown-menu button.dropdown-item {
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.dropdown-menu button.dropdown-item.active {
  background: var(--ap-blue-soft);
  color: var(--ap-blue);
  font-weight: 500;
}

.ap-dropdown-tick { flex: none; width: 1rem; opacity: 0.65; }
.dropdown-menu button.dropdown-item.active .ap-dropdown-tick { opacity: 1; }

/* Empty state ---------------------------------------------------------- */
.ap-empty {
  text-align: center;
  padding: 3.5rem 1.5rem;
  border: 1px dashed var(--ap-hairline-strong);
  border-radius: var(--ap-r-lg);
  background: var(--ap-surface-2);
}

.ap-empty-icon {
  width: 52px;
  height: 52px;
  margin: 0 auto 1rem;
  border-radius: var(--ap-r-md);
  display: grid;
  place-items: center;
  font-size: 1.5rem;
  background: var(--ap-fill);
  color: var(--ap-ink-3);
}

.ap-empty h3 { font-size: 1.0625rem; margin-bottom: 0.375rem; }

.ap-empty p {
  color: var(--ap-ink-3);
  font-size: 0.9375rem;
  margin-bottom: 0;
}

/* -------------------------------------------------------------------------
   16. Scenario parameters
   ---------------------------------------------------------------------- */

.ap-back {
  color: var(--ap-ink-3);
  font-weight: 500;
  letter-spacing: -0.006em;
  text-transform: none;
  font-size: 0.8125rem;
}

.ap-back:hover { color: var(--ap-blue); }
.ap-back i { font-size: 0.75rem; }

.ap-hint {
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
  margin-bottom: 0.75rem;
}

/* Scalar parameters, two or three across depending on room. */
.ap-param-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1rem 1.25rem;
}

.ap-param-field { min-width: 0; }

/* What the parameter controls, under its value. */
.ap-param-about {
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--ap-ink-4);
  margin: 0.375rem 0 0;
}

.ap-switch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
}

.ap-switch-row .form-check-label {
  font-size: 0.9375rem;
  padding-left: 0.25rem;
}

/* 24 hourly agent counts. Falls from 8 across to 3 on a phone. */
.ap-hour-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 0.5rem;
}

/* 23 road types, wider cells because the labels are words. */
.ap-road-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.75rem;
}

@media (max-width: 991.98px) {
  .ap-hour-grid { grid-template-columns: repeat(6, 1fr); }
}

@media (max-width: 767.98px) {
  .ap-hour-grid { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 479.98px) {
  .ap-hour-grid { grid-template-columns: repeat(3, 1fr); }
}

.ap-hour-cell label,
.ap-road-cell label {
  display: block;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
  margin-bottom: 0.25rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ap-hour-cell .form-control,
.ap-road-cell .form-control {
  padding: 0.4rem 0.5rem;
  font-size: 0.875rem;
  border-radius: var(--ap-r-sm);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.ap-hour-cell.has-error .form-control,
.ap-road-cell.has-error .form-control { border-color: var(--ap-red); }

.ap-form-actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding-bottom: 1rem;
}

/* Read-only values ---------------------------------------------------
   The parameters are published, not edited, so they are shown as plain
   values. They keep the metrics of the input they replace, so the grids
   and columns line up exactly as they would in a form. */
.ap-value {
  padding: 0.5rem 0.875rem;
  background: var(--ap-fill);
  border: 1px solid transparent;
  border-radius: var(--ap-r-md);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ap-ink);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.ap-hour-cell .ap-value,
.ap-road-cell .ap-value {
  padding: 0.4rem 0.5rem;
  font-size: 0.875rem;
  border-radius: var(--ap-r-sm);
  text-align: center;
}

/* A boolean parameter: its state, then what it is. */
.ap-flag {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.9375rem;
  color: var(--ap-ink-2);
}

/* -------------------------------------------------------------------------
   17. Scenario area
   ---------------------------------------------------------------------- */

/* Accept/discard buttons anchored to a freshly drawn polygon. */
.ap-map-overlay {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem;
  background: var(--ap-chrome);
  -webkit-backdrop-filter: var(--ap-chrome-blur);
  backdrop-filter: var(--ap-chrome-blur);
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-md);
  box-shadow: var(--ap-shadow-lg);
  white-space: nowrap;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ap-map-overlay { background: var(--ap-surface); }
}

/* The draw button flips to a cancel state while an interaction is armed. */
#drawBtn.is-armed {
  background: var(--ap-blue);
  border-color: var(--ap-blue);
  color: #fff;
}

kbd {
  font-family: var(--ap-font-mono);
  font-size: 0.75rem;
  padding: 0.1rem 0.35rem;
  border-radius: var(--ap-r-sm);
  background: var(--ap-fill);
  color: var(--ap-ink-2);
  border: 1px solid var(--ap-hairline);
}

/* Key/value summary of the stored area. */
.ap-summary {
  margin: 0;
  font-size: 0.875rem;
}

.ap-summary > div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--ap-hairline);
}

.ap-summary > div:last-child { border-bottom: 0; }

.ap-summary dt {
  color: var(--ap-ink-3);
  font-weight: 400;
  flex: none;
}

.ap-summary dd {
  margin: 0;
  text-align: right;
  font-weight: 500;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}

.ap-summary dd.ap-mono { font-size: 0.75rem; font-weight: 400; }

/* The parameter list in a step modal: the description carries the weight, so
   the value column stacks under its term rather than sitting beside it. */
.ap-param-list > div {
  display: block;
  padding: 0.5rem 0;
}

.ap-param-list dt { margin-bottom: 0.125rem; }

.ap-param-list dd {
  font-weight: 400;
  color: var(--ap-ink-3);
  text-align: left;
}

/* File inputs are hidden behind a label styled as a button. */
label.btn { cursor: pointer; }

/* -------------------------------------------------------------------------
   18. Animations
   ---------------------------------------------------------------------- */

/* Transport controls sit under the map, inside the same card. */
.ap-anim-controls {
  padding: 0.875rem 1rem 1rem;
  border-top: 1px solid var(--ap-hairline);
  background: var(--ap-surface);
}

.ap-anim-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem;
}

/* The timeline is the control people actually drag, so it gets the room. */
.ap-anim-speed { flex: 1 1 160px; min-width: 140px; }
.ap-anim-time  { flex: 2 1 280px; min-width: 200px; }

.ap-anim-controls .form-range { width: 100%; }

.ap-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem 0.875rem;
  margin-top: 0.875rem;
  padding-top: 0.875rem;
  border-top: 1px solid var(--ap-hairline);
  font-size: 0.8125rem;
  color: var(--ap-ink-2);
}

.ap-legend strong {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
}

.ap-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.ap-legend-dot {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  display: inline-block;
}

/* Idle draws smaller on the map, so its swatch matches the same ratio. */
.ap-legend-dot.is-small { width: 6px; height: 6px; }

.ap-legend-star {
  width: 14px;
  height: 14px;
  display: inline-block;
  clip-path: polygon(
    50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
    50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%
  );
}

/* Tooltip following the cursor over a swap station. */
.ap-map-tip {
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  border-radius: var(--ap-r-sm);
  padding: 0.2rem 0.5rem;
  font-size: 0.75rem;
  white-space: nowrap;
  pointer-events: none;
}

/* --- Swap station playback ---------------------------------------------- */

.ap-station-status {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.ap-station-modal { max-width: 650px; }

.ap-station-controls {
  background: var(--ap-fill);
  border-radius: var(--ap-r-md);
  padding: 0.75rem;
}

/* A swap bay: dashed while free, solid and green once a vehicle is in it. */
.ap-post-box {
  border: 2px dashed var(--ap-blue);
  background: var(--ap-blue-soft);
  border-radius: var(--ap-r-sm);
  min-height: 70px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  padding: 0.5rem 0.25rem;
  transition: background-color var(--ap-dur) var(--ap-ease),
              border-color var(--ap-dur) var(--ap-ease);
}

.ap-post-box.is-occupied {
  border-style: solid;
  border-color: var(--ap-green);
  background: rgba(var(--bs-success-rgb), 0.14);
}

.ap-post-label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
}

.ap-post-free {
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
}

.ap-queue-box {
  border: 2px dashed var(--ap-orange);
  background: rgba(var(--bs-warning-rgb), 0.12);
  border-radius: var(--ap-r-sm);
  min-height: 80px;
  padding: 0.625rem;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  justify-content: center;
  gap: 0.375rem;
}

.ap-agent-badge {
  font-size: 0.6875rem;
  font-weight: 500;
  padding: 0.25rem 0.55rem;
  border-radius: var(--ap-r-pill);
  white-space: nowrap;
}

.ap-agent-badge.is-swapping {
  background: rgba(var(--bs-success-rgb), 0.2);
  color: var(--ap-green);
}

.ap-agent-badge.is-queueing {
  background: rgba(var(--bs-warning-rgb), 0.22);
  color: var(--ap-orange);
}

/* -------------------------------------------------------------------------
   19. Chart outputs
   ---------------------------------------------------------------------- */

/* One chart in the picker: fills its row so the whole strip is clickable. */
.ap-output-pick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  padding: 0.75rem 1rem;
  border: 0;
  background: transparent;
  text-align: left;
  color: var(--ap-ink);
  font-size: 0.9375rem;
  transition: background-color var(--ap-dur) var(--ap-ease);
}

.ap-output-pick:hover { background: var(--ap-fill); }

.ap-output-pick.is-on {
  background: var(--ap-blue-soft);
  color: var(--ap-blue);
  font-weight: 500;
}

.ap-output-name {
  min-width: 0;
  overflow-wrap: anywhere;
}

.ap-output-meta {
  flex: none;
  font-size: 0.75rem;
  color: var(--ap-ink-4);
  font-variant-numeric: tabular-nums;
}

.ap-output-pick.is-on .ap-output-meta { color: var(--ap-blue); }

/* Charts are rendered on white by matplotlib, so they get a light plate in
   both themes rather than glowing against a dark card. */
.ap-chart-frame {
  background: #ffffff;
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-md);
  padding: 0.5rem;
  overflow-x: auto;
}

.ap-chart {
  display: block;
  width: 100%;
  height: auto;
  /* Never blow a chart up past its own resolution - it only goes soft. */
  max-width: 100%;
}

/* -------------------------------------------------------------------------
   20. Model overview flow chart
   ---------------------------------------------------------------------- */

/* One hue per stage, so Define / Run / View / Analyse read apart at a glance.
   Declared on the stage and on the arrow leading into it, then picked up by the
   numbers, chevrons, title and hover states inside. */
.ap-flow-stage,
.ap-flow-down {
  --ap-stage: var(--ap-blue);
  --ap-stage-soft: var(--ap-blue-soft);
}

.ap-flow-stage.is-run,
.ap-flow-down.is-run {
  --ap-stage: var(--ap-orange);
  --ap-stage-soft: rgba(var(--bs-warning-rgb), 0.16);
}

.ap-flow-stage.is-view,
.ap-flow-down.is-view {
  --ap-stage: var(--ap-purple);
  --ap-stage-soft: rgba(175, 82, 222, 0.16);
}

.ap-flow-stage.is-analyse,
.ap-flow-down.is-analyse {
  --ap-stage: var(--ap-green);
  --ap-stage-soft: rgba(var(--bs-success-rgb), 0.16);
}

/* Stages run down the page as lanes rather than across as columns: a stage can
   hold one step or seven, and columns of wildly different heights read worse
   than rows that simply wrap. */
.ap-flow-stage {
  background: var(--ap-surface);
  border: 1px solid var(--ap-hairline);
  border-radius: var(--ap-r-lg);
  box-shadow: var(--ap-shadow-sm);
  padding: 1rem 1.125rem 1.125rem;
}

.ap-flow-stage-title {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ap-stage);
  margin: 0 0 0.875rem;
}

.ap-flow-steps {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0.5rem;
}

/* Each step is a button so it is reachable by keyboard and announced as
   something you can act on, not just a coloured box. */
.ap-flow-step {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.625rem 0.875rem;
  border: 1px solid var(--ap-hairline-strong);
  border-radius: var(--ap-r-md);
  background: var(--ap-surface);
  color: var(--ap-ink);
  font-size: 0.875rem;
  text-align: left;
  transition: border-color var(--ap-dur) var(--ap-ease),
              background-color var(--ap-dur) var(--ap-ease),
              transform var(--ap-dur) var(--ap-ease);
}

.ap-flow-step:hover {
  border-color: var(--ap-stage);
  background: var(--ap-stage-soft);
}

.ap-flow-step:active { transform: scale(0.985); }

.ap-flow-step-no {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ap-stage-soft);
  color: var(--ap-stage);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.ap-flow-step:hover .ap-flow-step-no {
  background: var(--ap-stage);
  color: #fff;
}

.ap-flow-step-title { min-width: 0; overflow-wrap: anywhere; }

/* Marks a step that has a walkthrough behind it. */
.ap-flow-step-video {
  margin-left: auto;
  flex: none;
  color: var(--ap-ink-4);
  font-size: 0.875rem;
}

.ap-flow-step:hover .ap-flow-step-video { color: var(--ap-stage); }

.ap-flow-join {
  display: flex;
  align-items: center;
  color: var(--ap-stage);
  font-size: 0.875rem;
  font-weight: 600;
  opacity: 0.6;
  flex: none;
}

.ap-flow-down {
  display: flex;
  justify-content: center;
  padding: 0.625rem 0;
}

.ap-flow-down i {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  font-size: 1.0625rem;
  background: var(--ap-stage-soft);
  color: var(--ap-stage);
}

/* On a phone the steps stack, so the between-step chevrons would point the
   wrong way. Hide them and let the stacking imply the order. */
@media (max-width: 575.98px) {
  .ap-flow-step { flex-basis: 100%; }
  .ap-flow-join { display: none; }
}

/* -------------------------------------------------------------------------
   21. About
   ---------------------------------------------------------------------- */

/* The funding statement is an acknowledgement, so it is set apart from the
   prose around it rather than reading as another paragraph. */
.ap-funding {
  border-left: 3px solid var(--ap-green);
  padding-left: 0.875rem;
  font-size: 0.9375rem;
  color: var(--ap-ink-2);
}

/* A setup step on the global page: a short piece of prose with its video. */
.ap-setup-step + .ap-setup-step {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ap-hairline);
}

.ap-setup-step h3 {
  font-size: 1rem;
  margin-bottom: 0.375rem;
}

.ap-setup-step p {
  font-size: 0.9375rem;
  color: var(--ap-ink-3);
  margin-bottom: 0.75rem;
}

.ap-about-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
}

/* -------------------------------------------------------------------------
   22. Demand frequencies
   ---------------------------------------------------------------------- */

.ap-freq-flow {
  padding: 0.875rem 0;
  border-bottom: 1px solid var(--ap-hairline);
}

.ap-freq-flow:last-child { border-bottom: 0; padding-bottom: 0; }
.ap-freq-flow:first-child { padding-top: 0; }

.ap-freq-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.625rem;
}

.ap-freq-route {
  font-size: 0.9375rem;
  font-weight: 500;
  overflow-wrap: anywhere;
}

.ap-freq-route i {
  font-size: 0.75rem;
  color: var(--ap-ink-4);
  margin: 0 0.15rem;
}

.ap-freq-total {
  flex: none;
  font-size: 0.8125rem;
  color: var(--ap-ink-4);
  font-variant-numeric: tabular-nums;
}

.ap-freq-group {
  display: flex;
  align-items: flex-end;
  gap: 0.625rem;
  margin-top: 0.5rem;
}

.ap-freq-label {
  flex: none;
  width: 3.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ap-ink-4);
}

/* Bars share the row's width, so a 24-hour profile and a 7-day one line up
   under each other however many buckets each has. */
.ap-freq-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  flex: 1;
  height: 34px;
}

.ap-freq-bar {
  flex: 1;
  height: 100%;
  display: flex;
  align-items: flex-end;
  min-width: 0;
}

.ap-freq-bar-fill {
  width: 100%;
  background: var(--ap-blue);
  border-radius: 2px 2px 0 0;
  opacity: 0.85;
}

/* -------------------------------------------------------------------------
   23. Tracking data
   ---------------------------------------------------------------------- */

/* Numbered pipeline stages. The counter sits in the margin so the prose keeps
   one left edge however many digits the number runs to. */
.ap-pipeline {
  list-style: none;
  counter-reset: stage;
  margin: 0;
  padding: 0;
}

.ap-pipeline > li {
  counter-increment: stage;
  position: relative;
  padding: 0 0 1.25rem 2.5rem;
}

.ap-pipeline > li:last-child { padding-bottom: 0; }

.ap-pipeline > li::before {
  content: counter(stage);
  position: absolute;
  left: 0;
  top: 0.05rem;
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--ap-blue-soft);
  color: var(--ap-blue);
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* A rule joining one stage to the next, so the column reads as a sequence. */
.ap-pipeline > li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 0.8rem;
  top: 1.875rem;
  bottom: 0.375rem;
  width: 1px;
  background: var(--ap-hairline);
}

.ap-pipeline h3 {
  font-size: 0.9375rem;
  margin-bottom: 0.25rem;
}

.ap-pipeline p {
  font-size: 0.9375rem;
  color: var(--ap-ink-3);
  margin-bottom: 0;
  line-height: 1.5;
}

.ap-out-title {
  font-size: 0.9375rem;
  margin-bottom: 0.375rem;
}

/* -------------------------------------------------------------------------
   24. Station siting
   ---------------------------------------------------------------------- */

/* The methods are best read side by side, so this one page has a table. It
   scrolls inside its own box rather than widening the page on a phone. */
.ap-table-wrap {
  overflow-x: auto;
  margin: 0 -0.25rem;
  padding: 0 0.25rem;
}

.ap-method-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
  white-space: nowrap;
}

.ap-method-table th,
.ap-method-table td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--ap-hairline);
  text-align: right;
}

.ap-method-table th:first-child,
.ap-method-table td:first-child {
  text-align: left;
  white-space: normal;
}

.ap-method-table thead th {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--ap-ink-3);
  border-bottom-color: var(--ap-hairline-strong);
}

.ap-method-table tbody td {
  font-family: var(--ap-font-mono);
  font-size: 0.8125rem;
  color: var(--ap-ink-2);
  font-variant-numeric: tabular-nums;
}

.ap-method-table tbody td:first-child {
  font-family: var(--ap-font-sans);
  font-size: 0.875rem;
  color: var(--ap-ink);
}

.ap-method-table tbody tr:last-child td { border-bottom: 0; }

/* The winning figure in each row, so the comparison reads at a glance. */
.ap-method-table td.is-best {
  color: var(--ap-green);
  font-weight: 600;
}

/* The run-time row is context rather than result. */
.ap-method-table tr.is-aside td {
  color: var(--ap-ink-4);
  border-top: 1px solid var(--ap-hairline-strong);
}

/* -------------------------------------------------------------------------
   25. Motion preferences
   ---------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:active { transform: none; }
}
