/* === BEGIN tenant brand (generated by build_brand_css.py — do not edit) === */
:root {
  --brand-primary:      #0D2A4D;
  --brand-accent:       #1565C0;
  --brand-accent-light: #42A5F5;
  --brand-accent-soft:  #3B8CC4;
  --brand-accent-deep:  #2563A0;
  --brand-surface:      #F6F8FB;
  --brand-font-heading: 'Inter', system-ui, sans-serif;
  --brand-font-display: 'Fredoka', 'Inter', sans-serif;
  --brand-font-serif:   'Playfair Display', Georgia, serif;
}
/* === END tenant brand === */

/* ================================================================
   InSpace Operations Hub — Design System
   Extends react-a.css / astro.css with app-specific utilities
   Theme: In Space blue (var(--brand-accent)) + academy (var(--brand-accent-light)) accents — light + dark
   ================================================================ */

:root {
  --hub-bg: #0a0a1a;
  --hub-surface: #111128;
  --hub-surface-raised: #1a1a3a;
  --hub-border: rgba(66, 165, 245, 0.15);
  --hub-border-strong: rgba(66, 165, 245, 0.3);
  --hub-text: #e2e8f0;
  --hub-text-muted: #94a3b8;
  --hub-text-dim: #64748b;
  /* Aliases used by some pages — kept here so colors stay centralized */
  --hub-dim: #94a3b8;
  --hub-primary: var(--brand-accent-light);

  /* Functional colors */
  --hub-success: #22c55e;
  --hub-success-bg: rgba(34, 197, 94, 0.1);
  --hub-warning: #f59e0b;
  --hub-warning-bg: rgba(245, 158, 11, 0.1);
  --hub-danger: #ef4444;
  --hub-danger-bg: rgba(239, 68, 68, 0.1);
  --hub-info: var(--brand-accent);
  --hub-info-bg: rgba(21, 101, 192, 0.1);

  /* Brand */
  --hub-purple: var(--brand-accent-light);
  --hub-purple-light: #6cb6f7;
  --hub-cyan: var(--brand-accent);
  --hub-cyan-light: var(--brand-accent-light);

  /* Sidebar */
  --hub-sidebar-width: 260px;
  --hub-sidebar-bg: #0d0d24;
  --hub-sidebar-active: rgba(66, 165, 245, 0.15);

  /* Spacing */
  /* The recessed tray behind a segmented control or a tab strip, and the
     raised colour of the selected item inside it. These were written inline on
     the billing page as `var(--hub-bg-subtle,#eef1f6)` and a hard `#fff` —
     and neither token existed, so in dark mode the tab strip fell back to a
     near-white bar on a near-black page. */
  --hub-bg-subtle: #0d0d24;
  --hub-seg-on: #26264e;
  --hub-radius: 12px;
  --hub-radius-sm: 8px;
  --hub-radius-lg: 16px;
  --hub-gap: 1.25rem;
}

/* ── Light theme (opt-in via data-theme="light" on <html>) ───────────
   Only color tokens are overridden; spacing/radii/widths stay shared. */
[data-theme="light"] {
  --hub-bg: #f4f6fb;
  --hub-surface: #ffffff;
  --hub-surface-raised: #ffffff;
  --hub-border: #e4e6f0;
  --hub-border-strong: rgba(21, 101, 192, 0.3);
  --hub-text: #1f2733;
  --hub-text-muted: #5b6478;
  --hub-text-dim: #8a90a2;
  --hub-dim: #6b6f8d;
  --hub-primary: var(--brand-accent);

  --hub-success: #16a34a;
  --hub-success-bg: rgba(34, 197, 94, 0.12);
  --hub-warning: #d97706;
  --hub-warning-bg: rgba(245, 158, 11, 0.14);
  --hub-danger: #dc2626;
  --hub-danger-bg: rgba(239, 68, 68, 0.10);
  --hub-info: var(--brand-accent-light);
  --hub-info-bg: rgba(21, 101, 192, 0.12);

  --hub-purple: var(--brand-accent);
  --hub-purple-light: var(--brand-accent-light);
  --hub-cyan: var(--brand-accent-light);
  --hub-cyan-light: var(--brand-accent);

  --hub-sidebar-bg: #ffffff;
  --hub-sidebar-active: rgba(21, 101, 192, 0.10);
  --hub-bg-subtle: #eef1f6;
  --hub-seg-on: #ffffff;
}

/* Reset & Base */
.hub-page {
  margin: 0;
  padding: 0;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--hub-bg);
  color: var(--hub-text);
  height: 100vh;
  overflow: hidden;            /* only .hub-main scrolls */
  -webkit-font-smoothing: antialiased;
}

.hub-page * { box-sizing: border-box; }

/* ================================================================
   LAYOUT: Sidebar + Main
   ================================================================ */
.hub-layout {
  display: grid;
  grid-template-columns: var(--hub-sidebar-width) 1fr;
  height: 100vh;
}

/* Anti-flash gate: hide the app shell until hub.js confirms the session, so an
   unauthenticated visitor never sees the dashboard before the login redirect.
   hub.js adds .hub-ready to <html> immediately for public pages and right after
   a confirmed session for gated pages. The animation is a safety net — if the
   script never runs, the shell still appears after 6s instead of staying blank. */
