/*
 * One stylesheet, no framework, no build step.
 *
 * Dark by default because the product is a dark viewport, but the reader's own setting wins — a light-mode
 * user opening a black page has been told the page does not care about them.
 */

:root {
  --bg: #0d1016;
  --bg-alt: #11151d;
  --panel: #151a23;
  --line: #222a36;
  --text: #dde2ec;
  --dim: #949dad;
  --accent: #7fd8a0;
  --accent-ink: #06281a;
  --code: #cbd4e4;
  --codebg: #0b0e14;
  --shadow: 0 8px 32px rgb(0 0 0 / 45%);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #ffffff;
    --bg-alt: #f6f8fa;
    --panel: #ffffff;
    --line: #e2e7ee;
    --text: #171c24;
    --dim: #5a6473;
    --accent: #14764a;
    --accent-ink: #ffffff;
    --code: #1e2a38;
    --codebg: #f2f5f8;
    --shadow: 0 8px 28px rgb(15 25 40 / 10%);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--accent); }

h1, h2, h3 { letter-spacing: -0.02em; line-height: 1.2; }

code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.88em;
  color: var(--code);
}

pre {
  margin: 0 0 16px;
  padding: 12px 14px;
  overflow-x: auto;
  background: var(--codebg);
  border: 1px solid var(--line);
  border-radius: 8px;
}

pre code { white-space: pre; }

/* The install line on a release card. site.js writes these; nothing on the landing page uses it any more. */
.install { display: inline-block; margin-bottom: 14px; font-size: 15px; }

/* The top bar lives in header.css — the same file, the same markup and the same class names as the other
   sites in this family. It reads --bar and the palette above and nothing else. */

/* ---- hero ----

   The hero stands on shots/hero.jpg, which is a dark photograph in both themes — so inside .hero-wrap the
   palette stops following the page and fixes itself. Redefining the custom properties on the wrapper rather
   than restyling each element means everything inside, including anything added later, is already correct:
   a light-mode reader gets a dark hero, which is the only readable answer short of shipping a second
   photograph. */

.hero-wrap {
  position: relative;
  isolation: isolate;
  background: #06080d url("shots/hero.jpg") center / cover no-repeat;

  --text: #eef2f8;
  --dim: #b6c0ce;
  --code: #dbe4f0;
  --codebg: rgb(6 9 14 / 66%);
  --line: rgb(255 255 255 / 17%);
  --bg-alt: rgb(255 255 255 / 8%);
  --accent: #7fd8a0;
  --accent-ink: #06281a;
}

/* A little more darkness under the words than the file already carries. The baked gradient is aimed at one
   crop; this one survives every other, which matters most on a phone, where cover throws away most of the
   width and the part that survives is not the part that was darkened. */
.hero-wrap::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(100deg, rgb(5 7 12 / 62%) 0%, rgb(5 7 12 / 24%) 46%, rgb(5 7 12 / 0%) 68%);
}

.hero {
  display: grid;
  grid-template-columns: minmax(0, 640px);
  gap: 48px;
  align-items: center;
  max-width: 1240px;
  margin: 0 auto;
  padding: 78px 28px 56px;
}

.hero-credit {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 28px 26px;
  color: var(--dim);
  font-size: 12.5px;
}

.eyebrow {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
}

.hero h1 { margin: 0 0 18px; font-size: clamp(30px, 4.4vw, 46px); }

.lede { color: var(--dim); font-size: 17px; margin: 0 0 22px; }

/* ---- the two commands ----

   A label over each command rather than beside it, and the copy button spanning both rows on the right.
   Beside would work for "Install the package" and then break on the clone URL, which is fifty-six
   characters and has nowhere to go on a phone; this way the command owns the full width of the row and
   scrolls inside it when there is not enough. */

.get { display: grid; gap: 10px; margin: 0 0 24px; max-width: 620px; }

