/* ==========================================================================
   NITRATE — mobile.css.  THE FOUNDATION EVERY OTHER MOBILE CHANGE STANDS ON.

   This file is the executable half of MOBILE_SPEC.md.  It exists because the
   site had grown 24 independently-invented breakpoints (320, 340, 379, 400,
   420, 430, 460, 470, 520, 560, 620, 640, 700, 720, 760, 820, 860, 900, 1000,
   1040, 1080, 1120, 1140, 1180, 1560).  There are now TWO below the desktop,
   and they are these:

       @media (max-width: 760px) { }    MOBILE  — phones and small tablets
       @media (max-width: 430px) { }    NARROW  — only when 760 is not enough

   ⛔ Do not add a 25th.  ⛔ Nothing above 760px may change: desktop is finished.

   LOAD ORDER.  This is the LAST <link> in index.html — after every view's
   stylesheet.  A rule here therefore beats a view's rule of EQUAL specificity.
   A view that must win over something here needs one extra class, not
   !important.  (The only CSS after this file is index.html's own inline
   <style> shell block, which styles the masthead and the nav and nothing a
   view owns.)

   Owned by the shell agent.  ⛔ Views consume these, and never redefine them.
   ========================================================================== */


/* ------------------------------------------------------------------ 1. TOKENS
   ⚠️ READ THIS BEFORE YOU USE --bp-mobile.  A custom property CANNOT be used
   in a media query — `@media (max-width: var(--bp-mobile))` is not invalid
   CSS, it is a query that never matches, and it fails SILENTLY.  The two
   --bp-* tokens are here to be read by a human and by JS (see NITRATE.isMobile
   in index.html, which is the supported way to branch on them).  In a
   stylesheet, write the literal number.
   -------------------------------------------------------------------------- */
:root {
  /* the two tiers, as documentation and for JS — NOT usable in @media */
  --bp-mobile: 760px;
  --bp-narrow: 430px;

  /* the minimum touch target.  Apple's HIG and the WCAG 2.2 target-size rule
     agree on 44: below it a thumb hits the neighbour instead. */
  --tap: 44px;

  /* the notch, the rounded corners and the home indicator.  env() is 0 in
     every desktop browser and 0 in the screenshot harness, so using these
     costs nothing anywhere they are not needed — which is why anything fixed
     or sticky that touches an edge should just use them unconditionally. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* the mobile rhythm.  Deliberately the same numbers as the house's --gap
     and --pad: a phone is the same room seen through a smaller window, not a
     second design system.  They are separate names so a mobile block reads as
     a mobile block, and so the rhythm can move on the phone alone if it ever
     has to. */
  --m-gap: 12px;
  --m-pad: 16px;
}


/* --------------------------------------------------------------- 2. UTILITIES
   Three classes, and they are the sanctioned way to do these three things.
   ⛔ Do not reinvent them locally; that is how 24 breakpoints happened.
   -------------------------------------------------------------------------- */

/* .m-hide — gone at ≤760px.
   !important is deliberate and is the ONE place this file reaches for it: the
   class has to beat the element's own `display: flex/grid/block`, which in a
   view's stylesheet usually carries more specificity than a bare utility.
   ⚠️ It hides at the MOBILE tier only.  There is no narrow-only variant, on
   purpose — if a block is not wanted at 430 it is not wanted at 760 either.
   ⛔ And per §5 of the spec: this is NOT how you drop a landing BLOCK.  Blocks
   are dropped declaratively in the BLOCKS[] registry in landing.js.  This is
   for one stray control or ornament inside a view. */
@media (max-width: 760px) {
  .m-hide { display: none !important; }
}

/* .m-only — present at ≤760px, gone above it.
   The query is the exact complement of the mobile tier.  `min-width: 761px`
   would leave the 760.0–761.0 band uncovered, and fractional viewport widths
   are real (browser zoom, Android density scaling).
   ⚠️ This class never SETS a display value — on a phone the element simply
   keeps whatever display its own CSS gives it.  So `.m-only` on a flex row
   stays a flex row; you do not have to fight a forced `display: block`. */
@media not all and (max-width: 760px) {
  .m-only { display: none !important; }
}

/* .m-scroll-x — the ONLY sanctioned way to show something wider than the
   screen (spec §4.1).  A table, a rail, a spectrum band: put it in one of
   these and it scrolls INSIDE its own box.  The page itself must never scroll
   sideways.

   Scoped to the mobile tier on purpose.  Adding overflow to an element on
   DESKTOP changes desktop — it creates a scroll container, clips descendants
   that used to hang out of the box, and can kill a `position: sticky` child.
   Confining it to ≤760 means no agent can break the finished desktop with it.

     min-width: 0    the single most common cause of a sideways page: a flex or
                     grid CHILD defaults to min-width:auto and refuses to
                     shrink below its content, so the container pushes the page
                     out instead of scrolling inside itself.
     overscroll-behavior-x: contain
                     stops a flick that reaches the end of the rail from
                     becoming the browser's back-swipe.

   ⚠️ If the thing inside SNAPS (`scroll-snap-align: start`), snapping ignores
   the container's own padding — you must also set `scroll-padding-left` to
   match that padding or the first card parks under the screen edge (spec
   §4.7).  mobile.css cannot do that for you: it does not know your number. */
@media (max-width: 760px) {
  .m-scroll-x {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    max-width: 100%;
    min-width: 0;
  }
}


