/* ==========================================================================
   Quorim visual system
   Typography and color direction adapted from the approved landing reference.
   ========================================================================== */

@font-face {
  font-family: "Metropolis";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/metropolis-400.woff2") format("woff2");
}
@font-face {
  font-family: "Metropolis";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/metropolis-500.woff2") format("woff2");
}
@font-face {
  font-family: "Metropolis";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/metropolis-600.woff2") format("woff2");
}
@font-face {
  font-family: "Metropolis";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/metropolis-700.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/36cc7328_file.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/e1f9f046_file.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/85fb821f_file.woff2") format("woff2");
}

:root {
  /* ------------------------------------------------------------------
     Color

     Orange is the accent: interactive by default (buttons, links, focus,
     selection, form-field hover/focus). Non-click brand accent is a closed
     list — two-tone heading spans, team H1, contact card H1 — documented
     in style.html. Not for roles, partner frames, legal titles, or errors.
     ------------------------------------------------------------------ */
  --q-orange: #d2764b;
  /* Single interactive hover step. All orange controls lift to this. */
  --q-orange-hover: #eac0ac;
  /* Alias kept for existing call sites; prefer --q-orange-hover. */
  --q-orange-light: var(--q-orange-hover);
  --q-orange-tint: rgba(210, 118, 75, 0.1);
  --q-orange-tint-hover: rgba(210, 118, 75, 0.22);
  --q-black: #000000;

  /* Marketing dark field — deep indigo-navy, hue ~235°.
     The whole site sits in this range: ink is the ambient floor the page
     never drops below (true black is reserved for scrims and ::selection
     text), deep is the long shoulder, mid is the crest. */
  --q-ink: #090a18;
  --q-navy-deep: #0d0f20;
  --q-navy-mid: #141436;
  /* Luminous center of the well swell — the brightest the field ever gets.
     Background light only, never a surface or text color. */
  --q-navy-bloom: #1c1e52;
  --q-blue-soft: #7fa3cc;

  /* Page chrome. Header and footer both wear the veil: ink glass —
     translucent enough for the field to glow through the blur, so each
     bar reads as a surface of the page, not a slab on it. Opaque chrome
     is reserved for mobile header / full-screen menu ground. */
  --q-chrome: #05060f;
  --q-chrome-veil: rgba(9, 10, 24, 0.6);

  /* Card surface — one face for well, press, team, contact, consent. */
  --q-card-face: linear-gradient(180deg, rgba(20, 20, 54, 0.9) 0%, rgba(13, 15, 32, 0.95) 100%);
  --q-card-border: rgba(184, 203, 225, 0.18);
  --q-card-border-hover: rgba(184, 203, 225, 0.34);
  --q-white: #ffffff;
  --q-text: #f6f5f3;
  /* Long-form reading tone: recessed from white headings. */
  --q-text-soft: #c5d4e6;
  /* Marketing blurb tone: recessed reading light in the field's hue. */
  --q-text-blurb: #dee7f1;
  --q-muted: #97a7b9;
  /* Deepest chrome text (copyright, fine print). */
  --q-text-recessed: #6f7b88;
  --q-border: rgba(151, 167, 185, 0.22);
  --q-border-subtle: rgba(151, 167, 185, 0.13);

  /* Semantic — distinct from brand orange so error ≠ CTA. */
  --q-success: #9fd4b8;
  --q-danger: #e8a090;

  /* ------------------------------------------------------------------
     Type roles

     Metropolis: display / headings / brand, and marketing blurbs (.q-copy).
     Inter: long-form reading (articles, legal), form fields, UI chrome.
     ------------------------------------------------------------------ */
  --q-font-display: "Metropolis", "Inter", system-ui, sans-serif;
  --q-font-marketing: "Metropolis", "Inter", system-ui, sans-serif;
  --q-font-body: "Inter", system-ui, sans-serif;
  --q-font-ui: "Inter", system-ui, sans-serif;

  /* ------------------------------------------------------------------
     Type scale — modular scale on a 16px base.

     Editorial rungs are fluid over a SHARED 380px–1280px viewport range.
     Body and UI are fixed so inputs never shrink below 16px (iOS zoom).

     Exemptions: brand wordmark, hero headline (.q-display).
     ------------------------------------------------------------------ */
  --fs-title:   clamp(30px, 18.6px + 3vw, 57px);       /* page titles     */
  --fs-heading: clamp(27px, 18.13px + 2.333vw, 48px);  /* section heads   */
  --fs-card:    clamp(21px, 18.04px + 0.7778vw, 28px); /* card headlines  */
  --fs-card-sm: clamp(19px, 17.31px + 0.4444vw, 23px); /* names in cards  */
  --fs-lead:    clamp(18px, 17.2px + 0.45vw, 20px);    /* leads, longform */
  --fs-body:    16px;                                  /* body, inputs    */
  --fs-ui:      13px;                                  /* buttons, nav    */
  --fs-label:   11px;                                  /* eyebrows, tags  */

  --ls-display: -0.042em;
  --ls-title: -0.036em;
  --ls-heading: -0.028em;
  --ls-card: -0.02em;
  --ls-caps: 0.1em;
  --ls-caps-wide: 0.18em;

  /* ------------------------------------------------------------------
     Spacing scale — 4px base.

     Prefer the semantic aliases (--q-section-y, --q-stack-gap, …) over
     raw steps in component rules. Section Y is generous by design:
     institutional pages need air between blocks.
     ------------------------------------------------------------------ */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;
  --sp-9: 80px;
  --sp-10: 96px;
  --sp-11: 128px;
  --sp-12: 160px;

  /* Semantic spacing */
  --q-section-y: var(--sp-11);       /* primary section block padding */
  --q-section-y-sm: var(--sp-9);     /* same on small screens */
  --q-band-y: var(--sp-10);          /* page-title bands */
  --q-band-y-sm: var(--sp-8);
  --q-stack-gap: var(--sp-6);        /* vertical gap in centered stacks */
  --q-card-pad: var(--sp-6);
  --q-field-gap: 22px;               /* form field stack */

  --q-header-h: 76px;

  /* Radius scale */
  --q-radius: 6px;
  --q-radius-btn: 14px;
  --q-radius-btn-sm: 12px;
  --q-radius-card: 16px;

  /* ------------------------------------------------------------------
     Width map — one value per role, no one-off max-widths in markup.
     ------------------------------------------------------------------ */
  --q-container: 1360px;             /* reserved wide chrome; prefer --q-content */
  /* Shared column between side gutters — logo left edge → GIT button right. */
  --q-content: 1240px;
  --q-measure: 45rem;                /* long-form reading (~720px) */
  --q-form: 38.75rem;                /* contact card (~620px) */
  --q-card-narrow: 32.5rem;          /* press teaser (~520px) */

  /* Single horizontal gutter — header, footer, and content share this so
     logos and page blocks register on one left edge. */
  --q-gutter: 24px;
  /* Outer border-box width of the padded page column (content + gutters). */
  --q-page: calc(var(--q-content) + 2 * var(--q-gutter));
  /* Optical align with brand mark artwork inset */
  --q-mark-inset: 3px;

  /* Shared right-arrow, drawn as a mask so it inherits currentColor. */
  --q-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cpath d='M104.133 18.918l-9.431 8.947 61.913 65.269-137.498.938.089 13 138.096-.942-61.338 66.11 9.529 8.842 75.39-81.254-76.75-80.91z'/%3E%3C/svg%3E");

  --bs-body-font-family: var(--q-font-body);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Takes up the slack so the footer is pushed to the bottom of short pages
   and sits right under the content on long ones. */
main {
  flex: 1 0 auto;
}

html {
  scroll-behavior: smooth;
  background: var(--q-ink);
  /* Tells the browser to render native UI — caret, scrollbars, autofill
     heuristics, validation bubbles — for a dark surface. */
  color-scheme: dark;
}

/* Column flex + min-height is the sticky-footer pattern. Without it a short
   page (contact is barely one viewport) ends where its content ends, and
   the html background shows below the footer as a black band. The header is
   position:fixed so it takes no part in this; main does the growing. */
body {
  display: flex;
  min-height: 100svh;
  flex-direction: column;
  margin: 0;
  padding-top: var(--q-header-h);
  overflow-x: hidden;
  background: var(--q-ink);
  color: var(--q-text);
  font-family: var(--q-font-body);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ==========================================================================
   Page field

   One gradient for the whole page; the sections on top are transparent so
   there are no seams to disguise. The field is immersive: it never drops to
   true black — the page opens and closes on ink, the darkest ambient tone
   of the deep navy, so the brand hue is the constant medium the site swims
   in rather than a color the page visits. Same arc as before: ink at both
   ends, navy-deep shoulders, one navy-mid crest at 60%. Percentage stops
   scale the arc to each page's own height, so every page opens on ink under
   the header, breathes through the same navy heart, and settles back to ink
   before the glass footer — the same tonal shape whether the page is one
   viewport tall or six.
   ========================================================================== */
.q-page-home,
.q-page-contact,
.q-page-press,
.q-page-team,
.q-page-legal {
  background-color: var(--q-ink);
  background-image: linear-gradient(
    180deg,
    var(--q-ink) 0%,
    var(--q-navy-deep) 34%,
    var(--q-navy-mid) 60%,
    var(--q-navy-deep) 84%,
    var(--q-ink) 100%
  );
  background-repeat: no-repeat;
}

/* The home page takes only the arc's rising half, stretched over its full
   height — the experience the contact page gives in one screen (ink
   opening, slow climb, navy crest at the bottom), retold across six. The
   57% mid-stop keeps the same proportional shape as the shared curve's
   rise (34/60). No descent: the page ends on the crest; the glass footer
   lets that crest (and the horizon) show through rather than sealing it. */
.q-page-home {
  background-image: linear-gradient(
    180deg,
    var(--q-ink) 0%,
    var(--q-navy-deep) 57%,
    var(--q-navy-mid) 100%
  );
}

/* The horizon: the field's single source of light — the Auros globe
   technique in the site's cool navy. One vast circle sits below the
   viewport; its lit rim crosses the lower view as a gentle arc, with
   atmosphere rising from the rim and the body of the sphere deepening
   the ground beneath. One center, one light: focused, not scattered.
   It breathes slowly in place — the rim itself never moves.

   Stacking: the pseudo is z-index 0 — above the body's own background
   (in-flow backgrounds paint before positioned layers) — and main and the
   footer are lifted to z-index 1 so all content, cards, and glass chrome
   stay in front. A -1 here would paint the layer UNDER the body's opaque
   field and hide it entirely. Reduced motion freezes the breath into a
   still glow via the global block. */
.q-page-home::before,
.q-page-contact::before,
.q-page-press::before,
.q-page-team::before,
.q-page-legal::before,
.q-page-style::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  background: radial-gradient(
    circle at 50% 190%,
    rgba(9, 10, 24, 0.55) 0,
    rgba(9, 10, 24, 0.55) calc(110vh - 20px),
    rgba(127, 163, 204, 0.38) calc(110vh - 1px),
    rgba(28, 30, 82, 0.85) calc(110vh + 8px),
    rgba(28, 30, 82, 0.38) 128vh,
    rgba(28, 30, 82, 0) 152vh
  );
  animation: q-horizon 10s ease-in-out infinite alternate;
  pointer-events: none;
}

/* Content rides above the horizon. The header, mobile menu, and consent
   card are fixed with their own (higher) stacking; these two are the
   in-flow layers that need the lift. */
main,
.q-footer {
  position: relative;
  z-index: 1;
}

@keyframes q-horizon {
  from {
    opacity: 0.82;
  }

  to {
    opacity: 1;
  }
}

/* The hero is the one section that opts out of the field, so the sphere
   canvas sits directly on the field's ink opening (the additive canvas
   only ever adds its own light to it). The footer used to opt out here
   too, which is exactly why it rendered a different colour on every page
   — it now paints its own chrome instead. */
.q-page-home .q-hero {
  background: transparent;
}

::selection {
  background: var(--q-orange);
  color: var(--q-black);
}

a {
  transition:
    color 180ms ease,
    border-color 180ms ease,
    background-color 180ms ease,
    transform 180ms ease;
}

/* ==========================================================================
   Page chrome gutter

   Header, footer, and marketing columns share --q-page (content + gutters) so
   the brand mark lines up with wells, cards, and section stacks. Bootstrap's
   .container-fluid / .container-xl default paddings are overridden here and
   on the section containers below.
   ========================================================================== */
.q-header > .container-fluid,
.q-footer > .container-xl {
  width: min(100%, var(--q-page));
  max-width: none;
  margin-inline: auto;
  padding-inline: var(--q-gutter);
}

/* Header — ink glass. Saturate lets the warm field's color come through
   the blur rather than greying out, which is what makes glass over a
   tinted field read as premium instead of foggy. */
.q-header {
  min-height: var(--q-header-h);
  padding: 0;
  background: var(--q-chrome-veil);
  border-bottom: 1px solid var(--q-border-subtle);
  backdrop-filter: blur(16px) saturate(1.3);
}

/* Scroll-aware glass: over the hero the bar is pure field — fully
   transparent, no edge — and it condenses into ink glass across the first
   260px of scroll. Same zero-JS scroll-driven technique as the reveals;
   browsers without it keep the constant glass above. */
@supports (animation-timeline: scroll()) {
  .q-header {
    animation: q-header-glass linear both;
    animation-timeline: scroll();
    animation-range: 0 260px;
  }
}

@keyframes q-header-glass {
  from {
    background-color: rgba(9, 10, 24, 0);
    border-bottom-color: transparent;
    backdrop-filter: blur(0px) saturate(1);
  }

  to {
    background-color: rgba(9, 10, 24, 0.6);
    border-bottom-color: rgba(151, 167, 185, 0.13);
    backdrop-filter: blur(16px) saturate(1.3);
  }
}

.q-header > .container-fluid {
  min-height: var(--q-header-h);
}

/* Brand lockup — the official two-tone artwork (orange mark, white word)
   as one image, so the header and footer render the identical lockup on
   every page. Width follows the artwork's own proportions. */
.q-header .navbar-brand,
.q-footer .q-footer-logo {
  display: inline-flex;
  align-items: center;
  padding: 0;
  margin: 0;
  text-decoration: none;
}

.q-brand-lockup {
  display: block;
  width: auto;
  height: 30px;
}


.q-nav-pill {
  align-items: center;
  gap: 30px;
  padding: 0;
}

.q-header .nav-link {
  position: relative;
  padding: 28px 0 25px;
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

.q-header .nav-link::after {
  position: absolute;
  right: 0;
  bottom: 19px;
  left: 0;
  height: 1px;
  background: var(--q-orange);
  content: "";
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left;
  transition: opacity 180ms ease, transform 180ms ease;
}

.q-header .nav-link:hover,
.q-header .nav-link:focus-visible,
.q-header .nav-link.active {
  color: var(--q-white);
}

.q-header .nav-link:hover::after,
.q-header .nav-link:focus-visible::after,
.q-header .nav-link.active::after {
  opacity: 1;
  transform: scaleX(1);
}

/* The home page doesn't need to flag itself in its own nav, so the resting
   active rule under "Home" is suppressed there. Scoped to the body rather
   than the link so the four headers stay byte-identical. Hover and focus
   still draw it, otherwise the link would have no interactive feedback --
   .active already colours it white. */
.q-page-home .q-header .nav-link.active:not(:hover):not(:focus-visible)::after {
  opacity: 0;
  transform: scaleX(0);
}

.q-header .nav-item:last-child {
  margin-left: 14px;
}

/* The nav CTA is a button, so it follows the button language — not the
   underline treatment of its sibling links. */
.q-header .nav-item:last-child .nav-link {
  display: inline-flex;
  min-width: 174px;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 13px 22px;
  color: var(--q-orange);
  background: var(--q-orange-tint);
  border: 1.5px solid var(--q-orange);
  border-radius: var(--q-radius-btn-sm);
  text-align: center;
  transition:
    color 180ms ease,
    background-color 180ms ease,
    border-color 180ms ease;
}

/* Repurpose the underline pseudo-element as the arrow. */
.q-header .nav-item:last-child .nav-link::after {
  position: static;
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  background: currentColor;
  opacity: 1;
  transform: none;
  transition: transform 180ms ease;
  -webkit-mask: var(--q-arrow) center / contain no-repeat;
  mask: var(--q-arrow) center / contain no-repeat;
}

.q-header .nav-item:last-child .nav-link:hover,
.q-header .nav-item:last-child .nav-link:focus-visible {
  color: var(--q-orange-hover);
  background: var(--q-orange-tint-hover);
  border-color: var(--q-orange-hover);
}

.q-header .nav-item:last-child .nav-link:hover::after,
.q-header .nav-item:last-child .nav-link:focus-visible::after {
  transform: translateX(3px);
}

.q-header .navbar-toggler {
  padding: 10px;
  color: var(--q-white);
  border: 0;
}

.q-header .navbar-toggler svg {
  display: block;
  width: 26px;
  height: 20px;
}

.q-header .navbar-toggler:focus {
  box-shadow: 0 0 0 2px var(--q-orange);
}

.q-nav-close svg {
  display: block;
  width: 22px;
  height: 22px;
}

/* Footer — same ink glass as the header. Saturate lets the field's color
   come through the blur rather than greying out. The hairline is a soft
   edge on glass, not a hard chrome seam. */
.q-footer {
  padding: var(--sp-8) 0 var(--sp-7);
  color: var(--q-white);
  background: var(--q-chrome-veil);
  border-top: 1px solid var(--q-border-subtle);
  backdrop-filter: blur(16px) saturate(1.3);
}

/* Logo base, nav word bottoms, and LinkedIn mark share one horizontal
   line. Underlines hang below that line (absolute, outside the text box).
   The row uses align-items-end in markup; this keeps the intent in CSS
   if the utility is dropped. */
.q-footer > .container-xl > .d-flex {
  align-items: flex-end;
}

.q-footer .nav {
  gap: 8px;
  align-items: flex-end;
}

/* Matches the header: muted -> white, with the accent carried by a rule that
   wipes in rather than by recolouring the text. */
.q-footer .nav-link {
  /* The rule and the horizontal inset share one value, so the underline
     never drifts away from the word. Vertical: zero bottom padding so the
     text's lower edge is the box edge (aligned with the logo base); top
     padding only for hit area, growing upward. */
  --q-rule-inset: 14px;
  position: relative;
  padding: 8px var(--q-rule-inset) 0;
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

.q-footer .nav-link::after {
  position: absolute;
  right: var(--q-rule-inset);
  /* Below the shared logo/text baseline — not inside the text box. */
  bottom: -6px;
  left: var(--q-rule-inset);
  height: 1px;
  background: var(--q-orange);
  content: "";
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left;
  transition: opacity 180ms ease, transform 180ms ease;
}

.q-footer .nav-link:hover,
.q-footer .nav-link:focus-visible,
.q-footer .nav-link.active {
  color: var(--q-white);
}

.q-footer .nav-link:hover::after,
.q-footer .nav-link:focus-visible::after,
.q-footer .nav-link.active::after {
  opacity: 1;
  transform: scaleX(1);
}

.q-footer .q-linkedin {
  display: inline-flex;
  color: var(--q-muted);
  /* Row is flex-end so logo base + nav word bottoms share a line. The
     22px mark is taller than the 13px caps, so its center sits high of
     theirs; drop half the height delta so the centers match. */
  transform: translateY(calc((22px - var(--fs-ui)) / 2));
}

.q-footer .q-linkedin svg {
  width: 22px;
  height: 22px;
  fill: currentColor;
}

.q-footer .q-linkedin:hover,
.q-footer .q-linkedin:focus-visible {
  color: var(--q-white);
}

/* Close under the logo row — offset so the first glyph aligns with the
   mark's artwork, not with its bounding box. */
.q-footer .q-copyright {
  margin: var(--sp-3) 0 0;
  padding-left: var(--q-mark-inset);
  color: var(--q-text-recessed);
  font-family: var(--q-font-ui);
  font-size: var(--fs-label);
}

/* Legal links live inside the copyright line — same recessed tone, lifting
   to white on hover like everything else in the chrome. */
.q-footer .q-copyright a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--q-text-recessed) 45%, transparent);
  text-underline-offset: 3px;
}

