/* Digiri landing page — dark constellation theme, same tokens as the app
   (see CLAUDE.md's "Design tokens" section). Plain CSS, no build step. */

:root {
  --bg: #0e1220;
  --bg-deep: #0a0d18;
  --surface: #171d31;
  --line: #2a3350;
  --text: #edeff7;
  --muted: #8b93b0;
  --self: #8f86f0;
  --work: #4fa3f7;
  --community: #f2784b;
  --travel: #e4b450;
  --industry: #3fc58f;

  /* Utility face for section kickers — echoes the app's own
     provenance/coordinate texture (timestamps, degree numbers) without
     introducing a new webfont. */
  --mono: ui-monospace, 'SF Mono', 'Roboto Mono', 'Cascadia Mono', Consolas, monospace;

  /* Display face for the hero h1 — the one piece of text on the page where
     the generic system-sans stack (see body, below) shouldn't carry the
     voice (Impeccable audit finding, issue #496: one font family carrying
     ~80% of all page text reads as a template default, not an authored
     choice). Self-hosted (landing/fonts/, font-src 'self' in vercel.json —
     no CSP change needed), SIL Open Font License, single 700 weight only
     (no other weight of this face is used anywhere on the page). */
  --display: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/ibm-plex-sans-700-latin.woff2') format('woff2');
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
}

.wrap {
  max-width: 780px;
  margin: 0 auto;
  padding: 0 24px;
}

a {
  color: var(--self);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

header.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px;
  max-width: 780px;
  margin: 0 auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 700;
  font-size: 18px;
  color: var(--text);
}
.brand .wordmark {
  height: 36px;
  width: auto;
}

nav.header-links {
  display: flex;
  gap: 20px;
  font-size: 14px;
}
nav.header-links a {
  color: var(--muted);
}

.login-link {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 8px 16px;
  color: var(--text) !important;
}
.login-link:hover {
  border-color: var(--self);
  text-decoration: none;
}

section.hero {
  position: relative;
  padding: 56px 24px 88px;
  overflow: hidden;
}

/* Ambient starfield behind the hero — canvas-drawn (see hero-graph.js),
   mirroring utils/emptySky.ts's real scatter constants. Extends above the
   section so stars aren't hard-clipped right at the header. */
#starfield {
  position: absolute;
  inset: -80px 0 0 0;
  height: calc(100% + 80px);
  width: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Four soft, offset color pools (self/work/travel/industry — the app's own
   circle-taxonomy hues, see CLAUDE.md's Design tokens) instead of one flat
   single-hue spotlight (Impeccable audit finding, issue #496: a plain
   radial glow is the generic AI-marketing-page tell; this reads as several
   named circles overlapping, tying the atmosphere to what the product
   actually visualizes rather than decoration for its own sake). Self stays
   the strongest pool, matching its "you're the center" role everywhere
   else in the product. */
.hero-glow {
  position: absolute;
  top: -220px;
  left: 50%;
  width: 900px;
  height: 900px;
  transform: translateX(-50%);
  background:
    radial-gradient(circle at 38% 42%, rgba(143, 134, 240, 0.15) 0%, transparent 46%),
    radial-gradient(circle at 63% 36%, rgba(79, 163, 247, 0.08) 0%, transparent 42%),
    radial-gradient(circle at 58% 64%, rgba(63, 197, 143, 0.07) 0%, transparent 40%),
    radial-gradient(circle at 40% 62%, rgba(228, 180, 80, 0.06) 0%, transparent 38%);
  z-index: 0;
  pointer-events: none;
}

.hero-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 24px;
  align-items: center;
}

/* Real product screenshot (issue #480), replacing the old hand-drawn
   #graph-canvas-wrap simulation — same slot, same max-width contract
   (440px desktop / 280px mobile below), so .hero-grid needed no layout
   changes. Defined here, before the mobile media query, for the same
   reason the canvas rule it replaced was: both target the same selector
   at equal specificity, so the base rule must come first or the media
   query's override is dead CSS (see #488's own fix for the bug this
   ordering avoids repeating). Styled as a plain browser window, not a
   phone frame — the only live surface today is the web build. */
