/* ==========================================================================
   GameOnTime — page layouts, MIGRATED ONTO THE DESIGN SYSTEM
   ==========================================================================

   Three stylesheets, and the split is the migration made visible:

     system.css   the design system — tokens and components. Grows slowly.
     site.css     the legacy page CSS. SHRINKS TO NOTHING. Its remaining size
                  is the size of the remaining migration, which is the figure
                  `database/check_site_css.php` prints.
     pages.css    THIS FILE. Layout for the pages that have been migrated.
                  Grows as site.css shrinks.

   A page appears here exactly once, and when it does, every rule it had in
   site.css is DELETED rather than left orphaned. A page styled by both files
   is the two-systems problem this whole round exists to end.

   ------------------------------------------------------------- LOAD ORDER

   system.css, then site.css, then this file. Last, deliberately: a migrated
   page must not be reachable by a leftover legacy rule that happens to share
   a selector, and during a migration that is a real risk rather than a
   theoretical one.

   ------------------------------------------------------------- THE RULE

   🔴 TOKENS ONLY. No hex, no rgb(), no px font size, no px or rem spacing
   literal. `SiteTokensTest::test_no_component_or_page_rule_carries_a_raw_value`
   parses this file and asserts the count is zero — it is the round's stated
   falsification and it is a permanent test, not a one-off check.

   The only literals permitted are unitless numbers (`0`, `1fr`, `1`),
   percentages, `ch` measures, and the container-query and media-query
   breakpoints themselves, which are viewport facts rather than design
   decisions. Each of those is called out where it appears.

   🔴 LOGICAL PROPERTIES ONLY. Nothing below needs a [dir="rtl"] override for
   LAYOUT and nothing below has one. The one [dir] rule in this file is a
   GLYPH — the catalogue tile's drawn arrow — which is the documented exception
   (BUG-097 / BUG-119) and is stated where it is written.
   ========================================================================== */


/* ==========================================================================
   THE BASKET — /cart
   ==========================================================================

   Migrated 15 Aug. Its 80 lines in site.css are gone; this replaces them.

   ZERO FUNCTIONAL CHANGE. The quantity stepper, Remove, Empty basket and
   Continue behave exactly as they did — same routes, same payloads, same
   refusals. What changed is the layout, the states and the fact that every
   value below comes from the token scale.

   --------------------------------------------------------- THE TWO COLUMNS

   Items on the reading side, the summary on the far side, at desktop width.
   `grid-template-columns` with a named area is deliberate over flex: the
   summary has to be able to sit BESIDE a list of unknown length and STICK
   while that list scrolls, and a flex child cannot do the second thing
   without a wrapper that exists only to hold it.

   The single-column form is the DEFAULT and the two-column form is the
   enhancement, not the other way round. A phone is the common case here and a
   layout that starts wide and collapses is one that has to be undone at every
   breakpoint below the one it was designed at — which is the media-query trap
   this project has already paid for twice.

   THE BREAKPOINT IS A CONTAINER QUERY WHERE IT CAN BE AND A MEDIA QUERY WHERE
   IT CANNOT. `.got-cart` is inside `.container`, which is a fixed max-width
   with padding, so the page's own width IS the container's width here and a
   media query answers the same question. It is written as a media query for
   that reason and no other; if this page is ever placed inside a narrower
   shell, it becomes a container query in the same edit. Saying which one it
   is and why is the lesson from BUG-208, where sizing a panel widget by
   viewport width moved the defect to a new breakpoint twice. */

.got-cart {
  display: grid;
  gap: var(--gt-space-6);
  align-items: start;
}

/* 60rem = 960px. The number is here rather than in a token because it is a
   statement about THIS layout: below it, a 340px summary column and a line
   with a thumbnail, a name, a price and two controls cannot both have room.
   Measured by narrowing the real page until the actions wrapped. */
@media (min-width: 60rem) {
  .got-cart {
    grid-template-columns: minmax(0, 1fr) 21rem;
    gap: var(--gt-space-7);
  }
}

.got-cart__items {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-5);
  min-inline-size: 0;   /* so a long product name shrinks instead of pushing */
}


/* ------------------------------------------------------------- ONE LINE ---

   Artwork, body, and the trash pinned to the top inline-end corner (V-33 B3:
   Omar, "Remove should be a trash icon top-right"). The line is the
   containing block for that corner, so `position: relative` here is what
   `inset-inline-*` on the trash measures from — top-right on /en, top-LEFT
   on /ar, with no [dir] rule. The body reserves the corner's width on its
   inline-end so a long product name wraps before it runs under the icon. */
.got-cart__line {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--gt-space-5);
  align-items: start;
  padding: var(--gt-space-5);
  transition: var(--gt-transition-control);
}

/* The line lifts on hover — it is the only whole-surface hover on the page,
   and it is what makes the list read as a list of objects rather than as
   text. It is NOT a link and NOT interactive; `.got-card--interactive` is
   deliberately not used, because the line does not do anything when clicked
   and a cursor:pointer would promise that it does. */
.got-cart__line:hover {
  border-color: var(--gt-line-strong);
}

/* 🔴 THE ARTWORK BOX — V-33 B3. It was `.got-cart__thumb`, an empty well
   reserved for "images that are coming"; they came. `<x-artwork>` fills it
   with the card face, the game's poster, or — where there is no picture
   anywhere — the game's type mark on the system's placeholder gradient, so
   a line is NEVER a hole (hover-skill §7: "no artwork is not a blank").

   BOTH AXES ARE WRITTEN, from the system's `--gt-cart-art-*` tokens: a ratio
   cannot size an unloaded image (TRAPS, the brand box that stayed 0×0 until
   its PNG landed), and the whole point of the box is that nothing beside it
   moves when the picture arrives. The image fills the box and `object-fit:
   cover` crops what does not fit — nothing, for the 2:3 sources this
   catalogue bundles (the token says which sources are not 2:3). */
.got-cart__art {
  display: grid;
  place-items: center;
  inline-size: var(--gt-cart-art-inline);
  block-size: var(--gt-cart-art-block);
  overflow: hidden;
  border-radius: var(--gt-radius-sm);
  background: var(--gt-media-placeholder);
  border: var(--gt-border-hair) solid var(--gt-line);
  box-shadow: var(--gt-level-base);
}
.got-cart__img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
/* The type mark, when there is no picture: the short name on the placeholder
   gradient, in the on-artwork ink the tile's own mark takes — measured
   against the same composited ground (system.css, `.got-gamecard__initials`
   and the placeholder token carry the arithmetic). Smaller than the tile's,
   because the box is a fifth of the size. */
.got-cart__mark {
  padding-inline: var(--gt-space-2);
  font-size: var(--gt-text-sm);
  font-weight: var(--gt-weight-heavy);
  line-height: var(--gt-leading-tight);
  text-align: center;
  color: var(--gt-on-ink);
  text-shadow: var(--gt-media-text-shadow);
  overflow-wrap: anywhere;
}

/* The trash's corner is reserved on the body's inline-end (the tap minimum
   plus a step of air), so the heading wraps before the icon rather than
   under it. */
.got-cart__body {
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-3);
  padding-inline-end: calc(var(--gt-tap-min) + var(--gt-space-2));
}

/* THE TRASH — icon-only, at the line's top inline-end, ruling 15. The form
   is the classic remove POST; only the button moved. `inset-inline-end`
   lands it top-right on /en and top-left on /ar with no [dir] rule (the
   logical-properties rule this stylesheet is written to). Hover and press
   are the system's quiet button's; nothing here animates. */
.got-cart__remove {
  position: absolute;
  inset-block-start: var(--gt-space-3);
  inset-inline-end: var(--gt-space-3);
  margin: 0;
}

.got-cart__name {
  font-size: var(--gt-text-lg);
  font-weight: var(--gt-weight-bold);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
}
/* The product name is a link only while there is a page to link to — the
   Blade decides that, not this rule. Underline on hover rather than always:
   a list of underlined headings reads as a list of warnings. */
.got-cart__name a {
  color: inherit;
  transition: var(--gt-transition-control);
}
.got-cart__name a:hover {
  color: var(--gt-accent);
  text-decoration: underline;
  text-underline-offset: var(--gt-space-1);
}
.got-cart__name a:focus-visible {
  outline: var(--gt-ring-width) solid var(--gt-ring-color);
  outline-offset: var(--gt-ring-gap);
  border-radius: var(--gt-radius-xs);
  box-shadow: var(--gt-ring-shadow);
}
/* BUG-348: the game beside the service, quieter. V-33 B3 made it the line
   BENEATH the name (Omar: "the brand beneath") rather than a dot-separated
   run after it, so a composed Arabic card name and its brand never share a
   line's bidi resolution. The negative margin closes the body's gap to the
   heading's own leading: a brand line is part of the name, not a paragraph
   after it. */
.got-cart__game {
  margin-block: calc(var(--gt-space-2) * -1) 0;
  font-size: var(--gt-text-sm);
  font-weight: var(--gt-weight-medium);
  color: var(--gt-ink-muted);
}

/* The chosen options, by name, under the line's title — V-08b. A quiet
   inline run on the type scale, not a bulleted block: the names are a
   property of the line, like the method pill beside them. Separators are
   drawn (a ::before dot with logical margins), so the list is RTL-safe by
   construction and the markup carries no punctuation a screen reader would
   announce. Tokens only, so both themes are already answered. */
.got-cart__options {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  list-style: none;
  margin-block: 0 var(--gt-space-2);
  padding-inline-start: 0;
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
}
.got-cart__options li + li::before {
  content: "·";
  margin-inline: var(--gt-space-2);
  color: var(--gt-ink-muted);
}

.got-cart__meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--gt-space-3) var(--gt-space-5);
  font-size: var(--gt-text-sm);
  color: var(--gt-ink-muted);
}

/* The method as a word, in a quiet pill — it is a property of the line, not a
   sentence about it, and a pill is what says so at a glance. */
.got-cart__method {
  padding-block: var(--gt-space-1);
  padding-inline: var(--gt-space-3);
  border-radius: var(--gt-radius-pill);
  background: var(--gt-surface-sunken);
  border: var(--gt-border-hair) solid var(--gt-line);
  font-size: var(--gt-text-2xs);
  font-weight: var(--gt-weight-semibold);
  color: var(--gt-ink-muted);
}

.got-cart__price {
  font-size: var(--gt-text-md);
  font-weight: var(--gt-weight-bold);
  color: var(--gt-ink);
  font-variant-numeric: tabular-nums;
}


/* --------------------------------------------------------- THE CONTROLS ---

   `align-items: flex-end` so the Update button's baseline sits with the
   number box's, whatever the label above it does. `flex-wrap` so they stack
   rather than squeeze on a phone, and `gap` rather than a margin on either
   child so nothing needs mirroring. */
.got-cart__actions {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--gt-space-3);
  margin-block-start: var(--gt-space-2);
}

/* The stepper's form: the labelled field, the reason line under it, and the
   `<noscript>` Update button — stacked, so the reason reads directly under
   the control it explains. The stepper itself, its box and its reason are
   `.got-stepper*` in system.css (V-33 B3); this rule owns only the stack. */
.got-cart__qty {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--gt-space-2);
}

/* The quantity on a line that cannot be re-quantified. It is still SAID —
   quantity is part of what the line is even when it is fixed — so it gets the
   label treatment rather than disappearing.

   IT IS GIVEN THE CONTROL HEIGHT ON PURPOSE, and it is not a control. Its row
   is `align-items: flex-end`, which lines up the BOTTOM of this paragraph with
   the bottom of the Remove button beside it — so the text sat a few pixels
   BELOW the button's label rather than with it. Seen on the rendered page at
   1280 in both languages; no test could have reported it, and no amount of
   reading the rule would have either.

   Matching the tap minimum and centring inside it puts the two baselines
   together without giving a paragraph a border or a hover. */
.got-cart__qtyfixed {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--gt-tap-min);
  font-size: var(--gt-text-sm);
  color: var(--gt-ink-muted);
  font-variant-numeric: tabular-nums;   /* V-30 B3: the fixed quantity, one per line */
}


/* ------------------------------------------------------------ THE SUMMARY

   It STICKS at desktop width. A basket long enough to scroll is exactly the
   basket where the total and the Continue button need to stay reachable, and
   this is the only behavioural benefit in the round that costs no JavaScript.

   `top` is the system's `--gt-scroll-offset` — the header's height plus a
   step of air, derived from `--gt-header-height` since V-30 B4. The header
   is `position: sticky` in site.css, so a summary sticking to 0 would slide
   under it. It was a literal 5.5rem here ("73px plus one step", a copy of
   the header's height that disagreed with the offset's own 5.75rem) until
   there was a token to read. */
.got-cart__summary {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-5);
  padding: var(--gt-space-6);
}

@media (min-width: 60rem) {
  .got-cart__summary {
    position: sticky;
    inset-block-start: var(--gt-scroll-offset);   /* MEASURED after V-30 B4: 84px under a 64px bar, both languages */
  }
}

.got-cart__totalrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--gt-space-4);
}

.got-cart__totallabel {
  font-size: var(--gt-text-3xs);
  font-weight: var(--gt-weight-semibold);
  letter-spacing: var(--gt-tracking-label);
  text-transform: uppercase;
  color: var(--gt-ink-muted);   /* 6.53:1 on raised */
}

/* THE MONEY FIGURE. `dir="ltr"` and `<bdi>` are in the MARKUP and are not
   optional — a currency symbol and a decimal point are bidi-neutral, so
   without both the Arabic page reorders them and the figure RENDERS WRONG
   while COPYING CORRECTLY. Every assertSee in the suite would still pass;
   CartSurfacesTest asserts the markup for that reason.

   `white-space: nowrap` is correctness rather than tidiness: an isolated LTR
   run that WRAPS is an LTR run in two pieces, and the algorithm gets to
   reorder the pieces. */