.q-footer .q-copyright a:hover,
.q-footer .q-copyright a:focus-visible {
  color: var(--q-white);
  text-decoration-color: currentColor;
}

/* ==========================================================================
   Buttons and arrow links

   One interaction language across the site: a low-opacity accent fill that
   deepens on hover while the text lifts orange -> light orange. Nothing
   flips to a solid fill and nothing moves except the arrow — .q-btn--orbit
   (see "Orbit CTA" below) adds a one-shot border glow on top of this,
   without replacing hover behaviour.
   ========================================================================== */
.q-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-width: 210px;
  min-height: 48px;
  padding: 13px 26px;
  color: var(--q-orange);
  background: var(--q-orange-tint);
  border: 1.5px solid var(--q-orange);
  border-radius: var(--q-radius-btn);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--ls-caps);
  text-decoration: none;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    color 180ms ease,
    background-color 180ms ease,
    border-color 180ms ease;
}

.q-btn:hover,
.q-btn:focus-visible {
  color: var(--q-orange-hover);
  background: var(--q-orange-tint-hover);
  border-color: var(--q-orange-hover);
}

/* The arrow is generated, so every button carries one without markup. */
.q-arrow::after,
.q-btn::after {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  background: currentColor;
  content: "";
  transition: transform 180ms ease;
  -webkit-mask: var(--q-arrow) center / contain no-repeat;
  mask: var(--q-arrow) center / contain no-repeat;
}

