/*
 * site.css — GravityStack Main
 *
 * Client-specific, hand-authored CSS layer. This is where any one-off styling
 * for this particular site lives — never in base.css, which is shared across
 * every GravitySite fleet build, and never in assets/css/organisms/, which
 * holds copies of library entries that are not edited in place. Contractors
 * work here.
 *
 * LOAD ORDER, and this file's position in it is the point:
 *   Spectra One → base.css → the library organism files → site.css
 * Last of the three visitor-facing families, deliberately. A contractor
 * overriding one declaration from a copied organism has an obvious home
 * for it and does not have to win a specificity contest against a file they
 * did not write and must not edit. See functions.php,
 * gravitysite_enqueue_base_styles(), where the order is enforced by
 * dependency rather than by registration order.
 *
 * Anything base.css defines, and anything theme.json emits as a custom
 * property or preset, is available to reference from here.
 *
 * EMPTY AS AT 4.16.0, AND THAT IS THE CORRECT STATE. It held the How We Help
 * mega menu — moved here from base.css at 4.15.0, then split at 4.16.0 into
 * its substrate (base.css section 4) and its design
 * (assets/css/organisms/mega-featured.css, a copy of the library entry).
 * `gravitysite-css-standards.md` says this file starts near-empty; the fleet
 * baseline and the library organisms are supposed to carry the weight, and until
 * this site needs a genuine one-off, there is nothing for it to hold.
 *
 * The test for what belongs here, from gravitysite-css-standards.md, "Files",
 * in order — stop at the first yes:
 *   1. Does it exist so an EDITOR sees something?          → editor.css
 *   2. Did it arrive as part of a library organism?        → menus/, not edited
 *      in place; a change goes to the library entry and comes back as a
 *      re-pull, or it goes here as an override
 *   3. Would it be needed whatever the design looked like,
 *      because core, the parent, or Spectra defeats it?    → base.css
 *   4. Would the next client site want it too?             → base.css
 *   5. Otherwise                                           → here
 */

/* Mega menu — the three service icons.
 *
 * CLIENT LAYER BY DESIGN. The entry's `mega-featured.css` provides the masked
 * box and reads `--c-mega-icon`; which glyph each service takes is this site's
 * choice and belongs here, not in the copied entry. A different practice sets
 * different values against the same three modifier classes, or sets none.
 *
 * Lucide (ISC): scan-search, target, panels-top-left. Held as data URIs rather
 * than as files so nothing resolves through a plugin at render — the icon
 * survives deactivation of anything, which is what the icon ruling protects.
 * `stroke="currentColor"` is inert inside a mask and is kept so a path lifted
 * back out of here works unchanged as inline SVG somewhere it is allowed.
 */
