@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;0,700;1,400&family=Jost:wght@300;400;500;600;700;800&display=swap');

/* ─── Reset ─────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* overflow-x: clip (not hidden) — hidden with overflow-y unset forces
   overflow-y to compute as auto, turning body into an accidental scroll
   container that breaks position:sticky on its children (navbar, tab
   bars, etc.), since html — not body — is the element that actually
   scrolls. clip has no such pairing quirk. */
html, body { max-width: 100%; overflow-x: clip; }
/* Always reserve space for the vertical scrollbar, whether or not the
   current page actually needs one — without this, switching from a short
   page (no scrollbar) to a tall one (has a scrollbar) shifts all content,
   including the navbar, sideways by the scrollbar's width. That's the
   navbar "moving left" between tabs. */
html { scrollbar-gutter: stable; overflow-y: scroll; }


/* ─── Design Tokens ─────────────────────────────────────────────────────────── */
:root {
  /* Brand */
  --primary:       #c47c05;
  --primary-dark:  #9a6004;
  --primary-light: #fef5e0;
  --primary-muted: #f4c560;

  /* Semantic */
  --success:       #059669;
  --success-light: #d1fae5;
  --danger:        #dc2626;
  --danger-light:  #fee2e2;
  --warning:       #d97706;
  --warning-light: #fef3c7;
  --secondary:     #d97706;

  /* Neutral */
  --text:          #0f172a;
  --text-muted:    #7a5018;
  --text-subtle:   #a0722a;
  --border:        #e2e8f0;
  --bg:            #f8fafc;
  --bg-hover:      #f1f5f9;
  --white:         #ffffff;
  --card-bg:       #ffffff;

  /* Nav */
  --nav-bg:        #0f172a;
  --nav-h:         60px;

  /* Table/grid column headers -- their own tokens (not hardcoded to --bg/
     --text-subtle) so School Profile > Appearance can recolor just the
     header strip independently of the page background. Default value is a
     live reference to --bg/--text-subtle (var() resolves lazily at use
     time), so a school that never opens Appearance sees today's exact
     look, light or dark, with zero extra CSS needed. */
  --th-bg:         var(--bg);
  --th-text:       var(--text-subtle);

  /* Decorative page-background glows (body{}, below) -- same "live default,
     JS overrides on demand" pattern as --th-bg/--th-text above. Values here
     are today's exact hardcoded blob colors so nothing changes until a
     school actually opens Appearance; applyAppearancePalette() (utils.js)
     then recomputes these from the chosen palette's own primary/pageBg so
     the background visibly follows the pick instead of staying fixed. */
  --bg-glow-1: rgba(196,124,5,.28);
  --bg-glow-2: rgba(43,122,117,.22);
  --bg-tint:   rgba(248,250,252,.5);

  /* Scrollbars */
  --scrollbar-track: rgba(196,124,5,.08);
  --scrollbar-thumb:       var(--primary-muted);
  --scrollbar-thumb-hover: var(--primary);

  /* Radii */
  --radius:        10px;
  --radius-sm:     6px;
  --radius-lg:     16px;
  --radius-pill:   999px;

  /* Shadows */
  --shadow-sm:  0 1px 2px rgba(0,0,0,.05);
  --shadow:     0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
  --shadow-md:  0 4px 12px rgba(0,0,0,.08), 0 2px 4px rgba(0,0,0,.05);
  --shadow-lg:  0 12px 40px rgba(0,0,0,.12), 0 4px 12px rgba(0,0,0,.08);

  --transition: 180ms ease;
}

/* Full dark-token set, app-wide (not page-scoped like the earlier pilot) —
   most components (inputs, .stat-card, buttons, badges) already reference
   these variables rather than hardcoded colors, so they adapt automatically.
   Only a couple of components below (.card, .table-wrap) hardcode a "light
   glass" look regardless of theme and need explicit dark overrides too. */
:root[data-theme="dark"] {
  --primary:       #e0a020;
  --primary-dark:  #c47c05;
  --primary-light: rgba(224,160,32,.14);
  --primary-muted: #f4c560;
  --success-light: rgba(5,150,105,.18);
  --danger-light:  rgba(220,38,38,.18);
  --warning-light: rgba(217,119,6,.18);
  --text:          #E6DDC6;
  --text-muted:    #b9a483;
  --text-subtle:   #8f7c5c;
  --border:        rgba(255,255,255,.1);
  --bg:            #14110c;
  --bg-hover:      #1c1811;
  --white:         #1a160f;
  --card-bg:       #1a160f;
  --bg-glow-1: rgba(224,160,32,.28);
  --bg-glow-2: rgba(43,122,117,.11);
  --bg-glow-3: rgba(220,20,60,.11);
  --bg-base:   #0d0b08;
  --shadow-sm:  0 1px 2px rgba(0,0,0,.3);
  --shadow:     0 1px 3px rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.3);
  --shadow-md:  0 4px 12px rgba(0,0,0,.35), 0 2px 4px rgba(0,0,0,.3);
  --shadow-lg:  0 12px 40px rgba(0,0,0,.45), 0 4px 12px rgba(0,0,0,.35);

  /* Dark bg needs a lighter (not darker) track to read as a track at all;
     the thumb drops to primary-dark since primary/-muted are too close in
     value to the lighter track to show contrast here. */
  --scrollbar-track: rgba(255,255,255,.07);
  --scrollbar-thumb:       var(--primary-dark);
  --scrollbar-thumb-hover: var(--primary);
}

/* ─── Scrollbars ─────────────────────────────────────────────────────────────── */
/* Thin, on-brand scrollbars everywhere (page scroll, tables, filter strips,
   modals, roster grids, …) instead of each OS's default chrome. Firefox via
   scrollbar-width/-color, everyone else via ::-webkit-scrollbar*. The
   transparent border + background-clip:padding-box trick insets the thumb
   inside the track so it reads as a slim floating pill, not a bar that
   fills the full gutter. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: var(--scrollbar-track); border-radius: var(--radius-pill); }
*::-webkit-scrollbar-thumb {
  background-color: var(--scrollbar-thumb);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background-color: var(--scrollbar-thumb-hover); }
*::-webkit-scrollbar-corner { background: transparent; }

/* ─── Base ───────────────────────────────────────────────────────────────────── */
/* Background is two soft color glows painted directly as part of body's own
   `background` property — deliberately NOT pseudo-elements this time. A
   pseudo-element's visibility depends on z-index/stacking-context rules that
   went wrong twice in a row here; a plain background layer has no such
   failure mode; it is always painted, always behind body's children, no
   stacking context required. background-attachment:fixed keeps it visible
   while scrolling on long pages — the earlier concern about `fixed` was
   about a large bitmap photo specifically, not a cheap computed gradient
   like this one, so that tradeoff doesn't apply here.

   Light theme only, the two glows also slowly drift (bgDrift below) so the
   page doesn't sit dead still — the photo and flat tint layers stay put
   (animating those too would look janky on a full-viewport fixed image, and
   nobody's eye is on the corners anyway). That's why this is split into
   separate background-image/-position/-size properties instead of the
   single `background` shorthand: a shorthand bakes each layer's position
   into its own radial-gradient()/url() term, which isn't animatable, but
   background-position accepts one value per layer and can be keyframed. */
body {
  font-family: 'Jost', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background-image:
    radial-gradient(circle at center, var(--bg-glow-1), transparent 60%),
    radial-gradient(circle at center, var(--bg-glow-2), transparent 60%),
    linear-gradient(var(--bg-tint), var(--bg-tint)),
    url('/shared/img/bg-blur.jpg');
  background-position: 100% 0%, 0% 100%, center, center;
  background-size: auto, auto, auto, cover;
  background-color: var(--bg);
  background-repeat: no-repeat;
  background-attachment: fixed;
  animation: bgDrift 16s ease-in-out infinite alternate;
  color: var(--text);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}
@keyframes bgDrift {
  0%   { background-position: 100% 0%, 0% 100%, center, center; }
  100% { background-position: 65% 35%, 35% 65%, center, center; }
}
@media (prefers-reduced-motion: reduce) {
  body { animation: none; }
}
/* The bottom-left glow used to be all teal-green at .22 opacity -- now split
   into two layered glows at the same spot, teal and crimson each at half
   that opacity, so the corner reads as a green/crimson blend instead of
   solid green. Top-right amber glow is untouched. */