.q-arrow:hover::after,
.q-arrow:focus-visible::after,
.q-btn:hover::after,
.q-btn:focus-visible::after {
  transform: translateX(3px);
}

/* Opt out where a button is an endpoint rather than a next step. */
.q-btn--noarrow::after {
  display: none;
}

/* The declining option: same size and shape as its sibling — one click
   either way — but in the chrome's neutral voice, because orange is the
   colour of the path we're inviting. */
.q-btn--quiet {
  color: var(--q-muted);
  background: transparent;
  border-color: var(--q-border);
}

.q-btn--quiet:hover,
.q-btn--quiet:focus-visible {
  color: var(--q-white);
  background: rgba(151, 167, 185, 0.08);
  border-color: var(--q-muted);
}

/* Shared type */
/* Rung 9 of the scale is 83px, and this deliberately does not use it.
   The hero is an absolutely-positioned composition: the headline sits at
   --q-sphere-copy-top and the CTA at --q-sphere-cta-offset, both pinned to
   the sphere. Growing the headline does not push the button down, it lands
   on top of it — and at 1440x900 the current 78px already clears the CTA by
   only 12px. So this stays at its tuned sizes and steps at the same
   breakpoints as the sphere geometry it belongs to.

   To put the hero on the scale, the sphere offsets have to be re-tuned
   alongside it. Deliberately not done here. */
.q-display {
  margin: 0;
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: 78px;
  font-weight: 500;
  line-height: 1.04;
  letter-spacing: var(--ls-display);
  text-wrap: balance;
}

/* Shared section display heading. A <span> on the second line carries the
   accent AND steps down to the card rung — a subtitle, not a second
   heading. Color alone never carried the hierarchy; size does. */
.q-h-section {
  margin: 0;
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-heading);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: var(--ls-heading);
  text-wrap: balance;
}

