/* ==========================================================================
   Air Market - v9 mobile layer, the long tail of fixed-column grids
   (V9-MOBILE-SPEC.md section 3.5). Owner: T2.4. CSS only, no markup or JS
   change. Mechanical work: go view by view against the capture frames
   (.shots-mobile) as the before-image, not by inspection alone - the
   capture is the evidence this whole spec was written from and the
   before/after comparison for this file's changes.

   Every fixed-column grid NOT already covered by store-v9-editorial.css
   (T2.2 owns .ed-grid-5/.ed-grid-3/.promo3/.hero3) or store-v9-browse.css
   (T2.3 owns the facet layout): .cat-grid (5-col), .grid-5, .grid-3,
   .result-grid, .result-shots, .review-grid, .ql-grid, .sec-cats, the
   4-col chart grids at store.css:949 and :1601, the `repeat(3, 220px)`
   grid at store.css:1040, and the v6 collection / creator / me / studio
   grids. Each collapses to 2 columns or 1 column at <= 720px (or to a
   `.v9-rail` shelf where the desktop meaning is genuinely "shelf", not
   "grid" - see store-v9-mobile.css's v9-rail header for that primitive),
   and to 1 column at <= 480px wherever 2 columns still clips.

   HARD RULE, identical across all seven v9 sheets: every declaration in
   this file must live inside `@media (max-width: 720px)` or
   `@media (max-width: 480px)` (store-v9-mobile.css's BREAKPOINTS section
   explains why those two numbers and not a ladder). A rule outside either
   query is a desktop regression by definition - the >= 861px byte-identity
   bar this whole layer exists to hold depends on that being true of every
   one of the seven sheets, not just six of them.

   SCOPING: mirror the specificity of whatever you are overriding - see
   store-v9-mobile.css's SCOPING section for the exact prefixes. These
   grids span store.css (unscoped, byte-identity does not apply the same
   way - check whether the base rule is truly unscoped before assuming a
   prefix is even needed) through v6 (`:root[data-v6="on"]`). Check each
   selector's own source file individually; do not assume one prefix covers
   the whole list above. Never !important.
   ========================================================================== */

/* --------------------------------------------------------------------------
   5-COLUMN GRIDS (categories page, app lists)
   .cat-grid holds category tiles; .grid-5 holds 5-up app rows.
   Both collapse to 2 columns at 720px (still fits content), then 1 at 480px.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .cat-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-5 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  .cat-grid { grid-template-columns: 1fr; }
  .grid-5 { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   3-COLUMN GRIDS (reviews, results, quick links, search)
   .review-grid holds review cards; .result-grid and .result-shots hold
   search results; .ql-grid holds quick links; .sec-cats holds security
   category chips; .grid-3 is reused 3-up grid; .shots-row is the detail
   page's gallery rows. All collapse to 2 columns at 720px, then 1 at 480px.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .review-grid { grid-template-columns: repeat(2, 1fr); }
  .result-grid { grid-template-columns: repeat(2, 1fr); }
  .result-shots { grid-template-columns: repeat(2, 1fr); }
  .ql-grid { grid-template-columns: repeat(2, 1fr); }
  .sec-cats { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .shots-row { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  .review-grid { grid-template-columns: 1fr; }
  .result-grid { grid-template-columns: 1fr; }
  .result-shots { grid-template-columns: 1fr; }
  .ql-grid { grid-template-columns: 1fr; }
  .sec-cats { grid-template-columns: 1fr; }
  .grid-3 { grid-template-columns: 1fr; }
  .shots-row { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   4-COLUMN GRIDS (information table, efficiency report)
   .info-grid holds 4-column info cells; .eff-grid is the efficiency
   report's 4-cell layout. Both collapse to 2 columns at 720px, then 1 at 480px.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .info-grid { grid-template-columns: repeat(2, 1fr); }
  .eff-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  .info-grid { grid-template-columns: 1fr; }
  .eff-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   FIXED-WIDTH GRID (.filters-panel at repeat(3, 220px))
   Search filters panel has three fixed 220px columns. Collapses to 2 columns
   at 720px (each wider), then 1 at 480px. The 1fr fallback ensures content
   flows within the mobile gutter.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  .filters-panel { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  .filters-panel { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   LIST-STYLE GRID (.bom-row - bill of materials)
   The .bom-row uses grid for layout (icon, name, notes) but is semantically
   a list of rows, not a card grid. Each row uses
   `grid-template-columns: 22px minmax(120px, 240px) 1fr` at desktop; at 720px
   the name and note stack under one another and the glyph gutter stays, so a
   row is two lines rather than three. No second breakpoint is needed.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  /* Two columns, not one. Collapsing to a single column gave the icon (and, on
     a folder row, the caret) a centred line of its own above the name, so every
     row read as three stacked fragments - unbearable once a listing has fifty
     of them. The glyph keeps its 22px gutter and the note tucks under the name,
     which is the same reading order the desktop row has. */
  .bom-row { grid-template-columns: 22px 1fr; gap: 4px 10px; }
  .bom-row > .bom-note { grid-column: 2; }
}

