/* ============================================================================
   GovTheme UI - dist/govtheme-ui.css
   GENERATED by scripts/build-bundle.mjs. Do not edit; edit the source files
   listed below and re-run `npm run build:bundle`.

   Cascade order:
      1. fonts/fonts.css
      2. tokens/tokens.css
      3. base/base.css
      4. base/forms.css
      5. components/01-atoms/attachment/attachment.css
      6. components/01-atoms/badge/badge.css
      7. components/01-atoms/button/button.css
      8. components/01-atoms/icon/icon.css
      9. components/01-atoms/link/link.css
     10. components/01-atoms/progress/progress.css
     11. components/01-atoms/spinner/spinner.css
     12. components/01-atoms/tag/tag.css
     13. components/02-molecules/acknowledgement/acknowledgement.css
     14. components/02-molecules/alert/alert.css
     15. components/02-molecules/blockquote/blockquote.css
     16. components/02-molecules/breadcrumb/breadcrumb.css
     17. components/02-molecules/callout/callout.css
     18. components/02-molecules/card/card.css
     19. components/02-molecules/event-card/event-card.css
     20. components/02-molecules/event-list-item/event-list-item.css
     21. components/02-molecules/feature-card/feature-card.css
     22. components/02-molecules/news-list/news-list.css
     23. components/02-molecules/pagination/pagination.css
     24. components/02-molecules/person-card/person-card.css
     25. components/02-molecules/plan-card/plan-card.css
     26. components/02-molecules/search-facets/search-facets.css
     27. components/02-molecules/search-result/search-result.css
     28. components/02-molecules/share/share.css
     29. components/02-molecules/site-alert/site-alert.css
     30. components/02-molecules/stat/stat.css
     31. components/02-molecules/step-card/step-card.css
     32. components/02-molecules/utility-bar/utility-bar.css
     33. components/02-molecules/value/value.css
     34. components/03-organisms/accordion/accordion.css
     35. components/03-organisms/banner/banner.css
     36. components/03-organisms/command-palette/command-palette.css
     37. components/03-organisms/cta-band/cta-band.css
     38. components/03-organisms/drawer/drawer.css
     39. components/03-organisms/hero/hero.css
     40. components/03-organisms/plan-grid/plan-grid.css
     41. components/03-organisms/primary-nav/primary-nav.css
     42. components/03-organisms/site-footer/site-footer.css
     43. components/03-organisms/site-header/site-header.css
     44. components/03-organisms/spotlight/spotlight.css
     45. components/03-organisms/step-grid/step-grid.css
     46. components/03-organisms/table/table.css
     47. components/03-organisms/tabs/tabs.css
     48. base/chrome.css
   ========================================================================== */

/* ==========================================================================
   fonts/fonts.css
   ========================================================================== */

/* ============================================================================
   GovTheme UI - fonts/fonts.css
   All seven families in FONTS, self-hosted (OFL - see LICENSE in this folder).

   Self-hosted rather than pulled from Google Fonts because the customiser lets
   a visitor pick any of the seven at runtime: a <link> would have to request
   all of them on every page whether or not they are used, from a third party,
   on a government site. The files are the same ones the Drupal theme ships
   (doghouse-theme-factory/themes/govtheme/assets/fonts).

   URLs are relative to this file. scripts/build-bundle.mjs rewrites them to
   ./fonts/ when it inlines this into dist/govtheme-ui.css.
   ========================================================================== */