.got-cart__totalfigure {
  font-size: var(--gt-text-xl);
  font-weight: var(--gt-weight-heavy);
  color: var(--gt-ink);          /* 13.86:1 on raised */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Continue fills the summary column. The class is on the button beside
   `.got-btn--primary`, so everything else about it — fill, hover, press,
   focus ring, disabled — is the system's and cannot drift from the other
   primary buttons on the site. */
.got-cart__continue {
  inline-size: 100%;
}
/* BUG-349: emptying the basket sits LAST in the summary, quiet, centred under
   the primary — a secondary exit, not a rival to Continue. */
.got-cart__clear {
  display: flex;
  justify-content: center;
}
.got-cart__clearbtn {
  inline-size: 100%;
}
/* BUG-347: the unit price beside the line total, quieter. */
.got-cart__unit {
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
}

/* V-33 B3: the summary's line count — the total row's shape, one step
   quieter, so the two read as one small table. The number is tabular. */
.got-cart__countrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--gt-space-4);
  margin-block-end: calc(var(--gt-space-3) * -1);
}
.got-cart__count {
  font-size: var(--gt-text-md);
  font-weight: var(--gt-weight-bold);
  color: var(--gt-ink);
  font-variant-numeric: tabular-nums;
}

/* V-33 B3: THE EMPTY STATE at illustration weight. The system's ring
   (`.got-state__mark`, 3rem) is widened to twice the tap minimum and the
   glyph sized BY CLASS to fill it — a bare `svg` rule would size every
   glyph the ring ever holds. The panel keeps the system's padding; the
   heading and the lead are the state's own elements. */
.got-cart__emptymark {
  inline-size: calc(var(--gt-tap-min) * 2);
  block-size: calc(var(--gt-tap-min) * 2);
}
.got-cart__emptyicon {
  inline-size: var(--gt-space-10);
  block-size: var(--gt-space-10);
}


/* ==========================================================================
   THE CATALOGUE — /games/{game}, the store brand page, and the service page
   ==========================================================================

   Migrated 18 Aug (Batch 3 of the UI/UX round). The 60-odd rules these three
   templates held in site.css — .crumb, .svc-card, .tag, .price-row, .arrow,
   .empty, .pill/.dot, .cfg, .panel, .field, .qty, .opts/.opt, .assure, .buy,
   .total*, .lines/.line, .btn* — are DELETED there; the components moved
   into system.css (breadcrumb, card head/foot, pill, state, facts, check,
   input, button) and the LAYOUT is here.

   ZERO FUNCTIONAL CHANGE ON THE SERVICE FORM. Same <form class="cfg">, same
   action, same fields, same ids, same tree order — #buy, then .buy-note,
   then #add-to-cart — because a form's default button is the first submit
   in tree order and Enter in the quantity box presses it. The
   sold-out #buy stays a disabled NON-submit. The one thing this batch
   changes about those controls is what they are made of: `.got-btn`,
   `.got-input`, `.got-check` — which is BUG-250 (Arial) closed and BUG-308
   (Buy now white on 1.66:1) closed by construction, since the primary
   button paints ink on the accent.

   ---------------------------------------------- THE PHONE'S PRICE BAR

   Below 56.25em (900px) the aside that holds the total and the buttons is a
   STICKY BAR at the bottom of the viewport. Nothing moves in the DOM: the
   aside is the last child of the form, so `position: sticky; inset-block-end:
   0` pins it to the viewport's bottom edge for as long as the form is on
   screen — measured before this: at 390 the price and the Buy button sat
   1,000px under the quantity they answer. In the bar the four fact lines are
   not rendered (the game is in the breadcrumb, the service is the h1, the
   method is the chosen tile) and the note under Buy is not either; both come
   back the moment the two-column layout does. 56.25em is a viewport fact:
   the width at which the two columns stop fitting.

   ---------------------------------------------- THE ONE [dir] RULE

   The file's head says nothing here needs a [dir="rtl"] override. One thing
   does, and it is a GLYPH, which is the documented exception (BUG-097 /
   BUG-119): the arrow after "Configure" / "Buy" is drawn by ::after so no
   directional character is typed into a view, and a glyph has to be told
   which way is forward. */

/* The catalogue section sits straight under its breadcrumb: the row's own
   air is enough, not a whole section step. */
.got-cat {
  padding-block-start: var(--gt-space-4);
}

/* ---------------------------------------- THE SHELF PAGE'S TOP BAND (V-06)
   🔴 ONE ROW: the heading, its one line and the count, all on the same
   baseline. It replaces `.got-cat__intro`, which was a title over a lead with
   a 32px margin under it — 94px of page before the first card, on a page
   whose job is to show cards.

   THE BUDGET IS WHY THIS IS A ROW AND NOT A BLOCK, and it is arithmetic
   anybody can re-run. The round asks for the grid to start within 140px of
   the top of a 1536x735 window. The header is 73 (measured, the wide row).
   That leaves 67 for everything else, and this band wants 20 + 26.4 + 16 =
   62.4 — its own padding and one line of 22px type at --gt-leading-tight.
   There is no room in 67 for a section step as well, which is why
   `.got-index` zeroes `.section`'s, and no room for the breadcrumb that used
   to sit above it, which is why `/games` no longer renders one (the argument
   is in games.blade.php, at the deletion).

   MEASURED ON THE RENDERED PAGE at 1536 after this batch: the band's box is
   63.19 tall, the grid's top is 136.19, and row one ends at 544. The band is
   0.8 taller than the 62.4 above it and that is the baseline doing its job
   rather than a rounding error — the 13px intro and the 12px count are
   aligned to the 22px heading's baseline, so their descenders finish 0.8px
   below the heading's own box and the flex line grows to hold them. Stated
   because a comment that quotes the arithmetic and not the measurement is how
   a 13px leak went unnoticed for twenty minutes two paragraphs down.

   🔴 THIS BLOCK WAS CALLED `.got-shelf` FOR TWENTY MINUTES AND `.got-shelf`
   ALREADY EXISTED — it was the homepage's STORE shelf, in system.css (deleted
   with its last caller in V-18b, when the store became `.got-store-grid` —
   fixed 6/3/2 counts since V-18d; the collision hazard below happened while
   the old name lived), and
   it carried `.got-shelf .got-label { margin-block-end: var(--gt-space-4) }`.
   The count here IS a `.got-label`, so it inherited a 12px bottom margin from
   a component on another page and the band measured 75.19 instead of 63.19,
   putting the grid at 148 against a budget of 140. Nothing failed; the page
   simply rendered 13px lower than the arithmetic said, which is exactly what
   "CSS loaded later wins on equal specificity, and losing is silent" looks
   like from the inside. It was found by MEASURING THE BAND, not by reading
   either stylesheet — the two rules are 3,300 lines apart in different files.
   Grep the block name before you mint one. */
.got-index {
  /* `.section` opens every section with --gt-section-y (40 at a phone, 64
     here). This one has nothing above it but the header and the band carries
     its own air, so that step would be space with nothing on either side of
     it — and it is 64 of the 67 the budget has left. pages.css loads after
     site.css and the two selectors tie, so source order decides; that is the
     same mechanism `.got-cat` above uses and it is stated rather than left to
     be discovered. */
  padding-block-start: 0;
}
.got-index__top {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  /* Row gap first: it only ever applies once the row has wrapped, which is
     what a phone does with a heading, a sentence and no room for both. */
  gap: var(--gt-space-1) var(--gt-space-4);
  padding-block: var(--gt-space-6) var(--gt-space-5);
}
/* 🔴 THE SAME BAND UNDER THE HERO — V-06 Batch 5, 22 Aug. The homepage's
   `#games` section wears the band over the SAME `<x-games-grid>` inside the
   SAME `.container--shelf`, and it takes its top padding OFF.

   That is not a tidier number, it is a different question answered. On
   `/games` there is nothing above the band but the sticky header, so the 20px
   is the band's own air. On the homepage the hero directly above ends with
   `--gt-space-9` of padding and a 96px gradient dissolving into this section,
   so 20 more would be space with nothing on either side of it. Measured on the
   rendered page at 1536 this round: the band's box is 43.19 tall here against
   63.19 on `/games` — the 20px, exactly — its heading sits at 520.86 and the
   first card at 564.05. The batch's targets were 521 and 563.

   A MODIFIER AND NOT A SECOND BAND. Every type decision, the baseline row, the
   count's `margin-inline-start:auto` and the 568px breakpoint are the base
   class's; this changes one declaration. `CatalogueIndexTest` matches
   `//div[@class="got-index__top"]` exactly, so the two pages cannot be
   confused for one another by that test. */
.got-index__top--seam {
  padding-block-start: 0;
}
/* 🔴 OFF THE PAGE-TITLE STEP ON PURPOSE, AND IT IS NOT A HAND-PICKED SIZE.
   The register puts a page title at --gt-text-2xl/heavy (28) and this h1 is
   --gt-text-xl/semibold (22). The reference asks for 20/600, which is not a
   step of the scale; 22 is the step nearest it and the one this system
   already uses for a section heading. The reason it may not be 28 is
   measured, not aesthetic: 28 at --gt-leading-tight is 33.6, which puts the
   grid at 142 against a budget of 140. A heading that pushes the product it
   introduces off the screen is the thing this round exists to fix.
   No tracking: CLAUDE.md's standing decision is no negative tracking before
   launch, and the reference's -.015em is Latin-tuned (measured 22 Aug:
   the same declaration moves a Latin string 40% and an Arabic one 4%). */
.got-index__title {
  font-size: var(--gt-text-xl);
  font-weight: var(--gt-weight-semibold);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
}
/* 13px, which is what the reference asks for AND a step of the scale, so
   there is nothing to trade here. `--gt-text-xs` and not `--gt-text-2xs`
   (they are the same 13 today): the two names are two ROLES, and the day
   BUG-342's 12px floor moves again the help-text step moves with it and the
   metadata step does not. This line is page furniture beside a heading, not
   help under a control. */
.got-index__intro {
  font-size: var(--gt-text-xs);
  line-height: var(--gt-leading-snug);
  color: var(--gt-ink-muted);
}
/* The count is the system's `.got-label` — 12px, semibold, tracked,
   uppercase, muted — and this class adds the ONE thing the label cannot know
   about, which is that it sits at the far end of a flex row. Writing the
   other five declarations again here would be a fourth private answer to the
   micro-label question. `margin-inline-start` and not `margin-left`: it has
   to sit at the reading end in both directions. */
.got-index__count {
  margin-inline-start: auto;
  font-variant-numeric: tabular-nums;   /* V-30 B3: a count, so a figure */
}
/* 🔴 568, NOT THE REFERENCE'S 520 — AND THE FIRST CUT PUT IT AT 520 BECAUSE IT
   COPIED A NUMBER INSTEAD OF ASKING WHAT THE NUMBER WAS FOR.

   The reference drops the count at 520, which is also where its gutter steps
   to 16, and this file said "the same breakpoint is written in site.css for
   that gutter" as though the coincidence were the reason. It is not: the
   gutter's breakpoint is about the page's edges and this one is about whether
   the ROW STILL FITS ON ONE LINE. Bisected on the rendered page, 480→600 at
   one-pixel steps (22 Aug):

     489..520   62.39   one line — count already hidden
     521..568   85.58   THE COUNT COMES BACK AND THE ROW WRAPS: three rows,
                        grid top 146.58, the tallest band the page has
     569+       63.19   one line, count included

   So hiding it at 520 bought 23px and the very next pixel spent 24. The line
   the count actually has to clear is 568/569, and that is where it is now. It
   is a different question from the gutter and it gets its own number; below it
   the row is a heading and a sentence, and the count is a datum the grid
   underneath states by simply existing.

   RE-BISECTED AFTER THE MOVE, same method: the 85.58 band is gone and the
   page has three heights and no spike —

     320..488   83.94   heading and sentence on two rows (count hidden)
     489..568   62.39   one line (count hidden)
     569+       63.19   one line, count included

   The 83.94 at the bottom is the heading and its sentence, not the count, and
   nothing here can remove it. */
@media (max-width: 35.5em) {   /* 568 */
  .got-index__count {
    display: none;
  }
}

/* -------------------------------------------- THE IDENTITY BAND (V-02) ---
   A game page opens with the game: its poster, its name, its one line, and
   the facts — on a panel, with the services on the page's own ground below
   it. That step down IS the "clear visual transition" the direction asks for;
   a rule would have been a line, and a line under a heading is decoration.

   🔴 COMPACT ON PURPOSE. The direction is explicit that this is "not a giant
   hero repeated on every page": the poster is a THUMBNAIL at the same 2:3 the
   catalogue tile uses, not a band of artwork behind the title. Two reasons and
   both are measured elsewhere in this system. A full-bleed artwork header puts
   the page's h1 over a picture, which is the contrast problem the tile was
   just rewritten to remove; and every one of these pages is reached from a
   grid where the same artwork was just shown at four times the size, so
   repeating it large says nothing new.

   The band is `--gt-level-base`: flat on the page, because it is a heading and
   not an object to pick up. */
.got-cat__id {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--gt-space-5) var(--gt-space-6);
  margin-block-end: var(--gt-space-8);
  padding: var(--gt-space-6);
  border-radius: var(--gt-radius-lg);
  background: var(--gt-surface-raised);
  border: var(--gt-border-hair) solid var(--gt-line);
  box-shadow: var(--gt-level-base);
}
/* The poster. 6.5rem is a THUMBNAIL WIDTH — a fact about this layout, not a
   step of the spacing rhythm — and at 2:3 it is 104x156, which is a little
   taller than the two lines of type beside it and reads as the anchor of the
   row rather than as an icon. */
.got-cat__poster {
  position: relative;
  flex: none;
  inline-size: 6.5rem;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: var(--gt-radius-md);
  background: var(--gt-surface-sunken);
  display: grid;
  place-items: center;
}
/* 🔴 THE POSTER TAKES THE SHAPE OF THE MODEL IT SHOWS — V-20 B6. Omar: "the
   main picture is not coming correctly." The 2:3 above is a GAME poster's
   ratio; a store brand's mark is a SQUARE upload, and `cover` in a 2:3 box
   scaled the 600×600 mark to 156×156 and cropped it to 104 wide — a third of
   its width thrown away, measured on the rendered /games/playstation-store at
   1536 and 390, both languages, 28 Aug (the bag's handles were the part that
   went). The band must show the mark whole: no crop, no stretch, and never
   upscaled past its pixels — 156 CSS px from a 600px source downscales even
   at 2x DPR.

   THE WIDTH IS DERIVED, NOT PICKED: the square keeps the 2:3 poster's own
   BLOCK size (6.5rem × 3/2), so the band stands at one height on a game page
   and a store page and the mark keeps the anchor role the comment above
   argues for — a little taller than the two lines of type beside it, never an
   app icon in a grey box. CatalogueIndexTest asserts the arithmetic against
   the base rule rather than repeating the number.

   TWO CLASSES, NOT ONE: a bare `--square` rule would tie the base poster at
   (0,1,0) and hold only by source order — V-18a Batch 5's lesson, and the
   same shape `.got-gamecard.got-gamecard--square` uses in system.css. */
