/* ═══════════════════════════════════════════════════════════════
   Tolmon Marketing — styles.css
   Inherits DriveOps colour system. Montserrat headings per BRAND.md.
═══════════════════════════════════════════════════════════════ */

/* ── Self-hosted fonts ─────────────────────────────────────── */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/inter-v20-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/inter-v20-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/inter-v20-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/inter-v20-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/montserrat-v31-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/montserrat-v31-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/montserrat-v31-latin.woff2') format('woff2');
}

/* ── Design tokens (exact DriveOps values) ─────────────────── */
:root {
  --bg:           #0a0f1e;
  --bg-alt:       #0f172a;
  --bg-card:      #1e293b;
  --bg-card-alt:  #162032;
  --bg-elevated:  #263148;
  --border:       rgba(255,255,255,0.08);
  --border-light: rgba(255,255,255,0.15);

  /* TM7 accent re-weighting. Navy (--bg) is the structural base. Cyan is
     the primary accent and the cross-product family thread shared with
     DriveOps and Wrap Zero: links, interactive and hover states, focus
     rings, labels, the constellation. Green is demoted to a sparing ESG
     accent: sustainability CTAs, the green stop of the hero gradient, and
     the semantic "leading"/certified strength markers only. */
  --accent:        #22c55e; /* green: sparing ESG accent */
  --accent-teal:   #06b6d4; /* cyan: primary accent, family thread */
  --accent-blue:   #3b82f6;
  --accent-purple: #8b5cf6;
  --accent-orange: #f97316;

  --text:       #f1f5f9;
  --text-muted: #94a3b8;
  --text-faint: #475569;

  --radius:    12px;
  --radius-lg: 18px;
  --radius-xl: 24px;

  --shadow-card: 0 0 0 1px rgba(255,255,255,0.05), 0 4px 24px rgba(0,0,0,0.35);
  --shadow-lg:   0 0 0 1px rgba(255,255,255,0.05), 0 20px 60px rgba(0,0,0,0.5);

  --font:         'Inter', -apple-system, system-ui, sans-serif;
  --font-heading: 'Montserrat', 'Inter', -apple-system, system-ui, sans-serif;
  --transition:   0.2s cubic-bezier(0.4, 0, 0.2, 1);

  --track-h:    0.05em;
  --track-p:    0.01em;
}

/* ── Reset ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  font-family: var(--font);
  /* TM7: navy is the structural base and the dominant colour, anchoring
     the scheme on trust and credibility for an ESG, efficiency-focused
     brand. This replaces the TM5 green-black unification. The
     constellation field (alpha canvas) shows this single navy tone
     through it, so the whole document reads against one deep-blue base.
     It matches the --bg token used by the interior pages, so the homepage
     and directory pages now share one base. Slate cards and the semi
     footer remain as surface layers on top. */
  background: var(--bg);
  color: var(--text);
  font-size: 16px;
  line-height: 1.65;
  letter-spacing: var(--track-p);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* TM5: raise the main content above the fixed page-field canvas (z 0).
   Without an explicit positive z-index the field would paint over the
   in-flow content. The sections inside stay transparent, so the field
   still shows through; this only fixes paint order. */
main {
  position: relative;
  z-index: 1;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
ul { list-style: none; }

/* ── Container ──────────────────────────────────────────────── */
.container {
  max-width: 1180px;
  width: 92%;
  margin-left: auto;
  margin-right: auto;
  padding: 0 24px;
}

/* ── Buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 24px;
  border-radius: 10px;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: all 0.2s ease;
  border: 1px solid transparent;
  white-space: nowrap;
  cursor: pointer;
  /* TM17: magnetic CTAs. The transform composes two parts in one property
     so the magnetism never clobbers the existing hover lift: --mag-x/--mag-y
     are fed by the pointer handler in js/main.js (pointer-only, gated), and
     --btn-lift carries the hover lift the :hover rules below used to set
     directly. All default to 0, so touch and no-JS buttons sit still and
     keep their normal hover. Translate only, so there is no reflow (CLS 0);
     a 2D translate stays off its own GPU layer when idle and is promoted by
     .is-magnetic only while the pointer is engaging it. */
  --mag-x: 0px;
  --mag-y: 0px;
  --btn-lift: 0px;
  transform: translate(var(--mag-x), calc(var(--mag-y) + var(--btn-lift)));
}
/* While the pointer is actively pulling a CTA, promote it to its own layer
   so the eased follow stays GPU-composited and smooth. Removed on leave. */
.btn.is-magnetic { will-change: transform; }
.btn-lg { padding: 15px 36px; font-size: 1.05rem; }

.btn-primary {
  background: var(--accent);
  color: #052e16;
  border-color: var(--accent);
  font-weight: 700;
}
.btn-primary:hover {
  background: #16a34a;
  border-color: #16a34a;
  --btn-lift: -1px; /* TM17: fed into the composed .btn transform, same -1px lift */
  box-shadow: 0 8px 28px rgba(34,197,94,0.35);
}
.btn-primary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.btn-ghost {
  background: rgba(255,255,255,0.06);
  color: var(--text);
  border-color: rgba(255,255,255,0.15);
}
.btn-ghost:hover {
  background: rgba(255,255,255,0.1);
  border-color: rgba(6,182,212,0.5);
  --btn-lift: -1px; /* TM17: fed into the composed .btn transform, same -1px lift */
}
.btn-ghost:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 3px;
}

/* ── Section shared ─────────────────────────────────────────── */
.section { padding: 100px 0; }

.section-label {
  display: inline-block;
  font-family: var(--font-heading);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-teal);
  margin-bottom: 16px;
}

.section-title {
  font-family: var(--font-heading);
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 700;
  letter-spacing: var(--track-h);
  line-height: 1.15;
  color: var(--text);
  margin-bottom: 20px;
}

.section-sub {
  font-size: 1.05rem;
  color: var(--text-muted);
  line-height: 1.75;
  letter-spacing: var(--track-p);
  max-width: 620px;
}

.section-header {
  text-align: center;
  max-width: 680px;
  margin: 0 auto 56px;
}
.section-header .section-sub {
  margin: 0 auto;
}