/* Hanken Grotesk — self-hosted (OFL). */
@font-face { font-family: 'Hanken Grotesk'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/hanken-grotesk-400.woff2') format('woff2'); }
@font-face { font-family: 'Hanken Grotesk'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/hanken-grotesk-500.woff2') format('woff2'); }
@font-face { font-family: 'Hanken Grotesk'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/hanken-grotesk-600.woff2') format('woff2'); }
@font-face { font-family: 'Hanken Grotesk'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/hanken-grotesk-700.woff2') format('woff2'); }
@font-face { font-family: 'Hanken Grotesk'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/hanken-grotesk-800.woff2') format('woff2'); }

/* Public Sans — self-hosted (OFL). Civic + Editorial body. */
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/public-sans-400.woff2') format('woff2'); }
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/public-sans-500.woff2') format('woff2'); }
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/public-sans-600.woff2') format('woff2'); }
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/public-sans-700.woff2') format('woff2'); }
@font-face { font-family: 'Public Sans'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/public-sans-800.woff2') format('woff2'); }

/* Source Serif 4 — self-hosted (OFL). Editorial headings. */
@font-face { font-family: 'Source Serif 4'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/source-serif-4-400.woff2') format('woff2'); }
@font-face { font-family: 'Source Serif 4'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/source-serif-4-500.woff2') format('woff2'); }
@font-face { font-family: 'Source Serif 4'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/source-serif-4-600.woff2') format('woff2'); }
@font-face { font-family: 'Source Serif 4'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/source-serif-4-700.woff2') format('woff2'); }

/* Libre Franklin — self-hosted (OFL). */
@font-face { font-family: 'Libre Franklin'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/libre-franklin-400.woff2') format('woff2'); }
@font-face { font-family: 'Libre Franklin'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/libre-franklin-500.woff2') format('woff2'); }
@font-face { font-family: 'Libre Franklin'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/libre-franklin-600.woff2') format('woff2'); }
@font-face { font-family: 'Libre Franklin'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/libre-franklin-700.woff2') format('woff2'); }
@font-face { font-family: 'Libre Franklin'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/libre-franklin-800.woff2') format('woff2'); }

/* Source Sans 3 — self-hosted (OFL). */
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/source-sans-3-400.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/source-sans-3-500.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/source-sans-3-600.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/source-sans-3-700.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/source-sans-3-800.woff2') format('woff2'); }

/* Archivo — self-hosted (OFL). */
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/archivo-400.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/archivo-500.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/archivo-600.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/archivo-700.woff2') format('woff2'); }
@font-face { font-family: 'Archivo'; font-style: normal; font-weight: 800; font-display: swap; src: url('../fonts/archivo-800.woff2') format('woff2'); }

/* Lora — self-hosted (OFL). */
@font-face { font-family: 'Lora'; font-style: normal; font-weight: 400; font-display: swap; src: url('../fonts/lora-400.woff2') format('woff2'); }
@font-face { font-family: 'Lora'; font-style: normal; font-weight: 500; font-display: swap; src: url('../fonts/lora-500.woff2') format('woff2'); }
@font-face { font-family: 'Lora'; font-style: normal; font-weight: 600; font-display: swap; src: url('../fonts/lora-600.woff2') format('woff2'); }
@font-face { font-family: 'Lora'; font-style: normal; font-weight: 700; font-display: swap; src: url('../fonts/lora-700.woff2') format('woff2'); }

/* ==========================================================================
   tokens/tokens.css
   ========================================================================== */

/* tokens/tokens.css
   The --gt-* custom properties: the type, spacing, radius and elevation scales,
   and the Modern look as the :root defaults. Custom properties only, no rules,
   so it is safe to load anywhere (CKEditor included).

   :root IS the Modern look: every value presets/modern.css declares is repeated
   here byte for byte, and scripts/test-tokens.mjs fails if the two drift. A
   page that loads this file with no preset and no engine renders Modern; the
   other looks override it via their preset or the live engine. */

:root {
  /* — Type families (overridden live) — */
  --gt-font-head: 'Hanken Grotesk', system-ui, sans-serif;
  --gt-font-body: 'Hanken Grotesk', system-ui, sans-serif;
  --gt-font-mono: ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* — Type scale (rem, fluid-ish, fixed steps) — */
  --gt-text-2xs: 0.6875rem;  /* 11 */
  --gt-text-xs:  0.75rem;    /* 12 */
  --gt-text-sm:  0.875rem;   /* 14 */
  --gt-text-md:  1rem;       /* 16 */
  --gt-text-lg:  1.125rem;   /* 18 */
  --gt-text-xl:  1.375rem;   /* 22 */
  --gt-text-2xl: 1.75rem;    /* 28 */
  --gt-text-3xl: 2.25rem;    /* 36 */
  --gt-text-4xl: 3rem;       /* 48 */
  --gt-text-5xl: 3.75rem;    /* 60 */

  --gt-leading-tight: 1.1;
  --gt-leading-snug: 1.28;
  --gt-leading-normal: 1.55;
  --gt-leading-relaxed: 1.7;
  --gt-tracking-tight: -0.02em;
  --gt-tracking-normal: 0;
  --gt-tracking-wide: 0.04em;
  --gt-tracking-caps: 0.14em;

  --gt-w-regular: 400;
  --gt-w-medium: 500;
  --gt-w-semibold: 600;
  --gt-w-bold: 700;
  --gt-w-extrabold: 800;
  --gt-head-weight: 800;
  --gt-head-tracking: -0.02em;

  /* — Spacing scale (4px base) — */
  --gt-space-1: 4px;
  --gt-space-2: 8px;
  --gt-space-3: 12px;
  --gt-space-4: 16px;
  --gt-space-5: 20px;
  --gt-space-6: 24px;
  --gt-space-8: 32px;
  --gt-space-10: 40px;
  --gt-space-12: 48px;
  --gt-space-16: 64px;
  --gt-space-20: 80px;

  /* — Density (live; 1 = comfortable, ~.86 = compact) — */
  --gt-density: 1;
  --gt-control-h: calc(44px * var(--gt-density));
  --gt-control-px: calc(18px * var(--gt-density));
  --gt-control-h-sm: calc(36px * var(--gt-density));
  --gt-section-py: calc(var(--gt-space-16) * var(--gt-density));

  /* — Radius (live) — */
  --gt-radius-xs: 4px;
  --gt-radius-sm: 6px;
  --gt-radius-md: 10px;
  --gt-radius-lg: 14px;
  --gt-radius-xl: 20px;
  --gt-radius-pill: 999px;
  --gt-radius-btn: 999px;     /* live lever */
  --gt-radius-card: 14px;     /* live lever */
  --gt-radius-input: 12px;    /* live lever */
  --gt-radius-chip: var(--gt-radius-pill);    /* live lever */

  /* — Border (live) — */
  --gt-border-width: 1px;
  --gt-border-width-strong: 1.5px;

  /* — Colour: Modern light defaults (live) — */
  --gt-bg: #F4F6FB;
  /* A page band: 5 points of oklch L off bg, darker in light mode and lighter
     in dark. The engine derives it from bg. */
  --gt-bg-subtle: #e3e5ea;
  /* The dark band's ground and its brand colour (.gt-band--dark builds the
     rest from these). The engine derives both. */
  --gt-bg-dark: #0C0F16;
  --gt-primary-inverse: #a2a4ac;
  --gt-surface: #FFFFFF;
  --gt-surface-2: #F5F7FB;
  --gt-surface-sunken: #ECEFF7;
  --gt-ink: #1B1F2A;
  --gt-ink-2: #4B5468;
  /* ink-3 holds >= 4.5:1 on bg/bg-subtle/surface/surface-2 (it is small text:
     hints, metas, captions); border-2 is the control border and holds >= 3:1
     on surface, bg and bg-subtle (WCAG 1.4.11). The engine's NEUTRALS carry
     the same guarantee per look. */
  --gt-ink-3: #5E677B;
  --gt-border: #E2E6EF;
  --gt-border-2: #7E838F;

  --gt-primary: #17191F;
  --gt-primary-hover: #0c0e14;
  --gt-primary-active: #040509;
  --gt-primary-soft: color-mix(in srgb, #17191F 12%, var(--gt-surface));
  --gt-on-primary: #FFFFFF;
  /* The brand colour used AS TEXT on its own soft tint (badge, chip):
     guaranteed >= 4.5:1 on *-soft and on the surface. The engine derives it
     from whatever primary/accent the visitor picked; these are the values
     for the Modern (ink) palette. */
  --gt-primary-text: #17191f;

  --gt-accent: #2A6FDB;
  --gt-accent-soft: color-mix(in srgb, #2A6FDB 14%, var(--gt-surface));
  --gt-on-accent: #FFFFFF;
  --gt-accent-text: #1f65d0;

  --gt-link: #17191F;

  --gt-info: #2456C9;        --gt-info-soft: color-mix(in srgb, #2456C9 14%, var(--gt-surface));
  --gt-success: #157753;     --gt-success-soft: color-mix(in srgb, #157753 14%, var(--gt-surface));
  --gt-warning: #8A5A00;     --gt-warning-soft: color-mix(in srgb, #8A5A00 14%, var(--gt-surface));
  --gt-error: #C02A2A;       --gt-error-soft: color-mix(in srgb, #C02A2A 14%, var(--gt-surface));
  /* Destructive-action button colour, kept distinct from the --gt-error
     validation accent so a site can retint one without the other. Adopted from
     the Drupal theme, which had these and the kit hard-coded #fff instead. */
  --gt-danger: #C02A2A;
  --gt-on-danger: #FFFFFF;

  /* Control ornaments that cannot be currentColor: a CSS background image and a
     knob painted over a coloured track. Adopted from the Drupal theme, which
     had them tokenised while the kit baked the values in. */
  --gt-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23687185' stroke-width='1.8' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  --gt-switch-knob: #FFFFFF;

  /* — Image scrim (white text over photos; look-independent) —
     Adopted from the Drupal theme. `spotlight` used to hard-code
     rgba(5,7,13,…) and #fff, which no site could retint. */
  --gt-scrim: rgba(5, 7, 13, .82);
  /* The scrim's colour, opaque: the ground of a band that carries a scrim, which
     shows wherever there is no image under it (the vision band). */
  --gt-scrim-solid: #05070D;
  --gt-scrim-hover: rgba(5, 7, 13, .9);
  --gt-scrim-top: rgba(5, 7, 13, .05);
  --gt-scrim-top-hover: rgba(5, 7, 13, .15);
  --gt-on-scrim: #FFFFFF;
  --gt-on-scrim-soft: rgba(255, 255, 255, .82);

  /* Footer palette. The engine has always emitted these; until now only the
     Drupal theme declared them, which made them a cross-repo contract rather
     than a token. The kit's `acknowledgement` reads them, so they are declared
     here properly and that special case is gone. */
  --gt-footer-bg: var(--gt-ink);
  --gt-footer-fg: color-mix(in srgb, var(--gt-bg) 72%, var(--gt-ink));
  --gt-footer-heading: var(--gt-surface);
  --gt-footer-border: color-mix(in srgb, var(--gt-surface) 14%, transparent);

  --gt-focus: #17191F;
  --gt-focus-ring: 0 0 0 3px var(--gt-surface), 0 0 0 5.5px var(--gt-focus);

  /* — Elevation (live) — */
  --gt-shadow-sm: 0 1px 2px rgba(20,28,48,.06);
  --gt-shadow-md: 0 2px 4px rgba(20,28,48,.05), 0 8px 20px rgba(20,28,48,.07);
  --gt-shadow-lg: 0 8px 16px rgba(20,28,48,.06), 0 24px 48px rgba(20,28,48,.10);

  --gt-maxw: 1280px;

  /* — Banner (page header band) —
     Upstreamed with the banner component from the Drupal theme (2026-09-02).
     banner-bg is the article band's ground: the page ground, per the article
     design — the white article body opening below it marks the band edge. */
  --gt-banner-py: calc(var(--gt-space-12) * var(--gt-density));
  --gt-banner-bg: var(--gt-bg);
  --gt-banner-image-ratio: 16 / 6;
  --gt-banner-image-radius: var(--gt-radius-card);
}

/* ==========================================================================
   base/base.css
   ========================================================================== */

/* base/base.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/tokens.css (119-139, 141-149)
           css/components.css (226-230, 251-253, 255-263, 311-322)
           demo/css/site.css (505-508)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
/* The sticky header must not cover an element scrolled into view (WCAG 2.4.11).
   A margin on each element, not scroll-padding on html: padding applies to the
   header, drawer and palette too, which never move, so focusing or typing in
   one of them would scroll the page up on every keystroke. */
:where(:not(.gt-site-header, .gt-site-header *, .gt-drawer, .gt-drawer *, .gt-cmdk, .gt-cmdk *)) { scroll-margin-top: 140px; }
body {
  margin: 0;
  font-family: var(--gt-font-body);
  font-size: var(--gt-text-md);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink);
  background: var(--gt-bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  transition: background-color .35s ease, color .35s ease;
}
h1, h2, h3, h4, h5 { font-family: var(--gt-font-head); font-weight: var(--gt-head-weight); letter-spacing: var(--gt-head-tracking); line-height: var(--gt-leading-tight); margin: 0; color: var(--gt-ink); text-wrap: balance; }
p { margin: 0; }
a { color: var(--gt-link); }
img { max-width: 100%; display: block; }
button { font-family: inherit; }
:focus-visible { outline: none; box-shadow: var(--gt-focus-ring); border-radius: var(--gt-radius-sm); }
::selection { background: var(--gt-primary-soft); }

/* ── Utility layout ── */
.gt-mono { font-family: var(--gt-font-mono); }
.gt-eyebrow {
  font-family: var(--gt-font-head);
  font-size: var(--gt-text-xs);
  font-weight: var(--gt-w-semibold);
  color: var(--gt-ink-3);
}

/* ─────────────────────────── BLOCKQUOTE / PROSE ─────────────────────────── */
.gt-prose { color: var(--gt-ink-2); line-height: var(--gt-leading-relaxed); font-size: var(--gt-text-md); }
.gt-prose > * + * { margin-top: var(--gt-space-4); }
.gt-prose h2 { font-size: var(--gt-text-2xl); margin-top: var(--gt-space-8); }
.gt-prose h3 { font-size: var(--gt-text-xl); margin-top: var(--gt-space-6); }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* The table component's scroll wrapper. Global rather than in table.css because
   consumers emit the class from templates that never attach component CSS — the
   Drupal theme has two such templates — and a second copy inside the component
   would win the cascade and mask this one. (That was a real bug there: the
   component's copy lacked -webkit-overflow-scrolling.) */
.gt-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ═══════════════ SHARED LAYOUT UTILITIES (showcase + demo) ═══════════════ */
.gt-row { display: flex; flex-wrap: wrap; gap: var(--gt-space-3); align-items: center; }
.gt-stack { display: flex; flex-direction: column; gap: var(--gt-space-4); }
/* The one grid for card lists (card-grid, step-grid and plan-grid print it).
   A class per count, not a column variable, because each count collapses on
   its own path: 4 and 3 go to 2 at 1040px, anything wider than 1 goes to 1 at
   720px, and 1 never changes. A variable cannot know what it collapses from. */
.gt-grid { display: grid; gap: var(--gt-space-6); }
.gt-grid--1 { grid-template-columns: 1fr; }
.gt-grid--2 { grid-template-columns: repeat(2, 1fr); }
.gt-grid--3 { grid-template-columns: repeat(3, 1fr); }
.gt-grid--4 { grid-template-columns: repeat(4, 1fr); }
.gt-grid--auto { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
@media (max-width: 1040px) { .gt-grid--3, .gt-grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px) { .gt-grid--2, .gt-grid--3, .gt-grid--4 { grid-template-columns: 1fr; } }

/* Visually hidden, still announced. The shared layer had no such utility —
   css/homepage.css has .hp-vh, but that file is a closed surface. */
.gt-vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Section eyebrow + lead/small text helpers. `.gt-lead` was `.gt-lede` until the
   Drupal theme's name won (ledger D4); `.gt-small` comes from that theme, which
   had it and the kit did not. */
.gt-lead { color: var(--gt-ink-2); font-size: var(--gt-text-lg); max-width: 60ch; line-height: var(--gt-leading-normal); }
.gt-small { font-size: var(--gt-text-sm); color: var(--gt-ink-2); }

/* ==========================================================================
   base/forms.css
   ========================================================================== */

/* base/forms.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (80-151)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── FORM FIELDS ─────────────────────────── */
.gt-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--gt-space-5); }
.gt-label { font-size: var(--gt-text-sm); font-weight: var(--gt-w-semibold); color: var(--gt-ink); }
.gt-label .gt-req { color: var(--gt-error); margin-left: 2px; }
.gt-label .gt-opt { color: var(--gt-ink-3); font-weight: var(--gt-w-regular); margin-left: 6px; }
.gt-hint { font-size: var(--gt-text-xs); color: var(--gt-ink-3); }
.gt-error-msg { font-size: var(--gt-text-xs); color: var(--gt-error); font-weight: var(--gt-w-medium); display: flex; align-items: center; gap: 5px; }
.gt-error-msg[hidden] { display: none; }

.gt-input, .gt-select, .gt-textarea {
  width: 100%; min-height: var(--gt-control-h); padding: 0 14px;
  font-family: var(--gt-font-body); font-size: var(--gt-text-sm); color: var(--gt-ink);
  background: var(--gt-surface); border: var(--gt-border-width-strong) solid var(--gt-border-2);
  border-radius: var(--gt-radius-input); transition: border-color .15s, box-shadow .15s, background-color .15s;
}
.gt-textarea { padding: 12px 14px; min-height: 110px; line-height: var(--gt-leading-normal); resize: vertical; }
.gt-input::placeholder, .gt-textarea::placeholder { color: var(--gt-ink-3); }
.gt-input:hover, .gt-select:hover, .gt-textarea:hover { border-color: var(--gt-ink-3); }
.gt-input:focus, .gt-select:focus, .gt-textarea:focus { outline: none; border-color: var(--gt-primary); box-shadow: var(--gt-focus-ring); }
.gt-input--error, .gt-textarea--error, .gt-select--error,
.gt-input--error:hover, .gt-textarea--error:hover, .gt-select--error:hover,
.gt-input--error:focus, .gt-textarea--error:focus, .gt-select--error:focus { border-color: var(--gt-error); }
.gt-input[disabled], .gt-select[disabled], .gt-textarea[disabled] { background: var(--gt-surface-sunken); color: var(--gt-ink-3); cursor: not-allowed; }

.gt-select { appearance: none; padding-right: 40px;
  background-image: var(--gt-select-chevron);
  background-repeat: no-repeat; background-position: right 14px center; }

/* input with leading icon */
.gt-input-group { position: relative; display: flex; align-items: center; }
.gt-input-group .gt-input { padding-left: 42px; }
.gt-input-group svg { position: absolute; left: 14px; width: 18px; height: 18px; color: var(--gt-ink-3); pointer-events: none; }

/* checkbox & radio */
.gt-check, .gt-radio { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; min-height: 24px; font-size: var(--gt-text-sm); color: var(--gt-ink); }
.gt-check input, .gt-radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.gt-check .gt-box, .gt-radio .gt-box {
  flex: 0 0 auto; width: 22px; height: 22px; margin-top: 1px;
  border: var(--gt-border-width-strong) solid var(--gt-border-2); background: var(--gt-surface);
  display: grid; place-items: center; transition: border-color .15s, background-color .15s;
}
.gt-check .gt-box { border-radius: max(4px, calc(var(--gt-radius-input) - 4px)); }
.gt-radio .gt-box { border-radius: 50%; }
.gt-check .gt-box svg { width: 13px; height: 13px; color: var(--gt-on-primary); opacity: 0; transition: opacity .12s; }
.gt-radio .gt-box::after { content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--gt-on-primary); transform: scale(0); transition: transform .15s; }
.gt-check input:checked + .gt-box { background: var(--gt-primary); border-color: var(--gt-primary); }
.gt-check input:checked + .gt-box svg { opacity: 1; }
.gt-radio input:checked + .gt-box { background: var(--gt-primary); border-color: var(--gt-primary); }
.gt-radio input:checked + .gt-box::after { transform: scale(1); }
.gt-check input:focus-visible + .gt-box, .gt-radio input:focus-visible + .gt-box { box-shadow: var(--gt-focus-ring); }

/* switch */
.gt-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; min-height: 24px; font-size: var(--gt-text-sm); }
.gt-switch input { position: absolute; opacity: 0; }
.gt-switch .gt-track { width: 44px; height: 26px; border-radius: var(--gt-radius-pill); background: var(--gt-border-2); position: relative; transition: background-color .18s; flex: 0 0 auto; }
.gt-switch .gt-track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--gt-switch-knob); box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .18s; }
.gt-switch input:checked + .gt-track { background: var(--gt-primary); }
.gt-switch input:checked + .gt-track::after { transform: translateX(18px); }
.gt-switch input:focus-visible + .gt-track { box-shadow: var(--gt-focus-ring); }

/* file upload (dropzone) */
.gt-file {
  /* position: relative so the covering file input has this as its containing
     block; visually neutral on its own. display: block because a <label> is
     inline by default — inside .gt-field the flex layout blockifies it, but
     standalone the border would fragment around the block children. */
  display: block;
  position: relative;
  border: 2px dashed var(--gt-border-2); border-radius: var(--gt-radius-input);
  background: var(--gt-surface-2); padding: var(--gt-space-8); text-align: center; cursor: pointer;
  transition: border-color .15s, background-color .15s;
}
.gt-file:hover { border-color: var(--gt-primary); background: var(--gt-primary-soft); }
.gt-file svg { width: 32px; height: 32px; color: var(--gt-ink-3); margin-bottom: 8px; }
.gt-file strong { color: var(--gt-primary); }
/* The file input is visually hidden but NOT `hidden` — a `hidden` input is not
   focusable, which makes the whole control keyboard-unreachable. It covers the
   drop zone instead, so a click anywhere opens the picker and the label gets a
   focus ring from its focus-within state. */
.gt-file input[type="file"] { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.gt-file:focus-within { border-color: var(--gt-primary); box-shadow: var(--gt-focus-ring); }

/* ==========================================================================
   components/01-atoms/attachment/attachment.css
   ========================================================================== */

/* Attachment — a download link to a file (icon badge + title + meta). */
.gt-attach { display: flex; align-items: center; gap: 14px; padding: 16px; border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); text-decoration: none; color: var(--gt-ink); background: var(--gt-surface); transition: border-color .15s, background-color .15s; }
.gt-attach:hover { border-color: var(--gt-primary); background: var(--gt-primary-soft); }
/* Space stacked attachments (e.g. consecutive WYSIWYG embeds). */
.gt-attach { margin-top: var(--gt-space-4); }
.gt-attach__icon { flex: 0 0 auto; width: 42px; height: 42px; border-radius: var(--gt-radius-input); background: var(--gt-error-soft); color: var(--gt-error); display: grid; place-items: center; font-size: var(--gt-text-2xs); font-weight: var(--gt-w-extrabold); font-family: var(--gt-font-mono); }
.gt-attach__title { display: block; }
.gt-attach__meta { font-size: var(--gt-text-xs); color: var(--gt-ink-3); }

/* ==========================================================================
   components/01-atoms/badge/badge.css
   ========================================================================== */

/* components/01-atoms/badge/badge.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (53-66)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── BADGES & TAGS ─────────────────────────── */
.gt-badge {
  display: inline-flex; align-items: center; gap: var(--gt-space-1); min-height: 24px;
  padding: var(--gt-space-1) var(--gt-space-3); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold);
  line-height: var(--gt-leading-snug); border-radius: var(--gt-radius-chip);
  /* -text, not the bare brand colour: badge text is --gt-text-xs, so it needs
     4.5:1 on its soft tint, and primary/accent are visitor-configurable. The
     engine derives --gt-primary-text/--gt-accent-text to guarantee that. */
  background: var(--gt-primary-soft); color: var(--gt-primary-text);
}
.gt-badge--neutral { background: var(--gt-surface-sunken); color: var(--gt-ink-2); }
.gt-badge--success { background: var(--gt-success-soft); color: var(--gt-success); }
.gt-badge--warning { background: var(--gt-warning-soft); color: var(--gt-warning); }
.gt-badge--error   { background: var(--gt-error-soft);   color: var(--gt-error); }
.gt-badge--accent  { background: var(--gt-accent-soft);  color: var(--gt-accent-text); }
.gt-badge--solid { background: var(--gt-primary); color: var(--gt-on-primary); }

/* ==========================================================================
   components/01-atoms/button/button.css
   ========================================================================== */

/* components/01-atoms/button/button.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (7-41)
           demo/css/site.css (100-101, 275-278)
   The slice originally ran to 280 and swept in line 279, which is the
   .gt-doghouse-cta responsive rule and not a button rule at all. It moved to
   03-organisms/doghouse-cta when that component was created.
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── BUTTONS ─────────────────────────── */
.gt-btn {
  --_h: var(--gt-control-h);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--gt-space-2);
  min-height: var(--_h); min-width: var(--_h);
  padding: 0 var(--gt-control-px);
  font-family: var(--gt-font-body); font-size: var(--gt-text-sm); font-weight: var(--gt-w-semibold);
  line-height: 1; letter-spacing: var(--gt-tracking-normal); text-decoration: none; white-space: nowrap;
  border: var(--gt-border-width-strong) solid transparent;
  border-radius: var(--gt-radius-btn);
  cursor: pointer; transition: background-color .16s, border-color .16s, color .16s, box-shadow .16s, transform .06s;
  background: var(--gt-primary); color: var(--gt-on-primary);
}
.gt-btn:hover { background: var(--gt-primary-hover); }
.gt-btn:active { background: var(--gt-primary-active); transform: translateY(0.5px); }
.gt-btn svg { width: 1.15em; height: 1.15em; }

.gt-btn--secondary { background: transparent; color: var(--gt-primary); border-color: var(--gt-border-2); }
.gt-btn--secondary:hover { background: var(--gt-primary-soft); border-color: var(--gt-primary); }
.gt-btn--secondary:active { background: var(--gt-primary-soft); }

.gt-btn--ghost { background: transparent; color: var(--gt-primary); border-color: transparent; padding: 0 var(--gt-space-3); }
.gt-btn--ghost:hover { background: var(--gt-primary-soft); }

.gt-btn--subtle { background: var(--gt-surface-sunken); color: var(--gt-ink); border-color: transparent; }
.gt-btn--subtle:hover { background: var(--gt-border); }

.gt-btn--danger { background: var(--gt-danger); color: var(--gt-on-danger); }
.gt-btn--danger:hover { filter: brightness(.92); }

.gt-btn--lg { --_h: calc(52px * var(--gt-density)); font-size: var(--gt-text-md); padding: 0 calc(var(--gt-space-8) * var(--gt-density)); }
.gt-btn--sm { --_h: var(--gt-control-h-sm); font-size: var(--gt-text-xs); padding: 0 calc(var(--gt-space-4) * var(--gt-density)); }
.gt-btn[disabled] { opacity: .45; pointer-events: none; }
.gt-btn--block { display: flex; width: 100%; }

.gt-btn--on-primary { background: var(--gt-on-primary); color: var(--gt-primary); border-color: var(--gt-on-primary); }
.gt-btn--on-primary:hover { background: color-mix(in srgb, var(--gt-on-primary) 88%, var(--gt-primary)); }

.gt-btn--white { background: var(--gt-surface); color: var(--gt-ink); border-color: var(--gt-surface); }
.gt-btn--white:hover { background: color-mix(in srgb, var(--gt-surface) 88%, var(--gt-primary)); }
.gt-btn--outline-white { background: transparent; color: var(--gt-surface); border-color: color-mix(in srgb, var(--gt-surface) 40%, transparent); }
.gt-btn--outline-white:hover { border-color: var(--gt-surface); background: color-mix(in srgb, var(--gt-surface) 12%, transparent); }

/* ==========================================================================
   components/01-atoms/icon/icon.css
   ========================================================================== */

.gt-icon { display: inline-block; flex: 0 0 auto; vertical-align: middle; }

/* ==========================================================================
   components/01-atoms/link/link.css
   ========================================================================== */

/* components/01-atoms/link/link.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (42-52)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── LINKS ─────────────────────────── */
.gt-link {
  color: var(--gt-link); text-decoration: underline; text-underline-offset: 0.18em;
  text-decoration-thickness: 1.5px; text-decoration-color: color-mix(in srgb, var(--gt-link) 40%, transparent);
  border-radius: 3px; transition: text-decoration-color .15s, color .15s;
}
.gt-link:hover { text-decoration-color: currentColor; }
/* In small, tight text (alert and banner bodies) one line is under 24px. The
   negative margin keeps the 24px box from making its line taller, which drops
   the text below the alert's icon; it is 0 wherever a line is 24px or more. */
.gt-link--arrow { font-weight: var(--gt-w-semibold); text-decoration: none; display: inline-flex; align-items: center; gap: 6px; min-height: 24px; margin-block: min(0px, (1lh - 24px) / 2); }
.gt-link--arrow::after { content: "→"; transition: transform .18s; }
.gt-link--arrow:hover::after { transform: translateX(3px); }

/* RTL: the arrow points the other way, and travels the other way on hover.
   Adopted from the Drupal theme, which had this and the kit did not. */
[dir="rtl"] .gt-link--arrow::after { content: "←"; }
[dir="rtl"] .gt-link--arrow:hover::after { transform: translateX(-3px); }

/* ==========================================================================
   components/01-atoms/progress/progress.css
   ========================================================================== */

/* components/01-atoms/progress/progress.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (248-249)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-progress { height: 8px; border-radius: var(--gt-radius-pill); background: var(--gt-surface-sunken); overflow: hidden; }
.gt-progress__bar { height: 100%; background: var(--gt-primary); border-radius: var(--gt-radius-pill); }

/* ==========================================================================
   components/01-atoms/spinner/spinner.css
   ========================================================================== */

/* components/01-atoms/spinner/spinner.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (245-247)

   The gt-spin keyframes live here rather than in a global sheet: nothing else
   animates them, so leaving them behind would put a rule in base.css whose only
   consumer is this component. prefers-reduced-motion is handled globally. */

/* spinner / progress */
.gt-spinner { width: 22px; height: 22px; border: 2.5px solid var(--gt-border); border-top-color: var(--gt-primary); border-radius: 50%; animation: gt-spin .7s linear infinite; }
@keyframes gt-spin { to { transform: rotate(360deg); } }

/* ==========================================================================
   components/01-atoms/tag/tag.css
   ========================================================================== */

/* components/01-atoms/tag/tag.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (67-79)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-tag {
  display: inline-flex; align-items: center; gap: var(--gt-space-1); min-height: 28px;
  padding: var(--gt-space-1) var(--gt-space-3); font-size: var(--gt-text-xs); font-weight: var(--gt-w-medium);
  border-radius: var(--gt-radius-chip); border: var(--gt-border-width) solid var(--gt-border-2);
  background: var(--gt-surface); color: var(--gt-ink-2); text-decoration: none;
  transition: border-color .15s, color .15s, background-color .15s;
}
.gt-tag:hover { border-color: var(--gt-primary); color: var(--gt-primary); background: var(--gt-primary-soft); }
.gt-tag--active { border-color: var(--gt-primary); background: var(--gt-primary); color: var(--gt-on-primary); }
/* 24px, not 18: WCAG 2.5.8 sets 24x24 CSS px as the minimum target size. */
.gt-tag--removable button { all: unset; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: var(--gt-radius-pill); }
.gt-tag--removable button:hover { background: color-mix(in srgb, currentColor 18%, transparent); }

/* ==========================================================================
   components/02-molecules/acknowledgement/acknowledgement.css
   ========================================================================== */

/* Acknowledgement of Country: the footer's opening band. */
.gt-ack { border-bottom: 1px solid var(--gt-footer-border); }
.gt-ack__inner { max-width: var(--gt-maxw); margin: 0 auto; padding: 26px var(--gt-space-6); display: flex; align-items: flex-start; gap: 18px; }

/* Both flags render 5:3; height drives the box so the pair stays even. */
.gt-ack__flag { flex: 0 0 auto; height: 28px; border-radius: 3px; overflow: hidden; box-shadow: 0 0 0 1px color-mix(in srgb, var(--gt-footer-fg) 22%, transparent); margin-top: 3px; }
.gt-ack__flag svg { display: block; height: 100%; width: auto; }

.gt-ack__text { font-size: var(--gt-text-md); line-height: 1.65; opacity: .92; max-width: 90ch; }
.gt-ack__text p { margin: 0; }
.gt-ack__text p + p { margin-top: var(--gt-space-2); }
/* Compound selector beats the footer's block-level link rule, so a link in the
   acknowledgement stays inline in its sentence. */
.gt-ack .gt-ack__text a { display: inline; padding: 0; opacity: 1; text-decoration: underline; }

/* Narrow screens: flags share a row, text takes the next. */
@media (max-width: 640px) {
  .gt-ack__inner { flex-wrap: wrap; }
  .gt-ack__text { flex: 1 1 100%; }
}

/* ==========================================================================
   components/02-molecules/alert/alert.css
   ========================================================================== */

/* ─────────────────────────── ALERTS / CALLOUTS ─────────────────────────── */
.gt-alert {
  display: flex; gap: 14px; padding: 16px 18px; border-radius: var(--gt-radius-card);
  background: var(--gt-info-soft); border: var(--gt-border-width) solid transparent;
  color: var(--gt-ink);
}
/* One line tall, so the icon centres on the first line of whatever text comes
   first: the title, or the body when there is no title (Drupal's status
   messages). The untitled case repeats the body's size and leading for 1lh. */
.gt-alert__icon { flex: 0 0 auto; display: flex; align-items: center; width: 22px; height: 1lh; color: var(--gt-info); }
.gt-alert:not(:has(.gt-alert__title)) .gt-alert__icon { font-size: var(--gt-text-sm); line-height: var(--gt-leading-relaxed); }
/* flex: 1 so the text column fills the row instead of shrinking to its content —
   the class existed in the markup with no rule behind it in either repo. */
.gt-alert__text { flex: 1; min-width: 0; }
.gt-alert__title { font-weight: var(--gt-w-bold); margin-bottom: 2px; font-family: var(--gt-font-head); letter-spacing: var(--gt-head-tracking); }
/* Relaxed is the leading a theme gives a bare p (the Drupal theme's base.css),
   so the body reads the same whether its text is wrapped in paragraphs or not,
   and the icon's 1lh matches either. */
.gt-alert__body { font-size: var(--gt-text-sm); color: var(--gt-ink-2); line-height: var(--gt-leading-relaxed); }
.gt-alert--success { background: var(--gt-success-soft); }
.gt-alert--success .gt-alert__icon { color: var(--gt-success); }
.gt-alert--warning { background: var(--gt-warning-soft); }
.gt-alert--warning .gt-alert__icon { color: var(--gt-warning); }
.gt-alert--error { background: var(--gt-error-soft); }
.gt-alert--error .gt-alert__icon { color: var(--gt-error); }

/* ── Accent panel — editorial notice, no icon ── */
.gt-alert--default { display: block; padding: var(--gt-space-6); background: var(--gt-primary-soft); border: var(--gt-border-width) solid color-mix(in srgb, var(--gt-primary) 22%, transparent); }
.gt-alert--default .gt-eyebrow { margin-bottom: 8px; color: var(--gt-primary); }

/* ==========================================================================
   components/02-molecules/blockquote/blockquote.css
   ========================================================================== */

/* components/02-molecules/blockquote/blockquote.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (231-232)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-blockquote { border-left: 4px solid var(--gt-primary); padding: 4px 0 4px 20px; font-family: var(--gt-font-head); font-size: var(--gt-text-xl); font-weight: var(--gt-w-medium); color: var(--gt-ink); letter-spacing: var(--gt-head-tracking); line-height: var(--gt-leading-snug); }
.gt-blockquote cite { display: block; font-family: var(--gt-font-body); font-size: var(--gt-text-sm); font-weight: var(--gt-w-regular); font-style: normal; color: var(--gt-ink-3); margin-top: 10px; }

/* ==========================================================================
   components/02-molecules/breadcrumb/breadcrumb.css
   ========================================================================== */

/* components/02-molecules/breadcrumb/breadcrumb.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (200-205)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── BREADCRUMB ─────────────────────────── */
.gt-breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: var(--gt-text-sm); color: var(--gt-ink-3); }
.gt-breadcrumb a { color: var(--gt-ink-2); text-decoration: none; min-height: 24px; display: inline-flex; align-items: center; border-radius: var(--gt-radius-xs); }
.gt-breadcrumb a:hover { color: var(--gt-primary); text-decoration: underline; }
.gt-breadcrumb__sep { color: var(--gt-border-2); }
.gt-breadcrumb [aria-current] { color: var(--gt-ink); font-weight: var(--gt-w-medium); }

/* ==========================================================================
   components/02-molecules/callout/callout.css
   ========================================================================== */

/* ─────────────────────────── CALLOUT ─────────────────────────── */
/* A light information panel. Two neighbours are deliberately separate
   components rather than variants of this one: `cta-band` is the call-to-action
   band and `spotlight` is the full-bleed image hero. They share none of this
   component's structure, and folding them in here is what made the Drupal
   theme's `callout --default` mean "dark CTA band" — so a plain callout was
   unreachable. (Divergence ledger D9.) */
.gt-callout { padding: var(--gt-space-6); border-radius: var(--gt-radius-card); background: var(--gt-surface-2); border: var(--gt-border-width) solid var(--gt-border); }
.gt-callout--accent { background: var(--gt-primary-soft); border-color: color-mix(in srgb, var(--gt-primary) 22%, transparent); }

.gt-callout__eyebrow { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-primary); margin-bottom: var(--gt-space-2); }
.gt-callout__title { font-size: var(--gt-text-xl); margin: 0; }
.gt-callout__body { margin-top: var(--gt-space-3); }
.gt-callout__body :where(p, ul, ol):first-child { margin-top: 0; }

/* ==========================================================================
   components/02-molecules/card/card.css
   ========================================================================== */

/* ─────────────────────────── CONTENT CARD ─────────────────────────── */
.gt-card { background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); overflow: hidden; box-shadow: var(--gt-shadow-sm); transition: box-shadow .2s, transform .2s, border-color .2s; display: flex; flex-direction: column; }
.gt-card--link:hover { box-shadow: var(--gt-shadow-md); transform: translateY(-2px); border-color: var(--gt-border-2); }
.gt-card__media { aspect-ratio: 16/9; background: var(--gt-surface-sunken);
  background-image: repeating-linear-gradient(135deg, var(--gt-border) 0 1px, transparent 1px 12px);
  display: flex; align-items: center; justify-content: center; color: var(--gt-ink-3); font-family: var(--gt-font-mono); font-size: var(--gt-text-2xs); letter-spacing: .05em; }
.gt-card__body { padding: 20px 22px 22px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.gt-card__meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: var(--gt-text-xs); color: var(--gt-ink-3); }
.gt-card__title { font-size: var(--gt-text-xl); line-height: var(--gt-leading-snug); }
.gt-card__excerpt { font-size: var(--gt-text-sm); color: var(--gt-ink-2); line-height: var(--gt-leading-normal); }
/* Body is a flex column with gap; opt out of the global block rhythm. */
.gt-card__title, .gt-card__excerpt { margin-top: 0; }
.gt-card--row { flex-direction: row; }
.gt-card--row .gt-card__media { aspect-ratio: auto; width: 40%; flex: 0 0 auto; }

/* Real images fill the media box (overrides the placeholder pattern). */
.gt-card__media img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Title link reads as a heading; the whole card is the click target below. */
.gt-card__title a { color: inherit; text-decoration: none; }
/* Whole-card click target: the title link stretches over the card. */
.gt-card--link { position: relative; }
.gt-card--link .gt-card__title a::after { content: ""; position: absolute; inset: 0; }
/* The card is the target, so the card shows the focus. On the card itself
   because its overflow would clip a ring on the link's overlay. */
.gt-card--link:has(.gt-card__title a:focus-visible) { box-shadow: var(--gt-focus-ring); }
.gt-card--link:has(.gt-card__title a:focus-visible) .gt-card__title a:focus-visible { box-shadow: none; }

/* ─────────────────────────── SERVICE ─────────────────────────── */
/* Icon-led card: a tinted icon chip leads a single padded column. */
.gt-card--service .gt-card__body { padding: 24px; gap: 12px; }
.gt-card__icon { width: 44px; height: 44px; border-radius: var(--gt-radius-input); background: var(--gt-primary-soft); color: var(--gt-primary); display: grid; place-items: center; }
.gt-card__icon svg { width: 24px; height: 24px; }
.gt-card--service .gt-card__title { font-size: var(--gt-text-lg); }
.gt-card--service:hover { border-color: var(--gt-primary); }

/* ─────────────────────────── STANDARD ─────────────────────────── */
/* Photo-led program card: 5/3 media, tighter body, lg title, "Explore →" CTA. */
.gt-card--standard .gt-card__media   { aspect-ratio: 5 / 3; }
.gt-card--standard .gt-card__body    { padding: 18px 20px 20px; gap: 7px; }
.gt-card--standard .gt-card__title   { font-size: var(--gt-text-lg); }
.gt-card--standard .gt-card__excerpt { line-height: var(--gt-leading-snug); }
.gt-card--standard.gt-card--link:hover { transform: translateY(-3px); border-color: var(--gt-primary); }
.gt-card__more { margin-top: auto; padding-top: 10px; font-size: var(--gt-text-sm); font-weight: var(--gt-w-semibold); color: var(--gt-link); display: inline-flex; align-items: center; gap: 6px; }
.gt-card__more svg { width: 15px; height: 15px; transition: transform .18s; }
.gt-card--link:hover .gt-card__more svg { transform: translateX(3px); }

/* ─────────────────────────── CHILD PAGE ─────────────────────────── */
/* Compact link card: title carries a trailing arrow, no media or icon. */
.gt-card--child_page .gt-card__body { padding: 22px; gap: 8px; }
.gt-card--child_page .gt-card__title { font-size: var(--gt-text-lg); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.gt-card--child_page .gt-card__title::after { content: "→"; color: var(--gt-primary); transition: transform .18s; }
.gt-card--child_page:hover .gt-card__title::after { transform: translateX(3px); }
.gt-card--child_page:hover { border-color: var(--gt-primary); }

/* ─────────────────────────── TILE ─────────────────────────── */
/* Whole-card image link with a text overlay over a scrim. */
.gt-card--tile { position: relative; aspect-ratio: 3/4; border: none; border-radius: var(--gt-radius-card); overflow: hidden; text-decoration: none; color: var(--gt-on-scrim); display: block; box-shadow: var(--gt-shadow-sm); isolation: isolate; }
.gt-card--tile::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, var(--gt-scrim-top) 30%, var(--gt-scrim)); z-index: 1; transition: background .2s; }
.gt-card--tile:hover::after { background: linear-gradient(180deg, var(--gt-scrim-top-hover) 20%, var(--gt-scrim-hover)); }
.gt-card--tile img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
.gt-card--tile:hover img { transform: scale(1.05); }
.gt-card--tile .gt-card__overlay { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 18px; }
.gt-card--tile .gt-card__overlay strong { display: block; font-family: var(--gt-font-head); font-size: var(--gt-text-xl); letter-spacing: var(--gt-head-tracking); color: var(--gt-on-scrim); }
.gt-card--tile .gt-card__overlay span { display: block; font-size: var(--gt-text-xs); color: var(--gt-on-scrim-soft); margin-top: 3px; }
.gt-card--tile:focus-visible { box-shadow: var(--gt-focus-ring); }

@media (prefers-reduced-motion: reduce) {
  .gt-card--link:hover, .gt-card--standard.gt-card--link:hover, .gt-card--tile:hover img,
  .gt-card--child_page:hover .gt-card__title::after, .gt-card--link:hover .gt-card__more svg { transform: none; }
}

/* ==========================================================================
   components/02-molecules/event-card/event-card.css
   ========================================================================== */

/* ─────────────────────────── EVENT CARD ─────────────────────────── */
/* Listing row: fixed date chip, flexible body, action on the end. */
.gt-event-card { display: grid; grid-template-columns: 96px 1fr auto; gap: 22px; align-items: center; padding: 22px; background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); box-shadow: var(--gt-shadow-sm); transition: box-shadow .18s, border-color .18s; }
/* Focus-within gives keyboard users the same lift as hover. */
.gt-event-card:hover, .gt-event-card:focus-within { box-shadow: var(--gt-shadow-md); border-color: var(--gt-primary); }

.gt-event-card__date { text-align: center; background: var(--gt-primary-soft); border-radius: var(--gt-radius-input); padding: 12px 8px; }
.gt-event-card__day { font-family: var(--gt-font-head); font-weight: var(--gt-w-extrabold); font-size: var(--gt-text-3xl); color: var(--gt-primary); line-height: 1; }
.gt-event-card__mon { font-size: var(--gt-text-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--gt-primary); margin-top: 4px; }

/* min-width: 0 so a long unbroken title cannot blow the 1fr column out — grid
   items default to min-width: auto. The class was in the markup of both repos
   with no rule behind it. */
.gt-event-card__body { min-width: 0; }
.gt-event-card__type { margin-bottom: 6px; }
/* Explicit margins: the body opts out of the global block rhythm. */
.gt-event-card__title { font-size: var(--gt-text-lg); margin: 0 0 8px; }
.gt-event-card__title a { color: inherit; text-decoration: none; }
.gt-event-card__title a:hover, .gt-event-card__title a:focus-visible { text-decoration: underline; }
.gt-event-card__meta { display: flex; gap: 16px; flex-wrap: wrap; font-size: var(--gt-text-sm); color: var(--gt-ink-3); }
.gt-event-card__meta span { display: inline-flex; align-items: center; gap: 6px; }

@media (max-width: 680px) {
  .gt-event-card { grid-template-columns: 72px 1fr; }
  /* Action wraps onto its own full-width row. */
  .gt-event-card__cta { grid-column: 1 / -1; display: grid; }
}

/* ==========================================================================
   components/02-molecules/event-list-item/event-list-item.css
   ========================================================================== */

/* components/02-molecules/event-list-item/event-list-item.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (103-110)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* Event list */
.gt-event { display: flex; gap: 20px; align-items: flex-start; padding: 20px 0; border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-event:last-child { border-bottom: none; }
.gt-event__date { flex: 0 0 84px; text-align: center; background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-input); padding: 10px; }
.gt-event__day { font-family: var(--gt-font-head); font-weight: 800; font-size: var(--gt-text-2xl); color: var(--gt-primary); line-height: 1; }
.gt-event__mon { font-size: var(--gt-text-xs); text-transform: uppercase; letter-spacing: .08em; color: var(--gt-ink-3); margin-top: 4px; }
.gt-event__body h3 { font-size: var(--gt-text-lg); }
.gt-event__meta { font-size: var(--gt-text-sm); color: var(--gt-ink-3); margin-top: 4px; display: flex; gap: 14px; flex-wrap: wrap; }

/* ==========================================================================
   components/02-molecules/feature-card/feature-card.css
   ========================================================================== */

/* Generic feature/plan/option card; `highlighted` lifts the recommended one. */
.gt-feature { position: relative; display: flex; flex-direction: column; gap: 13px; padding: 30px 26px 26px; border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); background: var(--gt-surface); box-shadow: var(--gt-shadow-sm); }
.gt-feature--highlight { border-color: var(--gt-primary); border-width: 2px; box-shadow: var(--gt-shadow-md); background: linear-gradient(180deg, var(--gt-primary-soft), var(--gt-surface) 140px); }
.gt-feature__badge { position: absolute; top: -12px; left: 26px; background: var(--gt-primary); color: var(--gt-on-primary); font-family: var(--gt-font-mono); font-size: var(--gt-text-2xs); font-weight: var(--gt-w-semibold); letter-spacing: var(--gt-tracking-caps); text-transform: uppercase; padding: 6px 12px; min-height: 24px; display: inline-flex; align-items: center; border-radius: var(--gt-radius-pill); }
.gt-feature__icon { width: 46px; height: 46px; border-radius: var(--gt-radius-input); display: grid; place-items: center; background: var(--gt-primary-soft); color: var(--gt-primary); }
.gt-feature__icon svg { width: 24px; height: 24px; }
.gt-feature--highlight .gt-feature__icon { background: var(--gt-primary); color: var(--gt-on-primary); }
.gt-feature__tags { display: flex; flex-wrap: wrap; column-gap: 8px; }
.gt-feature__tag { font-family: var(--gt-font-mono); font-size: var(--gt-text-2xs); letter-spacing: var(--gt-tracking-caps); text-transform: uppercase; color: var(--gt-ink-3); }
.gt-feature__tag:not(:first-child)::before { content: "·"; margin-right: 8px; }
/* Card is a flex column with gap; opt the title out of the global block rhythm. */
.gt-feature__title { font-family: var(--gt-font-head); font-size: var(--gt-text-xl); letter-spacing: var(--gt-head-tracking); margin-top: 0; }
.gt-feature__body { font-size: var(--gt-text-sm); color: var(--gt-ink-2); line-height: var(--gt-leading-normal); flex: 1; }
/* Rich text from the editor: the kit resets p margins, so blocks need their own rhythm. */
.gt-feature__body > * + * { margin-top: var(--gt-space-4); }
.gt-feature__action { margin-top: 6px; }
/* 720px is where the shared grid stacks to one column. */
@media (max-width: 720px) { .gt-feature { padding-top: 28px; } }

/* ==========================================================================
   components/02-molecules/news-list/news-list.css
   ========================================================================== */

/* components/02-molecules/news-list/news-list.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (417-426)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* "More from the newsroom" — compact dated headlines below the news grid */
.gt-newslist { list-style: none; margin: 26px 0 0; padding: 22px 0 0; border-top: var(--gt-border-width) solid var(--gt-border); display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 32px; }
.gt-newslist li { min-width: 0; }
.gt-newslist a { display: flex; flex-direction: column; gap: 7px; padding: 4px 0; text-decoration: none; color: var(--gt-ink); min-height: 44px; justify-content: center; }
.gt-newslist__meta { display: inline-flex; align-items: center; gap: 9px; font-size: var(--gt-text-xs); color: var(--gt-ink-3); }
.gt-newslist__t { font-family: var(--gt-font-head); font-weight: var(--gt-w-semibold); font-size: var(--gt-text-md); letter-spacing: var(--gt-head-tracking); line-height: 1.32; color: var(--gt-ink); transition: color .14s; }
.gt-newslist a:hover .gt-newslist__t { color: var(--gt-primary); }
@media (max-width: 760px) { .gt-newslist { grid-template-columns: 1fr; gap: 0; } .gt-newslist li + li { border-top: var(--gt-border-width) solid var(--gt-border); } .gt-newslist a { padding: 14px 0; } }

/* ==========================================================================
   components/02-molecules/pagination/pagination.css
   ========================================================================== */

/* components/02-molecules/pagination/pagination.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (207-216)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── PAGINATION ─────────────────────────── */
.gt-pagination { display: flex; align-items: center; gap: 4px; }
.gt-pagination a, .gt-pagination span {
  display: inline-flex; align-items: center; justify-content: center; min-width: 40px; height: 40px; padding: 0 8px;
  border-radius: var(--gt-radius-input); font-size: var(--gt-text-sm); font-weight: var(--gt-w-medium);
  color: var(--gt-ink-2); text-decoration: none; transition: background-color .15s, color .15s;
}
.gt-pagination a:hover { background: var(--gt-primary-soft); color: var(--gt-primary); }
.gt-pagination .gt-page--active { background: var(--gt-primary); color: var(--gt-on-primary); font-weight: var(--gt-w-bold); }
.gt-pagination .gt-page--gap { color: var(--gt-ink-3); }

/* ==========================================================================
   components/02-molecules/person-card/person-card.css
   ========================================================================== */

/* Person/leadership card; avatar shows initials derived upstream. */
.gt-person { display: flex; gap: 18px; align-items: center; padding: 22px; background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); box-shadow: var(--gt-shadow-sm); }
.gt-person__avatar { flex: 0 0 auto; width: 76px; height: 76px; border-radius: 50%; background: var(--gt-primary-soft); color: var(--gt-primary); display: grid; place-items: center; font-family: var(--gt-font-head); font-weight: var(--gt-w-extrabold); font-size: var(--gt-text-2xl); letter-spacing: var(--gt-head-tracking); }
/* As .gt-alert__text: the wrapper was named but never styled. */
.gt-person__body { flex: 1; min-width: 0; }
.gt-person__name { font-family: var(--gt-font-head); font-size: var(--gt-text-lg); letter-spacing: var(--gt-head-tracking); }
.gt-person__role { color: var(--gt-ink-2); font-size: var(--gt-text-sm); margin-top: 3px; }
.gt-person__action { display: inline-flex; margin-top: 8px; font-size: var(--gt-text-sm); }

/* ==========================================================================
   components/02-molecules/plan-card/plan-card.css
   ========================================================================== */

/* components/02-molecules/plan-card/plan-card.css
   Moved out of the GovTheme marketing site by hand, after the Phase 1
   extract-css.mjs pass had already run.
   Source: css/home.css (514-519, 523-527), verbatim except for the rename out
   of the marketing `hm-` namespace: gt-plan-card -> gt-plan-card, and its
   __badge / __icon elements. Recorded in govtheme-marketing's
   scripts/check-cascade.mjs.

   The `.hm .gt-plan-card__badge` red-fill override stays in css/home.css - it
   paints from `--hm-red-text`, which is not a kit token.

   The card is glass. It used to be hardcoded whites - rgba(255,255,255,.30)
   fill and a white border, carried over verbatim from the marketing landing
   page - which read as broken on a dark surface (light-grey glass under
   dark-mode ink text, 2.7-3.6:1 in Cosmos). The 2026-09 a11y pass retokened
   the whites to --gt-surface at the same alphas: in every light look the
   surface is #FFFFFF so nothing changes, and in a dark look the glass goes
   dark with it, which puts the ink tokens back above 7:1. */

.gt-plan-card { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 14px;
  padding: 30px 28px 30px; border: var(--gt-border-width) solid color-mix(in srgb, var(--gt-surface) 75%, transparent); border-radius: var(--gt-radius-xl);
  background: color-mix(in srgb, var(--gt-surface) 30%, transparent); box-shadow: var(--gt-shadow-md);
  backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%); }
.gt-plan-card--feature { box-shadow: var(--gt-shadow-lg); }
.gt-plan-card__badge { position: absolute; top: 18px; right: 20px; background: var(--gt-primary); color: var(--gt-on-primary); font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); padding: 6px 12px; min-height: 24px; display: inline-flex; align-items: center; border-radius: var(--gt-radius-pill); white-space: nowrap; }
.gt-plan-card__icon { width: 52px; height: 52px; border-radius: var(--gt-radius-lg); display: grid; place-items: center; background: color-mix(in srgb, var(--gt-surface) 30%, transparent); box-shadow: var(--gt-shadow-md); }
.gt-plan-card__icon svg { width: 24px; height: 24px; }
/* Plain small text, not the gt-tag pill - the plan tag is a quiet qualifier
   ("Free / download"), not an interactive filter chip. */
.gt-plan-card__tag { font-size: var(--gt-text-sm); font-weight: var(--gt-w-medium); color: var(--gt-ink-2); }
.gt-plan-card h3 { font-family: var(--gt-font-head); font-size: var(--gt-text-xl); letter-spacing: var(--gt-head-tracking); }
.gt-plan-card p { font-size: var(--gt-text-sm); color: var(--gt-ink-2); line-height: 1.6; flex: 1; }
.gt-plan-card .gt-link { margin-top: 4px; }

/* ==========================================================================
   components/02-molecules/search-facets/search-facets.css
   ========================================================================== */

/* components/02-molecules/search-facets/search-facets.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (361-365)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-facetgroup { padding: 16px 0; border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-facetgroup:first-child { padding-top: 0; }
.gt-facetgroup__title { font-family: var(--gt-font-head); font-weight: var(--gt-w-bold); font-size: var(--gt-text-sm); margin-bottom: 12px; letter-spacing: var(--gt-head-tracking); }
.gt-facetgroup__more { background: none; border: none; color: var(--gt-primary); font-family: inherit; font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); cursor: pointer; padding: 8px 0 0; min-height: 24px; }

/* ==========================================================================
   components/02-molecules/search-result/search-result.css
   ========================================================================== */

/* components/02-molecules/search-result/search-result.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (352-360)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-result { padding: 22px 0; border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-result:first-child { padding-top: 0; }
.gt-result__meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; font-size: var(--gt-text-xs); color: var(--gt-ink-3); }
.gt-result__url { color: var(--gt-ink-3); font-family: var(--gt-font-mono); font-size: var(--gt-text-2xs); }
.gt-result h3 { font-size: var(--gt-text-lg); margin-bottom: 6px; }
.gt-result h3 a { color: var(--gt-ink); text-decoration: none; }
.gt-result h3 a:hover { color: var(--gt-primary); text-decoration: underline; }
.gt-result p { font-size: var(--gt-text-sm); color: var(--gt-ink-2); line-height: var(--gt-leading-normal); }
.gt-result mark { background: var(--gt-warning-soft); color: inherit; padding: 0 2px; border-radius: 2px; }

/* ==========================================================================
   components/02-molecules/share/share.css
   ========================================================================== */

/* components/02-molecules/share/share.css
   Share row on article/event pages: label left, round intent buttons right,
   above a hairline. Authored for the 2026-09 article-page design (Figma
   Article 6:3) — no marketing extraction source. */

.gt-share { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; border-top: var(--gt-border-width) solid var(--gt-border); padding-top: var(--gt-space-6); }
.gt-share__label { font-family: var(--gt-font-head); font-size: var(--gt-text-md); font-weight: var(--gt-w-semibold); color: var(--gt-ink); letter-spacing: var(--gt-head-tracking); }
.gt-share__buttons { display: flex; gap: 10px; flex-wrap: wrap; }
.gt-share__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: var(--gt-border-width) solid var(--gt-border-2);
  border-radius: var(--gt-radius-pill);
  background: var(--gt-surface); color: var(--gt-ink-2);
  cursor: pointer; transition: background-color .16s, border-color .16s, color .16s;
}
.gt-share__btn:hover { border-color: var(--gt-primary); color: var(--gt-primary); background: var(--gt-primary-soft); }
.gt-share__btn svg { width: 18px; height: 18px; }
.gt-share__copy .gt-share__ic-done { display: none; }
.gt-share__copy.is-copied { border-color: var(--gt-success); color: var(--gt-success); }
.gt-share__copy.is-copied .gt-share__ic-done { display: block; }
.gt-share__copy.is-copied .gt-share__ic-idle { display: none; }

/* ==========================================================================
   components/02-molecules/site-alert/site-alert.css
   ========================================================================== */

/* Site alert: site-wide dismissible notice strip at the top of the page, above the utility bar. */
.gt-site-alert { background: var(--gt-primary-soft); border-bottom: var(--gt-border-width) solid color-mix(in srgb, var(--gt-primary) 22%, transparent); }
.gt-site-alert__inner { max-width: var(--gt-maxw); margin: 0 auto; padding: 14px 24px; display: flex; align-items: flex-start; gap: 14px; }
/* One line of the body tall (its size and leading), so the icon centres on the
   first line, whether that is the title or the message: both are the same size.
   Relaxed is the leading a theme gives a bare p (the Drupal theme's base.css),
   so the body reads the same whether its text is wrapped in paragraphs or not. */
.gt-site-alert__icon { flex: 0 0 auto; display: flex; align-items: center; width: 24px; height: 1lh; font-size: var(--gt-text-sm); line-height: var(--gt-leading-relaxed); color: var(--gt-primary); }
.gt-site-alert__body { flex: 1; font-size: var(--gt-text-sm); line-height: var(--gt-leading-relaxed); color: var(--gt-ink); }
.gt-site-alert__body p + p { margin-top: var(--gt-space-1); }
.gt-site-alert__body strong { font-family: var(--gt-font-head); font-weight: var(--gt-w-bold); letter-spacing: var(--gt-head-tracking); }
.gt-site-alert__body a { color: var(--gt-primary); font-weight: var(--gt-w-semibold); text-decoration-color: currentColor; }
.gt-site-alert__title { font-family: var(--gt-font-head); font-weight: var(--gt-w-bold); letter-spacing: var(--gt-head-tracking); font-size: var(--gt-text-sm); color: var(--gt-ink); }
/* Centred on the first line like the icon, but through a negative margin rather
   than a 1lh height: one line is under 24px, the minimum target size. */
.gt-site-alert__close { all: unset; box-sizing: border-box; cursor: pointer; flex: 0 0 auto; width: 28px; height: 28px; font-size: var(--gt-text-sm); line-height: var(--gt-leading-relaxed); margin-block: calc((1lh - 28px) / 2); border-radius: var(--gt-radius-sm); display: grid; place-items: center; color: var(--gt-ink-2); }
.gt-site-alert__close:hover { background: color-mix(in srgb, var(--gt-primary) 14%, transparent); color: var(--gt-primary); }
.gt-site-alert__close:focus-visible { box-shadow: var(--gt-focus-ring); }
.gt-site-alert--emergency { background: var(--gt-error-soft); border-bottom-color: color-mix(in srgb, var(--gt-error) 30%, transparent); }
.gt-site-alert--emergency .gt-site-alert__icon { color: var(--gt-error); }
.gt-site-alert--emergency .gt-site-alert__body a { color: var(--gt-error); }
.gt-site-alert--update { background: var(--gt-warning-soft); border-bottom-color: color-mix(in srgb, var(--gt-warning) 32%, transparent); }
.gt-site-alert--update .gt-site-alert__icon { color: var(--gt-warning); }
.gt-site-alert--update .gt-site-alert__body a { color: var(--gt-warning); }
html.js .gt-site-alert--pending { display: none; }

/* ==========================================================================
   components/02-molecules/stat/stat.css
   ========================================================================== */

/* components/02-molecules/stat/stat.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (551-556)
   Token-coloured, so a stat reads on any ground. The white text, divider and
   spacing of the dark vision band are scoped to .gt-vision here rather than
   beside .gt-vision in base/chrome.css: chrome.css is a merged foundation the
   theme reconciles by hand, and keeping the rules in the component file keeps
   them adopted verbatim. The one-column step at 560px: at two columns a 4xl
   figure is wider than its cell on a 320px screen and scrolls the page
   sideways. */

/* ── Key figures ── */
.gt-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gt-space-5) var(--gt-space-8); }
.gt-stat strong { display: block; font-family: var(--gt-font-head); font-weight: var(--gt-head-weight); font-size: var(--gt-text-4xl); line-height: var(--gt-leading-tight); letter-spacing: var(--gt-head-tracking); color: var(--gt-ink); }
.gt-stat span { display: block; font-size: var(--gt-text-sm); color: var(--gt-ink-3); margin-top: var(--gt-space-2); line-height: var(--gt-leading-snug); max-width: 24ch; }

