/* ==========================================================================
   Air Market - v11 mobile density, THE LISTING IDENT BAND
   V11-MOBILE-DENSITY-SPEC.md section 4.1.

   Measured problem (2026-08-15, 375x812, #/app/frontend-design): .detail-head
   runs y111 to y752. 641px, 79% of the first viewport, for one identity
   block. Its budget was icon 72px alone on its own line, h1 30px, byline
   24px, a four-line lede 102px, three STACKED benefit pills 111px, then a
   183px .cta-block. Everything that argues the listing is worth installing -
   the ratings/quality/security strip at y752, the screenshots at y985 - was
   below the fold.

   Apple's page at the same viewport puts the icon LEFT of the title, the
   price/CTA line directly under the subtitle, the quick facts under that and
   the screenshot rail fully visible, all inside 812px. This sheet does the
   same thing with the markup app.js already emits.

   NO JS CHANGE. Spec 4.1 asked for a tagline subtitle under the name and a
   "more" toggle on a clamped lede, and both were dropped after reading the
   code rather than after writing it:

     - The tagline subtitle would have duplicated the lede on 40 of 49
       listings. `lede` is `it.simple || it.tagline || desc[0]` (app.js:2461)
       and only 9 listings carry `simple`, so for everything else the lede IS
       the tagline and the band would have printed the same sentence twice.

     - The "more" toggle would reveal, at most, the fourth line of a
       three-line clamp. Where the lede came from `desc[0]` the identical
       paragraph is repeated verbatim in the description section further down
       the same page, so the clamp hides nothing that page does not already
       contain; where it came from `simple` or `tagline` it is two to four
       lines to begin with.

   The consequence is worth stating plainly because it retires a whole
   acceptance criterion: with no added nodes, the desktop DOM is unchanged, so
   AC7's "one sanctioned fixture re-baseline" is not needed and v9's existing
   byte-identity gate keeps running untouched.

   HOW THE BAND IS BUILT.

   .detail-head (store.css:735) is a flex row of three children: the icon, an
   identity wrapper carrying an inline `style="flex:1"`, and .cta-col
   (store-v5.css:31). v9 turned that row into a column (store-v9-detail.css
   section 1), which is what put the icon alone on a line.

   v11 makes it a grid with named areas instead, and flattens the two
   wrappers with `display: contents` so their children become grid items
   directly. That is the whole trick: no node moves, no node is added, and
   h1 / .dev / .cta-split / .v4-lede / .v4-benefits / .cta-secondary can each
   be placed where a phone wants them rather than where a desktop flex column
   left them.

   `display: contents` is safe on both wrappers: neither paints (the identity
   wrapper has only the inline flex:1, .cta-col only width/margin/align-self,
   all of which are meaningless once its children are the grid's items), and
   nothing inside either one relies on being positioned against them.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. THE GRID.

   Row order, top to bottom:

     name  |  the listing name, beside the icon
     dev   |  creator plus the Official/Verified badges, beside the icon
     cta   |  the split button, beside the icon - this is the Apple move, and
              it is where most of the 641px goes: the primary action costs no
              vertical space of its own because the icon column is taller than
              two lines of text anyway
     lede  |  full width under the band
     chip  |  the benefits, as a rail (section 3)
     help  |  the CTA helper line
     arrv  |  the attribution line, when a ?via= brought the visitor here
     scnd  |  Save and Share
     sync  |  the save-sync slot

   Named areas with no element in them collapse to a zero-height track, so the
   four optional rows (arrv, sync, and chip/help when a listing has no
   benefits or renders under v0-v4) cost nothing when absent.

   The unscoped selector matches store.css:735's own unscoped rule, so this
   wins on source order at equal specificity. The one property v5 claims
   unconditionally at higher specificity is align-items (store-v5.css:28),
   which v9 already had to match selector-for-selector; on a grid container
   align-items: flex-start would top-align every item in its area, so the same
   override is repeated here for the same reason it existed there. */
