/* ==========================================================================
   Air Market - v9 mobile layer, listing detail (V9-MOBILE-SPEC.md section
   3.2, route #/app/:id). Owner: T2.1. CSS only, no markup or JS change.

   At <= 720px: .detail-head stacks (72px icon, centred identity below it,
   the CTA column full-width under the identity - v5 already wraps the CTA
   column at this width, store-v5.css:329-335, leave that rule alone and
   build around it, do not duplicate it). .stats-bar becomes
   overflow-x: auto with flex: none cells sized to content, scroll-snap and
   a fade mask matching the gallery rail's. Benefit rows (.v4-lede
   checklist) wrap to full width. The gallery grid becomes a swipe rail,
   one tile = 85vw - use store-v9-mobile.css's `.v9-rail` primitive (see
   its header for the exact contract and the two ways to apply it) rather
   than hand-rolling a second scroll-snap implementation; this is
   specifically one of the surfaces that primitive was written for. The
   permissions table and version-history table each get an
   `overflow-x: auto` wrapper via CSS (display: block scroll container),
   not a markup change. The chrome bar keeps title truncation, the compact
   CTA, Save and back, all at >= 44px (T2.5 owns the tap-target floor
   itself; this file only needs to not shrink anything below it).

   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. The detail
   hero and CTA column are v5-only: `:root[data-ui="v5"]`. The stats bar,
   lede checklist and permissions/version tables predate v5 and are scoped
   more widely in their own sheets - check the source file for each
   selector before assuming the prefix, do not guess it from the filename.
   Never !important.
   ========================================================================== */

/* Rules start here. */

/* --------------------------------------------------------------------------
   1. THE HERO STACKS (spec 3.2, bullet 1).

   .detail-head (store.css:735) is a version-agnostic flex ROW - icon, then
   an identity column carrying an inline `style="flex:1"` (app.js:2158), and
   under v5 a third child, .cta-col (store-v5.css:31). store-v5.css:329-335
   already wraps .cta-col onto its own full-width line at this breakpoint;
   what it does NOT do is stop the icon and the identity column sharing the
   FIRST line, so that line still has to fit a 116px icon plus 22px of gap
   before the identity column sees a single pixel. At a 390px viewport minus
   this sheet's own 16px gutter (store-v9-mobile.css's GUTTER STEP-DOWN),
   that first line has 358px to work with; subtract the icon and its gap and
   the identity column - title, byline, the value-prop line - is fighting
   over roughly 220px. That squeeze is the measured "two words per line"
   defect (V9-MOBILE-SPEC.md section 1.4): not overflow, under-width.

   flex-direction: column turns the row into a stack. It composes with
   store-v5.css:331's flex-wrap: wrap rather than fighting it - wrap only
   ever matters on the CROSS axis, and once the main axis is vertical there
   is no fixed cross-axis size for anything to overflow, so that rule keeps
   pushing .cta-col onto "its own line" exactly as before, just as the third
   stacked block instead of a wrapped second row. Base .detail-head carries
   no [data-ui] prefix at all (store.css's rule applies to every version),
   so this mirrors that: an unscoped selector, same specificity, later
   source order wins. */
