/* ============================================================================
   Grosso University — site layer
   Components built on theme/tokens/*. Nothing here hardcodes a color: every
   value resolves from the [data-surface] the component sits in, so a section
   can be moved between plates without touching its CSS.
   ============================================================================ */

/* ── Page frame ─────────────────────────────────────────────────────────── */
body { min-height: 100dvh; display: flex; flex-direction: column; }
#main { flex: 1; }

/* CRITICAL — THE min-width:0 RULE — the single most load-bearing line in this file.
   A grid or flex CHILD defaults to `min-width: auto`, which refuses to shrink
   below its own min-content width. So a one-column grid does not produce a
   one-column-wide track: it produces a track as wide as the longest unbreakable
   thing inside it, and the page then scrolls sideways.
   Measured on the home hero at 360px: container 326px, computed
   `grid-template-columns: 390.203px` — the min-content of the word
   "PROFESSIONALS" set at --gu-text-5xl. 47px of spill from one word.
   `overflow-wrap: break-word` does NOT fix this; it permits a break but does
   not reduce min-content size (that is `anywhere`, which collapses tracks).
   Every layout component's children are listed here. Add new ones to the list. */
:where(.gu-split, .gu-grid, .gu-btn-row, .gu-checklist, .gu-header__inner,
       .gu-footer__cols, .gu-nav__list, .gu-crumbs ol, .gu-social,
       .gu-drawer__inner, .gu-band__inner, .gu-card, .gu-sitemap,
       /* S7/S8. Added after the sweep caught 81px of spill at 320px on the
          countdown band: an <h2> and two leads refusing to shrink inside a
          grid whose children were still min-width:auto. Every one of these is
          a grid or flex parent introduced with the players, logo walls, stats,
          countdowns and the lead form. */
       .gu-countdown-band, .gu-countdown__clock, .gu-countdown__details,
       .gu-countdown__head, .gu-countdown__side,
       .gu-hero__copy--center,
       .gu-stats, .gu-logos, .gu-video__titles, .gu-video__titles li,
       .gu-form, .gu-form__actions, .gu-field,
       /* /about/ rebuild (JQ-BJ66CM19): the team profile card is a flex
          column and its links row is a flex row; the hero aside wraps the
          logo wall inside the hero's gu-split. */
       .gu-profile, .gu-profile__links, .gu-hero__aside,
       /* The video pull-quote band (JQ-E5F6DHXH / JQ-EK2N4SSS): the outer
          stack, each two-column row, and its copy column are all grids. */
       .gu-vidquotes, .gu-vidquote, .gu-vidquote__copy,
       /* JQ-891F0JD6: the /insights/ feed's filter strip and card meta row are
          both flex parents. */
       .gu-feedtabs, .gu-card__meta,
       /* The /answers/ FAQ feed: the search controls and pagination that
          app.js creates are flex parents, and so is every .gu-faq summary
          row (a long question must wrap, not widen the band). */
       .gu-faqfeed__controls, .gu-faqfeed__nav, .gu-faq summary,
       /* Partner bands on /partners/ (JQ-K6YAJE0W) — a grid parent and a flex
          parent, both new, both listed here per the rule above. */
       .gu-partner, .gu-partner__info,
       /* JQ-53TVB3Z2: the reviews slider and archive (trap 19 — every new
          grid/flex parent joins this list the day it is written). */
       .gu-slider__track, .gu-slider__nav, .gu-reviews__meta,
       .gu-reviews__staronly-list, .gu-reviews__staronly-list li,
       /* JQ-SBMJR4F0: the multi-step lead form. .gu-wizard is a grid (stepper
          + form), .gu-wizard__steps a flex row of step dots, and each
          .gu-form__step fieldset is now the grid the whole .gu-form used to
          be — the field grid moved DOWN a level when the fields split across
          steps, so it joins this list in the fieldset's place, not the
          form's. .gu-form__stepnav is the Back/Next flex row. */
       .gu-wizard, .gu-wizard__steps, .gu-form__step, .gu-form__stepnav) > * { min-width: 0; }

/* Every section is a plate. `container-type: inline-size` makes its children
   size to the section, not the viewport — the reason this site needs no
   component-level media queries. */
.gu-band {
  position: relative;
  padding-block: var(--gu-section-y);
  container-type: inline-size;
  container-name: band;
}
.gu-band__inner {
  position: relative;
  z-index: 1;                       /* above the canvas backdrop */
  max-width: var(--gu-max-width);
  margin-inline: auto;
  padding-inline: var(--gu-gutter);
}
.gu-band--tight { padding-block: calc(var(--gu-section-y) * 0.55); }

/* QA round 3 / section audit: `gu-band--titleonly` is on 423 sections and had
   NO CSS RULE AT ALL, so every one of them silently fell through to the full
   section rhythm. Measured across the live site: a title-only band landed at
   83.2px padding with a 56px heading, while `--tight` — the same role, the
   other implementation — gave 45.76px and 36.7px. Two spellings of one idea,
   one of which does nothing, is exactly how a site stops looking like itself.

   A band that carries only a title is a LABEL for what follows, not a section
   in its own right, so it takes the tight rhythm and drops the head's trailing
   margin — there is nothing under it to be separated from.
   SECTION-SPEC.md §3 permits exactly two rhythm variants; this is the second
   spelling of one of them, kept because 423 sections use the name. */
.gu-band--titleonly { padding-block: calc(var(--gu-section-y) * 0.55); }
.gu-band--titleonly .gu-section__head { margin-bottom: 0; }

/* A title band and the content it introduces are ONE unit, so they must not
   pay two full rhythms into the seam between them. The section audit reported
   this as "6 of 8 index pages stack two ink bands back-to-back, erasing the
   title/content seam" — and the screenshot showed what that actually means:
   about 150px of dead space between the "PARTNERS" heading and the first thing
   under it, because the title band's bottom padding and the content band's top
   padding compound.
   The seam is not missing; there is simply too much of it. The following band
   drops its top padding and the title band's own bottom rhythm carries the
   gap. SECTION-SPEC.md S1 covers the surface repeat; this covers the space. */
.gu-band--titleonly + .gu-band,
.gu-band--tight + .gu-band { padding-block-start: 0; }

/* `gu-band--form` is on 123 sections. It used to clamp .gu-band__inner to
   62rem here, on the theory that the lead form's measure needed holding —
   but that left the ONE section on the page with a narrower inner than every
   other band (JQ-SBMJR4F0: "fit the same width as the other sections"),
   visibly inconsistent against the shared 78rem --gu-max-width every other
   .gu-band__inner uses. The multi-step wizard below (.gu-wizard) now holds
   its own readable measure via its two-column layout at wide containers, so
   the band itself no longer needs a special case and drops back to the
   shared rule in .gu-band__inner. */

/* ── Canvas backdrop ────────────────────────────────────────────────────────
   A decorative layer only. It is `aria-hidden` and carries no information: if
   the canvas fails to create a context, or JS never runs, the band still has
   its surface background and every word is still in the DOM. */
.gu-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  pointer-events: none;
}

/* ── Header ─────────────────────────────────────────────────────────────── */
.gu-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--gu-bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--gu-rule);
}
.gu-header__inner {
  max-width: var(--gu-max-width);
  margin-inline: auto;
  padding: var(--gu-space-2xs) var(--gu-gutter);
  display: flex;
  align-items: center;
  gap: var(--gu-space-md);
  min-height: 64px;
}
.gu-header__logo { flex: none; width: clamp(120px, 18vw, 190px); }
.gu-header__logo a { display: block; }
.gu-header__spacer { flex: 1; }

/* At 320px the bar is logo + CTA + hamburger inside a 288px content box, and
   the hamburger ended up 1px past the edge. The CTA is dropped below 480px
   rather than shrunk — it is the first item in the drawer, so nothing is lost,
   and a squeezed 8px-padding button would be a worse touch target than none.
   WARNING: Selector is `.gu-btn.gu-header__cta`, not `.gu-header__cta`. The element
   carries BOTH classes, and `.gu-btn { display: inline-flex }` is declared later
   in this file — at equal specificity the later rule wins and the CTA stayed
   visible, wrapping onto two lines at 390px. This is the second time source
   order silently beat a media query here (see the header toggle above); the
   answer is specificity, which does not depend on where the rule sits. */
@media (max-width: 479px) {
  .gu-btn.gu-header__cta { display: none; }
  .gu-header__inner { gap: var(--gu-space-2xs); }
}

.gu-nav { display: none; }
.gu-nav__list { display: flex; gap: var(--gu-space-md); list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.gu-nav__link {
  font-family: var(--gu-font-display);
  font-size: var(--gu-text-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-decoration: none;
  color: var(--gu-fg);
  white-space: nowrap;
}
.gu-nav__link:hover, .gu-nav__link[aria-current="page"] { color: var(--gu-accent); }

.gu-header__toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;                 /* 44px = the minimum touch target */
  background: none; border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-sm);
  color: var(--gu-fg); cursor: pointer; flex: none;
}
/* The toggle ships `hidden` until app.js wires the drawer, and the base rule's
   author `display` beats the UA's [hidden] — without this, JS-off mobile
   visitors see a dead button. Same pattern as `.gu-countdown[hidden]`. */
.gu-header__toggle[hidden] { display: none; }

/* WARNING: This media query MUST come after `.gu-header__toggle`'s base rule. Same
   specificity means source order decides, and with the query written first the
   base `display: inline-flex` won — the hamburger sat next to the full desktop
   nav at 1440px. A media query does not add specificity. */
@media (min-width: 1080px) {
  .gu-nav { display: block; }
  .gu-header__toggle { display: none; }
}
.gu-header__toggle .gu-icon { width: 24px; height: 24px; }

/* The mobile drawer is a <dialog>: focus trap, Esc, and inert background all
   come free from the platform rather than from hand-written JS. */
.gu-drawer {
  border: 0; padding: 0; max-width: min(420px, 92vw); width: 100%;
  max-height: 100dvh; height: 100dvh; margin: 0 0 0 auto;
  background: var(--gu-bg); color: var(--gu-fg);
}
.gu-drawer::backdrop { background: rgb(0 0 0 / 0.6); }
/* Inertness is not a scroll lock — app.js sets this while the drawer is open,
   because a wheel gesture over the backdrop otherwise scrolls the page behind it.
   No z-index here on purpose: a modal <dialog> paints in the TOP LAYER, which
   outranks every stacking context, so `z-index: auto` on it is correct. */
.gu-scroll-lock { overflow: hidden; }
.gu-drawer__inner { padding: var(--gu-space-md); display: grid; gap: var(--gu-space-sm); }
.gu-drawer__head { display: flex; align-items: center; justify-content: space-between; }
.gu-drawer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-2xs); }
.gu-drawer__list li.is-sub a { padding-left: var(--gu-space-sm); font-size: var(--gu-text-sm); color: var(--gu-fg-muted); }
.gu-drawer__list a {
  display: block; padding: var(--gu-space-2xs) 0;
  font-family: var(--gu-font-display); text-transform: uppercase;
  letter-spacing: 0.06em; font-size: var(--gu-text-base); text-decoration: none;
  color: var(--gu-fg); border-bottom: 1px solid var(--gu-rule);
}
.gu-drawer__list a:hover { color: var(--gu-accent); }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.gu-btn {
  display: inline-flex; align-items: center; gap: var(--gu-space-2xs);
  font-family: var(--gu-font-display); font-weight: 700; font-size: var(--gu-text-sm);
  text-transform: uppercase; letter-spacing: 0.1em; text-decoration: none;
  padding: 0.85em 1.6em; border: 2px solid transparent;
  border-radius: var(--gu-radius-sm); cursor: pointer; text-align: center;
  transition: background-color var(--gu-dur-fast) var(--gu-ease),
              color var(--gu-dur-fast) var(--gu-ease),
              border-color var(--gu-dur-fast) var(--gu-ease),
              transform var(--gu-dur-fast) var(--gu-ease);
}
.gu-btn:hover { transform: translateY(-1px); text-decoration: none; }
.gu-btn--primary { background: var(--gu-accent); color: var(--gu-bg); border-color: var(--gu-accent); }
.gu-btn--primary:hover { background: var(--gu-fg-strong); border-color: var(--gu-fg-strong); color: var(--gu-bg); }
.gu-btn--ghost { border-color: currentColor; color: var(--gu-fg); }
.gu-btn--ghost:hover { background: var(--gu-fg); color: var(--gu-bg); }
.gu-btn .gu-icon { width: 1.1em; height: 1.1em; flex: none; }

.gu-btn-row { display: flex; flex-wrap: wrap; gap: var(--gu-space-xs); margin-top: var(--gu-space-md); }

/* ── Icons ──────────────────────────────────────────────────────────────── */
.gu-icon {
  width: 1.25em; height: 1.25em;
  fill: none; stroke: currentColor;         /* stroke icons; filled ones override */
  vertical-align: -0.15em; flex: none;
}

/* ── Type helpers ───────────────────────────────────────────────────────── */
.gu-eyebrow {
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.18em;
  color: var(--gu-accent); margin: 0 0 var(--gu-space-xs);
}
.gu-lede { font-size: var(--gu-text-lg); color: var(--gu-fg-muted); max-width: var(--gu-measure); }
.gu-prose > * + * { margin-top: var(--gu-space-sm); }
/* WARNING: `:is(h2…)` not `:where(h2…)`. Trap 5 in CLAUDE.md: this and the rule above
   it both compute to (0,1,0) with :where, so the heading spacing only won by
   being written second. `:is()` takes its most specific argument's weight, so
   the type selector inside lifts this to (0,1,1) and it wins on merit.
   h5/h6 joined the list when render.mjs began emitting restored subheads, which
   step down a level for each level the source stepped down. */
.gu-prose :is(h2, h3, h4, h5, h6) { margin-top: var(--gu-space-lg); }
/* A restored subhead is capped at the body measure rather than the band width,
   so it wraps on the same line length as the paragraph it introduces. `ch` is
   font-size-relative, so a larger heading still gets a proportionally wider box
   and this is a cap, never a stretch. */
.gu-prose__head { max-width: var(--gu-measure); }

/* ── Hero ───────────────────────────────────────────────────────────────── */
.gu-hero { padding-block: clamp(3.5rem, 2rem + 7vw, 9rem); }

/* CRITICAL: The hero copy column is its OWN container, and the title is sized in `cqi`
   against it.
   Two bugs led here, in order:
   1. `--gu-text-5xl` is viewport-relative, so in a two-column split it sized to
      a 1440px WINDOW while living in a ~550px COLUMN — 88px type, hard-breaking
      into "IMPROVEME / NT" and "PROFESSION / ALS".
   2. Switching to `cqi` was not enough on its own: the nearest container was
      `.gu-band`, which is the FULL-WIDTH section. `cqi` still measured 1440px
      and still overflowed the column.
   Declaring the copy column a container makes `cqi` mean what it looks like it
   means. 9cqi resolves to ~49px in a 550px column and ~29px in a 326px phone
   column — both fit the longest word in this site's headings. */
.gu-hero__copy {
  container-type: inline-size;
  container-name: herocopy;
}
.gu-hero__title {
  font-size: clamp(1.75rem, 9cqi, 3.5rem);
  margin-bottom: var(--gu-space-sm);
}
.gu-hero__sub { font-size: var(--gu-text-lg); color: var(--gu-fg-muted); max-width: 58ch; }
.gu-hero .gu-figure img { max-height: 70vh; object-fit: cover; }

/* ── Video hero (home) ──────────────────────────────────────────────────────
   The <video> is decoration: aria-hidden, poster-backed, and the scrim is what
   guarantees the copy stays legible — the video's own frames are unmeasurable,
   so legibility must not depend on them. The scrim darkens toward `midnight`,
   the band's declared surface, so the token pairings the audit measures are
   the ones a visitor actually reads against. */