html:not(.hub-ready) .hub-layout {
  visibility: hidden;
  animation: hub-reveal-fallback 0s linear 6s forwards;
}
@keyframes hub-reveal-fallback { to { visibility: visible; } }

/* The overlay is a mobile-only backdrop. Hide it by default so on desktop
   it isn't a grid item — otherwise it consumes the 1fr column and pushes
   .hub-main into the 260px sidebar column. The media query below re-enables
   it (position:fixed) for the slide-out sidebar on small screens. */
.hub-sidebar-overlay {
  display: none;
}

@media (max-width: 768px) {
  .hub-layout {
    grid-template-columns: 1fr;
  }
  .hub-sidebar {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    z-index: 100;
    transform: translateX(-100%);
    transition: transform 0.3s ease;
  }
  .hub-sidebar.is-open {
    transform: translateX(0);
  }
  .hub-sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 99;
  }
  .hub-sidebar-overlay.is-visible {
    display: block;
  }
}

/* ================================================================
   SIDEBAR
   ================================================================ */
.hub-sidebar {
  background: var(--hub-sidebar-bg);
  border-right: 1px solid var(--hub-border);
  display: flex;
  flex-direction: column;
  height: 100vh;            /* bound the column so .hub-sidebar-nav scrolls inside it */
  padding: 1.5rem 0;
}

.hub-sidebar-brand {
  padding: 0 1.5rem 1.5rem;
  border-bottom: 1px solid var(--hub-border);
  margin-bottom: 1rem;
}

.hub-sidebar-brand h1 {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--hub-text);
  letter-spacing: -0.02em;
}

.hub-sidebar-brand p {
  margin: 0.25rem 0 0;
  font-size: 0.75rem;
  color: var(--hub-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.hub-sidebar-nav {
  flex: 1;
  min-height: 0;          /* allow the nav to shrink so overflow-y can scroll */
  overflow-y: auto;
  padding: 0 0.75rem;
  scrollbar-width: none;        /* Firefox: hide bar, keep wheel/trackpad scroll */
  -ms-overflow-style: none;     /* old Edge/IE */
}
.hub-sidebar-nav::-webkit-scrollbar {   /* Chrome / Safari / new Edge */
  width: 0;
  height: 0;
}

.hub-sidebar-section {
  margin-bottom: 1.5rem;
}

.hub-sidebar-section-title {
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  color: var(--hub-text-dim);
  padding: 0 0.75rem;
  margin-bottom: 0.5rem;
}

.hub-sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  border-radius: var(--hub-radius-sm);
  color: var(--hub-text-muted);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  transition: all 0.15s ease;
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
}

.hub-sidebar-link:hover {
  background: rgba(66, 165, 245, 0.08);
  color: var(--hub-text);
}

.hub-sidebar-link.is-active {
  background: var(--hub-sidebar-active);
  color: var(--hub-purple-light);
  box-shadow: inset 3px 0 0 var(--hub-purple);
}

.hub-sidebar-link svg {
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
  opacity: 0.7;
}

.hub-sidebar-link.is-active svg {
  opacity: 1;
}

.hub-sidebar-badge {
  margin-left: auto;
  background: var(--hub-danger);
  color: white;
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.125rem 0.375rem;
  border-radius: 999px;
  min-width: 1.25rem;
  text-align: center;
}

.hub-sidebar-footer {
  padding: 1rem 1.5rem 0;
  border-top: 1px solid var(--hub-border);
  margin-top: auto;
}

.hub-sidebar-user {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.875rem;
}

.hub-sidebar-user-avatar {
  width: 2rem; height: 2rem;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--hub-purple), var(--hub-cyan));
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.75rem;
  color: white;
  flex-shrink: 0;
}

/* ================================================================
   TOP BAR
   ================================================================ */
.hub-topbar {
  height: 64px;
  background: var(--hub-surface);
  border-bottom: 1px solid var(--hub-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1.5rem;
  position: sticky;
  top: 0;
  z-index: 50;
}

.hub-topbar-left {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.hub-topbar-menu-btn {
  display: none;
  background: none;
  border: none;
  color: var(--hub-text-muted);
  cursor: pointer;
  padding: 0.5rem;
}

@media (max-width: 768px) {
  .hub-topbar-menu-btn { display: block; }
}

.hub-topbar-title {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--hub-text);
  margin: 0;
}

.hub-topbar-right {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.hub-search {
  position: relative;
}

.hub-search input {
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-sm);
  padding: 0.5rem 0.75rem 0.5rem 2.25rem;
  color: var(--hub-text);
  font-size: 0.875rem;
  width: 280px;
  outline: none;
  transition: border-color 0.15s;
}

.hub-search input:focus {
  border-color: var(--hub-purple);
}

.hub-search input::placeholder {
  color: var(--hub-text-dim);
}

.hub-search svg {
  position: absolute;
  left: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--hub-text-dim);
  width: 1rem; height: 1rem;
}

