/* ═══════════════════════════════════════════════════════════════
   Winston Design System — Dark Mode / School Colors (Red, Blue, White)
   ═══════════════════════════════════════════════════════════════ */

/* ── CSS Custom Properties (Design Tokens) ──────────────────── */
/* Calm, dense ops-tool palette. Saturated school colors are reserved for
   *state and CTA only* — neutral slate carries the chrome. The cyan/violet
   from the Winston hex-flake mark supply the "info"/secondary accent. */
:root {
  /* School palette — accent, used sparingly for CTA + danger */
  --w-red: #dc2626;
  --w-red-light: #f87171;
  --w-red-dark: #991b1b;
  --w-blue: #2563eb;
  --w-blue-light: #60a5fa;
  --w-blue-dark: #1e40af;
  /* Literal white — deliberately NOT overridden by the light theme, because
     its job is contrast against a saturated fill (button/pill backgrounds,
     the toggle knob). Never use it for text on a surface: on a light .card
     that renders white-on-white. Use --w-text-heading there. */
  --w-white: #ffffff;

  /* Brand accents pulled from the hex-flake mark */
  --w-cyan: #22d3ee;
  --w-violet: #7c5cff;
  /* Summer-mode pill. Its own token rather than --w-violet (a fixed brand-mark
     color) so it can take a light-theme variant. */
  --w-purple: #a855f7;

  /* Surfaces */
  --w-bg: #0b0f19;
  --w-bg-card: #111827;
  --w-bg-card-hover: #1a2236;
  --w-bg-sidebar: #0f1629;
  --w-bg-input: #1e293b;
  --w-bg-modal: rgba(0, 0, 0, 0.6);
  /* Secondary surface — was referenced across templates but never declared */
  --w-surface-2: var(--w-bg-card-hover);

  /* Borders */
  --w-border: #1e293b;
  --w-border-light: #334155;
  --w-border-focus: var(--w-blue);

  /* Text */
  --w-text: #e2e8f0;
  --w-text-muted: #94a3b8;
  --w-text-dim: #64748b;
  --w-text-heading: #f8fafc;
  /* Link hover — white reads on the dark chrome; the light theme overrides
     it (below) so hovered links don't vanish on a light background. */
  --w-link-hover: var(--w-white);

  /* Status colors */
  --w-success: #22c55e;
  --w-success-bg: rgba(34, 197, 94, 0.12);
  --w-warning: #f59e0b;
  --w-warning-bg: rgba(245, 158, 11, 0.12);
  /* red-400, not the red-500 the tint is built from: #ef4444 on its own 12%
     tint over the dark card is 4.24:1, just under AA. The tint stays on
     red-500 so the surface keeps its color; only the ink lifts. */
  --w-error: var(--w-red-light);
  --w-error-bg: rgba(239, 68, 68, 0.12);
  /* "info" now reads as the brand cyan rather than a generic blue-500 */
  --w-info: #22d3ee;
  --w-info-bg: rgba(34, 211, 238, 0.12);
  /* Danger — alias of error. Referenced across helpdesk/dashboard templates
     (urgent-ticket titles, Danger-Zone delete, required-field asterisks) but
     was never declared, so the urgency signal silently rendered as plain text. */
  --w-danger: var(--w-error);

  /* Status-token map — single source of truth for the pill component */
  /* These resolve to --w-info, not the raw --w-cyan: the brand cyan is a fixed
     brand value with no light-theme variant, so pills tinted from it sat at
     1.3:1 on a white card. --w-info IS the cyan in dark, so nothing changes
     there. */
  --w-status-open:     var(--w-info);
  --w-status-progress: var(--w-warning);
  --w-status-pending:  var(--w-text-muted);
  --w-status-resolved: var(--w-success);
  /* --w-text-muted, not --w-text-dim: the dim grey is the quietest text token
     in the system and can't survive being tinted 14% into its own pill
     background (3.4:1 light, 3.8:1 dark). The "quiet" states read as quiet
     from the muted tone already — same as .pill.normal and .pill.neutral. */
  --w-status-closed:   var(--w-text-muted);
  --w-prio-urgent:     var(--w-danger);
  --w-prio-high:       var(--w-warning);
  --w-prio-normal:     var(--w-text-muted);
  --w-prio-low:        var(--w-text-muted);
  --w-type-tech:       var(--w-info);
  --w-type-maint:      var(--w-warning);
  /* Ink for .pill.solid — dark, because every tone is bright on this theme. */
  --pill-solid-ink:    var(--w-bg);

  /* Compatibility aliases — these names are referenced across templates
     (helpdesk, eoy, sync, dashboard, logs) but were never declared. Several
     only had light-theme fallbacks baked in at the call site (#f3f4f6 hovers,
     #eff6ff rows, #1e40af text), so on this dark UI they rendered as jarring
     near-white surfaces or low-contrast text. Mapping them to the dark palette
     here fixes all those sites at once — same class of bug as --w-danger. */
  --w-surface:         var(--w-bg-card);
  --w-bg-darker:       var(--w-bg);
  --w-bg-hover:        var(--w-surface-2);
  --w-accent:          var(--w-cyan);
  --w-primary:         var(--w-blue);
  --w-primary-subtle:  rgba(37, 99, 235, 0.12);

  /* Native widget rendering — the select popup, date picker, scrollbars and
     autofill are drawn by the OS and ignore CSS colors. Without this they
     render light-on-dark: a white dropdown list over the dark UI. The light
     theme flips it below. */
  color-scheme: dark;

  /* Sizing — tighter radii for an IDE-adjacent feel */
  --w-sidebar-width: 220px;
  --w-radius: 6px;
  --w-radius-lg: 10px;
  --w-radius-sm: 5px;

  /* Shadows */
  --w-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
  --w-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.2);

  /* Transitions */
  --w-transition: 0.2s ease;

  /* Typography */
  --w-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --w-font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}


/* ── Light theme ──────────────────────────────────────────────
   Winston is dark by default (the :root tokens above). The personal
   Appearance setting switches a user to light by stamping
   data-theme="light" on <html>; "system" applies the same overrides via
   prefers-color-scheme when no theme is pinned. Only the chrome tokens
   (surfaces / borders / text / shadows / link-hover) flip here — the school
   accent, brand, and status colors carry over unchanged from :root, so the
   whole app relights from this one block. The two selectors below share an
   identical token set; keep them in sync. */
:root[data-theme="light"] {
  --w-bg: #f1f5f9;
  --w-bg-card: #ffffff;
  --w-bg-card-hover: #f8fafc;
  --w-bg-sidebar: #ffffff;
  --w-bg-input: #ffffff;
  --w-bg-modal: rgba(15, 23, 42, 0.35);
  --w-surface-2: #eef2f7;

  --w-border: #e2e8f0;
  --w-border-light: #cbd5e1;

  --w-text: #1e293b;
  --w-text-muted: #475569;
  --w-text-dim: #64748b;
  --w-text-heading: #0f172a;
  --w-link-hover: var(--w-blue-dark);

  /* Status colors genuinely need different values per theme rather than one
     shared hue. Each of these drives four surfaces — flash text, pill text on
     the pill's own 14% tint, stat-icon glyphs, and the .text-* utilities — and
     the bright :root values sit at 1.6–3.2:1 on a white card. These are the
     lightest variants of the same hues that clear AA (4.5:1) on all four; the
     tightest is the pill, whose tint darkens along with the text. The *-bg
     tints stay on the bright originals, so the surfaces keep their color
     identity and only the ink changes. */
  --w-warning: #92400e;
  --w-success: #166534;
  --w-error:   #b91c1c;
  --w-info:    #155e75;
  /* Same reasoning — the Summer mode pill is tinted from this. */
  --w-purple:  #6d28d9;
  /* Mirror of the :root rule: the tones are dark here, so the ink is white. */
  --pill-solid-ink: var(--w-white);

  --w-shadow: 0 4px 24px rgba(15, 23, 42, 0.12);
  --w-shadow-sm: 0 2px 8px rgba(15, 23, 42, 0.08);

  color-scheme: light;
}

@media (prefers-color-scheme: light) {
  /* "System" theme: no data-theme attribute is stamped, so follow the OS.
     Gated with :not([data-theme]) so an explicit Dark/Light choice always wins. */
  :root:not([data-theme]) {
    --w-bg: #f1f5f9;
    --w-bg-card: #ffffff;
    --w-bg-card-hover: #f8fafc;
    --w-bg-sidebar: #ffffff;
    --w-bg-input: #ffffff;
    --w-bg-modal: rgba(15, 23, 42, 0.35);
    --w-surface-2: #eef2f7;

    --w-border: #e2e8f0;
    --w-border-light: #cbd5e1;

    --w-text: #1e293b;
    --w-text-muted: #475569;
    --w-text-dim: #64748b;
    --w-text-heading: #0f172a;
    --w-link-hover: var(--w-blue-dark);

    /* Keep in sync with the :root[data-theme="light"] block above. */
    --w-warning: #92400e;
    --w-success: #166534;
    --w-error:   #b91c1c;
    --w-info:    #155e75;
    --w-purple:  #6d28d9;
    --pill-solid-ink: var(--w-white);

    --w-shadow: 0 4px 24px rgba(15, 23, 42, 0.12);
    --w-shadow-sm: 0 2px 8px rgba(15, 23, 42, 0.08);

    color-scheme: light;
  }
}

/* ── Reset & Base ────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* Denser base — ops tools should feel like an IDE, not a marketing site.
   rem-based sizing across the app scales down proportionally. */
html { font-size: 13px; }

body {
  font-family: var(--w-font);
  background: var(--w-bg);
  color: var(--w-text);
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--w-blue-light); text-decoration: none; transition: color var(--w-transition); }
a:hover { color: var(--w-link-hover); }

/* ── Layout Shell ────────────────────────────────────────────── */
.app-layout {
  display: flex;
  min-height: 100vh;
}

/* ── Sidebar ─────────────────────────────────────────────────── */
.sidebar {
  width: var(--w-sidebar-width);
  background: var(--w-bg-sidebar);
  border-right: 1px solid var(--w-border);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 100;
  transition: transform var(--w-transition);
}

/* Brand = the hex-flake mark + a clean Inter wordmark. The mark carries the
   personality on its own; dropping the 220px baked-in lockup reclaims the
   vertical space the long role-gated nav now needs. */
.sidebar-brand {
  padding: 16px 16px 14px;
  border-bottom: 1px solid var(--w-border);
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The brand is a link back to the user's home page — give it a click
   affordance without recoloring the wordmark (its spans set their own color). */
a.sidebar-brand { text-decoration: none; transition: background var(--w-transition); }
a.sidebar-brand:hover { background: var(--w-bg-card-hover); }

.sidebar-mark {
  width: 30px;
  height: 30px;
  border-radius: var(--w-radius);
  overflow: hidden;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #0a1530;
}

.sidebar-mark img {
  width: 30px;
  height: 30px;
  object-fit: contain;
  display: block;
}

.sidebar-wordmark {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.sidebar-name {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--w-text-heading);
  letter-spacing: -0.01em;
  line-height: 1.2;
}

.sidebar-sub {
  font-size: 0.68rem;
  color: var(--w-text-dim);
  font-weight: 400;
}

.sidebar-nav {
  flex: 1;
  padding: 12px 10px;
  overflow-y: auto;
}

.sidebar-section {
  margin-bottom: 8px;
}

.sidebar-section-label {
  padding: 8px 12px 4px;
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--w-text-dim);
}

/* Collapsible variant — the label becomes a button with a chevron */
.sidebar-section.is-collapsible .sidebar-section-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--w-text-dim);
  transition: color var(--w-transition);
}
.sidebar-section.is-collapsible .sidebar-section-toggle:hover {
  color: var(--w-text-heading);
}
.sidebar-section-chevron {
  font-size: 0.7rem;
  margin-right: 6px;
  transition: transform var(--w-transition);
  display: inline-block;
}
.sidebar-section[data-collapsed="true"] .sidebar-section-chevron {
  transform: rotate(-90deg);
}
.sidebar-section-body {
  overflow: hidden;
  max-height: 1000px;
  transition: max-height 220ms ease;
}
.sidebar-section[data-collapsed="true"] .sidebar-section-body {
  max-height: 0;
}