.gu-hero--video {
  position: relative;
  overflow: hidden;
  display: grid;
  align-items: center;
  min-height: min(88vh, 56rem);
}
.gu-hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.gu-hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgb(9 14 26 / 0.72) 0%, rgb(9 14 26 / 0.55) 45%, rgb(9 14 26 / 0.78) 100%);
}
/* The section is a grid, which makes the inner a GRID ITEM — and a grid item
   with auto inline margins shrink-to-fits instead of stretching. Its only
   child is the copy column, whose `container-type: inline-size` makes its
   max-content contribution ZERO, so the inner collapsed to its own padding
   (88px) and every line inside wrapped at one word. An explicit width defeats
   shrink-to-fit; max-width still caps it and the auto margins still center. */
.gu-hero--video .gu-band__inner { position: relative; width: 100%; }
.gu-hero__copy--center {
  margin-inline: auto;
  max-width: 64ch;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.gu-hero__copy--center .gu-hero__sub { margin-inline: auto; }
/* ── Cross-document view transitions (ADR-001 compat row 1) ──────────────
   MPA page-to-page cross-fade. Chrome 126+ / Safari 18.2+; Firefox has no
   @view-transition (bugzil.la/1860854) and drops the unknown at-rule per the
   CSS error-handling spec — plain navigation, zero errors, same content.
   Guarded by @supports per the ADR, and by reduced-motion: a visitor who
   asked for less motion navigates with no transition at all. */
@supports (view-transition-name: none) {
  @media (prefers-reduced-motion: no-preference) {
    @view-transition { navigation: auto; }
    ::view-transition-old(root),
    ::view-transition-new(root) { animation-duration: 160ms; }
  }
}

/* A visitor who asked for less motion gets the poster, not a playing loop.
   The section keeps the poster as a background so the frame is never blank. */
@media (prefers-reduced-motion: reduce) {
  .gu-hero__video { display: none; }
  .gu-hero--video {
    background-image: url("/assets/video/home-hero-poster.jpg");
    background-size: cover;
    background-position: center;
  }
}

/* ── Sections ───────────────────────────────────────────────────────────── */
/* CRITICAL — the measure belongs to the PROSE, not to the head.
   --gu-measure is 66ch, documented in tokens.css as "the line length body copy
   is capped at". It was applied to the whole .gu-section__head, so a display
   title inherited a body-copy measure: at a 1920 viewport the head was 660px
   wide inside a 1248px band, and "Latest Training Videos" wrapped to two lines
   with 588px of empty room beside it. Measured across dist: 374 section heads,
   243 of them (65%) carry NO paragraph at all, so two thirds of the site was
   paying a prose constraint for prose that is not there.
   jadeqa JQ-FY5ZT1DS reported exactly this ("extend this so it is not broken
   into two lines") and it was rejected with no reason recorded. It is real.
   The cap now sits on .gu-section__lede, which is the body copy it was written
   for; the title takes the band's own width and wraps only when it must. */
.gu-section__head { margin-bottom: var(--gu-space-lg); }
.gu-section__lede { max-width: var(--gu-measure); }
.gu-section__title { font-size: var(--gu-text-3xl); }

/* ── Cards ──────────────────────────────────────────────────────────────── */
.gu-card__date {
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--gu-accent); margin-bottom: var(--gu-space-3xs);
}
/* Upcoming-event cards on the events index (JQ-NY6HJK5S): venue line under the
   title, and a View Details affordance pinned to the card's bottom edge. The
   card is one <a>, so the affordance is a styled span, not a nested link. */
.gu-card__loc {
  font-size: var(--gu-text-sm); color: var(--gu-fg-muted);
  margin-bottom: var(--gu-space-2xs);
}
.gu-card__cta {
  /* auto, not a fixed gap: .gu-card is a flex column, so this pushes the CTA to
     the BOTTOM of the card and every CTA in a row lines up regardless of how
     many lines its title wrapped to. With a fixed margin the links stagger --
     visible on the home page's video row (titles wrap to 2, 4 and 5 lines) and
     on the team row, and no gate can see it: staggered links are not an error,
     an overflow or a failed request. */
  margin-top: auto;
  padding-top: var(--gu-space-sm);
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--gu-accent);
}
.gu-grid { display: grid; gap: var(--gu-space-md); }
/* CRITICAL — the number in the name is a CAP, and it was not being honored.
   `repeat(auto-fit, minmax(200px, 1fr))` packs as many tracks as fit, so at
   1920 .gu-grid--4 laid out FIVE columns: the team band asked for "2 rows of 4"
   and rendered 5 + 3, and every gate stayed green because a class named --4
   rendering 5 is not an error, an overflow or a failed request. ADR-008's
   silent-name class again.
   The floor is now the LARGER of the readable minimum and an Nth of the row, so
   at most N tracks can ever fit, while narrow viewports still fall back to
   fewer columns exactly as before. `--gu-space-md` is .gu-grid's gap, and
   (N-1) of them sit between N tracks. */
.gu-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(320px, calc((100% - 1 * var(--gu-space-md)) / 2))), 1fr)); }
.gu-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(260px, calc((100% - 2 * var(--gu-space-md)) / 3))), 1fr)); }
.gu-grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, max(200px, calc((100% - 3 * var(--gu-space-md)) / 4))), 1fr)); }
.gu-grid > * { min-width: 0; }               /* the no-spill rule, per track */

.gu-card {
  display: flex; flex-direction: column;
  padding: var(--gu-space-md);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-md);
  transition: border-color var(--gu-dur-fast) var(--gu-ease),
              transform var(--gu-dur-fast) var(--gu-ease);
}
.gu-card:hover { border-color: var(--gu-accent); transform: translateY(-2px); }
.gu-card__title { font-size: var(--gu-text-xl); margin-bottom: var(--gu-space-2xs); }
.gu-card__body { color: var(--gu-fg-muted); flex: 1; }
.gu-card__media { margin: calc(var(--gu-space-md) * -1) calc(var(--gu-space-md) * -1) var(--gu-space-sm); }
.gu-card__media img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
                      border-radius: var(--gu-radius-md) var(--gu-radius-md) 0 0; }
/* QA round 2, F2: "the logos are way too big for the space, all of them are cut
   off by the borders." Third instance of the same defect as the portrait crop
   below — `.gu-card__media`'s 16:9 `cover` scales art to FILL the box and
   crops the overflow, which is right for a photograph and destructive for a
   wordmark. Partner logos are wide and short (measured on the live grid:
   Paradigm Vendo 230x43 = 5.3:1, GreenSky 2395x631 = 3.8:1) inside a 341x192
   = 1.78:1 box, so `cover` cropped the ends off every one — "GreenSky"
   rendered as "eenSk" and "Skeps" as "kep".
   `contain` fits the whole mark; the padding keeps it off the card edges, and
   these are mixed-provenance assets (png/svg/webp, light and dark artwork), so
   a logo must never be scaled past its natural size either. */
.gu-card--partners .gu-card__media img {
  object-fit: contain;
  padding: var(--gu-space-sm);
  background: var(--gu-bg-raised);
}

.gu-card--link { text-decoration: none; color: inherit; }
.gu-card--link:hover .gu-card__title { color: var(--gu-accent); }
.gu-card--link:focus-visible { outline: 2px solid var(--gu-accent); outline-offset: 3px; }

/* ── Icon-led cards (JQ-SF1ZGKVJ, the academia band) ─────────────────────────
   The one card grid on the site that leads with a symbol instead of a photo.
   `.gu-card__icon` is a fixed square badge — sized in px, not `em`, so it
   never inherits the surrounding text's font-size the way an inline `.gu-icon`
   does elsewhere — with the accent-tinted circle every icon in this set sits
   on. `.gu-card--icon` widens the CTA link to hold the trailing arrow icon
   without wrapping the label to its own line. */
.gu-card--icon .gu-card__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem; margin-bottom: var(--gu-space-sm);
  border-radius: var(--gu-radius-pill);
  background: color-mix(in srgb, var(--gu-accent) 14%, transparent);
  color: var(--gu-accent);
}
.gu-card--icon .gu-card__icon .gu-icon { width: 1.5rem; height: 1.5rem; }
.gu-card--icon .gu-card__cta {
  display: inline-flex; align-items: center; gap: var(--gu-space-3xs);
}
.gu-card--icon .gu-card__cta .gu-icon { width: 0.85em; height: 0.85em; }

/* ── Child listing ──────────────────────────────────────────────────────────
   The grid an index page uses to LINK its children. It is the same .gu-card as
   everywhere else; what it adds is an even card height, so a listing of 65
   webinars does not read as a ragged wall.
   WARNING: The excerpt is CLAMPED, never truncated. The full text stays in the DOM —
   clamping is a display decision and a build-time cut would be a content one,
   and these excerpts are already the legacy site's own truncations. */
.gu-cardlist { align-items: stretch; margin-bottom: var(--gu-space-xl); }
.gu-cardlist:last-child { margin-bottom: 0; }
.gu-cardlist .gu-card__title { font-size: var(--gu-text-lg); }
.gu-cardlist .gu-card__body { font-size: var(--gu-text-sm); margin-top: var(--gu-space-2xs); }
.gu-cardlist .gu-card__body p {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
}

/* JQ-0BV990GX: "In-Home Sales Training Resources" — the home page's blog
   cards carry long WordPress headlines (some 100+ characters) that, unclamped,
   pushed the description and CTA off the bottom of a mixed-height row. The
   body copy above already clamps to 3 lines; the title gets the same
   treatment so every card in the row reads as "featured image, short title,
   description, Read more" rather than "title" alone. */
.gu-card--resource .gu-card__title {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
/* JQ-MRY2T6F9: "make it 2 rows of 4 partners." Dropped a hand-rolled
   `.gu-cardlist--partners` container-query ladder here — as of e574fec
   `.gu-grid--4` itself now CAPS at 4 tracks (the floor is the larger of the
   readable minimum and an Nth of the row), so eight partner cards already
   lay out 4-then-4 with no extra class. Re-adding a fixed column override
   would just be a second, redundant grid system fighting the first. */

/* ── Team profiles (/about/ rebuild, JQ-BJ66CM19) ───────────────────────────
   One card per person: portrait, linked name, role, then "Read Bio" and
   LinkedIn on one row. The portraits are the legacy page's own team photos
   (600x800 and friends), so the media well uses a PORTRAIT ratio — reusing
   .gu-card__media's 16:9 crop would take everyone's forehead off. */
.gu-profile__media { margin: calc(var(--gu-space-md) * -1) calc(var(--gu-space-md) * -1) var(--gu-space-sm); }
.gu-profile__media img { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; object-position: top;
                         border-radius: var(--gu-radius-md) var(--gu-radius-md) 0 0; }

/* QA round 2, B6: the /team/ INDEX renders through the generic childCard, so
   its portraits kept `.gu-card__media`'s 16:9 centred crop and went on cutting
   heads off after round 1 fixed the home page. Same geometry as the instructor
   band below, for the same reason and on the same photographs. */
.gu-card--person .gu-card__media img { aspect-ratio: 4 / 5; object-position: 50% 20%; }

/* QA round 1, B7: the home page's instructor cards were rendering portraits
   through `.gu-card__media` — a 16:9 CENTERED crop — and cut people's heads
   off, exactly as the note above predicts. They get the portrait treatment
   instead. `50% 20%` rather than `top`: several of these photographs are
   square, so anchoring at y=0 lands the crop above the hairline and takes the
   chin instead. Measured against all eight. */
.gu-instructor__media img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover;
                            object-position: 50% 20%; display: block;
                            border-radius: var(--gu-radius-md) var(--gu-radius-md) 0 0; }
.gu-profile__namelink { text-decoration: none; color: inherit; }
.gu-profile__namelink:hover { color: var(--gu-accent); }
.gu-profile__role { color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-profile__links {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gu-space-sm); margin-top: auto; padding-top: var(--gu-space-sm);
}
.gu-profile__bio {
  display: inline-flex; align-items: center; gap: 0.35em;
  color: var(--gu-accent); text-decoration: none;
  font-family: var(--gu-font-display); font-weight: 600; font-size: var(--gu-text-sm);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.gu-profile__bio:hover { text-decoration: underline; }
.gu-profile__bio .gu-icon { width: 0.9em; height: 0.9em; }
.gu-profile__linkedin {
  display: inline-flex; width: 36px; height: 36px; flex: none;
  align-items: center; justify-content: center;
  border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-sm); color: inherit;
}
.gu-profile__linkedin:hover { border-color: var(--gu-accent); color: var(--gu-accent); }
.gu-profile__linkedin .gu-icon { width: 18px; height: 18px; fill: currentColor; stroke: none; }

/* The hero's aside on /about/: the legacy hero's association marks, on the
   same measured logo-wall tiles the partner pages use — but laid out as a ROW
   of tiles (the bare .gu-logos ul has no layout rule of its own; a vertical
   stack of three 92px plates would be taller than the hero copy). */
.gu-hero__aside .gu-logos {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--gu-space-sm);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 110px), 1fr));
}

/* The self-hosted facade's face is the legacy poster, not the gradient plate.
   `.gu-embed > *` already makes it absolute inset:0 100%; object-fit stops the
   2.44:1 legacy thumbnail from squashing into the 16:9 frame. */
.gu-video__poster { object-fit: cover; }
/* ── /insights/ feed (JQ-891F0JD6) ──────────────────────────────────────────
   The Learning Center card feed: one grid of every article, podcast and
   webinar, behind a client-side filter strip. The strip is progressive
   enhancement — buttons are inert without JS and every card renders visible.
   WARNING — filtering toggles a CLASS, not the `hidden` attribute: .gu-card is
   `display: flex`, which outranks the UA's `[hidden] { display: none }`. */
.gu-section__lede { margin-top: var(--gu-space-xs); color: var(--gu-fg-muted); }
.gu-feedtabs {
  display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs);
  margin-bottom: var(--gu-space-lg);
}
.gu-feedtab {
  font: inherit; font-size: var(--gu-text-sm); font-weight: 600;
  color: var(--gu-fg-muted);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule);
  border-radius: 999px;
  padding: var(--gu-space-2xs) var(--gu-space-md);
  cursor: pointer;
  transition: border-color var(--gu-dur-fast) var(--gu-ease),
              color var(--gu-dur-fast) var(--gu-ease);
}
.gu-feedtab:hover { border-color: var(--gu-accent); color: var(--gu-fg); }
.gu-feedtab:focus-visible { outline: 2px solid var(--gu-accent); outline-offset: 3px; }
.gu-feedtab[aria-pressed="true"] { border-color: var(--gu-accent); color: var(--gu-fg); }
.gu-card__meta {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--gu-space-sm);
  margin-top: var(--gu-space-sm);
  font-size: var(--gu-text-sm); color: var(--gu-fg-muted);
}
.gu-card__cat { font-weight: 600; }
.gu-feed .gu-card.is-filtered { display: none; }

/* ── HTML sitemap ───────────────────────────────────────────────────────────
   A flat link list per namespace, in as many columns as the band can hold.
   WARNING — COLUMNS, not a grid. Grid rows are as tall as their tallest cell, so a
   2-link "Site" group beside a 24-link "Insights" group left a screen and a
   half of empty space under it. Multi-column flows the groups instead and
   balances them; `break-inside: avoid` keeps a group's links with its heading. */
.gu-sitemap { columns: 3 260px; column-gap: var(--gu-space-lg); }
.gu-sitemap__group { break-inside: avoid; margin-bottom: var(--gu-space-lg); }
.gu-sitemap .gu-section__title { font-size: var(--gu-text-xl); margin-bottom: var(--gu-space-xs); }
.gu-linklist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-3xs); }
.gu-linklist > * { min-width: 0; }
.gu-linklist a { color: var(--gu-fg-muted); text-decoration: none; font-size: var(--gu-text-sm); }
.gu-linklist a:hover { color: var(--gu-accent); text-decoration: underline; }
/* Band-sized variant — the Academia de Ventas listing: full-color links in a
   responsive column grid, arrow icon aligned to the text. */
.gu-linklist--band { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: var(--gu-space-2xs); }
.gu-linklist--band a { display: inline-flex; align-items: center; gap: var(--gu-space-3xs); color: var(--gu-fg); font-size: var(--gu-text-base); font-weight: 600; }
.gu-linklist--band .gu-icon { width: 16px; height: 16px; flex: none; }
.gu-linklist--band .gu-nap__mode { display: inline-flex; align-items: center; gap: var(--gu-space-3xs); color: var(--gu-fg); font-size: var(--gu-text-base); font-weight: 600; }

