/* ===========================================================================
   @arag CMS — canvas grid
   ---------------------------------------------------------------------------
   Load AFTER src/index.css:

     <link rel="stylesheet" href="../src/index.css">
     <link rel="stylesheet" href="cms.css">

   UNLAYERED BY DESIGN. @arag declares `@layer reset, tokens, base, components,
   utilities, overrides` and puts every one of its own rules inside those, so
   unlayered CSS beats all six without a specificity trick and without a
   priority-raising declaration. That is the documented mechanism this file
   uses, not a hack around one.

   It cuts both ways, which is why this file is as short as it is: anything
   declared here DEFEATS the matching @arag utility. So this file declares only
   properties no utility sets — the grid itself, the span, the image ratio.
   Background, block padding, gap and max-inline-size are emitted by render.js
   as u-* classes instead, so an author's choice actually wins. A `gap` in this
   file would silently override every u-gap-* on the page.

   Nothing in this file ships in the npm package. @arag stays plain CSS with no
   JavaScript and a `files: ["src"]` publish surface; cms/ is a sibling tool
   that consumes it.

   Rules this file keeps, same as src/
   -----------------------------------
   - No priority-raising declarations.
   - Tokens for every length, colour and radius. The exceptions are marked
     below and are structural: grid track counts, span integers, aspect ratios,
     and the two container-query breakpoints (a size query cannot read a
     custom property — see the ponytail note).
   - Logical properties throughout: inline/block, never left/right/top/bottom.
   - No parent selector. The floor is Firefox 120 and it would raise it to 121.
     Stated without naming the selector on purpose: the CI invariant is a grep
     for the literal, so writing it even inside a comment fails the check.
   - Long-text safe: a translated string routinely runs 15–25% longer than its
     English source; no truncation, no line clamp, no nowrap, no fixed
     inline-size. The package ships English copy; the rule keeps it
     localisation-ready. Every box here grows.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   Page
   --------------------------------------------------------------------------- */

/* The render root. Carries `lang` and nothing visual — sections are full-bleed
   bands and paint their own background. */

/* ---------------------------------------------------------------------------
   Section — a full-bleed band, and the query container
   ---------------------------------------------------------------------------
   The container is the BAND rather than its inner grid, because an element
   cannot query itself. Querying the band is also the honest question: a band
   spans the viewport, so `inline-size` here is how much room the layout
   actually has.
   --------------------------------------------------------------------------- */

.cms-section {
  container-type: inline-size;
  container-name: cms-section;

  /* The page gutter. On the band, not the inner grid, so the inner grid's
     max-inline-size still measures the content and not the content plus the
     gutter. */
  padding-inline: var(--space-5);
}

/* ---------------------------------------------------------------------------
   Section grid — 12 columns
   ---------------------------------------------------------------------------
   minmax(0, 1fr) rather than 1fr: a 1fr track floors at its content's min
   size, so one long unbroken word in a narrow column would push the
   whole row wider than the section. The 0 floor lets the track shrink and the
   text wrap instead.

   No align-items here. The initial `stretch` is what makes a row of pricing
   cards equal height, which is what .card__footer's `margin-block-start: auto`
   pushes against — and stretch only ever GROWS the shorter card. It never
   clips, so it is safe under the long-text rule.
   --------------------------------------------------------------------------- */