/* -------------------------------------------------- 3. THE iOS ZOOM TRAP
   Safari on iOS zooms the ENTIRE PAGE in when a form control smaller than
   16px takes focus, and it does not zoom back out when the control blurs.
   The user is left on a page 1.3x too wide, scrolling sideways, for the rest
   of the session.  It is the single worst mobile defect a dense interface can
   ship, and it is invisible on desktop and in a screenshot — so it is fixed
   here, once, for every view, rather than trusted to seven agents.

   Specificity is (0,4,1) — four :not() attribute selectors plus the element —
   which beats the `.some-toolbar input { font-size: 11px }` shape this
   codebase is full of, without !important.  A view that genuinely must win
   still can; it just has to say so out loud.

   ⭐ IF YOUR CONTROL NOW LOOKS TOO BIG: size the BOX, not the text — height,
   padding, width.  Do not put the font-size back.
   Checkbox / radio / range / color are excluded: they render no text, and
   some of them size their box from the font. */
@media (max-width: 760px) {
  /* 🔴 The four :not()s on select/textarea are NOT decoration — they are the
     specificity. The bare `select, textarea` this rule shipped with scored
     (0,0,1), so ANY class outranked it, and `.nw-bar select` (12.5px),
     `.scr-select` (13px) and `.scr-sql-in` (13px) went on zooming Safari on
     focus while this rule sat here looking like it had handled it. The input
     selector above accidentally got this right — its type negations lift it to
     (0,4,1). These now match it deliberately. The attributes negated are ones a
     live control never carries, so nothing real is excluded. ⛔ Do not "tidy"
     them away. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
  select:not([hidden]):not([disabled]):not([inert]):not([aria-hidden="true"]),
  textarea:not([hidden]):not([disabled]):not([inert]):not([aria-hidden="true"]) {
    font-size: 16px;
  }
}


/* -------------------------------------------------------- 4. THE TOUCH TARGET
   ⭐ --tap has existed since the mobile sweep and reached exactly three
   stylesheets — login, quicksearch and wall.  The masthead was walked by hand
   on 2026-08-22 and brought to 8 of 8; the rest of the house never was.  A
   measured pass at 390px (/root/probes/polishaudit.js) found NINETEEN distinct
   controls under 44px, the worst a 13x13 tick inside a modal that refuses to
   close until it is ticked.

   ⛔ THIS IS A MINIMUM-BOX RULE, NOT A LAYOUT RULE.  Every selector below is
   already a native button or an align-items:center flex row, so a minimum box
   is the whole fix and nothing reflows.  A taller button is not a wider one —
   that is the masthead precedent, and it changed no layout either.
   ⛔ Nothing here escapes the mobile tier.  Desktop is finished.
   ⚠️ min-height beats a view's fixed `height` by specification, not by luck,
   so none of these needs a class of its own or an !important.
   -------------------------------------------------------------------------- */
@media (max-width: 760px) {

  /* the rail steppers — square, and the smallest controls on the page */
  .shf-arrow, .prg-arrow, .an-arrow, .hm-nudge {
    min-width: var(--tap);
    min-height: var(--tap);
  }

  /* the quick-search trigger came out of the masthead sweep at 40 wide: it
     cleared the height budget and missed the width by four pixels */
  .qs-trigger { min-width: var(--tap); }

  /* the shelf / bill / anime openers, the index chips, the discover controls */
  .shf-open, .prg-open, .an-open,
  .shf-ix, .prg-ix,
  .dsc-ask, .dsc-pick, .dsc-btn, .dsc-more,
  .hm-thr-more {
    min-height: var(--tap);
  }
  /* the two that are also too narrow for a thumb, not merely too short */
  .hm-thr-more, .dsc-more { min-width: var(--tap); }

  /* the disclosure rows.  Both are inline-flex with the native marker hidden
     and a custom ::before, so a minimum box grows the row without disturbing
     the +/- that opens it. */
  .shf-sum, .prg-sum { min-height: var(--tap); }

  /* 🔴 THE TICK, and it is the sharp one: 13x13 inside Discover's read-me
     gate, which cannot be dismissed until it is ticked.  The input already
     sits inside its own <label>, so the whole row becomes the target as soon
     as the row is tall enough — and the box grows to something a thumb can
     actually aim at. */
  .dsc-rm-tick, .dsc-season { min-height: var(--tap); }
  .dsc-rm-tick input[type="checkbox"],
  .dsc-season  input[type="checkbox"] {
    width: 22px; height: 22px; flex: 0 0 auto;
  }
}


/* ------------------------------------------------------ 5. WHAT LIVES ELSEWHERE
   Two things a view will look for and not find here:

   --shell-tabbar   the height of the bottom navigation bar, published by the
                    shell in index.html.  0px above 760, and below it the real
                    number INCLUDING the home-indicator inset.  Any view sizing
                    itself against the viewport subtracts it:
                      height: calc(100dvh - var(--scr-top) - var(--shell-tabbar));
                    Reading it is always safe — it is always defined.

   the touch rule   `@media (hover: none)` in index.html already unhides or
                    re-sizes the shared .nact controls for a finger.  A control
                    that only appears on :hover does not exist on a phone;
                    that query, not a width, is the right test for it — a phone
                    held in LANDSCAPE is 844px wide and is above the mobile
                    tier entirely.
   -------------------------------------------------------------------------- */