/* ── Review stars (QA round 1, F2) ──────────────────────────────────────── */
.gu-stars { margin: 0 0 var(--gu-space-3xs); line-height: 1; letter-spacing: 2px; }
.gu-star { color: var(--gu-accent); font-size: var(--gu-text-lg); }
/* An unfilled star must still be VISIBLE — an invisible one turns a 3-star
   review into a three-star-wide gap and reads as a 5. */
.gu-star--off { color: var(--gu-fg-muted); }

/* ── Feed detail imagery (QA round 1, G2) ───────────────────────────────── */
.gu-detail__figure { margin: 0 0 var(--gu-space-md); }
.gu-detail__figure img { width: 100%; height: auto; display: block; border-radius: var(--gu-radius-md); }
.gu-detail__gallery {
  display: grid; gap: var(--gu-space-2xs); margin-top: var(--gu-space-md);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
}
.gu-detail__gallery img { width: 100%; height: auto; display: block; border-radius: var(--gu-radius-sm); }

/* ── Instant search on feed indexes (board item 18) ─────────────────────── */
/* The header's search disclosure (QA round 3, R3A1). A native <details>, so it
   opens with no JavaScript and needs no script in the header at all — the same
   progressive-enhancement floor the nav uses. */
/* The popover anchors to the header's content column, not to its own 44px
   button. A 480px panel right-aligned to a 44px button at x=438 runs to x=-42
   on a 1440 screen, and a 328px one right-aligned to a button at x=185 on a
   360 screen runs off the other edge — a popover positioned against a button
   escapes the viewport at SOME width, always. .gu-header__inner is the column
   the whole header is laid out in, so anchoring here fits by construction. */
.gu-header__inner { position: relative; }
.gu-header__search { position: static; }
.gu-header__search > summary {
  display: grid; place-items: center;
  width: 44px; height: 44px;              /* a real 44px touch target */
  cursor: pointer; list-style: none;
  color: var(--gu-fg); border-radius: var(--gu-radius-sm);
}
.gu-header__search > summary::-webkit-details-marker { display: none; }
.gu-header__search > summary:hover { color: var(--gu-accent); }
/* CRITICAL — the doubled class is load-bearing, not style.
   `.gu-search { position: relative; max-width: 44ch }` is declared BELOW this
   rule with the same specificity, so for as long as this selector was a single
   class it LOST: the header popover computed `position: relative`, `right: 0`
   did nothing, and the panel sat in flow at the search button's own x with a
   328px width. Measured at 360px on 2026-08-25 it ran from x=141 to x=469 —
   109px outside the viewport, unreachable, because scrollWidth still equalled
   innerWidth so there was nothing to scroll.
   ADR-008's silent-name class exactly: no console error, no failed request, no
   failing gate, and it reads as a design decision. `.gu-search.gu-search--header`
   is two classes and wins wherever it is declared. */
.gu-search.gu-search--header {
  position: absolute; left: auto; top: calc(100% + 6px); z-index: 40;
  /* Both values are measured against .gu-header__inner's PADDING box, whose
     width is the content column plus one gutter each side. So the panel's
     right edge lands exactly on the content column's right edge and its width
     can never exceed that column — at 360 and at 1920 alike. */
  right: var(--gu-gutter);
  width: min(30rem, calc(100% - 2 * var(--gu-gutter))); max-width: none;
  margin-bottom: 0; padding: var(--gu-space-sm);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-fg-muted);
  border-radius: var(--gu-radius-md);
  box-shadow: 0 12px 32px rgb(0 0 0 / 35%);
}
/* The results panel is absolutely positioned against .gu-search; inside the
   header popover it must sit under the input, not escape it. */
.gu-search.gu-search--header .gu-search__results { top: calc(100% + 4px); }


.gu-search { position: relative; max-width: 44ch; margin-bottom: var(--gu-space-sm); }
.gu-search__label { display: block; font-weight: 700; margin-bottom: var(--gu-space-3xs); }
.gu-search__input {
  width: 100%; padding: 10px 14px;
  background: var(--gu-bg-raised); color: var(--gu-fg);
  border: 1px solid var(--gu-fg-muted); border-radius: var(--gu-radius-sm);
  font: inherit;
}
.gu-search__input:focus-visible { outline: 2px solid var(--gu-accent); outline-offset: 2px; }
.gu-search__results {
  position: absolute; inset-inline: 0; top: 100%; z-index: 30;
  max-height: 60vh; overflow-y: auto;
  background: var(--gu-bg-raised); border: 1px solid var(--gu-fg-muted);
  border-radius: var(--gu-radius-sm); box-shadow: 0 12px 32px rgba(0,0,0,.35);
}
.gu-search__hit {
  display: flex; justify-content: space-between; gap: var(--gu-space-3xs);
  padding: 10px 14px; color: var(--gu-fg); text-decoration: none; min-width: 0;
}
.gu-search__hit:hover, .gu-search__hit:focus-visible { background: var(--gu-bg); color: var(--gu-accent); }
.gu-search__hit-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gu-search__hit-feed { color: var(--gu-fg-muted); font-size: var(--gu-text-xs); flex: none; align-self: center; }
.gu-search__none { margin: 0; padding: 10px 14px; color: var(--gu-fg-muted); }

/* ── Contact / campus NAP + map (GBP mirror) ────────────────────────────── */
.gu-contactgrid { align-items: start; }
.gu-nap__org { font-weight: 700; font-size: var(--gu-text-lg); margin: 0 0 var(--gu-space-3xs); }
.gu-nap__addr { font-style: normal; color: var(--gu-fg-muted); margin: 0 0 var(--gu-space-xs); }
.gu-nap__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-3xs); }
.gu-nap__list li { display: inline-flex; align-items: center; gap: var(--gu-space-3xs); min-width: 0; }
.gu-nap__list .gu-icon { width: 18px; height: 18px; flex: none; }
.gu-nap__list a { color: var(--gu-fg); font-weight: 600; }
.gu-nap__list a:hover { color: var(--gu-accent); }
.gu-mapfig { margin: 0; min-width: 0; }
.gu-map { position: relative; display: block; border-radius: var(--gu-radius-md); overflow: hidden; }
.gu-map__tiles { display: grid; grid-template-columns: repeat(3, 1fr); }
.gu-map__tiles img { width: 100%; height: auto; display: block; }
.gu-map__pin { position: absolute; transform: translate(-50%, -100%); color: var(--gu-accent); }
.gu-map__pin .gu-icon { width: 34px; height: 34px; fill: currentColor; }
.gu-map__attrib { font-size: var(--gu-text-xs); color: var(--gu-fg-muted); margin-top: var(--gu-space-3xs); }
.gu-map__attrib a { color: inherit; }

/* ── Media ──────────────────────────────────────────────────────────────── */
/* THE UA STYLESHEET GIVES <figure> `margin: 1em 40px`, AND ONLY .gu-figure WAS
   RESETTING IT. `.gu-video` is also a <figure> but carries a different class, so
   it kept the 40px INLINE margin and sat 40px further in than everything else in
   the band — the share-button row directly above it starts at the band's own
   padding edge, the player did not. Visible on all 59 players across 58 pages,
   and invisible to every gate here: it is not overflow, not a console error and
   not a failed request, so the sweep passes and only a screenshot shows it.
   Block margins are left alone; the 16px bottom is doing real work. */
.gu-figure { margin: 0; }
.gu-video { margin-inline: 0; }
.gu-figure img { width: 100%; border-radius: var(--gu-radius-md); }
.gu-figure figcaption { margin-top: var(--gu-space-2xs); font-size: var(--gu-text-sm); color: var(--gu-fg-muted); }
/* CRITICAL — `display: block` IS LOAD-BEARING; WITHOUT IT EVERY FACADE IS 0x0.
   `aspect-ratio` does nothing on an inline box, and the facade is an <a> (it is
   a LINK by design, so that with no JS it still opens the video on YouTube —
   see initVideoFacades in app.js). An <a> is inline, so this rule sized the
   post-click <div class="gu-embed"> wrapper correctly and collapsed the facade
   that ships in the HTML to nothing on all 59 players / 58 pages.
   What that looked like: no plate, and the absolutely-positioned play button
   escaping the zero-height figure to overlap the share-button row above it.
   Reported as "the video and buttons are not in proper alignment" on 2026-08-11.
   NOTHING HERE COULD SEE IT — it is not overflow, not a console error and not a
   failed request, so sweep.mjs passed 1,529 loads over it. */
.gu-embed { display: block; position: relative; aspect-ratio: 16 / 9; border-radius: var(--gu-radius-md); overflow: hidden; }
.gu-embed > * { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ── Split ──────────────────────────────────────────────────────────────── */
.gu-split { display: grid; gap: var(--gu-space-xl); align-items: center; }
@container band (min-width: 780px) { .gu-split { grid-template-columns: 1fr 1fr; } }

/* ── Lists ──────────────────────────────────────────────────────────────── */
.gu-checklist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-xs); }
.gu-checklist li { display: grid; grid-template-columns: 1.4em 1fr; gap: var(--gu-space-2xs); align-items: start; }
/* A 29-item list in one narrow column is a scroll, not a list. Above 8 items it
   flows into as many columns as the BAND can hold — container-driven, so it is
   one column in a sidebar and three in a full-width section with no breakpoint. */
.gu-checklist--cols { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); column-gap: var(--gu-space-lg); }
.gu-checklist .gu-icon { color: var(--gu-accent); margin-top: 0.2em; }

/* ── FAQ ────────────────────────────────────────────────────────────────── */
.gu-faq { border-top: 1px solid var(--gu-rule); }
.gu-faq details { border-bottom: 1px solid var(--gu-rule); }
.gu-faq summary {
  cursor: pointer; list-style: none; padding: var(--gu-space-sm) 0;
  display: flex; align-items: center; justify-content: space-between; gap: var(--gu-space-sm);
  font-family: var(--gu-font-display); font-weight: 600; font-size: var(--gu-text-lg);
}
.gu-faq summary::-webkit-details-marker { display: none; }
.gu-faq summary .gu-icon { transition: transform var(--gu-dur-fast) var(--gu-ease); }
.gu-faq details[open] summary .gu-icon { transform: rotate(180deg); }
.gu-faq__body { padding-bottom: var(--gu-space-sm); color: var(--gu-fg-muted); }

/* ── FAQ feed (/answers/) ───────────────────────────────────────────────────
   The interactive layer's controls are created by app.js (initFaqFeed); with
   JS off only .gu-faqfeed__list and its <details open> items exist, and that
   is the complete FAQ. Input styling repeats the .gu-form token pairs — the
   search box is not inside a .gu-form, and audit-surfaces already measures
   every pair used here on every surface. */
.gu-faqfeed { margin-top: var(--gu-space-md); max-width: 76ch; }
.gu-faqfeed__controls {
  display: flex; flex-direction: column; gap: 0.35rem;
  margin-bottom: var(--gu-space-sm); max-width: 32rem;
}
.gu-faqfeed__controls label { font-size: 0.9rem; font-weight: 600; letter-spacing: 0.01em; }
.gu-faqfeed__controls input {
  font: inherit; font-size: 1rem;
  color: var(--gu-fg);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-md);
  padding: 0.7rem 0.85rem;
  width: 100%;
  appearance: none;
}
.gu-faqfeed__controls input:focus-visible {
  outline: 2px solid var(--gu-focus);
  outline-offset: 2px;
  border-color: var(--gu-focus);
}
.gu-faqfeed__status { color: var(--gu-fg-muted); font-size: 0.9rem; margin: 0 0 var(--gu-space-xs); }
.gu-faqfeed__more { margin-top: var(--gu-space-xs); }
.gu-faqfeed__more a { font-weight: 600; }
.gu-faqfeed__nav {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--gu-space-sm); margin-top: var(--gu-space-md);
}
.gu-faqfeed__page { color: var(--gu-fg-muted); font-size: 0.9rem; }
/* A disabled pager end must LOOK disabled — .gu-btn is styled for links and
   has no :disabled state of its own anywhere else on the site. */
.gu-faqfeed__nav .gu-btn:disabled {
  opacity: 0.45; cursor: default; pointer-events: none; transform: none;
}

/* ── Quote ──────────────────────────────────────────────────────────────── */
.gu-quote { border-left: 3px solid var(--gu-accent); padding-left: var(--gu-space-md); margin: 0; }
.gu-quote p { font-size: var(--gu-text-lg); }
/* An attribution arrives as its own crawl block, not a field — render.mjs pairs
   it into the blockquote. Not italic: `cite` is already italic by default and
   these are job titles, not work titles. */
.gu-quote__cite { display: block; margin-top: var(--gu-space-sm); font-style: normal;
  font-size: var(--gu-text-sm); color: var(--gu-fg-muted); }
/* The separator is CSS, not markup, on purpose. "Sarah Hart December 2024" runs
   together without it, but putting a middot in the HTML would change the page's
   text unit and fidelity.mjs compares the attribution string exactly — so a
   decorative divider would have to be mirrored into the gate's corpus. It is
   presentation, so it lives here and the measured text stays the source's. */
.gu-quote__cite time { color: var(--gu-fg-muted); }
.gu-quote__cite time::before { content: "·"; margin: 0 0.4em; }
/* A video testimonial's attribution is three SOURCE fields — name, role,
   company — each its own element for the same reason the middot above is CSS:
   fidelity.mjs compares the attribution string exactly, and joining them in
   markup invents a unit that appears nowhere in the source. They are DIVs
   rather than SPANs because that gate splits rendered text on block-level tags
   only; `display: inline` puts them back on one line for the reader.
   WARNING: `:not(:first-child)` matters — a quote with no author (Krumwiede has only
   a company) otherwise renders with a leading comma. */
.gu-quote__name, .gu-quote__role, .gu-quote__co { display: inline; }
.gu-quote__role:not(:first-child)::before,
.gu-quote__co:not(:first-child)::before { content: ", "; }
/* The star row is a single role="img" with one accessible name, so a screen
   reader says "5 out of 5 stars" once instead of naming five decorative marks. */
.gu-quote__stars { display: flex; gap: 2px; margin: 0 0 var(--gu-space-xs); }
.gu-icon--star { width: 1em; height: 1em; color: var(--gu-accent); }

/* ── Video pull-quotes (JQ-E5F6DHXH / JQ-EK2N4SSS) ──────────────────────────
   The legacy home page's testimonial carousel, rebuilt as stacked pull-quote
   rows on the midnight plate: company kicker, headline, LARGE quote, styled
   attribution, and the slide's own self-hosted clip beside it. Above 860px the
   copy and the player sit in two columns and alternate sides per row; below,
   they stack. Every color here resolves from [data-surface="midnight"] tokens
   that audit-surfaces already measures. */
.gu-vidquotes { display: grid; gap: var(--gu-space-2xl); margin-top: var(--gu-space-lg); }
.gu-vidquote { display: grid; gap: var(--gu-space-lg); align-items: center; }
@container band (min-width: 860px) {
  .gu-vidquote { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: var(--gu-space-2xl); }
  /* Alternate the player's side so four rows read as a rhythm, not a table. */
  .gu-vidquote:nth-child(even) > .gu-vidquote__media { order: -1; }
}
.gu-vidquote + .gu-vidquote { border-top: 1px solid var(--gu-rule); padding-top: var(--gu-space-2xl); }
.gu-vidquote__copy { display: grid; align-content: center; }
.gu-vidquote__kicker {
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.18em;
  color: var(--gu-accent); margin: 0 0 var(--gu-space-xs);
}
.gu-vidquote__head { font-size: var(--gu-text-3xl); line-height: 1.1; margin: 0 0 var(--gu-space-md); }
/* The pull quote itself: no border rail here — the oversized quotation mark is
   the device. `content` carries empty alternative text so a screen reader does
   not announce a stray punctuation glyph before the quote. */