.hub-topbar-btn {
  background: none;
  border: none;
  color: var(--hub-text-muted);
  cursor: pointer;
  padding: 0.5rem;
  border-radius: var(--hub-radius-sm);
  position: relative;
  transition: color 0.15s;
}

.hub-topbar-btn:hover {
  color: var(--hub-text);
  background: rgba(66, 165, 245, 0.08);
}

.hub-topbar-btn .hub-badge {
  position: absolute;
  top: 2px; right: 2px;
}

/* ================================================================
   MAIN CONTENT AREA
   ================================================================ */
.hub-main {
  background: var(--hub-bg);
  overflow-y: auto;
  height: 100vh;               /* sole scroll region */
}

.hub-content {
  padding: 1.5rem;
  max-width: 1400px;
}

/* ================================================================
   STAT CARDS (Dashboard)
   ================================================================ */
.hub-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--hub-gap);
  margin-bottom: 1.5rem;
}

.hub-stat-card {
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius);
  padding: 1.25rem;
  transition: transform 0.15s, box-shadow 0.15s;
}

.hub-stat-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
}

.hub-stat-card--alert {
  border-left: 4px solid var(--hub-danger);
}

.hub-stat-card--success {
  border-left: 4px solid var(--hub-success);
}

.hub-stat-card--warning {
  border-left: 4px solid var(--hub-warning);
}

.hub-stat-card--info {
  border-left: 4px solid var(--hub-info);
}

.hub-stat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.hub-stat-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--hub-text-dim);
  font-weight: 600;
}

.hub-stat-icon {
  width: 2.5rem; height: 2.5rem;
  border-radius: var(--hub-radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--hub-surface-raised);
}

.hub-stat-icon svg {
  width: 1.25rem; height: 1.25rem;
}

.hub-stat-value {
  font-size: 2rem;
  font-weight: 700;
  color: var(--hub-text);
  line-height: 1.2;
  margin-bottom: 0.25rem;
}

.hub-stat-trend {
  font-size: 0.75rem;
  color: var(--hub-text-dim);
}

.hub-stat-trend--up { color: var(--hub-success); }
.hub-stat-trend--down { color: var(--hub-danger); }

/* ================================================================
   CARDS
   ================================================================ */
.hub-card {
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius);
  overflow: hidden;
  color: var(--hub-text);
}

/* Space between stacked cards.
   `.hub-card` carried no margin, so two consecutive cards sat border against
   border and read as one box with a line through it. Payroll shows it most
   (three cards straight down) but it affects every page that stacks them —
   the dashboard has nine.

   A margin on the card itself would fight the grid layouts that already space
   their own children, so this only applies to a card FOLLOWING another element
   inside a content area: the first card keeps its position, anything laid out
   in a grid is untouched. */
.hub-content > .hub-card + .hub-card,
.hub-content > * + .hub-card {
  margin-top: 1.25rem;
}

.hub-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--hub-border);
}

.hub-card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--hub-text);
  margin: 0;
}

.hub-card-subtitle {
  font-size: 0.75rem;
  color: var(--hub-text-dim);
  margin-top: 0.25rem;
}

.hub-card-body {
  padding: 1.25rem;
}

.hub-card-footer {
  padding: 0.875rem 1.25rem;
  border-top: 1px solid var(--hub-border);
  background: rgba(66, 165, 245, 0.03);
}

/* ================================================================
   TABLES
   ================================================================ */
.hub-table-wrap {
  overflow-x: auto;
}

.hub-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.hub-table thead th {
  text-align: left;
  padding: 0.875rem 1rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--hub-text-dim);
  font-weight: 600;
  border-bottom: 1px solid var(--hub-border-strong);
  white-space: nowrap;
}

.hub-table tbody td {
  padding: 1rem;
  border-bottom: 1px solid var(--hub-border);
  color: var(--hub-text);
  vertical-align: middle;
}

.hub-table tbody tr:hover {
  background: rgba(66, 165, 245, 0.04);
}

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

/* Status pills in tables */
.hub-status {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: capitalize;
}

.hub-status::before {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
}

.hub-status--enrolled,
.hub-status--active,
.hub-status--paid,
.hub-status--compliant {
  background: var(--hub-success-bg);
  color: var(--hub-success);
}
.hub-status--enrolled::before,
.hub-status--active::before,
.hub-status--paid::before,
.hub-status--compliant::before { background: var(--hub-success); }

/* Boy / girl marker. Muted rather than the usual pink-and-blue: this sits
   beside a child's name all day and shouldn't shout. Colour is a hint, the
   title attribute carries the word. */
