:root {
  --text: #6b6375;
  --text-h: #08060d;
  --bg: #fff;
  --border: #e5e4e7;
  --code-bg: #f4f3ec;
  --accent: #aa3bff;
  --accent-bg: rgba(170, 59, 255, 0.1);
  --accent-border: rgba(170, 59, 255, 0.5);
  --social-bg: rgba(244, 243, 236, 0.5);
  --heading: Arial;
  --mono: Arial;
  --shadow:
    rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;

  letter-spacing: 0.18px;
  color-scheme: light dark;
  color: var(--text);
  background: var(--bg);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  @media (max-width: 1024px) {
    font-size: 16px;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: #9ca3af;
    --text-h: #f3f4f6;
    --bg: #16171d;
    --border: #2e303a;
    --code-bg: #1f2028;
    --accent: #c084fc;
    --accent-bg: rgba(192, 132, 252, 0.15);
    --accent-border: rgba(192, 132, 252, 0.5);
    --social-bg: rgba(47, 48, 58, 0.5);
    --shadow:
      rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
  }

  #social .button-icon {
    filter: invert(1) brightness(2);
  }
}

body {
  margin: 0;
}

#root {
  width: auto;
  max-width: 100%;
  margin: 0 auto;
  border-inline: 1px solid var(--border);
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

h1,
h2 {
  font-family: var(--heading);
  font-weight: 500;
  color: black;
}

h1 {
  font-size: 56px;
  letter-spacing: -1.68px;
  margin: 32px 0;
  @media (max-width: 1024px) {
    font-size: 36px;
    margin: 20px 0;
  }
}
h2 {
  font-size: 24px;
  line-height: 118%;
  letter-spacing: -0.24px;
  margin: 0 0 8px;
  @media (max-width: 1024px) {
    font-size: 20px;
  }
}
p {
  margin: 0;
}

code,
.counter {
  font-family: var(--mono);
  display: inline-flex;
  border-radius: 4px;
  color: var(--text-h);
}

code {
  font-size: 15px;
  line-height: 135%;
  padding: 4px 8px;
  background: var(--code-bg);
}.counter {
  font-size: 16px;
  padding: 5px 10px;
  border-radius: 5px;
  color: var(--accent);
  background: var(--accent-bg);
  border: 2px solid transparent;
  transition: border-color 0.3s;
  margin-bottom: 24px;

  &:hover {
    border-color: var(--accent-border);
  }
  &:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }
}

.hero {
  position: relative;

  .base,
  .framework,
  .vite {
    inset-inline: 0;
    margin: 0 auto;
  }

  .base {
    width: 170px;
    position: relative;
    z-index: 0;
  }

  .framework,
  .vite {
    position: absolute;
  }

  .framework {
    z-index: 1;
    top: 34px;
    height: 28px;
    transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
      scale(1.4);
  }

  .vite {
    z-index: 0;
    top: 107px;
    height: 26px;
    width: auto;
    transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
      scale(0.8);
  }
}

#center {
  display: flex;
  flex-direction: column;
  gap: 125px;
  place-content: center;
  place-items: center;
  flex-grow: 1;

  @media (max-width: 1024px) {
    padding: 32px 20px 24px;
    gap: 18px;
  }
}

#next-steps {
  display: flex;
  border-top: 1px solid var(--border);
  text-align: left;

  & > div {
    flex: 1 1 0;
    padding: 32px;
    @media (max-width: 1024px) {
      padding: 24px 20px;
    }
  }

  .icon {
    margin-bottom: 16px;
    width: 22px;
    height: 22px;
  }

  @media (max-width: 1024px) {
    flex-direction: column;
    text-align: center;
  }
}

#docs {
  border-right: 1px solid var(--border);

  @media (max-width: 1024px) {
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
}

#next-steps ul {
  list-style: none;
  padding: 0;
  display: flex;
  gap: 8px;
  margin: 32px 0 0;

  .logo {
    height: 18px;
  }

  a {
    color: var(--text-h);
    font-size: 16px;
    border-radius: 6px;
    background: var(--social-bg);
    display: flex;
    padding: 6px 12px;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    transition: box-shadow 0.3s;

    &:hover {
      box-shadow: var(--shadow);
    }
    .button-icon {
      height: 18px;
      width: 18px;
    }
  }

  @media (max-width: 1024px) {
    margin-top: 20px;
    flex-wrap: wrap;
    justify-content: center;

    li {
      flex: 1 1 calc(50% - 8px);
    }

    a {
      width: 100%;
      justify-content: center;
      box-sizing: border-box;
    }
  }
}

#spacer {
  height: 88px;
  border-top: 1px solid var(--border);
  @media (max-width: 1024px) {
    height: 48px;
  }
}

.ticks {
  position: relative;
  width: 100%;

  &::before,
  &::after {
    content: '';
    position: absolute;
    top: -4.5px;
    border: 5px solid transparent;
  }

  &::before {
    left: 0;
    border-left-color: var(--border);
  }
  &::after {
    right: 0;
    border-right-color: var(--border);
  }
}/* ============================================
   STRIDE+ Dashboard — Modern Styling
   ============================================ */

* {
  box-sizing: border-box;
}

