/* ==========================================================================
   GOOD STUART DESIGN SYSTEM  (gs-ds.css)
   The single source of colour, type, spacing and components for every
   marketing + opportunity page.

   THE ONE RULE THAT MATTERS
   -------------------------
   Background colour and text colour are set TOGETHER, in ONE place: a
   .surface-* class. Nothing else in this file — and nothing you write on top
   of it — may set `background` and `color` on the same element.

   That inverts the old guidance ("never set bg and text together"), which is
   what produced dark-text-on-dark-background: authors set a background and
   left the text colour to chance. Here a surface always ships its own
   foreground, so an unreadable pair is not expressible.

   HOW TO BUILD A PAGE
   -------------------
   <section class="gs-section surface-inverse">
     <div class="gs-container">
       <p class="gs-eyebrow">Eyebrow</p>
       <h2 class="gs-h2">Headline</h2>
       <p class="gs-lede">Supporting sentence.</p>
       <a class="gs-btn gs-btn--primary" href="#">Book a fit call</a>
     </div>
   </section>

   Pick a surface. Put content inside. Never write a hex value.
   Every contrast pair below is verified >= WCAG AA (4.5:1); the ratio is
   noted next to each surface.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS — the only place raw values exist
   -------------------------------------------------------------------------- */
:root {
  /* Brand palette. Change these three to rebrand; everything else follows. */
  --gs-brand:        #0b1f3a;  /* deep navy   — authority, public sector */
  --gs-brand-mid:    #1d4ed8;  /* action blue — links, focus */
  --gs-accent:       #f59e0b;  /* amber       — CTA only, never a background for body copy */

  /* Derived brand steps */
  --gs-brand-900:    #071527;
  --gs-brand-800:    #0b1f3a;
  --gs-brand-700:    #12305a;
  --gs-brand-600:    #1b4585;
  --gs-brand-100:    #dbe6f7;
  --gs-brand-050:    #f0f5fd;

  --gs-accent-600:   #d97706;
  --gs-accent-500:   #f59e0b;
  --gs-accent-400:   #fbbf24;

  /* Neutrals */
  --gs-ink:          #0f172a;
  --gs-ink-soft:     #475569;
  --gs-line:         #e2e8f0;
  --gs-paper:        #ffffff;
  --gs-paper-alt:    #f8fafc;

  /* Light text used on dark surfaces */
  --gs-ink-invert:      #ffffff;
  --gs-ink-invert-soft: #cbd5e1;
  --gs-ink-invert-dim:  #93c5fd;

  /* Semantic status */
  /* Soft pill grounds. Each pairs with the strong ink of the same name at
     >= 4.5:1, so a filled pill is readable without an outline. Matches the
     client portal (good-stuart-rfq globals.css .badge.strong / .moderate). */
  --gs-ok-ink:       #166534;   /* on --gs-ok-soft  = 6.49:1 */
  --gs-warn-ink:     #92400e;   /* on --gs-warn-soft = 6.37:1 */
  --gs-ok-soft:      #dcfce7;
  --gs-warn-soft:    #fef3c7;
  --gs-stop-soft:    #fee2e2;
  --gs-neutral-soft: #f1f5f9;
  --gs-ok:           #15803d;
  --gs-warn:         #b45309;
  --gs-stop:         #b91c1c;

  /* Type scale — 1.25 ratio, clamped for fluid sizing */
  --gs-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --gs-t-xs:   0.75rem;
  --gs-t-sm:   0.875rem;
  --gs-t-base: 1rem;
  --gs-t-lg:   1.125rem;
  --gs-t-xl:   clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --gs-t-2xl:  clamp(1.5rem,  1.3rem + 1.0vw, 2rem);
  --gs-t-3xl:  clamp(1.875rem,1.5rem + 1.8vw, 2.75rem);
  --gs-t-4xl:  clamp(2.25rem, 1.7rem + 2.8vw, 3.75rem);

  /* Spacing scale — use these, not arbitrary px */
  --gs-s-1: 4px;   --gs-s-2: 8px;   --gs-s-3: 12px;  --gs-s-4: 16px;
  --gs-s-5: 24px;  --gs-s-6: 32px;  --gs-s-7: 48px;  --gs-s-8: 64px;
  --gs-s-9: 96px;  --gs-s-10: 128px;

  /* Shape */
  --gs-r-sm: 6px; --gs-r-md: 10px; --gs-r-lg: 16px; --gs-r-pill: 999px;

  /* Elevation — only two, deliberately */
  --gs-shadow-1: 0 1px 2px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.10);
  --gs-shadow-2: 0 10px 30px rgba(15,23,42,.10), 0 2px 6px rgba(15,23,42,.06);

  /* Measure + container */
  --gs-container: 1180px;
  --gs-measure: 68ch;
}

/* --------------------------------------------------------------------------
   2. SURFACES — the ONLY place background + foreground are paired.
   Each surface declares its own foreground, muted foreground, border and
   button colours, so any component placed inside is automatically legible.
   -------------------------------------------------------------------------- */

.surface-base,
.surface-subtle,
.surface-inverse,
.surface-brand,
.surface-accent {
  background: var(--surface);
  color: var(--on-surface);
}

/* White. Body text 17.0:1 */
.surface-base {
  --surface: var(--gs-paper);
  --on-surface: var(--gs-ink);
  --on-surface-muted: var(--gs-ink-soft);      /* 7.2:1 */
  --border: var(--gs-line);
  --link: var(--gs-brand-mid);
  --card-bg: var(--gs-paper-alt);
  --btn-primary-bg: var(--gs-accent-500);       /* CTA is ALWAYS amber */
  --btn-primary-fg: #1f2937;                    /* 6.8:1 — never white on amber */
  --btn-secondary-fg: var(--gs-brand-800);
  --btn-secondary-border: var(--gs-brand-800);
  --focus-ring: var(--gs-brand-800);           /* 16.5:1 on white */
  --field-border: var(--gs-ink-soft);          /* 7.6:1 — 1.4.11 needs 3:1 */
}

/* Light grey. Body text 17.1:1 */
.surface-subtle {
  --surface: var(--gs-paper-alt);
  --on-surface: var(--gs-ink);
  --on-surface-muted: var(--gs-ink-soft);      /* 7.2:1 */
  --border: var(--gs-line);
  --link: var(--gs-brand-mid);
  --card-bg: var(--gs-paper);
  --btn-primary-bg: var(--gs-accent-500);       /* CTA is ALWAYS amber */
  --btn-primary-fg: #1f2937;
  --btn-secondary-fg: var(--gs-brand-800);
  --btn-secondary-border: var(--gs-brand-800);
  --focus-ring: var(--gs-brand-800);           /* 15.8:1 on #f8fafc */
  --field-border: var(--gs-ink-soft);
}

/* Deep navy. White text 16.5:1, muted 11.1:1 */
.surface-inverse {
  --surface: var(--gs-brand-800);
  --on-surface: var(--gs-ink-invert);
  --on-surface-muted: var(--gs-ink-invert-soft);
  --border: rgba(255,255,255,.16);
  --link: var(--gs-ink-invert-dim);            /* 9.2:1 */
  --card-bg: rgba(255,255,255,.06);
  --btn-primary-bg: var(--gs-accent-500);
  --btn-primary-fg: #1f2937;                    /* 6.8:1 — amber NEVER takes white */
  --btn-secondary-fg: #ffffff;
  --btn-secondary-border: rgba(255,255,255,.45);
  --focus-ring: var(--gs-accent-400);          /* 9.9:1 on navy */
  --field-border: var(--gs-ink-soft);
}

