/* ===========================================================================
   site.css - the shared design system for the three PUBLIC marketing pages
   (index.html, howitworks.html, security.html).

   Why this file exists: those three pages each carried their own verbatim copy
   of the same ~80 lines of tokens/nav/buttons/footer/reveal CSS, with a comment
   on each saying "ported verbatim from index.html". They had already drifted -
   index used the brand cyan --link (#0e7490) while the other two still used the
   generic #2f6fdb blue that tokens.css's own comment calls out as the reason the
   brand identity "never actually showed up anywhere users spend their day". One
   file removes the drift by construction.

   It is deliberately NOT static/css/app.css: that stylesheet locks html/body to
   height:100vh;overflow:hidden for the kiosk console shell, which is fatal for a
   page meant to scroll (the same reason tokens.css was split out for briefing.html).
   Tokens here are a subset of tokens.css, copied rather than @imported, because an
   @import costs a second blocking round-trip on the page a prospect sees first -
   and because the marketing site legitimately needs a few values the app has no
   use for (the hero aura, the grid mask).

   Referenced as {{ASSET:css/site.css}}, so it is served content-hashed and
   immutable, and editing it busts the referencing pages' own cached payloads.
   =========================================================================== */

:root {
  --bg: #f5f5f7; --surface: #ffffff; --surface-2: #f5f5f7; --border: #e3e3e6;
  --border-strong: #d5d5d9;
  --text: #1d1d1f; --text-muted: #6e6e73;
  --accent-bg: #1d1d1f; --accent-fg: #ffffff; --accent-tint: rgba(255,255,255,0.08);
  /* Lighter stop for the primary button's gradient - the app's own --accent-bg-hi.
     A flat black rectangle reads as an unstyled default; the gradient is what makes
     it read as a deliberate, weighted CTA (DESIGN_LANGUAGE.md). */
  --accent-bg-hi: #38383c;
  --success: #1a8f5f; --danger: #d1342a; --warning: #b7791f;
  /* One brand cyan, two named roles - a tappable link, and the AI signature. Same
     value today, distinct tokens so they can diverge without dragging each other. */
  --link: #0e7490; --signal: #0e7490;
  --signal-wash: rgba(14,116,144,0.055);
  --signal-rgb: 14,116,144;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* Depth. Two layers, always: a 1px contact shadow that grounds the edge, and a
     wide soft one that lifts it. A single blurred shadow reads as a sticker. */
  --shadow-card: 0 1px 2px rgba(0,0,0,0.04), 0 16px 40px rgba(0,0,0,0.06);
  --shadow-card-hover: 0 1px 2px rgba(0,0,0,0.06), 0 24px 56px rgba(0,0,0,0.10);
  --shadow-hero: 0 2px 4px rgba(0,0,0,0.05), 0 32px 80px rgba(0,0,0,0.13);
  --btn-shadow: 0 1px 2px rgba(0,0,0,0.18), 0 3px 8px rgba(0,0,0,0.10);
  --btn-shadow-hover: 0 2px 6px rgba(0,0,0,0.20), 0 10px 22px rgba(0,0,0,0.16);
  /* The 1px top highlight that separates a "milled" surface from a plain filled
     rectangle. Near-invisible on its own, and most of why cards read as crafted. */
  --edge-hi: inset 0 1px 0 rgba(255,255,255,0.7);

  /* Motion scale, mirrored from the app's own tokens.css so the site and the
     product move the same way. press = instant, micro = hover, exit is always
     faster than the matching enter, panel = sheets and large surfaces. */
  --dur-press: 100ms; --dur-micro: 150ms; --dur-exit: 200ms;
  --dur-enter: 300ms; --dur-panel: 400ms;
  --ease-decel: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-accel: cubic-bezier(0.4, 0, 1, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.34, 1.12, 0.64, 1);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #000000; --surface: #1c1c1e; --surface-2: #2c2c2e; --border: #38383a;
    --border-strong: #4a4a4e;
    --text: #f5f5f7; --text-muted: #98989d;
    --accent-bg: #f5f5f7; --accent-fg: #1d1d1f; --accent-tint: rgba(0,0,0,0.06);
    --accent-bg-hi: #ffffff;
    --success: #30d158; --danger: #ff6961; --warning: #ffd60a;
    --link: #45e6ff; --signal: #45e6ff;
    --signal-wash: rgba(69,230,255,0.06);
    --signal-rgb: 69,230,255;
    --shadow-card: 0 1px 2px rgba(0,0,0,0.3), 0 16px 40px rgba(0,0,0,0.4);
    --shadow-card-hover: 0 1px 2px rgba(0,0,0,0.35), 0 24px 56px rgba(0,0,0,0.55);
    --shadow-hero: 0 2px 4px rgba(0,0,0,0.4), 0 32px 80px rgba(0,0,0,0.6);
    --btn-shadow: 0 1px 2px rgba(0,0,0,0.5), 0 3px 8px rgba(0,0,0,0.35);
    --btn-shadow-hover: 0 2px 6px rgba(0,0,0,0.55), 0 10px 22px rgba(0,0,0,0.45);
    --edge-hi: inset 0 1px 0 rgba(255,255,255,0.06);
  }
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  background: var(--bg); color: var(--text); font-family: var(--font); line-height: 1.5;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  font-kerning: normal;
  /* No overflow-x:hidden here, deliberately. It makes <body> its own scroll
     container in every engine, and position:sticky on the nav then resolves
     against a scrollport that never scrolls - the bar detaches and rides up
     with the page. Every section already clips its own decorative layers. */
}
a { color: inherit; }
::selection { background: var(--text); color: var(--bg); }
:focus-visible { outline: 2px solid var(--link); outline-offset: 3px; border-radius: 6px; }
img { max-width: 100%; }