.sidebar-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 12px;
  border-radius: var(--w-radius-sm);
  color: var(--w-text-muted);
  font-size: 0.9rem;
  font-weight: 500;
  transition: all var(--w-transition);
}

.sidebar-link:hover {
  background: var(--w-bg-card);
  color: var(--w-text-heading);
}

/* Active = neutral surface + a restrained accent bar, not saturated chrome.
   Color should mean "state", so "you are here" is a quiet marker, not a fill. */
.sidebar-link.active {
  background: var(--w-bg-card);
  color: var(--w-text-heading);
}

.sidebar-link.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 16px;
  border-radius: 2px;
  background: var(--w-red);
}

/* Icons are now stroked SVGs (currentColor) — they inherit the link color. */
.sidebar-link .icon {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: inherit;
}

.sidebar-link .icon svg {
  width: 16px;
  height: 16px;
  display: block;
}

.sidebar-footer {
  padding: 12px 16px;
  border-top: 1px solid var(--w-border);
  font-size: 0.75rem;
  color: var(--w-text-dim);
}

/* ── Sidebar Overlay (mobile) ────────────────────────────────── */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 99;
}
.sidebar-overlay.visible { display: block; }

/* ── Main Content ────────────────────────────────────────────── */
.main-content {
  flex: 1;
  margin-left: var(--w-sidebar-width);
  min-height: 100vh;
  /* A flex item's automatic minimum is its content's min-content width, so a
     wide table would stretch the whole page (topbar included) past the
     viewport instead of scrolling inside its overflow-x:auto wrapper. */
  min-width: 0;
}

.topbar {
  padding: 12px 24px;
  border-bottom: 1px solid var(--w-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--w-bg);
  position: sticky;
  top: 0;
  z-index: 50;
  backdrop-filter: blur(12px);
}

.topbar-title {
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--w-text-heading);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}

/* ── Topbar sub-sections ─────────────────────────────────────── */
.topbar-modes {
  display: flex;
  align-items: center;
  gap: 8px;
}

.topbar-user-name {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.topbar-signout {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--w-text-dim);
  text-decoration: none;
  padding: 5px 10px;
  border-radius: var(--w-radius);
  border: 1px solid var(--w-border);
  white-space: nowrap;
  transition: background var(--w-transition), color var(--w-transition);
  background: transparent;
  cursor: pointer;
  font-family: inherit;
}
.topbar-signout:hover {
  background: var(--w-surface-2);
  color: var(--w-text);
}

/* Inline SVG glyphs sit on the text baseline and inherit color by default. */
.wi { display: inline-block; vertical-align: -0.18em; }
.btn .wi { vertical-align: middle; }

/* ── Hamburger Button (mobile only) ──────────────────────────── */
.mobile-menu-btn {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  border-radius: var(--w-radius-sm);
  flex-shrink: 0;
}
.mobile-menu-btn span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--w-text);
  border-radius: 2px;
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.mobile-menu-btn.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.mobile-menu-btn.is-open span:nth-child(2) { opacity: 0; }
.mobile-menu-btn.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.page-content {
  padding: 20px 24px;
}

/* Mode badges (top-bar Test/EOY/Summer indicators) are now .pill.upper
   tones — see the unified pill component below. */

/* ── Pill — one token-driven component for every colored status/priority/
   type/role chip. Replaces the five hand-rolled inline-hex systems that had
   drifted apart across helpdesk/dashboard/base templates. Drive it from a
   single tone, never inline hex. Use via the {{ pill(tone, label) }} macro. */
.pill {
  --pill-color: var(--w-text-muted);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--w-radius-sm);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  font-family: var(--w-font-mono);
  line-height: 1.5;
  white-space: nowrap;
  color: var(--pill-color);
  background: color-mix(in srgb, var(--pill-color) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--pill-color) 30%, transparent);
}

.pill::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--pill-color);
  flex-shrink: 0;
}

.pill.no-dot::before { display: none; }

/* Solid variant — for the loudest signal (e.g. urgent priority).
   The ink can't be white in both themes: the tones are bright in dark (white
   on them is 1.8–4.0:1) and dark in light (white is 6.5–7.6:1). --pill-solid-ink
   flips with the theme, so the fill stays the tone's own color and every tone
   clears AA either way. */
.pill.solid {
  color: var(--pill-solid-ink);
  background: var(--pill-color);
  border-color: transparent;
}
.pill.solid::before { background: var(--pill-solid-ink); opacity: 0.85; }

/* Status */
.pill.open        { --pill-color: var(--w-status-open); }
.pill.in_progress { --pill-color: var(--w-status-progress); }
.pill.progress    { --pill-color: var(--w-status-progress); }
.pill.pending     { --pill-color: var(--w-status-pending); }
.pill.resolved    { --pill-color: var(--w-status-resolved); }
.pill.closed      { --pill-color: var(--w-status-closed); }
/* Priority */
.pill.urgent      { --pill-color: var(--w-prio-urgent); }
.pill.high        { --pill-color: var(--w-prio-high); }
.pill.normal      { --pill-color: var(--w-prio-normal); }
.pill.low         { --pill-color: var(--w-prio-low); }
/* Type */
.pill.tech        { --pill-color: var(--w-type-tech); }
.pill.technology  { --pill-color: var(--w-type-tech); }
.pill.maint       { --pill-color: var(--w-type-maint); }
.pill.maintenance { --pill-color: var(--w-type-maint); }
/* Roles */
.pill.admin       { --pill-color: var(--w-danger); }
.pill.tech_team   { --pill-color: var(--w-info); }
.pill.maintenance_lead { --pill-color: var(--w-warning); }
.pill.maintenance_team { --pill-color: var(--w-warning); }
.pill.standard_user    { --pill-color: var(--w-text-muted); }
/* Approval workflow (forms + timesheets) — a submission moves
   draft → submitted/pending → approved | rejected | changes_requested. */
.pill.draft             { --pill-color: var(--w-text-muted); }
.pill.submitted         { --pill-color: var(--w-warning); }
.pill.changes_requested { --pill-color: var(--w-warning); }
.pill.approved          { --pill-color: var(--w-success); }
.pill.rejected          { --pill-color: var(--w-danger); }
/* Generic semantic tones */
.pill.success     { --pill-color: var(--w-success); }
.pill.warning     { --pill-color: var(--w-warning); }
.pill.danger      { --pill-color: var(--w-danger); }
.pill.info        { --pill-color: var(--w-info); }
.pill.neutral     { --pill-color: var(--w-text-muted); }
/* Operational modes (top-bar indicators) */
.pill.test        { --pill-color: var(--w-warning); }
.pill.eoy         { --pill-color: var(--w-info); }
.pill.summer      { --pill-color: var(--w-purple); }
/* Red, unlike the amber .pill.test, because this one means a production job is
   not running tonight — the state you most want to notice from across a room. */
.pill.paused      { --pill-color: var(--w-danger); }
/* Louder, uppercase variant — for the top-bar mode pills */
.pill.upper       { text-transform: uppercase; letter-spacing: 0.05em; }

/* ── Cards ───────────────────────────────────────────────────── */
.card {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 16px;
  transition: border-color var(--w-transition), box-shadow var(--w-transition);
}

.card:hover {
  border-color: var(--w-border-light);
  box-shadow: var(--w-shadow-sm);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

.card-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--w-text-heading);
}

.card-subtitle {
  font-size: 0.78rem;
  color: var(--w-text-dim);
  margin-top: 2px;
}

.card-grid {
  display: grid;
  gap: 16px;
}

/* Auto-filling variant — for grids whose item count varies (form pickers,
   tool launchers) where a fixed column count would leave ragged gaps. */
.card-grid.auto { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

.card-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.card-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.card-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.card-grid.cols-5 { grid-template-columns: repeat(5, 1fr); }

/* ── Stat Cards ──────────────────────────────────────────────── */
.stat-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.stat-icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3rem;
  flex-shrink: 0;
}

/* --w-info, not the raw --w-cyan its siblings don't use: the brand cyan is not
   theme-aware, so this glyph sat at 1.7:1 on a light card while the other
   three followed their status token. Identical in dark — --w-info is the cyan
   there — and legible in light. */
.stat-icon.blue { background: var(--w-info-bg); color: var(--w-info); }
.stat-icon.green { background: var(--w-success-bg); color: var(--w-success); }
.stat-icon.red { background: var(--w-error-bg); color: var(--w-error); }
.stat-icon.amber { background: var(--w-warning-bg); color: var(--w-warning); }
/* For counts that carry no status of their own — an unassigned pile is not a
   warning, it is just work nobody has picked up. */
.stat-icon.slate { background: rgba(148, 163, 184, 0.1); color: var(--w-text-dim); }

/* Stat icons are now stroked SVGs that inherit the chip's color. */
.stat-icon svg { width: 22px; height: 22px; display: block; }

.stat-value {
  font-size: 1.6rem;
  font-weight: 700;
  /* Was --w-white, which is not overridden by the light theme — the figure
     rendered white-on-white on a light .card. --w-text-heading is the same
     near-white in dark mode and legible in light. */
  color: var(--w-text-heading);
  line-height: 1.2;
  /* Figures share a width, so live-updating counters don't jitter. */
  font-variant-numeric: tabular-nums;
}

.stat-label {
  font-size: 0.78rem;
  color: var(--w-text-muted);
}

/* A stat card that filters the list under it. It is an anchor, so it needs the
   text colour and underline reset a .card div never asked for, plus the two
   affordances a plain card has no reason to carry: a keyboard focus ring, and
   a lit state saying "this is the slice you are looking at". */
.stat-filter {
  display: block;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.stat-filter:hover { border-color: var(--w-blue); }

/* Same ring the form controls use, deliberately — a focus indicator that
   differs per widget is one users have to learn twice. */
.stat-filter:focus-visible {
  outline: none;
  border-color: var(--w-blue);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}

.stat-filter.is-active {
  border-color: var(--w-blue);
  background: color-mix(in srgb, var(--w-blue) 8%, var(--w-bg-card));
}

/* ── Health Indicators ───────────────────────────────────────── */
.health-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}

.health-dot.ok { background: var(--w-success); box-shadow: 0 0 6px rgba(34, 197, 94, 0.5); }
.health-dot.warn { background: var(--w-warning); box-shadow: 0 0 6px rgba(245, 158, 11, 0.5); }
.health-dot.error { background: var(--w-error); box-shadow: 0 0 6px rgba(239, 68, 68, 0.5); animation: pulse-error 2s infinite; }
.health-dot.unknown { background: var(--w-text-dim); }

@keyframes pulse-error {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

.health-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--w-border);
}

.health-item:last-child { border-bottom: none; }

.health-label { font-size: 0.88rem; color: var(--w-text); flex: 1; }
.health-status { font-size: 0.78rem; color: var(--w-text-muted); }

/* ── Buttons ─────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 18px;
  border-radius: var(--w-radius);
  font-size: 0.88rem;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  transition: all var(--w-transition);
  font-family: var(--w-font);
  text-decoration: none;
  line-height: 1.4;
}

.btn-primary { background: var(--w-blue); color: var(--w-white); }
.btn-primary:hover { background: var(--w-blue-dark); color: var(--w-white); }

.btn-danger { background: var(--w-red); color: var(--w-white); }
.btn-danger:hover { background: var(--w-red-dark); color: var(--w-white); }

.btn-secondary { background: var(--w-bg-input); color: var(--w-text); border-color: var(--w-border-light); }
.btn-secondary:hover { background: var(--w-border-light); color: var(--w-text-heading); }

.btn-success { background: #16a34a; color: var(--w-white); }
.btn-success:hover { background: #15803d; color: var(--w-white); }

.btn-warning { background: #d97706; color: var(--w-white); }
.btn-warning:hover { background: #b45309; color: var(--w-white); }

.btn-sm { padding: 6px 12px; font-size: 0.78rem; }
.btn-lg { padding: 14px 28px; font-size: 1rem; }
.btn-block { width: 100%; }

.btn:disabled, .btn.disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ── Forms ────────────────────────────────────────────────────── */
.form-group { margin-bottom: 20px; }
/* form-reveal.js folds a group away with [hidden]; stated here so a later
   display rule on .form-group cannot quietly bring it back. */