/* ── Inside the vision band ── */
.gt-vision .gt-stats { margin-top: var(--gt-space-12); padding-top: var(--gt-space-8); border-top: var(--gt-border-width) solid color-mix(in srgb, var(--gt-on-scrim) 18%, transparent); }
.gt-vision .gt-stat strong { color: var(--gt-on-scrim); }
.gt-vision .gt-stat span { color: var(--gt-on-scrim-soft); }

@media (max-width: 760px) { .gt-stats { grid-template-columns: 1fr 1fr; gap: var(--gt-space-6); } }
@media (max-width: 560px) { .gt-stats { grid-template-columns: 1fr; } }

/* ==========================================================================
   components/02-molecules/step-card/step-card.css
   ========================================================================== */

/* Numbered process step. Without a printed number the step shows its position
   from the CSS counter on .gt-steps, so a reordered list stays numbered. */
.gt-step { position: relative; counter-increment: step; padding: 28px 22px 24px; border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); background: var(--gt-surface); box-shadow: var(--gt-shadow-sm); }
.gt-step__n { width: 38px; height: 38px; border-radius: 50%; background: var(--gt-primary); color: var(--gt-on-primary); display: grid; place-items: center; font-family: var(--gt-font-head); font-weight: var(--gt-w-extrabold); font-size: var(--gt-text-md); margin-bottom: 16px; }
.gt-step__n:empty::before { content: counter(step); }
.gt-step__title { font-family: var(--gt-font-head); font-size: var(--gt-text-lg); letter-spacing: var(--gt-head-tracking); display: block; }
.gt-step__body { font-size: var(--gt-text-sm); color: var(--gt-ink-2); margin-top: 8px; line-height: var(--gt-leading-normal); }
/* Rich text from the editor: the kit resets p margins, so blocks need their own rhythm. */
.gt-step__body > * + * { margin-top: var(--gt-space-4); }
/* The chevron bridging this card to the next. Centred on the midpoint of the
   grid gap: past the card's border by half the gap, then pulled back by half
   its own width, so it holds for any gap and icon size. Vertically it lines up
   with the number (28px padding + half of 38px). */
