/* Assets are linked with a ?v= stamp (see the <link> and <script> tags in public/*.html).
   cs.csigrocery.com sits behind Cloudflare, which caches CSS and JS at its edge, so a deploy
   alone does not reach anyone already holding the old file — a change can look live to whoever
   wrote it and be invisible to every member of staff. Bump the stamp on every page whenever this
   file or anything in public/js changes; a new URL is a new cache key, at the edge and in the
   browser both, which is more reliable than remembering to purge. Current: v=20260908a */

@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap');

:root {
  /* Dark theme (default). Brand green/amber sourced from the CSI Grocery logo
     (#3bb54a leaf green, #e99c4a amber) — used as glowing accents on glass
     panels rather than flat fills, per the v2 design direction. */
  color-scheme: dark;
  --bg-0: #05090a;
  --glass: rgba(255, 255, 255, 0.045);
  --glass-border: rgba(59, 181, 74, 0.22);
  --glass-border-strong: rgba(59, 181, 74, 0.42);

  --sage-50: rgba(59, 181, 74, 0.08);
  --sage-100: rgba(59, 181, 74, 0.15);
  --sage-200: rgba(59, 181, 74, 0.24);
  --sage-300: rgba(59, 181, 74, 0.36);
  --sage-500: #3bb54a;
  --sage-600: #4fcf5e;
  --sage-700: #96e6a0;

  --amber: #f0ac68;
  --amber-bg: rgba(233, 156, 74, 0.16);

  --ink: #eaf3ec;
  --ink-soft: #9fb3a6;
  --ink-faint: #64766b;

  --bg: var(--bg-0);
  --card: var(--glass);
  --border: var(--glass-border);
  --input-bg: rgba(255, 255, 255, 0.035);

  --red: #ff8078;
  --red-bg: rgba(255, 90, 80, 0.16);
  --purple: #c9aaff;
  --purple-bg: rgba(160, 120, 255, 0.16);
  --green: #4fcf5e;
  --green-bg: rgba(59, 181, 74, 0.18);
  --grey: #aab4ae;
  --grey-bg: rgba(160, 170, 165, 0.14);

  --radius: 16px;
  --shadow: 0 10px 40px rgba(0, 0, 0, 0.5), 0 2px 10px rgba(0, 0, 0, 0.35);
  --blur: blur(18px);

  --body-bg:
    radial-gradient(1200px 650px at 15% -10%, rgba(59, 181, 74, 0.14), transparent 60%),
    radial-gradient(900px 500px at 90% 8%, rgba(233, 156, 74, 0.08), transparent 55%),
    radial-gradient(800px 500px at 50% 108%, rgba(59, 181, 74, 0.07), transparent 55%),
    var(--bg-0);
}

:root[data-theme="light"] {
  color-scheme: light;
  --sage-50: #eff8f1;
  --sage-100: #dcefe0;
  --sage-200: #b9dfc2;
  --sage-300: #8ecb9e;
  --sage-500: #3bb54a;
  --sage-600: #2e9a3c;
  --sage-700: #013f0d;

  --amber: #e99c4a;
  --amber-bg: #fdf0e0;

  --ink: #14231a;
  --ink-soft: #52655a;
  --ink-faint: #8b988e;

  --bg: #f6f9f6;
  --card: #ffffff;
  --border: #dde7de;
  --input-bg: #eff8f1;

  --red: #dc2626;
  --red-bg: #fee2e2;
  --purple: #7c3aed;
  --purple-bg: #ede9fe;
  --green: #2e9a3c;
  --green-bg: #dcefe0;
  --grey: #6b7280;
  --grey-bg: #f3f4f6;

  --radius: 12px;
  --shadow: 0 1px 3px rgba(1, 63, 13, 0.08), 0 1px 2px rgba(1, 63, 13, 0.05);
  --blur: none;
  --body-bg: var(--bg);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: 'Inter', system-ui, sans-serif;
  background: var(--body-bg);
  background-attachment: fixed;
  color: var(--ink);
  min-height: 100vh;
  transition: background 0.3s ease, color 0.3s ease;
}

