/* ==================== RESPONSIVE SYSTEM ====================================
   Loaded last, after every feature stylesheet. Two jobs:

     1. publish ONE breakpoint scale, so new work stops inventing widths;
     2. hold the cross-device guards that are nobody's feature in particular —
        fluid gutters, notch insets, short/landscape viewports, and overlays
        that are taller than the screen.

   ---------------------------------------------------------------------------
   THE SCALE

   Before this file the stylesheets between them used 33 different max-widths:
   340, 380, 390, 420, 440, 460, 480, 520, 540, 560, 600, 620, 640, 680, 700,
   720, 760, 768, 780, 800, 820, 860, 880, 900, 920, 940, 960, 980, 1000, 1024,
   1040, 1080, 1340. Each was reasonable on its own; together they are what
   reads as "inconsistent", because neighbouring components reflow at different
   widths. Drag a window from 720px to 640px and the reading grid collapses at
   680, the topic list at 680, the checker at 920 and the sample cards at 640 —
   four separate jolts through one drag, with in-between states where one panel
   has already gone single-column and the panel beside it has not.

   Five steps cover every real device class. Pick from these:

     xs    400px   small phones          (iPhone SE / 13 mini, 320–400)
     sm    560px   large phones          (iPhone 15 Pro Max, Pixel, 390–560)
     md    768px   tablet portrait       (iPad portrait)
     lg   1024px   tablet landscape / small laptop
     xl   1400px   laptop — the width the full nav needs before it must
                   collapse to the burger. This one is measured, not chosen:
                   logo 182 + menu 821 + actions 284 + gaps 44 + .wrap's 52 of
                   padding = 1383, rounded up for a cushion. It is spelled out
                   in THREE places that must move together — the burger arms in
                   core/13-responsive.css and core/36-mobile-nav.css, and the
                   .wrap.nav cap. Re-measure it whenever a nav item is added.

   Custom properties cannot be used inside a media query, so the numbers are
   spelled out in the queries below; these exist for JS and for documentation.

   This file does NOT rewrite the 33 existing queries — retuning every feature
   at once would be a large, hard-to-verify change. It removes the need for the
   most common ones (gutters, small-phone text and control sizing are fluid
   now) and fixes the guards that were missing outright. Existing queries
   should migrate to the scale as their components are next touched.
   ------------------------------------------------------------------------- */
:root{
  --bp-xs:  400px;
  --bp-sm:  560px;
  --bp-md:  768px;
  --bp-lg: 1024px;
  --bp-xl: 1400px;
}

/* ============================================================================
   1. CONTAINER GUTTERS — one continuous ramp instead of three steps

   .wrap was 26px, stepping to 16px at 560 and 12px at 380 (00-base.css:152 and
   31-responsive-global.css). Two hard jumps, and neither lined up with the
   breakpoints the content grids inside .wrap use, so the gutter changed at one
   width and the columns at another. A clamp() ramps continuously across every
   width instead, which removes both jumps and both breakpoints.

   14px at 320 → 17.9px at 560 → 26px from 812 up: within a pixel or two of the
   old values at the sizes that mattered, minus the discontinuities.
   ========================================================================= */
.wrap{ padding-inline: clamp(14px, 3.2vw, 26px); }

/* Notched phones in landscape put the camera cutout over the left or right
   gutter, so anything sitting in it (the logo, the burger) is under the notch.
   max() keeps the normal gutter on every device that has no inset to report.
   This is what the .safe-x utility in 31-responsive-global.css was for — it
   was declared but never applied to anything, so no page ever got it. */
@supports (padding: max(0px)){
  .wrap{
    padding-inline:
      max(clamp(14px, 3.2vw, 26px), env(safe-area-inset-left))
      max(clamp(14px, 3.2vw, 26px), env(safe-area-inset-right));
  }
}

/* ============================================================================
   2. OVERLAYS THAT ARE TALLER THAN THE SCREEN

   .overlay is `position:fixed; inset:0; display:grid; place-items:center`
   (10-footer-modal.css:28), and it has no overflow of its own. A fixed element
   does not scroll, and the page behind it cannot scroll it either — so
   whatever does not fit inside those 100% of viewport height is simply
   unreachable, with no scrollbar anywhere to say so.

   Measured on a landscape phone (844×390): the login modal is 444px tall in a
   390px viewport, so its bottom 54px — the tail of the digit row and the
   "Didn't get a code?" instructions — sat below the fold with no way to reach
   them. On a landscape iPhone SE (320px tall) it is 124px.

   The fix is the standard pair: let the overlay scroll, align the modal to the
   start, and give the modal `margin-block:auto`. Auto margins still centre it
   whenever it fits, so nothing changes on a screen with room to spare; when it
   does not fit, it sits at the top and the overlay scrolls the rest into view.
   ========================================================================= */
