/*
 * Palette shared with the LedgerGuard dashboard so the two read as one property. It is
 * still a deliberate copy of shell/src/index.css rather than a shared import — the
 * dashboard is a Vite bundle and this page is static files served straight off disk with
 * no build step between them — so a change to either :root must be made in both. The
 * dashboard also repeats several of these as literals in its Recharts props, because SVG
 * attributes cannot resolve CSS variables; those are listed in shell/src/pages/.
 *
 * Two values are chosen for contrast rather than taste, and should not be "fixed":
 *
 *   --accent (#1D3FCC) is a fill, not a text colour. White on it is 7.97:1, but it on
 *   --bg-primary is only 2.53:1 — which fails AA — so link text uses --accent-hover
 *   (5.03:1) instead. Recolouring links back to --accent would break that.
 *
 *   --text-muted inside .card is lifted to --text-secondary, because .card carries
 *   opacity 0.72 and compositing drops muted text to 3.04:1. See the rule below.
 */
:root {
  /* Structure — near-black, cooled toward blue */
  --bg-primary: #05070e;
  --bg-secondary: #0a0e1c;
  --bg-card: #10162b;
  --border: #1b2340;

  /* Text. --text-primary is for headings and metric values only; body copy is
     --text-secondary. If everything is bright the page reads flat and the blue stops
     landing. */
  --text-primary: #eef1f8;
  --text-secondary: #c6cee4;
  --text-muted: #78859f;

  /* Bleu roi — the one saturated family on the page */
  --accent: #1d3fcc;
  --accent-hover: #4c74ff;
  --accent-deep: #0e2278;

  /* State — reserved, never decorative */
  --green: #2bd98a;
  --yellow: #e8b42b;
  --red: #ff4d5e;

  /*
   * Self-hosted from ./fonts, Latin-subset woff2, so the page still makes no
   * third-party request. The system fallbacks stay in the stack for the moment between
   * first paint and the font arriving, and for the case where a face fails to load.
   */
  --font-display: "Archivo", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/*
 * Subset to Basic Latin plus the handful of typographic marks this page uses (em dash,
 * curly quotes, ellipsis, middle dot, minus sign). Regenerating them, from the Latin
 * subsets Google serves:
 *
 *   pyftsubset FONT.woff2 --output-file=OUT.woff2 --flavor=woff2 --no-hinting \
 *     --unicodes="U+0020-007E,U+00A0,U+00B0,U+00B7,U+00A9,U+00D7,U+2013-2014,\
 *                 U+2018-2019,U+201C-201D,U+2022,U+2026,U+2192,U+2212" \
 *     --layout-features="kern,liga,tnum"
 *
 * All three are SIL Open Font License 1.1 — see fonts/LICENSE.md.
 */
@font-face {
  font-family: "Archivo";
  /* Display only: the H1 and the section headings. One weight, so no variable axis. */
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/archivo-700.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  /*
   * A weight *range*, not a single value: this is the variable font with its wght axis
   * clamped to 400-600, which is every weight the page actually asks for. One 14 KB file
   * covering three weights beats three static instances at ~8 KB each, and it means no
   * weight is ever synthesised.
   */
  font-weight: 400 600;
  font-display: swap;
  src: url("fonts/inter-400-600.woff2") format("woff2-variations"),
       url("fonts/inter-400-600.woff2") format("woff2");
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  /* Built without liga/calt. JetBrains Mono ships a large programming-ligature set
     (=>, ->, ===, !=) that prose will never form; dropping it saved 11.5 KB of the
     25.5 KB font budget. */
  src: url("fonts/jetbrains-mono-500.woff2") format("woff2");
}

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

body {
  font-family: var(--font-sans);
  background-color: var(--bg-primary);
  color: var(--text-secondary);
  -webkit-font-smoothing: antialiased;
  line-height: 1.6;

  /*
   * The whole background whenever the canvas is not there — no WebGL, a shader that
   * failed to compile, a lost context, scripting off. The page has to look finished in
   * every one of those cases, so this is not a placeholder: it is a deliberately composed
   * still of the same scene, dark at the lower left where the reading column sits and
   * carrying a soft glow at the upper right where the planet would be. The colours are
   * --accent-deep and --accent-hover, written as rgba because a gradient needs the alpha.
   *
   * No background-attachment: fixed. It was a documented scroll-jank source on mobile,
   * and the canvas now does the parallax properly.
   */
  background-image:
    radial-gradient(ellipse 34% 46% at 87% 2%, rgba(76, 116, 255, 0.14), transparent 62%),
    radial-gradient(ellipse 85% 55% at 50% -8%, rgba(14, 34, 120, 0.55), transparent 68%),
    radial-gradient(ellipse 60% 45% at 92% 4%, rgba(29, 63, 204, 0.16), transparent 62%);
  background-repeat: no-repeat;
  min-height: 100vh;
}

/*
 * Behind everything, and inert. Fixed rather than absolute so it costs one compositor
 * layer instead of a document-height bitmap.
 */
#space {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  display: block;
}