@media (prefers-reduced-motion: no-preference) {
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background: inherit;
    animation: drift 24s ease-in-out infinite alternate;
  }
  :root[data-theme="light"] body::before { animation: none; background: none; }
}
@keyframes drift {
  0% { opacity: 0.85; transform: scale(1) translateY(0); }
  100% { opacity: 1; transform: scale(1.04) translateY(-8px); }
}

.shell, .auth-shell { position: relative; z-index: 1; }

h1, h2, h3, .display { font-family: 'Space Grotesk', sans-serif; font-weight: 600; letter-spacing: -0.01em; margin: 0; }
.mono { font-family: 'JetBrains Mono', monospace; }

a { color: var(--sage-600); }

.shell { display: grid; grid-template-columns: 220px 1fr; min-height: 100vh; }

.sidebar {
  padding: 24px 16px;
  border-right: 1px solid var(--border);
  background: var(--card);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.brand { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; }
.brand-logo {
  width: 148px; height: auto; display: block; border-radius: 8px;
  box-shadow: 0 0 18px rgba(59, 181, 74, 0.35);
}
.auth-card .brand-logo { width: 190px; }

.nav-link {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 8px;
  color: var(--ink-soft); text-decoration: none; font-size: 14px; font-weight: 500;
}
.nav-link:hover { background: var(--sage-50); color: var(--ink); }
.nav-link.active { background: var(--sage-100); color: var(--sage-700); }

.nav-spacer { flex: 1; }
.nav-footer { border-top: 1px solid var(--border); padding-top: 12px; margin-top: 12px; }

.main { padding: 28px 32px; overflow-x: auto; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; gap: 16px; flex-wrap: wrap; }
.page-header h1 { font-size: 22px; }
.page-sub { color: var(--ink-soft); font-size: 13px; margin-top: 4px; }

/* Mobile navigation: below this width the fixed sidebar would eat most of the screen, so it
   becomes an off-canvas drawer toggled by a hamburger button (both injected by nav.js). */
#mobile-nav-toggle {
  display: none;
  position: fixed; top: 12px; left: 12px; z-index: 70;
  width: 42px; height: 42px; align-items: center; justify-content: center;
  font-size: 20px; line-height: 1;
  background: var(--card); color: var(--ink);
  border: 1px solid var(--glass-border-strong, var(--border)); border-radius: 10px;
  backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  cursor: pointer;
}
#mobile-nav-backdrop {
  display: none; position: fixed; inset: 0; z-index: 55;
  background: rgba(0, 0, 0, 0.5);
}
#mobile-nav-backdrop.show { display: block; }

@media (max-width: 760px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100vh; width: 240px;
    z-index: 60; transform: translateX(-100%); transition: transform 0.25s ease;
  }
  .sidebar.open { transform: translateX(0); }
  .main { padding: 64px 16px 24px; }
  #mobile-nav-toggle { display: flex; }
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  padding: 20px;
  transition: border-color 0.2s ease;
}
.card:hover { border-color: var(--glass-border-strong, var(--border)); }

.kpi-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 20px; }
@media (max-width: 1100px) { .kpi-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .kpi-grid { grid-template-columns: 1fr; } }
.kpi-tile {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  padding: 16px 18px; transition: border-color 0.2s ease;
}
.kpi-tile:hover { border-color: var(--glass-border-strong, var(--border)); }
.kpi-label { font-size: 12px; color: var(--ink-soft); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.kpi-value { font-family: 'Space Grotesk'; font-size: 26px; font-weight: 700; margin-top: 6px; }
.kpi-sub { font-size: 12px; color: var(--ink-faint); margin-top: 4px; }
.kpi-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.kpi-sparkline { flex-shrink: 0; }
.kpi-gauge-wrap { display: flex; align-items: center; gap: 14px; margin-top: 4px; }
.kpi-gauge-value { font-family: 'Space Grotesk'; font-size: 22px; font-weight: 700; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px; }
@media (max-width: 900px) { .grid-2 { grid-template-columns: 1fr; } }

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
th { text-align: left; padding: 10px 12px; color: var(--ink-soft); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; border-bottom: 1px solid var(--border); }
td { padding: 10px 12px; border-bottom: 1px solid var(--border); transition: background 0.12s ease; }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--sage-50); }

