/* ─────────────────────────────────────────────────────────────────────────────
   GLOBAL RESPONSIVE BASELINE — loaded from every portal header.

   User has repeatedly flagged that pages break on phones / tablets across the
   ERP because individual modules forget to add @media collapse rules. This
   file gives EVERY page a defensive baseline so the worst regressions
   (horizontal-scrolling body, clipped tables, fixed-track grids that don't
   collapse, modals wider than the viewport) can never happen — regardless of
   what the page-level CSS does.

   Cascade: loaded LAST in every portal header, so module-specific rules
   either reinforce or override these defensively. Most rules are scoped
   under @media (max-width: ...) so desktop layouts are untouched.

   Audit / scanner: tools/responsive_check.ps1 runs in pre_push_check.ps1
   as step 6b. Catches new regressions; this baseline catches the rest.
   ─────────────────────────────────────────────────────────────────────────── */

/* ── 1. THE GOLDEN RULE: NO HORIZONTAL SCROLL ON BODY ──────────────────── */
/* The only place horizontal scroll is allowed is INSIDE a .table-wrap or
   any element that explicitly opts in (overflow-x:auto/scroll). Body /
   html / .main-area / .page-content must never produce a horizontal
   scrollbar — if they do, something inside is wider than the viewport. */
html, body {
  max-width: 100%;
  overflow-x: clip;        /* modern - same as hidden but doesn't establish containing block */
}
@supports not (overflow-x: clip) {
  html, body { overflow-x: hidden; }
}

/* ── 2. MEDIA: never overflow the container ────────────────────────────── */
img, video, iframe, embed, object, svg {
  max-width: 100%;
  height: auto;
}
/* SVG icons inside flex rows should keep their declared size — don't shrink
   them via this rule. Only loose <svg> elements get the height:auto. */
svg[width][height] { height: revert; }

/* ── 3. TABLES: respect container width ────────────────────────────────── */
table { max-width: 100%; }
/* Long pre/code blocks get a horizontal scroll instead of overflowing. */
pre, code, samp, kbd { max-width: 100%; }
pre { overflow-x: auto; }

/* The canonical wrapper for any wide admin table.
   USE: <div class="table-wrap"><table>...</table></div>
   Adds an edge-shadow on the right so users SEE there's more content
   off-screen (the "silent clip" anti-pattern user has flagged). */
.table-wrap {
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: visible;
  -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(to right, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 30px),
    radial-gradient(at right, rgba(0,0,0,.10), transparent 70%),
    linear-gradient(to left, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 30px),
    radial-gradient(at left, rgba(0,0,0,.10), transparent 70%);
  background-attachment: local, scroll, local, scroll;
  background-position: 0 0, 100% 0, 0 0, 0 0;
  background-repeat: no-repeat;
  background-size: 30px 100%, 14px 100%, 30px 100%, 14px 100%;
}

/* ── 4. FORMS: inputs never wider than container ───────────────────────── */
input, select, textarea {
  max-width: 100%;
}

/* ── 5. MODALS: never wider than viewport ──────────────────────────────── */
.modal, .modal-dialog, .modal-content, .mdl, .mdl-content,
[role="dialog"], .dialog, .popup {
  max-width: 95vw;
}