.hub-sex {
  display: inline-block;
  margin-left: .3rem;
  font-size: .95em;
  line-height: 1;
  cursor: help;
  opacity: .75;
}
.hub-sex--boy  { color: #3b7ea1; }
.hub-sex--girl { color: #a1568b; }

/* `incoming` — a place held for a start date that hasn't arrived. Blue rather
   than amber: nothing is wrong and nothing needs chasing, it simply hasn't begun.
   Without a rule here statusBadge() emitted hub-status--incoming and it rendered
   unstyled. */
.hub-status--incoming {
  background: rgba(21, 101, 192, .13);
  color: #1565C0;
}
.hub-status--incoming::before { background: #1565C0; }

.hub-status--waitlist,
.hub-status--hold,
.hub-status--pending {
  background: var(--hub-warning-bg);
  color: var(--hub-warning);
}
.hub-status--waitlist::before,
.hub-status--hold::before,
.hub-status--pending::before { background: var(--hub-warning); }

.hub-status--withdrawn,
.hub-status--inactive,
.hub-status--overdue,
.hub-status--expired,
.hub-status--non-compliant {
  background: var(--hub-danger-bg);
  color: var(--hub-danger);
}
.hub-status--withdrawn::before,
.hub-status--inactive::before,
.hub-status--overdue::before,
.hub-status--expired::before,
.hub-status--non-compliant::before { background: var(--hub-danger); }

.hub-status--part-time,
.hub-status--graduated,
.hub-status--partial {
  background: var(--hub-info-bg);
  color: var(--hub-info);
}
.hub-status--part-time::before,
.hub-status--graduated::before,
.hub-status--partial::before { background: var(--hub-info); }

/* ================================================================
   BUTTONS
   ================================================================ */
.hub-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.625rem 1rem;
  border-radius: var(--hub-radius-sm);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.15s;
  border: none;
  outline: none;
  text-decoration: none;
}

.hub-btn:hover { transform: translateY(-1px); }
.hub-btn:active { transform: translateY(0); }

.hub-btn--primary {
  background: linear-gradient(135deg, var(--hub-purple), var(--hub-purple-light));
  color: white;
  box-shadow: 0 4px 12px rgba(66, 165, 245, 0.3);
}
.hub-btn--primary:hover {
  box-shadow: 0 6px 16px rgba(66, 165, 245, 0.4);
}

.hub-btn--secondary {
  background: var(--hub-surface-raised);
  color: var(--hub-text);
  border: 1px solid var(--hub-border);
}
.hub-btn--secondary:hover {
  border-color: var(--hub-purple);
  background: rgba(66, 165, 245, 0.08);
}

.hub-btn--danger {
  background: var(--hub-danger-bg);
  color: var(--hub-danger);
  border: 1px solid rgba(239, 68, 68, 0.2);
}
.hub-btn--danger:hover {
  background: rgba(239, 68, 68, 0.2);
}

.hub-btn--success {
  background: var(--hub-success-bg);
  color: var(--hub-success);
  border: 1px solid rgba(34, 197, 94, 0.2);
}

/* A ghost button is still a BUTTON. It was transparent with muted text and no
   border, so on the dark theme it rendered as a run of grey words — "Set aside"
   and "Not ours" sat mid-sentence looking like prose, and people reported that
   the buttons were missing. The hairline keeps it quieter than secondary while
   making it obvious there is something to press. */
.hub-btn--ghost {
  background: transparent;
  color: var(--hub-text-muted);
  border: 1px solid var(--hub-border);
  padding: 0.5rem 0.75rem;
}
.hub-btn--ghost:hover {
  color: var(--hub-text);
  background: rgba(66, 165, 245, 0.08);
  border-color: var(--hub-border-strong);
}
/* Icon-only ghosts stay square. */
.hub-btn--ghost.hub-btn--icon { padding: 0.5rem; }

.hub-btn--sm {
  padding: 0.375rem 0.75rem;
  font-size: 0.75rem;
}

.hub-btn--lg {
  padding: 0.875rem 1.5rem;
  font-size: 1rem;
}

.hub-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none !important;
}

/* ================================================================
   FORMS
   ================================================================ */
.hub-form-group {
  margin-bottom: 1.25rem;
}

.hub-form-label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--hub-text-dim);
  font-weight: 600;
  margin-bottom: 0.5rem;
}

.hub-form-input,
.hub-form-select,
.hub-form-textarea {
  width: 100%;
  box-sizing: border-box;
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-sm);
  padding: 0.625rem 0.875rem;
  color: var(--hub-text);
  font-size: 0.875rem;
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}

/* One height for inputs and selects.
   A <select> and an <input type="date"> have different intrinsic heights, and
   Safari adds its own padding to date inputs — so a row mixing them came out
   ragged, with labels above them landing at different heights. Padding alone
   never equalised it because the browser was adding to it. */
.hub-form-input,
.hub-form-select {
  height: 40px;
  padding-top: 0;
  padding-bottom: 0;
  line-height: normal;
}

/* File inputs are not text inputs.
   The rule above gives every .hub-form-input a fixed 40px height with NO
   vertical padding, which is right for something you type into. A file input
   renders a "Choose file" button inside itself, and with no vertical padding
   to sit in it lands hard against the top of the box and spills past the
   border — the button ends up outside the field it belongs to.
   So: let the height follow the button, and give the button back its padding. */
.hub-form-input[type="file"] {
  height: auto;
  min-height: 40px;
  padding: 0.42rem 0.55rem;
  line-height: 1.5;
  cursor: pointer;
}
.hub-form-input[type="file"]::file-selector-button {
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--hub-text);
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: 999px;
  padding: 0.3rem 0.85rem;
  margin: 0 0.65rem 0 0;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.hub-form-input[type="file"]:hover::file-selector-button,