.overlay{
  overflow-y:auto;
  overscroll-behavior:contain;
  align-items:start;
  padding-block:20px;
}
.overlay > *{ margin-block:auto; }

/* The OTP digit size, restored on touch screens.

   31-responsive-global.css sizes every input at 16px below 768px to stop iOS
   zooming the page on focus. Its selector carries three :not() attribute
   tests, so it scores (0,3,1) — which quietly outranks BOTH `.otp-boxes input`
   (0,1,1) at 26px and the small-phone override at 22px. The result was that
   the login digits rendered at 16px on every screen up to 768px, including
   tablets and large phones where the cell is still its full 46px wide: a
   16px digit adrift in a 46×58 box, nothing like the design.

   This selector is (0,3,1) too and this file loads later, so it wins on order.
   The clamp keeps the 16px floor exactly where it is needed — it is the iOS
   zoom threshold, not a style choice — and lets the digit grow with the cell
   above that, reaching the designed 26px by the time the cell stops shrinking. */
.otp-modal .otp-boxes .otp-cell input{ font-size:clamp(16px, 4.4vw, 26px); }

/* ============================================================================
   3. SHORT AND LANDSCAPE VIEWPORTS

   A phone turned sideways is ~390px tall (and a landscape iPhone SE is 320px).
   Full-height heroes and their vertical rhythm were tuned against portrait
   phones and desktop, where height is never the scarce axis; in landscape the
   same padding pushes the actual content entirely below the fold.

   Scoped to short viewports rather than to `orientation`, so a small window on
   a laptop gets the same treatment — and so a tall tablet in landscape, which
   has plenty of height, does not.
   ========================================================================= */
@media (max-height: 520px){
  /* Full-height page shells stop reserving a whole screen of height: at 390px
     tall, `min-height:100vh` on a shell whose content is 200px tall is just
     190px of emptiness to scroll past. */
  #shadowing-page,
  #writing-practice-page,
  #writing-sprint-page{ min-height:0; }

  /* The modal's own padding is 34px all round (10-footer-modal.css:36), which
     is 17% of a landscape phone's height before any content. */
  .modal{ padding:20px 22px; }
  .otp-modal .otp-logo{ margin-bottom:8px; }
}

/* ============================================================================
   4. VIEWPORT UNITS ON MOBILE BROWSERS

   `100vh` is the viewport with the URL bar RETRACTED, so on iOS Safari and
   Chrome Android a 100vh box is taller than what you can actually see and its
   last ~60-100px sit under the browser chrome. `dvh` is the live value.

   The codebase already knows this — 15-exam.css, 36-mobile-nav.css and
   34-writing-practice.css all use dvh — but it was applied per-file as each
   surface hit the problem, so several public pages still measured themselves
   against the wrong number.

   Those are fixed IN their own stylesheets rather than overridden from here,
   using the same `100vh` then `100dvh` fallback pair the codebase already
   uses. A blanket override from this file would be actively wrong: the studio
   layout deliberately resets `.sh-studio{min-height:0}` for its desktop grid
   (25-shadowing.css:641), and a late `.sh-studio{min-height:100dvh}` here
   would have quietly put that height back and broken the flex column.

   Converted: 25-shadowing.css (page shell, studio, report + intro panels)
   and 04-samples.css (samples view). Left alone: the exam surfaces, which
   already handle dvh themselves.
   ========================================================================= */

/* ============================================================================
   5. POINTER-AWARE HOVER

   Every :hover lift in the site also fires on touch, where it sticks after the
   tap (there is no pointer to leave). Card grids end up with one card stuck in
   its hover state after you scroll past it. Only neutralise the transform —
   colour and border hovers are harmless and still give tap feedback.
   ========================================================================= */
@media (hover: none){
  .sh-stat:hover,
  .g-summary-stat:hover,
  .wb-stat:hover{ transform:none; }
}