.c-mega__svc--score  { --c-mega-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M3%207V5a2%202%200%200%201%202-2h2%22%2F%3E%3Cpath%20d%3D%22M17%203h2a2%202%200%200%201%202%202v2%22%2F%3E%3Cpath%20d%3D%22M21%2017v2a2%202%200%200%201-2%202h-2%22%2F%3E%3Cpath%20d%3D%22M7%2021H5a2%202%200%200%201-2-2v-2%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%223%22%2F%3E%3Cpath%20d%3D%22m16%2016-1.9-1.9%22%2F%3E%3C%2Fsvg%3E"); }
.c-mega__svc--target { --c-mega-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%2210%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%226%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%222%22%2F%3E%3C%2Fsvg%3E"); }
.c-mega__svc--site   { --c-mega-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Crect%20width%3D%2218%22%20height%3D%2218%22%20x%3D%223%22%20y%3D%223%22%20rx%3D%222%22%2F%3E%3Cpath%20d%3D%22M3%209h18%22%2F%3E%3Cpath%20d%3D%22M9%2021V9%22%2F%3E%3C%2Fsvg%3E"); }


/* point-grid — the title line box the Beside icon is sized to.
 *
 * CLIENT LAYER BY DESIGN. base.css's *Card icon position* section sizes the
 * icon for Icon position: Beside from `--c-point-grid-title-line-box` and does
 * not set it, because the title's line box depends on how this theme maps
 * `h3`. Unset, base.css falls back to one line of the card's body text.
 *
 * COUPLED TO THIS THEME'S `h3`: `text-4` and `1.25` are the font-size step and
 * line height in theme.json's `styles.elements.h3`, generated from
 * `gravitystack-design.md`. If either moves in the design file, this moves
 * with it in the same release. The bare `1.25` is a deliberate deviation from
 * the raw-value rule in `gravitysite-css-standards.md`: there is no line-height
 * token for it to resolve to, so the coupling is stated here instead.
 *
 * SET ON `point-split` TOO, since 4.47.0. Its points take Beside always
 * (Decision 92), base.css sizes them with the same rule, and its point titles
 * are the same unstyled `h3`. The property keeps its `point-grid` name because
 * that name is the fleet contract; see base.css's size rule for why.
 */
.c-point-grid,
.c-point-split {
  --c-point-grid-title-line-box: calc(var(--wp--preset--font-size--text-4) * 1.25);
}

/* ── Gravity Hacks archive — the index ────────────────────────────────────
 * Added 2026-09-16 at 4.39.0 with `templates/archive-gravity-hacks.html`.
 *
 * WHY THIS IS IN site.css AND NOT IN THE ORGANISM. `split` is a masthead entry
 * in `theme-builds/library/mastheads/`, and a masthead is the band at the top
 * of a section. The lead item, the hairline index, and the pagination below it
 * are the section's BODY. Putting them in `assets/css/organisms/split.css`
 * would put site-specific selectors inside a file that is copied to other
 * clients, which `library/README.md` is written to prevent.
 *
 * WHY NOT base.css, WHICH IS THE OTHER CANDIDATE. The routing test's step 4 —
 * would the next client site want it too — is arguably yes: an archive of short
 * pieces as scannable rows is not peculiar to GravityStack. It is here anyway,
 * on the same discipline `cluster.css` records: this estate extracts at
 * instance TWO, not instance one, because the constant-versus-variable split is
 * read from two real cases rather than guessed from one. `mega-featured` was
 * extracted at instance one and its own README records what that cost.
 * EXTRACTION TRIGGER: the Signals archive, which will want the same rows.
 *
 * The classes take the `c-` prefix rather than the masthead's `gs-`, and the
 * two are not inconsistent. `gs-` is retired with one exception — a class that
 * must sit outside the `c-` namespace so a `--` in it is not read as a pattern
 * address — and the masthead qualifies because it carries the divider axis
 * value. Nothing here carries a `--`, so nothing here needs the exception.
 * `.c-cluster-shell` is the precedent for a template-level `c-` component.
 *
 * NO BREAKPOINT IN THIS SECTION, AND THE MASTHEAD HAS ONE. The difference is
 * the standard's own, not a preference. `gravitysite-css-standards.md` reserves
 * the two viewport breakpoints for "genuine page-level and template-level
 * decisions" and Copy-to-Site Decision 63 adds a full-bleed section's column
 * behaviour to that list — which is the masthead, and is why it takes `bp-2`.
 * An index row is a component repeated in a list, and stays a component
 * concern that Decision 63 explicitly does not take back.
 *
 * SINCE 4.43.0 THE ROW HAS NOTHING TO ADAPT: it stacks at every width, so no
 * breakpoint is owed and none is written. THIS PARAGRAPH USED TO CARRY THE
 * WHOLE FLEX-WRAP ARGUMENT — two zones with flex bases, a measured crossover
 * near 840px of viewport against the mockup's 900, and a note that the row is
 * `is-layout-flex` and therefore barred from being a containment host. All of
 * it described a layout removed at 4.43.0, and it is struck rather than
 * deleted because a reader who finds `is-layout-flex` asserted here would go
 * looking for a flex container that is not there. `CHANGELOG.md` 4.43.0 holds
 * the measurements.
 *
 * THE GROUND AND THE COLUMN ARE THE BAND'S, NOT THIS FILE'S. Added 4.45.0:
 * `templates/archive-gravity-hacks.html` wraps the query in an `alignfull`
 * `layout: constrained` group carrying `gravity-surface-subtle` and its own
 * block padding. The tint and the 40rem cap are block attributes, so
 * `.c-hacks-index-band` INTENTIONALLY HAS NO RULE IN THIS FILE — if one ever
 * appears, the reason belongs in its docblock, because a width or a colour
 * stated twice drifts the first time either moves.
 *
 * The ground is load-bearing rather than decorative, and it is the same
 * argument `.c-hack-page` records below for the single: a centred 40rem column
 * on white, under a left-aligned 1140px masthead, reads as a second left edge.
 * The full-bleed band makes the index its own zone with its own centring, so
 * the space beside the column reads as field rather than as unfilled paper.
 */

/* The row.
 *
 * THE ROW IS NOT A FLEX CONTAINER AND MUST NOT BECOME ONE AGAIN. It stacks:
 * `"layout":{"type":"default"}` on the group in the template, which is where
 * the arrangement actually lives — core emits the flex layout CSS off that
 * attribute and it cannot be overridden from here. The two-column version and
 * its cross-axis alignment (first `baseline`, then `align-items: start`) came
 * off at 4.43.0 against a measurement: 481.7 against 626.3 produced a void of
 * 39–338px between the end of the title text and the start of the excerpt
 * text, a 10.6x variance against a declared 32px gap.
 *
 * THE INLINE BLEED IS THE ONE THING THAT SURVIVED THE REWRITE, and it does
 * more work now than it did. It lets the hover fill and the hairlines run a
 * little wider than the text, as the design draws them — a negative margin
 * equal to the inline padding, so the text still starts on the column edge.
 * Since 4.45.0 that column is the band's 40rem rather than 1140px, which is
 * what makes the rule end where the text ends instead of running roughly
 * 400px past it.
 */
/* "Latest", above the first row on page 1.
 *
 * A MARKER, NOT A TREATMENT. It says where the top of the collection is and
 * does no other visual work — the moment it starts to, the featured block
 * that was ruled out twice has returned by another route.
 *
 * NOT THE REGISTERED EYEBROW. The eyebrow is uppercase with positive tracking,
 * and this page's standing position excludes uppercase labels. Sentence case,
 * regular weight, muted, at `text-2`.
 *
 * `display: block` is authored because the element is a `<span>` — injected by
 * `inc/gravity-hacks-archive.php` rather than authored in the template, so that
 * it can be gated on `! is_paged()`. It is a real element and not `::before`
 * generated content: generated content carrying meaningful text is not reliably
 * exposed and is not selectable.
 */
.c-hacks-index__marker {
  display: block;
  margin-block-end: var(--wp--preset--spacing--20);
  font-size: var(--wp--preset--font-size--text-2);
  font-weight: var(--wp--custom--weight--normal);
  color: var(--wp--preset--color--gravity-text-muted);
}

.c-hacks-index__row {
  position: relative;
  padding-block: var(--wp--preset--spacing--40);
  margin-inline: calc(var(--wp--preset--spacing--30) * -1);
  border-block-start: 1px solid var(--wp--preset--color--gravity-border);
  transition: background-color 160ms ease;
}

/* THE INLINE PADDING IS A SPECIFICITY FIGHT WITH THE PARENT THEME, AND IT IS
 * THE ONLY DECLARATION IN THIS COMPONENT THAT HAS TO WIN ONE. It is split out
 * of the rule above so the fight is visible rather than buried in a shorthand.
 *
 * Spectra One ships:
 *   .wp-block-group.is-layout-flow.wp-block-group-is-layout-flow
 *     { padding-left: 0; padding-right: 0 }   (0,3,0)
 * so a single-class selector here loses outright:
 *   .c-hacks-index__row                                          (0,1,0)
 *   this rule                                                    (0,4,0)
 * Four classes, not three. Three would tie and be decided by stylesheet
 * order, which the title-link block above already records as a thing not to
 * rely on.
 *
 * FOUND ON THE 4.45.0 RENDER, INTRODUCED AT 4.43.0, AND THE SHAPE IS THE
 * TRANSFERABLE PART. Until 4.43.0 the row was `is-layout-flex`, so the parent's
 * flow-layout selector did not match and the shorthand's 16px applied. Moving
 * the group to `"layout":{"type":"default"}` swapped `is-layout-flex` for
 * `is-layout-flow` and handed the parent theme a match it did not have before.
 * The declaration did not change; what it was competing against did. Nothing
 * errored — inline padding silently became 0 while the -16px margin stayed, so
 * the row bled its own text 16px past the column instead of bleeding the rule
 * past the text, and the measured column came out 672px against the band's 640.
 * A block layout-type change is a class change, so re-read the computed box
 * after one rather than only the rendered result.
 */
.c-hacks-index .c-hacks-index__row.wp-block-group.is-layout-flow {
  padding-inline: var(--wp--preset--spacing--30);
}

/* The closing hairline.
 *
 * Qualified with a core block class because core's post-template inserts its
 * own `<li class="wp-block-post">` between the loop and this group, so
 * `:last-child` cannot be expressed on the row itself. Putting the rule on the
 * list instead would draw it two inline-paddings narrower than the rows above
 * it, because of the bleed.
 */
.wp-block-post:last-child > .c-hacks-index__row {
  border-block-end: 1px solid var(--wp--preset--color--gravity-border);
}

.c-hacks-index__row:hover {
  background-color: var(--wp--preset--color--gravity-surface-subtle);
}

/* The whole row is the target, and the title's link is what owns it.
 *
 * The stretch itself is `.u-stretched-link` in `base.css`, applied from this
 * template's `className` on `core/post-title` — it moved there at 4.40.0
 * because a card grid wants the identical mechanism and this was its second
 * consumer waiting to happen. What stays this component's business is the
 * `position: relative` on `.c-hacks-index__row` above, which is the box the
 * link fills; the utility deliberately does not guess at that.
 *
 * The effect is what it always was: the excerpt is clickable without being a
 * second link to the same place, which is what a screen reader would otherwise
 * have to read past.
 */
/* `margin: 0` is the whole rule now, and it is here rather than left to core.
 * The row stopped being a flex container at 4.43.0, so the title's margins are
 * live again and its block-end is what the excerpt's own block-start collapses
 * against — authoring it is what makes that gap exactly the excerpt's rule and
 * not a collapse against a UA value. */
.c-hacks-index__title {
  margin: 0;
}

/* The title link takes body text colour, and goes to the link colour on row
 * hover. Colour only — THE UNDERLINE IS NOT HANDLED HERE AND MUST NOT BE.
 *
 * THIS RULE USED TO CARRY `text-decoration: none`, with a long docblock
 * arguing why a WCAG G183 underline was wrong on an index row title. That
 * argument was accepted and MOVED: `base.css` rule 1 now excepts a link that
 * is a heading's only child, which is what these titles are. Removed at
 * 4.40.0. Do not reinstate it — two mechanisms producing one result is how
 * the next reader gets a false reading about which one is working, and the
 * per-consumer version is the thing that has to be remembered at every future
 * query loop.
 *
 * The stretched pseudo-element moved out in the same pass and is now
 * `.u-stretched-link` in `base.css`, applied from the template's `className`.
 * `.c-hacks-index__row` carries the `position: relative` it fills; that is
 * above, and the utility deliberately does not set it.
 *
 * Specificity, and the count is why this is two classes and not one:
 *   theme.json   a:where(:not(.wp-element-button))   (0,1,1)
 *   this rule    .c-hacks-index__row … a             (0,2,1)
 * A single-class selector here ties on specificity and is then decided by
 * stylesheet order, which is not a thing to rely on. That is what shipped
 * first and is why the count is written down.
 */
.c-hacks-index__row .c-hacks-index__title a {
  color: var(--wp--preset--color--gravity-text);
}

.c-hacks-index__row:hover .c-hacks-index__title a {
  color: var(--wp--preset--color--gravity-link);
}

/* Focus lands on the row, not on the invisible stretched box.
 *
 * The anchor's own focus ring would draw around the title text while the thing
 * that responds to Enter is the whole row, so the ring is moved to the row and
 * suppressed on the anchor. `gravitysite-css-standards.md`, *Accessibility
 * floor*: the visible state is required, and this keeps it truthful about what
 * is focused rather than merely present.
 */
.c-hacks-index__row:has(a:focus-visible) {
  outline: 2px solid var(--wp--preset--color--gravity-focus);
  outline-offset: -2px;
}

.c-hacks-index__title a:focus-visible {
  outline: none;
}

/* The excerpt.
 *
 * The clamp is on core's inner paragraph rather than on the block wrapper,
 * because that is the element the text is in; the wrapper is a div core emits
 * around it. Two lines is the design's, and it is a guard rather than the
 * mechanism — `excerptLength` in the template does the real trimming, and the
 * clamp catches a manual excerpt that ignores it.
 *
 * FONT SIZE IS `text-3`, THE `body` ROLE, NOT `text-2`. The design file
 * assigns `body` to text-3, `small` to text-2, and `caption` to text-1; this
 * is prose a reader reads to decide whether to click, and `small` was scoped
 * for fineprint and metadata, not for that. Hierarchy against the title does
 * not lose anything — it is already carried three ways (family, weight,
 * colour) and size was a fourth signal on the only prose on the page. Family,
 * weight, and the 1.6 leading all come from the global body style in
 * `theme.json` and are not set here; only size and colour are this
 * component's own.
 *
 * THREE FIGURES IN THIS BLOCK WERE WRONG AND ARE CORRECTED HERE, 2026-09-16.
 * The block previously said `contentSize` 40rem is roughly 75 characters, that
 * the excerpt column computes to about 596px, and that at text-3 the column
 * holds roughly 70 characters. Measured by shaping the shipped Figtree binary
 * through HarfBuzz: 40rem holds about 81 characters, not 75; the column
 * computed to 626.3px, not 596, under the r6 grid this block was written
 * against; and it held a mean of 81 characters, range 79-84, not 70. (The
 * column is the content size itself since 4.45.0 — see below.) The 70 was
 * arithmetic from an assumed average advance width
 * and it was 13% low. The instruction to count the real characters on the
 * render is what caught it — an estimate that says it is an estimate is worth
 * more than one that does not.
 *
 * THE MEASURE IS STILL NOT THE LEVER, AND THE COLUMN NARROWED ANYWAY. Both
 * halves are load-bearing and they are about different things.
 *
 * `gravitystack-design.md` v4.8, Layout stands: the reading measure governs
 * sustained prose, and a two-line block in a scannable index makes the return
 * sweep once, so the measure range does not reach it. That ruling permits the
 * wide column; it never required one. What moved the column at 4.45.0 was the
 * hairline, not the measure — the row's rule ran the full 1140px while its text
 * stopped around 626px, leaving roughly 400px of bare rule on every row. That
 * is the r6 void rotated ninety degrees rather than fixed, and the fix is to
 * end the rule where the content ends.
 *
 * SO THIS RULE DECLARES NO WIDTH. The band wrapping the query in
 * `templates/archive-gravity-hacks.html` is `layout: constrained`, which caps
 * its children at the global content size; a `max-inline-size` here would be a
 * second statement of a width the layout already owns, and the two would drift
 * the first time either moved. The `ch` unit is not the alternative either —
 * `gravitystack-design.md` v4.8 measures an `Nch` cap over-delivering by about
 * 41% in Figtree, because `ch` is the zero glyph's advance against a mean prose
 * advance of 0.4114em.
 *
 * The top margin is authored here rather than left to the group's `blockGap`:
 * the row's gap is spacing 50, which was the column gap when this was a flex
 * container and is too much between a title and its own excerpt. */
.c-hacks-index__excerpt {
  margin-block-start: var(--wp--preset--spacing--20);
  color: var(--wp--preset--color--gravity-text-muted);
  font-size: var(--wp--preset--font-size--text-3);
}

.c-hacks-index__excerpt .wp-block-post-excerpt__excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Pagination.
 *
 * TARGET SIZE IS COMPOSED FROM PADDING RATHER THAN DECLARED AS A MINIMUM, for
 * the reason the masthead's field rule gives: `min-width: 44px` is a raw value
 * and *The raw-value exception* permits literals only inside a media condition.
 * Spacing 30 block and spacing 40 inline against the text-2 line box resolves
 * to roughly 52 by 56, which clears the 44px floor on both axes.
 *
 * KNOWN LIMIT: the boxes are therefore intrinsically sized, so a two-digit page
 * number is wider than a one-digit one. At fifteen Hacks and ten to a page this
 * is invisible. It becomes visible past page 9, which is 90 Hacks, which is
 * roughly two years at one a week. Recorded rather than pre-solved.
 */
.c-hacks-index__pager {
  margin-block-start: var(--wp--preset--spacing--60);
  gap: var(--wp--preset--spacing--20);
}

.c-hacks-index__pager .page-numbers,
.c-hacks-index__pager .wp-block-query-pagination-previous,
.c-hacks-index__pager .wp-block-query-pagination-next {
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
  border: 1px solid var(--wp--preset--color--gravity-border);
  border-radius: var(--wp--custom--rounded--md);
  color: var(--wp--preset--color--gravity-link);
  font-size: var(--wp--preset--font-size--text-2);
  font-weight: var(--wp--custom--weight--strong);
  text-decoration: none;
  transition: border-color 160ms ease;
}

.c-hacks-index__pager a:hover {
  border-color: var(--wp--preset--color--gravity-brand);
}

/* The current page is the one thing here that is not a link, and it is filled
 * rather than merely bordered because *Do not rely on colour alone* means the
 * distinction has to survive a monochrome render. Core already carries
 * `aria-current="page"` on it, so the non-visual channel is covered. */
.c-hacks-index__pager .page-numbers.current {
  background-color: var(--wp--preset--color--gravity-cta);
  border-color: var(--wp--preset--color--gravity-cta);
  color: var(--wp--preset--color--gravity-cta-label);
}

@media (prefers-reduced-motion: reduce) {
  .c-hacks-index__row,
  .c-hacks-index__pager .page-numbers,
  .c-hacks-index__pager .wp-block-query-pagination-previous,
  .c-hacks-index__pager .wp-block-query-pagination-next {
    transition: none;
  }
}


/* ── Gravity Hacks single — the page ──────────────────────────────────────
 * Added 2026-09-16 at 4.42.0 with `templates/single-gravity-hacks.html`.
 *
 * THE READING PAGE IS `gravity-surface-subtle`, FULL-BLEED. White stops being
 * the ground. Ruled after the first render read thin against a full-width
 * viewport: the measure is not the fault and does not move — `contentSize`
 * 40rem holds about 81 characters at `text-3`, measured 2026-09-16 (this block
 * said 75 until then, which the design file also claimed and which was never
 * measured) — so what was missing was a field for the column to sit in rather
 * than unfilled paper beside it. The conclusion is unchanged by the correction:
 * the measure still is not the fault.
 *
 * NOT `gravity-surface-nested`. The first reason recorded here was a sequence:
 * the footer's close band was nested, so a nested page merged with it. That
 * band was removed at 6.8.0 (Copy-to-Site Decision 128), and the page now meets
 * the dark footer directly, so the reason that stands is the other one:
 * `gravity-surface-nested` MEANS the surface that sits on top of the page
 * ground, so making it the ground leaves anything nesting on this template
 * nowhere to go but white — inverting the system's own meaning on every client
 * site derived from it. *What would change it:* warming
 * `gravity-surface-subtle` itself in the design file, one pinned value, fleet-
 * wide, done once — not borrowing a role that is spoken for.
 *
 * IT IS PAINTED ON `main`, NOT ON `body` OR `.wp-site-blocks`, and the choice is
 * scope. `main` is already full-viewport width here — `.wp-site-blocks` is a
 * flow container, so nothing caps it — which makes this full-bleed without a
 * negative margin or an `alignfull` wrapper. Painting the body would reach every
 * template that has no ground of its own, which is a fleet decision this page is
 * not entitled to make. The class is the template's own, so the ground travels
 * with the template and with nothing else.
 *
 * IT IS HERE AND NOT IN `assets/css/organisms/bar.css` for the routing reason
 * the sections below already record: the organism is identity chrome and is
 * copied to other clients; a page ground is this template's body. A ground
 * inside the organism would arrive at the next client as an opinion about their
 * page.
 *
 * MEASURED ON THE RENDER rather than assumed, 2026-09-16, against
 * `gravity-surface-subtle` #f7faf3:
 *   gravity-text          15.49:1   (16.28 on white)
 *   gravity-text-muted     5.65:1   AA for normal text
 *   gravity-link / cta     6.95:1
 *   gravity-border         1.34:1   a separator, not a foreground (1.41 on white)
 * Nothing crosses a threshold in either direction; the ground costs roughly
 * half a point across the board and buys the field.
 */

.c-hack-page {
  background-color: var(--wp--preset--color--gravity-surface-subtle);
}


/* ── Gravity Hacks single — the title ─────────────────────────────────────
 * Added 2026-09-16 at 4.42.0.
 *
 * **THIS REVERSES THE RULING MADE EARLIER THE SAME DAY THAT THE TITLE AUTHORS
 * NO TYPE**, and the reversal is recorded rather than slipped in. That ruling's
 * reasoning was that a component with no reason to differ from the theme writes
 * no rule, and it was right about the principle: what changed is that a reason
 * appeared. `theme.json`'s h1 caps at 35px, and 35px here is not consistency —
 * it is the default that applies when nobody decided, and the page read thin at
 * the top because of it.
 *
 * NOT THE `display` PRESET, AND THIS IS THE PART MOST LIKELY TO BE "CORRECTED"
 * LATER. Its clamp interpolates on viewport width and reaches 72px at 1488px,
 * while this title's container stops growing at `contentSize`. That is the
 * archive title's recorded fault reproduced exactly — type rising, box fixed,
 * the wrap worsening the wider the screen — and `split.css`'s identity zone
 * carries the full account of it.
 *
 * SO THE CURVE STOPS WHERE THE CONTAINER STOPS. The ceiling is reached at 688px
 * of viewport, which is `contentSize` 640px plus both gutters, and nothing moves
 * above it. **This is where it differs from `split`'s title, which takes a fixed
 * size rather than a clamp:** that container is fixed at every width from `bp-2`
 * up, so interpolation had nothing to track. This one is viewport-bound below
 * 688 and fixed above it, so a clamp is the correct instrument and its ceiling
 * is the crossover.
 *
 * THE CEILING IS 52px, MEASURED ON THE RENDER AND NOT TAKEN FROM THE MOCKUP,
 * which carried 56px off a probe button. Measured 2026-09-16 in the live 640px
 * column in Outfit 600 at 1.12 leading, against the real title set:
 *
 *            58ch   53ch   45ch   44ch   16ch
 *     35px    2      2      2      2      1
 *     52px    3      2      2      2      1
 *     53px    3      3      3      3      1
 *     56px    3      3      3      3      1
 *
 * **The flip is between 52 and 53, and it is a cliff rather than a slope.** At
 * 53px three more titles go to three lines at once, including 44- and
 * 45-character ones, which are mid-length rather than outliers. At 52px only the
 * single longest title in the set takes three lines — "Go With A Printed Offer
 * To Increase Networking Conversions", 58 characters, 175px tall. 56px would
 * have cost three lines on a mid-length title for 4px of scale.
 *
 * RE-MEASURE THIS IF THE TITLE SET OR THE MEASURE MOVES. The value is fitted to
 * a real corpus of fifteen titles at a 640px column, which is the same accepted
 * cost `split.css` records for its own measured ceiling. The instrument is the
 * one to reuse: render the longest live title in the real column and count
 * lines, rather than reasoning from characters per line.
 *
 * FLOOR BINDS `text-6`, AN EXISTING PRESET STEP, not a literal. The ceiling is a
 * measured one-off and is not a preset, **and that is not a new preset by the
 * back door**: a preset is a size an author selects, and no author selects a
 * size for a post title in a template. That half of the earlier ruling still
 * holds.
 *
 * FAMILY, WEIGHT, LEADING, AND TRACKING ARE BOUND IN THE SAME RULE, for the
 * reason `base.css` gives at its own preset bindings: a font-size preset cannot
 * carry them, and omitting any one renders the title at the correct size in the
 * wrong weight, which looks deliberate and is not. Leading is 1.12 rather than
 * `theme.json`'s 1.15 — at 52px the looser value opens a three-line title by
 * roughly 5px a line for nothing.
 *
 * SPECIFICITY, MEASURED ON THE RENDER rather than assumed: `theme.json`'s h1
 * element style emits as a bare `h1` at (0,0,1), so one class wins and no
 * `:root` qualifier is needed. *Prefer the lowest specificity that works.*
 */

.c-hack__title {
  font-family: var(--wp--preset--font-family--outfit);
  font-size: clamp(
    var(--wp--preset--font-size--text-6),
    0.9145rem + 5.4313vw,
    3.25rem
  );
  font-weight: var(--wp--custom--weight--strong);
  line-height: 1.12;
  letter-spacing: var(--wp--custom--tracking--normal);
}


/* ── Gravity Hacks single — the subscribe close ───────────────────────────
 * Added 2026-09-16 at 4.41.0 with `templates/single-gravity-hacks.html`.
 *
 * WHY THIS IS IN site.css AND NOT IN THE ORGANISM, and the split is the same
 * one the index rows above are here for. `assets/css/organisms/bar.css` holds
 * the running head, which is IDENTITY CHROME — it says what section this is and
 * carries one route back. Everything below the article is the section's BODY:
 * what it asks, and what it offers next. Identity chrome travels to other
 * clients as a library entry; a subscribe block with this cadence line and this
 * ask does not.
 *
 * ON WHITE, IN THE READING MEASURE, OPENED BY THE INDEX HAIRLINE. Ruled against
 * a dark band, which was the first instinct: the page below this already runs
 * nested, dark, and subtle, so a fourth ground stripes it. (A dark close band
 * here also competed with the footer's own close band, until that band was
 * removed at 6.8.0.) The hairline is the archive index's
 * device, reused rather than invented, which is also what ties the two pages
 * together without repeating the band.
 *
 * NO `scroll-margin-block-start` IS AUTHORED HERE, AND THE OMISSION IS THE
 * DECISION. The running head's jump lands on this block under a sticky header,
 * so the margin is required — and `base.css` section 2f already writes it, for
 * `:where([id], a, button, summary, input, select, textarea)` under
 * `:root.has-header-stance`, sized from `--c-header-height`, which is the
 * header module's own live measurement rather than a number anyone picked. This
 * block carries `id="subscribe"`, so it is already covered by the one mechanism
 * that knows how tall the header actually is. Authoring a second would outrank
 * it at (0,1,0) against that rule's `:where()` and would be a literal — and two
 * mechanisms producing one result is how the next reader gets a false reading
 * about which one is working. VERIFY IT ON THE RENDER rather than trusting this
 * paragraph: if the block lands under the header, the fault is that the stance
 * module is not running, and the fix is there, not here.
 *
 * ONE blockGap FOR THE WHOLE BLOCK, spacing 30, set in the template. The
 * approved mockup carries three different gaps inside this block — 0.5rem,
 * 1.5rem, then 1rem. They are spent as one token, and the precedent is not
 * arbitrary: the split masthead's companion zone ships this exact set of
 * elements — a cadence line, the capture form, the fineprint — at spacing 30
 * already. Two homes for one rhythm on one site is the thing to avoid; the cost
 * is 8px above the cadence line.
 */

.c-hacks-subscribe {
  padding-block-start: var(--wp--preset--spacing--50);
  border-block-start: 1px solid var(--wp--preset--color--gravity-border);
}

/* The ask sits a step below the article's own h1 and a step above body prose.
 * `theme.json` binds h2 to text-5, which is the page-section size and reads as
 * a second article title here; text-4 is the same step the archive's index
 * titles take, and it is what makes this a block on the page rather than a new
 * document. Size and leading only — family and weight come from the h2 element
 * style and are not restated. */
.c-hacks-subscribe__title {
  font-size: var(--wp--preset--font-size--text-4);
  line-height: 1.25;
}

.c-hacks-subscribe__cadence {
  color: var(--wp--preset--color--gravity-text-muted);
}

/* ── The capture form — UNPOWERED MARKUP ──────────────────────────────────
 *
 * No action, no method, no handler. The mechanism is undecided and a task is
 * owed on it — the SAME task the archive masthead's form is waiting on, and
 * this page deliberately has ONE form rather than two so there is one success
 * state to wire and no id collision to unpick. The running head's Subscribe is
 * a jump link to this block for exactly that reason.
 *
 * It is a `core/html` block rather than `core/buttons` plus a field, on the
 * grounds `split.css` records at length: `core/button` renders an anchor and
 * has no submit type, and a form whose mechanism is undecided should not be
 * assembled out of blocks an author can rearrange before it has one. The
 * control is `type="button"` and the wrapper is a `div` rather than a `<form>`,
 * because an unpowered `<form>` submits to the current URL on Enter, which is a
 * worse default than doing nothing visibly.
 *
 * THE COST IS THE SAME COST, AND IT IS NAMED RATHER THAN DISCOVERED:
 * `theme.json`'s `button` element styles bind `.wp-element-button`, which a
 * bare `<button>` does not carry, so the fill, the label colour, the geometry,
 * and the hover are authored below. Every value is read off `theme.json`'s own
 * button element rather than chosen — including the `action-10` hover, which is
 * the fleet's light-band button hover and not a local pick.
 *
 * LIGHT-GROUND STYLING, WHICH IS NEW. `split.css`'s field and submit rules are
 * all INVERSE — a translucent well, a white-ish border, the inverse CTA pair —
 * and none of them may be reached for here. On white the field takes
 * `gravity-surface` with a `gravity-border` edge, both of which are roles, so
 * this block needs none of the literals that band was forced into.
 *
 * TARGET HEIGHT IS COMPOSED FROM PADDING, NOT DECLARED AS A MINIMUM, per *The
 * raw-value exception*: spacing 30 block, plus the text-3 line box at 1.2, plus
 * the two 1px borders, resolves to roughly 54px. Measured on the render — if
 * the type scale moves, re-measure here.
 *
 * The 14rem basis is a raw value and is the deviation `split.css` already
 * records: a flex basis is the width at which a box stops being usable, which
 * the spacing scale does not measure and no token in this system carries. A
 * layout-dimension token set would close it, and that is a design-file
 * decision.
 */

.c-hacks-subscribe__form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--20);
}

/* THE FIELD IS BOUND TO `gravity-surface` EXPLICITLY, AND AFTER 4.42.0 THAT IS
 * LOAD-BEARING RATHER THAN TIDY. The page ground is `gravity-surface-subtle`, so
 * a field left to inherit or to a transparent default disappears into the
 * ground — a tinted field on a tinted page is not a field, it is a gap. White
 * is what makes it read as somewhere to type. Do not "simplify" this to
 * `transparent` or remove it on the grounds that it matches the default; the
 * default is the ground, and the ground moved. */
.c-hacks-subscribe__field {
  flex: 1 1 14rem;
  min-inline-size: 0;
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
  border: 1px solid var(--wp--preset--color--gravity-border);
  border-radius: var(--wp--custom--rounded--lg);
  background-color: var(--wp--preset--color--gravity-surface);
  color: var(--wp--preset--color--gravity-text);
  font-family: inherit;
  font-size: var(--wp--preset--font-size--text-3);
  line-height: 1.2;
}

/* `opacity: 1` because Firefox dims placeholders by default, which would put a
 * measured muted role below its measured figure without saying so. Same note
 * as `split.css`'s. */
.c-hacks-subscribe__field::placeholder {
  color: var(--wp--preset--color--gravity-text-muted);
  opacity: 1;
}

.c-hacks-subscribe__submit {
  flex: 0 0 auto;
  padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--50);
  border: 0;
  border-radius: var(--wp--custom--rounded--lg);
  background-color: var(--wp--preset--color--gravity-cta);
  color: var(--wp--preset--color--gravity-cta-label);
  font-family: var(--wp--preset--font-family--figtree);
  font-size: var(--wp--preset--font-size--text-3);
  font-weight: var(--wp--custom--weight--strong);
  line-height: 1.2;
  cursor: pointer;
  transition: background-color 180ms ease;
}

