/* ============================================================
   Base — reset, typography, page chrome, shared primitives
   Named classes only, flat specificity: this converts to
   native Webflow elements. No :has(), no nesting, no @layer.
   ============================================================ */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--black);
  color: var(--paper);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* height: auto is REQUIRED now that every <img> carries width/height
   attributes: max-width alone would constrain the width while the attribute
   held the height, squashing the picture. */
img, video { max-width: 100%; height: auto; display: block; }
/* White on the crimson fill measures 6.00; paper would be 4.48. */
::selection { background: var(--red); color: #ffffff; }

:focus-visible {
  outline: 2px solid var(--brass);
  outline-offset: 3px;
}

/* Keyboard users must be able to skip the cinematic hero.
   Clipped rather than pushed to -9999px: an off-canvas element can itself
   create horizontal scroll, and clip-path avoids that entirely. */
.skip-link {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2000;
  padding: 12px 20px;
  background: var(--paper);
  color: var(--black);
  font-family: var(--mono);
  font-size: 12px;
  text-decoration: none;
  clip-path: inset(50%);
  width: 1px;
  height: 1px;
  overflow: hidden;
  white-space: nowrap;
}
.skip-link:focus {
  clip-path: none;
  width: auto;
  height: auto;
  overflow: visible;
}

/* ---- Headings: serif italic display is the brand's voice ---- */
h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.02em;
  margin: 0;
}
h1 { font-size: var(--display); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); }

.display-italic { font-style: italic; }
/* Safe at any size: --red-text clears AA (4.5) on both dark grounds. Use
   this unless you have checked the size and the ground. */
.accent { color: var(--red-text); }

/* Opt-in, for the brand crimson at DISPLAY size. WCAG relaxes to 3.0 for
   large text (24px+, or 18.66px+ bold), and crimson measures 3.32 on the
   black ground — so it is compliant there and reads far punchier than the
   lighter tint.
   TWO CONDITIONS, both required:
     1. the text is genuinely 24px+ (or 18.66px+ bold)
     2. it sits on --black, NOT on --ink navy, where crimson is 2.94 and
        fails even the large-text threshold
   If either is uncertain, use .accent instead. */
.accent-display { color: var(--red); }
.brass  { color: var(--brass); }

p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

a { color: var(--paper); text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--red-text); }

/* ---- Eyebrow: mono, wide-tracked, hairline rules ---- */
.eyebrow {
  font-family: var(--mono);
  font-size: var(--eyebrow-size);
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  color: var(--paper-mute);
  display: inline-flex;
  align-items: center;
  gap: 16px;
  margin: 0;
}
.eyebrow-bar {
  display: block;
  width: 32px;
  height: 1px;
  background: currentColor;
  flex: none;
}
.eyebrow-accent { color: var(--red-text); }

/* ---- Lede: the sentence under a display heading ---- */
.lede {
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--paper-dim);
  max-width: 62ch;
}

/* ---- Layout ---- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.wrap-text {
  width: 100%;
  max-width: var(--wrap-text);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.section {
  position: relative;
  padding: var(--section-pad) 0;
}
.section-hairline { border-top: 1px solid var(--line); }

/* ---- Film chrome: grain + vignette. Purely decorative. ---- */
/* The grain is a light speckle over dark. On a light ground it reads as
   dirt, so the theme turns it down rather than off — enough tooth to keep
   the surface from feeling flat. */
.film-grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1000;
  opacity: calc(var(--grain-opacity, 0.08) * var(--motion-parallax, 1));
  /* base64, not `;utf8,`: in an unencoded data URI the browser
     percent-decodes the URL before the SVG is parsed, so the filter
     reference url(%23n) never resolved internally — the browser went
     looking for a file literally named "%23n", 404ing on every page
     load, and the noise silently did not render. Encoding removes it. */
  background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMjAnIGhlaWdodD0nMjIwJz48ZmlsdGVyIGlkPSduJz48ZmVUdXJidWxlbmNlIHR5cGU9J2ZyYWN0YWxOb2lzZScgYmFzZUZyZXF1ZW5jeT0nMC45NScgbnVtT2N0YXZlcz0nMicgc3RpdGNoVGlsZXM9J3N0aXRjaCcvPjxmZUNvbG9yTWF0cml4IHZhbHVlcz0nMCAwIDAgMCAwLjk1IDAgMCAwIDAgMC45MyAwIDAgMCAwIDAuODUgMCAwIDAgMSAwJy8+PC9maWx0ZXI+PHJlY3Qgd2lkdGg9JzIyMCcgaGVpZ2h0PScyMjAnIGZpbHRlcj0ndXJsKCNuKScvPjwvc3ZnPg==");
}
.vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 999;
  opacity: var(--motion-parallax, 1);
  background: radial-gradient(140% 80% at 50% 50%, transparent 35%, rgba(var(--scrim-rgb), 0.45) 80%, rgba(var(--scrim-rgb), 0.75) 100%);
}
.scroll-progress {
  position: fixed;
  top: 0; left: 0;
  height: 1px;
  width: 0;
  background: var(--paper);
  opacity: 0.6;
  z-index: 1001;
}

