/* ============ Design tokens ============ */
:root {
  --bg: #100E1B;
  --panel: #1B1730;
  --ghost: #F1EEFF;
  --coral: #FF8969;
  --mint: #8FF7C7;
  --muted: #A79FC4;

  --font-display: 'Fraunces', serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;

  --max-width: 1120px;
  /* Corner-radius scale — every rounded corner on the page pulls from here */
  --radius-xl: 36px;
  --radius-lg: 28px;
  --radius-md: 16px;
  --radius-sm: 10px;

  /* Spacing rhythm scale — deliberate, not ad-hoc */
  --space-section: clamp(5.5rem, 11vw, 8.5rem);
  --space-section-lg: clamp(7.5rem, 15vw, 12rem);
  --space-heading-gap: clamp(2.5rem, 6vw, 4rem);

  /* Depth — one soft, low card shadow, one deeper shadow for the phone */
  --shadow-card: 0 16px 40px -20px rgba(0, 0, 0, 0.55);
  --shadow-deep: 0 30px 80px -20px rgba(0, 0, 0, 0.7);
  --hairline: 1px solid rgba(241, 238, 255, 0.08);
}

/* ============ Reset ============ */
*, *::before, *::after { box-sizing: border-box; }
/* overflow-x needs to be on html, not just body — the root element governs
   the viewport's scroll box, and decorative absolutely-positioned .glow
   elements (deliberately offset partway off their section) were still
   letting the page scroll ~23px horizontally on mobile despite body's own
   overflow-x: hidden below. */
html { scroll-behavior: auto; overflow-x: hidden; }
body {
  margin: 0;
  /* Fine technical grid baked directly into the body background (not a
     separate fixed/blend-mode layer) — a second always-on composited
     overlay measurably cost frame time during scroll. This is free. */
  background:
    linear-gradient(rgba(241, 238, 255, 0.02) 1px, transparent 1px),
    linear-gradient(90deg, rgba(241, 238, 255, 0.02) 1px, transparent 1px),
    var(--bg);
  background-size: 48px 48px, 48px 48px, auto;
  color: var(--ghost);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; cursor: pointer; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input { font: inherit; color: inherit; }
h1, h2, h3, p { margin: 0; }

/* Visible keyboard focus everywhere */
a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--mint);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ============ Ghost glyph ============ */
.ghost-glyph {
  width: 40px;
  height: 40px;
  color: var(--ghost);
}
.ghost-glyph--tiny { width: 22px; height: 22px; }

/* ============ Buttons ============ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.85em 1.6em;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  transition: background 0.15s ease;
  white-space: nowrap;
}
.btn--coral {
  background: linear-gradient(135deg, #ffab85, var(--coral) 55%, #ff7a54);
  color: #1a0e08;
  box-shadow: 0 8px 24px -10px rgba(255, 137, 105, 0.55);
  transition: box-shadow 0.2s ease, background 0.15s ease;
}
.btn--coral:hover { box-shadow: 0 12px 32px -8px rgba(255, 137, 105, 0.7); }
.btn--ghost { background: rgba(241, 238, 255, 0.1); color: var(--ghost); border: 1px solid rgba(241, 238, 255, 0.25); }
.btn--ghost:hover { background: rgba(241, 238, 255, 0.16); }
.btn--outline { background: transparent; color: var(--ghost); border: 1px dashed rgba(241, 238, 255, 0.4); padding: 0.7em 1.3em; font-size: 0.9rem; }
.btn--outline:hover { border-color: var(--ghost); }
.btn[disabled] { opacity: 0.6; cursor: not-allowed; transform: none; }

/* ============ Header ============ */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.1rem clamp(1.25rem, 4vw, 3rem);
  background: linear-gradient(to bottom, rgba(16, 14, 27, 0.9), rgba(16, 14, 27, 0));
  backdrop-filter: blur(6px);
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.15rem;
}
.header-cta { font-size: 0.85rem; padding: 0.6em 1.3em; }
.header-actions { display: flex; align-items: center; gap: 1.25rem; }
.header-login-link { font-size: 0.85rem; color: var(--muted); text-decoration: none; }
.header-login-link:hover { color: var(--ghost); }
@media (max-width: 560px) { .header-login-link { display: none; } }

/* ============ Section basics ============ */
main > section {
  padding: var(--space-section) clamp(1.25rem, 5vw, 3rem);
  max-width: var(--max-width);
  margin: 0 auto;
  position: relative;
}
.section-heading {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  text-align: center;
  max-width: 42ch;
  margin: 0 auto var(--space-heading-gap);
  color: var(--ghost);
}

/* ============ HERO ============ */
.hero-scroll {
  position: relative;
  /* Was 300vh — at typical wheel/trackpad speed that read as scrolling had
     stalled. 200vh still gives the type→grab→rewrite→send sequence room
     to play out but resolves in roughly two screen-heights of scrolling
     instead of three. */
  height: 200vh;
  max-width: none;
  padding: 0;
}
.hero-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  /* On short viewports the centered content can be taller than 100vh, which
     pushes its top edge above y:0 — underneath the fixed, blurred header.
     `safe center` keeps centering when it fits and falls back to
     top-alignment (respecting the padding below) when it would overflow,
     so content can never render behind the header. Declared after the
     plain `center` above as a progressive enhancement for browsers that
     don't yet support the `safe` keyword. */
  align-items: safe center;
  padding: 5.5rem clamp(1.25rem, 6vw, 6rem) 1.5rem;
  gap: clamp(3rem, 8vw, 7rem);
  max-width: var(--max-width);
  margin: 0 auto;
  flex-wrap: wrap;
}
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 1.75rem;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  animation: scroll-cue-bob 2s ease-in-out infinite;
}
@keyframes scroll-cue-bob {
  0%, 100% { transform: translateX(-50%) translateY(0); opacity: 0.7; }
  50% { transform: translateX(-50%) translateY(6px); opacity: 1; }
}
@media (max-width: 768px), (prefers-reduced-motion: reduce) {
  .scroll-cue { display: none; }
}
.hero-copy { flex: 1 1 380px; max-width: 560px; }
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  color: var(--muted);
  background: rgba(241, 238, 255, 0.06);
  border: var(--hairline);
  border-radius: 999px;
  padding: 0.45em 0.95em 0.45em 0.75em;
  margin-bottom: 1.75rem;
}
.eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 8px 1px rgba(143, 247, 199, 0.6);
  flex-shrink: 0;
}
.hero-headline {
  font-family: var(--font-display);
  font-weight: 400;
  font-style: normal;
  font-size: clamp(2.8rem, 7vw, 6rem);
  line-height: 1.14;
  letter-spacing: -0.03em;
  color: var(--ghost);
}
.headline-accent {
  color: var(--coral);
  text-shadow: 0 0 28px rgba(255, 137, 105, 0.35);
}
.hero-sub {
  margin-top: 2rem;
  color: var(--muted);
  font-size: 1rem;
  line-height: 1.6;
  max-width: 42ch;
}

