/* ASYST base, layout and components. Reads tokens.css.
   Never hardcodes a brand colour. */

/* ---------- Base (Tailwind preflight is disabled) ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Sticky nav: offset scroll for BOTH anchor jumps and sequential focus.
     scroll-padding-top on the scrollport, not scroll-margin-top on a selector
     list, because focus navigation scrolls elements you did not think to name. */
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
/* overflow-wrap is inert until a single word is wider than its line box, so it
   changes nothing at any normal text size. It matters at SC 1.4.4's 200%: at a
   32px browser default "requirement." measured 371px of min-content inside a
   310px .wrap, and because a grid item's automatic minimum size is its
   min-content, that one word set the floor for the whole column and took the
   document 102px wide. The hero <h1> had the same problem in a harsher form -
   see the note on .hero. */
h1, h2, h3 { font-family: var(--font-display); margin: 0 0 0.5em; line-height: 1.12;
             overflow-wrap: break-word; }
h1 { font-size: var(--step-4); font-weight: 700; letter-spacing: -0.035em; }
h2 { font-size: var(--step-3); font-weight: 700; letter-spacing: -0.028em; }
h3 { font-size: var(--step-2); font-weight: 600; letter-spacing: -0.02em; }
p { margin: 0 0 1em; }
a { color: var(--accent); }
img, svg { max-width: 100%; height: auto; display: block; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
#main:focus { outline: none; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-contrast);
  padding: 0.75rem 1.25rem; border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }

/* ---------- Layout ---------- */
/* A content max-width is an outer box, not a max-width plus padding. */
.wrap { width: min(100% - var(--gutter) * 2, var(--content-max)); margin-inline: auto; }
.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }
.section--surface { background: var(--surface); }
.section--surface-2 { background: var(--surface-2); }

/* minmax(min(Npx, 100%), 1fr), never minmax(Npx, 1fr): the bare form is the
   documented horizontal-overflow bug below N px. */
