/* ============================================================
   TaskLogix360 — base: reset, type, nav, footer, buttons,
   shared section primitives, FAQ, CTA band, gradient page hero.
   2026-07-09 (evening): retrofitted to the 4-Point Spacing System
   — all spatial values on the 4px scale, buttons element-first
   (40/48px heights), type snapped to the token scale. Embedded
   section stylesheets (hero.css etc.) are exempt (1:1 mandate).
   ROUND 2 (2026-07-11) NAV-SPRUCE:
   1) Nav elevated Radix-NavigationMenu style — soft accent hover
      pills, chevron rotates open, dropdown is a translucent
      backdrop-blur panel (fade + zoom + slide from top) with rich
      icon-tile rows + slide-in arrow; mobile menu = icon-tile rows
      with smooth open/close. Open state = .nav-drop.open /
      aria-expanded on the trigger (was aria-expanded on .nav-drop).
   2) Global type bump per Sam (bigger, consistent titles/subtitles):
      .h1 → clamp(34px,5vw,60px) · .h2 → clamp(32px,4.4vw,48px) ·
      .eyebrow → 13px/800/.11em · .lede → clamp(17px,1.8vw,21px)/1.6 ·
      .faq-title → clamp(32px,4.2vw,48px).
   ============================================================ */

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:88px}
body{
  margin:0;overflow-x:clip;font-family:var(--font);color:var(--navy-deep);background:var(--cloud);
  font-size:var(--fs-base);line-height:1.5;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:var(--navy)}
:focus-visible{outline:2px solid var(--navy);outline-offset:2px;border-radius:6px}
/* skip-to-content link (a11y) — hidden until keyboard-focused */
.skip-link{position:absolute;left:var(--sp-2);top:var(--sp-2);z-index:200;background:var(--navy);color:#fff;
  padding:var(--sp-2) var(--sp-4);border-radius:8px;font-weight:600;text-decoration:none;transform:translateY(-150%);transition:transform .18s var(--ease)}
.skip-link:focus{transform:none}
/* focus ring on dark grounds (navy outline is invisible there) */
.cta-band :focus-visible,.tlx-footer :focus-visible{outline-color:#fff}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
  html{scroll-behavior:auto}
}

/* ---------------- Buttons (element-first: fixed heights, h-pad ≈ 2× v-space) ---------------- */
.btn{display:inline-flex;align-items:center;justify-content:center;height:var(--h-btn-lg);padding:0 var(--sp-6);
  border-radius:var(--radius);font-size:var(--fs-base);font-weight:700;text-decoration:none;cursor:pointer;
  transition:transform .2s var(--ease),box-shadow .2s var(--ease),background-color .2s var(--ease),border-color .2s var(--ease)}
.btn-primary{background:var(--green-aa);color:#fff;border:1.5px solid var(--green-aa);
  box-shadow:0 1px 2px rgba(8,54,93,.06),0 10px 22px -12px rgba(30,154,68,.6)}
.btn-primary:hover{background:var(--green-aa-deep);border-color:var(--green-aa-deep);transform:translateY(-2px);
  box-shadow:0 1px 2px rgba(8,54,93,.06),0 16px 30px -12px rgba(30,154,68,.7)}
.btn-secondary{background:#fff;color:var(--navy);border:1.5px solid var(--border);border-radius:var(--radius-lg);
  box-shadow:0 1px 2px rgba(8,54,93,.05),0 8px 18px -12px rgba(8,54,93,.25)}
.btn-secondary:hover{border-color:var(--navy);transform:translateY(-2px);
  box-shadow:0 1px 2px rgba(8,54,93,.05),0 12px 24px -14px rgba(11,70,120,.5)}
