/* ============================================================
 * Omphalos — blocks.css (FSE core-block chrome)
 * v0.1.0 — Phase 8: Text group
 *
 * Block-level chrome for WordPress core blocks, layered on top of
 * prose.css. prose.css owns the long-form text baseline (typescale,
 * rhythm, inline elements, list/quote/code/table framing). This file
 * owns the per-block visual decisions that belong to a specific
 * `core/*` block and its style variations — things that are NOT a
 * generic text baseline:
 *
 *   §1  core/paragraph   — drop cap
 *   §2  core/quote       — cite row
 *   §3  core/pullquote   — headline italic + dividers
 *   §4  core/code|preformatted|verse — separate the three surfaces
 *   §5  core/table       — stripes / row variations
 *
 * Scope: every rule is bound to `.wp-block-post-content` so block chrome
 * never leaks into UI surfaces (Query Loop cards, navigation, comment
 * forms). The lab pilot's blocks.css is a reference only — selectors are
 * re-contracted here for the FSE post-content surface rather than the
 * standalone `.prose`/`.sg-demo` styleguide context.
 *
 * Enqueue order: tokens -> prose.css -> blocks.css. base.css is unused.
 * Token-only values (--md-sys-* / --space-* / --md-ref-*).
 * ============================================================ */

/* ============================================================
 * §0 core/image — corner radius
 *
 * prose.css §8 still covers generic prose images. core/image is block chrome.
 * Default image radius is owned by Global Styles:
 *   theme.json styles.blocks.core/image.border.radius
 * WordPress core ships the Rounded style; Omphalos does not register a separate
 * "No rounding" style because Global Styles Radius now covers that use case.
 * The Rounded style targets the <img>, not the figure, so the caption is
 * unaffected.
 * Width and intrinsic media sizing stay owned by WordPress core's image block
 * stylesheet (`.wp-block-image img { max-width:100%; height:auto; }`). If a
 * Stylebook preview loses that rule, fix the editor iframe style injection rather
 * than duplicating core image sizing here.
 * ============================================================ */
.wp-block-image.is-style-rounded img {
  border-radius: var(--md-sys-shape-corner-full);
}

/* ============================================================
 * §1 core/paragraph — drop cap
 *
 * Drop cap is owned by core/paragraph, not the theme: WordPress ships
 *   .has-drop-cap:not(:focus)::first-letter {
 *     float:left; font-size:8.4em; line-height:0.68; font-weight:100;
 *     margin:0.05em 0.1em 0 0; text-transform:uppercase; font-style:normal;
 *   }
 * in wp-includes/blocks/paragraph/style.css. TT5 does not touch drop cap.
 * Omphalos overrides that core default inside the post-content surface:
 *   - 8.4em is oversized, especially for a Hangul first glyph -> 3.2em.
 *   - core weight 100 is too thin for the M3 tone -> brand medium.
 *   - core forces uppercase (Latin-only intent, meaningless for Hangul)
 *     -> text-transform: none.
 *   - colour the cap with the brand primary; use the brand typeface.
 * ============================================================ */
.wp-block-post-content .wp-block-paragraph.has-drop-cap::first-letter,
.wp-block-post-content p.has-drop-cap::first-letter {
  float: left;
  font-family: var(--md-ref-typeface-brand);
  font-size: 3.2em;
  line-height: 0.78;
  font-weight: var(--md-ref-typeface-weight-medium);
  margin-block-start: var(--space-xs);
  margin-inline-end: var(--space-sm);
  text-transform: none;
  font-style: normal;
  color: var(--md-sys-color-primary);
}

/* ============================================================
 * §2 core/quote — cite row
 *
 * prose.css already styles the blockquote bar + padding. core/quote
 * outputs <cite> as a sibling of the inner paragraph; ensure it sits on
 * its own line (prose.css §4 cite styling — typescale/colour — applies).
 * ============================================================ */
.wp-block-post-content .wp-block-quote cite {
  display: block;
}

/* ============================================================
 * §3 core/pullquote — distinct from quote
 *
 * A pullquote is a display device, not a citation. prose.css gives it a
 * centered box with top/bottom dividers; this section sets the headline
 * type treatment (serif italic, headline-medium) that distinguishes it
 * from a normal quote. Covers both the legacy <figure><blockquote><p>
 * markup and the <figure><p> form.
 * ============================================================ */
/* The legacy pullquote markup nests a <blockquote>. prose.css §4 styles every
 * blockquote in the surface with the quote bar (border-inline-start + padding),
 * but a pullquote is a centered display device, not a quote — strip that bar so
 * the pullquote isn't drawn with a left rule. */
.wp-block-post-content .wp-block-pullquote blockquote {
  margin: 0;
  padding-inline-start: 0;
  border-inline-start: 0;
}
.wp-block-post-content .wp-block-pullquote :is(p, blockquote p) {
  font-family:    var(--md-sys-typescale-headline-medium-font);
  font-size:      var(--md-sys-typescale-headline-medium-size);
  line-height:    var(--md-sys-typescale-headline-medium-line-height);
  font-weight:    var(--md-sys-typescale-headline-medium-weight);
  letter-spacing: var(--md-sys-typescale-headline-medium-tracking);
  font-style: italic;
}
.wp-block-post-content .wp-block-pullquote cite {
  display: block;
  margin-block-start: var(--space-md);
  font-family: var(--md-sys-typescale-body-small-font);
  font-size:   var(--md-sys-typescale-body-small-size);
  font-style: normal;
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* ============================================================
 * §4 core/code · core/preformatted · core/verse — three surfaces
 *
 * prose.css §6 frames pre/.wp-block-code as a mono code card. That is
 * correct for code and preformatted, but core/verse is poetry, not a
 * code specimen: it must read in the body typeface, keep its line breaks,
 * and drop the code-card surface. Re-contract verse here.
 * ============================================================ */
.wp-block-post-content .wp-block-verse {
  /* Undo the prose.css code-card treatment: poetry is body text. */
  background-color: transparent;
  color: var(--md-sys-color-on-surface);
  padding: 0;
  border-radius: 0;
  overflow-x: visible;
  white-space: pre-wrap;          /* preserve poetic line breaks, allow wrap */
  font-family: var(--md-ref-typeface-serif);
  font-size:   var(--md-sys-typescale-body-large-size);
  line-height: var(--md-sys-typescale-body-large-line-height);
}
.wp-block-post-content .wp-block-verse code {
  /* defensive: no inline pill if a verse ever wraps text in <code> */
  background-color: transparent;
  padding: 0;
}

/* ============================================================
 * §5 core/table — reset core borders + style variations
 *
 * WordPress core ships full per-cell borders for core/table:
 *   .wp-block-table td, .wp-block-table th { border: 1px solid; }
 *   .wp-block-table thead { border-bottom: 3px solid; }
 *   .wp-block-table tfoot { border-top: 3px solid; }
 * prose.css §10 only sets border-block-end / border-inline-end on cells, so the
 * core top/left 1px and the 3px thead/tfoot rules survive underneath and clash
 * with the M3 outer frame (doubled/!= weight lines). Reset the core cell border
 * to zero here, then redraw just the M3 separators the design wants: a single
 * 1px grid (right + bottom), a stronger 1px outline under the header, and a
 * 1px outline above the footer. (prose.css keeps the outer frame + radius +
 * header tint + cell padding; this owns the internal rules.)
 * ============================================================ */
.wp-block-post-content .wp-block-table :is(th, td) {
  border: 0;
  border-block-end: 1px solid var(--md-sys-color-outline-variant);
  border-inline-end: 1px solid var(--md-sys-color-outline-variant);
}
.wp-block-post-content .wp-block-table tr > :is(th, td):last-child {
  border-inline-end: 0;
}
.wp-block-post-content .wp-block-table tbody tr:last-child > :is(th, td) {
  border-block-end: 0;
}
/* Stronger 1px outline under the header and above the footer (core uses 3px). */
.wp-block-post-content .wp-block-table thead :is(th, td) {
  border-block-end: 1px solid var(--md-sys-color-outline);
}
.wp-block-post-content .wp-block-table tfoot tr:first-child > :is(th, td) {
  border-block-start: 1px solid var(--md-sys-color-outline);
}
.wp-block-post-content .wp-block-table tfoot tr:last-child > :is(th, td) {
  border-block-end: 0;
}

/* --- Stripes variation -----------------------------------------------------
 * The header band is surface-container-high (prose §10 th). To make the zebra
 * read as ONE continuous alternation starting FROM the header, the striped
 * rows use the SAME tone (even rows, since row 1 sits directly under the
 * header and stays base):
 *   header  surface-container-high
 *   row 1   base
 *   row 2   surface-container-high  <- same as header
 *   row 3   base ...
 * The bg banding carries the rhythm, so per-cell grid lines are dropped.
 *
 * IMPORTANT: core ships `is-style-stripes tbody tr:nth-child(odd){ background:
 * #f0f0f0 }` as a literal hex on the ROW. Setting our tone on the cells alone
 * leaves that core row background showing through transparent cells — invisible
 * in light mode (≈surface) but a bright band in dark mode. So target the row at
 * the same level core does, and use surface for the "base" rows so nothing
 * shows through. */
/* core also puts a literal `border-bottom: 1px solid #f0f0f0` on the FIGURE for
 * the stripes style — a bright line under the table in dark mode. Reset it; the
 * table element keeps the M3 outer frame. */
.wp-block-post-content .wp-block-table.is-style-stripes {
  border-block-end: 0;
}
.wp-block-post-content .wp-block-table.is-style-stripes tbody tr:nth-child(odd) {
  background-color: var(--md-sys-color-surface);
}
.wp-block-post-content .wp-block-table.is-style-stripes tbody tr:nth-child(even) {
  background-color: var(--md-sys-color-surface-container-high);
}
/* Cell reset is limited to tbody/tfoot — thead must KEEP its header tint and
 * is not part of the zebra. (A blanket :is(th,td) reset would blank the head.) */
.wp-block-post-content .wp-block-table.is-style-stripes :is(tbody, tfoot) :is(th, td) {
  background-color: transparent;   /* let the row tone show; no per-cell paint */
  border-block-end: 0;
  border-inline-end: 0;
}
.wp-block-post-content .wp-block-table.is-style-stripes thead :is(th, td) {
  /* keep the header tint + bottom separator, but a striped table has no
   * vertical grid — drop the header's inline-end rule too. */
  background-color: var(--md-sys-color-surface-container-high);
  border-inline-end: 0;
  border-block-end: 1px solid var(--md-sys-color-outline);
}

/* ============================================================
 * §6 core/list — Segmented list (is-style-list-segmented)
 *
 * Theme-registered style (register_block_style in functions.php). Each item
 * becomes a filled surface-container card with a large radius; a 2px gap
 * separates them instead of bullets/borders.
 * ============================================================ */
.wp-block-post-content .wp-block-list.is-style-list-segmented {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.wp-block-post-content .wp-block-list.is-style-list-segmented > li {
  margin: 0;
  padding: var(--space-md);
  background-color: var(--md-sys-color-surface-container);
  color: var(--md-sys-color-on-surface);
  border-radius: var(--md-sys-shape-corner-large);
}
.wp-block-post-content .wp-block-list.is-style-list-segmented > li::marker {
  content: none;
}

/* ============================================================
 * §7 core/quote — Plain (is-style-plain)
 *
 * Core ships the "Plain" quote variation. It drops only the emphasis BAR that
 * Global Styles gives core/quote — the left padding stays so the quote keeps
 * its indent and surface typography, just without the primary rule.
 * ============================================================ */
.wp-block-quote.is-style-plain,
.wp-block-post-content .wp-block-quote.is-style-plain {
  border-inline-start: 0;
}

/* ============================================================
 * §8 core/group — Card style variations (M3 Card)
 *
 * Ontology: a bare core/group is a plain LAYOUT container and gets NO chrome —
 * only these opt-in style variations (registered via register_block_style in
 * functions.php and theme.json partials) turn a group into an M3 Card surface.
 * Keeping the card chrome
 * on the variation, never on `.wp-block-group` itself, is what stops every
 * layout group in a pattern from accidentally reading as a card.
 *
 * The three variants map to the M3 Card types (ported from the lab Group bridge,
 * re-contracted for FSE block chrome and the Site Editor style-variation preview):
 *   is-style-card-filled    -> Filled card   — surface-container-highest
 *   is-style-card-elevated  -> Elevated card — surface-container-low + level-1 shadow
 *   is-style-card-outlined  -> Outlined card — surface + outline-variant hairline
 * Common: radius and `overflow: clip` so nested media honors
 * the rounded corners (the box-shadow on the elevated card is on the element box,
 * not clipped). Card padding is deliberately owned by Global Styles Dimensions
 * (default 0px in the style partials) or by nested content groups, not by this CSS
 * rule. These are CONTENT cards; a whole-card action/navigation is a markup +
 * semantic decision (button/anchor), not this visual variation.
 * ============================================================ */
.wp-block-group.is-style-card-filled,
.wp-block-group.is-style-card-elevated,
.wp-block-group.is-style-card-outlined {
  border-radius: var(--comp-card-radius);
  overflow: clip;
}
.wp-block-group.is-style-card-filled {
  background-color: var(--md-sys-color-surface-container-highest);
  color: var(--md-sys-color-on-surface);
}
.wp-block-group.is-style-card-elevated {
  background-color: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-on-surface);
  box-shadow: var(--md-sys-elevation-shadow-level1);
}
.wp-block-group.is-style-card-outlined {
  background-color: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  outline: 1px solid var(--md-sys-color-outline-variant);
  outline-offset: -1px; /* sit on the box edge, not outside it */
}

/* ============================================================
 * §9 core/button + button-surface base (.wp-element-button) — GLOBAL
 *
 * Unlike the rest of blocks.css (post-content scoped), the button base is GLOBAL
 * on purpose. `.wp-element-button` is WordPress's shared hook for every
 * button-shaped control — core/button's <a>, the core/search + comments submit
 * <button>, the core/file download <a> — and these live outside post content
 * (template parts, search form, comment form). Consistency across the
 * action/control surface matters more than the post-content fence here.
 * Navigation / pagination / post-nav links carry NO `.wp-element-button`, so the
 * global base never reaches them (that's why global is safe; see docs/BUTTON-ROUTE).
 *
 * Layering: theme.json `styles.elements.button` owns the minimal cross-surface
 * base that must reach the editor + every template (label-large type, pill
 * radius, Filled color = primary / on-primary). This section adds the
 * interaction layout the JSON element can't express, focus, and the core/button
 * variant color treatments. Filled needs no rule here — it IS the theme.json
 * base; core ships the `fill` (default) / `outline` style slugs and the theme
 * registers tonal / elevated / text (functions.php). First consumer of
 * --comp-button-height / --comp-button-radius (promoted in the comp-token audit).
 * State layer (hover/pressed) is a deliberate follow-up; focus-visible only here.
 * ============================================================ */
.wp-element-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-block-size: var(--comp-button-height);   /* SIZE: default 40px only; the xs..xl
                                                * matrix stays a future contract. */
  /* Padding lives in theme.json styles.elements.button (block 0 + inline space-lg)
   * so it overrides the parent TT5 button padding at the global-styles layer —
   * an editor CSS rule here loses to global styles for the same property. With
   * padding-block 0 + min-block-size, the pill resolves to the M3 default 40px. */
  border: 0;
  user-select: none;
  -webkit-user-select: none;
  /* Filled is the default surface. theme.json sets these too, but WP emits the
   * element-button color at :where() (zero specificity), which the <a> link
   * colour beats on core/button — so re-assert here at class specificity so every
   * button surface (incl. the file download <a>) reads on-primary, not the link
   * colour. Variants below override at higher specificity. */
  background-color: var(--md-sys-color-primary);
  color: var(--md-sys-color-on-primary);
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--md-sys-motion-curve-fast-effects-duration) var(--md-sys-motion-curve-fast-effects),
    border-radius var(--md-sys-motion-curve-fast-effects-duration) var(--md-sys-motion-curve-fast-effects);
}
.wp-element-button:hover {
  background-color: color-mix(in srgb, var(--md-sys-color-on-primary) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), var(--md-sys-color-primary));
}
.wp-element-button:focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-on-primary) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), var(--md-sys-color-primary));
}
.wp-element-button:active {
  background-color: color-mix(in srgb, var(--md-sys-color-on-primary) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), var(--md-sys-color-primary));
}
/* Pressed shape morph: pill → corner-medium. */
.wp-element-button:active {
  border-radius: var(--md-sys-shape-corner-medium);
}
.wp-element-button:focus-visible {
  outline: 3px solid var(--md-sys-color-secondary);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .wp-element-button {
    transition: none;
  }
}