.grid-3 { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.grid-4 { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
/* minmax(0, 1fr), not 1fr - the same rule .hero__row and .site-footer__inner
   already follow, and .two-col was the one grid still missing it. A grid item's
   automatic minimum size is its min-content, so a bare 1fr track cannot shrink
   below the longest unbreakable thing inside it: at a 32px browser default the
   word "requirement." set a 371px floor inside a 310px .wrap and took the page
   21px wide. With the track free to reach 0, overflow-wrap on the heading has a
   narrow enough box to act on. Identical to 1fr at every size where the content
   fits, which is every normal text size. */
.two-col { display: grid; gap: clamp(2rem, 5vw, 4rem);
           grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }

.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem;
         grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.steps h3 { margin: 0.4rem 0 0.3rem; }
/* The list carries no bottom margin, so a following paragraph would butt straight
   into the last row. */
.steps-more { margin-top: 1.75rem; }
/* Last paragraph inside a step card: the card supplies the padding. */
.steps .card p:last-child { margin-bottom: 0; }

.prose { max-width: var(--measure); }
.prose h2 { font-size: var(--step-2); margin-top: 2.25rem; }
.prose h3 { font-size: var(--step-1); margin-top: 1.75rem; }
.prose ul, .prose ol { padding-left: 1.25rem; margin: 0 0 1em; }
.prose li { margin-bottom: 0.4em; }

@media (max-width: 860px) {
  .two-col { grid-template-columns: minmax(0, 1fr); }
}

/* Alternating image side on /workflow. The flip is visual only and desktop only:
   DOM order stays copy-then-image on every stage, so the heading is announced
   first and stacks first once .two-col collapses to one column. Ordering cannot
   be inlined on the element because it has to be media-scoped. */
@media (min-width: 861px) {
  .two-col--flip > :first-child { order: 2; }
}

/* ---------- Hero ---------- */
/* No `overflow: hidden` on .hero. It existed to keep the atmosphere layer inside
   the section, but it clipped every child, and a clip is not scrollable: at 390px
   with a 32px browser default the h1 rendered as "Your engineers are writing
   requireme" with the rest unrecoverable by any means (measured scrollWidth 531
   against clientWidth 390). The clip now sits on the layer it was for. */
.hero { position: relative; padding-block: clamp(2.5rem, 7vw, 5.5rem); }
.hero::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; overflow: hidden;
  background:
    var(--hero-glow),
    linear-gradient(var(--hero-grid) 1px, transparent 1px) 0 0 / 34px 34px,
    linear-gradient(90deg, var(--hero-grid) 1px, transparent 1px) 0 0 / 34px 34px;
  mask-image: linear-gradient(to bottom, #000 0%, transparent 85%);
}
.hero > * { position: relative; }

/* Heading band: full content width, centred, above the two-column row. The h1
   keeps --step-4; only its column changed. text-wrap: balance so a full-width
   line does not leave one orphaned word on the second row. */
.hero__head { text-align: center; }
/* The h1 inherits overflow-wrap: break-word from the base heading rule, and it is
   load-bearing here: without it, removing the hero clip above would trade an
   unreadable headline for a horizontally-scrolling page, because "requirements"
   at a 32px browser default is wider than a 390px viewport. */
.hero__head h1 { text-wrap: balance; margin-bottom: 0.4em; }
/* .eyebrow is a flex row with a rule drawn in ::before, so centring it needs
   justify-content, not text-align. */
.hero__head .eyebrow { justify-content: center; }

.hero__row { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
             gap: clamp(2rem, 5vw, 4rem); align-items: stretch; }
/* Copy column drives the row height; centred within it so the lead and the CTA
   sit optically level with the middle of the image. */
.hero__copy { display: flex; flex-direction: column; justify-content: center; }
.hero__lead { font-size: var(--step-1); color: var(--muted); max-width: 56ch; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }

/* Height alignment. The image is taken OUT of flow inside its column, so the row
   height is set by the copy column alone and the image then fills it exactly.
   The alternatives both fail: an in-flow image with height:100% still contributes
   its aspect-ratio height during intrinsic sizing, so the taller of the two wins
   and the shorter one floats; and height:100% without object-fit stretches the
   art. min-height is the floor for the case where the copy column is shorter than
   the image would ever sensibly be, so the art can never become a letterbox
   sliver. */
.hero__visual { position: relative; min-height: 260px; }
/* Abstract hero art, not a product screenshot: no device chrome, just the page's
   own radius and elevation so it reads as an illustration. */
.hero__image {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 45%;
  border-radius: var(--radius); box-shadow: var(--shadow-lift);
}

/* Stacked hero. The image returns to normal flow at its own aspect ratio: there
   is no column to align to, and an absolutely-positioned image in a one-column
   stack collapses to nothing. */
@media (max-width: 860px) {
  .hero__row { grid-template-columns: 1fr; }
  .hero__visual { position: static; min-height: 0; }
  .hero__image { position: static; height: auto; object-fit: fill; }
}

/* No full-bleed hero rule. The landing hero art is an illustration rather than a
   device frame running off the edge, so the hero uses the same wrap as every
   other section and its left and right margins match. */

/* ---------- Signature: the trace link ---------- */
.eyebrow {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--font-display); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.16em;
  color: var(--muted); margin-bottom: 1rem;
}
.eyebrow::before { content: ''; width: 28px; height: 1px; flex: none; background: var(--accent); }

.stage-num { font-family: var(--font-mono); font-size: var(--step--1); color: var(--accent); letter-spacing: 0.04em; }
/* Connector between consecutive numbered stages. */
.stage { position: relative; }
.stage + .stage::before {
  content: ''; position: absolute; left: 0; top: -0.9rem;
  width: 28px; height: 1px; background: var(--accent); opacity: 0.5;
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--font-body); font-weight: 600; font-size: var(--step-0);
  min-height: 44px; padding: 0.6rem 1.4rem;
  border-radius: var(--radius-sm); border: 1px solid transparent;
  text-decoration: none; white-space: nowrap;
  transition: background-color var(--dur-ui) ease, border-color var(--dur-ui) ease, color var(--dur-ui) ease;
}
.btn--primary { background: var(--accent); color: var(--accent-contrast); }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
/* Shape modifier only. It changes the radius to the existing pill token and buys
   back the horizontal padding a pill needs to keep its label off the curve; the
   fill and label colour still come from the variant it is combined with, so
   .btn--ghost.btn--pill stays a secondary action and never competes with the one
   primary CTA. min-height: 44px is inherited from .btn. */
.btn--pill { border-radius: var(--radius-pill); padding-inline: 1.6rem; }