/* ---- Reveal on scroll. No-JS safe: see .no-js rule below. ---- */
.reveal {
  opacity: 0;
  transform: translateY(var(--reveal-shift));
  transition: opacity var(--reveal-dur) var(--ease-cine),
              transform var(--reveal-dur) var(--ease-cine);
}
.reveal.is-in { opacity: 1; transform: translateY(0); }
.reveal-d1 { transition-delay: 0.12s; }
.reveal-d2 { transition-delay: 0.24s; }
.reveal-d3 { transition-delay: 0.36s; }
.reveal-d4 { transition-delay: 0.48s; }

/* If JS never runs, content must still be visible. */
.no-js .reveal { opacity: 1; transform: none; }

/* ---- Spacing utilities ----
   Named classes rather than inline styles, because inline styles do not
   survive the Webflow conversion — only classes become editable Webflow
   styles. Values follow the section rhythm, and scale with --density so the
   utility tier stays proportionally tighter. */
.stack-xs { margin-top: calc(20px * var(--density)); }
.stack-sm { margin-top: calc(24px * var(--density)); }
.stack-md { margin-top: calc(40px * var(--density)); }
.stack-lg { margin-top: calc(56px * var(--density)); }
.stack-xl { margin-top: calc(80px * var(--density)); }

.text-left   { text-align: left; }
.text-center { text-align: center; }

/* A lede that should not be centred by its parent section. */
.lede-left {
  text-align: left;
  margin-left: 0;
  margin-right: 0;
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 16px 30px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid var(--line-strong);
  color: var(--paper);
  background: transparent;
  transition: background 0.4s var(--ease-cine), border-color 0.4s var(--ease-cine), color 0.4s var(--ease-cine);
}
.btn:hover {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--black);
}
.btn-primary {
  background: var(--red);
  border-color: var(--red);
  /* Not a paper token: the fill does not change between themes, so neither
     may its text. See --on-accent in tokens.css. */
  color: var(--on-accent);
}
.btn-primary:hover {
  background: var(--paper);
  border-color: var(--paper);
  color: var(--black);
}

/* ---- Top navigation ---- */
.topnav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 998;
  padding: 20px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  background: linear-gradient(to bottom, rgba(var(--scrim-rgb), 0.92), rgba(var(--scrim-rgb), 0));
}
.topnav-brand { display: block; flex: none; }

/* ---- Wordmark ----
   Rendered as a masked block rather than an <img>, because an <img>-embedded
   SVG cannot inherit the page's colour — its currentColor resolves against the
   SVG's own root. The mask takes the shape from the same single file and the
   colour from CSS, so one asset serves both themes. */