.form-group[hidden] { display: none; }

.form-label {
  display: block;
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--w-text);
  margin-bottom: 6px;
}

.form-hint {
  font-size: 0.75rem;
  color: var(--w-text-dim);
  margin-top: 4px;
}
/* form-days.js shows its "counted N weekdays" line with [hidden], same
   guard as .form-group above. */
.form-hint[hidden] { display: none; }

/* An optional field folded away behind one line of its own. It reads as a
   question the form is asking, not as a control that has been taken away:
   shut it is a link, open it is an ordinary form group. */
.form-collapse {
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  padding: 10px 14px;
  margin-bottom: 18px;
}
.form-collapse > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--w-text-dim);
  list-style-position: outside;
}
.form-collapse > summary:hover,
.form-collapse > summary:focus-visible { color: var(--w-text); }
.form-collapse[open] > summary {
  color: var(--w-text);
  margin-bottom: 12px;
}
/* Nothing follows the last group inside, so its own margin is the padding. */
.form-collapse > .form-group:last-child { margin-bottom: 0; }

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  padding: 10px 14px;
  font-size: 0.9rem;
  font-family: var(--w-font);
  background: var(--w-bg-input);
  color: var(--w-text);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  transition: border-color var(--w-transition), box-shadow var(--w-transition);
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
  border-color: var(--w-blue);
  outline: none;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}

.form-textarea { min-height: 80px; resize: vertical; }

/* Invalid field — pair with a .form-hint.text-error message below the control. */
.form-input.has-error,
.form-select.has-error,
.form-textarea.has-error { border-color: var(--w-error); }
.form-input.has-error:focus,
.form-select.has-error:focus,
.form-textarea.has-error:focus {
  border-color: var(--w-error);
  box-shadow: 0 0 0 3px var(--w-error-bg);
}

/* Toggle Switch */
.toggle-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid var(--w-border);
}

.toggle-wrap:last-child { border-bottom: none; }

.toggle-info { flex: 1; }
.toggle-name { font-size: 0.9rem; font-weight: 600; color: var(--w-text); }
.toggle-desc { font-size: 0.78rem; color: var(--w-text-dim); margin-top: 2px; }

.toggle {
  position: relative;
  width: 46px;
  height: 26px;
  flex-shrink: 0;
  margin-left: 16px;
}

.toggle input { opacity: 0; width: 0; height: 0; }

.toggle-slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--w-border-light);
  border-radius: 26px;
  transition: background var(--w-transition);
}

.toggle-slider::before {
  content: '';
  position: absolute;
  width: 20px;
  height: 20px;
  left: 3px;
  bottom: 3px;
  background: var(--w-white);
  border-radius: 50%;
  transition: transform var(--w-transition);
}

.toggle input:checked + .toggle-slider { background: var(--w-blue); }
.toggle input:checked + .toggle-slider::before { transform: translateX(20px); }

/* ── Tables ──────────────────────────────────────────────────── */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

thead th {
  padding: 9px 14px;
  text-align: left;
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--w-text-dim);
  background: var(--w-surface-2);
  border-bottom: 1px solid var(--w-border);
}

/* Clickable sortable column headers (see static/js/sortable-tables.js). */
.col-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.col-sort:hover { color: var(--w-primary, #2563eb); }
.col-sort .sort-ind { font-size: 0.72em; margin-left: 2px; color: var(--w-text-dim, #999); }
@media print { .col-sort .sort-ind { display: none !important; } }

tbody td {
  padding: 8px 14px;
  border-bottom: 1px solid var(--w-border);
  color: var(--w-text);
}

/* IDs should look like IDs — barcodes, usernames, ticket #s in monospace. */
tbody td.text-mono { color: var(--w-text-muted); }

tbody tr:last-child td { border-bottom: none; }

tbody tr:hover { background: var(--w-bg-card-hover); }

/* ── Flash Messages ──────────────────────────────────────────── */
.flash {
  padding: 14px 18px;
  border-radius: var(--w-radius);
  margin-bottom: 16px;
  font-size: 0.88rem;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* display:flex above beats the UA [hidden] rule, so restore it — otherwise a
   flash used as a toggled error slot is permanently visible. */
.flash[hidden] { display: none; }

.flash.success { background: var(--w-success-bg); color: var(--w-success); border: 1px solid rgba(34, 197, 94, 0.2); }
.flash.error { background: var(--w-error-bg); color: var(--w-error); border: 1px solid rgba(239, 68, 68, 0.2); }
.flash.warning { background: var(--w-warning-bg); color: var(--w-warning); border: 1px solid rgba(245, 158, 11, 0.2); }
.flash.info { background: var(--w-info-bg); color: var(--w-info); border: 1px solid rgba(59, 130, 246, 0.2); }

/* ── Settings Sections ───────────────────────────────────────── */
.settings-section {
  margin-bottom: 32px;
}

.settings-section-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--w-text-heading);
  margin-bottom: 4px;
}

.settings-section-desc {
  font-size: 0.8rem;
  color: var(--w-text-dim);
  margin-bottom: 16px;
}

/* Configuration schema drift is actionable, but the path list can be dozens
   of rows long after an upgrade. Keep the notice compact and let the operator
   expand a bounded list when they actually need to review it. */
.config-drift-card {
  border-left: 4px solid var(--w-warning);
  background: color-mix(in srgb, var(--w-warning-bg) 34%, var(--w-bg-card));
}

.config-drift-main {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.config-drift-icon {
  display: inline-flex;
  color: var(--w-warning);
  margin-top: 1px;
  flex-shrink: 0;
}

.config-drift-copy {
  flex: 1;
  min-width: 0;
}

.config-drift-copy .settings-section-desc {
  margin-bottom: 6px;
}

.config-drift-action {
  margin-left: 12px;
  flex-shrink: 0;
}

.config-drift-details summary {
  width: fit-content;
  cursor: pointer;
  color: var(--w-info);
  font-size: 0.8rem;
  font-weight: 600;
}

.config-drift-details summary:hover,
.config-drift-details summary:focus-visible {
  color: var(--w-link-hover);
}

.config-drift-list {
  max-height: 240px;
  overflow-y: auto;
  margin: 10px 0 0;
  padding: 10px 10px 10px 28px;
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  background: var(--w-bg);
  color: var(--w-text-muted);
  font-size: 0.78rem;
}

.config-drift-list li + li {
  margin-top: 4px;
}

.config-drift-list code {
  overflow-wrap: anywhere;
}

@media (max-width: 600px) {
  .config-drift-main { flex-wrap: wrap; }
  .config-drift-action {
    flex-basis: calc(100% - 30px);
    margin-left: 30px;
  }
}

/* ── Login Page ──────────────────────────────────────────────── */
.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: var(--w-bg);
}

.login-card {
  width: 100%;
  max-width: 400px;
  padding: 40px;
}

.login-card h1 {
  text-align: center;
  font-size: 1.5rem;
  color: var(--w-text-heading);
  margin-bottom: 8px;
}

.login-card .subtitle {
  text-align: center;
  color: var(--w-text-dim);
  font-size: 0.85rem;
  margin-bottom: 32px;
}

/* ── Responsive ──────────────────────────────────────────────── */
@media (max-width: 900px) {
  .sidebar {
    transform: translateX(-100%);
    z-index: 100;
  }
  .sidebar.open { transform: translateX(0); }
  .main-content { margin-left: 0; }
  .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: repeat(2, 1fr); }
  .card-grid.cols-5 { grid-template-columns: repeat(3, 1fr); }
  .page-content { padding: 20px 16px; }
  .topbar { padding: 12px 16px; }
  .mobile-menu-btn { display: flex; }

  /* Sync pipeline: stack the two-column control panel */
  .sync-layout { grid-template-columns: 1fr !important; }
  .sync-layout > .sync-col-left { border-right: none !important; padding-right: 0 !important; }

  /* EOY scan: stack station info + action buttons */
  .scan-header { flex-direction: column; align-items: flex-start; gap: 10px; }
}

@media (max-width: 600px) {
  .card-grid.cols-2, .card-grid.cols-3, .card-grid.cols-4 { grid-template-columns: 1fr; }
  .card-grid.cols-5 { grid-template-columns: repeat(2, 1fr); }
  .topbar-modes { display: none; }
  .topbar-user-name { display: none; }

  /* EOY scan: wrap action buttons */
  .scan-actions { flex-wrap: wrap; }

  /* Row-per-record tables → card layout on phone. Any table whose cells carry
     data-label attributes opts in with .table-stack; .ticket-table is the
     original helpdesk-specific name, kept so its markup keeps working. */
  /* A stacked table is no longer a grid, so the min-width that keeps columns
     readable on desktop would just force the page to scroll sideways. */
  .table-stack { min-width: 0 !important; }
  .ticket-table thead, .table-stack thead { display: none; }
  .ticket-table tbody tr, .table-stack tbody tr {
    display: block;
    padding: 12px 0;
    border-bottom: 1px solid var(--w-border);
  }
  .ticket-table tbody tr:last-child, .table-stack tbody tr:last-child { border-bottom: none; }
  .ticket-table td, .table-stack td {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 3px 0;
    font-size: 0.88rem;
    white-space: normal !important;
    max-width: none !important;
    overflow: visible !important;
    text-overflow: unset !important;
  }
  .ticket-table td::before, .table-stack td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--w-text-dim);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    min-width: 72px;
    flex-shrink: 0;
    padding-top: 2px;
  }
  .ticket-table td[data-label=""], .table-stack td[data-label=""] { display: none; }
  .ticket-table td[data-label="Summary"] strong { font-size: 0.95rem; }
  /* Numeric columns lose their right-alignment once stacked. */
  .table-stack td.num,
  .table-stack td.text-center { text-align: left !important; }
}