.got-cat__poster.got-cat__poster--square {
  inline-size: 9.75rem;
  aspect-ratio: 1 / 1;
}
.got-cat__posterimg {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
  /* Lit, not dimmed. The tile holds its artwork back because hover has
     somewhere to go; this one is not a control and has no state to reward, so
     dimming it would just be a darker picture. */
}
/* No artwork: the same material the tile's fallback uses, so the two cannot
   drift apart into two ideas of what "no cover yet" looks like. */
.got-cat__poster--type {
  background: var(--gt-media-placeholder);
  /* V-05: the tile's REST value, which is the tile's own filter and no
     longer a dimming one. This rule exists so the game page's fallback
     poster and the catalogue tile's fallback cannot drift into two ideas of
     what "no cover yet" looks like; it follows whatever the tile does. */
  filter: var(--gt-media-rest);
}
/* 🔴 THE MARK READS THE ON-ARTWORK INK, NOT THE PAGE'S — 23 Aug, and it was
   ALREADY WRONG before this round touched the material. It was `--gt-ink`,
   which FOLLOWS THE THEME, on a mark sitting on the placeholder, which does
   not: measured on the rendered /games/rocket-league at 1536 by the same
   with/without-text diff the tile's own mark uses, against the shipped bright
   gradient, it was **2.14:1 on the dark page** and 6.76 on the light one — one
   colour passing in one theme by accident, which is the reason F-1 refused
   `--gt-ink` for the tile's mark in the first place.

   Darkening `--gt-media-placeholder` (Omar's row-rhythm rule) would only have
   swapped which theme it failed in — measured at 1.92 on light with `--gt-ink`
   still here. `--gt-on-ink` is the tile's answer and it is the same decision
   read twice rather than a second one: 7.55:1 at 1536 in BOTH themes, which is
   what a colour that does not follow the page looks like. */
.got-cat__mark {
  font-size: var(--gt-text-lg);
  font-weight: var(--gt-weight-heavy);
  color: var(--gt-on-ink);
  text-shadow: var(--gt-media-text-shadow);
}
/* The words. `1 1 20rem` so the band wraps to two rows on a phone with the
   poster above the text rather than a 104px column squeezing the title into
   one word per line. */
.got-cat__idbody {
  flex: 1 1 20rem;
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-4);
  align-items: flex-start;
}
.got-cat__title {
  font-size: var(--gt-text-2xl);
  font-weight: var(--gt-weight-heavy);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
}
.got-cat__lead {
  margin-block-start: var(--gt-space-2);
  max-inline-size: 60ch;   /* a measure */
  font-size: var(--gt-text-sm);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink-muted);
}

/* ------------------------------------------------------------ THE GROUPS ---
   A category or a region: the system's micro-label over its grid. The first
   group sits straight under the head; every later one carries the section
   rhythm above it. */
.got-cat__group + .got-cat__group {
  margin-block-start: var(--gt-space-8);
}
.got-cat__group .got-label {
  margin-block-end: var(--gt-space-4);
}
/* auto-fill and not auto-fit: a group of one keeps its column, rather than
   one card stretching to the whole row. 18rem is the tile's minimum measure;
   min() so a narrow screen never gets a track wider than itself. */
.got-cat__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
  gap: var(--gt-tile-gap);
}

/* ------------------------------------------------------------ THE TILE ---
   A service or a card: the system's interactive card, compact (the tile
   body is --gt-space-5, the standard says 16 for a tile against 24 for a
   panel), a flex column so the foot sits at the bottom of every tile in a
   row whatever the description's length. Everything it is made of — the
   head, the pill, the lead, the foot, the figure — is the system's. */
.got-svc {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-3);
  padding: var(--gt-space-5);
  color: var(--gt-ink);
  text-decoration: none;
}
.got-svc .got-card__lead {
  margin-block-start: 0;
}
/* The "go" word at the foot's end. `<span class="arrow">` is pinned by two
   tests (OutOfStockCardTest, StageBVerificationTest), so the class stays and
   is styled here. The glyph is drawn, not typed. */
.got-svc .arrow {
  flex: none;
  font-size: var(--gt-text-sm);
  font-weight: var(--gt-weight-semibold);
  color: var(--gt-accent);
}
.got-svc .arrow::after {
  content: "\2192";
  margin-inline-start: var(--gt-space-1);
}
[dir="rtl"] .got-svc .arrow::after {
  content: "\2190";
}

/* --------------------------------- THE SERVICE TILE'S ARTWORK SLOT (V-02) ---
   A service tile can carry a picture, and today none does.

   🔴 THE NO-PICTURE LAYOUT IS THE DESIGNED ONE, NOT A FALLBACK. Sixteen
   services exist and `services.cover_image` is null on every one of them, so
   the tile every customer sees is the one without a picture; drawing the
   design around a photograph and then bolting a no-photograph case underneath
   it would have made the common case the exception. The slot is ADDITIVE: a
   tile with no image renders exactly what it rendered yesterday, byte for
   byte, and `CatalogueUxTest`'s direct-child XPaths still hold because the
   thumbnail is a sibling that only exists when there is one.

   WHY A THUMBNAIL AND NOT A BANNER. A service is a configuration — a rank, a
   number of wins, a camo — and its own words are what a customer scans. The
   picture is context, so it takes the reading edge at a fixed 4.5rem and the
   head, the lead and the foot keep the row they already had. Artwork is what
   arrives later "through `services.cover_image` alone"; the day it does, this
   is the only rule that has to already exist. */
.got-svc__thumb {
  position: relative;
  flex: none;
  inline-size: 4.5rem;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--gt-radius-md);
  background: var(--gt-surface-sunken);
  border: var(--gt-border-hair) solid var(--gt-line);
}
.got-svc__thumb img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}
/* With a thumbnail the tile becomes two columns: the picture at the reading
   edge and everything it already had beside it. Without one, nothing here
   applies and the tile is the single column it has always been. */
.got-svc--art {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--gt-space-5);
}
.got-svc--art .got-svc__col {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-3);
  flex: 1 1 auto;
  min-inline-size: 0;
}

/* ------------------------------------------------ THE SERVICE PAGE: LAYOUT ---
   `<form class="cfg">` — the class is pinned by three tests, so it keeps its
   name and gets the system's layout. One column by default; two from 56.25em
   with the price aside beside the panel, sticky under the header by the same
   offset every anchored section clears (--gt-scroll-offset, so it can never
   sit under a taller header — the hand-computed `top:92px` it replaces was a
   trap the old rule described at length). */
.cfg {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gt-space-6);
  align-items: start;
  padding-block: var(--gt-space-4) var(--gt-space-9);
}
@media (min-width: 56.25em) {
  .cfg {
    grid-template-columns: minmax(0, 1fr) minmax(0, 23.75rem);   /* 380px: the aside's measure */
  }
  .buy {
    position: sticky;
    inset-block-start: var(--gt-scroll-offset);
  }
}

/* The configurator panel: the system's panel, with the page's h1 and lead. */
.got-svc__title {
  font-size: var(--gt-text-2xl);
  font-weight: var(--gt-weight-heavy);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
  margin-block-end: var(--gt-space-2);
}
.got-svc__lead {
  font-size: var(--gt-text-md);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink-muted);
  margin-block-end: var(--gt-space-6);
}
.got-svc__flash {
  margin-block-end: var(--gt-space-5);
}
/* A field group: the label (the system's field label — ink, not the
   uppercase tracked grey the legacy `.field label` painted onto the method
   tiles too), then its control(s). */
.field {
  margin-block-end: var(--gt-space-6);
}
.field:last-child {
  margin-block-end: 0;
}

/* The quantity: minus · number · plus. Three system controls in a row, no
   group border and no overflow:hidden — which is what let each keep the
   system's ring at its +2px offset (BUG-242 had to draw it INWARDS to
   survive the clipping group). The buttons are square: the tap minimum on
   both axes. */
.qty {
  display: flex;
  align-items: center;
  gap: var(--gt-space-4);   /* ≥8 between adjacent targets (was 6; the ring at its offset left 2px of panel between neighbours — 19 Aug) */
}
.qty .got-btn {
  inline-size: var(--gt-tap-min);
  padding-inline: 0;
  font-size: var(--gt-text-lg);
}
.qty .got-input {
  inline-size: var(--gt-space-11);
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.qty .got-input::-webkit-outer-spin-button,
.qty .got-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
}

/* The method: two tiles, each a <label class="opt got-check"> around a REAL
   radio (`.got-check__box`, the system's — the dot is the second carrier
   beside the colour, which BUG-251 asked for). The tile adds the surface,
   the edge and the tint; the check adds the row and the box. `.opt` and
   `.opts` keep their names for the page's own script and the tests that
   assert the chooser's absence on coaching. */
.opts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gt-space-3);
}
.opt {
  flex: 1 1 10rem;   /* a tile's minimum measure; below it the tiles stack */
  align-items: flex-start;
  padding: var(--gt-space-4);
  border: var(--gt-border-hair) solid var(--gt-line-strong);
  border-radius: var(--gt-radius-md);
  background: var(--gt-surface-overlay);
  transition: var(--gt-transition-control);
}
.opt:hover {
  border-color: var(--gt-accent);
}
.opt.active {
  border-color: var(--gt-accent);
  background: var(--gt-tint-accent);
}
.opt__body {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-1);
}
.opt__title {
  font-weight: var(--gt-weight-bold);
}
.opt__note {
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
}
.opt__extra {
  /* A flex item that carries dir="ltr" (the "+30%" run) stretched to the
     tile's width and put its text at its OWN start — the LEFT edge of a
     right-aligned Arabic tile (measured 19 Aug: x 58–93 under a note ending
     at 306). Shrunk to content, it sits at the COLUMN's start, which is the
     tile's reading edge in both directions. */
  align-self: flex-start;
  font-size: var(--gt-text-xs);
  font-weight: var(--gt-weight-bold);
  color: var(--gt-accent);
}

/* ------------------------------------------- THE CONFIGURATOR — V-08b ---

   One fieldset per live option group, between the method chooser and "What
   you get". The CONTROLS are the system's (`.got-check` + `.got-check__box`
   carry the row, the box, every state and the focus ring — nothing here may
   restate any of that) and the ERROR SLOT is the system's
   `.got-field__error` (the danger dot, the :empty hide). What this section
   owns is only what a fieldset needs to sit in the panel's rhythm: the UA
   box model stripped, the legacy `.field` spacing matched, and the price
   tag beside an option's name. Logical properties and tokens only, so RTL
   and both themes are answered by construction. */
.got-cfg {
  margin: 0;
  margin-block-end: var(--gt-space-6);   /* the .field rhythm, without the banned name */
  padding: 0;
  border: 0;
  /* A fieldset's UA default is min-inline-size: min-content, which refuses
     to shrink below its widest option name and breaks the panel on a
     phone. */
  min-inline-size: 0;
}
/* The legend IS `.got-field__label` (the system's class, on the element);
   this only strips the UA's legend padding, which that rule cannot know
   about. */
.got-cfg__legend {
  padding-inline: 0;
}
/* The required mark beside the group's name. aria-hidden decoration — the
   semantics ride on required/aria-required on the controls. */
.got-cfg__req {
  margin-inline-start: var(--gt-space-1);
  color: var(--gt-danger);
}
/* The price tag beside an option's name: the `.opt__extra` treatment (it is
   the same kind of fact — what this choice does to the figure), inline
   after the name rather than a flex item. */
.got-cfg__price {
  margin-inline-start: var(--gt-space-2);
  font-size: var(--gt-text-xs);
  font-weight: var(--gt-weight-bold);
  color: var(--gt-accent);
  font-variant-numeric: tabular-nums;   /* V-30 B3: one delta per option row, a column */
}

/* ------------------------------------------------- THE PRICE ASIDE ---
   The system's panel; the total in ink at the display step below the h1;
   the four facts as quiet lines over a rule; then the two buttons in tree
   order. */
.buy {
  display: flex;
  flex-direction: column;
}
/* `#total` is dir="ltr" (a money figure), so its own `start` is the LEFT and
   on /ar it sat left-aligned under a right-aligned label (the verifier's
   observation, most visible in the phone bar). The BOX shrink-wraps at the
   column's start instead — the account menu's email fix, one page over.
   (`text-align: match-parent` was measured computing to `start` in Chrome.) */
.total {
  align-self: flex-start;
  margin-block: var(--gt-space-1) var(--gt-space-1);
  font-size: var(--gt-text-3xl);
  font-weight: var(--gt-weight-heavy);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
  /* 🔴 PROPORTIONAL FIGURES, AND THE CAUSE WAS MEASURED BEFORE IT WAS CHANGED
     — V-27 Batch 1b.

     The gift page's hero price rendered `1 1.50$`: a visible hole between the
     two `1` characters, at 46px, on the one number the page exists to state.
     Measured in headless Chrome on `.got-gcpage .total` BEFORE any edit —
     font-family `Inter, …`, font-feature-settings `normal`, letter-spacing
     `normal`, font-size `46px`. Letter-spacing was NOT the cause and is not
     what moved; `tabular-nums` was.

     Tabular figures force every digit into one advance width, so a narrow `1`
     is centred in a slot cut for a `0` and carries air on both sides. That is
     invisible at 16px and glaring at display size, which is why the same
     declaration has stood harmlessly on this rule since the boosting page's
     22px price and only became a defect when V-25 B3 took the gift price to
     46px.

     `.total` IS the hero price and nothing else — one Blade uses it
     (`service.blade.php`'s `#total`, every service page's single figure), so
     there is no column here for tabular figures to align. THEY STAY EVERYWHERE
     THAT STACKS: the cart lines, the order tables, the wallet ledger and the
     admin tables all keep `tabular-nums`, because a column of figures that
     jiggles as a digit changes is the defect those declarations were added
     for. This is the one place the trade-off runs the other way. */
  font-variant-numeric: proportional-nums;
}
.total-note {
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
  margin-block-end: var(--gt-space-5);
}
.buy__lines {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-2);
  padding-block-start: var(--gt-space-4);
  margin-block-end: var(--gt-space-5);
  border-block-start: var(--gt-border-hair) solid var(--gt-line);
}
.buy__line {
  display: flex;
  justify-content: space-between;
  gap: var(--gt-space-4);
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
}
.buy__line b {
  color: var(--gt-ink);
  font-weight: var(--gt-weight-semibold);
  text-align: end;
}
/* The sentence under Buy — the whole explanation of a refusal on a sold-out
   card, so it is --gt-ink-muted (6.5:1) and never dimmer. */