.gt-step__arrow { position: absolute; right: calc(var(--gt-space-6) / -2 - var(--gt-border-width)); top: 47px; transform: translate(50%, -50%); z-index: 1; color: var(--gt-border-2); display: grid; place-items: center; }

/* ==========================================================================
   components/02-molecules/utility-bar/utility-bar.css
   ========================================================================== */

/* components/02-molecules/utility-bar/utility-bar.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (18-27, 577-579)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs).

   One deliberate exception: the demo site's kill rule
   (`.gt-utility { display: none !important; }` — it prefers its product bar)
   moved back to demo/css/site.css. A page decision must not make the kit
   component unrenderable for every consumer. */
.gt-utility { background: var(--gt-ink); color: color-mix(in srgb, var(--gt-bg) 70%, var(--gt-ink)); font-size: var(--gt-text-sm); }
.gt-utility__inner { max-width: var(--gt-maxw); margin: 0 auto; padding: 9px 24px; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.gt-utility__gov { display: flex; align-items: center; gap: 9px; }
.gt-utility__flag { width: 18px; height: 12px; border-radius: 1px; display: block; flex: 0 0 auto; }
.gt-utility a { color: inherit; text-decoration: none; opacity: .85; min-height: 24px; display: inline-flex; align-items: center; }
.gt-utility a:hover { opacity: 1; text-decoration: underline; }
.gt-utility__links { display: flex; gap: 18px; }

/* Dark / cosmos theme: keep the top bar and footer black (they key off --gt-ink,
   which inverts to light in dark mode) and restore legible headings. */
html[data-mode="dark"] .gt-utility { background: var(--gt-surface-sunken); color: color-mix(in srgb, var(--gt-ink) 60%, transparent); }

/* ==========================================================================
   components/02-molecules/value/value.css
   ========================================================================== */

/* components/02-molecules/value/value.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (537-541)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ── Values chips ── */
.gt-values { display: flex; flex-wrap: wrap; gap: 12px; }
.gt-value { display: inline-flex; align-items: center; gap: 9px; padding: 11px 18px; min-height: 44px; border-radius: var(--gt-radius-pill); background: var(--gt-surface); color: var(--gt-ink); border: var(--gt-border-width) solid var(--gt-border-2); font-family: var(--gt-font-head); font-weight: var(--gt-w-semibold); letter-spacing: var(--gt-head-tracking); }
.gt-value svg { width: 18px; height: 18px; color: var(--gt-primary); flex: 0 0 auto; }

/* ==========================================================================
   components/03-organisms/accordion/accordion.css
   ========================================================================== */

/* ─────────────────────────── ACCORDION ─────────────────────────── */
.gt-accordion { border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card); overflow: hidden; background: var(--gt-surface); }
.gt-accordion__item + .gt-accordion__item { border-top: var(--gt-border-width) solid var(--gt-border); }
.gt-accordion__heading { margin: 0; font: inherit; }
.gt-accordion__btn {
  all: unset; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 16px;
  width: 100%; padding: 18px 20px; cursor: pointer; min-height: 24px;
  font-family: var(--gt-font-head); font-weight: var(--gt-w-bold); font-size: var(--gt-text-lg); color: var(--gt-ink);
  letter-spacing: var(--gt-head-tracking); transition: background-color .15s;
}
.gt-accordion__btn:hover { background: var(--gt-surface-2); }
/* The frame's overflow clips anything outside a button, so the ring is drawn
   inside it, on the frame's inner radius to fit the corner items too. */