/* ── Utility ─────────────────────────────────────────────────── */
.text-mono { font-family: var(--w-font-mono); }
/* Numeric column — figures line up on the decimal instead of jittering. */
.num { text-align: right; font-variant-numeric: tabular-nums; }
th.num, td.num { text-align: right; }
.text-muted { color: var(--w-text-muted); }
.text-dim { color: var(--w-text-dim); }
.text-success { color: var(--w-success); }
.text-error { color: var(--w-error); }
.text-warning { color: var(--w-warning); }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mt-3 { margin-top: 24px; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }
.mb-3 { margin-bottom: 24px; }
.gap-1 { gap: 8px; }
.gap-2 { gap: 16px; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.flex-wrap { flex-wrap: wrap; }
.w-full { width: 100%; }
.hidden { display: none !important; }

/* ── Loading Overlay ─────────────────────────────────────────── */
.loading-overlay {
  position: fixed;
  inset: 0;
  background: var(--w-bg-modal);
  backdrop-filter: blur(4px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  color: var(--w-text-heading);
  gap: 16px;
  transition: opacity var(--w-transition);
}

/* ── Loading Spinner / Core ──────────────────────────────────── */
.loader-3 {
  width: 80px;
  height: 80px;
  position: relative;
}
.core {
  position: absolute;
  top: 26px; left: 26px;
  width: 28px; height: 28px;
  background: radial-gradient(circle at 30% 30%, #00ffff, #4b0082);
  border-radius: 50%;
  box-shadow: 0 0 15px #8a2be2;
  animation: corePulse 2s ease-in-out infinite alternate;
}
.orbit {
  position: absolute;
  top: 8px; left: 8px;
  width: 64px; height: 64px;
  border: 2px solid rgba(0, 255, 255, 0.1);
  border-radius: 50%;
  animation: spin 4s linear infinite;
}
.orbit2 {
  position: absolute;
  top: 0; left: 0;
  width: 80px; height: 80px;
  border: 1px dashed rgba(138, 43, 226, 0.4);
  border-radius: 50%;
  animation: spinReverse 7s linear infinite;
}
.node1, .node2, .node3 {
  position: absolute;
  width: 8px; height: 8px;
  background: #00ffff;
  border-radius: 50%;
  box-shadow: 0 0 10px #00ffff;
}
.node1 { top: -4px; left: 28px; }
.node2 { bottom: 6px; left: 6px; background: #9400d3; box-shadow: 0 0 10px #9400d3;}
.node3 { top: 34px; right: -4px; background: #1e90ff; box-shadow: 0 0 10px #1e90ff;}
.connection {
  position: absolute;
  top: 40px; left: 40px;
  width: 1px; height: 23px;
  background: linear-gradient(to top, rgba(0,255,255,0.8), transparent);
  transform-origin: top;
  animation: spinReverse 4s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
@keyframes spinReverse { 100% { transform: rotate(-360deg); } }
@keyframes corePulse { 
  0% { transform: scale(0.85); box-shadow: 0 0 10px #4b0082; } 
  100% { transform: scale(1.15); box-shadow: 0 0 25px #00ffff, 0 0 12px #8a2be2; } 
}

.loading-text {
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-size: 0.8rem;
}

/* ═══════════════════════════════════════════════════════════════
   Winston Premium Component Styling & Layout Extensions
   ═══════════════════════════════════════════════════════════════ */

/* ── Container & Grid Layouts ── */
.kb-container,
.warranty-container {
  max-width: 1280px;
  margin: 0 auto;
  animation: fadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ── Knowledge Base Layout ── */
.kb-search-bar {
  display: flex;
  align-items: center;
  gap: 16px;
  background: rgba(17, 24, 39, 0.7);
  backdrop-filter: blur(12px);
  padding: 16px 24px;
  border-radius: var(--w-radius-lg);
  border: 1px solid var(--w-border);
  margin-bottom: 28px;
}

.kb-search-form {
  display: flex;
  flex: 1;
  gap: 12px;
  align-items: center;
}

.kb-search-input {
  flex: 1;
  padding: 11px 16px;
  font-size: 0.95rem;
  background: var(--w-bg-input);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  color: var(--w-text);
  transition: all var(--w-transition);
}
.kb-search-input:focus {
  border-color: var(--w-blue);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.25);
  outline: none;
}

.kb-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 32px;
  align-items: start;
}

.kb-sidebar {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--w-bg-sidebar);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 16px 12px;
}

.kb-sidebar-label {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--w-text-dim);
  padding: 4px 12px 8px;
  border-bottom: 1px solid var(--w-border);
  margin-bottom: 8px;
}

.kb-cat-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 9px 12px;
  border-radius: var(--w-radius);
  color: var(--w-text-muted);
  font-size: 0.9rem;
  font-weight: 500;
  transition: all var(--w-transition);
}
.kb-cat-link:hover {
  background: var(--w-bg-card-hover);
  color: var(--w-text-heading);
  transform: translateX(4px);
}
.kb-cat-link.active {
  background: rgba(37, 99, 235, 0.12);
  color: var(--w-blue-light);
  font-weight: 600;
}
.kb-cat-link.active .kb-cat-count {
  background: var(--w-blue-dark);
  color: var(--w-white);
}

.kb-cat-count {
  background: var(--w-bg-input);
  font-size: 0.72rem;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 10px;
  color: var(--w-text-dim);
  transition: all var(--w-transition);
}

/* ── KB Article Listings ── */
.kb-content {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.kb-results-info {
  font-size: 0.9rem;
  color: var(--w-text-muted);
  margin-bottom: -4px;
}

.kb-article-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.kb-article-card {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 24px;
  transition: all var(--w-transition);
  position: relative;
  overflow: hidden;
}
.kb-article-card:hover {
  border-color: var(--w-border-light);
  transform: translateY(-2px);
  box-shadow: var(--w-shadow);
}

.kb-article-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 12px;
}

.kb-article-title {
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--w-text-heading);
  text-decoration: none;
  line-height: 1.4;
  transition: color var(--w-transition);
}
.kb-article-title:hover {
  color: var(--w-blue-light);
}

/* .kb-article-category folded into .pill (info, upper). */

.kb-article-excerpt {
  color: var(--w-text-muted);
  font-size: 0.92rem;
  line-height: 1.6;
  margin-bottom: 18px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.kb-article-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  font-size: 0.8rem;
  color: var(--w-text-dim);
  align-items: center;
  border-top: 1px solid var(--w-border);
  padding-top: 14px;
}
.kb-article-meta span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.kb-article-actions {
  margin-left: auto;
}

/* ── KB Single Article View ── */
.kb-article-view {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 32px;
}

.kb-article-header-bar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
  border-bottom: 1px solid var(--w-border);
  padding-bottom: 24px;
  margin-bottom: 28px;
}

.kb-back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--w-text-muted);
  margin-bottom: 12px;
  transition: color var(--w-transition);
}
.kb-back-link:hover {
  color: var(--w-blue-light);
}

.kb-article-h1 {
  font-size: 2rem;
  font-weight: 700;
  color: var(--w-text-heading);
  line-height: 1.3;
  margin-bottom: 12px;
  letter-spacing: -0.01em;
}

.kb-article-body {
  font-size: 1rem;
  line-height: 1.7;
  color: var(--w-text);
}

/* ── Markdown Content Styling ── */
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4 {
  color: var(--w-text-heading);
  margin-top: 24px;
  margin-bottom: 12px;
  font-weight: 600;
}
.markdown-content h1 { font-size: 1.5rem; border-bottom: 1px solid var(--w-border); padding-bottom: 8px; }
.markdown-content h2 { font-size: 1.3rem; }
.markdown-content h3 { font-size: 1.1rem; }
.markdown-content p { margin-bottom: 16px; }
.markdown-content ul,
.markdown-content ol { margin-bottom: 18px; padding-left: 24px; }
.markdown-content li { margin-bottom: 6px; }
.markdown-content code {
  background: var(--w-bg-input);
  color: var(--w-red-light);
  padding: 2px 6px;
  border-radius: var(--w-radius-sm);
  font-family: var(--w-font-mono);
  font-size: 0.9rem;
}
.markdown-content pre {
  background: var(--w-bg-input);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  padding: 16px;
  overflow-x: auto;
  margin-bottom: 18px;
}
.markdown-content pre code {
  background: transparent;
  padding: 0;
  color: var(--w-text);
}
.markdown-content blockquote {
  border-left: 4px solid var(--w-blue);
  padding-left: 16px;
  color: var(--w-text-muted);
  margin: 16px 0;
  font-style: italic;
}

/* ── Article attachments (corporation documents) ── */
.kb-attachments {
  margin-top: 24px;
  border-top: 1px solid var(--w-border);
  padding-top: 16px;
}
.kb-attachments-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--w-text-dim);
  margin-bottom: 10px;
}
.kb-attachment-list,
.file-queue {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kb-attachment {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 8px 12px;
}
.kb-attachment-link,
.file-queue-file {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
.kb-attachment-link:hover .kb-attachment-name {
  text-decoration: underline;
}
.kb-attachment-ext {
  flex: none;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 3px 6px;
  border-radius: 4px;
  background: rgba(59, 130, 246, 0.15);
  color: var(--w-blue-light);
  min-width: 42px;
  text-align: center;
}
.kb-attachment-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.kb-attachment-meta {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.8rem;
  color: var(--w-text-dim);
}
/* Files chosen but not yet uploaded (file-queue.js), listed under the picker. */
.file-queue {
  margin-top: 8px;
}
.file-queue-note {
  margin-top: 6px;
}

.kb-pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 16px;
  margin-top: 12px;
}
.kb-page-info {
  font-size: 0.85rem;
  color: var(--w-text-dim);
}

.kb-empty,
.warranty-empty {
  background: var(--w-bg-card);
  border: 1px dashed var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 48px;
  text-align: center;
  color: var(--w-text-muted);
}
.kb-empty p,
.warranty-empty p {
  margin-bottom: 16px;
  font-size: 1rem;
}

/* ── Form layouts ── */
.kb-form-wrapper {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 32px;
  max-width: 720px;
  margin: 0 auto;
}

.kb-form fieldset {
  border: 1px solid var(--w-border);
  background: rgba(17, 24, 39, 0.4);
  padding: 20px;
  border-radius: var(--w-radius-lg);
  margin-bottom: 24px;
}
.kb-form legend {
  padding: 0 10px;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--w-blue-light);
}

.form-row {
  display: flex;
  gap: 16px;
}
.form-row .form-group {
  flex: 1;
}

.form-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  border-top: 1px solid var(--w-border);
  padding-top: 20px;
  margin-top: 12px;
}

/* ── Warranty & Lifecycle Summary Cards ── */
.warranty-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-bottom: 28px;
}

.warranty-card {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 20px;
  transition: all var(--w-transition);
  position: relative;
  overflow: hidden;
}
.warranty-card:hover {
  border-color: var(--w-border-light);
  transform: translateY(-2px);
}
.warranty-card::after {
  content: '';
  position: absolute;
  top: 0; left: 0; width: 4px; height: 100%;
}
.warranty-card.danger::after  { background: var(--w-error); }
.warranty-card.warning::after { background: var(--w-warning); }
.warranty-card.info::after    { background: var(--w-info); }
.warranty-card.success::after { background: var(--w-success); }
.warranty-card.neutral::after { background: var(--w-text-dim); }
.warranty-card.total::after   { background: var(--w-blue); }

.wc-number {
  font-size: 2.2rem;
  font-weight: 700;
  color: var(--w-text-heading);
  line-height: 1.1;
  margin-bottom: 4px;
}
.wc-label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--w-text-muted);
}
.wc-sub {
  font-size: 0.72rem;
  color: var(--w-text-dim);
  margin-top: 4px;
}

/* ── Warranty Toolbar & Tabs ── */
.warranty-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.warranty-tabs,
.tab-bar {
  display: flex;
  flex-wrap: wrap;
  background: var(--w-bg-sidebar);
  border: 1px solid var(--w-border);
  padding: 4px;
  border-radius: var(--w-radius);
  gap: 2px;
  overflow-x: auto;
}

.wt-tab,
.tab-link {
  padding: 8px 16px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--w-text-muted);
  border-radius: var(--w-radius-sm);
  transition: all var(--w-transition);
  white-space: nowrap;
}
.wt-tab:hover,
.tab-link:hover {
  color: var(--w-text-heading);
  background: var(--w-bg-card-hover);
}
.wt-tab.active,
.tab-link.active {
  background: var(--w-blue);
  color: var(--w-white);
}

.warranty-search {
  display: flex;
  gap: 8px;
  align-items: center;
}
.ws-input {
  padding: 8px 14px;
  font-size: 0.85rem;
  background: var(--w-bg-input);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius);
  color: var(--w-text);
  width: 240px;
  transition: border-color var(--w-transition);
}
.ws-input:focus {
  border-color: var(--w-blue);
  outline: none;
}

.warranty-table-wrapper {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  overflow: hidden;
}

.warranty-table {
  width: 100%;
  border-collapse: collapse;
}
.warranty-table th {
  padding: 12px 16px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--w-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  background: rgba(17, 24, 39, 0.8);
  border-bottom: 1px solid var(--w-border);
  text-align: left;
}
.warranty-table td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--w-border);
  font-size: 0.88rem;
  color: var(--w-text);
}
.warranty-table tr:last-child td {
  border-bottom: none;
}
.warranty-table tr:hover {
  background: var(--w-bg-card-hover);
}

.warranty-table tr.row-danger td {
  background: rgba(239, 68, 68, 0.03);
}
.warranty-table tr.row-warning td {
  background: rgba(245, 158, 11, 0.02);
}

/* ── Timesheet grids: a day whose hours (worked plus leave) add up past the
   long-day threshold. Shaded, never refused — see timesheet_service.long_day_hours. ── */
/* The district calendar on the same grids: a closed day is marked so nobody
   files a Monday's hours on Labor Day; weekends are only dimmed. Declared
   before the flags below so a flagged row always reads amber. See
   timesheet_service.holidays. */