/* ── Animations ─────────────────────────────────────────────── */
.animate-in {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s cubic-bezier(0.16,1,0.3,1), transform 0.6s cubic-bezier(0.16,1,0.3,1);
}
.animate-in.visible {
  opacity: 1;
  transform: translateY(0);
}
.delay-1 { transition-delay: 0.12s; }
.delay-2 { transition-delay: 0.24s; }
.delay-3 { transition-delay: 0.36s; }

/* ── TM6 scroll reveals ─────────────────────────────────────────
   Content is visible by DEFAULT. The hidden-initial state below is
   scoped to .reveal-ready, a class the inline head script adds to
   <html> only when motion is welcome. So no-JS and reduced-motion
   users never get the hidden state and see everything immediately.

   Reveal is opacity + transform only, and the transform is small, so
   layout never shifts (CLS 0). The CSS transition is the universal
   floor used by the IntersectionObserver path (mobile and any browser
   without the desktop GSAP stack). On desktop the GSAP layer takes
   ownership and drives its own eased, staggered tween instead. */
.reveal-ready .reveal {
  opacity: 0;
  /* TM15: richer entrance. The small clip mask and slight scale-from-0.985
     give the copy more character as it enters, on top of TM6's rise. All
     three are paint/transform only within reserved space, so layout never
     shifts (CLS 0). On fine-pointer cards this transform is overridden by
     the interactive tilt rule (cards enter via opacity + clip instead),
     so it never fights the tilt. */
  transform: translateY(16px) scale(0.985);
  clip-path: inset(0% 0% 10% 0%);
  will-change: opacity, transform, clip-path;
}
.reveal-ready .reveal.is-visible {
  opacity: 1;
  transform: none;
  clip-path: inset(0% 0% 0% 0%);
  transition: opacity 0.6s ease, transform 0.6s ease, clip-path 0.6s ease;
  transition-delay: calc(var(--ri, 0) * 0.08s);
}

/* Desktop GSAP owns the easing for the elements it controls, so the
   CSS transition must not also fire on them (no double reveal). GSAP
   sets this flag on <html> the moment it takes over. */
.gsap-reveal .reveal.is-visible {
  transition: none;
  transition-delay: 0s;
}

/* ═══════════════════════════════════════════════════════════
   NAV
═══════════════════════════════════════════════════════════ */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: rgba(10,15,30,0.75);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  transition: background 0.3s ease, box-shadow 0.3s ease;
}
.nav.scrolled {
  background: rgba(10,15,30,0.96);
  box-shadow: 0 1px 0 var(--border);
}
.nav-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 24px;
  height: 80px;
  display: flex;
  align-items: center;
  gap: 24px;
}
.nav-logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.nav-logo-img {
  height: 22px;
  width: auto;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}
.nav-links li a {
  padding: 8px 16px;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--text-muted);
  border-radius: 8px;
  transition: color 0.15s, background 0.15s;
}
.nav-links li a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
.nav-links li a:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 2px;
}
.nav-links .nav-signin {
  margin-left: 8px;
  padding: 9px 20px;
  font-size: 0.82rem;
  letter-spacing: 0.07em;
}

.nav-mobile-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 8px;
  background: none;
  border: none;
  margin-left: auto;
}
.nav-mobile-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text);
  border-radius: 2px;
  transition: var(--transition);
}
.nav-mobile-toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-mobile-toggle.open span:nth-child(2) { opacity: 0; }
.nav-mobile-toggle.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ═══════════════════════════════════════════════════════════
   HERO
═══════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  padding: 160px 0 100px;
  overflow: hidden;
  /* TM5: transparent so the page-field constellation shows through. The
     left-weighted ::after scrim plus the unified body base supply the
     look; the cursor spotlight and both extra grids are gone. */
  background: transparent;
  min-height: 90vh;
}

.hero-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 64px;
  position: relative;
  z-index: 10;
}

.hero-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  flex: 1;
  max-width: 640px;
}

.hero-mark {
  position: relative;
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}


.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 18px;
  border-radius: 100px;
  font-family: var(--font-heading);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--accent-teal);
  background: rgba(6,182,212,0.08);
  border: 1px solid rgba(6,182,212,0.25);
  margin-bottom: 32px;
}
.hero-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-teal);
  animation: pulse 2s infinite;
  flex-shrink: 0;
}
@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.75); }
}