/* Colour feedback is NOT motion: it stays outside the reduced-motion query, so a
   reduced-motion visitor still gets a hover state. Only transform and shadow are gated. */
.btn--primary:hover { background: var(--accent-hover); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

@media (prefers-reduced-motion: no-preference) {
  .btn { transition: background-color var(--dur-ui) ease, border-color var(--dur-ui) ease,
                     color var(--dur-ui) ease, transform var(--dur-ui) ease, box-shadow var(--dur-ui) ease; }
  [data-theme='dark'] .btn--primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
}

/* ---------- Cards ---------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.5rem; box-shadow: var(--shadow-card);
}
.section--surface .card, .section--surface-2 .card { background: var(--bg); }
/* No .card--hover. Not one card on this site is a link or a control, so a hover
   affordance on them would promise a click that does not exist. Deleted rather
   than applied (2026-07-29). */

/* ---------- Pain framing: amber, never a CTA ---------- */
.pain-accent { color: var(--warn); font-weight: 600; }

/* ---------- Pull-quote band ----------
   The page's thesis, given its own scroll moment between the problem and the
   answer to it. Display face at heading scale but NOT a heading: two sentences
   make a poor outline entry, and the band carries no section heading of its own.
   Measure is deliberately tight, so it breaks into short lines and reads as a
   statement rather than as body copy set large. */
.pullquote {
  font-family: var(--font-display); font-weight: 600;
  font-size: var(--step-3); line-height: 1.25; letter-spacing: -0.028em;
  text-align: center; max-width: 34ch; margin-inline: auto; text-wrap: balance;
}
/* Amber is the pain half, violet the answer: the same semantic split the rest of
   the page uses. <strong> rather than a colour-only span, so the emphasis
   survives for anyone who cannot see the colour. */
.pullquote strong { color: var(--accent); font-weight: inherit; }

/* ---------- Standards strip ---------- */
.standards__row {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(1.5rem, 5vw, 3.5rem);
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: 0.08em; color: var(--muted);
}

/* ---------- Device mockup ----------
   The app screenshot is single-theme. The frame scopes its own theme vars so the
   chrome matches the IMAGE, not the page: a bright screenshot in eggplant chrome
   reads as broken. */
.mockup-card {
  --surface: #ffffff; --surface-2: #f4f4f7; --border: #e2e2ea;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-lift);
}
.mockup-card-header {
  display: flex; align-items: center; gap: 3px;
  padding: 0.6rem 0.9rem; background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.mockup-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border); }
.mockup-card img { width: 100%; }

/* ---------- Pricing tiers ----------
   Card anatomy ported from the RallyIQ pricing page (05_Marketing/Landing_Pages/
   NextGen_TournyHub_LandingPage) and retokenised to the ASYST palette and type
   scale: name, tagline, large price, full-width CTA, then the feature list.
   Alignment across the row comes from min-heights on the tagline and the price
   note, not from stretching the cards, which is why the grid is align-items:
   start and the featured card is allowed to be taller. */
.tier { display: flex; flex-direction: column; gap: 0.9rem; }
.tier__price { font-family: var(--font-display); font-size: var(--step-3); font-weight: 700; }
.tier__note { color: var(--muted); font-size: var(--step--1); }
.tier__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.55rem; }
.tier__list li { padding-left: 1.4rem; position: relative; }
.tier__list li::before { content: ''; position: absolute; left: 0; top: 0.62em;
  width: 12px; height: 1px; background: var(--accent); }
.badge {
  align-self: flex-start; border-radius: var(--radius-pill);
  background: rgb(var(--accent-rgb) / 0.12); color: var(--accent);
  font-size: var(--step--1); font-weight: 600; padding: 0.2rem 0.7rem;
}

/* Wrapping row of .badge chips (/features uses it to name the stages a single
   edit invalidates). Flex-wrap, not a grid: the chips are sized by their labels
   and the row must reflow to a phone without a column count to argue with. */
.chip-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.25rem; }

/* Three-up tier grid. Fixed 3 columns rather than auto-fit: a pricing row that
   reflows to 4/2 or 2/1 breaks the comparison the reader is making. */
.tier-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; align-items: start; }
@media (max-width: 900px) {
  .tier-grid { grid-template-columns: 1fr; max-width: 420px; margin-inline: auto; }
}