/* ── 6. RESPONSIVE COLLAPSE: ≤ 768 px (tablet portrait + phones) ───────── */
@media (max-width: 768px) {
  /* Any fixed multi-track grid collapses to a single column at this width.
     This is the safety net for modules that forgot a @media rule. Pages
     that need a different breakpoint can override at higher specificity. */
  .sm-grid, .bm-grid, .hrm-grid, .recm-grid, .acm-grid, .setm-grid,
  .bi-grid, .rpt-grid, .rpt-kpis, .rc-kpi, .rpt-kpi,
  .grid-2, .grid-3, .grid-4, .grid-auto,
  .form-row, .frm-row, .fld-row,
  .kpis, .kpi-grid, .stats-grid, .cards-grid {
    grid-template-columns: 1fr !important;
  }

  /* Filter rows / button rows / pill rows must wrap on phones. */
  .filters, .filter-row, .flt, .flt-row, .actions, .action-row,
  .btn-row, .pill-row, .chip-row, .tab-row,
  .toolbar, .topbar-actions, .form-actions, .rpt-actions {
    flex-wrap: wrap !important;
  }

  /* Buttons in those rows should stay readable, not get squished to icons. */
  .btn, .button, .btn-rpt, .btn-pri, .btn-sec, .btn-light, .btn-primary {
    white-space: nowrap;
  }

  /* Hero / banner padding scales down so titles don't wrap awkwardly. */
  .sm-hero, .bm-hero, .hrm-hero, .recm-hero, .acm-hero, .setm-hero,
  .rpt-hero, .admp-hero, .ax-hero {
    padding: 16px 18px !important;
  }
  .sm-hero h1, .bm-hero h1, .hrm-hero h1, .recm-hero h1, .acm-hero h1,
  .setm-hero h1, .rpt-hero h1, .admp-hero h1, .ax-hero h1 {
    font-size: 18px !important;
  }

  /* Any wrapper with an inline width >= 600px is a phone-killer. The
     scanner flags new occurrences; this max-width is the safety net for
     legacy debt. Wrappers with `data-no-shrink` opt out. */
  .container, .wrapper, .page-wrapper, .content-wrapper,
  .page-content, .main-area, .ax-form, .ax-card {
    max-width: 100% !important;
    box-sizing: border-box;
  }

  /* Helper: any column on a wide grid that should hide on mobile. Use:
       <th class="hide-mobile">Non-essential column</th>
       <td class="hide-mobile">...</td>                                       */
  .hide-mobile { display: none !important; }
}

/* ── 7. NARROW PHONES: ≤ 480 px (iPhone SE etc.) ───────────────────────── */
@media (max-width: 480px) {
  /* Any grid that's still 2+ columns at this width collapses to 1. */
  [style*="grid-template-columns"]:not([data-keep-cols]) {
    grid-template-columns: 1fr !important;
  }

  /* Tables with more than ~5 columns lose secondary columns. Use the
     `.hide-narrow` class on optional <th>/<td> to opt rows out. */
  .hide-narrow { display: none !important; }

  /* Tighten page padding so content gets more room. */
  .page-content { padding: 10px !important; }
}