.hero-headline {
  font-family: var(--font-heading);
  font-size: clamp(2.75rem, 5.5vw, 4.5rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: var(--track-h);
  margin-bottom: 28px;
  color: var(--text);
}
.hero-accent {
  background: linear-gradient(135deg, var(--accent-teal) 0%, var(--accent) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* ── TM10 hero headline kinetic mask reveal ─────────────────────
   The headline is visible by DEFAULT. The hidden-initial state below is
   scoped to .kinetic-ready, a class the synchronous head script adds to
   <html> only when motion is welcome. So no-JS and reduced-motion users
   never get the hidden state and see the headline immediately.

   Each visible line is a clip container (.kinetic-line, overflow hidden)
   holding an inner element (.kinetic-inner) that carries the transform.
   The clips reserve the headline's natural layout height: each is one
   line box tall, the two stack exactly as the former <br> did, and the
   padding-bottom / negative margin-bottom pair gives descenders ('g',
   'y') paint room INSIDE the clip without changing the occupied space
   (net zero). flow-root on the headline keeps the gap to the sub-text
   exact. So nothing shifts (CLS 0); only transform and opacity animate. */
.hero-headline {
  display: flow-root;
}
.kinetic-line {
  display: block;
  overflow: hidden;
  /* descender paint room, cancelled by the negative margin (net zero) */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.kinetic-inner {
  /* inline-block so the gradient box hugs the text width, keeping the
     accent gradient identical to the original inline rendering */
  display: inline-block;
  will-change: transform;
}

/* Hidden-initial: only when motion is welcome. The lines start fully
   below their clip (hidden by the mask) and faded out. */
.kinetic-ready .hero-headline .kinetic-inner {
  transform: translateY(115%);
  opacity: 0;
}
/* Revealed: the lines rise to their natural position and fade in, with a
   restrained eased transition and a small per-line stagger so the second
   line follows the first. */
.kinetic-ready .hero-headline.is-revealed .kinetic-inner {
  transform: translateY(0);
  opacity: 1;
}
.kinetic-ready .hero-headline .kinetic-inner {
  transition: transform 0.72s cubic-bezier(0.16, 1, 0.3, 1),
              opacity 0.72s cubic-bezier(0.16, 1, 0.3, 1);
}
.kinetic-ready .hero-headline .kinetic-line:nth-child(2) .kinetic-inner {
  transition-delay: 0.12s;
}

.hero-sub {
  max-width: 540px;
  margin: 0 0 44px;
  font-size: 1.1rem;
  color: var(--text-muted);
  line-height: 1.75;
  letter-spacing: var(--track-p);
}

.hero-ctas {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

.hero-mark-img {
width: 280px;
  height: auto;
  opacity: 0.18;
  transition: opacity 0.6s ease-in-out;
}

.hero-mark:hover .hero-mark-img {
  opacity: 0.65;
}
/* ═══════════════════════════════════════════════════════════
   PRODUCTS
═══════════════════════════════════════════════════════════ */
.products-section {
  /* TM5: transparent so the page field shows through; the borders stay
     to keep the section edges. position relative anchors the legibility
     scrim and the raised container below. */
  background: transparent;
  position: relative;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

/* TM5: a soft top-down scrim behind the section header, settling the
   field enough for the label, title and intro to stay crisp. Sits above
   the field and below the raised container. TM7: tinted in the navy base
   tone so it blends with the base and is invisible on the gated-off
   fallback. */
.products-section::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* TM13: deepened from 0.78/0.45/0.2 to hold the cards and copy legible
     over the brighter field as the camera tightens through this scene. */
  background: linear-gradient(
    180deg,
    rgba(10, 15, 30, 0.9) 0%,
    rgba(10, 15, 30, 0.62) 32%,
    rgba(10, 15, 30, 0.34) 60%,
    rgba(10, 15, 30, 0) 100%
  );
}

.products-section .container,
.about-section .container {
  position: relative;
  z-index: 1;
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 24px;
}

.product-card {
  display: block;
  /* TM15: anchors the cursor-tracked sheen overlay (a positioned pseudo). */
  position: relative;
  /* TM15: frosted glass. A semi-transparent navy lets the field show
     through, blurred, behind each card. 0.66 keeps the title, body and
     muted text legible over the blurred field, so a11y holds. The blur is
     applied only where supported (the @supports block below); this value
     doubles as the translucent base for supporting browsers. */
  background: rgba(30, 41, 59, 0.66);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 36px;
  /* TM8: 0.3s smooth ease for the hover lift and cyan accent. The border
     stays 1px and only changes colour, and the lift is transform plus
     box-shadow only, so the box size never changes (CLS 0). */
  transition: border-color 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;
  text-decoration: none;
  color: var(--text);
  min-height: 320px;
  box-shadow: var(--shadow-card);
}
.product-card:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 3px;
}

/* TM15: the backdrop blur is gated behind @supports so unsupported browsers
   keep a near-solid navy panel (no broken transparency, text fully legible). */
@supports ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .product-card {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }
}
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .product-card {
    background: var(--bg-card); /* solid navy fallback */
  }
}

/* TM8 + TM15: cursor-reactive cards on fine-pointer devices only (touch
   never gets a sticky hover or tilt). TM8's affordance is preserved: the
   cyan border colour and a soft cyan box-shadow glow on hover, plus the
   small lift. TM15 composes a subtle pointer-tracked 3D tilt and the lift
   into a SINGLE transform on the card, driven by CSS custom properties that
   js/main.js updates on pointermove (rAF-coalesced).

   Why !important: both the TM6 reveal CSS and GSAP write `transform` on
   .reveal cards (GSAP leaves an inline transform after the entrance), which
   would otherwise win over a stylesheet hover and block the lift/tilt. The
   card therefore ENTERS via opacity + clip-path only (no transform, see
   animations.js and the reveal floor), leaving its transform for this
   interactive state to own. At rest (--rx/--ry/--lift all 0) the transform
   resolves to identity, so nothing moves and the box size never changes
   (CLS 0). Reduced-motion neutralises it further down the file. */
@media (hover: hover) and (pointer: fine) {
  .product-card {
    transform: perspective(900px)
               rotateX(var(--rx, 0deg))
               rotateY(var(--ry, 0deg))
               translateY(var(--lift, 0px)) !important;
    transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
                border-color 0.3s ease,
                box-shadow 0.3s ease;
  }
  /* While the pointer tracks across the card we drop the transform easing so
     the tilt follows live; it eases back on pointerleave when js removes the
     class and resets the custom properties to rest. */
  .product-card.is-tilting {
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
  }
  .product-card:hover,
  .product-card.is-tilting { will-change: transform; }
  .product-card:hover {
    --lift: -6px; /* TM8 lift, preserved */
    border-color: rgba(6,182,212,0.45);
    box-shadow: var(--shadow-lg), 0 0 48px rgba(6,182,212,0.18);
  }

  /* TM15: the cyan sheen. A soft radial highlight centred on the cursor
     (--sx/--sy, updated alongside the tilt), screen-blended over the glass
     and UNDER the card text (z-index below .product-card-inner), fading in
     on hover. A pseudo-element overlay only, inset to the card, so it never
     changes the box size (CLS 0). It tilts with the card surface. */
  .product-card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    z-index: 0;
    opacity: 0;
    transition: opacity 0.4s ease;
    background: radial-gradient(
      circle at var(--sx, 50%) var(--sy, 50%),
      rgba(6, 182, 212, 0.18),
      rgba(6, 182, 212, 0) 55%
    );
    mix-blend-mode: screen;
  }
  .product-card:hover::after { opacity: 1; }
  /* keep the card content above the sheen */
  .product-card-inner { position: relative; z-index: 1; }
}

.product-card-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.product-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}

.product-card-label {
  font-family: var(--font-heading);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-teal);
}

