/* ============================================================================
   AIWA · Base - reset, fonts, globals (spec §5.6), shared primitives
   ========================================================================= */

@font-face {
  font-family: 'Bricolage Grotesque';
  src: url('/wl-assets/fonts/bricolage-grotesque.woff2') format('woff2-variations');
  font-weight: 200 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/wl-assets/fonts/inter.woff2') format('woff2-variations');
  font-weight: 100 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'JetBrains Mono';
  src: url('/wl-assets/fonts/jetbrains-mono.woff2') format('woff2-variations');
  font-weight: 100 800; font-style: normal; font-display: swap;
}

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

/* §5.6 */
html, body { overscroll-behavior: none; }
html { scrollbar-gutter: stable; -webkit-text-size-adjust: 100%; }
html.is-loading,
html.is-loading body { overflow: hidden; height: 100%; }
/* Lenis: keep native scroll geometry; never stack CSS smooth on top. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }
* { -webkit-tap-highlight-color: transparent; }
*:focus { outline: 0; }
*:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

body {
  background: var(--ink-900);
  color: var(--cream-050);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img, video, canvas, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button, input { font: inherit; color: inherit; background: none; border: 0; }
button { cursor: pointer; }
ul, ol { list-style: none; }

/* Phosphor Regular (CDN web font) — sitewide UI icons */
.ph {
  display: inline-block;
  line-height: 1;
  vertical-align: middle;
  speak: never;
}

/* §5.5 - a pointer cursor on touch is a lie about affordance */
@media only screen and (max-width: 999px) { a { cursor: default; } }

/* Native cursor affordances; artwork is retained under archive/cursors. */
[draggable='true'], .is-draggable, [data-grab] { cursor: grab; }
[draggable='true']:active, .is-draggable:active, .is-dragging, [data-grab]:active { cursor: grabbing; }

/* Hide scrollbars without losing scrollability */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------------------
   Layout helpers
   ------------------------------------------------------------------------ */
.shell { width: 100%; max-width: var(--measure); margin-inline: auto; padding-inline: var(--gutter); }
.shell--wide { max-width: 96rem; }

.section { position: relative; padding-block: clamp(5rem, 11vh, 9rem); }
.section__lead { max-width: 46rem; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--cream-500);
}

/* ---------------------------------------------------------------------------
   Type scale - display face, tight, uppercase-optional
   ------------------------------------------------------------------------ */
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 500; line-height: 0.94; letter-spacing: -0.03em; }

.t-display { font-size: clamp(3rem, 8.2vw, 8.5rem); line-height: 0.88; letter-spacing: -0.045em; }
.t-h2 { font-size: clamp(2.2rem, 5vw, 4.4rem); }
.t-h3 { font-size: clamp(1.5rem, 2.6vw, 2.4rem); }
.t-h4 { font-size: clamp(1.15rem, 1.7vw, 1.5rem); line-height: 1.1; }
.t-body { font-size: clamp(1rem, 1.1vw, 1.125rem); color: var(--cream-300); line-height: 1.6; }
.t-small { font-size: 0.875rem; color: var(--cream-500); }
.t-mono { font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.06em; }

.muted { color: var(--cream-500); }

/* Two-tone treatment (spec §2.5 panel-3 chips, §3.2 stage headings):
   "quiet setup, loud claim" - the qualifier drops to 50-70%. */
.two-tone > .quiet { opacity: 0.5; }