/* Mid navy, for a second dark section that isn't identical to the first. */
.surface-brand {
  --surface: var(--gs-brand-700);
  --on-surface: var(--gs-ink-invert);
  --on-surface-muted: var(--gs-ink-invert-soft);
  --border: rgba(255,255,255,.18);
  --link: var(--gs-ink-invert-dim);
  --card-bg: rgba(255,255,255,.07);
  --btn-primary-bg: var(--gs-accent-500);
  --btn-primary-fg: #1f2937;
  --btn-secondary-fg: #ffffff;
  --btn-secondary-border: rgba(255,255,255,.45);
  --focus-ring: var(--gs-accent-400);          /* 7.9:1 on mid navy */
  --field-border: var(--gs-ink-soft);
}

/* Amber. Dark ink only — 6.8:1. Use sparingly: CTA bands, never long copy. */
.surface-accent {
  --surface: var(--gs-accent-500);
  --on-surface: #1f2937;
  --on-surface-muted: #3f3016;                 /* 5.4:1 */
  --border: rgba(31,41,55,.22);
  --link: #1f2937;
  --card-bg: rgba(255,255,255,.55);
  --btn-primary-bg: var(--gs-brand-800);
  --btn-primary-fg: #ffffff;
  --btn-secondary-fg: #1f2937;
  --btn-secondary-border: rgba(31,41,55,.5);
  --focus-ring: var(--gs-brand-800);           /* 7.7:1 on amber */
  --field-border: var(--gs-brand-800);
}

/* --------------------------------------------------------------------------
   3. BASE
   -------------------------------------------------------------------------- */
/* Root canvas. Sets background only — text colour is a surface's job, so any
   content outside a surface falls back to the browser default (black on white)
   rather than inheriting an unreadable pair. */
/* The legacy app.css is dequeued on these pages (see rfp-assets.php), and it was
   the only source of a body reset. Without this every full-bleed surface band
   gets an 8px UA gutter and the document scrolls sideways on overflow. */
body.gs-scope { margin: 0; padding: 0; overflow-x: hidden; }

.gs-scope { font-family: var(--gs-font); -webkit-font-smoothing: antialiased; background: var(--gs-paper); box-sizing: border-box; }
.gs-scope *, .gs-scope *::before, .gs-scope *::after { box-sizing: border-box; }
.gs-scope img { max-width: 100%; height: auto; display: block; }

/* Link colour applies to PROSE ONLY. Never style bare `a` at scope level — it
   outranks every component class (0,1,1 beats 0,1,0) and will silently recolour
   buttons, the logo, cards and rail links. Components own their colour. */
.gs-prose a,
.gs-body a,
.gs-lede a,
.gs-note a,
.gs-list a,
.gs-fineprint a,
.gs-step__body a,
.gs-card__meta a,
.gs-sources a { color: var(--link, var(--gs-brand-mid)); }

/* Visible focus everywhere — never remove it. */
/* outline-offset draws the ring on the surface BEHIND the control, so the ring
   colour must contrast with --surface, not with the control. Surface-owned. */
.gs-scope :focus-visible {
  outline: 3px solid var(--focus-ring, var(--gs-brand-800));
  outline-offset: 2px;
  /* No border-radius here — browsers already follow the element's own radius,
     and setting it made every button visibly snap 10px -> 6px on focus. */
}

/* --------------------------------------------------------------------------
   4. LAYOUT
   -------------------------------------------------------------------------- */
.gs-section { padding: var(--gs-s-9) 0; }
.gs-section--tight { padding: var(--gs-s-7) 0; }
.gs-section--loose { padding: var(--gs-s-10) 0; }

.gs-container {
  max-width: var(--gs-container);
  margin: 0 auto;
  padding: 0 var(--gs-s-5);
}
.gs-container--narrow { max-width: 780px; }

/* Vertical rhythm: the default way to stack anything. */
.gs-stack > * + * { margin-top: var(--gs-s-4); }
.gs-stack--lg > * + * { margin-top: var(--gs-s-6); }

/* Horizontal group that wraps (button rows, badge rows). */
.gs-cluster {
  display: flex; flex-wrap: wrap;
  gap: var(--gs-s-3); align-items: center;
}

/* Responsive grid without media queries. */
.gs-grid { display: grid; gap: var(--gs-s-5); grid-template-columns: 1fr; }
@media (min-width: 720px)  { .gs-grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 720px)  { .gs-grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .gs-grid--3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 720px)  { .gs-grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .gs-grid--4 { grid-template-columns: repeat(4, 1fr); } }

/* Split hero / feature row */
.gs-split { display: grid; gap: var(--gs-s-7); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) { .gs-split { grid-template-columns: 1.1fr 0.9fr; } }
/* Grid children default to min-width:auto, so a wide child (a table, a long
   badge row) stretches the column past the viewport instead of shrinking. Seen
   on /rfps/ at 390px: the commission table pushed the layout to 436px. */
.gs-split > *, .gs-grid > * { min-width: 0; }

/* --------------------------------------------------------------------------
   5. TYPE — colour always comes from the surface
   -------------------------------------------------------------------------- */
.gs-h1, .gs-h2, .gs-h3, .gs-h4 {
  margin: 0; font-weight: 800; letter-spacing: -.02em; line-height: 1.1;
  color: var(--on-surface);
}
.gs-h1 { font-size: var(--gs-t-4xl); }
.gs-h2 { font-size: var(--gs-t-3xl); }
.gs-h3 { font-size: var(--gs-t-2xl); letter-spacing: -.01em; }
.gs-h4 { font-size: var(--gs-t-xl); letter-spacing: -.01em; }

.gs-eyebrow {
  margin: 0 0 var(--gs-s-3);
  font-size: var(--gs-t-xs); font-weight: 700;
  letter-spacing: .10em; text-transform: uppercase;
  color: var(--on-surface-muted);
}

.gs-lede {
  margin: var(--gs-s-4) 0 0;
  font-size: var(--gs-t-lg); line-height: 1.6;
  max-width: var(--gs-measure);
  color: var(--on-surface-muted);
}

.gs-body { margin: 0; font-size: var(--gs-t-base); line-height: 1.7; max-width: var(--gs-measure); color: var(--on-surface); }
.gs-muted { color: var(--on-surface-muted); }
.gs-small { font-size: var(--gs-t-sm); line-height: 1.6; }

/* Fine print / disclaimers. Still meets AA on every surface. */
.gs-fineprint { font-size: var(--gs-t-xs); line-height: 1.6; color: var(--on-surface-muted); max-width: var(--gs-measure); }

/* --------------------------------------------------------------------------
   6. BUTTONS — colours come from the surface, so they can't clash
   -------------------------------------------------------------------------- */
.gs-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--gs-s-2);
  padding: 14px 24px;
  font-family: inherit; font-size: var(--gs-t-base); font-weight: 700; line-height: 1;
  text-decoration: none; text-align: center;
  border: 2px solid transparent; border-radius: var(--gs-r-md);
  cursor: pointer;
  transition: transform .12s ease, filter .12s ease, background-color .12s ease;
}
.gs-btn:hover { transform: translateY(-1px); filter: brightness(1.06); }
.gs-btn:active { transform: translateY(0); }

/* Specificity: `.gs-scope a` is (0,1,1) and beats a bare `.gs-btn--*` (0,1,0).
   Every CTA on this site is an <a>, so without the `.gs-scope a` prefix here the
   buttons inherit the surface's --link colour — which rendered light blue text
   on the amber CTA (~1.9:1) and near-invisible text on the header button.
   Keep the prefix on every button colour rule. */
.gs-scope a.gs-btn, .gs-btn { text-decoration: none; }