.product-card-mark {
  height: 40px;
  width: auto;
  max-width: 64px;
  flex-shrink: 0;
}

.product-card-title {
  font-family: var(--font-heading);
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: var(--track-h);
  line-height: 1.2;
  color: var(--text);
  margin-bottom: 16px;
}

.product-card-body {
  font-size: 0.98rem;
  color: var(--text-muted);
  line-height: 1.75;
  letter-spacing: var(--track-p);
  margin-bottom: 32px;
  flex: 1;
}

.product-card-cta {
  font-family: var(--font-heading);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-teal);
  margin-top: auto;
  transition: color 0.15s;
}
.product-card:hover .product-card-cta {
  /* TM7: cyan is the primary interactive and hover colour. The card lift
     and the cyan border carry the hover affordance; the CTA stays in the
     cyan family rather than flipping to green. */
  color: var(--accent-teal);
}

/* ═══════════════════════════════════════════════════════════
   ABOUT
═══════════════════════════════════════════════════════════ */
.about-section {
  /* TM5: transparent so the field carries through. The About copy is
     the highest legibility risk (three runs of muted body text), so it
     gets the strongest treatment: a left-column wash mirroring the hero
     scrim. */
  background: transparent;
  position: relative;
}

/* TM5: left-weighted wash over the About text column, fading to clear on
   the open right where the brand mark sits. Mirrors the hero ::after so
   the two left columns read identically. TM7: navy base tone, so it costs
   nothing on the gated-off fallback. */
.about-section::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* TM13: deepened from 0.85/0.6/0.2 so the About column stays legible as
     the camera reaches its furthest, airier pose over the louder field. */
  background: linear-gradient(
    90deg,
    rgba(10, 15, 30, 0.93) 0%,
    rgba(10, 15, 30, 0.72) 38%,
    rgba(10, 15, 30, 0.36) 68%,
    rgba(10, 15, 30, 0) 100%
  );
}

.about-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: 80px;
  align-items: center;
}

.about-body {
  font-size: 1.05rem;
  color: var(--text-muted);
  line-height: 1.8;
  letter-spacing: var(--track-p);
  margin-bottom: 20px;
  max-width: 640px;
}
.about-body:last-child { margin-bottom: 0; }

.about-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.12;
  filter: none;
}
.about-mark-img {
  width: 220px;
  height: auto;
}

/* ═══════════════════════════════════════════════════════════
   FOOTER
═══════════════════════════════════════════════════════════ */
.site-footer {
  /* TM5: semi-opaque so the field fades into a grounded footer rather
     than stopping at a hard edge. Raised above the fixed field. */
  background: rgba(15, 23, 42, 0.85);
  position: relative;
  z-index: 1;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  padding: 56px 0 36px;
  font-size: 0.9rem;
}

.footer-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 32px;
  margin-bottom: 40px;
}

.footer-brand {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.footer-logo {
  height: 16px;
  width: auto;
  opacity: 0.85;
}
.footer-tagline {
  font-size: 0.88rem;
  color: var(--text-faint);
  letter-spacing: var(--track-p);
}

.footer-links {
  display: flex;
  gap: 28px;
  flex-wrap: wrap;
  align-items: center;
}
.footer-links a {
  color: var(--text-muted);
  font-size: 0.9rem;
  transition: color 0.15s;
}
.footer-links a:hover { color: var(--text); }
.footer-links a:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 2px;
  border-radius: 2px;
}

.footer-bottom {
  border-top: 1px solid var(--border);
  padding-top: 28px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 0.8rem;
  color: var(--text-faint);
}
.footer-bottom a {
  color: var(--text-muted);
  transition: color 0.15s;
}
.footer-bottom a:hover { color: var(--text); }

/* ═══════════════════════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════════════════════ */
@media (max-width: 960px) {
  .hero-content {
    flex-direction: column;
    align-items: flex-start;
    gap: 48px;
  }

  .hero-mark {
    align-self: center;
  }

  .hero-mark-img {
    width: 220px;
  }

  .about-layout {
    grid-template-columns: 1fr;
    gap: 40px;
  }

  .about-mark {
    display: none;
  }
}

@media (max-width: 900px) and (min-width: 641px) {
  .nav-links .nav-link-internal { display: none; }
}

@media (max-width: 640px) {
  .hero {
    padding: 120px 0 80px;
    min-height: auto;
  }
  .section { padding: 72px 0; }
  .hero-ctas { flex-direction: column; }
  .hero-ctas .btn { width: 100%; justify-content: center; }
  .nav-links { display: none; }
  .nav-links.open {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 80px;
    left: 0;
    right: 0;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--border);
    padding: 20px 24px 28px;
    gap: 8px;
  }
  .nav-links.open li a {
    width: 100%;
    min-height: 44px;
    display: flex;
    align-items: center;
    padding: 8px 12px;
    border-radius: 8px;
  }
  .nav-links.open li a:hover,
  .nav-links.open li a:focus-visible {
    background: rgba(255, 255, 255, 0.04);
  }
  .nav-mobile-toggle { display: flex; }
  .footer-top { flex-direction: column; }
  .footer-bottom { flex-direction: column; }
  .product-grid { grid-template-columns: 1fr; }
}

/* ────────────────────────────────────────────────────
   Vendor Directory
   Used by /directory, /get-listed, /rating-framework
   ──────────────────────────────────────────────────── */

/* ── Active nav link ── */
.nav-links li a.active {
  color: var(--text);
  background: rgba(255,255,255,0.06);
}

/* ── Vendor notice ── */
.vendor-notice {
  background: rgba(6,182,212,0.06);
  border: 1px solid rgba(6,182,212,0.3);
  border-radius: var(--radius);
  color: var(--text-muted);
  padding: 14px 18px;
  margin: 24px auto;
  text-align: center;
  font-size: 0.9rem;
  line-height: 1.6;
}
.vendor-notice a { color: var(--accent-teal); text-decoration: underline; }