.c-hacks-subscribe__submit:hover {
  background-color: var(--wp--custom--color--action-10);
}

/* Focus states are authored because these are not core's elements — the same
 * reason `split.css` gives, with the light focus role rather than the inverse
 * one. `gravitysite-css-standards.md`, *Accessibility floor*: a hover state
 * with no matching focus state is a defect, and it prefers theme.json, which
 * cannot reach a bare input or a bare button. The 2px width and offset are
 * theme.json's own geometry, reproduced rather than reinvented. */
.c-hacks-subscribe__field:focus-visible,
.c-hacks-subscribe__submit:focus-visible {
  outline: 2px solid var(--wp--preset--color--gravity-focus);
  outline-offset: 2px;
}

.c-hacks-subscribe__fineprint {
  font-size: var(--wp--preset--font-size--text-1);
  color: var(--wp--preset--color--gravity-text-muted);
}


/* ── Gravity Hacks single — the closing set ───────────────────────────────
 * Added 2026-09-16 at 4.41.0 with `inc/gravity-hacks-closing-set.php`.
 *
 * Three titles, a muted label, and a verb. TITLES ONLY, NO EXCERPTS: at the
 * 40rem reading measure the archive's two-column row wraps, so each item
 * becomes four stacked lines and three items become twelve after an article.
 * Titles alone also earn the archive's own argument — the value of a Hack sits
 * in its title. Unruled on the render, and the lighter hierarchy below weakens
 * the case for adding them back rather than settling it.
 *
 * THE LABEL GOES DOWN FURTHER THAN THE ITEMS, and that is the whole of round 2.
 * At the first draft both sat at text-4 in Outfit 600 and the group name read
 * as a fourth item. The label is furniture — it names the group and nothing
 * else — so it drops to text-2 Figtree muted and is a PARAGRAPH rather than a
 * heading, because it names a group of three and puts nothing in the document
 * outline worth landing on. The titles are the content and the only
 * interactive thing here, so they drop one step to text-3 and no further: they
 * stay separated from body prose by family and weight, which is two channels
 * and enough, and fineprint styling would throw the Hack's own value away on
 * three of the best assets on the page.
 *
 * NO STRETCHED LINK, AND `.u-stretched-link` IS NOT REACHED FOR. The utility
 * exists in `base.css` and the archive index rows use it — because those rows
 * have an excerpt beside the title that would otherwise not be clickable.
 * There is nothing here to stretch across: the title IS the row. So the ANCHOR
 * carries the row's padding and the row's hairline, which makes the whole row
 * the target with no second element and no `position: relative` to keep in
 * step.
 *
 * NO `text-decoration` IS AUTHORED ON THESE TITLES AND NONE MUST BE. `base.css`
 * rule 1's heading exception already leaves them unlined — `core/post-title` at
 * `isLink: true` renders `<h3 class="wp-block-post-title"><a>…</a></h3>`, which
 * is a heading whose only element child is the link. That is the exception's
 * whole population. Adding a local `text-decoration: none` here would be the
 * per-consumer override that rule 1 retired at 4.40.0.
 */

