/* ==========================================================================
   Air Market - v11 mobile density, THE ROW PRESENTATION
   V11-MOBILE-DENSITY-SPEC.md section 4.4, applied per 4.2 and 4.3.

   Measured problem (2026-08-15, 375x812, #/): Discover is 24,932px tall and
   the first listing name appears at y1189. Every list section renders full
   .a-card tiles single file at 385 to 403px each, art panel and all, so a
   shopper sees two products per screen. "Skills and Agents We Love" alone is
   roughly 8,000px of scroll. Apple's mobile page renders the same kind of
   section as icon rows and puts six to eight products on a screen.

   This sheet is one primitive: at <= 720px, a card in a LIST context lays out
   as an icon row. It is pure CSS over the markup appCard already emits
   (app.js:865) - no node added, none removed, none moved.

   SCOPE, AND WHY IT IS A DIRECT-CHILD SELECTOR.

   Everything here is `.card-grid > .a-card`. .card-grid is emitted by exactly
   two places, rowGrid (app.js:915) and the search results container
   (app.js:3277), and both are catalogue lists: Discover's ranked and
   editorial-picked sections, browse results, search results, creator
   storefronts. The surfaces that must KEEP their art - the editorial rails
   (.ed-grid-5, .ed-grid-3, .promo3, .hero3, already rails since v9) and the
   v6 recommendation strips (.v6-rec-cell .a-card, store-v6-rec.css:76) - do
   not use .card-grid, so the direct-child selector excludes them by
   construction rather than by an exclusion list that would need maintaining.
   That distinction is the point: rails are merchandising and merchandising is
   pictures, rows are the catalogue and a catalogue is a list.

   WHAT THE ROW DROPS, AND THE ONE THAT WAS A REAL DECISION.

   Hidden at row width: the 16:10 screenshot, the type chip, the "area of
   work" overline, the byline, the spec strip (rating, installs, permissions)
   and .a-verif, the "Checked <date>" security line. Kept: rank where the list
   is ranked, icon, name, tagline, and the Get button.

   .a-verif is the one worth defending, because trust signals are this store's
   whole differentiator and this sheet hides one. At 375px the row has about
   180px of text column after the icon, the gaps and the Get button.
   "Checked Jul 2026" with its shield costs about 110px of that, and it would
   have to come out of the tagline, which is the only line in the row that
   says what the thing DOES. The security signal is not lost, it is one tap
   away on a listing page where it is a full section, a stats-bar cell and a
   dedicated route; the tagline has no second home. Above 720px nothing
   changes and the badge is on every card as before.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE LIST BECOMES A LIST.

   .card-grid is `repeat(auto-fill, minmax(258px, 1fr))` with a 26px/20px gap
   (store-v2.css:195). At 375px auto-fill already resolves to one column, so
   the columns line changes nothing measurable and is restated only so the
   intent is readable; the gap is what matters. Rows are separated by their
   own hairline, so a 26px gutter between them would draw the separator twice
   and read as a list of loose slabs rather than as one list.

   The five-version prefix is copied from store-v2.css:195 exactly. An
   unprefixed `.card-grid` here is specificity (0,1,0) against that rule's
   (0,4,0) and would lose silently at every width - see store-v11-density.css's
   header for the v9 build that learned this the expensive way. */
