/* mobile.css — the phone rules that belong to EVERY page, in one place.
 *
 * ⭐⭐⭐ THE RULE, IN BEN'S WORDS (Jul 30 2026), AND IT IS THE SECOND VERSION — the first was wrong:
 *   "Every screen should behave like the main dashboard… it does allow you to zoom in, but it
 *    doesn't allow you to zoom out. When you try to zoom out, it brings you back to the default
 *    view. We have older members, so I don't mind them being able to zoom in, but they shouldn't be
 *    able to zoom out. Also, there should be no vertical or horizontal movement. If there is
 *    information inside a card that needs to scroll left and right horizontally, that's fine, but
 *    the screen itself should not be moving left and right horizontally."
 *
 * ⚠⚠⚠ WHAT THAT ACTUALLY ASKS FOR, AND WHY IT IS NOT A ZOOM LOCK. The main dashboard behaves the way
 * he wants for ONE reason: nothing on it is wider than the phone. When a page fits, the browser will
 * not let you zoom out past fit-to-width — it snaps back on its own. There is no code doing that.
 * ⛔ SO THE FIX FOR A DRIFTING SCREEN IS ALWAYS "MAKE IT FIT", NEVER "FORBID THE GESTURE."
 * The first version of this file forbade the gesture (`user-scalable=no`, `maximum-scale=1`,
 * `touch-action: pan-x pan-y` and a script cancelling iOS pinch events). It shipped to preview and
 * Ben rejected it: it also locks out the older members who need to magnify. ⛔ Do not reintroduce any
 * part of it. `js/no-zoom.js` was deleted; if you find a reference to it, it is stale.
 *
 * ⚠ SO PINCHING IN IS ALLOWED, AND PANNING WHILE ZOOMED IN IS CORRECT — that is how you read a
 * magnified page. "The screen should not move horizontally" means AT NORMAL SCALE. Those two are not
 * in conflict, and a report of drifting is only a bug if it happens un-zoomed.
 *
 * ⚠ THE 16px INPUT FLOOR SURVIVED THE REWRITE, FOR A DIFFERENT REASON THAN BEFORE. Under the old
 * lock it was load-bearing (iOS zooms in on a focused sub-16px field, and with pinch disabled the
 * user was trapped). Pinch is no longer disabled, so it is no longer a trap — but that auto-zoom is
 * still the screen moving on its own when all you did was tap a box, which is the thing he is
 * objecting to. Keep it.
 * ⚠ `!important` on it is deliberate, same reason the `select { padding-right }` rule in
 * dashboard.html carries it: dozens of inputs set `font-size` INLINE, and an inline style beats any
 * stylesheet selector. Without it the floor applies to about half the controls — worse than not
 * applying at all, because it looks done.
 */

html {
    -webkit-text-size-adjust: 100%;          /* iOS must not re-flow text at its own chosen size */
    text-size-adjust: 100%;
}

/* No rubber band, no pull-to-refresh. ⚠ This is OVERSCROLL, a different thing from zoom and from
   panning — it is the page bouncing past its own end. Keeping it is not in tension with letting
   people zoom in. */
html, body { overscroll-behavior: none; }

/* Nothing may make the page wider than the phone. One oversized image turns the whole app into a
   horizontally-draggable sheet, which is the "screen moving left and right" being removed.
   ⚠ DELIBERATELY NOT `svg`, `canvas` or `table`, each for its own reason: every svg here is an
   explicitly-sized icon (constraining them distorts a squeezed toolbar), a Chart.js canvas manages
   its own size and a max-width fights it, and squashing a wide table is worse than letting it
   scroll. A table that really overflows wants its own scrolling wrapper — fix that table. */
img, video, iframe { max-width: 100%; }