/* ---- Phone mockup ---- */
.phone-stage { position: relative; flex: 0 0 auto; margin: 0 auto; }
.viewfinder-corner {
  position: absolute;
  width: 18px;
  height: 18px;
  border-color: rgba(241, 238, 255, 0.22);
  border-style: solid;
  border-width: 0;
  pointer-events: none;
}
.viewfinder-corner--tl { top: -10px; left: -10px; border-top-width: 1.5px; border-left-width: 1.5px; }
.viewfinder-corner--tr { top: -10px; right: -10px; border-top-width: 1.5px; border-right-width: 1.5px; }
.viewfinder-corner--bl { bottom: -10px; left: -10px; border-bottom-width: 1.5px; border-left-width: 1.5px; }
.viewfinder-corner--br { bottom: -10px; right: -10px; border-bottom-width: 1.5px; border-right-width: 1.5px; }
.phone {
  /* 9:19.5-ish — matches a real modern phone's aspect ratio instead of a flat 1:2 rounded rectangle */
  width: 300px;
  height: 638px;
  background: #0B0A14;
  border-radius: var(--radius-xl);
  border: 8px solid #26223B;
  box-shadow:
    var(--shadow-deep),
    0 0 0 1px rgba(241, 238, 255, 0.04),
    inset 0 1px 0 rgba(241, 238, 255, 0.08);
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.phone-notch {
  /* Dynamic-Island-style floating pill, not a wide notch band fused to the edge */
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 90px;
  height: 26px;
  background: #050409;
  border-radius: 999px;
  z-index: 5;
}
.phone-screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--panel);
}
.chat-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  /* Extra top padding clears the floating Dynamic-Island-style notch pill */
  padding: 2.75rem 1.1rem 0.8rem;
  border-bottom: 1px solid rgba(241, 238, 255, 0.08);
}
.chat-title { font-weight: 600; font-size: 0.95rem; }
.online-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 6px 1px rgba(143, 247, 199, 0.6);
  animation: pulse-dot 2.2s ease-in-out infinite;
}
@keyframes pulse-dot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.8); }
}

.chat-thread {
  flex: 1;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  overflow: hidden;
}
.chat-message { display: flex; flex-direction: column; gap: 0.3rem; max-width: 85%; }
.chat-message--in { align-self: flex-start; }
.chat-message--out { align-self: flex-end; }
.chat-timestamp {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  /* Solid --muted, not a translucent version of it — at 0.8 alpha this dipped
     to a 4.37:1 contrast ratio against the panel background, just under the
     4.5:1 WCAG AA threshold (caught by axe-core once both timestamps became
     reliably visible outside the pinned hero's scroll-scrubbed state). */
  color: var(--muted);
  padding: 0 0.35rem;
}
.chat-message--out .chat-timestamp { align-self: flex-end; opacity: 0; }
.chat-bubble {
  font-size: 0.82rem;
  padding: 0.6em 0.85em;
  border-radius: var(--radius-md);
  line-height: 1.35;
}
.chat-bubble--in {
  background: rgba(241, 238, 255, 0.08);
  border-bottom-left-radius: 4px;
}
.chat-bubble--calm {
  background: var(--mint);
  color: #0d2318;
  border-bottom-right-radius: 4px;
  opacity: 0;
  transform: translateY(10px);
  will-change: opacity, transform;
}

.typing-indicator {
  display: flex;
  align-items: center;
  gap: 4px;
  align-self: flex-end;
  background: rgba(241, 238, 255, 0.08);
  border-radius: var(--radius-md);
  padding: 0.6em 0.8em;
  opacity: 0;
  width: fit-content;
}
.typing-indicator span {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted);
  /* Paused by default — only running during the brief window the indicator
     is visible (toggled by JS) rather than looping for the page's entire
     lifetime, which measurably cost frame time during the hero scrub. */
  animation: typing-bounce 1s ease-in-out infinite;
  animation-play-state: paused;
}
.typing-indicator span:nth-child(2) { animation-delay: 0.15s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.3s; }
@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
  30% { transform: translateY(-3px); opacity: 1; }
}

.chat-input-bar {
  position: relative;
  margin: 0.75rem;
  padding: 0.75em 0.9em;
  background: rgba(241, 238, 255, 0.06);
  border: 1px solid rgba(241, 238, 255, 0.12);
  border-radius: 999px;
  min-height: 2.6em;
  display: flex;
  align-items: center;
}
.chat-input-text-wrap {
  position: relative;
  flex: 1;
  min-height: 1.3em;
  overflow: hidden;
}
.chat-input-text {
  position: absolute;
  top: 0;
  left: 0;
  font-size: 0.78rem;
  white-space: normal;
  color: var(--ghost);
  will-change: clip-path, opacity;
}
.chat-input-text--original {
  clip-path: inset(0 100% 0 0);
}
.chat-input-text--calm {
  clip-path: inset(0 100% 0 0);
  color: #d8ffe9;
}
.ghost-grab {
  position: absolute;
  right: -6px;
  top: 50%;
  width: 34px;
  height: 34px;
  transform: translateY(-50%) scale(0.4);
  opacity: 0;
  color: var(--ghost);
  filter: drop-shadow(0 0 10px rgba(241, 238, 255, 0.55));
  will-change: opacity, transform;
}
.ghost-grab .ghost-glyph { width: 100%; height: 100%; }