.gu-vidquote__quote { border-left: 0; padding-left: 0; position: relative; }
.gu-vidquote__quote::before {
  content: "\201C" / ""; display: block; font-family: var(--gu-font-display);
  font-size: var(--gu-text-4xl); font-weight: 700; line-height: 0.6;
  color: var(--gu-accent); margin-bottom: var(--gu-space-2xs);
}
.gu-vidquote__quote p { font-size: var(--gu-text-xl); line-height: 1.5; font-weight: 500; }
.gu-vidquote__cite { margin-top: var(--gu-space-md); font-size: var(--gu-text-base); }
.gu-vidquote__cite .gu-quote__name { font-family: var(--gu-font-display); font-weight: 700; color: var(--gu-fg); }
/* The player. `display: block` on the <video> for the same reason trap 31
   records for .gu-embed — and a <video> with a poster has intrinsic size only
   after metadata arrives, so the aspect ratio is declared, not inferred. */
.gu-vidquote__media video {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
  border-radius: var(--gu-radius-lg); background: #000;
  /* The legacy posters are 1663x681 (2.44:1) in a 16:9 frame — `cover` fills
     the frame instead of letterboxing them. The clips themselves are exactly
     16:9, so playback is untouched. */
  object-fit: cover;
}

/* ── Breadcrumbs ────────────────────────────────────────────────────────── */
.gu-crumbs { font-size: var(--gu-text-sm); color: var(--gu-fg-muted); margin-bottom: var(--gu-space-sm); }
.gu-crumbs ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs); }
.gu-crumbs li { display: flex; align-items: center; gap: var(--gu-space-2xs); }
.gu-crumbs li + li::before { content: "/"; color: var(--gu-rule); }
.gu-crumbs a { color: inherit; text-decoration: none; }
.gu-crumbs a:hover { color: var(--gu-accent); text-decoration: underline; }

/* ── Footer ─────────────────────────────────────────────────────────────── */
.gu-footer { border-top: 1px solid var(--gu-rule); padding-block: var(--gu-space-xl) var(--gu-space-lg); }
.gu-footer__inner { max-width: var(--gu-max-width); margin-inline: auto; padding-inline: var(--gu-gutter); }
.gu-footer__cols { display: grid; gap: var(--gu-space-lg);
                   grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr)); }
.gu-footer__logo { width: 180px; margin-bottom: var(--gu-space-sm); }
.gu-footer h2 { font-size: var(--gu-text-sm); letter-spacing: 0.16em; color: var(--gu-fg-muted); margin-bottom: var(--gu-space-xs); }
.gu-footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-3xs); }
.gu-footer a { color: var(--gu-fg); text-decoration: none; font-size: var(--gu-text-sm); }
.gu-footer a:hover { color: var(--gu-accent); text-decoration: underline; }
.gu-footer__bottom {
  margin-top: var(--gu-space-xl); padding-top: var(--gu-space-sm);
  border-top: 1px solid var(--gu-rule);
  display: flex; flex-wrap: wrap; gap: var(--gu-space-sm);
  justify-content: space-between; font-size: var(--gu-text-sm); color: var(--gu-fg-muted);
}
.gu-social { display: flex; gap: var(--gu-space-xs); }
.gu-social a { display: inline-flex; width: 40px; height: 40px; align-items: center; justify-content: center;
               border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-sm); }
.gu-social a:hover { border-color: var(--gu-accent); color: var(--gu-accent); }
.gu-social .gu-icon { width: 20px; height: 20px; fill: currentColor; stroke: none; }

/* ── Reveal on scroll ───────────────────────────────────────────────────────
   Opt-in via JS adding .is-in. Without JS the element is simply visible — the
   initial state is only applied when the class hook exists, so content is never
   hidden behind a script that failed to run. */
.js .gu-reveal { opacity: 0; transform: translateY(12px);
                 transition: opacity var(--gu-dur-slow) var(--gu-ease-out),
                             transform var(--gu-dur-slow) var(--gu-ease-out); }
.js .gu-reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .gu-reveal { opacity: 1; transform: none; }
}

/* An icon substituted for a pictograph in body copy. It sits ON the text
   baseline and scales with the surrounding font, so a sentence that used to
   carry an emoji keeps its rhythm instead of gaining a fixed-size box. */
.gu-icon--inline {
  width: 1em; height: 1em;
  vertical-align: -0.125em;
  margin-inline-end: 0.15em;
}

/* ── Players, logo walls, stats and countdowns (S7) ────────────────────── */
.gu-drawer__inner, .gu-band__inner, .gu-logos, .gu-video__facade:hover { border-color: var(--gu-accent); }
/* The plate is a token gradient, not an image: a YouTube thumbnail would be a
   third-party request on load, which is the whole thing the facade avoids. */
.gu-video__plate {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--gu-accent) 16%, transparent), transparent 62%),
    linear-gradient(160deg, color-mix(in srgb, var(--gu-fg) 8%, transparent), transparent 70%);
}
.gu-embed > .gu-video__play {
  position: absolute; inset: 0; margin: auto;
  width: clamp(56px, 14cqi, 84px); height: clamp(56px, 14cqi, 84px);
  display: grid; place-items: center;
  border-radius: 50%; background: var(--gu-accent); color: var(--gu-bg);
  transition: transform var(--gu-dur-fast) var(--gu-ease);
}
.gu-video__play .gu-icon { width: 42%; height: 42%; fill: currentColor; stroke: none; }
.gu-video__facade:hover .gu-video__play { transform: scale(1.06); }
/* WARNING: `.gu-embed > *` (declared above) sets `height: 100%` on every child, and
   `inset: auto 0 0 0` alone does NOT undo that — the label ends up full-height
   anchored at the bottom, so its padding puts the text at the TOP of the frame.
   That is what shipped first. The selector is also deliberately more specific
   than `.gu-embed > *` rather than merely later in the file (trap 5). */
.gu-embed > .gu-video__label {
  position: absolute; inset: auto 0 0 0; height: auto;
  padding: var(--gu-space-sm) var(--gu-space-md);
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em;
  background: linear-gradient(to top, color-mix(in srgb, var(--gu-bg) 92%, transparent), transparent);
}
/* The wall itself never had a layout rule — every wall to date held one or two
   tiles, so nobody saw the default vertical <ul> stack. The home "Proud
   Sponsors" band (JQ-Y3EB7BFM) renders eight, which made it visible: a wrapped
   row is the layout, tiles keep their measured plates. Already in the
   min-width:0 guard list at the top of this file. */
.gu-logos {
  display: flex; flex-wrap: wrap; gap: var(--gu-space-sm);
  margin: var(--gu-space-md) 0 0; padding: 0; list-style: none;
}
.gu-logos__tile {
  display: grid; place-items: center;
  padding: var(--gu-space-sm);
  min-height: 92px; min-width: 130px;
  background: var(--gu-logo-plate, var(--gu-bg-raised));
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-sm);
}
/* WARNING: `width: 100%` plus `max-height` SQUASHES a logo — the box is content-sized,
   so `object-fit` has nothing to fit into and the mark is scaled to fit a box
   of the wrong aspect ratio. "GUTTER CAP" shipped clipped on both sides that
   way. Constraining BOTH axes with `width/height: auto` letterboxes correctly. */
.gu-logos__tile picture { display: grid; place-items: center; max-width: 100%; }
.gu-logos__tile img { width: auto; height: auto; max-width: 100%; max-height: 72px; }

/* CRITICAL: THE SVG TILES HAD NO RULE AT ALL, and `--gu-logo-h` was READ BY NOTHING.
   The markup sets an optical height per mark inline —
   `<svg class="gu-logo" style="--gu-logo-h:33px">` — and no stylesheet has ever
   consumed it, so every SVG logo rendered at whatever its viewBox implied.
   Measured on the live home page: 96px, 25px, 9px, 34px, 19px, 19px, 57px,
   25px — a TEN-FOLD spread in one row, and not one height matching its own
   declared value. `.gu-logos__tile--svg` was likewise undefined (it is one of
   the 23 classes tools/qa/css-classes.mjs now refuses).

   Same family as `--gu-fg-subtle` and `gu-band--titleonly`: a name that
   resolves to nothing, failing silently, looking like a design decision.

   Both axes are constrained so a mark letterboxes rather than squashes — the
   warning above this block was written about exactly that, and it applies to
   an <svg> as much as to an <img>. max-width keeps a very wide wordmark
   (Sherwin-Williams is a 10:1 viewBox) inside its tile even at a tall
   optical height. */
/* QA round 3 / media audit: with the sizing fixed, several marks turned out to
   be the wrong COLOUR for their ground — RILLA is black and One Click's only
   artwork is literally the `-dark` variant, both near-invisible on the ink
   plate, and Qualified Remodeler washes out. The sprite carries exactly ONE
   variant per brand (checked: 8 marks, 8 symbols, no light alternates), so
   there is no artwork to swap to.
   A light plate is the conventional answer for third-party marks and the one
   their brand guidelines assume: each logo is drawn to sit on white. The hook
   was already here — `.gu-logos__tile` has read `var(--gu-logo-plate, …)` all
   along and nothing ever set it.
   WARNING: This is a visible design change to the sponsor band, made because the
   alternative is shipping two invisible logos. Easily reverted by dropping
   this one declaration. */
.gu-logos__tile { --gu-logo-plate: var(--gu-paper); }
.gu-logos__tile--svg { overflow: hidden; }
.gu-logos__tile .gu-logo {
  height: var(--gu-logo-h, 40px);
  width: auto;
  max-width: 100%;
  max-height: 72px;
}

/* ── Partner bands (/partners/, JQ-K6YAJE0W + JQ-Y3EB7BFM) ──────────────────
   One band per partner: mark + linked name + the partner's own copy, with the
   partner page's video facade beside it. Splits to two columns only when a
   band actually HAS a player (`--split`); hellojade has none and stays one
   column. `--flip` alternates which side the player sits on. */
.gu-partner { display: grid; gap: var(--gu-space-xl); align-items: center; }
@container band (min-width: 780px) {
  .gu-partner--split { grid-template-columns: 1fr 1fr; }
  .gu-partner--split.gu-partner--flip .gu-partner__info { order: 2; }
}
/* The mark sits on its measured plate, same contract as .gu-logos__tile: both
   axes auto so a wide wordmark letterboxes instead of squashing. */
.gu-partner__mark {
  display: inline-grid; place-items: center;
  padding: var(--gu-space-sm) var(--gu-space-md);
  margin-bottom: var(--gu-space-md);
  max-width: 100%;
  background: var(--gu-logo-plate, var(--gu-bg-raised));
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-sm);
}
.gu-partner__mark picture { display: grid; place-items: center; max-width: 100%; }
.gu-partner__mark img { width: auto; height: auto; max-width: 220px; max-height: 64px; }
/* JQ-MRY2T6F9: the SVG twin of the `img` rule above never existed — `.gu-logo`
   only had a sizing rule scoped to `.gu-logos__tile` (the sponsor wall), so an
   `<svg class="gu-logo" style="--gu-logo-h:Npx">` inside `.gu-partner__mark`
   (the /partners/ bands, and now this band's cards) rendered at whatever a
   viewBox-only <svg> defaults to instead of its measured optical height.
   Same two-axis letterbox contract as the `img` rule: both constrained so a
   wide wordmark (Sherwin-Williams is 10:1) shrinks to fit rather than
   overflowing its plate. */
.gu-partner__mark--svg .gu-logo {
  height: var(--gu-logo-h, 40px); width: auto;
  max-width: 220px; max-height: 64px;
}
.gu-partner__hjmark { display: block; width: 64px; height: 64px; }
.gu-partner__title { font-size: var(--gu-text-2xl); margin: 0 0 var(--gu-space-xs); }
.gu-partner__title a { text-decoration: none; color: inherit; }
.gu-partner__title a:hover, .gu-partner__title a:focus-visible { color: var(--gu-accent); }
.gu-partner__desc { max-width: 56ch; }
/* .gu-video already resets the <figure> UA margin (trap 32); the media column
   needs no further sizing — .gu-embed is display:block with its own ratio. */

/* ── Team ───────────────────────────────────────────────────────────────────
   Eight people who were reachable from nowhere on the site. Each card links to
   its `/about/team/<slug>/` page when a route owns one. */
.gu-stats__cell { border-top: 2px solid var(--gu-accent); padding-top: var(--gu-space-xs); }
.gu-stats__figure { font-family: var(--gu-font-display); font-weight: 700; font-size: var(--gu-text-4xl); line-height: 1; }
.gu-stats__label { margin: var(--gu-space-2xs) 0 0; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }

/* ── Countdown ──────────────────────────────────────────────────────────────
   The clock ships `hidden` and app.js reveals it. That keeps the build
   byte-reproducible (rendered digits would differ every run) and keeps the
   band honest with JS off: the date and location sentences below are the
   content, and they are always in the DOM. */
/* ── Helmet watermark band backgrounds ──────────────────────────────────────
   The mono helmet re-tinted per surface family, generated into
   dist/assets/brand/helmet-bg-*.svg by render.mjs. The tint pairs with the
   band's declared surface: dark bands wear gold, light bands wear blue or
   ink, colored bands wear white. Opacity is baked into the SVG (0.05-0.08),
   so text contrast is the surface's own measured pairing, untouched. */
.gu-band--helmet {
  background-repeat: no-repeat;
  background-position: calc(100% + 6rem) center;
  background-size: min(44vw, 34rem);
}
.gu-band--helmet[data-surface="midnight"],
.gu-band--helmet[data-surface="ink"] { background-image: url("/assets/brand/helmet-bg-gold.svg"); }
.gu-band--helmet[data-surface="paper"] { background-image: url("/assets/brand/helmet-bg-blue.svg"); }
.gu-band--helmet[data-surface="white"] { background-image: url("/assets/brand/helmet-bg-ink.svg"); }
.gu-band--helmet[data-surface="blue"],
.gu-band--helmet[data-surface="deep"],
.gu-band--helmet[data-surface="steel"] { background-image: url("/assets/brand/helmet-bg-white.svg"); }

/* A thin callout strip: head, clock, details+CTA laid on one line, wrapping
   into stacked rows as the container narrows. The band around it drops to a
   fraction of the normal section padding — this is a callout, not a chapter. */
.gu-countdown-band {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--gu-space-md) var(--gu-space-xl);
}
.gu-countdown__head { flex: 1 1 16rem; }
.gu-countdown__side { display: grid; gap: var(--gu-space-xs); justify-items: start; }
.gu-band:has(> .gu-band__inner > .gu-countdown-band) { padding-block: var(--gu-space-lg); }
.gu-countdown__title { font-size: var(--gu-text-xl); margin: 0; }
.gu-countdown__lead {
  font-family: var(--gu-font-display); font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; font-size: var(--gu-text-sm);
  color: var(--gu-fg-muted); margin-top: var(--gu-space-2xs);
}
.gu-countdown[hidden] { display: none; }
/* Revealed by app.js in place of `.gu-countdown__clock` once the target
   instant passes (initCountdowns' `tick`). It ships `hidden` and had no rule
   of its own — a real, user-facing state with no gate able to see it (it only
   ever renders after a live countdown expires), so it fell back to the bare
   base-layer `p` margin while every sibling in this component is deliberately
   styled. Matches `.gu-countdown__value`'s weight/color so the "done" message
   reads as this component's voice, not the browser default. */
.gu-countdown__done {
  font-family: var(--gu-font-display); font-weight: 700;
  font-size: var(--gu-text-lg); color: var(--gu-accent);
}
.gu-countdown__clock {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--gu-space-xs);
}
.gu-countdown__cell {
  border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-sm);
  background: var(--gu-bg-raised); padding: var(--gu-space-2xs) var(--gu-space-xs);
  min-width: 4.25rem;
  text-align: center;
}
.gu-countdown__value {
  display: block; font-family: var(--gu-font-display); font-weight: 700;
  font-size: var(--gu-text-2xl); line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--gu-accent);
}
.gu-countdown__unit {
  display: block; margin-top: var(--gu-space-3xs);
  font-size: var(--gu-text-sm); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gu-fg-muted);
}
.gu-countdown__details { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-2xs); }
.gu-countdown__details li { display: grid; grid-template-columns: 1.4em 1fr; gap: var(--gu-space-2xs); align-items: start; }
.gu-countdown__details .gu-icon { color: var(--gu-accent); margin-top: 0.2em; }
.gu-countdown__details li:not(:has(.gu-icon)) { grid-template-columns: 1fr; }