/* --------------------------------------------------------------------------
   v6 COLLECTION GRID
   .v6-colls-grid starts at 1fr, goes to repeat(2, 1fr) at 560px, repeat(3, 1fr)
   at 900px. At 720px we drop back to 2 columns to fit phone width; at 480px,
   1 column.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root[data-v6="on"] .v6-colls-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 480px) {
  :root[data-v6="on"] .v6-colls-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   v6 STUDIO GRIDS
   .v6-wiz-previews (2 columns) and .v6-mismatch-cols (2 columns) both
   need to collapse to 1 column at 720px for mobile.
   -------------------------------------------------------------------------- */

@media (max-width: 720px) {
  :root[data-v6="on"] .v6-wiz-previews { grid-template-columns: 1fr; }
  :root[data-v6="on"] .v6-mismatch-cols { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   v6 CREATOR GRID
   .v6-dir-grid uses repeat(auto-fill, minmax(220px, 1fr)) which is already
   responsive and adapts naturally below 720px. No explicit rule needed here;
   the auto-fill will scale it down to fit the viewport without explicit
   media query override.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   CHILD ELEMENT CONSTRAINTS
   Where a grid item with unbreakable content (long text, URLs) overflows a
   1fr track, add min-width: 0 to the item. This is handled by existing CSS
   rules in the source files (e.g., .cat-tile { min-width: 0 }, .app-row { min-width: 0 }),
   so no rules are needed here unless a specific grid's children show clipping
   after collapse. Verify with mobile-qa.mjs if any cells clip after changes.
   -------------------------------------------------------------------------- */

/* ==========================================================================
   ADDED POST-AC1: three surfaces AC1 (measuring only `.window`) could not
   see, found by measuring `.content` directly once the shell floor was gone.
   `.window` is `overflow-x: hidden`, so a scroller inside it that still
   overflows is the same defect as the original bug, one level deeper -
   content clipped and unreachable, just no longer visible in the one metric
   the harness checked. None of the three below is a card grid, which is why
   none of them was already on this file's list above; they land here anyway
   because the story page has no other owner (see the first one) and the two
   report-page rings/figures are the same "fixed-size content in a shrinking
   row" shape as this file's other long-tail fixes, not a listing-detail
   concern (store-v9-detail.css) or an editorial-rail concern
   (store-v9-editorial.css). Same hard rule as the rest of this file: every
   declaration below lives inside a `max-width: 720px` or `max-width: 480px`
   query, never !important, prefix mirrors store.css's own (none - every
   selector below is unscoped there too).
   ========================================================================== */

/* --------------------------------------------------------------------------
   THE STORY PAGE (store.css:973-1011, unscoped, route #/story/:id) - the
   worst overflow measured for this whole task and the only one broken at
   BOTH 390 and 320. It is not a card grid, which is the plainer reason it
   is not in the header's list above: no Phase-2 task in
   V9-MOBILE-SPEC.md section 4 ever named the story route at all. T2.1 owns
   the listing detail, T2.2 owns Discover and editorial, T2.4 (this file)
   owns "the long tail of grids" - and `.story-layout` is a two-column
   hero-plus-prose layout, not a grid of cards, so no task description was
   ever written to catch it. It shipped un-owned and was found by measuring
   `.content` directly after AC1 (on `.window` only) went green - the exact
   gap this whole follow-up task exists to close, and worth flagging plainly
   for the next reader rather than leaving it to look like an oversight in
   this file specifically: it is a gap in the BUILD PLAN, not in T2.4's
   execution of it.

   `.story-layout` is `grid-template-columns: 420px 1fr` - a FIXED 420px
   track for the sticky hero image, sharing the row with the story prose in
   whatever is left. A `px` track never shrinks for its container the way an
   `fr` track does: at any viewport under roughly 460px (420 plus the 40px
   gap) the grid still allocates the full 420px to the hero, forcing the row
   past the viewport regardless of what the `1fr` column resolves to.
   Measured: `.story-body` (the `1fr` column) landed 244px past the glass at
   390px and 314px past it at 320px - almost exactly the fixed column's own
   width, which is the signature of a `px` track that cannot respond to the
   viewport at all, not a text-wrapping squeeze the way the listing hero's
   identity column was (store-v9-detail.css section 1's "two words per
   line" defect).

   One column removes the fixed track outright instead of shrinking it to
   fit: at <= 720px there is no honest way to keep a 420px hero image beside
   body text on a 390px screen, so the two stack instead, in source order
   (hero, then body) - which a single `1fr` track already gives for free,
   since grid auto-flow places both items into the one column it has.

   The hero's own `position: sticky` (store.css:982, `top: 60px`, height
   `calc(100vh - 130px)`) is written for the side-by-side layout, where
   pinning the image while the body scrolls past it NEXT TO IT is the whole
   point. Stacked into one column, the same rule would instead pin a
   near-full-screen image at the top of the page while the reader tries to
   scroll the story text underneath it - not an overflow bug, but exactly
   the "measures clean, reads broken" failure mode this task's verification
   step is required to catch by looking, not only by measuring. Switching to
   `position: relative` keeps `.story-hero` a positioned container for its
   own absolutely-positioned art (`heroArt()`'s `.art-fill`/`.glyph-center`,
   both `position: absolute; inset: 0`, app.js:521-524) - `position: static`
   would have let that art escape to the next positioned ancestor up the
   tree instead of staying inside the banner - while dropping the stickiness
   and letting the banner size itself to its own content instead of a
   viewport-relative height, so it reads as a hero card at the top of the
   story rather than a screen-filling pane.

   `height: auto` rather than a guessed fixed height is required, not just
   tidier: a fixed height combined with `.story-hero`'s inherited
   `overflow: hidden` (store.css:986) clips anything that does not fit, and
   a first attempt at a fixed 320px did exactly that - `tools/mobile-qa.mjs`
   caught "Trust Toolkit" / "Security & compliance suite" (the story's own
   `.banner` content) clipped at 320px, where the narrower column wraps the
   `h1` onto more lines than it needed at 390px and leaves less room under a
   fixed cap. Letting the box size to its own in-flow content (the eyebrow,
   the heading, the banner - the background art is `position: absolute`
   and does not participate in that sizing) means there is no cap left to
   clip against, at any width or any title length this data set or a future
   one might use. `min-height: 220px` is a floor for a short title, not a
   ceiling - it can never cause the clipping a fixed height did.

   `.story-layout`'s own `padding: 0 36px 60px` (store.css:979) is a second,
   separate reason the hero was too narrow at this width, independent of the
   column fix above: 36px is a stale pre-v9 figure, not a read of
   `--v3-gutter` the way `.page`'s padding is (store-v9-mobile.css's GUTTER
   STEP-DOWN section), so this route never felt the mobile gutter drop to
   16px at all. At 320px that stale 36px-per-side padding left the hero's
   own `.banner` - a fixed-cost row of a 40px icon, a 54px "Get" button, two
   10px gaps and the banner's own 12px side padding, 138px of fixed cost
   before the featured app's name and tagline see a single pixel - with only
   30px of `.bmeta` for two lines of text that need more like 50-60px each,
   which is what `tools/mobile-qa.mjs` was catching as AC4 clipped text
   ("Trust Toolkit" / "Security & compliance suite") even after the height
   fix above stopped it being a HERO-level overflow. Reading `--v3-gutter`
   here instead - already redefined to 16px at this width by
   store-v9-mobile.css, and safe to read because the shipped document is
   always `data-ui="v5"`, the one selector subset that redefinition covers -
   gives the hero, and therefore the banner inside it, the same 40px of
   width back that every other surface on this route already gained. */
@media (max-width: 720px) {
  .story-layout {
    grid-template-columns: 1fr;
    padding: 0 var(--v3-gutter) 40px;
  }
  .story-hero {
    position: relative;
    top: auto;
    height: auto;
    min-height: 220px;
  }
}

/* --------------------------------------------------------------------------
   THE REPORT PAGES' QUALITY RING (store.css:1247-1253 `.ring`/`.ring.big`;
   store.css:1335-1341 `.rep-head`; store.css:1621-1627 `.eff-summary`/
   `.eff-sum-ring`, all unscoped) - two routes, one shared cause. #/quality/:id's
   `.rep-head` and #/cost/:id's `.eff-summary` are both flex rows that end in
   a `.ring.big`: a fixed 110x110px circle, `flex: none` on `.eff-sum-ring`
   and picked up nowhere else in either row that would let the ring itself
   shrink even if the row wanted it to. At 320px `.rep-wrap`'s available
   width (`max-width: 780px`, no padding of its own beyond `.page`'s
   stepped-down 16px gutter) is 288px; `.rep-head` alone already spends 72
   (icon) + 18 + 18 (its two gaps) + 110 (the ring) = 218px of that before
   the headline gets a single pixel, and `.eff-summary`'s own 22px
   horizontal padding on both sides plus its 22px gap leaves even less room
   for `.eff-sum-main`'s comparison rows once the ring has taken its share.
   Measured: `.q-ring`/`.ring.big`/`.rn`/`.q-grade` land 33px past the glass
   on #/quality/frontend-design; `.cpct` (the dollar-figure column inside
   `.eff-sum-cmp`'s comparison rows, store.css:1642-1645) lands up to 35px
   past it on #/cost/frontend-design, once the ring alone has already
   claimed most of the row.

   Per V9-MOBILE-SPEC.md's own guidance on fixed-size content ("a ring that
   has to be swiped to be seen is worse than a ring scaled to the screen"),
   the fix is not a rail, it is a smaller ring: at <= 480px (both overflow
   measurements are 320px-only, the phone fine-tuning breakpoint, not the
   720px tablet one - at 390px both routes already fit) `.ring.big` drops
   from 110px to 64px - close to the 72px store-v9-detail.css section 1
   already uses to shrink the listing hero's icon, trimmed 8px further for
   the reason the next paragraph explains - so report pages and the listing
   settle on nearly one "small circular avatar" size at phone widths instead
   of two. `::before`'s inset and `.rn`'s font-size scale down with it in
   the same proportion (64/110 ~= 0.58) so the ring keeps reading as a ring
   rather than thickening into a painted disc. This targets `.ring.big`
   only, not bare `.ring` - the small 74px ring on the listing page's own
   `.qual-card` (store-v9-detail.css section 8) is a different, already-small
   instance of the same class that this rule must leave alone.

   72px (tried first, matching the icon figure exactly) still left
   `.q-grade` as the offender instead of the ring: the
   grade words this build's data actually uses (store.css, "Excellent",
   "Outstanding", "Very Good") are set at the same 11px/600 weight as the
   listing's own small ring, and "Outstanding" - one unbroken word, nothing
   to wrap on - measures ~67.6px wide, comfortably under the small ring's
   74px but wider than a 72px (and worse, any smaller) `.ring.big`. Shrinking
   the ring far enough to fit `.rep-head`'s row (64px, freeing the last 8px
   `.rep-head`'s icon/gap/ring budget needed) would have left that same word
   overhanging the ring by a few pixels again. Shrinking the grade label's
   own type one step further closes both gaps at once: at 9px the same word
   measures ~55px, well inside a 64px ring, so the ring can drop to the size
   `.rep-head`'s row actually has room for without `.q-grade` becoming the
   next thing to overflow it. */
@media (max-width: 480px) {
  .ring.big {
    width: 64px;
    height: 64px;
  }
  .ring.big::before {
    inset: 6px;
  }
  .ring.big .rn {
    font-size: 20px;
  }
  .rep-head .q-grade,
  .eff-sum-ring .q-grade {
    font-size: 9px;
  }
}

/* --------------------------------------------------------------------------
   THE COMPARISON ROW (store.css:1266-1271 `.cmp`/`.crow`/`.cpct`, unscoped) -
   the same shape of overflow as the ring above, one layer further in, and
   only visible once the ring shrink above stopped masking it. `.cmp .crow`
   is `grid-template-columns: 108px 1fr 38px` - TWO fixed tracks (the "Claude
   alone" / "With X" label and the percentage or dollar figure) that never
   shrink for their container, exactly the same `px`-track behaviour
   `.story-layout` had above, just at a smaller scale. This single rule backs
   three surfaces at once: the listing's own Quality Report card
   (`.qual-card .cmp`, store-v9-detail.css section 8), the Quality report's
   per-scenario cards (`.scn-card .cmp`), and the Cost report's summary and
   "Cost of One Finished Job" rows (`.eff-sum-cmp .crow` /
   `.section[data-section="cost-per-outcome"] .crow`, store.css:1642-1645,
   whose own override only widens the third column to `auto` - still fixed
   in the sense that an `auto` track's minimum is its content's max-content,
   the same trap as `.q-side` in store-v9-detail.css section 8). Measured at
   320px, after the ring shrink above: the listing's own comparison row still
   pushed `.content` 10px past the glass (`.cpct`, `.crow`, and their
   `q-head`/`cmp`/`q-side` ancestors all reporting the same 10px), and the
   Cost report's percentage figures 7-11px past it - the two fixed columns
   (108 + 38, or 108 + content-width for the dollar figures) simply do not
   fit 288px of card once the ring, the card's own padding and the row's own
   gaps have taken their share.

   Stacking the row is the same answer as `.story-layout` and `.qual-card`
   above: at <= 480px the label moves to its own line, and the bar and its
   value share the line below it - two rows instead of one three-column row
   forcing a minimum width nothing on a phone actually has. `:first-child`
   is a safe selector here (not a class match) because every `.crow` in this
   codebase is built in exactly this order - label span, `.cbar`, value span
   - across all three call sites, so no HTML change is needed for the CSS
   grid-area reassignment to line up. */
@media (max-width: 480px) {
  .cmp .crow {
    grid-template-columns: 1fr auto;
    grid-template-areas: "label label" "bar pct";
    row-gap: 4px;
  }
  .cmp .crow > :first-child {
    grid-area: label;
  }
  .cmp .crow .cbar {
    grid-area: bar;
    min-width: 0;
  }
  .cmp .crow .cpct {
    grid-area: pct;
  }
}