/* Every scroller keeps its bounce to itself. ⚠ Without this, flicking a list inside a modal scrolls
   the page behind the modal once the list hits its end — the modal appears to slide off under you.
   ⛔⛔ DO NOT ADD `-webkit-overflow-scrolling: touch` HERE. It was in the first version and Ben's
   phone got WORSE: "the actual pop-up or modal is not fixed on the screen… it's bouncing all over
   the screen." On iOS 13+ it is deprecated and already the default; on anything older it turns the
   element into its own momentum-scrolling, rubber-banding sub-surface — the exact feel being
   removed. It reads like a mobile-scrolling improvement and is the opposite. (`.menu-panel` in
   dashboard.html still sets it on itself; that is pre-existing and untouched. Do not copy it out.) */
.modal-overlay, .ev-overlay, .menu-panel, .help-body, .help-toc, .em-results, .em-sent-recip-list {
    overscroll-behavior: contain;
}

/* ⚠⚠⚠ THE MODAL MUST NEVER BE WIDER THAN THE PHONE — THIS IS THE RULE THAT MAKES "IT DRIFTS
   SIDEWAYS" IMPOSSIBLE AT NORMAL SCALE, AND IT IS THE ONE THAT IS EASY TO LOSE.
   `.modal` is a FLEX ITEM (`.modal-overlay` is `display: flex`), and a flex item's default
   `min-width: auto` resolves to its MIN-CONTENT width. So one nowrap row or one wide grid anywhere
   inside can force the modal past the viewport no matter that it says `width: 100%` — and then the
   whole overlay pans. `min-width: 0` is what lets it actually shrink to the screen.
   ⚠ Anything genuinely too wide to fit (the budget's line-item grid, the report tables) already
   scrolls INSIDE its own card via `overflow-x: auto`, which is exactly what Ben asked for:
   "if there is information inside a card that needs to scroll left and right, that's fine."
   ⛔ Do not answer a future overflow report by clipping the overlay with `overflow-x: hidden` —
   that hides content instead of fitting it, and hides the bug too. Find what is too wide and give
   THAT its own scroller.
   ⚠⚠⚠ `max-width: 100%` USED TO BE ON THIS LINE AND IT LEAKED TO THE DESKTOP (Ben, Jul 31 2026:
   "the member profile card on the desktop is now rendering to basically the whole width of the
   screen"). It was not the member modal at all — this ONE rule silently widened EVERY modal in the
   app. `:root .modal` is specificity (0,2,0) and beats `.modal { max-width: 560px }` in
   dashboard.html at (0,1,0), so it wins no matter that the page's own stylesheet is loaded later.
   ⛔ The `:root` prefix must stay for the phone rules below to work at all — the fix is to SCOPE
   the width, not to drop the prefix. `max-width: 100%` now lives in the phone media query.
   ⚠ `min-width: 0` stays global on purpose: it is the anti-drift rule described above and it has no
   effect on a desktop modal's width. */
:root .modal { min-width: 0; }

/* ⚠⚠ "TURN YOUR TELEPHONE SIDEWAYS" — injected under every module title by js/modal-close.js.
   PORTRAIT ONLY, and that is the whole design: it disappears the instant the advice is taken, and
   never appears on a desktop. A permanent banner on all 36 modules would be read once and ignored
   forever, which is worse than not having it. ⛔ Do not reduce this to a plain max-width query. */
.modal-rotate { display: none; }
@media (max-width: 640px) and (orientation: portrait) {
    /* ⚠ PLAIN TEXT, NOT A PILL (Ben, Jul 30 2026: "take it out of the gray pill… I don't like pills
       as placeholders"). ⛔ Do not give it a background, a border or a radius — it is a sentence,
       not a component.
       ⚠⚠ IT MUST READ SMALLER THAN THE MODULE DESCRIPTION BENEATH IT. Ben's words when it first
       shipped at the same 13px: make it "a little bit smaller so that it looks like a note and not
       like a description." At matching size the two lines read as one paragraph and the eye cannot
       tell which is the module's summary. 11.5px + amber makes the hierarchy obvious.
       ⚠ THE NEGATIVE TOP MARGIN IS PULLING AGAINST `.modal h2 { margin-bottom: 18px }` — it closes
       the gap to the title so the note reads as attached to it ("can we get it closer to the title
       so there's less of a gap"). ⛔ If that h2 margin ever changes, this number has to change with
       it. A `:has(+ .modal-rotate)` selector would be tidier and was rejected: `:has()` is too new
       for the older phones the Post's members actually carry. */
    :root .modal-rotate {
        display: block;
        font-size: 11.5px;
        color: #b45309;
        margin: -13px 0 9px;
    }
}