.buy-note {
  margin-block: var(--gt-space-3) var(--gt-space-4);
  font-size: var(--gt-text-xs);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink-muted);
  text-align: center;
}

/* -------------------------------------------- THE PHONE'S PRICE BAR ---
   Below the two-column width the aside sticks to the bottom of the viewport
   as a bar, full-bleed to the gutters: the total block at the start, Buy at
   the end spanning it, Add to basket under both. The fact lines and the note
   step out (see the head note); the tree order is untouched. */
@media (max-width: 56.24em) {
  .buy {
    position: sticky;
    inset-block-end: 0;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "label buy"
      "total buy"
      "note  buy"
      "add   add";
    column-gap: var(--gt-space-4);
    row-gap: 0;
    align-items: center;
    margin-inline: calc(-1 * var(--gt-gutter));
    padding-block: var(--gt-space-3);
    padding-inline: var(--gt-gutter);
    border-radius: 0;
    border-inline: 0;
    box-shadow: var(--gt-shadow-3);
  }
  .buy .got-label { grid-area: label; }
  .total {
    grid-area: total;
    justify-self: start;   /* the grid's inline start: right on /ar */
    margin-block: 0;
    font-size: var(--gt-text-xl);
  }
  .total-note {
    grid-area: note;
    margin-block-end: 0;
  }
  .buy__lines,
  .buy-note {
    display: none;
  }
  .buy #buy {
    grid-area: buy;
    inline-size: auto;
    align-self: center;
    padding-inline: var(--gt-space-8);   /* the bar's one primary reads as one: wider than its word */
  }
  .buy #add-to-cart {
    grid-area: add;
    margin-block-start: var(--gt-space-3);
  }
}


/* ==========================================================================
   THE GIFT CARD'S PRODUCT PAGE — V-21 Batches 4b / 6 / 7
   ==========================================================================

   The layout scope is `.got-gcpage`, one class the Blade puts on the page's
   own `.container` for a gift card and nothing else — so every rule here
   reaches by CLASS, the form keeps the literal `class="cfg"` three tests pin,
   and a boosting page or a membership can never be reached by any of it.

   ------------------------------------------------- THE THIRD COLUMN CLAIM

   🔴 `.cfg` IS ALREADY A TWO-COLUMN GRID from 56.25em (panel | price aside).
   The artwork does NOT get a wrapper grid of its own: it joins `.cfg` as a
   first grid child and the gift scope RE-TEMPLATES the same grid — artwork on
   the reading side spanning both rows, the panel and the aside stacked in the
   second column. `grid-template-rows: auto 1fr` is load-bearing: with two
   auto rows the artwork's spanned height is distributed into BOTH rows and
   the aside is pushed down by half the poster's spare height (measured while
   building: the aside opened 154px below the panel); with the second row
   flexible the spare height lands entirely in the aside's own row, below it.

   The measured columns either side of the breakpoint are in the batch report,
   read off computed `grid-template-columns` — one column and two columns both
   look deliberate in a screenshot (the checkout's own container-query lesson).

   26.25rem (420px) + the 40px gap + the 1fr remainder is the 1140px container:
   the first track is the poster's own cap, the gap is `--gt-space-9`, and the
   right column takes what is left — MEASURED after the trim at 1536:
   420px 680px. (V-21 fixed the poster at 28.75rem/460 and this comment stated
   460 + 40 + 640 = 1140; V-22 B3 trimmed it on Omar's decision — the poster
   was the only object on the page with real visual weight and out-shouted the
   column that sells, so 40px moved from the picture to the words. The columns
   were BALANCED before the trim (artwork bottom 845 against the aside's 812,
   33px apart, measured), which is why the trim is modest: the fix is a
   stronger right column, not a small picture.) */

@media (min-width: 56.25em) {
  /* 🔴 ONE PANEL, NOT TWO — V-25 B3. The sequence (what -> which -> how much
     -> act) was already right; what was missing was that it read as ONE
     object: two `.got-card--panel`s in identical clothing with a 20px seam
     (MEASURED 29 Aug at 1536: panel 680x309 @y155, aside 680x319 @y484).
     That seam is `.cfg`'s own `row-gap` (the base `gap: var(--gt-space-6)`),
     so it closes HERE, in the rule that already re-templates this grid —
     never as a new `.got-gcpage .cfg` declaration, which winningRuleFor()
     would answer with the wrong rule (44 of the 53 twice-declared selectors
     in this cascade omit a property the earlier copy sets). */
  .got-gcpage .cfg {
    grid-template-columns: minmax(0, 26.25rem) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    column-gap: var(--gt-space-9);
    row-gap: 0;
  }
  .got-gcpage .got-gc__art {
    grid-row: 1 / span 2;
  }
  /* THE WELD. The touching corners square off and the panel's bottom border
     leaves, so the aside's own top border is the ONE hairline between the
     selector group and the price group — the internal seam is the design
     language's own rule, not a new stroke. Logical corner properties only,
     verified rendered on /ar (a radius has no direction to get wrong, but
     the claim is measured, not assumed).

     🔴 BANDED, AND THAT IS LOAD-BEARING: below 56.25em `.buy` is the phone
     bar shared by EVERY service page, whose (0,1,0) rules set
     `border-radius: 0` and `border-inline: 0` — an unscoped (0,2,0) weld
     rule restoring a radius here would beat them and break the bar for gift
     cards, the exact defect the rhythm block's comment names one selector
     over. Every `.got-gcpage .buy` rule lives inside this query. */
  .got-gcpage .cfg > .got-card--panel:not(.buy) {
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    border-block-end: 0;
  }
  /* 🔴 `align-self: stretch` IS THE TEAR GUARD, AND THE TEAR WAS MEASURED,
     NOT REASONED ABOUT. `.buy` is sticky at `--gt-scroll-offset` (the base
     layout block above), and closing the row gap left ~13px of slack in the
     aside's 1fr row at 1536 (art 630 against panel 309.6 + aside ~307 at
     the 46px price) — scrolled with behavior:'instant', the welded seam
     opened 8.44px at scrollY 380 and reached 42.45px SIGNED IN / 13.66px as a
     GUEST by scrollY 500 (29 Aug; the first reading of this said 0 because
     scroll-behavior:smooth had swallowed the scrollTo).

     🔴 THIS PARAGRAPH FIRST RECORDED "12.7px from scrollY 380", WHICH IS
     NEITHER THE POSITION NOR THE SIZE — V-25 B3's tester, sweeping ten scroll
     positions x four widths x two languages x two auth states with the guard
     removed. 380 is where the tear STARTS, not where it is worst, and the
     figure taken there understated the defect by three times on the very page
     this batch changed. The mechanism and the guard were right; the number
     beside them was a single reading quoted as a maximum. Nothing can go red
     on a figure in a comment, which is exactly why it is corrected here rather
     than quietly dropped. (The tear is worse SIGNED IN because the work note
     is gone from between the CTAs — this batch's own change 1 — so the aside
     is shorter and its row holds more slack.)

     Stretch fills the row, so the sticky has zero slack at EVERY width and
     stays declared rather than being locally un-stuck — and the merged
     column's bottom edge lands flush with the artwork's (0.0px, measured).
     The cost is ~13px of interior air under the last button at 1536, looked
     at and invisible as a defect; at 900 the column is taller than the art
     and the whole rule is inert. */
  .got-gcpage .buy {
    border-start-start-radius: 0;
    border-start-end-radius: 0;
    align-self: stretch;
  }
  /* 🔴 THE SIGNED-IN SEAM BETWEEN THE TWO CTAs — found by RENDERING the
     authenticated page, which nothing in the suite had ever done. V-25 B3
     gated the `.buy-note` <p> off (gift AND signed in; service.blade.php),
     and the element's margins were the ONLY air between `#buy` and
     `#add-to-cart` on the wide band: signed in, the buttons measured ~4px
     apart and read as one fused control. The ADJACENT-sibling combinator is
     the state selector CSS does not otherwise have here: the note element is
     absent from the DOM exactly when that state renders, and `display: none`
     (the phone bar's treatment of the note) does NOT satisfy adjacency, so
     this rule can never double the guest's spacing or the bar's own
     `--gt-space-3` (the bar band never enters this query). 12px is the
     page's intra-group step — the chip rows' own gap — measured and looked
     at against 16: both read as two controls, so the smaller existing step
     wins. */
  .got-gcpage .buy #buy + #add-to-cart {
    margin-block-start: var(--gt-space-4);
  }
}

/* 🔴 THE MODULE SURFACE — V-27 B2, and this one declaration is most of the
   batch. MEASURED 30 Aug at 1440x1000, both themes, before any edit: the two
   welded panels were `--gt-surface-raised` on the page at 1.11:1 (dark) and
   1.13:1 (light) — the rendered form of "reads as a wall rather than an
   object". The purchase module is the page's one decision object, so it sits
   one step further off the page than an ordinary card: `--gt-surface-module`,
   the role alias system.css defines over the ladder (dark -> overlay, light ->
   raised; the theme argument lives on the alias). Measured after: 1.24:1 dark;
   light is unchanged at 1.13:1 BY THE LADDER'S OWN ARITHMETIC — white is
   already the light ladder's top and there is nothing lighter to give (light
   overlay computes 1.05:1, WEAKER, and is the hover-wash colour besides).

   🔴 AND THAT ARITHMETIC CLOSED ONLY THE FILL AXIS, WHICH IS NOT THE SAME AS
   CLOSING THE REQUIREMENT — the batch's tester, and Omar's ruling, 30 Aug.

   MEASURED on light at 1440x1000, this module against the "What you receive"
   fine-print panel below it:

       background   rgb(255,255,255)   vs  rgb(255,255,255)
       border       1px rgb(221,227,237) vs 1px rgb(221,227,237)
       box-shadow   rgba(20,26,38,.05) 0 1px 1px 0  vs  IDENTICAL

   Three properties, three matches: on light the page's one decision object was
   pixel-identical to its fine print. "White is the ladder's ceiling" is true
   and answers only "can the FILL go lighter". The brief permits controlled
   ELEVATION, light defines `--gt-shadow-1..4`, and the module was taking the
   same `--gt-level-base` the fine print takes — so a whole axis was sitting
   unused while the requirement was reported as unmeetable.

   Omar's ruling: give it elevation on light. `--gt-shadow-3` is the step —
   light's `0 12px 28px -6px rgba(20,26,38,.14)` — scoped to light because dark
   already separates by fill and does not need it (stacking both would make the
   module float rather than sit). The fine print keeps `--gt-shadow-1`, so the
   two now differ on light by the one axis white left available.

   UNBANDED AND A CHILD SELECTOR, both deliberate: the surface travels with the
   module into the phone band, where the aside is the shared sticky bar — a
   gift page's bar wears the module surface because the bar IS the module
   there — and `> .got-card--panel` keeps any future sibling card on the
   ordinary card surface. (It said "the three notes" until V-27 B3 deleted
   them; the selector's job is unchanged and its example is gone.) It is a NEW selector, not a
   redeclaration: GiftCardLayoutTest counts exactly one `.got-gcpage .buy` rule
   and reads the weld off `winningRuleFor('.got-gcpage .cfg >
   .got-card--panel:not(.buy)')`, and this selector is neither. */
.got-gcpage .cfg > .got-card--panel {
  background: var(--gt-surface-module);
}

/* The light theme's half of the same requirement — elevation, because fill has
   nothing left to give there. Both roots, because the toggle must win in both
   directions: `prefers-color-scheme: light` for the system default (guarded so
   an explicit dark choice does not take it) and `[data-theme="light"]` for the
   explicit one. Dark is untouched and separates by fill at 1.24:1. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .got-gcpage .cfg > .got-card--panel {
    box-shadow: var(--gt-shadow-3);
  }
}
:root[data-theme="light"] .got-gcpage .cfg > .got-card--panel {
  box-shadow: var(--gt-shadow-3);
}

/* --------------------------------------------------------- THE ART FRAME --

   A 2:3 box the page reserves whether or not the file has arrived: the store
   scans take ~2.5s to decode, and a frame that waits for its image is layout
   shift on the page's largest paint. `max-inline-size` caps the poster in the
   single-column band too (768 would otherwise draw a 704×1056 poster);
   `justify-self: center` centres the capped frame there and is inert in the
   two-column band, where the column is the cap. Logical properties and a
   symmetric centre only — nothing here needs a [dir] rule.

   THE CAP IS THE GRID'S FIRST TRACK — one number in two places, and the grid
   comment above owns the argument for its value (V-22 B3: 26.25rem, the
   trim). Move one and the other moves in the same pass, or the single-column
   poster and the two-column one stop being the same object.

   NO GLOW AND NO PLATFORM WASH BEHIND IT — Omar's decision, V-22 B3. The
   separation it has is the design language's own: the hairline and
   `--gt-level-base`, unchanged by the trim. (An elevation step up was
   considered and rejected by looking: the artwork is the brightest object on
   a dark page and separates itself.)

   🔴 NOT THE MEDIA CARD. No sheen, no scanlines, no veil, no lift, no zoom,
   no capsule: hover-skill §1 scopes that grammar to surfaces whose whole body
   is clickable, and this frame is not a control — the image IS the product,
   there is nothing to explore toward, and a photo that moves under the
   pointer is a distraction. It takes no state at all, so there is nothing
   here for (hover:none) or reduced motion to answer. */
.got-gc__art {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 100%;
  max-inline-size: 26.25rem;
  justify-self: center;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: var(--gt-radius-lg);
  background: var(--gt-surface-sunken);
  border: var(--gt-border-hair) solid var(--gt-line);
  box-shadow: var(--gt-level-base);
}
/* The image sits a whisper under rest — `--gt-media-touch`, brightness .94
   against the brief's ~.95 — STATICALLY, in every input mode: the token that
   already means "the artwork settled so content reads over it", read here
   because a product page has no hover reward to hold brightness back for.
   One filter, no transition, no state ever changes it. */
.got-gc__img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
  filter: var(--gt-media-touch);
}
/* No artwork anywhere up the chain (`artworkModel()` ends at a bare game):
   the same material every fallback on this site paints — the system's
   placeholder under the same settled filter, the short name as the mark. The
   frame keeps its exact dimensions either way; a broken image never renders
   because the <img> only exists when a file does (<x-artwork>). */