.btn-navy{background:var(--navy);color:#fff;border:1.5px solid var(--navy)}
.btn-navy:hover{background:var(--navy-deep);border-color:var(--navy-deep);transform:translateY(-2px);
  box-shadow:0 10px 22px -12px rgba(8,54,93,.55)}
.btn-ghost-dark{background:rgba(255,255,255,.04);color:#fff;border:1.5px solid rgba(255,255,255,.35);border-radius:var(--radius)}
.btn-ghost-dark:hover{border-color:#fff;transform:translateY(-2px)}
/* arrow text-link (industry cards) */
.arrow-link{display:inline-flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-base);font-weight:700;color:var(--navy);
  text-decoration:underline;text-underline-offset:3px}
.arrow-link:hover{color:var(--green-aa)}
.arrow-link svg{width:var(--sp-4);height:var(--sp-4);transition:transform .18s var(--ease)}
.arrow-link:hover svg{transform:translateX(3px)}

/* ---------------- Badge pill ---------------- */
.badge-pill{display:inline-flex;align-items:center;gap:var(--sp-2);height:var(--h-btn-sm);padding:0 var(--sp-4);border-radius:999px;
  background:#fff;border:1px solid var(--border);box-shadow:var(--shadow-card);
  font-size:var(--fs-xs);font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--navy)}
.badge-pill .dot{width:var(--sp-2);height:var(--sp-2);border-radius:50%;background:var(--green);flex-shrink:0}
.badge-pill.on-dark{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.22);color:#fff}

/* ---------------- Nav: floating pill bar ---------------- */
.tlx-nav{position:fixed;top:0;left:0;right:0;z-index:100;padding:var(--sp-3) clamp(12px,1.6vw,24px) 0}
.nav-inner{position:relative;max-width:var(--max-content);margin:0 auto;display:flex;align-items:center;gap:var(--sp-5);
  background:#fff;border-radius:var(--radius-lg);padding:var(--sp-3) var(--sp-4) var(--sp-3) var(--sp-6);
  box-shadow:0 1px 2px rgba(8,54,93,.06),0 12px 32px -18px rgba(8,54,93,.35);
  transition:box-shadow .25s var(--ease)}
.tlx-nav.scrolled .nav-inner{box-shadow:0 1px 2px rgba(8,54,93,.1),0 16px 40px -18px rgba(8,54,93,.45)}
.nav-logo{display:flex;align-items:center;text-decoration:none;margin-right:auto;flex-shrink:0}
/* 2026-07-28: 32px -> 40px per Sam. 40px == --h-btn, so the nav pills still set
   .nav-inner's height and the bar stays 64px: no scroll-padding-top knock-on. */
.nav-logo img{height:var(--sp-10);width:auto;aspect-ratio:526/128;flex-shrink:0}
.nav-links{display:flex;align-items:center;gap:var(--sp-1);margin:0 auto}
.nav-links>a,.nav-drop-btn{display:inline-flex;align-items:center;gap:var(--sp-1);height:var(--h-btn);padding:0 var(--sp-4);border-radius:9px;
  font-size:var(--fs-base);font-weight:500;color:var(--navy-deep);text-decoration:none;transition:background-color .18s var(--ease),color .18s var(--ease)}
.nav-links>a:hover,.nav-drop-btn:hover,.nav-drop.open .nav-drop-btn{background:var(--navy-50);color:var(--navy)}
.nav-links>a.active{background:#EEF1F4;color:var(--navy-deep);font-weight:600}
.tlxnav-ico{display:none} /* icon tiles only surface in the mobile menu */
.nav-cta{margin-left:auto;height:var(--h-btn);padding:0 var(--sp-5);font-size:var(--fs-sm);border-radius:12px}
.nav-links>a.nav-menu-cta{display:none} /* only surfaces inside the mobile menu at <=480px; specificity must beat .nav-links>a */

/* dropdown — translucent blurred panel, fade + zoom-95 + slide from top */
.nav-drop{position:relative}
.nav-drop::before{content:"";position:absolute;left:calc(-1*var(--sp-2));right:calc(-1*var(--sp-2));top:100%;height:var(--sp-3)} /* hover bridge over the gap */
.nav-drop-btn{background:transparent;border:0;cursor:pointer;font:inherit}
.nav-drop-btn .chev{width:var(--sp-3);height:var(--sp-3);transition:transform .2s var(--ease)}
.nav-drop.open .chev{transform:rotate(180deg)}
/* 2026-07-28: cap raised 440 -> 480px. The menu is now the SERVICE CATALOG (Enterprise +
   Closeout & QC, more to come), and .nav-drop-desc is white-space:nowrap with no ellipsis
   fallback, so an over-long description overflows silently. Keep every desc <= 50 chars. */
.nav-drop-menu{position:absolute;top:calc(100% + var(--sp-2));left:50%;width:max-content;max-width:min(480px,calc(100vw - 32px));
  background:rgba(255,255,255,.88);-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);
  border:1px solid rgba(8,54,93,.1);border-radius:14px;padding:var(--sp-2);
  box-shadow:0 1px 2px rgba(8,54,93,.06),0 12px 24px -12px rgba(8,54,93,.2),0 32px 64px -24px rgba(8,54,93,.32);
  transform-origin:top;opacity:0;visibility:hidden;pointer-events:none;
  transform:translateX(-50%) translateY(-6px) scale(.95);
  transition:opacity .22s var(--ease),transform .22s var(--ease),visibility 0s linear .22s}
.nav-drop.open .nav-drop-menu{opacity:1;visibility:visible;pointer-events:auto;
  transform:translateX(-50%) translateY(0) scale(1);
  transition:opacity .22s var(--ease),transform .22s var(--ease)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){.nav-drop-menu{background:#fff}}
/* rich rows: icon tile + title + one-line description + slide-in arrow */
.nav-drop-item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3);border-radius:10px;text-decoration:none;
  transition:background-color .16s var(--ease)}
.nav-drop-item:hover{background:rgba(11,70,120,.06)}
.nav-tile{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;width:var(--sp-10);height:var(--sp-10);border-radius:10px;
  background:var(--navy-50);border:1px solid rgba(11,70,120,.12);color:var(--navy);
  transition:background-color .16s var(--ease),color .16s var(--ease),border-color .16s var(--ease)}
.nav-tile svg{width:20px;height:20px}
.nav-drop-item:hover .nav-tile{background:var(--green-50);border-color:rgba(30,154,68,.25);color:var(--green-dark)}
.nav-drop-txt{display:flex;flex-direction:column;gap:var(--sp-05);min-width:0}
.nav-drop-title{font-size:var(--fs-sm);line-height:1.3;font-weight:600;color:var(--navy-deep)}
.nav-drop-desc{font-size:var(--fs-sm);line-height:1.4;color:var(--slate-light);white-space:nowrap}
.nav-drop-arrow{flex-shrink:0;width:var(--sp-4);height:var(--sp-4);margin-left:auto;color:var(--green-dark);
  opacity:0;transform:translateX(-6px);transition:opacity .18s var(--ease),transform .18s var(--ease)}
.nav-drop-item:hover .nav-drop-arrow,.nav-drop-item:focus-visible .nav-drop-arrow{opacity:1;transform:none}
.nav-drop-item.active .nav-drop-title{color:var(--green-dark)}
.nav-drop-item.active .nav-tile{background:var(--green-50);border-color:rgba(30,154,68,.25);color:var(--green-dark)}
/* 2026-07-28: the Solutions trigger inherits the active chip when one of its rows is the
   current page. Required now that Enterprise lives in the panel: .nav-links>a.active (L86)
   is a CHILD combinator, so it stops matching once product.html's link moves into the
   dropdown, and the closed desktop nav would show NO current-page state on product.html or
   qc-inspections.html. site.js already sets .active on .nav-drop-item (descendant selector).
   :has() is already a shipped dependency here (tier-compare.css L164-188); without it this
   rule is simply dropped and you get the pre-2026-07-28 behaviour. */
.nav-drop:has(.nav-drop-item.active) .nav-drop-btn{background:#EEF1F4;color:var(--navy-deep);font-weight:600}
/* legacy chip kept only for read-only v-* variants (removed from real nav per Sam) */
.nav-drop-menu .soon{display:inline-block;margin-left:var(--sp-2);padding:var(--sp-05) var(--sp-2);border-radius:999px;background:var(--green-50);
  color:var(--green-dark);font-size:var(--fs-sm);font-weight:700;letter-spacing:.06em;text-transform:uppercase}

/* mobile */
.nav-burger{display:none;background:transparent;border:0;cursor:pointer;padding:var(--sp-2);min-width:44px;min-height:44px}
/* Bar PITCH drives the X transform below: centre-to-centre = --bar-h + 2*--bar-gap.
   The outer bars must travel exactly one pitch to meet in the middle, so that travel
   is CALCULATED from these two properties rather than typed as a literal. Before
   2026-08-06 it was typed: margin:5px gave a 12px pitch against a hard-coded 7px
   travel, so the "X" was two parallel diagonals 10px apart, on every page of the
   site. Measured, not guessed - _shots.js reports burgerPitch. */
.nav-burger span{--bar-h:2px;--bar-gap:var(--sp-1);
  display:block;width:var(--sp-6);height:var(--bar-h);background:var(--navy-deep);margin:var(--bar-gap) 0;border-radius:2px;transition:transform .2s var(--ease),opacity .2s}
@media (max-width:860px){
  .nav-inner{gap:var(--sp-2);padding-left:var(--sp-3);padding-right:var(--sp-3)}
  .nav-links{position:absolute;top:calc(100% + var(--sp-2));left:0;right:0;flex-direction:column;align-items:stretch;gap:var(--sp-1);max-height:calc(100dvh - 96px);overflow-y:auto;
    background:rgba(255,255,255,.94);-webkit-backdrop-filter:blur(14px) saturate(1.4);backdrop-filter:blur(14px) saturate(1.4);
    border-radius:var(--radius-lg);border:1px solid rgba(8,54,93,.1);padding:var(--sp-3);box-shadow:0 24px 48px -24px rgba(8,54,93,.45);
    transform-origin:top;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-8px) scale(.98);
    transition:opacity .22s var(--ease),transform .22s var(--ease),visibility 0s linear .22s}
  .tlx-nav.menu-open .nav-links{opacity:1;visibility:visible;pointer-events:auto;transform:none;
    transition:opacity .22s var(--ease),transform .22s var(--ease)}
  /* icon-tile rows (NavItemMobile) */
  .nav-links>a{height:auto;padding:var(--sp-2) var(--sp-3);gap:var(--sp-3);border-radius:10px;font-weight:600}
  .tlxnav-ico{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--sp-10);height:var(--sp-10);border-radius:10px;
    background:var(--navy-50);border:1px solid rgba(11,70,120,.12);color:var(--navy)}
  .tlxnav-ico svg{width:var(--sp-5);height:var(--sp-5)}
  .nav-links>a.active .tlxnav-ico{background:var(--green-50);border-color:rgba(30,154,68,.25);color:var(--green-dark)}
  /* Solutions flattens to a top-level icon row (trigger hidden, rich row shown inline) */
  .nav-drop{position:static}
  .nav-drop::before{display:none}
  .nav-drop-btn{display:none}
  /* inherit visibility + pointer-events from .nav-links so the flattened dropdown
     is only interactive when the menu is OPEN. Hard-coding visible/auto here left
     the QC link clickable while the menu was closed+invisible → phantom taps near
     the top of the screen routed to qc-inspections.html (fixed 2026-07-15). */
  .nav-drop-menu{position:static;width:auto;max-width:none;opacity:1;visibility:inherit;pointer-events:inherit;transform:none;
    background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none;border:0;box-shadow:none;padding:0;transition:none}
  /* .open is a DESKTOP-only state (the trigger is display:none here) and site.js
     has no resize handler, so it SURVIVES a narrow-down: hover Solutions at
     1200px, drag the window to 700px, and .nav-drop.open .nav-drop-menu keeps
     applying translateX(-50%). That selector is 0,2,0 against this block's 0,1,0
     and a media query adds no specificity, so it wins and shifts the flattened
     panel half its own width off the left edge. Found 2026-08-06 while doing the
     base.css 4-point pass. */
  .nav-drop.open .nav-drop-menu{transform:none}
  .nav-drop-item{padding:var(--sp-2) var(--sp-3)}
  .nav-drop-title{font-size:var(--fs-base);font-weight:600}
  .nav-drop-desc{white-space:normal}
  .nav-drop-arrow{display:none}
  .nav-cta{margin-left:var(--sp-2);white-space:nowrap;padding:0 var(--sp-4)}
  .nav-burger{display:flex;flex-direction:column;align-items:center;justify-content:center}
  .tlx-nav.menu-open .nav-burger span:nth-child(1){transform:translateY(calc(var(--bar-h) + 2*var(--bar-gap))) rotate(45deg)}
  .tlx-nav.menu-open .nav-burger span:nth-child(2){opacity:0}
  .tlx-nav.menu-open .nav-burger span:nth-child(3){transform:translateY(calc(-1*(var(--bar-h) + 2*var(--bar-gap)))) rotate(-45deg)}
}