.gs-scope a.gs-btn--primary, .gs-btn--primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
}
.gs-scope a.gs-btn--secondary, .gs-btn--secondary {
  background: transparent;
  color: var(--btn-secondary-fg);
  border-color: var(--btn-secondary-border);
}
.gs-scope a.gs-btn--ghost, .gs-btn--ghost {
  background: transparent;
  color: var(--on-surface);
  border-color: transparent;
  padding-left: var(--gs-s-3); padding-right: var(--gs-s-3);
  text-decoration: underline; text-underline-offset: 3px;
}
.gs-btn--lg { padding: 18px 32px; font-size: var(--gs-t-lg); }
.gs-btn--block { display: flex; width: 100%; }

/* --------------------------------------------------------------------------
   7. COMPONENTS
   -------------------------------------------------------------------------- */

/* Card — inherits a surface-appropriate background, never sets its own text colour. */
.gs-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--gs-r-lg);
  padding: var(--gs-s-6);
}
.gs-card--flat { box-shadow: none; }
.gs-card--raised { box-shadow: var(--gs-shadow-2); }

/* Pricing cards. Two of them sit side by side on /pricing/ and they have very
   different amounts of content, so without this the second card's button floats
   in the middle of its own dead space. Flex column plus an auto top margin on
   the CTA pins both buttons to the same baseline.

   Both cards also carry a badge, for the same reason: one badge and one bare
   heading knocks every row below it out of alignment by the badge's height. */
.gs-pricecard { display: flex; flex-direction: column; }
/* A flex column stretches its children across the cross axis, which turns the
   badge pill into a full-width bar. Pull it back to its own content width. */
.gs-pricecard > .gs-badge { align-self: flex-start; }
/* The fine print and the button travel together and sit on the card's floor.
   Pinning only the button left the shorter card's fine print floating halfway
   up with a hole under it, because the slack collapsed in the wrong place.
   The whole foot moves instead, so two cards with different numbers of bullets
   still agree on their last two rows. */
.gs-pricecard__foot { margin-top: auto; padding-top: var(--gs-s-5); }
/* Exactly two line boxes, so a one-line note and a two-line note start at the
   same height rather than three pixels apart. The em value is the fallback for
   anything that does not know the lh unit; both land on the same place. */
.gs-pricecard__foot .gs-fineprint {
  margin: 0 0 var(--gs-s-4);
  min-height: 3em;
  min-height: 2lh;
}
/* The price and its unit are one statement, so close the paragraph gap between
   them. The big number's line box already supplies the breathing room. */
.gs-pricecard .gs-stat__value { margin: 0; }
.gs-card__title { margin: 0 0 var(--gs-s-2); font-size: var(--gs-t-xl); font-weight: 700; color: var(--on-surface); }
.gs-card__meta  { margin: 0; font-size: var(--gs-t-sm); color: var(--on-surface-muted); }

/* Badge / pill */
.gs-badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: var(--gs-r-pill);
  font-size: var(--gs-t-xs); font-weight: 700; letter-spacing: .03em;
  border: 1px solid var(--border);
  color: var(--on-surface-muted);
}
.gs-badge--ok   { color: var(--gs-ok);   border-color: currentColor; }
.gs-badge--warn { color: var(--gs-warn); border-color: currentColor; }
.gs-badge--stop { color: var(--gs-stop); border-color: currentColor; }
/* A label, not a status. Carries no colour meaning, so it can sit on a card
   that is not being recommended over the other one. */