:root[data-theme="dark"] body {
  background:
    radial-gradient(circle at 96% 2%, var(--bg-glow-1), transparent 45%),
    radial-gradient(circle at 2% 96%, var(--bg-glow-2), transparent 45%),
    radial-gradient(circle at 2% 96%, var(--bg-glow-3), transparent 45%),
    var(--bg-base);
  background-repeat: no-repeat;
  background-attachment: fixed;
  animation: none;
}

/* ─── Navbar ─────────────────────────────────────────────────────────────────── */
.navbar {
  background: #0f172a;
  height: var(--nav-h);
  padding: 0 1.5rem;
  display: flex;
  align-items: center;
  position: sticky;
  top: 0;
  z-index: 200;
  box-shadow: 0 1px 0 rgba(255,255,255,.07), var(--shadow-md);
  gap: 1rem;
}

/* Shows by default (the wheel favicon, sized/bordered per-page) - a real
   logo, not blank text-only. applyBranding() swaps the src to the school's
   own uploaded logo when one exists (utils.js). */
.nav-logo-img { display: block; }

.navbar-brand {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: 1.05rem;
  font-weight: 700;
  color: #E6DDC6;
  text-decoration: none;
  letter-spacing: -.01em;
  flex-shrink: 1;
  /* Not 0 -- the logo img next to this text has flex-shrink:0 (its own
     fixed 34px size never shrinks), but this container's own overflow:
     hidden crops it visually once the container gets squeezed below that,
     which is exactly what a long translated nav-link row does in Tamil.
     A floor here means the "Parth" text gives up space first instead. */
  min-width: 40px;
  max-width: 38%;
  overflow: hidden;
}

.navbar-brand-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.navbar-links {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .15rem;
  list-style: none;
  margin-left: .5rem;
  flex: 1;
}

.navbar-links li { display: contents; }

.navbar-links a {
  color: rgba(255,255,255,.7);
  text-decoration: none;
  padding: .38rem .72rem;
  border-radius: var(--radius-sm);
  font-size: .85rem;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
  white-space: nowrap;
}

.navbar-links a:hover        { background: rgba(255,255,255,.1);  color: #E6DDC6; }
.navbar-links a.active       {
  background: rgba(255,255,255,.15); color: #E6DDC6; font-weight: 600;
  box-shadow: inset 0 -2px 0 0 var(--primary);
}
.navbar-links a.nav-logout   { color: rgba(255,255,255,.5); }
.navbar-links a.nav-logout:hover { background: rgba(220,38,38,.25); color: #fca5a5; }

/* Dark/light toggle — lives in the navbar, deliberately the last element so
   it sits at the extreme right regardless of how many nav links a page has. */
.theme-toggle {
  width: 32px; height: 32px; border-radius: 50%; border: 1.5px solid rgba(255,255,255,.18);
  background: transparent; display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: rgba(255,255,255,.65); transition: background .2s ease, color .2s ease;
  flex-shrink: 0; margin-left: .5rem;
}
.theme-toggle:hover { background: rgba(255,255,255,.1); color: var(--primary-muted); }
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }

/* Language switcher — same icon-button treatment as .theme-toggle, sits
   right next to it. font-family here (not globally) since the dropdown is
   the one place multiple Indic scripts render side by side; system fonts on
   Android/iOS/Windows/Mac all ship Devanagari/Tamil/Telugu/Kannada/
   Gurmukhi/Gujarati/Bengali coverage already, so no webfont needed. */
.lang-switch { position: relative; margin-left: .5rem; }
/* Groups the theme toggle + language switcher into one row instead of two
   stacked nav items -- matters most in the mobile hamburger sidebar, where
   .navbar-links li becomes a full-width block and these two small icon
   buttons would otherwise each eat a whole row of their own. */
.nav-utility-row { display: flex; align-items: center; gap: .5rem; }
.lang-switch-btn { margin-left: 0; font-weight: 700; font-size: .8rem; }
.lang-switch-dropdown {
  display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 300;
  background: var(--white); border: 1.5px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: .4rem; min-width: 150px;
  font-family: "Noto Sans", "Noto Sans Devanagari", "Noto Sans Tamil", "Noto Sans Telugu",
    "Noto Sans Kannada", "Noto Sans Gurmukhi", "Noto Sans Gujarati", "Noto Sans Bengali", sans-serif;
}
.lang-switch-dropdown.open { display: block; }
.lang-option {
  display: block; width: 100%; text-align: left; padding: .5rem .65rem; font-size: .88rem;
  background: none; border: none; border-radius: var(--radius-sm); color: var(--text);
  cursor: pointer; transition: background .12s;
}

/* Non-English compact-UI trim -- Tamil/Bengali/Gujarati/Kannada/Punjabi/
   Telugu especially run visibly longer than the English source string for
   the same short label (button text, badge, nav item, table header), and
   every one of those was sized/nowrap-tuned against English string lengths
   only. A small across-the-board reduction on just the tight, single-line
   UI chrome (never body copy/paragraphs, which can wrap freely) buys enough
   room that translated labels fit instead of clipping or forcing an
   unwanted line-wrap inside a pill-shaped button. html[lang] is set by
   i18n.js's initI18n() on every page once a language is chosen. */
html:not([lang="en"]) .btn,
html:not([lang="en"]) .badge,
html:not([lang="en"]) .navbar-links a,
html:not([lang="en"]) .lp-nav .actions .btn,
html:not([lang="en"]) .tab-btn,
html:not([lang="en"]) .chart-toggle-btn,
html:not([lang="en"]) .status-pill,
html:not([lang="en"]) .mp-month,
html:not([lang="en"]) th,
html:not([lang="en"]) .stat-label,
html:not([lang="en"]) .dcr-label,
html:not([lang="en"]) .data-card-meta {
  font-size: .9em;
}
/* Tamil's conjunct/ligature-heavy script still ran noticeably wider than
   the other seven languages even after the .9em trim above -- same selector
   list, one step smaller. Placed right after so equal-specificity + source
   order lets this win over the general rule for Tamil specifically. */
html[lang="ta"] .btn,
html[lang="ta"] .badge,
html[lang="ta"] .navbar-links a,
html[lang="ta"] .lp-nav .actions .btn,
html[lang="ta"] .tab-btn,
html[lang="ta"] .chart-toggle-btn,
html[lang="ta"] .status-pill,
html[lang="ta"] .mp-month,
html[lang="ta"] th,
html[lang="ta"] .stat-label,
html[lang="ta"] .dcr-label,
html[lang="ta"] .data-card-meta {
  font-size: .82em;
}
/* Nav links specifically need to go smaller still -- a full row of Tamil
   labels at .82em was still wide enough to squeeze the logo (see
   .navbar-brand's min-width comment) out of the space it needs. */
html[lang="ta"] .navbar-links a {
  font-size: .7em;
}
.lang-option:hover { background: var(--primary-light); color: var(--primary); }
.lang-option.active { background: var(--primary); color: #E6DDC6; font-weight: 700; }
:root[data-theme="dark"] .lang-switch-dropdown { background: #1a1108; border-color: rgba(255,255,255,.14); }

/* Hamburger button */
.nav-hamburger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  margin-left: auto;
  padding: .5rem;
  background: none;
  border: 1px solid rgba(255,255,255,.15);
  border-radius: var(--radius-sm);
  cursor: pointer;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  transition: border-color var(--transition);
}
.nav-hamburger:hover { border-color: rgba(255,255,255,.35); }
.nav-hamburger span {
  display: block;
  width: 18px;
  height: 2px;
  background: rgba(255,255,255,.85);
  border-radius: 2px;
  margin: 0 auto;
  transition: transform var(--transition), opacity var(--transition);
}

/* Mobile overlay */
.nav-overlay {
  display: none;
  position: fixed;
  inset: var(--nav-h) 0 0 0;
  background: rgba(0,0,0,.45);
  z-index: 190;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

@media (max-width: 768px) {
  .nav-hamburger { display: flex; }

  .navbar-links {
    position: fixed;
    top: var(--nav-h);
    right: 0;
    bottom: 0;
    width: min(270px, 82vw);
    flex-direction: column;
    align-items: stretch;
    /* Desktop's justify-content:flex-end (right-aligns a single row) is
       still in effect here otherwise -- in a *column* flex container that
       packs overflowing content against the BOTTOM instead of the top, so
       once there are enough tabs to overflow the tray, the top items render
       above the visible box (negative offset) and are permanently
       unreachable: scrollHeight itself comes out equal to clientHeight,
       i.e. no scroller at all, not just a hidden one. */
    justify-content: flex-start;
    gap: .2rem;
    margin: 0;
    padding: 1rem .75rem 1.5rem;
    background: var(--nav-bg);
    border-left: 1px solid rgba(255,255,255,.08);
    transform: translateX(100%);
    transition: transform .28s cubic-bezier(.4,0,.2,1);
    overflow-y: auto;
    /* app.html renders this <ul> with only its 2 hardcoded items (Logout,
       theme/lang row) first, then inserts all the real route links
       (Dashboard, Students, ...) at the very TOP once the signed-in role
       is known (renderNavLinks()'s insertAdjacentHTML('afterbegin', ...)).
       Chrome's default scroll anchoring reacts to content appearing above
       an already-visible node by silently bumping scrollTop to keep that
       node pinned in place -- which shoves the newly-inserted top links
       out of view the instant they're added, well before the user has
       scrolled anything themselves. Not a "preserve reading position"
       case worth keeping; this is a one-time mount-time fill-in, so
       anchoring here only ever hides content. */
    overflow-anchor: none;
    z-index: 200;
  }

  .navbar-links li { display: block; }
  .navbar-links a  { display: block; padding: .75rem 1rem; border-radius: var(--radius); font-size: .9rem; }
  .navbar-links a.nav-logout { margin-left: 0; margin-top: .5rem; }
  .navbar-links .nav-utility-row { padding: .5rem 1rem; }

  .navbar.open .navbar-links          { transform: translateX(0); }
  .navbar.open .nav-overlay           { display: block; }
  .navbar.open .nav-hamburger span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .navbar.open .nav-hamburger span:nth-child(2) { opacity: 0; transform: scaleX(0); }
  .navbar.open .nav-hamburger span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
}

/* ─── Layout ─────────────────────────────────────────────────────────────────── */
.page-container {
  max-width: 1100px;
  margin: 1.75rem auto;
  padding: 0 1.25rem;
  min-width: 0;
}

h1, h2, h3, h4, h5, h6,
.page-title, .card-title, .navbar-brand, .stat-value, .data-card-title {
  font-family: 'Cormorant Garamond', serif;
}

.page-title {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -.02em;
  margin-bottom: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
}
.page-title-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

/* ─── Cards ──────────────────────────────────────────────────────────────────── */
.card {
  background: rgba(255,255,255,.45);
  border-radius: var(--radius-lg);
  border: 2px solid rgba(196,124,5,.3);
  box-shadow:
    inset 0 1px 0 rgba(255,220,120,.55),
    inset 1px 0 0 rgba(255,210,100,.3),
    inset 0 -1px 0 rgba(100,55,5,.14),
    inset -1px 0 0 rgba(100,55,5,.1),
    0 0 0 1px rgba(196,124,5,.06),
    0 0 20px -4px rgba(196,124,5,.28),
    0 6px 16px rgba(100,55,5,.14);
  padding: 1.35rem 1.5rem;
  margin-bottom: 1rem;
}
:root[data-theme="dark"] .card {
  background: rgba(255,255,255,.035);
  border-color: rgba(255,255,255,.08);
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}

.card-title {
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: -.01em;
  margin-bottom: 1rem;
  padding-bottom: .6rem;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: .5rem;
}

/* ─── Stat Cards ─────────────────────────────────────────────────────────────── */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(175px, 1fr));
  gap: 1rem;
  margin-bottom: 1.25rem;
}

.stat-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  padding: 1.1rem 1.25rem;
  position: relative;
  overflow: hidden;
  transition: box-shadow var(--transition), transform var(--transition);
}