/* narrow phones: header gets cramped -> move the CTA into the open menu */
@media (max-width:480px){
  .nav-cta{display:none}
  .nav-links>a.nav-menu-cta{display:inline-flex;width:100%;justify-content:center;height:var(--h-btn-lg);
    padding:0 var(--sp-6);margin-top:var(--sp-5);border-radius:var(--radius);
    background:var(--green-aa);border-color:var(--green-aa);color:#fff}
  /* keep the green CTA legible on tap/hover — must beat .nav-links>a:hover (light bg) */
  .nav-links>a.nav-menu-cta:hover,.nav-links>a.nav-menu-cta:active,.nav-links>a.nav-menu-cta:focus-visible{
    background:var(--green-aa-deep);border-color:var(--green-aa-deep);color:#fff}
}

/* ---------------- Shared section primitives ---------------- */
.tlx-section{padding:clamp(64px,8vw,112px) clamp(20px,5vw,64px)}
.tlx-section-inner{max-width:var(--max-content);margin:0 auto}
.tlx-section-copy{max-width:var(--max-copy);margin:0 auto;text-align:center}
.eyebrow{display:inline-block;font-size:var(--fs-sm);line-height:var(--lh-xs);font-weight:800;letter-spacing:.11em;text-transform:uppercase;
  color:var(--green-aa);margin-bottom:var(--sp-3)}
.h2{margin:0 0 var(--sp-4);font-size:clamp(32px,4.4vw,48px);font-weight:800;letter-spacing:var(--ls-heading);line-height:var(--lh-heading);color:var(--navy-deep)}
.h2 .accent{color:var(--green)}
.lede{margin:0 auto;max-width:760px;font-size:clamp(17px,1.8vw,21px);line-height:1.6;color:var(--slate)}
.section-cta{margin-top:var(--sp-8)}
/* ---------------- Scroll reveal engine (entrance choreography) ----------------
   .reveal (+ optional variant) animates in when scrolled into view — site.js adds
   .in once, at rootMargin -12% so it fires just as the element enters. .stagger
   cascades its .reveal children (site.js sets --rv-delay per child). Sections that
   carry their OWN keyframe intro use [data-anim-in]: their animations stay paused
   until the observer adds .rv-seen, so nothing plays off-screen. Reduced-motion =
   final state, no motion. Only opacity/transform/clip animate (GPU, no reflow). */
/* Expressive vocabulary (Sam-approved 2026-07-14): base rise 40px/800ms, pop easing
   for scale-in, plus signature variants — rv-wipe (image clip-wipe L→R + zoom-settle),
   rv-scale (scale-pop with overshoot), rv-blur (blur-in), rv-sweep (accent underline). */
:root{--rv-ease:cubic-bezier(.16,1,.3,1);--rv-ease-pop:cubic-bezier(.34,1.45,.5,1);--rv-dur:.8s}
.reveal{opacity:0;transform:translateY(40px);
  transition:opacity var(--rv-dur) var(--rv-ease),transform var(--rv-dur) var(--rv-ease),clip-path var(--rv-dur) var(--rv-ease),filter var(--rv-dur) var(--rv-ease);
  transition-delay:var(--rv-delay,0ms)}
.reveal.rv-sm{transform:translateY(18px)}                                   /* eyebrows, inline bits */
.reveal.rv-fade{transform:none}                                            /* opacity only */
.reveal.rv-scale{transform:translateY(26px) scale(.9);transition-timing-function:var(--rv-ease-pop)} /* feature visuals: scale-pop */
.reveal.rv-wipe{transform:scale(1.05);clip-path:inset(0 100% 0 0)}          /* images: wipe L→R + zoom-settle */
.reveal.rv-blur{transform:translateY(26px);filter:blur(7px)}               /* body/list: blur-in */
.reveal.in{opacity:1;transform:none;filter:none}
.reveal.rv-wipe.in{clip-path:inset(0 0 0 0)}   /* ONLY the wipe variant clips; a blanket clip-path on .reveal.in cut off any element whose children overflow its box (e.g. the orbital chips) */
/* Accent underline sweep — put .rv-sweep on a colored <span>; it inherits its color.
   🔴 Drawn as a CLONED BACKGROUND, not a positioned ::after (fixed 2026-08-07).
   The old rule was `position:relative` + `::after{position:absolute;left:0;right:0}`.
   That is correct only while the span occupies ONE line. An absolutely positioned
   child of an INLINE element resolves against the union of all its line
   fragments, so as soon as the span wrapped, the bar stretched across that whole
   bounding box while sitting under the LAST line.
   Measured on the home CTA at 390: line 1 spans x=32-358 and line 2 spans
   x=112-278, so the underline began 80px to the LEFT of the text it was
   underlining. Desktop never showed it because the span fits on one line there.
   box-decoration-break:clone gives every fragment its own background, so each
   wrapped line gets an underline the width of that line. The sweep still
   animates left-to-right, now via background-size instead of scaleX. */
.rv-sweep{
  position:relative;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;
  background-position:0 calc(100% + 2px);   /* matches the old bottom:-2px */
  background-size:0 var(--sp-1);
  -webkit-box-decoration-break:clone;
  box-decoration-break:clone;
  transition:background-size .55s var(--rv-ease) .3s;
}
.rv-sweep.in{background-size:100% var(--sp-1)}
[data-anim-in]:not(.rv-seen) *{animation-play-state:paused !important}
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1 !important;transform:none !important;clip-path:none !important;filter:none !important;transition:none !important}
  .rv-sweep{background-size:100% var(--sp-1);transition:none}
  [data-anim-in] *{animation-play-state:running !important}
}