/* ── 8. PRINT: never page-break inside small cards, force colour ──────── */
@media print {
  *, *::before, *::after {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
  html, body {
    height: auto !important;
    overflow: visible !important;
  }
  .table-wrap, .rpt-card, .sm-card, .bm-card, .hrm-card, .recm-card,
  .acm-card, .setm-card { overflow: visible !important; }
  .no-print, .sidebar, .sb-scroll, .topbar { display: none !important; }
}

/* ── 9. OPT-OUTS for legitimate exceptions ─────────────────────────────── */
/* Some elements MUST overflow (e.g. fullscreen viewer, exam paper preview).
   Tag them with .responsive-exempt to bypass all of the above. */
.responsive-exempt,
.responsive-exempt * {
  max-width: none !important;
  overflow: visible !important;
}

/* ── 10. WIDE DATA TABLES ON PHONES: scroll sideways, keep every column ───────
   User's chosen behaviour for wide admin tables on phones (2026-07-17) is
   "columns scroll sideways" — keep ALL columns, never hide or stack them. Two
   things made that feel broken / "non-responsive":
     (a) on touch the scrollbar is hidden, so a wide table looks CUT OFF and
         users don't realise it scrolls;
     (b) the row's identity scrolls out of view, so you lose track of whose
         row you're reading.
   This section fixes both for EVERY .table-wrap at once (no per-page edits):
     - a persistent slim scrollbar so it's obvious the table scrolls sideways;
     - freeze the first column when it is the row's identity (a name / roll,
       i.e. NOT a bulk-select checkbox), so it stays put while you swipe.
   Tables that lead with a checkbox keep normal scroll (freezing an empty
   checkbox column would be useless). Uses :has(); where a browser lacks it the
   whole freeze rule is simply ignored and the table still scrolls — safe. */
/* A SIDEWAYS SCROLLBAR YOU CAN ACTUALLY REACH (user, 2026-09-06: "if there is
   page width more than width of screen why there is not horizontal slider").
   .table-wrap already scrolls and the browser already draws a bar — but the bar
   is at the BOTTOM EDGE of the wrapper, and on the Practice Tests list that
   wrapper is 5,128px tall, so the bar sat 4,595px below the bottom of the
   screen. You had to scroll past all 170 rows to touch it, which is the same
   thing as not having one. Since the page itself may never scroll sideways
   (rule 1 at the top of this file), that made a third of every row unreadable.
   assets/js/table-scroll.js pairs each wide wrapper with this second bar, stuck
   to the bottom of the WINDOW while the table is on screen and wired to the
   real one in both directions. Only appears when a table is genuinely wider
   than its pane. */
.tw-bar {
  position: fixed;
  bottom: 0;
  z-index: 45;                 /* over page content, under modals (100050+) */
  display: none;
  overflow-x: auto;
  overflow-y: hidden;
  height: 15px;
  background: #eef2f7;
  border-top: 1px solid #d7dfea;
  box-shadow: 0 -2px 6px rgba(15, 23, 42, .08);
}
.tw-bar.on { display: block; }
/* The spacer is the only thing inside: it is what gives the bar a distance to
   travel, and its width is set to the table's full width. */
.tw-bar-in { height: 1px; }
.tw-bar, .table-wrap {
  scrollbar-width: thin;                   /* Firefox: slim, always-present bar */
  scrollbar-color: #94a3b8 #e8edf3;
}
.tw-bar::-webkit-scrollbar, .table-wrap::-webkit-scrollbar { height: 10px; -webkit-appearance: none; }
.tw-bar::-webkit-scrollbar-thumb, .table-wrap::-webkit-scrollbar-thumb { background: #94a3b8; border-radius: 5px; }
.tw-bar::-webkit-scrollbar-track, .table-wrap::-webkit-scrollbar-track { background: #e8edf3; border-radius: 5px; }
@media print { .tw-bar { display: none !important; } }

@media (max-width: 768px) {
  /* scroll-behavior:smooth used to be set here. It has to go: the sticky bar
     above drives the table by writing scrollLeft, and an eased scroll turns
     each of those writes into an animation the next write interrupts — dragging
     the bar at 768px moved the table 2px instead of the full 507. A sideways
     table drag should track the finger anyway, not glide after it. */
  .table-wrap { scroll-behavior: auto; }

  /* Freeze the identity first column — but only when it is NOT a select box. */
  .table-wrap table:not(:has(tbody td:first-child :is(input[type="checkbox"], input[type="radio"]))) td:first-child,
  .table-wrap table:not(:has(tbody td:first-child :is(input[type="checkbox"], input[type="radio"]))) th:first-child {
    position: sticky;
    left: 0;
    z-index: 3;
    background: #fff;
    box-shadow: 6px 0 7px -5px rgba(15, 23, 42, .26);
  }
  .table-wrap table:not(:has(tbody td:first-child :is(input[type="checkbox"], input[type="radio"]))) thead th:first-child {
    z-index: 4;
    background: #f8fafc;
  }
}

/* ── Person photos are RECTANGULAR (rounded 8px), never circles ───────────────
   Project rule (2026-06-28): a real uploaded photo must show as a rectangle.
   A person avatar slot holds a real photo as an <img>, or shows initials (plain
   text, no <img>) as a circular placeholder. :has(img) makes the slot
   rectangular ONLY when a real photo is present — initials placeholders stay
   round. One home for all the img-in-container avatars across every portal. */
.par-childcard-pic:has(img),
.par-childpill .pic:has(img),
.par-childpill-item .pic:has(img),
.pic:has(img),
.fac-avatar:has(img),
.saa-avatar:has(img) { border-radius: 8px !important; }

/* The WHOLE photo must be visible inside the rectangle — never cropped.
   object-fit:contain fits the entire image in the box (letterboxed on white),
   instead of cover which crops to fill. */
.par-childcard-pic img,
.par-childpill .pic img,
.par-childpill-item .pic img,
.pic img,
.fac-avatar img,
.saa-avatar img,
.al-photo,
.al-photo-slot img { object-fit: contain !important; background: #fff; }