/* core/button variants — override the Filled base color (higher specificity than
 * the theme.json `.wp-element-button` base). The link is itself .wp-element-button. */
.wp-block-button.is-style-tonal .wp-block-button__link {
  background-color: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}
.wp-block-button.is-style-tonal .wp-block-button__link:hover {
  background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), var(--md-sys-color-secondary-container));
}
.wp-block-button.is-style-tonal .wp-block-button__link:focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), var(--md-sys-color-secondary-container));
}
.wp-block-button.is-style-tonal .wp-block-button__link:active {
  background-color: color-mix(in srgb, var(--md-sys-color-on-secondary-container) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), var(--md-sys-color-secondary-container));
}
.wp-block-button.is-style-elevated .wp-block-button__link {
  background-color: var(--md-sys-color-surface-container-low);
  color: var(--md-sys-color-primary);
  box-shadow: var(--md-sys-elevation-shadow-level1);
}
.wp-block-button.is-style-elevated .wp-block-button__link:hover {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), var(--md-sys-color-surface-container-low));
}
.wp-block-button.is-style-elevated .wp-block-button__link:focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), var(--md-sys-color-surface-container-low));
}
.wp-block-button.is-style-elevated .wp-block-button__link:active {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), var(--md-sys-color-surface-container-low));
}
.wp-block-button.is-style-outline .wp-block-button__link {
  background-color: transparent;
  color: var(--md-sys-color-on-surface-variant);
  border: 0; /* drop core's outline border; M3 uses an inset hairline */
  /* Core's is-style-outline ships its own padding (border-compensated) at this
   * same specificity, which beats the theme.json base — reset it so the outlined
   * button keeps the 40px pill like the others. */
  padding-block: 0;
  padding-inline: var(--space-lg);
  box-shadow: inset 0 0 0 1px var(--md-sys-color-outline-variant);
}
.wp-block-button.is-style-outline .wp-block-button__link:hover {
  background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
}
.wp-block-button.is-style-outline .wp-block-button__link:focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), transparent);
}
.wp-block-button.is-style-outline .wp-block-button__link:active {
  background-color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), transparent);
}
.wp-block-button.is-style-text .wp-block-button__link {
  background-color: transparent;
  color: var(--md-sys-color-primary);
  padding-inline: var(--space-md);
}
.wp-block-button.is-style-text .wp-block-button__link:hover {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
}
.wp-block-button.is-style-text .wp-block-button__link:focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), transparent);
}
.wp-block-button.is-style-text .wp-block-button__link:active {
  background-color: color-mix(in srgb, var(--md-sys-color-primary) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), transparent);
}

/* core/buttons as a visual M3 Button group approximation. WordPress owns the
 * row layout and each child `core/button` remains an individual button/link;
 * this opt-in variation only connects the geometry (2px gap, pill outer edges,
 * small inner corners). It does NOT create radio / aria-pressed semantics. */
.wp-block-buttons.is-style-connected {
  gap: 2px;
  flex-wrap: nowrap;
}
.wp-block-buttons.is-style-connected > .wp-block-button {
  margin: 0;
}
.wp-block-buttons.is-style-connected > .wp-block-button > .wp-block-button__link {
  border-radius: var(--md-sys-shape-corner-small);
}
.wp-block-buttons.is-style-connected > .wp-block-button:first-child > .wp-block-button__link {
  border-start-start-radius: var(--comp-button-radius);
  border-end-start-radius: var(--comp-button-radius);
}
.wp-block-buttons.is-style-connected > .wp-block-button:last-child > .wp-block-button__link {
  border-start-end-radius: var(--comp-button-radius);
  border-end-end-radius: var(--comp-button-radius);
}
.wp-block-buttons.is-style-connected > .wp-block-button > .wp-block-button__link:active {
  border-radius: var(--md-sys-shape-corner-extra-small);
}
.wp-block-buttons.is-style-connected > .wp-block-button:first-child > .wp-block-button__link:active {
  border-start-start-radius: var(--comp-button-radius);
  border-end-start-radius: var(--comp-button-radius);
}
.wp-block-buttons.is-style-connected > .wp-block-button:last-child > .wp-block-button__link:active {
  border-start-end-radius: var(--comp-button-radius);
  border-end-end-radius: var(--comp-button-radius);
}

/* ============================================================
 * §10 core/file — download row (consumes the §9 button base)
 *
 * core/file renders a filename link + a `.wp-block-file__button.wp-element-button`
 * download button. The button already inherits the global §9 button base (40px
 * Filled pill, no underline — prose §9 excludes .wp-element-button), so this does
 * NOT redefine its colour / height / radius; it only lays out the row and drops
 * core's `.wp-block-file__button { margin-left }` so the flex `gap` owns spacing.
 * The filename link stays a prose link (prose §9). Embedded-preview (PDF) layout
 * and the download-size control are out of scope (Media follow-up only).
 * ============================================================ */
.wp-block-post-content .wp-block-file {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-block: var(--space-md);
}
.wp-block-post-content .wp-block-file .wp-block-file__button {
  margin-inline-start: 0; /* drop core's margin-left; the row gap owns spacing */
  flex-shrink: 0;
}

/* ============================================================
 * §11 core/separator — M3 divider + style variants
 *
 * core/separator emits <hr class="wp-block-separator …">. Core ships the
 * default / wide / dots styles (client block.json); the theme registers
 * divider-inset / divider-middle-inset (functions.php + styles/blocks partials
 * so they are discoverable in Global Styles). Ported from the lab separator
 * contract, re-contracted for FSE block chrome. The M3 divider is a 1px
 * outline-variant line; dots read in on-surface-variant.
 * ============================================================ */
.wp-block-separator,
.wp-block-post-content .wp-block-separator {
  inline-size: 25%;          /* core's short centered default, M3-recoloured */
  margin-inline: auto;
  block-size: 1px;
  border: 0;
  background-color: var(--md-sys-color-outline-variant);
}
.wp-block-separator.is-style-wide,
.wp-block-post-content .wp-block-separator.is-style-wide {
  inline-size: 100%;
}
.wp-block-separator.is-style-dots,
.wp-block-post-content .wp-block-separator.is-style-dots {
  inline-size: auto;
  background: none;          /* no line — just the dot row */
  block-size: auto;
  text-align: center;
  /* M3 colour only. Core owns the dot row itself (::before with content "···",
   * letter-spacing 2em, padding + serif) — don't override it, or the dots get
   * cramped; just recolour via currentColor. Divider colour = outline-variant,
   * same as the line separators above. */
  color: var(--md-sys-color-outline-variant);
}
/* Theme-registered inset dividers (centered line, inset on both edges).
 * FSE re-contract: the constrained layout clamps a separator to the content size
 * (max-width: --wp--style--global--content-size), so insetting off 100% (the full
 * post-content width) gets clamped back and the two insets collapse to the same
 * width. Inset off the CONTENT size instead so each variant is genuinely narrower
 * (base rule centers them via margin-inline: auto). */
.wp-block-separator.is-style-divider-inset,
.wp-block-post-content .wp-block-separator.is-style-divider-inset {
  inline-size: calc(var(--wp--style--global--content-size) - 2 * var(--space-md));
}
.wp-block-separator.is-style-divider-middle-inset,
.wp-block-post-content .wp-block-separator.is-style-divider-middle-inset {
  inline-size: calc(var(--wp--style--global--content-size) - 2 * var(--space-xl));
}

/* ============================================================
 * §12 core/social-links — icon-button sizing contract (geometry only)
 *
 * Treats the social-links icon cluster as an M3 icon-button cluster:
 * normalizes SIZE / SHAPE / GAP onto the --comp-icon-button-* tokens. It does
 * NOT touch colour — each service keeps its brand hue (default) and the neutral
 * M3 tonal set stays opt-in (per-block iconColor / iconBackgroundColor). See
 * docs/ICON-BUTTON-ROUTE.md (section 4 size map, section 5 colour policy).
 *
 * WP sizing model: the size class sets font-size on .wp-block-social-links and
 * each service svg is 1em, so font-size == icon px. WP's raw 16/24/36/48 is
 * RE-INTERPRETED as M3 container sizes (the name's ordinal, not its px): the
 * icon size follows the M3 column and the CONTAINER grows independently, so
 * "large" / "huge" buy container affordance, not a bigger glyph.
 *
 *   has-small-icon-size  -> XS   icon-size-xs (20)   height-xs (32)
 *   default / has-normal -> S    icon-size-s  (24)   height-s  (40)
 *   has-large-icon-size  -> M    icon-size-m  (24)   height-m  (56)
 *   has-huge-icon-size   -> L    icon-size-l  (32)   height-l  (96)
 *
 * Scope: the social-links block wherever it renders (Widgets surface — post
 * content AND template parts), not .wp-block-post-content-limited; geometry is
 * inherent to the block (like the section 9 button base).
 * ============================================================ */
.wp-block-social-links,
.wp-block-social-links.has-normal-icon-size {
  font-size: var(--comp-icon-button-icon-size-s);
  --_ib-container: var(--comp-icon-button-height-s);
}
.wp-block-social-links.has-small-icon-size {
  font-size: var(--comp-icon-button-icon-size-xs);
  --_ib-container: var(--comp-icon-button-height-xs);
}
.wp-block-social-links.has-large-icon-size {
  font-size: var(--comp-icon-button-icon-size-m);
  --_ib-container: var(--comp-icon-button-height-m);
}
.wp-block-social-links.has-huge-icon-size {
  font-size: var(--comp-icon-button-icon-size-l);
  --_ib-container: var(--comp-icon-button-height-l);
}

/* Container affordance: a square container sized to the M3 height, icon centered
 * (round via the li radius below). NOT for logos-only — that style has no
 * container, just the icon (per ICON-BUTTON-ROUTE section 5). */
.wp-block-social-links:not(.is-style-logos-only) .wp-block-social-link-anchor {
  min-inline-size: var(--_ib-container);
  min-block-size: var(--_ib-container);
  justify-content: center;
  box-sizing: border-box;
}

/* Resting shape = M3 round (full); matches core's 9999px, re-asserted via token. */
.wp-block-social-links .wp-social-link {
  border-radius: var(--comp-icon-button-shape-round);
}

