/* 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. */
:root .modal { min-width: 0; max-width: 100%; }

/* ⚠⚠ "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;
    }
}

@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%;
    }
}

@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; }
}