.pill { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.pill-open { background: var(--red-bg); color: var(--red); }
.pill-escalated { background: var(--purple-bg); color: var(--purple); }
.pill-resolved, .pill-recovered { background: var(--green-bg); color: var(--green); }
.pill-not-recovered { background: var(--grey-bg); color: var(--grey); }
.pill-amber { background: var(--amber-bg); color: var(--amber); }
/* Dispatch delivery statuses. statusPillClass() slugifies whatever string it is handed, so
   every value in the dispatch_deliveries status CHECK needs a class here or the chip renders
   as invisible transparent text. 'Picked Up' slugifies to pill-picked-up. */
.pill-unassigned { background: var(--amber-bg); color: var(--amber); }
.pill-assigned { background: var(--purple-bg); color: var(--purple); }
.pill-picked-up { background: var(--green-bg); color: var(--green); }
.pill-delivered { background: var(--green-bg); color: var(--green); }
.pill-failed { background: var(--red-bg); color: var(--red); }
.pill-cancelled { background: var(--grey-bg); color: var(--grey); }

.type-tag { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-soft); }
.type-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }

.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 16px; border-radius: 9px; border: 1px solid transparent;
  font-family: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
  background: var(--sage-500); color: #04140d;
  /* Colour/filter only — these are safe under reduced-motion (it targets spatial motion, not
     fades). The press-down transform lives in the no-preference block at the end of the file. */
  transition: background 0.15s ease, filter 0.15s ease, transform 0.08s ease;
}
.btn:hover { background: var(--sage-600); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-secondary { background: var(--card); color: var(--ink); border-color: var(--border); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur); }
.btn-secondary:hover { background: var(--sage-50); }
.btn-danger { background: var(--red); color: #2a0a08; }
.btn-danger:hover { filter: brightness(0.9); }
/* Light mode swaps --red to a dark #dc2626 — the near-black text above becomes unreadable on
   it, so danger buttons need white text there (dark mode keeps dark-on-salmon). */
:root[data-theme="light"] .btn-danger { color: #ffffff; }
.btn-sm { padding: 5px 10px; font-size: 12.5px; }

/* Order-number field paired with its Look Up button. The global `input { width: 100% }` was
   squeezing the button until its label wrapped onto two lines ("Look / Up"). */
.input-with-btn { display: flex; gap: 6px; align-items: stretch; }
.input-with-btn input { flex: 1; min-width: 0; }
.input-with-btn .btn { flex-shrink: 0; white-space: nowrap; width: auto; }

/* A list of radio choices — delivery options, and anything else picked from a set.
   The global `input { width: 100% }` below turns an unstyled radio into a full-width block that
   shoves its own label off the row and leaves the price sitting on top of the title. Any radio or
   checkbox in this app has to be given its size back explicitly; the same rule already caught the
   Look Up button above. */
.pick-list { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.pick-row { display: flex; align-items: center; gap: 11px; padding: 10px 12px; margin: 0; cursor: pointer; }
.pick-row + .pick-row { border-top: 1px solid var(--border); }
.pick-row:hover { background: var(--input-bg); }
.pick-row input[type="radio"] {
  width: 16px; height: 16px; flex: none; margin: 0; padding: 0;
  accent-color: var(--sage-500); border-radius: 50%;
}
/* Wraps rather than overflowing. Some of this shop's delivery options are a full address, and on
   a narrower window — a Windows laptop, where the scrollbar takes width the Mac does not — a
   nowrap title would push the price out of the row. The price stays on one line either way. */
.pick-row .pick-name { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 500; color: var(--ink); line-height: 1.35; overflow-wrap: anywhere; }
.pick-row .pick-tag { font-size: 11px; font-weight: 700; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .04em; }
.pick-row .pick-cost { flex: none; font-weight: 700; font-size: 13.5px; white-space: nowrap; }

.theme-toggle {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; padding: 7px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--card);
  color: var(--ink-soft); font-size: 12.5px; font-weight: 600; cursor: pointer;
  margin-top: 10px;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--glass-border-strong, var(--border)); }

.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
label { display: block; font-size: 12.5px; font-weight: 600; color: var(--ink-soft); margin-bottom: 5px; }
input, select, textarea {
  width: 100%; padding: 9px 11px; border: 1px solid var(--border); border-radius: 8px;
  font-family: inherit; font-size: 13.5px; background: var(--input-bg); color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--sage-500); }