.stat-card::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.stat-card.primary::before   { background: var(--primary); }
.stat-card.success::before   { background: var(--success); }
.stat-card.warning::before   { background: var(--warning); }
.stat-card.danger::before    { background: var(--danger); }

/* Keep left-border for backward compat */
.stat-card.primary   { border-left: 4px solid var(--primary); }
.stat-card.success   { border-left: 4px solid var(--success); }
.stat-card.warning   { border-left: 4px solid var(--warning); }
.stat-card.danger    { border-left: 4px solid var(--danger); }

.stat-label { font-size: .72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }
.stat-value { font-size: 1.65rem; font-weight: 800; margin-top: .2rem; letter-spacing: -.02em; line-height: 1.2; }
.stat-sub   { font-size: .78rem; color: var(--text-muted); margin-top: .25rem; }

/* Dashboard metric cards */
.metric-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  box-shadow: var(--shadow);
  padding: 1.15rem 1.35rem;
  display: flex;
  flex-direction: column;
  gap: .2rem;
  transition: box-shadow var(--transition);
}
.metric-card:hover { box-shadow: var(--shadow-md); }
.metric-card .m-label  { font-size: .72rem; color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
.metric-card .m-value  { font-size: 1.6rem; font-weight: 800; letter-spacing: -.025em; line-height: 1.2; margin-top: .15rem; }
.metric-card .m-prev   { font-size: .78rem; color: var(--text-muted); }
.metric-card .m-change { font-size: .9rem; font-weight: 700; margin-top: .1rem; display: flex; align-items: center; gap: .25rem; }
.m-change.up   { color: var(--success); }
.m-change.down { color: var(--danger); }
.m-change.flat { color: var(--text-subtle); }

.fin-primary { border-left: 4px solid var(--primary); }
.fin-prev    { border-left: 4px solid var(--primary-muted); }
.ops-enroll  { border-left: 4px solid #6366f1; }
.ops-instr   { border-left: 4px solid #0891b2; }
.ops-att     { border-left: 4px solid #7c3aed; }

/* Dashboard section titles */
.section-title {
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin: 1.75rem 0 .75rem;
  display: flex;
  align-items: center;
  gap: .75rem;
}
.section-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ─── Bar Chart ──────────────────────────────────────────────────────────────── */
.chart-wrap {
  background: rgba(255,255,255,.45);
  border-radius: var(--radius-lg);
  border: 1px solid rgba(196,124,5,.22);
  box-shadow:
    inset 0 1px 0 rgba(255,220,120,.55),
    inset 1px 0 0 rgba(255,210,100,.3),
    inset 0 -1px 0 rgba(100,55,5,.14),
    inset -1px 0 0 rgba(100,55,5,.1),
    0 2px 8px rgba(100,55,5,.08);
  padding: 1.35rem 1.5rem;
  margin-bottom: 1rem;
}
.chart-title { font-size: .95rem; font-weight: 700; margin-bottom: 1.1rem; letter-spacing: -.01em; }
.bar-chart {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  height: 140px;
  border-bottom: 2px solid var(--border);
  overflow-x: auto;
  padding-bottom: 4px;
  scrollbar-width: thin;
}
.bar-group { display: flex; flex-direction: column; align-items: center; gap: 4px; flex: 1; min-width: 48px; }
.bars      { display: flex; align-items: flex-end; gap: 3px; height: 120px; }
.bar {
  width: 18px;
  border-radius: 4px 4px 0 0;
  min-height: 4px;
  transition: height .4s cubic-bezier(.4,0,.2,1);
}
.bar.current  { background: var(--primary); }
.bar.previous { background: var(--primary-muted); }
.bar-lbl { font-size: .68rem; color: var(--text-subtle); text-align: center; white-space: nowrap; }
.chart-legend { display: flex; gap: 1rem; margin-top: .85rem; font-size: .8rem; color: var(--text-muted); }
.legend-dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; margin-right: 4px; vertical-align: middle; }

/* ─── Tables ─────────────────────────────────────────────────────────────────── */
.table-wrap {
  overflow-x: auto;
  border-radius: var(--radius);
  border: 1px solid rgba(196,124,5,.22);
  background: rgba(255,255,255,.45);
  box-shadow:
    inset 0 1px 0 rgba(255,220,120,.55),
    inset 1px 0 0 rgba(255,210,100,.3),
    inset 0 -1px 0 rgba(100,55,5,.14),
    inset -1px 0 0 rgba(100,55,5,.1),
    0 2px 8px rgba(100,55,5,.08);
}
:root[data-theme="dark"] .table-wrap {
  background: rgba(255,255,255,.025);
  border-color: rgba(255,255,255,.08);
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}
table { width: 100%; border-collapse: collapse; background: transparent; }
thead th {
  text-align: left;
  padding: .65rem 1rem;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-weight: 700;
  color: var(--th-text);
  background: var(--th-bg);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
td {
  padding: .8rem 1rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  font-size: .9rem;
}
tr:last-child td    { border-bottom: none; }
tbody tr:nth-child(even) td { background: rgba(15,23,42,.025); }
tbody tr:hover td   { background: rgba(196,124,5,.07); }
:root[data-theme="dark"] tbody tr:nth-child(even) td { background: rgba(255,255,255,.03); }
:root[data-theme="dark"] tbody tr:hover td { background: rgba(224,160,32,.09); }
.td-muted   { color: var(--text-muted); font-size: .85rem; }
.td-amount  { font-variant-numeric: tabular-nums; font-weight: 600; font-size: .9rem; text-align: right; }
th.th-amount { text-align: right; }

/* ─── Buttons ────────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  padding: .5rem 1rem;
  border-radius: var(--radius-sm);
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: .875rem;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  letter-spacing: -.005em;
  transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease, filter 180ms ease;
  will-change: transform, box-shadow;
}

.btn:hover  { transform: translateY(-2px); }
.btn:active { transform: translateY(1px) scale(.96); filter: brightness(.9); transition: transform 80ms ease, filter 80ms ease; }
.btn:disabled { opacity: .45; cursor: not-allowed; pointer-events: none; transform: none !important; }

/* Primary → black */
.btn-primary {
  background: #0f172a;
  color: #f1f5f9;
  box-shadow: 0 2px 6px rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.2);
}
.btn-primary:hover {
  background: #1e293b;
  box-shadow: 0 8px 24px rgba(0,0,0,.45), 0 3px 8px rgba(0,0,0,.25);
}
/* Its near-black fill is nearly the same tone as the dark theme's card
   background — same camouflage problem .btn-outline had, same amber-glow fix. */
:root[data-theme="dark"] .btn-primary {
  box-shadow: 0 0 0 1px rgba(224,160,32,.3), 0 0 16px -2px var(--primary), 0 2px 6px rgba(0,0,0,.35);
}
:root[data-theme="dark"] .btn-primary:hover {
  box-shadow: 0 0 0 1px rgba(224,160,32,.45), 0 0 26px -2px var(--primary), 0 3px 8px rgba(0,0,0,.25);
}

/* Success → stays green (semantic) */
.btn-success {
  background: var(--success);
  color: #E6DDC6;
  box-shadow: 0 2px 6px rgba(5,150,105,.3), 0 1px 2px rgba(0,0,0,.15);
}
.btn-success:hover {
  box-shadow: 0 8px 22px rgba(5,150,105,.4), 0 3px 8px rgba(0,0,0,.15);
}

/* Extra-emphasis variant (e.g. Record Payment) — same darkest-green fill +
   off-white text as .badge-success/.alert-success/.toast-success, not a
   separate shade. */
.btn-success-dark {
  background: #133000;
  color: #E6DDC6;
  box-shadow: 0 2px 6px rgba(5,150,105,.3), 0 1px 2px rgba(0,0,0,.15);
}
.btn-success-dark:hover {
  background: #1c4400;
  box-shadow: 0 8px 22px rgba(5,150,105,.4), 0 3px 8px rgba(0,0,0,.15);
}
/* Same camouflage problem .btn-primary's near-black fill has against the
   dark theme's card background (see comment above it) -- same glow-ring fix,
   green instead of amber. */
:root[data-theme="dark"] .btn-success-dark {
  box-shadow: 0 0 0 1px rgba(5,150,105,.35), 0 0 16px -2px var(--success), 0 2px 6px rgba(0,0,0,.35);
}
:root[data-theme="dark"] .btn-success-dark:hover {
  box-shadow: 0 0 0 1px rgba(5,150,105,.5), 0 0 26px -2px var(--success), 0 3px 8px rgba(0,0,0,.25);
}

/* Danger → stays red (semantic), but a darker fill than --danger itself
   (that variable also drives .alert-danger/.badge-danger text and stays as
   the brighter shade there) -- same darkest-fill + off-white text pattern
   as .btn-success-dark above, red instead of green. */
.btn-danger {
  background: #6b1414;
  color: #E6DDC6;
  box-shadow: 0 2px 6px rgba(220,38,38,.3), 0 1px 2px rgba(0,0,0,.15);
}
.btn-danger:hover {
  background: #841919;
  box-shadow: 0 8px 22px rgba(220,38,38,.4), 0 3px 8px rgba(0,0,0,.15);
}
/* Same camouflage problem .btn-primary/.btn-success-dark's dark fills have
   against the dark theme's card background -- same glow-ring fix, red. */
:root[data-theme="dark"] .btn-danger {
  box-shadow: 0 0 0 1px rgba(220,38,38,.35), 0 0 16px -2px var(--danger), 0 2px 6px rgba(0,0,0,.35);
}
:root[data-theme="dark"] .btn-danger:hover {
  box-shadow: 0 0 0 1px rgba(220,38,38,.5), 0 0 26px -2px var(--danger), 0 3px 8px rgba(0,0,0,.25);
}

/* Warning */
.btn-warning {
  background: var(--warning);
  color: #E6DDC6;
  box-shadow: 0 2px 6px rgba(217,119,6,.3), 0 1px 2px rgba(0,0,0,.15);
}
.btn-warning:hover {
  box-shadow: 0 8px 22px rgba(217,119,6,.4), 0 3px 8px rgba(0,0,0,.15);
}

/* Outline */
/* Border/shadow tinted warm amber instead of flat gray/black — echoes the
   gold accent already used for labels/text-muted everywhere on the page,
   so buttons stop looking like a mismatched neutral-gray afterthought next
   to them. Dark mode's own override two rules down replaces both
   properties outright, so this is light-mode-only in effect. */
.btn-outline {
  background: var(--white);
  border: 2px solid rgba(196,124,5,.35);
  color: var(--text);
  box-shadow: 0 0 10px -3px rgba(196,124,5,.3), 0 1px 3px rgba(100,55,5,.09), 0 1px 2px rgba(100,55,5,.05);
}
.btn-outline:hover {
  background: var(--bg-hover);
  border-color: rgba(196,124,5,.6);
  box-shadow: 0 0 16px -2px rgba(196,124,5,.4), 0 6px 18px rgba(100,55,5,.14), 0 2px 6px rgba(100,55,5,.08);
}
/* Both background and border resolve to near-black in dark mode (var(--white)
   is the dark surface color there, var(--border) is barely-visible white at
   10% opacity) — on a dark card/table, an outline button all but disappears.
   A warm amber glow (matching the dashboard's existing glow language on
   .metric-card/.chart-wrap) keeps it readable instead of just camouflaged. */
:root[data-theme="dark"] .btn-outline {
  border-color: rgba(224,160,32,.4);
  box-shadow: 0 0 0 1px rgba(224,160,32,.08), 0 0 10px -3px var(--primary);
}
:root[data-theme="dark"] .btn-outline:hover {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px rgba(224,160,32,.15), 0 0 18px -2px var(--primary);
}

.btn-sm   { padding: .3rem .65rem; font-size: .8rem; border-radius: var(--radius-sm); }
.btn-lg   { padding: .8rem 1.75rem; font-size: 1rem; border-radius: var(--radius); }
/* True circle everywhere, regardless of sibling button height — every
   .btn-icon in the app is the WhatsApp button, sitting in a flex row next
   to taller text buttons (Profile, Deactivate, …). The row's default
   align-items:stretch was stretching it tall, turning the circle oval;
   align-self:center opts this one item out of that stretch. Fixed
   width+height (not padding-driven sizing) plus a raised/embossed shadow
   for a "3D" look instead of the flat outline every other .btn-outline
   gets. !important because :root[data-theme="dark"] .btn-outline's own
   box-shadow below is more specific and would otherwise win the cascade. */
.btn-icon {
  padding: 0; width: 2.2rem; height: 2.2rem; border-radius: 50%;
  flex-shrink: 0; align-self: center;
  box-shadow:
    0 3px 6px rgba(0,0,0,.18), 0 1px 2px rgba(0,0,0,.12), 0 0 8px -3px rgba(37,211,102,.4),
    inset 0 1px 1px rgba(255,255,255,.6), inset 0 -2px 2px rgba(0,0,0,.06) !important;
}
/* Chromium quirk: inside an inline-flex button, the SVG child's WIDTH (only
   the width, not height) gets shrunk by the flex algorithm to a sliver
   (~6px) even with plenty of room in the container — flex-shrink:0
   confirmed via isolated testing to be the actual fix. This, not icon
   size, was why the WhatsApp glyph kept rendering as an unrecognizable dot
   no matter how many times width/height got bumped. */
.btn-icon svg { flex-shrink: 0; }
.btn-icon:hover {
  transform: translateY(-1px);
  box-shadow:
    0 5px 10px rgba(0,0,0,.2), 0 2px 4px rgba(0,0,0,.14), 0 0 12px -2px rgba(37,211,102,.55),
    inset 0 1px 1px rgba(255,255,255,.6), inset 0 -2px 2px rgba(0,0,0,.06) !important;
}
.btn-icon:active {
  transform: translateY(0);
  box-shadow: 0 1px 2px rgba(0,0,0,.18), inset 0 2px 3px rgba(0,0,0,.12) !important;
}
:root[data-theme="dark"] .btn-icon {
  box-shadow:
    0 3px 8px rgba(0,0,0,.4), 0 0 10px -2px rgba(37,211,102,.35),
    inset 0 1px 1px rgba(255,255,255,.08), inset 0 -2px 3px rgba(0,0,0,.35) !important;
}
:root[data-theme="dark"] .btn-icon:hover {
  box-shadow:
    0 5px 12px rgba(0,0,0,.45), 0 0 16px -1px rgba(37,211,102,.5),
    inset 0 1px 1px rgba(255,255,255,.1), inset 0 -2px 3px rgba(0,0,0,.35) !important;
}

/* Every checkbox/radio in the app was the bare browser default — tiny and OS
   blue instead of the app's amber accent. accent-color re-themes the native
   check/dot without rebuilding it from scratch; size bumped to match. */
input[type="checkbox"],
input[type="radio"] {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--primary);
  cursor: pointer;
}

/* ─── Toggle switch ──────────────────────────────────────────────────────────── */
/* A real checkbox underneath (visually hidden, not display:none, so it stays
   keyboard/screen-reader accessible) styled as an on/off switch via a
   ~sibling track+thumb. Usage:
   <label class="toggle-switch">
     <input type="checkbox" id="...">
     <span class="toggle-track"></span><span class="toggle-thumb"></span>
   </label>
   No JS changes needed anywhere already reading/writing that input's
   .checked -- this only changes how it looks. */
.toggle-switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; vertical-align: middle; }
.toggle-switch input {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: pointer; z-index: 1;
}
.toggle-switch .toggle-track {
  position: absolute; inset: 0; background: var(--border); border-radius: 999px;
  transition: background var(--transition);
}
.toggle-switch .toggle-thumb {
  position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3); transition: transform var(--transition);
}
.toggle-switch input:checked ~ .toggle-track { background: var(--primary); }
.toggle-switch input:checked ~ .toggle-thumb { transform: translateX(20px); }
.toggle-switch input:focus-visible ~ .toggle-track { box-shadow: 0 0 0 3px rgba(224,160,32,.35); }