.gt-accordion__btn:focus-visible { box-shadow: inset 0 0 0 2.5px var(--gt-focus), inset 0 0 0 5px var(--gt-surface); border-radius: calc(var(--gt-radius-card) - var(--gt-border-width)); }
.gt-accordion__chev { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; width: 20px; height: 20px; color: var(--gt-primary); transition: transform .22s; }
.gt-accordion__item--open .gt-accordion__chev { transform: rotate(180deg); }
.gt-accordion__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .28s ease; }
.gt-accordion__item--open .gt-accordion__panel { grid-template-rows: 1fr; }
/* The 0fr row only clips a closed panel, so its links would stay in the tab
   order and its text in the accessibility tree. visibility removes both; its
   transition keeps the panel visible until the collapse ends, and shows it at
   once on opening. */
.gt-accordion__panel-inner { overflow: hidden; padding: 0 20px; visibility: hidden; transition: visibility .28s; }
.gt-accordion__item--open .gt-accordion__panel-inner { padding-top: var(--gt-space-1); padding-bottom: 20px; visibility: visible; }
.gt-accordion__panel-inner p { color: var(--gt-ink-2); font-size: var(--gt-text-sm); }

/* ==========================================================================
   components/03-organisms/banner/banner.css
   ========================================================================== */

/* Page banner: full-bleed band holding the h1, subtitle and feature image. */
.gt-banner {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-bottom: var(--gt-banner-py, var(--gt-section-py));
}

/* Content and media stack within the page wrap. */
.gt-banner .gt-wrap {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-8);
}

.gt-banner__content {
  display: flex;
  flex-direction: column;
  gap: var(--gt-space-4);
}

.gt-banner__label { display: flex; flex-wrap: wrap; gap: var(--gt-space-2); }

.gt-banner__title { font-size: var(--gt-text-4xl); }

.gt-banner__subtitle {
  margin: 0;
  font-size: var(--gt-text-lg);
  line-height: var(--gt-leading-normal);
  color: var(--gt-ink-2);
}

.gt-banner__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gt-space-3);
  font-size: var(--gt-text-sm);
  color: var(--gt-ink-2);
}

/* Each item is icon + text; the icons come from the icon atom. */
.gt-banner__meta-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 24px;
}
.gt-banner__meta-item .gt-icon { flex: none; color: var(--gt-ink-3); }

/* Middot separator between metadata items. */
.gt-banner__meta-item + .gt-banner__meta-item::before {
  content: "·";
  margin-inline-end: var(--gt-space-1);
  color: var(--gt-ink-3);
}

/* ── Default banner: the 2026-09 landing-page design (Figma "Landing / Hero"
   frames). Without an image: a compact band on the page's own ground (no
   background of its own), closed by a hairline — title, optional subtitle,
   optional CTA. With an image: the split
   treatment at banner scale — copy left, the image bleeding to the band's
   top, right and bottom edges and mask-fading into the band behind the copy
   (the hero's split mechanism; see hero.css for why the mask, not a card). ── */
.gt-banner--default {
  --gt-banner-py: calc(44px * var(--gt-density));
  border-bottom: var(--gt-border-width) solid var(--gt-border);
  padding-top: var(--gt-banner-py);
  /* The band owns the breathing room to the content below it — consumers
     should not pad their page shells to compensate. */
  margin-bottom: var(--gt-space-10);
}

/* When the breadcrumbs render above the band (Drupal's breadcrumb region,
   not the slot), the band separates itself from them with the same hairline
   it closes with. With breadcrumbs in the slot the band sits directly under
   the nav, whose own hairline already does that job — no top border. */
.gt-banner--default:not(:has(.gt-banner__breadcrumbs)) {
  border-top: var(--gt-border-width) solid var(--gt-border);
}

/* Landing rhythm: 20px under the title, 32px above the CTA (gap + margin). */
.gt-banner--default .gt-banner__content { gap: var(--gt-space-5); }
.gt-banner--default .gt-banner__title { line-height: 1.06; max-width: 24ch; text-wrap: balance; }
.gt-banner--default .gt-banner__subtitle { line-height: var(--gt-leading-relaxed); max-width: 52ch; }
.gt-banner--default .gt-banner__cta { margin-top: var(--gt-space-3); }

/* With an image the band deepens and sits on the banner ground. */
.gt-banner--default.gt-banner--has-image {
  --gt-banner-py: clamp(48px, 7vw, 96px);
  background: var(--gt-banner-bg);
}

/* Desktop: the image is not a card but the band's right half — absolute
   against the full-bleed band, fading into the ground where the copy sits.
   Below 901px the base stacked treatment (16/6 card under the copy) holds. */
@media (min-width: 901px) {
  .gt-banner--default.gt-banner--has-image {
    position: relative;
    overflow: hidden;
    isolation: isolate;
  }
  .gt-banner--default.gt-banner--has-image .gt-banner__content { max-width: 50%; }
  .gt-banner--default.gt-banner--has-image .gt-banner__media {
    position: absolute;
    inset: 0 0 0 45%;
    z-index: -1;
    width: auto;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 45%);
    mask-image: linear-gradient(90deg, transparent, #000 45%);
  }
  .gt-banner--default.gt-banner--has-image .gt-banner__image {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    border-radius: 0;
  }
}

/* ── Article banner: the 2026-09 article-page design. A full-bleed band on its
   own ground, closed by a hairline: copy left (breadcrumbs, badges, title,
   subtitle, meta), the feature image right as a 16/9 card. The subtitle sits
   directly under the title and reads as the opening line of the piece, with
   the meta row closing the copy stack. ── */
.gt-banner--article {
  --gt-banner-py: clamp(40px, 5vw, 64px);
  --gt-banner-image-ratio: 16 / 9;
  background: var(--gt-banner-bg);
  border-bottom: var(--gt-border-width) solid var(--gt-border);
  padding-top: var(--gt-banner-py);
}

.gt-banner--article .gt-wrap {
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: var(--gt-space-8) clamp(32px, 4vw, 48px);
  align-items: center;
}

/* A page title, not a poster: wide enough that a long ministerial title wraps
   to two lines, not four. */
.gt-banner--article .gt-banner__title {
  line-height: 1.06;
  max-width: 24ch;
  text-wrap: balance;
}

.gt-banner--article .gt-banner__subtitle {
  font-size: var(--gt-text-xl);
  line-height: 1.45;
  font-family: var(--gt-font-head);
  font-weight: var(--gt-w-medium);
  letter-spacing: var(--gt-head-tracking);
  max-width: 60ch;
}

/* The split collapses under 900px; the image leads, as on the teasers. */
@media (max-width: 900px) {
  .gt-banner--article .gt-wrap { grid-template-columns: 1fr; }
  .gt-banner--article .gt-banner__media { order: -1; }
}

/* Optional primary action closing the copy stack (event pages: the register
   link — the 2026-09 event-page design). */
.gt-banner__cta { margin-top: var(--gt-space-2); }

.gt-banner__media { width: 100%; }

.gt-banner__image {
  display: block;
  width: 100%;
  aspect-ratio: var(--gt-banner-image-ratio, 16 / 6);
  object-fit: cover;
  border-radius: var(--gt-banner-image-radius, var(--gt-radius-card));
}

/* ==========================================================================
   components/03-organisms/command-palette/command-palette.css
   ========================================================================== */

/* components/03-organisms/command-palette/command-palette.css
   Moved out of the GovTheme marketing site by hand, after the Phase 1
   extract-css.mjs pass had already run. docs/INVENTORY.md line 148 originally
   ruled `gt-cmdk` out as "demo tooling, stays"; that was reversed - the
   palette is a site pattern an agency would ship, not scaffolding.
   Source: css/marketing-chrome.css (532-564), verbatim and in order.
   The marketing copy is deleted in the same change: govtheme-marketing's
   scripts/check-cascade.mjs proves the concatenation is unchanged. */