.c-hacks-more__label {
  font-size: var(--wp--preset--font-size--text-2);
  font-weight: var(--wp--custom--weight--strong);
  color: var(--wp--preset--color--gravity-text-muted);
}

/* `theme.json` binds h3 to text-4. One step down, and the override is the
 * component's only type rule — family, weight, and colour are the h3 element
 * style's and are not restated. */
.c-hacks-more__title {
  font-size: var(--wp--preset--font-size--text-3);
  line-height: 1.3;
}

/* The anchor IS the row.
 *
 * The inline bleed is the archive index's device, and it is a negative margin
 * equal to the inline padding, so the hover fill and the hairline run a little
 * wider than the text while the text still starts on the measure. Unlike the
 * archive, the padding, the hairline, and the fill are all on ONE element, so
 * they cannot come out at different widths.
 *
 * Specificity, and the count is why this is two classes and not one:
 *   theme.json   a:where(:not(.wp-element-button))          (0,0,1)
 *   theme.json   :root :where(a…:hover)                     (0,1,0)
 *   parent       :root :where(.wp-block-post-title a…)      (0,1,0)
 *   this rule    .c-hacks-more .c-hacks-more__title a       (0,2,1)
 * A single class ties the two `:root :where(…)` rules at (0,1,0) and is then
 * decided by stylesheet order, which is not a thing to rely on. Measured on the
 * render 2026-09-16, same as the index rule above.
 */