/* ─── Forms ──────────────────────────────────────────────────────────────────── */
.form-group  { margin-bottom: 1.1rem; }
.form-row    { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.form-row-3  { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; }

label {
  display: block;
  font-size: .82rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: .35rem;
  letter-spacing: .01em;
}

input[type="text"],
input[type="number"],
input[type="tel"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="datetime-local"],
select,
textarea {
  width: 100%;
  padding: .58rem .85rem;
  border: 1.5px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: .9rem;
  font-family: inherit;
  color: var(--text);
  background: var(--white);
  transition: border-color var(--transition), box-shadow var(--transition);
  appearance: none;
}

select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23c47c05' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .85rem center;
  padding-right: 2.25rem;
}

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(196,124,5,.15);
}

input::placeholder, textarea::placeholder { color: var(--text-subtle); }
.form-hint  { font-size: .78rem; color: var(--text-muted); margin-top: .3rem; }
.form-error { font-size: .78rem; color: var(--danger); margin-top: .3rem; font-weight: 500; }

/* ── TomSelect dropdown theme ──────────────────────────────────────────────
   Every page using TomSelect (via the CDN <link>/<script> pair) needs this
   to match the app's own inputs instead of the library's bare default look.
   All theme-var-driven, so light/dark adapt automatically — no separate
   dark-mode block needed here.
   !important on the visual properties is load-bearing, not decoration: the
   TomSelect CDN's own stylesheet is a <link> tag too, and on every page that
   uses it, it's declared AFTER main.css's <link> in <head> — so at equal
   specificity its bundled defaults (white background, near-black text)
   silently win the cascade and this whole block does nothing. Confirmed by
   loading the real CDN CSS (not a test stub) and inspecting computed style:
   .ts-dropdown resolved to TomSelect's own color, not var(--text). Refusing
   to depend on every page getting <link> order right forever. */