/* The clip titles beside a player. Most of these have no embed of their own —
   the old theme's slider injected them client-side — so they are a LIST, not a
   row of dead play buttons. */
.gu-video__titles {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--gu-space-xs);
  min-width: 0;                    /* trap 1: a long title must not widen the track */
}
.gu-video__titles li {
  display: flex; gap: 0.6rem; align-items: start;
  padding-block: 0.35rem;
  border-bottom: 1px solid var(--gu-rule);
}
.gu-video__titles li:last-child { border-bottom: 0; }
.gu-video__titles .gu-icon { flex: none; width: 1em; height: 1em; margin-top: 0.25em; color: var(--gu-accent); }
.gu-video__titles span { min-width: 0; }

/* A transactional third-party widget (Ticket Tailor, Calendly) rendered as a
   link rather than an iframe — it needs its own cookies and top-level context. */
.gu-embed-link { margin-block: var(--gu-space-sm); }
.gu-embed-link .gu-icon { margin-inline-end: 0.4em; }

/* ── Lead capture (S8) ──────────────────────────────────────────────────── */
/* CRITICAL — every color here is a TOKEN PAIR that theme/docs/audit-surfaces.mjs
   already measures on this surface. A form is the one component where a
   hand-picked "just a bit lighter" grey is most tempting and most expensive:
   placeholder and helper text are exactly the sizes where contrast fails. */
/* JQ-SBMJR4F0 — the lead form is now a wizard: .gu-wizard holds the stepper
   dots and the <form>, .gu-form stacks its three .gu-form__step fieldsets,
   and the FIELD grid that used to live directly on .gu-form now lives on
   whichever fieldset is showing. Nothing here changes a field's name, type
   or the /start/lead POST contract — see lead.go and ADR-013 item 79. */
.gu-wizard {
  display: grid;
  gap: var(--gu-space-lg);
  margin-top: var(--gu-space-md);
}
@container band (min-width: 56rem) {
  /* A rail of steps beside the fields once there is room for both — below
     this the rail sits above the form instead (the unconditional rule for
     .gu-wizard__steps further down already reads fine stacked). */
  .gu-wizard { grid-template-columns: 13rem 1fr; align-items: start; }
  .gu-wizard__steps { flex-direction: column; }
}
.gu-form { display: flex; flex-direction: column; gap: var(--gu-space-lg); }

.gu-wizard__steps { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--gu-space-md); }
.gu-wizard__step { display: flex; align-items: center; gap: var(--gu-space-2xs); color: var(--gu-fg-muted); }
.gu-wizard__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; flex: none;
  border-radius: var(--gu-radius-pill);
  border: 1px solid var(--gu-rule);
  background: var(--gu-bg-raised);
}
.gu-wizard__icon .gu-icon { width: 1.15rem; height: 1.15rem; }
.gu-wizard__label {
  font-family: var(--gu-font-display); font-size: var(--gu-text-sm);
  font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
}
.gu-wizard__step.is-active,
.gu-wizard__step.is-done { color: var(--gu-fg); }
.gu-wizard__step.is-active .gu-wizard__icon,
.gu-wizard__step.is-done .gu-wizard__icon {
  border-color: var(--gu-accent); color: var(--gu-accent);
}
.gu-wizard__step.is-active .gu-wizard__icon {
  background: color-mix(in srgb, var(--gu-accent) 14%, transparent);
}

/* Each step is a plain <fieldset>: no browser chrome to undo (border/padding/
   min-width all reset below), and native to the platform's own constraint
   validation — checkValidity()/reportValidity() work on a fieldset exactly
   as they do on the form, scoped to what is inside it. This is what lets
   wizard.js ask "is THIS step valid" without reimplementing validation. */
.gu-form__step {
  border: 0; margin: 0; padding: 0; min-inline-size: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: var(--gu-space-sm);
}
/* Author `display` beats the UA `[hidden]` rule at equal specificity (trap
   already recorded for .gu-slider__btn above) — without this, a step JS
   hides still renders as an empty grid track. */
.gu-form__step[hidden] { display: none; }
.gu-form__steplegend {
  grid-column: 1 / -1; padding: 0; margin: 0 0 var(--gu-space-2xs);
  font-family: var(--gu-font-display); font-size: var(--gu-text-lg);
  font-weight: 700;
}
.gu-form__stepnav {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--gu-space-sm);
}

.gu-field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
.gu-field--wide { grid-column: 1 / -1; }
.gu-field label { font-size: 0.9rem; font-weight: 600; letter-spacing: 0.01em; }
.gu-field__opt { font-weight: 400; color: var(--gu-fg-muted); }

/* One selector for every control, so a select can never drift from an input. */
.gu-form :is(input, select, textarea) {
  font: inherit; font-size: 1rem;
  color: var(--gu-fg);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-md);
  padding: 0.7rem 0.85rem;
  width: 100%;
  min-width: 0;                      /* trap 1: a grid child must be allowed to shrink */
  appearance: none;
}
.gu-form textarea { resize: vertical; min-height: 6.5rem; }
.gu-form :is(input, select, textarea):focus-visible {
  outline: 2px solid var(--gu-focus);
  outline-offset: 2px;
  border-color: var(--gu-focus);
}
.gu-form :is(input, textarea)::placeholder { color: var(--gu-fg-muted); }
/* :user-invalid, not :invalid — :invalid paints every required field red on
   first paint, before the visitor has typed anything. */
.gu-form :is(input, select, textarea):user-invalid { border-color: var(--gu-danger); }

.gu-form__note { color: var(--gu-fg-muted); font-size: 0.9rem; margin: var(--gu-space-2xs) 0 0; }
.gu-form__error {
  margin: 0;
  color: var(--gu-danger); font-weight: 600;
}
/* The honeypot. Off-screen rather than display:none — some bots skip hidden
   controls, and `aria-hidden` + `tabindex="-1"` already keep it away from
   assistive tech and the tab order. */
.gu-form__trap {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.gu-form__done { max-width: 56ch; }

/* == Reviews: home slider, /reviews/ archive, per-review pages (JQ-53TVB3Z2) ==
   The slider is scroll-snap FIRST: with JS off (or before app.js runs) the
   track is an ordinary swipeable overflow row, and the prev/next buttons ship
   `hidden` until initSliders wires them — the same ships-dead-until-wired
   pattern as .gu-header__toggle. Card text is the FULL review, clamped in CSS;
   a build-time cut would be a content decision (see .gu-cardlist's warning). */
.gu-slider__track {
  display: flex;
  gap: var(--gu-space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: var(--gu-space-2xs);        /* the scrollbar gets its own room */
  scrollbar-width: thin;
}
@media (prefers-reduced-motion: reduce) {
  .gu-slider__track { scroll-behavior: auto; }
}
.gu-slider__track:focus-visible { outline: 2px solid var(--gu-focus); outline-offset: 3px; }
/* One card per view on a phone, three from ~62rem of band width. flex-basis,
   not width: the track's min-width:0 guard entry lets a long unbroken word
   shrink instead of widening the card past its snap stop. */
.gu-slider__card { flex: 0 0 100%; scroll-snap-align: start; }
@container band (min-width: 62rem) {
  .gu-slider__card { flex-basis: calc((100% - 2 * var(--gu-space-md)) / 3); }
}
.gu-slider__nav { display: flex; gap: var(--gu-space-2xs); justify-content: flex-end; margin-top: var(--gu-space-xs); }
.gu-slider__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;                  /* minimum touch target */
  background: var(--gu-bg-raised); border: 1px solid var(--gu-rule);
  border-radius: 999px; color: var(--gu-fg); cursor: pointer; flex: none;
}
.gu-slider__btn[hidden] { display: none; }    /* author display beats UA [hidden] */
.gu-slider__btn:hover:not(:disabled) { border-color: var(--gu-accent); color: var(--gu-accent); }
.gu-slider__btn:disabled { opacity: 0.4; cursor: default; }

/* A review card. Same-height rows come from flex stretch + the clamp: the
   full text stays in the DOM (fidelity measures it there), six lines show,
   and "Read review" is the whole text's home. Meta pins to the bottom so
   short reviews do not float their byline mid-card. */
.gu-reviews__card { justify-content: flex-start; }
.gu-reviews__text { margin: 0; }              /* a blockquote carries UA margin (trap 32) */
.gu-reviews__text p {
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 6; line-clamp: 6; overflow: hidden;
}
.gu-reviews__meta {
  margin-top: auto; padding-top: var(--gu-space-xs);
  display: flex; flex-wrap: wrap; gap: var(--gu-space-3xs) var(--gu-space-sm);
  align-items: baseline; justify-content: space-between;
}
.gu-reviews__meta .gu-quote__cite { margin-top: 0; }
.gu-reviews__more { color: var(--gu-accent); font-weight: 600; text-decoration: none; white-space: nowrap; }
.gu-reviews__more:hover { text-decoration: underline; }

/* The archive grid reuses .gu-grid--3; stretch keeps rows even. */
.gu-reviews__grid { align-items: stretch; }

/* Star-only ratings: a compact strip, not cards — there is no text to card. */
.gu-reviews__staronly { margin-top: var(--gu-space-xl); }
.gu-reviews__staronly-head { font-size: var(--gu-text-lg); margin-bottom: var(--gu-space-sm); }
.gu-reviews__staronly-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--gu-space-2xs) var(--gu-space-lg);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
}
.gu-reviews__staronly-list li { display: flex; flex-wrap: wrap; gap: var(--gu-space-3xs) var(--gu-space-2xs); align-items: baseline; }
.gu-reviews__staronly-list .gu-quote__stars { margin: 0; }
.gu-reviews__staronly-list .gu-quote__cite { display: inline; margin-top: 0; }

/* One review, one page. */
.gu-reviews__single { max-width: var(--gu-measure); }
.gu-reviews__full p { font-size: var(--gu-text-lg); }
.gu-reviews__response {
  margin-top: var(--gu-space-lg);
  padding: var(--gu-space-md);
  background: var(--gu-bg-raised);
  border-left: 3px solid var(--gu-accent);
  border-radius: var(--gu-radius-md);
}
.gu-reviews__response-head { font-size: var(--gu-text-lg); margin-bottom: var(--gu-space-2xs); }
.gu-reviews__single .gu-btn-row { margin-top: var(--gu-space-lg); }

/* ── Feed detail: shared shell (2026-08-21) ─────────────────────────────────
   `.gu-detail` had NO rules of any kind until this date — see the note above
   the band wrapper in server/internal/feeds/tmpl/detail.html. These are the
   minimum a long article needs once it finally has a gutter: a reading measure
   on the prose, and the "Visit <partner>" / CTA links looking like the buttons
   they were named for. `.gu-cta` was likewise unstyled everywhere it appeared
   — the class existed in the template and in no stylesheet, so every call to
   action on every DB-rendered page shipped as an underlined blue link. */
.gu-detail__sub { font-size: var(--gu-text-lg); color: var(--gu-fg-muted); max-width: var(--gu-measure); }
.gu-detail__meta { color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-detail__summary { max-width: var(--gu-measure); margin-block: var(--gu-space-md); }
.gu-detail__summary p { font-size: var(--gu-text-lg); }
.gu-detail__body { max-width: var(--gu-measure); }
/* QA round 2, C4 — the enrolment block every program page was missing. */
.gu-detail__enroll {
  margin-top: var(--gu-space-xl);
  padding: var(--gu-space-lg);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-fg-muted);
  border-radius: var(--gu-radius-md);
}
.gu-detail__enroll h2 { margin-top: 0; }
.gu-detail__enrollrow { display: flex; flex-wrap: wrap; gap: var(--gu-space-sm); margin-bottom: 0; }

.gu-detail__ticketnote { color: var(--gu-fg-muted); font-size: var(--gu-text-sm); max-width: 60ch; }

.gu-detail__source { margin-top: var(--gu-space-xl); color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-cta {
  display: inline-flex; align-items: center; gap: var(--gu-space-3xs);
  padding: 0.7em 1.4em;
  background: var(--gu-accent); color: var(--gu-bg);
  border: 1px solid var(--gu-accent); border-radius: var(--gu-radius-pill);
  font-family: var(--gu-font-display); font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; font-size: var(--gu-text-sm);
  text-decoration: none;
  transition: background var(--gu-dur-fast) var(--gu-ease), color var(--gu-dur-fast) var(--gu-ease);
}
.gu-cta:hover, .gu-cta:focus-visible { background: transparent; color: var(--gu-accent); }
/* The secondary action. It has to clear 3:1 against the band as a BORDER
   (SC 1.4.11) and 4.5:1 as TEXT, so it borrows --gu-fg rather than the accent:
   an accent-on-accent ghost button reads as disabled. */
.gu-cta--ghost { background: transparent; color: var(--gu-fg); border-color: var(--gu-fg-muted); }
.gu-cta--ghost:hover, .gu-cta--ghost:focus-visible { background: var(--gu-fg); color: var(--gu-bg); border-color: var(--gu-fg); }

/* ── The partner brand plate ────────────────────────────────────────────────
   A partner's own lockup, at a size their kit permits and with the clear space
   their kit asks for. The padding IS the clear space: hellojade's rule is one
   dome-width of the mark on every side, and the mark is roughly a quarter of
   the lockup's width, so 26% of the rendered height is a faithful margin at
   any size. It is set in padding rather than baked into the file so the SVG
   stays byte-identical to the kit's — a logo you have edited is a logo you can
   no longer prove is theirs.
   `--gu-brandplate-h` caps the height, never the width: a lockup constrained by
   width alone becomes unreadably small on a phone and absurd on a desktop. */
.gu-brandplate {
  /* The 48px floor is the PARTNER'S rule, not a taste call: hellojade's kit
     forbids rendering the full constellation mark below about 48px, because the
     dots that make up the thread vanish at small sizes, and it ships a
     simplified mark-small file for anything under that. The lockup's mark is
     its full height, so the floor here IS that rule. A 44px floor shipped
     first and was 4px in breach at 360px. */
  --gu-brandplate-h: clamp(48px, 7vw, 76px);
  margin: var(--gu-space-lg) 0;
  padding: calc(var(--gu-brandplate-h) * 0.26) 0;
}
.gu-brandplate img {
  display: block;
  height: var(--gu-brandplate-h);
  width: auto; max-width: 100%;
}

/* ── Partner profile (board item 43) ────────────────────────────────────────
   The long-form body of a partner page. Six section kinds share one rhythm:
   heading, lede at a reading measure, then the shape the section needs. */
.gu-profile { margin-top: var(--gu-space-2xl); }
.gu-profile__sec + .gu-profile__sec { margin-top: var(--gu-space-2xl); }
.gu-profile__head { font-size: var(--gu-text-2xl); margin-bottom: var(--gu-space-2xs); }
.gu-profile__lede { color: var(--gu-fg-muted); max-width: var(--gu-measure); margin-bottom: var(--gu-space-lg); }
.gu-profile__cardtitle { font-size: var(--gu-text-lg); margin-bottom: var(--gu-space-3xs); color: var(--gu-fg-strong); }

/* Cards and steps are the same grid — `auto-fit` with a 260px floor, matching
   .gu-grid--3, so a 4-card set is 4 across on a desktop and 1 on a phone with
   no breakpoint of its own. `min-width: 0` per track is the no-spill rule. */
.gu-profile__cards, .gu-profile__steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--gu-space-md);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.gu-profile__cards > *, .gu-profile__steps > * { min-width: 0; }
/* CRITICAL — --gu-fg, NOT --gu-fg-muted. MEASURED 2026-08-21: muted is #808181, which
   is 4.59:1 on --gu-bg (#171717) and only 4.22:1 on --gu-bg-raised (#1f1f1f) —
   below AA for normal text. theme/docs/audit-surfaces.mjs pairs every fg token
   against --gu-bg and NEVER against --gu-bg-raised, so a card is exactly the
   place its gate cannot see, and it passed while these three rules failed.
   Card body copy is the substance of this page, not supporting text, so it
   takes the full foreground: 13.34:1 on the raised plate. */