/* ============ PROBLEM ============ */
.problem { overflow: hidden; }
.dashboard-feature { overflow: hidden; position: relative; }
.dashboard-feature-sub {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--muted);
  text-align: center;
  max-width: 46ch;
  margin: 0 auto 2.5rem;
}
.problem-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem;
}
.problem-card {
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 1.75rem;
  opacity: 0;
  transform: translateY(24px);
  transition: transform 0.25s ease, border-color 0.25s ease;
}
.problem-card:hover {
  transform: translateY(-4px);
  border-color: rgba(255, 137, 105, 0.35);
}
.problem-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--coral);
  margin-bottom: 0.9rem;
}
.problem-bubble {
  font-size: 1rem;
  line-height: 1.5;
  color: var(--ghost);
}

/* ============ TRY IT YOURSELF ============ */
.demo-lede {
  text-align: center;
  color: var(--muted);
  max-width: 46ch;
  margin: -1.5rem auto 2.5rem;
}
.demo-widget {
  max-width: 560px;
  margin: 0 auto;
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: clamp(1.75rem, 4vw, 2.75rem);
}
.demo-form { display: flex; flex-direction: column; gap: 1.25rem; }
.demo-input {
  background: rgba(241, 238, 255, 0.06);
  border: 1px solid rgba(241, 238, 255, 0.15);
  border-radius: var(--radius-sm);
  padding: 0.75em 0.9em;
  color: var(--ghost);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.5;
  resize: vertical;
}
.demo-input::placeholder { color: rgba(167, 159, 196, 0.6); }
.demo-input:focus {
  border-color: var(--mint);
  animation: input-glow-breathe 2.4s ease-in-out infinite;
}
.demo-form .btn { align-self: center; }
.demo-form .btn[disabled] { opacity: 0.55; cursor: wait; }
.demo-result { margin-top: 1.5rem; }
.demo-result-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--mint);
  margin-bottom: 0.6rem;
}
.demo-result-bubble {
  opacity: 1;
  transform: none;
  font-size: 0.95rem;
  padding: 1em 1.15em;
  border-bottom-right-radius: var(--radius-md);
}
.demo-error {
  margin-top: 1rem;
  color: var(--coral);
  font-size: 0.9rem;
  text-align: center;
}
.demo-cta {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: var(--hairline);
  text-align: center;
}
.demo-cta p { color: var(--muted); margin-bottom: 1rem; }

/* ============ HOW IT WORKS ============ */
.how-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
  perspective: 1000px;
}
@media (max-width: 720px) {
  .how-grid { grid-template-columns: 1fr; }
}
.how-card {
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 2rem;
  opacity: 0;
  transform: translateY(24px);
  transform-style: preserve-3d;
  will-change: transform;
  transition: border-color 0.25s ease;
}
.how-card:hover { border-color: rgba(255, 137, 105, 0.35); }
.how-icon {
  width: 30px;
  height: 30px;
  color: var(--coral);
  margin-bottom: 1.25rem;
}
.how-card h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.2rem;
  margin-bottom: 0.6rem;
  color: var(--ghost);
}
.how-card p {
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.55;
}

/* ============ GHOST SCENE ============ */
/* A single continuous 12s loop, always in motion — no dead stationary
   time anywhere in the cycle. (The first version dwelt at house 1 for
   60%-100% AND 0%-5% of a 16s loop — 7.2s of just sitting there, since
   those two ranges are adjacent across the loop boundary. Real, reported
   bug, not a one-off.) Structure: dwell at each house (8% ~1s), a short
   arcing transit to the next (12% ~1.4s), then instead of fading out and
   teleporting back, one continuous higher, banking swoop from house 4 all
   the way back to house 1 (32% ~3.8s) — visually distinct as an
   intentional flourish rather than a pause, and the ghost is never
   invisible. left (a % of the stage, matching each .scene-house's own
   left%) drives horizontal position; transform (px-based, self-relative so
   % ambiguity doesn't matter) drives the arc/bank/scale. */
.ghost-scene { overflow: hidden; }
.ghost-scene-stage {
  position: relative;
  height: clamp(200px, 26vw, 300px);
  max-width: 860px;
  margin: 0 auto;
}
.scene-ground {
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: 18%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(241, 238, 255, 0.14) 12%, rgba(241, 238, 255, 0.14) 88%, transparent);
}
.scene-house {
  position: absolute;
  bottom: 18%;
  width: clamp(48px, 8vw, 78px);
  color: var(--muted);
  transform: translateX(-50%);
}
.scene-house-icon { width: 100%; height: auto; position: relative; z-index: 1; }
.scene-house--1 { left: 8%; }
.scene-house--2 { left: 36%; }
.scene-house--3 { left: 64%; }
.scene-house--4 { left: 92%; }

/* A soft window-glow pulse behind each house exactly while the ghost is
   there — a "someone's home and it's handled" beat, not just the bubble
   doing all the work. */
.scene-house-glow {
  position: absolute;
  left: 50%;
  top: 42%;
  width: 150%;
  height: 150%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(143, 247, 199, 0.4), rgba(143, 247, 199, 0) 70%);
  filter: blur(3px);
  opacity: 0;
  will-change: opacity;
  animation-duration: 12s;
  animation-timing-function: ease;
  animation-iteration-count: infinite;
}
.scene-house-glow--1 { animation-name: house-glow-1; }
.scene-house-glow--2 { animation-name: house-glow-2; }
.scene-house-glow--3 { animation-name: house-glow-3; }
.scene-house-glow--4 { animation-name: house-glow-4; }
@keyframes house-glow-1 { 0%, 4% { opacity: 0.9; } 8%, 100% { opacity: 0; } }
@keyframes house-glow-2 { 0%, 19% { opacity: 0; } 22%, 26% { opacity: 0.9; } 28%, 100% { opacity: 0; } }
@keyframes house-glow-3 { 0%, 39% { opacity: 0; } 42%, 46% { opacity: 0.9; } 48%, 100% { opacity: 0; } }
@keyframes house-glow-4 { 0%, 59% { opacity: 0; } 62%, 66% { opacity: 0.9; } 68%, 100% { opacity: 0; } }