/* display:flex + the child-input rules below stop the control from growing
   taller the moment it's opened. TomSelect renders its filter <input> as a
   plain block-level child rather than replacing the shown text inline, so
   without this the input's own height stacks below the existing content
   instead of sharing the same line — confirmed by measuring .ts-control's
   box before/after opening (38.5px -> 56.5px) with the real library loaded. */
.ts-wrapper.single .ts-control { display: flex !important; align-items: center; border: 1.5px solid var(--border) !important; border-radius: var(--radius-sm); font-size: .9rem; font-family: inherit; padding: .58rem .85rem; background: var(--white) !important; color: var(--text) !important; box-shadow: none; cursor: pointer; }
.ts-wrapper.single .ts-control > input { flex: 1; min-width: 0; height: auto !important; min-height: 0 !important; padding: 0 !important; margin: 0 !important; line-height: normal !important; font-size: inherit !important; }
.ts-wrapper.single.focus .ts-control { border-color: var(--primary) !important; box-shadow: 0 0 0 3px rgba(37,99,235,.12); }
.ts-wrapper.single.input-active .ts-control { border-color: var(--primary) !important; background: var(--white) !important; color: var(--text) !important; }
.ts-dropdown { border: 1.5px solid var(--border) !important; border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); font-size: .9rem; font-family: inherit; background: var(--white) !important; color: var(--text) !important; margin-top: 4px; overflow: hidden; }
.ts-dropdown .ts-dropdown-content { max-height: 240px; }
.ts-dropdown .option { padding: .6rem 1rem; cursor: pointer; color: var(--text) !important; }
.ts-dropdown .option.active { background: var(--primary-light) !important; color: var(--primary) !important; }
.ts-dropdown .option:hover  { background: var(--primary-light) !important; color: var(--primary) !important; }
.ts-dropdown .option.selected { background: var(--primary-light) !important; color: var(--primary) !important; font-weight: 600; }
.ts-dropdown input.ts-search { border: none; border-bottom: 1.5px solid var(--border) !important; border-radius: 0; padding: .55rem 1rem; font-size: .875rem; font-family: inherit; background: var(--bg) !important; color: var(--text) !important; width: 100%; outline: none; }
.ts-dropdown input.ts-search:focus { background: var(--bg) !important; }
.ts-dropdown .no-results { padding: .75rem 1rem; color: var(--text-muted) !important; font-size: .875rem; }
.ts-wrapper .clear-button { color: var(--text-muted); }

/* ── Card list — phone alternative to a wide table ─────────────────────────
   A table with 5+ columns just doesn't fit a phone screen; scrolling it
   sideways inside .table-wrap works but is a bad experience. Pages that
   provide a card alternative render BOTH a `.table-wrap.has-mobile-cards`
   and a sibling `.data-cards` from the same data — this media query is the
   only thing that decides which is visible, so it reflows instantly on
   resize/rotation with no re-render. Add has-mobile-cards only to a
   .table-wrap that actually has a .data-cards sibling — plain .table-wrap
   elsewhere (tables that already fit fine on a phone) are unaffected. */
.data-cards { display: none; }
@media (max-width: 640px) {
  .table-wrap.has-mobile-cards, .table-scroll.has-mobile-cards { display: none; }
  .data-cards { display: flex; flex-direction: column; gap: .75rem; }
}
/* Light mode only — a warm-tinted glass edge echoing .card's bevel instead
   of a flat white box with a plain gray border. Dark mode is re-pinned
   right after to its exact prior values (var(--card-bg)/var(--border)/
   var(--shadow-sm)), unchanged. */