/* Touch target — DEFERRED. XS (32) / S (40) visual containers fall below the
 * 48px a11y minimum. The first cut used a transparent centered ::after of
 * --comp-touch-target, but hosting it needed `position:relative` on the anchor,
 * which shifted Gutenberg's per-social-link selection overlay in the editor
 * (the outline rendered off the icon). Reverted; revisit with anchor padding
 * (no positioned overlay) when the icon-button interaction states land.
 * See ICON-BUTTON-ROUTE §7. */

/* Cluster gap normalized to the M3 small spacing step. */
.wp-block-social-links.is-layout-flex {
  gap: var(--space-sm);
}

/* ============================================================
 * §13 core/search icon submit — default icon-button geometry (.has-icon ONLY)
 *
 * The icon search submit is a textbook M3 *default* icon button (it runs/opens
 * search — not a toggle, no aria-pressed; see SEARCH-ROUTE §5). It consumes the
 * S icon-button geometry tokens. By default the `.wp-element-button` base makes
 * it a 40-tall pill that is WIDER than tall (space-lg inline padding + the 24px
 * glyph → ~72x40); this squares it to the S container and rounds it.
 *
 * ISOLATION: scope to `.has-icon` only. A text submit ("Search" /
 * "Expand search field") must keep the base pill — fixing its inline-size to 40
 * would clip the label. So this never touches `.wp-block-search__button` bare.
 * The base already supplies inline-flex centring, the M3 state layer, and the
 * filled colour; §13 is geometry only — per-variant COLOUR (transparent /
 * on-surface-variant inside, standard, tonal) is the next SEARCH-ROUTE step.
 *
 * Icon: support BOTH core's inline <svg> and the Material Symbols font utility,
 * but do NOT replace core's icon (that is plugin / custom-block territory —
 * SEARCH-ROUTE option C). a11y aria-label is core-owned; do not hide it. */
.wp-block-search__button.has-icon {
  inline-size: var(--comp-icon-button-height-s);
  min-inline-size: var(--comp-icon-button-height-s);
  block-size: var(--comp-icon-button-height-s);
  min-block-size: var(--comp-icon-button-height-s);
  padding: 0;
  border-radius: var(--comp-icon-button-shape-round);
}
.wp-block-search__button.has-icon svg,
.wp-block-search__button.has-icon .material-symbols-outlined {
  inline-size: var(--comp-icon-button-icon-size-s);
  block-size: var(--comp-icon-button-icon-size-s);
  font-size: var(--comp-icon-button-icon-size-s);
  fill: currentColor;
}

/* ============================================================
 * §14 core/search — in-content search field bridge (M3 Search Bar tokens @ 48px)
 *
 * core/search is an in-content WP search field. The button-inside variant is
 * promoted to an M3 Search Bar BRIDGE — Search Bar tokens (surface-container-high,
 * elevation, full shape, body-large text) adapted to a 48px container. The
 * canonical Search Bar is 56dp; 48px is an intentional in-content deviation (56
 * reads too tall inside block-theme content). The other variants stay WP
 * search-form / icon-button variants. See SEARCH-ROUTE §3.
 *
 * Owns core/search by OVERRIDING the parent Twenty Twenty-Five residue
 * (styles.blocks.core/search.css: input border-radius 3.125rem / 25px padding /
 * accent-6 border, plus a dark input bg) via class specificity — the parent rule
 * is `:root :where(.wp-block-search .wp-block-search__input)` (0,1,0); these are
 * (0,2,0). No theme.json edit needed. Scoped to `.wp-block-search` — never a
 * global `input[type=search]` (SEARCH-BAR-WP-MAPPING anti-pattern).
 *
 * Two shells: for button-outside / no-button the INPUT is the field surface; for
 * button-inside the INSIDE-WRAPPER is the field and the input goes transparent
 * (the trailing §13 icon button / base text submit sits inside it). The text
 * submit keeps its base pill; the icon submit keeps §13. Per-variant colour
 * refinement + the connected button-outside layout are SEARCH-ROUTE Phase 2. */

/* The input AS the field (button-outside / no-button). */
.wp-block-search .wp-block-search__input {
  background-color: var(--md-sys-color-surface-container-high);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-full);
  color: var(--md-sys-color-on-surface);
  min-block-size: 48px;
  padding-block: 0;
  padding-inline: var(--space-md);
  /* body-large (16/24, tracking .5) — Search Bar input/supporting text size. */
  font-size: var(--md-sys-typescale-body-large-size);
  line-height: var(--md-sys-typescale-body-large-line-height);
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
}
.wp-block-search .wp-block-search__input::placeholder {
  color: var(--md-sys-color-on-surface-variant);
  opacity: 1;
}
.wp-block-search .wp-block-search__input:focus {
  border-color: var(--md-sys-color-primary);
  outline: 1px solid var(--md-sys-color-primary);
  outline-offset: -2px;
}

/* button-inside: the inside-wrapper IS the field shell; the input goes transparent
 * so the wrapper owns the surface/shape (input + trailing control share one pill). */
.wp-block-search__button-inside .wp-block-search__inside-wrapper {
  background-color: var(--md-sys-color-surface-container-high);
  border: 0;        /* the search-bar bridge reads via elevation, not a hairline */
  border-radius: var(--md-sys-shape-corner-full);
  box-shadow: var(--md-sys-elevation-shadow-level3);  /* light only; dark token = none → surface-tint separation */
  min-block-size: 48px;
  padding: 4px;
  align-items: center;
  transition:
    box-shadow var(--md-sys-motion-curve-fast-effects-duration) var(--md-sys-motion-curve-fast-effects),
    background-color var(--md-sys-motion-curve-fast-effects-duration) var(--md-sys-motion-curve-fast-effects);
}
/* Focus indicator — M3 Search Bar spec: 3dp / 2dp offset, secondary colour
 * (#625B71). Survives dark (where the elevation shadow is none). */
.wp-block-search__button-inside .wp-block-search__inside-wrapper:focus-within {
  outline: 3px solid var(--md-sys-color-secondary);
  outline-offset: 2px;
}
.wp-block-search__button-inside .wp-block-search__input {
  background-color: transparent;
  border: 0;
  border-radius: 0;
  outline: 0;
  min-block-size: auto;
  color: var(--md-sys-color-on-surface);
}

/* The trailing submit inside the bar is NOT the global filled button:
 *  - icon submit = STANDARD icon button (transparent, on-surface-variant, state
 *    layer only) — sits ON the field surface, must not carry a filled container.
 *  - text submit = compact TONAL submit (secondary-container). */
.wp-block-search__button-inside .wp-block-search__button.has-icon {
  background-color: transparent;
  color: var(--md-sys-color-on-surface-variant);
  box-shadow: none;
}
.wp-block-search__button-inside .wp-block-search__button:not(.has-icon) {
  background-color: transparent;
  color: var(--md-sys-color-primary);
  box-shadow: none;
  padding-inline: var(--space-md);   /* compact text button, not the 24px filled pill */
}

/* — Cross-variant alignment + focus — */

/* The inside-wrapper is a flex row holding the field and the submit (WP nests the
 * submit INSIDE the wrapper for every variant). WP leaves align-items normal, so a
 * 40px icon/text submit top-aligns ~4px above the 48px field's centre — centre
 * the row. Covers button-outside / button-only / no-button (button-inside already
 * sets this above). */
.wp-block-search__inside-wrapper {
  align-items: center;
}

/* button-inside: the WRAPPER owns the focus indicator (its focus-within ring), so
 * the transparent input must NOT also paint the base field's inset focus outline
 * — that is the stray "border box" that appears on click inside the search bar. */
.wp-block-search__button-inside .wp-block-search__input:focus {
  outline: 0;
  border-color: transparent;
}

/* ============================================================
 * §15 core/calendar — dark-safe table ink (recolour core's hardcoded colour)
 *
 * core/calendar's own style.css hardcodes non-scheme-aware colours:
 *   :where(table:not(.has-text-color)) { color: #40464d }   ← dark-grey ink
 *   :where(...) th, td { border-color: #ddd }                (overridden by prose)
 *   :where(... th) { background: #ddd }                      (overridden by prose)
 * The #40464d ink survives on the dark surface, so the dates are nearly invisible
 * in dark mode. core/calendar is NOT `.wp-block-table`, so the §5 table contract
 * never reaches it; the prose generic `table` rules give it the M3 frame / header /
 * borders (scheme-aware) but set no text colour. Recolour the ink to on-surface,
 * keeping core's `:not(.has-text-color)` guard so a user text colour still wins. */
.wp-block-post-content .wp-block-calendar table:not(.has-text-color) {
  color: var(--md-sys-color-on-surface);
}
/* core paints a full 1px #ddd border on EVERY cell side; the prose generic table
 * rules only re-colour bottom + right (the M3 grid), so core's #ddd survives on
 * top + left — light gridlines in dark. Drop top/left; the prose bottom/right +
 * the outer table frame own a single-colour outline-variant grid. */
.wp-block-post-content .wp-block-calendar :is(th, td) {
  border-block-start: 0;
  border-inline-start: 0;
}

/* ============================================================
 * §16 core/latest-posts + core/rss — collection cards (list + grid)
 *
 * Every item = a FILLED, shadow-less card (Drive reference — NOT elevated), in
 * both the list and the grid view. CRITICAL for the GRID: WordPress core ALREADY
 * ships its responsive grid — `.is-grid` is flex; `columns-N` set the widths at
 * ≥600px; on mobile each item is full-width (single column). So in the grid we
 * style ONLY the card SURFACE and never touch `display` / `width` / the layout —
 * an own `grid-template-columns` would throw away core's mobile collapse.
 * `box-sizing: border-box` keeps the padding inside core's width calc. The LIST
 * view is a bare `<ul>` with no core responsive grid, so there we DO own a simple
 * single-column stack + gap (below). */
/* container reset — ALL four uls (incl. the grid): drop the default <ul> list
 * indent + markers. Core does NOT reset the grid ul's padding-inline-start, so
 * without this the grid sits ~40px in from the left (the reported left gap). */
.wp-block-post-content .wp-block-latest-posts,
.wp-block-post-content .wp-block-rss {
  list-style: none;
  margin-inline: 0;
  padding: 0;
}
.wp-block-post-content .wp-block-latest-posts > li::marker,
.wp-block-post-content .wp-block-rss > li::marker {
  content: none;
}

/* card surface — Latest Posts items (list + grid) AND RSS GRID items are each a
 * filled, shadow-less card. RSS LIST is the one exception: a grouped feed card
 * (below), not per-item cards. */
.wp-block-post-content .wp-block-latest-posts > li,
.wp-block-post-content .wp-block-rss.is-grid > li {
  box-sizing: border-box;
  margin: 0;        /* spacing is owned by the list/grid gap below, not core's per-item margins */
  padding: var(--space-md);
  border-radius: var(--comp-card-radius);
  background-color: var(--md-sys-color-surface-container);
  box-shadow: none;
}

/* Latest Posts list = a single-column stack of cards with a gap. */
.wp-block-post-content .wp-block-latest-posts:not(.is-grid) {
  display: grid;
  gap: var(--space-md);
}

/* RSS list = ONE grouped feed card; rows split by dividers (a dense feed reads
 * better grouped than as separate cards). */
.wp-block-post-content .wp-block-rss:not(.is-grid) {
  overflow: clip;
  border-radius: var(--comp-card-radius);
  background-color: var(--md-sys-color-surface-container);
}
.wp-block-post-content .wp-block-rss:not(.is-grid) > li {
  box-sizing: border-box;
  padding: var(--space-md);
}
.wp-block-post-content .wp-block-rss:not(.is-grid) > li + li {
  border-block-start: 1px solid var(--md-sys-color-outline-variant);
}

/* card interior vertical rhythm. The card PADDING owns the edges; a single token
 * gap stacks the items. We reset core's ad-hoc per-element margins first
 * (latest-posts excerpt `0.5em / 1em`, centred featured-image `1em`) because they
 * left the rhythm uneven: the title→meta gap was 0, while the LAST child (excerpt)
 * doubled its `1em` (~14px) bottom margin against the card's 16px padding for a
 * ~30px bottom edge. Now every stacked item is one `--space-xs` apart and the card
 * padding alone owns all four edges. (No flex `gap`: featuredImageAlign uses
 * float, which a flex container would drop — so the rhythm is margin-based.) */
.wp-block-post-content .wp-block-latest-posts > li > *,
.wp-block-post-content .wp-block-rss > li > * {
  margin-block: 0;
}
.wp-block-post-content .wp-block-latest-posts > li > * + *,
.wp-block-post-content .wp-block-rss > li > * + * {
  margin-block-start: var(--space-xs);
}

/* GRID layout — UNIFIED for both blocks. Core ships the responsive grid
 * (columns-N widths at >=600px, single column below) via per-item margins that
 * differ per block (latest-posts 1.25em, rss 1em) AND leave a trailing column gap
 * (rss never clears the last column). Replace those margins with ONE tokenised
 * flex `gap`, so every collection — Latest Posts list, both grids — shares the
 * same `--space-md` gap; recompute the column width as `(100% - (N-1)*gap)/N` so
 * the row fills exactly. Responsive kept: core's base `width:100%` gives the
 * single column below 600px, and the card surface above already zeroed the item
 * margins (so no trailing/mobile margin remains). */