@media (max-width: 720px) {
  .detail-head {
    flex-direction: column;
    gap: var(--space-16);
  }
  /* align-items is the one property on this element v5 also claims
     UNCONDITIONALLY (store-v5.css:28, no media query, `flex-start`), at a
     specificity (`:root[data-ui="v5"] .detail-head`) the unscoped rule
     above cannot out-rank. Given the shipped page is always
     `data-ui="v5"` (index.html's own header), that rule is live at every
     width, so beating it takes matching its exact selector - not because
     v0-v4 need a second rule (nothing else here renders under them, the
     hero lede/benefits/CTA column are v4/v5-only to begin with), but
     because this is the one property with a live, unconditional v5 rule
     to out-rank. Stretch is what makes the identity column and .cta-col
     take the FULL row width once stacked, rather than each shrinking to
     its own content width and leaving a lopsided column down the middle -
     it is also the thing the AC's "identity column width close to content
     width" measurement is checking for. */
  :root[data-ui="v5"] .detail-head {
    align-items: stretch;
  }
  /* iconArt() (app.js:508) writes width/height as an INLINE style on this
     element (`style="width:116px;height:116px"` at this call site) - no
     external rule can out-specificity an inline declaration without
     !important, which this repo forbids outright. max-width/max-height are
     a DIFFERENT property from width/height, so they are never in that
     contest: the used width the UA paints is min(width, max-width)
     regardless of which rule supplied which half, so a 72px max-width here
     silently wins over the 116px inline width with no specificity fight
     and no !important. margin: 0 auto centres the now-smaller icon over
     the stacked identity column, matching spec 3.2's "centred identity
     below it". */
  .detail-head .app-icon {
    max-width: 72px;
    max-height: 72px;
    margin: 0 auto;
  }
  /* Two of the four verification listings (meeting-prep, csv-cleaner) have
     no generated icon art and fall to iconArt()'s SVG-gradient-plus-glyph
     branch, whose glyph is centred by .glyph-center (store.css:1133) with
     its own inline font-size - also sized off the same 116px, also beyond
     any external rule's reach, and unlike width/height there is no
     max-font-size property to repeat the trick above with. transform is a
     different property again, so it is free to shrink the already-laid-out
     glyph visually in place: 72/116 = 0.62. .app-icon's own
     overflow: hidden (store.css:512) quietly clips whatever box the
     untransformed layout still thinks it reserved, so nothing needs to be
     said about the now-harmless leftover space. This is a static, one-time
     transform applied at the breakpoint, not a transition, so there is
     nothing here for prefers-reduced-motion to switch off. */
  .detail-head .app-icon .glyph-center {
    transform: scale(0.62);
  }
}

/* --------------------------------------------------------------------------
   2. THE LONG-FORM DESCRIPTION GRID (store.css:816, unscoped, not part of
   the hero and not on any other T2 sheet's list - store-v9-grids.css's own
   header enumerates the fixed grids it owns and this is not one of them).

   .desc-grid is `1fr 200px` - the write-up on the left, the dev-links
   sidebar (creator / GitHub / website / support) pinned to a fixed 200px
   column on the right. That fixed column does not shrink, so at mobile
   widths the 1fr side is squeezed the same way the hero identity column
   was above - at 358px of content width that is roughly 128px left for
   the actual description text, which reproduces the exact "two words per
   line" symptom one section further down the page. Collapsing to a single
   column lets the sidebar drop below the prose at full width instead. */
@media (max-width: 720px) {
  .desc-grid {
    grid-template-columns: 1fr;
  }
}

/* --------------------------------------------------------------------------
   3. .stats-bar BECOMES A RAIL (spec 3.2, bullet 2).

   store.css:746-792 (unscoped) makes every `.stat` `flex: 1` with no
   scroll - five or six equal cells splitting whatever width the bar has,
   so at 390px a cell has room for a handful of characters and
   `.stat .lbl`'s own `white-space: nowrap; overflow: hidden;
   text-overflow: ellipsis` (store.css:765) does exactly what it is told:
   "QUALITY" becomes "QUAL...". The fix is not a narrower label, it is
   giving each cell room to be its own width and putting the OVERFLOW on
   the row instead of the text - store-v9-mobile.css's .v9-rail primitive,
   in its CSS-only form (that file's header, form 2), since this is
   existing JS-rendered markup this task may not add a class to.

   `--v9-rail-item: max-content` is the one departure from the primitive's
   own worked example (78vw/85vw): those are card rails, where every tile
   should read the same width regardless of content. A stat cell's whole
   point is that "New" and "4.8 out of 2,314 ratings" are different
   lengths - sizing every cell to a shared vw fraction would just move the
   truncation problem from the label to the cell padding. `flex: 0 0
   max-content` (the primitive's item rule with that one substitution) is
   what "cells sized to content rather than flex: 1" (spec 3.2) means
   literally: no grow, no shrink, basis = the content's own width.

   The label's white-space/overflow/ellipsis trio is reset alongside it:
   with the cell's width no longer capped, ellipsis has nothing left to
   activate on, but leaving `overflow: hidden` in place would still be one
   emoji-width rounding error away from clipping a glyph in `.val`'s star
   row, and this is a stated AC ("no truncated labels") rather than a
   side effect to hope holds. */