.q-h-section span {
  display: block;
  margin-top: var(--sp-2);
  color: var(--q-orange);
  font-size: var(--fs-card);
  line-height: 1.25;
  letter-spacing: var(--ls-card);
}

.q-sub {
  color: var(--q-orange);
  font-family: var(--q-font-ui);
  font-weight: 600;
}

/* Home hero */
.q-hero {
  --q-sphere-size: min(82svh, 900px);
  --q-sphere-top: -16px;
  --q-sphere-copy-offset: min(29.52svh, 324px);
  --q-sphere-copy-top: calc(-6px + var(--q-sphere-copy-offset));
  --q-sphere-cta-offset: min(54.94svh, 603px);
  position: relative;
  display: flex;
  height: calc(100svh - var(--q-header-h));
  min-height: 680px;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--q-ink);
  text-align: center;
}

/* Ambient bloom, painted *under* the canvas. The sphere composites with
   "lighter", so on the near-black ink it can only ever add its own light —
   this gives it something to spill into. Centred on the sphere, not the hero. */
.q-hero::before {
  position: absolute;
  top: calc(var(--q-sphere-top) + var(--q-sphere-size) / 2);
  left: 50%;
  width: calc(var(--q-sphere-size) * 1.15);
  height: calc(var(--q-sphere-size) * 1.15);
  background: radial-gradient(
    circle,
    rgba(210, 118, 75, 0.16) 0%,
    rgba(210, 118, 75, 0.06) 38%,
    rgba(210, 118, 75, 0) 68%
  );
  content: "";
  pointer-events: none;
  transform: translate(-50%, -50%);
}

/* Painted *over* the canvas, under the copy (which holds z-index 1).
   Layer 1 buys contrast where the type lands and nowhere else, so the
   sphere keeps its bright limb; layer 2 just grounds the section seam. */
.q-hero::after {
  position: absolute;
  inset: 0;
  /* Scrims are mixed from ink, not black, so the hero settles into the warm
     field instead of stepping cold. The bottom scrim dims the particles
     behind the CTA zone and then releases back to zero BEFORE the hero's
     edge — a scrim that is still dark where it terminates prints a hard
     line against the unscrimmed section below. */
  background:
    radial-gradient(
        ellipse 60% 58% at 50% 40%,
        rgba(9, 10, 24, 0.55) 0%,
        rgba(9, 10, 24, 0.3) 45%,
        rgba(9, 10, 24, 0) 100%
      )
      50% var(--q-sphere-copy-top) / min(1100px, 92vw) min(46svh, 420px) no-repeat,
    linear-gradient(
      to bottom,
      transparent 58%,
      rgba(9, 10, 24, 0.5) 84%,
      rgba(9, 10, 24, 0) 100%
    );
  content: "";
  pointer-events: none;
}

.q-hero .q-hero-sphere {
  position: absolute;
  top: var(--q-sphere-top);
  left: 50%;
  width: var(--q-sphere-size);
  height: var(--q-sphere-size);
  opacity: 0.9;
  pointer-events: none;
  transform: translateX(-50%);
}

.q-hero .q-hero-copy {
  position: absolute;
  top: var(--q-sphere-copy-top);
  left: 50%;
  z-index: 1;
  width: min(100%, var(--q-page));
  padding: 0 var(--q-gutter);
  transform: translateX(-50%);
}

/* Second contrast pass over the scrim — carries the type across the few
   bright particles the ellipse doesn't reach. */
.q-hero .q-display {
  text-shadow: 0 1px 16px rgba(0, 0, 0, 0.32);
}

/* Card rung — the shared subtitle size, two rungs under the section heads
   and far under the hero pin, so the support line reads as support.
   Tracking: not --ls-display (tight, for large mixed-case) and not full
   --ls-caps (0.1em, for small UI). Mid open tracking suits these caps. */
.q-hero .q-sub {
  margin: var(--sp-6) 0 0;
  font-family: var(--q-font-display);
  font-size: var(--fs-card);
  line-height: 1.3;
  letter-spacing: 0.05em;
  text-shadow: 0 1px 14px rgba(0, 0, 0, 0.35);
  text-transform: uppercase;
}

.q-hero .q-btn {
  position: absolute;
  top: var(--q-sphere-cta-offset);
  left: 50%;
  margin-top: 0;
  transform: translateX(-50%);
}

/* Home sections */
.q-section {
  position: relative;
  padding: var(--q-section-y) 0;
  background: transparent;
}

/* The diagram reads as part of the block above it, so that block drops its
   bottom padding and .graphic-wrap sets the whole gap. Doubled class beats
   the shorthand `padding` in the mobile block, which would otherwise put
   the padding back. */
.q-section.q-section--flush {
  padding-bottom: 0;
}

/* The section that follows the hero. The hero fills 100svh, but its
   composition is capped — copy offset tops out at 324px and the CTA at
   603px — so on viewports taller than ~1065px the leftover below the CTA
   grows 1:1 with the viewport. The margin claws back exactly that surplus
   and nothing else; on ordinary laptop heights it is 0 and the padding is
   the whole gap. */
.q-section.q-section--after-hero {
  margin-top: min(0px, 1065px - 100svh);
  padding-top: var(--q-section-y);
}

/* Content column — same outer page width and gutter as header/footer chrome. */
.q-section > .container-xl,
.q-press > .container-xl,
.q-team > .container-xl {
  max-width: var(--q-page);
  padding-inline: var(--q-gutter);
}

/* Marketing blurbs — Metropolis at lead size; weight, leading, and color match
   the approved landing #backing body (size stays on --fs-lead, not their 16px).
   Long-form articles/legal stay on Inter via .q-article-body. */
.q-copy {
  color: var(--q-text-blurb);
  font-family: var(--q-font-marketing);
  font-weight: 400;
  font-size: var(--fs-lead);
  line-height: 1.6;
}

/* Links sit in the same tone as the blurb, lift to white on hover — same as
   the reference backing links (not orange, not Bootstrap link-light). */
.q-copy a {
  color: inherit;
  text-decoration: none;
  transition: color 180ms ease;
}

.q-copy a:hover,
.q-copy a:focus-visible {
  color: var(--q-white);
}

/* Top pad is the only air between the Meet Quorim heading and the diagram
   (the section above is --flush). One rung tighter than section air. */
.graphic-wrap {
  max-width: var(--q-page);
  margin: 0 auto;
  padding: var(--sp-4) var(--q-gutter) var(--sp-6);
  background: transparent;
}

/* The diagram's closing couplet lives inside the wrap. The two lines are
   one thought, so the second sits close under the first. */
.graphic-wrap .q-copy {
  max-width: var(--q-measure);
  margin: var(--sp-6) auto 0;
}

.graphic-wrap .q-copy + .q-copy {
  margin-top: var(--sp-1);
}

/* The orbit system: the original diagram's identity — the Q mark at the
   core, octagon participants, blue-in / green-out flows — restaged
   as hairline celestial geometry. Octagons are half the old visual weight,
   packets carry the in/out story, and the core glyph anchors the brand.
   Width is capped below the content column: the object is a punctuation
   mark between subjects, not a wall. */
.graphic {
  display: block;
  width: min(100%, 920px);
  height: auto;
  margin-inline: auto;
  --quorim: #d2764b;
  --in: #7fa3cc;
  --out: #8fdcb4;
}

.graphic .oct {
  fill: #10182a;
  stroke-width: 1.3;
}

.graphic .node--in .oct {
  stroke: rgba(127, 163, 204, 0.8);
}

.graphic .node--out .oct {
  stroke: rgba(143, 220, 180, 0.75);
}