/* ⚠⚠ "IMPORTS ARE DONE ON A COMPUTER" — the three IMPORT modules only (Ben, Jul 31 2026: "can we
   just put a note on the mobile screen that import functions only work on the desktop?").
   ⚠⚠⚠ IT IS PHRASED AS AN INSTRUCTION, NOT AS A CAPABILITY CLAIM, AND THE DIFFERENCE IS LOAD-BEARING.
   The imports do NOT fail on a phone — the file pickers and the paste box all work; Ben said so
   plainly when it was raised ("it doesn't fail, but it's impractical"). So the note must not say
   "only works on a desktop" — that would be false, and this app does not make claims it cannot
   stand behind. He then asked for it direct rather than hedged ("it doesn't have to say either
   one"), which is why it reads as the Post's procedure rather than as a limitation of the software.
   ⛔ Do not "sharpen" it into "cannot" / "only works on" / "not supported on mobile".
   ⚠ Written in the MARKUP of the three modals, not injected like .modal-rotate, because it applies
   to three modules rather than all 36. ⚠ All three say the SAME sentence — do not vary it per
   module; the instruction is about where you sit, not about what that module reads.
   ⚠⚠ NOT PORTRAIT-ONLY, AND THAT IS THE DIFFERENCE FROM .modal-rotate. Rotating the phone fixes a
   cramped layout, so that note earns its portrait gate. Rotating does not conjure up a spreadsheet,
   so this one must not vanish in landscape — hence the pointer test alongside the width, which
   catches a phone held sideways (~932px on a Pro Max) and every tablet. ⛔ Do not "simplify" it to
   a bare max-width query; that is precisely the case it exists to cover. */
/* ⚠⚠ #grants-sub — the Grants module's one-line statement of the route a request takes: "Finance
   Committee → Executive Committee → Membership Vote." (Ben dropped the "Article VI, Sec. 4" citation
   on Jul 31 2026 — "that's just where the requirement comes from" — and said the type may shrink to
   keep the three stages on one row: "and that's for the phone, obviously.")
   ⚠⚠⚠ 11px IS MEASURED, NOT PICKED BY EYE, AND THE SMALLER PHONE IS WHAT SET IT. The line needs
   372.7px at the desktop 13px; the module content box is 338px on a 390px phone and 323px on a 375px
   one. 11.5px fits the 390 and WRAPS on the 375 — so 11px. ⛔ Do not nudge it up to match
   .modal-rotate's 11.5px: they are the same size by taste, not by the same constraint.
   ⚠ The `:root` prefix is required here for the reason given above — this file loads BEFORE the page's
   own <style>, where `#grants-sub { font-size: 13px }` sets the desktop size.
   ⛔ If an inline font-size ever returns to that element, this rule stops working SILENTLY. */
@media (max-width: 640px) {
    :root #grants-sub { font-size: 11px; }
}

.modal-desktop { display: none; }
@media (max-width: 900px), (hover: none) and (pointer: coarse) {
    /* ⚠ Same 11.5px amber plain text and the same negative top margin as .modal-rotate — it REPLACES
       that note on these three modals (they carry data-no-rotate-note), so it has to sit in exactly
       the same place under the title. ⛔ If .modal-rotate's margins change, change these with them. */
    :root .modal-desktop {
        display: block;
        font-size: 11.5px;
        color: #b45309;
        margin: -13px 0 9px;
    }
}

@media (max-width: 768px) {
    /* See the header note — stops iOS zooming the page when you tap a box to type. */
    input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),
    select,
    textarea { font-size: 16px !important; }
}