.gu-profile__card, .gu-profile__step {
  padding: var(--gu-space-md);
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule);
  border-radius: var(--gu-radius-md);
  color: var(--gu-fg);
}
.gu-profile__card p, .gu-profile__step p { margin: 0; }
/* The step number is decoration over a real ordered list: <ol> carries the
   sequence for anyone not looking at it, so the numeral is aria-hidden and the
   list is never restyled to hide its own semantics. */
.gu-profile__stepnum {
  display: block;
  font-family: var(--gu-font-display); font-size: var(--gu-text-2xl);
  line-height: 1; color: var(--gu-accent); margin-bottom: var(--gu-space-2xs);
}

.gu-profile__groups {
  display: grid; gap: var(--gu-space-lg);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
.gu-profile__groups > * { min-width: 0; }
.gu-profile__tags { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--gu-space-3xs); }
.gu-profile__tags li {
  padding: 0.3em 0.75em;
  background: var(--gu-bg-raised);
  border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-pill);
  /* 15px on the raised plate — normal text, so 4.5:1, so --gu-fg (see the
     measurement note on .gu-profile__card). */
  font-size: var(--gu-text-sm); color: var(--gu-fg);
  /* A tool name must be able to break rather than push the row wider than the
     band — "Google Local Services Ads" is 25 characters. */
  overflow-wrap: anywhere;
}
.gu-profile__note { margin-top: var(--gu-space-md); color: var(--gu-fg-muted); font-size: var(--gu-text-sm); max-width: var(--gu-measure); }
.gu-profile__prose { max-width: var(--gu-measure); display: grid; gap: var(--gu-space-sm); }
.gu-profile__ctarow { display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs); margin: 0; }

/* The player. `.gu-embed`'s 16/9 frame is for the post-click wrapper; the
   facade is an <a>, which is inline, so it needs the frame declared here too —
   the exact trap recorded above `.gu-embed` (a facade with no `display: block`
   collapses to 0x0 and the play button escapes it). */
.gu-profile__video {
  position: relative; display: block;
  max-width: 62rem; margin: 0 0 var(--gu-space-sm);
  border-radius: var(--gu-radius-md); overflow: hidden;
  background: var(--gu-bg-raised);
}
.gu-profile__video iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }
.gu-profile__vidmeta {
  display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs);
  align-items: baseline; color: var(--gu-fg-muted); font-size: var(--gu-text-sm);
  max-width: 62rem;
}
.gu-profile__vidtitle { color: var(--gu-fg); font-weight: 600; }
.gu-profile__vidlen, .gu-profile__vidshow { color: var(--gu-fg-muted); }
.gu-profile__vidshow::before { content: "· "; }
.gu-profile__vidlen::before { content: "· "; }

.gu-profile__person {
  margin-top: var(--gu-space-lg);
  max-width: var(--gu-measure);
  padding-left: var(--gu-space-md);
  border-left: 3px solid var(--gu-accent);
  color: var(--gu-fg-muted);
}
.gu-profile__personname { font-family: var(--gu-font-display); color: var(--gu-fg); margin-bottom: var(--gu-space-3xs); }
.gu-profile__personrole { color: var(--gu-fg-muted); font-family: var(--gu-font-text); font-weight: 400; }

.gu-profile__colophon {
  margin-top: var(--gu-space-2xl); padding-top: var(--gu-space-md);
  border-top: 1px solid var(--gu-rule);
  color: var(--gu-fg-muted); font-size: var(--gu-text-sm);
}
.gu-profile__contact { display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs) var(--gu-space-md); margin: 0 0 var(--gu-space-2xs); }
.gu-profile__parent { margin: 0 0 var(--gu-space-2xs); }
/* The partner's trademark attribution, in their own words. Muted, but it must
   still clear AA — --gu-fg-muted is measured on every surface (tokens.css). */
.gu-profile__tm { margin: 0; font-size: var(--gu-text-xs); }

/* ── Events: the past archive (AUDIT-3, board item 117) ──────────────────
   A past event is still worth a page — the photos, the recap, the reviews —
   but its card must not read like something to book. Muted, and the date line
   says "Held". The detail page's note under a past event does the same job. */
.gu-section__head--past { margin-top: var(--gu-space-2xl); }
.gu-card--past .gu-card__date { color: var(--gu-fg-muted); }
.gu-card--past .gu-card__media img { filter: saturate(0.6); }
.gu-detail__pastnote {
  padding: var(--gu-space-sm) var(--gu-space-md);
  border-inline-start: 3px solid var(--gu-accent);
  background: var(--gu-bg-raised);
  border-radius: var(--gu-radius-sm);
  max-width: 60ch;
}

/* ── Feed index: filter strip + numbered pager (AUDIT-13, board item 127) ── */
.gu-filter {
  display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs);
  margin-bottom: var(--gu-space-lg);
}
.gu-filter__chip {
  display: inline-flex; align-items: center; gap: var(--gu-space-3xs);
  padding: 0.4em 0.9em; border: 1px solid currentColor; border-radius: var(--gu-radius-pill);
  color: var(--gu-fg); text-decoration: none; font-size: var(--gu-text-sm);
}
.gu-filter__chip:hover { background: var(--gu-fg); color: var(--gu-bg); }
.gu-filter__chip--on { background: var(--gu-accent); border-color: var(--gu-accent); color: var(--gu-bg); }
.gu-filter__n { opacity: 0.7; font-variant-numeric: tabular-nums; }
.gu-pager { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gu-space-xs); margin-top: var(--gu-space-lg); }
.gu-pager__pages { display: flex; flex-wrap: wrap; gap: var(--gu-space-3xs); list-style: none; margin: 0; padding: 0; }
.gu-pager__num {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 2.4em; padding: 0.4em 0.5em; border-radius: var(--gu-radius-sm);
  color: var(--gu-fg); text-decoration: none; font-variant-numeric: tabular-nums;
}
.gu-pager__num:hover { background: var(--gu-bg-raised); }
.gu-pager__num--on { background: var(--gu-accent); color: var(--gu-bg); font-weight: 700; }
.gu-pager__gap { padding: 0.4em 0.2em; color: var(--gu-fg-muted); }

/* ── Related articles (AUDIT-8, board item 122) ──────────────────────────
   The legacy "Related Articles" block, as a link list rather than three
   headings over a byline stub. */
.gu-related { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-xs); }
.gu-related li { padding-inline-start: var(--gu-space-md); position: relative; }
.gu-related li::before { content: ""; position: absolute; inset-inline-start: 0; top: 0.65em; width: 0.6em; height: 2px; background: var(--gu-accent); }
.gu-related a { color: var(--gu-fg); text-decoration-color: var(--gu-accent); }

/* ══ PROGRAM PAGES v2 (board item 135, 50-design/PROGRAM-PAGES.md) ═════════════
   Mobile-first; widths come from @container band, never the viewport, so a band
   lays out by the space it actually has. No rule here may widen the page: long
   copy wraps, the section nav scrolls inside itself, the comparison table
   becomes stacked cards on narrow widths. */

.gu-prog [id^="p-"] { scroll-margin-top: calc(var(--gu-nav-header-h) + 64px); }
.gu-prog .gu-icon { width: 1.5rem; height: 1.5rem; }

/* 1 ── split hero */
.gu-prog-hero__inner { display: grid; gap: var(--gu-space-xl); align-items: center; }
.gu-prog-hero__tagline {
  font-family: var(--gu-font-display); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; font-size: var(--gu-text-sm); color: var(--gu-accent);
  margin: 0 0 var(--gu-space-sm);
}
.gu-prog-hero__media { position: relative; margin: 0; border-radius: var(--gu-radius-lg); overflow: hidden; box-shadow: var(--gu-shadow-lg); }
.gu-prog-hero__media img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.gu-prog-hero__play {
  position: absolute; inset: auto var(--gu-space-md) var(--gu-space-md) auto;
  display: grid; place-items: center; width: 4rem; height: 4rem; border-radius: 50%;
  background: var(--gu-accent); color: var(--gu-ink); box-shadow: var(--gu-shadow-md);
  transition: transform var(--gu-dur-fast) var(--gu-ease);
}
.gu-prog-hero__play .gu-icon { width: 1.75rem; height: 1.75rem; }
.gu-prog-hero__play:hover, .gu-prog-hero__play:focus-visible { transform: scale(1.08); }
@container band (min-width: 880px) {
  .gu-prog-hero__inner { grid-template-columns: 1.05fr 0.95fr; gap: var(--gu-space-2xl); }
}

/* 2 ── key facts strip */
.gu-prog-facts { background: var(--gu-bg); color: var(--gu-fg); border-block: 1px solid var(--gu-rule); container-type: inline-size; container-name: band; }
.gu-prog-facts__list {
  display: grid; gap: var(--gu-space-md); margin: 0 auto; padding-block: var(--gu-space-lg);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}
.gu-prog-fact { display: grid; grid-template-columns: 2.5rem 1fr; column-gap: var(--gu-space-xs); align-items: start; }
.gu-prog-fact__icon { grid-row: span 2; display: grid; place-items: center; width: 2.5rem; height: 2.5rem; border-radius: var(--gu-radius-md); background: var(--gu-bg-raised); color: var(--gu-accent); }
.gu-prog-fact dt { font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--gu-fg-muted); }
.gu-prog-fact dd { margin: 0; font-weight: 600; line-height: var(--gu-leading-snug); overflow-wrap: anywhere; }

/* 3 ── sticky section nav */
.gu-prog-nav {
  position: sticky; top: var(--gu-nav-header-h); z-index: 40;
  background: color-mix(in srgb, var(--gu-bg) 92%, transparent); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--gu-rule);
}
.gu-prog-nav.is-stuck { box-shadow: var(--gu-shadow-sm); }
.gu-prog-nav__inner { display: flex; align-items: center; gap: var(--gu-space-sm); padding-block: var(--gu-space-2xs); max-width: var(--gu-max-width); margin-inline: auto; padding-inline: var(--gu-gutter); }
.gu-prog-nav__list {
  display: flex; gap: var(--gu-space-3xs); list-style: none; margin: 0; padding: 0;
  overflow-x: auto; scrollbar-width: none; flex: 1 1 auto; min-width: 0;
  mask-image: linear-gradient(90deg, #000 88%, transparent);
}
.gu-prog-nav__list::-webkit-scrollbar { display: none; }
.gu-prog-nav__list a {
  display: inline-block; white-space: nowrap; padding: 0.45em 0.9em; border-radius: var(--gu-radius-pill);
  color: var(--gu-fg-muted); text-decoration: none; font-size: var(--gu-text-sm); font-weight: 600;
}
.gu-prog-nav__list a:hover, .gu-prog-nav__list a[aria-current="true"] { color: var(--gu-fg); background: var(--gu-bg-raised); }
.gu-prog-nav__cta { flex: none; padding-block: 0.5em; font-size: var(--gu-text-sm); }
@media (max-width: 520px) { .gu-prog-nav__cta { display: none; } }

/* 4 ── built for */
.gu-prog-builtfor { display: grid; gap: var(--gu-space-xl); align-items: center; }
.gu-prog-builtfor__lead {
  font-family: var(--gu-font-display); font-size: var(--gu-text-2xl); line-height: var(--gu-leading-snug);
  margin: 0; max-width: 34ch; text-wrap: balance;
}
.gu-prog-builtfor__media { margin: 0; border-radius: var(--gu-radius-lg); overflow: hidden; }
.gu-prog-builtfor__media img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }
@container band (min-width: 820px) { .gu-prog-builtfor { grid-template-columns: 1.1fr 0.9fr; } }

/* 5a ── stats */
.gu-prog-stats { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.gu-prog-stat { padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border-top: 3px solid var(--gu-accent); }
.gu-prog-stat__value { font-family: var(--gu-font-display); font-size: var(--gu-text-4xl); line-height: 1; margin: 0 0 var(--gu-space-2xs); color: var(--gu-accent); }
.gu-prog-stat__label { font-weight: 700; margin: 0 0 var(--gu-space-xs); }
.gu-prog-stat__text { margin: 0; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }

/* 5 ── outcomes */
.gu-prog-outcomes { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.gu-prog-outcome { position: relative; padding: var(--gu-space-lg); border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-lg); display: grid; gap: var(--gu-space-sm); align-content: start; }
.gu-prog-outcome__num { position: absolute; inset: var(--gu-space-md) var(--gu-space-md) auto auto; font-family: var(--gu-font-display); font-size: var(--gu-text-xl); color: var(--gu-fg-muted); opacity: 0.5; }
.gu-prog-outcome__icon { display: grid; place-items: center; width: 3rem; height: 3rem; border-radius: 50%; background: var(--gu-bg-raised); color: var(--gu-accent); }
.gu-prog-outcome__icon .gu-icon { width: 1.75rem; height: 1.75rem; }
.gu-prog-outcome__text { margin: 0; font-weight: 600; line-height: var(--gu-leading-snug); font-size: var(--gu-text-lg); }

/* 6 ── bento */
.gu-prog-bento { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); }
.gu-prog-bento__card { padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); display: grid; gap: var(--gu-space-xs); align-content: start; }
.gu-prog-bento__icon { color: var(--gu-accent); }
.gu-prog-bento__icon .gu-icon { width: 2.25rem; height: 2.25rem; }
.gu-prog-bento__title { margin: 0; font-size: var(--gu-text-lg); line-height: var(--gu-leading-snug); text-transform: none; letter-spacing: 0; }
.gu-prog-bento__text { margin: 0; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
@container band (min-width: 760px) {
  .gu-prog-bento { grid-template-columns: repeat(3, 1fr); }
  .gu-prog-bento__card--wide { grid-column: span 2; background: linear-gradient(135deg, var(--gu-bg-raised), color-mix(in srgb, var(--gu-accent) 14%, var(--gu-bg-raised))); }
  .gu-prog-bento__card--wide .gu-prog-bento__title { font-size: var(--gu-text-2xl); }
}

/* 6b ── audiences */
.gu-prog-audiences { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.gu-prog-audience { padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); border: 1px solid var(--gu-rule); }
.gu-prog-audience h3 { margin: var(--gu-space-xs) 0; font-size: var(--gu-text-lg); text-transform: none; letter-spacing: 0; }
.gu-prog-audience p { margin: 0; color: var(--gu-fg-muted); }
.gu-prog-audience__icon { color: var(--gu-accent); }

/* 7 ── track */
.gu-prog-track { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-lg); position: relative; }
.gu-prog-track__step { position: relative; display: grid; grid-template-columns: 3.5rem 1fr; column-gap: var(--gu-space-md); }
.gu-prog-track__step::before { content: ""; position: absolute; left: 1.75rem; top: 3.5rem; bottom: calc(-1 * var(--gu-space-lg)); width: 2px; background: var(--gu-rule); }
.gu-prog-track__step:last-child::before { display: none; }
.gu-prog-track__marker { grid-row: span 2; position: relative; display: grid; place-items: center; width: 3.5rem; height: 3.5rem; border-radius: 50%; background: var(--gu-accent); color: var(--gu-ink); }
.gu-prog-track__n { position: absolute; inset: -0.35rem -0.35rem auto auto; display: grid; place-items: center; width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--gu-bg); color: var(--gu-fg); font-size: var(--gu-text-xs); font-weight: 700; border: 1px solid var(--gu-rule); }
.gu-prog-track__title { margin: 0.35rem 0 var(--gu-space-3xs); font-size: var(--gu-text-lg); text-transform: none; letter-spacing: 0; }
.gu-prog-track__text { margin: 0; color: var(--gu-fg-muted); }
@container band (min-width: 900px) {
  .gu-prog-track { grid-template-columns: repeat(var(--steps, 3), minmax(0, 1fr)); gap: var(--gu-space-md); }
  .gu-prog-track__step { grid-template-columns: 1fr; row-gap: var(--gu-space-xs); }
  .gu-prog-track__step::before { left: 3.5rem; right: calc(-1 * var(--gu-space-md)); top: 1.75rem; bottom: auto; width: auto; height: 2px; }
  .gu-prog-track__marker { grid-row: auto; }
}