.gt-cmdk { position: fixed; inset: 0; z-index: 200; display: flex; flex-direction: column; align-items: center; padding: clamp(16px,11vh,120px) 16px 16px; background: color-mix(in srgb, var(--gt-ink) 52%, transparent); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.gt-cmdk.is-open { opacity: 1; pointer-events: auto; }
.gt-cmdk[hidden] { display: none; }
.gt-cmdk__panel { width: min(660px, 100%); max-height: min(74vh, 640px); display: flex; flex-direction: column; background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-card); box-shadow: var(--gt-shadow-lg); overflow: hidden; transform: translateY(-8px) scale(.99); transition: transform .2s cubic-bezier(.4,0,.2,1); }
.gt-cmdk.is-open .gt-cmdk__panel { transform: none; }
@media (prefers-reduced-motion: reduce) { .gt-cmdk, .gt-cmdk__panel { transition: none; } }
.gt-cmdk__search { display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-bottom: var(--gt-border-width) solid var(--gt-border); flex: 0 0 auto; }
.gt-cmdk__search > svg { width: 22px; height: 22px; color: var(--gt-ink-3); flex: 0 0 auto; }
.gt-cmdk__input { flex: 1; min-width: 0; border: none; background: none; font-family: var(--gt-font-body); font-size: var(--gt-text-xl); color: var(--gt-ink); padding: 4px 0; }
.gt-cmdk__input:focus { outline: none; box-shadow: none; }
.gt-cmdk__input::placeholder { color: var(--gt-ink-3); }
.gt-cmdk__esc { flex: 0 0 auto; font-family: var(--gt-font-mono); font-size: var(--gt-text-2xs); letter-spacing: .08em; color: var(--gt-ink-3); border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-sm); padding: 4px 8px; min-height: 24px; display: inline-flex; align-items: center; }
.gt-cmdk__results { flex: 1 1 auto; overflow-y: auto; padding: 8px; overscroll-behavior: contain; }
.gt-cmdk__group { margin-bottom: 6px; }
.gt-cmdk__grouptitle { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-ink-3); padding: 10px 12px 6px; }
.gt-cmdk__item { display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; padding: 11px 12px; min-height: 44px; border-radius: var(--gt-radius-input); text-decoration: none; color: var(--gt-ink); cursor: pointer; border: none; background: none; font-family: inherit; }
.gt-cmdk__item:hover, .gt-cmdk__item[aria-selected="true"] { background: var(--gt-primary-soft); }
.gt-cmdk__item[aria-selected="true"] { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--gt-primary) 40%, transparent); }
.gt-cmdk__ico { flex: 0 0 auto; width: 38px; height: 38px; border-radius: var(--gt-radius-input); background: var(--gt-primary-soft); color: var(--gt-primary); display: grid; place-items: center; }
.gt-cmdk__ico svg { width: 20px; height: 20px; }
.gt-cmdk__item[aria-selected="true"] .gt-cmdk__ico { background: var(--gt-primary); color: var(--gt-on-primary); }
.gt-cmdk__txt { flex: 1; min-width: 0; }
.gt-cmdk__title { display: block; font-weight: var(--gt-w-semibold); font-size: var(--gt-text-md); }
.gt-cmdk__title mark { background: var(--gt-warning-soft); color: inherit; border-radius: 2px; padding: 0 1px; }
.gt-cmdk__desc { display: block; font-size: var(--gt-text-xs); color: var(--gt-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ink-3 is fitted to the neutral surfaces, not to primary-soft - on the tinted
   hover/selected row it drops to ~3.6:1, so the desc steps up to ink-2 there. */
.gt-cmdk__item:hover .gt-cmdk__desc, .gt-cmdk__item[aria-selected="true"] .gt-cmdk__desc { color: var(--gt-ink-2); }
.gt-cmdk__go { flex: 0 0 auto; color: var(--gt-ink-3); opacity: 0; transition: opacity .12s, transform .12s; }
.gt-cmdk__item[aria-selected="true"] .gt-cmdk__go, .gt-cmdk__item:hover .gt-cmdk__go { opacity: 1; color: var(--gt-primary); }
.gt-cmdk__empty { padding: 40px 20px; text-align: center; color: var(--gt-ink-3); }
.gt-cmdk__empty strong { display: block; font-family: var(--gt-font-head); color: var(--gt-ink); font-size: var(--gt-text-lg); margin-bottom: 6px; }
.gt-cmdk__foot { flex: 0 0 auto; display: flex; align-items: center; gap: 18px; padding: 10px 16px; border-top: var(--gt-border-width) solid var(--gt-border); background: var(--gt-surface-2); font-size: var(--gt-text-2xs); color: var(--gt-ink-3); flex-wrap: wrap; }
.gt-cmdk__foot kbd { font-family: var(--gt-font-mono); font-size: 10px; background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-xs); padding: 2px 6px; color: var(--gt-ink-2); }
.gt-cmdk__foothint { display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 560px) { .gt-cmdk__input { font-size: var(--gt-text-lg); } .gt-cmdk__foot { display: none; } .gt-cmdk__desc { display: none; } }

/* ==========================================================================
   components/03-organisms/cta-band/cta-band.css
   ========================================================================== */

/* ─────────────────────────── CTA BAND ─────────────────────────── */
/* Two looks for one job. `primary` is the primary-coloured band the marketing
   site has always used; `dark` is the ink band with a corner glow, which existed
   twice before this — as the kit's `doghouse-cta` and as the Drupal theme's
   `callout --default`, byte-for-byte the same treatment under two names.
   (Divergence ledger D9.) */
.gt-cta-band { background: var(--gt-primary); color: var(--gt-on-primary); border-radius: var(--gt-radius-card); padding: clamp(32px,5vw,56px); display: flex; align-items: center; justify-content: space-between; gap: 32px; flex-wrap: wrap; }
/* :is(h2…h6), not h2: the schema offers title_level 2-6, so every level the
   component can emit gets the band treatment. :is() keeps h2's specificity. */
.gt-cta-band :is(h2, h3, h4, h5, h6) { color: var(--gt-on-primary); font-size: var(--gt-text-2xl); max-width: 22ch; }
.gt-cta-band__body { color: color-mix(in srgb, var(--gt-on-primary) 88%, transparent); margin-top: 10px; max-width: 48ch; }
.gt-cta-band__body :where(p, ul, ol) { color: inherit; line-height: inherit; }
/* 88%, not 75: the eyebrow is --gt-text-xs, and at 75% over a light primary
   (Cosmos adapts brand colours up to L 0.72) it dimmed to ~4.0:1. */
.gt-cta-band__eyebrow { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: color-mix(in srgb, var(--gt-on-primary) 88%, transparent); margin-bottom: 10px; }
.gt-cta-band__copy { min-width: 0; }
.gt-cta-band__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
.gt-cta-band__meta { font-size: var(--gt-text-xs); color: color-mix(in srgb, var(--gt-on-primary) 88%, transparent); }

/* ── Dark: ink ground, corner glow, two-column grid ── */
.gt-cta-band--dark { background: var(--gt-ink); color: var(--gt-surface); padding: clamp(36px,5vw,60px); display: grid; grid-template-columns: 1.4fr 1fr; gap: 36px; align-items: center; position: relative; overflow: hidden; }
/* The ring inverts with the band, on the band's own two colours, so it still
   reads where .gt-band--dark swaps them. */
.gt-cta-band--dark { --gt-focus-ring: 0 0 0 3px var(--gt-ink), 0 0 0 5.5px var(--gt-surface); }
.gt-cta-band--dark .gt-cta-band__eyebrow { color: color-mix(in srgb, var(--gt-surface) 60%, var(--gt-primary)); margin-bottom: 14px; }
.gt-cta-band--dark :is(h2, h3, h4, h5, h6) { color: var(--gt-surface); font-size: var(--gt-text-3xl); max-width: 18ch; margin-top: 0; }
.gt-cta-band--dark .gt-cta-band__body { color: color-mix(in srgb, var(--gt-surface) 78%, transparent); margin-top: 14px; max-width: 52ch; font-size: var(--gt-text-lg); line-height: 1.5; }
.gt-cta-band--dark .gt-cta-band__actions { flex-direction: column; align-items: stretch; gap: 12px; position: relative; z-index: 1; }
.gt-cta-band--dark .gt-cta-band__meta { color: color-mix(in srgb, var(--gt-surface) 78%, transparent); }
/* The primary action on the ink ground is the surface button, whatever the
   palette: a primary button there is invisible in any palette whose primary
   is near ink (1.05:1 boundary against 1.4.11's 3:1). Covers custom
   `actions` markup as well as the twig's own buttons. */
.gt-cta-band--dark .gt-btn:not(.gt-btn--outline-white, .gt-btn--secondary, .gt-btn--white) { background: var(--gt-surface); color: var(--gt-ink); border-color: var(--gt-surface); }
.gt-cta-band--dark .gt-btn:not(.gt-btn--outline-white, .gt-btn--secondary, .gt-btn--white):hover { background: color-mix(in srgb, var(--gt-surface) 88%, var(--gt-primary)); }
@media (max-width: 720px) { .gt-cta-band--dark { grid-template-columns: 1fr; gap: 24px; } }

/* ==========================================================================
   components/03-organisms/drawer/drawer.css
   ========================================================================== */

/* components/03-organisms/drawer/drawer.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (143-165, 167-183, 571-573)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-drawer { display: none; }

@media (max-width: 900px) {
  .gt-hero__inner { grid-template-columns: 1fr; gap: 32px; }
  .gt-hero__media { order: -1; aspect-ratio: 16/9; }
}
/* Collapse the primary nav to the drawer below 1024px so the generous desktop
   spacing never overflows on tablet widths (WCAG 1.4.10 reflow). */
@media (max-width: 1024px) {
  .gt-search { display: none; }
  /* Compound with .gt-iconbtn to outrank chrome's .gt-iconbtn { display: grid }
     regardless of file order — the desktop hide in site-header.css does the
     same, in the complementary media range. */
  .gt-iconbtn.gt-burger { display: grid; }
  .gt-nav-wrap { display: none; }
}
@media (max-width: 560px) {
  .gt-utility__links { display: none; }
  .gt-hero__title { font-size: var(--gt-text-3xl); }
  .gt-article__title { font-size: var(--gt-text-3xl); }
  .gt-event__date { flex-basis: 68px; }
}

/* mobile drawer */
.gt-drawer-scrim { position: fixed; inset: 0; background: rgba(8,10,18,.55); z-index: 110; opacity: 0; pointer-events: none; transition: opacity .25s; }
.gt-site.gt-menu-open .gt-drawer-scrim { opacity: 1; pointer-events: auto; }
.gt-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(340px, 88vw); background: var(--gt-surface);
  z-index: 120; transform: translateX(100%); box-shadow: var(--gt-shadow-lg);
  display: flex; flex-direction: column; padding: 20px; overflow-y: auto;
  /* Hidden as well as slid off-screen, so the closed drawer holds no tab stops.
     The visibility flip waits for the slide out and is immediate on open. */
  visibility: hidden; transition: transform .26s ease, visibility 0s linear .26s;
}
.gt-site.gt-menu-open .gt-drawer { transform: translateX(0); visibility: visible; transition: transform .26s ease, visibility 0s; }
.gt-drawer__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.gt-drawer details { border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-drawer summary { padding: 14px 4px; font-weight: var(--gt-w-semibold); font-size: var(--gt-text-md); cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center; }
.gt-drawer summary::-webkit-details-marker { display: none; }
.gt-drawer summary::after { content: "+"; color: var(--gt-primary); font-size: 20px; }
.gt-drawer details[open] summary::after { content: "–"; }
.gt-drawer details a { display: block; padding: 9px 14px; color: var(--gt-ink); text-decoration: none; font-size: var(--gt-text-sm); min-height: 24px; }
.gt-drawer details a:hover { color: var(--gt-primary); }
/* Flat (sectionless) menu items: same row treatment and ink as the section
   summaries, no link underline — the drawer is a menu, not prose. Direct
   children only, so section sub-links above keep their own rule. Consumers
   used to carry this as inline styles on each link; they no longer need to. */
.gt-drawer > a { display: block; padding: 14px 4px; font-weight: var(--gt-w-semibold); font-size: var(--gt-text-md); color: var(--gt-ink); text-decoration: none; border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-drawer > a:hover { color: var(--gt-primary); }

/* ==========================================================================
   components/03-organisms/hero/hero.css
   ========================================================================== */

/* ─────────────────────────── HERO ─────────────────────────── */
/* Two layout mechanisms, one stylesheet. Every variant rule is written twice:
   once as `.gt-hero--x`, which the Drupal theme sets from a prop, and once as
   `html[data-hero="x"]`, which the marketing customiser flips live from
   localStorage. Neither consumer can use the other's, and duplicating the
   selector is cheaper than duplicating the rule. (Divergence ledger, plan 0.5.)

   The scrim is tokenised: this used to hard-code rgba(8,10,18,.78) and #fff, so
   no site could retint an image hero. The `:has(.gt-hero__media)` guards matter
   too — without them the feature variant inverts its text to white even when
   there is no image behind it.

   2026-09 restyle (inspiration board `hero-banner`): oversized headline with
   high scale contrast against a lighter-weight lead, generous whitespace,
   soft depth on the media and the search box. All of it expressed through
   tokens so every look keeps its own palette, radius and shadow scale. */

/* Full width comes from the unwrapped hero region; the inner element constrains. */
.gt-hero { position: relative; overflow: hidden; background: var(--gt-surface-2); border-bottom: var(--gt-border-width) solid var(--gt-border); }
/* --_py is read back by the mobile split treatment, whose negative margins
   must exactly cancel this padding for the image to reach the band's edges. */
.gt-hero__inner {
  --_py: clamp(48px,7vw,96px);
  position: relative; z-index: 1; max-width: var(--gt-maxw); margin: 0 auto; padding: var(--_py) 24px;
  display: grid; grid-template-columns: 1.5fr 1fr; gap: clamp(40px,5vw,72px); align-items: center;
}
/* Split rules are the one deliberate exception to "write every variant rule
   twice": split is the default the consumers bake into the markup
   (`gt-hero--split`), so the class selector already covers the live lever's
   split state, and an unqualified `html[data-hero="split"]` rule would win on
   specificity and drag every OTHER variant back to split wherever the lever
   sits at its default — which is exactly what it did to the feature story in
   Storybook. The other variants keep both forms: their attr rules exist to
   override the baked split class. */

/* Split with no media: collapse to a single column. */
.gt-hero--split .gt-hero__inner:not(:has(.gt-hero__media)) { grid-template-columns: 1fr; }

/* Oversized headline; the lead drops to regular weight and muted ink so the
   scale contrast carries the hierarchy. Weight, tracking and line height stay
   with the heading rule in base.css — the customiser owns the weight and tracking. */
.gt-hero__title { font-size: var(--gt-text-5xl); max-width: 17ch; text-wrap: balance; }
.gt-hero__lead { font-size: var(--gt-text-lg); font-weight: var(--gt-w-regular); color: var(--gt-ink-2); margin-top: 20px; max-width: 52ch; line-height: var(--gt-leading-relaxed); }
.gt-hero__actions { display: flex; gap: 12px; margin-top: 32px; flex-wrap: wrap; }

/* Site search: one bordered box carrying the icon, a bare input and the submit
   button. The input's own focus ring is suppressed in favour of :focus-within
   on the box, so the visible indicator wraps the whole control. */
.gt-hero__search { margin-top: 32px; width: 100%; max-width: 560px; }
.gt-hero__search-box {
  display: flex; align-items: center; gap: 8px; padding: 6px 6px 6px 16px;
  background: var(--gt-surface); border: var(--gt-border-width) solid var(--gt-border-2);
  border-radius: calc(var(--gt-radius-input) + 6px); box-shadow: var(--gt-shadow-md);
  transition: border-color .14s, box-shadow .14s;
}
.gt-hero__search-box > svg { flex: none; color: var(--gt-ink-3); }
.gt-hero__search-input {
  flex: 1; min-width: 0; border: 0; background: transparent; padding: 0;
  min-height: calc(var(--gt-control-h) - 12px);
  font-family: var(--gt-font-body); font-size: var(--gt-text-md); color: var(--gt-ink);
}
.gt-hero__search-input::placeholder { color: var(--gt-ink-3); }
.gt-hero__search-input:focus-visible { box-shadow: none; }
.gt-hero__search-box:focus-within { border-color: var(--gt-focus); box-shadow: var(--gt-focus-ring); }
.gt-hero__search .gt-btn { flex: none; }

/* Popular-search chips. */
.gt-hero__tasks { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 24px; }
.gt-hero__tasks-label { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-ink-3); }
.gt-hero__tasks a {
  font-size: var(--gt-text-sm); font-weight: var(--gt-w-semibold); color: var(--gt-ink-2); text-decoration: none;
  padding: 8px 15px; min-height: 24px; display: inline-flex; align-items: center;
  border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-pill); background: var(--gt-surface);
  transition: border-color .14s, color .14s, background-color .14s;
}
.gt-hero__tasks a:hover { color: var(--gt-primary); border-color: var(--gt-primary); background: var(--gt-primary-soft); }

/* Media. The card treatment (radius, border, shadow) is the base and is what
   small screens use; the split variant replaces it with a full-bleed
   image, below. */
.gt-hero__media {
  position: relative; aspect-ratio: 4/3; overflow: hidden; border-radius: var(--gt-radius-xl);
  background: color-mix(in srgb, var(--gt-primary) 7%, var(--gt-surface-sunken));
  border: var(--gt-border-width) solid var(--gt-border); box-shadow: var(--gt-shadow-lg);
}
.gt-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Split, desktop: the image is not a card but the band's right half. It sits
   in the grid's second column, so the grid gap always separates it from the
   copy, and negative margins bleed it to the band's top, right and bottom
   edges. The right bleed is the distance from the inner's content edge to the
   band's, which is why the band is an inline-size container. There is no
   fade: a dark image faded into a light surface left a grey smear, and a hard
   edge at a fixed 45% ran under the copy column. The feature lever resets the
   margins; centred shares its treatment; minimal hides the media. Below 900px the stacked
   treatment takes over. */
@media (min-width: 901px) {
  .gt-hero--split { container-type: inline-size; }
  .gt-hero--split .gt-hero__inner { grid-template-columns: 1fr 1fr; }
  .gt-hero--split .gt-hero__media {
    align-self: stretch; aspect-ratio: auto; border: none; border-radius: 0; box-shadow: none;
    margin: calc(-1 * var(--_py)) calc(-1 * (max(0px, (100cqw - var(--gt-maxw)) / 2) + 24px)) calc(-1 * var(--_py)) 0;
  }
}

/* ── Centered: single column, everything centered ── */
.gt-hero--centered .gt-hero__inner,
html[data-hero="centered"] .gt-hero__inner { grid-template-columns: 1fr; text-align: center; justify-items: center; }
.gt-hero--centered .gt-hero__title,
html[data-hero="centered"] .gt-hero__title { margin-left: auto; margin-right: auto; }
.gt-hero--centered .gt-hero__lead,
html[data-hero="centered"] .gt-hero__lead { margin-left: auto; margin-right: auto; }
.gt-hero--centered .gt-hero__actions,
html[data-hero="centered"] .gt-hero__actions { justify-content: center; }
.gt-hero--centered .gt-hero__search,
html[data-hero="centered"] .gt-hero__search { margin-left: auto; margin-right: auto; }
.gt-hero--centered .gt-hero__tasks,
html[data-hero="centered"] .gt-hero__tasks { justify-content: center; }
/* ── Feature: full-bleed media behind the copy, dark scrim overlay ──
   Centred takes the same treatment: the same full-bleed media and scrim
   with the copy centred by its own rules above. */
:is(.gt-hero--feature, .gt-hero--centered),
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero { isolation: isolate; }
/* The inner drops its positioning so the media's containing block is the
   full-width band, not the max-width box: otherwise, past --gt-maxw, the
   image stops short and the band's ground shows either side of it. */