@media (max-width: 900px) {
    /* ⚠⚠ THE PAGE BEHIND AN OPEN MODAL IS FROZEN — this is what stops the modal "not staying fixed
       on the screen". `overscroll-behavior` stops a scroll CHAINING out of the modal, but not the
       document scrolling in its own right; and on iOS a scrolling document collapses and re-expands
       the address bar, which changes the viewport height under a `position: fixed` overlay
       mid-gesture. That is the jump. Freezing the body stops the bar moving, which holds the modal
       still.
       ⚠ `position: fixed`, not `overflow: hidden` — iOS ignores `overflow: hidden` on the body.
       ⚠⚠ THE SCROLL POSITION IS PRESERVED BY js/modal-scroll-lock.js, which stamps
       `top: -<scrollY>px` before adding this class and restores the page after. ⛔ Without that
       script this rule throws the reader to the top of the page on every modal. The two ship
       together, and the 900px here must match the media query in that file. */
    :root body.modal-open {
        position: fixed;
        left: 0;
        right: 0;
        width: 100%;
    }

    /* ⚠⚠ SCOPED HERE, NOT GLOBAL — see the long note by `:root .modal { min-width: 0 }` above.
       Left unscoped this beat every modal's own `max-width` and stretched the whole app's dialogs
       across a desktop screen. It belongs on phones, where a modal should fill what it is given. */
    :root .modal { max-width: 100%; }
}