.wp-block-post-content .wp-block-latest-posts.is-grid,
.wp-block-post-content .wp-block-rss.is-grid {
  gap: var(--space-md);
}
@media (min-width: 600px) {
  .wp-block-post-content :is(.wp-block-latest-posts, .wp-block-rss).is-grid.columns-2 > li { inline-size: calc((100% - var(--space-md)) / 2); }
  .wp-block-post-content :is(.wp-block-latest-posts, .wp-block-rss).is-grid.columns-3 > li { inline-size: calc((100% - 2 * var(--space-md)) / 3); }
  .wp-block-post-content :is(.wp-block-latest-posts, .wp-block-rss).is-grid.columns-4 > li { inline-size: calc((100% - 3 * var(--space-md)) / 4); }
  .wp-block-post-content :is(.wp-block-latest-posts, .wp-block-rss).is-grid.columns-5 > li { inline-size: calc((100% - 4 * var(--space-md)) / 5); }
  .wp-block-post-content :is(.wp-block-latest-posts, .wp-block-rss).is-grid.columns-6 > li { inline-size: calc((100% - 5 * var(--space-md)) / 6); }
}

/* Featured image = the card media frame. Theme owns fit / aspect / radius; WP
 * owns the WIDTH + ALIGNMENT (featuredImageSizeWidth / featuredImageAlign), so we
 * cap with max-inline-size (never force a width) and let core's float/centre
 * class place it. */
.wp-block-post-content .wp-block-latest-posts .wp-block-latest-posts__featured-image img {
  max-inline-size: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--md-sys-shape-corner-small);
}

/* item typography (list + grid). These rules are the CSS EQUIVALENT of the theme's
 * typescale utility classes — `.t-title-small` (title), `.t-body-small` (meta),
 * `.t-body-medium` (excerpt) — applied by SELECTOR rather than as a class because
 * core/latest-posts + core/rss are DYNAMIC blocks: their render markup is built in
 * PHP and can't carry a utility class declaratively (a static pattern we own would
 * just add `class="… t-title-small"`). A future `render_block` filter (HTML Tag
 * Processor) could inject the classes and retire this duplication.
 *
 * The title is the item HEADLINE, not a body link, and must OUT-RANK the
 * body-medium (14/20) excerpt — so it is title-MEDIUM (16/24). title-small collided
 * with the excerpt size (both 14/20) and flattened the hierarchy, which made the
 * title read as body text regardless of the underline. Drop the prose blue/underline
 * at rest (in a card where the whole item is the affordance an underlined link reads
 * as body text and weakens the title); underline on hover/focus only. The policy is
 * IDENTICAL for latest-posts and rss — a one-sided underline would be a bug. */
.wp-block-post-content .wp-block-latest-posts .wp-block-latest-posts__post-title,
.wp-block-post-content .wp-block-rss .wp-block-rss__item-title a {
  /* = .t-title-medium + headline colour, no resting underline */
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
  font-family: var(--md-sys-typescale-title-medium-font);
  font-size: var(--md-sys-typescale-title-medium-size);
  line-height: var(--md-sys-typescale-title-medium-line-height);
  font-weight: var(--md-sys-typescale-title-medium-weight);
  letter-spacing: var(--md-sys-typescale-title-medium-tracking);
}
.wp-block-post-content .wp-block-latest-posts .wp-block-latest-posts__post-title:hover,
.wp-block-post-content .wp-block-latest-posts .wp-block-latest-posts__post-title:focus-visible,
.wp-block-post-content .wp-block-rss .wp-block-rss__item-title a:hover,
.wp-block-post-content .wp-block-rss .wp-block-rss__item-title a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.wp-block-post-content .wp-block-latest-posts :is(.wp-block-latest-posts__post-date, .wp-block-latest-posts__post-author),
.wp-block-post-content .wp-block-rss :is(.wp-block-rss__item-publish-date, .wp-block-rss__item-author) {
  color: var(--md-sys-color-on-surface-variant);
  font-size: var(--md-sys-typescale-body-small-size);
  line-height: var(--md-sys-typescale-body-small-line-height);
}
.wp-block-post-content .wp-block-latest-posts .wp-block-latest-posts__post-excerpt,
.wp-block-post-content .wp-block-rss .wp-block-rss__item-excerpt {
  /* = .t-body-medium. The excerpt is the body summary, so it gets a wider gap than
   * the generic --space-xs sibling rhythm — --space-sm above (more specific than the
   * owl rule above, so it wins) keeps it from reading cramped under the meta. */
  color: var(--md-sys-color-on-surface-variant);
  font-size: var(--md-sys-typescale-body-medium-size);
  line-height: var(--md-sys-typescale-body-medium-line-height);
  margin-block-start: var(--space-sm);
}

/* ============================================================================
 * §17 core/embed — the embed OUTER SHELL only
 *
 * core/embed is static save() + `the_content` autoembed (oEmbed): the figure + a
 * bare URL saves, and the provider HTML is injected at render. So the DOM is
 * PROVIDER-owned and falls into three buckets (see docs/EMBEDS-ROUTE.md): an
 * IFRAME, a BLOCKQUOTE the provider <script> later swaps for an iframe, or — when
 * the provider is unsupported/unreachable — the bare URL as text. The theme styles
 * the OUTER shell ONLY: figure rhythm, the media frame radius/clip, and a surface
 * for the unresolved fallback. It never reaches inside the provider iframe/script,
 * and it does NOT set width or aspect — core already owns those via
 * `wp-embed-aspect-*` / `wp-has-aspect-ratio`. (figcaption is owned by prose.css.) */

/* figure rhythm — tokenise the browser-default, uneven embed figure margin. */
.wp-block-post-content .wp-block-embed {
  margin-block: var(--space-md);
}

/* media — fill + centre only; NO theme radius. We deliberately do NOT round embed
 * media: providers carry their own, very different corner treatments (reddit 8,
 * spotify 12, square players, none), so imposing one corner reads inconsistent
 * across the lane. Equally important — NEVER round via `overflow: clip` on the
 * wrapper: a WordPress-POST embed iframe completes its wp-embed height handshake
 * while `position: absolute; visibility: hidden`, and a clipping wrapper suppresses
 * the child's `height` postMessage so the iframe is never un-hidden (ma.tt tolerated
 * it; the wordpress.org directory embeds did not). Width + aspect stay core's;
 * fixed-width provider embeds are centred (margin-inline:auto — a no-op for the
 * full-width ones). */
.wp-block-post-content .wp-block-embed iframe {
  display: block;
  max-inline-size: 100%;
  margin-inline: auto;
}

/* Script-hydrated social centring — X (widgets.js) and Bluesky (embed.js) hydrate
 * their blockquote into a FIXED-WIDTH container that HOLDS the iframe
 * (`.twitter-tweet-rendered` ~500px, `.bluesky-embed` ~600px), so `margin-inline:
 * auto` on the iframe only centres WITHIN that box (no effect — the box itself sits
 * left in the wider column). Centre the provider OUTER container instead — still not
 * touching the iframe interior. (Both classes are provider-specific, so this stays
 * agnostic to the provider-slug figure variants.) */
.wp-block-post-content .wp-block-embed :is(.twitter-tweet-rendered, .bluesky-embed) {
  margin-inline: auto;
}

/* WordPress post embeds (iframe.wp-embedded-content) ship at a fixed width="500"
 * and sit LEFT-ALIGNED in the wider content column (a ~145px dead strip on the
 * right). Unlike provider video/audio iframes — which own a fixed aspect — the WP
 * embed card is fluid, so stretch it to the content width. wp-embed.js drives only
 * HEIGHT, so width:100% reflows the card and the script re-reports the height. The
 * iframe INTERIOR is cross-origin: its colours / dark-mode belong to that site, not
 * us — we own width + frame only. (Our OWN posts get a real M3 embed card in the
 * separate /embed/ template lane; see EMBEDS-ROUTE.md §9.) */
.wp-block-post-content .wp-block-embed iframe.wp-embedded-content {
  inline-size: 100%;
}

/* unresolved fallback — when oEmbed can't resolve, the wrapper holds the bare URL
 * as TEXT (no iframe, no provider blockquote). Box it in a quiet surface so it does
 * not read as naked text. Scoped with :has() to wrappers that have NEITHER an
 * iframe NOR a provider blockquote, so resolved/loading embeds never get a
 * redundant surface behind their media. A truly clickable LINK card needs the raw
 * URL wrapped in an <a>, which CSS cannot do — that is a render_block_core/embed
 * step, deferred (see EMBEDS-ROUTE.md §7). */
.wp-block-post-content .wp-block-embed__wrapper:not(:has(iframe)):not(:has(blockquote)) {
  box-sizing: border-box;
  padding: var(--space-md);
  background-color: var(--md-sys-color-surface-container-low);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-medium);
  color: var(--md-sys-color-on-surface-variant);
  font-size: var(--md-sys-typescale-body-small-size);
  line-height: var(--md-sys-typescale-body-small-line-height);
  overflow-wrap: anywhere;
}

/* WordPress-embed fallback (blockquote.wp-embedded-content) — WP's OWN no-JS /
 * failed-load fallback, a titled LINK. wp-embed.js sets it display:none once the
 * iframe loads successfully, so this surfaces ONLY when the embed can't render
 * (e.g. a wordpress.org plugin/theme DIRECTORY page, which has no working /embed/
 * endpoint and so leaves the blockquote). Unlike the raw-URL bucket this fallback
 * already contains a real <a>, so the same quiet surface makes it a proper fallback
 * LINK card. (provider blockquotes — .reddit-embed-bq / .bluesky-embed — are a
 * different class and are NOT matched here; their script upgrades them.) */
.wp-block-post-content .wp-block-embed blockquote.wp-embedded-content {
  box-sizing: border-box;
  margin: 0;
  padding: var(--space-md);
  background-color: var(--md-sys-color-surface-container-low);
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-medium);
}

/* ============================================================================
 * §18 core/theme blocks — chrome baseline (NOT prose)  [first cut]
 *
 * Theme blocks (site identity, navigation, Query Loop post-meta) are template
 * CHROME, not long-form prose. In a real header/footer they sit OUTSIDE
 * .wp-block-post-content, but in the VQA (and any in-content placement) they inherit
 * the prose body size + the §9 always-underline link, so meta / actions / titles read
 * as body links. Re-own them here — blocks.css loads AFTER prose.css, so an
 * equal-specificity rule (0,2,1) ties the prose link rule and wins by order.
 *
 * FIRST CUT = typography (site identity) + de-prose-ify (no resting underline,
 * hover-only). The M3 component treatments — terms → chips, read-more → text button,
 * pagination component, metadata cluster layout — are later contracts
 * (THEME-VQA-ROUTE §7). */

/* site identity: title = title-large (a header brand title, not a 16px body line);
 * tagline = body-small / on-surface-variant. The size is also scoped under
 * post-content so the VQA specimen (which inherits the 16px body) is corrected. */
.wp-block-site-title,
.wp-block-post-content .wp-block-site-title {
  font-family:    var(--md-sys-typescale-title-large-font, inherit);
  font-size:      var(--md-sys-typescale-title-large-size, 22px);
  line-height:    var(--md-sys-typescale-title-large-line-height, 28px);
  font-weight:    var(--md-sys-typescale-title-large-weight, 400);
  letter-spacing: var(--md-sys-typescale-title-large-tracking, 0);
}
.wp-block-post-content .wp-block-site-title a {
  color: var(--md-sys-color-on-surface);
  text-decoration: none;
}
.wp-block-post-content .wp-block-site-tagline {
  font-size:   var(--md-sys-typescale-body-small-size);
  line-height: var(--md-sys-typescale-body-small-line-height);
  color: var(--md-sys-color-on-surface-variant);
}

/* de-prose-ify theme-block links — the post title is a HEADLINE (on-surface), the
 * meta/nav links are METADATA (on-surface-variant); none keep the resting prose
 * underline (hover/focus only). read-more keeps the primary affordance colour (it
 * becomes a text button in the B contract). */