.get-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px;
  padding: 9px 9px 11px 14px;
  background: var(--codebg);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.get-label {
  grid-column: 1;
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--dim);
}

.get-row code {
  grid-column: 1;
  overflow-x: auto;
  white-space: nowrap;
  font-size: 13.5px;
  scrollbar-width: none;
}

.get-row code::-webkit-scrollbar { display: none; }

.copy {
  grid-column: 2;
  grid-row: 1 / 3;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--dim);
  cursor: pointer;
}

.copy:hover { color: var(--text); border-color: var(--accent); }
.copy svg { width: 16px; height: 16px; fill: currentColor; }

/* Both children occupy the same cell, so the button does not change size when the word appears. */
.copy > * { grid-area: 1 / 1; }
.copy .copy-label {
  font-size: 10px;
  font-weight: 650;
  letter-spacing: 0.03em;
  color: var(--accent);
  opacity: 0;
}

.copy[data-copied] { border-color: var(--accent); }
.copy[data-copied] svg { opacity: 0; }
.copy[data-copied] .copy-label { opacity: 1; }

/* Present to a screen reader, absent from the page. The icon says "copy" to everyone who can see it. */
.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.beta-note {
  margin: 0 0 22px;
  padding: 12px 16px;
  border-left: 3px solid var(--accent);
  background: var(--bg-alt);
  border-radius: 0 8px 8px 0;
  color: var(--dim);
  font-size: 14px;
}

.beta-note strong { color: var(--text); }

.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; margin: 0 0 20px; }

.button {
  display: inline-block;
  padding: 10px 18px;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 550;
}

.button:hover { border-color: var(--accent); }

.button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

figcaption { color: var(--dim); font-size: 13px; margin-top: 10px; }

/* ---- the demo stage ----

   demo.html is one control filling the window, so its page is a column: bar, stage, one line underneath.
   The stage takes what is left rather than a fixed height, because "what is left" is the only answer that
   is right on a laptop, on a phone in either orientation, and on the two-row bar in between. */

/* height, not min-height. A flex column sized only by min-height has no definite height to hand down, so
   the percentage below has nothing to resolve against — and an iframe with no resolvable height falls back
   to its default of 150 pixels, which is what a three-dimensional scene squeezed into a letterbox at the
   top of a black page looks like. */
body.demo-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100svh;
}

/* Grid with one explicit row, for the same reason. A flex item's used height is not definite enough for a
   percentage child in every browser; 1fr of a definite container is. */
.stage {
  flex: 1 1 auto;
  min-height: 320px;
  display: grid;
  grid-template-rows: 1fr;
}

.stage-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #06080d;
}

.stage-note {
  margin: 0;
  padding: 12px 22px 14px;
  border-top: 1px solid var(--line);
  background: var(--panel);
  color: var(--dim);
  font-size: 13px;
}

.stage-note strong { color: var(--text); }

/* ---- sections ---- */

/* scroll-margin because the header is sticky: without it a jump to #start puts the heading behind the bar.
   The 64px padding hides that on a wide screen by accident, and stops hiding it the moment the bar wraps. */
section { max-width: 1240px; margin: 0 auto; padding: 64px 28px; scroll-margin-top: var(--bar); }

section h2 {
  font-size: clamp(22px, 2.6vw, 30px);
  margin: 0 0 10px;
}

section > .lede { max-width: 70ch; }

.story, .measured { border-top: 1px solid var(--line); }
.features, .start, .limits { border-top: 1px solid var(--line); }

/* ---- story slider ---- */

.slider { position: relative; margin-top: 28px; }

.track { display: grid; gap: 40px; }

/* Applied by script. Without it every slide is simply visible, stacked, which is a perfectly good page. */
.slider.live .track { display: block; }
.slider.live .slide { display: none; }
.slider.live .slide.current { display: grid; }

.slide {
  margin: 0;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: 32px;
  align-items: center;
}

.slide img {
  border-radius: 12px;
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
  background: var(--codebg);
}