.gs-badge--note { color: var(--on-surface); border-color: var(--border); }
/* On dark surfaces the semantic colours would fail contrast — force them light. */
.surface-inverse .gs-badge--ok,
.surface-brand   .gs-badge--ok   { color: #86efac; }
.surface-inverse .gs-badge--warn,
.surface-brand   .gs-badge--warn { color: #fcd34d; }
.surface-inverse .gs-badge--stop,
.surface-brand   .gs-badge--stop { color: #fca5a5; }
/* Amber is light enough that the default semantics fail on it too. */
.surface-accent .gs-badge--ok   { color: #052e16; }
.surface-accent .gs-badge--warn { color: #5c2708; }   /* 4.27:1 was below AA at 12px */
.surface-accent .gs-badge--stop { color: #7f1d1d; }

/* A badge used as a link (industry chips on the homepage). Component owns its colour. */
.gs-scope a.gs-badge { text-decoration: none; }
.gs-scope a.gs-badge:hover, .gs-scope a.gs-badge:focus-visible { color: var(--on-surface); border-color: var(--btn-primary-bg); }

/* Stat block */
.gs-stat__value { display: block; font-size: var(--gs-t-3xl); font-weight: 800; letter-spacing: -.02em; color: var(--on-surface); }
.gs-stat__label { display: block; margin-top: var(--gs-s-2); font-size: var(--gs-t-sm); color: var(--on-surface-muted); }

/* Numbered process step */
.gs-step { display: grid; grid-template-columns: auto 1fr; gap: var(--gs-s-4); align-items: start; }
.gs-step__n {
  display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: var(--gs-r-pill);
  background: var(--btn-primary-bg); color: var(--btn-primary-fg);
  font-weight: 800; font-size: var(--gs-t-base);
}
.gs-step__title { margin: 0 0 var(--gs-s-2); font-size: var(--gs-t-lg); font-weight: 700; color: var(--on-surface); }
.gs-stepnum {
  display: inline-block; margin-right: 8px;
  font-family: var(--gs-font); font-size: var(--gs-t-xs); font-weight: 800;
  letter-spacing: .08em; color: var(--on-surface-muted); vertical-align: 2px;
}
.gs-step__body  { margin: 0; color: var(--on-surface-muted); line-height: 1.65; }

/* Compact process rail — template-parts/process.php with $gs_process_compact.
   Step titles, numbers and icons only. The bodies live on /how-it-works/. */
.gs-steprail { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
.gs-steprail__item {
  display: flex; align-items: center; gap: var(--gs-s-4);
  padding: var(--gs-s-4) 0; border-bottom: 1px solid var(--border);
}
.gs-steprail__item .gs-stepnum { margin-right: 0; vertical-align: baseline; }
.gs-steprail__title { flex: 1 1 auto; font-size: var(--gs-t-lg); font-weight: 700; color: var(--on-surface); }
@media (max-width: 640px) {
  .gs-steprail__item { flex-wrap: wrap; gap: var(--gs-s-3); }
  .gs-steprail__title { flex: 1 1 100%; order: 3; }
}

/* Step ownership tag. Icon plus one word — the word carries the meaning, the
   icon is decorative. Never render one without the other. */
.gs-owner {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--gs-t-sm); font-weight: 600; letter-spacing: .01em;
  color: var(--on-surface-muted);
}
.gs-owner--you { color: var(--on-surface); }
.gs-icon--sm { width: 16px; height: 16px; }
.gs-iconbox--sm { width: 36px; height: 36px; border-radius: var(--gs-r-sm); }
.gs-iconbox--sm .gs-icon { width: 20px; height: 20px; }

/* Checklist */
.gs-list { margin: 0; padding: 0; list-style: none; }
.gs-list li { position: relative; padding-left: 30px; margin-bottom: var(--gs-s-3); line-height: 1.6; color: var(--on-surface); }
.gs-list li::before {
  content: ""; position: absolute; left: 4px; top: .55em;
  width: 12px; height: 6px; border-left: 2.5px solid currentColor; border-bottom: 2.5px solid currentColor;
  transform: rotate(-45deg); opacity: .85;
}

/* Pull quote / emphasis panel */
.gs-note {
  border-left: 4px solid var(--btn-primary-bg);
  padding: var(--gs-s-4) var(--gs-s-5);
  background: var(--card-bg);
  border-radius: 0 var(--gs-r-md) var(--gs-r-md) 0;
  color: var(--on-surface);
}

.gs-divider { height: 1px; background: var(--border); border: 0; margin: var(--gs-s-7) 0; }

/* Table */
.gs-tablewrap { overflow-x: auto; }
.gs-table { width: 100%; border-collapse: collapse; font-size: var(--gs-t-sm); }
.gs-table th, .gs-table td { padding: var(--gs-s-3) var(--gs-s-4); text-align: left; border-bottom: 1px solid var(--border); }
.gs-table th { font-weight: 700; color: var(--on-surface); }
.gs-table td { color: var(--on-surface-muted); }

/* Form controls */
.gs-field { display: block; margin-bottom: var(--gs-s-4); }
.gs-label { display: block; margin-bottom: var(--gs-s-2); font-size: var(--gs-t-sm); font-weight: 600; color: var(--on-surface); }
/* Deliberately pins a light field on every surface — a form control reads as a
   control. This is the ONE component that sets an absolute bg+fg pair; the
   border comes from the surface so it always clears 1.4.11's 3:1. */
.gs-input, .gs-select, .gs-textarea {
  width: 100%; padding: 13px 14px;
  font-family: inherit; font-size: var(--gs-t-base);
  background: var(--gs-paper); color: var(--gs-ink);
  border: 1px solid var(--field-border, var(--gs-ink-soft)); border-radius: var(--gs-r-md);
}
.gs-input[aria-invalid="true"], .gs-select[aria-invalid="true"], .gs-textarea[aria-invalid="true"] {
  border-color: var(--gs-stop); border-width: 2px;
}
.gs-textarea { min-height: 120px; resize: vertical; }
.gs-input::placeholder, .gs-textarea::placeholder { color: var(--gs-ink-soft); opacity: 1; }

/* --------------------------------------------------------------------------
   8. UTILITIES — deliberately few
   -------------------------------------------------------------------------- */
.gs-center { text-align: center; }
.gs-center .gs-lede, .gs-center .gs-body { margin-left: auto; margin-right: auto; }
.gs-mt-0 { margin-top: 0 !important; }
.gs-mt-3 { margin-top: var(--gs-s-3); }
.gs-mt-4 { margin-top: var(--gs-s-4); }
.gs-mt-5 { margin-top: var(--gs-s-5); }
.gs-mt-6 { margin-top: var(--gs-s-6); }
.gs-mt-8 { margin-top: var(--gs-s-8); }
.gs-mt-7 { margin-top: var(--gs-s-7); }
.gs-maxw { max-width: var(--gs-measure); }

@media (prefers-reduced-motion: reduce) {
  .gs-scope *, .gs-scope *::before, .gs-scope *::after {
    animation-duration: .01ms !important;
    /* Without this an INFINITE animation does not stop, it restarts every
       hundredth of a millisecond, which is worse than leaving it alone. */
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   8b. EDITORIAL — /guides/ pages
   Colour still comes only from the surface. Nothing here pairs bg + fg.
   -------------------------------------------------------------------------- */

/* Two-column article + rail */
.gs-guide { display: grid; gap: var(--gs-s-7); grid-template-columns: 1fr; align-items: start; }
@media (min-width: 1000px) { .gs-guide { grid-template-columns: minmax(0,1fr) 320px; gap: var(--gs-s-8); } }
.gs-guide__body { min-width: 0; }
@media (min-width: 1000px) {
  .gs-guide__rail {
    position: sticky; top: 92px; display: grid; gap: var(--gs-s-5);
    /* Taller than the viewport and the lower boxes never come into view. */
    max-height: calc(100vh - 92px - var(--gs-s-5)); overflow-y: auto;
  }
}
.gs-guide__rail { display: grid; gap: var(--gs-s-5); }
.gs-railbox { padding: var(--gs-s-5); }

.gs-guide__section { margin-top: var(--gs-s-8); scroll-margin-top: 88px; }
.gs-guide__section > .gs-h3 { margin-bottom: var(--gs-s-4); }

/* Long-form prose. Inherits colour; only spacing and measure are set here. */
.gs-prose { color: var(--on-surface); }
.gs-prose p, .gs-prose li { line-height: 1.75; max-width: var(--gs-measure); }
.gs-prose p { margin: 0; }
/* Sibling rhythm at (0,2,0). The original `.gs-prose > * + *` was (0,1,0) and
   lost to `.gs-prose p { margin: 0 }` (0,1,1) — which silently collapsed every
   paragraph gap in all 93 guides. Headings get their own larger gap below. */
.gs-prose > :not(:first-child) { margin-top: var(--gs-s-4); }
.gs-prose > h3:not(:first-child) { margin-top: var(--gs-s-6); }
.gs-prose > h4:not(:first-child) { margin-top: var(--gs-s-5); }
/* A guide's intro sits directly under its H1 and needs the same breathing room a lede gets. */
.gs-h1 + .gs-prose { margin-top: var(--gs-s-5); }
.gs-prose h3 { font-size: var(--gs-t-lg); font-weight: 700; margin: var(--gs-s-6) 0 0; color: var(--on-surface); }
.gs-prose h4 { font-size: var(--gs-t-base); font-weight: 700; margin: var(--gs-s-5) 0 0; color: var(--on-surface); }
.gs-prose ul, .gs-prose ol { margin: 0; padding-left: var(--gs-s-5); }
.gs-prose li + li { margin-top: var(--gs-s-2); }
.gs-prose a { text-decoration: underline; text-underline-offset: 2px; }
.gs-prose strong { font-weight: 700; }
.gs-prose blockquote {
  margin: 0; padding: var(--gs-s-4) var(--gs-s-5);
  border-left: 3px solid var(--btn-primary-bg); background: var(--card-bg);
  border-radius: 0 var(--gs-r-md) var(--gs-r-md) 0;
}
.gs-prose code { font-size: .88em; }
.gs-prose table { width: 100%; border-collapse: collapse; font-size: var(--gs-t-sm); display: block; overflow-x: auto; }
.gs-prose th, .gs-prose td { padding: var(--gs-s-3) var(--gs-s-4); text-align: left; border-bottom: 1px solid var(--border); }
.gs-prose th { font-weight: 700; }
.gs-prose--lead p { font-size: var(--gs-t-lg); line-height: 1.65; }

/* Breadcrumbs */
.gs-crumbs ol { display: flex; flex-wrap: wrap; gap: var(--gs-s-2); list-style: none; margin: 0; padding: 0; font-size: var(--gs-t-sm); }
.gs-crumbs li + li::before { content: "/"; margin-right: var(--gs-s-2); color: var(--on-surface-muted); }
.gs-scope .gs-crumbs a { color: var(--on-surface-muted); text-decoration: none; }
.gs-crumbs a:hover { color: var(--on-surface); text-decoration: underline; }
.gs-crumbs span { color: var(--on-surface); font-weight: 600; }

/* On-this-page */
.gs-toc { border: 1px solid var(--border); border-radius: var(--gs-r-lg); padding: var(--gs-s-5); background: var(--card-bg); }
.gs-toc ol { margin: var(--gs-s-2) 0 0; padding-left: var(--gs-s-5); }
.gs-toc li { margin-bottom: var(--gs-s-2); line-height: 1.5; }
.gs-scope .gs-toc a { color: var(--on-surface); text-decoration: none; }
.gs-toc a:hover { text-decoration: underline; }

/* FAQs */
.gs-faqs { display: grid; gap: var(--gs-s-3); margin-top: var(--gs-s-4); }
.gs-faq { border: 1px solid var(--border); border-radius: var(--gs-r-md); background: var(--card-bg); }
.gs-faq > summary {
  cursor: pointer; list-style: none; padding: var(--gs-s-4) var(--gs-s-5);
  font-weight: 700; color: var(--on-surface); display: flex; justify-content: space-between; gap: var(--gs-s-4);
}
.gs-faq > summary::-webkit-details-marker { display: none; }
.gs-faq > summary::after { content: "+"; font-weight: 400; font-size: 1.3em; line-height: 1; color: var(--on-surface-muted); }
.gs-faq[open] > summary::after { content: "\2212"; }
.gs-faq > .gs-prose { padding: 0 var(--gs-s-5) var(--gs-s-5); }

/* Sources */
.gs-sources { list-style: none; margin: var(--gs-s-3) 0 0; padding: 0; }
.gs-sources li { padding: var(--gs-s-2) 0; border-bottom: 1px solid var(--border); font-size: var(--gs-t-sm); }
.gs-sources li:last-child { border-bottom: 0; }
.gs-sources a { color: var(--link); }

/* Rail lists */
.gs-opplist, .gs-linklist { list-style: none; margin: 0; padding: 0; }
.gs-opplist li { padding: var(--gs-s-3) 0; border-bottom: 1px solid var(--border); }
.gs-opplist li:last-child { border-bottom: 0; }
.gs-scope .gs-opplist a { display: block; font-weight: 600; font-size: var(--gs-t-sm); line-height: 1.4; color: var(--on-surface); text-decoration: none; }
.gs-opplist a:hover { text-decoration: underline; }
.gs-opplist__meta { display: block; margin-top: 3px; font-size: var(--gs-t-xs); color: var(--on-surface-muted); }
.gs-linklist li { padding: var(--gs-s-2) 0; }
.gs-scope .gs-linklist a { font-size: var(--gs-t-sm); color: var(--on-surface); text-decoration: none; line-height: 1.45; display: block; }
.gs-linklist a:hover { text-decoration: underline; }

/* Gated / locked state on opportunity pages */
.gs-locked { position: relative; overflow: hidden; }
.gs-locked__blur {
  filter: blur(5px); opacity: .55; pointer-events: none; user-select: none;
  margin-bottom: var(--gs-s-5);
}
.gs-locked__blur p { margin: 0 0 var(--gs-s-2); font-size: var(--gs-t-sm); color: var(--on-surface); }
.gs-gated > * + * { margin-top: var(--gs-s-4); }

.gs-note--ok { border-left-color: var(--gs-ok); }
.surface-inverse .gs-note--ok, .surface-brand .gs-note--ok { border-left-color: #86efac; }

.gs-fielderr { color: var(--gs-stop); }
.surface-inverse .gs-fielderr, .surface-brand .gs-fielderr { color: #fca5a5; }

/* Skip link — visible only on focus. */
.gs-skip {
  position: absolute; left: -9999px; top: 0; z-index: 300;
  padding: 12px 18px; background: var(--gs-brand-800); color: #fff;
  font-weight: 700; text-decoration: none; border-radius: 0 0 var(--gs-r-md) 0;
}
.gs-skip:focus { left: 0; }

/* An ordered list that keeps its numbers (.gs-list replaces them with ticks). */
.gs-list--numbered { list-style: decimal; padding-left: var(--gs-s-5); }
.gs-list--numbered li { padding-left: 0; }
.gs-list--numbered li::before { content: none; }

/* In-page anchors must clear the sticky header. */
.gs-section[id] { scroll-margin-top: 88px; }

/* Same reason, for the skip link's own target. Without this the jump puts
   <main> at y=0, the sticky header repaints over its top 69px, and the first
   focusable element inside it — the breadcrumb — is completely hidden along
   with its focus ring. WCAG 2.2 SC 2.4.11. */
#gs-main { scroll-margin-top: 88px; }

/* Product screenshots. Framed so a light UI shot sits cleanly on a dark band. */
.gs-shot { margin: 0; }
.gs-shot img {
  display: block; width: 100%; height: auto;
  border-radius: var(--gs-r-lg);
  border: 1px solid var(--border);
  box-shadow: var(--gs-shadow-2);
}
.gs-shot figcaption { margin-top: var(--gs-s-3); }

/* Icons — one sprite, one style, always currentColor. */
.gs-icon { width: 24px; height: 24px; display: block; flex-shrink: 0; color: currentColor; }
.gs-icon--lg { width: 32px; height: 32px; }
.gs-iconbox {
  width: 48px; height: 48px; border-radius: var(--gs-r-md);
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--card-bg); border: 1px solid var(--border);
  color: var(--btn-primary-bg);
}
.surface-inverse .gs-iconbox, .surface-brand .gs-iconbox { color: var(--gs-accent-400); }
.gs-card__icon { color: var(--btn-primary-bg); margin-bottom: var(--gs-s-4); }
.surface-inverse .gs-card__icon, .surface-brand .gs-card__icon { color: var(--gs-accent-400); }

/* Whole-card links */
.gs-scope a.gs-cardlink, .gs-cardlink { display: block; text-decoration: none; color: var(--on-surface); transition: transform .12s ease, border-color .12s ease; }
.gs-cardlink:hover { transform: translateY(-2px); border-color: var(--btn-primary-bg); }
.gs-cardlink .gs-card__title { display: block; }
.gs-cardlink .gs-card__meta { display: block; margin-top: var(--gs-s-2); }
/* Specificity note: `.gs-scope a` is (0,1,1) and beat a bare `.gs-plainlink`
   (0,1,0), which rendered pillar headings on /guides/ as blue underlined links.
   Scoped here so it wins. */
.gs-scope a.gs-plainlink {
  color: inherit;
  text-decoration: underline; text-decoration-color: var(--on-surface-muted);
  text-underline-offset: 6px; text-decoration-thickness: 2px;
}
.gs-scope a.gs-plainlink:hover { text-decoration-color: currentColor; }

/* --------------------------------------------------------------------------
   8c. CONVERSION PARTIALS — template-parts/{what-you-get,faq,examples,
       open-categories,origin}.php  (sales-audit pass, 2026-09-10)
   Colour still comes only from the surface. Nothing here pairs bg + fg.
   -------------------------------------------------------------------------- */

/* Sample verdict card — the hero card and template-parts/examples.php share it,
   so the "what reaches you" promise looks identical everywhere it is made. */
.gs-sample { display: flex; flex-direction: column; }
.gs-sample__head { display: flex; flex-wrap: wrap; gap: var(--gs-s-2); justify-content: space-between; align-items: center; }
.gs-sample__tags { display: flex; flex-wrap: wrap; gap: var(--gs-s-2); }
.gs-sample__title { margin: var(--gs-s-4) 0 var(--gs-s-1); font-size: var(--gs-t-xl); font-weight: 700; line-height: 1.25; color: var(--on-surface); }
.gs-sample__label {
  margin: var(--gs-s-5) 0 var(--gs-s-2);
  font-size: var(--gs-t-xs); font-weight: 700; letter-spacing: .10em; text-transform: uppercase;
  color: var(--on-surface-muted);
}
.gs-sample__why { margin: 0; font-size: var(--gs-t-sm); line-height: 1.65; color: var(--on-surface); }
.gs-sample__reqs { list-style: none; margin: var(--gs-s-4) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--gs-s-2); }
/* A "pass" is the product too: the same card, a stop-coloured left edge. */
.gs-sample--pass { border-left: 4px solid var(--gs-stop); }
.surface-inverse .gs-sample--pass, .surface-brand .gs-sample--pass { border-left-color: #fca5a5; }
/* Compact on a phone so the hero button and the top of the card both land
   within roughly two screens. */
@media (max-width: 600px) {
  .gs-sample { padding: var(--gs-s-4); }
  .gs-sample__title { margin-top: var(--gs-s-3); font-size: var(--gs-t-base); }
  .gs-sample__label { margin-top: var(--gs-s-4); }
}

/* The offer stack (what-you-get.php): an ordered list of cards, numbers kept. */
.gs-offer { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gs-s-5); grid-template-columns: 1fr; }
@media (min-width: 720px) { .gs-offer { grid-template-columns: repeat(2, 1fr); } }
.gs-offer__item { min-width: 0; }

/* "Recently closed" block on a hub — a hook, spacing comes from the utility. */
.gs-recent { min-width: 0; }

/* One-column grid — a single sample card at reading width. */
.gs-grid--1 { grid-template-columns: 1fr; max-width: 780px; }

/* Seats table (open-categories.php). The caption is the update note. */
.gs-table caption { caption-side: bottom; text-align: left; padding: var(--gs-s-3) 0 0; }
.gs-table--seats th[scope="row"] { font-weight: 600; }
.gs-scope .gs-table--seats a { color: var(--on-surface); }

/* --- Comparison matrix -------------------------------------------------- */
/* A row-per-option matrix where the reader should be able to find OUR row
   without reading. Background only, never a bg+color pair: the surface still
   owns the foreground, so the pair cannot go unreadable. */
.gs-table__us td,
.gs-table__us th[scope="row"] { background: var(--gs-paper-alt); font-weight: 600; }
.gs-table__us th[scope="row"] { box-shadow: inset 4px 0 0 var(--gs-accent-600); }

/* Yes / no marks. Colour only, and never colour alone: each mark carries a
   visually-hidden word so it survives screen readers and colour blindness. */
.gs-mark { font-weight: 800; font-size: var(--gs-t-base); line-height: 1; }
.gs-mark--yes { color: var(--gs-ok); }
.gs-mark--no  { color: var(--on-surface-muted); font-weight: 600; }
.gs-mark--part { color: var(--gs-warn); }

/* Matrix cells are narrow. Keep the option name readable without forcing the
   table so wide that the in-wrapper scroll becomes the whole experience on a
   phone: no nowrap and a smaller row-header floor below 720px. */
.gs-table--matrix th[scope="row"] { min-width: 9.5rem; }
.gs-table--matrix td { text-align: center; }
.gs-table--matrix td:last-child { text-align: left; }
@media (min-width: 720px) {
  .gs-table--matrix th[scope="row"] { min-width: 13rem; }
  .gs-table--matrix td:last-child { white-space: nowrap; }
}

/* A plain text link inside a component (not prose), so it owns its colour. */
.gs-scope a.gs-textlink { color: var(--link); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.gs-scope a.gs-textlink:hover { color: var(--on-surface); }

/* Fine print directly under a button row. Centered when the row is. */
.gs-center .gs-fineprint { margin-left: auto; margin-right: auto; }

/* Bottom-margin utilities, to retire the inline `style="margin-bottom:…"`. */
.gs-mb-0 { margin-bottom: 0 !important; }
.gs-mb-2 { margin-bottom: var(--gs-s-2); }
.gs-mb-4 { margin-bottom: var(--gs-s-4); }

/* --------------------------------------------------------------------------
   9. GUARDRAILS — what NOT to do
   --------------------------------------------------------------------------
   ✗ Never write a hex value in a template. If you need a colour, it is a
     token, or the surface is wrong.
   ✗ Never set `color` on a section wrapper. The surface did it.
   ✗ Never put body copy on .surface-accent. Amber is a CTA band only.
   ✗ Never put white text on amber (2.1:1 — fails). --btn-primary-fg
     already handles this.
   ✗ Never nest a surface inside the same surface. Alternate:
     base → subtle → inverse → base.
   ✗ Never remove :focus-visible.
   ✓ New component? Give it `background: var(--card-bg)` and let text inherit.
   ========================================================================== */

/* ── A4 additions ── */
/* Proof, booking and guides pass (2026-09-10). Colour still comes only from the
   surface; the avatar consumes the surface's button pair, so it stays legible on
   every band. Nothing here pairs a raw bg with a raw fg. */

/* Initials avatar — used until a photo exists (option gs_founder_photo). */
.gs-avatar {
  display: inline-grid; place-items: center; flex-shrink: 0;
  width: 56px; height: 56px; border-radius: var(--gs-r-pill);
  background: var(--btn-primary-bg); color: var(--btn-primary-fg);
  font-weight: 800; font-size: var(--gs-t-lg); letter-spacing: .02em;
  overflow: hidden;
}
.gs-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gs-avatar--sm { width: 40px; height: 40px; font-size: var(--gs-t-sm); }
.gs-avatar--lg { width: 96px; height: 96px; font-size: var(--gs-t-2xl); }

/* Person card (booking rail, About page). */
.gs-person { display: flex; gap: var(--gs-s-4); align-items: flex-start; }
.gs-person__name { margin: 0; font-size: var(--gs-t-lg); font-weight: 700; color: var(--on-surface); line-height: 1.3; }
.gs-person__role { margin: 2px 0 0; font-size: var(--gs-t-sm); color: var(--on-surface-muted); }
.gs-person__bio { margin: var(--gs-s-3) 0 0; font-size: var(--gs-t-sm); line-height: 1.6; color: var(--on-surface); }
.gs-scope .gs-person a { color: var(--link); }

/* Byline under a guide H1. */
.gs-byline { display: flex; align-items: center; gap: var(--gs-s-3); margin-top: var(--gs-s-4); font-size: var(--gs-t-sm); color: var(--on-surface-muted); line-height: 1.5; }
.gs-byline__text { margin: 0; }
.gs-byline__text strong { color: var(--on-surface); font-weight: 600; }
.gs-scope .gs-byline a { color: var(--on-surface-muted); text-decoration: underline; text-underline-offset: 2px; }
.gs-scope .gs-byline a:hover { color: var(--on-surface); }
/* The byline follows the H1 directly; the intro's lede gap then applies to the byline's next sibling. */
.gs-byline + .gs-prose { margin-top: var(--gs-s-5); }

/* On-this-page: sticky in the rail on wide screens, collapsed inline on narrow ones. */
.gs-toc--rail { display: none; }
.gs-toc--inline { margin-top: var(--gs-s-5); }
.gs-toc--inline > summary { cursor: pointer; font-size: var(--gs-t-xs); font-weight: 700; letter-spacing: .10em; text-transform: uppercase; color: var(--on-surface-muted); list-style: none; display: flex; justify-content: space-between; gap: var(--gs-s-4); }
.gs-toc--inline > summary::-webkit-details-marker { display: none; }
.gs-toc--inline > summary::after { content: "+"; font-weight: 400; font-size: 1.3em; line-height: 1; }
.gs-toc--inline[open] > summary::after { content: "\2212"; }
.gs-toc--inline[open] > summary { margin-bottom: var(--gs-s-2); }
.gs-toc ol { font-size: var(--gs-t-sm); }
.gs-toc li:last-child { margin-bottom: 0; }
@media (min-width: 1000px) {
  .gs-toc--rail { display: block; }
  .gs-toc--inline { display: none; }
}

/* Weekly-note opt-in — reuses the card + field components; only layout lives here. */
.gs-subscribe .gs-field:last-of-type { margin-bottom: 0; }
.gs-subscribe .gs-btn { margin-top: var(--gs-s-4); }
.gs-subscribe--wide { max-width: 560px; }

/* Principles list on the About page. */
.gs-principles { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gs-s-4); counter-reset: gs-principle; }
.gs-principles li { display: grid; grid-template-columns: auto 1fr; gap: var(--gs-s-4); align-items: start; padding: var(--gs-s-4) 0; border-top: 1px solid var(--border); }
.gs-principles li::before { counter-increment: gs-principle; content: counter(gs-principle, decimal-leading-zero); font-size: var(--gs-t-xs); font-weight: 800; letter-spacing: .08em; color: var(--on-surface-muted); padding-top: .35em; }
.gs-principles strong { display: block; color: var(--on-surface); font-size: var(--gs-t-lg); margin-bottom: var(--gs-s-1); }
.gs-principles p { margin: 0; color: var(--on-surface-muted); line-height: 1.6; max-width: var(--gs-measure); }

/* ── Fast-contact callouts (2026-09-10) ─────────────────────────────── */
.gs-reach__row{display:flex;align-items:center;justify-content:space-between;gap:var(--gs-s-5);flex-wrap:wrap}
.gs-reach__actions{gap:var(--gs-s-3)}
.gs-stickycta{display:none}
@media (max-width:880px){
  .gs-stickycta{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:190;gap:var(--gs-s-3);align-items:center;padding:var(--gs-s-3) var(--gs-s-4);padding-bottom:calc(var(--gs-s-3) + env(safe-area-inset-bottom));border-top:1px solid var(--border);box-shadow:0 -8px 24px rgba(0,0,0,.08)}
  .gs-stickycta .gs-btn{flex:1}
  .gs-stickycta__mail{font-weight:700;text-decoration:underline;white-space:nowrap}
  /* body.gs-scope (0,1,1) — a bare .gs-scope here lost to the body reset above,
     so the bar covered the footer's fine print by ~40px on every page. */
  body.gs-scope{padding-bottom:84px}
}
.gs-guide__callout{margin:var(--gs-s-6) 0}
.gs-linklist--cols{columns:2;column-gap:var(--gs-s-6)}
.gs-linklist--cols li{break-inside:avoid}
@media (max-width:600px){.gs-linklist--cols{columns:1}}

/* ── Audit pass 2 (2026-09-10) ──────────────────────────────────────── */

/* Screen-reader-only text (e.g. the trade name after a repeated "How it's bought"). */
.gs-visually-hidden{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* Sticky mobile bar: the Email link is a tap target too. */
.gs-stickycta__mail{display:inline-flex;align-items:center;min-height:44px;padding:0 var(--gs-s-3)}

/* Trades grid (open-categories.php grid layout, the industries pillar on /guides/):
   four groups of plain links. Colour is the surface's; only layout lives here. */
.gs-tradegrid{align-items:start}
.gs-tradegrid__group{min-width:0}
.gs-tradegrid__heading{margin:0 0 var(--gs-s-2);font-size:var(--gs-t-xs);font-weight:700;letter-spacing:.10em;text-transform:uppercase;color:var(--on-surface-muted)}
.gs-scope .gs-tradegrid__list a{text-decoration:underline;text-decoration-color:var(--border);text-underline-offset:3px;font-size:var(--gs-t-base)}
.gs-scope .gs-tradegrid__list a:hover{text-decoration-color:currentColor}
.gs-tradegrid__list li{padding:var(--gs-s-2) 0}

/* Grouped rows in the trades table. */
.gs-table__grouprow th{padding-top:var(--gs-s-5);font-size:var(--gs-t-xs);letter-spacing:.10em;text-transform:uppercase;color:var(--on-surface-muted)}

/* Touch widths: every link and control at least 44px tall, without changing
   the desktop rhythm. Text links inside tables/cards get vertical padding. */
@media (max-width:880px){
  .gs-scope a.gs-textlink,.gs-scope .gs-linklist a,.gs-scope .gs-opplist a,.gs-scope .gs-toc a,.gs-scope .gs-crumbs a,.gs-scope .gs-byline a,.gs-scope .gs-sources a,.gs-scope .gs-person a{display:inline-flex;align-items:center;min-height:44px}
  .gs-scope .gs-linklist a,.gs-scope .gs-opplist a,.gs-scope .gs-toc a{display:flex}
  .gs-scope a.gs-plainlink{display:inline-block;padding:9px 0}
  .gs-linklist li,.gs-opplist li{padding-top:0;padding-bottom:0}
  .gs-table td,.gs-table th{padding-top:var(--gs-s-2);padding-bottom:var(--gs-s-2)}
}

/* ── Opportunity filter chips (2026-09-11) ──────────────────────────── */
.gs-filters{border-top:1px solid var(--border);padding-top:var(--gs-s-5)}
.gs-filters__label{display:block;font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--on-surface-muted);margin-bottom:var(--gs-s-3)}
.gs-filters__list{display:flex;flex-wrap:wrap;gap:var(--gs-s-2);list-style:none;margin:0;padding:0}
.gs-chip{display:inline-flex;align-items:center;gap:6px;padding:9px 14px;min-height:44px;border:1px solid var(--border);border-radius:999px;font-size:14px;font-weight:600;text-decoration:none;color:var(--on-surface)}
.gs-chip:hover{border-color:var(--on-surface-muted)}
.gs-chip.is-on{border-color:var(--gs-accent-600);box-shadow:inset 0 0 0 1px var(--gs-accent-600)}
.gs-chip__n{font-size:12px;font-weight:700;color:var(--on-surface-muted);font-variant-numeric:tabular-nums}

/* --------------------------------------------------------------------------
   FILLED PILLS — matching the client portal
   The outlined .gs-badge reads as a form control next to real content. The
   portal (good-stuart-rfq globals.css .badge) uses a soft ground with strong
   ink, which is what the fit grades look like to a client already.
   Background + colour are set together here deliberately: this is a self-
   contained chip that must stay legible on ANY surface, the same exemption
   the form controls take.
   -------------------------------------------------------------------------- */
.gs-pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--gs-t-xs); font-weight: 700; letter-spacing: .01em;
  padding: 4px 11px; border-radius: var(--gs-r-pill);
  white-space: nowrap; border: 0;
  /* A pill can carry an upstream label of any length. Never let it push past
     its card: ellipsis inside the pill rather than overflow outside it. */
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
/* Dedicated inks, darker than the outline-badge versions. On a soft ground
   --gs-ok gave 4.57:1 and --gs-warn 4.51:1, which pass but leave no headroom
   for a future tint change. These are 6.5:1 and 6.4:1. */
.gs-pill--strong   { background: var(--gs-ok-soft);      color: var(--gs-ok-ink); }    /* 6.49:1 */
.gs-pill--moderate { background: var(--gs-warn-soft);    color: var(--gs-warn-ink); }  /* 6.37:1 */
.gs-pill--weak     { background: var(--gs-stop-soft);    color: var(--gs-stop); }      /* 5.30:1 */
.gs-pill--plain    { background: var(--gs-neutral-soft); color: var(--gs-ink); }       /* 16.3:1 */

/* --------------------------------------------------------------------------
   TICKER — the industries marquee
   WCAG 2.2.2: motion that starts automatically and lasts more than five
   seconds needs a mechanism to pause it. Hover and focus are not enough on
   touch, so the markup ships a real button and this styles it.
   -------------------------------------------------------------------------- */
.gs-ticker { position: relative; overflow: hidden; }
.gs-ticker__track {
  display: flex; gap: var(--gs-s-3); width: max-content;
  animation: gs-ticker-scroll 60s linear infinite;
}
.gs-ticker:hover .gs-ticker__track,
.gs-ticker:focus-within .gs-ticker__track,
.gs-ticker[data-paused="true"] .gs-ticker__track { animation-play-state: paused; }
.gs-ticker__item {
  display: inline-flex; align-items: center; gap: var(--gs-s-2);
  padding: 9px 16px; border-radius: var(--gs-r-pill);
  border: 1px solid var(--border); color: var(--on-surface);
  font-size: var(--gs-t-sm); font-weight: 600; text-decoration: none;
}
.gs-ticker__item:hover { border-color: var(--btn-primary-bg); }
.gs-ticker__item .gs-icon { width: 17px; height: 17px; opacity: .85; }
/* Sits BELOW the rail, not on top of it. Overlaying it meant a trade pill slid
   underneath the button as the track scrolled, which read as a rendering fault. */
.gs-ticker__controls { display: flex; align-items: center; gap: var(--gs-s-4); flex-wrap: wrap; }
.gs-ticker__pause {
  border: 1px solid var(--border); background: transparent;
  color: var(--on-surface); border-radius: var(--gs-r-pill);
  font-size: var(--gs-t-xs); font-weight: 700; padding: 7px 14px; cursor: pointer;
  min-height: 34px;
}
.gs-ticker__pause:hover { border-color: var(--btn-primary-bg); }
/* Edge fade so items do not appear to be cut off mid-word. */
.gs-ticker__fade { position: absolute; top: 0; bottom: 0; width: 56px; pointer-events: none; z-index: 1; }
.gs-ticker__fade--l { left: 0;  background: linear-gradient(to right, var(--surface), transparent); }
.gs-ticker__fade--r { right: 0; background: linear-gradient(to left,  var(--surface), transparent); width: 120px; }
@keyframes gs-ticker-scroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  /* The global rule sets duration and iteration-count; belt and braces for the
     one component here that would otherwise animate forever. */
  .gs-ticker__track { animation: none !important; }
  .gs-ticker { overflow-x: auto; }
  .gs-ticker__controls { display: none; }
}

/* --------------------------------------------------------------------------
   PERSONA CARDS — the three "where you are right now" states
   A real 4:5 image slot. Until photography exists the slot carries a
   token-built plate with a sprite icon, which is obviously a graphic and so
   cannot be mistaken for a fake photograph of a real tradesperson.
   -------------------------------------------------------------------------- */
.gs-persona { display: block; text-decoration: none; overflow: hidden; }
.gs-persona__media {
  /* display:block is load-bearing. These are <span>s inside an <a>, and an
     inline box ignores aspect-ratio entirely: the media collapsed to 0x0 and
     the three cards rendered 125px tall with the quote spilling out. */
  display: block;
  position: relative; aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: var(--gs-r-md); margin-bottom: var(--gs-s-4);
}
.gs-persona__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gs-persona__plate {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
}
.gs-persona__plate .gs-icon { width: 76px; height: 76px; opacity: .5; }
/* The drawn portrait fills the slot exactly like a photograph would, so
   swapping in real photography later changes nothing else. */
.gs-persona__art { display: block; width: 100%; height: 100%; }
.gs-persona__quote {
  display: block;
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--gs-s-7) var(--gs-s-4) var(--gs-s-4);
  font-size: var(--gs-t-xl); font-weight: 800; line-height: 1.2;
  /* Scrim, not a colour pair: the quote keeps the surface's own foreground and
     this only darkens whatever sits behind it. Without it the text is legible
     on the placeholder plate but not over an arbitrary photograph. */
  background: linear-gradient(to top, rgba(7,21,39,.92) 0%, rgba(7,21,39,.72) 55%, rgba(7,21,39,0) 100%);
  text-shadow: 0 1px 2px rgba(7,21,39,.55);
}
/* The media plate is always dark, so the quote and the trade pill are placed on
   an inverse surface regardless of whether a photograph has been set. */
.gs-persona__media { color: var(--gs-ink-invert); }
.gs-persona__trade {
  position: absolute; left: var(--gs-s-4); top: var(--gs-s-4); z-index: 1;
}
.gs-persona .gs-card__meta { display: block; }

/* --------------------------------------------------------------------------
   SCROLLER — horizontal card rail for live opportunities
   Real overflow scrolling with snap, so it works with a trackpad, a finger,
   the keyboard and a screen reader without any JavaScript.
   -------------------------------------------------------------------------- */
.gs-scroller {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(268px, 1fr);
  gap: var(--gs-s-4); overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  /* overflow-x:auto clips the cross axis too, which cut the cards' shadows and
     focus rings flat along the top and bottom edge. Pad the scroll box, then
     pull the padding back so the rail still aligns with the section grid. */
  padding: var(--gs-s-3) var(--gs-s-3) var(--gs-s-5);
  margin: calc(var(--gs-s-3) * -1) calc(var(--gs-s-3) * -1) 0;
}
.gs-scroller > * { scroll-snap-align: start; min-width: 0; }
@media (min-width: 980px) { .gs-scroller { grid-auto-columns: minmax(300px, 1fr); } }

/* --------------------------------------------------------------------------
   ENTRANCE — a short, once-only fade and rise.
   Opacity starts at 1 so anything is readable if the script never runs.
   -------------------------------------------------------------------------- */
.gs-scope [data-gs-reveal] { opacity: 1; }
.gs-scope.gs-js [data-gs-reveal] { opacity: 0; transform: translateY(14px); }
.gs-scope.gs-js [data-gs-reveal].is-in {
  opacity: 1; transform: none;
  transition: opacity .5s ease, transform .5s cubic-bezier(.22,.61,.36,1);
}
@media (prefers-reduced-motion: reduce) {
  .gs-scope.gs-js [data-gs-reveal] { opacity: 1 !important; transform: none !important; }
}

/* --------------------------------------------------------------------------
   MATCH PANEL — the hero's right half
   Three compact rows showing what a matched opportunity looks like when it
   reaches a client: the work, where it is, and the fit pill. The hero visual
   should be the product's output, not a statistic.
   -------------------------------------------------------------------------- */
.gs-matchcard {
  border: 1px solid var(--border); border-radius: var(--gs-r-lg);
  background: var(--card-bg); padding: var(--gs-s-6) var(--gs-s-5);
}
.gs-matchlist { list-style: none; margin: 0; padding: 0; }
.gs-matchlist__row {
  display: flex; align-items: center; gap: var(--gs-s-3);
  padding: var(--gs-s-4) 0; border-top: 1px solid var(--border);
}
.gs-matchlist__row:first-child { border-top: 0; padding-top: 0; }
.gs-matchlist__row:last-child  { padding-bottom: 0; }
/* The middle column absorbs the slack so the pill stays hard right. */
.gs-matchlist__what { flex: 1 1 auto; min-width: 0; }
.gs-matchlist__title {
  display: block; font-weight: 700; color: var(--on-surface);
  font-size: var(--gs-t-sm); line-height: 1.3;
}
.gs-matchlist__where {
  display: block; font-size: var(--gs-t-xs); color: var(--on-surface-muted); margin-top: 2px;
}
.gs-matchlist .gs-pill { flex: 0 0 auto; }
@media (max-width: 520px) {
  .gs-matchcard { padding: var(--gs-s-5) var(--gs-s-4); }
  .gs-matchlist__row { gap: var(--gs-s-2); }
}

/* --------------------------------------------------------------------------
   V2 COMPARISON PAGE — private draft only
   -------------------------------------------------------------------------- */
/* A standing reminder that this is not the live page. Fixed-appearance utility,
   same exemption the skip link takes: it must read on any surface. */
.gs-v2flag {
  position: sticky; top: 68px; z-index: 150;
  background: #1f2937; color: #ffffff;
  font-size: 13px; font-weight: 600;
  padding: 9px var(--gs-s-5); text-align: center;
}
.gs-v2flag a { color: #fbbf24; }
/* The mechanism diagram sits in the hero's right half. */
.gs-mech { display: block; width: 100%; height: auto; }