.got-gc__art--type {
  background: var(--gt-media-placeholder);
  filter: var(--gt-media-touch);
}
.got-gc__mark {
  font-size: var(--gt-text-3xl);
  font-weight: var(--gt-weight-heavy);
  color: var(--gt-on-ink);
  text-shadow: var(--gt-media-text-shadow);
}

/* ------------------------------------------------------------- THE CHIPS --

   Real links to a sibling's own page, styled as the method tiles' small
   sibling: overlay surface, strong hairline, accent edge on hover, the
   accent tint when chosen. Selected keys on `[aria-current="page"]` so the
   state and its meaning are one attribute.

   44×44 minimum on both axes (`--gt-tap-min`) — these are the page's primary
   variant controls and they are tapped on phones.

   🔴 THE RING IS THE SYSTEM'S THREE DECLARATIONS, beside the component they
   ring — never a parallel outline (BUG-508) and never an entry in a list
   somebody maintains (the `.got-consent__text a` rename lesson). */
/* V-22 B3: the seam between the metadata group (the derived lead) and the
   selector group. The lead's own margin is 20px — the same 20px the region
   row puts before the CARD VALUE label — so the boundary BETWEEN two groups
   measured the same as the spacing INSIDE one, and the panel read as a
   single grey column. Inter-group beats intra-group: 32 over 20. Unscoped
   by width on purpose — the panel stacks identically in the single-column
   band, and this is panel rhythm, not the aside's (which is banded; see the
   rhythm block below). */
/* 🔴 THE HEADER ROW — V-27 B2: the product title and the fulfilment badge on
   one line, two weights (the composition's first row). The badge is DATA-FREE
   BY CONSTRUCTION: its word is one lang key (`site.service.gift.digital_badge`)
   said of every gift card of every brand — the day a badge needs a per-brand
   word it becomes a product field, not a second key. `space-between` puts the
   badge at the line's end in BOTH directions with no [dir] rule;
   `align-items: center` because the badge is a capsule beside a 28px title,
   not a run of its text. */
.got-gc__head {
  display: flex;
  /* wrap: on a phone the 28px title needs the panel's whole measure — the
     badge drops under it rather than squeezing it to a third line (looked at
     rendered at 390: unwrapped cost the title a line). */
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--gt-space-3) var(--gt-space-4);
  margin-block-end: var(--gt-space-8);
}
/* Inside the row the title's own seam collapses — the ROW owns the seam
   under it (the lead's old space-8, the inter-group step). */
.got-gc__head .got-svc__title {
  margin-block-end: 0;
}
/* The micro-label in a capsule: `.got-label`'s type rides the element in the
   Blade; this box is only the pill around it. `--gt-line-strong` and not the
   hairline: it is the same "quiet bordered capsule" weight the chips carry,
   and the hairline vanishes on the module surface. */
.got-gc__badge {
  flex: none;
  padding-block: var(--gt-space-2);
  padding-inline: var(--gt-space-4);
  border: var(--gt-border-hair) solid var(--gt-line-strong);
  border-radius: var(--gt-radius-pill);
  white-space: nowrap;
}

.got-gcpage .got-svc__lead {
  margin-block-end: var(--gt-space-8);
}
.got-gc__chiplabel {
  margin-block-end: var(--gt-space-3);
}
.got-gc__chips {
  display: flex;
  flex-wrap: wrap;
  /* V-22 B3: was --gt-space-3. At 8px two 44px pills read as one segmented
     control; at 12px they read as the separate destinations they are, and a
     wrapped second row no longer touches the first. */
  gap: var(--gt-space-4);
  margin-block-end: var(--gt-space-6);
}
.got-gc__chips:last-child {
  margin-block-end: 0;
}
.got-gc__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--gt-tap-min);
  min-inline-size: var(--gt-tap-min);
  padding-inline: var(--gt-space-6);
  /* V-22 B3: the pill, not --gt-radius-md. The 10px corner was the BUTTON'S
     shape on a control that is not a button — these are links wearing a
     label, and the pill is what this system already puts on a capsule, a
     badge and the product card's price: the "small labelled capsule" shape.
     The padding steps up with it (16 -> 20) because a pill's rounded ends
     eat inline padding optically; measured on the rendered row, "United
     States" holds one line and the row still fits the column with air. */
  border-radius: var(--gt-radius-pill);
  /* 🔴 THE STATE'S COLOUR IF A STATE HAS DECLARED ONE, THE NEUTRAL HAIRLINE
     IF NOT — the system's `--gt-edge-color` idiom, and this chip had to learn
     it the way every control in system.css already had.

     AS SHIPPED IN THE FIRST CUT, `:hover` and `[aria-current="page"]` each
     wrote `border-color: var(--gt-accent)` — which is the colour
     `--gt-ring-color` resolves to. So a SELECTED, FOCUSED chip painted the
     accent TWICE: the round's tester cut a 20px strip across the free edge at
     4x and run-length encoded it —

       card x24 | accent x8 | band x8 | ACCENT x4 | tint x36

     — two concentric accent strokes with the dark band between them, which
     CLAUDE.md quotes verbatim as BUG-247's defect rather than as a variation
     on it. It is reachable by pressing Tab on EVERY gift-card page, because
     the current page's own chip is the selected one in each of the two rows.

     THE REPAIR IS THE SYSTEM'S, NOT A LOCAL ONE. A state declares ONE colour
     and the ring is DEFINED as it, so the two cannot disagree; the state stops
     writing `border-color` itself, so `:focus-visible` out-specifies the edge
     with no appeal to source order and the border JOINS THE BAND while
     focused. `--gt-edge-color` is deliberately undefined at `:root` — its
     ABSENCE is the neutral state, which is what lets this one declaration
     serve both. */
  border: var(--gt-border-hair) solid var(--gt-edge-color, var(--gt-line-strong));
  /* V-27 B2: SUNKEN, not overlay — the chip is a choice control on the module
     surface, and on dark the module IS overlay now, so an overlay fill would
     be invisible against it. Sunken is the ladder's own answer for "a control
     recessed into its panel" (inputs and wells), and it reads in both themes:
     the fill sits below the module on dark and below white on light, with the
     `--gt-line-strong` boundary still the control's edge — 3.78:1 dark /
     3.52:1 light against sunken, config/brand.php's own measured table, so
     BUG-344's 3:1 floor holds on the new fill too. */
  background: var(--gt-surface-sunken);
  color: var(--gt-ink);
  font-size: var(--gt-text-sm);
  font-weight: var(--gt-weight-semibold);
  text-decoration: none;
  transition: var(--gt-transition-control);
}
/* HOVER HAS NOTHING TO SAY ABOUT A CONTROL THAT IS ALREADY FOCUSED, and
   `:not(:focus-visible)` is that sentence rather than a specificity device —
   `.got-field input:hover` carries the identical guard for the identical
   reason. Without it this rule (0,2,0) out-specifies the focus rule below
   (0,2,0, earlier in source) and could write over the focused border under
   the pointer.

   🔴 V-27 B2: HOVER NO LONGER RECOLOURS THE EDGE AT ALL — the
   `border-color: var(--gt-edge-color, var(--gt-accent-strong))` line is
   DELETED, not retuned, and the requirement it answers is Omar's: "selected
   chips must out-rank hover in BOTH themes". MEASURED 30 Aug before the
   change, stroke against the chip's own fill: on light the hovered stroke
   (accent-strong -> #0A5570) hit 7.68:1 while the SELECTED stroke (accent ->
   #0E6E92) sat at 5.34:1 — the accent family flips its relative weight
   between the themes, so any hover stroke drawn from it out-ranks selection
   on one theme or the other. No per-theme stroke pair can fix what a colour
   should not be saying: an accent edge on this row means YOU ARE HERE
   (`aria-current`), and an edge that previews it under the pointer is a
   control saying two things at once — the tint-layer half of this hover was
   repaired for exactly that (the guard below pins it). So hover keeps the
   state LAYER and the elevation, the edge stays the neutral
   `--gt-line-strong`, and selection out-ranks hover by construction:
   selected accent vs hovered line-strong, measured after with a REAL
   dispatched mouse event (the hovered chip's computed border read back as
   line-strong).

   TWO DENOMINATORS, BOTH WRITTEN OUT — corrected 30 Aug after the batch's
   tester reproduced them. Against the UNSELECTED chip's sunken fill, which is
   the like-for-like pair that proves the ORDERING: 10.87 vs 3.78 (dark), 4.66
   vs 3.52 (light). Against the fill each stroke ACTUALLY sits on — the
   selected chip's is tinted, not sunken: 7.23 vs 3.43 (dark), 4.81 vs 3.26
   (light). Selection wins in both sets; only the second is on screen.
   GiftCardLayoutTest's docblock carries the argument. */
.got-gc__chip:hover:not(:focus-visible) {
  /* V-22 B3: the edge alone was the whole hover, and on a 1px hairline it
     was easy to miss. The fill and the shadow are `.got-btn--secondary:
     hover`'s own two lines — a wash-soft STATE LAYER as background-image
     (so the selected chip's tint underneath survives and hover composes
     over it instead of previewing selection), and the system's hover
     elevation.

     🔴 AND "BOTH EASE" IS WHAT THIS COMMENT SAID, WHICH IS FALSE FOR THE
     FILL — corrected by V-22's tester, who installed a `transitionrun`
     listener and dispatched a REAL mouse event rather than reading the
     declaration. What actually started on hover then, 140ms each, was
     `border-*-color, box-shadow`; V-27 B2 deleted the border recolour (the
     block comment above), so the eased half is `box-shadow` alone now.

     `background-image` is not in that list and cannot be: the shorthand
     `--gt-transition-control` carries `background-COLOR`, and a
     background-color transition does not animate a background-IMAGE layer.
     So the wash arrives INSTANTLY and the elevation eases.

     THE CSS IS UNCHANGED AND IS CORRECT: `.got-btn--secondary:hover`
     measures identically, so this is the house pattern rather than a
     defect, and a 4% white layer appearing at once is not something anyone
     can see arrive. Only the sentence was wrong — which is this project's
     own named shape, a docblock stating a mechanism nobody had run.

     No new transition and no transform either way: chips are controls, and
     in this system a control answers hover with colour, not with the media
     card's lift. */
  background-image: linear-gradient(var(--gt-wash-soft), var(--gt-wash-soft));
  box-shadow: var(--gt-level-hover);
}
.got-gc__chip:active {
  transform: translateY(var(--gt-press-shift));
}
/* THE BORDER JOINS THE BAND — `--gt-ring-band` is the page colour, so the
   chip's own edge and the band are ONE 3px moat and the ring is the only
   stroke. A token and not `transparent`: transparent shows the chip's FILL
   through the border box, which would put the moat at 2px here and leave it
   at 3px on every other control for no reason a reader could recover. */
.got-gc__chip:focus-visible {
  outline: var(--gt-ring-width) solid var(--gt-ring-color);
  outline-offset: var(--gt-ring-gap);
  border-color: var(--gt-ring-band);
  box-shadow: var(--gt-ring-shadow);
}
/* THE SELECTED CHIP DECLARES A COLOUR AND DOES NOT PAINT ONE. Both
   declarations or neither: a state that recoloured the edge and forgot the
   ring is the defect `FocusVisibilityTest`'s derived invariant exists for, and
   a state that painted `border-color` itself is BUG-247 coming back. */
.got-gc__chip[aria-current="page"] {
  --gt-edge-color: var(--gt-accent);
  --gt-ring-color: var(--gt-edge-color);
  background: var(--gt-tint-accent);
  /* V-22 B3: a THIRD carrier for "you are here", beside the edge and the
     tint — at a glance the .12 tint under a 1px accent edge sat close to
     its neighbours' overlay-and-hairline. Weight, not colour: the ink stays
     --gt-ink (the method tiles' own selected treatment, and the label on
     the tint stays at the panel's full contrast instead of dropping to the
     accent's). The pill widens by the bold glyphs only — measured with the
     weight forced back to semibold on the rendered chip: "United States"
     grew 3.28px, and the row's 12px gaps absorb it without a wrap. */
  font-weight: var(--gt-weight-bold);
}
/* Sold out: STILL A LINK (Omar, 14 Aug — the basket takes a sold-out card),
   marked by a second carrier beside the colour: the edge goes dashed and the
   ink goes muted. Deliberately only those two declarations, so a chip that is
   both selected and sold out composes — dashed ACCENT edge over the tint —
   instead of one state erasing the other (the current page's own chip is
   exactly that state on a sold-out card). The accessible name carries
   "(Sold out)" in the markup. */
.got-gc__chip--out {
  border-style: dashed;
  color: var(--gt-ink-muted);
}

/* ------------------------------------- THE RIGHT COLUMN'S RHYTHM — V-22 B3 --

   V-27 B2 re-drew the column as the composition's three zones — header row +
   chips / exchange + CTAs / assurances — so the aside now reads exchange →
   Buy now → Add to basket → the guest sentence → the assurance zone. What
   this block owns is unchanged: the AIR between the groups and the price's
   own weight.

   THE PRICE IS THE DECISION POINT, so it takes `--gt-text-price` — its own
   step since V-25 B3. V-22 B3 gave it `--gt-text-display`, whose ceiling is
   56px from 1067px up — MEASURED 29 Aug at 1536: 56 against the same page's
   28px H1, exactly double, and with the two panels welded into one object
   the figure out-shouted the column it concludes. V-25 B3 chose 46 by
   rendering 44/46/48 while the figure stood ALONE; V-27 B2 took it to Omar's
   40 when the exchange block gave it company — the history and both
   arguments are on the token in system.css. Heavy stays; `dir="ltr"` and the
   shrink-wrap come from `.total`.

   THE HAIRLINE MOVED AGAIN, AND STILL DID NOT DIE: `.buy__lines` carried the
   aside's one rule, V-22 B3 handed it to the caption under the price, and
   V-27 B2 — which deleted that caption with the `Fixed price` string —
   handed it DOWN to the assurance zone's border-block-start: figure and
   actions are one group now, and the seam separates the group from its fine
   print.

   🔴 AND NO METADATA ROW SITS AT THAT SEAM, MEASURED SO THE NEXT ROUND DOES
   NOT RE-DERIVE IT (V-25 B3, 29 Aug). There is no availability datum to
   print: `supply_mode` is `on_demand` on 17 of 18 card rows, so there is no
   shelf and no count, and the one bit that exists — `isOutOfStock()` — is
   already spent on the disabled `#buy` + sold-out note (a row saying "In
   stock" would be a second answer to a question the button answers). The
   delivery TYPE is already prose in "What you receive", and any re-derived
   delivery FIGURE resurrects the number V-21 B3 deleted (`eta_hours = 2` is
   still in `psn-10-us`'s row; `OrderTransitions::deliver()`'s one call site
   is an admin action, not a clock) — GiftCardPageTest reddens on the promise
   words. Bring a NEW datum or bring nothing.

   SCOPED TO THE TWO-COLUMN BAND on purpose: below 56.25em `.buy` is the
   PHONE BAR — every service page's, not this page's (pages.css above), with
   its own grid. A `.got-gcpage .total` OUTSIDE any query is (0,2,0) and
   would beat every (0,1,0) rule the bar writes — which is exactly how the
   bar would break. The bar's PRICE SIZE alone is overridden for gift pages,
   deliberately, by the banded (0,2,0) rule in the max-width block BELOW
   this one; nothing else of the bar is touched.

   The lead and chip-group air sits UNSCOPED above (the panel stacks the same
   way at every width); only the aside's rules need the band guard. */