:is(.gt-hero--feature, .gt-hero--centered) .gt-hero__inner,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero__inner { position: static; grid-template-columns: 1fr; min-height: 480px; align-content: center; }
/* Buttons over the scrim take the dark-hero convention: the primary is
   on-scrim (white) with scrim-coloured text, the secondary an outline in
   on-scrim. A primary in the ink palette is ink on an ink scrim — text
   readable, boundary invisible (1.1:1 against 1.4.11's 3:1). Scoped to
   media being present, like the text colours below. */
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__actions .gt-btn:not(.gt-btn--secondary),
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__actions .gt-btn:not(.gt-btn--secondary) { background: var(--gt-on-scrim); color: var(--gt-scrim); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__actions .gt-btn:not(.gt-btn--secondary):hover,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__actions .gt-btn:not(.gt-btn--secondary):hover { background: var(--gt-on-scrim-soft); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__actions .gt-btn--secondary,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__actions .gt-btn--secondary { background: transparent; color: var(--gt-on-scrim); border-color: var(--gt-on-scrim); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__actions .gt-btn--secondary:hover,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__actions .gt-btn--secondary:hover { background: color-mix(in srgb, var(--gt-on-scrim) 16%, transparent); }
:is(.gt-hero--feature, .gt-hero--centered) .gt-hero__media,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero__media { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; aspect-ratio: auto; border: none; border-radius: 0; box-shadow: none; z-index: -1; }
/* The scrim itself — the feature variant is the only one that draws one. */
:is(.gt-hero--feature, .gt-hero--centered) .gt-hero__media::after,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero__media::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--gt-scrim), color-mix(in srgb, var(--gt-scrim) 55%, transparent));
}
/* Light copy is legible only over the media+scrim, so scope it to when an image
   is present; an imageless feature hero falls back to the default
   dark-on-surface treatment. The search box keeps its own surface, so it needs
   no scrim variant.

   The band's own ground also goes dark: the white copy must never depend on
   the image having painted — while it loads, if it fails, or for a contrast
   checker that cannot see an absolutely-positioned sibling behind the text. */
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media),
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) { background: var(--gt-scrim); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__title,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__title { color: var(--gt-on-scrim); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__lead,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__lead { color: var(--gt-on-scrim-soft); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__tasks-label,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__tasks-label { color: var(--gt-on-scrim-soft); }
/* The ring inverts with the copy: the default's dark line is lost on the
   scrim. The search box keeps its own surface, so its contents take the
   default ring back. */
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media),
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) { --gt-focus-ring: 0 0 0 3px var(--gt-scrim-solid), 0 0 0 5.5px var(--gt-on-scrim); }
:is(.gt-hero--feature, .gt-hero--centered):has(.gt-hero__media) .gt-hero__search-box > *,
html:is([data-hero="feature"], [data-hero="centered"]) .gt-hero:has(.gt-hero__media) .gt-hero__search-box > * { --gt-focus-ring: 0 0 0 3px var(--gt-surface), 0 0 0 5.5px var(--gt-focus); }

/* ── Minimal: plain surface, compact padding, no media ── */
.gt-hero--minimal,
html[data-hero="minimal"] .gt-hero { background: var(--gt-surface); }
.gt-hero--minimal .gt-hero__inner,
html[data-hero="minimal"] .gt-hero__inner { grid-template-columns: 1fr; padding-top: 44px; padding-bottom: 44px; }
.gt-hero--minimal .gt-hero__media,
html[data-hero="minimal"] .gt-hero__media { display: none; }
.gt-hero--minimal .gt-hero__title,
html[data-hero="minimal"] .gt-hero__title { font-size: var(--gt-text-3xl); }

/* ── Responsive ── */
@media (max-width: 900px) {
  .gt-hero__inner { grid-template-columns: 1fr; gap: 32px; }
  .gt-hero__title { font-size: var(--gt-text-4xl); }
  .gt-hero__media { order: -1; aspect-ratio: 16/9; }
  /* Split: the image runs edge-to-edge above the copy (negative margins
     cancel the inner's padding), with a hard bottom edge like the desktop's.
     Scoped to split: the feature variant's media keeps its scrim. */
  .gt-hero--split .gt-hero__media {
    margin: calc(-1 * var(--_py)) -24px 0;
    aspect-ratio: 4/3; border: none; border-radius: 0; box-shadow: none;
  }
}
@media (max-width: 600px) {
  .gt-hero__media { aspect-ratio: 16/10; }
  .gt-hero--split .gt-hero__media { aspect-ratio: 5/4; }
  .gt-hero__actions { flex-direction: column; align-items: stretch; }
  .gt-hero__actions .gt-btn { width: 100%; }
}
@media (max-width: 560px) {
  .gt-hero__title { font-size: var(--gt-text-3xl); }
  .gt-hero__search-box { flex-wrap: wrap; padding: 6px; }
  .gt-hero__search-box > svg { display: none; }
  .gt-hero__search-input { padding-left: 10px; }
}

/* ==========================================================================
   components/03-organisms/plan-grid/plan-grid.css
   ========================================================================== */

/* components/03-organisms/plan-grid/plan-grid.css
   The columns and breakpoints are the shared .gt-grid (base/base.css). Stretch
   is the grid's default already; it is stated because equal-height cards are
   what this wrapper promises plan-card (see the README), and it keeps
   .gt-plan-grid a defined class for the kit's class contract. */

.gt-plan-grid { align-items: stretch; }

/* ==========================================================================
   components/03-organisms/primary-nav/primary-nav.css
   ========================================================================== */

/* components/03-organisms/primary-nav/primary-nav.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (40-51, 52-63, 249-253, 256-260, 261-267, 333-346, 449-462, 463-467, 502-503)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* Primary nav + mega menu */
.gt-nav-wrap { background: var(--gt-surface); border-bottom: var(--gt-border-width) solid var(--gt-border); }
.gt-nav { max-width: var(--gt-maxw); margin: 0 auto; padding: 0 24px; display: flex; gap: 8px; }
.gt-nav__item { position: relative; }
/* The first link's own padding would otherwise inset the menu 20px from the
   brand above it; pull the item back so the text starts on the same line. */
.gt-nav__item:first-child { margin-left: -20px; }
.gt-nav__link {
  display: inline-flex; align-items: center; gap: 7px; padding: 19px 20px; min-height: 24px;
  font-weight: var(--gt-w-semibold); font-size: var(--gt-text-md); color: var(--gt-ink); text-decoration: none;
  border-bottom: 3px solid transparent; margin-bottom: -1px; cursor: pointer; background: none; border-top: none; border-left: none; border-right: none; font-family: inherit;
}
.gt-nav__link:hover, .gt-nav__item--open .gt-nav__link { color: var(--gt-primary); border-bottom-color: var(--gt-primary); }
/* Current page: the same primary underline as hover. Each header variant
   overrides it below, because on the bold bar primary-on-primary vanishes. */
.gt-nav__link[aria-current="page"] { color: var(--gt-primary); border-bottom-color: var(--gt-primary); }
.gt-nav__link svg { width: 16px; height: 16px; transition: transform .2s; }
.gt-nav__item--open .gt-nav__link svg { transform: rotate(180deg); }

.gt-mega {
  position: absolute; top: 100%; left: 0; min-width: 640px; background: var(--gt-surface);
  border: var(--gt-border-width) solid var(--gt-border); border-radius: var(--gt-radius-card);
  box-shadow: var(--gt-shadow-lg); padding: 24px; margin-top: 8px; z-index: 95;
  display: none; grid-template-columns: 1fr 1fr; gap: 6px 36px;
}
.gt-nav__item--open .gt-mega { display: grid; }
.gt-mega__col-title { grid-column: 1 / -1; font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-ink-3); margin-bottom: 4px; }
.gt-mega a { display: block; padding: 10px 12px; border-radius: var(--gt-radius-sm); text-decoration: none; color: var(--gt-ink); transition: background-color .14s, color .14s; }
.gt-mega a:hover { background: var(--gt-primary-soft); color: var(--gt-primary); }
.gt-mega a strong { display: block; font-weight: var(--gt-w-semibold); font-size: var(--gt-text-sm); }
.gt-mega a span { display: block; font-size: var(--gt-text-xs); color: var(--gt-ink-3); margin-top: 2px; }

/* Bold — primary-coloured nav band */
html[data-header="bold"] .gt-nav-wrap { background: var(--gt-primary); border-bottom-color: transparent; }
html[data-header="bold"] .gt-nav__link { color: var(--gt-on-primary); }
html[data-header="bold"] .gt-nav__link:hover, html[data-header="bold"] .gt-nav__item--open .gt-nav__link { color: var(--gt-on-primary); border-bottom-color: var(--gt-on-primary); }
html[data-header="bold"] .gt-nav__link[aria-current="page"] { color: var(--gt-on-primary); border-bottom-color: var(--gt-on-primary); }

html[data-header="minimal"] .gt-nav-wrap { background: transparent; border-bottom: none; }
html[data-header="minimal"] .gt-nav { gap: 4px; padding-bottom: 8px; }
html[data-header="minimal"] .gt-nav__item:first-child { margin-left: -14px; }
html[data-header="minimal"] .gt-nav__link { border-bottom: none; border-radius: var(--gt-radius-input); padding: 9px 14px; margin-bottom: 0; }
html[data-header="minimal"] .gt-nav__link:hover, html[data-header="minimal"] .gt-nav__item--open .gt-nav__link { background: var(--gt-primary-soft); color: var(--gt-primary); }
html[data-header="minimal"] .gt-nav__link[aria-current="page"] { background: var(--gt-primary-soft); color: var(--gt-primary); }

/* ── Menu variants (data-menu on <html>) ── */
html[data-menu="simple"] .gt-mega { min-width: 340px; grid-template-columns: 1fr; }
html[data-menu="list"] .gt-mega { min-width: 240px; grid-template-columns: 1fr; padding: 8px; }
html[data-menu="list"] .gt-mega__col-title { display: none; }
/* Hide only the summary line. `.gt-mega a span` would also match the
   .gt-mega__ico and .gt-mega__t wrapper spans of the rich markup, emptying
   every link; :not(.gt-mega__ico) keeps the plain-markup summaries covered. */
html[data-menu="list"] .gt-mega .gt-mega__t > span,
html[data-menu="list"] .gt-mega a > span:not(.gt-mega__ico):not(.gt-mega__t) { display: none; }
html[data-menu="list"] .gt-mega a { padding: 9px 12px; }

/* Rich mega-menu: links region + featured column */
.gt-mega--rich { min-width: 0; width: max-content; max-width: min(940px, calc(100vw - 32px)); grid-template-columns: minmax(0,1fr) 264px; gap: 0 36px; align-items: start; }
.gt-mega__links { display: flex; flex-direction: column; min-width: 0; }
.gt-mega__linkgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 28px; }
.gt-mega__feature { background: var(--gt-surface-2); border-radius: var(--gt-radius-card); padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.gt-mega__feature-eyebrow { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-primary); }
.gt-mega__feature-media { aspect-ratio: 16/10; border-radius: var(--gt-radius-sm); background: var(--gt-surface-sunken); background-image: repeating-linear-gradient(135deg, var(--gt-border) 0 1px, transparent 1px 11px); margin-bottom: 4px; }
.gt-mega__feature strong { font-family: var(--gt-font-head); font-size: var(--gt-text-md); letter-spacing: var(--gt-head-tracking); }
.gt-mega__feature p { font-size: var(--gt-text-xs); color: var(--gt-ink-2); line-height: var(--gt-leading-snug); margin: 0; }
.gt-mega__browse { margin-top: 8px; padding: 11px 12px 4px; border-top: var(--gt-border-width) solid var(--gt-border); font-weight: var(--gt-w-semibold); color: var(--gt-primary); }
/* 340px, not 280px: at 280px the link titles wrap and the browse line's arrow
   drops onto its own line. The list variant stays at its own 240px. */
html[data-menu="simple"] .gt-mega--rich, html[data-menu="list"] .gt-mega--rich { min-width: 340px; width: auto; grid-template-columns: 1fr; }
html[data-menu="list"] .gt-mega--rich { min-width: 240px; }
html[data-menu="simple"] .gt-mega__browse, html[data-menu="list"] .gt-mega__browse { white-space: nowrap; }
html[data-menu="simple"] .gt-mega__feature, html[data-menu="list"] .gt-mega__feature { display: none; }
html[data-menu="simple"] .gt-mega__linkgrid, html[data-menu="list"] .gt-mega__linkgrid { grid-template-columns: 1fr; }

/* ── Mega-menu link icons ── */
.gt-mega a { display: flex; align-items: flex-start; gap: 12px; }
.gt-mega__ico { flex: 0 0 auto; width: 38px; height: 38px; border-radius: var(--gt-radius-sm); background: var(--gt-primary-soft); color: var(--gt-primary); display: inline-grid; place-content: center; place-items: center; line-height: 0; transition: background-color .14s, color .14s; }
.gt-mega__ico svg { width: 19px; height: 19px; display: block; }
.gt-mega a:hover .gt-mega__ico { background: var(--gt-primary); color: var(--gt-on-primary); }
.gt-mega__t { display: block; flex: 1; min-width: 0; }
/* L2 dropdown text must be black/ink — the generic `.gt-mega a span` rule
   tints the .gt-mega__t wrapper grey, so set title + description explicitly. */
.gt-mega .gt-mega__t strong { color: var(--gt-ink); font-weight: var(--gt-w-semibold); line-height: 1.25; }
.gt-mega .gt-mega__t > span { color: var(--gt-ink-2); margin-top: 3px; line-height: 1.35; }
.gt-mega a:hover .gt-mega__t strong { color: var(--gt-primary); }
html[data-menu="list"] .gt-mega__ico { width: 30px; height: 30px; }
html[data-menu="list"] .gt-mega__ico svg { width: 16px; height: 16px; }

/* Hover bridge: the dropdown sits 8px below the trigger, creating a dead zone
   where the pointer leaves both the link and the panel (closing the menu while
   travelling down). This invisible bridge keeps the hover intent unbroken. */
.gt-mega::before { content: ""; position: absolute; left: 0; right: 0; top: -12px; height: 12px; }

.gt-nav__link { white-space: nowrap; }

/* A panel on its own, outside the nav (a design-system page, a story): in the
   flow, shown, at its desktop width. inline-grid shrinks it to its content, so
   Simple and List keep their dropdown widths. The hover bridge goes: with no
   positioned panel it would sit against some ancestor, an invisible strip in
   the pointer's way. */
.gt-mega--standalone { position: static; display: inline-grid; margin-top: 0; max-width: none; vertical-align: top; }
.gt-mega--standalone::before { content: none; }

/* ==========================================================================
   components/03-organisms/site-footer/site-footer.css
   ========================================================================== */

/* components/03-organisms/site-footer/site-footer.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (125-126, 132-139, 580-586)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* Site footer */
.gt-site-footer { background: var(--gt-footer-bg); color: var(--gt-footer-fg); margin-top: auto; }

.gt-site-footer__main { max-width: var(--gt-maxw); margin: 0 auto; padding: 48px 24px; display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 40px; }
/* The collapse lives beside the base rule so file order settles it, in the
   bundle and when Drupal SDC attaches this stylesheet on its own. */
@media (max-width: 900px) { .gt-site-footer__main { grid-template-columns: 1fr 1fr; gap: 28px; } }
@media (max-width: 560px) { .gt-site-footer__main { grid-template-columns: 1fr; } }
.gt-site-footer h4 { color: var(--gt-footer-heading); font-family: var(--gt-font-head); font-size: var(--gt-text-md); margin-bottom: 14px; }
/* Drupal renders the footer's menus as nested ul/li and its text as p, and the
   theme's base.css gives ul/ol their own ink colour — which intercepts the
   inheritance `a { color: inherit }` depends on and turns every menu link
   footer-dark. Zero-specificity, so anything that sets its own colour on
   purpose still wins. Same fix spotlight and cta-band carry for their body
   slots. */
.gt-site-footer :where(p, ul, ol) { color: inherit; }
.gt-site-footer a { color: inherit; text-decoration: none; opacity: .82; display: block; padding: 6px 0; min-height: 24px; }
.gt-site-footer a:hover { opacity: 1; text-decoration: underline; }
/* The ring inverts on the footer's ground, where the default's dark line is
   lost; full opacity, as on hover, so the link's .82 does not dim it. */
.gt-site-footer { --gt-focus-ring: 0 0 0 3px var(--gt-footer-bg), 0 0 0 5.5px var(--gt-footer-heading); }
.gt-site-footer a:focus-visible { opacity: 1; }
.gt-site-footer__brand-name { font-family: var(--gt-font-head); font-weight: 800; font-size: var(--gt-text-xl); color: var(--gt-footer-heading); letter-spacing: var(--gt-head-tracking); }
.gt-site-footer__brand-sub { font-size: var(--gt-text-2xs); opacity: .6; margin-top: 6px; }
.gt-site-footer__bottom { border-top: 1px solid var(--gt-footer-border); }
/* .9, not .75: this row's links also carry the shared `a { opacity: .82 }`,
   and .75 x .82 dimmed them to ~4.0:1 on the footer ground in every look.
   .9 x .82 holds >= 5:1 while keeping the bottom row quieter than the columns. */
.gt-site-footer__bottom-inner { max-width: var(--gt-maxw); margin: 0 auto; padding: 20px 24px; display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; font-size: var(--gt-text-sm); opacity: .9; }

/* ==========================================================================
   components/03-organisms/site-header/site-header.css
   ========================================================================== */

/* components/03-organisms/site-header/site-header.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (842-849)
           demo/css/site.css (29-38, 141-142, 184-184, 240-248, 254-255, 431-448, 499-501, 592-596)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* Header search button (opens the command palette) */
.gt-searchbtn { display: inline-flex; align-items: center; gap: 10px; min-height: 40px; padding: 0 14px 0 12px; border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-input); background: var(--gt-surface); color: var(--gt-ink-3); cursor: pointer; font-family: var(--gt-font-body); font-size: var(--gt-text-sm); transition: border-color .14s, color .14s; }
.gt-searchbtn:hover { border-color: var(--gt-primary); color: var(--gt-ink-2); }
.gt-searchbtn:focus-visible { box-shadow: var(--gt-focus-ring); }
.gt-searchbtn svg { width: 18px; height: 18px; flex: 0 0 auto; }
.gt-searchbtn__label { flex: 1; text-align: left; }
.gt-searchbtn__kbd { font-family: var(--gt-font-mono); font-size: 10px; letter-spacing: .04em; border: var(--gt-border-width) solid var(--gt-border-2); border-radius: var(--gt-radius-xs); padding: 2px 6px; color: var(--gt-ink-3); }
@media (max-width: 980px) { .gt-searchbtn__kbd { display: none; } }

/* Site header */
.gt-site-header { background: var(--gt-surface); border-bottom: var(--gt-border-width) solid var(--gt-border); position: sticky; top: 0; z-index: 90; }
.gt-site-header__bar { max-width: var(--gt-maxw); margin: 0 auto; padding: 16px 24px; display: flex; align-items: center; gap: 20px; }
.gt-logo { display: flex; align-items: center; gap: 13px; text-decoration: none; color: var(--gt-ink); flex: 0 1 auto; }
.gt-logo__mark { width: 46px; height: 46px; border-radius: var(--gt-radius-input); background: var(--gt-primary); color: var(--gt-on-primary); display: grid; place-items: center; font-family: var(--gt-font-head); font-weight: 800; font-size: 22px; flex: 0 0 auto; }
.gt-logo__crest { display: block; font-size: var(--gt-text-2xs); color: var(--gt-ink-3); letter-spacing: .04em; }
.gt-logo__name { display: block; font-family: var(--gt-font-head); font-weight: var(--gt-w-extrabold); font-size: var(--gt-text-lg); letter-spacing: var(--gt-head-tracking); line-height: 1.15; }
.gt-site-header__spacer { flex: 1; }
/* The header search shows only where the full nav does; ≤1024px drawer.css
   hides it with the nav. The display is media-scoped (like the burger's, in
   the complementary range) so bundle file order cannot re-show it on mobile —
   the bare rule here used to override drawer.css's hide by coming later. */
.gt-search { align-items: center; gap: 8px; }
@media (min-width: 1025px) { .gt-search { display: flex; } }
.gt-search .gt-input-group { width: 260px; }

/* Mobile nav: the burger shows only where the nav has collapsed to the drawer
   (drawer.css, ≤1024px). Compound with .gt-iconbtn: chrome's .gt-iconbtn sets
   display:grid and sits later in the bundle, so a bare .gt-burger rule loses
   to it at every width. The two media ranges are exclusive, so file order
   cannot flip the result. */