/* ---------------------------------------------------------------- layout -- */
.wrap { max-width: 1160px; margin: 0 auto; padding: 0 24px; }
.section { position: relative; overflow: hidden; padding: 88px 0; }
.section-alt { background: var(--surface-2); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.section > .wrap { position: relative; z-index: 1; }
.section-head { max-width: 640px; margin: 0 auto 44px; text-align: center; }
.eyebrow { font-size: 13px; font-weight: 600; letter-spacing: 0.01em; color: var(--text-muted); margin-bottom: 10px; }
.section-head h2 { font-size: clamp(24px, 3.2vw, 33px); font-weight: 600; letter-spacing: -0.018em; line-height: 1.22; text-wrap: balance; }
.section-head p { margin-top: 12px; color: var(--text-muted); font-size: 16px; line-height: 1.55; text-wrap: pretty; }

/* ------------------------------------------------------------ components -- */
.wordmark { display: flex; align-items: center; gap: 10px; font-size: 17px; letter-spacing: -0.01em; }
.wordmark .wm-bold { font-weight: 600; color: var(--text); }
.wordmark .wm-light { font-weight: 400; color: var(--text-muted); }
.wordmark span { white-space: nowrap; }
.logo {
  width: 30px; height: 30px; border-radius: 9px; flex-shrink: 0; display: block; object-fit: cover;
  transition: transform var(--dur-enter) var(--ease-spring);
}
.wordmark:hover .logo { transform: rotate(-6deg) scale(1.06); }

/* The primary button: gradient + lift, never a flat fill (DESIGN_LANGUAGE.md), plus
   a sheen that sweeps across on hover. The sheen is a pseudo-element translated on
   the compositor - no repaint - and it fires once per hover, on the page's loudest
   control only. */
.btn-primary {
  position: relative; overflow: hidden; isolation: isolate;
  background: linear-gradient(180deg, var(--accent-bg-hi), var(--accent-bg));
  color: var(--accent-fg); border: none; border-radius: 10px;
  height: 44px; padding: 0 22px; font-size: 15px; font-weight: 500; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  text-decoration: none; white-space: nowrap; font-family: var(--font);
  box-shadow: var(--btn-shadow);
  transition: transform var(--dur-micro) var(--ease-standard),
              box-shadow var(--dur-micro) var(--ease-standard);
}
.btn-primary::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(105deg, transparent 35%, rgba(255,255,255,0.22) 50%, transparent 65%);
  transform: translateX(-100%);
  transition: transform var(--dur-panel) var(--ease-standard);
}
@media (prefers-color-scheme: dark) {
  .btn-primary::after { background: linear-gradient(105deg, transparent 35%, rgba(0,0,0,0.12) 50%, transparent 65%); }
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: var(--btn-shadow-hover); }
.btn-primary:hover::after { transform: translateX(100%); }
.btn-primary:active { transform: translateY(0) scale(0.985); transition-duration: var(--dur-press); }

.btn-secondary {
  background: var(--surface); border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; height: 44px; padding: 0 20px; font-size: 15px; font-weight: 500;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  cursor: pointer; text-decoration: none; white-space: nowrap; font-family: var(--font);
  transition: border-color var(--dur-micro) var(--ease-standard),
              transform var(--dur-micro) var(--ease-standard),
              box-shadow var(--dur-micro) var(--ease-standard);
}
.btn-secondary:hover {
  border-color: var(--border-strong);
  transform: translateY(-1px); box-shadow: var(--shadow-card);
}
.btn-secondary:active { transform: translateY(0) scale(0.985); transition-duration: var(--dur-press); }