.hero-screenshot-frame {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  overflow: hidden;
  max-width: 440px;
  margin: 0 auto;
  box-shadow: 0 20px 48px -20px rgba(0, 0, 0, 0.6);
}
@media (max-width: 860px) {
  .hero-grid {
    grid-template-columns: 1fr;
  }
  /* Headline/CTA stays first on mobile (DOM order) — the screenshot is a
     supporting visual, not the hook, and shouldn't push the actual pitch
     and waitlist form below the fold on a small screen. */
  .hero-screenshot-frame {
    max-width: 280px;
  }
}

/* Was preceded by a "Bridge-consent introductions" eyebrow/kicker above
   this h1 — removed (Impeccable audit finding, issue #496: a kicker above
   a heading is a hard ban per this codebase's craft floor, not a style
   choice — the heading carries its own weight). Removing it also fixed a
   second, unrelated finding from the same audit: that exact phrase was
   the first specific term a visitor saw, before the product's own copy
   ever explains what "bridge-consent" means. */
.hero h1 {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(34px, 4.6vw, 52px);
  letter-spacing: -0.02em;
  line-height: 1.08;
  margin: 0 0 20px;
}
.hero h1 .accent {
  color: var(--self);
}

.hero p.lede {
  color: var(--muted);
  font-size: 18px;
  max-width: 46ch;
  margin: 0 0 32px;
}

/* Thin title bar with 3 plain dots — a muted echo of the small filled-dot
   language used elsewhere on the page (e.g. the mini graph nodes) rather
   than imitating a literal macOS/Windows window control (those read as OS
   chrome, which isn't the point; this just needs to say "this is a
   window", not which OS it's running on). */
.hero-screenshot-chrome {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  background: var(--bg-deep);
  border-bottom: 1px solid var(--line);
}
.hero-screenshot-chrome span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--line);
}
.hero-screenshot-img {
  display: block;
  width: 100%;
  height: auto;
}

section.problem,
section.views,
section.features {
  padding: 48px 0;
  border-top: 1px solid var(--line);
}

/* Scroll-triggered reveal (hero-graph.js's initScrollReveal) — sections
   fade/rise into place once, the first time they cross into view.
   Progressive enhancement, not a hide-by-default: [data-reveal] elements
   are fully visible unless hero-graph.js actually runs and opts the page
   in via body.reveal-ready — so a blocked/broken script, or JS disabled
   entirely, never leaves a section permanently invisible. prefers-reduced-
   motion never gets reveal-ready at all (see hero-graph.js). */
[data-reveal] {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s ease, transform 0.6s ease;
}
body.reveal-ready [data-reveal]:not(.is-revealed) {
  opacity: 0;
  transform: translateY(16px);
}

section.problem .wrap,
section.features .wrap {
  max-width: 640px;
}

section.problem p {
  font-size: 17px;
  color: var(--text);
}
section.problem p.muted {
  color: var(--muted);
}

h2 {
  font-size: 26px;
  margin: 0 0 24px;
}

/* Section headings styled as a small mono "kicker" label instead of a big
   display line — still a real <h2> (heading semantics/SEO structure
   intact), just visually quiet so it doesn't compete with the hero. This
   is a real heading styled quietly, not a decorative label sitting above
   one — the pattern issue #496 removed from the hero was the latter. */
h2.section-kicker {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 28px;
}

.views-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}
@media (max-width: 640px) {
  .views-grid {
    grid-template-columns: 1fr;
  }
}
.view-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 28px;
  position: relative;
  overflow: hidden;
  transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.view-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(220px circle at 85% -10%, var(--card-glow, transparent), transparent 70%);
  opacity: 0.5;
  pointer-events: none;
}
.view-card:hover {
  transform: translateY(-3px);
  border-color: var(--card-accent, var(--self));
  box-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.5);
}
.view-card.circles {
  --card-accent: var(--work);
  --card-glow: rgba(79, 163, 247, 0.22);
}
.view-card.degrees {
  --card-accent: var(--community);
  --card-glow: rgba(242, 120, 75, 0.22);
}
.view-card .mini-canvas {
  width: 100%;
  height: 92px;
  display: block;
  margin-bottom: 18px;
}
.view-card h3 {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 700;
}
.view-card p {
  margin: 0;
  color: var(--muted);
  font-size: 15px;
}