/* ---------------- Gradient grid page hero ---------------- */
.grid-hero{position:relative;margin:0 clamp(12px,1.6vw,24px);border-radius:0 0 16px 16px;overflow:hidden;
  padding:clamp(152px,17vw,224px) clamp(20px,5vw,64px) clamp(72px,8vw,112px);text-align:center;
  background:
    linear-gradient(115deg, rgba(75,105,140,.55) 0%, rgba(160,185,210,.25) 22%, rgba(255,255,255,0) 46%),
    radial-gradient(90% 90% at 88% 55%, rgba(30,154,68,.32) 0%, rgba(30,154,68,0) 60%),
    linear-gradient(160deg,#F4F8FC 0%,#FDFEFE 50%,#EAF6EE 100%)}
.grid-hero::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:linear-gradient(rgba(11,70,120,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(11,70,120,.07) 1px,transparent 1px);
  background-size:56px 56px}
.grid-hero>*{position:relative}
.grid-hero .badge-pill{margin-bottom:var(--sp-6)}
.h1{margin:0 0 var(--sp-5);font-size:clamp(34px,5vw,60px);font-weight:800;letter-spacing:var(--ls-heading);line-height:var(--lh-heading);color:var(--navy-deep)}
.h1 .accent{color:var(--green)}
.grid-hero .lede{max-width:760px}
.grid-hero .cta-row{display:flex;flex-wrap:wrap;gap:var(--sp-4);justify-content:center;margin-top:var(--sp-8)}
@keyframes fadeUpBase{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.anim-in{opacity:0;animation:fadeUpBase .6s var(--ease) forwards}
.anim-in.d1{animation-delay:.15s}.anim-in.d2{animation-delay:.3s}

/* ---------------- Rounded dark CTA panel ---------------- */
/* Ground unified 2026-08-07 (Sam: "give the CTA on the enterprise page the same
   background as the one on the home page"). This was its own bespoke navy —
   #12507F -> navy-deep -> #052A3E at 125deg — the last dark band on the site
   still painting its own colour after the --gd-* pass. Moving the home formula
   here rather than copying it onto enterprise means all four CTA bands (home,
   enterprise, qc-inspections, markets) are identical by construction and follow
   the tuner like everything else. index.html keeps its own --bleed rules for
   SHAPE (margin/radius) and its pointer-reactive dots canvas; only the ground
   is shared. */
.cta-band{position:relative;margin:0 clamp(12px,1.6vw,24px);padding:clamp(64px,8vw,96px) clamp(20px,5vw,64px);
  border-radius:16px;overflow:hidden;text-align:center;
  background:
    radial-gradient(120% 78% at 50% 118%, rgba(30,154,68,calc(var(--gd-glow-green) + .12)) 0%, rgba(30,154,68,0) 60%),
    radial-gradient(85% 55% at 14% -8%, rgba(19,88,146,var(--gd-glow-blue)) 0%, rgba(19,88,146,0) 62%),
    linear-gradient(var(--gd-angle),var(--gd-1) 0%,var(--gd-2) 46%,var(--gd-3) 100%)}
/* Full-bleed modifier. Moved out of index.html's inline <style> 2026-08-07 so
   enterprise can use it too; index keeps its .ctab-dots canvas rules inline
   because those are genuinely page-specific. */
.cta-band--bleed{margin:0;border-radius:0}
.cta-band::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;
  background-image:radial-gradient(rgba(255,255,255,.13) 1.1px,transparent 1.1px);background-size:26px 26px;
  -webkit-mask-image:radial-gradient(90% 100% at 50% 50%,#000 30%,transparent 100%);
  mask-image:radial-gradient(90% 100% at 50% 50%,#000 30%,transparent 100%)}
.cta-band::after{content:"";position:absolute;pointer-events:none;left:50%;bottom:-45%;width:130%;height:95%;transform:translateX(-50%);
  background:radial-gradient(50% 60% at 50% 100%,rgba(30,154,68,.32) 0%,rgba(30,154,68,0) 70%);
  animation:tlxCtaGlow 8s var(--ease) infinite alternate}
@keyframes tlxCtaGlow{from{opacity:.55}to{opacity:1}}
.cta-band>*{position:relative;z-index:1}
.cta-band .badge-pill{margin-bottom:var(--sp-6)}
.cta-band .eyebrow{color:var(--green-bright)}
.cta-band .h2{color:#fff}
.cta-band .h2 .accent{color:var(--green-bright)}
.cta-band .lede{color:rgba(255,255,255,.8)}
.cta-band .cta-row{display:flex;flex-wrap:wrap;gap:var(--sp-4);justify-content:center;margin-top:var(--sp-8)}
@media (prefers-reduced-motion:reduce), (max-width:860px){.cta-band::after{animation:none}}

/* ---------------- FAQ (rule-separated, "General" tab) ---------------- */
.faq{background:var(--surface)}
.faq-inner{max-width:960px;margin:0 auto}
.faq-title{margin:0 0 var(--sp-6);font-size:clamp(32px,4.2vw,48px);font-weight:800;letter-spacing:var(--ls-heading);color:#101828;text-align:left}
.faq-tab{display:inline-block;padding:0 var(--sp-1) var(--sp-2);margin-bottom:var(--sp-2);font-size:var(--fs-base);font-weight:600;color:var(--green-aa);
  border-bottom:2.5px solid var(--green)}

/* --- FAQ CATEGORY TABS (2026-08-07; all five FAQ pages 2026-08-13) ---------
   Opt-in: a page only gets these if _faq-round4.py emitted a `groups` set for
   it. All five FAQ pages now do (2 tabs on index/contact, 3 on
   qc-inspections/enterprise/markets). A page without a `groups` set keeps the
   single static `.faq-tab` span above and is untouched by this block.
   The ACTIVE tab is drawn to match that span exactly — same green, same 2.5px
   rule — so switching a page between one tab and many is not also a restyle.
   Inactive tabs are grey with a transparent rule of the SAME width, so nothing
   shifts by a pixel when the selection moves. */
.faq-tabs{display:flex;flex-wrap:wrap;gap:var(--sp-6);margin-bottom:var(--sp-2);
  border-bottom:1px solid #33475B26}
.faq-tabs .faq-tab{appearance:none;background:transparent;border:0;cursor:pointer;font:inherit;
  display:inline-block;padding:0 var(--sp-1) var(--sp-2);margin-bottom:-1px;
  font-size:var(--fs-base);font-weight:600;color:#5A6B7E;
  border-bottom:2.5px solid transparent;
  transition:color .2s var(--ease),border-color .2s var(--ease)}
.faq-tabs .faq-tab:hover{color:#101828}
.faq-tabs .faq-tab[aria-selected="true"]{color:var(--green-aa);border-bottom-color:var(--green)}
.faq-tabs .faq-tab:focus-visible{outline:2px solid var(--green-aa);outline-offset:3px;border-radius:3px}
/* 🔴 The tab bar is HIDDEN until site.js claims it, and panels are only ever
   hidden by the [hidden] attribute that same script sets. Nothing in CSS
   pre-collapses the list.

   An earlier cut gated the collapse on html.tlx-js. That was wrong: .tlx-js
   means "scripting is enabled", not "site.js ran" — so blocking site.js still
   collapsed the list and left four questions unreachable, which is precisely
   the stale-cache failure this was meant to prevent. The element that hides
   content must be written by the code that can reveal it again.

   NO JS / FAILED JS: no tab bar, all ten questions in one plain list. */
.faq-tabs-wrap{display:none;position:relative}
.faq-inner.faq-tabs-ready .faq-tabs-wrap{display:block}
.faq-panel[hidden]{display:none}

/* Narrow viewports: scroll the bar sideways instead of wrapping it (2026-08-13,
   when qc/enterprise/markets gained a third category).
   Three labels measure 505-564px against a 350px bar on a 390px phone, so
   flex-wrap stacked them into 2-3 full-width rows: the active tab's green rule
   floated mid-block and the group read as three unrelated links, not a tab bar.
   Shorter labels do not rescue it — even three SHORT labels overflow 350px — so
   the fix is the layout, not the copy (which is frozen for review anyway).
   Tab 2 stays half-visible at the right edge, which is the affordance that says
   "this scrolls". Desktop is untouched: the bar fits, so nothing here fires. */
/* --- scroll affordance (2026-08-13) ---------------------------------------
   Sam: "let them scroll the bar over so they can see the tab, and make it
   obvious that they can scroll." A half-visible tab at the edge was the only
   hint and it is far too quiet, so the strip now says so three ways: the tabs
   FADE OUT at whichever edge has more content, an ARROW BUTTON sits at that
   edge, and the bar NUDGES once the first time it scrolls into view.
   🔴 The fade is a MASK on the tabs, not a coloured gradient over them. The FAQ
   ground is not the same colour on every page (white on home/contact/qc,
   #F9FAFB on enterprise/markets), so a gradient would have to be re-matched per
   page and would go wrong the moment a section's background changed. A mask
   fades the content itself and is right on any ground.
   .can-l / .can-r are set by site.js from real scroll position, so an edge with
   nothing beyond it never shows a fade that promises more.

   The arrows are a real BUTTON, not a chevron drawn on a pseudo-element. As a
   glyph it landed on top of the last tab's letters and read as clutter, and it
   only ever said "you could swipe" — which is no help at all with a mouse on a
   narrow window. As a button it is unmistakably a control and it works. They are
   built by site.js, so they never exist without the JS that scrolls them, and
   they stay out of the page source a compliance reviewer reads.
   `aria-hidden` + `tabindex=-1` on purpose: they duplicate the arrow-key
   behaviour the tablist already has, so exposing them would add two redundant
   stops inside a tablist. Not focusable, so aria-hidden is legitimate here. */
.faq-scroll-btn{position:absolute;top:0;bottom:0;margin:auto 0;
  display:none;align-items:center;justify-content:center;
  width:var(--sp-8);height:var(--sp-8);padding:0;
  border:1px solid #33475B26;border-radius:50%;
  background:var(--faq-fade,#fff);color:#33475B;cursor:pointer;
  box-shadow:0 1px 3px rgba(8,54,93,.10);
  transition:color .18s var(--ease),border-color .18s var(--ease)}
/* The visible chip stays 32px so it does not tower over a 40px tab row, but the
   touch target is 48px — a 32px control is under every mobile hit-size floor. */
.faq-scroll-btn::after{content:"";position:absolute;inset:calc(var(--sp-2) * -1)}
.faq-scroll-btn:hover{color:#101828;border-color:#33475B4D}
.faq-scroll-btn svg{display:block}
.faq-scroll-r{right:0}
.faq-scroll-l{left:0}
.faq-tabs-wrap.can-r .faq-scroll-r,.faq-tabs-wrap.can-l .faq-scroll-l{display:flex}
/* Mask only the side that actually continues, so a bar scrolled hard right does
   not fade into empty space on the right. */
.faq-tabs-wrap.can-r .faq-tabs{
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - var(--sp-16)),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - var(--sp-16)),transparent)}
.faq-tabs-wrap.can-l .faq-tabs{
  -webkit-mask-image:linear-gradient(to left,#000 calc(100% - var(--sp-16)),transparent);
  mask-image:linear-gradient(to left,#000 calc(100% - var(--sp-16)),transparent)}
.faq-tabs-wrap.can-l.can-r .faq-tabs{
  -webkit-mask-image:linear-gradient(to right,transparent,#000 var(--sp-16),#000 calc(100% - var(--sp-16)),transparent);
  mask-image:linear-gradient(to right,transparent,#000 var(--sp-16),#000 calc(100% - var(--sp-16)),transparent)}

@media (max-width:860px){
  /* The rule moves onto the FRAME at this size. Left on the scroller it would
     be masked along with the tabs, so the fade would visibly chew a hole in the
     line under the tab bar. */
  .faq-tabs-wrap{border-bottom:1px solid #33475B26}
  .faq-tabs{border-bottom:0}
  .faq-tabs{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;gap:var(--sp-5);
    /* Bottom: the tabs' margin-bottom:-1px hangs 1px past the content box, which
       overflow-y:hidden would clip off the active underline. 1px of padding puts
       it back inside (a 1px stroke compensation, exempt from the 4pt grid as a
       hairline). Top: the focus ring is outline-offset:3px on a 2px outline, so
       it needs 5px of clearance - taken as a full --sp-2 step rather than a bare
       5px, which is off the 4-point grid and tripped the spacing gate. The
       negative margin cancels the padding, so nothing on the page shifts. */
    padding-top:var(--sp-2);padding-bottom:1px;margin-top:calc(var(--sp-2) * -1);
    scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch}
  .faq-tabs::-webkit-scrollbar{display:none}
  .faq-tabs .faq-tab{flex:0 0 auto;white-space:nowrap}
}
.faq-list{text-align:left}
.faq-item{border-bottom:1px solid #33475B33}
.faq-q{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);width:100%;padding:var(--sp-6) 0;
  background:transparent;border:0;cursor:pointer;font:inherit;font-size:var(--fs-base);line-height:var(--lh-base);font-weight:500;color:#101828;text-align:left}
.faq-q .faq-icon{flex-shrink:0;width:var(--sp-6);height:var(--sp-6);color:#101828;display:inline-flex;align-items:center;justify-content:center;
  transition:transform .25s var(--ease)}
.faq-item[open-state="true"] .faq-icon{transform:rotate(180deg)}
.faq-a{max-height:0;overflow:hidden;transition:max-height .3s var(--ease)}
.faq-a-inner{padding:0 0 var(--sp-6);font-size:var(--fs-base);line-height:1.7;color:#344054;max-width:920px}
/* FAQ round 4 (2026-08-06): Brandon's set brought the first answers that are not a
   single bare text node. "How is customer support handled?" carries a <ul> of the
   three service tiers, and "Where can I find more information about pricing?" carries
   three inline links (his explicit ask). Single-paragraph answers are still emitted as
   a bare text node, so nothing below changes an existing answer's rendering.
   Accordion height is safe: site.js:90 reads a.scrollHeight at click time. */
.faq-a-inner p{margin:0}
/* p + p is not enough: the support answer is p -> ul -> p, so the closing
   paragraph is not a sibling paragraph and collided with the last <li>. */
.faq-a-inner p + p,
.faq-a-inner .faq-a-list + p{margin-top:var(--sp-4)}
.faq-a-list{margin:var(--sp-4) 0 0;padding-left:var(--sp-5);list-style:disc}
.faq-a-list li{margin-top:var(--sp-2)}
.faq-a-list li::marker{color:var(--green)}
.faq-a-list b{color:#101828;font-weight:700}
/* Underlined, not colour-alone: the answer ground is #F9FAFB and links must survive
   a greyscale read. --navy on that ground clears AA comfortably; --green-aa is the
   5.0:1 green kept for text on light (never --green, which is a graphics colour). */
.faq-a-inner a{color:var(--navy);font-weight:600;text-decoration:underline;
  text-underline-offset:2px;text-decoration-thickness:1px;transition:color .18s var(--ease)}
.faq-a-inner a:hover{color:var(--green-aa)}

/* ---------------- Footer ---------------- */
/* Footer sits on the SAME --gd-* ramp as every dark section (2026-08-07). It
   used to run its own #0E4A7C->#062A49, which measured ~3x brighter in the
   gutter than the section bodies above it — that is why it read as bolted on.
   Same --gd-angle as every other band on purpose: the footer should read as
   one more step in the rhythm, not as its own object with its own light. */
.tlx-footer{background:linear-gradient(var(--gd-angle),var(--gd-1) 0%,var(--gd-2) 46%,var(--gd-3) 100%);
  color:rgba(255,255,255,.85);padding:clamp(48px,6vw,80px) clamp(20px,5vw,64px) 0}
.footer-inner{max-width:var(--max-content);margin:0 auto;display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:clamp(28px,4vw,56px)}
.footer-brand img{height:var(--sp-10);width:auto;aspect-ratio:657/160;margin-bottom:var(--sp-5)}
.footer-brand p{margin:0 0 var(--sp-4);font-size:var(--fs-sm);line-height:1.7;color:rgba(255,255,255,.85)}
.footer-brand .footer-email{display:inline-block;padding:var(--sp-2) 0;color:#fff;font-size:var(--fs-sm);font-weight:500}
/* social row — LinkedIn (added 2026-07-14): 40x40 gives an AA tap target on the navy footer */
.footer-social{display:flex;gap:var(--sp-3);margin-top:var(--sp-4)}
.footer-social a{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.16);color:#fff;
  transition:background-color .18s var(--ease),border-color .18s var(--ease),transform .18s var(--ease)}
.footer-social a:hover{background:var(--green-aa);border-color:var(--green-aa);transform:translateY(-2px)}
.footer-social svg{width:20px;height:20px;fill:currentColor}
.footer-col h2{margin:0 0 var(--sp-4);font-size:var(--fs-sm);font-weight:700;color:#fff}
.footer-col a{display:block;padding:var(--sp-3) 0;font-size:var(--fs-sm);color:rgba(255,255,255,.85);text-decoration:underline;text-underline-offset:3px}
.footer-col a:hover{color:#fff}
.footer-bottom{max-width:var(--max-content);margin:var(--sp-10) auto 0;padding:var(--sp-6) 0;border-top:1px solid rgba(255,255,255,.16);
  display:flex;flex-wrap:wrap;gap:var(--sp-3);align-items:center;justify-content:space-between;font-size:var(--fs-sm);color:rgba(255,255,255,.8)}
@media (max-width:900px){.footer-inner{grid-template-columns:1fr 1fr}}
@media (max-width:560px){.footer-inner{grid-template-columns:1fr}}

/* 2026-08-19: .arrow-link renders 24px tall on a phone and is a section CTA on
   markets, not an inline link. Hit area grown to 44px; nothing moves visually. */
.arrow-link{position:relative}
.arrow-link::after{content:"";position:absolute;left:0;right:0;top:-12px;bottom:-12px}


/* ---------------- mockup disclosure (2026-09-23 legal audit) ----------------
   The product screens carry invented operational data. The badges on them used
   to read LIVE, which asserted the opposite; they now read DEMO, and each
   mockup section carries this note. Placed WITH the mockup on purpose -- a
   disclosure that sits far from the claim it qualifies is the defect this
   audit found in the Oracle/SOC 2 note, 520 lines from its chip. */
.mock-note{margin:var(--sp-5) auto var(--sp-5);max-width:640px;text-align:center;
  font-size:13.5px;line-height:1.5;color:var(--slate)}
.mock-note.on-dark{color:rgba(255,255,255,.72)}