@media (min-width: 56.25em) {
  /* V-27 B2: the exchange grid owns the air around the figure now, so the
     old block margins go to zero. (The comment sits OUTSIDE the rule body on
     purpose: two falsification anchors grip this rule pair's declarations,
     and an anchor may not be made of comment prose.) */
  .got-gcpage .total {
    font-size: var(--gt-text-price);
    margin-block: 0;
  }
}

/* ------------------------------------------ THE EXCHANGE BLOCK — V-27 B2 --

   `You pay $11.50  ->  Account receives $10.00 {platform} credit` — the block
   that replaces the derived `United States · $10` lead, which sat above a
   DIFFERENT figure and never explained the difference. Face value belongs
   inside the exchange, where it explains rather than contradicts.

   🔴 FLAT CHILDREN, NO CELL WRAPPERS, AND THAT IS THE PHONE BAR'S DOING. The
   five elements (pay label, `.total`, arrow, receive label, receive cell) are
   DIRECT children of one wrapper, placed by grid-area — because below 56.25em
   the aside is the shared sticky bar, whose grid places DIRECT children into
   its `label/total/note` areas. `display: contents` on the wrapper (the banded
   rule below) dissolves it and hands the bar exactly the children it already
   knows how to place: the pay label is a `.got-label` (the bar's `label`
   area), `.total` is the bar's own price, and the receive cell takes `note`.
   A nested cell wrapper would strand the price inside a box the bar's grid
   cannot see.

   The grid's columns flip with the writing direction, so /ar lays out
   receive <- arrow <- pay with no [dir] rule; the ONE thing that cannot flip
   itself is the arrow GLYPH (U+2192 is not Bidi_Mirrored — the media card's
   own documented exception), swapped below exactly as `.got-gamecard__go`
   swaps its own.

   🔴 EVERY GRID-PLACEMENT RULE HERE IS BANDED TO THE WIDE BAND, AND THE FIRST
   CUT WAS NOT — measured broken before it was fixed: an unbanded
   `.got-gc__exchange .total { grid-area: pay }` is (0,2,0) against the phone
   bar's (0,1,0) `.total { grid-area: total }`, so inside the bar the price
   fell out of its named area into auto-placement — rendered at 390x844, the
   figure sat full-size at the bar's foot below Add to basket. Below 56.25em
   the wrapper is `display: contents` and the BAR owns placement; only the
   type rules (the arrow's colour, the value and caption steps) are unbanded,
   because they say what the elements look like, not where they go. */
@media (min-width: 56.25em) {
  .got-gc__exchange {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    grid-template-areas:
      "paylabel arrow recvlabel"
      "pay      arrow recv";
    column-gap: var(--gt-space-7);
    row-gap: var(--gt-space-2);
    align-items: baseline;
    margin-block-end: var(--gt-space-6);
  }
  .got-gc__paylabel { grid-area: paylabel; }
  .got-gc__recvlabel { grid-area: recvlabel; }
  /* The figure joins its grid cell: the base `.total` alignment answers a
     flex column this element is no longer in. `justify-self: start` keeps the
     shrink-wrap fact (a dir="ltr" money run must not stretch across an /ar
     cell); baseline puts the 40px figure and the 22px face value on one
     line. */
  .got-gc__exchange .total {
    grid-area: pay;
    justify-self: start;
    align-self: baseline;
  }
  .got-gc__arrow {
    grid-area: arrow;
    align-self: center;
  }
  .got-gc__receive {
    grid-area: recv;
  }
}
.got-gc__arrow {
  color: var(--gt-ink-muted);
  font-size: var(--gt-text-xl);
}
.got-gc__arrow::before { content: "\2192"; }
[dir="rtl"] .got-gc__arrow::before { content: "\2190"; }
/* The face value: real money, one step under the hero figure — the exchange
   reads as a conversion, not two competing prices. The credit phrase is the
   caption's own quiet step beside it. */
.got-gc__recvalue {
  font-size: var(--gt-text-xl);
  font-weight: var(--gt-weight-bold);
  color: var(--gt-ink);
  /* V-30 B3: a face value that changes with the chosen chip, in a slot the
     credit phrase sits beside — tabular so the phrase does not walk. */
  font-variant-numeric: tabular-nums;
}
/* THE TILE LABEL'S MONEY HALF gets its first rule. The component's own
   comment says the class was a handle for a guard and carried no CSS; it now
   carries the one declaration a stacked price needs, because ten tiles in a
   row each print a face value and the row reads as a column. V-30 B3. */
.got-gclabel__money {
  font-variant-numeric: tabular-nums;
}
.got-gc__credit {
  margin-inline-start: var(--gt-space-2);
  font-size: var(--gt-text-sm);
  color: var(--gt-ink-muted);
}

/* ------------------------------------------ THE ASSURANCE ZONE — V-27 B2 --

   The module's third zone: the hairline seam, then the three one-line
   assurances — TWO of them since V-27 B3 deleted `assure_region` with its
   reader, not three; the region is the chips' business in the module, the
   warning's below it, and the Basic info table's as a reference, one register
   each. (The comment here said "three" until V-27 B5's verifier counted them;
   the Blade's own note at `service.blade.php` already said "the two that
   stay".) The system's stacked facts list — dot, 13px, muted. The seam
   the caption used to carry between the figure and the buttons moved DOWN
   with the composition: figure and actions are one group now, and the seam
   separates the group from its fine print. GiftCardLayoutTest's seam
   assertion was re-pointed here in the same pass. Hidden inside the phone
   bar (banded below): a sticky bar carrying prose would eat the viewport the
   bar exists to save.

   🔴 WHAT CARRIES THAT GROUND ON A PHONE IS NOT THE THREE NOTES ANY MORE —
   V-27 B3 deleted them, and this comment named them until B5's verifier
   caught it. It is the About region's amber warning and its Basic info table,
   both of which are OUTSIDE any `<details>` and never fold (measured 388px and
   159px at 390x844). That is the same standing decision V-25 B4 made — the
   refund-preventing facts stay open on a phone — carried by the blocks that
   replaced the notes rather than by the notes. */
.got-gc__assure {
  margin-block-start: var(--gt-space-6);
  padding-block-start: var(--gt-space-6);
  border-block-start: var(--gt-border-hair) solid var(--gt-line);
}

/* -------------------- THE PHONE BAR'S GIFT ZONE — V-25 B3, grown by V-27 B2 --

   Below 56.25em the gift page's ONLY price element is the shared phone bar's
   `#total` — the left panel carries no price at all — so a fixed-price
   product's hero figure was 22px at the foot of a phone. The (0,2,0) rules
   here beat the bar's (0,1,0) rules DELIBERATELY, inside the same band, and
   reach only gift pages: `.got-gcpage` is emitted for `isGiftCard()` and
   nothing else.

   V-25 B3 set the bar's gift price to 36px (`--gt-text-3xl`) after rendering
   36 against 40 and preferring the smaller (bar 134.0px at 390, the fixed-bar
   rule: a fixed bar must not obscure content). V-27 B2 re-pointed the rule at
   `--gt-text-price`, whose PHONE value is Omar's 32 — the brief's number.

   🔴 AND THE SENTENCE THAT STOOD HERE CLAIMED V-25's MEASUREMENT "BOUNDS THIS
   ONE", WHICH IS FALSE — corrected by V-27 B5's browser verifier, and worth
   keeping as the shape rather than quietly deleting.

   The argument was: 32 is smaller than 36, V-25 measured the bar at 134.0px
   with 36, therefore the bar is under 134.0px now. It is not. MEASURED 30 Aug
   at 390x844, both locales: the gift bar is 153.17px — 19.17px ABOVE the
   figure said to bound it, and 18.1% of the viewport (a non-gift bar measures
   136.38px). The font-size premise is true; the conclusion does not follow from
   it, because V-27 B2 folded the exchange's three lines — YOU PAY, the figure,
   the receive note — into a bar the 134.0px measurement never carried. A
   smaller FONT in a taller COMPOSITION is a bigger bar.

   The bar is still well inside the ~30% budget the fixed-bar rule cares about,
   so nothing here changes; what changes is that the number is measured rather
   than inherited from a batch whose bar held less. Measured 30 Aug at 390x844: 32px rendered,
   `#buy` one 44px line in its area, receive line under the figure, no
   horizontal overflow (scrollWidth 390 = clientWidth 390, rect sweep clean —
   every flagged rect clipped by an overflow-hidden ancestor).

   🔴 THIS BLOCK MUST STAY BELOW THE BAR'S OWN `@media (max-width: 56.24em)`
   BLOCK: CatalogueUxTest preg_matches the FIRST such block in this file and
   asserts five things about its body, so a second block ABOVE the bar's
   turns that guard red on correct CSS. (Its message says "One phone-bar
   block", which a preg_match cannot check — an overstatement reported in
   V-25 B3's round notes, not relied on here.) */
@media (max-width: 56.24em) {
  .got-gcpage .total {
    /* V-27 B2: the same token as the wide band — 32px here, 40px there; the
       breakpoint in system.css moves the value, the two banded rules exist
       only to out-specify two different base rules (the aside's flex column
       and the bar's 22px). */
    font-size: var(--gt-text-price);
  }
  /* THE EXCHANGE JOINS THE BAR — the wrapper dissolves and the bar's own grid
     places the children: pay label -> `label` (it is a `.got-label`), `.total`
     -> `total`, and the receive cell -> `note`, so the bar reads
     "YOU PAY / $11.50 / $10 {platform} credit" with Buy beside it. The arrow
     and the second label step out: a two-cell diagram has no room in a bar,
     and the credit phrase says the receiving half on its own. */
  .got-gcpage .got-gc__exchange { display: contents; }
  .got-gcpage .got-gc__arrow,
  .got-gcpage .got-gc__recvlabel { display: none; }
  .got-gcpage .got-gc__receive { grid-area: note; }
  .got-gcpage .got-gc__recvalue { font-size: var(--gt-text-xs); }
  .got-gcpage .got-gc__credit { margin-inline-start: var(--gt-space-1); font-size: var(--gt-text-xs); }
  /* The fine print leaves the bar with the fact lines and the note — the
     shared bar's own rule for `.buy__lines`/`.buy-note`, said of this zone. */
  .got-gcpage .got-gc__assure { display: none; }

  /* 🔴 THE ARTWORK IS CROPPED, NOT SHRUNK — Omar's requirement, V-27 B2.
     Measured before: the 2:3 frame was 350x525 at 390x844 — most of the first
     screen — and shrinking it would surrender the flag and the value that
     make the card recognisable. A 4:3 WINDOW anchored to the frame's top
     (object-position: top — a vertical anchor, nothing to mirror on /ar)
     keeps the top 262px of a 350px-wide poster, where the covers put the
     flag, the value and the top-up line, and `--gt-media-crop-fade` (a mask,
     so the fade lands on either theme's page without naming a colour)
     dissolves the window's lower edge instead of cutting it. Measured after
     at 390x844, both languages: frame 350x262.5 (was 350x525), the art's
     bottom edge moves y668 -> y405.5 — 262.5px of first screen handed back
     to the module, whose title and chips now open above the fold. */
  .got-gcpage .got-gc__art {
    aspect-ratio: 4 / 3;
    -webkit-mask-image: var(--gt-media-crop-fade);
            mask-image: var(--gt-media-crop-fade);
  }
  .got-gcpage .got-gc__img {
    object-position: top;
  }
}

/* ------------------------------------------------------ THE ABOUT REGION --

   V-27 B3. It REPLACES the three note panels (V-21 B5) in the same slot, so
   it inherits their seams: the section seam above is `.cfg`'s own
   padding-block-end and the one below is the related row's margin (V-22 B5's
   40/40 argument, unchanged). Four blocks in one stacked grid: the amber
   region warning, the Basic info table, and two <details> folds — the words
   all come from the product record, `site.service.brands.*` through
   `App\Support\GiftCardCopy`, or `site.service.about.*`; nothing here or in
   the component names a brand, a region or a number.

   THE GAP IS THE PAGE'S OWN CARD SEAM (--gt-space-6), the notes' V-22 B5
   argument inherited whole: `.cfg`'s panel-to-aside gap and the related
   grid's --gt-tile-gap directly below are both 20px in the same container.

   🔴 WHY THE WARNING IS NOT A `.got-message`: the flash message is a
   TRANSIENT — it appears after an action and reads in the page's voice for a
   moment. This is a STANDING block with a heading and a document-outline
   place, and it borrows only the flash's amber GRAMMAR (the tint fill, the
   --gt-border-bar inline-start rule) so a customer who has seen a warning
   flash reads this as the same severity. The tokens are the system's
   `--gt-warning` / `--gt-tint-warning`; the soon-orange is NOT touched —
   Omar's 19 Aug ruling scopes that colour to the coming-soon tile alone, and
   `--gt-warning` is the palette's own amber, already worn by
   `.got-message--warning`, the attention pill and the pending ring. */
.got-gc__about {
  display: grid;
  gap: var(--gt-space-6);
}
/* The region's micro-label, the related row's own treatment: the section is
   labelled the way every store section on this page is. */