.feature-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.feature-list li {
  padding-left: 28px;
  position: relative;
  color: var(--text);
}
.feature-list li strong {
  display: block;
  margin-bottom: 2px;
}
.feature-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--dot, var(--self));
  box-shadow: 0 0 10px var(--dot, var(--self));
}
/* Each feature gets its own accent instead of defaulting to violet
   everywhere — echoes the circle color language elsewhere on the page. */
.feature-list li:nth-child(1) {
  --dot: var(--self);
}
.feature-list li:nth-child(2) {
  --dot: var(--community);
}
.feature-list li:nth-child(3) {
  --dot: var(--industry);
}
.feature-list li p {
  margin: 0;
  color: var(--muted);
  font-size: 15px;
}

section.faq {
  padding: 56px 0;
  border-top: 1px solid var(--line);
}
section.faq .wrap {
  max-width: 680px;
}
.faq-list {
  margin-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.faq-list details {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 4px 20px;
  background: var(--surface);
}
.faq-list summary {
  padding: 14px 0;
  font-weight: 600;
  font-size: 15px;
  cursor: pointer;
  /* Default disclosure triangle differs enough across browsers to look
     inconsistent next to this page's otherwise deliberate visual language
     — a plain "+"/"−" via ::after (below) reads the same everywhere. */
  list-style: none;
}
.faq-list summary::-webkit-details-marker {
  display: none;
}
.faq-list summary::after {
  content: '+';
  float: right;
  color: var(--muted);
  font-weight: 400;
}
.faq-list details[open] summary::after {
  content: '−';
}
.faq-list details p {
  margin: 0 0 16px;
  color: var(--muted);
  font-size: 14px;
}
.faq-list details p a {
  color: var(--self);
}

section.waitlist {
  padding: 56px 0;
  border-top: 1px solid var(--line);
  text-align: center;
}
section.waitlist .wrap {
  max-width: 480px;
}
section.waitlist p {
  color: var(--muted);
  margin-bottom: 24px;
}
.waitlist-cta {
  display: inline-block;
  padding: 12px 24px;
  border-radius: 8px;
  background: var(--self);
  color: #0e1220;
  font-weight: 600;
  font-size: 15px;
}
.waitlist-cta:hover {
  opacity: 0.9;
  text-decoration: none;
}

/* Issue #482 — real waitlist-count social proof, filled in by
   waitlist-count.js. :empty collapses it entirely (no gap, no placeholder
   text) whenever the count fetch hasn't resolved yet or fails — the
   section already reads correctly with nothing here. */
.waitlist-count {
  font-size: 13px;
  color: var(--muted);
  margin: -12px 0 20px !important;
}
.waitlist-count:empty {
  display: none;
}

form#waitlist-form {
  display: flex;
  gap: 8px;
}
@media (max-width: 480px) {
  form#waitlist-form {
    flex-direction: column;
  }
}
form#waitlist-form input[type='email'] {
  flex: 1;
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font-size: 15px;
}
form#waitlist-form input[type='email']:focus {
  outline: none;
  border-color: var(--self);
}
form#waitlist-form button {
  padding: 12px 20px;
  border-radius: 8px;
  border: none;
  background: var(--self);
  color: #0e1220;
  font-weight: 600;
  font-size: 15px;
  cursor: pointer;
}
form#waitlist-form button:hover {
  opacity: 0.9;
}
form#waitlist-form button:disabled {
  opacity: 0.6;
  cursor: default;
}

#waitlist-status {
  min-height: 20px;
  margin-top: 12px;
  font-size: 14px;
  color: var(--muted);
}
#waitlist-status.success {
  color: var(--industry);
}
#waitlist-status.error {
  color: var(--community);
}