@media (max-width: 720px) {
  .stats-bar {
    --v9-rail-item: max-content;
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* A fade at the trailing edge is the same "there is more here" cue the
       gallery rail below uses - a plain `overflow-x: auto` row with a hard
       edge reads as "the layout broke", not "swipe". Always-on rather than
       JS-toggled at "fully scrolled": this file is CSS only, and a subtle
       24px fade starting from 100% costs nothing to look at even on the
       rare bar short enough to need no scrolling at all. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  }
  .stats-bar::-webkit-scrollbar { display: none; }
  .stats-bar .stat {
    flex: 0 0 var(--v9-rail-item);
    min-width: 0;
    scroll-snap-align: start;
  }
  .stats-bar .stat .lbl {
    white-space: nowrap;
    overflow: visible;
    text-overflow: clip;
  }
  .stats-bar .stat .sub {
    white-space: nowrap;
    overflow: visible;
    text-overflow: clip;
  }
}

/* --------------------------------------------------------------------------
   4. THE GALLERY BECOMES A SWIPE RAIL (spec 3.2, bullet 4).

   .shots-row (store.css:776, unscoped) is `grid-template-columns:
   repeat(3, 1fr)` - a fixed 3-up grid that, at 390px, gives each
   screenshot roughly 110px of width regardless of how legible that makes
   it. store-v9-mobile.css's .v9-rail primitive, CSS-only form again, same
   reasoning as the stats bar above: this is exactly the surface that
   primitive's own header names as one of its three intended callers.
   --v9-rail-item: 85vw is the primitive's own suggested width for "the
   wider detail gallery" (store-v9-mobile.css's v9-rail comment), picked
   over the 78vw editorial-rail figure because a screenshot needs to read
   at a size closer to how large it will actually render once tapped open,
   not just wide enough to signal "there's a picture here". `.shot` already
   carries `aspect-ratio: 16/10` (store.css:783), so height still follows
   width automatically - nothing here touches it. */
@media (max-width: 720px) {
  .shots-row {
    --v9-rail-item: 85vw;
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%);
  }
  .shots-row::-webkit-scrollbar { display: none; }
  .shots-row .shot {
    flex: 0 0 var(--v9-rail-item);
    min-width: 0;
    scroll-snap-align: start;
  }
}

/* --------------------------------------------------------------------------
   5. PERMISSIONS AND VERSION-HISTORY, SCROLLABLE RATHER THAN CLIPPED
   (spec 3.2, bullet 5).

   Neither of these is a literal `<table>` - there are none anywhere in
   this codebase (`grep -r "<table" assets/js` is empty) - they are the two
   places app.js's own comment (line 148) calls "the permissions table"
   (`.perm-chips`, store.css:939, a wrapping flex row of chips) and its
   sibling on the Security Report route (`.hist-row`, store.css:1395, a
   three-column flex row: a 64px version, a 110px date, then a free-width
   result). Neither renders on this build's four verification listings
   (all four are `type: "skill"` with `StoreFlags.showsSecurity` off, which
   is the exact non-bug V9-MOBILE-SPEC.md section 1 already records for the
   security route), but both are reachable from other listings on this
   same page template today and from any skill listing the moment that flag
   flips, so a defect here would just be invisible until someone else's
   route or someone else's flag change found it.

   `.perm-chips` already wraps (`flex-wrap: wrap`, store.css:939) - a chip
   dropping to the next line can never force the row wider than its
   container, so there is no overflow to fix in the ordinary case. What IS
   unguarded is the automatic-minimum-size flex bug store-v9-mobile.css's
   own SHELL FLOOR section traces one level up in #topbar: a flex item's
   default minimum width is its content's min-content, not 0, so one
   unbroken long word in a permission's declared value could still force
   its chip past the viewport with nowhere to wrap to. min-width: 0 is the
   same one-line fix that section describes, applied pre-emptively here
   rather than after a real permission string trips it.

   `.hist-row` has no such safety net - its third child, `.hres`, carries
   no flex-basis and no min-width, so it inherits that same automatic
   minimum from its own content. Given it is `overflow-x: auto` because
   `.section[data-section="version-history"]` is - a plain block element
   already, so making it a scroll container needs nothing more than the
   one property spec 3.2 asks for ("make the wrapper... a block-level
   scroll container") - a row that still cannot fit slides into view
   instead of `.window`'s `overflow-x: hidden` silently clipping it, which
   is what qualifies it for AC4's own scroll-container exemption rather
   than counting as a clipped node. */