textarea { resize: vertical; min-height: 60px; }

/* The native <option> popup can't use the translucent --input-bg, so Windows Chrome/Edge fall
   back to a solid white background — which makes this dark theme's light option text unreadable.
   Force a solid, theme-matched colour on every dropdown's options so they're legible everywhere. */
select option { background-color: #0d1512; color: var(--ink); }
:root[data-theme="light"] select option { background-color: #ffffff; color: var(--ink); }

/* Data tables are wider than a phone screen; scroll them inside their own container instead of
   letting them blow the card out sideways and force the whole page to pan. Harmless on desktop
   (no overflow means no scrollbar). */
#entries-table, #staff-table, #sessions-table, #subscriptions-table, #attention-table,
#delay-alerts-table, #recovery-checkins-table, #repeat-contacts-table, #staff-performance-table,
#call-roi-table, #customer-history-content, #dispatch-table { overflow-x: auto; }

/* Action-button cells: keep the td a real table cell (display:flex on a td breaks row height
   and vertical alignment); .btn is already inline-flex so buttons line up inline on their own. */
td.row-actions { white-space: nowrap; }
td.row-actions .btn { margin-right: 6px; vertical-align: middle; }
td.row-actions .btn:last-child { margin-right: 0; }

/* Show/hide password toggle sitting inside the input */
.password-wrap { position: relative; }
.password-wrap input { padding-right: 62px; }
.password-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer;
  color: var(--ink-soft); font-family: inherit; font-size: 12px; font-weight: 600;
  padding: 5px 8px; border-radius: 6px; width: auto;
}
.password-toggle:hover { color: var(--ink); background: var(--sage-50); }

.call-summary { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.summary-stat { background: var(--input-bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px 16px; min-width: 120px; }
.summary-label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-faint); margin-bottom: 4px; }
.summary-value { font-family: 'Space Grotesk', sans-serif; font-size: 20px; font-weight: 700; color: var(--ink); }
.summary-sub { font-size: 13px; font-weight: 500; color: var(--ink-soft); }
.field { margin-bottom: 0; }

.filter-bar { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 16px; }
.filter-bar .field { min-width: 140px; }

.auth-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: transparent; }
.auth-card { width: 360px; padding: 32px; }
.auth-card-wide { width: 440px; }
.backup-codes { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 14px 0; }
.backup-codes span { background: var(--input-bg); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; font-size: 13px; text-align: center; }
.auth-card .brand { margin-bottom: 24px; }
.auth-card h1 { font-size: 18px; margin-bottom: 4px; }
.auth-card .page-sub { margin-bottom: 20px; }
.auth-card .field + .field { margin-top: 14px; }
.auth-card .btn { width: 100%; justify-content: center; margin-top: 18px; }

.error-banner { background: var(--red-bg); color: var(--red); padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 14px; }
.success-banner { background: var(--green-bg); color: var(--green); padding: 10px 14px; border-radius: 8px; font-size: 13px; margin-bottom: 14px; }