@media (max-width: 720px) {
  .detail-head {
    display: grid;
    grid-template-columns: var(--v11-band-icon) minmax(0, 1fr);
    grid-template-areas:
      "icon name"
      "icon dev"
      "icon cta"
      "lede lede"
      "chip chip"
      "help help"
      "arrv arrv"
      "scnd scnd"
      "sync sync";
    column-gap: var(--v11-band-gap);
    row-gap: 0;
    align-items: start;
  }
  /* store-v5.css:28 is `:root[data-ui="v5"] .detail-head { gap:
     var(--space-32); align-items: flex-start }` - specificity (0,2,1) against
     the unscoped rule above, so BOTH of its properties have to be matched
     selector-for-selector or they keep applying at every width. v9 already
     had to do this for align-items and stopped there, because a flex column
     with a 32px gap was the layout it wanted. On a grid that same gap is 32px
     between EVERY one of nine rows: measured 650px of band where the rows
     themselves totalled 366px. The row gap belongs to each element's own
     margin here (they are tuned per pair below), and the column gap is the
     band's, so both are restated. */
  :root[data-ui="v5"] .detail-head {
    align-items: start;
    column-gap: var(--v11-band-gap);
    row-gap: 0;
  }

  /* The two wrappers dissolve. `.detail-head > div` would also match
     .app-icon, which must stay a box, so both are excluded by class. The
     identity wrapper has no class at all (app.js writes it as
     `<div style="flex:1">`), which is why this is a :not() pair rather than a
     name. */
  .detail-head > div:not(.app-icon):not(.cta-col),
  :root[data-ui="v5"] .detail-head > .cta-col,
  :root[data-ui="v5"] .detail-head .cta-block {
    display: contents;
  }

  /* PLACEMENT. Each of these is one line and they are listed in visual order
     so the block reads as the diagram above. */
  .detail-head .app-icon { grid-area: icon; }
  .detail-head h1 { grid-area: name; align-self: end; }
  .detail-head .dev { grid-area: dev; }
  :root[data-ui="v5"] .detail-head .cta-split { grid-area: cta; }
  :root:is([data-ui="v4"],[data-ui="v5"]) .detail-head .v4-lede { grid-area: lede; }
  :root:is([data-ui="v4"],[data-ui="v5"]) .detail-head .v4-benefits { grid-area: chip; }
  :root[data-ui="v5"] .detail-head .cta-helper { grid-area: help; }
  :root[data-ui="v5"] .detail-head .cta-arrival { grid-area: arrv; }
  :root[data-ui="v5"] .detail-head .cta-secondary { grid-area: scnd; }
  :root[data-ui="v5"] .detail-head .save-sync { grid-area: sync; }

  /* The icon grows back. v9 shrank it to 72px because it was stacked alone
     on a line, where every pixel of icon was a pixel of scroll; beside three
     lines of text it is free, and 96px is what Apple uses at this viewport.
     max-width/max-height rather than width/height because iconArt (app.js:739)
     writes `style="width:116px;height:116px"` inline and no external rule can
     out-specificity that without !important. This only SHRINKS 116 to 96,
     which is why max-* is enough here and section 2 of store-v11-rows.css has
     to do something different to make an icon bigger.

     The gradient-branch listings (no generated icon art) centre their glyph
     with an inline font-size sized off the same 116px, equally unreachable
     and with no max-font-size property to repeat the trick on. Scaling the
     already-laid-out glyph is v9's answer (store-v9-detail.css section 1) and
     the ratio just changes: 96/116 = 0.83 where v9 needed 72/116 = 0.62.
     margin: 0 auto is dropped - the icon is in a fixed-width column now, and
     auto margins would slide it off the text it is meant to sit beside. */
  .detail-head .app-icon {
    max-width: var(--v11-band-icon);
    max-height: var(--v11-band-icon);
    margin: 0;
  }
  .detail-head .app-icon .glyph-center {
    transform: scale(0.83);
  }

  /* Vertical rhythm inside the band. store-v4.css section 3 gives each hero
     element a top margin tuned for a desktop column; in a grid those margins
     are what the row-gap is for, so they are reset here and re-stated as the
     four gaps that actually matter. The numbers are small on purpose: the
     name/byline/button stack has to stay under the icon's 96px or the band
     grows for no reason. */
  .detail-head h1 { margin: 0; }
  .detail-head .dev { margin: 4px 0 0; }
  :root[data-ui="v5"] .detail-head .cta-split { margin: 10px 0 0; }
  :root:is([data-ui="v4"],[data-ui="v5"]) .detail-head .v4-lede { margin: 14px 0 0; }
  :root:is([data-ui="v4"],[data-ui="v5"]) .detail-head .v4-benefits { margin: 12px 0 0; }
  :root[data-ui="v5"] .detail-head .cta-helper { margin: 8px 0 0; text-align: left; }
  :root[data-ui="v5"] .detail-head .cta-secondary { margin: 10px 0 0; justify-content: flex-start; }

  /* THE EMPTY SLOT. `.save-sync` (app.js's saveSyncSlot) renders as
     `<div class="save-sync" data-save-sync="...">` with no children until the
     account layer has something to say about where a save was stored. It is
     `display: flex` with its own line-height, so an empty one still measures
     21px and store-v5.css:240 puts 8px above it: 29px of first viewport, on
     every listing, for a message that is not there. Harmless on a 1440px page
     and worth a twenty-fifth of a phone screen.

     `:empty` is the whole fix and it is self-cancelling: the moment the JS
     writes a child the selector stops matching and the slot appears exactly
     as before. Scoped to the band rather than globally, because this sheet
     has no business deciding how the slot behaves anywhere else. */
  .detail-head .save-sync:empty {
    display: none;
  }

  /* store.css pads the head `6px 0 22px`. The bottom half of that is the gap
     to the stats bar on a page where the head is a wide banner; here the
     head's last row already carries its own 10px margin. */
  .detail-head {
    padding-bottom: 12px;
  }

  /* The split button was sized for a 360px desktop column (store-v5.css:31).
     In the band it has whatever is left of the viewport after a 96px icon and
     a 14px gap, so it stretches to that rather than centring in it. */
  :root[data-ui="v5"] .detail-head .cta-split {
    width: 100%;
  }
}