.have-code {
  margin-top: 16px;
  font-size: 14px;
  color: var(--muted);
}
.have-code a {
  color: var(--self);
  font-weight: 600;
}

footer.site-footer {
  border-top: 1px solid var(--line);
  padding: 32px 24px;
  text-align: center;
  color: var(--muted);
  font-size: 14px;
}
footer.site-footer nav {
  display: flex;
  justify-content: center;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

/* Issue #523 — hidden entirely above the .view-card breakpoint (640px,
   already used elsewhere in this file): on a real desktop viewport the
   hero's own CTA is one scroll away at most, so a persistent bar would
   just be visual noise. Below it, a long page means the hero CTA is off-
   screen for most of the scroll with nothing else reachable without
   scrolling back up — this fixes that. */
.sticky-cta {
  display: none;
}
@media (max-width: 640px) {
  .sticky-cta {
    display: block;
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: 16px;
    z-index: 10;
    padding: 14px;
    border-radius: 10px;
    background: var(--self);
    color: #0e1220;
    font-weight: 600;
    font-size: 15px;
    text-align: center;
    text-decoration: none;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  }
  /* Keeps the fixed bar from covering the footer's own links at the very
     bottom of the page — a fixed element sits in its own compositing
     layer, on top of everything else, regardless of DOM position. */
  body {
    padding-bottom: 72px;
  }
}

/* Issue #485 — exit-intent capture. `hidden` (the HTML attribute, toggled
   by exit-intent.js) already handles show/hide; the styling below just
   makes it read as a real modal once shown, matching the page's existing
   surface/border/radius tokens (.view-card, .faq-list details). */
.exit-intent-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  padding: 20px;
  background: rgba(10, 13, 24, 0.72);
}
/* Real bug caught live: `display: flex` directly on .exit-intent-overlay
   (equal specificity to the UA stylesheet's `[hidden] { display: none }`,
   and author styles always beat UA styles regardless of source order)
   made the modal render REGARDLESS of the `hidden` attribute — it was
   visible on every single page load. Scoping `display: flex` to
   `:not([hidden])` means no author rule sets `display` at all while
   `hidden` is present, so the browser's own default actually applies. */
.exit-intent-overlay:not([hidden]) {
  display: flex;
  align-items: center;
  justify-content: center;
}
.exit-intent-modal {
  position: relative;
  max-width: 420px;
  width: 100%;
  padding: 32px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  text-align: center;
  box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.6);
}
.exit-intent-modal h2 {
  margin: 0 0 8px;
}
.exit-intent-modal p {
  color: var(--muted);
  margin: 0 0 24px;
}
.exit-intent-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.exit-intent-close:hover {
  background: var(--line);
  color: var(--text);
}
@media (prefers-reduced-motion: no-preference) {
  .exit-intent-modal {
    animation: exit-intent-rise 0.2s ease;
  }
}
@keyframes exit-intent-rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Shared chrome for /privacy, /terms, /support — a simpler content page,
   not the marketing hero. */
main.doc {
  max-width: 680px;
  margin: 0 auto;
  padding: 48px 24px 80px;
}
main.doc h1 {
  font-size: 28px;
  margin-bottom: 8px;
}
main.doc h2 {
  font-size: 20px;
  margin-top: 36px;
}
main.doc p,
main.doc li {
  color: var(--text);
  font-size: 15px;
}
main.doc a {
  color: var(--self);
}
main.doc hr {
  border: none;
  border-top: 1px solid var(--line);
  margin: 24px 0;
}

/* Freezes every animation/transition added in this pass (starfield twinkle,
   hero-graph draw-in, card hover lift, scroll reveal) to their end state —
   hero-graph.js's own reduceMotion check stops the canvas RAF loops, this
   is the CSS-side half for the plain transition/animation properties. */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* html{scroll-behavior:smooth} (top of file) isn't a transition/animation,
     so the rule above doesn't touch it — the #waitlist-jump CTA would still
     force a smooth scroll on a motion-sensitive visitor without this. */
  html {
    scroll-behavior: auto;
  }
}