/* ── Directory hero ── */
.dir-hero {
  background: var(--bg);
  padding: 120px 0 64px;
  text-align: center;
}
.dir-hero .section-label { margin-bottom: 0.75rem; }
.dir-hero h1 {
  font-family: var(--font-heading);
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 700;
  letter-spacing: var(--track-h);
  color: var(--text);
  margin: 0 0 1rem;
  line-height: 1.15;
}
.dir-hero p {
  color: var(--text-muted);
  font-size: 1.1rem;
  max-width: 580px;
  margin: 0 auto 2rem;
}
.dir-hero-actions {
  display: flex;
  gap: 1rem;
  justify-content: center;
  flex-wrap: wrap;
}

/* ── Directory filters ── */
.dir-filters {
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
  padding: 24px 0;
  position: sticky;
  top: 80px;
  z-index: 100;
}
.dir-filters .container {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}
.dir-filter-search {
  flex: 1;
  min-width: 200px;
  max-width: 320px;
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 0.9rem;
  padding: 10px 14px;
  font-family: var(--font);
}
.dir-filter-search::placeholder { color: var(--text-faint); }
.dir-filter-search:focus { outline: none; border-color: var(--accent-teal); }
.dir-filter-select {
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 0.85rem;
  padding: 10px 14px;
  font-family: var(--font);
  cursor: pointer;
}
.dir-filter-select:focus { outline: none; border-color: var(--accent-teal); }
.dir-filter-select option { background: var(--bg-card); }
.dir-filter-count {
  margin-left: auto;
  color: var(--text-faint);
  font-size: 0.8rem;
  white-space: nowrap;
}

/* ── Rating badges ── */
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 3px 8px;
  border-radius: 4px;
}
.rating-strength-leading    { background: rgba(34,197,94,0.15);   color: var(--accent);        border: 1px solid rgba(34,197,94,0.3); }
.rating-strength-credible   { background: rgba(6,182,212,0.15);   color: var(--accent-teal);   border: 1px solid rgba(6,182,212,0.3); }
.rating-strength-declared   { background: rgba(148,163,184,0.12); color: var(--text-muted);    border: 1px solid rgba(148,163,184,0.2); }
.rating-strength-unverified {
  background: transparent;
  color: var(--text-faint);
  border: 1px solid rgba(71,85,105,0.2);
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  opacity: 0.7;
  padding: 2px 6px;
}
.rating-reference           { background: rgba(249,115,22,0.12);  color: var(--accent-orange); border: 1px solid rgba(249,115,22,0.25); }

/* ── Vendor grid + cards ── */
.dir-body {
  background: var(--bg);
  padding: 48px 0 80px;
  min-height: 60vh;
}
.dir-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}
.vendor-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: border-color 0.2s, transform 0.2s;
}
.vendor-card:hover {
  border-color: rgba(6,182,212,0.3);
  transform: translateY(-2px);
}
.vendor-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
.vendor-card-name {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.3;
}
.vendor-card-cat {
  font-size: 0.75rem;
  color: var(--text-faint);
  margin-top: 2px;
}
.vendor-card-location {
  font-size: 0.8rem;
  color: var(--text-faint);
  display: flex;
  align-items: center;
  gap: 5px;
}
.vendor-card-summary {
  font-size: 0.875rem;
  color: var(--text-muted);
  line-height: 1.55;
  /* flex: 1 removed — truncated cards are uniform enough */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.vendor-card-summary.expanded {
  display: block;
  overflow: visible;
  -webkit-line-clamp: unset;
}
.vendor-card-readmore {
  font-size: 0.78rem;
  color: var(--accent-teal);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font);
  align-self: flex-start;
  margin-top: -4px;
}
.vendor-card-readmore:hover { text-decoration: underline; }
.vendor-card-footer {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 4px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.vendor-card-link {
  font-size: 0.78rem;
  color: var(--accent-teal);
  text-decoration: none;
  font-weight: 500;
}
.vendor-card-link:hover { text-decoration: underline; }
.vendor-card-email {
  font-size: 0.78rem;
  color: var(--text-faint);
}

/* ── Schema chips ── */
.vendor-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 4px;
  margin-bottom: 4px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 4px;
  white-space: nowrap;
  background: rgba(255,255,255,0.05);
  color: var(--text-muted);
  border: 1px solid rgba(255,255,255,0.1);
}
.chip-cert {
  background: rgba(34,197,94,0.08);
  color: #4ade80;
  border-color: rgba(34,197,94,0.2);
}
.chip-location {
  background: rgba(6,182,212,0.07);
  color: #22d3ee;
  border-color: rgba(6,182,212,0.18);
}

/* ── Empty state ── */
.no-results {
  text-align: center;
  color: var(--text-faint);
  padding: 80px 20px;
  font-size: 1rem;
  grid-column: 1 / -1;
}

/* ── Category heading ── */
.dir-cat-heading {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}
.dir-cat-heading h2 {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  white-space: nowrap;
}
.dir-cat-heading::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ── Rating legend ── */
.dir-legend {
  background: var(--bg-alt);
  border-top: 1px solid var(--border);
  padding: 32px 0;
}
.dir-legend-inner {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
}
.dir-legend-label {
  font-size: 0.75rem;
  color: var(--text-faint);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-right: 4px;
}
.dir-legend-link {
  font-size: 0.78rem;
  color: var(--accent-teal);
  text-decoration: none;
  margin-left: auto;
}
.dir-legend-link:hover { text-decoration: underline; }