.data-card {
  background: rgba(255,255,255,.55);
  border: 2px solid rgba(196,124,5,.25);
  border-radius: var(--radius-lg);
  padding: 1rem;
  box-shadow:
    inset 0 1px 0 rgba(255,220,120,.5),
    0 0 16px -4px rgba(196,124,5,.25),
    0 4px 10px rgba(100,55,5,.1);
}
:root[data-theme="dark"] .data-card {
  background: var(--card-bg);
  border-color: var(--border);
  box-shadow: var(--shadow-sm);
}
.data-card-header { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .65rem; padding-bottom: .65rem; border-bottom: 1px solid var(--border); }
.data-card-identity { display: flex; align-items: center; gap: .65rem; min-width: 0; }
.data-card-title { font-weight: 700; font-size: .95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-card-meta { font-size: .85rem; color: var(--text-muted); flex-shrink: 0; text-align: right; }
.data-card-body { display: flex; flex-direction: column; }
.data-card-row { display: flex; justify-content: space-between; align-items: center; gap: .75rem; font-size: .85rem; padding: .35rem 0; border-bottom: 1px solid var(--border); }
.data-card-row:last-child { border-bottom: none; }
.data-card-row .dcr-label { color: var(--text-muted); flex-shrink: 0; }
.data-card-row .dcr-value { text-align: right; }
/* All 4 buttons (Copy Link, Availability, WhatsApp, Remove) sit in one row,
   each sized to its own content -- Remove and WhatsApp are both .btn-icon
   (compact circles) specifically so a labeled Remove doesn't blow the row's
   width budget. flex-wrap stays on as a fallback for genuinely tiny
   screens, but at normal phone widths this fits without wrapping. */
.data-card-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .75rem; }
.data-card-actions .btn-icon { flex: 0 0 auto; }

/* Date input tweaks */
input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: .55;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 14px;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
input[type="date"]::-webkit-datetime-edit-fields-wrapper { padding: 0; }
input[type="date"]::-webkit-datetime-edit { padding: 0; color: var(--text); }
input[type="date"]::-webkit-datetime-edit-year-field,
input[type="date"]::-webkit-datetime-edit-month-field,
input[type="date"]::-webkit-datetime-edit-day-field { color: var(--text); }
input[type="date"]:invalid::-webkit-datetime-edit { color: var(--text-subtle); }

/* ── Shared date / date-time picker (see shared/js/datepicker.js) ─────────
   Native <input type="date"/"datetime-local"> stays the real value store
   (nothing else on a page changes), but its own popup/segment highlighting
   is unstylable OS chrome. On mouse/desktop it's hidden and driven from this
   custom calendar instead; on touch the native input is left visible so
   phones keep their familiar sliding picker. */