/* 7b ── lists */
.gu-prog-lists { display: grid; gap: var(--gu-space-xl); }
.gu-prog-list__title { font-size: var(--gu-text-xl); margin: 0 0 var(--gu-space-md); }
@container band (min-width: 820px) { .gu-prog-lists { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } }

/* 8 ── video + rail */
/* The program page's featured video + rail is the shared watch gallery now
   (.gu-watch, board item 145) — styled once, with every other YouTube player. */

/* 9 ── instructor */
.gu-prog-instructor { display: grid; gap: var(--gu-space-xl); align-items: center; }
.gu-prog-instructor__photo { margin: 0; max-width: 22rem; border-radius: var(--gu-radius-lg); overflow: hidden; box-shadow: var(--gu-shadow-md); }
.gu-prog-instructor__photo img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; object-position: 50% 20%; }
/* Item 144: this carried margin-top: calc(-1 * space-sm), which pulled the role
   16px up INTO the display-size name above it at every width. */
.gu-prog-instructor__role { color: var(--gu-accent); font-weight: 700; margin-top: 0; }
.gu-prog-instructor__copy p { max-width: var(--gu-measure); }
@container band (min-width: 780px) { .gu-prog-instructor { grid-template-columns: minmax(0, 22rem) 1fr; gap: var(--gu-space-2xl); } }

/* 9b ── quote */
.gu-prog-quote { margin: 0 auto; max-width: 52rem; text-align: center; }
.gu-prog-quote__mark { color: var(--gu-accent); }
.gu-prog-quote__mark .gu-icon { width: 2.5rem; height: 2.5rem; }
.gu-prog-quote blockquote { margin: var(--gu-space-sm) 0; }
.gu-prog-quote blockquote p { font-family: var(--gu-font-display); font-size: var(--gu-text-xl); line-height: var(--gu-leading-snug); }
.gu-prog-quote figcaption { font-weight: 700; color: var(--gu-fg-muted); }

/* 10 ── reviews */
.gu-prog-reviews__head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: var(--gu-space-md); }
.gu-prog-rating { display: inline-grid; grid-template-columns: auto auto; column-gap: var(--gu-space-xs); align-items: center; padding: var(--gu-space-xs) var(--gu-space-md); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); color: var(--gu-fg); text-decoration: none; }
.gu-prog-rating__score { grid-row: span 2; font-family: var(--gu-font-display); font-size: var(--gu-text-3xl); line-height: 1; }
.gu-prog-rating__stars { letter-spacing: 2px; }
.gu-prog-rating__count { font-size: var(--gu-text-xs); color: var(--gu-fg-muted); }
.gu-prog-carousel { display: grid; gap: var(--gu-space-sm); }
.gu-prog-carousel__track {
  list-style: none; margin: 0; padding: 0 0 var(--gu-space-xs); display: grid; grid-auto-flow: column;
  grid-auto-columns: min(88%, 24rem); gap: var(--gu-space-md); overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
}
.gu-prog-carousel__track:focus-visible { outline: var(--gu-focus-width) solid var(--gu-focus); outline-offset: var(--gu-focus-offset); }
.gu-prog-review { scroll-snap-align: start; padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); display: grid; gap: var(--gu-space-xs); align-content: start; }
.gu-prog-review__text { margin: 0; }
.gu-prog-review__text p { margin: 0; }
.gu-prog-review__who { margin: 0; font-weight: 700; }
.gu-prog-review__who a { color: var(--gu-fg); }
.gu-prog-carousel__controls { display: flex; gap: var(--gu-space-xs); justify-content: flex-end; }
.gu-prog-carousel__btn { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: 50%; border: 1px solid var(--gu-rule); background: var(--gu-bg); color: var(--gu-fg); cursor: pointer; }
.gu-prog-carousel__btn:disabled { opacity: 0.35; cursor: default; }

/* 11 ── compare */
.gu-prog-compare { overflow-x: auto; border-radius: var(--gu-radius-lg); border: 1px solid var(--gu-rule); }
.gu-prog-compare__table { width: 100%; border-collapse: collapse; font-size: var(--gu-text-sm); }
.gu-prog-compare__table th, .gu-prog-compare__table td { text-align: left; padding: var(--gu-space-sm) var(--gu-space-md); border-bottom: 1px solid var(--gu-rule); vertical-align: top; }
.gu-prog-compare__table thead th { font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--gu-fg-muted); }
.gu-prog-compare__table tbody th a { color: var(--gu-fg); font-weight: 700; }
.gu-prog-compare__current { background: color-mix(in srgb, var(--gu-accent) 12%, transparent); }
.gu-prog-compare__here { display: inline-block; margin-top: var(--gu-space-3xs); padding: 0.1em 0.6em; border-radius: var(--gu-radius-pill); background: var(--gu-accent); color: var(--gu-ink); font-size: var(--gu-text-xs); font-weight: 700; white-space: nowrap; }
@container band (max-width: 640px) {
  .gu-prog-compare { overflow: visible; border: 0; }
  .gu-prog-compare__table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .gu-prog-compare__table, .gu-prog-compare__table tbody, .gu-prog-compare__table tr, .gu-prog-compare__table th, .gu-prog-compare__table td { display: block; }
  .gu-prog-compare__table tr { border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-lg); margin-bottom: var(--gu-space-sm); overflow: hidden; }
  .gu-prog-compare__table td { border-bottom: 0; padding-top: 0; }
  .gu-prog-compare__table td::before { content: attr(data-label); display: block; font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--gu-fg-muted); }
}

/* 12 ── investment */
.gu-prog-invest { display: grid; gap: var(--gu-space-md); justify-items: center; text-align: center; }
.gu-prog-invest__card { width: 100%; max-width: 46rem; padding: var(--gu-space-xl); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid color-mix(in srgb, var(--gu-accent) 45%, var(--gu-rule)); box-shadow: var(--gu-shadow-md); }
.gu-prog-invest__icon { color: var(--gu-accent); }
.gu-prog-invest__icon .gu-icon { width: 2.5rem; height: 2.5rem; }
.gu-prog-invest__title { margin: var(--gu-space-2xs) 0 var(--gu-space-sm); font-size: var(--gu-text-2xl); }
.gu-prog-invest__card .gu-btn-row { justify-content: center; flex-wrap: wrap; }
.gu-prog-invest__card .gu-btn { overflow-wrap: anywhere; }
.gu-prog-invest__addon { color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-prog-invest__line { font-family: var(--gu-font-display); font-size: var(--gu-text-xl); text-transform: uppercase; letter-spacing: 0.04em; margin: 0; }

/* 13 ── faq */
.gu-prog-faq { display: grid; gap: var(--gu-space-xs); max-width: 52rem; }
.gu-prog-faq__item { border: 1px solid var(--gu-rule); border-radius: var(--gu-radius-md); background: var(--gu-bg-raised); }
.gu-prog-faq__item summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; gap: var(--gu-space-sm); align-items: center; padding: var(--gu-space-md); font-weight: 700; }
.gu-prog-faq__item summary::-webkit-details-marker { display: none; }
.gu-prog-faq__item summary .gu-icon { flex: none; transition: transform var(--gu-dur-fast) var(--gu-ease); }
.gu-prog-faq__item[open] summary .gu-icon { transform: rotate(180deg); }
.gu-prog-faq__answer { padding: 0 var(--gu-space-md) var(--gu-space-md); }
.gu-prog-faq__answer > :last-child { margin-bottom: 0; }

/* 14 ── mosaic + lightbox */
.gu-prog-mosaic { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-sm); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gu-prog-mosaic__tile a { display: block; height: 100%; border-radius: var(--gu-radius-md); overflow: hidden; }
.gu-prog-mosaic__tile img { display: block; width: 100%; height: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: transform var(--gu-dur-base) var(--gu-ease); }
.gu-prog-mosaic__tile a:hover img { transform: scale(1.04); }
@container band (min-width: 760px) {
  .gu-prog-mosaic { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .gu-prog-mosaic__tile:first-child { grid-column: span 2; grid-row: span 2; }
}
.gu-lightbox { padding: 0; border: 0; background: transparent; max-width: min(94vw, 1400px); max-height: 92vh; }
.gu-lightbox::backdrop { background: rgb(0 0 0 / 0.85); }
.gu-lightbox__img { display: block; max-width: 100%; max-height: 92vh; object-fit: contain; border-radius: var(--gu-radius-md); }
.gu-lightbox__close { position: absolute; inset: var(--gu-space-xs) var(--gu-space-xs) auto auto; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: 50%; border: 0; background: rgb(0 0 0 / 0.6); color: #fff; cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .gu-prog-hero__play, .gu-prog-mosaic__tile img, .gu-prog-faq__item summary .gu-icon { transition: none; }
}

/* Every facade player, not only the partner profile's. feed.js replaces the
   facade with a bare <iframe>, which is 300×150 by default; the only rule for
   it was scoped to .gu-profile__video, so clicking play on any /videos/ detail
   page — and on the program pages — shrank the player to a thumbnail. Found by
   clicking play on /programs/certified-closer/ (2026-09-16). */
.gu-video iframe { display: block; width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0; border-radius: inherit; }

/* ── Watch gallery (board item 145) ──────────────────────────────────────────
   Owner, 2026-09-17: YouTube videos play ON the site and never link out. Every
   YouTube player — both renderers — is a <button class="gu-embed gu-video__facade">
   that src/runtime/watch.js swaps for a youtube-nocookie iframe in place. A
   gallery adds a rail; selecting an entry swaps the main player, no navigation.

   CRITICAL: the facade is a BUTTON now, so the UA button styles must be undone here or
   it renders as a grey bordered box with centered text. `.gu-embed` gives it
   the 16/9 frame and `display: block` (trap 31). */
.gu-video__facade {
  appearance: none; width: 100%; margin: 0; padding: 0; border: 0;
  font: inherit; color: var(--gu-fg); text-align: inherit; cursor: pointer;
  background: var(--gu-bg-raised);
}
.gu-video__facade:focus-visible { outline: 3px solid var(--gu-accent); outline-offset: 3px; }
.gu-video__facade:focus-visible .gu-video__play { transform: scale(1.06); }
/* With scripting off the button is a dead control; the <noscript> iframe beside
   it plays the same video on the page instead. */
@media (scripting: none) { .gu-video__facade { display: none; } }
.gu-watch__player { border-radius: var(--gu-radius-md); overflow: hidden; background: var(--gu-bg-raised); }
/* watch.js scrolls a swapped player into view; clear the fixed header and the
   program page's sticky section nav rather than tucking the player under them. */
.gu-watch__player { scroll-margin-top: 9rem; }

.gu-watch { display: grid; gap: var(--gu-space-md); min-width: 0; }
.gu-watch__stage { min-width: 0; }
.gu-watch__now {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--gu-space-2xs) var(--gu-space-sm);
  margin: var(--gu-space-sm) 0 0;
}
.gu-watch__title { font-weight: 600; color: var(--gu-fg); overflow-wrap: anywhere; }
.gu-watch__len, .gu-watch__itemlen { font-variant-numeric: tabular-nums; font-size: var(--gu-text-xs); }
.gu-watch__len { color: var(--gu-fg-muted); }
.gu-watch__page { font-size: var(--gu-text-sm); color: var(--gu-accent); }
.gu-watch__side { min-width: 0; }
.gu-watch__railtitle {
  margin: 0 0 var(--gu-space-xs);
  font-family: var(--gu-font-display); font-size: var(--gu-text-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--gu-fg-muted);
}
/* Phones: player on top, the rail a horizontal scroll-snap strip under it. The
   strip scrolls inside itself, so the page never spills sideways. */
.gu-watch__rail {
  list-style: none; margin: 0; padding: 0 0 var(--gu-space-2xs);
  display: grid; grid-auto-flow: column; grid-auto-columns: min(72%, 15rem);
  gap: var(--gu-space-sm);
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
  scrollbar-width: thin; scrollbar-color: var(--gu-rule) transparent;
}
.gu-watch__rail > li { min-width: 0; scroll-snap-align: start; }
.gu-watch__item {
  display: grid; gap: var(--gu-space-2xs); align-content: start;
  padding: var(--gu-space-3xs); border-radius: var(--gu-radius-md);
  color: var(--gu-fg); text-decoration: none;
}
.gu-watch__item:hover .gu-watch__itemtitle { text-decoration: underline; }
.gu-watch__item:focus-visible { outline: 3px solid var(--gu-accent); outline-offset: 2px; }
.gu-watch__item[aria-current] { background: var(--gu-bg-raised); box-shadow: inset 0 0 0 2px var(--gu-accent); }
.gu-watch__thumb {
  position: relative; display: block; aspect-ratio: 16 / 9;
  border-radius: var(--gu-radius-sm); overflow: hidden; background: var(--gu-bg-raised);
}
.gu-watch__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.gu-watch__badge {
  position: absolute; inset: 0; margin: auto; width: 2.25rem; height: 2.25rem;
  display: grid; place-items: center; border-radius: 50%;
  background: rgb(0 0 0 / 0.6); color: #fff; opacity: 0;
  transition: opacity var(--gu-dur-fast) var(--gu-ease);
}
.gu-watch__badge .gu-icon { width: 45%; height: 45%; fill: currentColor; stroke: none; }
.gu-watch__item:hover .gu-watch__badge,
.gu-watch__item:focus-visible .gu-watch__badge,
.gu-watch__item[aria-current] .gu-watch__badge { opacity: 1; }
.gu-watch__item[aria-current] .gu-watch__badge { background: var(--gu-accent); color: var(--gu-bg); }
.gu-watch__itemlen {
  position: absolute; inset: auto 0.3rem 0.3rem auto; padding: 0.1em 0.4em;
  border-radius: var(--gu-radius-sm); background: rgb(0 0 0 / 0.75); color: #fff;
}
.gu-watch__itemtitle {
  font-size: var(--gu-text-sm); font-weight: 600; line-height: var(--gu-leading-snug);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Wide bands: the rail becomes a column beside the player, scrolling itself. */
@container band (min-width: 900px) {
  .gu-watch--rail { grid-template-columns: minmax(0, 1.9fr) minmax(0, 1fr); align-items: start; gap: var(--gu-space-lg); }
  .gu-watch--rail .gu-watch__rail {
    grid-auto-flow: row; grid-auto-columns: auto; gap: var(--gu-space-2xs);
    max-height: 30rem; overflow-x: hidden; overflow-y: auto;
    scroll-snap-type: y proximity; overscroll-behavior-y: contain;
  }
  .gu-watch--rail .gu-watch__item { grid-template-columns: 8.5rem minmax(0, 1fr); align-items: center; gap: var(--gu-space-sm); }
  .gu-watch--rail .gu-watch__itemtitle { -webkit-line-clamp: 3; }
}
@media (prefers-reduced-motion: reduce) { .gu-watch__badge { transition: none; } }

/* 15 ── package tiers (Experiences) */
.gu-prog-tiers { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); align-items: stretch; }
.gu-prog-tier { position: relative; display: grid; grid-template-rows: auto auto auto 1fr auto; gap: var(--gu-space-xs); padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); border: 1px solid var(--gu-rule); background: var(--gu-bg-raised); }
.gu-prog-tier--featured { border: 2px solid var(--gu-accent); box-shadow: var(--gu-shadow-md); }
.gu-prog-tier__n { margin: 0; font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.1em; color: var(--gu-accent); font-weight: 700; }
.gu-prog-tier__name { margin: 0; font-size: var(--gu-text-2xl); text-transform: none; letter-spacing: 0; }
.gu-prog-tier__lead { margin: 0; color: var(--gu-fg-muted); }
.gu-prog-tier__cta { margin: var(--gu-space-sm) 0 0; }
.gu-prog-tier__cta .gu-btn { width: 100%; justify-content: center; }