.graphic .icon {
  fill: none;
  stroke: #d6ecff;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Off-scale by design: drawn inside the diagram's own 1040x445 viewBox, so
   "12.5" is SVG user units that scale with the artwork — it is not a page
   size and must not track the page scale. */
.graphic .node-label {
  fill: rgba(234, 242, 251, 0.78);
  font-family: var(--q-font-ui);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-anchor: middle;
  text-transform: uppercase;
}

/* The ring is static by design: nothing travels it — it is structure, not
   a data path. Motion lives only where data moves (the packets) and at
   the core. */
.graphic .orbit {
  stroke: rgba(127, 163, 204, 0.26);
  stroke-width: 1;
}

.graphic .flow {
  fill: none;
  stroke: rgba(127, 163, 204, 0.18);
  stroke-width: 1;
}

/* ==========================================================================
   Well swell

   Not a panel: a swell of light in the page field itself. The radial bloom
   reaches full transparency before it meets the well's own box edges, so
   the stage has no outline anywhere — it gathers out of the gradient and
   releases back into it. Subjects on the home page are separated by
   breathing room and light, never by lines.
   ========================================================================== */
/* Centred content rhythm, shared by feature sections. */
.q-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--q-stack-gap);
  text-align: center;
}

/* Width-only: the voice comes from .q-copy alongside it. */
.q-section-copy {
  max-width: var(--q-measure);
  margin: 0;
}

/* The well is a dedicated stage, not a bordered card — and not a box: the
   bloom (rgba stops are --q-navy-mid's rgb) hits zero alpha by 80% of the
   ellipse, inside the box on every side, so no edge of the element ever
   prints. Radius and overflow clipping are gone because there is nothing
   left to clip. */
.q-well {
  padding: clamp(var(--sp-7), 6vw, var(--sp-9)) clamp(var(--sp-5), 5vw, var(--sp-8));
  /* Centered 80%/80% ellipse, zero alpha by 60%: every box edge sits at
     ≥62.5% of the gradient (50/80), so the glow dies before ALL four
     edges — top included — and the element never prints a line. An
     off-center or oversized ellipse leaves one edge tinted and draws a
     boundary the eye reads as a card frame. */
  background: radial-gradient(
    80% 80% at 50% 50%,
    var(--q-navy-bloom) 0%,
    rgba(20, 20, 54, 0.5) 28%,
    rgba(20, 20, 54, 0) 60%
  );
  border: 0;
}

/* Credential rail: the well's provenance, presented as one engraved strip
   rather than floating logo boxes — a single card-material surface, two
   cells split by a hairline, label above each mark. One object reads as
   structure; two boxes read as widgets. */
.q-cred-rail {
  display: flex;
  width: min(100%, 720px);
  margin-top: var(--sp-4);
  background: var(--q-card-face);
  border: 1px solid var(--q-card-border);
  border-radius: var(--q-radius-card);
}

.q-cred {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  text-decoration: none;
  transition: background-color 200ms ease;
}

.q-cred:first-child {
  border-radius: var(--q-radius-card) 0 0 var(--q-radius-card);
}

.q-cred:last-child {
  border-radius: 0 var(--q-radius-card) var(--q-radius-card) 0;
}

.q-cred:hover,
.q-cred:focus-visible {
  background: rgba(184, 203, 225, 0.06);
}

.q-cred-divider {
  width: 1px;
  margin: var(--sp-4) 0;
  background: var(--q-card-border);
}

.q-logo-label {
  color: var(--q-blue-soft);
  font-family: var(--q-font-ui);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--ls-caps-wide);
  text-indent: 0.18em;
  text-transform: uppercase;
  transition: color 200ms ease;
}

.q-cred:hover .q-logo-label,
.q-cred:focus-visible .q-logo-label {
  color: var(--q-white);
}

/* Heights are per-mark: the Tradeweb lockup carries a starburst above its
   wordmark, so matching pixel heights would leave its text looking smaller. */
.q-cred img {
  display: block;
  width: auto;
}

.q-cred--ialta img {
  height: 34px;
}

.q-cred--tradeweb img {
  height: 38px;
}

/* Standard button proportions — matching primary CTAs, not logo boxes. */
.q-well-cta {
  margin-top: var(--sp-3);
}

/* ==========================================================================
   Orbit CTA — one-shot border glow

   The whole border softens with a brief, even glow, then goes quiet — once,
   not a loop. Overlay ::before keeps the translucent tint and normal hover
   intact (fill / text / border / arrow still behave like a plain .q-btn).
   ========================================================================== */

.q-btn--orbit {
  position: relative;
}

/* Soft even band on the border footprint only (face punched out). Starts
   invisible; the keyframes bring it up and out once. */
.q-btn--orbit::before {
  position: absolute;
  inset: -2px;
  padding: calc(2px + 1.5px);
  border-radius: calc(var(--q-radius-btn) + 2px);
  background: var(--q-orange-hover);
  -webkit-mask:
    linear-gradient(#000, #000) content-box,
    linear-gradient(#000, #000);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#000, #000) content-box,
    linear-gradient(#000, #000);
  mask-composite: exclude;
  content: "";
  pointer-events: none;
  filter: blur(1.5px);
  opacity: 0;
  /* One play, then gone. Delay lets the section settle before the whisper. */
  animation: q-btn-orbit-glow 2.2s ease-in-out 0.5s 1 both;
}

/* If the CTA sits in a scroll-reveal container, don't spend the one-shot
   while it's still below the fold — start only after .is-in. Without JS
   (no .q-anim), the load-time animation above still runs. */
body.q-anim .reveal:not(.is-in) .q-btn--orbit::before,
body.q-anim .reveal-stagger:not(.is-in) .q-btn--orbit::before {
  animation: none;
  opacity: 0;
}

body.q-anim .reveal.is-in .q-btn--orbit::before,
body.q-anim .reveal-stagger.is-in .q-btn--orbit::before {
  animation: q-btn-orbit-glow 2.2s ease-in-out 0.35s 1 both;
}

/* Rise to a soft full-border glow, ease slightly outward, fade away. Peak
   opacity is intentionally low — a hint, not a spotlight. */
@keyframes q-btn-orbit-glow {
  0% {
    opacity: 0;
    inset: -2px;
    padding: calc(2px + 1.5px);
    border-radius: calc(var(--q-radius-btn) + 2px);
    filter: blur(1.5px);
  }

  40% {
    opacity: 0.38;
    inset: -4px;
    padding: calc(4px + 1.5px);
    border-radius: calc(var(--q-radius-btn) + 4px);
    filter: blur(2px);
  }

  100% {
    opacity: 0;
    inset: -5px;
    padding: calc(5px + 1.5px);
    border-radius: calc(var(--q-radius-btn) + 5px);
    filter: blur(2.5px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .q-btn--orbit::before {
    animation: none !important;
    opacity: 0;
  }
}

/* Press teaser — sits closer under the veterans well than a full section gap. */
.q-section:has(+ .q-press) {
  padding-bottom: var(--sp-8);
}

.q-press {
  padding: var(--sp-8) 0 var(--q-section-y);
  background: transparent;
}

/* Eyebrow is a label, never a heading. Left-aligned with the well above
   (same content column). Lead size for presence. */
.q-press .q-eyebrow {
  margin: 0 0 var(--sp-6);
  color: var(--q-blue-soft);
  font-family: var(--q-font-ui);
  font-size: var(--fs-lead);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--ls-caps);
  text-align: left;
  text-transform: uppercase;
}

/* The press rail: an auto-fill grid so the wire card sits left with open
   track beside it — the layout itself says more is coming. New releases
   are added as more .q-press-card cells; nothing else changes. */
.q-press-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(380px, 100%), 1fr));
  gap: var(--sp-5);
}

/* The wire card: the announcement grammar of a market newswire — chip,
   date, statement, one "go" affordance. It is the page's featured drop, so
   it earns the elevated treatment: a brighter bloom-family face, a real
   shadow, and a gradient hairline frame running orange into steel. The
   whole card is the link, so the accent frame IS the action's edge —
   consistent with "orange is interactive", not an exception to it. On
   hover only light changes (shadow, chip, disc) and only the arrow moves. */
.q-press-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  padding: var(--q-card-pad);
  background:
    linear-gradient(180deg, rgba(28, 30, 82, 0.92) 0%, rgba(13, 15, 32, 0.96) 100%) padding-box,
    linear-gradient(135deg, rgba(210, 118, 75, 0.6) 0%, rgba(127, 163, 204, 0.35) 45%, rgba(184, 203, 225, 0.2) 100%) border-box;
  border: 1px solid transparent;
  border-radius: var(--q-radius-card);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
  text-decoration: none;
  transition: box-shadow 200ms ease;
}