@media (max-width: 720px) {
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid {
    grid-template-columns: 1fr;
    gap: 0;
  }

  /* THE ROW. Two text rows beside a fixed icon column, with the action
     column sized to its button:

       mark | name | act
       mark | sub  | act

     align-items: center on the card, with the name bottom-aligned to its row
     and the tagline top-aligned to its own, puts the pair optically centred
     against a 56px icon whatever the type ramp does to either line.

     The card sheds its tile chrome: no border box, no radius, no background,
     no clipping. What is left is a hairline under each row, which is the
     separator every list on a phone uses and the one Apple uses here. */
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card {
    display: grid;
    grid-template-columns: var(--v11-row-icon) minmax(0, 1fr) auto;
    grid-template-areas:
      "mark name act"
      "mark sub  act";
    align-items: center;
    column-gap: var(--v11-row-gap);
    row-gap: 0;
    padding: var(--v11-row-pad) 0;
    border: 0;
    border-bottom: 1px solid var(--v2-line);
    border-radius: 0;
    background: transparent;
    overflow: visible;
  }

  /* The two wrappers dissolve so their children can be placed directly. Same
     technique as the ident band (store-v11-detail.css section 1): neither
     .a-shot nor .a-body carries anything that has to survive - .a-shot's
     aspect-ratio, field background and bottom hairline are the art panel this
     row does not draw, and .a-body's padding is replaced by the row's own. */
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card > .a-shot,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card > .a-body {
    display: contents;
  }

  /* WHAT THE ROW DOES NOT SHOW. `.a-shot > .art-fill` is a direct-child
     selector on purpose: shotSVG emits the screenshot as .art-fill in both
     its branches (an <img> when the listing has generated media, an inline
     <svg> otherwise, app.js:779), and the raster-icon branch of iconArt uses
     that SAME class one level deeper inside .a-mark. An unqualified
     `.art-fill { display: none }` here would blank the row's icon on every
     listing that has real icon art and leave the gradient ones showing,
     which is exactly the kind of half-working that reads as a data bug. */
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-shot > .art-fill,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-kind,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-over,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-by,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-spec,
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-verif {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   2. THE ICON, AND THE ONE PLACE A TRANSFORM HAS TO GROW SOMETHING.

   appCard renders its mark at 34px (`iconArt(it, 34)`, app.js:868) and
   iconArt writes that as an inline `style="width:34px;height:34px"`. A row
   icon at 34px reads as a favicon; Apple's is about 60px at this viewport.

   Inline width cannot be beaten by an external rule without !important, and
   the max-width trick the ident band uses only works DOWNWARD (the used width
   is min(width, max-width), so a max cannot raise a 34px width). The two
   properties left are `zoom`, which is not something to introduce into a
   sheet this deep in a cascade, and `transform: scale()`, which paints at any
   size but changes no layout box.

   So the box and the paint are supplied separately: .a-mark becomes a real
   56px element in the grid's mark column, and the 34px icon inside it is
   scaled 56/34 = 1.647 from the top-left corner to fill exactly that box.
   .a-mark already carries `overflow: hidden` (store-v2.css:237) so the
   scaled paint is clipped to the box on the pixel, and the same scale takes
   the gradient branch's inline glyph font-size with it - one transform, both
   icon branches, which is the same property store-v9-detail.css section 1
   used for the same inline-style reason in the other direction.

   The absolute positioning and the 3px paper ring go: both exist to make the
   mark ride the bottom edge of an art panel this row does not draw. */
@media (max-width: 720px) {
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-mark {
    grid-area: mark;
    position: static;
    width: var(--v11-row-icon);
    height: var(--v11-row-icon);
    border-radius: 13px;
    box-shadow: none;
    /* The row's column-gap is 8px, tuned for the two trailing buttons where a
       tighter gap reads as a pair. Between the icon and the text 8px reads as
       a collision, so the icon carries the missing 4px itself. */
    margin-right: 4px;
  }
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-mark .app-icon {
    transform: scale(1.647);
    transform-origin: 0 0;
  }
}

/* --------------------------------------------------------------------------
   3. THE TWO TEXT LINES AND THE ACTION.

   The name keeps the card's weight and goes up a point to 16px, which is the
   size a row title has to be to stay the loudest thing in a row that also
   holds a button. The tagline drops to 13px and to ONE line with an ellipsis:
   it wraps to three at this width otherwise, and a three-line tagline is what
   turns a 76px row back into a 120px one. Apple truncates the same line in
   the same place.

   BOTH LINES CLAMP RATHER THAN NOWRAP, and the difference matters to more
   than taste. The first build used `white-space: nowrap` with
   `text-overflow: ellipsis`, which lays the text out on one infinitely wide
   line and then hides the overhang: the element's scrollWidth genuinely
   exceeds its clientWidth, which is precisely what tools/mobile-qa.mjs's AC4
   probe defines as clipped text. It fired 283 times.

   Exempting `text-overflow: ellipsis` from that probe was the tempting fix
   and it is the wrong one: `.stat .lbl` (store.css:765) is nowrap-plus-
   ellipsis too, and its truncation to "QUAL..." is the defect
   V9-MOBILE-SPEC.md section 1.4 was written about. A probe that ignored
   ellipsis would have been blind to it.

   `-webkit-line-clamp: 1` reaches the same picture by a different mechanism:
   the text wraps to the box width, so nothing overflows horizontally and
   AC4's premise is honestly not violated, and the ellipsis lands at the end
   of the last permitted line. A single unbreakable word longer than the
   column still overflows and still fails the probe, which is correct - that
   one IS a layout defect. The prefixed properties are the spelling every
   shipping engine implements; unprefixed `line-clamp` is set alongside them
   for when that changes.

   .a-sub carries `flex: 1` from the card body (store-v2.css:306), which is
   inert in a grid area but reads as a leftover; it is left alone rather than
   reset, because resetting properties that do nothing is how a sheet becomes
   impossible to audit. Its margin is not inert and is reset.

   The action column is .a-foot stripped of the divider it drew across the
   bottom of a tile. Its Get button keeps whatever hit box store-v9-taps.css
   gave it - that sheet's 44px floor is a v9 acceptance criterion and nothing
   here touches its size. */
@media (max-width: 720px) {
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-name {
    grid-area: name;
    align-self: end;
    font-size: 16px;
    margin: 0;
    /* An h3 is a block box with `min-width: auto` in a grid track, so its
       longest word sets a floor the minmax(0, 1fr) track cannot go under: the
       name was measured 137px wide in a 113px track, overlapping the button
       column rather than wrapping. min-width: 0 releases that floor, and the
       one-line clamp below keeps the handful of names still too long for the
       column from wrapping the row to a third line. */
    min-width: 0;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    overflow: hidden;
  }
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-sub {
    grid-area: sub;
    align-self: start;
    font-size: 13px;
    margin: 2px 0 0;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    overflow: hidden;
  }
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-foot {
    grid-area: act;
    align-self: center;
    justify-content: flex-end;
    margin: 0;
    padding: 0;
    border-top: 0;
  }
}

/* --------------------------------------------------------------------------
   3b. THE "+ STACK" CHIP GETS A HOME.

   v3/stack.js:363 injects this button into `.a-shot` and store-v3-stack.css:108
   parks it `position: absolute; right: 8px; bottom: 8px` in "the only free
   corner" of the artwork (that file's own words). This row does not draw the
   artwork, and `display: contents` on .a-shot removes it as a containing
   block, so without a rule here the chip resolves its offsets against some
   ancestor several levels up and lands in the middle of an unrelated row.
   That is exactly what it did on first build.

   Hiding it was the cheaper fix and it is wrong: stack.js decorates ONLY
   `.a-card`s, so a card is the sole place in the store where anything can be
   added to the Stack. The listing page has Save and Share and no Stack
   control, and the dock pill opens the drawer rather than adding to it. A row
   list with no chip would mean a phone user cannot build a Stack at all.

   So the chip becomes a real column instead. Icon only: the label is two
   words in a row that has about 180px of text column to defend, and the
   button's accessible name comes from the aria-label paintChip already sets
   (stack.js:336), not from the visible span, so dropping the span costs the
   screen-reader nothing. `span:not(.ic)` rather than `span` because
   UI.icon renders into `.ic` (store-v3-stack.css:131) and blanking that
   would leave an empty button.

   WHERE IT SITS, AND WHY NOT IN A COLUMN OF ITS OWN. The first build gave it
   a fifth grid column. Measured, that left a ranked row 89px for the name and
   tagline out of 343px: rank 16, icon 56, chip 44, Get 54 and four gaps ate
   the rest, and every listing name in the store truncated. Two actions in a
   phone row is one more than the row can pay for.

   So the chip is placed in the SAME grid area as the icon and pinned to that
   cell's bottom-right corner. Grid items sharing an area overlap by design,
   which is all this needs: no absolute positioning, no containing-block
   question, and the mark keeps its own `overflow: hidden` (the chip is its
   sibling, not its child, so nothing clips it). It reads as a badge on the
   artwork, which is where store-v3-stack.css put it on a card anyway - the
   corner just got smaller. The name and tagline get about 193px on a ranked
   row and 217px on a plain one, roughly double.

   The 44px box, the transparent element background and the pill repainted on
   ::before are all v9's (store-v9-taps.css section 2) and are left exactly as
   they are - that treatment is a v9 acceptance criterion and this sheet only
   changes where the chip sits, never how big its hit box is. The visible pill
   is the ::before inset 9px inside that box, so what paints over the icon is
   about 26px tall while the thumb gets the full 44. Its ::before is
   positioned against the chip, which therefore has to stay a positioned
   element: `position: relative` replaces `absolute` rather than `static`.
   The opacity/transform pair that hides the chip until hover is forced open:
   `@media (hover: none)` already does this on a real phone
   (store-v3-stack.css:152), and a row list should not depend on that query
   being right about the device. */
@media (max-width: 720px) {
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .v3-stack-chip {
    grid-area: mark;
    justify-self: end;
    align-self: end;
    position: relative;
    right: auto;
    bottom: auto;
    width: 44px;
    justify-content: center;
    /* The 44px box is the thumb's, not the eye's. Pushing the content box
       into its bottom-right 24px, and the ::before pill with it, paints a
       badge on the icon's corner while the hit box stays where v9 put it.
       Without this the pill v9 paints (inset 9px, so 46x26) lands across the
       middle of a 56px icon and hides the artwork the row is identifying the
       listing by - which is what the first build of this rule did. */
    padding: 20px 0 0 20px;
    opacity: 1;
    transform: none;
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .v3-stack-chip::before {
    inset: 20px 0 0 20px;
    border-radius: 50%;
  }
  :root:is([data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .v3-stack-chip > span:not(.ic) {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   4. RANKED LISTS KEEP THEIR NUMBERS.

   rowGrid takes `{ ranked: true }` for Top Free, Top Paid and the per-category
   chart (app.js:2392-2394), and there the position IS the information - a
   chart with the numbers taken off is just a list. So the row grows a leading
   24px gutter and .a-rank moves into it as ordinary text.

   :has() is what makes that conditional without a second class from JS, and
   the degradation is deliberate: .a-rank is hidden by the base rule and only
   the :has() rule brings it back. An engine without :has() support drops both
   rules together and shows an unnumbered list, which is the same list in the
   same order. The alternative - placing .a-rank into a "rank" area that the
   unmatched grid-template never declared - would hand it to auto-placement
   and break the row for everyone.

   The badge chrome comes off with the same reasoning as .a-mark's ring: the
   white pill and its absolute corner offsets exist to keep a number legible
   ON a gradient screenshot. In a text column it is text. */
@media (max-width: 720px) {
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card .a-rank {
    display: none;
  }
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card:has(.a-rank) {
    grid-template-columns: var(--v11-row-rank) var(--v11-row-icon) minmax(0, 1fr) auto;
    grid-template-areas:
      "rank mark name act"
      "rank mark sub  act";
  }
  :root:is([data-ui="v2"],[data-ui="v3"],[data-ui="v4"],[data-ui="v5"]) .card-grid > .a-card:has(.a-rank) .a-rank {
    display: block;
    grid-area: rank;
    align-self: center;
    position: static;
    min-width: 0;
    height: auto;
    padding: 0;
    background: none;
    border-radius: 0;
    color: var(--text-3);
    font-size: 14px;
    text-align: center;
  }
}