/* An arrow inside any "go somewhere" control nudges forward on hover - one shared
   rule rather than a per-button variant, so they all read the same. */
.btn-primary .arw, .btn-secondary .arw, .link-arrow .arw {
  transition: transform var(--dur-enter) var(--ease-decel);
}
.btn-primary:hover .arw, .btn-secondary:hover .arw, .link-arrow:hover .arw { transform: translateX(3px); }

.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 20px 22px;
  box-shadow: var(--edge-hi);
}

.pill { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; padding: 4px 11px; border-radius: 100px; white-space: nowrap; }
.pill.success { background: rgba(26,143,95,0.12); color: var(--success); }
.pill.warning { background: rgba(183,121,31,0.12); color: var(--warning); }
@media (prefers-color-scheme: dark) {
  .pill.success { background: rgba(48,209,88,0.14); }
  .pill.warning { background: rgba(255,214,10,0.14); }
}

.icon-wrap {
  width: 40px; height: 40px; border-radius: 10px; background: var(--surface-2);
  border: 1px solid var(--border); display: flex; align-items: center; justify-content: center;
  color: var(--text); margin-bottom: 14px;
  transition: border-color var(--dur-enter) var(--ease-standard),
              color var(--dur-enter) var(--ease-standard),
              background var(--dur-enter) var(--ease-standard),
              transform var(--dur-enter) var(--ease-spring);
}
.icon-wrap svg { width: 20px; height: 20px; }

/* ------------------------------------------------------------------- nav -- */
/* Transparent-to-frosted on scroll: at the top the bar belongs to the hero, and it
   only earns its border and its blur once content is passing underneath it. The
   .scrolled class comes from a single IntersectionObserver sentinel in site.js, not
   a scroll listener - no per-frame work while the page is simply being read. */
nav {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-enter) var(--ease-standard),
              background var(--dur-enter) var(--ease-standard);
}
/* No color-mix (older Safari/Firefox): fall back to the opaque bar these pages
   shipped with before, never to a transparent unreadable one. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  nav { background: var(--bg); }
}
nav.scrolled { border-bottom-color: var(--border); }
.nav-inner { display: flex; align-items: center; justify-content: space-between; height: 64px; gap: 12px; }
.nav-actions { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.nav-actions .btn-secondary, .nav-actions .btn-primary { height: 40px; }
.nav-cta-short { display: none; }

/* Reading-progress hairline welded to the bottom edge of the nav. scaleX only -
   a compositor-only property, driven by one rAF-coalesced scroll handler. */
.nav-progress {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
  background: linear-gradient(90deg, var(--signal), color-mix(in srgb, var(--signal) 45%, var(--text)));
  transform: scaleX(0); transform-origin: 0 50%;
  opacity: 0; transition: opacity var(--dur-enter) var(--ease-standard);
  pointer-events: none;
}
nav.scrolled .nav-progress { opacity: 1; }
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .nav-progress { background: var(--signal); }
}

@media (max-width: 420px) {
  .wrap { padding: 0 16px; }
  .nav-inner { height: 58px; }
  .logo { width: 26px; height: 26px; }
  .wordmark { font-size: 14.5px; gap: 8px; }
  .nav-actions { gap: 8px; }
  .nav-actions .btn-secondary, .nav-actions .btn-primary { height: 36px; padding: 0 12px; font-size: 13px; }
  .nav-cta-full { display: none; }
  .nav-cta-short { display: inline; }
}

/* ---------------------------------------------------------------- footer -- */
footer { padding: 40px 0; }
.footer-inner { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: 16px; }
.copyright { font-size: 12px; color: var(--text-muted); margin-top: 8px; }
.footer-links { display: flex; gap: 22px; font-size: 13px; color: var(--text-muted); flex-wrap: wrap; }
.footer-links a {
  position: relative; text-decoration: none;
  transition: color var(--dur-micro) var(--ease-standard);
}
/* An underline that wipes in from the left rather than appearing all at once -
   scaleX on a 1px pseudo-element, so it costs nothing and reads as deliberate. */
.footer-links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: 0 50%;
  transition: transform var(--dur-enter) var(--ease-decel);
}
.footer-links a:hover { color: var(--text); }
.footer-links a:hover::after { transform: scaleX(1); }