/* Padding + overflow so a modal taller than the window can be reached at all. Without them a
   tall modal is centred and clipped at BOTH ends, with no way to scroll to what is cut off —
   which is exactly what happened the first time a modal held a real form.

   Centred with `margin: auto` rather than `align-items: center`, deliberately. A flex item
   centred by alignment and taller than its container overflows EQUALLY in both directions, and
   the part above the top edge cannot be scrolled to at all — scroll offsets do not go negative.
   Auto margins collapse to zero instead when there is no room, so the item starts at the top and
   every part of it stays reachable. Belt and braces alongside the max-height below: if that ever
   fails to apply, this degrades to a scrollable panel rather than an unusable one. */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: flex-start; justify-content: center; z-index: 50; overflow-y: auto; padding: 24px 16px; }
.modal {
  width: 420px; max-width: 90vw; padding: 24px; margin: auto;
  /* dvh where supported: on a phone, 100vh is the window with the browser chrome hidden, so a
     modal sized to it hides its own footer behind the address bar. The vh line stays as the
     fallback for anything that does not know dvh. */
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  background: var(--card); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
}

/* For a modal long enough to need scrolling on its own: the title stays put, the body scrolls,
   and the buttons stay on screen. Scrolling the whole panel instead would push Save off the
   bottom of a long form, which is the one control the user is trying to reach. */
.modal.modal-flow { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.modal.modal-flow > .modal-head { padding: 20px 24px 14px; border-bottom: 1px solid var(--border); flex: none; }
.modal.modal-flow > .modal-head h2 { margin-bottom: 6px; }
.modal.modal-flow > .modal-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px 24px; }
.modal.modal-flow > .modal-actions { margin: 0; padding: 14px 24px; border-top: 1px solid var(--border); flex: none; background: var(--card); }
:root[data-theme="light"] .modal { backdrop-filter: none; -webkit-backdrop-filter: none; }
.modal h2 { font-size: 17px; margin-bottom: 16px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
.hidden { display: none !important; }

.empty-state { text-align: center; padding: 40px 20px; color: var(--ink-faint); font-size: 13.5px; }

.badge-admin { font-size: 10.5px; background: var(--sage-100); color: var(--sage-700); padding: 2px 7px; border-radius: 5px; font-weight: 700; text-transform: uppercase; }
.badge-deleted { font-size: 10.5px; background: var(--red-bg); color: var(--red); padding: 2px 7px; border-radius: 5px; font-weight: 700; text-transform: uppercase; }
.badge-disabled { font-size: 10.5px; background: var(--grey-bg); color: var(--grey); padding: 2px 7px; border-radius: 5px; font-weight: 700; text-transform: uppercase; }
.strikethrough { text-decoration: line-through; color: var(--ink-faint); }

/* Command palette */
.cmdk-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 100; display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh; }
.cmdk-panel {
  width: 560px; max-width: 92vw; background: var(--card); border: 1px solid var(--glass-border-strong, var(--border));
  border-radius: 14px; box-shadow: var(--shadow); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);
  overflow: hidden;
}
:root[data-theme="light"] .cmdk-panel { backdrop-filter: none; -webkit-backdrop-filter: none; }
.cmdk-input {
  width: 100%; border: none; border-bottom: 1px solid var(--border); background: transparent;
  color: var(--ink); font-size: 15px; padding: 16px 18px; font-family: inherit;
}
.cmdk-input:focus { outline: none; }
.cmdk-list { max-height: 320px; overflow-y: auto; padding: 8px; }
.cmdk-item { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; border-radius: 8px; cursor: pointer; font-size: 13.5px; color: var(--ink); }
.cmdk-item.active { background: var(--sage-100); }
.cmdk-item-hint { font-size: 11.5px; color: var(--ink-faint); }
.cmdk-empty { padding: 24px; text-align: center; color: var(--ink-faint); font-size: 13px; }

/* Widget customize panel */
.widget-toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.widget-toggle-row:last-child { border-bottom: none; }
/* Same trap as .pick-row above: the global `input { width: 100% }` stretches this checkbox across
   the whole row, pushing the label it belongs to off the end. */