/* 16 ── upcoming dates (Masterclass) */
.gu-prog-dates { list-style: none; margin: 0 0 var(--gu-space-lg); padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.gu-prog-date { padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); border: 1px solid var(--gu-rule); background: var(--gu-bg-raised); }
.gu-prog-date--next { border: 2px solid var(--gu-accent); }
.gu-prog-date__badge { display: inline-block; margin: 0 0 var(--gu-space-xs); padding: 0.15em 0.7em; border-radius: var(--gu-radius-pill); background: var(--gu-accent); color: var(--gu-ink); font-size: var(--gu-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; }
.gu-prog-date__when { margin: 0; font-family: var(--gu-font-display); font-size: var(--gu-text-lg); line-height: var(--gu-leading-snug); }
.gu-prog-date__meta { margin: var(--gu-space-2xs) 0 0; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-prog-tier .gu-checklist { align-content: start; }

/* ══ Item 144 — rhythm fixes from the 2026-09-17 audit ═══════════════════════
   (80-qa/audit-program-events-2026-09-17.md P5–P9). Each rule answers a
   screenshot, not a preference. */

/* P5: a track step whose text is one line sat ~10px lower than its neighbours —
   the step grid stretched its auto rows to the tallest sibling. */
.gu-prog-track__step { align-content: start; }

/* P6: the carousel's native scrollbar painted a bright bar under dark cards. */
.gu-prog-carousel__track { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--gu-accent) 70%, transparent) transparent; }

/* P7: five or seven outcomes left an orphan card alone on the last row. */
@container band (min-width: 900px) {
  .gu-prog-outcomes:has(> li:nth-child(5):last-child) { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .gu-prog-outcomes:has(> li:nth-child(5):last-child) > li { grid-column: span 2; }
  .gu-prog-outcomes:has(> li:nth-child(5):last-child) > li:nth-child(n+4) { grid-column: span 3; }
  .gu-prog-outcomes:has(> li:nth-child(6):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .gu-prog-outcomes:has(> li:nth-child(7):last-child) { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .gu-prog-outcomes:has(> li:nth-child(7):last-child) > li { grid-column: span 3; }
  .gu-prog-outcomes:has(> li:nth-child(7):last-child) > li:nth-child(n+5) { grid-column: span 4; }
}

/* P8: the bento's wide first card makes n+1 cells in three columns, so 3, 4, 6
   and 7 deliverables left an empty cell. The last card takes what is left. */
@container band (min-width: 760px) {
  .gu-prog-bento:has(> li:nth-child(3n):last-child) > li:last-child { grid-column: span 3; }
  .gu-prog-bento:has(> li:nth-child(3n+4):last-child) > li:last-child { grid-column: span 2; }
}

/* P9: on two columns a 3- or 9-tile mosaic left a hole; the lead tile spans. */
@container band (max-width: 759px) {
  .gu-prog-mosaic:has(> li:nth-child(odd):last-child) > li:first-child { grid-column: span 2; }
}

/* 12b ── closing line (programs with no investment card) */
.gu-prog-closer { display: grid; justify-items: center; gap: var(--gu-space-sm); text-align: center; max-width: 52rem; margin-inline: auto; }
.gu-prog-closer__icon { display: grid; place-items: center; width: 3.5rem; height: 3.5rem; border-radius: 50%; background: var(--gu-bg-raised); color: var(--gu-accent); }
.gu-prog-closer__icon .gu-icon { width: 1.75rem; height: 1.75rem; }
.gu-prog-closer__line { margin: 0; font-family: var(--gu-font-display); font-size: var(--gu-text-2xl); line-height: var(--gu-leading-snug); text-transform: uppercase; letter-spacing: 0.02em; text-wrap: balance; }
.gu-prog-closer .gu-btn-row { justify-content: center; flex-wrap: wrap; }

/* ══ EVENT PAGES v2 (item 144) ══════════════════════════════════════════════
   The program components carry the page (tmpl/event.html); these are only the
   pieces an event has and a program does not. Same tokens, same container
   queries, no viewport widths. */
.gu-evt-hero__eyebrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gu-space-xs); margin: 0 0 var(--gu-space-xs); }
.gu-evt-hero__eyebrow .gu-eyebrow { margin: 0; }
.gu-evt-status { display: inline-block; padding: 0.15em 0.7em; border-radius: var(--gu-radius-pill); background: var(--gu-accent); color: var(--gu-ink); font-size: var(--gu-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; }
.gu-evt-status--past { background: transparent; color: var(--gu-fg-muted); box-shadow: inset 0 0 0 1px var(--gu-rule); }
.gu-evt-hero__meta { list-style: none; margin: 0 0 var(--gu-space-md); padding: 0; display: grid; gap: var(--gu-space-2xs); }
.gu-evt-hero__meta li { display: flex; gap: var(--gu-space-xs); align-items: flex-start; font-weight: 600; min-width: 0; }
.gu-evt-hero__meta .gu-icon { flex: none; color: var(--gu-accent); }
.gu-evt-hero__meta span { overflow-wrap: anywhere; }
.gu-evt-hero__lang { margin: var(--gu-space-sm) 0 0; font-size: var(--gu-text-sm); }
.gu-evt-hero__lang a { display: inline-flex; gap: var(--gu-space-2xs); align-items: center; color: var(--gu-fg-muted); }
.gu-evt-hero__lang .gu-icon { width: 1.25rem; height: 1.25rem; }
.gu-evt-hero__badge {
  position: absolute; inset: var(--gu-space-md) auto auto var(--gu-space-md);
  display: grid; justify-items: center; min-width: 4.25rem; padding: var(--gu-space-2xs) var(--gu-space-xs);
  border-radius: var(--gu-radius-md); background: var(--gu-accent); color: var(--gu-ink); box-shadow: var(--gu-shadow-md);
  font-family: var(--gu-font-display); line-height: 1;
}
.gu-evt-hero__month { font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.12em; font-weight: 700; }
.gu-evt-hero__day { font-size: var(--gu-text-3xl); font-weight: 800; }

/* overview: the client's copy, a photo beside it at width */
.gu-evt-overview { display: grid; gap: var(--gu-space-xl); align-items: start; }
.gu-evt-overview__text { max-width: var(--gu-measure); }
.gu-evt-overview__text p { font-size: var(--gu-text-lg); line-height: var(--gu-leading-body); }
.gu-evt-overview__media { position: sticky; top: calc(var(--gu-nav-header-h) + 5rem); }
@container band (min-width: 880px) { .gu-evt-overview--media { grid-template-columns: 1.15fr 0.85fr; gap: var(--gu-space-2xl); } }
.gu-evt-audience { margin-top: var(--gu-space-lg); }
.gu-evt-audience__title { margin: 0 0 var(--gu-space-xs); font-family: var(--gu-font-display); font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; font-size: var(--gu-text-sm); color: var(--gu-accent); }
.gu-evt-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--gu-space-2xs); }
.gu-evt-chips li { padding: 0.35em 0.9em; border-radius: var(--gu-radius-pill); border: 1px solid var(--gu-rule); background: var(--gu-bg-raised); font-size: var(--gu-text-sm); font-weight: 600; }
.gu-evt-chips--center { justify-content: center; margin-block: var(--gu-space-sm); }

/* tracks */
.gu-evt-tracks__n { display: block; font-family: var(--gu-font-display); font-size: var(--gu-text-2xl); color: var(--gu-accent); line-height: 1; }

/* agenda: ten sessions, two columns at width, five at wide */
.gu-evt-agenda { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-sm); grid-template-columns: repeat(auto-fit, minmax(min(100%, 12.5rem), 1fr)); }
.gu-evt-agenda__day { position: relative; padding: var(--gu-space-md); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid var(--gu-rule); min-width: 0; }
.gu-evt-agenda__n { display: grid; margin-bottom: var(--gu-space-xs); place-items: center; width: 2.25rem; height: 2.25rem; border-radius: 50%; background: var(--gu-accent); color: var(--gu-ink); font-weight: 800; font-size: var(--gu-text-sm); }
.gu-evt-agenda__label { margin: 0; font-size: var(--gu-text-xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--gu-fg-muted); }
.gu-evt-agenda__step { margin: var(--gu-space-3xs) 0 0; font-size: var(--gu-text-base); line-height: var(--gu-leading-snug); text-transform: none; letter-spacing: 0; overflow-wrap: break-word; hyphens: auto; }
@container band (min-width: 1000px) { .gu-evt-agenda { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

/* speakers grid */
.gu-evt-speakers { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fill, minmax(min(100%, 9rem), 1fr)); align-items: start; }
.gu-evt-speaker { text-align: center; min-width: 0; }
.gu-evt-speaker__photo { display: block; aspect-ratio: 4 / 5; border-radius: var(--gu-radius-lg); overflow: hidden; background: var(--gu-bg-raised); margin-bottom: var(--gu-space-xs); }
.gu-evt-speaker__photo img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; }
.gu-evt-speaker__photo--initials { display: grid; place-items: center; width: 5rem; aspect-ratio: 1; margin-inline: auto; border-radius: 50%; font-family: var(--gu-font-display); font-size: var(--gu-text-xl); font-weight: 800; color: var(--gu-accent); border: 1px solid var(--gu-rule); }
.gu-evt-speaker__name { margin: 0; font-weight: 700; overflow-wrap: anywhere; }
.gu-evt-speaker__name a { color: var(--gu-fg); }
.gu-evt-speaker__role { margin: var(--gu-space-3xs) 0 0; font-size: var(--gu-text-sm); color: var(--gu-fg-muted); overflow-wrap: anywhere; }

/* venue */
.gu-evt-venue { display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); }
.gu-evt-venue__card { padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid var(--gu-rule); min-width: 0; }
.gu-evt-venue__icon { color: var(--gu-accent); }
.gu-evt-venue__icon .gu-icon { width: 2.25rem; height: 2.25rem; }
.gu-evt-venue__name { margin: var(--gu-space-xs) 0; font-size: var(--gu-text-xl); text-transform: none; letter-spacing: 0; }
.gu-evt-venue__address { font-style: normal; color: var(--gu-fg-muted); overflow-wrap: anywhere; }
.gu-evt-venue__mode { display: flex; gap: var(--gu-space-2xs); align-items: center; margin: var(--gu-space-sm) 0 0; font-weight: 600; }
.gu-evt-venue__mode .gu-icon { color: var(--gu-accent); width: 1.25rem; height: 1.25rem; }
.gu-evt-venue__hotels { margin: 0; padding-left: 1.2em; display: grid; gap: var(--gu-space-2xs); }
.gu-evt-venue__hotels span { display: block; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }

/* registration / what's next */
.gu-evt-register__when { font-weight: 700; margin: 0 0 var(--gu-space-xs); overflow-wrap: anywhere; }
.gu-evt-register__note { color: var(--gu-fg-muted); font-size: var(--gu-text-sm); max-width: 44ch; margin-inline: auto; }
.gu-evt-register__program { margin: var(--gu-space-md) 0 0; padding-top: var(--gu-space-sm); border-top: 1px solid var(--gu-rule); font-size: var(--gu-text-sm); color: var(--gu-fg-muted); }
.gu-evt-register__program a { display: inline-flex; align-items: center; gap: var(--gu-space-3xs); font-weight: 700; color: var(--gu-fg); }
.gu-evt-register__program .gu-icon { width: 1rem; height: 1rem; }

/* /events/ index: the next event, featured */
.gu-evt-feature { display: grid; gap: var(--gu-space-lg); align-items: center; margin-bottom: var(--gu-space-xl); padding: var(--gu-space-md); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid color-mix(in srgb, var(--gu-accent) 45%, var(--gu-rule)); color: var(--gu-fg); text-decoration: none; }
.gu-evt-feature__media { margin: 0; border-radius: var(--gu-radius-md); overflow: hidden; }
.gu-evt-feature__media img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: cover; }
.gu-evt-feature__copy { display: grid; gap: var(--gu-space-xs); align-content: center; min-width: 0; }
.gu-evt-feature__title { margin: 0; font-size: var(--gu-text-3xl); }
.gu-evt-feature__meta { margin: 0; color: var(--gu-accent); font-weight: 700; overflow-wrap: anywhere; }
.gu-evt-feature__text { margin: 0; color: var(--gu-fg-muted); }
.gu-evt-feature .gu-btn { justify-self: start; }
@container band (min-width: 820px) { .gu-evt-feature { grid-template-columns: 1.1fr 0.9fr; padding: var(--gu-space-lg); gap: var(--gu-space-2xl); } }

/* /programs/ index: grouped */
.gu-prog-index__group + .gu-prog-index__group { margin-top: var(--gu-space-2xl); }
.gu-prog-index__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--gu-space-xs); margin-bottom: var(--gu-space-md); padding-bottom: var(--gu-space-xs); border-bottom: 1px solid var(--gu-rule); }
.gu-prog-index__title { margin: 0; font-size: var(--gu-text-2xl); }
.gu-prog-index__blurb { margin: 0; color: var(--gu-fg-muted); }

/* Price table + priced investment card (decision 22 = C, item 142). Three plans
   side by side at desktop, stacked on phones; the page's own plan is marked. */
.gu-prog-invest__price { margin: var(--gu-space-xs) 0 0; font-family: var(--gu-font-display); line-height: 1; }
.gu-prog-invest__amount { font-size: var(--gu-text-4xl); color: var(--gu-accent); }
.gu-prog-invest__per { font-size: var(--gu-text-lg); color: var(--gu-fg-muted); margin-left: var(--gu-space-2xs); }
.gu-price-table { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gu-space-md); grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); align-items: stretch; }
.gu-price-table__plan { display: flex; flex-direction: column; gap: var(--gu-space-xs); padding: var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid var(--gu-rule); box-shadow: var(--gu-shadow-md); min-width: 0; }
.gu-price-table__plan--current { border-color: var(--gu-accent); box-shadow: 0 0 0 2px var(--gu-accent), var(--gu-shadow-md); }
.gu-price-table__name { margin: 0; font-size: var(--gu-text-xl); overflow-wrap: anywhere; }
.gu-price-table__name a { color: inherit; }
.gu-price-table__here { margin: 0; color: var(--gu-accent); font-size: var(--gu-text-sm); text-transform: uppercase; letter-spacing: 0.06em; }
.gu-price-table__price { margin: var(--gu-space-xs) 0 0; font-family: var(--gu-font-display); line-height: 1; }
.gu-price-table__amount { font-size: var(--gu-text-3xl); color: var(--gu-accent); }
.gu-price-table__per { font-size: var(--gu-text-base); color: var(--gu-fg-muted); margin-left: var(--gu-space-2xs); }
.gu-price-table__terms { margin: 0; font-weight: 600; }
.gu-price-table__includes { margin: var(--gu-space-xs) 0 0; padding-left: 1.2em; display: grid; gap: var(--gu-space-2xs); font-size: var(--gu-text-sm); }
.gu-price-table__note { margin: var(--gu-space-xs) 0 0; color: var(--gu-fg-muted); font-size: var(--gu-text-sm); }
.gu-price-table__cta { margin: auto 0 0; padding-top: var(--gu-space-sm); }
.gu-price-table__cta .gu-btn { width: 100%; justify-content: center; }

/* Investment card email: read as an address, not a label (item 144, P11). */
.gu-prog-invest__card .gu-prog-invest__email { text-transform: none; letter-spacing: 0; overflow-wrap: normal; }

/* A one-card listing does not stretch its lone card across the band: on
   /es/events/ a 316px cut-out was upscaled to 1158px (item 144, audit E7). */
.gu-grid--3.gu-cardlist > .gu-card:only-child { max-width: 26rem; }

/* A partner logo drawn only for a light ground (Siro's black wordmark) gets a
   light plate on the dark card, or it disappears (board item 150). */
.gu-card--partners .gu-card__media--light img { background: var(--gu-paper); }

/* Partner detail logo plate (board item 151): a consistent box at a readable
   size, on the ground the artwork was drawn for — light for a dark wordmark
   (Siro), the raised dark surface otherwise (Channel Automation's white mark). */
.gu-partner__logo { display: inline-grid; place-items: center; margin: var(--gu-space-md) 0; padding: var(--gu-space-md) var(--gu-space-lg); border-radius: var(--gu-radius-lg); background: var(--gu-bg-raised); border: 1px solid var(--gu-rule); max-width: 100%; }
.gu-partner__logo img { display: block; width: auto; height: auto; max-width: min(320px, 100%); max-height: 72px; }
.gu-partner__logo--light { background: var(--gu-paper); }
