/* ============================================================================
   Webronic Support Center — design system (light, world-class, dark-ready).
   Colors flow from CSS variables so a future dark theme is a variable swap.
   ========================================================================== */
:root {
  /* Surfaces */
  --bg: #f4f6fb;
  --bg-grad: radial-gradient(1200px 600px at 70% -10%, #eef2fe 0%, #f4f6fb 55%);
  --surface: #ffffff;
  --surface-2: #f8fafc;
  --border: #e6e9f0;
  --border-strong: #d6dbe6;

  /* Text — all pass WCAG AA (>=4.5:1) on --surface/--bg */
  --text: #0f172a;
  --text-2: #475569;
  --muted: #5c6472;      /* ~5.1:1 on white — safe for small labels/placeholders */

  /* Brand + semantics */
  --primary: #2563eb;
  --primary-600: #1d4ed8;
  --primary-tint: #e8eefe;
  --success: #16a34a; --success-bg: #dcfce7;
  --warning: #d97706; --warning-bg: #fef3c7;
  --danger:  #dc2626; --danger-bg:  #fee2e2;
  --purple:  #7c3aed; --purple-bg:  #ede9fe;
  --info:    #2563eb; --info-bg:    #dbeafe;

  --radius: 10px;
  --radius-lg: 16px;
  --radius-xl: 22px;
  --shadow-sm: 0 1px 2px rgba(15,23,42,.06);
  --shadow: 0 4px 16px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.04);
  --shadow-lg: 0 20px 45px rgba(15,23,42,.12);
  --ring: 0 0 0 4px rgba(37,99,235,.15);
  --focus: 0 0 0 3px rgba(37,99,235,.5);
  --transition: .16s ease;
  --sidebar-w: 264px;
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Brand marks — sampled from the official logo (Projects/Designs/Webronic.png),
     not invented. Use these, never a hardcoded hex in markup. Note they are NOT
     --primary: the UI blue is its own, softer token and stays independent so a
     brand refresh cannot silently restyle every button in the app. */
  --brand-blue: #2776ea;
  --brand-green: #76ea27;
}

/* Dark theme — set by data-theme="dark" on <html>, written by the toggle in
   index.php before first paint (see the inline script in <head>) so there is
   never a light flash. Same token names as :root above; every component here
   already reads var(--x), so this is the "variable swap" the comment above
   promised rather than a second stylesheet. Status colors use a translucent
   background (the dark surface shows through) instead of light-mode's solid
   pale tint, which would look like a mistake floating on a dark page. */