/* ── Form inputs (used by /get-listed) ── */
.form-group { margin-bottom: 18px; }
.form-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.form-label .required { color: var(--accent-teal); margin-left: 2px; }
.form-input,
.form-select,
.form-textarea {
  width: 100%;
  box-sizing: border-box;
  background: var(--bg-alt);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  color: var(--text);
  font-size: 0.9rem;
  padding: 11px 14px;
  font-family: var(--font);
  transition: border-color 0.2s;
}
.form-input::placeholder,
.form-textarea::placeholder { color: #334155; }
.form-input:focus,
.form-select:focus,
.form-textarea:focus { outline: none; border-color: var(--accent-teal); }
.form-select { cursor: pointer; }
.form-select option { background: var(--bg-card); }
.form-textarea { resize: vertical; min-height: 100px; }
.form-divider { border: none; border-top: 1px solid var(--border); margin: 24px 0; }
.btn-submit { width: 100%; padding: 14px; font-size: 0.95rem; font-weight: 600; justify-content: center; }
.form-submit-hint {
  font-size: 0.75rem;
  color: var(--text-faint);
  margin-top: 12px;
  text-align: center;
  line-height: 1.5;
}
.form-submit-hint a { color: var(--accent-teal); text-decoration: none; }
.form-submit-hint a:hover { text-decoration: underline; }
.form-input.invalid,
.form-select.invalid,
.form-textarea.invalid { border-color: #ef4444; }

/* ── Get-listed page layout ── */
.gl-hero {
  background: var(--bg);
  padding: 120px 0 64px;
}
.gl-hero h1 {
  font-family: var(--font-heading);
  font-size: clamp(2rem, 4vw, 2.75rem);
  font-weight: 700;
  letter-spacing: var(--track-h);
  color: var(--text);
  margin: 0 0 1rem;
  line-height: 1.15;
}
.gl-hero p { color: var(--text-muted); font-size: 1.05rem; max-width: 560px; line-height: 1.7; }
.gl-body { background: var(--bg-alt); padding: 64px 0 80px; }
.gl-inner {
  display: grid;
  grid-template-columns: 1fr 420px;
  gap: 64px;
  align-items: start;
}
.gl-info h2 { font-family: var(--font-heading); letter-spacing: var(--track-h); font-size: 1.3rem; font-weight: 700; color: var(--text); margin: 0 0 1rem; }
.gl-info ul {
  list-style: none;
  padding: 0;
  margin: 0 0 2rem;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.gl-info ul li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--text-muted);
  font-size: 0.875rem;
  line-height: 1.55;
}
.gl-info ul li::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-teal);
  flex-shrink: 0;
  margin-top: 7px;
}
.gl-note {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px;
}
.gl-note h3 { font-size: 0.9rem; font-weight: 700; color: var(--text-muted); margin: 0 0 8px; }
.gl-note p { color: #64748b; font-size: 0.8rem; line-height: 1.6; margin: 0; }
.gl-note p + p { margin-top: 8px; }
.gl-form-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 36px 32px;
}
.gl-form-card h2 { font-family: var(--font-heading); letter-spacing: var(--track-h); font-size: 1.1rem; font-weight: 700; color: var(--text); margin: 0 0 8px; }

/* ── Rating framework page ── */
.rf-hero { background: var(--bg); padding: 120px 0 64px; }
.rf-hero h1 {
  font-family: var(--font-heading);
  font-size: clamp(2rem, 4vw, 2.75rem);
  font-weight: 700;
  letter-spacing: var(--track-h);
  color: var(--text);
  margin: 0 0 1rem;
  line-height: 1.15;
}
.rf-hero p { color: var(--text-muted); font-size: 1.05rem; max-width: 600px; line-height: 1.7; }
.rf-section { padding: 64px 0; }
.rf-section--alt { background: var(--bg-alt); }
.rf-section--dark { background: var(--bg); }
.rf-prose { max-width: 720px; }
.rf-prose h2 { font-family: var(--font-heading); letter-spacing: var(--track-h); font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0 0 1rem; }
.rf-prose p { color: var(--text-muted); line-height: 1.75; margin: 0 0 1rem; }
.rf-prose p:last-child { margin-bottom: 0; }
.rf-tiers {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  margin-top: 2rem;
}
.rf-tier-card {
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  padding: 28px 24px;
  border-left: 4px solid transparent;
}
.rf-tier-card--leader    { border-left-color: var(--accent); }
.rf-tier-card--committed { border-left-color: var(--accent-teal); }
.rf-tier-card--baseline  { border-left-color: var(--text-muted); }
.rf-tier-card--unassessed{ border-left-color: var(--text-faint); }
.rf-tier-card--reference { border-left-color: var(--accent-orange); }
.rf-tier-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 3px 8px;
  border-radius: 4px;
  margin-bottom: 12px;
}
.rf-tier-badge--leader    { background: rgba(34,197,94,0.15);   color: var(--accent);        border: 1px solid rgba(34,197,94,0.3); }
.rf-tier-badge--committed { background: rgba(6,182,212,0.15);   color: var(--accent-teal);   border: 1px solid rgba(6,182,212,0.3); }
.rf-tier-badge--baseline  { background: rgba(148,163,184,0.12); color: var(--text-muted);    border: 1px solid rgba(148,163,184,0.2); }
.rf-tier-badge--unassessed{ background: rgba(71,85,105,0.2);    color: #64748b;              border: 1px solid rgba(71,85,105,0.3); }
.rf-tier-badge--reference { background: rgba(249,115,22,0.12);  color: var(--accent-orange); border: 1px solid rgba(249,115,22,0.25); }
.rf-tier-card h3 { font-size: 1rem; font-weight: 700; color: var(--text); margin: 0 0 8px; }
.rf-tier-card p { font-size: 0.875rem; color: var(--text-muted); line-height: 1.6; margin: 0; }
.rf-criteria { width: 100%; border-collapse: collapse; margin-top: 1.5rem; }
.rf-criteria th {
  text-align: left;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
}
.rf-criteria td {
  padding: 14px 16px;
  font-size: 0.875rem;
  color: var(--text-muted);
  border-bottom: 1px solid rgba(255,255,255,0.04);
  vertical-align: top;
}
.rf-criteria tr:last-child td { border-bottom: none; }
.rf-criteria td:first-child { color: var(--text); font-weight: 500; }
.rf-criteria tr:hover td { background: rgba(255,255,255,0.02); }
.rf-transparency {
  background: var(--bg-card);
  border: 1px solid rgba(6,182,212,0.2);
  border-radius: var(--radius-lg);
  padding: 32px;
  margin-top: 2rem;
}
.rf-transparency h3 { font-family: var(--font-heading); letter-spacing: var(--track-h); font-size: 1rem; font-weight: 700; color: var(--text); margin: 0 0 12px; }
.rf-transparency p { color: var(--text-muted); font-size: 0.9rem; line-height: 1.7; margin: 0 0 10px; }
.rf-transparency p:last-child { margin: 0; }
.rf-cta { background: var(--bg-alt); border-top: 1px solid var(--border); padding: 48px 0; text-align: center; }
.rf-cta h2 { font-family: var(--font-heading); font-size: 1.5rem; font-weight: 700; color: var(--text); margin: 0 0 0.75rem; }
.rf-cta p { color: var(--text-muted); margin: 0 0 1.75rem; }
.rf-cta-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }

/* ── Directory responsive ── */
@media (max-width: 860px) {
  .gl-inner { grid-template-columns: 1fr; gap: 40px; }
  .gl-form-card { padding: 28px 20px; }
}
@media (max-width: 600px) {
  .dir-grid { grid-template-columns: 1fr; }
  .rf-tiers { grid-template-columns: 1fr; }
  .dir-filters .container { flex-direction: column; align-items: stretch; }
  .dir-filter-count { margin-left: 0; }
}

/* ═══════════════════════════════════════════════════════════
   PAGE-FIELD CANVAS (TM5 full-viewport background)
   A fixed full-viewport layer sitting behind every section, so the
   constellation reads as one continuous field from the hero down to the
   footer. Fixed and out of normal flow, so it never shifts layout
   (CLS 0). js/scene3d.js adds .is-3d-active only after the gate passes,
   fading it in. On the no-WebGL / mobile / low-power / reduced-motion
   fallback the canvas never activates, leaving the static unified-tone
   page with the brand mark as the sole hero visual.
═══════════════════════════════════════════════════════════ */
.field-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.2s ease;
}
.field-canvas.is-3d-active {
  opacity: 1;
}

/* TM8: scroll-depth parallax helper. The two decorative brand marks drift
   at a slightly different rate to the content as you scroll, reading as a
   deeper layer behind the text and continuing the field's subtle depth.
   They are parallaxed on the INNER images (.hero-mark-img, .about-mark-img),
   never on the .reveal wrappers, so the TM6 reveal transform and the
   parallax transform live on different nodes and never fight.

   will-change is scoped to the exact desktop fine-pointer breakpoint that
   animations.js uses to load the motion stack, so it is only paid for where
   the parallax actually runs (never on touch, mobile, or reduced-motion). */
@media (min-width: 1025px) and (pointer: fine) {
  .hero-mark-img,
  .about-mark-img {
    will-change: transform;
  }
}

/* Hero legibility scrim: a same-colour wash that darkens the left text
   column over the field and fades to clear on the open right, keeping
   the headline and body crisp. Sits above the field and below the hero
   content (z 10). TM7: navy base tone, invisible over the base on the
   gated-off fallback, so it costs nothing there. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* TM13: strengthened from 0.85/0.55 and extended to 66% so the headline
     and body stay legible over the brighter, louder, moving field. */
  background: linear-gradient(
    90deg,
    rgba(10, 15, 30, 0.92) 0%,
    rgba(10, 15, 30, 0.66) 32%,
    rgba(10, 15, 30, 0) 66%
  );
}