.tier-grid .tier { gap: 0; padding: 2rem 1.75rem; }
.tier-grid .tier > * { margin: 0; }
.tier__name { font-family: var(--font-display); font-size: var(--step-1); font-weight: 700; letter-spacing: -0.02em; }
.tier-grid .tier__tag { color: var(--muted); font-size: var(--step--1); line-height: 1.5;
  margin-top: 0.4rem; min-height: 3em; }
.tier-grid .tier__price { display: flex; align-items: baseline; gap: 0.1rem;
  margin-top: 1.35rem; line-height: 1.05; }
.tier__cur { font-size: var(--step-1); }
.tier__per { font-family: var(--font-body); font-size: var(--step--1); font-weight: 500; color: var(--muted); }
.tier-grid .tier__note { margin-top: 0.5rem; min-height: 2.8em; }
.tier-grid .tier > .btn { width: 100%; margin: 1.4rem 0 1.6rem; }
.tier__feat-head { font-family: var(--font-display); font-size: var(--step--1); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 0 0 0.8rem; }

/* Featured tier: accent border plus a 1px ring, so the emphasis survives a theme
   swap without introducing a second accent. */
.tier--featured { position: relative; border-color: rgb(var(--accent-rgb) / 0.55);
  box-shadow: 0 0 0 1px rgb(var(--accent-rgb) / 0.25), var(--shadow-lift); }
.tier__badge {
  position: absolute; top: -0.8rem; left: 50%; transform: translateX(-50%);
  background: var(--accent); color: var(--accent-contrast);
  font-family: var(--font-display); font-size: var(--step--1); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 0.25rem 0.8rem; border-radius: var(--radius-pill); white-space: nowrap;
}

/* Tick markers for the feature list. Drawn with two borders on a rotated box
   rather than a mask: it stays crisp at any zoom and carries no data URI. Same
   specificity as the dash rule above, so it must stay after it. */
.tier__list--check li { padding-left: 1.6rem; }
.tier__list--check li::before {
  left: 4px; top: 0.42em; width: 6px; height: 11px;
  background: none; border: solid var(--accent); border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.tier-table { width: 100%; border-collapse: collapse; }
.tier-table th, .tier-table td { text-align: left; padding: 0.65rem 0.75rem; border-bottom: 1px solid var(--border); }
.tier-table th { font-family: var(--font-display); font-size: var(--step--1);
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); }
/* Row headers carry the feature name, so they read as body copy. Only the column
   headers get the small uppercase treatment. Without this, scoping the first
   column as <th> (which it is, semantically) sets the whole column in muted
   uppercase micro-type, so correct table semantics would cost legibility. */
.tier-table tbody th { font-family: var(--font-body); font-size: var(--step-0);
  text-transform: none; letter-spacing: normal; color: var(--text); font-weight: 600; }
/* Horizontal scroller for the comparison table on narrow viewports. Keyboard
   focusable, because a scroll region that only a mouse can reach is a trap. */
.table-scroll { overflow-x: auto; }
.table-scroll .tier-table { min-width: 34rem; }

/* ---------- FAQ (native details, zero JS) ---------- */
.faq-item { border-bottom: 1px solid var(--border); }
/* A flex row, NOT a float. The marker is generated by ::after, so as a float it
   is placed after the last line box rather than reserving a column: at 390px
   that dropped the `+` onto its own line under "Does ASYST replace my MBSE
   tool?" and butted it against the `?` of "What counts as an active project?".
   The `flex: none` on the marker was already assuming this row. */
.faq-item summary {
  cursor: pointer; list-style: none; padding: 1.15rem 0; min-height: 44px;
  font-family: var(--font-display); font-weight: 600; font-size: var(--step-1);
  display: flex; align-items: center; justify-content: space-between; gap: 1.25rem;
}
.faq-item summary::-webkit-details-marker { display: none; }
/* Drawn, not text: a `+` in ::after is announced as "plus" on top of the native
   expanded/collapsed state. */
