:root {
  --color-primary: #2B3A67;
  --color-primary-dark: #1E2A4A;
  --color-primary-light: #445785;
  --color-accent: #0EA5A0;
  --color-warn: #F2994A;
  --color-danger: #DC2626;
  --color-bg: #F4F5F7;
  --color-surface: #FFFFFF;
  --color-text: #1F2937;
  --color-text-muted: #6B7280;
  --color-border: #E6E8EC;
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --shadow-card: 0 1px 2px rgba(16,24,40,0.04), 0 2px 8px rgba(16,24,40,0.06);
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Room for a notch / gesture bar when the page itself runs edge to edge (a phone browser). */
  --safe-top: env(safe-area-inset-top);
  --safe-bottom: env(safe-area-inset-bottom);
}
/* Inside the Android app the shell already keeps the page clear of the status bar and the
   navigation bar, but the WebView still reports the phone's notch / bar size as a safe area, so the
   header got about 100px of empty colour above its row and the bottom bar a big empty margin below
   its labels: the same space counted twice. js/connectivity.js adds .app-shell in the Android app. */
html.app-shell { --safe-top: 0px; --safe-bottom: 0px; }
/*
 * Per-school theme overrides. Set with
 * `php scripts/set_school_theme.php --schid=8003 --theme=green`, which
 * writes `ui.theme` into that school's app_gateway; lib/Theme.php reads it
 * back. (It lived in a school_theme table of its own until sql/019 folded
 * every one-value-per-school table into app_gateway.)
 *
 * Adding a theme here means adding its name to Theme::VALID too — the
 * reader rejects anything not on that list, so a rule with no matching
 * constant is a block of CSS no school can ever select.
 *
 * Only the brand color trio changes —
 * --color-accent stays constant across every theme as the app's own
 * secondary color, so it still reads as a deliberate second color rather
 * than "the theme didn't apply everywhere". "yellow" uses a deep gold/amber
 * rather than a bright yellow — plain white header/button text on a bright
 * yellow background fails contrast, so amber-700 is the closest match that
 * still reads clearly as "yellow" while staying legible.
 */
:root[data-theme="red"] {
  --color-primary: #B91C1C; --color-primary-dark: #7F1D1D; --color-primary-light: #DC2626;
}
:root[data-theme="yellow"] {
  --color-primary: #A16207; --color-primary-dark: #713F12; --color-primary-light: #CA8A04;
}
:root[data-theme="teal"] {
  --color-primary: #0F766E; --color-primary-dark: #115E59; --color-primary-light: #14B8A6;
}
:root[data-theme="orange"] {
  --color-primary: #C2410C; --color-primary-dark: #9A3412; --color-primary-light: #EA580C;
}
:root[data-theme="blue"] {
  --color-primary: #1D4ED8; --color-primary-dark: #1E3A8A; --color-primary-light: #3B82F6;
}
:root[data-theme="green"] {
  --color-primary: #15803D; --color-primary-dark: #14532D; --color-primary-light: #22C55E;
}
* { box-sizing: border-box; }
html, body { margin:0; padding:0; font-family: var(--font-body); color: var(--color-text); background: var(--color-bg); }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
a, button, select, input { -webkit-tap-highlight-color: transparent; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

/* ---------- Offline banner (see js/connectivity.js) ---------- */
/* Anchored to the bottom, not the top: the top edge already carries the
   school header and the device notch, and a parent who has lost signal
   still needs to read where they are. Translated out of view rather than
   display:none so it slides rather than snaps. */
.offline-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9998;
  background: #1F2937; color: #fff; font-size: 12.5px; font-weight: 600;
  text-align: center; padding: 11px 16px calc(11px + var(--safe-bottom));
  transform: translateY(100%); transition: transform .18s ease;
}
.offline-banner.is-visible { transform: translateY(0); }

/* ---------- Navigation spinner (see js/nav-spinner.js) ---------- */
.nav-spinner-overlay {
  position:fixed; inset:0; z-index:9999; background:rgba(244,245,247,0.85);
  display:flex; align-items:center; justify-content:center;
}
.nav-spinner {
  width:36px; height:36px; border-radius:50%;
  border:4px solid var(--color-border); border-top-color: var(--color-primary);
  animation: nav-spin 0.7s linear infinite;
}
@keyframes nav-spin { to { transform: rotate(360deg); } }

/* ---------- Auth pages (login / change-password / forgot) ---------- */
.auth-wrap {
  min-height: 100vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-dark) 55%);
  padding: calc(48px + var(--safe-top)) 20px calc(32px + var(--safe-bottom)); color: #fff;
}
.auth-brand { text-align: center; margin-bottom: 36px; }
.auth-brand .logo-circle {
  width: 68px; height: 68px; border-radius: 18px; background: rgba(255,255,255,0.12);
  display: flex; align-items: center; justify-content: center; margin: 0 auto 14px;
  font-size: 28px; font-weight: 700; border: 1px solid rgba(255,255,255,0.25);
  overflow: hidden;
}
.auth-brand .logo-circle.has-image {
  width: 84px; height: 84px; background: none; border: none;
}
.auth-brand .logo-circle img { width: 100%; height: 100%; object-fit: contain; }
.auth-brand .logo-circle.has-image img { filter: drop-shadow(0 2px 8px rgba(0,0,0,0.35)); }
.auth-brand h1 { font-size: 19px; margin: 0 0 4px; font-weight: 700; }
.auth-brand p { margin: 0; font-size: 13px; opacity: 0.75; }