tr.ts-weekend td { background: var(--w-surface-2); }
tr.ts-holiday td { background: color-mix(in srgb, var(--w-blue) 8%, var(--w-bg-card)); }
.pill.holiday { --pill-color: var(--w-blue); }
tr.ts-holiday-hours td,
tr.ts-long-day td { background: var(--w-warning-bg); }
tr.ts-holiday-hours td.day,
tr.ts-long-day td.day { color: var(--w-warning); font-weight: 600; }

/* ── Badges → folded into the unified .pill component (see above). The legacy
   .badge / .badge-* classes (several of which were never defined and rendered
   as plain grey) are gone; all chips now use {{ pill(tone, label) }} or
   `class="pill <tone>"`. ── */

/* ── Loaners Header ── */
.loaner-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  margin-bottom: 24px;
}
.loaner-summary {
  flex: 0 1 240px;
}

/* ── Ticket Automation Rules Layout ── */
.rules-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 28px;
}

.rules-info {
  background: var(--w-info-bg);
  color: var(--w-info);
  border: 1px solid rgba(59, 130, 246, 0.2);
  border-radius: var(--w-radius-lg);
  padding: 16px 20px;
}
.rules-info h4 {
  font-size: 0.95rem;
  font-weight: 600;
  margin-bottom: 4px;
  color: var(--w-blue-light);
}
.rules-info p {
  font-size: 0.85rem;
  line-height: 1.5;
}

.rules-add-form {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 24px;
}
.rules-add-form h3,
.rules-list h3 {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--w-text-heading);
  margin-bottom: 18px;
  border-bottom: 1px solid var(--w-border);
  padding-bottom: 8px;
}

.rules-list {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 24px;
}

.form-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.form-check input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
}
.form-check label {
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--w-text);
  cursor: pointer;
}

.row-disabled td {
  opacity: 0.45;
}
tr.row-disabled:hover {
  background: transparent;
}

/* ── Rules Sandbox / Testing Panel ── */
.rules-sandbox {
  background: linear-gradient(135deg, rgba(15, 22, 41, 0.8), rgba(17, 24, 39, 0.9));
  border: 1px solid var(--w-border-light);
  border-radius: var(--w-radius-lg);
  padding: 24px;
  margin-top: 16px;
  box-shadow: var(--w-shadow);
}
.rules-sandbox-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--w-text-heading);
  margin-bottom: 14px;
}
.rules-sandbox-form {
  display: grid;
  grid-template-columns: 2fr 3fr auto;
  gap: 16px;
  align-items: end;
}
.rules-sandbox-result {
  grid-column: 1 / -1;
  padding: 12px 16px;
  border-radius: var(--w-radius);
  font-size: 0.88rem;
  background: rgba(17, 24, 39, 0.9);
  border: 1px solid var(--w-border);
  margin-top: 12px;
  min-height: 44px;
  display: flex;
  align-items: center;
}

/* Sandbox Rule Highlight Animation */
@keyframes rowMatchGlow {
  0% { background-color: rgba(34, 197, 94, 0); box-shadow: none; }
  30% { background-color: rgba(34, 197, 94, 0.15); box-shadow: inset 0 0 10px rgba(34, 197, 94, 0.3); }
  100% { background-color: rgba(34, 197, 94, 0.05); }
}
.rule-row-match {
  animation: rowMatchGlow 1.8s ease-out forwards;
}

/* ── Global Reusable Barcode Scanner Modal ── */
.scanner-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.scanner-modal.active {
  opacity: 1;
  pointer-events: auto;
}
.scanner-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(8px);
}
.scanner-modal-content {
  position: relative;
  background: #000;
  border: 1px solid var(--w-border-light);
  border-radius: var(--w-radius-lg);
  padding: 24px;
  width: 90%;
  max-width: 460px;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  z-index: 10;
}
.scanner-modal-content h3 {
  color: var(--w-white);
  margin: 0;
  font-size: 1.1rem;
  font-weight: 600;
}
.scanner-viewport {
  width: 100%;
  border-radius: 8px;
  overflow: hidden;
  aspect-ratio: 4/3;
  background: #111;
  border: 1px solid var(--w-border);
}

.scanner-hint {
  color: var(--w-text-muted);
  font-size: 0.85rem;
  text-align: center;
  line-height: 1.4;
}
.scanner-error {
  color: var(--w-error);
  font-size: 0.85rem;
  text-align: center;
}

.scanner-input-group {
  position: relative;
  display: flex;
  align-items: center;
}
.scanner-input-group input {
  padding-right: 40px !important;
}
.scanner-input-btn {
  position: absolute;
  right: 6px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  font-size: 1.15rem;
  opacity: 0.65;
  transition: opacity 0.15s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--w-text);
}
.scanner-input-btn:hover {
  opacity: 1;
}

/* responsive rule sandbox layout */
@media (max-width: 768px) {
  .rules-sandbox-form {
    grid-template-columns: 1fr;
    gap: 12px;
  }
}

/* ═══════════════════════════════════════════════════════════════
   Winston Resources Mobile Responsive & Overflow Safeguards
   ═══════════════════════════════════════════════════════════════ */

.warranty-table-wrapper {
  overflow-x: auto !important;
  -webkit-overflow-scrolling: touch;
}

.warranty-card {
  word-wrap: break-word !important;
  overflow: hidden !important;
}

@media (max-width: 900px) {
  html, body {
    max-width: 100vw !important;
    overflow-x: hidden !important;
  }
  .main-content {
    max-width: 100vw !important;
    overflow-x: hidden !important;
    min-width: 0 !important;
  }
  .page-content {
    max-width: 100vw !important;
    overflow-x: hidden !important;
    padding: 20px 16px !important;
  }
  .kb-container,
  .warranty-container {
    max-width: 100vw !important;
    overflow-x: hidden !important;
  }
}

@media (max-width: 768px) {
  /* ── Tables → stacked cards on mobile ─────────────────────────────────
     Any <table class="warranty-table"> with rows whose <td>s carry
     data-label attributes collapses into vertical cards. Default = every
     row is a self-contained card. The mismatch table uses a continuation
     pattern (main row + .note-row right after) — those two visually merge
     into one card.

     Selector strategy:
       * default tr -> full card
       * tr immediately followed by .note-row -> "card top" (no bottom radius/margin/border)
       * tr.note-row -> "card bottom" (no top radius/margin, flush with the row above)
  */
  .warranty-table thead { display: none !important; }
  .warranty-table-wrapper {
    border: none !important;
    background: transparent !important;
    border-radius: 0 !important;
    overflow: visible !important;
  }

  /* Default: every row is a full standalone card */
  .warranty-table tbody tr {
    display: block !important;
    padding: 14px 16px !important;
    margin-bottom: 12px !important;
    background: var(--w-bg-card) !important;
    border: 1px solid var(--w-border) !important;
    border-radius: var(--w-radius) !important;
  }

  /* Continuation pattern — only fires for the mismatch notes row */
  .warranty-table tbody tr:has(+ tr.note-row) {
    margin-bottom: 0 !important;
    border-bottom: none !important;
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
  }
  .warranty-table tbody tr.note-row {
    margin-top: 0 !important;
    padding-top: 0 !important;
    border-top: none !important;
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
  }

  /* Cell styling — label/value row inside each card */
  .warranty-table td {
    display: flex !important;
    justify-content: space-between !important;
    align-items: center !important;
    padding: 8px 0 !important;
    border-bottom: 1px solid rgba(255, 255, 255, 0.03) !important;
    font-size: 0.88rem !important;
    text-align: right !important;
    min-height: 38px !important;
    white-space: normal !important;
    max-width: none !important;
    overflow: visible !important;
  }
  .warranty-table td:last-child {
    border-bottom: none !important;
  }
  .warranty-table td::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--w-text-dim);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    text-align: left;
    margin-right: 12px;
    flex-shrink: 0;
  }
  /* Cells with no label (utility cells, note continuation) drop the prefix */
  .warranty-table td:not([data-label])::before,
  .warranty-table td[data-label=""]::before {
    content: none !important;
  }
  .warranty-table td:not([data-label]),
  .warranty-table td[data-label=""] {
    justify-content: flex-start !important;
    text-align: left !important;
  }
  .warranty-table td strong { font-size: 0.95rem !important; }
  .warranty-table td[data-label="Actions"] {
    justify-content: flex-end !important;
    gap: 8px !important;
    flex-wrap: wrap !important;
  }

  /* Summary cards clean distribution */
  .warranty-summary {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 12px !important;
  }

  /* Toolbar items stacking */
  .warranty-toolbar {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 16px !important;
  }
  .warranty-tabs,
  .tab-bar {
    width: 100% !important;
  }
  .warranty-search {
    width: 100% !important;
  }
  .warranty-search form {
    width: 100% !important;
  }
  .ws-input {
    width: 100% !important;
    box-sizing: border-box !important;
  }

  /* Knowledge Base search and filter stacking */
  .kb-search-bar {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 12px !important;
    padding: 16px !important;
  }
  .kb-search-form {
    flex-direction: column !important;
    align-items: stretch !important;
    width: 100% !important;
    gap: 10px !important;
  }
  .kb-search-form button,
  .kb-search-form a {
    width: 100% !important;
    box-sizing: border-box !important;
  }
  .kb-search-bar > .btn-primary {
    width: 100% !important;
    margin-left: 0 !important;
    margin-top: 4px !important;
  }
}

@media (max-width: 480px) {
  .warranty-summary {
    grid-template-columns: 1fr !important;
  }
}

/* ═══════════════════════════════════════════════════════════════
   SRO Safety Module — Mobile-First Styles
   ═══════════════════════════════════════════════════════════════ */