.q-press-card:hover,
.q-press-card:focus-visible {
  box-shadow:
    0 18px 56px rgba(0, 0, 0, 0.5),
    0 4px 28px rgba(210, 118, 75, 0.16);
}

.q-press-card .q-press-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin: 0;
}

.q-press-card .q-press-chip {
  padding: 5px 10px;
  color: var(--q-orange);
  background: var(--q-orange-tint);
  border: 1px solid rgba(210, 118, 75, 0.45);
  border-radius: var(--q-radius);
  font-family: var(--q-font-ui);
  font-size: var(--fs-label);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  transition: color 180ms ease, background-color 180ms ease, border-color 180ms ease;
}

.q-press-card:hover .q-press-chip,
.q-press-card:focus-visible .q-press-chip {
  color: var(--q-orange-hover);
  background: var(--q-orange-tint-hover);
  border-color: var(--q-orange-hover);
}

.q-press-card .q-press-date {
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

.q-press-card h2,
.q-press-card h3 {
  margin: 0;
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-card-sm);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--ls-card);
}

/* Footer row: the action's words on the left, the go disc on the right. */
.q-press-card .q-press-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: auto;
  padding-top: var(--sp-2);
}

.q-press-card .q-press-cta {
  color: var(--q-orange);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  font-weight: 600;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  transition: color 180ms ease;
}

.q-press-card:hover .q-press-cta,
.q-press-card:focus-visible .q-press-cta {
  color: var(--q-orange-hover);
}

/* The go disc: the arrow at button proportions, marking the whole card as
   one action. Only the arrow moves on hover — the shared 3px nudge. */
.q-press-card .q-press-go {
  display: flex;
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  color: var(--q-orange);
  background: var(--q-orange-tint);
  border: 1.5px solid var(--q-orange);
  border-radius: 50%;
  transition: color 180ms ease, background-color 180ms ease, border-color 180ms ease;
}

.q-press-card .q-press-go::after {
  width: 17px;
  height: 17px;
  background: currentColor;
  content: "";
  transition: transform 180ms ease;
  -webkit-mask: var(--q-arrow) center / contain no-repeat;
  mask: var(--q-arrow) center / contain no-repeat;
}

.q-press-card:hover .q-press-go,
.q-press-card:focus-visible .q-press-go {
  color: var(--q-orange-hover);
  background: var(--q-orange-tint-hover);
  border-color: var(--q-orange-hover);
}

.q-press-card:hover .q-press-go::after,
.q-press-card:focus-visible .q-press-go::after {
  transform: translateX(3px);
}

/* Page title bands — one protocol: white display type, sentence case. */
.q-band {
  padding: var(--q-band-y) var(--q-gutter) var(--sp-9);
  background: transparent;
  text-align: center;
}

.q-band h1 {
  margin: 0;
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-title);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: var(--ls-title);
}

/* Team page title only — keep size on --fs-title; accent with brand orange. */
.q-page-team .q-band h1 {
  color: var(--q-orange);
}

.q-team {
  padding: 0 0 var(--sp-8);
  background: transparent;
}

.q-team .row {
  --bs-gutter-x: var(--sp-5);
  --bs-gutter-y: var(--sp-5);
}

.q-team-card {
  display: flex;
  height: 100%;
  min-height: 236px;
  gap: var(--sp-6);
  align-items: center;
  padding: var(--sp-3);
  background: var(--q-card-face);
  border: 1px solid var(--q-card-border);
  border-radius: var(--q-radius-card);
  transition: border-color 200ms ease;
}

/* Card is not a link — only the LinkedIn mark is. Border lift only. */
.q-team-card:hover,
.q-team-card:focus-within {
  border-color: var(--q-card-border-hover);
}

.q-team-card img {
  width: 210px;
  height: 210px;
  flex: 0 0 auto;
  object-fit: cover;
  aspect-ratio: 1;
  border-radius: 3px;
  filter: saturate(0.9) contrast(1.03);
}

.q-team-card .q-team-info {
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
}

.q-team-card h2 {
  margin: 0 0 var(--sp-2);
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-card-sm);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: var(--ls-card);
}

/* Roles are metadata, not actions — muted, not accent. */
.q-team-card p {
  margin: 0 0 var(--sp-5);
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  line-height: 1.5;
}

.q-team-card .q-linkedin {
  display: inline-flex;
  color: var(--q-muted);
}

.q-team-card .q-linkedin svg {
  width: 19px;
  height: 19px;
  fill: currentColor;
}

.q-team-card .q-linkedin:hover,
.q-team-card .q-linkedin:focus-visible {
  color: var(--q-orange-hover);
}

.q-team-cta {
  padding: 0 var(--sp-4) var(--q-section-y);
  background: transparent;
  text-align: center;
}

/* Contact */
.q-contact {
  position: relative;
  min-height: 720px;
  padding: var(--q-band-y) var(--q-gutter) var(--sp-12);
  background: transparent;
}

.q-contact-card {
  position: relative;
  max-width: var(--q-form);
  margin: 0 auto;
  padding: var(--sp-7) var(--sp-7);
  background: var(--q-card-face);
  border: 1px solid var(--q-card-border);
  border-radius: var(--q-radius-card);
}

/* Contact card title: card-headline size, brand orange (interactive accent). */
.q-contact-card h1 {
  margin: 0 0 var(--sp-6);
  color: var(--q-orange);
  font-family: var(--q-font-display);
  font-size: var(--fs-card);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: var(--ls-card);
}

.q-contact-card .form-label {
  margin-bottom: 8px;
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

/* Fields sit in the same material as the card: a slight cool lift for the
   surface, with the boundary carried by the border rather than by a white
   fill. Both the border and the focus ring clear 3:1 against the card and
   against the field interior (WCAG 1.4.11). */
.q-contact-card .form-control {
  min-height: 48px;
  padding: 10px 13px;
  color: var(--q-white);
  background: rgba(184, 203, 225, 0.05);
  border: 1px solid rgba(184, 203, 225, 0.47);
  border-radius: var(--q-radius);
  font-family: var(--q-font-body);
  font-size: var(--fs-body);
  transition:
    color 180ms ease,
    background-color 180ms ease,
    border-color 180ms ease,
    box-shadow 180ms ease;
}

.q-contact-card .form-control::placeholder {
  color: var(--q-muted);
  opacity: 1;
}

.q-contact-card .form-control:hover {
  background: rgba(184, 203, 225, 0.08);
  border-color: var(--q-orange);
}

/* The old ring was rgba(orange, .22) with 0 blur and 3px spread, so it
   painted on the card, not the field — 1.34:1, effectively invisible. The
   solid border is what carries the indicator now; the glow is decoration. */
.q-contact-card .form-control:focus {
  color: var(--q-white);
  background: rgba(184, 203, 225, 0.08);
  border-color: var(--q-orange);
  box-shadow: 0 0 0 3px rgba(210, 118, 75, 0.3);
}

/* Browsers force their own fill on autofilled inputs and ignore
   background-color; a large inset shadow is the only reliable override. */
.q-contact-card .form-control:-webkit-autofill,
.q-contact-card .form-control:-webkit-autofill:hover,
.q-contact-card .form-control:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--q-white);
  -webkit-box-shadow: 0 0 0 1000px #191b32 inset;
  caret-color: var(--q-white);
}

.q-contact-card textarea.form-control {
  min-height: 112px;
  resize: vertical;
}