.c-hacks-more .c-hacks-more__title a {
  display: block;
  padding: var(--wp--preset--spacing--30);
  margin-inline: calc(var(--wp--preset--spacing--30) * -1);
  border-block-start: 1px solid var(--wp--preset--color--gravity-border);
  color: var(--wp--preset--color--gravity-text);
  transition: background-color 160ms ease;
}

/* The closing hairline, qualified with core's own block class for the reason
 * the index rule records: `core/post-template` inserts an `<li class="wp-block-post">`
 * between the loop and its children, so `:last-child` cannot be expressed on
 * the title itself. */
.c-hacks-more .wp-block-post:last-child .c-hacks-more__title a {
  border-block-end: 1px solid var(--wp--preset--color--gravity-border);
}

/* THE HOVER FILL INVERTED AT 4.42.0 WHEN THE PAGE GROUND MOVED. It was
 * `gravity-surface-subtle` on white; the page is now subtle, so the fill is
 * `gravity-surface` — white on tint rather than tint on white. The differential
 * is unchanged at 1.05:1 either way, measured, because it is the same two
 * values with the roles swapped. That is a whisper on its own and it is not
 * carrying the affordance alone: the title also moves to `gravity-link`, so
 * there are two channels and *Do not rely on colour alone* is satisfied by the
 * pair rather than by the fill.
 *
 * ANY OTHER BOX ON THIS PAGE THAT ASSUMED A WHITE GROUND NEEDS THE SAME
 * INVERSION. Checked at 4.42.0: the subscribe field is the only other one and
 * it was already bound to `gravity-surface` explicitly rather than left to a
 * default, which is what made it survive the ground move unchanged. In-content
 * images carry no fill of their own. */