.wp-block-post-content .wp-block-post-title a {
  color: var(--md-sys-color-on-surface);
}
.wp-block-post-content :is(
  .wp-block-post-terms,
  .wp-block-post-comments-count,
  .wp-block-post-comments-link,
  .wp-block-post-navigation-link
) a {
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-query-pagination a {
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-post-content :is(
  .wp-block-post-title,
  .wp-block-post-terms,
  .wp-block-post-comments-count,
  .wp-block-post-comments-link,
  .wp-block-post-navigation-link
) a,
.wp-block-query-pagination a,
.wp-block-post-content a.wp-block-read-more {
  text-decoration: none;
}
.wp-block-post-content :is(
  .wp-block-post-title,
  .wp-block-post-terms,
  .wp-block-post-comments-count,
  .wp-block-post-comments-link,
  .wp-block-post-navigation-link
) a:hover,
.wp-block-post-content :is(
  .wp-block-post-title,
  .wp-block-post-terms,
  .wp-block-post-comments-count,
  .wp-block-post-comments-link,
  .wp-block-post-navigation-link
) a:focus-visible,
.wp-block-query-pagination a:hover,
.wp-block-query-pagination a:focus-visible,
.wp-block-post-content a.wp-block-read-more:hover,
.wp-block-post-content a.wp-block-read-more:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* ----------------------------------------------------------------------------
 * §18b core/query-loop + post-context — B contract Phase 1 (FLAT article baseline)
 *
 * Route (THEME-VQA-ROUTE §10/B): the Query Loop post item is a FLAT ARTICLE by default —
 * NOT a card. core/query is a layout PRIMITIVE reused across home / archive / search /
 * category / related, so a card baseline would over-apply. The card form is a DEFERRED
 * opt-in block style (.is-style-* list/grid) that can reuse the §16 Latest Posts/RSS card
 * ontology. This baseline only: (1) strips the prose list indent off the post-template
 * (chrome, not a prose list), (2) maps title / excerpt / meta / byline / read-more /
 * pagination to M3 typescale + role colour. Layout (flow, gap, columns) stays CORE-owned;
 * read-more / pagination get a TEXT-button-LITE typescale only (the full M3 button surface
 * is the button module, Phase 2). Link colour/underline already set in the §18 de-prose
 * block above. */

/* 1) prose list-indent leak — the post-template ul is a query RESULT list (chrome), not a
 *    prose list; prose's :is(ul,ol) padding-inline-start (~32px) shifts the whole loop in. */
.wp-block-post-content .wp-block-post-template {
  padding-inline-start: 0;
  list-style: none;
  margin-block: 0;
}

/* 2) post title = article HEADLINE → title-large (on-surface). Overrides the prose h2
 *    (28/36 + ~23px block margin), wrong for a chrome article-list headline. */
.wp-block-post-content .wp-block-post-title {
  font-size:      var(--md-sys-typescale-title-large-size);
  line-height:    var(--md-sys-typescale-title-large-line-height);
  font-weight:    var(--md-sys-typescale-title-large-weight);
  letter-spacing: var(--md-sys-typescale-title-large-tracking);
  color: var(--md-sys-color-on-surface);
}

/* 2b) post featured image = article/list media. Layout, object-fit, and border radius
 * stay core/global-styles-owned. The default radius lives in theme.json so the Site
 * Editor's core/post-featured-image Radius control can override it. */
.wp-block-post-featured-image img {
  display: block;
}

/* 3) excerpt = body copy → body-large, on-surface. */
.wp-block-post-content .wp-block-post-excerpt {
  font-size:      var(--md-sys-typescale-body-large-size);
  line-height:    var(--md-sys-typescale-body-large-line-height);
  font-weight:    var(--md-sys-typescale-body-large-weight);
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
  color: var(--md-sys-color-on-surface);
}

/* 4) META cluster (date / terms / time-to-read / comments / total / byline) → body-small,
 *    on-surface-variant. Unifies core's mixed defaults (date 12/grey, terms 12/600/ink). */
.wp-block-post-content :is(
  .wp-block-post-date,
  .wp-block-post-terms,
  .wp-block-post-time-to-read,
  .wp-block-post-comments-count,
  .wp-block-post-comments-link,
  .wp-block-query-total,
  .wp-block-post-author-name
) {
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  font-weight:    var(--md-sys-typescale-body-small-weight);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* 4b) Query meta row is MARKUP-owned (patterns/vqa-theme.php): avatar, author, date,
 * terms, and time-to-read are grouped into a flex row. CSS owns only alignment, gap and
 * margin cleanup; it does not synthesize the row from loose sibling blocks. */
.wp-block-post-content .omph-query-meta-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-post-content .omph-query-meta-row > * {
  margin-block: 0;
}
.wp-block-post-content .omph-query-meta-row .wp-block-avatar {
  flex-shrink: 0;
}

/* 5) author biography = supporting text → body-medium, on-surface-variant. */
.wp-block-post-content .wp-block-post-author-biography {
  font-size:      var(--md-sys-typescale-body-medium-size);
  line-height:    var(--md-sys-typescale-body-medium-line-height);
  font-weight:    var(--md-sys-typescale-body-medium-weight);
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* 6) read-more + pagination = TEXT-button-LITE → label-large typescale only (colour from
 *    the §18 de-prose block: read-more primary, pagination on-surface-variant). NO button
 *    surface — that is the button module (Phase 2). */
.wp-block-post-content a.wp-block-read-more,
.wp-block-query-pagination {
  font-size:      var(--md-sys-typescale-label-large-size);
  line-height:    var(--md-sys-typescale-label-large-line-height);
  font-weight:    var(--md-sys-typescale-label-large-weight);
  letter-spacing: var(--md-sys-typescale-label-large-tracking);
}

/* 7) query pagination = pagination-lite. Core owns pagination semantics and URL state;
 * theme only binds a compact page-number affordance. Full M3 button surfaces stay in the
 * button module / Phase 2. */
.wp-block-query-pagination {
  gap: var(--space-sm);
  align-items: center;
}
.wp-block-query-pagination-numbers {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-xs);
}
.wp-block-query-pagination :is(.page-numbers, .wp-block-query-pagination-previous, .wp-block-query-pagination-next) {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 32px;
  min-block-size: 32px;
  padding-inline: var(--space-sm);
  border-radius: var(--md-sys-shape-corner-full);
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-query-pagination .page-numbers.current {
  background-color: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}
.wp-block-query-pagination a:is(.page-numbers, .wp-block-query-pagination-previous, .wp-block-query-pagination-next):hover,
.wp-block-query-pagination a:is(.page-numbers, .wp-block-query-pagination-previous, .wp-block-query-pagination-next):focus-visible {
  background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
  text-decoration: none;
}

/* 8) query NO-RESULTS = empty state → muted supporting copy (body-medium, on-surface-variant).
 *    Renders only when the query returns nothing; the inner paragraph is prose, so bind the
 *    typescale/colour on it too (colour also set on the container so any markup inherits). */
.wp-block-post-content .wp-block-query-no-results {
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-post-content .wp-block-query-no-results :is(p, .wp-block-paragraph) {
  font-size:      var(--md-sys-typescale-body-medium-size);
  line-height:    var(--md-sys-typescale-body-medium-line-height);
  font-weight:    var(--md-sys-typescale-body-medium-weight);
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* ----------------------------------------------------------------------------
 * §18c core/query `.is-style-cards` — SURFACE-only card opt-in (reuses §16 ontology)
 *
 * Opt-in block style (functions.php → core/query) turning each flat post item into a
 * filled, shadow-less card — the SAME surface as the §16 Latest Posts/RSS cards. It owns
 * ONLY the surface + interior rhythm; LAYOUT stays core (the Query/Post-Template List|Grid
 * view + core's responsive column collapse). Like §16's grid note, we never touch
 * `display` / `width` / `grid-template-columns` here — core's flow block-gap (list) and
 * grid gap (grid) own the inter-card spacing, so a `.is-style-cards` loop works unchanged
 * in BOTH List and Grid view. Media (featured-image corner/full-bleed) remains deferred
 * (§10); meta-row composition is pattern-owned in §18b/`patterns/vqa-theme.php`. */
.wp-block-post-content .wp-block-query.is-style-cards .wp-block-post {
  box-sizing: border-box;
  margin: 0;               /* inter-card spacing = core flow/grid gap, not per-item margin */
  padding: var(--space-md);
  border-radius: var(--comp-card-radius);
  background-color: var(--md-sys-color-surface-container);
  box-shadow: none;
}
/* card interior vertical rhythm — the card padding owns the edges; one token gap stacks the
 * blocks (resets core's ad-hoc per-block margins, e.g. the post-title's prose block margin,
 * so the top/bottom card edges stay even). */
.wp-block-post-content .wp-block-query.is-style-cards .wp-block-post > * {
  margin-block: 0;
}
.wp-block-post-content .wp-block-query.is-style-cards .wp-block-post > * + * {
  margin-block-start: var(--space-xs);
}

/* ----------------------------------------------------------------------------
 * §18d core/query-title + term blocks — Theme VQA Phase 3 (archive / terms)
 *
 * Archive/Search `core/query-title` is TEMPLATE-CONTEXT chrome (outside
 * .wp-block-post-content), so bind it globally like navigation/comments. The Terms Query
 * specimen is in-content; it remains a core layout primitive and only gets list-indent
 * reset + token binding for term name/description/count. */
.wp-block-query-title {
  font-size:      var(--md-sys-typescale-title-large-size);
  line-height:    var(--md-sys-typescale-title-large-line-height);
  font-weight:    var(--md-sys-typescale-title-large-weight);
  letter-spacing: var(--md-sys-typescale-title-large-tracking);
  color: var(--md-sys-color-on-surface);
}
.wp-block-post-content .wp-block-term-template {
  padding-inline-start: 0;
  list-style: none;
  margin-block: 0;
}
.wp-block-post-content .wp-block-term + .wp-block-term {
  margin-block-start: var(--space-md);
}
.wp-block-post-content .wp-block-term-name {
  margin-block: 0;
  font-size:      var(--md-sys-typescale-title-medium-size);
  line-height:    var(--md-sys-typescale-title-medium-line-height);
  font-weight:    var(--md-sys-typescale-title-medium-weight);
  letter-spacing: var(--md-sys-typescale-title-medium-tracking);
  color: var(--md-sys-color-on-surface);
}
.wp-block-post-content .wp-block-term-name a {
  color: inherit;
  text-decoration: none;
}
.wp-block-post-content .wp-block-term-name a:hover,
.wp-block-post-content .wp-block-term-name a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.wp-block-post-content .wp-block-term-description {
  margin-block-start: var(--space-xs);
  font-size:      var(--md-sys-typescale-body-medium-size);
  line-height:    var(--md-sys-typescale-body-medium-line-height);
  font-weight:    var(--md-sys-typescale-body-medium-weight);
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-post-content .wp-block-term-count {
  margin-block-start: var(--space-xs);
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  font-weight:    var(--md-sys-typescale-body-small-weight);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* ----------------------------------------------------------------------------
 * §18e core/archives + core/categories + core/page-list — Theme list WIDGETS
 *
 * Parked Widgets rejoined from the Widgets lane into the Theme/Archive family. Each is an
 * in-content list of links, so the prose `:is(ul,ol)` indent (32px) + disc markers + the
 * prose always-underline PRIMARY link all leak onto them (same trap as nav / term blocks).
 * Bind (post-content-scoped): strip the indent + markers, de-prose the links (on-surface, no
 * resting underline — hover/focus only), tokenise typography. NESTED children (categories
 * hierarchy, page-list) keep a modest indent so depth still reads. The post-count text stays
 * muted (on-surface-variant); the link is on-surface. The `:is(…)` (0,2,1) ties the prose
 * link rule and wins on source order (blocks.css loads after prose.css). */
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) {
  padding-inline-start: 0;
  list-style: none;
  margin-block: 0;
}
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) ul {
  list-style: none;
  padding-inline-start: var(--space-md);      /* nested depth indent (hierarchy) */
}
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) li {
  margin-block: 0;
  font-size:      var(--md-sys-typescale-body-medium-size);
  line-height:    var(--md-sys-typescale-body-medium-line-height);
  font-weight:    var(--md-sys-typescale-body-medium-weight);
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
  color: var(--md-sys-color-on-surface-variant);     /* post-count / muted text */
}
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) a {
  color: var(--md-sys-color-on-surface);             /* link = emphasised vs the muted count */
  text-decoration: none;
}
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) a:hover,
.wp-block-post-content :is(.wp-block-archives, .wp-block-categories, .wp-block-page-list) a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* ----------------------------------------------------------------------------
 * §18e2 core/breadcrumbs — navigation TRAIL (chrome)
 *
 * Secondary navigation trail. Typography + link colour are GLOBAL (chrome — works in a
 * template too); the prose list-indent (32px) + always-underline link only leak IN-CONTENT,
 * so those resets are post-content-scoped and lifted to (0,2,1) to beat the prose link rule.
 * The trail is muted (body-small / on-surface-variant); the CURRENT (aria-current) crumb is
 * on-surface. core owns the flex trail + separators. */
.wp-block-breadcrumbs {
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  font-weight:    var(--md-sys-typescale-body-small-weight);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-breadcrumbs a,
.wp-block-post-content .wp-block-breadcrumbs a {
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: none;
}
.wp-block-breadcrumbs [aria-current="page"] {
  color: var(--md-sys-color-on-surface);
}
.wp-block-post-content .wp-block-breadcrumbs :is(ol, ul) {
  padding-inline-start: 0;
  margin-block: 0;
  list-style: none;
}
.wp-block-breadcrumbs a:hover,
.wp-block-breadcrumbs a:focus-visible,
.wp-block-post-content .wp-block-breadcrumbs a:hover,
.wp-block-post-content .wp-block-breadcrumbs a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* ============================================================================
 * §19 core/navigation — chrome contract A1 + A2  [cleanup: M3-faithful, core-leaning]
 *
 * The submenu is CONTEXT-SENSITIVE (THEME-VQA-ROUTE §9.1) — core renders it two ways:
 *   inline dropdown (header / never-overlay / mobile>=600)  → position:absolute
 *   responsive overlay (mobile<600 / always, .is-menu-open) → position:static, nested
 * So the menu SURFACE applies to the INLINE DROPDOWN only. The gate is a single
 * direction: `nav.wp-block-navigation:not(:has(… .is-menu-open))` — in a nav whose
 * overlay is OPEN, NONE of the A2 skin applies, so the submenu stays core's nested
 * section (fully core). No media query, no skin+reset; "if .is-menu-open exists, don't
 * touch it" (per review). The leading `nav` type selector is load-bearing (see the A2
 * comment — the inner container ul also carries the class). A3 side-sheet + A4
 * icon-button stay deferred.
 *
 * Targets (corrected): nav items are DESTINATION LINKS (no `.wp-element-button`).
 * Top-level TYPOGRAPHY/PADDING is LEFT TO CORE — overriding line-height/padding shifted
 * the front-end baseline ~1px in the content specimen; we only de-underline + a
 * conservative hover underline (no box, so geometry stays core's). The state layer
 * lives on the menu ROW (`li`), not the anchor (M3 menu item: the row owns the state
 * layer), and only for SUBMENU rows — top-level stays conservative. */

/* A1 — chrome de-underline (both contexts; theme block is chrome, not prose). The nav
 * item affordance is the ROW STATE LAYER (M3: the li owns it), NOT an underline. */
.wp-block-navigation a,
.wp-block-post-content .wp-block-navigation a {
  text-decoration: none;
}

/* A1 — navigation uls are CHROME, not prose lists. prose.css gives every post-content
 * `:is(ul,ol)` a list indent (padding-inline-start: --space-xl = 32px); that LEAKS onto
 * the nav container + submenu uls, indenting the depth-1 items (Home / submenu sit 32px
 * right of loginout, which core renders OUTSIDE the container ul). Strip the prose list
 * indent + markers on nav uls — ALL nav contexts (horizontal / vertical / overlay), so
 * depth-1 items share one line. Scoped to post-content (a real header/template-part nav
 * never gets prose.css, so no leak there). The Menu skin's own submenu padding (§19
 * gated) still wins by specificity for the horizontal dropdown. */
.wp-block-post-content :is(.wp-block-navigation__container, .wp-block-navigation__submenu-container) {
  padding-inline-start: 0;
  list-style: none;
}

/* A1 + A2 — INLINE context only (gated; the open overlay stays fully core). The leading
 * `nav` type selector is load-bearing: the inner `ul.wp-block-navigation__container`
 * also carries `.wp-block-navigation` but sits inside the responsive container, so a
 * class-only gate leaks the skin into the open overlay. Values follow the M3 standard
 * Menu spec; state-layer opacities + focus-indicator come from the sys.state tokens
 * (mechanics), color is role-chosen. Measurements here are the dropdown/popover Menu
 * measurements only; overlay / vertical navigation is a nav-rail/drawer-like surface
 * and is intentionally excluded by the open-overlay gate. */
/* CONTEXT GATE — the Menu/popover measurements apply ONLY to the horizontal FLOATING
 * dropdown. core/navigation-submenu is NOT always a Menu: by `submenuVisibility` /
 * `orientation` / overlay it becomes a nav-rail/drawer-like NESTED SECTION where popover
 * elevation + floating container + first/last menu-item corners do not belong. Verified
 * class signals (PHP render): vertical → `nav.is-vertical`; submenuVisibility "always" →
 * the submenu li gets `.open-always` (static, always-open nested list); overlay open →
 * `.…responsive-container.is-menu-open`. The gate excludes all three; what remains is the
 * hover/click FLOATING dropdown. `nav` type still load-bearing (inner ul carries the
 * class too). The nav-rail (expanded) contract for vertical/always is a SEPARATE lane. */
nav.wp-block-navigation:not(.is-vertical):not(:has(.open-always)):not(:has(.wp-block-navigation__responsive-container.is-menu-open)) {
  /* baseline fix: the top-level submenu trigger li carries a stray margin-block-start
   * (~4px) that drops the submenu label ~1px below its non-submenu siblings (Home /
   * Log in). Zero it so the whole top row shares one baseline. */
  & .wp-block-navigation-submenu { margin-block-start: 0; }

  /* item ROW state layer (M3 nav item: the li owns it; covers label + trailing icon).
   * BOTH top-level and submenu rows. NO box padding on top-level, so its geometry/
   * baseline stays core. hover/focus opacities from sys.state. */
  & .wp-block-navigation-item:hover {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
  }
  & .wp-block-navigation-item:focus-within {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), transparent);
  }
  & .wp-block-navigation-item:active {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), transparent);
  }
  /* A2b — top-level item = M3 Nav bar item, "Nav bar-LITE" (header-nav bridge). The WP
   * horizontal nav root is HEADER-TEMPLATE CHROME, not a standalone bottom nav, so the
   * theme owns only SELECTED pieces: item SHAPE (corner-full pill), the state layer
   * (above), and the current-page active INDICATOR. EXTERNAL layout — container height /
   * gap / justify / item spacing / header row rhythm — is left to the header/template
   * (NO 64dp container, NO min-height, NO container gap here). Pill internal padding is
   * minimal (space-sm) pending header-context measurement. Label stays label-large 14 —
   * the M3 nav-bar 12dp label-medium is a lab reference, too small for a header site nav
   * (recorded, NOT applied). */
  & .wp-block-navigation__container > .wp-block-navigation-item {
    display: flex;
    align-items: center;
    min-block-size: 40px;                  /* M3 nav bar horizontal active-indicator height 40dp */
    padding-inline: var(--space-md, 16px); /* M3 active-indicator leading/trailing 16dp */
    border-radius: var(--md-sys-shape-corner-full);
  }
  /* current-page active indicator: secondary-container pill + secondary label (M3 nav
   * bar active item). core marks the current item with aria-current="page" on the anchor.
   * (Inactive label color = on-surface-variant is a candidate, left to header-context
   * review; not applied here.) */
  & .wp-block-navigation__container > .wp-block-navigation-item:has(> a[aria-current="page"]) {
    background-color: var(--md-sys-color-secondary-container);
  }
  & .wp-block-navigation__container > .wp-block-navigation-item > a[aria-current="page"] {
    color: var(--md-sys-color-secondary);
  }
  /* keyboard focus ring (M3 focus indicator: secondary, 3px) — outer offset on the
   * top-level pill. */
  & .wp-block-navigation__container > .wp-block-navigation-item > a.wp-block-navigation-item__content:focus-visible {
    outline: var(--md-sys-state-focus-indicator-thickness) solid var(--md-sys-color-secondary);
    outline-offset: var(--md-sys-state-focus-indicator-outer-offset);
  }
}