.scene-ghost,
.scene-ghost-echo {
  position: absolute;
  bottom: 42%;
  width: 44px;
  height: 44px;
  color: var(--ghost);
}
.scene-ghost .ghost-glyph,
.scene-ghost-echo .ghost-glyph { width: 100%; height: 100%; }
.scene-ghost {
  z-index: 3;
  filter: drop-shadow(0 0 16px rgba(241, 238, 255, 0.4));
  will-change: left, transform;
  animation: ghost-fly 12s ease-in-out infinite;
}
/* Two faint, blurred afterimages running the identical path on a fixed
   time lag (positive animation-delay against the same infinite 12s loop
   holds a constant phase offset forever, not just on the first cycle) —
   a cheap CSS-only trail/motion-blur effect with no JS and no extra
   elements to keep in sync by hand. */
.scene-ghost-echo {
  z-index: 2;
  pointer-events: none;
  will-change: left, transform;
  animation: ghost-fly 12s ease-in-out infinite;
  animation-fill-mode: backwards;
}
.scene-ghost-echo--1 { opacity: 0.22; filter: blur(1.5px); animation-delay: 0.15s; }
.scene-ghost-echo--2 { opacity: 0.1; filter: blur(3px); animation-delay: 0.3s; }

@keyframes ghost-fly {
  0%, 8%    { left: 8%;  transform: translateY(0) rotate(0deg) scale(1); }
  14%       { left: 22%; transform: translateY(-50px) rotate(-10deg) scale(1); }
  20%, 28%  { left: 36%; transform: translateY(0) rotate(0deg) scale(1); }
  34%       { left: 50%; transform: translateY(-50px) rotate(10deg) scale(1); }
  40%, 48%  { left: 64%; transform: translateY(0) rotate(0deg) scale(1); }
  54%       { left: 78%; transform: translateY(-50px) rotate(-10deg) scale(1); }
  60%, 68%  { left: 92%; transform: translateY(0) rotate(0deg) scale(1); }
  /* The "swoop home" — higher and more dramatic than the short hops
     above, banking hard both ways, so it reads as a deliberate flourish
     rather than the ghost just finding its way back. */
  76%       { left: 70%; transform: translateY(-95px) rotate(14deg) scale(1.08); }
  84%       { left: 42%; transform: translateY(-118px) rotate(-12deg) scale(1.08); }
  92%       { left: 16%; transform: translateY(-50px) rotate(-6deg) scale(1.02); }
  100%      { left: 8%;  transform: translateY(0) rotate(0deg) scale(1); }
}

.scene-bubble {
  position: absolute;
  bottom: 66%;
  transform: translateX(-50%) translateY(10px);
  /* width, not max-width — an absolutely-positioned box with only `left`
     set (no `right`) shrink-to-fits against the remaining distance to its
     containing block's edge, not the full max-width, when that distance is
     smaller. Bit the house-4 bubble (left: 92%, close to the stage's own
     right edge) hard: it silently wrapped into a narrow 4-line column
     instead of using the full 148px. An explicit width sidesteps the
     containing-block-edge calculation entirely; centering still happens
     purely via the transform above, which runs after layout. */
  width: 152px;
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
  padding: 0.55em 0.8em;
  font-size: 0.76rem;
  line-height: 1.35;
  color: var(--ghost);
  text-align: center;
  opacity: 0;
  will-change: opacity, transform;
  animation-duration: 12s;
  animation-timing-function: ease;
  animation-iteration-count: infinite;
}
/* Speech-bubble tail, pointing down toward the house it belongs to — a
   plain rounded rectangle floating above a house read as an unrelated
   label; the tail visually anchors it as *that house's* moment. */
.scene-bubble::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 10px;
  height: 10px;
  background: var(--panel);
  border-right: var(--hairline);
  border-bottom: var(--hairline);
  transform: translateX(-50%) rotate(45deg);
}
.scene-bubble--1 { left: 8%;  animation-name: bubble-1; }
.scene-bubble--2 { left: 36%; animation-name: bubble-2; }
.scene-bubble--3 { left: 64%; animation-name: bubble-3; }
.scene-bubble--4 { left: 92%; animation-name: bubble-4; }

@keyframes bubble-1 {
  0%, 4%    { opacity: 1; transform: translateX(-50%) translateY(0); }
  8%, 100%  { opacity: 0; transform: translateX(-50%) translateY(10px); }
}
@keyframes bubble-2 {
  0%, 19%   { opacity: 0; transform: translateX(-50%) translateY(10px); }
  22%, 26%  { opacity: 1; transform: translateX(-50%) translateY(0); }
  28%, 100% { opacity: 0; transform: translateX(-50%) translateY(10px); }
}
@keyframes bubble-3 {
  0%, 39%   { opacity: 0; transform: translateX(-50%) translateY(10px); }
  42%, 46%  { opacity: 1; transform: translateX(-50%) translateY(0); }
  48%, 100% { opacity: 0; transform: translateX(-50%) translateY(10px); }
}
@keyframes bubble-4 {
  0%, 59%   { opacity: 0; transform: translateX(-50%) translateY(10px); }
  62%, 66%  { opacity: 1; transform: translateX(-50%) translateY(0); }
  68%, 100% { opacity: 0; transform: translateX(-50%) translateY(10px); }
}

/* Static snapshot instead of motion — same idea (every house, something
   different handled), all four beats visible at once rather than none. */
@media (prefers-reduced-motion: reduce) {
  .scene-ghost { animation: none; left: 50%; bottom: 60%; transform: translateY(0) rotate(0deg) scale(1); }
  .scene-ghost-echo { display: none; }
  .scene-house-glow { animation: none; opacity: 0.5; }
  .scene-bubble { animation: none; opacity: 1; transform: translateX(-50%) translateY(0); }
}

@media (max-width: 640px) {
  .scene-bubble { width: 112px; font-size: 0.68rem; padding: 0.45em 0.6em; }
}