.q-contact-card .mb-field {
  margin-bottom: var(--q-field-gap);
}

/* Honeypot: off-screen, not display:none (some bots skip display:none fields). */
.q-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.q-turnstile {
  min-height: 65px;
}

.q-form-status {
  margin: var(--sp-4) 0 0;
  min-height: 1.4em;
  font-family: var(--q-font-body);
  font-size: var(--fs-body);
  line-height: 1.45;
  color: var(--q-muted);
}

.q-form-status--ok {
  color: var(--q-success);
}

.q-form-status--err {
  color: var(--q-danger);
}

.q-btn--submit {
  min-width: 140px;
}

.q-btn--submit:disabled {
  opacity: 0.6;
  cursor: default;
  pointer-events: none;
}

/* Press release — longer headline, soft deck under it. */
.q-band--press {
  padding-bottom: var(--sp-9);
}

.q-band--press h1 {
  max-width: 54rem;
  margin: 0 auto var(--sp-5);
  font-size: var(--fs-heading);
}

.q-band--press p {
  max-width: 41rem;
  margin: 0 auto;
  color: var(--q-text-soft);
  font-family: var(--q-font-marketing);
  font-weight: 400;
  font-size: var(--fs-lead);
  font-style: normal;
  line-height: 1.65;
}

/* Article body — one long-form reading column for press + legal. */
.q-article {
  padding: var(--sp-9) var(--q-gutter) var(--q-section-y);
  background: transparent;
}

/* Reading panel: ink glass under the body only (not the title band), so
   long-form stays legible over the field and horizon without a solid slab.
   Same veil + blur language as header/footer; card radius so it reads as
   a surface, not a full-bleed band. */
.q-article-body {
  max-width: var(--q-measure);
  margin: 0 auto;
  padding: var(--sp-7) clamp(var(--sp-5), 4vw, var(--sp-7));
  color: var(--q-text-soft);
  font-family: var(--q-font-body);
  font-size: var(--fs-lead);
  line-height: 1.78;
  background: var(--q-chrome-veil);
  border: 1px solid var(--q-border-subtle);
  border-radius: var(--q-radius-card);
  backdrop-filter: blur(16px) saturate(1.3);
}

.q-article-body p {
  margin: 0 0 var(--sp-6);
}

.q-article-body > :last-child {
  margin-bottom: 0;
}

.q-article-body a {
  color: var(--q-orange);
  text-decoration-color: rgba(210, 118, 75, 0.45);
  text-underline-offset: 3px;
}

.q-article-body a:hover,
.q-article-body a:focus-visible {
  color: var(--q-orange-hover);
  text-decoration-color: currentColor;
}

/* Legal meta line under the title — UI voice, not lead body. */
.q-band--legal {
  padding-bottom: var(--sp-8);
}

.q-band--legal p {
  margin: var(--sp-4) 0 0;
  color: var(--q-muted);
  font-family: var(--q-font-ui);
  font-size: var(--fs-ui);
  font-weight: 500;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
}

.q-article-body h2 {
  margin: var(--sp-8) 0 var(--sp-4);
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-card);
  font-weight: 600;
  letter-spacing: var(--ls-card);
  line-height: 1.25;
}

.q-article-body h2:first-child {
  margin-top: 0;
}

.q-article-body h3 {
  margin: var(--sp-6) 0 var(--sp-3);
  color: var(--q-white);
  font-family: var(--q-font-display);
  font-size: var(--fs-lead);
  font-weight: 600;
  line-height: 1.35;
}

.q-article-body ul,
.q-article-body ol {
  margin: 0 0 var(--sp-6);
  padding-left: 26px;
}

.q-article-body li {
  margin-bottom: var(--sp-2);
}

.q-article-body li::marker {
  color: var(--q-orange);
}

/* A button that reads as body copy — the privacy policy's "change your
   cookie preferences" is an action, not a destination, so it's a <button>
   wearing the article link's clothes. */
.q-linklike {
  padding: 0;
  border: 0;
  background: none;
  color: var(--q-orange);
  font: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(210, 118, 75, 0.45);
  text-underline-offset: 3px;
  cursor: pointer;
}

.q-linklike:hover,
.q-linklike:focus-visible {
  color: var(--q-orange-hover);
  text-decoration-color: currentColor;
}

/* ==========================================================================
   Cookie consent

   One compact card, bottom-left, injected by assets/consent.js only when a
   choice is still owed. Both answers are the same size one click apart —
   accept simply speaks in the accent, decline in the chrome's neutral.
   ========================================================================== */
.q-consent {
  position: fixed;
  z-index: 1080;
  right: var(--sp-5);
  bottom: var(--sp-5);
  left: var(--sp-5);
  max-width: 480px;
  padding: var(--sp-5) var(--sp-5) var(--sp-5);
  background: var(--q-navy-deep);
  border: 1px solid var(--q-card-border);
  border-radius: var(--q-radius-card);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
}

.q-consent[hidden] {
  display: none;
}

.q-consent p {
  margin: 0 0 var(--sp-4);
  color: var(--q-text-soft);
  font-family: var(--q-font-body);
  font-size: var(--fs-body);
  line-height: 1.55;
}

.q-consent a {
  color: var(--q-orange);
  text-decoration-color: rgba(210, 118, 75, 0.45);
  text-underline-offset: 3px;
}

.q-consent a:hover,
.q-consent a:focus-visible {
  color: var(--q-orange-hover);
  text-decoration-color: currentColor;
}

.q-consent-actions {
  display: flex;
  gap: var(--sp-3);
}

.q-consent-actions .q-btn {
  min-width: 0;
  min-height: 44px;
  flex: 1;
  padding: 11px 18px;
}

@media (prefers-reduced-motion: no-preference) {
  .q-consent:not([hidden]) {
    animation: q-consent-in 260ms ease;
  }

  @keyframes q-consent-in {
    from {
      opacity: 0;
      transform: translateY(10px);
    }
  }
}

/* Network motion. Participants and the ring hold still; motion belongs to
   the data — the packets on the flow arcs (SMIL animateMotion in the SVG)
   — and to the core's tick ring and slow heartbeat. The global
   reduced-motion block freezes all of it to a legible still. */
.graphic .hub-glow {
  animation: graphic-halo 3.6s ease-in-out infinite;
}

/* The heartbeat: a hairline ring swells out from the core and dissolves.
   Scale is on the wrapping group so the ring stays hairline as it grows. */
.graphic .core-beat {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: graphic-beat 4.8s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
}

.graphic .spin-cw {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: graphic-spin 20s linear infinite;
}

@keyframes graphic-halo {
  0%, 100% { opacity: 0.75; }
  50% { opacity: 1; }
}
@keyframes graphic-beat {
  0% { transform: scale(1); opacity: 0.7; }
  70%, 100% { transform: scale(2.1); opacity: 0; }
}
@keyframes graphic-spin {
  to { transform: rotate(360deg); }
}