/* =====================================================================
   MOTION
   ---------------------------------------------------------------------
   .reveal is the one entrance primitive. site.js adds .in the first time an
   element crosses into view and then stops observing it - an entrance plays
   once, never again on every scroll past. (A re-triggering reveal is the
   single most recognisable tell of a template.)

   Stagger is a --i index written onto each child and multiplied into
   animation-delay, rather than N hand-written nth-child delays. site.js caps
   it at 8 slots so a twelve-row panel's last row is never a second behind
   its first.

   An ANIMATION, not a transition, and that is load-bearing. A transition lives on
   the element's own `transition` property, so any page-level shorthand on the same
   element silently replaces it - `.panel-row { transition: background ... }` and
   `.chip { transition: color, border-color, transform ... }` each dropped opacity
   out of the list entirely and reset the stagger delay to zero, turning a 400ms
   staggered fade into an instant pop. An animation is a separate channel that
   nothing in a page stylesheet can overwrite by accident.

   .done then removes the animation, which matters for the inverse reason: a filled
   animation keeps applying its final `transform: none` forever, and would outrank
   the :hover transform on a chip or a card for the life of the page. Removing it
   lets the element fall back to its own base declarations, where hover works
   normally again.
   ===================================================================== */
.reveal { opacity: 0; will-change: opacity, transform; }
.reveal.in {
  animation: ds-reveal var(--dur-panel) var(--ease-decel) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes ds-reveal {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to   { opacity: 1; transform: none; }
}

/* A softer variant for large media - a big image sliding 18px reads as a jolt,
   where a 2% scale settle reads as it coming into focus. */
.reveal-scale { opacity: 0; will-change: opacity, transform; }
.reveal-scale.in { animation: ds-reveal-scale 700ms var(--ease-decel) both; }
@keyframes ds-reveal-scale {
  from { opacity: 0; transform: scale(0.975); }
  to   { opacity: 1; transform: none; }
}

/* The finished state. Also drops will-change: a permanent one keeps a compositor
   layer alive for the life of the page, for no benefit after the single entrance
   it was declared for. */
.reveal.done, .reveal-scale.done { opacity: 1; animation: none; will-change: auto; }

/* ------------------------------------------------------- hover primitives -- */
/* One shared lift, so a card, a panel row and a CTA all respond with the same
   weight instead of three near-miss values. Gated on a real pointer: on a
   touchscreen :hover sticks after a tap and leaves the card raised. */
.lift { transition: transform var(--dur-enter) var(--ease-decel), box-shadow var(--dur-enter) var(--ease-decel); }
@media (hover: hover) {
  .lift:hover { transform: translateY(-3px); box-shadow: var(--shadow-card-hover); }
}

/* A pointer-tracked soft light over a surface. site.js writes --mx/--my (in
   percent) on pointermove, rAF-coalesced, and only while the element is actually
   on screen. Deliberately a very low-alpha wash of the brand cyan rather than a
   spotlight cone: the point is that the surface feels responsive under the hand,
   not that it glows. */
[data-spotlight] { position: relative; isolation: isolate; }
[data-spotlight]::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%),
              rgba(var(--signal-rgb), 0.10), transparent 62%);
  opacity: 0; transition: opacity var(--dur-panel) var(--ease-standard);
}
[data-spotlight].lit::before { opacity: 1; }
[data-spotlight] > * { position: relative; z-index: 1; }

/* Numbers that count up must not reflow while they do it - tabular figures keep
   every digit the same width, so 4 -> 12 never shifts the label underneath. */
.tnum { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* Visually hidden, still in the accessibility tree. Used by the typewriter, which
   keeps the whole sentence here for a screen reader while an aria-hidden twin
   streams it on screen. Clip rather than display:none - a hidden element is not
   announced at all. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* The caret trailing the streaming text. A 1.05em bar rather than an underscore
   so it sits on the text baseline the way a real insertion point does, and it
   disappears the moment the sentence finishes rather than blinking forever. */
.tw-caret {
  display: inline-block; width: 2px; height: 1.05em; margin-left: 2px;
  background: var(--signal); vertical-align: -0.15em; border-radius: 1px;
  animation: tw-blink 1.05s steps(2, start) infinite;
}
.tw-done .tw-caret { display: none; }
@keyframes tw-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }

/* -------------------------------------------------------- reduced motion --- */
/* One global off-switch. Everything above degrades to its finished state: nothing
   on these pages depends on a transition having run in order to be readable. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }
  .reveal, .reveal-scale { opacity: 1 !important; transform: none !important; }
}