.hub-form-input[type="file"]:focus::file-selector-button {
  border-color: var(--hub-purple);
  color: var(--hub-purple);
}
/* Safari before 16.4 doesn't know ::file-selector-button; the old prefixed
   name still lands there, so the button matches rather than falling back to
   the platform default inside a styled field. */
.hub-form-input[type="file"]::-webkit-file-upload-button {
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--hub-text);
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: 999px;
  padding: 0.3rem 0.85rem;
  margin: 0 0.65rem 0 0;
  cursor: pointer;
}
.hub-form-textarea { height: auto; }

/* Header pickers — the programme and location filters hub.js injects into the
   topbar. Same shape as the form selects but sized for a header row: slightly
   shorter, no full width, and a hover state because they're the one control on
   the page people reach for constantly.

   These used to be styled inline from JavaScript, which beat every stylesheet
   rule — so when the form controls were given a consistent height, these two
   stayed on their old sizing and became the odd ones out. */
.hub-picker {
  height: 34px;
  box-sizing: border-box;
  margin-left: .6rem;
  padding: 0 2rem 0 .7rem;
  border: 1px solid var(--hub-border);
  border-radius: 8px;
  background-color: var(--hub-surface-raised);
  color: var(--hub-text);
  font: inherit;
  font-size: .85rem;
  cursor: pointer;
  outline: none;
  transition: border-color .15s, box-shadow .15s;
  -webkit-appearance: none;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 15px) 50%, calc(100% - 10px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.hub-picker:hover { border-color: var(--hub-text-dim); }
.hub-picker:focus {
  border-color: var(--brand-accent);
  box-shadow: 0 0 0 3px rgba(21, 101, 192, .12);
}
/* The programme picker locks the whole hub to one programme — weightier than a
   location filter, and worth looking like it. */
.hub-picker--scope { margin-left: 1rem; font-weight: 600; }

/* appearance:none removes the native chrome causing the height difference; the
   arrow is drawn back on so it still reads as a select. */
.hub-form-select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 17px) 50%, calc(100% - 12px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.hub-form-input:focus,
.hub-form-select:focus,
.hub-form-textarea:focus {
  border-color: var(--hub-purple);
  box-shadow: 0 0 0 3px rgba(66, 165, 245, 0.15);
}

.hub-form-input::placeholder,
.hub-form-textarea::placeholder {
  color: var(--hub-text-dim);
}

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

.hub-form-hint {
  font-size: 0.75rem;
  color: var(--hub-text-dim);
  margin-top: 0.375rem;
}

.hub-form-error {
  font-size: 0.75rem;
  color: var(--hub-danger);
  margin-top: 0.375rem;
}

/* ================================================================
   MODAL
   ================================================================ */
.hub-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s, visibility 0.2s;
}

.hub-modal-overlay.is-open {
  opacity: 1;
  visibility: visible;
}

.hub-modal {
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-lg);
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
  transform: scale(0.95) translateY(10px);
  transition: transform 0.2s;
}

.hub-modal-overlay.is-open .hub-modal {
  transform: scale(1) translateY(0);
}

.hub-modal--wide {
  max-width: 800px;
}

.hub-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid var(--hub-border);
}

.hub-modal-title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0;
}

.hub-modal-close {
  background: none;
  border: none;
  color: var(--hub-text-dim);
  cursor: pointer;
  padding: 0.25rem;
  border-radius: var(--hub-radius-sm);
  transition: color 0.15s;
}

.hub-modal-close:hover {
  color: var(--hub-text);
}

.hub-modal-body {
  padding: 1.5rem;
}

.hub-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--hub-border);
  background: rgba(66, 165, 245, 0.03);
}

/* ================================================================
   KIOSK MODE (Clock In/Out)
   ================================================================ */