.slide figcaption { margin: 0; color: var(--text); }
.slide figcaption h3 { margin: 0 0 10px; font-size: 19px; }
.slide figcaption p { margin: 0; color: var(--dim); }

.slide-nav {
  position: absolute;
  top: 34%;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--text);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: none;
}

.slider.live .slide-nav { display: block; }
.slide-nav:hover { border-color: var(--accent); }
.slide-nav.prev { left: -8px; }
.slide-nav.next { right: -8px; }

.dots { display: none; gap: 8px; justify-content: center; margin-top: 28px; flex-wrap: wrap; }
.slider.live .dots { display: flex; }

.dots button {
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: none;
  color: var(--dim);
  font-size: 11px;
  cursor: pointer;
}

.dots button[aria-selected="true"] {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 700;
}

/* ---- concepts ----
   Hand-drawn SVG, coloured from the same custom properties as the page, so one diagram is legible in both
   themes. Narrow screens scroll it rather than shrink it: a diagram squeezed to 320px is not a smaller
   diagram, it is an unreadable one. */

.concepts { border-top: 1px solid var(--line); }

.diagram { overflow-x: auto; margin: 28px 0 20px; }
.diagram svg { display: block; width: 100%; min-width: 720px; height: auto; }

.d-box { fill: var(--panel); stroke: var(--line); }
.d-box-accent { fill: var(--panel); stroke: var(--accent); }
.d-row { fill: var(--bg-alt); stroke: var(--line); }
.d-band-label {
  fill: var(--dim); font: 650 10px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.d-title { fill: var(--text); font: 600 13.5px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.d-sub { fill: var(--dim); font: 11.5px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.d-note { fill: var(--dim); font: 11px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.d-edge { stroke: var(--dim); stroke-width: 1.4; fill: none; }
.d-head { fill: var(--dim); }
.d-rule { stroke: var(--line); stroke-width: 1; stroke-dasharray: 5 5; fill: none; }

/* Small line drawings inside the boxes. A label alone says what a stage is called; a picture of a tree, a
   copy, a photograph and a window says what it *is*, which is the thing a reader who has not used the
   library yet is missing. */
.d-glyph { fill: none; stroke: var(--accent); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.d-glyph-solid { fill: var(--panel); stroke: var(--accent); stroke-width: 1.6; }
.d-glyph-ink { fill: var(--accent); }
.d-glyph-dim { fill: none; stroke: var(--dim); stroke-width: 1.4; }

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
  margin-top: 32px;
}

.card {
  display: block;
  padding: 22px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 12px;
  text-decoration: none;
}

.card:hover { border-color: var(--accent); }
.card h3 { margin: 0 0 8px; font-size: 16px; color: var(--text); }
.card p { margin: 0; color: var(--dim); font-size: 15px; }

/* ---- features ---- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
  margin-top: 28px;
}

.grid article {
  padding: 22px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 12px;
}

.grid h3 { margin: 0 0 8px; font-size: 16px; }
.grid p { margin: 0; color: var(--dim); font-size: 15px; }

/* ---- measured ---- */

.scroller { overflow-x: auto; margin: 24px 0 12px; }

table { width: 100%; border-collapse: collapse; min-width: 560px; }

th, td { text-align: left; padding: 11px 16px 11px 0; border-bottom: 1px solid var(--line); }

th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--dim); font-weight: 600; }

td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody td.num { font-weight: 650; }

.footnote { color: var(--dim); font-size: 14px; max-width: 78ch; }

/* ---- quick start ---- */

.two-up {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 32px;
  margin: 24px 0 8px;
}

.two-up h3 { font-size: 15px; margin: 0 0 10px; color: var(--dim); font-weight: 600; }
.two-up h3:not(:first-child) { margin-top: 24px; }
.two-up p { color: var(--dim); font-size: 15px; }

.start > h3 { font-size: 16px; margin: 32px 0 10px; }

/* ---- limits ---- */

.limits ul { margin: 24px 0 0; padding-left: 22px; max-width: 82ch; }
.limits li { margin-bottom: 10px; color: var(--dim); }
.limits strong { color: var(--text); }

/* The way into the book, for a reader who has never done any of this. Same shape as the beta note, because
   both are an aside that should not read as another feature card. */
.book-note { margin-top: 28px; max-width: 82ch; }
.book-note strong { color: var(--text); }

/* The language switcher, built by site.js only when there is a language to switch to. */
.langs { display: flex; gap: 10px; align-items: center; }
.langs a { color: var(--dim); text-decoration: none; font-size: 12.5px; }
.langs a:hover { color: var(--text); }
.langs a.on { color: var(--accent); font-weight: 600; }

/* ---- releases ----
   One card per version, the notes rendered from the same Markdown the GitHub release carries. The prose is
   held to a reading measure while the card is not, because a release-notes table wants the width and a
   paragraph does not. */

.releases { max-width: 1000px; }

.rel {
  margin: 28px 0 0;
  padding: 22px 24px 18px;
  background: var(--bg-alt);
  border: 1px solid var(--line);
  border-radius: 12px;
}

.rel h3 { margin: 0 0 14px; font-size: 20px; }

.rel-date { margin-left: 8px; font-size: 13px; font-weight: 400; color: var(--dim); letter-spacing: 0; }

.rel-pre {
  margin-left: 8px;
  padding: 2px 8px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  color: var(--accent);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  vertical-align: 2px;
}

.rel .install { display: block; margin-bottom: 18px; }

.rel-body { max-width: 82ch; }
.rel-body p { color: var(--dim); }
/* The Markdown renderer shifts every heading down a level to sit under the card's own title, so the notes'
   "## API" arrives as an h3 and its "### Scene" as an h4. The two were styled the wrong way round: the
   section headings came out smaller, greyer and uppercase, while the subsections nested inside them were
   larger and white — which reads as the hierarchy running backwards. API, Improvements and Known limits
   are the three things somebody scans for, so they get the weight; Scene and Renderers are subdivisions of
   one of them and take the quiet label style that used to be on their parent. */
.rel-body h3 {
  margin: 28px 0 10px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-size: 17px;
  font-weight: 650;
  color: var(--text);
}

/* A rule reads as a separator between sections, not as the top of the first one. */
.rel-body h3:first-child { margin-top: 6px; padding-top: 0; border-top: 0; }

.rel-body h4 {
  margin: 18px 0 6px;
  font-size: 12.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim);
}
.rel-body ul { margin: 0 0 14px; padding-left: 22px; color: var(--dim); }
.rel-body .scroller { max-width: none; }
.rel-body table { min-width: 480px; }
.rel-body strong { color: var(--text); }

.rel-links { margin: 18px 0 0; font-size: 14px; color: var(--dim); }

/* ---- footer ---- */

footer {
  border-top: 1px solid var(--line);
  padding: 32px 28px 48px;
  text-align: center;
  color: var(--dim);
  font-size: 14px;
}

footer p { max-width: 74ch; margin: 0 auto 10px; }
footer .thin { font-size: 13px; opacity: 0.8; }

/* ---- narrow ---- */

@media (max-width: 900px) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: 32px; padding-top: 44px; }

  /* cover keeps the middle of the file, and the middle is where the photographs are — so the words end up
     over them. Pull the crop left onto the planet, and darken the whole thing rather than one side of it. */
  .hero-wrap { background-position: 26% center; }
  .hero-wrap::before { background: linear-gradient(180deg, rgb(5 7 12 / 74%), rgb(5 7 12 / 90%)); }
  .slide { grid-template-columns: 1fr; gap: 20px; }
  .slide-nav { top: auto; bottom: -6px; }
  .slide-nav.prev { left: 0; }
  .slide-nav.next { right: 0; }
  .slider.live .dots { margin-top: 20px; }
}