a {
  /* --accent-hover, not --accent: see the contrast note at the top of this file. */
  color: var(--accent-hover);
  text-decoration: none;
}

a:hover {
  color: var(--accent-hover);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* Visible only once focused, so keyboard users can jump the intro. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: 0.6rem 1rem;
  background: var(--accent);
  color: #fff;
  border-radius: 0 0 8px 0;
  z-index: 10;
}

.skip-link:focus {
  left: 0;
  color: #fff;
}

/* --accent-hover clears 3:1 against --bg-primary (5.03:1); --accent would not (2.53:1). */
:focus-visible {
  outline: 2px solid var(--accent-hover);
  outline-offset: 3px;
  border-radius: 4px;
}

.page {
  width: 100%;
  max-width: 60rem;
  margin: 0 auto;
  padding: clamp(3rem, 9vw, 7rem) clamp(1.15rem, 5vw, 2rem) 4rem;
}

/* ---------- Type ---------- */

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text-primary);
  line-height: 1.15;
}

/*
 * Every figure on the page is monospaced with tabular numerals. The metrics are the
 * argument, and lining figures of equal width read as measurements rather than as
 * marketing.
 */
.eyebrow,
.pill,
.stack li,
.metrics-title,
.metric dt,
.metric dd,
.delta,
.card-meta {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
}

/* ---------- Intro ---------- */

.eyebrow {
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.75rem;
}

.intro h1 {
  /* Scales with the viewport rather than stepping at breakpoints, so the headline never
     lands on an awkward two-word last line. */
  font-size: clamp(1.9rem, 5.2vw, 3.05rem);
  line-height: 1.12;
  max-width: 20ch;
  text-wrap: balance;
}

.intro h1 em {
  font-style: normal;
  color: var(--accent-hover);
}

.lede {
  margin-top: 1.5rem;
  max-width: 60ch;
  font-size: clamp(1rem, 1.6vw, 1.1rem);
  color: var(--text-secondary);
}

.intro-links {
  margin-top: 1.75rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.95rem;
  color: var(--text-muted);
}

/* ---------- Featured project ---------- */

.feature {
  margin-top: clamp(3.5rem, 8vw, 5.5rem);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: clamp(1.4rem, 4vw, 2.5rem);
}

.feature-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.feature-head h2 {
  font-size: clamp(1.6rem, 3.6vw, 2.15rem);
}

.feature-lede {
  margin-top: 1.25rem;
  font-size: clamp(1rem, 1.5vw, 1.08rem);
  max-width: 68ch;
  color: var(--text-secondary);
}

.feature-body {
  margin-top: 1rem;
  color: var(--text-secondary);
  max-width: 68ch;
}

.feature-body em {
  font-style: italic;
  color: var(--text-primary);
}

.pill {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-secondary);
  color: var(--text-muted);
  white-space: nowrap;
}

.pill-live {
  border-color: rgba(43, 217, 138, 0.35);
  color: var(--green);
}

.dot {
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(43, 217, 138, 0.16);
}

/* ---------- Metrics ---------- */

.metrics-title {
  margin-top: 2.25rem;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  /* Overrides the display face inherited from the h3 rule — this is a label, not a
     heading, and it belongs to the data voice. */
  font-family: var(--font-mono);
  letter-spacing: 0.1em;
}

.metrics {
  margin-top: 0.9rem;
  display: grid;
  /* auto-fit + minmax collapses 4 → 2 → 1 columns on its own; no media query needed.
     The min(...,100%) is load-bearing: a bare minmax() floor is a hard minimum, so on a
     viewport narrower than the floor the track overflows the page instead of shrinking. */
  grid-template-columns: repeat(auto-fit, minmax(min(9.5rem, 100%), 1fr));
  gap: 0.75rem;
}

.metric {
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 11px;
  padding: 0.95rem 1.05rem;
}

.metric dt {
  font-size: 0.78rem;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}