.wordmark {
  display: block;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: left center;
  mask-position: left center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.wordmark-wide {
  -webkit-mask-image: url("../assets/wordmark-wide.svg");
  mask-image: url("../assets/wordmark-wide.svg");
  aspect-ratio: 982 / 70;
}
.wordmark-stacked {
  -webkit-mask-image: url("../assets/wordmark-stacked.svg");
  mask-image: url("../assets/wordmark-stacked.svg");
  aspect-ratio: 510 / 169;
}
.wordmark-center {
  -webkit-mask-position: center;
  mask-position: center;
}
/* The wide lockup is very wide for its height — cap it so it cannot
   outgrow a narrow viewport. */
/* Width-driven so aspect-ratio governs the height. A fixed height plus a
   max-width defeats the ratio: once the cap bites, the mask letterboxes inside
   a box that is still 26px tall and leaves dead space under the mark.
   object-fit is a leftover from when this was an <img>; a masked span ignores
   it. The lockup is ~14:1, so 365px wide is the 26px-tall equivalent. */
.topnav-logo {
  width: min(365px, 58vw);
  height: auto;
  opacity: 0.9;
}
.topnav-links {
  display: flex;
  align-items: center;
  gap: 28px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.topnav-links a {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--paper-dim);
  text-decoration: none;
  transition: color 0.3s ease;
}
.topnav-links a:hover,
.topnav-links a.is-current { color: var(--paper); }

/* The homepage hides nav over the hero, then fades it in. */
.topnav-cinematic {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.topnav-cinematic.is-shown {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* Available to screen readers, absent from the visual layout. Clipped rather
   than pushed off-canvas, which can create horizontal scroll. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The nav carries two copies of the toggle — one in the bar, one inside the
   mobile panel — because the bar has no room for it at small sizes. Exactly
   one is visible at a time. */
.topnav-links .themetoggle { display: none; }

/* ---- Floating contact ----
   A persistent shortcut to the enquiry form. The point is fewer clicks between
   arriving and getting in touch, so it stays put rather than living only at the
   foot of the page. Appears once the reader is past the masthead — before that
   the masthead's own CTA is right there and this would just be noise. */
.floatcta {
  position: fixed;
  right: clamp(16px, 3vw, 32px);
  bottom: clamp(16px, 3vw, 32px);
  z-index: 997;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 26px;
  border: 1px solid var(--red);
  border-radius: 999px;
  background: var(--red);
  color: var(--on-accent);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
  box-shadow: 0 12px 34px rgba(var(--scrim-rgb), 0.5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity 0.4s var(--ease-cine), transform 0.4s var(--ease-cine),
              visibility 0.4s, background 0.3s ease;
}
.floatcta.is-shown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.floatcta:hover { background: var(--red-deep); border-color: var(--red-deep); }
/* Hidden entirely once the form itself is on screen — offering a shortcut to
   something the reader is already looking at is just clutter. */
.floatcta.is-atdestination {
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
}
@media (max-width: 767px) {
  .floatcta { left: clamp(16px, 3vw, 32px); justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .floatcta { transition: opacity 0.01s, visibility 0.01s; transform: none; }
  .floatcta.is-shown { transform: none; }
}

/* ---- Theme toggle ----
   Two explicit states rather than a single ambiguous icon, so it is obvious
   which theme you are in and which you would get. */
/* HIDDEN 2026-09-03 at the client's request — they chose dark and did not want
   the choice surfaced. The light theme, theme.js and this component are all
   intact and working; only the control is hidden. Delete this one rule to bring
   it back. NOTE: strip theme.js, the light palette and this component before
   final handover if the decision holds. */
.themetoggle { display: none !important; }

.themetoggle {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: transparent;
}
.themetoggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--paper-mute);
  cursor: pointer;
  transition: background 0.25s ease, color 0.25s ease;
}
.themetoggle-btn:hover { color: var(--paper); }
/* aria-pressed carries the state, so the styling has no separate source of
   truth to drift from. */
.themetoggle-btn.is-active {
  background: var(--ink-2);
  color: var(--paper);
}
.themetoggle-btn svg { width: 15px; height: 15px; display: block; }

/* Colour changes are animated on the surfaces, not on everything: a blanket
   transition on * makes the whole page lurch and fights the reveal states. */
body, .card, .sitefooter, .cta-band, .topnav, .end-card, .title-card,
.field-input, .tabpanel, .faq-item, .ph {
  transition: background-color 0.35s ease, border-color 0.35s ease, color 0.35s ease;
}
@media (prefers-reduced-motion: reduce) {
  body, .card, .sitefooter, .cta-band, .topnav, .end-card, .title-card,
  .field-input, .tabpanel, .faq-item, .ph { transition: none; }
}

/* ---- Mobile nav toggle ----
   Hidden on desktop. Below 767px the link list becomes a panel and this
   button reveals it: the logo plus four links cannot fit a 375px bar. */
.navtoggle {
  display: none;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--paper);
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
.navtoggle-bars {
  display: block;
  position: relative;
  width: 18px;
  height: 1px;
}
/* Three real spans rather than one span plus ::before/::after. Webflow's HTML
   converter rejects pseudo-elements outright — "StyleParser drops ::
   pseudo-elements. Add a real child element and style that class instead." */
.navtoggle-bar {
  position: absolute;
  left: 0;
  width: 18px;
  height: 1px;
  background: currentColor;
  transition: transform 0.3s var(--ease-cine), opacity 0.3s var(--ease-cine);
}
.navtoggle-bar-top { top: -6px; }
.navtoggle-bar-mid { top: 0; }
.navtoggle-bar-bot { top: 6px; }
/* Collapsed to an X while the panel is open. The state class sits on each BAR
   rather than on the button, because Webflow's converter takes single and
   combo class selectors only — a state on an ancestor is a descendant
   selector, which it drops. */
.navtoggle-bar-mid.is-open { opacity: 0; }
.navtoggle-bar-top.is-open { transform: translateY(6px) rotate(45deg); }
.navtoggle-bar-bot.is-open { transform: translateY(-6px) rotate(-45deg); }

/* ---- Footer ---- */
.sitefooter {
  border-top: 1px solid var(--line);
  padding: 72px 0 48px;
  background: var(--black);
}
.sitefooter-grid {
  display: grid;
  /* Two columns since the State Partners list came out (Billy, 2026-10-09):
     the pages stay live for customers, just not advertised. */
  grid-template-columns: 2fr 1fr;
  gap: 48px;
  align-items: start;
}
/* Grid items default to min-width:auto, so an item refuses to shrink below its
   widest content and overflows its own track instead. That is what made a
   477px wordmark drag the whole document to 501px on a 375px screen — and
   because the page was then wider than the viewport, it pushed the mobile nav
   button off the right edge too. One cause, two symptoms. */
.sitefooter-grid > div { min-width: 0; }
/* Footer column labels are NOT headings — they'd pollute the document
   outline alongside real content headings. Use <p class="sitefooter-label">. */
.sitefooter-label {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: var(--eyebrow-track);
  text-transform: uppercase;
  color: var(--paper-mute);
  margin-bottom: 20px;
}
.sitefooter ul { list-style: none; margin: 0; padding: 0; }
.sitefooter li { margin-bottom: 12px; }
.sitefooter a {
  font-size: var(--step--1);
  color: var(--paper-dim);
  text-decoration: none;
}
.sitefooter a:hover { color: var(--paper); }
/* A masked span has no intrinsic size, so width comes from the aspect ratio. */
.sitefooter-logo {
  width: min(477px, 100%);
  height: auto;
  margin-bottom: 20px;
  opacity: 0.8;
}
.sitefooter-note {
  margin-top: 56px;
  padding-top: 24px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-mute);
  display: flex;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

/* ---- Responsive ---- */
@media (max-width: 991px) {
  .sitefooter-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 767px) {
  .section { padding: calc(var(--section-pad) * 0.72) 0; }
  .navtoggle { display: flex; }
  /* The bar is already carrying a wide lockup and a menu button. */
  .topnav > .themetoggle { display: none; }
  .topnav-links .themetoggle { display: inline-flex; margin-top: 18px; }
  .topnav { background: var(--black); border-bottom: 1px solid var(--line); }

  /* Off-canvas panel. The closed state is inert to pointer AND keyboard —
     visibility: hidden is what keeps the links out of the tab order. */
  .topnav-links {
    position: fixed;
    top: 76px;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 8px var(--gutter) 28px;
    background: var(--black);
    border-bottom: 1px solid var(--line);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s;
  }
  .topnav-links.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .topnav-links li { border-bottom: 1px solid var(--line); }
  .topnav-links a {
    display: block;
    padding: 18px 0;
    font-size: 11px;
    letter-spacing: 0.22em;
  }

  /* The cinematic nav must not stay hidden on mobile: there is no
     hover-to-reveal on touch and the toggle has to be reachable. */
  .topnav-cinematic {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
  .sitefooter-grid { grid-template-columns: 1fr; gap: 36px; }
}

/* ---- Login menu ------------------------------------------------------------
   A <details> styled to read as a nav link, with its list dropping below. */
.navmenu-item { position: relative; }
.navmenu-trigger {
  list-style: none;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--paper-dim);
  transition: color 0.3s ease;
}
.navmenu-trigger::-webkit-details-marker { display: none; }
.navmenu-trigger::after {           /* a small chevron, drawn, not an icon file */
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  margin-left: 8px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: translateY(-3px) rotate(45deg);
  transition: transform 0.2s ease;
}
.navmenu[open] .navmenu-trigger::after { transform: translateY(0) rotate(-135deg); }
.navmenu-trigger:hover,
.navmenu[open] .navmenu-trigger { color: var(--paper); }
.navmenu-trigger:focus-visible { outline: 2px solid var(--brass); outline-offset: 4px; }

.navmenu-list {
  position: absolute;
  top: calc(100% + 14px);
  right: 0;
  min-width: 210px;
  margin: 0;
  padding: 8px 0;
  list-style: none;
  background: var(--black);
  border: 1px solid var(--line-strong);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
  z-index: 20;
}
.navmenu-list a {
  display: block;
  padding: 10px 18px;
  white-space: nowrap;
}
.navmenu-list a:hover { background: rgba(232, 222, 197, 0.06); }

/* In the phone menu the list opens inline, under its label, rather than as a
   floating panel that would sit on top of the links below it. */
@media (max-width: 767px) {
  /* The summary must match the phone menu's links — 18px rows, 11px type — or
     Login sits cramped between Support and Contact. */
  .navmenu-trigger {
    display: block;
    padding: 18px 0;
    font-size: 11px;
    letter-spacing: 0.22em;
  }
  .navmenu-list {
    position: static;
    min-width: 0;
    padding: 0 0 10px 14px;
    background: none;
    border: 0;
    box-shadow: none;
  }
  /* The two logins are sub-items: no dividers of their own, tighter rows. */
  .navmenu-list li { border-bottom: 0; }
  .navmenu-list a { padding: 12px 0; }
}