.dashboard-page {
  min-height: 100vh;
  background: #eef2ff;
  background: radial-gradient(circle at top right, #e0e7ff 0%, #eef2ff 45%, #f8fafc 100%);
  color: #1f2937;
  overflow-x: hidden;
  transition: background-color 0.5s ease, background 0.5s ease, color 0.5s ease;
  /* Same stack as .auth-page in authentication.css — keep these two in
     sync any time either one changes. */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  /* Sidebar width lives in one place. Collapsed state (below) overrides it,
     and everything that depends on the sidebar's width — .dashboard-sidebar
     itself, .dashboard-container's margin-left — reads from this var so
     they always stay in sync and animate together. */
  /* Was 200px: after padding that left a 164px nav column, narrower than the
     "Predictive Analytics" button, so it was already clipped and the hover
     shift pushed it further out. 220px gives every label room plus the
     hover shift; the rem term makes it grow with the user's font size
     setting so a bigger font can't clip it again.
     NOTE: 13rem == 208px at the default 16px root size, so the max()
     that used to sit here was a no-op — it never actually granted the
     extra room described above. Using 13rem (208px at a 16px root) both
     gives the intended 208px baseline AND scales proportionally with the
     root font size (e.g. a 22px root → ~330px), so a larger font can't
     clip the labels again. */
  --sidebar-width: max(220px, 13rem);
}

/* Retractable sidebar (YouTube-style: full nav ↔ icon-only rail). Toggled
   via the button in the sidebar's brand row (Dashboard1.jsx sidebarCollapsed
   state), persisted to localStorage so it survives reloads. */
.dashboard-page.sidebar-collapsed {
  --sidebar-width: 68px;
}

/* ============================================
   Dark Mode
   ============================================ */

.dashboard-page.dark {
  background: #0b0e1a;
  color: #e2e8f0;
}

/* The sidebar lives outside .dashboard-main, so it keeps its own
   hand-authored dark palette rather than being auto-inverted below. */
.dashboard-page.dark .dashboard-sidebar {
  background: #812227;
  box-shadow:
    0 20px 40px rgba(0, 0, 0, 0.55),
    0 4px 12px rgba(0, 0, 0, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

.dashboard-page.dark .dashboard-footer { color: #cbd5e1; }

/* ── Smart-invert the entire main content area ──────────────────────────
   Dozens of section components (charts, tables, modals) hardcode their own
   colors inline — #fff backgrounds, #1e293b/#64748b text, colored badges,
   emoji, etc. Chasing every literal hex one-by-one is exactly how the last
   pass missed things (white cards, invisible bell icon, washed-out emoji).
   Instead: invert(1) flips every light value dark and every dark value
   light (white⇄black) in one shot, and hue-rotate(180deg) swings hue back
   close to its original so blues stay blue-ish, reds stay red-ish, etc.
   (same trick iOS uses for "Smart Invert".) This guarantees the "any
   black → white, any white → black" rule across every component without
   having to special-case each file. */
/* The dark invert is applied to .dashboard-main's children, NOT to
   .dashboard-main itself, and skips .dashboard-header. The header used to sit
   under this filter and cancel it with a second copy; stacked
   invert+hue-rotate filters clamp between passes, so #812227 rendered as
   #752A2F. Un-filtered, the header shows #812227 exactly. The header lives in
   a sticky wrapper, so two selectors are needed (wrapper's siblings, and the
   wrapper's non-header children). Side benefit: .dashboard-main is no longer a
   filter containing block for position:fixed descendants.
   .dark-section = a container that must NOT be filtered as a whole because it
   holds a brand-colored button (see "Self-inverting sections" below). */
.dashboard-page.dark .dashboard-main > :not(:has(> .dashboard-header)):not(.dark-section),
.dashboard-page.dark .dashboard-main > :has(> .dashboard-header) > :not(.dashboard-header):not(.dark-section) {
  filter: invert(1) hue-rotate(180deg);
}

/* ── Self-inverting sections ─────────────────────────────────────────────
   A brand-colored element under ANY invert filter can't render #812227
   exactly (the cancel pass clamps). So a section that contains one
   (.dark-section) is left unfiltered itself; each direct child is filtered
   individually EXCEPT the brand button (.dark-keep) and nested
   .dark-section containers. .dark-card / .dark-rule hand-author the values
   the container's own inverted look used to give (#fff -> #000, etc). */
.dashboard-page.dark .dark-section > :not(.dark-keep):not(.dark-section) {
  filter: invert(1) hue-rotate(180deg);
}
.dashboard-page.dark .dark-card {
  background: #000 !important;
  border-color: #0e131b !important;
  box-shadow: none !important;
}
.dashboard-page.dark .dark-rule { border-bottom-color: #070b0f !important; }

/* Brand button in dark mode: exact #812227, no filter, no glow. */
.dashboard-page.dark .dark-brand-btn,
.dashboard-page.dark .dashboard-button.dark-keep {
  filter: none;
  background: #812227 !important;
  color: #ffffff !important;
}
.dashboard-page.dark .dark-brand-btn { box-shadow: none !important; }
.dashboard-page.dark .dark-brand-btn:hover,
.dashboard-page.dark .dashboard-button.dark-keep:hover { filter: brightness(0.92); }
.dashboard-page.dark .dark-brand-btn:disabled { opacity: 0.65; }

/* AI chat widget (ChatAssistant.jsx) — FAB, header, user bubble, send. */
.dashboard-page.dark .ca-fab,
.dashboard-page.dark .ca-header,
.dashboard-page.dark .ca-bubble-user { background: #812227 !important; }
.dashboard-page.dark .ca-fab { box-shadow: none !important; }
.dashboard-page.dark .ca-send:not(:disabled) {
  background: #812227 !important;
  color: #ffffff !important;
}

/* The theme toggle button is rendered *inside* .dashboard-main, so without
   this it'd get auto-inverted too. Applying the SAME filter to it again
   cancels the parent's invert out (two invert(1) hue-rotate(180deg) filters
   compose to identity), so it falls back to its own hand-authored colors
   below instead of being flipped.
   NOTE: SettingsPanel (.sp-backdrop / .sp-panel) used to need this same
   cancel-filter when it was rendered inside .dashboard-main. It has since
   been moved to render outside .dashboard-main (sibling of
   .dashboard-container) to fix a position:fixed containing-block bug, so it
   no longer sits under the parent invert and must NOT get this filter —
   SettingsPanel already ships its own complete hand-authored dark-mode CSS
   (see the .dashboard-page.dark .sp-panel rules further down in this file /
   inline in SettingsPanel.jsx). Re-adding sp-backdrop/sp-panel here will
/* NOTE: .theme-toggle-btn used to need its own invert-cancel filter here
   too, back when .dashboard-header had no cancel of its own and was still
   getting auto-inverted by .dashboard-main. Now that .dashboard-header
   cancels the invert for its whole subtree (see below), adding this same
   filter to .theme-toggle-btn would triple-stack the inversion (main
   invert → header cancel → button re-invert = net inverted), which is
   exactly why the moon icon was stuck showing its light-mode look. Do NOT
   re-add a filter here — .theme-toggle-btn's dark colors are hand-authored
   directly at .dashboard-page.dark .theme-toggle-btn further down and need
   no filter now that its parent already un-inverts it.

/* .dashboard-header lives inside .dashboard-main too, so without this it
   gets the same blanket invert(1) hue-rotate(180deg) as every other section
   — which turns its intentional maroon gradient into a murky teal instead
   of a proper dark look, and doesn't match the sidebar (which already
   bypasses the invert with its own hand-authored dark palette above).
   Cancel the parent filter here, then hand-author the dark version below
   so header + sidebar still read as one surface in dark mode. */
/* (No filter on .dashboard-header — it is excluded from the dark invert
   above instead of cancelling it.) */

/* "More Details" modals (EnrollmentRetentionSection, PloBoardSection,
   SatisfactionFacultySection, CompetitionsAffiliationsSection,
   TracerEmploymentSection) all portal to document.body, completely outside
   .dashboard-page/.dashboard-main — so they need their own body-level dark
   class + the same invert trick. See the "dark" class also being toggled
   on <body> in Dashboard1 / DetailTable / ProgramAnalysis. */
/* Dark mode for these body-level cards uses a BACKDROP overlay instead of a
   filter on the card. A filter would put the header and table head under an
   invert (and cancelling it clamps #812227 to a murky shade). Here the card is
   left unfiltered; a ::after overlay inverts everything painted beneath it,
   and the header + table head sit ABOVE the overlay (z-index 2) with a
   hand-authored, exact #812227. Positioning uses :where() (zero specificity)
   so it never overrides a component's own position rules. */
:where(body.dark .stride-modal-card, body.dark .stride-widget-card) {
  position: relative;
  isolation: isolate;
}
body.dark .stride-modal-card::after,
body.dark .stride-widget-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: 1;
  pointer-events: none;
  -webkit-backdrop-filter: invert(1) hue-rotate(180deg);
  backdrop-filter: invert(1) hue-rotate(180deg);
}
:where(body.dark .stride-modal-header, body.dark .stride-widget-header,
       body.dark .stride-modal-card thead tr, body.dark .stride-modal-card thead th,
       body.dark .stride-widget-card thead tr, body.dark .stride-widget-card thead th) {
  position: relative;
}
body.dark .stride-modal-header,
body.dark .stride-widget-header,
body.dark :is(.stride-modal-card, .stride-widget-card) thead tr,
body.dark :is(.stride-modal-card, .stride-widget-card) thead th {
  z-index: 2;
  background: #812227 !important;
}
body.dark :is(.stride-modal-card, .stride-widget-card) thead th,
body.dark :is(.stride-modal-card, .stride-widget-card) thead th * {
  color: #ffffff !important;
}

/* The colored header bar inside these modals is a direct child of
   .stride-modal-card, so it inherits that same invert — which would turn
   the brand red header into cyan. Cancel it here (same double-invert
   trick as .dashboard-header/.theme-toggle-btn above) so the header stays
   red and only the white card body + table actually go dark. */
/* (.stride-modal-header: no filter — handled by the overlay block above.) */

/* ============================================
   Draggable/resizable chart widgets (DraggableWidget.jsx) — the floating
   popups opened by clicking a chart (Retention Rate, Enrollment by
   Program, etc). Their content is built from many small chart/table
   components with hardcoded text colors (#1e293b etc.), so a plain
   hand-authored dark background would leave that text dark-on-dark and
   unreadable. Using the same invert-filter trick as .stride-modal-card
   flips all of that content's colors together correctly, then the header
   cancel below keeps the header on-brand red instead of inverting to cyan. */
.stride-widget-card {
   background: #fff;
   border: 1px solid #e2e8f0;
   box-shadow: 0 20px 50px rgba(0, 0, 0, 0.28);
 }
 body.dark .stride-widget-card {
  border-color: #121820;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
 }
.stride-widget-header {
  background: #9e2a2f;
}
/* (.stride-widget-header: no filter — handled by the overlay block above.) */

/* ============================================
   Theme toggle button
   ============================================ */

.theme-toggle-btn {
  position: relative;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: #ffffff;
  overflow: hidden;
  transition: background 0.5s ease, color 0.5s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.theme-toggle-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 16px rgba(139, 0, 0, 0.18);
}

.theme-toggle-btn:active {
  transform: translateY(0) scale(0.92);
}

.dashboard-page.dark .theme-toggle-btn {
  background: #812227;
  color: #ffffff;
}

.dashboard-page.dark .theme-toggle-btn:hover {
  box-shadow: 0 8px 16px rgba(158, 42, 47, 0.35);
}

.dashboard-button.notif-bell-btn {
  background: transparent;
  color: #ffffff;
}
/* Notification bell — same situation as .theme-toggle-btn: it sits inside
   .dashboard-header, which now cancels .dashboard-main's invert (so its
   maroon gradient shows correctly), so the bell no longer gets
   auto-flipped dark either. Hand-authored to match the toggle button.
   Selector kept as .dashboard-button.notif-bell-btn (not just
   .notif-bell-btn) so this reliably wins over .dashboard-button's own
   background — the bell renders with both classes at once
   (see NotificationCenter.jsx), and two single-class selectors of equal
   specificity would otherwise be decided by source order alone, which is
   exactly what broke this the first time .dashboard-button gained a
   background. */
.dashboard-page.dark .dashboard-button.notif-bell-btn {
  background: #812227;
  color: #ffffff;
}
/* The dropdown panel isn't inside a colored header of its own, so the
   simple invert trick (rather than a hand-authored palette) is enough —
   it flips the plain white background + dark text + alert-row colors
   together correctly, same reasoning as .stride-modal-card. */
.dashboard-page.dark .notif-dropdown {
  filter: invert(1) hue-rotate(180deg);
}

.theme-toggle-btn .theme-icon {
  position: absolute;
  transition: transform 0.5s cubic-bezier(0.65, 0, 0.35, 1), opacity 0.35s ease;
}

.theme-toggle-btn .theme-icon--sun {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

.theme-toggle-btn .theme-icon--moon {
  opacity: 0;
  transform: rotate(-90deg) scale(0.4);
}

.dashboard-page.dark .theme-toggle-btn .theme-icon--sun {
  opacity: 0;
  transform: rotate(90deg) scale(0.4);
}

.dashboard-page.dark .theme-toggle-btn .theme-icon--moon {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

.dashboard-container {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  max-width: 100%;
  margin: 0 auto;
  margin-left: calc(var(--sidebar-width) + 20px); /* reserves space for the fixed sidebar */
  padding-left: 0;
  padding-right: 25px;
  padding-top: 20px;
  padding-bottom: 20px;
  /* No margin-left transition (was `margin-left 0.25s ease`). Animating it
     re-ran layout for the ENTIRE dashboard on every frame of the sidebar
     collapse, and every Recharts ResponsiveContainer (20+ charts) re-measured
     and re-rendered each frame — that was the lag. The margin now changes
     once; only the fixed sidebar's own width animates (cheap). */
}

/* ============================================
   Sidebar — fixed, gradient, animated nav
   ============================================ */

.dashboard-sidebar {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /*background: linear-gradient(to bottom, #9e2a2f, #FF6347, #DB7093);*/
  /*background: linear-gradient(to bottom, #9e2a2f, #FF6347, #9e2a2f);*/
  background: #9E2A2F;
  /*border-radius: 0 24px 24px 0;*/
  border-radius: 0px;
  padding: 22px 18px;
  width: var(--sidebar-width);
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  overflow: hidden; /* the scrollable region is .dashboard-sidebar-scroll below —
                        the footer stays outside it so it's always visible */
  z-index: 100;
  box-shadow:
    0 20px 40px rgba(74, 20, 20, 0.35),
    0 4px 12px rgba(74, 20, 20, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  /* No width/padding transition: like YouTube's sidebar, collapse/expand is an
     instant switch between the full nav and the icon rail. Animating width
     forced the whole page to re-layout every frame (charts re-rendering),
     which was the lag. */
}

.dashboard-sidebar.collapsed {
  padding: 22px 10px;
}

/* Scrollable nav area — takes all remaining height above the pinned footer.
   Previously the *entire* sidebar (including the footer) scrolled together,
   which meant a long nav list could push Settings/Logout off-screen with no
   visible way to reach them without scrolling. Now only this region scrolls. */
.dashboard-sidebar-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* Extend the clip box 8px into the sidebar's own right padding so the
     hover shift + shadow on nav buttons isn't cut off at the edge. */
  padding-right: 8px;
  margin-right: -8px;
  scrollbar-width: thin;
  scrollbar-color: #9E2A2F transparent;
}

.dashboard-sidebar-scroll::-webkit-scrollbar {
  width: 5px;
}

.dashboard-sidebar-scroll::-webkit-scrollbar-thumb {
  background: #9E2A2F;
  border-radius: 10px;
}

.dashboard-brand-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.dashboard-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #fdfdfd;
  /* Matches .auth-topbar-wordmark in authentication.css exactly (same
     clamp() size, same regular weight, no letter-spacing or text-shadow)
     so the "STRIDE+" wordmark reads identically whether it's seen on the
     login page's topbar or here in the sidebar. Font stack was already
     shared via .dashboard-page/.auth-page's own font-family — this just
     brings the weight/spacing to match too. */
  font-size: clamp(1.15rem, 1.8vw, 1.6rem);
  font-weight: 400;
  white-space: nowrap;
  overflow: hidden;
}

.dashboard-brand-logo {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  object-fit: contain;
  background: #ffffff;
  border: 2px solid #ffffff;
  border-radius: 0;
  box-sizing: border-box;
  padding: 2px;
}

.sidebar-toggle-btn {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.2s ease, transform 0.15s ease;
}

.sidebar-toggle-btn:hover {
  background: rgba(255, 255, 255, 0.22);
}

.sidebar-toggle-btn:active {
  transform: scale(0.92);
}

.dashboard-sidebar.collapsed .dashboard-brand-row {
  flex-direction: column;
  gap: 10px;
}

.dashboard-sidebar.collapsed .dashboard-brand {
  font-size: 1.05rem;
  text-align: center;
  justify-content: center;
}

.dashboard-sidebar.collapsed .dashboard-brand-logo {
  width: 26px;
  height: 26px;
}

.dashboard-nav {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 22px;
}

/* Collapsed rail: center icons, hide text labels. Labels fade out instead of
   just disappearing so the collapse doesn't feel like a jump-cut. */
.dashboard-sidebar.collapsed .dashboard-nav-item,
.dashboard-sidebar.collapsed .dashboard-nav-button {
  justify-content: center;
  width: 100%;
  padding: 11px;
}

.dashboard-sidebar.collapsed .nav-label {
  display: none;
}

.nav-label {
  transition: opacity 0.15s ease;
}

.dashboard-nav-item,
.dashboard-nav-button {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  text-align: left;
  width: fit-content;
  max-width: 100%;
  white-space: nowrap;
  padding: 10px 14px;
  border-radius: 14px;
  text-decoration: none;
  color: #f3d8d8;
  font-weight: 600;
  font-size: 0.9rem;
  line-height: 1.3;
  border: none;
  background: transparent;
  cursor: pointer;
  position: relative;
  transition: background 0.25s ease, color 0.25s ease, transform 0.2s ease, box-shadow 0.25s ease;
}

/* Icons stay pinned at the start of the button and never shrink */
.dashboard-nav-item svg,
.dashboard-nav-button svg {
  flex-shrink: 0;
}

.dashboard-nav-item:hover,
.dashboard-nav-button:hover {
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  transform: translateX(4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.dashboard-nav-item:active,
.dashboard-nav-button:active {
  transform: translateX(4px) scale(0.97);
}

.dashboard-nav-item.active {
  background: rgba(255, 255, 255, 0.18);
  color: #ffffff;
  box-shadow:
    0 4px 14px rgba(0, 0, 0, 0.25),
    inset 0 1px 0 rgba(255, 255, 255, 0.15);
}

.dashboard-nav-button {
  text-align: left;
}

.dashboard-footer {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: #dbeafe;
  flex-shrink: 0; /* never let this get squeezed by the scrollable nav above it */
}

.dashboard-footer-label {
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin-bottom: 10px;
  opacity: 0.85;
}

.dashboard-footer-text {
  margin: 0;
  line-height: 1.6;
}

/* ============================================
   Main content
   ============================================ */

.dashboard-main {
  display: flex;
  flex-direction: column;
  gap: 24px;
  min-width: 0;
  /* .auth-footer (authentication.css) is position:fixed, bottom:0 and
     floats over whatever is scrolled to the bottom of the page. Without
     reserved space here, the last piece of content — e.g. Legacy's AI
     Report bar — ends up hidden behind it. 64px comfortably clears the
     footer's own height (~44px) plus breathing room. */
  padding-bottom: 64px;
  /* NOTE: do not put a transform/filter/animation directly on .dashboard-main —
     any ancestor with those properties creates a new containing block, which
     breaks position:fixed children (e.g. <ChatAssistant /> rendered inside
     <main className="dashboard-main">). Entrance animation is applied to
     .dashboard-main-inner instead, which wraps just the section content. */
}

.dashboard-main-inner {
  display: flex;
  flex-direction: column;
  gap: 24px;
  animation: fadeInUp 0.5s ease both;
}

.dashboard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #9E2A2F;
  border-radius: 20px;
  padding: 16px 22px;
  box-shadow: none;
  transition: box-shadow 0.25s ease;
}

.dashboard-header:hover {
  box-shadow: none;
}


/* Dark mode: match the sidebar's dark maroon gradient (see
   .dashboard-page.dark .dashboard-sidebar) instead of relying on the
   parent invert filter, which is cancelled above for this element. */
.dashboard-page.dark .dashboard-header {
  background: #812227;
}

.dashboard-page.dark .dashboard-header:hover {
  box-shadow: none;
} /* Can be change box shadows*/

.dashboard-subtitle {
  margin: 0 0 8px;
  color: #f3d8d8;
  font-size: 0.95rem;
}

.dashboard-title {
  margin: 0;
  font-size: 2.25rem;
  letter-spacing: -0.01em;
  color: #ffffff;
}

/* Name/email block sits directly on the header gradient now — bump these
   to light colors so they stay legible (previously assumed a white card). */
.dashboard-header .dashboard-actions > div:last-child > div:first-child {
  color: #ffffff;
}
.dashboard-header .dashboard-actions > div:last-child > div:last-child {
  color: #f3d8d8 !important;
}
.dashboard-header .dept-badge {
  background: rgba(255, 255, 255, 0.22);
  color: #ffffff;
}

.dashboard-actions {
  display: flex;
  align-items: center;
  gap: 16px;
}

.dashboard-page.dark .dashboard-button,
.dashboard-page.dark .dashboard-pill {
  /* These live directly in .dashboard-main's content area, which applies
     a blanket invert(1) hue-rotate(180deg) filter in dark mode. Without a
     cancel + hand-authored color here, the brand maroon #9E2A2F gets
     flipped into a light pink/salmon. Re-apply the same filter to cancel
     it out (two invert+hue-rotate passes compose to identity — same
     trick as .dashboard-header/.theme-toggle-btn above), then set the
     real color underneath. */
  filter: invert(1) hue-rotate(180deg);
  background: #812227;
  color: #ffffff;
}

.dashboard-page.dark .dashboard-button:hover,
.dashboard-page.dark .dashboard-pill:hover {
  filter: invert(1) hue-rotate(180deg) brightness(0.92);
}

/* Anything using .dashboard-button/.dashboard-pill INSIDE .dashboard-header
   (notif bell, and header action buttons like Manage Data's "Download
   Template" / "Import New Data") sits in a context that .dashboard-header
   has already un-inverted for its whole subtree. Stacking the cancel
   filter above on TOP of that un-inversion re-inverts it a second time —
   that's what was turning these into pink boxes with black text/icons.
   These need no filter at all; hand-author the real colors directly. */
.dashboard-page.dark .dashboard-header .dashboard-button,
.dashboard-page.dark .dashboard-header .dashboard-pill {
  filter: none;
  background: #812227;
  color: #ffffff;
}

.dashboard-page.dark .dashboard-header .dashboard-button:hover,
.dashboard-page.dark .dashboard-header .dashboard-pill:hover {
  filter: brightness(0.92);
}

.dashboard-button,
.dashboard-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  border-radius: 16px;
  padding: 12px 18px;
  cursor: pointer;
  font-weight: 700;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
}

.dashboard-button {
  /* This class never declared a background — only .dashboard-pill (below)
     did — so any button using .dashboard-button alone (Legacy's "Download
     Template"/"Import New Data", Manage Data's header actions) fell back
     to the browser's default white button chrome instead of the app's
     look. Reusing .dashboard-pill's gradient here so both classes render
     consistently. */
  background: #9E2A2F;
  color: #eef2ff;
  box-shadow: 0 6px 14px rgba(158, 42, 47, 0.3);
}

/* Hover = the sidebar's .dashboard-nav-button hover (4px shift + soft
   shadow), turned vertical: lifts 4px instead of sliding right 4px. */
.dashboard-button:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  filter: brightness(0.98);
}

.dashboard-pill {
  background: #9E2A2F;
  color: #ffffff;
  box-shadow: 0 6px 14px rgba(158, 42, 47, 0.3);
}

.dashboard-pill:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 20px rgba(158, 42, 47, 0.4);
}

.dashboard-pill:active {
  transform: translateY(0) scale(0.97);
}
/* Same as the sidebar: press keeps the hover offset and adds scale(0.97). */
.dashboard-button:active {
  transform: translateY(-4px) scale(0.97);
}

/* ════════════════════════════════════════════════════════════════════
   Universal button hover — the sidebar's .dashboard-nav-button hover,
   turned vertical, on every button in the dashboard.

   What the sidebar hover does (dashboard.css, .dashboard-nav-button):
     • transition  background .25s, color .25s, transform .2s, shadow .25s
     • hover       translateX(4px)  +  shadow 0 4px 12px rgba(0,0,0,.2)
                   +  a translucent white wash rgba(255,255,255,.12) over
                      the button (the "shade change")
     • press       keeps the 4px shift and adds scale(0.97)
   Here: the shift is translateY(-4px) (lifts instead of sliding right); the
   wash is drawn as a background-image layer so it lightens ANY fill color
   (maroon, green, transparent…) instead of replacing it. !important on the
   wash because many buttons set `background` inline.

   Skipped on purpose: sidebar links (already animate horizontally),
   list-style items where a lift would collide with neighbours (account menu
   items, privacy TOC links, Settings accordion rows),
   table-header buttons, toggles/swatches, and disabled buttons.
   ════════════════════════════════════════════════════════════════════ */
:is(.dashboard-page button, .stride-modal-card button, .stride-widget-card button,
    .ai-disclaimer-card button, .dashboard-pill):not(:disabled):not(.dashboard-nav-button):not(.account-menu-item):not(.privacy-toc-link):not(.sp-acc-header):not(.sp-toggle):not(.sp-swatch):not(.sp-swatch-none):not(th button) {
  transition: background 0.25s ease, color 0.25s ease, transform 0.2s ease,
              box-shadow 0.25s ease, filter 0.2s ease, border-color 0.25s ease,
              opacity 0.2s ease;
}
:is(.dashboard-page button, .stride-modal-card button, .stride-widget-card button,
    .ai-disclaimer-card button, .dashboard-pill):not(:disabled):not(.dashboard-nav-button):not(.account-menu-item):not(.privacy-toc-link):not(.sp-acc-header):not(.sp-toggle):not(.sp-swatch):not(.sp-swatch-none):not(th button):hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  background-image: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)) !important;
}
:is(.dashboard-page button, .stride-modal-card button, .stride-widget-card button,
    .ai-disclaimer-card button, .dashboard-pill):not(:disabled):not(.dashboard-nav-button):not(.account-menu-item):not(.privacy-toc-link):not(.sp-acc-header):not(.sp-toggle):not(.sp-swatch):not(.sp-swatch-none):not(th button):active {
  transform: translateY(-4px) scale(0.97);
}

@media (prefers-reduced-motion: reduce) {
  :is(.dashboard-page button, .stride-modal-card button, .stride-widget-card button,
      .ai-disclaimer-card button, .dashboard-pill):hover { transform: none; }
}

/* ============================================
   Stat cards
   ============================================ */

.dashboard-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

.dashboard-card {
  background: #ffffff;
  border-radius: 24px;
  padding: 28px;
  box-shadow:
    0 1px 2px rgba(15, 23, 42, 0.04),
    0 16px 40px rgba(15, 23, 42, 0.08);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
  animation: fadeInUp 0.5s ease both;
}

.dashboard-card:hover {
  transform: translateY(-4px);
  box-shadow:
    0 2px 4px rgba(15, 23, 42, 0.06),
    0 22px 48px rgba(15, 23, 42, 0.12);
}

.dashboard-card-highlight {
  border: 1px solid rgba(59, 130, 246, 0.18);
  background: linear-gradient(160deg, #ffffff 0%, #f5f7ff 100%);
}

.dashboard-card-label {
  margin: 0 0 14px;
  color: #64748b;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

.dashboard-card-value {
  margin: 0;
  font-size: 2rem;
  font-weight: 800;
  background: linear-gradient(135deg, #1e293b 0%, #9e2a2f 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ============================================
   Body: summary + tasks
   ============================================ */

.dashboard-body {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 20px;
  min-height: 50px;
}

.dashboard-summary,
.dashboard-tasks {
  background: #ffffff;
  border-radius: 24px;
  padding: 28px;
  box-shadow:
    0 1px 2px rgba(15, 23, 42, 0.04),
    0 16px 40px rgba(15, 23, 42, 0.08);
  transition: box-shadow 0.25s ease;
}

.dashboard-summary:hover,
.dashboard-tasks:hover {
  box-shadow:
    0 2px 4px rgba(15, 23, 42, 0.05),
    0 20px 44px rgba(15, 23, 42, 0.1);
}

.dashboard-section-header h2 {
  margin: 0;
  font-size: 1.4rem;
}

.dashboard-chart {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px;
  margin-top: 26px;
  min-height: 180px;
}

.dashboard-chart-bar {
  flex: 1;
  border-radius: 14px 14px 4px 4px;
  background: linear-gradient(180deg, #c7d2fe 0%, #6366f1 100%);
  box-shadow: 0 8px 16px rgba(99, 102, 241, 0.25);
  transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
  animation: growUp 0.6s ease both;
  transform-origin: bottom;
}

.dashboard-chart-bar:hover {
  filter: brightness(1.08);
  box-shadow: 0 10px 22px rgba(99, 102, 241, 0.35);
}

.bar-1 { height: 52%; animation-delay: 0.05s; }
.bar-2 { height: 82%; animation-delay: 0.1s; }
.bar-3 { height: 65%; animation-delay: 0.15s; }
.bar-4 { height: 90%; animation-delay: 0.2s; }
.bar-5 { height: 70%; animation-delay: 0.25s; }

.dashboard-task-list {
  list-style: none;
  margin: 24px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.dashboard-task-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 18px 20px;
  border-radius: 18px;
  background: #f8fafc;
  border: 1px solid transparent;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.dashboard-task-item:hover {
  background: #eef2ff;
  border-color: rgba(59, 130, 246, 0.15);
  transform: translateX(3px);
}

.dashboard-task-item span {
  display: block;
}

.dashboard-task-time {
  color: #6b7280;
  font-size: 0.9rem;
}

/* ============================================
   Animations
   ============================================ */

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

@keyframes growUp {
  from {
    transform: scaleY(0);
    opacity: 0;
  }
  to {
    transform: scaleY(1);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ============================================
   Responsive
   ============================================ */

@media (max-width: 900px) {
  .dashboard-page {
    --sidebar-width: 64px;
  }

  .dashboard-sidebar {
    padding: 20px 10px;
  }

  .dashboard-brand-row {
    flex-direction: column;
    gap: 10px;
  }

  .dashboard-brand {
    font-size: 1.05rem;
    text-align: center;
  }

  .dashboard-nav-item,
  .dashboard-nav-button {
    justify-content: center;
    padding: 10px;
    width: 100%;
  }

  .nav-label {
    display: none;
  }

  .dashboard-cards {
    grid-template-columns: 1fr;
  }

  .dashboard-body {
    grid-template-columns: 1fr;
  }
}


/* Base .auth-footer / .auth-card rules live in authentication.css —
   only the dark-mode overrides (dashboard-specific behavior) belong
   here, so the two files stay a single source of truth instead of
   silently drifting apart.

   One more dashboard-specific override, though: authentication.css's
   .auth-footer spans the full viewport width (left: 0), which is fine
   on the login page but on dashboard pages the fixed sidebar
   (z-index: 100, width: var(--sidebar-width)) sits on top of the
   footer's left edge, clipping/covering the copyright text behind it.
   Pull the footer's left edge in to start after the sidebar instead —
   using the same --sidebar-width var the main content area already
   reserves space with (see .dashboard-main's margin-left above), so it
   automatically follows the sidebar's collapsed/expanded width. */
.dashboard-page .auth-footer {
  left: var(--sidebar-width);
  width: auto;
}

.dark .auth-footer,
.dashboard-page.dark .auth-footer {
  background: #0b0e1a;
  border-top-color: #1e2235;
}

.dark .auth-footer-copyright,
.dashboard-page.dark .auth-footer-copyright {
  color: #f87171;
}

.dark .auth-footer-links,
.dashboard-page.dark .auth-footer-links,
.dark .auth-footer-links a,
.dashboard-page.dark .auth-footer-links a {
  color: #94a3b8;
}/* ============================================================
   pages.css — shared styles extracted from Dashboard1.jsx,
   DetailTable.jsx, and ProgramAnalysis.jsx inline \3c style> blocks.

   Import this in each page:
     import "../../css/pages.css";   (adjust path to match your project)
   ============================================================ */


.dept-select {
  appearance: none;
  border: 1.5px solid #e2e8f0;
  border-radius: 10px;
  padding: 7px 32px 7px 14px;
  font-size: 0.85rem;
  font-weight: 600;
  color: #1e293b;
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E") no-repeat right 12px center;
  cursor: pointer;
  outline: none;
  font-family: inherit;
  /* With Chrome's customizable-select rendering (appearance:base-select,
     applied below under @supports), the closed control's own internal
     "button part" seems to carry a minimum height of its own. When a
     caller overrides padding down to something smaller (e.g. the compact
     widget program filters use "4px 24px 4px 10px" instead of the 7px
     default), the box gets stretched to that minimum but the label text
     stays anchored to the top instead of re-centering — the extra space
     just appears below it. Making the select itself a centered flex box
     re-centers the text regardless of how short the padding is. */
  display: inline-flex;
  align-items: center;
}
.dept-select:focus { border-color: #6366f1; }

/* Closed select hover — same animation as the buttons (the sidebar's hover
   turned vertical): 4px lift, soft shadow, white wash. The chevron is a
   background image, so the wash is added as a second layer above it
   instead of replacing it. */
.dept-select {
  transition: transform 0.2s ease, box-shadow 0.25s ease, border-color 0.25s ease, background-color 0.25s ease;
}
.dept-select:not(:disabled):hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
  background-repeat: no-repeat, no-repeat;
  background-position: 0 0, right 12px center;
  background-size: 100% 100%, auto;
}
.dept-select:not(:disabled):active { transform: translateY(-4px) scale(0.97); }
/* Admin Panel selects (Archive / Audit / Login History / Departments /
   Programs / Roles) are styled inline rather than with .dept-select, and set
   no background — so they rendered transparent, the same tint as the page.
   Give them the same look as .dept-select (white fill, 600 weight, chevron)
   and the same hover animation. !important only where an inline style
   (padding / radius) would otherwise win. */
.admin-tab-content select {
  background-color: #fff;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  color: #1e293b;
  font-weight: 600;
  font-family: inherit;
  padding-right: 32px !important;
  border-radius: 10px !important;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.25s ease, border-color 0.25s ease, background-color 0.25s ease;
}
.admin-tab-content select:focus { border-color: #6366f1 !important; }
/* The chevron is drawn above as a background image. */
.admin-tab-content select::picker-icon { display: none; }
.admin-tab-content select:not(:disabled):hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
  background-repeat: no-repeat, no-repeat;
  background-position: 0 0, right 12px center;
  background-size: 100% 100%, auto;
}
.admin-tab-content select:not(:disabled):active { transform: translateY(-4px) scale(0.97); }
.admin-tab-content select:open,
.admin-tab-content select:open:hover { transform: none; box-shadow: none; }

/* While its list is open the select stays put, so the list doesn't drift
   away from it (browsers without :open ignore this rule). */
.dept-select:open,
.dept-select:open:hover { transform: none; box-shadow: none; }

/* ── Department / Program grouping toggle (ProgramAnalysis, All tab, OPVAA/admin) ── */
.view-mode-toggle {
  display: inline-flex;
  border: 1.5px solid #e2e8f0;
  border-radius: 10px;
  padding: 3px;
  gap: 2px;
  background: #fff;
}
.view-mode-btn {
  border: none;
  background: transparent;
  color: #64748b;
  font-size: 0.78rem;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.view-mode-btn:hover { color: #1e293b; }
.view-mode-btn.active {
  background: #eef2ff;
  color: #4f46e5;
}

/* ── Dept badge pill ── */
.dept-badge {
  display: inline-block;
  background: #eef2ff;
  color: #6366f1;
  font-size: 0.72rem;
  font-weight: 700;
  border-radius: 6px;
  padding: 2px 10px;
  margin-left: 10px;
  vertical-align: middle;
  letter-spacing: 0.5px;
}

/* ── Section / chart layout helpers (Dashboard1 + ProgramAnalysis) ── */
.section-inner-grid   { display: grid; gap: 18px; }
.section-inner-grid-2 { grid-template-columns: 1fr 1fr; }
.section-inner-grid-3 { grid-template-columns: 1fr 1fr 1fr; }

.chart-sub-label {
  font-size: 0.72rem;
  font-weight: 700;
  color: #94a3b8;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  margin: 0 0 10px;
}

.chart-block {
  background: #f8fafc;
  border-radius: 14px;
  padding: 14px 16px 10px;
}

/* ── Global scrollbar — matches the header's gradient
   (linear-gradient(135deg, #8B0000, #FF6347, #DB7093)) so the scrollbar
   reads as part of the same visual language instead of the browser
   default. Firefox's scrollbar-color only accepts flat colors (no
   gradients), so it gets a solid mid-tone fallback instead.

   Targets `html`, not `.dashboard-page`: .dashboard-page only sets
   min-height: 100vh with no overflow-y, so it never becomes its own
   scroll container — the document (html/body) is what actually scrolls,
   which is why the browser's own default scrollbar was showing up
   instead of this rule. The auth/login page is unaffected in practice:
   it's `overflow: hidden` and sized to one viewport, so it never scrolls
   regardless of what's declared here. */
html {
  scrollbar-width: thin;
  scrollbar-color: #9E2A2F #f1f0fb;
}
html::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}
html::-webkit-scrollbar-track {
  background: #f1f0fb;
}
html::-webkit-scrollbar-thumb {
  background: #9E2A2F;
  border-radius: 10px;
  border: 2px solid #f1f0fb;
}
html::-webkit-scrollbar-thumb:hover {
  background: #7a1f22;
}

/* Dark-mode variant — the dark toggle adds .dark to .dashboard-page
   itself (a descendant of html), not to html/body, so :has() is used to
   read that state from the ancestor. Supported in all current evergreen
   browsers (Chrome/Edge/Safari, and Firefox 121+); on an older Firefox
   this simply falls back to the light-mode thumb above, which is a minor
   cosmetic miss, not a breakage. */
html:has(.dashboard-page.dark) {
  scrollbar-color: #812227 #0f1424;
}
html:has(.dashboard-page.dark)::-webkit-scrollbar-track {
  background: #0f1424;
}
html:has(.dashboard-page.dark)::-webkit-scrollbar-thumb {
  background: #812227;
  border-color: #0f1424;
}
html:has(.dashboard-page.dark)::-webkit-scrollbar-thumb:hover {
  background: #9E2A2F;
}

/* ── Scrollable tab strip / data-grid scroll region (DetailTable.jsx's
   .tab-scroll, reused for its vertical grid, not just a horizontal tab
   strip) — brand maroon thumb, same raw-color approach as
   .card-list-scroll below and for the same reason: this sits inside
   .dashboard-main's blanket invert(1) hue-rotate(180deg) dark-mode
   filter, and that filter doesn't reliably apply to
   ::-webkit-scrollbar-thumb/-track (see the longer comment on
   .card-list-scroll just below), so dark mode needs its own raw values
   rather than relying on the filter to produce #9E2A2F itself.
   Previously this only set `height` (for the horizontal case), so the
   vertical scrollbar on DetailTable's grid fell through to the browser
   default gray; `width` is added so both directions match. */
.tab-scroll {
  scrollbar-width: thin;
  scrollbar-color: #9E2A2F #f1f0fb;
}
.tab-scroll::-webkit-scrollbar        { height: 6px; width: 6px; }
.tab-scroll::-webkit-scrollbar-track  { background: #f1f0fb; }
.tab-scroll::-webkit-scrollbar-thumb  { background: #9E2A2F; border-radius: 10px; }
.tab-scroll::-webkit-scrollbar-thumb:hover { background: #7a1f22; }

/* ── Scrollable card lists — Program Health Scorecard, Year-over-Year
   Delta and Top & Bottom Performers cap themselves to ~8 visible rows
   and scroll for the rest. Colors match the global `html` scrollbar
   above. These cards live inside .dashboard-main's auto-invert dark-mode
   subtree, and a single pass of that filter turns #9E2A2F into pink
   (confirmed: it's the same issue called out in the .dashboard-button/
   .dashboard-pill comments below) — so it needs a dark-mode override,
   same as those buttons.

   Unlike .dashboard-button, though, `filter` doesn't reliably apply to
   ::-webkit-scrollbar-thumb/-track — they're proprietary pseudo-elements,
   not real DOM nodes, so the "cancel the ambient filter with a second
   invert(1) hue-rotate(180deg)" trick silently does nothing here (that
   was the first attempt, and it stayed pink). Instead this uses the
   *other* working pattern already in this codebase (see .admin-tab-btn's
   non-active state in adminpanel.css): write the PRE-inverted raw color
   that the ambient single-pass filter then turns into the color we
   actually want to see. Computed via the CSS hue-rotate matrix (verified
   against dashboard.css's own "#435166 displays as #a3b1c6" comment):
     raw #0E0D18 displays as #F1F0FB (track — exact)
     raw #FFA3A8 displays as ~#903439 (thumb — closest reachable match to
       #9E2A2F; the true inverse clips outside the sRGB gamut, but the
       result is visually the same brick-red)
     raw #FFB9BC displays as ~#6E282B (thumb hover) */
.card-list-scroll {
  scrollbar-width: thin;
  scrollbar-color: #9E2A2F #f1f0fb;
}
.card-list-scroll::-webkit-scrollbar        { width: 6px; }
.card-list-scroll::-webkit-scrollbar-track  { background: #f1f0fb; }
.card-list-scroll::-webkit-scrollbar-thumb  { background: #9E2A2F; border-radius: 10px; }
.card-list-scroll::-webkit-scrollbar-thumb:hover { background: #7a1f22; }

.dashboard-page.dark .card-list-scroll {
  scrollbar-color: #FFA3A8 #0E0D18;
}
.dashboard-page.dark .card-list-scroll::-webkit-scrollbar-track {
  background: #0E0D18;
}
.dashboard-page.dark .card-list-scroll::-webkit-scrollbar-thumb {
  background: #FFA3A8;
}
.dashboard-page.dark .card-list-scroll::-webkit-scrollbar-thumb:hover {
  background: #FFB9BC;
}

.account-menu-trigger {
  border-radius: 12px;
  padding: 4px 8px;
  transition: background 0.15s ease;
}
.account-menu-trigger:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* ── Header account dropdown (avatar/name trigger → Settings + Logout) ── */
@keyframes accountMenuOpen {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.account-menu-dropdown {
  position: absolute;
  top: calc(100% + 10px);
  right: -14px;
  min-width: 180px;
  background: #fff;
  border: 2px solid #6366f1;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
  padding: 6px;
  z-index: 60;
  animation: accountMenuOpen 0.15s ease both;
}
.account-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: none;
  background: transparent;
  color: #334155;
  font-size: 0.85rem;
  font-weight: 600;
  font-family: inherit;
  padding: 9px 10px;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
}
.account-menu-item:hover {
  background: #eef2ff;
  color: #4f46e5;
}
.account-menu-item--danger:hover {
  background: #fef2f2;
  color: #dc2626;
}

/* ── Select dropdown lists — same look as the account menu above ──────────
   The account menu is a custom popup (2px indigo border, 12px radius, soft
   shadow, 6px padding, rounded items that tint on hover). A native <select>
   list can't be styled by default, but Chrome/Edge 135+ support the
   customizable select (`appearance: base-select` + ::picker(select)), which
   lets the list use that exact design. Browsers without it (Firefox/Safari)
   keep their native list, unchanged. Covers the .dept-select dropdowns
   (department / dataset / year / program / rows-per-page) and the Settings
   panel's select. */
@supports (appearance: base-select) {
  .dept-select,
  .sp-panel select,
  .admin-tab-content select { appearance: base-select; }

  /* .dept-select already draws its own chevron as a background image. */
  .dept-select::picker-icon { display: none; }

  /* Open/close animation — fades and slides down 6px, the same motion as
     the account menu (accountMenuOpen). Uses @starting-style so it also
     plays on open. */
  :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select) {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.15s ease, transform 0.15s ease,
                overlay 0.15s allow-discrete, display 0.15s allow-discrete;
  }
  :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select):popover-open {
    opacity: 1;
    transform: translateY(0);
  }
  @starting-style {
    :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select):popover-open {
      opacity: 0;
      transform: translateY(-6px);
    }
  }

  :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select) {
    appearance: base-select;
    background: #fff;
    border: 2px solid #6366f1;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
    padding: 6px;
    margin-block: 6px;
    max-height: 320px;
  }
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    gap: 10px;
    padding: 9px 10px;
    border-radius: 8px;
    background: transparent;
    color: #334155;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, transform 0.2s ease, box-shadow 0.25s ease;
  }
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible,
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:checked {
    background: #eef2ff;
    color: #4f46e5;
  }
  /* Selecting animation — the sidebar's row hover (4px slide + soft shadow),
     a press-in on click, and the checkmark popping in on the chosen row. */
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible {
    transform: translateX(4px);
    box-shadow: 0 4px 12px rgba(15, 23, 42, 0.12);
  }
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:active {
    transform: translateX(4px) scale(0.97);
  }
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option:checked::checkmark {
    animation: selectCheckPop 0.25s ease both;
  }
  @keyframes selectCheckPop {
    from { transform: scale(0); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
  }

  /* Checkmark sits at the far right of the row; label stays left-aligned
     like the account menu items. */
  :is(.dept-select, .sp-panel select, .admin-tab-content select) option::checkmark {
    order: 1;
    margin-left: auto;
    color: #6366f1;
  }

  /* Dark — same values as .account-menu-dropdown / .account-menu-item in dark. */
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select) {
    background: #1e293b;
    border-color: #818cf8;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  }
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option { color: #cbd5e1; }
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible,
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option:checked {
    background: #812227;
    color: #ffffff;
  }
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  }
  .dashboard-page.dark :is(.dept-select, .sp-panel select, .admin-tab-content select) option::checkmark { color: #ffffff; }

  /* Same dark list styling as above, but for .dept-select instances
     rendered inside DraggableWidget/stride-modal popups. Those render via
     createPortal(..., document.body), so in the real DOM they sit OUTSIDE
     .dashboard-page's subtree entirely — .dashboard-page.dark above can
     never match them. Scoped to .stride-widget-card/.stride-modal-card
     specifically (rather than a bare body.dark) so this doesn't also
     apply to the (already correctly styled) .dept-select instances that
     live inside .dashboard-main. */
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select)::picker(select) {
    background: #1e293b;
    border-color: #818cf8;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
  }
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option { color: #cbd5e1; }
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible,
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option:checked {
    background: #812227;
    color: #ffffff;
  }
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option:hover,
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option:focus-visible {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
  }
  body.dark :is(.stride-widget-card, .stride-modal-card) :is(.dept-select, .sp-panel select, .admin-tab-content select) option::checkmark { color: #ffffff; }
}

/* ── Dark mode tweaks ── */

/* .dept-select relies on .dashboard-main's ambient invert(1) hue-rotate
   filter for its dark look (see dashboard.css's "Smart-invert" comment) —
   that works everywhere EXCEPT inside DraggableWidget/stride-modal
   popups, which render via createPortal(..., document.body) and so sit
   outside .dashboard-main's subtree entirely; no ambient filter reaches
   them. Same root cause as .stride-widget-card needing its own body.dark
   rule instead of relying on .dashboard-page.dark. Scoped to
   .stride-widget-card/.stride-modal-card so the (already correct)
   .dept-select instances inside .dashboard-main aren't double-styled. */
body.dark :is(.stride-widget-card, .stride-modal-card) .dept-select {
  background-color: #1e293b;
  color: #cbd5e1;
  border-color: #334155;
}
body.dark :is(.stride-widget-card, .stride-modal-card) .dept-select:focus {
  border-color: #818cf8;
}

.dashboard-page.dark .tab-scroll { scrollbar-color: #FFA3A8 #0E0D18; }
.dashboard-page.dark .tab-scroll::-webkit-scrollbar-track { background: #0E0D18; }
.dashboard-page.dark .tab-scroll::-webkit-scrollbar-thumb { background: #FFA3A8; }
.dashboard-page.dark .tab-scroll::-webkit-scrollbar-thumb:hover { background: #FFB9BC; }


.dashboard-page.dark .account-menu-dropdown {
  background: #1e293b;
  border-color: #818cf8;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.dashboard-page.dark .account-menu-item { color: #cbd5e1; }
.dashboard-page.dark .account-menu-item:hover { background: #812227; color: #ffffff; }
.dashboard-page.dark .account-menu-item--danger:hover { background: #812227; color: #ffffff; }

/* ── Manage Data toolbar button hover effects (DetailTable + LegacyManageData) ──
   Buttons in these toolbars are styled with inline `style` objects (ghostBtn,
   primaryBtn, primaryBtnAlt, tinyBtn, plus one-off color overrides for
   delete/verify actions), so a plain CSS :hover rule needs `!important` to
   win over the inline background/border/color that's already there.
   `.btn-hover` covers every button (lift + shadow + a slight darken via
   `filter`, which works regardless of the button's own fill color).
   `.btn-hover-ghost` is added on top for the white-background bordered
   buttons (ghostBtn/tinyBtn and their color-swapped variants) to give them
   a visible tinted background on hover, since `filter: brightness()` alone
   is barely noticeable against white. */
.btn-hover {
  transition: filter 0.25s ease, transform 0.2s ease, box-shadow 0.25s ease, background 0.25s ease;
}
/* Same hover as the sidebar's .dashboard-nav-button (4px shift + soft
   shadow), but vertical: lifts 4px instead of sliding right 4px. */
.btn-hover:hover:not(:disabled) {
  filter: brightness(0.94);
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.btn-hover:active:not(:disabled) {
  transform: translateY(-4px) scale(0.97);
  filter: brightness(0.88);
}
.btn-hover:disabled {
  cursor: not-allowed;
}
.btn-hover-ghost:hover:not(:disabled) {
  background: #f1f5f9 !important;
}
.dashboard-page.dark .btn-hover-ghost:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.08) !important;
}

/* ── "Save Changes" button — maroon to match the rest of the palette
   (was the generic indigo primaryBtn fill). !important because it's
   layered on top of an inline `style` object that already sets
   `background`. Dark-mode variant follows the same darker-maroon the
   scrollbar and account menu use elsewhere in this file. ── */
.btn-save {
  background: #9E2A2F !important;
}
.dashboard-page.dark .btn-save {
  background: #812227 !important;
}

/* ── Spinner (DetailTable) ── */
.spin { animation: spin 0.9s linear infinite; }
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ── Shimmer skeleton (Dashboard1 + ProgramAnalysis) ── */
@keyframes shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── Tab pill active color (kept maroon, everything else reverted to
   the original inline-style look in Dashboard1 / DetailTable /
   ProgramAnalysis). */
.tab-pill-active {
  background: #9E2A2F !important;
  color: #ffffff !important;
}

/* ── "All" tab grid (ProgramAnalysis) ──
   Three rows, each pairing one "primary" metric (left, larger) with one
   "secondary" metric (right, smaller) — matches the requested wireframe:
     Enrollment Forecast  | Program Health Scorecard
     Retention Risk Flags | Year-over-Year Delta
     Board Exam Pass Rates| Top & Bottom Performers
   The page scrolls normally (same as every other tab) rather than forcing
   everything into one fixed viewport height; each tile still scrolls
   internally first if its own content is long. Tiles have no visible
   controls of their own — clicking anywhere on a tile is the only
   interaction, and pops that chart open full-size in a draggable +
   resizable widget (see ProgramAnalysis.jsx). */
.all-tab-grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  grid-template-rows: repeat(3, minmax(420px, auto));
  grid-template-areas:
    "forecast   health"
    "retention  yoy"
    "boardexam  performers";
  gap: 16px;
  /* Previously this grid was squeezed into calc(100vh - 210px) with 3 equal
     rows, which forced every chart (axis labels, legends, tooltips) to
     compress or clip to fit whatever sliver of height was left after the
     header/filters. Each row now gets a real minimum height and the grid's
     overall height is driven by its content instead of the viewport, so
     charts render at a size where every label, legend, and tooltip is
     fully visible — the page scrolls if the content is taller than the
     viewport, same as every other tab. */
  height: auto;
  min-height: 0;
}
.all-tab-grid .all-tab-forecast    { grid-area: forecast; }
.all-tab-grid .all-tab-retention   { grid-area: retention; }
.all-tab-grid .all-tab-boardexam   { grid-area: boardexam; }
.all-tab-grid .all-tab-health      { grid-area: health; }
.all-tab-grid .all-tab-yoy         { grid-area: yoy; }
.all-tab-grid .all-tab-performers  { grid-area: performers; }

.all-tab-grid > div {
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: transform 0.1s ease;
  border-radius: 24px;
}
/* Was: box-shadow: 0 0 0 2px #c7d2fe; on :hover. Removed — clicking a tile
   doesn't move the cursor away from it, so the ring stayed visible behind
   the popup that opens on click, instead of clearing like a hover effect
   should. The press-scale below still gives clear click feedback on its
   own. */
.all-tab-grid > div:active {
  transform: scale(0.997);
}
.all-tab-grid > div > * {
  flex: 1;
  min-height: 0;
}

@media (max-width: 1100px) {
  .all-tab-grid {
    grid-template-columns: 1fr;
    grid-template-areas:
      "forecast"
      "health"
      "retention"
      "yoy"
      "boardexam"
      "performers";
    grid-template-rows: none;
    height: auto;
    min-height: 0;
  }
  .all-tab-grid > div { min-height: 420px; }
}

/* ── "Back to All" pill on a single-section tab ──
   Replaces the dropdown's other job — getting back out of a section tab. */
.back-to-all-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 14px;
  padding: 6px 14px;
  border-radius: 10px;
  border: 1.5px solid #e2e8f0;
  background: #fff;
  color: #475569;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
}
.back-to-all-btn:hover {
  border-color: #c7d2fe;
  background: #eef2ff;
  color: #4f46e5;
}

/* ── "Universal" chart toggle — slide animation (Dashboard1) ──
   Opening: content slides in from the right (feels like the view moved
   left). Closing / back to Overview: content slides in from the left
   (feels like the view moved back right). Re-fires on every toggle since
   each branch is a fresh mount. */
@keyframes universalSlideLeft {
  from { transform: translateX(48px); opacity: 0.4; }
  to   { transform: translateX(0);    opacity: 1;   }
}
@keyframes universalSlideRight {
  from { transform: translateX(-48px); opacity: 0.4; }
  to   { transform: translateX(0);     opacity: 1;   }
}
.universal-slide-left  { animation: universalSlideLeft 0.32s ease both; }
.universal-slide-right { animation: universalSlideRight 0.32s ease both; }
   /* Applied to each section when its tab becomes active, keyed by tab so it
   re-fires on every switch. Honors prefers-reduced-motion via the global
   rule already defined in dashboard.css. */
.tab-content-enter {
  animation: tabFadeIn 0.35s ease both;
}
@keyframes tabFadeIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ── "Universal" / "Overview" edge toggle (Dashboard1) ──────────────────
   Invisible until hovered. Light #9E2A2F, dark #812227 (exact — the strip
   is .dark-keep inside a .dark-section row, so dashboard.css never inverts
   it). The content beside it makes room by resizing (margin in
   Dashboard1.jsx), not by translating, so nothing is pushed off-screen.
   !important overrides the generic lift + white wash that dashboard.css
   puts on every .dashboard-page button:hover — wrong for a tall edge tab. */
.universal-toggle-strip {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 56px;
  display: flex;
  align-items: stretch;
  z-index: 2;
}
.universal-toggle-strip--left  { left: 0; }
.universal-toggle-strip--right { right: 0; }

.universal-toggle-btn {
  flex: 1;
  border: none;
  border-radius: 18px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: transparent;
  color: transparent;
  box-shadow: none;
}
.universal-toggle-label {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.dashboard-page .universal-toggle-strip:hover .universal-toggle-btn,
.dashboard-page .universal-toggle-btn:focus-visible {
  background: #9E2A2F;
  color: #ffffff;
  box-shadow: 0 8px 20px rgba(158, 42, 47, 0.28) !important;
}
.dashboard-page.dark .universal-toggle-strip:hover .universal-toggle-btn,
.dashboard-page.dark .universal-toggle-btn:focus-visible {
  background: #812227;
  box-shadow: none !important;
}
.dashboard-page .universal-toggle-btn:hover,
.dashboard-page .universal-toggle-btn:active {
  transform: none !important;
  background-image: none !important;
}/* src/auth/authentication.css
   Shared visual language for Authentication.jsx (page chrome) and both
   Login.jsx / Register.jsx tab contents. */

/* Shared type stack — matches dashboard.css/.dashboard-page exactly so
   the auth pages and the dashboard never feel like two different apps.
   (Previously this page hard-locked to Arial while the dashboard used
   the browser/system default, which meant every label, button, and
   heading rendered in a visibly different typeface the moment a user
   moved between login and the dashboard.) */
.auth-page {
  position: relative;
  min-height: 100vh;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  background: #ffffff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
}

.auth-page * {
  font-family: inherit;
}

/* Background is split 50/50: photo on the left half, plain white on the
   right half (where the auth card lives). No fixed topbar anymore — the
   brand mark now lives inside the card (see .auth-card-brand below) — so
   the photo runs the full page height instead of stopping short to clear
   a topbar. */
.auth-background-photo {
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  z-index: 0;
}

/* Sits directly on top of the <img>, same footprint as the photo. */
.auth-background-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 50%;
  height: 100%;
  background: linear-gradient(180deg, rgba(15, 23, 42, 0.05), rgba(15, 23, 42, 0.25));
  z-index: 0;
  pointer-events: none;
}

@media (max-width: 900px) {
  /* Below this width the card stacks under a shorter strip of photo
     instead of trying to keep a literal side-by-side split. */
  .auth-background-photo,
  .auth-background-overlay {
    width: 100%;
    height: 34vh;
  }
}

.auth-card-wrap {
  position: relative;
  z-index: 1;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  /* No fixed topbar to clear anymore (brand mark moved inside the card),
     so this is just the usual breathing room around the card. */
  padding: clamp(24px, 5vw, 40px) clamp(12px, 3vw, 16px) clamp(56px, 6vw, 80px);
}

/* Both Login and Register now live in the same right-hand half of the
   page, on the plain white background next to the photo. */
.auth-card-wrap--split {
  margin-left: 50%;
  width: 50%;
  box-sizing: border-box;
}

@media (max-width: 900px) {
  .auth-card-wrap--split {
    margin-left: 0;
    width: 100%;
  }
}

.auth-footer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 24px;
  background: #ffffff;
  border-top: 1px solid #e5e7eb;
  font-size: 0.78rem;
}

.auth-footer-copyright {
  color: #8d3131;
}

.auth-footer-links {
  color: #64748b;
}

.auth-footer-links a {
  color: #64748b;
  text-decoration: none;
}

.auth-footer-links a:hover {
  text-decoration: underline;
}

.auth-card {
  width: 100%;
  max-width: 380px;
  background: #ffffff;
  border-radius: 4px;
  padding: 28px 30px 26px;
  box-shadow: 0 20px 60px rgba(15, 23, 42, 0.25);
  transition: max-width 0.15s ease;
}

/* Register tab needs room for a two-lane field layout so the whole form
   fits without scrolling — Login stays at the tighter default width. */
.auth-card--wide {
  max-width: 640px;
}

.auth-card-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 18px;
}

.auth-wordmark {
  font-size: clamp(1.4rem, 2.4vw, 1.8rem);
  font-weight: 400;
  color: #1f2937;
  line-height: 1;
}

.auth-sub-wordmark {
  font-size: clamp(0.56rem, 0.8vw, 0.62rem);
  font-weight: 400;
  color: #374151;
  letter-spacing: 0.02em;
  margin-top: 2px;
}

.auth-crest {
  border-radius: 6px;
  /* Flat fill — same maroon used across the header/sidebar gradients in
     dashboard.css, but solid here so the mark reads as one seamless
     shape instead of a bordered, beveled badge. */
  display: flex;
  align-items: center;
  justify-content: center;
  color: #f3d97a;
  font-weight: 400;
  flex-shrink: 0;
}

/* ─── Tabs ────────────────────────────────────────────────────────────── */

.auth-tabs {
  display: flex;
  gap: 8px;
  margin-bottom: 18px;
}

.auth-tab-btn {
  flex: 1;
  padding: 9px 10px;
  border-radius: 16px;
  border: 1.5px solid #e2e8f0;
  background: #f8fafc;
  cursor: pointer;
  font-weight: 400;
  font-size: 0.82rem;
  color: #475569;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.auth-tab-btn.active {
  background: #9E2A2F;
  color: #fff;
  border-color: transparent;
}

/* ─── Form elements (shared by Login + Register) ─────────────────────── */

.auth-required-note {
  font-size: 0.8rem;
  color: #374151;
  margin: 0 0 14px;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Two-lane variant, used by Register.jsx (paired with .auth-card--wide)
   so the longer field set fits without scrolling. Fields default to one
   lane; add .auth-span-2 to a field to have it span both. */
.auth-form--grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 16px;
}

.auth-span-2 {
  grid-column: 1 / -1;
}

@media (max-width: 560px) {
  .auth-form--grid {
    grid-template-columns: 1fr;
  }
  .auth-card--wide {
    max-width: 380px;
  }
}

.auth-input {
  padding: clamp(8px, 1.1vw, 10px) clamp(10px, 1.4vw, 12px);
  border-radius: 4px;
  border: 1px solid #cbd5e1;
  background: #f8fafc;
  color: #000000;
  font-size: clamp(0.84rem, 1vw, 0.9rem);
  outline: none;
  width: 100%;
  box-sizing: border-box;
  font-family: inherit;
}

.auth-input::placeholder {
  color: #6b7280;
}

.auth-input:hover {
  background: #f1f5f9;
}

.auth-input:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
}

.auth-input:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.auth-input:-webkit-autofill,
.auth-input:-webkit-autofill:hover,
.auth-input:-webkit-autofill:focus {
  -webkit-text-fill-color: #000000;
  -webkit-box-shadow: 0 0 0 1000px #f8fafc inset;
  transition: background-color 9999s ease-in-out 0s;
}

.auth-field-label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 0.82rem;
  color: #374151;
  font-weight: 400;
}

.auth-hint {
  margin: 4px 0 0;
  font-size: 0.74rem;
  color: #94a3b8;
}

.auth-error-box {
  background: #fef2f2;
  color: #b91c1c;
  border: 1px solid #fecaca;
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 0.8rem;
}

.auth-lockout-box {
  background: #fffbeb;
  color: #92400e;
  border: 1px solid #fde68a;
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 0.8rem;
}

.auth-ok-box {
  background: #f0fdf4;
  color: #166534;
  border: 1px solid #bbf7d0;
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 0.8rem;
}

.auth-link-btn {
  font-size: 0.82rem;
  color: #8d3131;
  text-decoration: none;
  background: transparent;
  border: none;
  padding: 0;
  text-align: left;
  cursor: pointer;
}

.auth-panel {
  margin-top: 14px;
  padding: 14px 16px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
}

.auth-panel-hint {
  margin: 0 0 4px;
  font-size: 0.78rem;
  color: #64748b;
  line-height: 1.5;
}

.auth-submit-btn {
  margin-top: 2px;
  padding: 9px 0;
  border-radius: 4px;
  border: none;
  background: #9E2A2F;
  color: #fff;
  font-weight: 400;
  font-size: 0.86rem;
  cursor: pointer;
}

.auth-submit-btn:disabled {
  opacity: 0.65;
  cursor: not-allowed;
}

.auth-primary-btn {
  margin-top: 4px;
  padding: 10px 0;
  border-radius: 4px;
  border: none;
  background: #9E2A2F;
  color: #fff;
  font-weight: 400;
  font-size: 0.92rem;
  cursor: pointer;
}

.auth-primary-btn:disabled {
  opacity: 0.65;
  cursor: not-allowed;
}

.auth-back-link {
  background: transparent;
  border: none;
  padding: 0;
  color: #64748b;
  font-size: 0.76rem;
  text-align: left;
  cursor: pointer;
  text-decoration: underline;
}

.auth-divider {
  margin: 22px 0 14px;
  border: none;
  border-top: 1px solid #e5e7eb;
}

.auth-footer-note {
  font-size: 0.78rem;
  color: #64748b;
  line-height: 1.5;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .auth-tab-btn {
    transition: none;
  }
}

/* ─── Split-area card (redesign) ─────────────────────────────────────────
   Shared container for both Login and Register — same right-hand area,
   same card chrome. Width scales with the viewport via clamp() instead
   of a fixed pixel max-width. */

/* authentication.css */

.auth-card-split {
  position: relative;
  width: 100%;
  max-width: clamp(320px, 32vw, 460px);
  background: #ffffff;
  border-radius: 14px;
  border-top: 5px solid #9E2A2F;
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.3);
  overflow: visible;
  box-sizing: border-box;
  padding: clamp(24px, 3vw, 34px) clamp(20px, 3.4vw, 34px) clamp(24px, 3.4vw, 32px);
}

/* Register's field set needs more breathing room for its two-lane grid. */
.auth-card-split--wide {
  max-width: clamp(360px, 42vw, 640px);
}

@media (max-width: 560px) {
  .auth-card-split,
  .auth-card-split--wide {
    max-width: 100%;
  }
}

/* Crest + wordmark, shown at the top of the card — Login tab only.
   Sits above .auth-login-heading ("Welcome") so it reads as the top of
   both the card and the greeting. Register hides this block entirely
   (see the `isLogin &&` guard in Authentication.jsx). */
.auth-card-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}

.auth-card-brand-wordmark {
  font-size: clamp(1.25rem, 2.4vw, 1.55rem);
  font-weight: 400;
  color: #8d3131;
  letter-spacing: 0.02em;
}

.auth-login-heading {
  font-size: clamp(1.25rem, 2.4vw, 1.55rem);
  font-weight: 400;
  color: #8d3131;
  margin: 0 0 4px;
}

.auth-login-subheading {
  font-size: clamp(0.78rem, 1vw, 0.86rem);
  color: #1d4ed8;
  margin: 0 0 18px;
}

.auth-login-divider {
  border: none;
  border-top: 1px solid #e5e7eb;
  margin: 0 -32px 20px;
}

.auth-card-split .auth-tabs {
  margin-bottom: 18px;
}

/* Icon-prefixed inputs */

.auth-field-label-plain {
  font-size: 0.78rem;
  font-weight: 400;
  color: #89a3cc;
  margin-bottom: 6px;
  display: block;
}

.auth-field-row-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}

.auth-trouble-link {
  font-size: 0.74rem;
  color: #8d3131;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  text-decoration: none;
}

.auth-trouble-link:hover {
  text-decoration: underline;
}

.auth-input-icon-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.auth-input-icon-wrap .auth-input {
  padding-left: 40px;
}

.auth-input-icon-wrap .auth-input.has-trailing-icon {
  padding-right: 38px;
}

.auth-input-icon {
  position: absolute;
  left: 12px;
  width: 16px;
  height: 16px;
  color: #64748b;
  pointer-events: none;
  flex-shrink: 0;
}

.auth-input-eye-btn {
  position: absolute;
  right: 10px;
  background: transparent;
  border: none;
  cursor: pointer;
  color: #64748b;
  display: flex;
  align-items: center;
  padding: 4px;
}

.auth-input-eye-btn:hover {
  color: #374151;
}

.auth-login-signin-btn {
  border-radius: 16px;
  margin-top: 6px;
  padding: clamp(10px, 1.4vw, 12px) 0;
  border: none;
  background: #9E2A2F;
  color: #fff;
  font-weight: 400;
  font-size: clamp(0.82rem, 1vw, 0.9rem);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  width: 100%;
}

.auth-login-signin-btn:disabled {
  opacity: 0.65;
  cursor: not-allowed;
}

/* ─── CAPTCHA widget (Login.jsx main form + forgot-password panel) ──────
   react-google-recaptcha's checkbox iframe is a fixed ~304px wide, which
   is wider than .auth-card-split's content box once padding is
   subtracted at the low end of its clamp() (320px card - ~2*20px padding
   = ~280px). transform:scale keeps the checkbox itself fixed-size (it's
   an iframe — can't be resized via CSS) while shrinking its footprint so
   it still fits without clipping or forcing a horizontal scrollbar. The
   wrapping div's height is pulled in to match the scaled box, otherwise
   the unscaled 78px-tall layout box would leave dead space beneath it. */
.auth-captcha-widget {
  margin: 2px 0;
  display: flex;
  justify-content: center;
}

@media (max-width: 400px) {
  .auth-captcha-widget {
    transform: scale(0.92);
    transform-origin: center;
    height: 72px;
  }
}

/* ─── Register step wizard ────────────────────────────────────────────── */

.auth-step-indicator {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}

.auth-step-dot {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  font-weight: 400;
  background: #e2e8f0;
  color: #64748b;
  flex-shrink: 0;
}

.auth-step-dot.active {
  background: #8d3131;
  color: #fff;
}

.auth-step-dot.done {
  background: #16a34a;
  color: #fff;
}

.auth-step-track {
  flex: 1;
  height: 2px;
  background: #e2e8f0;
}

.auth-step-btn-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 4px;
}

.auth-step-btn-row .auth-login-signin-btn {
  width: auto;
  padding-left: clamp(20px, 3vw, 28px);
  padding-right: clamp(20px, 3vw, 28px);
  margin-top: 0;
}

/* Live password-requirement checklist under Register's password field. */
.auth-pw-requirements {
  list-style: none;
  margin: 2px 0 2px;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 14px;
  row-gap: 3px;
}

@media (max-width: 480px) {
  .auth-pw-requirements {
    grid-template-columns: 1fr;
  }
}

.auth-pw-requirements li {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: clamp(0.7rem, 0.9vw, 0.76rem);
  color: #94a3b8;
}

.auth-pw-requirements li.met {
  color: #16a34a;
}

.auth-pw-requirement-dot {
  width: 14px;
  display: inline-flex;
  justify-content: center;
  font-weight: 400;
}/* ============================================================
   adminpanel.css — styles specific to AdminPanel.jsx content.
   Header, sidebar, and footer chrome all come from dashboard.css
   (.dashboard-page / .dashboard-container / .dashboard-sidebar /
   .dashboard-main / .dashboard-header / .auth-footer) — this file
   only covers the admin tab strip and the content card wrapper
   that sits below the shared header.
   ============================================================ */

/* ── Admin section tab strip (Manage Accounts / Archive / Audit / ...) ── */
.admin-tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  border-bottom: 1px solid #e2e8f0;
  margin-top: 4px;
}

.admin-tab-btn {
  border: none;
  background: transparent;
  padding: 10px 18px;
  font-size: 0.85rem;
  font-weight: 600;
  color: #64748b;
  cursor: pointer;
  border-bottom: 3px solid transparent;
  font-family: inherit;
  /* Rounded top corners so the shared button hover (4px lift + shadow,
     see "Universal button hover" in dashboard.css) looks right on a tab. */
  border-radius: 10px 10px 0 0;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.admin-tab-btn:hover {
  color: #1e293b;
}
.admin-tab-btn.active {
  color: #9E2A2F;
  border-bottom: 3px solid #9E2A2F;
}

.dashboard-page.dark .admin-tabs {
  border-bottom: 1px solid #334155;
}
/* The tab strip sits inside .dashboard-main, whose dark-mode invert filter
   flips every color under it — so the colors below are written PRE-inversion.
   The old values (#94a3b8 / #e2e8f0) were the colors we wanted to SEE, but the
   filter turned them into #505f74 (dim) and #121820 (near-black on hover).
   #435166 displays as #a3b1c6; #1e293b displays as #cdd8ea. */
.dashboard-page.dark .admin-tab-btn {
  color: #435166;
}
.dashboard-page.dark .admin-tab-btn:hover {
  color: #1e293b;
}
.dashboard-page.dark .admin-tab-btn.active {
  /* .admin-tab-btn lives inside .dashboard-main, which auto-inverts its
     whole subtree in dark mode (see dashboard.css's "Smart-invert" note).
     A plain color: #9E2A2F here would still get flipped by that filter,
     so cancel it first (same double-invert trick used for
     .dashboard-header / .dashboard-button) to get the literal brand
     maroon instead of an inverted shade of it. */
  filter: invert(1) hue-rotate(180deg);
  color: #812227;
  border-bottom: 3px solid #812227;
}

/* ── Content wrapper for the active admin tab ── */
.admin-tab-content {
  min-width: 0;
  /* Native controls (inputs, checkboxes) that set no background of their own
     follow the OS/browser color scheme — on a dark OS they rendered dark
     grey even in light mode. Pin them to the light scheme so they match the
     white dropdowns; in the app's dark mode the page's invert filter then
     darkens them along with everything else. */
  color-scheme: light;
}

/* Text inputs / textareas in the admin tabs: same white fill, border, radius
   and text color as the dropdowns (see .admin-tab-content select in
   pages.css). !important on radius only, where an inline style sets 8px. */
.admin-tab-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
.admin-tab-content textarea {
  background-color: #fff;
  color: #1e293b;
  border-radius: 10px !important;
  font-family: inherit;
}
.admin-tab-content input::placeholder,
.admin-tab-content textarea::placeholder { color: #94a3b8; }
.admin-tab-content input:not([type="checkbox"]):not([type="radio"]):focus,
.admin-tab-content textarea:focus { border-color: #6366f1 !important; }

/* Checkboxes: light box, brand-maroon check (was the browser's default). */
.admin-tab-content input[type="checkbox"],
.admin-tab-content input[type="radio"] { accent-color: #9E2A2F; }

/* ── Per-table scroll region ──────────────────────────────────────────
   Every admin table (AdminProfile, Archive, AuditLog, LoginHistory,
   DepartmentManager, ProgramManager, RolesManager, PermissionsMatrix)
   used to just wrap its <table> in a plain overflowX:"auto" div, so a
   long list had nowhere to scroll except the whole browser window —
   the page just kept growing (see the "Manage Accounts" screenshot: 20+
   rows with no scrollbar of its own). This class replaces that inline
   style: the table now scrolls within its own bounded box, capped to
   the viewport, with its header row pinned so column labels stay
   visible while scrolling.
   No dark-mode override needed here — this sits inside .dashboard-main,
   which already applies a blanket invert(1) hue-rotate(180deg) filter
   in dark mode (see dashboard.css), so a plain #fff header auto-inverts
   along with the rest of each card. */
.admin-table-scroll {
  overflow-x: auto;
  overflow-y: auto;
  max-height: calc(100vh - 360px);
  min-height: 240px;
}
.admin-table-scroll thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #fff;
}
/* PermissionsMatrix has a two-row <thead> (category labels, then the
   actual permission labels) — the second row needs its own top offset
   or it would stick at the same top:0 as the first row and overlap it. */
.admin-table-scroll thead tr:nth-child(2) th {
  top: 33px;
}

/* ── Row-capped variant — Departments, Programs, Roles, and the
   Permissions Matrix all use this instead of the base class's
   viewport-relative height: rather than filling most of the screen
   before scrolling, these cap themselves to roughly 10 data rows and
   scroll for the rest. It's an approximate pixel height rather than an
   exact "10 rows" measurement — actual row height varies a little
   between these tables (RolesManager's flag-chip rows run taller than
   DepartmentManager's plain code/name rows, for instance) — tuned to
   land right around 10 rows for each. */
.admin-table-scroll--cap10 {
  max-height: 440px;
  min-height: 0;
}

/* ── Scrollbar — explicit colors so it looks the same in both themes ──
   This sits inside .dashboard-main's blanket invert(1) hue-rotate(180deg)
   dark-mode filter, but that filter doesn't reliably apply to
   ::-webkit-scrollbar-thumb/-track (they're proprietary pseudo-elements,
   not real DOM nodes) — canceling it with a second invert() on the
   pseudo-element silently does nothing, so this instead uses the working
   pattern already established for .card-list-scroll in pages.css: write
   the raw pre-inverted color that the ambient single-pass filter turns
   INTO #9E2A2F (or the closest reachable match — the true inverse clips
   outside sRGB gamut). Light mode needs no correction since nothing is
   inverting it there. */
.admin-table-scroll {
  scrollbar-width: thin;
  scrollbar-color: #9E2A2F #f1f0fb;
}
.admin-table-scroll::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
.admin-table-scroll::-webkit-scrollbar-track {
  background: #f1f0fb;
  border-radius: 8px;
}
.admin-table-scroll::-webkit-scrollbar-thumb {
  background: #9E2A2F;
  border-radius: 8px;
  border: 2px solid #f1f0fb;
}
.admin-table-scroll::-webkit-scrollbar-thumb:hover {
  background: #7a1f22;
}
.dashboard-page.dark .admin-table-scroll {
  scrollbar-color: #FFA3A8 #0E0D18;
}
.dashboard-page.dark .admin-table-scroll::-webkit-scrollbar-track {
  background: #0E0D18;
}
.dashboard-page.dark .admin-table-scroll::-webkit-scrollbar-thumb {
  background: #FFA3A8;
  border-color: #0E0D18;
}
.dashboard-page.dark .admin-table-scroll::-webkit-scrollbar-thumb:hover {
  background: #FFB9BC;
}

/* ── Access-restricted / signed-out states, styled like dashboard's own
   empty/error states rather than a bespoke full-page card. ── */
.admin-blocked-card {
  background: #fff;
  border-radius: 20px;
  padding: 32px 36px;
  box-shadow: 0 8px 30px rgba(15, 23, 42, 0.08);
  max-width: 420px;
  text-align: center;
  margin: 60px auto;
}
.admin-blocked-title {
  margin: 0 0 10px;
  font-size: 1.3rem;
  color: #1e293b;
}
.admin-blocked-text {
  margin: 0 0 18px;
  color: #64748b;
  font-size: 0.9rem;
  line-height: 1.5;
}

.dashboard-page.dark .admin-blocked-card {
  background: #1e293b;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
}
.dashboard-page.dark .admin-blocked-title {
  color: #e2e8f0;
}
.dashboard-page.dark .admin-blocked-text {
  color: #94a3b8;
}
/* ── Brand-maroon action buttons (Add Role, Add Department, Archive,
   Unarchive) — these use an inline React style (#9E2A2F) rather than a
   CSS class for their base color, same as every other inline-styled
   button in these admin tables. They live inside .dashboard-main, which
   auto-inverts its whole subtree in dark mode (see the "Smart-invert"
   note in dashboard.css), so left alone they'd render as whatever
   invert(1) hue-rotate(180deg) turns #9E2A2F into — not the brand's
   actual dark-mode maroon. `.brand-btn` cancels that ambient filter on
   just this element (same double-invert trick as .admin-tab-btn.active
   above), then sets the literal #812227 background — the same
   dark-mode maroon used everywhere else in the app (adminpanel.css's
   own active-tab color, ConfirmDialog/AiDisclaimerModal's dark
   overrides, etc). !important is required because a plain CSS rule
   can't out-rank the inline style on its own. */
.dashboard-page.dark .brand-btn {
  filter: invert(1) hue-rotate(180deg);
  background: #812227 !important;
}/* ============================================================
   privacypolicy.css — PrivacyPolicy.jsx content only.
   Header, sidebar, and footer chrome all come from dashboard.css
   (.dashboard-page / .dashboard-container / .dashboard-sidebar /
   .dashboard-main / .dashboard-header / .auth-footer), same as
   adminpanel.css does for AdminPanel.jsx.

   Import in PrivacyPolicy.jsx:
     import "../../css/privacypolicy.css";
   ============================================================ */

/* ── Page wrapper — hosts the document/TOC layout ── */
.privacy-page-content {
  position: relative;
  padding: 4px 2px 40px;
}

/* ── Make position: sticky work ─────────────────────────────────────
   dashboard.css sets `overflow-x: hidden` on .dashboard-page. Per the CSS
   spec that forces overflow-y to `auto` too, which turns .dashboard-page
   into its own (non-scrolling) scroll container. A sticky element sticks
   relative to its NEAREST scroll container, so the TOC was "sticking" to a
   box that never scrolls and simply scrolled away with the page.
   `overflow-x: clip` still prevents horizontal overflow but does NOT create
   a scroll container, so the TOC now sticks to the real viewport scroll.
   (Scoped to this page via :has(); browsers without `clip` keep the old
   `hidden` value, i.e. no regression, just no sticky.) */
.dashboard-page:has(.privacy-layout) {
  overflow-x: clip;
}

/* ── Layout: document on the left, sticky TOC on the right ── */
.privacy-layout {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 24px;
  align-items: start;
}

@media (max-width: 900px) {
  .privacy-layout {
    grid-template-columns: 1fr;
  }
}

/* ── Table of contents ──────────────────────────────────────────────
   Sticky within the viewport and taller than before (roomier padding,
   larger label) so it reads as a real navigation panel rather than a
   compact index. The active link updates live via the IntersectionObserver
   scroll-spy in PrivacyPolicy.jsx as the reader scrolls the document. */
.privacy-toc {
  position: sticky;
  top: 20px; /* header is not sticky on this page, so the TOC only needs a small gap from the screen edge */
  z-index: 2;
  background: #fff;
  border-radius: 16px;
  padding: 22px 20px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.1);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
}
@media (max-width: 900px) {
  .privacy-toc {
    position: static;
    max-height: none;
  }
}

.privacy-toc-label {
  margin: 0 0 14px;
  font-size: 0.76rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #94a3b8;
}

.privacy-toc ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.privacy-toc-link {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  color: #64748b;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 7px 10px;
  border-radius: 8px;
  cursor: pointer;
  border-left: 3px solid transparent;
  font-family: inherit;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.privacy-toc-link:hover {
  background: #f8fafc;
  color: #1e293b;
}
.privacy-toc-link.active {
  background: #eef2ff;
  color:#9E2A2F;
  border-left-color: #9E2A2F;
  font-weight: 700;
}

/* ── Document card ── */
.privacy-doc-card {
  background: #fff;
  border-radius: 20px;
  padding: 32px 40px 40px;
  box-shadow: 0 8px 30px rgba(15, 23, 42, 0.08);
  color: #334155;
  line-height: 1.65;
}

.privacy-doc-headline {
  margin-bottom: 20px;
  padding-bottom: 18px;
  border-bottom: 1px solid #f1f5f9;
}
.privacy-doc-headline h1 {
  margin: 0 0 6px;
  font-size: 1.5rem;
  color: #1e293b;
}

.privacy-doc-meta {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 700;
  color: #94a3b8;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.privacy-doc-card section {
  scroll-margin-top: 24px; /* small gap above a section when jumped to from the TOC */
}

.privacy-doc-card h2 {
  font-size: 1.05rem;
  color: #1e293b;
  margin: 28px 0 10px;
  padding-bottom: 6px;
  border-bottom: 2px solid #f1f5f9;
}
.privacy-doc-card section:first-of-type h2 {
  margin-top: 0;
}

.privacy-doc-card p {
  margin: 0 0 12px;
  font-size: 0.92rem;
}

.privacy-doc-card ul {
  margin: 0 0 14px;
  padding-left: 22px;
}
.privacy-doc-card ul li {
  margin-bottom: 8px;
  font-size: 0.92rem;
}

.privacy-doc-card strong {
  color: #1e293b;
}

.privacy-doc-table {
  width: 100%;
  border-collapse: collapse;
  margin: 4px 0 16px;
  font-size: 0.88rem;
}
.privacy-doc-table th,
.privacy-doc-table td {
  border: 1px solid #e2e8f0;
  padding: 8px 12px;
  text-align: left;
}
.privacy-doc-table th {
  background: #f8fafc;
  color: #475569;
  font-weight: 700;
}

.privacy-doc-disclaimer {
  margin-top: 28px;
  padding: 14px 16px;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: 10px;
  color: #92400e;
  font-size: 0.82rem;
  font-style: italic;
}

/* ── Dark mode ──────────────────────────────────────────────────────
   IMPORTANT: dashboard.css applies `filter: invert(1) hue-rotate(180deg)` to
   every direct child of .dashboard-main in dark mode. That would invert the
   hand-authored dark palette below a second time — turning the dark card
   and TOC pale blue-grey with black text (the bug seen in dark mode).
   This page already ships its own dark colors, so opt it out of the filter.
   !important is needed to beat the long :not(:has(...)) selector there.
   (Removing the filter also stops it acting as a containing block.) */
.dashboard-page.dark .dashboard-main > .privacy-page-content {
  filter: none !important;
}

/* ── Dark palette (matches the black theme used elsewhere) ─────────
   Written explicitly rather than relying on .dashboard-main's blanket
   invert(1) filter, so the TOC/card get an intentional dark palette
   instead of an uncontrolled color flip. */
.dashboard-page.dark .privacy-toc {
  background: #040608;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.dashboard-page.dark .privacy-toc-label { color: #64748b; }
.dashboard-page.dark .privacy-toc-link { color: #94a3b8; }
.dashboard-page.dark .privacy-toc-link:hover { background: #812227; color: #ffffff; }
.dashboard-page.dark .privacy-toc-link.active {
  background: #812227;
  color: #ffffff;
  border-left-color: #812227;
}

.dashboard-page.dark .privacy-doc-card {
  background: #040608;
  color: #cbd5e1;
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
}
.dashboard-page.dark .privacy-doc-headline { border-bottom-color: #0e131b; }
.dashboard-page.dark .privacy-doc-headline h1 { color: #e2e8f0; }
.dashboard-page.dark .privacy-doc-meta { color: #64748b; }
.dashboard-page.dark .privacy-doc-card h2 { color: #e2e8f0; border-bottom-color: #0e131b; }
.dashboard-page.dark .privacy-doc-card strong { color: #e2e8f0; }
.dashboard-page.dark .privacy-doc-table th,
.dashboard-page.dark .privacy-doc-table td { border-color: #0e131b; }
.dashboard-page.dark .privacy-doc-table th { background: #0a0f16; color: #94a3b8; }
.dashboard-page.dark .privacy-doc-disclaimer {
  background: #3b2f0f;
  border-color: #92720b;
  color: #fcd34d;
}