@media (max-width: 720px) {
  .perm-chip {
    min-width: 0;
  }
  .section[data-section="version-history"] {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .hist-row .hres {
    min-width: 0;
  }
}

/* --------------------------------------------------------------------------
   6. STICKY CHROME BAR - TITLE TRUNCATION ONLY (spec 3.2, last bullet).

   The compact CTA, Save and back all stay exactly as they render today -
   this file's own header is explicit that the >= 44px tap-target floor for
   the chrome bar (and every other bar/button/row it applies to across the
   store) is T2.5's sheet (store-v9-taps.css) to own, and that this file's
   job is only to not make anything here smaller than it already is. It
   does not: nothing above touches the chrome bar's buttons.

   .chrome-title (store.css:385, unscoped) is `flex: 1; text-align: center`
   with no overflow handling at all, because at every width this bar has
   shipped at until now the title had the whole bar's width to itself once
   the two 30px icon buttons were accounted for. At <= 720px it now shares
   that row with a compact split-button CTA and a Save button too, so a
   long listing name (`app.js:2150`, `esc(it.name)` with nothing capping
   it) has meaningfully less room and would otherwise wrap the title onto a
   second line, growing the sticky bar's height on scroll. `min-width: 0`
   is required alongside the ellipsis trio precisely because this is a
   `flex: 1` item - without it, the flex item's automatic minimum content
   width (the same footgun section 5 above and store-v9-mobile.css's SHELL
   FLOOR section both name) would keep the title as wide as its longest
   word demands and overflow-x: auto/hidden. */
@media (max-width: 720px) {
  .chrome-title {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* --------------------------------------------------------------------------
   6b. THE SAME BAR, WHAT SECTION 6 DID NOT ACTUALLY BUY (found by scrolling
   a real listing at 375px, not by reading the spec).

   Section 6's ellipsis trio is correct and does nothing, because
   `.chrome-title` is also `display: flex` (store.css:388) and the listing
   name is a bare text node inside it - an ANONYMOUS flex item. `overflow`
   clips that item, but `text-overflow` only ever applies to the block box
   that carries it, and this box's own inline content is the mini-icon span
   and that anonymous item, neither of which the property can reach. With
   `justify-content: center` on top, the overflow is split across BOTH ends
   of the name, so "Frontend Design" rendered as "ntend De": no ellipsis, and
   the first characters - the ones that identify the listing - are the first
   thing thrown away. That is strictly worse than the wrap section 6 was
   written to prevent.

   `display: block` puts the mini-icon and the name back in one inline
   formatting context, which is the arrangement `text-overflow: ellipsis`
   is defined for, and left alignment makes the truncation happen at the end
   of the string where the ellipsis is. `line-height` replaces the vertical
   centring that `align-items: center` was providing, matched to the
   mini-icon's own 20px so the bar's height does not move.

   The second half is room, not overflow handling. At 375px the bar seats a
   44px back button, the 175px compact CTA and a 68px Save, which leaves the
   title about 34px - enough for the icon and one clipped syllable. The Save
   label is the cheapest 24px on the row to reclaim: the icon is the same
   bookmark the buy row and the top nav use, the button keeps its full 44px
   tap target, and the label survives for assistive tech as a visually
   hidden span rather than being deleted (the `title` attribute app.js:1093
   sets is a tooltip, not a substitute accessible name). Dropping the
   mini-icon at the same width buys the other ~27px. Both are scoped to
   <= 480px: between 481 and 720px the bar has the room for both. */
@media (max-width: 720px) {
  .chrome-title {
    display: block;
    text-align: left;
    line-height: 20px;
  }
  .chrome-title .mini-icon {
    display: inline-block;
    vertical-align: middle;
    margin-right: 7px;
  }
}
@media (max-width: 480px) {
  .chrome-title .mini-icon {
    display: none;
  }
  .chrome-bar .save-btn.compact {
    position: relative;
    width: 44px;
    padding: 0;
    justify-content: center;
  }
  /* `display: none` rather than the 1px clip-path the visually-hidden
     pattern usually reaches for: that pattern is a leaf whose scrollWidth
     runs 30px past a 1px clientWidth, which is precisely what AC4 (no
     clipped text, tools/mobile-qa.mjs) counts as a defect - it cannot tell
     a deliberately hidden label from a truncated one, and it should not
     have to. The button's name is not lost with it: app.js:1097 gives every
     Save an `aria-label` carrying the same words. */
  .chrome-bar .save-btn.compact > span:not(.sv-glyph) {
    display: none;
  }
  /* :not(.sv-glyph): the unsaved state's heart is a span-wrapped inline glyph
     (app.js SAVE_GLYPH), and hiding it would leave the 44px button empty. The
     label and the phone/cloud where-glyph still go; the aria-label keeps both
     of their words. */
}

/* --------------------------------------------------------------------------
   6c. THE DEVELOPER LINK COLUMN (store.css:824, unscoped).

   `.dev-links` is `text-align: right` with its anchors on
   `justify-content: flex-end`, which is right for the 200px sidebar it was
   written for. Section 2 above collapses `.desc-grid` to one column at this
   width, so the same block now spans the full content width directly under
   the prose - four short green links pinned to the right edge of a
   left-aligned page, reading as something that failed to lay out rather
   than as the creator's links.

   Left alignment only. The column direction stays: `store-v9-taps.css:403`
   grows every `.dev-links a` by 13px of block padding with a matching
   negative margin to make a 44px target out of an 18px link, and turning
   this into a wrapped row would stack those padded boxes into each other
   the moment a longer creator name pushed the row onto a second line. */
@media (max-width: 720px) {
  .dev-links {
    text-align: left;
  }
  .dev-links a {
    justify-content: flex-start;
  }
}

/* --------------------------------------------------------------------------
   6d. THE "NO RATINGS YET" CARD (store.css:1589, unscoped, rendered by
   app.js:1457 - the empty state every newly compiled listing shows, so the
   one most readers meet first).

   `.be-first` is a three-item flex row with `flex: none` on both the stars
   and the button, so the copy in the middle absorbs every pixel the other
   two do not want. At 375px that is about 95px: the invitation to write the
   first review wraps to one or two words a line for eleven lines, and the
   button still overhangs the card. The copy is the only item here with
   anything to say, and it is the one being squeezed.

   Wrapping is the whole fix. `flex: 1 1 180px` gives the copy a basis the
   line-fitting pass can actually see (the same reasoning section 7 spells
   out for `.repo-strip .rmeta`), so the stars keep their place beside the
   heading and the button takes a full-width line of its own underneath -
   where it is also a comfortable tap target rather than a 150px sliver. */
@media (max-width: 720px) {
  .be-first {
    flex-wrap: wrap;
    gap: 12px 14px;
  }
  .be-first .bf-copy {
    flex: 1 1 180px;
  }
  .be-first .get-btn {
    flex: 1 1 100%;
  }
}

/* --------------------------------------------------------------------------
   7. THE GITHUB REPO STRIP (store.css:1228, unscoped) - found by running
   tools/mobile-qa.mjs against this file, not by reading the spec: it names
   `.detail-head`, `.stats-bar`, the gallery and the two chip/row "tables"
   explicitly, but this row renders lower on the same page for any listing
   with a repo (one of the four verification listings, frontend-design,
   has one) and the harness caught it clipping `.rname` to 26px wide.

   `.repo-strip` is one `display: flex` row: a 34px icon, `.rmeta` (the repo
   name plus its facts line, `flex: 1; min-width: 0`), then `.stars-big` and
   `.rlink` - both `white-space: nowrap`, both sized off real content
   ("View the skill folder ↗" alone is over 150px). Because `.rmeta`'s
   `flex: 1` shorthand sets its flex-basis to 0%, the flex-wrap line-fitting
   pass (which sizes each item at its BASIS, before any grow) sees it as
   needing zero room and happily seats all four items on one line; growth is
   only handed out afterward, by which point `.rmeta` is left with whatever
   the two nowrap siblings did not claim - about 10px of actual free space
   at 390px, not the 128px `.rname`'s content ("anthropics/skills", one
   unbroken token with no space to wrap on) needs.

   The fix is not more shrinking, it is telling the SAME line-fitting pass
   that ran before growth that `.rmeta` needs real room too: an explicit
   `min-width` participates in that basis calculation the way `flex: 1`'s
   0% basis alone does not, so the two nowrap items are the ones pushed to
   a second line instead. `.repo-strip`'s own `gap: 14px` (store.css:1229)
   already applies as both column- and row-gap, so the wrapped line gets
   the same breathing room above it with no extra rule needed.

   Amended 2026-08-23: the strip was slimmed to a hairline meta row (15px
   icon, 12px type, 10px gap) and `.rmeta` became a wrapping flex row of its
   own. The reasoning above is unchanged - the basis calculation does not care
   about the sizes - so both rules stay exactly as they are. */
@media (max-width: 720px) {
  .repo-strip {
    flex-wrap: wrap;
  }
  .repo-strip .rmeta {
    min-width: 160px;
  }
}

/* --------------------------------------------------------------------------
   8. THE QUALITY CARD'S SIDE COLUMN (store.css:1256-1265, unscoped) - not
   named anywhere in V9-MOBILE-SPEC.md section 3.2, and not caught by AC1
   either: found by measuring `.content` directly after AC1 (on `.window`)
   went green, which is exactly the class of defect this task exists for.

   `.qual-card` is `grid-template-columns: auto 1fr auto`. The third column,
   `.q-side` ("N of M checks passed" plus the "See Full Report" link), is
   `flex-direction: column; align-items: flex-end` with no width rule of its
   own - a flex child with no stretch takes its own content's max-content
   width on the cross axis, and an `auto` grid track sizes to the largest
   max-content contribution any item in it makes. That is the same
   automatic-sizing trap store-v9-mobile.css's SHELL FLOOR section and this
   file's section 6 both name for MINIMUM width, one step over into
   PREFERRED width instead: nothing here ever asked `.q-side` to be small,
   so it simply took however much room its longest line wanted, regardless
   of how little `.qual-card` actually had left once the ring and the
   headline column had taken their share. Measured before this rule: at
   320px `.content` reports +78px, with `.q-side`, `.q-checks` and the
   anchor landing 62-64px past the glass; at 390px the same column still
   lands 6-8px over, because nothing about the sizing logic changes between
   the two widths, only how much room is left to be short by.

   The fix is not a narrower font or an ellipsis on a link, it is giving the
   column a row of its own: two grid columns instead of three, with
   `.q-side` spanning both of them on its own line under the ring and the
   headline rather than being squeezed beside them, and its own children
   switched from a right-aligned column stack to a left-aligned wrapping row
   so "N of M checks passed" and "See Full Report ›" sit side by side (or
   wrap under each other) at the card's full width instead of shrink-wrapping
   to whatever the third `auto` column happened to allow. */
@media (max-width: 720px) {
  .qual-card {
    grid-template-columns: auto 1fr;
  }
  .qual-card .q-side {
    grid-column: 1 / -1;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    gap: 6px 16px;
    margin-top: 2px;
  }
}

/* --------------------------------------------------------------------------
   9. THE SUPPORTS GRID (store.css:962-969, unscoped) - the small residual
   this task was asked to chase down rather than wave off as rounding.
   `.supports` is a fixed `1fr 1fr` grid with a 40px column gap; at 320px
   that leaves each column 126px of the card's width. `.support-row`'s text
   cell (an unclassed `<div>` wrapping an `<h6>` and a `<p>`) carries no
   `min-width: 0`, so its default automatic minimum - its own longest
   word's min-content, not 0 - can land a couple of pixels past the 126px
   the grid actually gave it. Measured: `.content` +2px on both
   `app-meeting-prep` and `app-csv-cleaner` at 320px (the only two
   verification listings whose Support blurbs happen to be long enough to
   trip it), 0 at 390px where the same two columns have enough room.

   One column at <= 480px removes the squeeze outright rather than papering
   over 2px with a `min-width: 0` patch that would still be one longer
   support description away from tripping again on some other listing this
   capture never happened to include. */
@media (max-width: 480px) {
  .supports {
    grid-template-columns: 1fr;
  }
}