.hub-kiosk {
  min-height: 100vh;
  background: var(--hub-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

.hub-kiosk-brand {
  text-align: center;
  margin-bottom: 2rem;
}

.hub-kiosk-brand h1 {
  font-size: 1.5rem;
  color: var(--hub-purple-light);
  margin: 0 0 0.5rem;
}

.hub-kiosk-brand p {
  color: var(--hub-text-dim);
  margin: 0;
}

.hub-kiosk-pad {
  background: var(--hub-surface);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-lg);
  padding: 2rem;
  width: 100%;
  max-width: 400px;
}

.hub-kiosk-display {
  background: var(--hub-surface-raised);
  border: 2px solid var(--hub-border);
  border-radius: var(--hub-radius);
  padding: 1rem;
  text-align: center;
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: 0.5em;
  color: var(--hub-text);
  margin-bottom: 1.5rem;
  min-height: 3.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hub-kiosk-display.is-error {
  border-color: var(--hub-danger);
  color: var(--hub-danger);
}

.hub-kiosk-display.is-success {
  border-color: var(--hub-success);
  color: var(--hub-success);
}

.hub-kiosk-keys {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.hub-kiosk-key {
  aspect-ratio: 1;
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius);
  color: var(--hub-text);
  font-size: 1.5rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.1s;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.hub-kiosk-key:hover {
  background: rgba(66, 165, 245, 0.1);
  border-color: var(--hub-purple);
}

.hub-kiosk-key:active {
  transform: scale(0.95);
  background: rgba(66, 165, 245, 0.2);
}

.hub-kiosk-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}

.hub-kiosk-actions .hub-btn {
  padding: 1rem;
  font-size: 1rem;
}

/* ================================================================
   RATIO ALERT BAR
   ================================================================ */
.hub-ratio-bar {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding: 0.75rem 1.25rem;
  background: var(--hub-surface);
  border-bottom: 1px solid var(--hub-border);
  scrollbar-width: none;
}

.hub-ratio-bar::-webkit-scrollbar { display: none; }

.hub-ratio-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 1rem;
  border-radius: var(--hub-radius-sm);
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  white-space: nowrap;
  flex-shrink: 0;
}

.hub-ratio-item--alert {
  border-color: var(--hub-danger);
  background: var(--hub-danger-bg);
  animation: hub-pulse-alert 2s infinite;
}

@keyframes hub-pulse-alert {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
  50% { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
}

.hub-ratio-room {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--hub-text-dim);
  font-weight: 600;
}

.hub-ratio-count {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--hub-text);
}

.hub-ratio-count span {
  font-size: 0.875rem;
  color: var(--hub-text-dim);
  font-weight: 500;
}

.hub-ratio-status {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
}

.hub-ratio-status--ok { color: var(--hub-success); }
.hub-ratio-status--alert { color: var(--hub-danger); }

/* ================================================================
   GRID LAYOUTS
   ================================================================ */
.hub-grid-2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--hub-gap);
}

.hub-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--hub-gap);
}

.hub-grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--hub-gap);
}

@media (max-width: 1024px) {
  .hub-grid-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  .hub-grid-2,
  .hub-grid-3,
  .hub-grid-4 { grid-template-columns: 1fr; }
}

/* ================================================================
   UTILITIES
   ================================================================ */
.hub-flex { display: flex; }
.hub-flex-col { display: flex; flex-direction: column; }
.hub-items-center { align-items: center; }
.hub-justify-between { justify-content: space-between; }
.hub-gap-1 { gap: 0.25rem; }
.hub-gap-2 { gap: 0.5rem; }
.hub-gap-3 { gap: 0.75rem; }
.hub-gap-4 { gap: 1rem; }

.hub-text-sm { font-size: 0.875rem; }
.hub-text-xs { font-size: 0.75rem; }
.hub-text-lg { font-size: 1.125rem; }
.hub-text-xl { font-size: 1.25rem; }

.hub-font-medium { font-weight: 500; }
.hub-font-semibold { font-weight: 600; }
.hub-font-bold { font-weight: 700; }

.hub-mb-0 { margin-bottom: 0; }
.hub-mb-1 { margin-bottom: 0.25rem; }
.hub-mb-2 { margin-bottom: 0.5rem; }
.hub-mb-3 { margin-bottom: 0.75rem; }
.hub-mb-4 { margin-bottom: 1rem; }

.hub-mt-2 { margin-top: 0.5rem; }
.hub-mt-3 { margin-top: 0.75rem; }
.hub-mt-4 { margin-top: 1rem; }

.hub-ml-auto { margin-left: auto; }

.hub-w-full { width: 100%; }

/* Loading spinner */
.hub-spinner {
  width: 1.25rem;
  height: 1.25rem;
  border: 2px solid var(--hub-border);
  border-top-color: var(--hub-purple);
  border-radius: 50%;
  animation: hub-spin 0.8s linear infinite;
}

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

/* Empty state */
.hub-empty {
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--hub-text-dim);
}

.hub-empty svg {
  width: 3rem;
  height: 3rem;
  margin-bottom: 1rem;
  opacity: 0.3;
}

/* Toast notifications */
.hub-toast-container {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 300;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.hub-toast {
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-sm);
  padding: 0.875rem 1.25rem;
  box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  animation: hub-toast-in 0.3s ease;
  max-width: 360px;
}

@keyframes hub-toast-in {
  from { transform: translateX(100%); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

.hub-toast--success { border-left: 3px solid var(--hub-success); }
.hub-toast--error { border-left: 3px solid var(--hub-danger); }
.hub-toast--warning { border-left: 3px solid var(--hub-warning); }

/* Tabs */
.hub-tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--hub-border);
  margin-bottom: 1.25rem;
}

.hub-tab {
  padding: 0.625rem 1rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--hub-text-muted);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s;
  margin-bottom: -1px;
}

.hub-tab:hover {
  color: var(--hub-text);
}

.hub-tab.is-active {
  color: var(--hub-purple-light);
  border-bottom-color: var(--hub-purple);
}

/* Avatar */
.hub-avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--hub-purple), var(--hub-cyan));
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.875rem;
  color: white;
  flex-shrink: 0;
}

.hub-avatar--sm {
  width: 2rem;
  height: 2rem;
  font-size: 0.75rem;
}