/* ============ PRICING ============ */
.pricing {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding-top: var(--space-section-lg);
  padding-bottom: var(--space-section-lg);
  overflow: hidden;
}
.pricing-lede {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.3rem, 3vw, 1.7rem);
  letter-spacing: -0.01em;
  color: var(--ghost);
  text-align: center;
  max-width: 30ch;
  margin: 0 auto 3rem;
}
.pricing-founding-badge {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--coral);
  text-align: center;
  max-width: 460px;
  margin: -2rem auto 3rem;
  background: rgba(255, 137, 105, 0.1);
  border: 1px solid rgba(255, 137, 105, 0.3);
  border-radius: 999px;
  padding: 0.6em 1.2em;
}
.pricing-includes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  max-width: 480px;
  margin: 2.5rem auto 0;
}
.pricing-includes span {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--muted);
  background: rgba(241, 238, 255, 0.06);
  border: var(--hairline);
  border-radius: 999px;
  padding: 0.5em 0.95em;
}
.pricing-footnote {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted);
  text-align: center;
  max-width: 420px;
  margin: 1.25rem auto 0;
}
.pricing-includes span::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--mint);
  flex-shrink: 0;
}
.billing-toggle {
  display: inline-flex;
  gap: 0.25rem;
  background: rgba(241, 238, 255, 0.06);
  border: var(--hairline);
  border-radius: 999px;
  padding: 0.3rem;
  margin: 0 auto 2rem;
}
.billing-toggle-btn {
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: none;
  border-radius: 999px;
  padding: 0.55em 1.1em;
  /* WCAG 2.5.8 minimum target size — the padding above alone renders at
     roughly 34px tall, under the 44px minimum. */
  min-height: 44px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  transition: background 0.15s ease, color 0.15s ease;
}
.billing-toggle-btn.active {
  background: var(--coral);
  color: #100e1b;
}
.billing-toggle-badge {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  background: rgba(143, 247, 199, 0.18);
  color: var(--mint);
  border-radius: 999px;
  padding: 0.2em 0.55em;
}
.billing-toggle-btn.active .billing-toggle-badge {
  background: rgba(16, 14, 27, 0.15);
  color: #100e1b;
}

.currency-toggle {
  display: inline-flex;
  gap: 0.25rem;
  background: rgba(241, 238, 255, 0.06);
  border: var(--hairline);
  border-radius: 999px;
  padding: 0.25rem;
  margin: 0 auto 2rem;
}
.currency-toggle-btn {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: none;
  border-radius: 999px;
  padding: 0.4em 0.9em;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.currency-toggle-btn.active {
  background: var(--mint);
  color: #100e1b;
}

.tier-toggle {
  display: inline-flex;
  gap: 0.25rem;
  background: rgba(241, 238, 255, 0.06);
  border: var(--hairline);
  border-radius: 999px;
  padding: 0.25rem;
  margin: 0 auto 2rem;
}
.tier-toggle-btn {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: none;
  border-radius: 999px;
  padding: 0.4em 0.9em;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.tier-toggle-btn.active {
  background: var(--coral);
  color: #100e1b;
}

.receipt {
  background: var(--panel);
  border: 1px dashed rgba(241, 238, 255, 0.25);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 2.5rem 2.25rem;
  font-family: var(--font-mono);
  text-align: center;
  max-width: 360px;
  width: 100%;
}
.receipt [data-reveal] {
  opacity: 0;
  transform: translateY(24px);
}
.receipt-title { font-size: 0.95rem; letter-spacing: 0.02em; margin-bottom: 0.75rem; }
.receipt-divider { color: rgba(241, 238, 255, 0.25); margin: 0.5rem 0; font-size: 0.85rem; }
.receipt-line { font-size: 1.1rem; margin: 0.75rem 0; }
.receipt-line--muted { color: var(--muted); font-size: 0.9rem; }
.receipt-cta { margin-top: 1.5rem; width: 100%; font-family: var(--font-body); }

/* ============ SIGNUP ============ */
.signup { overflow: hidden; }
.signup-sub {
  text-align: center;
  color: var(--muted);
  max-width: 40ch;
  margin: -2rem auto 2.5rem;
}
.signup-form {
  max-width: 560px;
  margin: 0 auto;
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: clamp(1.75rem, 4vw, 2.75rem);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.field { display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
.field--referral { max-width: 360px; }
.field-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.field-hint {
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.4;
}
.field-hint--error { color: var(--coral); }

.password-strength { display: flex; align-items: center; gap: 0.6em; margin-top: 0.35em; }
.password-strength[hidden] { display: none; }
.password-strength-bars { display: flex; gap: 0.3em; flex: 1; }
.password-strength-bar {
  height: 4px;
  flex: 1;
  border-radius: 2px;
  background: rgba(241, 238, 255, 0.12);
  transition: background-color 0.2s ease;
}
.password-strength-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  white-space: nowrap;
}
.field input {
  background: rgba(241, 238, 255, 0.06);
  border: 1px solid rgba(241, 238, 255, 0.15);
  border-radius: var(--radius-sm);
  padding: 0.75em 0.9em;
  color: var(--ghost);
}
.field input::placeholder { color: rgba(167, 159, 196, 0.6); }
.field input:focus {
  border-color: var(--mint);
  animation: input-glow-breathe 2.4s ease-in-out infinite;
}

@keyframes input-glow-breathe {
  0%, 100% { box-shadow: 0 0 0 3px rgba(143, 247, 199, 0.12); }
  50% { box-shadow: 0 0 0 6px rgba(143, 247, 199, 0.24); }
}

.flatmates-label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin-bottom: 0.25rem;
}
.flatmate-row {
  display: flex;
  gap: 1rem;
  align-items: flex-end;
  margin-bottom: 1rem;
  position: relative;
}
.signup-account-row {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  margin-bottom: 1.25rem;
}
.field--half { flex: 1 1 0; min-width: 0; }
/* Phone numbers need more room than most names — 40/60 rather than an even split */
.flatmate-row label.field--half:first-of-type { flex: 0 1 40%; }
.flatmate-row label.field--half:last-of-type { flex: 1 1 60%; }
.remove-flatmate {
  flex: 0 0 auto;
  width: 2em;
  height: 2em;
  /* The visible circle stays small and compact within the row, but the
     clickable box is expanded to the WCAG 2.5.8 minimum via min-width/
     min-height (2em ~= 35px at this font-size, under the 44px minimum). */
  min-width: 44px;
  min-height: 44px;
  border-radius: 50%;
  background: rgba(255, 137, 105, 0.15);
  color: var(--coral);
  font-size: 1.1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.remove-flatmate:hover { background: rgba(255, 137, 105, 0.28); }

.phone-input-group { display: flex; gap: 0.5rem; }
.phone-country-select {
  flex: 0 0 auto;
  width: 6.5em;
  background: rgba(241, 238, 255, 0.06);
  border: 1px solid rgba(241, 238, 255, 0.15);
  border-radius: var(--radius-sm);
  padding: 0.75em 0.5em;
  color: var(--ghost);
  font-size: 0.85rem;
}
.phone-input-group input { flex: 1 1 auto; min-width: 0; }
.field input.input-invalid,
.phone-country-select.input-invalid {
  border-color: var(--coral);
  animation: none;
}
.field-error {
  color: var(--coral);
  font-size: 0.8rem;
}

.signup-submit-row { display: flex; justify-content: center; }
.signup-submit-row .btn { width: 100%; }
.signup-error {
  color: var(--coral);
  font-size: 0.9rem;
  text-align: center;
  background: rgba(255, 137, 105, 0.1);
  border-radius: var(--radius-sm);
  padding: 0.75em 1em;
}

/* ============ FOOTER ============ */
.site-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  padding: 3rem 1.5rem 4rem;
  color: var(--muted);
  font-size: 0.85rem;
}
.site-footer .ghost-glyph { color: var(--muted); }
.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.footer-links a { color: var(--muted); }
.footer-links a:hover { color: var(--ghost); }
.footer-links span { color: rgba(167, 159, 196, 0.35); }
.footer-company { font-size: 0.78rem; color: rgba(167, 159, 196, 0.7); margin-top: -0.75rem; }
.footer-company a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.footer-company a:hover { color: var(--ghost); }

/* ============ Content pages (privacy/terms/about/help/contact) ============ */
.content-page {
  position: relative;
  overflow: hidden;
  max-width: none;
  padding: var(--space-section) clamp(1.25rem, 5vw, 3rem);
}
.content-page-inner {
  position: relative;
  z-index: 1;
  max-width: 680px;
  margin: 0 auto;
  animation: content-fade-up 0.7s ease both;
}
@keyframes content-fade-up {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 2.5rem;
}
.back-link:hover { color: var(--ghost); }
.content-page h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.2rem, 5.5vw, 3.2rem);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin-bottom: 0.75rem;
  color: var(--ghost);
}
.page-subtitle {
  color: var(--muted);
  font-size: 1rem;
  margin-bottom: 3rem;
}
.content-page h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.4rem;
  margin: 2.5rem 0 1rem;
  color: var(--ghost);
}
.content-page p {
  color: var(--muted);
  line-height: 1.7;
  margin-bottom: 1.25rem;
}
.content-page p strong { color: var(--ghost); font-weight: 600; }
.content-page a { color: var(--mint); text-decoration: underline; text-underline-offset: 2px; }
.content-page a:hover { color: var(--ghost); }
.content-page a.btn--coral, .content-page a.btn--coral:hover { text-decoration: none; color: #1a0e08; }
.content-page ul { margin: 0 0 1.25rem; padding-left: 1.25rem; color: var(--muted); line-height: 1.7; }
.content-page li { margin-bottom: 0.5rem; }
/* Sub-groups within a single card (e.g. help.html's command reference,
   grouped into "Everyday" / "Money" / "Admin only" instead of one flat
   list) — deliberately smaller and quieter than .faq-item's own h2, so the
   card's actual heading still reads as the primary label. */
.content-page h3.group-label {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--coral);
  margin: 1.5rem 0 0.6rem;
}
.content-page h3.group-label:first-child { margin-top: 0; }