.cms-section__inner {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

/* ---------------------------------------------------------------------------
   Block spans
   ---------------------------------------------------------------------------
   Width is a span, always. The drag layer quantizes to one of these twelve and
   writes an integer; there is no pixel width in the data model, which is what
   keeps the mobile reflow below free and a translation's length budget safe.

   THE END LONGHAND, NOT THE `grid-column` SHORTHAND, and the difference is
   load-bearing rather than stylistic. `grid-column: span 6` expands to
   `grid-column-start: span 6; grid-column-end: auto` — the span lives in the
   START. That leaves nowhere for the Block start column rules below to write
   a real start line without destroying the span: setting grid-column-start: 7
   on top of it yields `7 / auto`, a one-track block sitting in the right
   place at the wrong width. Putting the span in the END instead leaves START
   free, so a span and a start compose into "N wide, beginning at column M".
   With no start, `grid-column-end: span N` and auto start auto-place the
   block spanning N, which is exactly what the shorthand did before.

   Twelve rules rather than one attr()-driven rule on purpose: attr() with a
   type is Chrome 133+, well above the documented floor.
   --------------------------------------------------------------------------- */

.cms-block[data-span="1"] { grid-column-end: span 1; }
.cms-block[data-span="2"] { grid-column-end: span 2; }
.cms-block[data-span="3"] { grid-column-end: span 3; }
.cms-block[data-span="4"] { grid-column-end: span 4; }
.cms-block[data-span="5"] { grid-column-end: span 5; }
.cms-block[data-span="6"] { grid-column-end: span 6; }
.cms-block[data-span="7"] { grid-column-end: span 7; }
.cms-block[data-span="8"] { grid-column-end: span 8; }
.cms-block[data-span="9"] { grid-column-end: span 9; }
.cms-block[data-span="10"] { grid-column-end: span 10; }
.cms-block[data-span="11"] { grid-column-end: span 11; }
.cms-block[data-span="12"] { grid-column-end: span 12; }

/* ---------------------------------------------------------------------------
   Block start column
   ---------------------------------------------------------------------------
   Optional, and absent by default: without data-start a block flows wherever
   the grid puts it, which is what every block did before this existed. With
   it, a block can begin at a named column — which is the only way to put one
   block UNDER another in a multi-column row rather than beside it. A heading
   at span 6 beside an image, with a paragraph at span 6 and start 7, produces
   a paragraph under the heading; with no start the paragraph flows to column
   1 and lands under the image instead.

   TWO LONGHANDS, NEVER THE SHORTHAND. The span block above writes the END
   (`grid-column-end: span N` — see the note there for why); these rules write
   only the START. The two compose into "N wide, beginning at column M".
   Writing `grid-column: M / span N` here instead would need to know N, which
   is a different attribute, and would mean 144 rules rather than 24.

   These must sit AFTER the span block and BEFORE the container queries below.
   Both responsive tiers use the `grid-column` SHORTHAND, which resets both
   longhands at once — so a start column is wiped automatically the moment the
   grid collapses to one or two tracks, which is exactly the wanted
   degradation: a start out of twelve is meaningless there, and the block
   should simply flow again. Nothing extra is needed to undo it.

   render.js clamps start to `13 - span`, so a contradictory pair can never
   ask for a thirteenth column and overflow the section — the same failure
   this file's responsive notes describe for an unclamped span. See
   startCol() there.

   Twelve rules rather than one attr()-driven rule, for the same reason the
   spans above are twelve: attr() with a type is Chrome 133+, above the floor.
   --------------------------------------------------------------------------- */

.cms-block[data-start="1"] { grid-column-start: 1; }
.cms-block[data-start="2"] { grid-column-start: 2; }
.cms-block[data-start="3"] { grid-column-start: 3; }
.cms-block[data-start="4"] { grid-column-start: 4; }
.cms-block[data-start="5"] { grid-column-start: 5; }
.cms-block[data-start="6"] { grid-column-start: 6; }
.cms-block[data-start="7"] { grid-column-start: 7; }
.cms-block[data-start="8"] { grid-column-start: 8; }
.cms-block[data-start="9"] { grid-column-start: 9; }
.cms-block[data-start="10"] { grid-column-start: 10; }
.cms-block[data-start="11"] { grid-column-start: 11; }
.cms-block[data-start="12"] { grid-column-start: 12; }

/* ---------------------------------------------------------------------------
   Container block — one grid item, a stack inside
   ---------------------------------------------------------------------------
   The container is a normal grid item in the section's 12 columns: it takes a
   span and a start like any other block. What is different is inside it — its
   children stack in the block axis instead of joining the outer grid, which
   is the whole point. A heading and a paragraph in a container beside a tall
   image stay together in that image's row, rather than the paragraph falling
   through to the next grid row.

   No `gap` here, deliberately, and no padding: render.js emits both as u-*
   classes so an author's choice actually wins. See this file's header — a gap
   declared in an unlayered file defeats every u-gap-* on the page.

   Nothing here pins the children to the top, because nothing has to: the
   container stretches to its grid row (the row is as tall as the image beside
   it), and a column flex container already packs its children at the start
   and leaves the slack at the end. The empty space this block exists to
   remove ends up below the stack, not between its two halves.

   Children fill the container's inline size by flexbox's own default
   (`align-items: stretch`), so no width rule is needed here either — a
   child's `data-span` is a column count for the OUTER grid and is simply
   inert in here.
   --------------------------------------------------------------------------- */

.cms-stack {
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------------------
   Container layout modes — stack, row, grid
   ---------------------------------------------------------------------------
   One block answers "which way do the children run", rather than three blocks
   an author has to choose between before they know. The flex column above is
   still the base and still the fallback: these rules RE-POINT it, so a
   container whose layout attribute is missing or unrecognised is a stack,
   exactly as it was before this prop existed.

   No gap here, for the same reason the container above declares none:
   render.js emits u-gap-* and this file is unlayered, so a gap declared here
   would defeat every step an author picked. `gap` is one property in both
   flex and grid, so the utility keeps working unchanged across all three
   modes — which is most of why this is three rules rather than a rewrite.

   The row WRAPS. That is not a nicety: a nowrap row would push its children
   past the band rather than onto a second line, and a translated label — 15–25%
   longer than its English source — is what overflows first. Same reasoning as
   the no-truncation rule in this file's header.

   The grid's tracks are minmax(0, 1fr), not 1fr, for the reason the section
   grid above gives: a 1fr track floors at its content's min size, so one long
   unbroken word would push the whole container wider than its span.

   Three explicit column rules rather than one attr()-driven rule, the same as
   the twelve spans and the twelve starts above: attr() with a type is Chrome
   133+, above the documented floor.

   ponytail: the collapse at the end of this file queries `cms-section`, so a
   container's track count reflects how wide the BAND is, not how wide the
   container is. A four-track container at span 3 of a 1440px section keeps
   four tracks in about 300px, because the section is wide and the section is
   what the query can see. Upgrade path: declare
   `container-type: inline-size` on .cms-stack and query that instead — the
   rules below barely change. It is not done here because container-type also
   applies layout, style and inline-size containment: the container's own
   inline size stops depending on its contents, and its children can no longer
   affect anything outside it. That is a behaviour change for every container
   already on a page, not a new mode, so it wants its own change and its own
   visual pass rather than riding along with this one.
   --------------------------------------------------------------------------- */

.cms-block[data-layout="row"] {
  flex-direction: row;
  flex-wrap: wrap;
}

.cms-block[data-layout="grid"] { display: grid; }

.cms-block[data-layout="grid"][data-columns="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cms-block[data-layout="grid"][data-columns="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cms-block[data-layout="grid"][data-columns="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* A CHILD'S SPAN STAYS INERT INSIDE A CONTAINER, and grid mode is the one
   place that has to be said out loud rather than got for free.

   The container section above already states the rule: a child's data-span is
   a column count for the OUTER grid, and inside a container it does nothing.
   In a stack and in a row that is true by itself, because grid-column-end is
   not a property a flex item has. Turn the container into a grid and it
   becomes one — so the block-span rules near the top of this file start
   applying to the CHILDREN, and every child defaults to span 12. Measured
   before this rule: a two-track container reported
   `248px 248px 0px 0px 0px 0px 0px 0px 0px 0px 0px 0px` and each child filled
   all 628px of it, because `grid-column-end: span 12` in a two-track grid
   creates ten implicit columns rather than clamping — the same failure the
   collapse block at the end of this file describes for an unclamped span in
   the section grid.

   `grid-column: auto` — the shorthand, deliberately, because it resets the
   END the span block writes AND the START the start block writes, in one
   declaration. The two longhands are kept apart everywhere else in this file
   so a span and a start can compose; here they both have to go.

   Mapping the child's span onto the container's tracks instead was the other
   option and is worse: a span is out of twelve, a container has two to four
   tracks, and every block defaults to span 12 — so every untouched child
   would claim the whole row and a grid would render as one column.

   Specificity (0,4,0), against (0,2,0) for the span and start rules and for
   the collapse rules at the end of the file. It wins on specificity rather
   than on source order, which is what lets it sit here instead of last. */
.cms-block[data-layout="grid"] > .cms-block[data-span] { grid-column: auto; }

/* ---------------------------------------------------------------------------
   Loop — a repeater that is not a box
   ---------------------------------------------------------------------------
   THE WHOLE OF THE LAYOUT CODE A REPEATER NEEDS, and it is one declaration.

   A loop renders its children once per row of a named page data list. If it
   generated a box, that box would be ONE grid item and its copies would lay
   out inside it — so a loop of span-4 cards would be three cards crammed into
   a single column of the section grid, and every span, start and responsive
   rule above would apply to the wrong element. `display: contents` removes the
   loop's own box entirely, leaving the copies as direct items of the section's
   twelve tracks. A loop of span-4 cards is then a three-up row for exactly the
   reason three hand-built span-4 cards are: the block-span rules, the
   start-column rules and both container-query tiers above all see ordinary
   siblings and need no clause about loops.

   Its own data-span is therefore inert, and blocks.json offers the block no
   span control rather than a control that does nothing.

   `display: contents` on a <div> loses no semantics — a div has none to lose;
   it is not used here on anything with a role, a border or a background.
   Chrome 65 / Firefox 37 / Safari 11.1, far below this repo's floor.

   The editor draws its empty-container affordance around a loop with no rows,
   which needs a box back; that override lives in cms/editor.css, scoped to
   #cms-canvas, because an empty repeater is an authoring state and must never
   reserve space in a published page.
   --------------------------------------------------------------------------- */

.cms-block[data-cms-block="loop"] { display: contents; }

/* Button alignment, respelled for flex. A button is inline-flex and blockifies
   as a grid item, so the Button block section below shrinks it back with
   justify-self and maps `data-align` onto the same property. NONE of that
   reaches a stack: justify-self is a grid property and is inert in a flex
   container, so inside a container or a card the alignment control silently
   did nothing and the button sat wherever the default put it.

   These three mirror the three below one-for-one, on the cross axis of a
   column flex — which is the inline axis, the same axis justify-self aligns
   on in the grid. Keyed off [data-cms-children] rather than .cms-stack so
   they cover a CARD as well: a card holds blocks and is a flex column too,
   but it is `.card`, not a stack, and a .cms-stack selector would have fixed
   containers while leaving every card CTA stretched.

   The default is `start` for the same reason it is below: a stretched CTA is
   a very large target for a very small action, which button.css says is what
   [data-block] is opt-in for. */
/* A spacer that absorbs the leftover height, so everything after it sits at
   the block-end edge. Only inside a block that HOLDS blocks — a container or
   a card, both flex columns — because flex-grow is the mechanism and a grid
   item does not flex; in the section grid the flag is simply inert.

   This is the replacement for what `.card__footer` used to do automatically.
   While a card was a fixed template, its footer carried
   `margin-block-start: auto` and every CTA in a row lined up for free. A card
   is composed now, so there is no footer to pin — an author puts a grown
   spacer above the CTA instead, and gets the same result explicitly, in any
   container rather than only in a card.

   `flex-basis: auto` on purpose: the spacer's own height is its padding (see
   its `space` prop), so basis-auto keeps that as the minimum and grow only
   adds the surplus. A zero basis would collapse the chosen step. */
[data-cms-children] > .cms-block[data-cms-block="spacer"][data-grow] {
  flex: 1 1 auto;
}

[data-cms-children] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"]) { align-self: start; }
[data-cms-children] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"])[data-align="center"] { align-self: center; }
[data-cms-children] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"])[data-align="end"] { align-self: end; }

/* And undone again in the two layouts where align-self points the wrong way.
   The three rules above are correct for a COLUMN flex and only for a column
   flex: there the cross axis is the inline axis, which is the axis `start /
   center / end` names. Turn the container into a row or a grid and the cross
   axis becomes the BLOCK axis, so `data-align="center"` would stop centring
   the button horizontally and start centring it vertically — the control
   silently doing something other than what it says.

   GRID: align-self goes back to its initial value, and the Button block rules
   below take over. Their justify-self is inert in a flex container — which is
   why the three rules above exist at all — but a grid item is exactly what
   justify-self is for, so start/center/end mean what they say with no extra
   rule here.

   ROW: there is nothing to hand it to. Per-item inline alignment in a flex
   row is not a property an item owns; the line's free space is distributed by
   justify-content on the CONTAINER, and one item cannot claim it without
   moving its siblings. So data-align is INERT in row mode, stated here and in
   blocks.json rather than left to be discovered. The buttons sit where the
   row puts them, pinned to the block-start of their line so a tall sibling
   does not stretch a CTA down beside it.

   Specificity, because this file is unlayered and source order only decides a
   tie: each selector below is one class and three attributes (0,4,0), which
   MATCHES the [data-align] rules above, and sits later. Anything lower would
   lose to them however late it sat — the same trap the collapse block at the
   end of this file documents. */
/* A LOOP is the third case, and it is here for the same reason as the two
   above: a loop declares [data-cms-children] so the editor treats it as a
   holder of blocks, but it generates no box (see the Loop section above), so
   its button copies are items of the SECTION grid — where data-align is
   already carried by justify-self in the Button block rules below, and
   align-self would only add a stray vertical centring the control never
   promised. Same (0,4,0) as the [data-align] rules above, sitting later. */
[data-cms-children][data-layout="row"] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"]),
[data-cms-children][data-layout="grid"] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"]),
[data-cms-children][data-cms-block="loop"] > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"]) { align-self: start; }

/* ---------------------------------------------------------------------------
   Button block
   ---------------------------------------------------------------------------
   .button is inline-flex, and a grid item is blockified — so without this a
   CTA stretches across its whole span and becomes a very large target for a
   very small action, which button.css says is exactly what [data-block] is
   opt-in for. justify-self shrinks it back to its label; [data-block] still
   works, because it fills the span it is justified into.
   --------------------------------------------------------------------------- */

/* A drawerTrigger is the same `.button` with the same `data-align` (blocks.json
   says "same mechanism as button.props.align"), and until this :is() it was
   not in these rules at all: it stretched across its whole span whatever its
   alignment said. :is() takes the specificity of its most specific argument,
   one attribute, so every selector here keeps the specificity it had. */
.cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"]) { justify-self: start; }
.cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"])[data-align="center"] { justify-self: center; }
.cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"])[data-align="end"] { justify-self: end; }

/* ---------------------------------------------------------------------------
   Badge block
   ---------------------------------------------------------------------------
   .badge is inline-flex (badge.css:30) and a grid item is blockified — the
   same problem the Button block above already has a fix for. Left alone, a
   badge dropped straight into a section stretches across its whole span, and
   the same thing happens inside a card or a container: both are flex
   columns whose default `align-items: stretch` reaches the badge on the
   cross axis exactly the way the section grid's own stretch does.

   No data-align control here — ROADMAP Phase 1 asks only for `text` and
   `variant` on this block — so this is unconditional, unlike the button
   rules above: every badge gets `start`, in every context, with nothing to
   read. justify-self covers the section grid; align-self covers a card or a
   container, keyed off [data-cms-children] the same way the button fix is. */
.cms-block[data-cms-block="badge"] { justify-self: start; }
[data-cms-children] > .cms-block[data-cms-block="badge"] { align-self: start; }

/* ---------------------------------------------------------------------------
   Section alignment — a band's register
   ---------------------------------------------------------------------------
   `section.props.align`. `center` is the default and writes nothing, so every
   page authored before the prop existed renders byte-identically and each
   block keeps whatever alignment it sets itself. `start` writes
   data-align="start" on the band, and these rules lay the band's DIRECT
   blocks from the start edge — a block's own centring included. That is the
   point of putting it on the section: a centred hero register and an
   editorial register are one switch, not one edit per heading, lede and CTA.

   Only CENTRING is undone. A block set to `end` keeps it: that is a
   deliberate placement, not a register. Three things render.js does to
   centre a block, so three undos:
   - `u-text-center` on a heading or a paragraph -> text-align back to start;
   - `u-margin-inline-auto`, which renderText adds so a measure-capped <p> is
     centred in its span as well as internally -> margin back to 0;
   - `data-align="center"` on a button or a drawer trigger, which the Button
     block rules above read as justify-self -> start.

   DIRECT children only, via `>`. A container or a card is its own composition
   and keeps its own alignment; a loop's copies sit inside the loop's
   display: contents box in the DOM, so they are not direct children either
   and keep theirs. Specificity (0,5,0) against the Button block's (0,3,0), so
   this wins on specificity and could sit anywhere in the file. No literal
   except 0. */
.cms-section[data-align="start"] > .cms-section__inner > .cms-block.u-text-center { text-align: start; }
.cms-section[data-align="start"] > .cms-section__inner > .cms-block.u-margin-inline-auto { margin-inline: 0; }
.cms-section[data-align="start"] > .cms-section__inner > .cms-block:is([data-cms-block="button"], [data-cms-block="drawerTrigger"])[data-align="center"] { justify-self: start; }

/* ---------------------------------------------------------------------------
   Stat block in a band — a ledger rule above each figure
   ---------------------------------------------------------------------------
   A row of stats straight in the section grid read as four loose numbers with
   nothing tying them to one another. A 1px rule above each one makes the row
   a ledger strip: the rule SEPARATES the figure from the band's intro above
   it, which is the kind of line rule 9 allows, not a decoration down one edge.
   It is on the block-start edge and on every stat, so it survives the collapse
   below unchanged: stacked on a phone, each figure keeps its rule above it
   instead of losing a column separator that no longer has a neighbour.

   Border only. The room between rule and figure is the block's own `pad`
   prop (u-pad-block-*), which this file must not declare over — see the
   header. Direct children of the grid only: a stat inside a card or a
   container is part of that composition and gets no rule. */
.cms-section__inner > .cms-block[data-cms-block="stat"] {
  border-block-start: var(--border-width-1) solid var(--color-border-subtle);
}

/* ---------------------------------------------------------------------------
   Code block — the copy button gets its own row
   ---------------------------------------------------------------------------
   src/components/code.css positions `.button.code__copy` absolutely over the
   figure's block-start/inline-end corner, a documented trade against
   reserving a gutter: that file's own header ("WHY THE BUTTON OVERLAYS THE
   CODE INSTEAD OF RESERVING A GUTTER") argues a fixed-width reservation would
   be wrong once the label is translated, so the button overlays instead, on
   the bet that a monospace line rarely reaches that corner. On this page it
   does: phase1Code's first HTTP line runs the full measure ("GET
   /v1/settlements?window=2026-09-...") and the button sits on top of live
   text at 390px, in both themes.

   Fixed here, in the consumer, rather than by reopening that trade in
   code.css: this file is unlayered, so `position: static` beats the
   layered `position: absolute` outright, with no specificity match needed.
   The button drops out of the overlay and into normal flow, on its own row,
   with the gap it already carries as a local custom property, re-read
   rather than restated (see code.css's local API for --code-copy-gap).

   WHERE THAT ROW GOES. highlight.js appends the button, so in the DOM it is
   the figure's LAST child, and in plain block flow that puts it below the
   <figcaption>, detached from the sample it copies. The docs site fixed the
   same class of bug with `order` (docs.css, `.code`); `order` needs a flex
   or grid parent, so a figure that carries a copy label becomes a one-column
   flex container and the caption takes `order: 1`, after the pre and the
   button (both order 0, in DOM order). DOM order is deliberately left alone:
   a screen reader hears sample, caption, button, and the button is a
   control, so it should be reached after the content it acts on.
   `display: flex` is scoped to figures that carry data-copy-label, which is
   exactly the set the button can appear in; every other code block keeps
   ordinary block flow. A flex item is stretched by default, so the button
   opts out with `align-self: start` (it would otherwise span the whole
   figure); the pre and the caption keep the stretch they need.

   SPACING. Flex items do not collapse margins, and block flow did: the
   pre's block-end margin and the caption's block-start margin used to merge
   into one gap. So the caption's own margin is zeroed here (otherwise a code
   block with no script, which has no button, would open a wider gap than a
   captioned block without a copy label) and the button carries the row
   instead: the pre's block-end margin above it, and --code-copy-gap below it
   when a caption follows (the sibling rule after the button's own).
   No `gap` declaration, for the reason in the header.

   Deliberately NOT re-aligned to the inline-end edge to match its old
   corner position: a start-aligned button below the sample is the smaller,
   honest trade. */
.cms-block[data-cms-block="code"][data-copy-label]:not([hidden]) {
  display: flex;
  flex-direction: column;
}

.cms-block[data-cms-block="code"][data-copy-label] > figcaption {
  order: 1;
  margin-block-start: 0;
}

.cms-block[data-cms-block="code"][data-copy-label] .button.code__copy {
  position: static;
  align-self: start;
}

/* Only when a caption precedes it in the DOM, which is the only time one
   follows it on screen: a captionless block gets no trailing space. */
.cms-block[data-cms-block="code"][data-copy-label] > figcaption + .button.code__copy {
  margin-block-end: var(--code-copy-gap);
}

/* ---------------------------------------------------------------------------
   Card CTA baseline — an `lg` card's own padding outruns its row siblings
   ---------------------------------------------------------------------------
   card.css's `[data-size="lg"]` raises --card-padding, which sets EVERY
   edge, to --space-7 (card.css:320-322). A card's spacer-pushed CTA (the
   spacer rule earlier in this file) sits exactly `padding-block-end` above
   the card's block-end border, so an `lg` card's CTA lands --space-7 above
   it while a default-size sibling's lands --space-6 above — 8px higher,
   measured, in a three-up pricing row where only the centre card is `lg`.
   Fixed in DATA, not here: the plan cards' own `pad` prop (u-pad-block-*,
   layered in utilities, already beats card.css's `padding` shorthand on the
   block axis) is set to the SAME step across the row regardless of `size`,
   which pins every CTA to one baseline while leaving --card-padding's extra
   --space-7 on the inline edges, where the size variant's premium feel
   actually reads. See cms/page.json's planStandard (pad "6", matching
   planStarter and planScale) and DECISIONS.md for the fuller argument. No
   rule added here — this comment exists so the fix is not mistaken for
   dead code the next time this file is read top to bottom. */

/* ---------------------------------------------------------------------------
   Tab panel
   ---------------------------------------------------------------------------
   A `tab` block's root is a .tabs__panel holding blocks, and its children
   need the container's flex column so render.js's u-gap-4 spaces them — every
   block carries u-margin-block-0, so without a gap they would sit flush.

   Not .cms-stack, and the :not([hidden]) is load-bearing: this file is
   unlayered, so a bare `display: flex` here beats reset.css's
   `[hidden] { display: none }` and every panel src/behaviors/tabs.js hides
   would stay on screen. tabs.css guards its own display the same way
   (tabs.css:339). Keyed off .tabs__panel rather than
   [data-cms-block="tab"], because the tab's LABEL in the strip carries that
   attribute too. */
.cms-block.tabs__panel:not([hidden]) {
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------------------
   Menu block: the open menu is IN FLOW
   ---------------------------------------------------------------------------
   menu.css makes `.menu` an absolutely positioned overlay under its anchor.
   On a CMS page that overlay cannot be trusted, and the reason is this file's
   own section rule: `.cms-section` is a size container, and at the browser
   floor (Chrome 123, Firefox 120, Safari 17.5) `container-type` still implies
   layout containment, which makes every section a stacking context. A menu
   that hangs past the bottom of its section is then painted UNDER the next
   section, and its links cannot be clicked. Measured with that containment
   forced on the fixture at 375: three of four links hit-tested to the next
   section.

   So here the open menu takes its place in the flow and pushes what follows
   down, like any disclosure. Nothing overhangs, so there is nothing to stack,
   and it holds with scripting off — which the alternatives do not:
     - lifting the section on :focus-within misses Safari, which does not
       focus a clicked <summary>;
     - a class set by menu.js is no answer when the script is absent;
     - the section cannot stop being a container: the tiers below are
       `@container cms-section` rules.
   The cost is a layout shift on open, and it is the honest one: the same
   move an accordion row makes.

   --menu-inline-space goes back to 100%: menu.css points it at the viewport
   for a menu inside an anchor because the anchor there is only as wide as
   its trigger; here the anchor is a grid item as wide as its span.

   ponytail: an overlay menu is the better component and this gives it up on
   CMS pages. Upgrade path: when the floor reaches engines where
   container-type no longer implies layout containment (Chrome 129, Firefox
   131, Safari 18), delete this rule. */
.cms-block.menu-anchor > .menu:not([hidden]) {
  --menu-inline-space: 100%;

  position: static;
  margin-block-start: var(--menu-offset);
}

/* ---------------------------------------------------------------------------
   Image block
   ---------------------------------------------------------------------------
   The one size handle in the system, and it is a ratio rather than a height.
   reset.css already gives img display: block and block-size: auto, so the
   ratio resolves against the inline size the span gives it — the image reflows
   with the grid instead of holding a fixed box open. `auto` sets no ratio at
   all and the image keeps its intrinsic one.

   Integers in a ratio are structure, not a design value.
   --------------------------------------------------------------------------- */

.cms-block[data-cms-block="image"] > img {
  inline-size: 100%;
  object-fit: cover;
}

.cms-block[data-fit="contain"] > img { object-fit: contain; }

.cms-block[data-ratio="1/1"] > img { aspect-ratio: 1; }
.cms-block[data-ratio="4/3"] > img { aspect-ratio: 4 / 3; }
.cms-block[data-ratio="3/2"] > img { aspect-ratio: 3 / 2; }
.cms-block[data-ratio="16/9"] > img { aspect-ratio: 16 / 9; }
.cms-block[data-ratio="21/9"] > img { aspect-ratio: 21 / 9; }

/* ---------------------------------------------------------------------------
   Collapse
   ---------------------------------------------------------------------------
   A container query, not a media query: the answer depends on how wide the
   BAND is, and a section inside a narrow shell on a wide screen should collapse
   too. This is also why the editor canvas can be resized and show the truth.

   ponytail: 48rem and 64rem are literals. They mirror --size-container-md and
   --size-container-lg exactly, but a size container query cannot read a custom
   property — var() is not resolvable in a query condition. Upgrade path: none
   in CSS today; if the container scale ever moves, these two numbers move with
   it, and they are the only literal lengths in this file. Keep them in step
   with src/tokens.css:605-606.
   --------------------------------------------------------------------------- */

/* Each tier changes the TRACK COUNT as well as the spans, and both halves are
   required.

   The track count, because 12 tracks carry 11 gaps: at --space-7 that is 352px
   of gap inside a 327px section, minmax(0, 1fr) floors every track at 0, and
   the row overflows the band by the difference. Verified at 375px — a `1 / -1`
   block measured 352px inside a 327px container before the track count moved.
   Two tracks carry one gap and one track carries none, so neither can overflow
   whatever gap step the author picked.

   The spans, because `span 4` in a one-track grid does not clamp — it creates
   three implicit columns and overflows just as badly.

   `[data-span]` rather than a bare `.cms-block`, and the attribute is
   load-bearing rather than decoration: every rule in this file is unlayered,
   so a bare `.cms-block` (0,1,0) LOSES to `.cms-block[data-span="4"]` (0,2,0)
   however late it sits, and a three-column pricing row stayed three columns at
   375px. Verified: cards measured 93px wide. Matching the specificity is what
   puts source order back in charge. */

/* Narrow band: one block per row, whatever it asked for. A three-column
   pricing row at 375px is three unreadable columns, and a translated label is
   what overflows first. */
@container cms-section (inline-size < 48rem) {
  .cms-section__inner { grid-template-columns: minmax(0, 1fr); }
  .cms-block[data-span] { grid-column: 1 / -1; }

  /* A container collapses with the section, and for the same reason: three
     tracks in a 327px band are three unreadable columns whatever the tracks
     belong to. Row goes back to a column, grid goes back to one track.

     Both must MATCH the specificity of the base rules above, not merely sit
     later — every rule in this file is unlayered, so source order decides a
     tie and nothing else. `.cms-block[data-layout="row"]` is (0,2,0) there
     and (0,2,0) here; the three grid selectors are (0,3,0) there and (0,3,0)
     here, which is why they are written out one per column count rather than
     collapsed to `[data-layout="grid"]` (0,2,0) — that would LOSE to the
     three-column base rule and leave a container at three tracks on a phone,
     which is the regression already on record above. */
  .cms-block[data-layout="row"] { flex-direction: column; }

  .cms-block[data-layout="grid"][data-columns="2"],
  .cms-block[data-layout="grid"][data-columns="3"],
  .cms-block[data-layout="grid"][data-columns="4"] { grid-template-columns: minmax(0, 1fr); }
}

/* Mid band: halves at most. Anything that asked for more than half takes the
   whole row rather than being squeezed. */
@container cms-section (48rem <= inline-size < 64rem) {
  .cms-section__inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .cms-block[data-span] { grid-column: span 1; }

  .cms-block[data-span="7"],
  .cms-block[data-span="8"],
  .cms-block[data-span="9"],
  .cms-block[data-span="10"],
  .cms-block[data-span="11"],
  .cms-block[data-span="12"] { grid-column: 1 / -1; }

  /* Halves at most, inside a container as well as in the section grid. A
     container in this tier occupies at most one of two tracks of a band under
     64rem, so three or four tracks inside it are narrower than the tier above
     already refuses for the section itself. Two is the floor this tier keeps.

     The row is left alone: it wraps, so it degrades on its own without a rule
     and without a track count to argue about. */
  .cms-block[data-layout="grid"][data-columns="3"],
  .cms-block[data-layout="grid"][data-columns="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