.dt-field { position: relative; }
.dt-display {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: .58rem .85rem;
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--white); color: var(--text);
  font-size: .9rem; font-family: inherit; cursor: pointer;
  transition: border-color var(--transition);
}
.dt-display:hover { border-color: var(--primary-muted); }
.dt-display svg { color: var(--text-muted); flex-shrink: 0; margin-left: .5rem; }
.dt-dropdown {
  display: none; position: absolute; top: calc(100% + 6px); left: 0; z-index: 50;
  background: var(--white); border: 1.5px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: 1rem; width: 270px;
}
.dt-dropdown.open { display: block; }
.dt-cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: .6rem; }
.dt-cal-title {
  font-weight: 700; font-size: .92rem; color: var(--text);
  background: none; border: none; cursor: pointer; font-family: inherit;
  padding: .2rem .5rem; border-radius: var(--radius-sm); transition: background .12s;
}
.dt-cal-title:hover { background: var(--bg-hover); }
.dt-nav-btn {
  width: 26px; height: 26px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: none; cursor: pointer; font-size: .95rem; color: var(--text);
  display: flex; align-items: center; justify-content: center;
  transition: background .12s;
}
.dt-nav-btn:hover { background: var(--bg-hover); }
.dt-weekdays {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted); text-align: center; padding-bottom: .3rem;
}
.dt-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: .15rem; }
.dt-day {
  aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: .82rem; cursor: pointer; color: var(--text);
  transition: background .12s, color .12s;
}
.dt-day:hover { background: var(--primary-light); color: var(--primary); }
.dt-day.selected { background: var(--primary); color: #E6DDC6; font-weight: 700; }
.dt-day.today:not(.selected) { border: 1.5px solid var(--primary-muted); }
.dt-day.other-month { color: var(--text-subtle); opacity: .45; cursor: default; }
.dt-day.other-month:hover { background: none; color: var(--text-subtle); }
.dt-day.disabled { color: var(--text-subtle); opacity: .35; cursor: not-allowed; }
.dt-day.disabled:hover { background: none; color: var(--text-subtle); }

/* Month/year zoom grids — click the header title to reach these, so a
   birthdate decades back doesn't need dozens of prev-month clicks. */
.dt-months, .dt-years { display: grid; grid-template-columns: repeat(3, 1fr); gap: .3rem; }
.dt-month, .dt-year {
  display: flex; align-items: center; justify-content: center;
  padding: .55rem 0; border-radius: var(--radius-sm); font-size: .82rem; cursor: pointer; color: var(--text);
  transition: background .12s, color .12s;
}
.dt-month:hover, .dt-year:hover { background: var(--primary-light); color: var(--primary); }
.dt-month.selected, .dt-year.selected { background: var(--primary); color: #E6DDC6; font-weight: 700; }
.dt-year.other-decade { color: var(--text-subtle); opacity: .45; }
.dt-year.other-decade:hover { background: none; color: var(--text-subtle); }
.dt-month.disabled, .dt-year.disabled { color: var(--text-subtle); opacity: .35; cursor: not-allowed; }
.dt-month.disabled:hover, .dt-year.disabled:hover { background: none; color: var(--text-subtle); }
.dt-time-row {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  margin-top: .85rem; padding-top: .85rem; border-top: 1.5px solid var(--border);
}
.dt-dropdown.dt-dropdown-time-only { width: auto; }
.dt-dropdown-time-only .dt-time-row { margin-top: 0; padding-top: 0; border-top: none; }
.dt-time-group { display: flex; flex-direction: column; align-items: center; gap: .15rem; }
.dt-time-step {
  background: none; border: none; cursor: pointer; color: var(--text-muted);
  font-size: .7rem; line-height: 1; padding: .3rem; min-width: 1.8rem; min-height: 1.8rem;
  display: flex; align-items: center; justify-content: center; transition: color .12s;
}
.dt-time-step:hover { color: var(--primary); }
.dt-time-val {
  width: 2.7rem; text-align: center; border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  padding: .3rem 0; font-size: .95rem; font-weight: 700; color: var(--text);
  background: var(--white); font-family: inherit; cursor: text;
  transition: border-color var(--transition);
}
.dt-time-val:focus { outline: none; border-color: var(--primary); }
.dt-time-colon { font-weight: 700; font-size: 1.05rem; color: var(--text-muted); }
.dt-ampm { display: flex; flex-direction: column; gap: .2rem; margin-left: .3rem; }
.dt-ampm-btn {
  padding: .22rem .5rem; font-size: .68rem; font-weight: 700; font-family: inherit;
  border: 1.5px solid var(--border); border-radius: var(--radius-sm);
  background: var(--white); color: var(--text-muted); cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.dt-ampm-btn.active { background: var(--primary); color: #E6DDC6; border-color: var(--primary); }
.dt-actions { display: flex; margin-top: .85rem; }

/* Slider fallback for hour/minute, alongside (not instead of) the up/down
   step buttons above -- dragging is a lot faster than repeated taps once
   you're more than a couple steps away from the current value, especially
   on a touchscreen where each tap is a bigger, slower gesture than a mouse
   click. Coarse-pointer-only: a mouse already has the step buttons plus
   direct typing-adjacent precision, a slider there would just be clutter. */
.dt-time-slider-row { display: none; }
@media (pointer: coarse) {
  .dt-time-step { font-size: .85rem; min-width: 2.3rem; min-height: 2.3rem; }
  .dt-time-slider-row {
    display: flex; flex-direction: column; gap: .6rem;
    margin-top: .75rem; padding-top: .75rem; border-top: 1.5px solid var(--border);
  }
  .dt-time-slider-item { display: flex; align-items: center; gap: .6rem; }
  .dt-time-slider-label { font-size: .72rem; font-weight: 700; color: var(--text-muted); width: 3.2rem; flex-shrink: 0; }
  .dt-time-slider {
    flex: 1; height: 2rem; appearance: none; background: transparent; cursor: pointer;
  }
  .dt-time-slider::-webkit-slider-runnable-track {
    height: 6px; border-radius: 999px; background: var(--border);
  }
  .dt-time-slider::-webkit-slider-thumb {
    appearance: none; width: 24px; height: 24px; margin-top: -9px;
    border-radius: 50%; background: var(--primary); border: 3px solid var(--white);
    box-shadow: var(--shadow-sm);
  }
  .dt-time-slider::-moz-range-track { height: 6px; border-radius: 999px; background: var(--border); }
  .dt-time-slider::-moz-range-thumb {
    width: 24px; height: 24px; border-radius: 50%; background: var(--primary);
    border: 3px solid var(--white); box-shadow: var(--shadow-sm);
  }
}

/* Plain <input type="range"> sliders (e.g. School Profile's default session
   count) otherwise render with the browser's native blue accent -- retheme
   track+thumb to match the site's gold accent, same colors as
   .dt-time-slider above and the gold scrollbar thumb. */
input[type="range"] {
  appearance: none; -webkit-appearance: none; width: 100%; height: 1.4rem;
  background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; background: var(--border); }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 20px; height: 20px; margin-top: -7px;
  border-radius: 50%; background: var(--primary); border: 3px solid var(--white); box-shadow: var(--shadow-sm);
}
input[type="range"]::-moz-range-track { height: 6px; border-radius: 999px; background: var(--border); }
input[type="range"]::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: 50%; background: var(--primary);
  border: 3px solid var(--white); box-shadow: var(--shadow-sm);
}

/* Scrollable table block */
.table-scroll { overflow-y: auto; overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--border); background: var(--white); box-shadow: var(--shadow-sm); }
.table-scroll table { width: 100%; border-collapse: collapse; background: var(--white); }

/* Width constraints */
input[type="tel"]    { max-width: 210px; }
input[type="date"]   { max-width: 185px; }
input[type="number"] { max-width: 180px; }

.field-sm   { max-width: 160px !important; }
.field-md   { max-width: 320px !important; }
.field-full { max-width: 100% !important; }

/* ─── Badges ─────────────────────────────────────────────────────────────────── */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: .18rem .6rem;
  border-radius: var(--radius-pill);
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
/* Success/danger capsules are hardcoded, not theme-variable — the same
   darkest-green/darkest-red fill with off-white text in both light and dark
   theme, matching .alert-success/.alert-danger and .toast-success/-danger. */
.badge-success { background: #133000; color: #E6DDC6; }
.badge-danger  { background: #6f0000; color: #E6DDC6; }
.badge-warning { background: var(--warning-light); color: var(--warning); }
.badge-primary { background: var(--primary-light); color: var(--primary); }
.badge-muted   { background: var(--bg-hover); color: var(--text-muted); }

/* text-align:center matters here specifically -- a long translated status
   word (some languages run well past what the pill's padding was sized
   for) wraps to two lines rather than overflowing, and without this that
   second line reads left-aligned inside an otherwise-centered pill shape. */
.badge-active   { background: #133000; color: #E6DDC6; font-size: .7rem; padding: .18rem .6rem; border-radius: var(--radius-pill); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; display: inline-flex; align-items: center; justify-content: center; text-align: center; }
.badge-inactive { background: #6f0000; color: #E6DDC6; font-size: .7rem; padding: .18rem .6rem; border-radius: var(--radius-pill); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; display: inline-flex; align-items: center; justify-content: center; text-align: center; }

/* ─── Filters Bar ────────────────────────────────────────────────────────────── */
.filters {
  display: flex;
  gap: .75rem;
  align-items: flex-end;
  flex-wrap: wrap;
  background: rgba(255,255,255,.45);
  border-radius: var(--radius-lg);
  border: 1px solid rgba(196,124,5,.22);
  box-shadow:
    inset 0 1px 0 rgba(255,220,120,.55),
    inset 1px 0 0 rgba(255,210,100,.3),
    inset 0 -1px 0 rgba(100,55,5,.14),
    inset -1px 0 0 rgba(100,55,5,.1),
    0 2px 8px rgba(100,55,5,.08);
  padding: 1rem 1.25rem;
  margin-bottom: 1rem;
}
:root[data-theme="dark"] .filters {
  background: rgba(255,255,255,.035);
  border-color: rgba(255,255,255,.08);
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}
.filter-group { display: flex; flex-direction: column; gap: .25rem; min-width: 130px; max-width: 100%; }
.filter-group label { font-size: .75rem; font-weight: 600; color: var(--text-subtle); margin: 0; letter-spacing: .04em; text-transform: uppercase; }

/* ─── Signature Pad ──────────────────────────────────────────────────────────── */
.sig-wrap {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  /* Off-white (not stark white) in both themes -- a signature pad needs a
     light surface for the amber ink stroke to read against, but "light"
     doesn't have to mean the one pure-white surface the rest of the app
     deliberately avoids. Same cream as .badge-primary/.btn-success-dark's
     text color, used here as a fill instead. */
  background: #E6DDC6;
  position: relative;
  display: inline-block;
  transition: border-color var(--transition);
}
.sig-wrap:focus-within { border-color: var(--primary); }
.sig-canvas {
  display: block;
  touch-action: none;
  cursor: crosshair;
  border-radius: calc(var(--radius) - 2px);
}
.sig-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: .85rem;
  pointer-events: none;
  gap: .4rem;
}
.sig-clear { position: absolute; top: 8px; right: 8px; }

/* ─── Alerts ─────────────────────────────────────────────────────────────────── */
.alert {
  padding: .85rem 1.1rem;
  border-radius: var(--radius);
  font-size: .875rem;
  margin-bottom: 1rem;
  border-left: 3px solid transparent;
  line-height: 1.5;
}
.alert-info    { background: var(--primary-light); color: var(--primary-dark); border-color: var(--primary); }
/* Solid darkest-shade fill + off-white text, same treatment as
   .toast-success/.toast-danger and .btn-success-dark -- unlike alert-info/
   -warning below, this pairing doesn't need a light/dark theme override
   since it's already dark enough to hold its own text color either way. */
.alert-success { background: #133000; color: #E6DDC6; border-color: var(--success); }
.alert-danger  { background: #6f0000; color: #E6DDC6; border-color: var(--danger); }
.alert-warning { background: var(--warning-light); color: #92400e; border-color: var(--warning); }
:root[data-theme="dark"] .alert-warning { color: #fcd34d; }

/* ─── Photo ──────────────────────────────────────────────────────────────────── */
.student-photo {
  width: 90px; height: 90px;
  border-radius: var(--radius);
  object-fit: cover;
  border: 2px solid var(--border);
  background: var(--bg);
}
.school-logo, .school-logo-placeholder {
  width: 90px; height: 90px;
  border-radius: var(--radius);
  border: 2px solid var(--border);
  background: var(--white);
}
.school-logo { object-fit: contain; padding: .4rem; }
.school-logo-placeholder {
  display: flex; align-items: center; justify-content: center;
  color: var(--text-subtle); font-size: 2rem;
}
.student-photo-placeholder {
  width: 90px; height: 90px;
  border-radius: var(--radius);
  border: 2px solid var(--border);
  background: var(--bg-hover);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-subtle);
  font-size: 2rem;
}

/* ─── Letterhead (attendance/profile/receipt print views) ──────────────────── */
.letterhead {
  display: flex; align-items: center; gap: .85rem;
  padding-bottom: .85rem; margin-bottom: 1rem;
  border-bottom: 2px solid var(--border);
}
.letterhead-logo { width: 52px; height: 52px; object-fit: contain; border-radius: var(--radius-sm); flex-shrink: 0; }
.letterhead-name { font-size: 1.15rem; font-weight: 800; letter-spacing: -.01em; }
.letterhead-contact { font-size: .8rem; color: var(--text-muted); margin-top: .15rem; }
@media print {
  .letterhead { border-bottom-color: #000; }
  .letterhead-name { color: #000; }
  .letterhead-contact { color: #333; }
}

/* ─── Loading & Empty ────────────────────────────────────────────────────────── */
.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1rem;
  color: var(--text-muted);
  font-size: .9rem;
  gap: .75rem;
}
.spinner {
  width: 20px; height: 20px;
  border: 2.5px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spin .7s linear infinite;
  flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }

.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--text-muted);
}
.empty-state h3 { font-size: 1rem; margin-bottom: .35rem; color: var(--text); font-weight: 600; }
.empty-state p  { font-size: .875rem; }

/* ─── Toast ──────────────────────────────────────────────────────────────────── */
#toast-container {
  position: fixed;
  bottom: 1.5rem;
  right: 1rem;
  left: 1rem;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .5rem;
  pointer-events: none;
}
#toast-container .toast { pointer-events: auto; }
.toast {
  background: var(--nav-bg);
  color: #E6DDC6;
  padding: .8rem 1.25rem;
  border-radius: var(--radius);
  font-size: .875rem;
  font-weight: 500;
  max-width: 300px;
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(8px) scale(.97);
  transition: opacity .2s, transform .2s;
  border-left: 3px solid rgba(255,255,255,.3);
}
.toast.show           { opacity: 1; transform: none; }
.toast.toast-success  { background: #133000; border-color: var(--success); }
.toast.toast-danger   { background: #6f0000; border-color: var(--danger); }
.toast.toast-warning  { background: #78350f; border-color: var(--warning); }

/* ─── Login Page ─────────────────────────────────────────────────────────────── */
.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  background: url('/login-bg.webp') 30% center / cover no-repeat;
  padding: 18vh 5% 2vh 1.5rem;
}
.login-page::before {
  content: '';
  position: fixed;
  inset: 0;
  background:
    linear-gradient(to right, rgba(0,0,0,.05) 0%, rgba(0,0,0,.0) 45%, rgba(5,3,1,.68) 100%);
  z-index: 0;
}
@media(max-width:640px){
  .login-page {
    justify-content: center;
    padding: 1rem;
    padding-bottom: 6vh;
    background-position: 35% center;
  }
  .login-page::before {
    background: rgba(5,3,1,.6);
  }
  .role-card .label {
    font-size: .62rem !important;
    letter-spacing: .04em !important;
  }
}
/* Warm cream tint (not stark white) + a soft amber glow ring, so this reads
   as this app's own card sitting over the photo instead of a generic flat
   white box dropped on top of it -- same warm-gold language as the rest of
   the app's glowing borders, just dialed down since it sits over a busy
   photo, not a plain card grid. */
.login-card {
  position: relative;
  z-index: 1;
  background: rgba(255,252,246,.94);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-radius: var(--radius-lg);
  box-shadow:
    0 24px 70px rgba(0,0,0,.4),
    0 0 0 1px rgba(224,160,32,.22),
    0 0 42px -14px rgba(224,160,32,.4);
  padding: 2.5rem 2.25rem;
  width: 100%;
  max-width: 420px;
  border: 1px solid rgba(224,160,32,.3);
}
.login-logo { text-align: center; margin-bottom: 2rem; }
.login-logo h1 {
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--text);
  letter-spacing: -.02em;
}
.login-logo p { font-size: .875rem; color: var(--text-muted); margin-top: .35rem; }

.tab-group {
  display: flex;
  border-bottom: 2px solid var(--border);
  margin-bottom: 1.5rem;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  overflow: hidden;
}
.tab-btn {
  flex: 1;
  padding: .65rem;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: .875rem;
  font-weight: 500;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  transition: color var(--transition), border-color var(--transition);
}
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 700; }

.otp-row { display: flex; gap: .5rem; align-items: flex-end; }
.otp-row input { flex: 1; }

/* Confirm screen */
.confirm-photo {
  width: 100px; height: 100px; border-radius: 50%;
  object-fit: cover; border: 3px solid var(--primary-muted);
  display: block; margin: 0 auto 1rem;
}
.confirm-placeholder {
  width: 100px; height: 100px; border-radius: 50%;
  background: var(--bg); border: 3px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: 2.5rem; margin: 0 auto 1rem;
}
.confirm-name { text-align: center; font-size: 1.15rem; font-weight: 700; margin-bottom: .2rem; }
.confirm-role { text-align: center; font-size: .85rem; color: var(--text-muted); margin-bottom: 1.5rem; text-transform: capitalize; }

/* ─── Student Sign Page ──────────────────────────────────────────────────────── */
.sign-page { max-width: 500px; margin: 2rem auto; padding: 0 1.25rem; }
.sign-card {
  background: var(--white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border);
  padding: 1.75rem;
}
.sign-date {
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  color: var(--text-muted);
  margin-bottom: 1.5rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}
.success-card { text-align: center; padding: 2.5rem 1.5rem; }
.success-icon { font-size: 3rem; margin-bottom: .75rem; }

/* ─── Mentors split layout ────────────────────────────────────────────────── */
.mentors-layout {
  display: grid;
  /* Was 230px -- fine for "Add Instructor" in English, too narrow for the
     same label in Tamil/other longer translations, which wrapped the
     card-title mid-word instead of fitting on one or two clean lines. */
  grid-template-columns: 280px 1fr;
  gap: 1.25rem;
  align-items: start;
}
.mentor-form-card  { position: sticky; top: calc(var(--nav-h) + 1rem); }
.mentor-grid-inner { max-height: calc(100vh - 200px); overflow-y: auto; }
.search-bar { display: flex; gap: .5rem; margin-bottom: 1rem; }
.search-bar input { flex: 1; }
.td-actions { display: flex; gap: .4rem; justify-content: flex-end; flex-wrap: wrap; }


/* ─── Print ──────────────────────────────────────────────────────────────────── */
.print-only { display: none; }
@media print {
  body { background: #fff !important; font-size: 12px; }
  .navbar, .no-print, .btn, button { display: none !important; }
  .print-only { display: block !important; }
  .card  { box-shadow: none !important; border: 1px solid #ddd !important; margin-bottom: .5rem; }
  .table-wrap { box-shadow: none !important; border: 1px solid #ddd !important; }
  .page-container { margin: 0; padding: 0; max-width: 100%; }
  th, td { padding: .35rem .6rem; }
  .print-header {
    text-align: center;
    margin-bottom: 1rem;
    padding-bottom: .75rem;
    border-bottom: 2px solid #000;
  }
  .print-header h1 { font-size: 1.2rem; font-weight: 700; }
  .print-header p  { font-size: .85rem; color: #555; }
}

/* ─── Responsive ─────────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .mentors-layout { grid-template-columns: 1fr; }
  .mentor-form-card { position: static; }
  .mentor-grid-inner { max-height: none; }
}

@media (max-width: 640px) {
  .page-container { margin: 1.25rem auto; padding: 0 1rem; }
  .form-row, .form-row-3 { grid-template-columns: 1fr; }
  .stat-grid { grid-template-columns: 1fr 1fr; }
  .page-title { font-size: 1.2rem; }
  th, td { padding: .6rem .75rem; }
  .card { padding: 1rem 1.1rem; }
  .login-card { padding: 2rem 1.5rem; }

  /* On mobile, constrained inputs can go full width */
  input[type="tel"]    { max-width: 100%; }
  input[type="date"]   { max-width: 100%; }
  input[type="number"] { max-width: 100%; }

  /* THE "page zooms/pans out of place the moment I tap anything" bug —
     iOS Safari auto-zooms the whole page in when a focused input's
     font-size is under 16px, then leaves it zoomed/pannable afterwards
     ("dancing", pulling the page to a corner). Every input/select/textarea
     in the app was sized at .9rem (14.4px) or smaller. !important because
     several contexts (TomSelect's .ts-control, date inputs, etc.) set
     their own smaller font-size more specifically than this rule.
     Excludes the date-picker's small readonly hour/minute steppers —
     readonly inputs never take a keyboard and can't trigger this zoom, so
     forcing them to 16px would just make that compact widget look oversized
     for no benefit. */
  input:not([type="checkbox"]):not([type="radio"]):not([readonly]),
  select,
  textarea {
    font-size: 16px !important;
  }
  /* TomSelect's own search inputs (mark-student/mark-mentor pickers etc.)
     set font-size: inherit !important at equal specificity to the rule
     above — spelled out explicitly here instead of relying on source-order
     tie-breaking, since that's what was still triggering the iOS zoom/
     "dancing" on pages like Mark Attendance. */
  .ts-wrapper.single .ts-control > input,
  .ts-dropdown input.ts-search {
    font-size: 16px !important;
  }
}

@media (max-width: 400px) {
  .stat-grid { grid-template-columns: 1fr; }
  .btn { font-size: .82rem; padding: .45rem .85rem; }
}