/* Reduced-motion: no live canvas at all, static unified-tone page. */
@media (prefers-reduced-motion: reduce) {
  .field-canvas { display: none; }

  /* TM6: belt and braces. .reveal-ready is never added for these users,
     so .reveal is already visible, but force the visible, untransformed,
     untransitioned state regardless so no reveal motion can occur. */
  .reveal {
    opacity: 1 !important;
    transform: none !important;
    /* TM15: neutralise the clip mask too, so the copy is fully visible. */
    clip-path: none !important;
  }
  .reveal.is-visible { transition: none !important; }

  /* TM10: belt and braces. .kinetic-ready is never added for these users,
     so the headline is already visible, but force the visible, untransformed,
     untransitioned state regardless so no reveal motion can occur. */
  .hero-headline .kinetic-inner {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  /* TM8 + TM15: no hover movement and no tilt or sheen motion. The cyan
     accent (border and glow) may still appear instantly as a static
     affordance, but the lift and the pointer tilt are removed so nothing
     translates or rotates, and the sheen overlay is hidden. The glass stays
     (it is not motion). The scroll parallax never initialises here at all,
     because animations.js bails out on reduced-motion before loading the
     motion stack, so there is nothing further to neutralise for it. The
     tilt/sheen js in main.js is likewise gated off for reduced-motion. */
  .product-card { transform: none !important; }
  .product-card::after { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════
   TM9 — branded preloader
   A brief navy entrance overlay carrying the Tolmon mark on the cyan
   accent, held briefly, then faded out and removed by JS.

   HIDDEN BY DEFAULT. The overlay only ever shows when the synchronous
   head script adds .preloader-on to <html>, which it does only when
   prefers-reduced-motion is NOT set. So no-JS users (the class is never
   added) and reduced-motion users get the page immediately, with no
   overlay and no animation. The overlay is fixed and out of flow, so its
   presence and removal never shift layout (CLS 0); only opacity and
   transform are animated (GPU-composited, no layout properties).
═══════════════════════════════════════════════════════════ */
.preloader {
  display: none;
}

.preloader-on .preloader {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 1000; /* above the nav (100) and the field (0/2) */
  align-items: center;
  justify-content: center;
  background: var(--bg); /* navy base tone, matching the page */
  opacity: 1;
  transition: opacity 0.45s ease; /* FADE_MS in the dismiss script */
}

/* The cyan glow: a soft radial wash behind the mark, faded in via opacity
   only. CSS gradients are not LCP candidates and opacity is composited. */
.preloader-on .preloader::before {
  content: '';
  position: absolute;
  width: min(60vmin, 380px);
  height: min(60vmin, 380px);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(6, 182, 212, 0.30) 0%,
    rgba(6, 182, 212, 0) 68%
  );
  opacity: 0;
  animation: preloader-glow-in 0.9s ease forwards;
  will-change: opacity;
}

/* The inlined brand mark: a gentle fade and slight scale entrance. */
.preloader-on .preloader-mark {
  position: relative;
  width: min(22vmin, 104px);
  height: auto;
  opacity: 0;
  transform: scale(0.92);
  animation: preloader-mark-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  will-change: transform, opacity;
}

/* Fade-out: JS adds .is-leaving, then removes the node from the DOM after
   the fade so the overlay can never block interaction or assistive tech. */
.preloader-on .preloader.is-leaving {
  opacity: 0;
  pointer-events: none;
}

@keyframes preloader-glow-in {
  to { opacity: 1; }
}

@keyframes preloader-mark-in {
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* Belt and braces: even if .preloader-on were somehow present for a
   reduced-motion user, never show the overlay or run its animation. The
   head script already prevents the class, so this is defensive only. */
@media (prefers-reduced-motion: reduce) {
  .preloader,
  .preloader-on .preloader {
    display: none !important;
    animation: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════
   TM16: cinematic film grain
   A faint full-page noise wash, on theme for a film and TV brand. It is
   pure CSS (no JavaScript, no canvas): the texture is an inline SVG
   feTurbulence fractal-noise data URI, so it costs no network request and
   never repaints the moving field.

   SAFETY, in order of importance:
   - pointer-events: none, always. The overlay must never intercept the
     TM15 card tilt or sheen, any hover, or any click. This is the rule.
   - The opacity is kept low so text contrast is unaffected and the layer
     reads as a subtle filmic texture, not a dirty screen.
   - Fixed and out of flow, so it never shifts layout (CLS 0).
   - It sits above the content (z 900, above the nav at 100) for the
     filmic-over-everything look, but BELOW the TM9 preloader (z 1000), so
     the load screen stays clean.
   - No mix-blend-mode: plain low opacity is the safe default and avoids
     forcing a full-screen blend recomposite over the animating field.
═══════════════════════════════════════════════════════════ */
.film-grain {
  position: fixed;
  inset: 0;
  z-index: 900; /* above the nav (100) and content (1), below the preloader (1000) */
  pointer-events: none; /* never block the cards, hovers, or clicks */
  overflow: hidden; /* clip the oversized inner tile so its edges never show */
}

/* The texture lives on a pseudo-element sized larger than the viewport, so
   the gentle desktop flicker (a cheap GPU-composited transform on the inner
   tile) can never expose an edge. baseFrequency is high and there are a few
   octaves, for fine film grain rather than coarse blotches; saturate 0 keeps
   it monochrome so it reads as grain, not colour speckle; stitchTiles makes
   the tile repeat seamlessly. */
.film-grain::before {
  content: '';
  position: absolute;
  top: -100%;
  left: -100%;
  width: 300%;
  height: 300%;
  opacity: 0.05; /* subtle filmic layer, not a dirty screen */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='tmgrain'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23tmgrain)'/%3E%3C/svg%3E");
  background-size: 220px 220px;
  background-repeat: repeat;
}

/* Optional gentle film flicker. It is a transform-only jitter on the inner
   tile, so it is GPU-composited (no repaint of the grain layer and none of
   the field beneath it) and stays cheap. It is gated to desktop fine-pointer
   so mobile ships STATIC grain and pays no battery for it, and it is disabled
   entirely under reduced motion below. The static grain itself always renders
   on every device. */
@media (min-width: 1025px) and (pointer: fine) {
  .film-grain::before {
    animation: film-grain-flicker 0.7s steps(6) infinite;
    will-change: transform;
  }
}

@keyframes film-grain-flicker {
  0%   { transform: translate3d(0, 0, 0); }
  20%  { transform: translate3d(-8%, 5%, 0); }
  40%  { transform: translate3d(6%, -7%, 0); }
  60%  { transform: translate3d(-5%, 9%, 0); }
  80%  { transform: translate3d(9%, -4%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* Reduced motion: disable the flicker entirely. The static grain remains,
   which carries no motion and is legible. */
@media (prefers-reduced-motion: reduce) {
  .film-grain::before {
    animation: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════
   TM17: animated link underlines
   Nav links, footer links, and the in-body footer links get a clean cyan
   underline that draws in from the left on hover AND on keyboard focus, so
   keyboard navigation keeps the same affordance the mouse gets.

   SAFETY, in order of importance:
   - The underline is a positioned ::after pseudo-element, never inline text
     decoration, so showing it never reflows the link or moves a neighbour
     (CLS 0). It animates transform: scaleX only, which is GPU-composited.
   - It appears on :focus-visible as well as :hover. The existing
     focus-visible outline on each link is left untouched, so keyboard users
     keep both the focus ring and the underline (a11y stays at 100).
   - The nav "Talk to us" pill is excluded (it is a .btn, not a text link),
     so its button styling is not double-decorated.
   - Under prefers-reduced-motion the transition is dropped, so the underline
     simply appears (no draw animation), still on both hover and focus.
═══════════════════════════════════════════════════════════ */
.nav-links li a:not(.nav-signin),
.footer-links a,
.footer-bottom a {
  position: relative;
}

.nav-links li a:not(.nav-signin)::after,
.footer-links a::after,
.footer-bottom a::after {
  content: '';
  position: absolute;
  height: 2px;
  background: var(--accent-teal); /* cyan: the family interactive thread */
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none; /* purely decorative; never intercepts the click */
}

/* Per-group offsets: inset under the nav link's text padding, and just
   below the unpadded footer links. */
.nav-links li a:not(.nav-signin)::after { left: 16px; right: 16px; bottom: 6px; }
.footer-links a::after  { left: 0; right: 0; bottom: -3px; }
.footer-bottom a::after { left: 0; right: 0; bottom: -2px; }

/* Draw in on hover AND on keyboard focus. */
.nav-links li a:not(.nav-signin):hover::after,
.nav-links li a:not(.nav-signin):focus-visible::after,
.footer-links a:hover::after,
.footer-links a:focus-visible::after,
.footer-bottom a:hover::after,
.footer-bottom a:focus-visible::after {
  transform: scaleX(1);
}

/* Reduced motion: no draw animation. The underline still appears instantly
   on hover and focus, so the affordance is preserved without motion. */
@media (prefers-reduced-motion: reduce) {
  .nav-links li a:not(.nav-signin)::after,
  .footer-links a::after,
  .footer-bottom a::after {
    transition: none;
  }
}