/* --------------------------------------------------------------------------
   1b. BELOW 375px THE BUTTON LEAVES THE BAND.

   Section 1 puts the split button beside the icon, and section 1's last rule
   gives it "whatever is left of the viewport after a 96px icon and a 14px
   gap" - measured, `vw - 138`. The button's content is 136px (18px glyph,
   10px gap, 110px of "Open in Claude") inside a 44px caret and 48px of side
   padding, so it wants 228px and .cta-split has `overflow: hidden`. Below
   about 356px the label runs past .cta-primary and is cut: at 320px, the
   narrowest width this store claims to support, the primary action of the
   whole page reads "Open in Clau".

   This predates the Stack button next to it and is not caused by it - the
   clip measures identically with that button removed from the DOM - but it is
   the same hero, at the same widths, and it is the more serious of the two.

   The band is the right layout everywhere it fits, so this only takes the
   button out of it, one row lower, at full width, which is where a phone this
   narrow wants the primary action anyway. The icon now spans the two text
   rows instead of three; align-items: start already stops it stretching, so
   nothing else in section 1 moves.

   374px, not the measured 356: 356 is one string in one font at one text-size
   setting, and 5px of slack is not a margin. 375 is the narrowest width in
   common use and the one V11-MOBILE-DENSITY-SPEC measured the band against
   (store-v11-density.css:81), so cutting immediately below it protects every
   real handset's layout while giving the ones under it a button that fits
   with 100px to spare. */
@media (max-width: 374px) {
  .detail-head {
    grid-template-areas:
      "icon name"
      "icon dev"
      "cta cta"
      "lede lede"
      "chip chip"
      "help help"
      "arrv arrv"
      "scnd scnd"
      "sync sync";
  }
  /* The 10px that separated it from .dev was a gap inside the band; on its own
     row it is separating two blocks, and the icon is the taller neighbour. */
  :root[data-ui="v5"] .detail-head .cta-split {
    margin: 12px 0 0;
  }
}