/* .how-card is normally faded in by script.js's scroll-reveal (index.html
   only) — content pages don't load that script, so reused here as a plain
   static card, opacity/transform reset back to visible. */
.content-page .how-card { opacity: 1; transform: none; }
.content-page .how-grid { margin-bottom: 1.5rem; }

/* Shared card treatment for every content page (help FAQs, privacy/terms
   sections, contact methods, the about-page CTA) — one visual language
   instead of some pages using cards and others just floating bare
   h2/p pairs on the page background. */
.faq-item,
.contact-card,
.info-card {
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 1.75rem;
  margin-bottom: 1.5rem;
}
.faq-item h2,
.contact-card h2,
.info-card h2 {
  margin-top: 0;
}
.faq-item p:last-child,
.contact-card p:last-child,
.info-card p:last-child,
.info-card ul:last-child {
  margin-bottom: 0;
}
.contact-card a { font-family: var(--font-mono); font-size: 1.05rem; }
.contact-card p.contact-desc { color: var(--muted); font-size: 0.92rem; margin-top: 0.5rem; margin-bottom: 0; }

/* A single accent-colored line breaking up a long run of plain paragraphs
   (about.html) — same idea as .pricing-lede's display-font treatment,
   scaled down and left-aligned to sit naturally inside body copy. */
.pull-quote {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.3rem, 3vw, 1.6rem);
  line-height: 1.4;
  color: var(--coral);
  border-left: 2px solid rgba(255, 137, 105, 0.4);
  padding-left: 1.25rem;
  margin: 2rem 0;
}

.content-page .glow--1 { width: 40vw; height: 40vw; max-width: 480px; max-height: 480px; top: -8%; right: -10%; }

@media (prefers-reduced-motion: reduce) {
  .content-page-inner { animation: none; }
}

.not-found-inner {
  text-align: center;
  max-width: 480px;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 2rem;
}
.ghost-glyph--large { width: 96px; height: 96px; color: var(--coral); margin-bottom: 1.5rem; }
.not-found-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--mint);
  letter-spacing: 0.08em;
  margin-bottom: 0.75rem;
}
.not-found-inner .page-subtitle { margin-bottom: 2rem; }

/* ============ Scroll-reveal base state ============ */
[data-reveal] { will-change: transform, opacity; }

/* ============ Ambient glow shapes ============ */
/* z-index: -1 so glows always sit behind normal-flow content in their
   stacking context (.problem/.pricing/.signup are position:relative via
   `main > section`) — no per-section content elevation needed. */
.glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  pointer-events: none;
  z-index: -1;
  will-change: transform;
}
.glow--lavender { background: radial-gradient(circle, rgba(241, 238, 255, 0.22), rgba(241, 238, 255, 0) 70%); }
.glow--coral { background: radial-gradient(circle, rgba(255, 137, 105, 0.20), rgba(255, 137, 105, 0) 70%); }