/* ---------------------------------------------------------------------------
   Button - the product's button, ported.

   This WAS the §4.4 gradient-sweep control. It is now apps/web's
   ui/button.tsx (variant default + outline) and .aiwa-primary, rule for rule,
   because a visitor who clicks "Start Building" should not meet a different
   button one screen later. Every literal below is a value from
   apps/web/src/styles/globals.css - see the --app-* block in tokens.css.
   ------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------
   BUTTONS · sitewide primary + secondary (homepage is the source of truth).

   Primary  = `.btn.btn--primary` (sunset fill, optional hover arrow)
   Secondary = `.btn.btn--ghost` / `.btn.btn--secondary` / bare `.btn`
               (tactile charcoal). Bare `.btn` is secondary so no unmarked
               third style can appear on sub-pages.

   Literals sample apps/web globals via --app-* / --btn-secondary-* in tokens.
   ------------------------------------------------------------------------ */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  gap: 0.6rem; white-space: nowrap;
  padding: 0.85rem 1.5rem; border-radius: 999px;
  font-size: 0.875rem; font-weight: 500;
  border: 1px solid transparent;
  /* Secondary by default — Dreamcut tactile charcoal. */
  color: var(--cream-050);
  background: linear-gradient(
    180deg,
    var(--btn-secondary-top) 0%,
    var(--btn-secondary-mid) 48%,
    var(--btn-secondary-bottom) 100%
  );
  box-shadow:
    inset 0 1px 0 var(--btn-secondary-rim),
    0 1px 2px rgb(0 0 0 / 0.45),
    0 6px 14px -6px rgb(0 0 0 / 0.55);
  /* The app's button is `transition-colors`, and `filter` is NOT in that
     property list - so its hover brightness lands on the same frame as the
     pointer. That snap is the feel, so do not "fix" it with a filter
     transition here. */
  transition: color var(--app-dur-colors) var(--ease-out),
              background-color var(--app-dur-colors) var(--ease-out),
              border-color var(--app-dur-colors) var(--ease-out),
              box-shadow var(--app-dur-colors) var(--ease-out);
}
.btn:hover { filter: brightness(1.08); }
.btn:active { filter: brightness(0.96); }
/* ring-1 ring-ring, and outline-none over the global *:focus-visible rule. */
.btn:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 1px var(--app-ring),
    inset 0 1px 0 var(--btn-secondary-rim),
    0 1px 2px rgb(0 0 0 / 0.45),
    0 6px 14px -6px rgb(0 0 0 / 0.55);
}
.btn[disabled], .btn[aria-disabled='true'] { pointer-events: none; opacity: 0.5; }

/* Explicit secondary aliases (same look as bare .btn). */
.btn--ghost,
.btn--secondary { /* intentional no-op: secondary is the .btn default */ }

/* Primary — sunset gradient + edge lights. */
.btn--primary {
  color: var(--app-primary-fg); font-weight: 600;
  background: linear-gradient(180deg, var(--app-sunset-1), var(--app-sunset-2));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.28),
    inset 0 -10px 18px -10px rgb(255 190 80 / 0.55),
    0 1px 2px rgb(0 0 0 / 0.4);
}
.btn--primary:hover { filter: brightness(1.05); }
.btn--primary:active { filter: brightness(0.97); }
.btn--primary:focus-visible {
  box-shadow:
    0 0 0 1px var(--app-ring),
    inset 0 1px 0 rgb(255 255 255 / 0.28),
    inset 0 -10px 18px -10px rgb(255 190 80 / 0.55),
    0 1px 2px rgb(0 0 0 / 0.4);
}

/* Secondary CTAs never reveal an arrow — primary keeps the hover treat. */
.btn:not(.btn--primary) .btn__arrow { display: none; }

.btn__label { position: relative; white-space: nowrap; }

/* Two stacked arrows in an overflow mask - one exits right as the next enters.
   On fine-pointer devices the arrow is hover-only: hidden at rest, revealed
   with the gap, then the dual-arrow slide runs. Touch keeps the arrow visible
   so the direction cue is never missing without a hover surface. */
.btn__arrow { position: relative; width: 0.9rem; height: 0.9rem; overflow: hidden; flex: 0 0 auto; }
.btn__arrow > span {
  display: flex; width: 200%; height: 100%;
  transition: transform var(--app-dur-colors) var(--ease-out);
}
.btn__arrow svg,
.btn__arrow .ph {
  width: 50%;
  height: 100%;
  flex: 0 0 50%;
  font-size: 0.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

@media (hover: hover) and (pointer: fine) {
  .btn--primary:has(.btn__arrow) {
    gap: 0;
    transition: color var(--app-dur-colors) var(--ease-out),
                background-color var(--app-dur-colors) var(--ease-out),
                border-color var(--app-dur-colors) var(--ease-out),
                gap var(--app-dur-colors) var(--ease-out);
  }
  .btn--primary:has(.btn__arrow) .btn__arrow {
    width: 0;
    opacity: 0;
    transition: width var(--app-dur-colors) var(--ease-out),
                opacity var(--app-dur-colors) var(--ease-out);
  }
  .btn--primary:has(.btn__arrow):hover,
  .btn--primary:has(.btn__arrow):focus-visible {
    gap: 0.6rem;
  }
  .btn--primary:has(.btn__arrow):hover .btn__arrow,
  .btn--primary:has(.btn__arrow):focus-visible .btn__arrow {
    width: 0.9rem;
    opacity: 1;
  }
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .btn--primary:hover .btn__arrow > span,
  .btn--primary:focus-visible .btn__arrow > span { transform: translate(-50%, 0); }
}

/* ---------------------------------------------------------------------------
   Live ping · status dots
   Core never moves. A ring scales out and fades — the Mastra/Linear live
   indicator, not a square that breathes. Tone is --pulse-dot-color.
   ------------------------------------------------------------------------ */
.pulse-dot {
  position: relative;
  display: inline-block;
  width: var(--pulse-dot-size);
  height: var(--pulse-dot-size);
  flex: 0 0 auto;
  isolation: isolate;
  pointer-events: none;
  border-radius: 50%;
  overflow: visible;
}
.pulse-dot::before,
.pulse-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--pulse-dot-color);
}
.pulse-dot::before {
  z-index: 0;
  opacity: var(--pulse-dot-ring-opacity);
  animation: pulse-dot-ping var(--pulse-dot-period) var(--ease-ping) infinite;
}
.pulse-dot::after {
  z-index: 1;
}
.pulse-dot--good { --pulse-dot-color: var(--good); }
.pulse-dot--accent { --pulse-dot-color: var(--accent); }