.hub-avatar--lg {
  width: 3.5rem;
  height: 3.5rem;
  font-size: 1.25rem;
}

/* Timeline (for daily feed) */
.hub-timeline {
  position: relative;
  padding-left: 2rem;
}

.hub-timeline::before {
  content: '';
  position: absolute;
  left: 0.5rem;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--hub-border);
}

.hub-timeline-item {
  position: relative;
  padding-bottom: 1.5rem;
}

.hub-timeline-item::before {
  content: '';
  position: absolute;
  left: -1.75rem;
  top: 0.25rem;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--hub-purple);
  border: 2px solid var(--hub-surface);
}

.hub-timeline-time {
  font-size: 0.75rem;
  color: var(--hub-text-dim);
  margin-bottom: 0.25rem;
}

.hub-timeline-content {
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-sm);
  padding: 0.875rem 1rem;
}

/* Print styles */
@media print {
  .hub-sidebar,
  .hub-topbar,
  .hub-btn,
  .hub-modal-overlay {
    display: none !important;
  }
  .hub-layout {
    grid-template-columns: 1fr;
  }
  .hub-main {
    overflow: visible;
    max-height: none;
  }
  .hub-card {
    break-inside: avoid;
  }
}

/* ── Aliases used by the Administration pages (staff-admin, audit) ──────
   These mirror the .hub-form-* primitives so those pages render styled. */
.hub-label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  color: var(--hub-text-dim);
  margin-bottom: 0.4rem;
}
.hub-input,
select.hub-input,
textarea.hub-input {
  width: 100%;
  background: var(--hub-surface-raised);
  border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-sm);
  color: var(--hub-text);
  padding: 0.6rem 0.75rem;
  font: inherit;
  box-sizing: border-box;
}
.hub-input:focus {
  outline: none;
  border-color: var(--hub-accent, var(--brand-accent));
  box-shadow: 0 0 0 3px rgba(21, 101, 192, 0.15);
}
.hub-form-row {
  margin-bottom: 1rem;
}


/* ── Time picker ────────────────────────────────────────────────────
   Replaces `<input type="time">`, which renders differently in every
   browser and opens a keyboard on a tablet. Columns of tappable values
   with AM/PM, because nobody at a clock wall wants a spinner.

   If the panel fails to build the input stays a plain text field — the
   worst case is what was there before, not a broken control. */
.hub-tp{z-index:9999;background:var(--hub-surface-raised,#fff);
  border:1px solid var(--hub-border);border-radius:12px;
  box-shadow:0 8px 28px rgba(13,42,77,.18);padding:.6rem;width:236px}
.hub-tp-cols{display:flex;gap:.4rem}
.hub-tp-col{flex:1;min-width:0}
.hub-tp-lbl{font-size:.66rem;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--hub-text-dim);text-align:center;
  margin-bottom:.25rem;min-height:.8rem}
.hub-tp-scroll{max-height:170px;overflow-y:auto;display:flex;
  flex-direction:column;gap:.15rem;scrollbar-width:thin}
.hub-tp-b{appearance:none;border:1px solid transparent;background:none;
  font:inherit;font-size:.92rem;padding:.3rem .2rem;border-radius:7px;
  cursor:pointer;color:var(--hub-text);text-align:center;
  font-variant-numeric:tabular-nums}