.faq-item summary::after {
  content: '';
  width: 12px; height: 12px; flex: none;
  background: var(--accent);
  clip-path: polygon(45% 0, 55% 0, 55% 45%, 100% 45%, 100% 55%, 55% 55%, 55% 100%, 45% 100%, 45% 55%, 0 55%, 0 45%, 45% 45%);
}
.faq-item[open] summary::after { clip-path: polygon(0 45%, 100% 45%, 100% 55%, 0 55%); }
.faq-item p { color: var(--muted); padding-bottom: 1.15rem; margin: 0; }

/* ---------- Final CTA ---------- */
.final-cta { text-align: center; }
.final-cta p { color: var(--muted); }

/* ---------- Theme toggle ---------- */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  background: transparent; color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm); cursor: pointer;
}
.theme-toggle__sun { display: none; }
[data-theme='dark'] .theme-toggle__sun { display: block; }
[data-theme='dark'] .theme-toggle__moon { display: none; }

/* ---------- Reveal on scroll: JS-only, motion-gated ----------
   .reveal is allowed on a GRID OR LIST OF REPEATED ITEMS only - .grid-3,
   .grid-4, .steps, .tier-grid - never on a container carrying a section heading
   or its body prose. Two reasons, and the second is the load-bearing one:
     1. Fading an 800px block as a single unit is a whole-screen curtain. The
        house form is a short stagger across a set of cards.
     2. It puts opacity: 0 on headings and copy, so anything that does not run
        the observer ships a blank section - a print render, a renderer that
        never scrolls, an uncaught error earlier in main.js. Motion enhances an
        already-visible default; it never decides whether copy paints.
   `.js` scoping and the motion query cover the no-JS and reduced-motion cases.
   The print block below and the 3s failsafe in main.js cover the rest.
   Asserted by check-reveal.mjs. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal { opacity: 0; transform: translateY(14px);
    transition: opacity var(--dur-reveal) ease, transform var(--dur-reveal) ease; }
  .js .reveal.visible { opacity: 1; transform: none; }
}
/* Print never scrolls, so the observer never fires. Selectors carry .js to beat
   the specificity of the rule above; a bare `.reveal` here would lose to it. */
@media print {
  .js .reveal, .js .reveal.visible { opacity: 1; transform: none; }
}

/* ---------- Staleness chip cascade (/features) ----------
   The paragraph above this row claims that flagging follows the dependency
   chain the whole way down. Ten identical pills arriving at once illustrates
   the opposite, so the chips resolve one at a time, in the order the real
   dependency map propagates through them (verified against the product's
   staleness/dependency_map.py: s01 -> s02 -> s03.1 -> s03.2 -> s04 -> s05 ->
   s06 -> s07 -> s08a -> s08b). A staggered sweep in arbitrary order would
   assert something false, which is why the markup order is the constraint.

   The START state is itself a finished, readable state - muted label, hairline
   border, no fill. Never opacity: 0. A reduced-motion visitor, a no-JS visitor,
   a print render and anyone who stops the page mid-sweep all get ten legible
   chips; they simply do not animate. Only two colours and a border move, and
   the 1px border is present in both states so nothing reflows. */