.metric dd {
  margin-top: 0.3rem;
  font-size: 1.6rem;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.metric .unit {
  font-size: 0.82rem;
  color: var(--text-secondary);
  margin-left: 0.15rem;
  letter-spacing: 0;
}

.delta {
  margin-top: 0.35rem;
  font-size: 0.76rem;
  color: var(--text-muted);
}

.delta-good {
  color: var(--green);
}

.footnote {
  margin-top: 1.1rem;
  font-size: 0.83rem;
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 72ch;
}

/* ---------- Stack tags ---------- */

.stack {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 1.5rem;
}

.stack li {
  font-size: 0.73rem;
  padding: 0.25rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

/* ---------- Buttons ---------- */

.actions {
  margin-top: 1.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.65rem;
}

.button {
  display: inline-block;
  padding: 0.65rem 1.15rem;
  border-radius: 9px;
  border: 1px solid var(--border);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-size: 0.92rem;
  font-weight: 500;
  transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease;
}

.button:hover {
  border-color: var(--accent);
  color: var(--text-primary);
  text-decoration: none;
}

.button-primary {
  background: var(--accent);
  border-color: var(--accent);
  /* 7.97:1 — --accent earns its place as a fill even though it fails as a text colour. */
  color: #fff;
}

.button-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #fff;
}

/* ---------- Section dividers ---------- */

.section-title {
  margin-top: clamp(3rem, 7vw, 4.5rem);
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  color: var(--text-secondary);
}

/* The only decoration on the page: one accent rule per section. */
.section-title::before {
  content: "";
  display: block;
  height: 1px;
  width: 100%;
  margin-bottom: 1.4rem;
  background: linear-gradient(90deg, var(--accent), transparent 40%);
}

/* ---------- Cards ---------- */

.grid {
  list-style: none;
  margin-top: 1.1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
  gap: 0.9rem;
}

/* Two longer descriptions rather than three short ones, so they want more room each.
   This is the pair that actually overflowed at 320px: a 19rem floor is 304px, and after
   the page padding only 283px remain. */
.grid-two {
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
}

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 13px;
  padding: 1.3rem;
  /* Muted against the feature card: these are not built yet, and the page should not
     pretend otherwise by giving them equal visual weight. */
  opacity: 0.72;
  display: flex;
  flex-direction: column;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

/*
 * Compositing an opacity of 0.72 over --bg-primary drops --text-muted to 3.04:1, under
 * AA for text this size. --text-secondary composites to 6.31:1 and keeps the pill
 * readable without giving up the 0.72 hierarchy signal.
 */
.card .pill {
  color: var(--text-secondary);
}

/* MIA.ai: real, finished work, so it is not dimmed. */
.card-solid {
  margin-top: 0.9rem;
  opacity: 1;
  padding: clamp(1.3rem, 3vw, 1.9rem);
}

.card-solid .pill {
  color: var(--text-muted);
}

/* Border and a soft ring only. Nothing lifts, translates or scales — the page has no
   other motion on hover and a card that jumps under the cursor would be the loudest
   thing on it. */
.card:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px rgba(76, 116, 255, 0.4);
}

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.7rem;
}

.card-head h2 {
  font-size: clamp(1.3rem, 2.6vw, 1.6rem);
}

.card-head h3 {
  font-size: 1.12rem;
}

.card p {
  font-size: 0.9rem;
  color: var(--text-secondary);
}

.card-solid p {
  font-size: clamp(0.94rem, 1.4vw, 1rem);
  max-width: 72ch;
}

.card-meta {
  margin-top: -0.35rem;
  margin-bottom: 0.6rem;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Pushes the tags to the bottom so cards of differing text length still line up. */
.card .stack {
  margin-top: auto;
  padding-top: 1.1rem;
}

.card .actions {
  margin-top: 1.1rem;
}

.aside {
  margin-top: 1.6rem;
  font-size: 0.86rem;
  color: var(--text-muted);
  max-width: 74ch;
}

/* ---------- Footer ---------- */

.footer {
  margin-top: clamp(3.5rem, 8vw, 5rem);
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
  font-size: 0.86rem;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.6rem;
}

/* ---------- Scroll reveal ---------- */

/*
 * The hidden state is applied only when space.js confirms scripting is live and motion
 * is allowed. Putting it in plain CSS would leave the whole page invisible to anyone
 * with JavaScript disabled — the failure mode where a "progressive enhancement" hides
 * the content it was meant to enhance.
 */
.reveal-ready [data-reveal] {
  opacity: 0;
  transform: translateY(12px);
}

.reveal-ready [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  transition: opacity 0.5s ease, transform 0.5s ease;
}

/* Respect a stated preference for less motion. The canvas checks the same query and
   renders a single static frame rather than starting its loop. */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
  }

  /* Belt and braces: if the class were ever applied under this preference, content must
     still be visible rather than stuck at opacity 0 with its transition disabled. */
  .reveal-ready [data-reveal] {
    opacity: 1;
    transform: none;
  }

  /* The single frame space.js draws under this preference is the only one there will ever
     be, which also means it is the one state where the scene cannot dim itself as the
     reader scrolls — that fade is driven from the frame loop. Held back here instead. */
  #space {
    opacity: 0.72;
  }
}