@media (min-width: 1025px) { .gt-iconbtn.gt-burger { display: none; } }
/* Chrome's .gt-iconbtn opens with `all: unset`, which resets flex-shrink, so
   without this the burger is what gives way when a long site name fills the bar. */
.gt-iconbtn.gt-burger { flex-shrink: 0; }

/* ── Header variants (data-header on <html>) ── */
/* Centered */
html[data-header="centered"] .gt-site-header__bar { justify-content: center; position: relative; }
html[data-header="centered"] .gt-logo { flex-direction: column; text-align: center; gap: 8px; }
html[data-header="centered"] .gt-site-header__spacer { display: none; }
html[data-header="centered"] .gt-search { position: absolute; right: 24px; top: 50%; transform: translateY(-50%); }
html[data-header="centered"] .gt-nav { justify-content: center; }
@media (max-width: 900px) { html[data-header="centered"] .gt-search { display: none; } }

/* Minimal — borderless, pill hover */
html[data-header="minimal"] .gt-site-header__bar { padding-top: 12px; padding-bottom: 12px; }

/* ── Logo emblem ── */
.gt-logo__mark { overflow: hidden; }
.gt-logo__mark svg { width: 30px; height: 30px; }

/* Header: replace the live input with a button that opens the command palette */
.gt-search .gt-searchbtn { width: 260px; }
@media (max-width: 1100px) { .gt-search .gt-searchbtn { width: 200px; } }

/* ==========================================================================
   components/03-organisms/spotlight/spotlight.css
   ========================================================================== */

/* ─────────────────────────── SPOTLIGHT ─────────────────────────── */
/* A full-bleed photograph with copy laid over a scrim. The scrim is tokenised
   (--gt-scrim*, --gt-on-scrim*) rather than the hard-coded rgba(5,7,13,…) and
   #fff this component used to carry — those came from the Drupal theme, where
   this treatment lived as `callout --spotlight`. (Divergence ledger D9.) */
.gt-spotlight { position: relative; overflow: hidden; border-radius: var(--gt-radius-card); isolation: isolate; display: flex; align-items: flex-end; min-height: 500px; box-shadow: var(--gt-shadow-md); }
/* The default ring's dark line is lost on the scrim, so it inverts with the copy. */
.gt-spotlight { --gt-focus-ring: 0 0 0 3px var(--gt-scrim-solid), 0 0 0 5.5px var(--gt-on-scrim); }
.gt-spotlight img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; transition: transform .6s ease; }
.gt-spotlight:hover img { transform: scale(1.03); }
@media (prefers-reduced-motion: reduce) { .gt-spotlight:hover img { transform: none; } }
.gt-spotlight::after { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(180deg, var(--gt-scrim-top) 0%, var(--gt-scrim) 100%); }

.gt-spotlight__inner { padding: clamp(28px,4vw,54px); max-width: 660px; color: var(--gt-on-scrim); }
.gt-spotlight__eyebrow { display: inline-flex; align-items: center; gap: 9px; font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: var(--gt-on-scrim); background: color-mix(in srgb, var(--gt-accent) 90%, var(--gt-scrim-solid)); padding: 7px 14px; min-height: 24px; border-radius: var(--gt-radius-pill); }
.gt-spotlight h2 { color: var(--gt-on-scrim); font-size: var(--gt-text-4xl); max-width: 18ch; margin-top: 20px; }
/* The heading may arrive linked (Drupal's featured event passes captured
   markup); it reads as a heading on the scrim, not a blue link — the card
   convention. Hover/focus restores the underline for affordance. */
.gt-spotlight h2 a { color: inherit; text-decoration: none; }
.gt-spotlight h2 a:hover, .gt-spotlight h2 a:focus-visible { text-decoration: underline; }
.gt-spotlight__body { color: var(--gt-on-scrim-soft); font-size: var(--gt-text-lg); max-width: 52ch; margin-top: 16px; line-height: var(--gt-leading-normal); }
.gt-spotlight__body :where(p, ul, ol) { color: inherit; line-height: inherit; }
.gt-spotlight__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 22px; margin-top: 26px; }
.gt-spotlight__meta { font-size: var(--gt-text-xs); color: var(--gt-on-scrim-soft); }
/* The buttons sit on the scrim, so they take the scrim tokens: --gt-surface and
   --gt-ink, which .gt-btn--white/--outline-white read, are remapped by
   .gt-band--dark and by a dark look, and would turn both buttons dark on a dark
   scrim. Specificity, not source order, carries these over button.css. */
.gt-spotlight .gt-btn--white { background: var(--gt-on-scrim); color: var(--gt-scrim-solid); border-color: var(--gt-on-scrim); }
.gt-spotlight .gt-btn--white:hover { background: color-mix(in srgb, var(--gt-on-scrim) 88%, var(--gt-scrim-solid)); }
/* The second button is filled, not transparent, so the photo does not show
   through it; on a fill as dark as the scrim, the solid outline is what marks
   its edge. The fill is --gt-bg-dark, not --gt-ink: it is dark in every look
   (ink is light in a dark one) and .gt-band--dark does not remap it. At 8%
   white it sits on Modern's ink. */
.gt-spotlight .gt-btn--outline-white { background: color-mix(in oklab, var(--gt-bg-dark), white 8%); color: var(--gt-on-scrim); border-color: var(--gt-on-scrim); }
.gt-spotlight .gt-btn--outline-white:hover { background: color-mix(in oklab, var(--gt-bg-dark), white 20%); border-color: var(--gt-on-scrim); }
@media (max-width: 560px) { .gt-spotlight { min-height: 460px; } .gt-spotlight h2 { font-size: var(--gt-text-3xl); } }

/* ==========================================================================
   components/03-organisms/step-grid/step-grid.css
   ========================================================================== */

/* Layout is the shared .gt-grid--N (base/base.css). The list owns only the
   counter and the arrows, which point along a single row: they go on the last
   step, in a one-column list, and wherever the grid breaks onto more rows. */
.gt-steps { counter-reset: step; }
.gt-steps .gt-step:last-child .gt-step__arrow,
.gt-steps.gt-grid--1 .gt-step__arrow { display: none; }
@media (max-width: 1040px) { .gt-steps.gt-grid--3 .gt-step__arrow, .gt-steps.gt-grid--4 .gt-step__arrow { display: none; } }
@media (max-width: 720px) { .gt-steps .gt-step__arrow { display: none; } }

/* ==========================================================================
   components/03-organisms/table/table.css
   ========================================================================== */

/* components/03-organisms/table/table.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (218-224)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── TABLE ─────────────────────────── */
.gt-table { width: 100%; border-collapse: collapse; font-size: var(--gt-text-sm); }
.gt-table caption { text-align: left; font-size: var(--gt-text-xs); color: var(--gt-ink-3); margin-bottom: var(--gt-space-2); }
.gt-table th { text-align: left; padding: var(--gt-space-3) var(--gt-space-4); font-weight: var(--gt-w-bold); color: var(--gt-ink); background: var(--gt-surface-2); border-bottom: 2px solid var(--gt-border-2); }
.gt-table td { padding: var(--gt-space-3) var(--gt-space-4); border-bottom: var(--gt-border-width) solid var(--gt-border); color: var(--gt-ink-2); }
.gt-table tr:last-child td { border-bottom: none; }
.gt-table tbody tr:hover { background: var(--gt-surface-2); }

/* ==========================================================================
   components/03-organisms/tabs/tabs.css
   ========================================================================== */

/* components/03-organisms/tabs/tabs.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: css/components.css (189-198)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

/* ─────────────────────────── TABS ─────────────────────────── */
.gt-tabs__list { display: flex; gap: 4px; border-bottom: 2px solid var(--gt-border); margin-bottom: var(--gt-space-5); flex-wrap: wrap; }
.gt-tab {
  all: unset; box-sizing: border-box; cursor: pointer; padding: 12px 16px; min-height: 24px;
  font-weight: var(--gt-w-semibold); font-size: var(--gt-text-sm); color: var(--gt-ink-2);
  border-bottom: 3px solid transparent; margin-bottom: -2px; transition: color .15s, border-color .15s;
}
.gt-tab:hover { color: var(--gt-ink); }
.gt-tab--active { color: var(--gt-primary); border-bottom-color: var(--gt-primary); }
.gt-tab:focus-visible { box-shadow: var(--gt-focus-ring); border-radius: var(--gt-radius-sm); }

/* ==========================================================================
   base/chrome.css
   ========================================================================== */

/* base/chrome.css
   Extracted from the GovTheme marketing site by scripts/extract-css.mjs.
   Source: demo/css/site.css (11-17, 75-81, 401-403, 414-416, 477-490, 542-550)
   Rules are verbatim and in their original order - the split must not change
   what any page renders (govtheme-marketing/scripts/check-cascade.mjs). */

.gt-site { min-height: 100vh; display: flex; flex-direction: column; }
/* Nothing to override for the framed preview any more. The site runs in an
   iframe now, so 100vh is the frame's height, and <body> is inside the scope
   again — tokens.css paints ground, ink and font on it exactly as it does when
   a demo page is opened on its own. */
.gt-wrap { max-width: var(--gt-maxw); margin: 0 auto; padding: 0 24px; width: 100%; }

/* Generic site section */
.gt-band { padding: clamp(48px,6vw,88px) 0; }
.gt-band--subtle { background: var(--gt-bg-subtle); }
/* Dark band: the tokens are remapped within scope, so whatever sits inside
   renders light-on-dark at AA. The neutrals are --gt-bg-dark mixed towards
   white, the steps the engine's dark palettes take; the brand is
   --gt-primary-inverse, which the engine fits against these percentages
   (DARK_BAND). Root tokens built from var() - the *-soft tints, the focus
   ring - resolve at :root, so the ones a band's content reads are redeclared.
   Accent and status tints keep their light look: each carries its own ground.
   The mixes are in oklab: white and black have no hue, and Chrome mixing them
   in oklch drops the other colour's hue to none and paints it red. */
.gt-band--dark {
  --gt-bg: var(--gt-bg-dark);
  --gt-surface: color-mix(in oklab, var(--gt-bg-dark), white 6%);
  --gt-surface-2: color-mix(in oklab, var(--gt-bg-dark), white 10%);
  --gt-surface-sunken: color-mix(in oklab, var(--gt-bg-dark), white 3%);
  --gt-border: color-mix(in oklab, var(--gt-bg-dark), white 16%);
  --gt-border-2: color-mix(in oklab, var(--gt-bg-dark), white 46%);
  --gt-ink-3: color-mix(in oklab, var(--gt-bg-dark), white 58%);
  --gt-ink-2: color-mix(in oklab, var(--gt-bg-dark), white 72%);
  --gt-ink: color-mix(in oklab, var(--gt-bg-dark), white 94%);
  --gt-primary: var(--gt-primary-inverse);
  --gt-primary-hover: color-mix(in oklab, var(--gt-primary), black 8%);
  --gt-primary-active: color-mix(in oklab, var(--gt-primary), black 16%);
  --gt-primary-soft: color-mix(in srgb, var(--gt-primary) 22%, var(--gt-surface));
  --gt-primary-text: var(--gt-primary);
  --gt-on-primary: var(--gt-bg-dark);
  --gt-link: var(--gt-primary);
  --gt-focus: var(--gt-primary);
  --gt-focus-ring: 0 0 0 3px var(--gt-surface), 0 0 0 5.5px var(--gt-focus);
  background: var(--gt-bg);
  color: var(--gt-ink);
}
.gt-section__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 36px; flex-wrap: wrap; }
.gt-section__title { font-size: var(--gt-text-3xl); }
.gt-section__title--sm { font-size: var(--gt-text-2xl); }

/* Warm the placeholder media with a hint of brand colour */
.gt-card__media, .gt-hero__media, .gt-mega__feature-media { background-color: color-mix(in srgb, var(--gt-primary) 7%, var(--gt-surface-sunken)); }

/* Section lead under a band heading */

/* ── Featured-first news grid ──
   Page layout rather than component CSS, so it lives here. Each rule carries two
   selectors because the consumers mark the feature card differently: the
   marketing markup puts .gt-card--feature on the card, while the Drupal theme's
   views row wraps it in .gt-newsgrid__item--feature. Same declarations either
   way. */
.gt-newsgrid { display: grid; grid-template-columns: 1.5fr 1fr; gap: var(--gt-space-5); align-items: stretch; }
.gt-newsgrid__item { display: grid; min-width: 0; }
.gt-newsgrid__item--feature,
.gt-card--feature { grid-row: 1 / span 2; }
/* The feature spans both rows, so it stretches to the height of the two cards
   beside it. The image takes that extra height (cover-cropped, never shorter
   than 16:10) and the body keeps its own, so the copy sits under the image
   instead of above a blank gap. Stacked, the image is back to 16:10. */
.gt-newsgrid__item--feature .gt-card__media,
.gt-card--feature .gt-card__media { aspect-ratio: auto; flex: 1 1 auto; min-height: 280px; }
.gt-newsgrid__item--feature .gt-card__body,
.gt-card--feature .gt-card__body { flex: 0 0 auto; }
.gt-newsgrid__item--feature .gt-card__body,
.gt-card--feature .gt-card__body { padding: var(--gt-space-6); gap: var(--gt-space-3); }
.gt-newsgrid__item--feature .gt-card__title,
.gt-card--feature .gt-card__title { font-size: var(--gt-text-2xl); }
.gt-newsgrid__item--feature .gt-card__excerpt,
.gt-card--feature .gt-card__excerpt { font-size: var(--gt-text-md); }
@media (max-width: 760px) {
  .gt-newsgrid { grid-template-columns: 1fr; }
  .gt-newsgrid__item--feature,
  .gt-card--feature { grid-row: auto; }
  .gt-newsgrid__item--feature .gt-card__media,
  .gt-card--feature .gt-card__media { aspect-ratio: 16 / 10; min-height: 0; }
}

.gt-section__summary { font-size: var(--gt-text-lg); color: var(--gt-ink-2); max-width: 64ch; margin: -16px 0 30px; line-height: var(--gt-leading-normal); }

/* Real photography fills any placeholder media box */
.gt-card__media, .gt-hero__media, .gt-mega__feature-media, .gt-img { position: relative; overflow: hidden; }
.gt-img img, .gt-card__media img, .gt-hero__media img, .gt-mega__feature-media img,
.gt-figure img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.gt-card__media:has(img), .gt-hero__media:has(img), .gt-mega__feature-media:has(img) {
  background-image: none; background-color: var(--gt-surface-sunken);
}
/* When media carries an image, hide the monospace placeholder hints */
.gt-card__media:has(img) { color: transparent; }
.gt-card__media img { aspect-ratio: inherit; }

/* ── Vision band (deep, with starfield in any mode) ── */
.gt-vision { position: relative; overflow: hidden; background: var(--gt-scrim-solid); color: var(--gt-on-scrim); isolation: isolate; }
.gt-vision img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .42; z-index: -2; }
.gt-vision::after { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(110deg, var(--gt-scrim-hover), color-mix(in srgb, var(--gt-scrim) 55%, transparent)); }
.gt-vision .gt-wrap { padding-top: clamp(56px,7vw,96px); padding-bottom: clamp(56px,7vw,96px); }
.gt-vision__eyebrow { font-family: var(--gt-font-head); font-size: var(--gt-text-xs); font-weight: var(--gt-w-semibold); color: color-mix(in srgb, var(--gt-on-scrim) 64%, var(--gt-primary)); }
.gt-vision h2 { color: var(--gt-on-scrim); font-size: var(--gt-text-4xl); max-width: 20ch; margin-top: 14px; }
.gt-vision p { color: var(--gt-on-scrim-soft); font-size: var(--gt-text-lg); max-width: 56ch; margin-top: 18px; line-height: var(--gt-leading-normal); }

/* ── Icon button (site-header burger + drawer close) ──
   Upstreamed verbatim from the Drupal theme's chrome.css: site-header.twig and
   drawer.twig both emit .gt-iconbtn, but only the theme had rules for it. Chrome
   rather than either component's CSS because both share it. */
.gt-iconbtn {
  all: unset; box-sizing: border-box; cursor: pointer; width: 40px; height: 40px; border-radius: var(--gt-radius-input);
  display: grid; place-items: center; color: var(--gt-ink-2); border: var(--gt-border-width) solid var(--gt-border);
  background: var(--gt-surface); transition: background-color .15s, color .15s, border-color .15s;
}
.gt-iconbtn:hover { background: var(--gt-surface-2); color: var(--gt-primary); border-color: var(--gt-border-2); }
.gt-iconbtn:focus-visible { box-shadow: var(--gt-focus-ring); }
.gt-iconbtn svg { width: 19px; height: 19px; }

/* ── Skip link ── */
/* Off-screen until focused (WCAG 2.4.1). A single anchor never needed to be a
   component: consumers render <a class="gt-skip" href="#main">…</a> directly —
   the Drupal theme translates the label at the call site, which an SDC could
   not do anyway. */
.gt-skip { position: absolute; left: 12px; top: -60px; z-index: 200; background: var(--gt-primary); color: var(--gt-on-primary); padding: 10px 18px; border-radius: var(--gt-radius-input); font-weight: 600; transition: top .15s; }
.gt-skip:focus { top: 12px; }