/* ── Chip Grid (activity type / risk level selectors) ─────────── */
.chip-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.chip {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 10px;
  border-radius: 10px;
  border: 2px solid var(--w-border, #334155);
  background: var(--w-bg-input);
  color: var(--w-text, #e2e8f0);
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
  text-align: center;
  min-height: 48px;
}
.chip:hover {
  border-color: var(--w-blue-light);
}
.chip-active {
  border-color: var(--w-blue);
  background: rgba(37, 99, 235, 0.15);
  color: var(--w-blue-light);
}
.chip input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* ── Duration Control ─────────────────────────────────────────── */
.duration-control {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: center;
}
.duration-control .btn {
  min-width: 48px;
  min-height: 48px;
  font-size: 1.2rem;
  border-radius: 10px;
}

/* ── Student Search ───────────────────────────────────────────── */
.student-search-bar {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.student-search-bar .form-input {
  flex: 1;
}
.student-results {
  position: absolute;
  z-index: 100;
  background: var(--w-bg-card);
  border: 1px solid var(--w-border, #334155);
  border-radius: 10px;
  max-height: 240px;
  overflow-y: auto;
  width: 100%;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.student-result-item {
  padding: 12px 16px;
  cursor: pointer;
  border-bottom: 1px solid var(--w-border, #1e293b);
  min-height: 48px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.student-result-item:hover {
  background: var(--w-bg-card-hover);
}
.student-result-item:last-child {
  border-bottom: none;
}
.selected-students {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}
.selected-student-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: rgba(37, 99, 235, 0.15);
  border: 1px solid var(--w-blue);
  border-radius: 20px;
  font-size: 0.85rem;
  color: var(--w-blue-light);
}
.selected-student-chip .btn-icon {
  padding: 2px 6px;
  min-width: auto;
  min-height: auto;
  font-size: 0.8rem;
  background: transparent;
  color: var(--w-blue-light);
  border: none;
}

/* ── FAB (Floating Action Button — mobile quick-log) ──────────── */
.fab-mobile {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 200;
  display: none;
  padding: 14px 20px;
  background: var(--w-blue);
  color: #fff;
  border-radius: 28px;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 4px 16px rgba(37, 99, 235, 0.4);
  transition: transform 0.15s ease;
}
.fab-mobile:hover {
  transform: scale(1.05);
  color: #fff;
}

/* ── Activity Feed (dashboard) ────────────────────────────────── */
.activity-feed {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.activity-feed-item {
  display: block;
  padding: 12px 14px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  transition: background 0.15s ease;
  border-bottom: 1px solid var(--w-border, #1e293b);
}
.activity-feed-item:hover {
  background: var(--w-bg-card-hover);
}
.activity-feed-item:last-child {
  border-bottom: none;
}
.activity-feed-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 4px;
}
.activity-feed-body {
  font-size: 0.9rem;
}
.activity-feed-students {
  margin-top: 4px;
}

/* ── Detail View ──────────────────────────────────────────────── */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin: 16px 0;
}
/* A grid item defaults to min-width:auto, so one unbreakable string -- a
   pasted product URL in a contact box, a Google Docs link in a justification --
   sets the whole track's width and pushes the card off the page. Both halves
   are needed: min-width lets the track shrink, overflow-wrap lets the string
   break somewhere other than a space it does not contain. */
.detail-item { min-width: 0; }
.detail-item > div, .detail-item a, .detail-notes { overflow-wrap: anywhere; }
.detail-item label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--w-text-dim, #94a3b8);
  margin-bottom: 4px;
}
.detail-section {
  margin: 16px 0;
}
.detail-section label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--w-text-dim, #94a3b8);
  margin-bottom: 8px;
}
.detail-notes {
  white-space: pre-wrap;
  line-height: 1.6;
}

/* ── Risk Chips (BTAM risk level selector) ─────────────────────── */
.risk-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.risk-chip {
  display: flex;
  align-items: center;
  padding: 12px 20px;
  border-radius: 24px;
  border: 2px solid var(--w-border, #334155);
  background: var(--w-bg-input);
  color: var(--w-text, #e2e8f0);
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s ease;
  min-height: 48px;
}
.risk-chip:hover {
  border-color: var(--w-blue-light);
}
.risk-chip input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.risk-chip:has(input:checked) {
  border-color: var(--w-blue);
  background: rgba(37, 99, 235, 0.15);
}
.risk-low:has(input:checked) { border-color: #22c55e; background: rgba(34, 197, 94, 0.15); }
.risk-moderate:has(input:checked) { border-color: #eab308; background: rgba(234, 179, 8, 0.15); }
.risk-high:has(input:checked) { border-color: #f97316; background: rgba(249, 115, 22, 0.15); }
.risk-imminent:has(input:checked) { border-color: #ef4444; background: rgba(239, 68, 68, 0.15); }

/* ── Timeline (BTAM incident timeline) ─────────────────────────── */
.timeline {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 16px 0;
}
.timeline-item {
  display: flex;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--w-border, #1e293b);
}
.timeline-item:last-child {
  border-bottom: none;
}
.timeline-marker {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--w-blue);
  margin-top: 6px;
  flex-shrink: 0;
  border: 2px solid var(--w-bg-card);
  box-shadow: 0 0 0 2px var(--w-blue);
}
.timeline-content {
  flex: 1;
}
.timeline-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
  flex-wrap: wrap;
}
.timeline-body {
  font-size: 0.9rem;
  line-height: 1.5;
}
.timeline-action {
  margin-top: 8px;
  font-size: 0.85rem;
  color: var(--w-text-dim, #94a3b8);
}
.student-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.student-chip {
  display: inline-block;
  padding: 6px 14px;
  background: var(--w-bg-input);
  border: 1px solid var(--w-border, #334155);
  border-radius: 20px;
  font-size: 0.85rem;
  text-decoration: none;
  color: var(--w-text, #e2e8f0);
}
.student-chip:hover {
  border-color: var(--w-blue);
  color: var(--w-blue-light);
}

/* ── Form Layout ──────────────────────────────────────────────── */
.form-row {
  display: flex;
  gap: 12px;
}
.form-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--w-border, #1e293b);
}
.form-group {
  margin-bottom: 16px;
}
.form-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 500;
  margin-bottom: 6px;
  color: var(--w-text, #e2e8f0);
}
.form-label.required::after {
  content: " *";
  color: var(--w-red);
}
.filter-bar {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
}
.filter-group {
  flex: 1;
  min-width: 140px;
}
.filter-group-check {
  display: flex;
  align-items: flex-end;
  padding-bottom: 4px;
}
.filter-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}
/* Type-ahead dropdown — anchor it on a position:relative wrapper (usually the
   .filter-group holding the input). */
.typeahead-wrap {
  position: relative;
}
.typeahead-results {
  display: none;
  position: absolute;
  z-index: 100;
  left: 0;
  right: 0;
  margin-top: 2px;
  max-height: 280px;
  overflow-y: auto;
  background: var(--w-surface);
  border: 1px solid var(--w-border);
  border-radius: 6px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
.typeahead-results.open {
  display: block;
}
.typeahead-item {
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid var(--w-border);
  font-size: 0.875rem;
}
.typeahead-item:last-child {
  border-bottom: none;
}
.typeahead-item:hover,
.typeahead-item.active {
  background: var(--w-surface-2);
}
.typeahead-empty {
  padding: 12px 14px;
  color: var(--w-text-dim);
  font-size: 0.875rem;
}
.check-label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 0.9rem;
}
.check-label input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--w-blue);
}
.check-label-lg {
  font-size: 1rem;
  padding: 8px 0;
}
.check-label-lg input[type="checkbox"] {
  width: 22px;
  height: 22px;
}

/* ── Card Header ──────────────────────────────────────────────── */
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--w-border, #1e293b);
}
.card-header h2 {
  margin: 0;
}
.card-actions {
  display: flex;
  gap: 8px;
}
.card-link {
  display: block;
  padding: 12px 16px;
  background: var(--w-bg-input);
  border-radius: 8px;
  text-decoration: none;
  color: var(--w-text, #e2e8f0);
  border: 1px solid var(--w-border, #334155);
}
.card-link:hover {
  border-color: var(--w-blue);
}

/* ── FAB Actions (dashboard quick actions) ─────────────────────── */
.fab-actions {
  display: none;
  margin-top: 20px;
  text-align: center;
}

/* ── Responsive: Mobile (< 640px) ─────────────────────────────── */
@media (max-width: 640px) {
  .chip-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .form-row {
    flex-direction: column;
  }
  .filter-row {
    flex-direction: column;
  }
  .filter-group {
    min-width: 100%;
  }
  .filter-actions {
    flex-wrap: wrap;
  }
  .form-actions {
    flex-direction: column-reverse;
  }
  .form-actions .btn {
    width: 100%;
  }
  .card-header {
    flex-direction: column;
    gap: 12px;
  }
  .card-actions {
    width: 100%;
  }
  .card-actions .btn {
    flex: 1;
  }
  .detail-grid {
    grid-template-columns: 1fr;
  }
  .fab-mobile {
    display: block;
  }
  .fab-actions {
    display: block;
  }
  /* Make tables scrollable on mobile */
  .table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Stack stat cards */
  .card-grid.cols-3 {
    grid-template-columns: 1fr;
  }
  .card-grid.cols-2 {
    grid-template-columns: 1fr;
  }
}

/* ── Responsive: Tablet (640-1024px) ──────────────────────────── */
@media (min-width: 641px) and (max-width: 1024px) {
  .chip-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .card-grid.cols-3 {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── Utility ──────────────────────────────────────────────────── */
.hidden { display: none !important; }
.ml-1 { margin-left: 4px; }
.ml-auto { margin-left: auto; }
.mt-1 { margin-top: 4px; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 12px; }
.p-2 { padding: 8px; }
.p-3 { padding: 12px; }
.text-sm { font-size: 0.85rem; }
.text-center { text-align: center; }
/* Was --w-red-light, which this second (later, therefore winning) utility
   block quietly overrode the real .text-error with. --w-red-light is not
   theme-aware, so it rendered at 2.8:1 on a light card. */
.text-error { color: var(--w-error); }

/* ── Offline / Staleness Banners ─────────────────────────────────── */
.offline-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: #f59e0b;
  color: #000;
  padding: 10px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 500;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.offline-banner .offline-icon { font-size: 1.1rem; }
.offline-banner .offline-text { flex: 1; }
.offline-banner .offline-staleness {
  font-size: 0.75rem;
  opacity: 0.8;
}

.staleness-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 999;
  background: #1e40af;
  color: #fff;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 0.8rem;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.staleness-banner .btn { flex-shrink: 0; }

/* When offline banner is visible, push staleness banner below it */
.offline-banner:not(.hidden) ~ .staleness-banner {
  top: 36px;
}

/* ── SRO mobile bottom navigation (phones only) ──────────────────────── */
.sro-bottom-nav { display: none; }

@media (max-width: 640px) {
  .sro-bottom-nav {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    z-index: 600;
    background: var(--w-bg-card, #fff);
    border-top: 1px solid var(--w-border, #e2e8f0);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.12);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }
  .sro-bottom-nav a {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 56px;
    padding: 6px 4px;
    text-decoration: none;
    color: var(--w-text-dim, #64748b);
    font-size: 0.7rem;
  }
  .sro-bottom-nav a.active { color: var(--w-accent, #2563eb); font-weight: 600; }
  .sro-bottom-nav .sro-bn-icon { font-size: 1.25rem; line-height: 1; }
  /* Keep the last bit of page content clear of the fixed bar. */
  body .main-content,
  body .content,
  body main { padding-bottom: 72px; }
}

/* ── Safety photo thumbnails ──────────────────────────────────────────── */
.photo-thumbs,
.safety-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.photo-thumb {
  position: relative;
  display: inline-block;
  width: 88px;
  height: 88px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--w-border, #e2e8f0);
  background: var(--w-bg-alt, #f1f5f9);
}
.photo-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.photo-thumb-del {
  position: absolute;
  top: 2px; right: 2px;
  width: 22px; height: 22px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
}

/* ── SRO offline write-queue status banner ───────────────────────────── */
.attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  max-width: 100%;
  border: 1px solid var(--w-border, #e2e8f0);
  border-radius: 8px;
  background: var(--w-bg-alt, #f1f5f9);
  overflow: hidden;
}
.attachment-chip a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 9px 10px;
  color: var(--w-text);
  text-decoration: none;
}
.attachment-chip a:hover {
  background: var(--w-bg-hover, rgba(37, 99, 235, 0.12));
}
.attachment-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--w-blue-light);
}
.attachment-name {
  min-width: 0;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attachment-delete {
  align-self: stretch;
  border: 0;
  border-left: 1px solid var(--w-border, #e2e8f0);
  background: transparent;
  color: var(--w-text-muted);
  padding: 0 9px;
  cursor: pointer;
}
.attachment-delete:hover {
  color: var(--w-danger, #dc2626);
  background: rgba(220, 38, 38, 0.08);
}

.sro-sync-status {
  padding: 10px 14px;
  border-radius: 8px;
  margin-bottom: 14px;
  font-size: 0.9rem;
  font-weight: 500;
  border: 1px solid transparent;
}
.sro-sync-status.hidden { display: none; }
.sro-sync-info,
.sro-sync-pending { background: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
.sro-sync-offline { background: #fef3c7; color: #92400e; border-color: #fde68a; }
.sro-sync-ok      { background: #dcfce7; color: #166534; border-color: #bbf7d0; }
.sro-sync-error   { background: #fee2e2; color: #991b1b; border-color: #fecaca; }


/* ── Digital Hall Pass ───────────────────────────────────────── */
.hallpass-student,
.hallpass-room,
.hallpass-board,
.hallpass-analytics,
.hallpass-kiosk {
  max-width: 1100px;
  margin: 0 auto;
}

.hallpass-student {
  max-width: 760px;
  min-height: 58vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 18px;
}

.hp-message {
  display: none;
  padding: 10px 12px;
  border-radius: var(--w-radius);
  border: 1px solid var(--w-border-light);
  background: var(--w-bg-card);
  color: var(--w-text);
  margin-bottom: 12px;
}
.hp-message.success,
.hp-message.error,
.hp-message.info { display: block; }
.hp-message.success { border-color: var(--w-success); background: var(--w-success-bg); }
.hp-message.error { border-color: var(--w-error); background: var(--w-error-bg); }
.hp-message.info { border-color: var(--w-info); background: var(--w-info-bg); }

.hp-prompt {
  font-size: clamp(1.5rem, 3vw, 2.4rem);
  text-align: center;
  color: var(--w-text-heading);
}

.hp-room-label {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
  color: var(--w-text-muted);
  font-weight: 600;
}

.hp-input {
  background: var(--w-bg-input);
  border: 1px solid var(--w-border-light);
  color: var(--w-text);
  border-radius: var(--w-radius);
  padding: 10px 12px;
}

.hp-destination-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
}

.hp-dest-btn,
.hp-btn {
  border: 1px solid var(--w-border-light);
  border-radius: var(--w-radius-lg);
  color: var(--w-text-heading);
  background: var(--w-bg-card);
  font: inherit;
  cursor: pointer;
  transition: transform var(--w-transition), background var(--w-transition), border-color var(--w-transition);
}

.hp-dest-btn {
  min-height: 104px;
  padding: 20px;
  font-size: 1.25rem;
  font-weight: 700;
}
.hp-dest-btn:hover,
.hp-btn:hover { transform: translateY(-1px); background: var(--w-bg-card-hover); border-color: var(--w-cyan); }

.hp-btn {
  padding: 8px 12px;
  font-weight: 700;
}
.hp-btn:disabled,
.hp-btn.is-busy { opacity: .6; cursor: progress; transform: none; }
.hp-btn-primary { background: var(--w-blue); border-color: var(--w-blue-light); color: var(--w-white); }
.hp-btn-secondary { background: var(--w-bg-input); }
.hp-btn-danger { background: var(--w-red-dark); border-color: var(--w-red); color: var(--w-white); }
.hp-btn-back { width: 100%; min-height: 72px; font-size: 1.4rem; }

.hp-status-card,
.hp-active-card,
.hp-section {
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  padding: 22px;
  box-shadow: var(--w-shadow-sm);
}

.hp-status-line { font-size: 1.3rem; color: var(--w-text-heading); margin-bottom: 12px; }
.hp-sub,
.hp-note { color: var(--w-text-muted); text-align: center; }
.hp-empty { color: var(--w-text-muted); padding: 14px; }

.hp-active-card {
  text-align: center;
  border-color: var(--w-success);
}
.hp-timer {
  font-family: var(--w-font-mono);
  font-size: clamp(4rem, 15vw, 8rem);
  line-height: 1;
  color: var(--w-text-heading);
  margin-bottom: 12px;
}
.hp-dest-active { color: var(--w-text-muted); font-size: 1.25rem; margin-bottom: 18px; }
.hp-over-limit { border-color: var(--w-error) !important; background: color-mix(in srgb, var(--w-error-bg) 40%, var(--w-bg-card)); }
.hp-over-limit .hp-timer,
.hp-over-limit .hp-timer-inline { color: var(--w-error); }

.hp-banner {
  border-radius: var(--w-radius);
  padding: 12px 14px;
  border: 1px solid var(--w-border-light);
}
.hp-banner-emergency { border-color: var(--w-error); background: var(--w-error-bg); color: var(--w-text-heading); }

.hp-section { margin-bottom: 18px; }
.hp-section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--w-text-heading);
  margin-bottom: 12px;
}
.hp-count {
  background: var(--w-bg-input);
  border: 1px solid var(--w-border-light);
  border-radius: 999px;
  padding: 2px 8px;
  font-size: .85rem;
}

.hp-request-row,
.hp-out-row {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) minmax(130px, 1fr) minmax(80px, .7fr) auto auto;
  gap: 10px;
  align-items: center;
  padding: 10px 0;
  border-top: 1px solid var(--w-border);
}
.hp-request-row:first-child,
.hp-out-row:first-child { border-top: 0; }
.hp-student { font-weight: 700; color: var(--w-text-heading); }
.hp-dest,
.hp-room { color: var(--w-text-muted); }
.hp-timer-inline { font-family: var(--w-font-mono); font-weight: 700; }

.hp-board-topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.hp-board-count { color: var(--w-text-heading); font-size: 1.2rem; }
.hp-board-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--w-bg-card);
  border: 1px solid var(--w-border);
  border-radius: var(--w-radius-lg);
  overflow: hidden;
}
.hp-board-table th,
.hp-board-table td {
  padding: 10px 12px;
  text-align: left;
  border-bottom: 1px solid var(--w-border);
}
.hp-board-table th { color: var(--w-text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; }
.hp-board-table tr:last-child td { border-bottom: 0; }

@media (max-width: 720px) {
  .hp-room-label { flex-direction: column; align-items: stretch; }
  .hp-request-row,
  .hp-out-row { grid-template-columns: 1fr; }
  .hp-board-topbar { flex-direction: column; align-items: stretch; }
  .hp-board-table { display: block; overflow-x: auto; }
}

.hp-state-held {
  border-left: 3px solid var(--w-warning);
  background: color-mix(in srgb, var(--w-warning-bg) 45%, transparent);
}
.hp-state-queued {
  border-left: 3px solid var(--w-info);
  background: color-mix(in srgb, var(--w-info-bg) 35%, transparent);
}
.hp-state-requested {
  border-left: 3px solid var(--w-success);
}

/* ═══════════════════════════════════════════════════════════════
   Tooltips & hover explanations
   ───────────────────────────────────────────────────────────────
   Attribute-driven tooltip. Put text in data-w-tip on any element
   (usually a .w-help "?" icon or a form label). Desktop shows it on
   hover/focus via CSS; touch devices toggle it on tap via tooltips.js
   (which sets .w-tip-open). Screen readers get the same text through an
   aria-label the macro sets, so the visual bubble is purely decorative.
   ═══════════════════════════════════════════════════════════════ */
.w-tip-host {
  position: relative;
}
.w-tip-host[data-w-tip]::after {
  content: attr(data-w-tip);
  position: absolute;
  z-index: 600;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  min-width: 140px;
  max-width: 260px;
  width: max-content;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--w-bg-card);
  color: var(--w-text);
  border: 1px solid var(--w-border-light);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: normal;
  text-transform: none;
  text-align: left;
  white-space: normal;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.14s ease, transform 0.14s ease;
}
/* Little arrow */
.w-tip-host[data-w-tip]::before {
  content: "";
  position: absolute;
  z-index: 601;
  bottom: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--w-border-light);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.14s ease;
}
.w-tip-host[data-w-tip]:hover::after,
.w-tip-host[data-w-tip]:focus-visible::after,
.w-tip-host[data-w-tip].w-tip-open::after,
.w-tip-host[data-w-tip]:hover::before,
.w-tip-host[data-w-tip]:focus-visible::before,
.w-tip-host[data-w-tip].w-tip-open::before {
  opacity: 1;
  visibility: visible;
}
.w-tip-host[data-w-tip]:hover::after,
.w-tip-host[data-w-tip]:focus-visible::after,
.w-tip-host[data-w-tip].w-tip-open::after {
  transform: translateX(-50%) translateY(0);
}
/* Position variants */
.w-tip-host[data-w-tip-pos="bottom"]::after {
  bottom: auto;
  top: calc(100% + 8px);
}
.w-tip-host[data-w-tip-pos="bottom"]::before {
  bottom: auto;
  top: calc(100% + 3px);
  border-top-color: transparent;
  border-bottom-color: var(--w-border-light);
}

/* The "?" help affordance that carries a tooltip. */
.w-help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  margin-left: 4px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--w-text-dim);
  cursor: help;
  vertical-align: -0.15em;
  transition: color 0.14s ease;
  -webkit-tap-highlight-color: transparent;
}
.w-help:hover,
.w-help:focus-visible,
.w-help.w-tip-open {
  color: var(--w-info);
  outline: none;
}
.w-help .wi { width: 16px; height: 16px; }

@media (prefers-reduced-motion: reduce) {
  .w-tip-host[data-w-tip]::after,
  .w-tip-host[data-w-tip]::before { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════
   Guided tour (self-hosted, driven by tour.js)
   ═══════════════════════════════════════════════════════════════ */
.w-tour-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  /* Four panels (drawn by JS) dim everything except the spotlight hole. */
  pointer-events: none;
}
.w-tour-scrim {
  position: fixed;
  background: rgba(3, 7, 18, 0.62);
  pointer-events: auto;
  transition: all 0.2s ease;
}
.w-tour-ring {
  position: fixed;
  z-index: 1001;
  border-radius: 10px;
  box-shadow: 0 0 0 2px var(--w-blue), 0 0 0 6px rgba(37, 99, 235, 0.35);
  pointer-events: none;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.w-tour-pop {
  position: fixed;
  z-index: 1002;
  pointer-events: auto;
  width: min(340px, calc(100vw - 32px));
  background: var(--w-bg-card);
  border: 1px solid var(--w-border-light);
  border-radius: 12px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  padding: 18px;
  color: var(--w-text);
  transition: top 0.2s ease, left 0.2s ease;
}
.w-tour-pop-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--w-text-heading);
  margin: 0 0 6px;
}
.w-tour-pop-body {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--w-text-muted);
  margin: 0 0 16px;
}
.w-tour-pop-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.w-tour-progress {
  font-size: 0.75rem;
  color: var(--w-text-dim);
  font-variant-numeric: tabular-nums;
}
.w-tour-btns { display: flex; gap: 8px; }
.w-tour-skip {
  background: transparent;
  border: none;
  color: var(--w-text-dim);
  font-size: 0.78rem;
  cursor: pointer;
  padding: 6px 4px;
}
.w-tour-skip:hover { color: var(--w-text); text-decoration: underline; }

@media (prefers-reduced-motion: reduce) {
  .w-tour-ring, .w-tour-pop, .w-tour-scrim { transition: none; }
}

/* ── "New here? Take a tour" opt-in banner ─────────────────────── */
.w-tour-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  margin: 0 0 18px;
  background: var(--w-info-bg);
  border: 1px solid color-mix(in srgb, var(--w-info) 40%, transparent);
  border-radius: 10px;
  font-size: 0.85rem;
  color: var(--w-text);
}
.w-tour-banner .w-tour-banner-icon {
  display: inline-flex;
  color: var(--w-info);
  flex-shrink: 0;
}
.w-tour-banner-text { flex: 1; }
.w-tour-banner-actions { display: flex; gap: 8px; flex-shrink: 0; }
@media (max-width: 560px) {
  .w-tour-banner { flex-wrap: wrap; }
  .w-tour-banner-actions { width: 100%; }
}

/* Topbar tour launcher "?" button */
.topbar-help-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--w-border);
  border-radius: 8px;
  background: transparent;
  color: var(--w-text-muted);
  cursor: pointer;
  transition: color 0.14s ease, border-color 0.14s ease;
}
.topbar-help-btn:hover {
  color: var(--w-info);
  border-color: var(--w-border-light);
}

/* ── Leave: professional-leave rows ──────────────────────────── */
/* Professional leave reads blue everywhere it is listed (dashboard, sub
   queue, print). The "Professional" pill beside the reason is what carries
   the meaning for anyone who cannot separate the hues; the tint and the left
   rule are the at-a-glance scan. The rule sits on the first cell rather than
   the row so it survives the .table-stack mobile layout. */
tr.leave-professional > td {
  background: color-mix(in srgb, var(--w-blue) 12%, transparent);
}
tr.leave-professional > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--w-blue);
}

/* Payroll's substitute report: a booking whose day was corrected out of the
   absence after the sub was told to come in. Amber, like the sub queue's
   stale flag, so what needs a decision reads the same on both pages. */
tr.sub-stale > td {
  background: var(--w-warning-bg);
}
tr.sub-stale > td:first-child {
  box-shadow: inset 3px 0 0 0 var(--w-warning);
}

/* ── Purchasing ───────────────────────────────────────────── */
/* The items table fits its card instead of scrolling sideways.
   It used to be pinned at min-width:880px with 104px-wide inputs, so eight
   columns always overflowed and the item description -- the column that
   actually needs the room -- was as narrow as the quantity box. Now every
   input is fluid, the small columns are given only what they need, and the
   description takes whatever is left. */
.line-items-table { width: 100%; }
@media (min-width: 769px) { .line-items-table { table-layout: fixed; } }
.line-items-table th, .line-items-table td { padding-left: 6px; padding-right: 6px; }
.line-items-table .form-input, .line-items-table .form-select {
  min-width: 0;
  width: 100%;
}
/* Columns are sized by what they hold. Anything not named here shares the
   remainder, so a form with different columns still lays out sensibly. */
.line-items-table [data-col="qty"] { width: 4.25rem; }
.line-items-table [data-col="unit_price"] { width: 6.5rem; }
.line-items-table [data-col="__extended"] { width: 6rem; }
.line-items-table [data-col="percent"] { width: 5.5rem; }
.line-items-table [data-col="amount"] { width: 6.5rem; }
.line-items-table [data-col="link"] { width: 11%; }
.line-items-table [data-col="fund"] { width: 14%; }
.line-items-table [data-col="notes"] { width: 12%; }
.line-items-table [data-col="catalog_number"] { width: 8%; }
.line-items-table [data-col="account_code"] { width: 9%; }
/* What the item actually IS gets the most room. Left to share the leftovers
   it came out the same width as the quantity box. */
.line-items-table [data-col="description"] { width: 30%; }
/* Numbers read better right-aligned, and it keeps the narrow columns narrow. */
.line-items-table [data-col="qty"] .form-input,
.line-items-table [data-col="unit_price"] .form-input { text-align: right; }
.line-remove-btn { padding-left: 8px; padding-right: 8px; }
/* The remove column no longer has to be pinned to a scroller: the table fits,
   so the button is on screen already. It keeps a fixed narrow width so the
   trash icon does not take room from the description. */
.line-items-table .line-remove-cell { width: 3rem; text-align: center; }
/* Cat. #, account code and notes are secondary: present, but not competing
   with the item and its price for attention. */
.line-items-table .col-secondary { opacity: .75; }
.purchase-waiting { opacity: .68; }
.requisition { max-width: 1000px; margin: 0 auto; }
.signature-block { padding-top: 28px; border-top: 1px solid var(--w-border); }
.fund-grant-row { display: flex; align-items: center; gap: 8px; }

@media (max-width: 768px) {
  /* One card per item beats an eight-column table on a phone, and the
     Superintendent approves from one. .table-stack does the work; this just
     gives the row card an edge and keeps the remove button reachable. */
  .line-items-table tbody tr {
    border: 1px solid var(--w-border);
    border-radius: var(--w-radius);
    padding: 10px 12px !important;
    margin-bottom: 10px;
  }
  .line-items-table .form-input, .line-items-table .form-select { width: 100%; }
  /* The stacked card is one field per line, so the desktop column widths
     have to let go or qty stays a 4.5rem box on a phone. */
  .line-items-table td[data-col] { width: auto !important; }
  /* The remove button carries no data-label, and .table-stack hides every
     unlabelled cell -- so on a phone the only way to take a row back off a
     request had been display:none. It stays; it just does not get a caption. */
  .line-items-table td.line-remove-cell[data-label=""] { display: flex; }
  .line-items-table td.line-remove-cell::before { content: none; }
  /* Nothing scrolls sideways once stacked, so there is no edge to pin to. */
  .line-items-table .line-remove-cell {
    position: static;
    box-shadow: none;
    background: none;
  }
  .line-items-table [data-remove-line] { width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════
   Purchase funds ledger
   ═══════════════════════════════════════════════════════════════
   The funds screen used to draw every fund twice: a row in a summary
   table, then a whole management card further down carrying three
   forms. That second copy was the entire scroll -- twenty funds put
   the budget history a thousand pixels below the fold, and finding
   one fund meant reading past nineteen. Here the row IS the editor.
   It opens in place, so the page grows one line per fund. */

/* Rows run to the card's edge; the card's own 16px padding would
   inset them and lose the full-width hover target. */
.fund-head,
.fund-ledger { margin-left: -16px; margin-right: -16px; }
.fund-ledger { margin-bottom: -16px; }

.fund-head,
.fund-row > summary {
  display: grid;
  grid-template-columns: minmax(190px, 2.2fr) repeat(4, minmax(86px, 1fr)) 96px 24px;
  gap: 12px;
  align-items: center;
  padding: 10px 16px;
}

.fund-head {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--w-text-dim);
  border-bottom: 1px solid var(--w-border);
  padding-top: 0;
}

.fund-row { border-bottom: 1px solid var(--w-border); }
.fund-ledger > .fund-row:last-of-type { border-bottom: none; }
.fund-row[hidden] { display: none; }
.fund-empty { padding: 24px; }

.fund-row > summary {
  cursor: pointer;
  list-style: none;
  transition: background var(--w-transition);
}
.fund-row > summary::-webkit-details-marker { display: none; }
.fund-row > summary:hover { background: var(--w-bg-card-hover); }
.fund-row > summary:focus-visible {
  outline: none;
  background: var(--w-bg-card-hover);
  box-shadow: inset 0 0 0 2px var(--w-blue);
}
.fund-row[open] > summary { background: var(--w-surface-2); }

/* An inactive fund is still administered from here, so it stays legible
   rather than being greyed to the point of unreadability. */
.fund-row.is-inactive .fund-name { color: var(--w-text-muted); }

.fund-name { min-width: 0; line-height: 1.35; }
.fund-num { font-variant-numeric: tabular-nums; }
.fund-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 3px;
}

.fund-money {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 0.88rem;
}

/* Each figure carries its own label. Visually hidden on desktop, where the
   column header row says the same thing, but never hidden from assistive
   tech: .fund-head is aria-hidden and a header div cannot be associated with
   a grid cell the way a <th> is with a <td>. Without this a screen reader
   announced four unlabelled money figures per fund. */
.fund-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* How much of the year's appropriation is gone, at a glance. Amber at 85%
   is the "start asking" line; red means the fund is already past its
   budget and the next request should not be waved through. */
.fund-meter {
  height: 3px;
  margin-top: 4px;
  border-radius: 2px;
  background: var(--w-border);
  overflow: hidden;
}
.fund-meter > span {
  display: block;
  height: 100%;
  background: var(--w-success);
}
.fund-meter.warn > span { background: var(--w-warning); }
.fund-meter.over > span { background: var(--w-error); }

.fund-chevron {
  display: flex;
  justify-content: flex-end;
  color: var(--w-text-dim);
  transition: transform var(--w-transition);
}
.fund-row[open] .fund-chevron { transform: rotate(90deg); }

.fund-body {
  padding: 4px 16px 20px;
  background: var(--w-surface-2);
  border-top: 1px solid var(--w-border);
}
.fund-body .detail-section:first-child { margin-top: 12px; }

/* ── Status filter chips ─────────────────────────────────────── */
.fund-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chip-filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--w-text-muted);
  background: var(--w-bg-input);
  border: 1px solid var(--w-border);
  border-radius: 999px;
  cursor: pointer;
  transition: all var(--w-transition);
}
.chip-filter:hover { color: var(--w-text-heading); border-color: var(--w-border-light); }
.chip-filter:focus-visible {
  outline: none;
  border-color: var(--w-blue);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
.chip-filter.is-active {
  color: var(--w-text-heading);
  border-color: var(--w-blue);
  background: color-mix(in srgb, var(--w-blue) 12%, var(--w-bg-card));
}
.chip-filter.warn { color: var(--w-warning); border-color: var(--w-warning); }
.chip-filter.warn.is-active { background: var(--w-warning-bg); }
.chip-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}

/* ── Access-rule chips ───────────────────────────────────────── */
/* These were .chip, which is the SRO module's 48px-tall tap target laid
   out three to a row -- four access rules filled half a screen. An
   access rule is a label with a remove button, so it is sized like one. */
.grant-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.grant-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 12px;
  font-size: 0.82rem;
  background: var(--w-bg-input);
  border: 1px solid var(--w-border);
  border-radius: 999px;
}
.grant-remove {
  display: flex;
  align-items: center;
  padding: 3px;
  color: var(--w-text-dim);
  background: none;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  transition: all var(--w-transition);
}
.grant-remove:hover { color: var(--w-error); background: var(--w-error-bg); }
.grant-remove:focus-visible {
  outline: none;
  color: var(--w-error);
  box-shadow: 0 0 0 2px var(--w-blue);
}

/* ── A card that opens ───────────────────────────────────────── */
/* For the things a treasurer needs occasionally -- adding a fund, reading
   the appropriation history -- which do not deserve permanent height. */
.disclosure-card { padding: 0; }
.disclosure-card > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  border-radius: var(--w-radius-lg);
}
.disclosure-card > summary::-webkit-details-marker { display: none; }
.disclosure-card > summary:hover { background: var(--w-bg-card-hover); }
.disclosure-card > summary:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--w-blue);
}
.disclosure-card > summary .card-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/* The affordance -- without it a summary styled as a heading reads as a
   heading and nobody clicks it. */
.disclosure-card > summary::after {
  content: "";
  width: 7px;
  height: 7px;
  margin-left: auto;
  border-right: 2px solid var(--w-text-dim);
  border-bottom: 2px solid var(--w-text-dim);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--w-transition);
}
.disclosure-card[open] > summary::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}
.disclosure-card[open] > summary { border-bottom: 1px solid var(--w-border); }
.disclosure-body { padding: 16px; }