/* Pre-login cards share .auth-card's 380px column — left to their own
   devices they stretched the full viewport (1240px against the login
   card's 380px), which reads as a broken layout rather than a design.
   .auth-wrap paints its text white for the gradient behind it, so the
   body color has to be re-asserted here or card text is white-on-white. */
/* width:100% is load-bearing, same as .auth-card: auto margins on a flex
   item override align-items:stretch, so without it the card shrink-wraps
   to its content (253px) instead of filling the 380px column. */
.auth-wrap .card {
  width: 100%; max-width: 380px; margin-left: auto; margin-right: auto;
  color: var(--color-text); text-align: left;
}
.public-notices .notices-heading, .school-picker .notices-heading {
  font-size:12px; font-weight:700; color: var(--color-text-muted);
  text-transform:uppercase; letter-spacing:0.5px; margin:0 0 10px;
}
/* Compact search field. Type stays at 16px on purpose — iOS zooms the
   whole page when a focused input is under 16px, which is far more
   jarring than a tall box; the smaller feel comes from padding instead. */
.school-picker .field { margin-bottom: 8px; }
.school-picker .field input { padding: 9px 12px; }
.school-picker .school-pick { padding: 8px 10px; margin-bottom: 6px; font-size: 13px; }
.school-picker .school-pick .badge { width: 26px; height: 26px; font-size: 12px; border-radius: 6px; }
.school-picker .muted-note { margin: 8px 2px 0; }
.notice-item + .notice-item { border-top:1px solid var(--color-border); margin-top:10px; padding-top:10px; }
.notice-item .notice-title { font-weight:700; font-size:13.5px; margin-bottom:2px; }
.notice-item .notice-msg { font-size:12.5px; line-height:1.4; color: var(--color-text); }
.notice-item .notice-date { font-size:10.5px; color: var(--color-text-muted); margin-top:4px; }

.auth-card {
  background: var(--color-surface); color: var(--color-text);
  border-radius: var(--radius-lg); padding: 26px 22px; max-width: 380px; width: 100%; margin: 0 auto;
  box-shadow: var(--shadow-card);
}
.auth-card h2 { font-size: 16px; margin: 0 0 4px; }
.auth-card .sub { font-size: 13px; color: var(--color-text-muted); margin: 0 0 18px; }