.hub-tp-b:hover{background:var(--hub-surface)}
.hub-tp-b.is-on{background:var(--brand-accent,#1565C0);color:#fff;font-weight:700}
.hub-tp-foot{display:flex;align-items:center;justify-content:space-between;
  gap:.5rem;margin-top:.5rem;padding-top:.5rem;
  border-top:1px solid var(--hub-border)}
/* The chosen time in words — the columns show 24-hour parts, and this is
   the line that confirms 2:30 PM rather than 02:30. */
.hub-tp-now{font-size:.86rem;font-weight:700;color:var(--hub-text)}
.hub-tp-done{appearance:none;border:1px solid var(--brand-accent,#1565C0);
  background:var(--brand-accent,#1565C0);color:#fff;font:inherit;
  font-size:.82rem;font-weight:600;padding:.25rem .7rem;border-radius:7px;
  cursor:pointer}
.hub-tp-done:hover{filter:brightness(1.08)}


/* Theme toggle. Was inline-styled from hub.js as a plain grey square, which
   beat every stylesheet rule and left it looking bolted on. Brand navy, so it
   belongs with the rest of the header. */
.hub-theme-toggle{
  background:var(--brand-navy,#0D2A4D);
  border:1px solid var(--brand-navy,#0D2A4D);
  border-radius:9px;width:36px;height:36px;cursor:pointer;
  color:#fff;font-size:1rem;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;
  transition:background .12s,transform .12s}
.hub-theme-toggle:hover{background:#164a86;border-color:#164a86;transform:translateY(-1px)}
.hub-theme-toggle:active{transform:none}
/* DARK is the default here and `light` is the opt-in — I had this backwards.
   On the dark page the navy disappears into the background, so the default
   rule is the translucent one and the navy is applied under [data-theme=light]. */
.hub-theme-toggle{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.22)}
.hub-theme-toggle:hover{background:rgba(255,255,255,.2);border-color:rgba(255,255,255,.3)}
[data-theme="light"] .hub-theme-toggle{
  background:var(--brand-navy,#0D2A4D);border-color:var(--brand-navy,#0D2A4D);color:#fff}
[data-theme="light"] .hub-theme-toggle:hover{background:#164a86;border-color:#164a86}


/* ════════════════════════════════════════════════════════════════════
   Page furniture shared by the money screens
   ────────────────────────────────────────────────────────────────────
   Billing grew a context row, a strip of headline figures and a pill tab
   bar, and they worked well enough that Finance should use the same ones
   rather than a near-copy that drifts. They lived in Billing's own
   <style> block, written against two tokens that did not exist, so they
   also only worked in light mode.

   Defined once here, against real tokens, in both themes. The `.bil-*`
   and `.seg` names are kept alongside the neutral ones so the billing
   page's markup and its JS (`.bil-tab`, `#seg-period`) keep working
   untouched.
   ════════════════════════════════════════════════════════════════════ */

/* A line of context, with a control sitting at the end of it. */
.hub-ctx-row, .bstat-row{
  display:flex;align-items:center;justify-content:space-between;
  gap:.7rem;flex-wrap:wrap;margin-bottom:.8rem}

/* Segmented control — a few mutually exclusive choices, one visibly on. */
.hub-seg, .seg{
  display:inline-flex;background:var(--hub-bg-subtle);border-radius:.55rem;
  padding:.18rem;gap:.18rem;border:1px solid var(--hub-border)}
.hub-seg button, .seg button{
  border:0;background:transparent;padding:.34rem .8rem;border-radius:.4rem;
  font:inherit;font-weight:600;font-size:.8rem;color:var(--hub-text-dim);
  cursor:pointer;transition:color .12s,background .12s}
.hub-seg button:hover, .seg button:hover{color:var(--hub-text)}
.hub-seg button.on, .seg button.on{
  background:var(--hub-seg-on);color:var(--hub-text);
  box-shadow:0 1px 2px rgba(0,0,0,.18)}
.hub-seg button:focus-visible, .seg button:focus-visible{
  outline:2px solid var(--hub-primary);outline-offset:1px}

/* Headline figures. The accent stripe is set per tile with --accent. */
.hub-stat-grid, .bstat-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(195px,1fr));
  gap:.9rem;margin-bottom:1.4rem}
.hub-stat, .bstat{
  position:relative;border:1px solid var(--hub-border);border-radius:.85rem;
  padding:.95rem 1rem .9rem 1.15rem;background:var(--hub-surface);overflow:hidden}
.hub-stat::before, .bstat::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
  background:var(--accent,var(--brand-accent))}
.hub-stat-top, .bstat-top{display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem}
.hub-stat-ico, .bstat-ico{
  width:30px;height:30px;border-radius:.55rem;display:grid;place-items:center;
  font-size:.95rem;flex:none}
.hub-stat-label, .bstat-label{
  font-size:.7rem;letter-spacing:.03em;text-transform:uppercase;
  color:var(--hub-text-dim);font-weight:700}
.hub-stat-val, .bstat-val{
  font-size:1.55rem;font-weight:800;font-family:'Quicksand','DM Sans',sans-serif;
  color:var(--hub-text);line-height:1.05}
.hub-stat-sub, .bstat-sub{font-size:.74rem;color:var(--hub-text-dim);margin-top:.25rem}

/* Pill tabs, for switching the body of one card. */
.hub-tabs, .bil-tabs{
  display:inline-flex;background:var(--hub-bg-subtle);border-radius:.6rem;
  padding:.2rem;gap:.2rem;flex:0 0 auto;max-width:100%;overflow-x:auto;
  border:1px solid var(--hub-border);scrollbar-width:none}
.hub-tabs::-webkit-scrollbar, .bil-tabs::-webkit-scrollbar{display:none}
.hub-tab, .bil-tab{
  border:0;background:transparent;padding:.45rem .9rem;border-radius:.45rem;
  font:inherit;font-weight:600;white-space:nowrap;flex:0 0 auto;
  display:inline-flex;align-items:center;gap:.4rem;
  font-size:.85rem;color:var(--hub-text-dim);cursor:pointer;
  transition:color .12s,background .12s}
.hub-tab:hover, .bil-tab:hover{color:var(--hub-text)}
.hub-tab.is-active, .bil-tab.is-active{
  background:var(--hub-seg-on);color:var(--hub-text);
  box-shadow:0 1px 2px rgba(0,0,0,.18)}
.hub-tab:focus-visible, .bil-tab:focus-visible{
  outline:2px solid var(--hub-primary);outline-offset:1px}

/* The tools belonging to the open tab, at the other end of its header. */
.hub-tab-tools{
  display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;
  flex:1 1 auto;min-width:0;justify-content:flex-end}
.hub-tab-tools > *{flex:0 0 auto}

@media (prefers-reduced-motion: reduce){
  .hub-seg button, .seg button, .hub-tab, .bil-tab{transition:none}
}