.hero-scroll .glow--1 { width: 46vw; height: 46vw; max-width: 620px; max-height: 620px; top: 6%; left: -8%; }
.hero-scroll .glow--2 { width: 34vw; height: 34vw; max-width: 480px; max-height: 480px; bottom: 8%; right: -6%; }
.problem .glow--1 { width: 32vw; height: 32vw; max-width: 440px; max-height: 440px; top: -6%; right: -8%; }
.pricing .glow--1 { width: 40vw; height: 40vw; max-width: 520px; max-height: 520px; top: -10%; left: 50%; transform: translateX(-50%); }
.dashboard-feature .glow--1 { width: 36vw; height: 36vw; max-width: 480px; max-height: 480px; top: -14%; right: -10%; }
.signup .glow--1 { width: 34vw; height: 34vw; max-width: 460px; max-height: 460px; bottom: -12%; left: -8%; }

/* ============ Intro splash ============ */
/* Plays on every full page load, unconditionally — deliberately not
   gated behind sessionStorage/"seen before" logic. That once-per-session
   version made this untestable in practice (a developer reloading to
   check a change would almost never see it, since the flag from the
   previous load was still set) and real visitors get the same fresh
   entrance each time they arrive, which is a reasonable trade for a
   ~4s moment, not an annoyance. fill-mode: both keeps the overlay fully
   opaque for the hold before the fade-out keyframe starts, then removes
   it from hit-testing and the accessibility tree at the very end via the
   (non-interpolated, so it just snaps) visibility/pointer-events
   properties in the `to` state. */
.intro-splash {
  display: flex;
  flex-direction: column;
  position: fixed;
  inset: 0;
  z-index: 300;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  background: var(--bg);
  animation: intro-splash-out 0.6s ease 3.4s both;
}
/* The wordmark is the actual centerpiece now — a comet-trailed ghost
   swirls a tight elliptical orbit around it (scale + rotateY standing in
   for depth: bigger/rotated when passing "in front", smaller when
   passing "behind"), then streaks off and fades, shooting-star style,
   rather than parking itself as a static icon once it lands. */
.intro-splash-orbit-stage {
  position: relative;
  display: inline-flex;
}
.intro-splash-word {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 500;
  font-size: clamp(2rem, 9vw, 3rem);
  color: var(--ghost);
  text-shadow: 0 0 34px rgba(241, 238, 255, 0.4);
  opacity: 0;
  animation: intro-word-in 0.6s ease 0.5s both;
}
/* Three copies of the same orbit path on a fixed time lag (positive
   animation-delay against the same duration holds a constant phase
   offset forever, not just on the first pass) — a cheap CSS-only
   trail/glow streak with no JS, same trick as the ghost-scene section's
   flight trail further down the page. */
.intro-splash-orbit-ghost {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  margin: -23px 0 0 -23px;
  color: var(--ghost);
  transform-style: preserve-3d;
  will-change: transform, opacity;
  animation: intro-ghost-orbit 2.3s cubic-bezier(0.4, 0, 0.2, 1) 0.35s both;
}
.intro-splash-orbit-ghost .ghost-glyph { width: 100%; height: 100%; }
.intro-splash-orbit-ghost--main {
  z-index: 3;
  filter: drop-shadow(0 0 18px rgba(241, 238, 255, 0.55));
}
.intro-splash-orbit-ghost--trail1 { z-index: 2; opacity: 0.28; filter: blur(2px); animation-delay: 0.42s; }
.intro-splash-orbit-ghost--trail2 { z-index: 1; opacity: 0.13; filter: blur(4px); animation-delay: 0.5s; }
@keyframes intro-ghost-orbit {
  0%   { transform: translate(14px, -78px) scale(0.6) rotateY(20deg) rotate(-10deg); opacity: 0; }
  7%   { opacity: 1; }
  18%  { transform: translate(132px, -18px) scale(0.95) rotateY(-15deg) rotate(8deg); }
  34%  { transform: translate(150px, 46px) scale(1.2) rotateY(-35deg) rotate(11deg); }
  50%  { transform: translate(0px, 74px) scale(1.3) rotateY(0deg) rotate(0deg); }
  66%  { transform: translate(-150px, 40px) scale(1.15) rotateY(35deg) rotate(-10deg); }
  82%  { transform: translate(-128px, -22px) scale(0.9) rotateY(15deg) rotate(-8deg); }
  93%  { transform: translate(-16px, -56px) scale(0.65) rotateY(-10deg) rotate(5deg); opacity: 1; }
  100% { transform: translate(8px, -100px) scale(0.3) rotateY(0deg) rotate(0deg); opacity: 0; }
}
.intro-splash-tagline {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  opacity: 0;
  animation: intro-word-in 0.55s ease 2.75s both;
}
@keyframes intro-word-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes intro-splash-out {
  0% { opacity: 1; }
  100% { opacity: 0; visibility: hidden; pointer-events: none; }
}
/* No separate "page fades in underneath" animation here — deliberately.
   A previous version put an opacity 0→1 animation on <body> itself,
   timed to start as the splash faded out, meant to read as one
   continuous crossfade. Real, serious bug: CSS opacity on a parent
   multiplies into every descendant's actual rendered pixels, including a
   position:fixed child — so while body sat at opacity 0, the splash
   (a child of body) was ALSO invisible for that entire window, no matter
   what the splash's own opacity said. getComputedStyle on the splash
   itself kept reporting "1" throughout testing, because that check only
   reads an element's own opacity property, not the final composited
   result after ancestor opacity — a real blind spot, and the actual root
   cause of "blank pause, then the site just appears" across this whole
   feature's testing. The splash's own fade-out (it's opaque, covers the
   page, then fades to transparent) already reveals the real page
   underneath on its own — a second, independent fade on body was
   redundant even before it was actively harmful. */

@media (prefers-reduced-motion: reduce) {
  .intro-splash { display: none; }
}

/* html.quick-nav is set by an inline pre-paint <head> script that checks
   sessionStorage, written by nav-transition.js right before a click on an
   internal link navigates away. Landing here via a typed URL, bookmark,
   external link, or refresh never sets the flag, so the full splash above
   still plays — this only fires for genuine in-site navigation. */
html.quick-nav .intro-splash { display: none; }