.chip-cascade .badge {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.chip-cascade.visible .badge {
  background: rgb(var(--accent-rgb) / 0.12);
  color: var(--accent);
  border-color: rgb(var(--accent-rgb) / 0.35);
}
@media (prefers-reduced-motion: no-preference) {
  .chip-cascade .badge {
    transition: color var(--dur-ui) ease, background-color var(--dur-ui) ease,
                border-color var(--dur-ui) ease;
    transition-delay: calc(var(--i, 0) * 55ms);   /* 10 chips x 55ms = 550ms total */
  }
}

/* ---------- Nav ---------- */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  background: var(--bg);                                        /* fallback first */
  background: color-mix(in srgb, var(--bg) 88%, transparent);   /* dropped wholesale if unsupported */
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.site-nav__inner {
  width: min(100% - var(--gutter) * 2, var(--content-max)); margin-inline: auto;
  min-height: var(--nav-h);
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.site-nav__brand { display: flex; align-items: baseline; gap: 0.5rem; text-decoration: none; color: var(--text); }
.site-nav__mark { font-family: var(--font-display); font-weight: 700; letter-spacing: -0.02em; font-size: var(--step-1); }
.site-nav__parent { font-size: var(--step--1); color: var(--muted); }
.site-nav__links { display: flex; gap: 1.5rem; }
/* nowrap: without it a squeezed row breaks "The Problem" over two lines, which
   grows the whole nav bar from 72px to 83px and shifts the page down. Measured on
   /features at 862-900px and 1050-1100px. The row is kept fitting by the two
   media queries below, not by letting labels wrap. */
.site-nav__links a { color: var(--muted); text-decoration: none; padding: 0.75rem 0; min-height: 44px; display: inline-flex; align-items: center; white-space: nowrap; }
.site-nav__links a:hover { color: var(--text); }
.site-nav__links a[aria-current='page'] { color: var(--text); box-shadow: inset 0 -2px 0 var(--accent); }
.site-nav__actions { display: flex; align-items: center; gap: 0.6rem; }

.nav-toggle {
  display: none; width: 44px; height: 44px; padding: 10px;
  flex-direction: column; justify-content: space-between;
  background: transparent; border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); cursor: pointer;
}
.nav-toggle span { display: block; height: 2px; background: var(--text); border-radius: 2px; }

/* Five nav links plus brand, theme toggle, Sign in and Start free do not fit the
   row immediately above the mobile breakpoint: measured 816px of content into
   792px of container at exactly 861px with four links. Drop the two lowest-value
   items first rather than moving the breakpoint, which would desync main.js's
   MOBILE query. Both are duplicated elsewhere: the parent brand line in the
   footer, Sign in in the hero and the footer.
   Raised 1000px -> 1150px when /features added the fifth link: at 1050-1100px the
   two items reappeared before the row had room for them, wrapping the labels.

   UNITS: every breakpoint in this nav block is `em`, not `px`, and must stay that
   way. The whole type scale is clamp(rem...), so raising the browser's default
   font size grows every label while a px breakpoint stays put - measured at 1280px
   with a 20px default (Chrome "Large"), the row overflowed by 87px and "Start
   free" was cut mid-word; at 32px (200%) by 805px, and at 390px the hamburger sat
   at x 443-487 in a 390px viewport, off-screen, with the five links behind it.
   `em` in a media query resolves against the INITIAL font size - i.e. the user's
   own default - not against the root element's computed size, so an em breakpoint
   moves with the text and the row collapses to hamburger mode before it can
   overflow. Divide the old px value by 16 to read these:
   71.875em = 1150px, 60em = 960px, 53.75em = 860px, 25em = 400px at the default. */
@media (max-width: 71.875em) {
  .site-nav__parent { display: none; }
  .site-nav__signin { display: none; }
}

/* Even with both dropped, five labels at a 1.5rem gap do not clear the narrow end
   of the desktop row. Tighten the gap rather than wrap or shrink the 44px targets. */
@media (max-width: 60em) {
  .site-nav__links { gap: 1.05rem; }
}

/* The hamburger and the overlay panel it controls are a JS affordance, so both are
   scoped to a class. Unscoped, with main.js blocked, #site-nav-links stayed
   hidden=false and absolutely positioned over y 72-378: elementFromPoint at the
   hero eyebrow and at the <h1> both returned the nav panel, the headline never
   painted, and .nav-toggle still reported aria-expanded="false" - the markup
   contradicting the screen. Asserted by check-a11y.mjs section G.

   The class is `.nav-js`, set by main.js, and NOT `.js`. `.js` is set by the inline
   head script, which runs whether or not main.js does - it is the script that
   survives exactly the failure being guarded against, so scoping to it changes
   nothing (measured: the panel still covered the <h1>). The rule has to be scoped
   to something only the file that can OPEN AND CLOSE the panel is able to set. That
   is the difference from `.reveal`, where the guarantee genuinely does belong in
   the always-running script, because there the fix is to undo a hide rather than to
   withdraw a control.

   SLOW main.js, as opposed to a dead one. Chrome does NOT reliably paint after a
   deferred module executes: with main.js held for 2.5s, six runs each showed ~2.2s
   of painted pre-main.js layout. So this state is visible, and it was visible
   before this change too - the difference is what it shows. Before: the open
   overlay sitting on top of the <h1>, i.e. the same occlusion, temporarily.
   Now: the five links in flow above the hero, headline fully readable, collapsing
   to the hamburger when main.js lands. Content pushed down for a moment beats
   content hidden for a moment, and it degrades into the dead-main.js case rather
   than away from it. Removing the flash entirely means moving the closed state
   into the markup and adding a nav failsafe to the inline head script; that is a
   redesign of the panel's state contract, not a scoping change, so it is not done
   here. Screenshots: screenshots/flash-*.png. */
@media (max-width: 53.75em) {
  .site-nav__links { flex-direction: column; gap: 0; }
  .site-nav__links a { padding: 0.85rem 0; border-bottom: 1px solid var(--border); }

  .nav-js .nav-toggle { display: flex; }
  .nav-js .site-nav__links {
    position: absolute; left: 0; right: 0; top: 100%;
    background: var(--bg); border-bottom: 1px solid var(--border);
    padding: 0.5rem var(--gutter) 1rem;
  }
  .nav-js .site-nav__links[hidden] { display: none; }

  /* No working main.js: the five links are in normal flow, so give them their own
     row under the brand rather than a third column squeezed between it and the
     actions, and let the taller bar scroll away instead of sticking over a third
     of the viewport for the whole page. */
  html:not(.nav-js) .site-nav { position: static; }
  html:not(.nav-js) .site-nav__inner { flex-wrap: wrap; }
  html:not(.nav-js) .site-nav__links { order: 1; flex-basis: 100%; }
}

/* Narrow phones. The nav row is brand + toggle + "Start free" + hamburger, and
   .btn is white-space: nowrap, so the row cannot shrink below the sum of its
   parts: at 320px it overflowed by 9px. Tighten the gutter and the button
   padding rather than letting the label wrap or shrinking the 44px targets. */
@media (max-width: 25em) {
  /* The row is sized by width, not padding, so widen the box itself. */
  .site-nav__inner { width: min(100% - 1.5rem, var(--content-max)); gap: 0.5rem; }
  .site-nav__actions { gap: 0.4rem; }
  .site-nav__actions .btn { padding-inline: 0.85rem; }
}

/* Very large browser default text on a phone. 390px at a 32px default (the 200%
   setting) is 12.2em; 320px at the 16px default is 20em and 390px is 24.4em, so
   nothing in this block fires at any normal text size - it exists only for SC
   1.4.4. Both symptoms come from `.btn { white-space: nowrap }`:

   - The nav row is brand + theme toggle + "Start free" + hamburger. "Start free"
     measured 211px of the 342px available and pushed the hamburger to x 443-487
     in a 390px viewport - off screen, and it is the ONLY route to the five nav
     links. The button is repeated in the hero directly below and in the footer,
     so it drops, exactly as "Sign in" does at 71.875em. Dropping the label the
     visitor can reach two other ways beats hiding the control they cannot.

   - A nowrap label inside a grid track is a hard floor on that track's width:
     "How the agent works" measured 452px inside a 310px .wrap and carried the
     whole document 102px wide with it. Content buttons may wrap here; the nav's
     may not, which is why this is scoped to main - that nowrap is what stops the
     nav row growing to two lines. */
@media (max-width: 18em) {
  .site-nav__actions .btn--primary { display: none; }
  main .btn { white-space: normal; }
}

/* ---------- Footer ---------- */
.site-footer { background: var(--bg); border-top: 1px solid var(--border); }
.site-footer__inner {
  width: min(100% - var(--gutter) * 2, var(--content-max)); margin-inline: auto;
  padding-block: clamp(2.5rem, 6vw, 4rem);
  display: grid; gap: 2.5rem; grid-template-columns: minmax(0, 2fr) repeat(2, minmax(0, 1fr));
}
@media (max-width: 720px) { .site-footer__inner { grid-template-columns: 1fr; } }
.site-footer__mark { font-family: var(--font-display); font-weight: 700; font-size: var(--step-1); }
.site-footer__expansion { color: var(--muted); font-size: var(--step--1); letter-spacing: 0.04em; margin: 0.4rem 0; }
.accent-cap { color: var(--accent); font-weight: 700; }
.site-footer__parent { color: var(--muted); font-size: var(--step--1); }
.site-footer__col h2 { font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
.site-footer__col a { display: flex; align-items: center; min-height: 44px; color: var(--text); text-decoration: none; }
.site-footer__col a:hover { color: var(--accent); }
.site-footer__base {
  border-top: 1px solid var(--border); padding: 1.25rem var(--gutter);
  text-align: center; color: var(--muted); font-size: var(--step--1);
}
.site-footer__base p { margin: 0; }
