/* ============================================================
   WISTERIA · BASE + SHARED PRIMITIVES
   Reset, typography, layout, buttons, phone frame, reveals.
   Section stylesheets build ON TOP of this — never fork it.
   ============================================================ */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body {
  background: var(--w-bone);
  color: var(--w-ink);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

::selection { background: var(--w-wisteria); color: #fff; }

:focus-visible {
  outline: 2px solid var(--w-wisteria);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------- Typography ---------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  font-variation-settings: "SOFT" 20, "WONK" 0, "opsz" 60;
  line-height: var(--lh-snug);
  letter-spacing: -0.018em;
  text-wrap: balance;
}

.t-hero {
  font-size: var(--t-hero);
  line-height: var(--lh-tight);
  letter-spacing: -0.035em;
  font-variation-settings: "SOFT" 30, "WONK" 1, "opsz" 144;
}
.t-d1 { font-size: var(--t-d1); letter-spacing: -0.028em; font-variation-settings: "SOFT" 26, "WONK" 1, "opsz" 100; }
.t-d2 { font-size: var(--t-d2); letter-spacing: -0.024em; }
.t-d3 { font-size: var(--t-d3); letter-spacing: -0.018em; }

.lead {
  font-size: var(--t-lead);
  line-height: 1.5;
  color: var(--text-2);
  letter-spacing: -0.011em;
  text-wrap: pretty;
}

p { text-wrap: pretty; }

/* Editorial italic emphasis — the house accent */
.em {
  font-style: italic;
  font-variation-settings: "SOFT" 40, "WONK" 1, "opsz" 144;
  color: var(--w-plum);
}
.on-dark .em { color: var(--w-wisteria-lt); }

/* Eyebrow label.
   Uses --text-2, not --text-3: at this size and weight, --text-3
   lands near 3:1 on bone, under the 4.5:1 floor for body text.
   Eyebrows are real content, so they hold the contrast rule. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .55em;
  font-family: var(--font-body);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-2);
  line-height: 1;
}
.eyebrow::before {
  content: "";
  width: 18px; height: 1px;
  background: currentColor;
  opacity: .5;
}
.eyebrow--dot::before {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--w-terra);
  opacity: 1;
}

/* ---------- Layout ---------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: 940px; }
.wrap--wide   { max-width: 1440px; }

.section { padding-block: var(--section-y); position: relative; }
.section--tight { padding-block: clamp(3.5rem, 7vw, 6rem); }

.stack > * + * { margin-top: var(--stack-gap, 1.25rem); }

/* Hairline divider that reads as editorial, not utilitarian */
.rule {
  height: 1px;
  background: var(--border);
  border: 0;
}

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--w-ink);
  --btn-fg: var(--w-bone);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6em;
  padding: 1.05em 1.9em;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: var(--t-sm);
  font-weight: 500;
  letter-spacing: -0.006em;
  line-height: 1;
  white-space: nowrap;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  transition: transform var(--dur-fast) var(--ease-soft),
              box-shadow var(--dur) var(--ease),
              background-color var(--dur-fast) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft);
}
.btn::after {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: var(--grad-violet);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--sh-md); }
.btn:hover::after { opacity: 1; }
.btn:active { transform: translateY(0); }