/* ----------------------------------------------------------------------------
 * §19b core/navigation — SUBMENU = M3 Menu surface (popover) [UNIFIED]
 *
 * "All submenus = popover" (user directive). The Menu/popover SURFACE applies to BOTH
 * horizontal and vertical CLICK/HOVER FLOATING submenus. Two cases stay EXCLUDED:
 * submenuVisibility "always" (`.open-always` = static always-expanded tree, kept as the
 * §20 nav-rail tree by design) and the open responsive OVERLAY (`.is-menu-open`). core
 * OWNS submenu POSITION (absolute floating, both orientations); the theme owns the surface
 * + menu-row skin only. The top-level Nav-bar-lite skin (above) is HORIZONTAL-only (vertical
 * top-level = §20 56dp capsule), so it keeps its `:not(.is-vertical)` gate; this block drops
 * it so vertical click submenus get the same popover Menu. */
nav.wp-block-navigation:not(:has(.open-always)):not(:has(.wp-block-navigation__responsive-container.is-menu-open)) {
  /* submenu row STATE LAYER (M3: the li owns it) — SUBMENU rows only, so the vertical
   * top-level li keeps its §20 ::before paint (a li-direct bg would tint the whole subtree). */
  & .wp-block-navigation__submenu-container .wp-block-navigation-item:hover {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
  }
  & .wp-block-navigation__submenu-container .wp-block-navigation-item:focus-within {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-focus-state-layer-opacity) * 100%), transparent);
  }
  & .wp-block-navigation__submenu-container .wp-block-navigation-item:active {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-pressed-state-layer-opacity) * 100%), transparent);
  }

  /* A2 — submenu = M3 standard Menu surface. surface-container-low, NO border, elevation
   * level2 (light shadow; dark = tonal lift), container corner-large, 2dp group padding/gap —
   * NO overflow:hidden (it would clip nested flyouts). The submenu carries the
   * has-base-background-color preset (!important), so the tone must be !important too. */
  & .wp-block-navigation__submenu-container {
    display: flex;
    flex-direction: column;
    gap: 2px;                /* M3 menu group gap 2dp */
    background-color: var(--md-sys-color-surface-container-low) !important;
    border: 0 !important;
    border-radius: var(--md-sys-shape-corner-large);
    box-shadow: var(--md-sys-elevation-shadow-level2);
    padding: 2px 4px;        /* M3 menu group padding: 2dp block / 4dp inline */
    min-inline-size: 11rem;
  }
  /* menu ROW = the li (M3 menu item): the li owns the row box, state layer, hit area,
   * height and item padding; the anchor (label) + the toggle button are content/action
   * SLOTS inside it. min 48dp · padding-inline 12dp (effective leading/trailing =
   * ul 4 + li 12 = 16dp) · gap 8dp between the label slot and the trailing icon button.
   * Item shape 4dp; first/last outer corners 12dp. (li is already flex-row/align-center.) */
  & .wp-block-navigation__submenu-container .wp-block-navigation-item {
    min-block-size: 48px;
    padding-inline: 12px;
    gap: 8px;
    border-radius: var(--md-sys-shape-corner-extra-small);
  }
  /* first/last menu item outer corners = corner-medium 12dp (hug the 16dp container
   * inset by the 2dp group padding: 16 − 2 − 2 = 12). Middle rows keep 4dp. */
  & .wp-block-navigation__submenu-container > .wp-block-navigation-item:first-child {
    border-start-start-radius: var(--md-sys-shape-corner-medium);
    border-start-end-radius:   var(--md-sys-shape-corner-medium);
  }
  & .wp-block-navigation__submenu-container > .wp-block-navigation-item:last-child {
    border-end-start-radius: var(--md-sys-shape-corner-medium);
    border-end-end-radius:   var(--md-sys-shape-corner-medium);
  }
  /* anchor = the LABEL slot only. NO padding / NO min-height — the li owns the row box
   * (above). flex-grow:1 so the label fills the remaining row width and pushes the
   * trailing toggle button to the row's trailing edge (the 8dp li gap is the min space
   * between them). DECISION: this applies to leaf items too — a leaf anchor also grows
   * to fill the row, so its click/hit area becomes the full row width (a deliberate
   * change from core's content-width hit area; a full-row target is the M3/menu norm).
   * Internally flex-column so label + supporting text stack. */
  & .wp-block-navigation__submenu-container a.wp-block-navigation-item__content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    flex-grow: 1;
    min-block-size: auto;
    padding: 0;
    color: var(--md-sys-color-on-surface);
    font-size:      var(--md-sys-typescale-label-large-size);
    line-height:    var(--md-sys-typescale-label-large-line-height);
    font-weight:    var(--md-sys-typescale-label-large-weight);
    letter-spacing: var(--md-sys-typescale-label-large-tracking);
  }
  & .wp-block-navigation__submenu-container a.wp-block-navigation-item__content:focus-visible {
    outline: var(--md-sys-state-focus-indicator-thickness) solid var(--md-sys-color-secondary);
    outline-offset: var(--md-sys-state-focus-indicator-inner-offset);
  }
  /* menu item supporting text (core/navigation-link `description`) → M3 supporting text.
   * core hides it (`…__description { display:none }`) unless the theme opts in — we opt
   * in for the INLINE menu (overlay stays core). body-small + 0.4 tracking. */
  & .wp-block-navigation__submenu-container .wp-block-navigation-item__description {
    display: block;
    margin-block-start: 2px;
    font-size:      var(--md-sys-typescale-body-small-size);
    line-height:    var(--md-sys-typescale-body-small-line-height);
    font-weight:    var(--md-sys-typescale-body-small-weight);
    letter-spacing: var(--md-sys-typescale-body-small-tracking);
    color: var(--md-sys-color-on-surface-variant);
  }
  /* trailing submenu indicator = M3 menu trailing icon. core ships a 12-unit inline
   * <svg> chevron (button ~8px); forcing it to 20px stretched the path. Replace it with
   * Material Symbols `arrow_right` (the cascading-submenu glyph) at the 20dp measurement
   * via the theme icon font (registered in theme.json, full glyph table). Hide the core
   * svg; render the glyph on the button. on-surface-variant; transparent bg so it does
   * not double the row state layer. Scoped to the dropdown only (the top-level down
   * chevron stays core). */
  & .wp-block-navigation__submenu-container .wp-block-navigation__submenu-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 20px;
    block-size: 20px;
    margin: 0;     /* core gives the toggle a ~3.5px inline margin → zero it so the
                    * li gap (8) and trailing inset (li 12 + ul 4 = 16) are exact */
    background-color: transparent;
    color: var(--md-sys-color-on-surface-variant);
  }
  & .wp-block-navigation__submenu-container .wp-block-navigation__submenu-icon svg { display: none; }
  & .wp-block-navigation__submenu-container .wp-block-navigation__submenu-icon::after {
    content: "arrow_right";
    font-family: "Material Symbols Outlined", sans-serif;
    font-size: 20px;          /* M3 menu trailing icon 20dp */
    line-height: 1;
    font-feature-settings: "liga";
    font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 20;
    color: var(--md-sys-color-on-surface-variant);
  }
}

/* ----------------------------------------------------------------------------
 * §19c core/navigation — TOP-LEVEL submenu DISCLOSURE control
 *
 * Ontology (route analysis): a top-level submenu trigger is a DESTINATION ROW + a DISCLOSURE
 * control. That trailing affordance belongs to the List/disclosure family — NOT the Nav
 * bar/rail icon slot (those are destination PRIMARIES; Nav bar/rail carry no trailing icon
 * spec), and NOT the Menu cascading indicator (that is the nested popover `arrow_right`,
 * §19b). Chip icon measures (18dp) are component-private and excluded. So replace core's
 * tiny caret <svg> with Material Symbols arrow_drop_down (collapsed) / arrow_drop_up
 * (expanded) at the List trailing-icon 24dp measurement, on-surface-variant. Applies to the
 * TOP-LEVEL disclosure only (`__container > item > __submenu-icon`) — whether core renders it
 * as a <span> (submenuVisibility:"click") or a <button> (hover toggle). The open-always TREE
 * carries no trailing icon (showSubmenuIcon:false) per the Nav-rail baseline. The open
 * overlay (.is-menu-open) stays fully core. Future: a 40dp corner-full container is the List
 * expanded-parent pattern if a stronger disclosure target is wanted. */