/* The uncoded pile -- money committed against no fund at all. It sits above
   the ledger because it is absent from every remaining balance in it. */
.uncoded-callout {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  border-color: var(--w-warning);
}
.uncoded-callout > div:nth-child(2) { flex: 1; min-width: 0; }
.uncoded-callout .btn { flex-shrink: 0; }

@media (max-width: 900px) {
  /* Same trade the tables make at this width: the column headers go, and
     each figure carries its own label. */
  .fund-head { display: none; }
  .fund-row > summary {
    grid-template-columns: 1fr 1fr;
    gap: 4px 12px;
    padding: 12px 16px;
  }
  .fund-name { grid-column: 1 / -1; }
  .fund-money {
    text-align: left;
    font-size: 0.85rem;
  }
  .fund-label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    display: block;
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--w-text-dim);
  }
  .fund-chevron { display: none; }
}

@media print {
  /* These two pages end up in a board packet. */
  /* A fund's forms are screen furniture; its numbers are the report. */
  .fund-body, .fund-chevron { display: none !important; }
  .fund-row, .fund-row > summary { break-inside: avoid; page-break-inside: avoid; }
  .disclosure-card > summary::after { display: none; }
  .sidebar, .topbar, .sidebar-overlay, .mobile-menu-btn,
  .filter-bar, .tab-bar, .form-actions, .card-actions { display: none !important; }
  .app-layout, .main-content, .page-content { display: block !important; margin: 0 !important; padding: 0 !important; }
  .requisition, .card { box-shadow: none !important; border: 0; max-width: none; }
  .table-wrap { overflow: visible !important; border: 0; }
  .table th, .table td { border-color: #999 !important; }
  tr, .detail-section { break-inside: avoid; page-break-inside: avoid; }
  thead { display: table-header-group; }
}

/* SRO offline queue — submissions Winston refused, held on the device for
   review (static/js/sro-offline.js renders into #sroRejected). */
.sro-rejected { margin-bottom: 14px; border-color: #fecaca; }
.sro-rejected-item {
  padding: 10px 0;
  border-top: 1px solid var(--w-border, #e5e7eb);
}
.sro-rejected-title { font-weight: 600; }
.sro-rejected-reason { color: #991b1b; font-size: 0.9rem; margin: 2px 0 4px; }
.sro-rejected-actions { display: flex; gap: 8px; margin-top: 8px; }

/* ── Session keeper (static/js/session-keeper.js) ─────────── */
/* The flash colours are translucent, so the bar carries its own opaque card
   underneath them to stay readable over whatever the page has there. */
.w-session-bar {
    position: fixed;
    left: 50%;
    bottom: 16px;
    transform: translateX(-50%);
    z-index: 2000;
    width: min(640px, calc(100% - 32px));
    background: var(--w-bg-card);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.w-session-bar .flash,
.w-draft-offer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}
.w-session-bar .flash { margin: 0; }
.w-session-msg { flex: 1 1 240px; }
.w-draft-offer > span { flex: 1 1 240px; }