.got-gc__aboutlabel {
  margin: 0;
}
.got-gc__warn {
  padding-block: var(--gt-space-5);
  padding-inline: var(--gt-space-6) var(--gt-space-5);
  border-radius: var(--gt-radius-md);
  border-inline-start: var(--gt-border-bar) solid var(--gt-warning);
  background: var(--gt-tint-warning);
}
.got-gc__warntitle {
  margin: 0;
  font-size: var(--gt-text-sm);
  font-weight: var(--gt-weight-bold);
  line-height: var(--gt-leading-tight);
  color: var(--gt-warning);
}
.got-gc__warnbody {
  margin-block: var(--gt-space-2) 0;
  font-size: var(--gt-text-sm);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink);
  max-inline-size: 110ch;   /* a measure: the callout spans the container, its sentence must not */
}

/* The Basic info table: one dl carrying the two shared columns, each row a
   SUBGRID over them — `max-content` sizes the label column to the longest
   label in the rendered language (so neither language carries a guessed
   width), and subgrid is what makes that one answer for the whole table
   rather than one per row (MEASURED before it: seven rows, seven different
   value-column starts). Where subgrid is unsupported the row falls back to
   its own two columns — ragged, never broken. */
.got-gc__specs {
  margin: 0;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--gt-space-3) var(--gt-space-6);
  margin-block-start: var(--gt-space-4);
}
.got-gc__spec {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: var(--gt-space-6);
  align-items: baseline;
  padding-block-end: var(--gt-space-3);
  border-block-end: var(--gt-border-hair) solid var(--gt-line);
}
@supports (grid-template-columns: subgrid) {
  .got-gc__spec {
    grid-template-columns: subgrid;
  }
}
.got-gc__spec:last-child {
  padding-block-end: 0;
  border-block-end: 0;
}
.got-gc__spec dt {
  font-size: var(--gt-text-xs);
  color: var(--gt-ink-muted);
}
.got-gc__spec dd {
  margin: 0;
  font-size: var(--gt-text-sm);
  line-height: var(--gt-leading-snug);
}

/* -------------------------------------------------------------- THE FOLDS

   Native <details>, shipped open — the component's script closes them on a
   phone-band LOAD and nothing else touches them (the fold is an enhancement;
   JS-off reads everything open). The summary is the whole ruled row, so the
   tap target is the full card width; its own padding replaces the panel's
   (the panel's is zeroed) so the hit area reaches the card edge.

   🔴 THE SUMMARY'S OWN RULES LIVE IN system.css (THE DISCLOSURE ROW), NOT
   HERE — a <summary> is a form-like CONTROL, and SystemCoverageTest requires
   every control the browser draws to wear a class the SYSTEM styles: the
   first cut put the whole control in this file and that test found it
   unclaimed on its first run, which is that guard working. This file keeps
   only the fold's LAYOUT — the panel's zeroed padding, the body grid, and
   the phone band's density overrides (a later stylesheet, so they win the
   block axis at equal specificity). */
.got-gc__foldbody {
  padding-block: 0 var(--gt-space-7);
  padding-inline: var(--gt-space-7);
  display: grid;
  gap: var(--gt-space-5);
}
/* Prose measures — the fold spans the container; a 14px sentence may not. */
.got-gc__foldbody > .got-card__lead {
  max-inline-size: 80ch;   /* a measure */
}
.got-gc__buys {
  margin: 0;
  padding-inline-start: var(--gt-space-6);
  display: grid;
  gap: var(--gt-space-2);
  font-size: var(--gt-text-sm);
  line-height: var(--gt-leading-normal);
  max-inline-size: 80ch;   /* a measure */
}

/* The two feature blocks — and since V-28 B1 the image is OPTIONAL, so the
   CLASS says which composition renders rather than the width alone:

     `.got-gc__feature`        the FULL-WIDTH TEXT composition — the resting
                               state, worn by every brand with no art on
                               disk. One column at every width; the lead
                               takes the fold's own 80ch measure below, so
                               it reads as the same document as the intro
                               and the lists around it, never as a column
                               whose picture failed to arrive.
     `.got-gc__feature--duo`   art exists (StoreBrandArt found the file):
                               two columns from the page's own two-column
                               width, the second row flipped so text and
                               art alternate sides. `order` swaps GRID
                               PLACEMENT, not reading order — the art box
                               is aria-hidden decoration, so the accessible
                               sequence is untouched in both directions.

   The V-27 B3 sized-empty 16:9 box is DELETED with its reason: it reserved
   a ratio for art that had not landed, and art now either exists — the
   <img>'s own width/height attributes reserve the box (see the frame
   below) — or no box is owed at all. */
.got-gc__feature {
  display: grid;
  gap: var(--gt-space-5);
  align-items: center;
}
/* The feature paragraph's measure. 🔴 THE FOLDBODY'S OWN 80ch RULE IS A
   DIRECT-CHILD SELECTOR and never reached this lead, which is NESTED in
   `__featuretext` — inert while every feature was a half-width column,
   and the commonest failure of a full-width text composition the moment
   V-28 made one. V-28's reading, on /games/steam/steam-10-us at viewport
   1536 while Steam was still imageless: without this rule the paragraph
   runs the fold's full 1090.4px (~125ch); with it, 700px.

   🔴 THAT URL NO LONGER SHOWS THE STATE IT WAS MEASURED ON — V-29 B1
   installed feature art for every brand in the catalogue, so every product
   page in the product renders the `--duo` composition today and the
   full-width one is reachable only from a brand with no file on disk.
   The reading is kept with its provenance rather than re-pointed at a page
   that would not reproduce it, and the half that is re-measurable was
   re-measured on this tree: 80ch resolves to 700px on the rendered lead
   (ch = 8.75px at its 14px, probed in the page's own resolved font — the
   figure moves in a browser that never received Inter). Same 80ch as every
   measure in this fold, and `StoreBrandArtTest` holds the imageless
   composition on a synthetic brand for exactly this reason. */
.got-gc__featuretext > .got-card__lead {
  max-inline-size: 80ch;   /* a measure */
}
@media (min-width: 56.25em) {
  .got-gc__feature--duo {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .got-gc__feature--flip .got-gc__featureart {
    order: -1;
  }
}
.got-gc__featuretitle {
  margin: 0 0 var(--gt-space-2);
  font-size: var(--gt-text-md);
  font-weight: var(--gt-weight-bold);
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
}
/* The feature figure's frame: THE PURCHASE MODULE'S OWN ART TOKENS, so the
   two halves of the page read as one object — `.got-gc__art` above is the
   precedent and every value is its: radius-lg, the system hairline,
   `--gt-level-base`, the sunken surface behind the lazy image while it
   loads, and the image settled at `--gt-media-touch` STATICALLY (the token
   that already means "artwork settled so content reads over it"; one
   filter, no transition, no state ever changes it — the figure is not a
   control and takes no hover, per the V-28 brief's own rule that an
   interaction leading nowhere is decoration).

   NO aspect-ratio here: the box is sized by the image's OWN width/height
   attributes (aspect-ratio:auto w/h) against the grid column's definite
   inline size — the img writes inline-size:100% because the TRAPS lesson
   is that the attributes reserve nothing once a stylesheet leaves
   inline-size auto. Reservation measured in Chrome, rects before/after
   decode. Sources are 1586x992 (~16:10, measured off the files); framing
   them 16:9 would crop 10% of a composed render, so the frame takes the
   image's ratio rather than the other way round. */
.got-gc__featureart {
  border-radius: var(--gt-radius-lg);
  border: var(--gt-border-hair) solid var(--gt-line);
  background: var(--gt-surface-sunken);
  box-shadow: var(--gt-level-base);
  overflow: hidden;
}
.got-gc__featureimg {
  display: block;
  inline-size: 100%;
  block-size: auto;
  filter: var(--gt-media-touch);
}
.got-gc__steps {
  margin: 0;
  padding-inline-start: var(--gt-space-6);
  display: grid;
  gap: var(--gt-space-3);
  font-size: var(--gt-text-sm);
  line-height: var(--gt-leading-normal);
  max-inline-size: 80ch;   /* a measure */
}
.got-gc__steps li::marker {
  color: var(--gt-accent);
  font-weight: var(--gt-weight-bold);
}

/* ------------------------------------- THE ABOUT REGION ON A PHONE — B3 --

   The notes' V-25 B4 density argument, inherited by their replacement: on a
   phone the region stacks into one tall group, so the seams and the block
   paddings each drop one step while the INLINE padding keeps the measure.
   The warning and the Basic info table are OPEN on this band — they carry
   the region, delivery and problem-window facts alone here, because the
   assurance zone is display:none below 56.25em (the aside is the bar) — and
   only the two prose folds start closed, via the component's script.

   🔴 SCOPED TO THE PHONE BAND AND PLACED BELOW THE GIFT PRICE BLOCK — the
   ordering note the notes' block carried, still load-bearing: this is a
   THIRD `@media (max-width: 56.24em)` block and it must sit after the shared
   phone bar's own (the FIRST, which CatalogueUxTest reads by first-match)
   AND after the gift price block (GiftCardLayoutTest's `.got-gcpage .total`
   regex reads the first max-width block after the bar). */
@media (max-width: 56.24em) {
  .got-gc__about {
    gap: var(--gt-space-4);
  }
  /* Block axis only, the notes' own lesson: the panel's `padding:
     var(--gt-space-7)` keeps its inline pair so the text keeps its measure. */
  .got-gc__basics {
    padding-block: var(--gt-space-5);
  }
  .got-gc__warn {
    padding-block: var(--gt-space-4);
  }
  .got-gcpage .got-gc__foldhead {
    padding-block: var(--gt-space-4);
  }
  .got-gc__foldbody {
    padding-block-end: var(--gt-space-5);
  }
  /* V-28 B1: stacked, a feature's art sits under its own words at the SAME
     16px the fold puts before the NEXT feature — looked at rendered at 390,
     the first feature's image read as the second feature's picture, because
     nothing said which heading owns it. Inter-group beats intra-group (the
     module's own 12-over-16 argument): inside a stacked feature the gap
     drops one step to space-4 and the fold's 16px seam stays between
     features. Banded because the two-column band's 16px is a COLUMN gap
     between text and art, which has no such ambiguity. */
  .got-gc__feature {
    gap: var(--gt-space-4);
  }
}

/* -------------------------------------------------- THE RELATED PRODUCTS --

   One row, below the notes: the micro-label over the store's own
   `.got-product-grid` (fixed 4/3/2) holding the Batch 2 product card —
   nothing here styles the card, because the card is the tile's and one copy
   is the point. The section only exists when there are siblings (the Blade
   gates it), so there is no empty state to design.

   THE SEAM ABOVE IS A SECTION SEAM AND IT TAKES THE SECTION STEP — V-22 B5:
   `margin-block-start` was --gt-space-8, and the page's other section seam
   (`.cfg`'s own padding-block-end, buy panel to notes) is --gt-space-9. That
   put the page's NARROWEST section seam on its biggest change of topic —
   32px between this product's prose and OTHER products, under 40px between
   the panel and prose about the same card. One step, both seams, measured
   after the change at 40/40 either side of the notes.

   WHAT IS LEFT ALONE, BY NAME: the label is the category page's group
   heading exactly — `.got-label` over the grid with the same --gt-space-4
   seam `.got-cat__group .got-label` carries — and it stays that way on
   purpose. This row IS a catalogue group on another page; a bigger heading
   here would be the related row heading itself differently from the grid it
   quotes, which is the drift the shared card just closed. */
.got-gc__related {
  margin-block: var(--gt-space-9);
}
.got-gc__relatedlabel {
  margin-block-end: var(--gt-space-4);
}


/* ==========================================================================
   /design — THE REFERENCE PAGE
   ==========================================================================

   The page's own furniture: swatch grids, specimen rows, the ratio table's
   failure marker. Nothing here is a component — a component is something a
   product page uses, and none of these appear anywhere but /design.

   IT IS STILL TOKENS ONLY. The reference page is held to the rule it exists
   to document, and `SiteTokensTest` parses this whole file rather than the
   basket's part of it.

   🔴 THERE IS NO FORCED-STATE CSS HERE, and its absence is the design. A
   `.is-hover` class showing what hover looks like would be every hover
   declaration written a SECOND time, and the page would then show what the
   copy says rather than what the component does. The two would agree until
   somebody changed one of them. The controls on /design are live: you hover
   them. */

.got-design > section {
  margin-block-end: var(--gt-space-7);
}
/* The last one wants room before the footer. */
.got-design > section:last-of-type {
  margin-block-end: var(--gt-space-9);
}
.got-design h3.got-label {
  margin-block: var(--gt-space-7) var(--gt-space-4);
}
.got-design > p.got-message {
  margin-block-end: var(--gt-space-7);
}
/* The two media-card specimens. Two fixed tracks under a measure, so a 3:4
   tile stays a tile on this page rather than a 700px poster — the reference
   has to be looked at, and a card that fills half the viewport cannot be
   compared with its neighbour. 30rem is a measure, not a rhythm step. */
.got-design__tiles {
  display: grid;
  gap: var(--gt-space-5);
  /* BUG-383 (19 Aug): four columns at every width gave the game-card
     specimens 76px at 390 and they clipped their own content. Auto-fill with
     the tile's own minimum under the strip's cap: four at 52rem, two at 390. */
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  max-inline-size: 52rem;
}
.got-design .got-card__lead + .got-stack,
.got-design .got-field__help {
  margin-block-start: var(--gt-space-4);
}
/* Inline code in the prose. It names an identifier, so it is monospace and
   sits in a well — the same recessed surface an input uses, for the same
   reason: it is machine text rather than a sentence. */
.got-design code {
  padding-block: var(--gt-space-1);
  padding-inline: var(--gt-space-2);
  border-radius: var(--gt-radius-xs);
  background: var(--gt-surface-sunken);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--gt-text-2xs);
  color: var(--gt-ink-muted);
}

/* -------------------------------------------------------------- SWATCHES */
.got-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gt-space-5);
}
.got-swatch {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-2);
}
.got-swatch__chip {
  display: block;
  inline-size: 5rem;
  block-size: 3rem;
  border-radius: var(--gt-radius-sm);
  border: var(--gt-border-hair) solid var(--gt-line-strong);
}
/* A tint is transparent, so it has to be shown ON something or it shows the
   page through itself and reads as the wrong colour. */