.c-hacks-more .c-hacks-more__title a:hover {
  background-color: var(--wp--preset--color--gravity-surface);
  color: var(--wp--preset--color--gravity-link);
}

/* The closing link.
 *
 * WEIGHT ONLY. The colour is `theme.json`'s link role and THE UNDERLINE IS
 * `base.css` RULE 1'S — this is a link inside a paragraph, which is the exact
 * population that rule was written for, so it arrives underlined without
 * anything being authored. The archive's lead carried the mirror of this — a
 * `core/read-more` link, which renders with no prose ancestor, so rule 1 never
 * reached it and it had to author its own underline. Two components, one rule,
 * opposite conclusions, and the difference was the markup rather than the
 * design. The lead was removed at 4.43.0 and that counter-example went with it;
 * the point is kept because it is what stops this rule being "fixed" with an
 * authored underline it does not need.
 *
 * "Browse all Gravity Hacks", and the VERB is load-bearing rather than
 * stylistic: at the foot of a list of three titles a bare noun phrase reads as
 * a fourth item. The string is the template's, not this file's.
 */
.c-hacks-more__all {
  font-weight: var(--wp--custom--weight--strong);
}

@media (prefers-reduced-motion: reduce) {
  .c-hacks-subscribe__submit,
  .c-hacks-more .c-hacks-more__title a {
    transition: none;
  }
}