.btn--accent { --btn-bg: var(--w-wisteria); --btn-fg: #fff; }
.btn--accent::after { background: linear-gradient(155deg, #9C81D4, #6247A0); }

.btn--warm { --btn-bg: var(--w-terra); --btn-fg: #fff; }
.btn--warm::after { background: var(--grad-warm); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.btn--ghost:hover { --btn-fg: var(--w-bone); }
.btn--ghost::after { background: var(--w-ink); }
.on-dark .btn--ghost:hover { --btn-fg: var(--w-ink); }
.on-dark .btn--ghost::after { background: var(--on-dark); }

.btn--sm { padding: .8em 1.4em; font-size: var(--t-xs); }

/* Text link with an animated underline */
.link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-weight: 500;
  font-size: var(--t-sm);
  color: var(--text);
  padding-bottom: 2px;
}
.link::after {
  content: "";
  position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform-origin: right;
  transform: scaleX(1);
  transition: transform var(--dur) var(--ease);
}
.link:hover::after { transform-origin: left; animation: link-sweep var(--dur) var(--ease); }
@keyframes link-sweep {
  0%   { transform: scaleX(1);  transform-origin: right; }
  49%  { transform: scaleX(0);  transform-origin: right; }
  50%  { transform: scaleX(0);  transform-origin: left;  }
  100% { transform: scaleX(1);  transform-origin: left;  }
}

/* ---------- Pills / chips / badges ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  padding: .5em .9em;
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  box-shadow: inset 0 0 0 1px var(--border);
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1;
  color: var(--text-2);
}
.chip--accent {
  background: color-mix(in srgb, var(--w-wisteria) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--w-wisteria) 26%, transparent);
  color: var(--w-wisteria-dk);
}
.on-dark .chip--accent { color: var(--w-wisteria-lt); }
.chip--warm {
  background: color-mix(in srgb, var(--w-terra) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--w-terra) 26%, transparent);
  color: var(--w-terra);
}

/* ---------- Cards ---------- */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: inset 0 0 0 1px var(--border), var(--sh-sm);
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
}

/* ============================================================
   PHONE FRAME — shared primitive.
   Every app mockup on the site uses this exact frame so the
   product reads as one device across all sections.
   Usage:
     <div class="phone"><div class="phone__screen"> …UI… </div></div>
   Screen is a 390 × 844 logical canvas scaled by --phone-w.
   ============================================================ */
.phone {
  /* Size the frame by setting --phone-n, a UNITLESS pixel width.
     It must be unitless: --phone-scale feeds transform: scale(), and
     scale() rejects a length. (calc(300px / 390) is 0.769px, not 0.769,
     which silently invalidates the transform and lets the 390×844 screen
     burst out of the frame.) --phone-w is derived for layout use.

     Height is computed from --phone-scale rather than from --phone-n so
     that a section overriding --phone-w and --phone-scale as a pair
     still gets a correctly proportioned frame. */
  --phone-n: 300;
  --phone-bezel: 10px;
  --phone-w: calc(var(--phone-n) * 1px);
  --phone-scale: calc((var(--phone-n) - 20) / 390);
  width: var(--phone-w);
  height: calc(844px * var(--phone-scale) + var(--phone-bezel) * 2);
  position: relative;
  border-radius: var(--r-phone);
  padding: var(--phone-bezel);
  background: linear-gradient(165deg, #4B4453 0%, #191320 34%, #100C16 66%, #3A3342 100%);
  box-shadow: var(--sh-phone);
  flex: none;
}
.phone::after { /* screen glass sheen */
  content: "";
  position: absolute; inset: var(--phone-bezel);
  border-radius: calc(var(--r-phone) - var(--phone-bezel));
  background: linear-gradient(122deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,0) 38%);
  pointer-events: none;
  z-index: 4;
}
.phone__screen {
  position: absolute;
  inset: var(--phone-bezel);
  width: 390px;
  height: 844px;
  transform: scale(var(--phone-scale));
  transform-origin: top left;
  border-radius: calc((var(--r-phone) - var(--phone-bezel)) / var(--phone-scale));
  overflow: hidden;
  background: var(--w-bone);
  color: var(--w-ink);
  font-size: 15px;
  line-height: 1.4;
  isolation: isolate;
}
/* Dynamic island — scales with the frame, or it reads oversized
   on any phone rendered smaller than the 390px reference width. */
.phone__island {
  position: absolute;
  top: calc(var(--phone-bezel) + 11px * var(--phone-scale));
  left: 50%;
  transform: translateX(-50%);
  width: calc(112px * var(--phone-scale));
  height: calc(32px * var(--phone-scale));
  border-radius: var(--r-pill);
  background: #0B0810;
  z-index: 5;
}
/* Status bar inside the screen canvas */
.phone__status {
  display: flex; align-items: center; justify-content: space-between;
  padding: 15px 26px 0;
  font-size: 14px; font-weight: 600;
  letter-spacing: -0.01em;
  height: 54px;
}
.phone__status-right { display: flex; gap: 4px; align-items: center; }
.phone__status-right i {
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; display: block;
}

/* ---------- Scroll reveal (driven by assets/js/main.js) ----------
   PROGRESSIVE ENHANCEMENT, deliberately.
   Content is VISIBLE by default. The hidden pre-reveal state only
   applies once main.js has confirmed IntersectionObserver actually
   works, by putting .reveal-ready on <html>. If JS fails to load,
   is blocked, or IO never fires (hidden tab, offscreen render,
   some privacy browsers, screenshot pipelines), the page still
   reads perfectly — it just doesn't animate.
   Never invert this. An invisible marketing site is a dead one. */
.reveal-ready [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity 900ms var(--ease) var(--reveal-delay, 0ms),
    transform 900ms var(--ease) var(--reveal-delay, 0ms),
    filter 900ms var(--ease) var(--reveal-delay, 0ms);
  filter: blur(6px);
  will-change: opacity, transform;
}
.reveal-ready [data-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}
.reveal-ready [data-reveal="scale"]  { transform: translateY(28px) scale(.965); }
.reveal-ready [data-reveal="left"]   { transform: translateX(-26px); }
.reveal-ready [data-reveal="right"]  { transform: translateX(26px); }
.reveal-ready [data-reveal="fade"]   { transform: none; filter: blur(3px); }

/* Per-word / per-line hero reveal helper */
.reveal-line { display: block; overflow: hidden; }

/* Safety net for the whole reveal system: until JS confirms it is
   running, nothing anywhere may sit translated out of its own mask.
   Section stylesheets set their own line offsets (hero.css does), so
   this has to outrank them — that is what the !important is for.
   It guarantees the page can never render with its headlines hidden. */
html:not(.reveal-ready) .reveal-line > span {
  transform: none !important;
  transition: none !important;
}

.reveal-ready .reveal-line > span {
  display: block;
  transform: translateY(105%);
  transition: transform 1150ms var(--ease) var(--reveal-delay, 0ms);
}
.reveal-ready .is-in .reveal-line > span,
.reveal-ready .reveal-line.is-in > span { transform: none; }

/* ---------- Utilities ---------- */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; top: 0; left: 50%;
  transform: translate(-50%, -120%);
  z-index: 200;
  background: var(--w-ink); color: var(--w-bone);
  padding: .85em 1.4em; border-radius: 0 0 var(--r-md) var(--r-md);
  font-size: var(--t-sm); font-weight: 500;
  transition: transform var(--dur) var(--ease);
}
.skip-link:focus { transform: translate(-50%, 0); }

.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.measure { max-width: var(--maxw-text); }

/* Grain overlay — applied to the body once, gives the bone
   surfaces a printed-paper quality instead of flat digital. */
.grain::before {
  content: "";
  position: fixed;
  inset: -50%;
  z-index: 9999;
  pointer-events: none;
  opacity: .038;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 220 220' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 220px 220px;
}