.got-swatch__chip--onraised {
  background-color: var(--gt-surface-raised);
}
.got-swatch__chip--radius,
.got-swatch__chip--raised {
  background: var(--gt-surface-overlay);
}
.got-swatch__name {
  font-size: var(--gt-text-2xs);
  color: var(--gt-ink);
}
.got-swatch__hex {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--gt-text-3xs);
  color: var(--gt-ink-muted);
}
/* The dot beside a role name in the ratio table. */
.got-swatch__dot {
  display: inline-block;
  inline-size: var(--gt-space-4);
  block-size: var(--gt-space-4);
  border-radius: var(--gt-radius-pill);
  border: var(--gt-border-hair) solid var(--gt-line-strong);
  vertical-align: middle;
  margin-inline-end: var(--gt-space-2);
}

/* A ratio under the 4.5:1 AA floor. The tint carries it and the text stays
   `--gt-ink`, which is the same rule every message box follows — and it has
   to be, because this cell is the one place on the site where a number being
   hard to read would be actively funny. */
.got-ratio--fails {
  background: var(--gt-tint-danger);
  color: var(--gt-ink);
  font-weight: var(--gt-weight-bold);
  border-radius: var(--gt-radius-xs);
}

/* ---------------------------------------------------------- SPECIMEN ROWS */
.got-spec {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--gt-space-4);
  padding-block: var(--gt-space-3);
  border-block-start: var(--gt-border-hair) solid var(--gt-line);
}
/* The token name column. A fixed measure so the specimens beside them line
   up into a column you can read down — `ch` because the content is
   monospace identifiers and a character IS the right unit for them. */
.got-spec__token {
  flex: 0 0 auto;
  /* BUG-357 (19 Aug): 22ch at 390 pushed the row's other children past the
     card; the measure is capped at the row's own width now, and every child
     may shrink and wrap its words. */
  min-inline-size: min(22ch, 100%);
  max-inline-size: 100%;
  overflow-wrap: anywhere;
}
.got-spec > * {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
/* BUG-357: the reference tables (the surface ladder, the contrast grid) are
   wider than a phone; they scroll inside their own box rather than pushing
   the page — the same rule every wide thing on the customer side follows. */
.got-design table {
  display: block;
  max-inline-size: 100%;
  overflow-x: auto;
}
.got-spec__note {
  font-size: var(--gt-text-2xs);
  color: var(--gt-ink-muted);
}
.got-spec__bar {
  display: block;
  block-size: var(--gt-space-4);
  border-radius: var(--gt-radius-xs);
  background: var(--gt-accent);
}
.got-spec__rule {
  display: block;
  inline-size: 8rem;
  border-block-start-style: solid;
  border-block-start-color: var(--gt-line-strong);
}

/* ------------------------------------------------------------ ARRANGEMENTS

   Three layout helpers, used only by this page. They are NOT utilities and
   must not be reached for from a product page: a `.got-row` on /account is
   the first utility class, and this project has already decided that a
   utility reached across boundaries is one somebody edits for a single
   caller. A product page gets its own named layout in this file. */
.got-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--gt-space-3);
}
.got-stack {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-4);
}
.got-grid {
  display: grid;
  gap: var(--gt-space-5);
  /* auto-fit with a minimum: the reference page has to be legible at 390px
     and at 1280px without a breakpoint of its own, because it is the page
     you check the OTHER pages' breakpoints on. */
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
}


/* --------------------------------------------------- THE HEADER'S ENTRY ---

   The basket link in the site header. Migrated with the page because it is
   `got-cart__*` and leaving it behind would have split one component across
   two stylesheets and two systems.

   Structure only, exactly as before: a link the size of a finger, a number
   that is always in the same place, and a focus ring. The measurements that
   justify each of those are in site.css's header section, which owns the row
   this sits in and has not been migrated yet.

   `min-block-size` is BUG-037's 44px tap floor, now `--gt-tap-min`.

   V-30 B6: THE COUNT IS A BADGE ON THE GLYPH. MEASURED before: `🛒 2` and
   `$10.00` sat 8px apart with no label between them and read as one
   figure. The count now sits in a filled disc pinned to the glyph's
   top-inline-end corner (absolute inside this link, which is the tap
   square: 44 wide by the floor, the glyph centred). The disc's insets are
   the offset that centres the disc on the glyph's corner — B6 wrote it for
   a 16px disc on a 20px glyph, (44 − 20) / 2 − 16 / 2 = 4; V-33 B4 grew
   both (24 and 20, `.got-cart__navicon` / `.got-cart__navcount` below) and
   the same arithmetic gives 0 — and are the only inset declarations the
   badge carries; HeaderHeightTest's sweep reads them as a position inside
   the link's own box. */
.got-cart__nav {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--gt-tap-min);
  min-block-size: var(--gt-tap-min);
  padding-inline: var(--gt-space-2);
  border-radius: var(--gt-radius-sm);
  color: var(--gt-ink);          /* 15.45:1 over the header's background */
  transition: var(--gt-transition-control);
}
.got-cart__nav:hover {
  background: var(--gt-wash-soft);
}
.got-cart__nav:focus-visible {
  outline: var(--gt-ring-width) solid var(--gt-ring-color);
  outline-offset: var(--gt-ring-gap);
  box-shadow: var(--gt-ring-shadow);
}

/* currentColor, so the icon and the count are one thing that cannot drift
   apart the day the header's text colour moves.

   V-33 B4: THE GLYPH IS `--gt-space-7` (24px, was 20) AND THE BADGE
   `--gt-space-6` (20, was 16) WITH A `--gt-text-2xs` DIGIT (13px, was 12).
   Omar's words, 4 Sep: the header icon is small and plain. The tap square
   stays 44 (the floor, both axes); the picture inside it grows a step, the
   wallet's glyph and the menu's move with it (one token for the family —
   HeaderAccountTest), and the count is a disc a thumb can read at arm's
   length rather than a dot. The insets are re-derived from the same
   arithmetic V-30 B6 wrote — the offset that centres the disc on the
   glyph's corner: (44 − 24) / 2 − 20 / 2 = 0 on both axes, so the badge
   sits in the tap square's own corner. `--gt-space-0` and not a bare 0: the
   two insets stay a position inside the link's box for HeaderHeightTest's
   sweep, and stay the scale's word for it. */
.got-cart__navicon {
  inline-size: var(--gt-space-7);
  block-size: var(--gt-space-7);
  display: block;
}
.got-cart__navcount {
  position: absolute;
  inset-block-start: var(--gt-space-0);
  inset-inline-end: var(--gt-space-0);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: var(--gt-space-6);   /* a disc for one digit, a pill for two */
  block-size: var(--gt-space-6);
  padding-inline: var(--gt-space-1);
  border-radius: var(--gt-radius-pill);
  background: var(--gt-accent);         /* dark on the accent — the avatar's and .got-lang__current's rule */
  color: var(--gt-ink-on-accent);
  font-size: var(--gt-text-2xs);
  font-weight: var(--gt-weight-bold);
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;   /* V-30 B3: a count in the header, beside the wallet figure that already had it */
}

/* 🔴 `.got-cart__navlabel` IS GONE, CLASS AND ALL.

   It was nine declarations hand-copied from `.got-credit__navlabel` eight
   lines away, and the comment on it said so: "written out for the same reason
   it was: two callers in two blocks of this file, and a utility reached
   across block boundaries is a utility somebody edits for one of them."

   That argument was correct while there was no system to put the utility in.
   There is one now — `.got-sr-only` — so the layout says that instead and
   this rule does not exist. Checked first: no test pins the class name, only
   `.got-cart__nav` and `.got-cart__navcount`, both untouched.

   `.got-credit__navlabel` and `.got-order__sr` are the other two copies. They
   go the same way when the header and the order page are migrated. */


/* ==========================================================================
   /checkout/pay — PAYING FOR THE BASKET. Round 2.
   ==========================================================================

   BORN MIGRATED. This page has no rules in site.css and never had any: it did
   not exist before Round 2, so it is the first page in this file that is here
   because it was WRITTEN onto the system rather than moved onto it.

   Everything with a shape — the card, the buttons, the help text, the labels,
   the message — is a component. What is left below is LAYOUT: how the two
   panels sit beside each other, and how a line of the summary lays out. That
   is the whole of what a page file is for.

   TWO COLUMNS ABOVE 900px, ONE BELOW, and the breakpoint is a CONTAINER
   query rather than a viewport one — CLAUDE.md: "a media query cannot see the
   sidebar, the column span or fi-width-7xl", and the same is true here of the
   page's own max-width. The grid is what is being measured, so the grid is
   what carries the container. */
/* 🔴 THE CONTAINER IS THE PAGE, NOT THE GRID, AND THAT IS NOT A DETAIL.
   `container-type` was on `.got-checkout__grid` and the `@container` query
   selected `.got-checkout__grid` — AN ELEMENT CANNOT BE STYLED BY ITS OWN
   CONTAINER QUERY, so the two-column rule never applied at any width.
   Measured, not noticed: `grid-template-columns` computed to a single
   `1140px` track at 1280. A query that silently matches nothing is exactly
   the failure mode CLAUDE.md names about media queries and the sidebar. */
.got-checkout {
  container-type: inline-size;
  padding-block: var(--gt-space-8) var(--gt-space-10);
}

.got-checkout__back {
  margin-block-end: var(--gt-space-4);
  font-size: var(--gt-text-xs);   /* the order pages' back link size (M-18, 19 Aug) */
}
/* The arrow, drawn and mirrored exactly as `.got-order__back a::before` draws
   it (site.css) — the checkout's back link had none while the order, pay and
   top-up pages' did (measured 19 Aug). No glyph in the markup (GAP-001). */
.got-checkout__back a::before {
  content: "\2190";
  display: inline-block;
  margin-inline-end: var(--gt-space-2);
}
[dir="rtl"] .got-checkout__back a::before {
  content: "\2192";
}
/* Its link is a 44px target by the recipe (GAP-100, Batch 5) — 44 tall, the
   line keeps its rhythm. */
.got-checkout__back a {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--gt-tap-min);
  padding-inline: var(--gt-space-2);
  margin-inline-start: calc(-1 * var(--gt-space-2));
  margin-block: calc(-1 * var(--gt-space-3));
  border-radius: var(--gt-radius-xs);
  color: var(--gt-ink-muted);
  transition: var(--gt-transition-control);
}
/* The recipe's other half — the verifier's F1: the checkout's copy had the 44px
   and not the ring, hover or colour the order pages' back link carries. */
.got-checkout__back a:hover {
  color: var(--gt-ink);
  background: var(--gt-wash-soft);
}
.got-checkout__back a:focus-visible {
  outline: var(--gt-ring-width) solid var(--gt-ring-color);
  outline-offset: var(--gt-ring-gap);
  box-shadow: var(--gt-ring-shadow);
}

/* V-20 B7 (28 Aug): the "Refunds & Disputes" link at the foot of the pay page
   rendered as its paragraph's own muted text — no accent, no underline, no
   hover answer, no branded ring (measured: claimed by zero :hover and zero
   :focus rules, in both languages, on the one page where a customer decides
   to hand over money). The rest state is the prose-link recipe `.got-legal a`
   wears (accent + underline), the tap height is the inline recipe (padding
   with the negative margin, so the line's rhythm is untouched), and the hover
   and ring are the same as the back link two rules up. */
.got-checkout__policy a {
  display: inline-block;
  padding-block: var(--gt-space-1);
  margin-block: calc(-1 * var(--gt-space-1));
  border-radius: var(--gt-radius-xs);
  color: var(--gt-accent);
  text-decoration: underline;
  text-underline-offset: var(--gt-space-1);
  transition: var(--gt-transition-control);
}
.got-checkout__policy a:hover {
  background: var(--gt-wash-soft);
}
.got-checkout__policy a:focus-visible {
  outline: var(--gt-ring-width) solid var(--gt-ring-color);
  outline-offset: var(--gt-ring-gap);
  box-shadow: var(--gt-ring-shadow);
}

.got-checkout__title {
  font-size: var(--gt-text-2xl);
  font-weight: var(--gt-weight-heavy);   /* the order pages' title weight (M-18) */
  line-height: var(--gt-leading-tight);
  color: var(--gt-ink);
  margin-block-end: var(--gt-space-6);
}

.got-checkout__grid {
  display: grid;
  gap: var(--gt-space-6);
}

@container (min-width: 56rem) {
  .got-checkout__grid {
    grid-template-columns: 1fr 22rem;
    align-items: start;
  }
}

.got-checkout__lines {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--gt-space-3);
  margin-block-start: var(--gt-space-4);
}

/* THE NAME TAKES THE SPACE AND THE TWO FIGURES KEEP THEIRS. `1fr auto auto`
   rather than a flex row with a spacer: the quantity and the price are two
   different columns and they must line up DOWN the list, not just across one
   row of it. */
.got-checkout__line {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--gt-space-4);
  align-items: baseline;
  font-size: var(--gt-text-sm);
  color: var(--gt-ink);
}

.got-checkout__qty {
  color: var(--gt-ink-muted);
  font-size: var(--gt-text-xs);
  font-variant-numeric: tabular-nums;   /* V-30 B3: one per line, a column */
}

/* `text-align: end`, never `right` — the site is RTL-safe by construction and
   the price sits at the reading end in both directions. */
.got-checkout__price {
  text-align: end;
  font-variant-numeric: tabular-nums;
}

.got-checkout__total,
.got-checkout__due {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--gt-space-4);
  margin-block-start: var(--gt-space-5);
  padding-block-start: var(--gt-space-4);
  border-block-start: var(--gt-border-hair) solid var(--gt-line);
  font-size: var(--gt-text-md);
  color: var(--gt-ink);
  /* V-30 B3: the figure is an unclassed <b> inside; font-variant-numeric
     INHERITS, so the line carries it and the <b> reads it. */
  font-variant-numeric: tabular-nums;
}

.got-checkout__due b {
  font-size: var(--gt-text-xl);
  font-weight: var(--gt-weight-bold);
}

.got-checkout__balance {
  display: grid;
  gap: var(--gt-space-3);
  justify-items: start;
}

.got-checkout__balance-line {
  font-size: var(--gt-text-sm);
  color: var(--gt-ink);
  font-variant-numeric: tabular-nums;   /* V-30 B3: the applied amount is an inline <bdi> inside; inherited */
}

.got-checkout__soon {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--gt-space-2);
  margin-block-start: var(--gt-space-6);
  padding-block-start: var(--gt-space-4);
  border-block-start: var(--gt-border-hair) solid var(--gt-line);
  font-size: var(--gt-text-sm);
  color: var(--gt-ink-muted);
}

.got-checkout__soon li {
  display: flex;
  align-items: baseline;
  gap: var(--gt-space-3);
}