@media (max-width: 640px) {
    /* A modal on a 375px phone was giving its content 287px: 20px of overlay padding and 24px of
       modal padding on each side. Buying back 36px is the difference between a two-column form
       fitting and spilling. Desktop is untouched.
       ⚠⚠ THE `:root` PREFIX IS NOT DECORATION AND MUST STAY. This file is linked in <head> BEFORE
       each page's own inline <style>, and a media query adds NO specificity — so a bare
       `.modal { padding }` here ties with `.modal { padding: 24px }` in dashboard.html and LOSES on
       source order. The rule would be present, correct, and doing nothing. `:root` makes it (0,2,0)
       and settles it wherever the <link> sits. ⛔ Do not "tidy" the prefix away; the failure is
       silent. */
    :root .modal-overlay { padding: 10px; }
    :root .modal { padding: 16px; margin: 12px 0; }
    /* A footer row of square buttons must wrap rather than push the modal wider than the screen. */
    :root .modal-actions { flex-wrap: wrap; row-gap: 12px; }

    /* ═══ THE TWO PATTERNS THAT WERE FIXED BY HAND IN GRANTS AND THE BUDGET, NOW APPLIED TO EVERY
       MODULE AT ONCE (Ben, Jul 30 2026: "take what you just did to the finance module budget tab
       and duplicate that across the entire app"). An audit of all 36 modules found the same two
       shapes repeated, and both are better fixed once here than in 20 places. ═══ */

    /* ① MULTI-COLUMN GRIDS WRITTEN INLINE COLLAPSE TO ONE COLUMN.
       ⚠ The substring match is deliberate and load-bearing: `[style*="grid-template-columns:1fr 1fr"]`
       also matches the 3- and 4-column grids (`…:1fr 1fr 1fr`), which need this more, not less.
       ⚠ It matches the style attribute AS AUTHORED, so it only catches INLINE grids — the ones
       written in the stylesheet (`.form-grid`, `.gr-fin-dates`, `.sched-datetime`,
       `.ro-grid.ro-pairs`) already have their own stacking rules. Checked, not assumed.
       ⛔ Keep new inline grids written WITHOUT a space after the colon or this silently misses them.
       The whole file is written that way today; it is a real constraint, not a style preference. */
    :root .modal [style*="grid-template-columns:1fr 1fr"],
    :root .ev-overlay [style*="grid-template-columns:1fr 1fr"] { grid-template-columns: 1fr !important; }

    /* ② A VIEWPORT-HEIGHT SCROLLER INSIDE A SCROLLING MODAL RELEASES ON A PHONE.
       ⚠⚠ THIS IS THE `.gr-list` FIX GENERALISED — two nested scrollers on a phone is the thing that
       reads as "bouncing": the inner list hits its end and the modal slides under your finger. On a
       phone there should be exactly ONE scroller, and it should be the modal. On desktop the vh cap
       stays, because there it is what keeps the header and footer buttons in view.
       ⚠ `[style*="vh; overflow"]` catches the inline ones (`max-height:62vh; overflow:auto`) and
       needs `!important` because an inline max-height beats any selector.
       ⛔ FOUR vh SCROLLERS ARE DELIBERATELY EXCLUDED — do not "finish the job" by adding them:
         · `.menu-panel`  — a dropdown, not modal content; uncapped it would run off the screen.
         · `.consol-modal`— that IS the dialog box, not something inside it.
         · `.lay-wrap`    — the room-layout canvas, which has its own pan/zoom model.
         · `.doc-editor`  — you TYPE in it; released, it grows to the length of the document and the
                            toolbar you are reaching for ends up thousands of pixels away. */
    :root .modal [style*="vh; overflow"],
    :root .ev-overlay [style*="vh; overflow"] { max-height: none !important; overflow: visible !important; }
    :root .help-body,
    :root .qr-list { max-height: none; overflow: visible; }

    /* ③ THE MEMBER PORTAL'S OVERLAY HAS TO ACTUALLY SCROLL — ② ABOVE ASSUMES IT ALREADY DOES.
       ⚠⚠⚠ THIS IS THE OTHER HALF OF ②, AND IT WAS MISSING FOR TWO WEEKS. ② releases the inner
       `max-height:66vh; overflow:auto` scroller on BOTH overlay families, on the stated principle
       that the MODAL should be the single scroller. That is true of the dashboard, whose
       `.modal-overlay` is `overflow-y: auto` + `align-items: flex-start`. The portal's `.ev-overlay`
       is NEITHER — `align-items: center`, no overflow at all — so on a phone ② took away the only
       scroller it had. An `.ev-modal` taller than the screen was then CENTRED, hanging off the top
       AND the bottom, with the ✕ above the viewport and the buttons below it. And
       `body.modal-open { position: fixed }` means the page behind cannot move either. Nothing on
       screen responds to anything.
       ⛔ FOUND ON THE CHILDREN & YOUTH SCREENING (Ben, Aug 12 2026, relaying a committee member on
       his phone: "it just freezes the screen. It doesn't let him do anything else"). Measured at
       492px: a 3,108px modal in a 688px viewport, the ✕ 1,204px above the top of the screen and
       "Complete This Later" 1,069px below the bottom. It is the tallest form in the portal — two
       verbatim manual chapters — and the ONLY one that opens ITSELF at sign-in, so the member met it
       with no way past. Every other `.ev-overlay` still fits a phone once released, which is why
       nothing surfaced until a committee member was appointed.
       ⚠⚠ `margin: auto 0` ON THE MODAL, NOT `align-items: center` ON THE OVERLAY — and that swap IS
       the fix, not a tidy-up. Auto margins centre a short modal exactly as before, and collapse to 0
       once it is taller than the container. `align-items: center` instead overflows the TOP of a
       scroll container, which no amount of scrolling can reach. ⛔ Do not put `align-items: center`
       back on the phone; it silently restores the unreachable half.
       ⚠ Desktop is deliberately untouched: above 640px the inner 66vh cap is still in force, which
       is what keeps the title and the ✕ in view there. ⛔ Do not "unify" the two by giving the
       desktop overlay the scroll as well.
       ⚠ The 640px here must stay equal to ②'s — they are one fix in two rules.
       ✅ Guarded by tools/test-youth-screening-mobile.mjs, which measures the real form in Edge. */
    :root .ev-overlay { align-items: flex-start; overflow-y: auto; }
    :root .ev-modal { margin: auto 0; }
}
