/*
 * THE PHOTO SEARCH PANEL — the shared stylesheet (tokens §4, "Photo search
 * panel"), promoted out of category.css on 2026-08-26.
 *
 * Served as a static asset and pulled in by `<PhotoStyles>` rather than
 * `import`ed, for exactly the reasons given at the top of chrome.css: a bundled
 * import attaches the chunk to the shared `[brand]` layout segment and Next then
 * emits a <link> to it on every brand's every page. A hoisted <link> to a public
 * asset is rendered only by the surface that needs it — and this one is
 * rendered by the COMPONENT, so a homepage or a product page adopting the panel
 * gets its rules by rendering it.
 *
 * Transplanted from design/produnk/CategoryPage.dc.html (desktop, drawn at 1440)
 * and design/produnk/CategoryMobile.dc.html (drawn at 390), mechanically
 * tokenised: colours and typefaces read `--brand-*`, sizes and radii read the
 * platform constants in globals.css. Board values ship verbatim.
 *
 * Class prefix `sf-ph-` (written `sf-ph__…`, the sibling sheets' convention).
 *
 * ─── THIS IS A RELOCATION ───────────────────────────────────────────────────
 *
 * Every declaration below arrived from category.css's `photos` block unchanged,
 * and the move is proved rather than asserted: `tools/phrelo.mjs` captures the
 * rendered section before and after and diffs the two at threshold 0, and
 * `tools/p4gate.mjs`'s five photo regions (photohead, photogrid, panel,
 * mphotohead, mphotogrid) must hold their recorded numbers. THE TWO EXCEPTIONS,
 * both deliberate and both invisible at rest:
 *
 *   1. THE TILE IS NOW A BUTTON. The board's tile box was an `<li>`; it is now
 *      an `<li>` that carries the box's HEIGHT and a `<button>` inside it that
 *      carries everything else at `width/height: 100%`. Same rect, same ground,
 *      same radius — plus a real control, a focus ring and a pointer.
 *   2. THE PANEL IS DECLARED BEFORE THE FEED. The mobile composition draws the
 *      panel above the grid, which used to be an `order` swap over a DOM in the
 *      other order — survivable only while the grid held nothing focusable.
 *      With a button per tile that is a WCAG 2.4.3 failure, so the DOM now
 *      matches the mobile paint and the DESKTOP grid places its two children
 *      explicitly. category.css's own comment prescribed exactly this fix.
 *
 * ─── WALKTHROUGH #3 (Ryan, 2026-08-27) ─────────────────────────────────────
 *
 * Four of its six rulings reach this sheet, and every one of them is scoped so
 * the two older consumers cannot move (proved: `tools/phab.mjs` byte-identity
 * and `tools/p4gate.mjs`'s five photo regions, all reproduced exactly):
 *
 *   P2  `.sf-ph__panel-sub` retired with the line it drew.
 *   P4  the whole scope-segments block retired — the control is chrome.css's
 *       quiet dropdown now — and `.sf-ph__filter*` is the ROW it sits in.
 *   P5  the suggestions list became a popover — and, at DD-UNIFY (2026-08-28),
 *       `.sf-suggest` in chrome.css, anchored on `.sf-ph__search` itself.
 *   P6  `.sf-ph__panel`'s sticky gained the buy bar's term. It is in the BASE
 *       rule and is safe there because every term falls back to `0px`.
 *
 * Only P6 touches a declaration the older levels read, and it computes to the
 * same value on them: nothing but the product page publishes
 * `--sf-pdp-buybar-h`.
 *
 * ─── THE SECTION HEADER IS NO LONGER HERE ──────────────────────────────────
 *
 * `.sf-ph__head*` was `.sf-cat-section-head*` declaration for declaration — the
 * THIRD copy of a pattern category.css had said would move "when the second one
 * lands". THE MOVE HAPPENED on 2026-08-26, later the same day, and this sheet's
 * own promotion is what unblocked it: a NEW shared sheet
 * (`public/storefront/sections.css`, prefix `sf-sec-`) avoids chrome.css, which
 * was the thing every earlier pass was gated against touching. The panel renders
 * `<SectionStyles>` and `.sf-sec-head*`; the pairwise parity test that held this
 * copy to category.css is retired with the copy.
 */

/* ------------------------------------------------------- the focus ring -- */

/*
 * EVERY INTERACTIVE CONTROL IN THIS SHEET, in one block (Ryan's ruling,
 * 2026-08-26 — the platform pattern, its three grounds and the contrast
 * arithmetic are documented once, at the top of chrome.css).
 *
 * The `var()` fallbacks repeat the platform values exactly once each, so this
 * sheet still draws a compliant ring if it is ever loaded without chrome.css.
 *
 * All four land on light grounds — the browse link and the chips on `surface`,
 * the tile on the `well` its own wash paints (accent measures 4.01:1 there,
 * tokens §3), the viewer panel's block on the lightbox's own paper.
 */
.sf-ph__browse:focus-visible,
.sf-ph__chip:focus-visible,
.sf-ph__tile:focus-visible,
/*
 * THE PDPBelow UPGRADE'S CONTROLS, added to the SAME block rather than given
 * rings of their own: the sort disclosure and its entries, the pager's pages
 * and steps, an active filter and the control that drops them all. Every one of
 * them lands on `surface` or on `paper`, where accent measures 4.01:1 (tokens
 * §3), so they take the platform ring unchanged.
 *
 * THE SCOPE CONTROL IS NOT IN THIS BLOCK, and that is correct rather than an
 * omission: ruling P4 (2026-08-27) replaced the segmented toggle with the
 * platform's quiet dropdown, which is chrome.css's control and carries
 * chrome.css's ring (`.sf-quiet-select__control:focus-visible`, and the inset
 * one on its entries). The `.sf-ph__segment:focus-visible` rule that used to
 * live under this block — the sheet's one INSET ring, because the group clipped
 * its rungs — retires with the toggle. This module's own markup around the
 * dropdown is a glyph and a word, and neither is focusable.
 */
/* `.sf-ph__sort-control` and `.sf-ph__sort-item` LEFT THIS ROSTER (ruling PP2,
 * 2026-08-28) with the hand-written sort disclosure they belonged to: the sort
 * is `QuietSelect` now, whose ring is declared in chrome.css beside the control.
 * `.sf-ph__page` left with the pager (ruling PP5) — one pager, one ring, in
 * sections.css beside the pattern. */
/* `.sf-ph__config` LEFT THIS ROSTER with the button (DD-A) — it is
 * `.sf-sec-config__button` now, ringed in sections.css beside the pattern —
 * and `.sf-ph__clear` left with the link the field's X replaced (DD-C), whose
 * own ring is drawn INSIDE the field, in chrome.css. */