html[data-theme="dark"] {
  --bg: #0b1220;
  --bg-grad: radial-gradient(1200px 600px at 70% -10%, #121b30 0%, #0b1220 55%);
  --surface: #131c2e;
  --surface-2: #0f1728;
  --border: #223049;
  --border-strong: #2c3c5a;

  --text: #e8edf7;
  --text-2: #aab7cc;
  --muted: #8a96ac;

  --primary: #4c8dff;
  --primary-600: #6ba2ff;
  --primary-tint: rgba(76,141,255,.16);
  --success: #34d17e; --success-bg: rgba(52,209,126,.16);
  --warning: #f0a63a; --warning-bg: rgba(240,166,58,.16);
  --danger:  #f0655c; --danger-bg:  rgba(240,101,92,.16);
  --purple:  #a78bfa; --purple-bg:  rgba(167,139,250,.16);
  --info:    #4c8dff; --info-bg:    rgba(76,141,255,.16);

  --shadow-sm: 0 1px 2px rgba(0,0,0,.35);
  --shadow: 0 4px 16px rgba(0,0,0,.4), 0 1px 3px rgba(0,0,0,.3);
  --shadow-lg: 0 20px 45px rgba(0,0,0,.5);
  --ring: 0 0 0 4px rgba(76,141,255,.25);
  --focus: 0 0 0 3px rgba(76,141,255,.55);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
/* linkify() in app.js wraps a URL found inside free text (a handover note, a
   Notice Board message) in one of these — it needs to actually LOOK tappable,
   which the bare `a` reset above deliberately does not. */
.auto-link { color: var(--primary-600); text-decoration: underline; word-break: break-all; }
.auto-link:hover, .auto-link:focus-visible { text-decoration-thickness: 2px; }
h1, h2, h3 { margin: 0; }
.muted { color: var(--text-2); }

/* Accessibility: visible focus, skip link, screen-reader-only, reduced motion */
:focus-visible { outline: none; }
a:focus-visible, button:focus-visible, input:focus-visible,
[tabindex]:focus-visible, .nav-item:focus-visible {
  outline: none; box-shadow: var(--focus); border-radius: 8px;
}
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 14px; top: -60px; z-index: 200;
  background: var(--primary); color: #fff; padding: 10px 16px; border-radius: 10px;
  font-weight: 600; box-shadow: var(--shadow); transition: top var(--transition);
}
.skip-link:focus { top: 14px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* Buttons ------------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; border: 1px solid transparent; border-radius: var(--radius);
  padding: 11px 18px; font-family: var(--font); font-size: 14.5px; font-weight: 600;
  letter-spacing: -0.01em; cursor: pointer; transition: var(--transition);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--primary); color: #fff; box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--primary-600); }
.btn-primary:active { background: #1a44b8; }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
.btn-block { width: 100%; }
.btn-ghost { background: var(--surface); border-color: var(--border-strong); color: var(--text-2); }
.btn-ghost:hover { background: var(--surface-2); color: var(--text); }
.btn-sm { min-height: 36px; padding: 8px 13px; font-size: 13.5px; }

/* Inputs ------------------------------------------------------------------- */
.field { position: relative; display: grid; gap: 7px; }
.field > label { font-size: 12.5px; font-weight: 600; color: var(--text-2); }
.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap .lead-icon { position: absolute; left: 13px; color: var(--muted); pointer-events: none; }
.input {
  width: 100%; background: var(--surface); border: 1.5px solid var(--border-strong);
  border-radius: var(--radius); color: var(--text); padding: 12px 14px; font-size: 14.5px;
  transition: var(--transition);
}
.input.has-lead { padding-left: 40px; }
.input.has-trail { padding-right: 44px; }
.input::placeholder { color: var(--muted); }
.input:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.input-toggle {
  position: absolute; right: 8px; display: grid; place-items: center; width: 32px; height: 32px;
  border: none; background: transparent; color: var(--muted); cursor: pointer; border-radius: 8px;
}
.input-toggle:hover { color: var(--text-2); background: var(--surface-2); }

.checkline { display: flex; align-items: center; gap: 9px; font-size: 13.5px; color: var(--text-2); }
.checkline input { width: 17px; height: 17px; accent-color: var(--primary); }
.link { color: var(--primary); font-weight: 600; font-size: 13.5px; }
.link:hover { text-decoration: underline; }

/* ============================================================================
   Auth (split screen)
   ========================================================================== */
.auth { display: grid; grid-template-columns: 1.05fr 1fr; min-height: 100vh; }
.auth-hero {
  position: relative; overflow: hidden; color: #fff; padding: 56px 60px;
  display: flex; flex-direction: column;
  background: linear-gradient(150deg, #2f6bf6 0%, #2456d6 45%, #1e40af 100%);
}
.auth-hero::before, .auth-hero::after {
  content: ""; position: absolute; border-radius: 50%; background: rgba(255,255,255,.08);
}
.auth-hero::before { width: 460px; height: 460px; top: -120px; right: -120px; }
.auth-hero::after  { width: 320px; height: 320px; bottom: -140px; left: 40px; background: rgba(255,255,255,.06); }
.auth-brand { display: flex; flex-direction: column; align-items: flex-start; gap: 9px; position: relative; z-index: 1; }
/* The W glyph is ~1.8:1, so height is fixed and width follows — forcing a square
   squashed the globe. It keeps a white plate because the hero behind it is blue. */
.auth-brand img { height: 46px; width: auto; border-radius: 10px; padding: 5px 8px; }
.auth-brand .b-name { font-weight: 800; font-size: 17px; letter-spacing: .2px; }
.auth-brand .b-sub { font-size: 11px; letter-spacing: 2.5px; opacity: .85; }
/* The heading follows the brand instead of floating away from it. `margin-top: auto`
   used to push it into the vertical middle, which left a large dead gap under the logo
   and read as three unrelated blocks rather than one identity: mark → label → name.
   `.auth-features` keeps its `margin-bottom: auto`, so the copyright still sits at the
   foot. max-width:12ch is deliberate — it wraps to Support / Operations / Center. */
.auth-hero h1 { position: relative; z-index: 1; font-size: 46px; line-height: 1.05; font-weight: 800; margin: 36px 0 18px; max-width: 12ch; }
.auth-hero .lede { position: relative; z-index: 1; font-size: 16px; opacity: .92; max-width: 40ch; margin-bottom: 34px; }
.auth-features { position: relative; z-index: 1; display: grid; gap: 20px; margin-bottom: auto; }
.auth-feature { display: flex; gap: 14px; align-items: flex-start; }
.auth-feature .fi { width: 40px; height: 40px; border-radius: 12px; background: rgba(255,255,255,.14); display: grid; place-items: center; flex: none; }
.auth-feature h4 { font-size: 15px; margin-bottom: 2px; }
.auth-feature p { font-size: 13.5px; opacity: .85; margin: 0; }
.auth-copy { position: relative; z-index: 1; font-size: 12.5px; opacity: .7; }

.auth-panel { display: grid; place-items: center; padding: 40px; background: var(--bg-grad); }
.auth-card { width: 100%; max-width: 380px; }
/* The mark sits directly on the panel — no plate, no shadow, no rounded container.
   It had all three, on a background that is already near-white, so the "card" was a
   62px box drawn around a logo that needed nothing behind it (§4: no unnecessary
   shadows / borders / decorative backgrounds / oversized containers). The hero mark
   keeps its white plate because it sits on the blue panel and genuinely needs the
   contrast — that is the same logo in a different context, which is the point of §16.
   Also used by the password-change and two-factor gates, which sit on the same
   near-white panel and get the same treatment. */
.auth-card .card-logo { display: grid; place-items: center; margin: 0 auto 16px; }
/* The W glyph is ~3:2, not square — fixing both dimensions squashed the globe. */
/* Sized by HEIGHT so the ~3:2 glyph keeps its proportions (fixing both dimensions
   squashed the globe). 42px against a 26px "Welcome back!" supports the heading
   without out-shouting it — the heading stays the primary element. */
.auth-card .card-logo img { height: 42px; width: auto; }
/* Mobile-only brand strip — see the comment in login.php. Hidden by default because
   the hero carries the identity at desktop width. */
.auth-mbrand { display: none; }
.auth-card h2 { text-align: center; font-size: 26px; font-weight: 800; }
/* 6px under the heading, 26px under the pair: the subtitle belongs to the heading, and
   the spacing has to say so (§5) rather than leaving them visually disconnected. */
.auth-card .sub { text-align: center; color: var(--text-2); margin: 6px 0 26px; max-width: 34ch; margin-inline: auto; }
.auth-form { display: grid; gap: 18px; }
.auth-row { display: flex; align-items: center; justify-content: space-between; }
.auth-error { margin-top: 16px; color: var(--danger); background: var(--danger-bg); border: 1px solid #fecaca; padding: 10px 12px; border-radius: var(--radius); font-size: 13px; }
.auth-foot { text-align: center; color: var(--muted); font-size: 12.5px; margin-top: 22px; line-height: 1.7; }
/* Public-schedule link on the sign-in page — the one thing here that needs no account. */
.auth-public { text-align: center; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border); line-height: 1.7; }
.auth-public a { color: var(--primary); font-size: 14px; font-weight: 600; text-decoration: none; }
.auth-public a:hover { text-decoration: underline; }
.auth-public span { color: var(--muted); font-size: 12.5px; }
.mfa-hint { text-align: center; color: var(--text-2); font-size: 13.5px; margin: -4px 0 4px; }

/* ============================================================================
   App shell
   ========================================================================== */
.app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }
/* Mobile-only chrome, off by default so desktop is unchanged by any of the above. */
.nav-backdrop, .nav-toggle, .tb-wordmark, .sb-user { display: none; }

.sidebar {
  background: var(--surface); border-right: 1px solid var(--border);
  display: flex; flex-direction: column; padding: 15px 12px 12px;
  position: sticky; top: 0; height: 100vh;
}
/* Brand lockup. The master artwork is a WIDE wordmark (≈5:1), so it is sized by
   width and stacked above the app name rather than squeezed into a 32px square —
   scaling it to an icon box destroyed the "DESIGNING THE WORLD" line entirely. */
.sb-brand { display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
            padding: 6px 8px 13px; margin-bottom: 5px; border-bottom: 1px solid var(--border); }
.sb-brand .sb-wordmark { width: 100%; max-width: 196px; height: auto; display: block; }
.sb-brand .n { font-weight: 800; font-size: 15px; letter-spacing: -.01em; line-height: 1.05; }
.sb-brand .s { font-size: 9.5px; font-weight: 700; letter-spacing: 1.7px; color: var(--muted); }

.nav { display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; margin: 0 -4px; padding: 5px 4px; }
.nav::-webkit-scrollbar { width: 6px; }
.nav::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }
.nav::-webkit-scrollbar-track { background: transparent; }

/* Every top-level entry — flat item or collapsible group header — is a row with
   a rounded icon "chip", a label, and (for groups) a chevron. */
.nav-item, .nav-group > .gh {
  display: flex; align-items: center; gap: 11px; padding: 6px 11px 6px 7px; border-radius: 12px;
  color: var(--text-2); font-weight: 600; font-size: 14.5px; cursor: pointer;
  list-style: none; user-select: none; transition: var(--transition);
}
.nav-item .nl, .gh .nl { flex: 1; min-width: 0; letter-spacing: -.006em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ni {
  width: 34px; height: 34px; flex: none; border-radius: 10px; display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-2); transition: var(--transition);
}
.ni svg { width: 18px; height: 18px; }

.nav-item:hover, .nav-group > .gh:hover { background: var(--surface-2); color: var(--text); }
.nav-item:hover .ni, .nav-group > .gh:hover .ni { background: var(--border); color: var(--text); }
.nav-item:focus-visible, .nav-group > .gh:focus-visible { outline: none; box-shadow: var(--focus); }

.nav-item.active { background: var(--primary-tint); color: var(--primary-600); font-weight: 700; }
.nav-item.active .ni { background: var(--primary); color: #fff; }

.nav-group { margin-top: 3px; }
.nav-group > .gh::-webkit-details-marker { display: none; }
.nav-group[open] > .gh { color: var(--text); }
.gh-chev { display: grid; place-items: center; color: var(--muted); opacity: .7; transition: transform .2s ease; flex: none; }
.nav-group[open] > .gh .gh-chev { transform: rotate(90deg); }

/* Sub-items: a touch smaller, grouped tightly beneath the header. */
.ng-body { display: flex; flex-direction: column; gap: 2px; padding: 2px 0 5px; }
.ng-body .nav-item { padding: 5px 11px 5px 8px; font-size: 13.5px; }
.ng-body .nav-item .ni { width: 29px; height: 29px; border-radius: 9px; }
.ng-body .nav-item .ni svg { width: 16px; height: 16px; }

/* Nested sub-group (Administration → Config, 2026-09-10): one level deeper
   than a normal group, so it needs a visible indent and slightly quieter
   header to read as "inside Administration" rather than a sibling group. */
.nav-subgroup { margin-left: 14px; margin-top: 2px; }
.nav-subgroup > .gh-sub { font-size: 13px; padding: 5px 11px 5px 8px; }
.nav-subgroup > .gh-sub .ni { width: 27px; height: 27px; border-radius: 8px; }
.nav-subgroup .ng-subbody .nav-item { padding-left: 8px; }

.nav-item.soon { color: var(--muted); cursor: default; }
.nav-item.soon .ni { opacity: .55; }
.nav-item.soon:hover { background: transparent; color: var(--muted); }
.nav-item.soon:hover .ni { background: var(--surface-2); color: var(--text-2); }
.badge-soon { flex: none; font-size: 10px; font-weight: 600; letter-spacing: .2px; color: var(--muted); background: var(--surface-2); border: 1px solid var(--border); padding: 2px 8px; border-radius: 999px; }

.main { display: flex; flex-direction: column; min-width: 0; }
.topbar { display: flex; align-items: center; justify-content: flex-end; gap: 18px; padding: 16px 30px; }
/* flex:none — the bell is a fixed 40px target, never a shrinkable one. Without it the
   flex row steals its width first: at 360px it was squashed to 21px AND pushed to
   x=377, which is what made every app page 400px wide on a phone. */
.bell { position: relative; width: 40px; height: 40px; flex: none; padding: 0; border-radius: 12px; display: grid; place-items: center; background: var(--surface); border: 1px solid var(--border); color: var(--text-2); cursor: pointer; transition: var(--transition); }
.bell:hover { background: var(--surface-2); color: var(--text); }
.bell .dot { position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--danger); color: #fff; font-size: 10.5px; font-weight: 700; display: grid; place-items: center; }
/* Theme toggle — a track-and-knob switch, same shape everywhere this pattern
   is used. The knob's icon is swapped (sun/moon) by JS, not two elements, so
   there is one thing to animate rather than a cross-fade. */
.theme-toggle { width: 44px; height: 24px; border-radius: 20px; background: var(--surface-2); border: 1px solid var(--border);
                display: flex; align-items: center; padding: 2px; cursor: pointer; flex: none; transition: var(--transition); }
.theme-toggle:hover { border-color: var(--border-strong); }
.theme-toggle:focus-visible { outline: none; box-shadow: var(--focus); }
.theme-toggle .knob { width: 18px; height: 18px; border-radius: 50%; background: var(--primary); color: #fff;
                       display: grid; place-items: center; transition: transform .2s ease, background .2s ease; }
html[data-theme="dark"] .theme-toggle .knob { transform: translateX(20px); background: var(--warning); }
.theme-toggle .knob svg { width: 12px; height: 12px; stroke: currentColor; fill: none; }
.who { display: flex; align-items: center; gap: 11px; }
.who .avatar { width: 40px; height: 40px; border-radius: 50%; background: var(--primary); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 14px; }
.who .n { font-weight: 700; font-size: 14px; }
.who .r { font-size: 12px; color: var(--text-2); }
.who .logout { font-size: 11.5px; color: var(--primary); font-weight: 600; }

/* Quicknav: Dashboard / My Work / SOD-EOD / Notice Board as one horizontal
   strip (2026-09-09), replacing their old spread across the sidebar Home
   group and a separate topbar icon row. Reuses .nav-item as-is (icon chip +
   label, active-state colouring) — only the CONTAINER differs from the
   sidebar's vertical stack: a row, not a column, with items sized to their
   own content instead of stretched full-width. */
.quicknav { display: flex; align-items: center; gap: 6px; padding: 0 30px 12px; overflow-x: auto; }
.quicknav .nav-item { flex: none; }
@media (max-width: 700px) { .quicknav { padding: 0 14px 10px; } }

.content { padding: 8px 30px 40px; }
.loader, .empty { color: var(--text-2); padding: 40px; text-align: center; }

/* Page hero */
.page-hero { display: flex; align-items: center; gap: 18px; margin: 10px 0 24px; }
.page-hero .hero-icon { width: 56px; height: 56px; border-radius: 16px; background: var(--primary); color: #fff; display: grid; place-items: center; box-shadow: var(--shadow); }
.page-hero h1 { font-size: 30px; font-weight: 800; letter-spacing: -.5px; }
.page-hero p { color: var(--text-2); margin-top: 2px; }

/* Stat cards */
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 22px; }
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px; display: flex; align-items: center; gap: 16px; box-shadow: var(--shadow-sm); transition: var(--transition); }
.stat-card:hover { box-shadow: var(--shadow); }
.stat-card.alert { border-color: #f6c9c9; box-shadow: 0 0 0 1px #f8d4d4 inset, var(--shadow-sm); }
.stat-ic { width: 46px; height: 46px; border-radius: 14px; display: grid; place-items: center; flex: none; }
.stat-num { font-size: 30px; font-weight: 800; line-height: 1; }
.stat-lbl { color: var(--text-2); font-size: 13px; margin-top: 5px; }

/* Panels grid */
.dash-cols { display: grid; grid-template-columns: 1.6fr 1fr; gap: 18px; align-items: start; }
.stack { display: grid; gap: 18px; }
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 22px; box-shadow: var(--shadow-sm); }
.panel-head { display: flex; align-items: center; gap: 9px; margin-bottom: 16px; }
/* Collapsible panels (Hide/Show toggle added by enhancePanels in app.js). */
.panel.panel-collapsed > *:not(.panel-head) { display: none !important; }
.panel.panel-collapsed > .panel-head { margin-bottom: 0; }
.panel-collapse-btn { flex: none; }
.panel-head .ph-ic { color: var(--primary); display: grid; place-items: center; }
.panel-head h2 { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }

/* Alert banner */
.alert-banner { display: flex; align-items: center; gap: 12px; background: var(--danger-bg); border: 1px solid #fbcaca; color: #991b1b; padding: 13px 15px; border-radius: var(--radius); font-weight: 600; font-size: 13.5px; }
.ok-banner { display: flex; align-items: center; gap: 12px; background: var(--success-bg); border: 1px solid #bbf7d0; color: #166534; padding: 13px 15px; border-radius: var(--radius); font-weight: 600; font-size: 13.5px; }

.sub-head { font-size: 11.5px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: var(--muted); margin: 18px 0 6px; }
.divider { height: 1px; background: var(--border); margin: 18px 0; }
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }

/* Colored tiles (team / incidents) */
.tile-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.tile { border-radius: var(--radius-lg); padding: 16px; text-align: center; }
.tile .t-num { font-size: 26px; font-weight: 800; line-height: 1; }
.tile .t-lbl { font-size: 12.5px; font-weight: 600; margin-top: 6px; }
.tile.green  { background: var(--success-bg); color: var(--success); }
.tile.purple { background: var(--purple-bg);  color: var(--purple); }
.tile.blue   { background: var(--info-bg);    color: var(--info); }
.tile.red    { background: var(--danger-bg);  color: var(--danger); }
.tile.amber  { background: var(--warning-bg); color: var(--warning); }
/* Small labels need AA (4.5:1) — darken ink vs the tint (numbers are large text). */
.tile.green  .t-lbl { color: #166534; }
.tile.purple .t-lbl { color: #5b21b6; }
.tile.blue   .t-lbl { color: #1e40af; }
.tile.red    .t-lbl { color: #991b1b; }
.tile.amber  .t-lbl { color: #92400e; }

/* List (follow-ups / projects) */
/* grid-template-columns matters here even though there is only one column. A grid
   with no explicit template gets an implicit `auto` track, and an `auto` track sizes
   to MAX-content — so a row wider than the container makes the track overflow rather
   than the row wrap. On Incidents at 360px that put every row at 434px and pushed the
   status and root-cause selects off the right edge, unreachable.

   It was invisible until 2026-08-10 only because dev had no incidents to render: an
   empty .list has no track to blow out, so the mobile audit passed the page without
   ever laying a row out. Same lesson as the page_runtime false pass — a green check
   on an empty fixture is not evidence.

   minmax(0, 1fr), not 1fr: a bare 1fr still floors at min-content. */
.list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; }
.list-item { display: flex; align-items: center; gap: 12px; padding: 12px 4px; border-bottom: 1px solid var(--border); }
.list-item:last-child { border-bottom: none; }
.list-item .li-title { font-weight: 600; font-size: 13.5px; }
.list-item .li-meta { font-size: 12px; color: var(--text-2); }
.pill { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.pill.high { background: var(--danger-bg); color: var(--danger); }
.pill.med  { background: var(--warning-bg); color: var(--warning); }
.pill.low  { background: var(--info-bg); color: var(--info); }
.empty-mini { color: var(--muted); font-size: 13px; padding: 16px 4px; }

/* Icon chips reused for stat icons */
.ic-green  { background: var(--success-bg); color: var(--success); }
.ic-amber  { background: var(--warning-bg); color: var(--warning); }
.ic-purple { background: var(--purple-bg);  color: var(--purple); }
.ic-red    { background: var(--danger-bg);  color: var(--danger); }
.ic-blue   { background: var(--info-bg);    color: var(--info); }

/* ============================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1080px) {
  /* minmax(0,…), not 1fr: a bare 1fr floors at the card's min-content width, and at
     320px two of those plus the gap exceed the viewport — the numbers ran past the
     right edge. The mobile audit only tests 360px, which is why this survived; the
     320px column is a real device (iPhone SE, older Androids). */
  .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dash-cols { grid-template-columns: 1fr; }
}
/* Tablet: keep both columns but stop the hero shouting. At 46px in a ~500px column the
   heading crowded its own padding; the panel narrows and the type steps down with it. */
@media (max-width: 1080px) and (min-width: 861px) {
  .auth { grid-template-columns: .92fr 1fr; }
  .auth-hero { padding: 40px 36px; }
  .auth-hero h1 { font-size: 36px; margin: 26px 0 14px; }
  .auth-hero .lede { font-size: 15px; margin-bottom: 26px; }
  .auth-features { gap: 15px; }
  .auth-panel { padding: 32px 26px; }
}

/* Phone: one column, and an intentional layout rather than the desktop one with a
   panel deleted. The hero does not shrink usefully — a 460px decorative circle and a
   three-item feature list are desktop furniture — so it is dropped and its JOB (saying
   whose product this is) is handed to .auth-mbrand, which is built for this width. */
@media (max-width: 860px) {
  .auth { grid-template-columns: 1fr; }
  .auth-hero { display: none; }

  .auth-panel { padding: 28px 20px max(28px, env(safe-area-inset-bottom)); align-content: start; }
  .auth-card { max-width: 420px; padding-top: 8px; }

  .auth-mbrand { display: flex; flex-direction: column; align-items: center; gap: 9px; margin-bottom: 24px; }
  .auth-mbrand img { width: 172px; max-width: 58vw; height: auto; display: block; }
  .auth-mbrand span { font-size: 10.5px; font-weight: 700; letter-spacing: 2.4px; color: var(--text-2); }
  /* One mark per screen: the wordmark above replaces the round glyph here. Scoped to
     `.auth` so the standalone password / two-factor gates — which have no other
     branding — keep theirs. */
  .auth .auth-card .card-logo { display: none; }

  .auth-card h2 { font-size: 23px; }
  /* Let the subtitle wrap over two lines rather than shrink or clip (§5). */
  .auth-card .sub { font-size: 14px; max-width: 26ch; }
}

/* Small phones: the form is the page. Nothing decorative competes for the fold. */
@media (max-width: 380px) {
  .auth-panel { padding: 20px 14px; }
  .auth-mbrand { margin-bottom: 18px; }
  .auth-mbrand img { width: 148px; }
  .auth-card h2 { font-size: 21px; }
  .auth-row { flex-wrap: wrap; gap: 10px; }
}
/* ── Mobile navigation ────────────────────────────────────────────────────────
   Until 2026-08-10 the phone layout collapsed the grid to one column and made the
   sidebar `position: static`. That does not shrink the sidebar — it turns it into
   the first grid ROW, so the entire navigation stacked above the page: logo, every
   nav item, both expanded groups, then the profile (which lives in .topbar, the
   first thing inside .main), and only then the content. Reaching Monitoring meant
   scrolling past the whole menu, twice — once to open it, once to get past it.

   It becomes an off-canvas drawer instead. 900px rather than 720 so a portrait
   tablet gets the drawer too: at 768px the fixed 240px sidebar left the content
   about 500px, which is not a working layout either.

   The nav itself is NOT duplicated. One PHP render, one set of role checks, one
   active-state rule — a second mobile menu would be a second thing to forget. */
@media (max-width: 900px) {
  .app { grid-template-columns: 1fr; }

  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 60;
    width: min(86vw, 320px); height: 100dvh;   /* dvh: the address bar must not clip it */
    transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: 0 0 40px rgba(0,0,0,.18);
    padding-top: max(15px, env(safe-area-inset-top));
    padding-bottom: max(12px, env(safe-area-inset-bottom));
    overscroll-behavior: contain;    /* scrolling the drawer must not scroll the page */
    visibility: hidden;              /* keeps it out of the tab order while closed */
  }
  body.nav-open .sidebar { transform: none; visibility: visible; }

  .nav-backdrop { display: block; position: fixed; inset: 0; z-index: 55;
                  background: rgba(15,23,42,.45); opacity: 0; transition: opacity .22s ease; }
  body.nav-open .nav-backdrop { opacity: 1; }

  /* The page must not scroll behind an open drawer. position:fixed rather than
     overflow:hidden — iOS Safari ignores the latter on body. */
  body.nav-open { position: fixed; width: 100%; overflow: hidden; }

  .nav-toggle { display: inline-flex; align-items: center; justify-content: center;
                width: 44px; height: 44px; flex: none;    /* 44px: a real touch target */
                border: 1px solid var(--border); border-radius: 10px;
                background: var(--surface); color: var(--text); cursor: pointer; }
  .nav-toggle .ico-close { display: none; }
  body.nav-open .nav-toggle .ico-open  { display: none; }
  body.nav-open .nav-toggle .ico-close { display: block; }

  .tb-wordmark { display: block; width: 132px; max-width: 42vw; height: auto; margin-right: auto; order: 1; }
  /* Sticky so the way out is always one tap away, however far down the page is. */
  .topbar { position: sticky; top: 0; z-index: 40; justify-content: flex-start;
            gap: 10px; background: var(--bg); border-bottom: 1px solid var(--border);
            padding: 10px 14px; padding-top: max(10px, env(safe-area-inset-top));
            /* Wrap, so a row that runs out of width grows DOWN instead of sideways.
               Four items (brand, timer, bell, hamburger) never fit on one 320px line. */
            flex-wrap: wrap; }

  /* Order, not DOM order: the markup stays one header for both layouts. Brand left,
     hamburger hard right (it is the control a thumb reaches for), bell beside it, and
     the shift timer dropped onto its own full-width row where it has room to breathe.
     Previously the timer sat mid-row and shoved the bell off the screen entirely. */
  .bell { order: 2; }
  .nav-toggle { order: 3; }
  .shift-timer { order: 4; flex-basis: 100%; margin-right: 0; justify-content: flex-start; }
  /* The desktop profile block duplicates what the drawer now shows. */
  .topbar .who { display: none; }

  /* The drawer keeps the brand. It was hidden on the grounds that the topbar already
     shows one — but while the drawer is open the topbar sits behind a 45%-black
     backdrop, so the menu read as an anonymous list of links. Only one lockup is ever
     legible at a time, so there is no duplication to avoid. Same asset, same treatment
     as desktop: the sidebar lockup is not the thing that needed changing. */
  .sidebar .sb-brand { display: block; order: 1; padding: 2px 8px 12px; margin-bottom: 0; }
  .sidebar .sb-brand .sb-wordmark { max-width: 168px; }
  .sb-user { order: 2; }
  .sidebar .nav { order: 3; }

  .sb-user { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
             padding: 4px 8px 13px; margin-bottom: 5px; border-bottom: 1px solid var(--border); }
  .sb-user .sb-user-t { min-width: 0; }
  .sb-user .n { font-weight: 700; font-size: 14px; line-height: 1.2; }
  .sb-user .r { font-size: 11.5px; color: var(--muted); }
  .sb-user .sb-user-a { flex-basis: 100%; display: flex; gap: 14px; font-size: 12px; }
  .sb-user .sb-user-a a { color: var(--primary-600); text-decoration: none; font-weight: 600; }

  .content { padding: 8px 16px 30px; }
  .two-col { grid-template-columns: minmax(0, 1fr); }

  /* A list row is "text ... actions". On a phone the action group cannot shrink
     (it is flex:none in every module), so the text column was squeezed to ~13px
     and rendered one word per line UNDER the buttons — Monitoring was unusable.
     Stack instead: text on its row, actions on theirs, left-aligned. This is the
     shared .list-item, so it fixes Monitoring, Follow-ups, Templates and Team
     together rather than four times over. */
  .list-item { flex-direction: column; align-items: stretch; gap: 8px; }
  .list-item > :last-child { justify-content: flex-start; }

  /* Three fixed columns at 360px gave each tile ~100px and clipped the numbers. */
  .tile-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* ── Touch targets ──────────────────────────────────────────────────────────
     .btn is already 44px; the compact variants are not, and on a phone every one of
     them is something a thumb has to hit. 36px and 32px are comfortable with a mouse
     and fiddly with a finger, so they grow HERE ONLY — desktop keeps the dense sizing
     the pages were designed around, which is why this is not a change to .btn-sm
     itself. */
  .btn-sm { min-height: 44px; padding: 10px 14px; }
  .input-toggle { width: 44px; height: 44px; right: 4px; }
  .input.has-trail { padding-right: 52px; }   /* clear the now-wider toggle */

  /* Compact controls declared INSIDE page fragments. `.icon-btn` is written out
     identically in six of them, so the floor is set once here and every page inherits
     it — editing six copies would leave the seventh page to be discovered later. The
     measured sizes were 21–32px, i.e. roughly half a fingertip.

     The `#main-content` prefix is load-bearing, not decoration. A fragment's <style>
     is injected into the document AFTER this file, so an equal-specificity rule here
     LOSES the cascade on source order: monitoring's own `.chk-warn{min-height:30px}`
     and inbox's `.sthub .ib-not` both beat a bare `.chk-warn`/`.ib-not`. The id lifts
     this to (1,1,0) so the floor actually holds. Every selector below is a page-
     fragment class, which is why they all live inside #main-content anyway. */
  #main-content .icon-btn, #main-content .chk-warn, #main-content .chk-fail,
  #main-content .bc-btn, #main-content .sd-navbtn, #main-content .cellbtn,
  #main-content .rowbtn, #main-content .ib-tab, #main-content .ghead,
  /* Segmented toggles. The same control exists under three names — .ib-viewtoggle
     (Split/Table), .ib-period (Today/7d/…) and .bc-viewtoggle (Cards/Table) — so a
     fourth is likely; add it here rather than to the page. */
  #main-content .ib-period button, #main-content .ib-viewtoggle button,
  #main-content .bc-viewtoggle button {
    min-height: 44px;
  }
  #main-content .icon-btn { min-width: 44px; }
  /* Inline text pills are part of a sentence, not blocks — they reach a usable target
     through padding instead of becoming 44px squares mid-paragraph. */
  #main-content .bc-rc-toggle { min-height: 40px; padding: 6px 12px; }
  /* The NOT filter is a segmented sibling of its select: it matches the control's
     height rather than being sized on its own, so it grows with it. */
  #main-content .ib-not { min-height: 44px; padding: 0 11px; }

  /* Card actions stack rather than overflow (§15). A row of buttons that cannot fit
     wraps to the next line instead of pushing out of its card. */
  .panel-head, .io-row { flex-wrap: wrap; }
}

/* The narrowest phones still in use. Two columns of anything is one too many. */
@media (max-width: 380px) {
  .stat-grid, .tile-row { grid-template-columns: minmax(0, 1fr); }

  /* Week/period navigators — "‹ 14–20 Jul › [Today]". The same cluster is written as
     .cal-nav in calendar.php and roster.php and .schd-wknav in schedule.php; all three
     are a nowrap flex row, so at 320px the Today button hung 35–78px past the screen
     and took the page with it. One rule, because it is one pattern with three names. */
  .cal-nav, .schd-wknav, .sd-wknav { flex-wrap: wrap; row-gap: 8px; }
  .cal-nav > *, .schd-wknav > * { min-width: 0; }

  /* A full-width action inside a padded card: box-sizing keeps the padding INSIDE the
     width instead of adding to it, which is what pushed Team's submit past its form. */
  .btn-block, form .btn-block { width: 100%; box-sizing: border-box; }

  /* Nothing may force the page wider than the phone. A grid track's implicit
     minimum is min-content, so one nowrap heading is enough to do it. */
  .page, .panel { min-width: 0; }
  .panel-head { flex-wrap: wrap; }
}

/* Deliberately NOT `html, body { overflow-x: hidden }` here. That hides sideways
   overflow instead of fixing it — and it hides it from the mobile audit too, so
   the next component to blow out the viewport would ship unnoticed while its
   buttons sat clipped and unreachable. Fix the row that overflows. */


/* ── CSV import panel ───────────────────────────────────────────────────
   Shared by every module that mounts mountCsvImport() (assets/js/app.js).
   Lived in pages/shifts.php until four more pages needed them. */
.io-bar { margin-left:auto; display:flex; gap:8px; align-items:center; }
.io-menu { position:relative; }
.io-drop { position:absolute; right:0; top:calc(100% + 6px); z-index:20; min-width:180px; padding:6px;
  background:var(--surface); border:1px solid var(--border); border-radius:10px; box-shadow:var(--shadow-lg); display:flex; flex-direction:column; gap:2px; }
.io-drop button { text-align:left; padding:8px 10px; font-size:13.5px; border:0; background:none;
  width:100%; border-radius:7px; cursor:pointer; color:var(--text); }
.io-drop button:hover { background:var(--surface-2); color:var(--text); }
.io-panel { margin-bottom:18px; }
.io-hint { font-size:13px; color:var(--muted); margin:0 0 12px; line-height:1.6; }
.io-hint code { background:var(--surface-2); padding:1px 5px; border-radius:4px; font-size:12.5px; }
.io-row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.io-row .input { flex:1; min-width:220px; }
.io-result { margin-top:14px; }
.io-sum { display:flex; gap:14px; flex-wrap:wrap; margin-bottom:10px; font-size:13.5px; font-weight:600; }
.io-ok { color:var(--success); } .io-bad { color:var(--danger); }
.io-tbl { width:100%; border-collapse:collapse; font-size:13px; }
.io-tbl th, .io-tbl td { text-align:left; padding:6px 8px; border-bottom:1px solid var(--border); }
.io-tbl th { color:var(--muted); font-weight:600; }
.io-errs { margin-top:10px; padding:10px 12px; border-radius:8px; background:var(--danger-bg); color:var(--danger); font-size:13px; }
.io-errs div { margin:3px 0; }
.io-scroll { max-height:320px; overflow:auto; }

/* Ambient shift-clock chip in the topbar (Sprint 5 — SOD/EOD timer). Hidden until a
   session is active, so the Manager with no shift never sees it. */
.shift-timer { display:inline-flex; align-items:center; gap:8px; margin-right:14px; padding:5px 10px;
  background:var(--success-bg); border:1px solid var(--success); border-radius:999px; font-size:12.5px; font-weight:600; color:var(--success); }
.shift-timer.paused { background:var(--warning-bg); border-color:var(--warning); color:var(--warning); }
.shift-timer .st-dot { width:8px; height:8px; border-radius:50%; background:var(--success); }
.shift-timer.paused .st-dot { background:var(--warning); }
.shift-timer .st-live { font-variant-numeric:tabular-nums; letter-spacing:.01em; }
.shift-timer .st-brk { border:0; background:rgba(0,0,0,.06); color:inherit; font:inherit; font-size:11.5px; font-weight:700;
  padding:2px 9px; border-radius:999px; cursor:pointer; }
.shift-timer .st-brk:hover { background:rgba(0,0,0,.12); }

/* Non-collapsible nav group (e.g. Operations) — always-open, no chevron/toggle.
   The whole block gets a tinted "card" background so its children read as grouped;
   the heading becomes a compact small-caps section label (no icon chip). */
.nav-group-flat { background:var(--bg); border:1px solid var(--border); border-radius:12px;
  padding:4px 5px 6px; margin:6px 0; }                        /* --bg reads clearly vs the white sidebar */
.nav-group-flat > .ng-body { display:block; }
.nav-group-flat .gh-flat { cursor:default; padding:7px 9px 4px; font-size:10.5px; font-weight:750;
  text-transform:uppercase; letter-spacing:.08em; color:var(--muted); }
.nav-group-flat .gh-flat:hover { background:none; }
.nav-group-flat .gh-flat .ni { display:none; }               /* heading is text, not an icon chip */
.nav-group-flat .ng-body .nav-item { border-radius:9px; }
.nav-group-flat .ng-body .nav-item:hover { background:var(--surface); }   /* children pop to white on hover */
.nav-group-flat .ng-body .nav-item.active { background:var(--primary-tint); }

/* Compact sidebar — tighter rows so the fuller nav fits without scrolling. */
.nav { gap:1px; }
.nav-item, .nav-group > .gh { padding:5px 10px 5px 6px; font-size:14px; }
.ni { width:30px; height:30px; }
.ng-body .nav-item { padding:4px 10px 4px 8px; font-size:13px; }
.ng-body .nav-item .ni { width:27px; height:27px; }

/* Shift chip during the post-shift grace window: the clock is frozen and the
   only thing left to do is file the EOD, so say how long that window has left. */
.st-grace{margin-left:8px;font-size:11px;font-weight:700;letter-spacing:.02em;
  background:var(--warning-bg);color:var(--warning);padding:2px 8px;border-radius:999px;
  font-variant-numeric:tabular-nums}
#shift-timer.grace{border-color:var(--warning)}

/* Stop clocks you out without an EOD — deliberately quieter than Break, which is
   the routine action, and tinted danger because it ends the session. */
.st-stop{margin-left:4px}
.st-stop:hover{border-color:var(--danger);color:var(--danger)}

/* "Sign in with Webronic HR" on login.php (SSO pilot). */
.sso-divider { display: flex; align-items: center; gap: 10px; margin: 16px 0; color: var(--text-3, var(--text-2)); font-size: 12px; }
.sso-divider::before, .sso-divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.auth-note { margin-top: 12px; font-size: 13px; color: var(--text-2); }