/* --------------------------------------------------------------------------
   2. THE LEDE CLAMPS TO THREE LINES.

   Four lines at 375px is 102px of the band measured on frontend-design, and
   the fourth line is the one that pushes the quick-facts strip through the
   fold. -webkit-line-clamp is the only cross-browser way to clamp by LINE
   rather than by height, and it needs the display/box-orient pair to work;
   the prefixed properties are still the standard spelling in every shipping
   engine, `line-clamp` unprefixed is not yet safe to rely on alone, so both
   are set.

   Why no "more" toggle: see this file's header. Nothing on the page becomes
   unreachable, so a toggle would be a control that reveals one line.

   max-width: 46ch (store-v4.css:67) is dropped at this width: 46ch is wider
   than the viewport here, so it never bites, but leaving it in place would
   make the clamp's line count depend on a cap that only exists for desktop. */
@media (max-width: 720px) {
  :root:is([data-ui="v4"],[data-ui="v5"]) .detail-head .v4-lede {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   3. THE BENEFITS BECOME A CHIP RAIL.

   .v4-benefits is `flex-wrap: wrap` (store-v4.css:100). At 375px each
   `.benefit-chip` is a nowrap phrase around 200px wide, so three of them wrap
   to three lines: 111px measured. As a rail they are one 40px line and the
   second and third chips are a thumb-flick away, which is the same trade the
   stats bar already makes one section down (store-v9-detail.css section 3)
   and the same trade Apple makes with every horizontal strip on its page.

   This is store-v9-mobile.css's .v9-rail primitive in its CSS-only form, with
   the mask and the hidden scrollbar copied from the two v9 callers so all
   three rails on this page behave identically. --v9-rail-item is max-content
   for the same reason the stats bar uses it: a chip's width is its phrase,
   and forcing every chip to one shared vw fraction would either truncate the
   long one or pad the short one. */
@media (max-width: 720px) {
  :root:is([data-ui="v4"],[data-ui="v5"]) .v4-benefits {
    --v9-rail-item: max-content;
    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%);
  }
  :root:is([data-ui="v4"],[data-ui="v5"]) .v4-benefits::-webkit-scrollbar { display: none; }
  :root:is([data-ui="v4"],[data-ui="v5"]) .benefit-chip {
    flex: 0 0 var(--v9-rail-item);
    scroll-snap-align: start;
  }
}

/* --------------------------------------------------------------------------
   4. THE QUICK-FACTS STRIP.

   v9 already made .stats-bar scrollable rather than truncated
   (store-v9-detail.css section 3) and this sheet does not touch that
   mechanism. What is left is density and voice: store.css:746 pads the bar
   14px top and bottom and leaves 24px under it, and its values are 19px.
   Apple's equivalent row at this viewport is a taller number over a smaller
   uppercase label with more air between cells than around the strip.

   22px values, 10px of vertical padding and a 16px bottom margin is 20px of
   page back, and the strip reads as the instrument panel it is rather than as
   a bordered band of small print. The .val row's min-height (store.css, the
   comment above it explains it locks six cells to one baseline) is raised in
   step so the AIR shield cell still lines up with the numeric ones. */
@media (max-width: 720px) {
  .stats-bar {
    padding: 10px 0;
    margin-bottom: 16px;
  }
  .stat .val {
    font-size: 22px;
    min-height: 32px;
  }
  .stat .lbl {
    font-size: 10.5px;
    margin-bottom: 3px;
  }
}

/* --------------------------------------------------------------------------
   5. THE SCREENSHOT RAIL, ONE TILE PER VIEW.

   v9 turned the fixed 3-up .shots-row into a rail at 85vw
   (store-v9-detail.css section 4). Measured on the page today that is a 199px
   tall strip, because `.shot` carries `aspect-ratio: 16/10` (store.css:783)
   and 85vw of 375px is 319px wide.

   88vw is the small change: it reads as one screenshot with the next one
   peeking, rather than as a row of three that happens to scroll. The height
   still follows the asset's own ratio, so this is 8px taller, not a redesign.

   What this rail CANNOT do from CSS is match Apple's 312px portrait tiles:
   those are phone-shaped assets, and ours are 16:10 desktop crops. Generating
   portrait variants is an asset-pipeline job and V11-MOBILE-DENSITY-SPEC.md
   section 6 flags it as out of scope rather than pretending a wider landscape
   tile closes that gap. */
@media (max-width: 720px) {
  .shots-row {
    --v9-rail-item: 88vw;
  }
}