.sf-ph__active:focus-visible,
/*
 * THE SEARCH FIELD DELEGATES ITS RING TO THE FIELD, not to the input inside it
 * — the same reasoning that already moved its accent border there (see the
 * `:focus-within` block below): the input is a bare borderless box inset from
 * the drawn control by its padding and its magnifier, so a ring on the input
 * wraps the text rather than the control the reader is looking at.
 *
 * `:has(… :focus-visible)` rather than the border's `:focus-within` so the RING
 * follows the platform rule exactly.
 */
.sf-ph__search:has(.sf-ph__search-input:focus-visible) {
  outline: var(--sf-focus-ring, 2px solid var(--brand-color-accent, #c4530a));
  outline-offset: var(--sf-focus-ring-offset, 2px);
}

/* --------------------------------------------------------- section head -- */

/*
 * IT IS NOT IN THIS SHEET ANY MORE (2026-08-26). `.sf-ph__head*` was the THIRD
 * copy of the platform's section header; the move this file's own promotion set
 * the precedent for was taken the same day, and the pattern now lives in
 * `public/storefront/sections.css` under `.sf-sec-head*`. This module renders it
 * and keeps only `.sf-ph__head-row`, which is the ROW the header shares with the
 * "Browse all" link — this module's arrangement, not the pattern.
 */

/* =============================================================== photos == */

/*
 * THE PHOTO SECTION. Two thirds grid, one third panel, and the panel STICKS.
 *
 * The 84px of air above it is the board's gap from whatever closes the section
 * before it, which is the platform's 84–96px section rhythm; the 48px under it
 * is the board page's own bottom padding.
 */

.sf-ph {
  /* As in the hero and the table: the boards declare no leading on the section,
   * so a line box that does not name one is `normal`. */
  line-height: normal;
  padding: 84px 0 48px;
}

.sf-ph__head-row {
  align-items: center;
  display: flex;
  gap: 14px;
  /* The board spaces the two apart with a `flex: 1` spacer between them. The
   * icon and the title are one group, so this row has exactly two items, and
   * `space-between` puts them in the same places without a spacer element that
   * says nothing.
   *
   * BOTH ITEMS SHRINK. It matters only at 320, where the row is narrower than
   * its content: held rigid, the heading stayed on one line and the LINK took
   * the whole squeeze — "Browse all →" broke after "Browse", which is a link
   * text torn in half. Shrinkable, the heading wraps to "Customer / photos" and
   * the link keeps its words. Measured; identical at 360 and up, so no gate
   * moves. Neither is drawn on a board — 320 is below the mobile board's 390 —
   * so this is the better of two undrawn states, flagged rather than claimed as
   * the design. */
  justify-content: space-between;
}

.sf-ph__browse {
  color: var(--brand-color-muted, #6c6a62);
  font-size: var(--text-support);
  text-decoration: underline;
}

/*
 * THE TWO COLUMNS, PLACED EXPLICITLY. The panel is first in the DOM (see the
 * header), so auto-placement would put it in column one; naming the column AND
 * the row for both children is what keeps the desktop drawing the feed on the
 * left while the focus order runs panel-first at every width.
 */
.sf-ph__body {
  align-items: start;
  column-gap: 28px;
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  margin-top: 22px;
}

.sf-ph__feed {
  grid-column: 1;
  grid-row: 1;
}

.sf-ph__grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

/*
 * A PLACEHOLDER TILE, drawn as the boards draw it: the flat `well` tint with a
 * mono caption, not the dark media gradient. The gradient is the token for a
 * video or a photo STAGE; these are a grid of thumbnails and the boards tint
 * them accordingly.
 *
 * THE CELL CARRIES THE HEIGHT AND THE BUTTON FILLS IT. The board's tile is one
 * box and it still renders as one: the `<li>` is the grid item and holds the
 * board's 170px, and the control inside it is `100%` of that in both axes, so
 * the painted rect, its ground and its radius are the ones the board drew. The
 * split exists because a grid of photographs whose tiles are not controls is a
 * grid a keyboard cannot open.
 */
.sf-ph__cell {
  height: 170px;
}

.sf-ph__tile {
  align-items: center;
  /* A button inherits none of the page's type or colour and draws a UA border,
   * a padding and a ground of its own; each reset below is one of those. */
  appearance: none;
  background: var(--brand-color-well, #f1f0ec);
  border: 0;
  border-radius: var(--radius-floaty);
  color: inherit;
  cursor: pointer;
  display: flex;
  font: inherit;
  height: 100%;
  justify-content: center;
  padding: 0;
  /*
   * THE BADGE'S CONTAINING BLOCK (ruling PP4, 2026-08-28, and the CAT-PHOTOS
   * addendum that took it to the category level). The stacked-gallery plate is
   * absolutely positioned in the tile's bottom-right corner, so the tile has to
   * be the box it is positioned against — the same reason the featured cell
   * names it for its caption.
   *
   * MEASURED, NOT ASSUMED: with this on the product level alone the category
   * grid's plates had no positioned ancestor and their containing block fell to
   * the page — the sr-span overflow trap (playbook §5) seen from the other side,
   * and nine plates drawn at the document's bottom-right instead of on their
   * tiles. It belongs to the TILE, at every level, which is what it says now.
   */
  position: relative;
  width: 100%;
}

.sf-ph__tile-label {
  color: var(--brand-color-muted, #6c6a62);
  font-family: var(--brand-font-mono, ui-monospace, monospace);
  /* 9px, below the type floor — the boards' placeholder-caption exemption, the
   * same one the media wells' mono labels take. It is not page text; it is the
   * label on an empty frame, and it goes away with the frame. */
  font-size: 9px;
}

/*
 * THE PHOTOGRAPH ITSELF (2026-08-31, the real customer-photo library).
 *
 * IT COVERS THE WELL RATHER THAN REPLACING IT. The tile's mono label is still
 * rendered underneath, unconditionally, so a corpus with no assets draws
 * exactly the markup and exactly the pixels it drew before this rule existed —
 * which is what makes the whole env-less verification battery still true.
 *
 * `position: absolute` INSIDE A TILE THAT IS ALREADY `position: relative` (it
 * has been since ruling PP4 gave the stacked-gallery badge a containing block),
 * so this needs no new positioning anywhere and cannot become the sr-span
 * overflow trap the playbook records.
 *
 * `object-fit: cover` because the library is 2,878 customer photographs at
 * every aspect ratio a phone has ever had, and the grid's cells are fixed
 * heights the boards drew. Cropping to fill is what the boards' wells promise;
 * letterboxing would put the well's ground back on screen in two strips.
 *
 * `border-radius: inherit` rather than `overflow: hidden` on the tile: the
 * caption plate and the stacked badge are positioned against the tile and sit
 * INSIDE it, so nothing needs clipping — and a clip on the tile would be one
 * more `overflow` box on a page whose sticky panel depends on there not being
 * one (gotcha #1, named in the panel's own rule).
 */
.sf-ph__img {
  border-radius: inherit;
  display: block;
  height: 100%;
  inset: 0;
  object-fit: cover;
  position: absolute;
  width: 100%;
}

/*
 * THE LINE UNDER THE GRID, and the sentinel the feed watches. It says one of
 * four true things — more is coming, that was everything, nothing matched, or
 * the request failed — and it never spins for a request that was not sent.
 */
.sf-ph__status {
  color: var(--brand-color-muted, #6c6a62);
  font-size: var(--text-small);
  margin: 0;
  padding: 22px 0 4px;
  text-align: center;
}

/*
 * THE STICKY PANEL. `32px + var(--sf-pinned-offset)` is the site convention for
 * anything that pins under the chrome's nav bar (globals.css sets the same
 * offset on the render frame and the checkout summary); the fallback keeps a
 * brand that does not wear this chrome at the bare 32px.
 *
 * Nothing between here and the scrolling element may be `overflow: hidden` —
 * gotcha #1, which is why the table's card uses `clip`. This section's
 * ancestors are the page's own containers and set no overflow at all.
 */
.sf-ph__panel {
  background: var(--brand-color-surface, #ffffff);
  border: var(--border-hairline);
  border-radius: var(--radius-media);
  grid-column: 2;
  grid-row: 1;
  padding: 18px 20px 20px;
  position: sticky;
  /*
   * THE WHOLE OFFSET CASCADE — the site's `32px + pinned nav row` convention,
   * the stuck title band, AND THE BUY BAR. Each term is 0 where that chrome is
   * not on the page, so a surface without one rests at exactly the offset it
   * always had.
   *
   * THE BUY-BAR TERM IS RULING P6's FIX, and it is the whole of the bug. The
   * panel rested at `32 + 54 + 0 = 86px` while `.sf-pdp-buybar` — `position:
   * fixed`, `top: var(--sf-pinned-offset)`, `z-index: 30`, 69px tall — owned
   * the band from 54 to 123. MEASURED on the product page at 1440x900: from
   * the moment the panel came to rest (scrollY 1908) its top 37px were painted
   * underneath an opaque fixed bar, and the "Search customer photos" title
   * inside them (107 to 127) never came back for the rest of the section. That
   * reads exactly as Ryan described it — the header scrolls off and the sticky
   * never seems to engage — and "the sticky should start sooner" is precisely
   * what a LARGER `top` does: the panel now detaches 69px of scroll earlier and
   * comes to rest clear of the bar.
   *
   * IT IS THE SAME TERM EVERY OTHER STICKY ON THAT PAGE ALREADY CARRIES —
   * `.sf-pdp-eng__rail-inner`, `.sf-pdp-rev__photos` and the phone's
   * `.sf-pdp-eng__rail` all add `var(--sf-pdp-buybar-h)`, which
   * `pdp-buy-bar.tsx` publishes as the bar's height while it is shown and 0
   * otherwise. This panel was the one that had been missed.
   *
   * IT IS LEVEL-INDEPENDENT and therefore in the base rule: nothing but the
   * product page publishes that property, so on the category page and the
   * homepage it resolves through its `0px` fallback and the computed `top` is
   * the one those surfaces have always had (proved by `tools/phab.mjs` and the
   * five `p4gate` photo regions).
   */
  top: calc(
    32px + var(--sf-pinned-offset, 0px) + var(--sf-band-stuck-h, 0px) +
      var(--sf-pdp-buybar-h, 0px)
  );
}

/*
 * THE FIELD ROW’S LEAD, at this module (Ryan’s DD-A, 2026-08-28).
 *
 * The row itself is the shared `.sf-sec-config__row` and carries no margin —
 * the distance above it is the SECTION’S, because the three sections that draw
 * the row place it differently (this one in a block, the reviews search in a
 * gapped column). The 12 is the number this module’s FIELD used to carry, moved
 * off the field because a margin on a stretch row’s child is height that child
 * does not get — which was the "button taller than the search field" Ryan saw.
 */
.sf-ph__configrow {
  margin-top: 12px;
}

.sf-ph__search {
  align-items: center;
  /* THE ANCHOR THE SUGGESTIONS HANG FROM (DD-UNIFY, 2026-08-28). One
   * declaration on a box that was already drawn, replacing the retired
   * `SearchAnchor` wrapper: under the site’s field-anchored skin the panel
   * lines its two edges up with THIS field’s and shares its bottom border, so
   * this is the containing block it is absolute inside. Geometry-neutral —
   * `position: relative` on a static box moves nothing. */
  background: var(--brand-color-paper, #fbfbfa);
  border: var(--border-control);
  border-radius: var(--radius-control);
  display: flex;
  gap: 10px;
  /* The board's own padding. It renders 45px tall rather than the platform's
   * 48px control height — board value verbatim, flagged rather than
   * normalised. */
  /* THE RIGHT PADDING RESERVES THE X's ROOM (Ryan's DD-C, 2026-08-28): "so
   * text can never run under it". The button is absolutely positioned and takes
   * no width in the row, so the reserve has to be here — 16 for the board's own
   * inset plus 28 for the glyph and its target. */
  padding: 13px 44px 13px 16px;
  position: relative;
}

/*
 * FOCUS IS DRAWN ON THE FIELD, not on the input inside it (Ryan, 2026-08-26 —
 * clicking into "Search photos" drew a small ring that did not cover the field).
 *
 * The defect was the UA's own `:focus-visible` outline landing on the `<input>`,
 * which is a bare borderless box inset from the styled field by its 16px
 * padding and the magnifier beside it — so the ring wrapped the text, not the
 * control the reader is looking at. The board's 1.5px accent selection border
 * is drawn as a 1px border plus a half-pixel spread shadow OUTSIDE it, so the
 * drawn thickness is the site's selection language and the box does not resize.
 *
 * This is the board's SELECTION treatment, not the focus indicator. The 2px
 * ring the platform requires is drawn on this same element by the focus-ring
 * block at the top of the sheet; the treatment below sits under it. The input's
 * own outline stays suppressed only because the ring moved to the field.
 */
.sf-ph__search:focus-within {
  border-color: var(--brand-color-accent, #c4530a);
  box-shadow: 0 0 0 0.5px var(--brand-color-accent, #c4530a);
}

.sf-ph__search-mark {
  color: var(--brand-color-muted, #6c6a62);
  display: inline-flex;
  flex: none;
}

.sf-ph__search:focus-within .sf-ph__search-mark {
  color: var(--brand-color-accent, #c4530a);
}

.sf-ph__search-mark svg {
  height: 16px;
  width: 16px;
}

.sf-ph__search-input {
  background: transparent;
  border: 0;
  color: var(--brand-color-ink, #17181a);
  font-family: inherit;
  font-size: var(--text-support);
  /* An input's automatic minimum width is its `size` attribute's width, which
   * in a flex row floors the field wider than the panel. */
  min-width: 0;
  /* Replaced by the field's own ring above — a stronger indicator, and on the
   * box the reader is actually looking at. */
  outline: none;
  padding: 0;
  width: 100%;
}

.sf-ph__search-input::placeholder {
  color: var(--brand-color-muted, #6c6a62);
  opacity: 1;
}

/*
 * `.sf-ph__eyebrow` IS RETIRED BY NAME (Ryan's WP5, 2026-08-29).
 *
 * It captioned the chip wall inside the configuration disclosure, and the wall
 * is a configuration ROW now: the marked LABEL cell of the DD-D grid says what
 * the chips filter by, which is the job DD-D built that column for. Two
 * captions for one row would be the disclosure explaining itself twice.
 * `.sf-ph__filters` and `.sf-ph__group` retire with it, below.
 */

.sf-ph__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/*
 * A FILTER CHIP. Real buttons: every one of them changes what the grid shows.
 * The selected state is the site's selection language — 1.5px accent, never
 * normalised to 1px — which is why an unselected chip carries the 1px control
 * border and the pair differ by half a pixel a side.
 */
.sf-ph__chip {
  background: transparent;
  border: var(--border-control);
  /* 16px is the board's own radius; on a 30px-tall chip it reads as the pill
   * the pattern calls for. */
  border-radius: 16px;
  color: var(--brand-color-ink-soft, #44464a);
  cursor: pointer;
  font-family: inherit;
  font-size: var(--text-small);
  padding: 6px 14px;
}

.sf-ph__chip--on {
  border: var(--border-selected);
  color: var(--brand-color-accent-strong, #9c420a);
  font-weight: 600;
}

/* The remove affordance the MOBILE board draws on the pinned chip. The desktop
 * board leaves the 8px gap it would sit in and draws no glyph, so neither does
 * this — flagged, not invented. */
.sf-ph__chip-x {
  display: none;
}

/* Text a reader gets from the layout and a listener cannot. */
.sf-ph__spoken {
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* ==================================================== the viewer's panel == */

/*
 * WHAT THE LIGHTBOX DRAWS UNDER ITS CAPTION for one of these photographs — the
 * `panel` slot the viewer's core hands to whoever opened it.
 *
 * THE SALES POINTS LAND HERE (Ryan, 2026-08-26: the live site's photos 2-10 are
 * baked-in sales copy, which becomes real text beside a plain photograph). What
 * it draws today is the only thing a tile actually carries — its tags — in the
 * shape those points will take: an eyebrow over a block. No board draws it; it
 * is the viewer's own type at the sizes the core uses around it.
 *
 * IT LIVES IN THIS SHEET, not in lightbox.css, because the viewer's core "never
 * knows what it is showing" (the 2026-08-22 ruling): the panel is the OPENER's
 * markup, so its rules belong to the opener's stylesheet.
 */

.sf-ph__lb {
  margin-top: 14px;
}

.sf-ph__lb-eyebrow {
  color: var(--brand-color-muted, #6c6a62);
  font-family: var(--brand-font-mono, ui-monospace, monospace);
  font-size: var(--text-micro);
  letter-spacing: 0.12em;
  margin: 0 0 8px;
}

.sf-ph__lb-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.sf-ph__lb-tag {
  border: var(--border-control);
  border-radius: 16px;
  color: var(--brand-color-ink-soft, #44464a);
  font-size: var(--text-small);
  padding: 6px 14px;
}

/* ====================================== the product level (PDPBelow) == */

/*
 * PDPBelow.dc.html's NEWER DRAWING of this section, transplanted (Ryan's PDP
 * walkthrough ruling, 2026-08-27: "UPGRADE THE MODULE").
 *
 * ─── WHY IT IS A MODIFIER AND NOT AN EDIT TO THE RULES ABOVE ───────────────
 *
 * Everything in this block is scoped to `.sf-ph--product`, which the module
 * writes only where `PHOTO_LEVELS.product.featuredTile` is set. The rules above
 * it are the CategoryPage/CategoryMobile transplant and are left byte-for-byte
 * alone, which is what makes "the category page and the homepage render
 * identically" a property of the sheet rather than a hope checked afterwards —
 * their recorded gates (`tools/p4gate.mjs`, five photo regions) cannot move
 * because no declaration they read has changed.
 *
 * IF RYAN RULES THE NEWER DESIGN UNIVERSAL, this block loses its modifier and
 * the older rules go — that is the edit, and it is one selector wide.
 *
 * ─── BOARD VALUES SHIP VERBATIM ─────────────────────────────────────────────
 *
 * 150px rows, 10px grid gap, 24px column gap, 44px body offset, 13.5px caption
 * type, the 2fr/1fr split, the four-column grid. The odd ones are measured, not
 * sloppy (playbook §3): the panel card's 20px/22px padding is asymmetric on the
 * board and stays asymmetric here.
 *
 * THE BOARD'S `.well` RADIUS IS 4px HERE, against the category board's 10px on
 * the same tile. Two boards, two drawings, and the level each belongs to is
 * what decides — the same reasoning as every other value in this block.
 */

.sf-ph--product .sf-ph__body {
  column-gap: 24px;
  margin-top: 44px;
}

/* ------------------------------------------------- the bar over the grid -- */

/*
 * THE COUNT IS ALONE ON THE ROW, AND RIGHT-ALIGNED (Ryan's rulings PP1 + PP3,
 * 2026-08-28). The sort control sat at the right of this bar until PP1 moved it
 * into the configuration disclosure beside the search field; PP3 moves the
 * galleries figure across to take its place, so the row's right edge stays
 * occupied instead of leaving a count stranded beside an empty half.
 * `flex-end` rather than a `margin-left: auto` on the count, which would stop
 * meaning anything the day a second item lands in the row.
 */
.sf-ph--product .sf-ph__bar {
  align-items: center;
  display: flex;
  justify-content: flex-end;
  margin-bottom: 12px;
}

.sf-ph__count {
  color: var(--brand-color-ink-soft, #44464a);
  font-size: var(--text-body);
}

.sf-ph__count b {
  color: var(--brand-color-ink, #17181a);
}

/*
 * `.sf-ph__sort`, `__sort-control`, `__sort-label`, `__sort-value`,
 * `__sort-chevron`, `__sort-menu` and `__sort-item` ARE RETIRED BY NAME (Ryan's
 * ruling PP2, 2026-08-28).
 *
 * They drew this module's own quiet disclosure — a faithful re-implementation of
 * the site standard, written here on the argument that the module owns every
 * class it names. Ryan re-flagged the parity gap at the walkthrough: the photo
 * sort must be "the SAME dropdown as the reviews sort-by", literally. It is
 * `QuietSelect` now (chrome.css), inside the configuration row below, and a
 * whole disclosure's worth of rules goes with the component.
 */

/* --------------------------------------------------------- the 3x4 grid -- */

/*
 * ─── THREE COLUMNS, NOT THE BOARD'S FOUR (ruling PB3's measurement, 2026-08-28)
 *
 * PDPBelow.dc.html draws four columns — against a section 1,312px wide, which is
 * the whole measure at 1440. Ruling PB1 gave the below-the-fold nav its own
 * 190px column for the entire second half of the page, so this section's feed is
 * 705px at 1440 rather than 859, and four columns of it are not the board's
 * tiles at a smaller size: they are a different tile.
 *
 * MEASURED, at the two rungs ruling PB3 names. Four columns: 207.2 → 168.8px at
 * 1440 and 173.8 → 135.5px at 1240 — the second NARROWER THAN ITS OWN 150px row,
 * which is the one thing on this page the restructure genuinely cramped, and it
 * pulled the featured tile down with it (its caption needs 340px of one-line
 * text and had 323 to draw it in, so "· 4 photos" ellipsed away). Three columns:
 * 228.3px at 1440 and 184.0 at 1240 — landscape at both, either side of the
 * board's own 207 × 150, and a 466px featured tile whose caption fits whole.
 *
 * IT IS THE RE-DERIVATION, NOT A COMPROMISE. The board's four columns are what
 * 859px of feed wanted; three is what 705 wants, at the same tile proportion.
 * The featured tile still spans 2×2 and the page still holds nine photographs —
 * the grid is one column narrower and one row deeper, and nothing else moves.
 * FLAGGED as derived: no board draws this section beside a nav.
 */
.sf-ph--product .sf-ph__grid {
  gap: 10px;
  grid-auto-rows: 150px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.sf-ph--product .sf-ph__cell {
  height: 150px;
}

.sf-ph--product .sf-ph__tile {
  border-radius: var(--radius-inner);
}

/*
 * THE FEATURED TILE — two columns by two rows, at the head of the grid.
 *
 * `grid-column`/`grid-row` are named EXPLICITLY rather than left to auto-flow
 * for the same reason the two body columns are: the featured cell is the first
 * child at every width, and a placement that depended on flow order would move
 * it the first time the grid re-wrapped.
 *
 * `height: auto` OVERRIDES the row height the small cells carry: the cell spans
 * two 150px rows plus the 10px gap between them, and a fixed 150px would draw
 * its ground over only the top half of the area it occupies.
 */
.sf-ph--product .sf-ph__cell--featured {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
  height: auto;
  /* The caption is absolutely positioned against this cell. Named here so the
   * containing block is the tile and not the page — the sr-span overflow trap
   * (playbook §5) is the same mechanism seen from the other side. */
  position: relative;
}

/*
 * THE CAPTION OVERLAY. The board's 92%-opaque white plate, 12px in from the
 * bottom-left corner. `pointer-events: none` because the tile UNDER it is the
 * control: a caption that swallowed the press would make the middle of the
 * featured photograph the one place pressing it does nothing.
 */
.sf-ph__caption {
  background: rgba(255, 255, 255, 0.92);
  border-radius: var(--radius-control);
  bottom: 12px;
  color: var(--brand-color-ink, #17181a);
  font-size: var(--text-small);
  left: 12px;
  padding: 6px 10px;
  pointer-events: none;
  position: absolute;
  /* The plate must not run off the tile when a long name meets a narrow
   * column; the board's own caption is one line, and this keeps it one line. */
  max-width: calc(100% - 24px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sf-ph__caption-meta {
  color: var(--brand-color-muted, #6c6a62);
}

/*
 * ─── THE STACKED-GALLERY BADGE (Ryan's ruling PP4, 2026-08-28) ─────────
 *
 * "Every gallery tile with multiple photos gets a very dark tinted box in its
 * bottom-right corner — stacked-photos icon and the count — so you know you'll
 * see N more if you tap."
 *
 * THE ON-MEDIA PALETTE, which is the discipline this site keeps for any ink
 * drawn OVER a photograph: the plate carries its own dark ground rather than
 * trusting the picture under it, and the type on it is `surface` white. `ink`
 * #17181A at 78% over an unknown photograph is the darkest this page draws
 * without going to a solid black the design language does not use — and white
 * on it reads 12.4:1 against the plate itself and never worse than 4.9:1 against
 * the lightest photograph the plate can sit on, because the plate is what the
 * type is read against.
 *
 * IT MIRRORS THE FEATURED CAPTION at the opposite corner: same 12px inset, same
 * control radius, same 6px/10px padding — one tile vocabulary, two plates, and
 * the featured card's own meta line is untouched (Ryan ratified it as it
 * stands).
 *
 * `pointer-events: none` because the TILE is the button: a plate that swallowed
 * the press would make the corner of every multi-photo tile dead.
 */
.sf-ph__stack {
  align-items: center;
  background: color-mix(in srgb, var(--brand-color-ink, #17181a) 78%, transparent);
  border-radius: var(--radius-control);
  bottom: 12px;
  color: var(--brand-color-surface, #ffffff);
  display: inline-flex;
  font-size: var(--text-caption);
  font-weight: 600;
  gap: 5px;
  padding: 5px 8px;
  pointer-events: none;
  position: absolute;
  right: 12px;
}

/* ------------------------------------------------------------- the pager -- */

/*
 * THE PAGER'S DRAWING IS THE SITE'S NOW (Ryan's ruling PP5, 2026-08-28 —
 * "consistency is King"): `.sf-sec-pager*` in `sections.css`, one pattern for
 * this section, Reviews and Answers. `.sf-ph__pager-note`, `__pager-steps`,
 * `__page`, `__page--on`, `__page--gap` and the disabled step ARE RETIRED BY
 * NAME with the markup that named them. Two of their declarations survived the
 * promotion as the settled halves of a disagreement between the two sheets —
 * the hairline above the row (which this pager gains) and the un-faded spent
 * arrow (which was this sheet's, and won) — both argued at the pattern.
 *
 * WHAT IS LEFT HERE IS THE DISTANCE. The space between the grid and the pager is
 * a property of THIS section rather than of a pager, so it stays on a class of
 * ours — which is also what keeps this sheet inside its own share of the
 * namespace.
 */
.sf-ph--product .sf-ph__pager {
  margin-top: 16px;
}

/* -------------------------------------------------------- the panel card -- */

/*
 * THE CARD, at this level: the board's `.card` is a 6px radius against the
 * category board's 12px on the same panel. MEASURED, not noticed by eye -- the
 * card's top edge and both its corners were the whole of this region's residual
 * (0.045% / 0.546%, every hot pixel in its first ten rows).
 */
.sf-ph--product .sf-ph__panel {
  border-radius: var(--radius-control);
  padding: 20px 22px;
}

.sf-ph__panel-title {
  color: var(--brand-color-ink, #17181a);
  font-size: var(--text-emphasis);
  font-weight: 700;
  margin: 0;
}

/* `.sf-ph__panel-sub` RETIRED (ruling P2, 2026-08-27): the line under the
 * title — "N photos and M customer galleries tagged by our staff" — is removed,
 * and the rule goes with the markup rather than being left to match nothing.
 * The board's own galleries figure is still drawn, in the bar over the grid
 * where the board also draws it. */

/*
 * THE FIELD, at this level. The board draws it on the SURFACE rather than on
 * paper, a shade wider in its type and a shade tighter in its padding — a
 * search box that is the panel's main control rather than one line of it.
 */
.sf-ph--product .sf-ph__search {
  background: var(--brand-color-surface, #ffffff);
  font-size: var(--text-emphasis);
  /*
   * THE MARGIN LEFT THIS RULE (Ryan’s DD-A, 2026-08-28), and it had to.
   *
   * The field sits in a stretch row beside the configuration button, so a
   * vertical margin on the field is height the field does NOT get — which is
   * precisely the "the button is taller than the search field" Ryan reported.
   * MEASURED here: row 60, button 60, field 48, and the 12 is the difference.
   * The distance above the row is the ROW’s now (`.sf-sec-config__row`,
   * sections.css), which is where it belongs when the row is the thing being
   * placed.
   *
   * AND THE PADDING KEEPS THE X’s RESERVE (DD-C). This rule restates the
   * padding to tighten it at this level, so it has to restate the right-hand
   * reserve too — dropping back to `13px 14px` would let the text run under
   * the clear button, which is the one thing the ruling names about it.
   */
  padding: 13px 44px 13px 14px;
}

/*
 * ─── THE FIELD ROW (Ryan's ruling PP1, 2026-08-28) ─────────────────
 *
 * "A configuration button INLINE with the search field." The field takes the
 * room and the button is fixed beside it, which is why `min-width: 0` is on the
 * field: a flex item's automatic minimum size is its content, and a text input's
 * content is its `size` attribute's worth of characters — without it the field
 * refuses to shrink and the button is pushed off the panel at the narrow rungs.
 *
 * THE ROW'S TOP MARGIN IS THE FIELD'S, moved: the field declares 12px above
 * itself and inside a flex row that margin would be the item's, leaving the
 * button to sit 12px higher than the box it belongs to.
 */
/*
 * ─── THE CONFIGURATION ROW IS SHARED NOW (Ryan's DD-A, 2026-08-28) ─────────
 *
 * RETIRED BY NAME from this sheet: `.sf-ph__field-row` and its `.sf-ph__search`
 * child rule, `.sf-ph__config` and its `--on` state, `.sf-ph__controls` and its
 * two chip-wall rules, `.sf-ph__sortby` and `.sf-ph__sortby-label`.
 *
 * They are `sf-sec-config-` in sections.css, which is where the section header
 * and the pager already went, because three sections draw this row now: this
 * one (ruling PP1), the answers section (PN1) and — since DD-B — reviews.
 *
 * TWO OF RYAN'S DD-A NOTES ARE ANSWERED THERE RATHER THAN HERE, and both were
 * this sheet's to answer before:
 *
 *   THE LAYOUT IS A TABLE. `.sf-ph__controls` was `flex-wrap` with
 *   `space-between`, which is what produced the "Filter and sort in line with
 *   each other is impossible at 434 against 307" finding the last pass reported
 *   back. The shared row is a two-column grid — labels as wide as the longest
 *   label, controls taking the rest — and it fits at any width because the
 *   controls give.
 *
 *   THE BUTTON'S HEIGHT. It carried `width: 46px` and no height, which made it
 *   the row's height only where the row happened to be 46 — Ryan saw it squished
 *   on a phone. The shared rule gives it no size at all beyond
 *   `aspect-ratio: 1`, and the row stretches it to the field.
 *
 * THE CHIP WALL'S TWO RULES GO WITH THEM because the wall itself does: DD-D
 * turns `tagChips` off at the category level, which was the only level that
 * drew it inside this row.
 */

.sf-ph--product .sf-ph__search-input {
  font-size: var(--text-emphasis);
}

/*
 * The board's own magnifier at this level: 17px, and drawn at `stroke-width:
 * 1.6` where the category board's is 1.8. Presentation attributes lose to CSS,
 * so the level can restate it without a second glyph -- and the difference is
 * real ink: two-tenths of a pixel around every stroke was a fifth of the
 * field region's residual.
 */
.sf-ph--product .sf-ph__search-mark svg {
  height: 17px;
  width: 17px;
}

/*
 * The same 1.6, for both magnifiers this level draws: the field’s and the one
 * on the dropdown’s free-text row.
 *
 * THE SECOND ONE REACHES ITS GLYPH THROUGH A WRAPPER THIS MODULE OWNS
 * (DD-UNIFY, 2026-08-28). That row is drawn by the platform control now, so a
 * selector for it would have to name `.sf-suggest__*` — and this sheet styles
 * only its own prefix, which `tests/photo-panel.test.ts` holds and which is
 * what keeps a page that loads photos.css from restyling a shared control. The
 * icon is a NODE this module hands the control, so the weight travels with the
 * node: the wrapper is ours, its class is ours, and the shared sheet learns
 * nothing about a photo module’s levels.
 */
.sf-ph--product .sf-ph__search-mark svg,
.sf-ph--product .sf-ph__facet-mark svg {
  stroke-width: 1.6;
}

/* `display: flex` so the span’s box is the glyph’s box rather than a line box
 * two or three pixels taller than it — the same call the quiet dropdown’s
 * chevron wrapper makes. */
.sf-ph__facet-mark {
  display: flex;
}

/*
 * THE SELECTION WASH the board draws around the focused field: a 3px spread in
 * `accent-soft`. It REPLACES the half-pixel spread the category board draws,
 * and it sits UNDER the platform's 2px focus ring exactly as that one does —
 * the ring is drawn by the block at the top of this sheet and is not a
 * treatment this level gets to opt out of.
 */
.sf-ph--product .sf-ph__search:focus-within {
  box-shadow: 0 0 0 3px var(--brand-color-accent-soft, #fbefe5);
}

/*
 * THE MAGNIFIER STAYS INK WHEN THE FIELD IS FOCUSED, at this level only.
 *
 * The category board tints its mark with the border (the base rule above);
 * PDPBelow draws the focused field with an ink `stroke="#17181A"` magnifier and
 * lets the border and the wash carry the state on their own. Two boards, two
 * drawings -- and this one was worth measuring rather than eyeballing: the
 * orange glyph was most of the field region's residual.
 */
.sf-ph--product .sf-ph__search:focus-within .sf-ph__search-mark {
  color: var(--brand-color-ink, #17181a);
}

/* ---------------------------------------------------- the facet dropdown -- */

/*
 * THE WHOLE BLOCK IS RETIRED BY NAME (DD-UNIFY, Ryan's ruling 2026-08-28).
 *
 * What stood here: `.sf-ph__searchbox` (the popover's anchor), `.sf-ph__menu`,
 * `.sf-ph__menu-eyebrow`, `.sf-ph__menu-list`, `.sf-ph__option` and its
 * `--on`/`--all` states, `.sf-ph__option-label`, `-mark`, `-count`, `-stack`
 * and `-sub`. Ruling P5 (2026-08-27) had made this list a real popover; the
 * DRAWING it was a popover in was this module's own — a floating card with all
 * four borders, its own radius, 4px row gaps and 10px of padding, against the
 * FLUSH SEAM the two AI ask fields draw under their fields.
 *
 * Ryan's unification ruling makes them one control and one drawing. The rows
 * keep everything that is theirs — the counts, the tag glyph, the typed run in
 * bold, the two-line free-text row, the split eyebrow — as CONFIGURATION on
 * `QuietSelect`, and the box they sit in is `.sf-suggest` in chrome.css, which
 * every page already loads. That is a real, deliberate pixel change on the
 * product page and on no other surface: `searchDropdown` is a product-level
 * rule, so the category and home levels never drew any of this and
 * `tools/phab.mjs` still holds them byte-identical.
 *
 * THE Z-INDEX NEGOTIATION RETIRES WITH IT. `.sf-ph__searchbox` raised itself to
 * 2 and `.sf-ph__filter` boxed the quiet dropdown at 1, because that control
 * asserted `z-index: 10` even while CLOSED and would otherwise paint over the
 * open suggestion list. The raise is scoped to the open state now (chrome.css),
 * so a closed control is out of the argument entirely and neither number is
 * needed.
 *
 * THE ANCHOR MOVED ONE ELEMENT IN, and deliberately: the panel used to span the
 * field AND the configuration button beside it, which is not what "flush under
 * the field" means. `.sf-ph__search` — the drawn field, which was already
 * there — carries the `position: relative` now.
 */

/* ------------------------------------------------ the active-filter row -- */

.sf-ph__showing {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 18px;
}

.sf-ph__showing-label {
  color: var(--brand-color-muted, #6c6a62);
  font-size: var(--text-small);
}

/*
 * AN ACTIVE FILTER. The site's selection language — 1.5px accent, never
 * normalised — at the board's own 12px radius, which is tighter than the tag
 * chips' 16px because this pill is shorter.
 */
.sf-ph__active {
  align-items: center;
  appearance: none;
  background: transparent;
  border: var(--border-selected);
  border-radius: 12px;
  color: var(--brand-color-ink, #17181a);
  cursor: pointer;
  display: inline-flex;
  font-family: inherit;
  font-size: var(--text-support);
  font-weight: 600;
  gap: 6px;
  padding: 5px 11px;
}

.sf-ph__active-x {
  color: var(--brand-color-muted, #6c6a62);
  font-weight: 400;
}

/*
 * `.sf-ph__clear` IS RETIRED BY NAME (Ryan's DD-C, 2026-08-28). It was a button
 * drawn as the board's link, at the end of the "Showing" chip row, dropping
 * every narrowing at once. The X inside the search field does that now —
 * `.sf-field-clear` in chrome.css, shared with the two PDP sections — which is
 * where a reader looks for it. Its focus-ring entry at the top of this sheet
 * goes with it.
 */

/* ------------------------------------------------- the filter dropdown -- */

/*
 * THE SCOPE CONTROL — the board's black three-way segmented toggle, RETIRED by
 * ruling P4 (Ryan, 2026-08-27): "it is taking up too much visual weight… that
 * black is too much", and it "won't be used much". What replaces it is the
 * platform's quiet dropdown (`.sf-quiet-select`, chrome.css) — the same control
 * the reviews sort and the answers filter took, which the same walkthrough
 * ruled the site standard for a dropdown (D1): "much more clear that it's a
 * drop down box."
 *
 * SO THERE IS ALMOST NOTHING HERE. The control's own drawing belongs to
 * chrome.css and this sheet does not reach into it (photos.css owns `sf-ph-`
 * and nothing else — `tests/photo-panel.test.ts` enforces the partition). What
 * this module owns is the ROW: the glyph, the word "Filter", and the fact that
 * the pair sits at the RIGHT of the panel, which is Ryan's word for where it
 * goes.
 *
 * FIVE DECLARATIONS AND ONE OF THEM IS RETIRED INK. The segments' whole block —
 * the clipped group, the inter-rung hairlines, the board's verbatim
 * last-rung border quirk, the every-width scroll treatment the overflow gate
 * found, and the inset focus ring at the top of this sheet — goes with the
 * toggle. The 768–1000 overflow that treatment existed for goes with it too:
 * a dropdown is one box the width of its own label, and the menu it opens is
 * anchored to its right edge.
 */
/*
 * `.sf-ph__filter`, `.sf-ph__filter-label` and `.sf-ph__filter-mark` ARE
 * RETIRED BY NAME (Ryan's DD-A, 2026-08-28), with the row they sat in: the
 * label pair is `.sf-sec-config__label` in sections.css now and the funnel is
 * `FilterMark` in `icons.tsx`, beside the sort glyph DD-A asked for to match
 * it. The right-alignment those rules carried retires with them — a table's
 * label column is left-aligned, which is the point of a table.
 */

/* --------------------------------------------- the product level, mobile -- */

/*
 * NO BOARD DRAWS THIS. PDPBelow.dc.html is a 1440 artboard and there is no
 * mobile companion for it, so the phone composition is DERIVED — the same
 * position the engineering section's chapter rail was in, and derived the same
 * way: keep the pattern the mobile boards DO draw for this module (the panel
 * collapses above the grid, the grid goes two columns), and give each new
 * control the phone treatment its own pattern already has on other surfaces.
 *
 * The base mobile block above still applies — this only says what the product
 * level does DIFFERENTLY, and it has to say the grid at all because a
 * `.sf-ph--product .sf-ph__grid` rule outweighs a `.sf-ph__grid` rule inside a
 * media query (media queries carry no specificity).
 */
@media (max-width: 767.98px) {
  .sf-ph--product .sf-ph__body {
    margin-top: 0;
  }

  /* TWO COLUMNS, and the featured tile takes BOTH of them — which is the same
   * proportion the desktop draws (half the grid's width, two rows tall) rather
   * than a shrunken copy of a 2×2 inside a 4-wide grid. */
  .sf-ph--product .sf-ph__grid {
    gap: 10px;
    grid-auto-rows: 130px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: 14px;
  }

  .sf-ph--product .sf-ph__cell {
    height: 130px;
  }

  /* RESTATED, because the rule above is the same specificity and comes later:
   * the featured cell spans two rows, and a fixed height would paint its ground
   * over the top half of the area it occupies. */
  .sf-ph--product .sf-ph__cell--featured {
    height: auto;
  }

  .sf-ph--product .sf-ph__tile {
    border-radius: var(--radius-control);
  }

  .sf-ph--product .sf-ph__bar {
    margin-bottom: 0;
    margin-top: 14px;
  }

  /* The panel is the section's controls here, not a card — the base block
   * strips its ground and padding, and the title goes with them: the section
   * header two lines above already says "Customer photos", and the panel's own
   * title is the CARD's name. The count line stays; it is a fact. */
  .sf-ph--product .sf-ph__panel {
    padding: 0;
  }

  .sf-ph__panel-title {
    display: none;
  }

  /*
   * ─── FX3: `.sf-ph--product .sf-ph__search { margin-top: 12px }` IS RETIRED
   *     BY NAME (Ryan, 2026-08-28) ──────────────────────────────────────────
   *
   * "The photos config button is STILL taller than the search field on mobile."
   * It was, and this rule was why — the DD-A fix was right in mechanism and
   * incomplete in reach.
   *
   * WHAT DD-A DID: it moved the field's `margin-top` off the field and onto the
   * row (`.sf-ph__configrow`), because `align-items: stretch` gives a flex
   * child the row's height MINUS its own vertical margins — so a margin on the
   * field is height the FIELD does not get while the button beside it takes the
   * lot. `.sf-sec-config__row > :first-child { margin-block: 0 }` (sections.css)
   * is the shared rule that says so about whatever field it is handed.
   *
   * WHY IT DID NOT REACH HERE, and this is the whole of the root cause: that
   * shared rule is `.sf-sec-config__row > :first-child` — one class and one
   * pseudo-class, so specificity (0,2,0). This rule was two classes, which is
   * (0,2,0) as well. A TIE is broken by sheet order, and photos.css is linked
   * after sections.css, so the level's own 12px won. DD-A retired the DESKTOP
   * copy of this declaration (see the note in `.sf-ph--product .sf-ph__search`
   * above, which says exactly that) and left this one, inside a media query, at
   * the level and the rungs Ryan was looking at. MEASURED before the fix, at
   * 390 on the product page: row 60, button 60, field 48 — a 12px divergence
   * that IS this declaration, and 0 at 1440 where the media query is not in
   * force. That is why it was product-level and phone-only.
   *
   * NOTHING MOVES ABOVE THE ROW. The distance this was setting is 12px, and
   * `.sf-ph__configrow` already declares `margin-top: 12px` unconditionally —
   * so the space over the field row at this rung is the same space, now owned
   * by the box whose position it describes.
   */

  /* The filter row's own phone rule retires with the row (DD-A): the shared
   * grid is the same grid at every width — the label column simply measures a
   * shorter longest-label — and its row gap is the distance this rule was
   * setting by hand. */

  /* THE PAGER NO LONGER STACKS HERE (ruling PP5, 2026-08-28). It used to — the
   * note over the steps — while the answers pager was being held to ONE LINE at
   * this rung by ruling A2. One pager cannot have two phone treatments, so the
   * one-line treatment is the shared pattern's (sections.css) and this section
   * takes it: the trimmed label, the tighter buttons, and the scroller at the
   * widths where seven buttons and a label genuinely cannot fit. What is left is
   * the distance above the row. */
  .sf-ph--product .sf-ph__pager {
    margin-top: 14px;
  }
}

@media (max-width: 767.98px) {
  /*
   * MOBILE. The panel COLLAPSES to a bar: the search field, then every chip on
   * one horizontal scroll row with the eyebrows gone, above a two-column grid.
   *
   * The panel is ALREADY FIRST in the DOM, so nothing here reorders anything —
   * the column simply draws its two children in the order they are written.
   */
  .sf-ph {
    background: var(--brand-color-surface, #ffffff);
    border-top: var(--border-hairline);
    padding: 28px 0 30px;
  }

  .sf-ph__head-row {
    gap: 11px;
  }

  .sf-ph__browse {
    font-size: var(--text-small);
  }

  .sf-ph__body {
    /* `align-items` is reset: the desktop rule's `start` is a CROSS-axis
     * alignment, and in a column flex box the cross axis is the WIDTH — left as
     * `start` the grid and the panel shrink to their min-content and the tiles
     * come out 83px wide. */
    align-items: stretch;
    display: flex;
    flex-direction: column;
    margin-top: 0;
  }

  /* The panel is above the grid here, and it is no longer a card or a sticky:
   * it is the section's own controls. */
  .sf-ph__panel {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    position: static;
  }

  .sf-ph__search {
    margin-top: 14px;
    padding: 12px 15px;
    /*
     * ─── FH2: THE LANDING BUFFER (Ryan, 2026-08-29) ────────────────────────
     *
     * "Check these search fields and ask the others for similar issues." The
     * hold applies to this field as it does to the three ask fields, and this
     * is its half of the machinery: `use-field-hold.ts` READS THIS
     * DECLARATION BACK to work out where the field should sit under the pinned
     * stack, exactly as `.sf-embed-field` supplies it for the others
     * (sections.css). MC3's ruling — "15px sides, 15px top, a uniform frame" —
     * therefore reaches this surface from the same token, and a future gutter
     * ruling still moves all four together.
     *
     * IT IS A READ-BACK CARRIER, NOT A CLEARANCE. Nothing scrolls to this
     * element as a fragment target, so the declaration draws nothing on its
     * own; it is where the number lives.
     *
     * PHONE-SCOPED, like the hold that reads it: above this cut nothing lifts.
     */
    scroll-margin-top: var(--sf-gutter, 15px);
  }

  .sf-ph__search-mark svg {
    height: 15px;
    width: 15px;
  }

  /*
   * THE CHIPS SCROLL SIDEWAYS IN THEIR OWN CELL (Ryan's WP5, 2026-08-29).
   *
   * `.sf-ph__filters` and `.sf-ph__group` RETIRE BY NAME with the wall they
   * wrapped. They built one full-bleed scrolling row out of two labelled
   * groups by dissolving four boxes with `display: contents`; the chips are a
   * cell of the configuration grid now, so there are no boxes to dissolve and
   * nothing to bleed — a row that escaped its own gutter would escape the
   * label column beside it too. What survives is the SCROLLING, moved onto the
   * element that is now the row.
   */
  .sf-ph__chips {
    display: flex;
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    /*
     * THE SCROLLBAR IS HIDDEN, THE SCROLLING IS NOT (Ryan, 2026-08-26). The
     * chips are 30px pills and the overlay bar paints ACROSS THEM while a
     * thumb is dragging — the reader's own gesture obscures the thing they are
     * choosing. Both spellings, because they are different engines: the
     * standard property for Firefox, the pseudo-element for WebKit/Blink.
     *
     * IT REMOVES NO CAPABILITY, and that is the constraint this had to meet:
     * the row still scrolls with a wheel, a drag and the arrow keys, and it
     * still paints the platform focus ring — `scrollbar-width` and
     * `::-webkit-scrollbar` are paint, not behaviour. (This row is NOT one of
     * the scrollers Chrome makes a tab stop, and was not before either: it
     * holds seven focusable chips, and Chrome's rule is no-focusable-child.
     * `tools/focusgate.mjs` enumerates it by that rule and agrees.)
     */
    scrollbar-width: none;
    white-space: nowrap;
  }

  .sf-ph__chips::-webkit-scrollbar {
    display: none;
  }

  .sf-ph__chip {
    flex: none;
    font-size: var(--text-caption);
    padding: 6px 13px;
  }

  .sf-ph__chip-x {
    display: inline;
  }

  .sf-ph__grid {
    gap: 10px;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-top: 14px;
  }

  .sf-ph__cell {
    height: 130px;
  }

  .sf-ph__tile {
    border-radius: var(--radius-control);
  }

  .sf-ph__status {
    font-size: var(--text-caption);
    padding: 16px 0 0;
  }
}