/* Responsive navigation and layout */
@media (max-width: 767.98px) {
  :root {
    --q-header-h: 68px;
    /* Keep --q-gutter at 24px so logos and blocks share one mobile edge. */
    --q-section-y: var(--q-section-y-sm);
    --q-band-y: var(--q-band-y-sm);
    --q-stack-gap: var(--sp-5);
  }

  .q-header {
    /* Opaque chrome and no blur on mobile: the full-screen menu needs a
       solid ground, and per-frame backdrop blur is costly on phones. The
       scroll-driven glass is switched off with it — animations would
       otherwise out-cascade this background. */
    background: var(--q-chrome);
    backdrop-filter: none;
    animation: none;
  }

  .q-header > .container-fluid {
    min-height: var(--q-header-h);
  }

  /* Same artwork; hold 30px on mobile (already compact). */
  .q-brand-lockup {
    height: 30px;
  }

  .q-header .navbar-collapse {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: none;
    padding: 104px 24px 32px;
    background: var(--q-chrome);
  }

  .q-header .navbar-collapse.show {
    display: block;
  }

  .q-nav-pill {
    display: block;
    max-width: 520px;
    margin: 0 auto;
    padding: 10px 0;
    text-align: center;
    border-top: 1px solid var(--q-border);
    border-bottom: 1px solid var(--q-border);
  }

  .q-header .nav-item + .nav-item {
    border-top: 1px solid var(--q-border-subtle);
  }

  .q-header .nav-link {
    padding: 24px 12px;
    font-size: var(--fs-body);
  }

  .q-header .nav-link::after {
    display: none;
  }

  .q-header .nav-item:last-child {
    margin: 24px 0 0;
    border-top: 0;
  }

  /* Block-level flex, not inline-flex — `margin: 0 auto` only centres a
     block-level box, and the desktop rule sets inline-flex. */
  .q-header .nav-item:last-child .nav-link {
    display: flex;
    width: min(100%, 250px);
    margin: 0 auto;
    padding: 16px 22px;
  }

  .q-nav-close {
    position: absolute;
    top: 24px;
    right: 24px;
    padding: 8px;
    color: var(--q-muted);
    background: none;
    border: 0;
    line-height: 0;
  }

  .q-hero {
    height: calc(100svh - var(--q-header-h));
    min-height: 620px;
  }

  .q-team-card img {
    width: 190px;
    height: 190px;
  }
}

@media (min-width: 768px) {
  .q-nav-close {
    display: none;
  }
}

/* Hero headline steps with the sphere geometry, not with the type scale.
   See .q-display for why. The sub uses --fs-heading and needs no pin. */
@media (min-width: 768px) and (max-width: 1100px) {
  .q-display {
    font-size: 61px;
  }
}

@media (max-width: 767.98px) {
  .q-display {
    font-size: 40px;
  }
}

@media (max-width: 359.98px) {
  .q-display {
    font-size: 36px;
  }
}

@media (max-width: 767.98px) {
  .q-footer {
    padding: var(--sp-7) 0 var(--sp-6);
  }

  .q-footer > .container-xl > .d-flex {
    flex-direction: column;
    align-items: flex-start !important;
  }

  .q-footer .nav {
    display: block;
    margin: var(--sp-2) 0 !important;
  }

  /* Stacked: fit-content keeps underlines on the word, not full width. */
  .q-footer .nav-link {
    --q-rule-inset: 0px;
    width: fit-content;
  }

  .q-hero {
    --q-sphere-size: min(76svh, 156vw, 700px);
    --q-sphere-top: 18px;
    --q-sphere-copy-offset: min(25.84svh, 53.04vw, 238px);
    --q-sphere-copy-top: calc(18px + var(--q-sphere-copy-offset));
    --q-sphere-cta-offset: min(49.4svh, 101.4vw, 455px);
    min-height: 560px;
  }

  .q-hero .q-hero-copy {
    padding: 0 var(--q-gutter);
  }

  /* Caps cost more width than mixed case; keep one line or it lands on the CTA. */
  .q-hero .q-sub {
    margin-top: var(--sp-5);
  }

  /* Section/press horizontal inset lives on the shared container gutter only
     — do not add a second padding-inline here or logos and blocks drift. */

  .graphic-wrap {
    padding: var(--sp-4) var(--q-gutter) var(--sp-4);
  }

  /* The artwork scales to a third of its desktop size here, which would
     take the 12.5-unit labels below legibility (~5px). Larger units and a
     heavier weight keep the participants readable; tracking comes in so
     the longer labels still clear their neighbours. */
  .graphic .node-label {
    font-size: 21px;
    font-weight: 600;
    letter-spacing: 0.06em;
    fill: rgba(234, 242, 251, 0.9);
  }

  .q-band {
    padding-bottom: var(--sp-7);
  }

  .q-press-card .q-press-meta {
    flex-wrap: wrap;
  }

  .q-team {
    padding-bottom: var(--q-section-y);
  }

  .q-team-cta {
    padding: 0 var(--sp-4) var(--q-section-y);
  }

  .q-contact {
    min-height: 0;
    padding: var(--sp-8) var(--sp-4) var(--sp-10);
  }

  .q-article {
    padding: var(--sp-7) var(--q-gutter) var(--q-section-y);
  }
}

@media (max-width: 575.98px) {
  .q-hero {
    height: calc(100svh - var(--q-header-h));
    min-height: 540px;
  }

  .q-cred-rail {
    flex-direction: column;
  }

  .q-cred:first-child {
    border-radius: var(--q-radius-card) var(--q-radius-card) 0 0;
  }

  .q-cred:last-child {
    border-radius: 0 0 var(--q-radius-card) var(--q-radius-card);
  }

  .q-cred-divider {
    width: auto;
    height: 1px;
    margin: 0 var(--sp-5);
  }

  .q-team-card {
    min-height: 0;
    gap: var(--sp-4);
    align-items: flex-start;
  }

  /* Keep square crop; scale down rather than changing aspect. */
  .q-team-card img {
    width: 120px;
    height: 120px;
  }

  .q-team-card .q-team-info {
    padding: var(--sp-2) var(--sp-1) var(--sp-2) 0;
  }

  .q-team-card p {
    margin-bottom: var(--sp-3);
  }

  .q-contact-card {
    padding: var(--sp-6) var(--sp-5);
  }
}

/* ==========================================================================
   Scroll reveal

   Subjects rise into view as the reader scrolls. Two engines, one look:

   Where scroll-driven animations exist, each reveal is scrubbed by its own
   travel through the viewport (animation-timeline: view()): the rise starts
   the moment the element peeks above the fold and completes about a third
   of the way up. Nothing pops — subjects flow in and out with the scroll,
   in both directions, and stagger children cascade naturally because each
   child scrubs its own position. Elsewhere, an IntersectionObserver plays
   the same rise on entry, tuned to fire the moment the element appears, so
   the fallback keeps the immediate feel.

   Everything is gated on body.q-anim, which only site.js adds — and only
   when IntersectionObserver exists and the user has not asked for reduced
   motion. No JS, old browser, or reduced motion all degrade to the same
   thing: nothing is ever hidden.

   .reveal          — the element rises as one piece.
   .reveal-stagger  — its direct children rise in sequence instead.

   Children animate with a keyframe rather than a transition so components
   that own a `transition` (the CTA button) keep their hover behaviour. */
.q-anim .reveal,
.q-anim .reveal-stagger > * {
  opacity: 0;
}

.q-anim .reveal.is-in {
  animation: q-rise 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.q-anim .reveal-stagger.is-in > * {
  animation: q-rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.q-anim .reveal-stagger.is-in > :nth-child(1) { animation-delay: 0.04s; }
.q-anim .reveal-stagger.is-in > :nth-child(2) { animation-delay: 0.11s; }
.q-anim .reveal-stagger.is-in > :nth-child(3) { animation-delay: 0.18s; }
.q-anim .reveal-stagger.is-in > :nth-child(4) { animation-delay: 0.25s; }

@keyframes q-rise {
  from {
    opacity: 0;
    transform: translateY(24px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* Scroll-scrubbed reveals. The same q-rise keyframes, but the timeline is
   the element's own position instead of a clock — .is-in coming and going
   changes nothing, so the observer can keep running untouched. The range
   ends at cover 38% so a subject is fully present by the time the eye
   reaches it, while taller pieces (the network diagram) still resolve
   before their midpoint. Time-based delays are meaningless against a
   scrubbed timeline; the :nth-child(n) rule zeroes the stagger delays at
   the same specificity they were set. */
@supports (animation-timeline: view()) {
  .q-anim .reveal,
  .q-anim .reveal.is-in,
  .q-anim .reveal-stagger > *,
  .q-anim .reveal-stagger.is-in > * {
    animation: q-rise linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 38%;
  }

  .q-anim .reveal-stagger.is-in > :nth-child(n) {
    animation-delay: 0s;
  }
}

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

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