.widget-toggle-row input[type="checkbox"] {
  width: 16px; height: 16px; flex: none; margin: 0; padding: 0; accent-color: var(--sage-500);
}

/* Funnel */
.funnel-row { display: flex; flex-direction: column; align-items: center; }
.funnel-label { font-size: 12px; color: var(--ink-soft); font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; margin-bottom: 4px; }
.funnel-bar {
  height: 46px; border-radius: 9px; display: flex; align-items: center; justify-content: center;
  color: #04140d; font-weight: 700; font-family: 'Space Grotesk'; font-size: 15px;
  transition: width 0.6s ease; min-width: 90px;
}
.funnel-connector { font-size: 11.5px; color: var(--ink-faint); margin: 6px 0; }

/* Leaderboard */
.leaderboard-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.leaderboard-row:last-child { border-bottom: none; }
.leaderboard-rank { font-family: 'Space Grotesk'; font-weight: 700; font-size: 15px; width: 24px; text-align: center; color: var(--ink-soft); }
.leaderboard-name { flex: 1; font-size: 13.5px; font-weight: 600; }
.leaderboard-metric { font-size: 13px; color: var(--ink-soft); }
.rank-up { color: var(--green); font-weight: 700; }
.rank-down { color: var(--red); font-weight: 700; }
.rank-same { color: var(--ink-faint); }

/* Heatmap */
.heatmap-wrap { display: flex; flex-direction: column; gap: 2px; overflow-x: auto; padding-bottom: 4px; }
.heatmap-hours { display: flex; gap: 2px; padding-left: 34px; }
.heatmap-hour-label { width: 20px; font-size: 9px; color: var(--ink-faint); text-align: center; flex-shrink: 0; }
.heatmap-row { display: flex; gap: 2px; align-items: center; }
.heatmap-day-label { width: 32px; font-size: 11px; color: var(--ink-soft); flex-shrink: 0; }
.heatmap-cell {
  width: 20px; height: 18px; border-radius: 3px; font-size: 8px;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink); flex-shrink: 0; border: 1px solid var(--border);
}

/* Issue Actions */
.issue-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.issue-row:last-child { border-bottom: none; }
.issue-status-open { color: var(--red); }
.issue-status-in-progress { color: var(--amber); }
.issue-status-done { color: var(--green); }

/* Paged list footer */
.pager { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.pager-count { font-size: 12.5px; color: var(--ink-faint); }

/* Motion — functional only, all fast. Gated behind no-preference to match the ambient `drift`
   above: users who ask their OS for reduced motion get none of it, and lose no information (the
   AI suggestion, for instance, still writes its result in text next to the button). This is a
   heavily-used desktop tool, so every duration here is short on purpose — feedback, not flourish. */
@media (prefers-reduced-motion: no-preference) {
  /* Press feedback: a 1px push-down so a click feels acknowledged. Every button, app-wide. */
  .btn:active:not(:disabled) { transform: translateY(1px); }

  /* Modals used to pop in abruptly (display:none -> flex). Fade the backdrop, rise the card. */
  .modal-backdrop { animation: backdrop-in 0.16s ease-out; }
  .modal { animation: modal-in 0.18s ease-out; }
  @keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
  @keyframes modal-in {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
  }

  /* When the AI suggestion fills a dropdown, glow it briefly so the eye lands on what changed.
     Applied per-field by action-log.html; see applySuggestion(). */
  .field-flash { animation: field-flash 1.1s ease-out; }
  @keyframes field-flash {
    0%   { box-shadow: 0 0 0 2px var(--sage-500); }
    100% { box-shadow: 0 0 0 2px transparent; }
  }

  /* A gentle pulse on "Reading the note…" / "Looking up…" so a slow call reads as working, not
     frozen — the same calls were measured at up to 16s on the live server. */
  .is-working { animation: working-pulse 1.1s ease-in-out infinite; }
  @keyframes working-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
}