nav.wp-block-navigation:not(:has(.wp-block-navigation__responsive-container.is-menu-open)) {
  & .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation__submenu-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 24px;       /* M3 List trailing icon 24dp */
    block-size: 24px;
    padding: 0;
    background-color: transparent;
    color: var(--md-sys-color-on-surface-variant);
  }
  & .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation__submenu-icon > svg {
    display: none;           /* hide core's caret svg */
  }
  & .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation__submenu-icon::after {
    content: "arrow_drop_down";
    font-family: "Material Symbols Outlined", sans-serif;
    font-size: 24px;
    line-height: 1;
    font-feature-settings: "liga";
    font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
    color: var(--md-sys-color-on-surface-variant);
  }
  /* expanded → arrow_drop_up. click: icon is a SIBLING span after the toggle <button>;
   * hover: the icon IS the toggle <button> carrying aria-expanded. */
  & .wp-block-navigation__container > .wp-block-navigation-item > button[aria-expanded="true"] ~ .wp-block-navigation__submenu-icon::after,
  & .wp-block-navigation__container > .wp-block-navigation-item > .wp-block-navigation__submenu-icon[aria-expanded="true"]::after {
    content: "arrow_drop_up";
  }
}

/* --------------------------------------------------------------------------
 * §19d core/navigation — OVERLAY icon controls
 *
 * "Show icon button" on core/navigation configures the overlay OPEN/toggle
 * affordance (`.wp-block-navigation__responsive-container-open`). When it is
 * enabled, WordPress renders raw inline SVGs; when disabled, it renders text
 * ("Menu" / "Close"). Replace only the SVG icon mode with Material Symbols:
 * `menu` for open, `menu_open` for close. Text mode stays core.
 * The sheet/drawer surface itself remains deferred (A3); this is only the icon
 * face.
 * -------------------------------------------------------------------------- */
.wp-block-navigation__responsive-container-open:has(> svg),
.wp-block-navigation__responsive-container-close:has(> svg) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 24px;
  block-size: 24px;
  line-height: 1;
}
.wp-block-navigation__responsive-container-open:has(> svg) > svg,
.wp-block-navigation__responsive-container-close:has(> svg) > svg {
  display: none;
}
.wp-block-navigation__responsive-container-open:has(> svg)::before,
.wp-block-navigation__responsive-container-close:has(> svg)::before {
  font-family: "Material Symbols Outlined", sans-serif;
  font-size: 24px;
  font-weight: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  color: currentColor;
  font-variation-settings:
    "FILL" var(--md-icon-fill, 0),
    "wght" var(--md-icon-wght, 400),
    "GRAD" var(--md-icon-grad, 0),
    "opsz" 24;
}
.wp-block-navigation__responsive-container-open:has(> svg)::before {
  content: "menu";
}
.wp-block-navigation__responsive-container-close:has(> svg)::before {
  content: "menu_open";
}

/* ============================================================================
 * §20 core/navigation — VERTICAL nav-rail skin: TOP-LEVEL capsule + open-always TREE
 * [A2c rolled back; submenu popover moved to §19b]
 *
 * SCOPE (post-"all submenus = popover"): the 56dp capsule rows + ::before paint apply to
 * the vertical TOP-LEVEL items (`.wp-block-navigation__container > .wp-block-navigation-item`)
 * and the always-expanded TREE rows (`.open-always …`) ONLY. A vertical CLICK submenu is now
 * a FLOATING popover styled by §19b — so the capsule/row-gap rules here are scoped away from
 * it, or it would get 56dp tree rows instead of the 48dp Menu rows.
 *
 * ELEMENT-AGNOSTIC: item selectors use the CLASS `.wp-block-navigation-item`, NOT `li…`. The
 * front-end save renders items as `<li>`, but the BLOCK EDITOR wraps each inner block in a
 * `<div class="block-editor-block-list__block … wp-block-navigation-item …">` (no `<li>`). A
 * `li.…` qualifier matches the front only and the nav-rail skin never reaches the editor
 * canvas (add_editor_style loads blocks.css there). The class form matches both.
 *
 * ROLLBACK. The earlier §20 implemented a full M3 EXPANDED RAIL on
 * `nav.is-vertical:has(.open-always)` — forced rail width (220), re-implemented
 * justifyContent with margin-auto, flex-wrap:nowrap, align-items:stretch, full-width
 * 56dp pills, etc. That OWNED layout that core/navigation already does (orientation,
 * flexWrap, justifyContent / alignment, container width, static nested layout, depth
 * indent) — lowering core dependency and FIGHTING the editor toolbar (align/justify did
 * two conflicting things on two layers). It also wrongly assumed open-always == a 220px
 * rail; an always-open vertical nav is a TREE / expanded-section candidate, not
 * necessarily an M3 rail, and deep nesting (depth 3-4 → label clamped to ~105px) is a
 * tree/flyout problem, not an in-place-rail one.
 *
 * So core now OWNS: orientation · flexWrap · justifyContent/alignment · container width ·
 * static nested layout · depth indent. The THEME keeps only a light skin here. The real
 * EXPANDED RAIL is DEFERRED to an explicit opt-in (a block style variation
 * `.is-style-expanded-rail`, or a template/sidebar context class like
 * `.ax-navigation-rail`), where a defined width + single-column + pill rows become
 * justified. (`:has(.open-always)` alone is not "a rail".) prose ul-indent leak removal
 * + link de-underline already live in §19 (un-gated). */
nav.wp-block-navigation.is-vertical {
  /* item ROW geometry. A vertical PARENT li wraps its nested subtree in-flow, but the
   * trigger row still needs the same 56dp / 16dp geometry as leaf rows. Put geometry on
   * the li; the first anchor/button and trailing span are slots inside it. State paint for
   * parent submenu rows stays on the trigger content to avoid tinting the whole subtree. */
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item),
  & .wp-block-loginout a {
    min-block-size: 56px;
    padding-inline: var(--space-md, 16px);
    border-radius: var(--md-sys-shape-corner-full);
  }
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item) {
    position: relative;
  }
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item)::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    block-size: 56px;
    border-radius: var(--md-sys-shape-corner-full);
    pointer-events: none;
    z-index: -1;
  }
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item):not(.wp-block-navigation-submenu)::before {
    block-size: 100%;
  }
  & .wp-block-loginout a:hover {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
  }
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item):hover::before {
    background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-hover-state-layer-opacity) * 100%), transparent);
  }
  /* current — a LEAF fills its li; a SUBMENU trigger fills itself; label → secondary. */
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item):has(> a[aria-current="page"])::before,
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item):has(> button[aria-expanded="true"])::before {
    background-color: var(--md-sys-color-secondary-container);
  }
  & a[aria-current="page"] {
    color: var(--md-sys-color-secondary);
  }
  /* The vertical nav/submenu row gap is `gap: var(--wp--style--block-gap)` (1.2rem ≈
   * 19.2px) — too large for a tight tree. Do NOT zero the variable: core also uses it
   * for the inline nested indent. Override only the BLOCK-axis gap and leave inline
   * spacing/indent core-owned. */
  & .wp-block-navigation__container {
    row-gap: 0 !important;
  }
  /* always-TREE submenu block-gap → 0; the floating click popover keeps the §19b 2dp gap. */
  &:has(.open-always) .wp-block-navigation__submenu-container {
    row-gap: 0 !important;
  }
  /* Keep core's inline depth, but remove padding on the opposite edge for nested submenu
   * rows. When the block is justified left, remove inline-end padding; when justified
   * right, remove inline-start padding. */
  &.is-content-justification-left .wp-block-navigation__submenu-container {
    padding-inline-end: 0;
  }
  &.is-content-justification-left .wp-block-navigation__submenu-container > .wp-block-navigation-item {
    padding-inline-end: 0;
  }
  &.is-content-justification-right .wp-block-navigation__submenu-container {
    padding-inline-start: 0;
  }
  &.is-content-justification-right .wp-block-navigation__submenu-container > .wp-block-navigation-item {
    padding-inline-start: 0;
  }
  & .wp-block-navigation-submenu {
    margin-block-start: 0;
  }
  /* TOP-LEVEL click trigger HIT AREA — make the WHOLE capsule the toggle. With
   * submenuVisibility:"click" core renders the label as a single <button> (the toggle) plus
   * a DECORATIVE sibling chevron `span.__submenu-icon`; the button is only label-width, so
   * the pill's inline padding + the chevron weren't clickable, and the chevron also carries
   * a ~-0.6em negative margin (overlap). Fix: move the row's inline padding off the li onto
   * the in-flow button (it sizes the row AND owns the padding = part of the click target),
   * reserve trailing room for the chevron, and float the chevron as a NON-INTERACTIVE
   * overlay at the trailing edge. A click anywhere on the capsule — chevron included — then
   * reaches the button. Scoped to top-level .open-on-click (nested popover triggers keep the
   * §19b Menu row; hover/always triggers keep their separate a + toggle-button). */
  & .wp-block-navigation__container > .open-on-click.wp-block-navigation-item {
    padding-inline: 0;
  }
  & .wp-block-navigation__container > .open-on-click > button.wp-block-navigation-item__content {
    inline-size: 100%;
    justify-content: flex-start;
    padding-inline-start: var(--space-md, 16px);
    padding-inline-end: calc(var(--space-md, 16px) + 24px);
  }
  & .wp-block-navigation__container > .open-on-click > .wp-block-navigation__submenu-icon {
    position: absolute;
    inset-block: 0;
    inset-inline-end: var(--space-md, 16px);
    margin: 0;
    display: flex;
    align-items: center;
    pointer-events: none;
  }
  /* focus ring stays on the focusable anchor / toggle button (a11y). */
  & .wp-block-navigation-item__content:focus-visible,
  & .wp-block-loginout a:focus-visible {
    outline: var(--md-sys-state-focus-indicator-thickness) solid var(--md-sys-color-secondary);
    outline-offset: var(--md-sys-state-focus-indicator-inner-offset);
  }
  /* supporting text (description) — core hides it; opt in. The anchor stacks label +
   * supporting text (flex-column, the only content layout the skin owns). body-small. */
  & a.wp-block-navigation-item__content:has(.wp-block-navigation-item__description) {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  & :is(.wp-block-navigation__container > .wp-block-navigation-item, .open-always .wp-block-navigation-item) > .wp-block-navigation-item__content {
    display: flex;
    align-items: center;
    flex-grow: 1;
    min-block-size: 56px;
    padding-inline: 0;
  }
  /* core/open-always sets nested item content back to flex-grow:0; for the 56dp vertical
   * row model, the first a/button slot should stretch across the row. */
  & .has-child.open-always .wp-block-navigation__submenu-container > .wp-block-navigation-item > .wp-block-navigation-item__content,
  & .has-child.open-always > .wp-block-navigation-item__content {
    flex-grow: 1 !important;
  }
  & .has-child.open-always > .wp-block-navigation-item__content {
    inline-size: 100%;
  }
  &.is-content-justification-left a.wp-block-navigation-item__content:has(.wp-block-navigation-item__description) {
    align-items: flex-start;
    text-align: start;
  }
  &.is-content-justification-right a.wp-block-navigation-item__content:has(.wp-block-navigation-item__description) {
    align-items: flex-end;
    text-align: end;
  }
  & .wp-block-navigation-item__description {
    display: block;
    margin-block-start: 2px;
    font-size:      var(--md-sys-typescale-body-small-size);
    line-height:    var(--md-sys-typescale-body-small-line-height);
    font-weight:    var(--md-sys-typescale-body-small-weight);
    letter-spacing: var(--md-sys-typescale-body-small-tracking);
    color: var(--md-sys-color-on-surface-variant);
  }
}

/* ============================================================================
 * §21 core/comments — comment display chrome (B contract P2, GLOBAL token binding)
 *
 * DIAGNOSTIC (THEME-VQA-ROUTE §11): the comments family renders in the SINGLE-POST
 * TEMPLATE (TT5 single.html → main.wp-block-group), OUTSIDE `.wp-block-post-content` —
 * the same template-context boundary found for the Query Loop. Comments are unambiguous
 * CHROME (never prose), so — like §19 navigation — they are bound by their own block
 * classes GLOBALLY (NOT post-content-scoped), which reaches the real TT5 template with no
 * prose-leak risk. TOKEN BINDING ONLY: typescale + role colour + de-prose links. Thread /
 * list LAYOUT stays core/TT5-owned. The comment FORM inputs (textarea/inputs surface +
 * radius) are DEFERRED to a form/input component lane; the submit button is already M3
 * (theme.json button element); here the form only gets label/helper typography + colour. */

/* comments section title → title-large / on-surface. The `.wp-block-comments` prefix keeps
 * this GLOBAL but lifts specificity to (0,2,0) so it wins over the prose `h2` (0,1,1) when a
 * comments block sits IN-CONTENT (e.g. the VQA page) — the title is chrome, never a prose
 * heading, in any context. */
.wp-block-comments .wp-block-comments-title {
  font-size:      var(--md-sys-typescale-title-large-size);
  line-height:    var(--md-sys-typescale-title-large-line-height);
  font-weight:    var(--md-sys-typescale-title-large-weight);
  letter-spacing: var(--md-sys-typescale-title-large-tracking);
  color: var(--md-sys-color-on-surface);
}

/* comment list = chrome, not a prose ol — drop default markers (thread INDENT stays core). */
.wp-block-comment-template {
  list-style: none;
}