.field { margin-bottom: 16px; }
.field label { display:block; font-size:12px; font-weight:600; color: var(--color-text-muted); margin-bottom:6px; text-transform: uppercase; letter-spacing: 0.4px; }
.field input, .field select {
  width:100%; padding:13px 14px; font-size:16px; border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm); background:#FAFAFB; color: var(--color-text);
  transition: border-color .12s ease, background-color .12s ease;
}
.field input:focus, .field select:focus { outline:none; border-color: var(--color-primary); background:#fff; }

.btn-primary {
  width:100%; padding:14px; font-size:15px; font-weight:700; background: var(--color-primary);
  color:#fff; border:none; border-radius: var(--radius-sm); cursor:pointer;
  transition: background-color .12s ease, transform .12s ease;
}
.btn-primary:active { background: var(--color-primary-dark); transform: scale(0.98); }
a.btn-primary { display:block; text-align:center; text-decoration:none; box-sizing:border-box; }
.btn-text {
  background:none; border:none; color: var(--color-primary); font-size:13px; font-weight:600;
  padding:10px; cursor:pointer; width:100%; text-align:center; display:block;
  transition: opacity .12s ease;
}
.btn-text:active { opacity:0.55; }
.form-error {
  background:#FEF2F2; color: var(--color-danger); font-size:13px; padding:10px 12px;
  border-radius: var(--radius-sm); margin-bottom:14px;
}
.form-notice {
  background:#EFF6FF; color:#1E40AF; font-size:13px; padding:10px 12px;
  border-radius: var(--radius-sm); margin-bottom:14px;
}
.school-pick {
  display:flex; align-items:center; gap:10px; padding:12px; border:1.5px solid var(--color-border);
  border-radius: var(--radius-sm); margin-bottom:10px; cursor:pointer;
  transition: background-color .12s ease, border-color .12s ease;
}
.school-pick:active { background: var(--color-bg); }
.school-pick:focus-within { border-color: var(--color-primary); }
.school-pick input { width:auto; margin-right: 4px; }
.school-pick .badge { width:34px; height:34px; border-radius:8px; background:#EEF1FB; color:var(--color-primary);
  display:flex; align-items:center; justify-content:center; font-weight:700; flex-shrink:0; }
.auth-footer { text-align:center; margin-top:22px; font-size:11px; opacity:0.6; }

/* ---------- Dashboard ---------- */
.app-header {
  background: var(--color-primary); color:#fff;
  /* Side padding grows on wide screens so the crest and Logout line up with
     the page column below instead of hugging the window edges. On a phone the
     calc goes negative and max() leaves the usual 16px. */
  padding: calc(14px + var(--safe-top)) var(--edge) 14px;
  display:flex; align-items:center; justify-content:space-between;
}
.app-header .title-wrap { display:flex; align-items:center; gap:10px; min-width:0; }
.app-header .logout-link { margin-left:10px; flex-shrink:0; }
.app-header .school-logo {
  width:36px; height:36px; border-radius:8px; background:#fff; color: var(--color-primary); overflow:hidden; padding:2px;
  display:flex; align-items:center; justify-content:center; font-weight:700; font-size:14px; flex-shrink:0;
}
.app-header .school-logo img { width:100%; height:100%; object-fit:contain; }
.app-header h2 { font-size:15px; margin:0; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.app-header .title-text { min-width:0; display:flex; flex-direction:column; justify-content:center; }
.app-header .hdr-school { font-size:11.5px; line-height:1.25; font-weight:600; opacity:.85; margin-top:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; letter-spacing:.1px; }

/* Sub-page header: a round back button at the left edge, then crest and
   title. 44px square tap target with a translucent disc so it reads as a
   button on every school colour, and sits clear of the crest. */
.app-header .back-btn {
  flex-shrink:0; width:44px; height:44px; margin:-4px 2px -4px -8px; border-radius:50%;
  display:flex; align-items:center; justify-content:center; color:#fff;
  transition: background-color .12s ease, transform .12s ease;
}
.app-header .back-btn svg { width:24px; height:24px; display:block; }
.app-header .back-btn:active { background: rgba(255,255,255,0.22); transform: scale(0.94); }
.app-header .title-wrap { flex:1; }
.app-header .back-btn + .title-wrap { margin-left:2px; }
/* Logout is a capsule button at every width. On a phone it is kept compact so the school
   name keeps its room; its visible shape is 34px tall but an invisible 44px-high hit
   area (the ::after) keeps it easy to tap, as the old plain link was. */
.app-header .logout-link {
  position: relative; display:flex; align-items:center; justify-content:center;
  min-height:34px; padding:0 14px; border-radius:999px;
  color:#fff; font-size:12.5px; font-weight:600; opacity:1;
  border:1.5px solid rgba(255,255,255,.7); background: rgba(255,255,255,.14);
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.app-header .logout-link::after { content:""; position:absolute; left:-4px; right:-4px; top:50%; height:44px; transform:translateY(-50%); }
.app-header .logout-link:active { background:#fff; color: var(--color-primary); border-color:#fff; }
@media (min-width: 768px) {
  .app-header .logout-link { min-height:38px; padding:0 18px; font-size:13px; }
  .app-header .logout-link:hover { background:#fff; color: var(--color-primary); border-color:#fff; }
}

/* A receipt number in a payment row: a tiny capsule that opens the receipt inside the app.
   The ::after is an invisible tap area so a 22px chip is still easy to hit. */
.rcpt-chip {
  position:relative; display:inline-block; padding:2px 10px; border-radius:999px; line-height:1.3;
  border:1px solid var(--color-primary); color: var(--color-primary); background: transparent;
  font-size:11px; font-weight:700; white-space:nowrap; vertical-align:baseline;
}
.rcpt-chip::after { content:""; position:absolute; left:-4px; right:-4px; top:50%; height:44px; transform:translateY(-50%); }
.rcpt-chip:active { background: var(--color-primary); color:#fff; }

.dash-pad { padding:16px; padding-bottom: calc(16px + var(--safe-bottom)); max-width: var(--page-max); margin: 0 auto 40px; }

.app-footer { text-align:center; margin-top:24px; padding-top:14px; border-top:1px solid var(--color-border); }
/* Row gap drops to 0 because each link now carries its own 44px height —
   these were ~15px tall, too small to hit reliably. */
.app-footer-links { display:flex; flex-wrap:wrap; justify-content:center; gap:0 14px; margin-bottom:8px; }
.app-footer-links a {
  font-size:11.5px; color: var(--color-text-muted); font-weight:600;
  display:inline-flex; align-items:center; min-height:44px; padding:0 2px;
}
.app-footer-links a:active { color: var(--color-primary); }
.app-footer-contact { font-size:11px; color: var(--color-text-muted); margin-bottom:4px; }
/* opacity:0.7 stacked on an already-muted colour measured 2.62:1 — worse
   than the disabled chip. Dropping the opacity brings it in line with the
   sibling footer links; the muted colour alone still reads as secondary. */
.app-footer-brand { font-size:10.5px; color: var(--color-text-muted); }

.legal-page h3 { font-size:14px; margin:18px 0 6px; color: var(--color-primary); }
.legal-page h3:first-child { margin-top:0; }
.legal-page p, .legal-page li { font-size:13px; line-height:1.6; color: var(--color-text); }
.legal-page ul { margin:6px 0; padding-left:20px; }
.legal-page .updated { font-size:11.5px; color: var(--color-text-muted); margin-bottom:16px; }
.contact-row { display:flex; justify-content:space-between; gap:12px; padding:10px 0; border-bottom:1px solid var(--color-border); font-size:13px; }
.contact-row:last-child { border-bottom:none; }
.contact-row .k { color: var(--color-text-muted); }
.contact-row .v { font-weight:600; text-align:right; }

.status-pill { display:inline-block; font-size:10px; font-weight:700; padding:2px 8px; border-radius:999px; text-transform:uppercase; letter-spacing:0.2px; flex-shrink:0; background:#E5E7EB; color:#374151; }
.status-pill.pending  { background:#FEF3C7; color:#92400E; }
.status-pill.verified { background:#DCFCE7; color:#166534; }
.status-pill.rejected { background:#FEE2E2; color:#991B1B; }

/* Scan & Pay: the school's QR and UPI ID, and the list of claims with their status. */
.pay-box { display:flex; gap:14px; align-items:flex-start; padding:12px; margin-bottom:14px; border:1px solid var(--color-border); border-radius: var(--radius-md); background:#FAFAF8; }
.pay-qr { width:132px; height:132px; flex-shrink:0; object-fit:contain; background:#fff; border-radius:8px; border:1px solid var(--color-border); }
.pay-box-text { min-width:0; flex:1; }
.pay-k { font-size:10.5px; font-weight:700; letter-spacing:.5px; text-transform:uppercase; color: var(--color-text-muted); margin-bottom:3px; }
.upi-line { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.upi-id { font-size:15px; font-weight:800; word-break:break-all; }
.copy-chip { border:1px solid var(--color-primary); color: var(--color-primary); background:transparent; border-radius:999px; padding:3px 12px; font-size:11.5px; font-weight:700; cursor:pointer; }
.copy-chip:active { background: var(--color-primary); color:#fff; }
@media (max-width: 430px) { .pay-box { flex-direction:column; align-items:center; text-align:center; } .upi-line { justify-content:center; } }
.claim-row { display:flex; justify-content:space-between; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid var(--color-border); color:inherit; text-decoration:none; }
.claim-row:last-child { border-bottom:none; }
.claim-row:active { background:#F6F6F4; }
.claim-main { display:flex; flex-direction:column; gap:2px; min-width:0; }
.claim-top { font-size:13.5px; font-weight:700; }
.claim-sub { font-size:12px; color: var(--color-text-muted); word-break:break-all; }
.claim-reason { font-size:12px; color:#991B1B; }
.field textarea { width:100%; box-sizing:border-box; padding:12px; border:1.5px solid var(--color-border); border-radius: var(--radius-sm); font:inherit; background:#FAFAFB; resize:vertical; min-height:76px; }
.field textarea:focus { outline:none; border-color: var(--color-primary); background:#fff; }

.motd { background: var(--color-surface); border-radius: var(--radius-md); padding:14px 16px; margin-bottom:14px; box-shadow: var(--shadow-card); display:flex; gap:10px; }
.motd .lbl { display:block; font-size:10.5px; font-weight:700; letter-spacing:0.5px; color: var(--color-text-muted); text-transform:uppercase; margin-bottom:2px; }
.motd .txt { font-size:13px; line-height:1.4; }

.motd-marquee { overflow:hidden; white-space:nowrap; width:100%; }
.motd-marquee-text { display:inline-block; padding-left:100%; font-size:13px; line-height:1.4; animation: motd-scroll 18s linear infinite; }
@keyframes motd-scroll {
  0%   { transform: translateX(0); }
  100% { transform: translateX(-100%); }
}
/* The marquee parks its text at padding-left:100% and depends on the
   animation to drag it into view. The global prefers-reduced-motion rule
   above stops that animation, which left the message sitting exactly at
   the clipped right edge — an empty Message of the Day box, failing
   silently for anyone with Reduce Motion switched on. Fall back to plain
   wrapped text, which is what someone asking for less motion wants
   anyway: the whole message at once instead of a 18s scroll. */
@media (prefers-reduced-motion: reduce) {
  .motd-marquee { overflow: visible; white-space: normal; }
  .motd-marquee-text { padding-left: 0; white-space: normal; animation: none; }
}

.motd.birthday { flex-direction:column; align-items:stretch; background: linear-gradient(135deg, #FFF7ED 0%, #FEF2F2 100%); border:1px solid #FED7AA; }
.bday-row { display:flex; align-items:center; gap:12px; }
.bday-row + .bday-row { margin-top:10px; }
.bday-photo { width:52px; height:52px; border-radius:50%; object-fit:cover; border:2px solid #fff; box-shadow: var(--shadow-card); flex-shrink:0; animation: bday-pulse 1.6s ease-in-out infinite; }
.bday-text { font-size:14px; font-weight:700; color:#9A3412; }
.bday-sub { font-size:12px; font-weight:500; color:#B45309; margin-top:2px; line-height:1.35; }
@keyframes bday-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}

.alert-strip { margin-bottom:14px; display:flex; flex-direction:column; gap:8px; }
.alert-item {
  display:block; background:#FFF7ED; border:1px solid #FED7AA; border-radius: var(--radius-sm);
  padding:10px 12px; font-size:12.5px; color:#9A3412; transition: transform .12s ease;
}
.alert-item.danger {
  background:#FEF2F2; border-color:#FECACA; color:#991B1B;
  animation: alert-pulse 1.8s ease-in-out infinite;
}
a.alert-item:active { transform: scale(0.98); }
@keyframes alert-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220,38,38,0.35); }
  50%      { box-shadow: 0 0 0 6px rgba(220,38,38,0); }
}

.family-strip { margin-bottom:12px; }
.family-strip .chips { display:flex; gap:8px; overflow-x:auto; padding-bottom:8px; }
.chip {
  flex-shrink:0; padding:10px 16px; border-radius:999px; font-size:13px; font-weight:600;
  background: var(--color-surface); border:1.5px solid var(--color-border); color: var(--color-text-muted);
  display:inline-flex; align-items:center; min-height:40px;
  transition: transform .12s ease, background-color .12s ease, border-color .12s ease, color .12s ease;
}
.chip:active { transform: scale(0.95); }
.chip.active { background: var(--color-primary); border-color: var(--color-primary); color:#fff; }
/* #9CA3AF on this background measured 2.31:1 — far below the 4.5 minimum,
   on a chip that is a real tappable link naming a real child, not decor.
   #5B6472 reads just as clearly "muted" at 5.45:1. */
.chip-disabled { opacity:1; color:#5B6472; background:#F3F4F6; }

/*
 * Deliberately styled as a calm, neutral card rather than a loud colored
 * banner — an early full-bleed color-fill design (esp. under a "red" school
 * theme) read as an alarm/warning rather than routine fee info, reinforcing
 * a "the school only cares about money" impression. Same visual weight as
 * any other .card on the dashboard now, not the most prominent thing on it.
 */
.due-card {
  background: var(--color-surface); border:1.5px solid var(--color-border);
  border-radius: var(--radius-md); padding:14px 16px; color: var(--color-text);
  margin-top:10px; box-shadow: var(--shadow-card);
}
.due-card .lbl { font-size:11.5px; color: var(--color-text-muted); text-transform:uppercase; letter-spacing:0.5px; font-weight:600; }
.due-card .amt { font-size:20px; font-weight:700; margin:4px 0 12px; color: var(--color-text); }
.due-card .amt.due-clear { color:#047857; }
.due-card .actions { display:flex; gap:10px; }
/* 44px minimum — these are the payment CTAs, the last controls that
   should be fiddly to hit. Was 36px. */
.due-card .actions a {
  flex:1; text-align:center; padding:9px; border-radius: var(--radius-sm); font-size:12.5px; font-weight:700;
  display:flex; align-items:center; justify-content:center; min-height:44px;
  transition: transform .12s ease, opacity .12s ease;
}
.due-card .actions a:active { transform: scale(0.96); opacity:0.85; }
.due-card .actions .pay { background: var(--color-primary); color:#fff; }
.due-card .actions .scan { background: var(--color-bg); color: var(--color-primary); border:1px solid var(--color-border); }
.due-card .actions .view { background:none; color: var(--color-primary); border:1px solid var(--color-border); }

.card {
  background: var(--color-surface); border-radius: var(--radius-md); padding:14px 16px;
  margin-bottom:10px; box-shadow: var(--shadow-card);
}
.card.fee-details { background:#EEF1FB; }

.section-lbl { font-size:12px; font-weight:700; color: var(--color-text-muted); text-transform:uppercase; letter-spacing:0.5px; margin:20px 2px 10px; }
/* minmax(0,1fr), not 1fr: a bare 1fr track keeps min-width:auto, so a
   long label ("Communication") pushes its column wider than the rest and
   every row visibly misaligns (measured 76/73/73/91px across the four). */
.feature-grid { display:grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap:10px; }
.feature-tile {
  background: var(--color-surface); border-radius: var(--radius-md); padding:12px 6px; display:flex; flex-direction:column;
  align-items:center; gap:6px; box-shadow: var(--shadow-card); text-align:center;
  transition: transform .12s ease;
}
.feature-tile:active { transform: scale(0.95); }
.feature-tile .ic {
  width:42px; height:42px; border-radius:12px; background:#EEF1FB; color: var(--color-primary);
  border: 1.5px solid var(--color-accent);
  display:flex; align-items:center; justify-content:center; font-size:19px;
}
.feature-tile .ic svg { width: 22px; height: 22px; }
.feature-tile .lbl { font-size:11px; font-weight:600; line-height:1.25; }

/* ---------- Shared child-detail-page components ---------- */
/* Used across fee/attendance/delinquency/marks/homework/profile/complaints/
   communication/performance/ai-suggestions/training — was duplicated
   per-page with drifting colors/margins before this consolidation. */
.child-name { font-size:14px; margin-bottom:8px; color: var(--color-text-muted); }
.child-name .name { font-weight:800; color: var(--color-primary); font-size:15px; }

/* Name on its own full-width line, with a right-aligned row of actions
   (badges/links) underneath — floating actions next to the name wraps the
   name itself onto two lines once the name text and the actions compete
   for the same line's width. */
.card-hdr .child-name { margin-bottom:4px; }
.card-hdr-actions { display:flex; justify-content:flex-end; align-items:center; gap:10px; }

/* Capsule/pill action link — used for small secondary navigation actions
   (Book Leave, Full Ledger, Change Password) that should catch the eye more
   than a plain text link, without competing with the page's primary button.
   Solid-filled rather than a light tint on purpose: some cards it sits on
   (e.g. .fee-details) use that same light-blue tint as their own background,
   which made a tinted pill disappear into the card instead of reading as
   a button. A solid fill contrasts against any card background. */
.action-link {
  display:inline-block; font-size:11px; font-weight:700; color:#fff; white-space:nowrap;
  background: var(--color-primary); padding:5px 12px; border-radius:999px;
  transition: transform .12s ease, opacity .12s ease;
}
.action-link:active { transform: scale(0.95); opacity:0.85; }

.payments-heading {
  font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px;
  color: var(--color-accent); margin-bottom:10px;
}

.detail-box {
  border:1.5px solid var(--color-border); border-radius: var(--radius-md); padding:8px 8px 2px;
  margin-bottom:14px; background: var(--color-bg);
}
.detail-box .card + .card { margin-top:24px; }
/* fee.php groups each child's cards in one column so the grid below pairs children, not cards. */
.detail-box > .child-col + .child-col { margin-top:24px; }

.filter-bar { display:flex; gap:10px; margin:12px 0; }
.filter-bar select {
  flex:1; padding:10px 12px; font-size:13.5px; border:1.5px solid var(--color-border);
  border-radius: var(--radius-sm); background:#FAFAFB; color: var(--color-text);
}

.muted-note { font-size:12.5px; color: var(--color-text-muted); margin:0; line-height:1.4; }

.empty-state { text-align:center; padding:26px 12px; color: var(--color-text-muted); }
.empty-state p { font-size:12.5px; margin:0; line-height:1.4; }

/* ---------- Home dashboard (dashboard.php) ---------- */
/* Status colours live here rather than reusing --color-danger/--color-warn:
   those sit next to the brand colour (a red school theme would make "all
   fine" and "overdue" the same colour family), and these need guaranteed
   contrast as text on a tinted pill. Each fg/bg pair is >= 7:1. */
:root {
  --ok-bg: #DCFCE7;   --ok-fg: #166534;   --ok-dot: #16A34A;
  --warn-bg: #FFEDD5; --warn-fg: #9A3412; --warn-dot: #F97316;
  --bad-bg: #FEE2E2;  --bad-fg: #991B1B;  --bad-dot: #DC2626;
}

body.has-bottom-nav { padding-bottom: calc(64px + var(--safe-bottom)); }

.hero {
  /* Starts on the header's own colour so the two read as one surface. */
  background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-dark) 130%);
  color: #fff; padding: 6px var(--edge) 52px; border-radius: 0 0 26px 26px;
}
.hero-inner { margin: 0; }
.hero-greet { font-size: 12.5px; font-weight: 600; opacity: .85; letter-spacing: .3px; }
.hero-title { font-size: 21px; line-height: 1.25; font-weight: 800; margin: 4px 0 0; }
.hero-motd {
  margin-top: 14px; background: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.22);
  border-radius: 12px; padding: 9px 12px;
}
.hero-motd .lbl { display: block; font-size: 10px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; opacity: .8; margin-bottom: 2px; }
.hero-motd .motd-marquee-text { color: #fff; font-weight: 600; }

/* The hero's bottom padding is the overlap: the first card rides up over it. */
.dash-pad-hero { margin-top: -34px; position: relative; }

.fee-hero {
  background: var(--color-surface); border-radius: var(--radius-md); padding: 14px 16px;
  margin-bottom: 14px; box-shadow: var(--shadow-card); border-left: 5px solid var(--ok-dot);
}
.fee-hero.fee-warn { border-left-color: var(--warn-dot); }
.fee-hero.fee-bad  { border-left-color: var(--bad-dot); }
.fee-hero-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.fee-hero .lbl { font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; color: var(--color-text-muted); }
.fee-hero .amt { font-size: 24px; font-weight: 800; margin: 2px 0 12px; }
.fee-hero .amt-ok { font-size: 17px; color: var(--ok-fg); }
.fee-hero .actions { display: flex; gap: 10px; }
.fee-hero .actions a {
  flex: 1; text-align: center; border-radius: var(--radius-sm); font-size: 13px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; min-height: 44px;
  transition: transform .12s ease, opacity .12s ease;
}
.fee-hero .actions a:active { transform: scale(.96); opacity: .85; }
.fee-hero .actions .pay  { background: var(--color-primary); color: #fff; }
.fee-hero .actions .scan { background: var(--color-bg); color: var(--color-primary); border: 1px solid var(--color-border); }
.fee-hero .actions .view { background: none; color: var(--color-primary); border: 1px solid var(--color-border); }

/* Status pill: the dot is decoration — the words carry the meaning, so the
   state is never conveyed by colour alone. */
.pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px;
  font-size: 12.5px; font-weight: 700; white-space: nowrap;
}
.pill .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.pill-ok   { background: var(--ok-bg);   color: var(--ok-fg); }   .pill-ok .dot   { background: var(--ok-dot); }
.pill-warn { background: var(--warn-bg); color: var(--warn-fg); } .pill-warn .dot { background: var(--warn-dot); }
.pill-bad  { background: var(--bad-bg);  color: var(--bad-fg); }  .pill-bad .dot  { background: var(--bad-dot); }

.section-lbl .count {
  display: inline-block; margin-left: 4px; min-width: 20px; padding: 0 6px; border-radius: 999px;
  background: var(--color-border); color: var(--color-text-muted); font-size: 11px; text-align: center; line-height: 18px;
}

.kid-card {
  display: block; background: var(--color-surface); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card); margin-bottom: 14px; padding: 14px 16px 0; overflow: hidden;
}
.kid-head { display: flex; align-items: center; gap: 12px; padding-bottom: 10px; }
.kid-avatar {
  position: relative; width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0; overflow: hidden;
  background: #EEF1FB; color: var(--color-primary); border: 2px solid var(--color-primary);
  display: flex; align-items: center; justify-content: center; font-size: 19px; font-weight: 800;
}
/* The photo covers the initial; onerror removes it, so a child without a
   photo on disk (the common case) just shows their initial. */
.kid-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.kid-id { flex: 1; min-width: 0; }
.kid-id h3 { margin: 0; font-size: 16px; font-weight: 800; line-height: 1.25; overflow-wrap: anywhere; }
.kid-sub { font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }

.today-chip { flex-shrink: 0; font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px; }
.today-ok   { background: var(--ok-bg);   color: var(--ok-fg); }
.today-warn { background: var(--warn-bg); color: var(--warn-fg); }
.today-none { background: #F3F4F6; color: #5B6472; }

.kid-rows { border-top: 1px solid var(--color-border); }
.kid-row {
  display: flex; align-items: center; gap: 10px; min-height: 60px; padding: 10px 0;
  border-bottom: 1px solid var(--color-border); transition: background-color .12s ease;
}
.kid-row:active { background: var(--color-bg); }
.kid-row-main { flex: 1; min-width: 0; }
.kid-row-lbl { font-size: 10.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; color: var(--color-text-muted); }
.kid-row-val { font-size: 18px; font-weight: 800; margin-top: 1px; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; }
.kid-row-val.muted { font-size: 13.5px; font-weight: 600; color: var(--color-text-muted); }
.kid-row-note { font-size: 11.5px; color: var(--color-text-muted); margin-top: 1px; }
.kid-row-go { width: 18px; height: 18px; color: #6B7280; flex-shrink: 0; }
.kid-row-go svg { width: 18px; height: 18px; display: block; }
/* A school remark, kept calm on purpose: a pulsing red banner per child made
   the home page read as an alarm. Still tinted and labelled so it isn't missed. */
.kid-alert {
  display: block; margin: 12px 0 0; padding: 9px 12px; border-radius: var(--radius-sm);
  background: var(--bad-bg); color: var(--bad-fg); font-size: 12.5px; line-height: 1.4;
}
.kid-alert-lbl { display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; opacity: .85; margin-bottom: 1px; }
.kid-alert:active { opacity: .75; }
.kid-more {
  display: flex; align-items: center; justify-content: center; gap: 2px; min-height: 46px;
  font-size: 13px; font-weight: 700; color: var(--color-primary);
}
.kid-more svg { width: 16px; height: 16px; }
.kid-more:active { opacity: .6; }

.kid-inactive { background: #F3F4F6; padding-bottom: 14px; box-shadow: none; border: 1px solid var(--color-border); }
.kid-inactive .kid-avatar { border-color: #9AA3AF; color: #5B6472; background: #E5E7EB; }
.kid-inactive .kid-head { padding-bottom: 0; }

.trend { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; font-weight: 700; }
.trend svg { width: 14px; height: 14px; }
.trend-up   { color: #15803D; }
.trend-down { color: #B91C1C; }
.trend-flat { color: #5B6472; }
.trend-none { color: var(--color-text-muted); font-weight: 600; }

.spark-wrap { position: relative; display: block; width: 92px; height: 36px; flex-shrink: 0; }
.spark { display: block; width: 100%; height: 100%; overflow: visible; }
.spark-up   { color: #15803D; }
.spark-down { color: #B91C1C; }
.spark-flat { color: #5B6472; }
.spark-dot {
  position: absolute; width: 10px; height: 10px; border-radius: 50%; background: currentColor;
  border: 2px solid #fff; transform: translate(-50%, -50%); box-shadow: 0 0 0 1px currentColor;
}

.meter { display: block; width: 84px; height: 8px; border-radius: 999px; background: #E5E7EB; overflow: hidden; flex-shrink: 0; }
.meter i { display: block; height: 100%; border-radius: 999px; }
.meter-ok   { background: var(--ok-dot); }
.meter-warn { background: var(--warn-dot); }
.meter-bad  { background: var(--bad-dot); }

.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 100; display: flex;
  /* Same colour as the header, so the two bars frame the page. Both labels are
     plain white (a translucent white fails contrast on the lighter school
     colours); the current tab is marked by a lit pill and a heavier weight,
     not by colour alone. */
  background: var(--color-primary);
  padding-bottom: var(--safe-bottom); box-shadow: 0 -2px 10px rgba(16,24,40,.12);
}
.bottom-nav a {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 56px; font-size: 11.5px; font-weight: 600; color: #fff;
  transition: background-color .12s ease;
}
.bottom-nav a svg { width: 22px; height: 22px; }
.bottom-nav a.active { font-weight: 800; background: rgba(255,255,255,.18); box-shadow: inset 0 3px 0 #fff; }
.bottom-nav a:active { background: rgba(255,255,255,.28); }

/* ---------- Layout by screen width ----------
   Width, not device sniffing: it also covers a narrow desktop window, a
   tablet, a rotated phone, and the Cordova app (which is just a phone-width
   viewport). Phones keep the single 640px column untouched.

   On a larger screen the page uses the room instead of sitting in a column:
     - one card per child, side by side                                 .detail-box
     - a list inside a single card flows into columns                   body.list-cols
     - home / menu / learning / training use tile grids                 body.page-wide
     - a form (change password) stays a narrow column: a 1700px-wide text
       field is harder to use, not easier                               body.page-narrow
   Only very wide monitors hit the 1760px cap; below that the page is full width. */
:root { --page-max: 640px; }
/* Declared on body, not :root: a var() inside a custom property is resolved
   where the property is declared, so on :root it would never see the wider
   --page-max that a body class sets. */
body { --edge: max(16px, calc((100% - var(--page-max)) / 2 + 16px)); }

.kid-grid { display: block; }

@media (min-width: 768px) {
  :root { --page-max: 1760px; }
  body.page-narrow { --page-max: 640px; }
  .dash-pad { padding-left: 24px; padding-right: 24px; }

  /* The chip row scrolls sideways on a phone; with room to spare it should wrap. */
  .family-strip .chips { flex-wrap: wrap; overflow: visible; }
  .filter-bar select { flex: 0 1 280px; }

  /* One card per child, side by side. */
  .detail-box {
    display: grid; gap: 16px; align-items: start; padding: 12px 12px 4px;
    grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  }
  .detail-box > .card, .detail-box > .child-col { width: 100%; margin-bottom: 12px; }
  .detail-box .card + .card { margin-top: 0; }
  .detail-box > .child-col + .child-col { margin-top: 0; }

  body.page-wide .kid-grid {
    display: grid; gap: 16px; align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  }
  body.page-wide .kid-card { margin-bottom: 0; }
  body.page-wide .feature-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 14px; }
  body.page-wide .feature-tile { padding: 18px 8px; }
  .hero-title { font-size: 26px; }

  /* Fee summary: amount on the left, the two pay buttons on the right. */
  .fee-hero { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
  .fee-hero .fee-hero-top { flex: 1; }
  .fee-hero .actions { flex: 0 0 min(420px, 45%); }
  .fee-hero .amt { margin-bottom: 0; }

  /* Fee ledger: each fee session is a block; let them sit side by side. */
  .ledger-grid { display: grid; gap: 16px; align-items: start; grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); }
  .ledger-grid .ledger-session { margin-bottom: 0; }
}

/* A list inside one card (attendance days, messages, remarks, exams, holidays):
   run it in columns so a lone child's card doesn't become a 1500px-long strip
   of rows with the amount at the far edge. Headings and any table span every
   column; rows are never split across two. Multi-child views already get one
   card per column, so only a card that is alone is flowed. */
@media (min-width: 1000px) {
  body.list-cols .detail-box > .card:only-child,
  body.list-cols .col-list {
    column-width: 430px; column-gap: 40px;
  }
  body.list-cols .detail-box > .card:only-child > *,
  body.list-cols .col-list > * { break-inside: avoid; }
  body.list-cols .col-list > :is(.child-name, .span-all) { column-span: all; }
  body.list-cols .detail-box > .card:only-child > :is(.child-name, .card-hdr, .span-all, .mk-notice, .muted-note, .payments-heading) {
    column-span: all;
  }
  /* Legal text reads best as two columns on a wide page. */
  .legal-page { column-width: 520px; column-gap: 56px; }
  .legal-page h3 { break-after: avoid; }
  .legal-page > .updated { column-span: all; }
}

@media (min-width: 1500px) {
  body.page-wide .feature-grid { grid-template-columns: repeat(10, minmax(0, 1fr)); }
}
@media (min-width: 1100px) and (max-width: 1499px) {
  body.page-wide .feature-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

/* A full-width bar stretched across a 1400px window looks like a mistake;
   on a wide screen it becomes a compact pill, centred and still fixed. */
@media (min-width: 900px) {
  .bottom-nav {
    left: 50%; right: auto; width: 320px; transform: translateX(-50%);
    bottom: 18px; border-radius: 999px; overflow: hidden; padding-bottom: 0;
  }
  body.has-bottom-nav { padding-bottom: 96px; }
}

/* A child's card responds to its own width: when it is wide (a family with one
   or two children on a big screen), the three summaries sit side by side
   instead of stacking in a strip. Where container queries aren't supported
   the rows simply stay stacked. */
.kid-card { container-type: inline-size; }
@container (min-width: 620px) {
  .kid-rows { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 8px; }
  .kid-row { border-bottom: 0; padding: 14px 8px; border-radius: 10px; }
  .kid-row + .kid-row { border-left: 1px solid var(--color-border); border-radius: 0; }
}

/* ---------- Public policy pages (/privacy.php, /terms.php, /refund-policy.php) ---------- */
.policy-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.policy-tabs a {
  padding: 10px 16px; border-radius: 999px; font-size: 13px; font-weight: 600; min-height: 40px;
  display: inline-flex; align-items: center;
  background: var(--color-surface); border: 1.5px solid var(--color-border); color: var(--color-text-muted);
}
.policy-tabs a.active { background: var(--color-primary); border-color: var(--color-primary); color: #fff; }
.policy-find { margin-bottom: 14px; }
.policy-find-title { font-size: 14px; font-weight: 800; margin-bottom: 4px; }
.policy-find input {
  width: 100%; margin-top: 10px; padding: 12px 14px; font-size: 16px; border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm); background: #FAFAFB; color: var(--color-text);
}
.policy-school { display: block; padding: 12px 4px; border-bottom: 1px solid var(--color-border); font-size: 14px; font-weight: 600; color: var(--color-primary); }
.policy-school:last-child { border-bottom: 0; }
.legal-page a { color: var(--color-primary); font-weight: 600; text-decoration: underline; }
.policy-public .app-header .logout-link { opacity: 1; }
@media (min-width: 768px) { body.policy-public { --page-max: 1100px; } }

.auth-policies { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 16px; margin-bottom: 6px; }
.auth-policies a { color: #fff; opacity: .9; font-size: 12px; font-weight: 600; text-decoration: underline; min-height: 40px; display: inline-flex; align-items: center; }

/* ---------- School name heading (lib/SchoolName.php) ----------
   Two tiers: the part that identifies the school, big; the generic tail
   ("Public School", "Classes") small, in capitals. Each tier is one line. The
   five size steps are picked from the length of the big part, and
   js/fit-name.js shrinks it further if the room is still too small. The tail
   is near-white rather than dimmed: a 70% tint fails contrast on the Gold,
   Orange and Green themes, and these are 10px letters. */
.app-header h2.school-name {
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
  min-width: 0; flex: 1; font-weight: 800; line-height: 1.12;
}
.school-name .sn-main { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: -.1px; }
.school-name .sn-xl { font-size: 26px; }
.school-name .sn-l  { font-size: 24px; }
.school-name .sn-m  { font-size: 21px; }
.school-name .sn-s  { font-size: 18px; }
.school-name .sn-xs { font-size: 16px; }
.school-name .sn-tail {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 11.5px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: rgba(255,255,255,.95);
}
@media (min-width: 768px) {
  .school-name .sn-xl { font-size: 30px; } .school-name .sn-l { font-size: 28px; }
  .school-name .sn-m { font-size: 25px; }  .school-name .sn-s { font-size: 22px; }
  .school-name .sn-xs { font-size: 19px; } .school-name .sn-tail { font-size: 13px; }
}