@keyframes pulse-dot-ping {
  75%, 100% {
    transform: scale(var(--pulse-dot-spread));
    opacity: 0;
  }
}

/* Shared outline pill badge (hero proof, page heroes). No fill. */
.page-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  max-width: 100%;
  padding: 0.42rem 0.95rem 0.42rem 0.72rem;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--cream-050) 14%, transparent);
  background: transparent;
  color: var(--cream-100);
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1.35;
  text-transform: none;
}
.page-badge__text { text-wrap: pretty; }

/* ---------------------------------------------------------------------------
   §4.1 Blur-reveal - initial hidden state lives in CSS so nothing flashes
   before main.js binds. JS clears these.
   ------------------------------------------------------------------------ */
[data-blur-in], [data-blur-child] {
  opacity: 0;
  filter: blur(var(--blur-reveal));
  transform: translateY(var(--y-reveal));
}
/* The spec asks the reveal tween to finish with clearProps:'all' so it leaves
   no inline styles behind. That means the rule above comes straight back the
   instant the tween ends - the element reveals and then vanishes again. The
   revealed state therefore needs somewhere to live that clearProps cannot
   reach, which is this class. JS adds it as the tween completes. */
[data-blur-in].is-revealed, [data-blur-child].is-revealed {
  opacity: 1; filter: none; transform: none;
}
.no-js [data-blur-in], .no-js [data-blur-child] {
  opacity: 1; filter: none; transform: none;
}
/* The same escape the reveal needs, for the same reason, one rule below. The
   gradient sweep parks at 100% 50% and the last third of that gradient is
   transparent, so an unswept [data-grad] heading is not dim - it is INVISIBLE.
   gradientText() moves it on load, and jumps it to 0% under reduced motion, but
   with no script at all nothing moves it and every gradient heading on the page
   disappears. Park it at the finished position instead. */
.no-js [data-grad] { background-position: 0% 50%; }

/* ---------------------------------------------------------------------------
   §4.2 Gradient text sweep
   ------------------------------------------------------------------------ */
[data-grad] {
  overflow: visible;
  background-image: linear-gradient(90deg,
    currentColor 0, currentColor 33.33%,
    var(--stop-4) 40%, var(--stop-2) 45%, var(--stop-1) 50%,
    var(--stop-5) 55%, var(--stop-3) 60%,
    transparent 66.67%, transparent);
  background-size: 400%;
  background-position: 100% 50%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: var(--cream-050);
}

/* ---------------------------------------------------------------------------
   §4.3 Marquee
   ------------------------------------------------------------------------ */
.marquee { overflow: hidden; position: relative; }
.marquee__track { display: flex; width: max-content; animation: marquee-x linear infinite; }
.marquee__track[data-direction='reverse'] { animation-direction: reverse; }
.marquee__group { display: flex; flex: 0 0 auto; }
@keyframes marquee-x { to { transform: translateX(-50%); } }

/* soft edges rather than a hard clip */
.marquee--masked {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
}

/* ---------------------------------------------------------------------------
   §5.1 Reduced motion - honoured at the CSS level too, so the page is correct
   even before main.js decides not to build anything.
   ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  [data-blur-in], [data-blur-child] { opacity: 1; filter: none; transform: none; }
  .marquee__track { animation: none; }
  .pulse-dot::before { animation: none !important; opacity: 0; }
}