/* comment avatar = M3 List leading avatar primitive (40dp, circular). M3 does not expose a
 * standalone Avatar component page; the stable contract comes from List leading media. Real
 * image avatars keep their pixels; initials/fallback colour, when present, uses the avatar
 * composition tokens. */
.wp-block-comments .wp-block-avatar,
.wp-block-comments .wp-block-avatar :is(img, .avatar, .wp-block-avatar__image) {
  inline-size: var(--comp-avatar-size);
  block-size: var(--comp-avatar-size);
  border-radius: var(--comp-avatar-radius);
}
.wp-block-comments .wp-block-avatar {
  flex-shrink: 0;
  overflow: hidden;
  background-color: var(--md-sys-color-primary-container);
  color: var(--md-sys-color-on-primary-container);
}
.wp-block-comments .wp-block-avatar :is(img, .avatar, .wp-block-avatar__image) {
  display: block;
  object-fit: cover;
}

/* comment META (author / date / reply / edit) → body-small / on-surface-variant */
.wp-block-comment-author-name,
.wp-block-comment-date,
.wp-block-comment-reply-link,
.wp-block-comment-edit-link {
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  font-weight:    var(--md-sys-typescale-body-small-weight);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}

/* comment body → body-medium / on-surface (inner paragraph bound too; no prose in template) */
.wp-block-comment-content,
.wp-block-comment-content :is(p, .wp-block-paragraph) {
  font-size:      var(--md-sys-typescale-body-medium-size);
  line-height:    var(--md-sys-typescale-body-medium-line-height);
  font-weight:    var(--md-sys-typescale-body-medium-weight);
  letter-spacing: var(--md-sys-typescale-body-medium-tracking);
  color: var(--md-sys-color-on-surface);
}

/* de-prose comment links — metadata/action links, no resting underline (hover/focus only).
 * The `.wp-block-comments` prefix lifts specificity to (0,2,1) to beat the prose always-
 * underline link rule `.wp-block-post-content a:not(.wp-element-button)` (0,2,1) on source
 * order (blocks.css loads after prose.css) when comments sit IN-CONTENT; in the template
 * context prose is absent and the rule applies globally anyway. */
.wp-block-comments :is(.wp-block-comment-author-name, .wp-block-comment-date, .wp-block-comment-reply-link, .wp-block-comment-edit-link) a {
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: none;
}
.wp-block-comments :is(.wp-block-comment-author-name, .wp-block-comment-date, .wp-block-comment-reply-link, .wp-block-comment-edit-link) a:hover,
.wp-block-comments :is(.wp-block-comment-author-name, .wp-block-comment-date, .wp-block-comment-reply-link, .wp-block-comment-edit-link) a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* comment FORM — label / helper typography + colour. The submit stays the M3 button element. */
.wp-block-post-comments-form :is(label, .comment-notes, .comment-form-cookies-consent label, .logged-in-as) {
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}
.wp-block-post-comments-form :is(.comment-notes, .logged-in-as) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-sm);
  margin-block: 0 var(--space-md);
}
.wp-block-post-comments-form :is(.comment-notes, .logged-in-as) a {
  font-size:      var(--md-sys-typescale-label-medium-size);
  line-height:    var(--md-sys-typescale-label-medium-line-height);
  font-weight:    var(--md-sys-typescale-label-medium-weight);
  letter-spacing: var(--md-sys-typescale-label-medium-tracking);
  color: var(--md-sys-color-primary);
  text-decoration: none;
}
.wp-block-post-comments-form :is(.comment-notes, .logged-in-as) a:hover,
.wp-block-post-comments-form :is(.comment-notes, .logged-in-as) a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
.wp-block-post-comments-form :is(.comment-notes, .logged-in-as) .required-field-message {
  flex-basis: 100%;
}

/* comment form INPUTS = M3 Text Field, OUTLINED — "text-field-LITE". Ontology: M3 §32 Text
 * Field (a first-class component; lab `.text-field`, style-guide #components-text-field). core
 * renders PLAIN <input>/<textarea> with NO `.text-field` structure, so this binds the OUTLINED
 * SURFACE tokens only — shape · outline · (no) fill · focus active-indicator · typography — on
 * the raw controls. The full component (floating label / __container / prefix-suffix slots /
 * supporting + counter / error icon) needs comment_form MARKUP ownership and is a form/plugin
 * lane (deferred). Also fixes core's hardcoded white field bg (broke dark mode). The cookies
 * consent CHECKBOX stays untouched (M3 selection control = form plugin territory). */
.comment-form :is(textarea, input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]) {
  box-sizing: border-box;
  inline-size: 100%;
  min-block-size: 56px;                                    /* M3 single-line text field height */
  padding: var(--space-md);                                /* 16dp; centres body-large in 56 */
  border: 0;
  border-radius: var(--md-sys-shape-corner-extra-small);   /* 4dp */
  background-color: transparent;                           /* outlined = no fill → dark-safe (was #fff) */
  /* M3 outlined ACTIVE INDICATOR = the full outline, as an inset shadow (lab spec): 1px
   * outline at rest → 1px on-surface on hover → 2px primary on focus. Shadow (not border-
   * width) so the field never reflows between states. */
  box-shadow: inset 0 0 0 1px var(--md-sys-color-outline);
  color: var(--md-sys-color-on-surface);
  font-size:      var(--md-sys-typescale-body-large-size);
  line-height:    var(--md-sys-typescale-body-large-line-height);
  font-weight:    var(--md-sys-typescale-body-large-weight);
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
}
.comment-form textarea {
  min-block-size: 96px;                                    /* multi-line: a few rows */
  resize: vertical;
}
.comment-form :is(textarea, input[type="text"], input[type="email"], input[type="url"])::placeholder {
  color: var(--md-sys-color-on-surface-variant);
}
.comment-form :is(textarea, input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]):hover {
  box-shadow: inset 0 0 0 1px var(--md-sys-color-on-surface);   /* lab hover: outline → on-surface */
}
/* focus = M3 outlined active indicator: 2px primary; global :focus-visible ring suppressed. */
.comment-form :is(textarea, input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]):focus,
.comment-form :is(textarea, input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="number"]):focus-visible {
  box-shadow: inset 0 0 0 2px var(--md-sys-color-primary);
  outline: none;
}
/* native validation (email/url, after interaction) → M3 error indicator (2px error) */
.comment-form :is(input[type="email"], input[type="url"]):user-invalid {
  box-shadow: inset 0 0 0 2px var(--md-sys-color-error);
}

/* comment consent CHECKBOX = M3 §13 Checkbox, "checkbox-lite" (lab .ax-checkbox visual). core
 * renders a bare native <input type="checkbox"> (no `.ax-checkbox__visual` structure), so bind
 * the M3 VISUAL via appearance:none on the input itself — 18dp box · 2dp corner · 2px
 * on-surface-variant outline (unselected); checked = primary fill + on-primary check (CSS
 * border, token-coloured → dark-safe); focus = 2px secondary. The full component (40dp
 * state-layer ripple, __visual/__check markup) needs structured markup = form/plugin lane. */
.comment-form input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  position: relative;
  inline-size: 18px;                                  /* M3 §13.1 container size */
  block-size: 18px;
  flex-shrink: 0;
  margin: 0 var(--space-sm) 0 0;
  border: 0;
  border-radius: 2px;                                 /* M3 §13.1 */
  background-color: transparent;
  /* outline as inset shadow (not border) so core's form border-color can't override it */
  box-shadow: inset 0 0 0 2px var(--md-sys-color-on-surface-variant);   /* M3 §13.2 unselected */
  vertical-align: text-bottom;
  cursor: pointer;
}
.comment-form input[type="checkbox"]:checked {
  background-color: var(--md-sys-color-primary);      /* selected = primary fill */
  box-shadow: inset 0 0 0 2px var(--md-sys-color-primary);
}
.comment-form input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  inset-block-start: 1px;
  inset-inline-start: 5px;
  inline-size: 4px;
  block-size: 9px;
  border: solid var(--md-sys-color-on-primary);       /* on-primary check, token → dark-safe */
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.comment-form input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--md-sys-color-secondary);   /* M3 §13 focus */
  outline-offset: 2px;
}

/* ============================================================================
 * §22 core/comment-template — FACEBOOK-style comment BUBBLE thread (THEME-VQA-ROUTE §11.1)
 *
 * Route correction: the Mastodon/Misskey NOTE model (flat meta-row → body → action-row) is too
 * rich for WP core comments — that layout belongs to a custom post type / ActivityPub OBJECT
 * renderer, not `core/comment-template`. WP comments map best to a FACEBOOK comment thread:
 * leading avatar + a rounded BUBBLE { author (top) + text } + a small action STRIP
 * (time · reply · edit) BELOW the bubble + a thread connector for nesting. STRUCTURE is owned by
 * the pattern (patterns/comments.php); CSS does the bubble surface + strip + rail (no stack
 * reorder — the Navigation trap). */
.omph-comment-item {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: var(--space-sm);                  /* avatar ↔ bubble (tight, FB) */
}
.omph-comment-item > .wp-block-avatar {
  flex-shrink: 0;                        /* leading avatar = fixed 40dp column */
}
.omph-comment-main {
  flex: 1 1 auto;
  min-inline-size: 0;                    /* let long content wrap, not overflow the row */
}

/* BUBBLE — a rounded surface hugging the author + comment text (FB comment bubble). */
.omph-comment-bubble {
  inline-size: fit-content;              /* hug content; grow to 100% for long text */
  max-inline-size: 100%;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--md-sys-shape-corner-large);
  background-color: var(--md-sys-color-surface-container);
}
.omph-comment-bubble > * { margin-block: 0; }
.omph-comment-bubble > .wp-block-comment-content { margin-block-start: 2px; }
.omph-comment-content > * { margin-block: 0; }                 /* the inner <p> too */
/* author = bold name at the TOP of the bubble (label-large) */
.omph-comment-bubble .wp-block-comment-author-name,
.omph-comment-bubble .wp-block-comment-author-name a {
  color: var(--md-sys-color-on-surface);
  font-size:      var(--md-sys-typescale-label-large-size);
  line-height:    var(--md-sys-typescale-label-large-line-height);
  font-weight:    var(--md-sys-typescale-label-large-weight);
}

/* action STRIP — below the bubble: date = body-small; Reply/Edit = label-medium action.
 * Indented to sit under the bubble text (matches the bubble's inline padding). */
.omph-comment-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  margin-block-start: var(--space-xs);
  padding-inline-start: var(--space-md);
}
.omph-comment-actions .wp-block-comment-date,
.omph-comment-actions .wp-block-comment-date a {
  font-size:      var(--md-sys-typescale-body-small-size);
  line-height:    var(--md-sys-typescale-body-small-line-height);
  font-weight:    var(--md-sys-typescale-body-small-weight);
  letter-spacing: var(--md-sys-typescale-body-small-tracking);
  color: var(--md-sys-color-on-surface-variant);
}
.omph-comment-actions :is(.wp-block-comment-reply-link, .wp-block-comment-edit-link),
.omph-comment-actions :is(.wp-block-comment-reply-link, .wp-block-comment-edit-link) a {
  font-size:      var(--md-sys-typescale-label-medium-size);
  line-height:    var(--md-sys-typescale-label-medium-line-height);
  font-weight:    var(--md-sys-typescale-label-medium-weight);
  letter-spacing: var(--md-sys-typescale-label-medium-tracking);
  color: var(--md-sys-color-on-surface-variant);
}
.omph-comment-actions :is(.wp-block-comment-reply-link, .wp-block-comment-edit-link) a:hover,
.omph-comment-actions :is(.wp-block-comment-reply-link, .wp-block-comment-edit-link) a:focus-visible {
  color: var(--md-sys-color-on-surface);
  text-decoration: underline;
}

/* thread indentation. The actual connector is drawn by
 * assets/scripts/comment-thread-connectors.js from measured avatar positions; CSS-only fixed
 * offsets drift as soon as a bubble wraps. */
.wp-block-comment-template ol {
  position: relative;
  list-style: none;
  margin-inline-start: 0;
  padding-inline-start: 32px;                          /* reply column indent (per level) */
}

.wp-block-comment-template {
  position: relative;
}
.omph-comment-connectors {
  position: absolute;
  inset: 0;
  z-index: 0;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  pointer-events: none;
}
.omph-comment-connector {
  fill: none;
  stroke: var(--md-sys-color-outline-variant);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 120ms ease, stroke-width 120ms ease;
}
.omph-comment-connector.is-reply-active {
  stroke: var(--md-sys-color-primary);
  stroke-width: 3;
}
.has-omph-comment-connectors .omph-comment-item {
  position: relative;
  z-index: 1;
}
.omph-comment-item.is-reply-target .wp-block-comment-reply-link a,
.omph-comment-item.is-reply-target .wp-block-comment-reply-link a:hover,
.omph-comment-item.is-reply-target .wp-block-comment-reply-link a:focus-visible {
  color: var(--md-sys-color-primary);
  text-decoration: none;
}
.wp-block-comment-template ol > li { position: relative; }

/* item density — a gap between sibling comments at every level. */
.wp-block-comment-template li + li {
  margin-block-start: var(--space-md);
}
/* Parent → first reply otherwise reads too tight, while sibling branches already have enough
 * separation from `li + li`. Add only a small parent→first-child step so branches keep their
 * current rhythm. */
.wp-block-comment-template ol > li:first-child {
  margin-block-start: var(--space-sm);
}