/* ============ Page flash (quick internal-nav transition) ============ */
.page-flash {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 300;
  pointer-events: none;
  overflow: hidden;
}
html.quick-nav .page-flash {
  display: block;
}
.page-flash-ghost {
  position: absolute;
  top: 50%;
  left: -10%;
  width: 44px;
  height: 44px;
  margin-top: -22px;
  color: var(--ghost);
  filter: drop-shadow(0 0 16px rgba(241, 238, 255, 0.6));
  animation: page-flash-slide 0.5s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.page-flash-ghost .ghost-glyph { width: 100%; height: 100%; }
@keyframes page-flash-slide {
  0%   { left: -10%; opacity: 0; transform: scale(0.7) rotate(-8deg); }
  15%  { opacity: 1; }
  50%  { transform: scale(1.05) rotate(4deg); }
  88%  { opacity: 1; }
  100% { left: 110%; opacity: 0; transform: scale(0.7) rotate(8deg); }
}
@media (prefers-reduced-motion: reduce) {
  html.quick-nav .page-flash { display: none; }
}

/* ============ Film grain overlay ============ */
.grain-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* ============ Responsive: simplify hero below 768px ============ */
@media (max-width: 768px) {
  .hero-scroll { height: auto; }
  .hero-pin {
    position: static;
    height: auto;
    flex-direction: column;
    text-align: center;
    padding: 7rem 1.25rem 3rem;
  }
  .hero-copy { max-width: 100%; }
  .phone { width: 240px; height: 511px; border-width: 6px; border-radius: 29px; }
  .phone-notch { top: 11px; width: 72px; height: 21px; }
}

/* Short-but-wide viewports (e.g. a laptop window at 1280x600): plenty of
   width for the side-by-side layout, but not enough height for the 638px
   phone mockup to fit inside a 100vh pin box. Matches the isSimple
   threshold in script.js's matchMedia — falls back to the same non-pinned,
   fade-in-on-scroll hero used on mobile instead of overflowing the pin. */
@media (min-width: 769px) and (max-height: 779px) {
  .hero-scroll { height: auto; }
  .hero-pin { position: static; height: auto; padding: 7rem clamp(1.25rem, 6vw, 6rem) 3rem; }
}

/* Stack name/phone vertically only on narrow screens — side-by-side above this */
@media (max-width: 600px) {
  .flatmate-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
  .flatmate-row label.field--half:first-of-type,
  .flatmate-row label.field--half:last-of-type {
    flex: 1 1 auto;
  }
  .remove-flatmate { align-self: flex-end; }

  /* Same fix, same reason — the password/confirm-password pair was missed
     when .flatmate-row got this treatment. Squeezed into two ~170px
     columns on a phone, both placeholders ("At least 8 characters",
     "Re-enter your password") clip mid-word, unreadable. */
  .signup-account-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
}

/* ============ Auth pages (dashboard/portfolio login, signup, reset) ============ */
/* A single-card, viewport-centered layout for the utility pages that sit
   outside the marketing site proper (house/landlord login, signup,
   password reset) — previously each shipped its own plain system-font
   inline <style> block with none of the site's actual identity (no
   Fraunces, no grain/glow, no ghost-glyph header), a stark contrast right
   at the first real interaction point after signing up or clicking
   through from the marketing pages. Reuses the same tokens/patterns as
   everywhere else rather than inventing a second design language. */
.auth-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 7rem 1.25rem 3rem;
  position: relative;
  overflow: hidden;
}
.auth-card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 400px;
  background: var(--panel);
  border: var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: clamp(1.75rem, 4vw, 2.5rem);
  animation: content-fade-up 0.7s ease both;
}
.auth-card h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.55rem;
  text-align: center;
  margin: 0 0 0.4rem;
  color: var(--ghost);
}
.auth-card .page-subtitle { text-align: center; font-size: 0.88rem; margin-bottom: 1.85rem; }
.auth-card .field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 0.4rem;
}
.auth-card input {
  width: 100%;
  font-family: var(--font-body);
  font-size: 0.92rem;
  background: rgba(241, 238, 255, 0.06);
  border: 1px solid rgba(241, 238, 255, 0.15);
  border-radius: var(--radius-sm);
  color: var(--ghost);
  padding: 0.7em 0.85em;
  margin-bottom: 1.15rem;
}
.auth-card input::placeholder { color: rgba(167, 159, 196, 0.6); }
.auth-card input:focus {
  border-color: var(--mint);
  animation: input-glow-breathe 2.4s ease-in-out infinite;
  outline: none;
}
.auth-card .btn { width: 100%; }
.auth-card .links { text-align: center; margin-top: 1.1rem; font-size: 0.85rem; color: var(--muted); }
.auth-card .links a { color: var(--mint); text-decoration: underline; text-underline-offset: 2px; }
.auth-card .links a:hover { color: var(--ghost); }
.auth-error-banner,
.auth-success-banner {
  padding: 0.65em 0.95em;
  border-radius: var(--radius-sm);
  margin-bottom: 1.15rem;
  font-size: 0.85rem;
  display: none;
}
.auth-error-banner { background: rgba(255, 89, 89, 0.15); color: #ff6b6b; }
.auth-success-banner { background: rgba(143, 247, 199, 0.15); color: var(--mint); }
.auth-card .back-link { display: block; text-align: center; margin-bottom: 1.85rem; }
.auth-page .glow--1 { width: 42vw; height: 42vw; max-width: 480px; max-height: 480px; top: -12%; right: -14%; }

@media (prefers-reduced-motion: reduce) {
  .auth-card { animation: none; }
}

/* ============ Reduced motion ============ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-scroll { height: auto; }
  .hero-pin { position: static; height: auto; flex-direction: column; text-align: center; padding: 7rem 1.25rem 3rem; }
  .chat-input-text--original,
  .chat-input-text--calm,
  .ghost-grab,
  .typing-indicator { display: none; }
  .chat-bubble--calm { opacity: 1; transform: none; }
  .chat-message--out .chat-timestamp { opacity: 1; }
  .online-dot { animation: none; }
  [data-reveal] { opacity: 1; transform: none; }
  .glow { animation: none !important; }
  .field input:focus {
    animation: none;
    box-shadow: 0 0 0 3px rgba(143, 247, 199, 0.18);
  }
}
