/* ==========================================================================
   Cambium — landing page shell
   Theme tokens: dark greys + dark green (dark), green + cream (light).
   ========================================================================== */

/* Spectral SC, SIL Open Font License — see assets/fonts/OFL-Spectral.txt.
   The Ridgeway wordmark only, which is set in the 700 cut. The 500 is kept
   declared so a `font-weight` other than 700 degrades to a real face rather
   than to a synthesised one. */
@font-face {
  font-family: "Spectral SC";
  src: url("../assets/fonts/SpectralSC-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Spectral SC";
  src: url("../assets/fonts/SpectralSC-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* Stack Sans Text, SIL Open Font License — see assets/fonts/OFL.txt.
   Only the Medium cut is shipped; it is the one weight the headings use. */
@font-face {
  font-family: "Stack Sans Text";
  src: url("../assets/fonts/StackSansText-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Stack Sans Text", var(--font-sans);
  --font-wordmark: "Spectral SC", ui-serif, Georgia, serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;

  --shell-max: 1440px;
  --shell-pad: clamp(24px, 4.2vw, 64px);
  /* The stacked screenshots stop just short of the content margins. */
  --shots-inset: 6px;
  --topbar-height: 64px;

  --radius-btn: 10px;
  --ease: cubic-bezier(0.32, 0.72, 0.35, 1);
}

/* ---------------------------------------------------------------- themes */

html[data-theme="dark"] {
  --ground: #141414;
  --band: #171B18;
  --band-edge: #232823;
  --surface: #1B201C;
  --line: #272D28;
  --line-soft: #1F251F;

  --text: #E7EBE5;
  --text-dim: #A3AC9E;
  --text-faint: #79826F;

  --accent: #63BC85;
  --accent-hover: #86CE9F;
  --accent-solid: #2E7248;
  --accent-solid-hover: #378554;
  --accent-on-solid: #F1F7F1;
  --accent-line: rgba(99, 188, 133, 0.42);
  --accent-soft: rgba(99, 188, 133, 0.13);
  --stamp: #E4756A;
  --stamp-line: rgba(228, 117, 106, 0.6);
  --stamp-soft: rgba(228, 117, 106, 0.12);
  /* Documentation hints, in the client's own Modified orange
     (--status-orange in app.css) so a hint reads as the app talking. */
  --hint: #E2A54E;
  --hint-line: rgba(226, 165, 78, 0.5);
  --hint-soft: rgba(226, 165, 78, 0.1);

  --ring-ink: rgba(122, 194, 152, 0.2);
  --topbar-bg: rgba(20, 20, 20, 0.84);
  /* An offset-free, wide-blur ambient layer is what wraps the window on all
     four sides; the small offset layers only ground it. Negative spread or a
     blur that is small next to the window reads as a hard rectangular band. */
  /* Outline buttons sit on the ground colour so they read as surfaces rather
     than holes, including where they overlap the graphic band. */
  --btn-ghost-bg: #141414;
  --btn-ghost-bg-hover: #1F2420;
  /* The prose card borrows the client's own panel greys in dark mode: the
     availability week bar's fill and the panel-frame outline. */
  --card-bg: #333333;
  --card-line: #5A5A5A;
  /* Code boxes sit below the page rather than on it: almost black, darker than
     the --ground they are drawn on. Shares the palette's existing near-black. */
  --code-bg: #0E100E;
  --shot-ph-bg: #0E100E;
  --shadow-demo: 0 0 0 1px rgba(255, 255, 255, 0.07), 0 1px 3px rgba(0, 0, 0, 0.6),
    0 12px 32px rgba(0, 0, 0, 0.5), 0 0 72px 10px rgba(0, 0, 0, 0.6);
  --focus: #63BC85;
}

html[data-theme="light"] {
  /* Linen: a warm, slightly darker cream for the page, with the graphic band
     dropped further and pulled toward green so the ring lines read. */
  --ground: #EFEADA;
  --band: #E2E1CD;
  --band-edge: #D2D2B9;
  --surface: #F5F1E2;
  --line: #D6D3BE;
  --line-soft: #E2DECB;

  --text: #1A1F1A;
  --text-dim: #515A4C;
  --text-faint: #77806F;

  --accent: #2C6B45;
  --accent-hover: #23583A;
  --accent-solid: #2C6B45;
  --accent-solid-hover: #23583A;
  --accent-on-solid: #F3EFE1;
  --accent-line: rgba(44, 107, 69, 0.34);
  --accent-soft: rgba(44, 107, 69, 0.09);
  --stamp: #A8443B;
  --stamp-line: rgba(168, 68, 59, 0.55);
  --stamp-soft: rgba(168, 68, 59, 0.08);
  /* A darker cut of the same orange: #E2A54E as text on cream is about 2:1,
     which no amount of good intentions makes readable. */
  --hint: #8A5A12;
  --hint-line: rgba(138, 90, 18, 0.42);
  --hint-soft: rgba(138, 90, 18, 0.09);

  --btn-ghost-bg: #EFEADA;
  --btn-ghost-bg-hover: #F7F3E6;
  --card-bg: #F5F1E2;
  --card-line: #E2DECB;
  /* Unchanged from --card-bg / --surface: the light theme's boxes were right. */
  --code-bg: #F5F1E2;
  --shot-ph-bg: #E3DECC;
  --ring-ink: rgba(38, 88, 54, 0.26);
  --topbar-bg: rgba(239, 234, 218, 0.86);
  --shadow-demo: 0 0 0 1px rgba(30, 45, 34, 0.12), 0 1px 3px rgba(30, 45, 34, 0.12),
    0 12px 30px rgba(30, 45, 34, 0.16), 0 0 72px 10px rgba(30, 45, 34, 0.15);
  --focus: #2C6B45;
}

/* ------------------------------------------------------------- baseline */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* In-page jumps must clear the sticky bar. */
[id] { scroll-margin-top: calc(var(--topbar-height) + 20px); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, p { margin: 0; }

a { color: inherit; text-decoration: none; }

/* Beats component rules that set their own display. */
[hidden] { display: none !important; }

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

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

.shell {
  width: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
  padding-left: var(--shell-pad);
  padding-right: var(--shell-pad);
}

/* ================================================================ top bar
   Its own bar above the graphic field, and it stays put on scroll.       */

.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--topbar-bg);
  border-bottom: 1px solid var(--line-soft);
  backdrop-filter: saturate(160%) blur(12px);
  -webkit-backdrop-filter: saturate(160%) blur(12px);
}

/* Cambium at one end, nav at the other. The bar used to run three tracks with
   the Ridgeway mountain leading and the wordmark centred on the page; the
   mountain is gone from the bar now — the footer lockup is the way back to the
   company home — and Cambium has taken its place at the head of the row. */
.topbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: var(--topbar-height);
}

/* The mountain is black artwork on transparency, shown at two sizes. An
   <img> inverted per theme rather than a mask: a mask can only take an inlined
   data URI (a file URL is blocked over file://), and the home page's hero
   version is far too large to inline. */
.ridgeway-mark {
  display: block;
  width: auto;
}

html[data-theme="dark"] .ridgeway-mark { filter: invert(1); }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.brand-icon {
  display: block;
  width: 28px;
  height: 28px;
  flex: none;
}

.brand-name {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1;
}

.topnav {
  display: flex;
  align-items: center;
  gap: 30px;
  font-size: 14.5px;
  color: var(--text-dim);
}

.topnav a { transition: color 0.15s var(--ease); }
.topnav a:hover { color: var(--text); }

/* ---- the narrow-screen menu ----
   Two rules that fold into an X, and the same `.topnav` re-laid-out as a second
   bar under the header rather than a duplicate list. One set of links means the
   two can never drift apart. */

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  width: 40px;
  height: 40px;
  margin-right: -8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
}

.nav-toggle:hover { color: var(--text); }

.nav-toggle-rule {
  display: block;
  width: 22px;
  height: 2px;
  margin: 0 auto;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.2s var(--ease);
}

/* The rules sit 8px apart centre to centre, so each travels 4px to meet in the
   middle before rotating. */
.topbar[data-nav="open"] .nav-toggle-rule:first-child {
  transform: translateY(4px) rotate(45deg);
}

.topbar[data-nav="open"] .nav-toggle-rule:last-child {
  transform: translateY(-4px) rotate(-45deg);
}

/* =================================================================== band */

.band {
  position: relative;
  isolation: isolate;
  background: var(--band);
  border-bottom: 1px solid var(--band-edge);
  overflow: hidden;
}

/* The ring field covers the whole band; its centre sits left of centre and the
   outermost ring is repeated outward so the pattern carries across the width.
   The SVG is inline in the page and tinted through currentColor, and its own
   fade is an SVG <mask> — nothing here depends on CSS masking or on one local
   file being allowed to reference another. */
.band-rings {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  color: var(--ring-ink);
}

.band-rings svg {
  position: absolute;
  left: 24%;
  top: 46%;
  /* --rings-x / --rings-y let a shorter band move the centre off-screen. */
  left: var(--rings-x, 24%);
  top: var(--rings-y, 46%);
  width: clamp(3400px, 250vw, 6000px);
  aspect-ratio: 1 / 1;
  height: auto;
  transform: translate(-50%, -50%);
  display: block;
}

/* ---------------------------------------------------------------- hero */

.hero {
  position: relative;
  z-index: 1;
  /* The preview reserves its own 80px of shadow room below, so the band's
     own bottom padding is trimmed to keep the spacing even. */
  padding: clamp(44px, 5.5vw, 78px) 0 clamp(8px, 1.5vw, 24px);
}

.hero-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(28px, 4vw, 64px);
  flex-wrap: wrap;
}

.hero-title {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 2.9vw, 2.55rem);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.16;
  text-wrap: balance;
}

.hero-word {
  display: flex;
  align-items: center;
  gap: 0.24em;
  font-size: 1.5em;
  line-height: 1.06;
  letter-spacing: -0.028em;
  margin-bottom: 0.06em;
}

.beta-tag {
  font-family: var(--font-mono);
  font-size: 0.44em;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--stamp);
  background: var(--stamp-soft);
  border: 2px solid var(--stamp-line);
  border-radius: 0.3em;
  padding: 0.26em 0.42em 0.28em;
  /* Optically centres the tag against the wordmark's cap height rather than
     its line box, which sits low because of the descender space. */
  transform: translateY(-0.1em);
}

/* Three buttons on a two-column grid: the download spans the full width of
   the pair beneath it, and the pair share that width evenly. */
.hero-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  padding-bottom: 6px;
}

.hero-actions .btn--primary { grid-column: 1 / -1; }

/* The two ghost buttons stay on one row at every width, together spanning the
   primary button above them — so the block always reads as one shape. They are
   never allowed to stack, which is why the labels have a short form rather than
   the grid a single-column fallback. */
.btn-label-short { display: none; }

@media (max-width: 560px) {
  .btn-label-long { display: none; }
  .btn-label-short { display: inline; }
}

/* ------------------------------------------------- interactive preview */

/* Full-bleed so the window's shadow has room on both sides; js/demo.js sizes
   the window itself to the hero text's own width and centres it there. */
.hero-demo {
  width: 100%;
  margin-top: clamp(4px, 1vw, 16px);
}

/* A scroll container clips at its box, so the window's shadow needs room
   inside it — js/demo.js reserves SHADOW_ROOM above, below and to the sides. */
.demo-viewport {
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.demo-viewport::-webkit-scrollbar { display: none; }

.demo-scaler {
  transform-origin: top left;
  will-change: transform;
}

/* ------------------------------------------- the same windows, as images */

/* Rendered by tools/generate-mock-shots.py and shown instead of the live
   windows under 720px, where none of what they cost can be used: no pointer
   to select a file with, no room to drag the graph, and three scaled stacking
   contexts of ~1,400 nodes each to lay out and paint. js/site.js goes further
   and removes the live markup, so on a phone this is all that is left of it.

   Hidden until site.js says otherwise, which does two jobs. An image with no
   box is an image whose lazy load never fires, so a wide screen never pays for
   these at all; and nothing is laid out before site.js has picked the
   platform, so nobody fetches the wrong one first. Both are why they carry
   loading="lazy" — see the markup. */
.app-shot {
  display: none;
  width: 100%;
  height: auto;
  /* The window's own radius. The images were captured with their shadow
     suppressed because --shadow-demo differs between the two site themes; it
     goes back on here, live, and follows this radius. */
  border-radius: 10px;
  box-shadow: var(--shadow-demo);
}

/* .hero-demo is full-bleed so the shadow has room; the window inside it is as
   wide as the hero copy, which demo.js measures off #hero-head. The image has
   no such measurement, so it takes the same width the same way .shell does. */
.app-shot--hero {
  max-width: var(--shell-max);
  margin: 0 auto;
  /* Sides: the same width .shell gives the hero copy. Top and bottom: what
     demo.js calls SHADOW_ROOM and reserves around the live window,
     min(clientWidth * 0.07, 80px) — matched here so the vertical rhythm does
     not shift when one is swapped for the other. */
  padding: min(7vw, 80px) var(--shell-pad);
}

/* The two offsets from .shot--back / .shot--front, which are compounded with
   .app-mock and so cannot be shared. Same box, same place in the same scaled
   composition — site.js sizes that composition either way. The explicit sizes
   come after the base rule's width:100%/height:auto deliberately: these two are
   placed in a fixed 1704x870 box, not fitted to a container. */
.app-shot--back,
.app-shot--front {
  position: absolute;
  width: 1160px;
  height: 720px;
}

.app-shot--back { right: 0; bottom: 0; z-index: 1; }
.app-shot--front { left: 0; top: 0; z-index: 2; }

/* Set by js/site.js once it has removed the live windows. It has to hold at
   every width, not just the narrow one: the markup demo.js drove is gone, so a
   phone turned to landscape has nothing to go back to. The images are renders
   of exactly what was there, so there is nothing to go back *for*.

   This attribute is deliberately the *only* thing that reveals them, rather
   than a @media (max-width: 720px) rule doing it in parallel. A media query
   applies while the page is still parsing, which is before site.js can point
   the images at the right platform — so a narrow Windows visitor would lay
   out the -mac file, fetch it, and then fetch -win over the top of it. Waiting
   for the attribute costs nothing and fetches one file instead of two.

   The visitor with JS off therefore keeps the live markup at every width, which
   is exactly what they had before these images existed. */
[data-shots="static"] .demo-viewport,
[data-shots="static"] .shot { display: none; }

[data-shots="static"] .app-shot { display: block; }

/* ================================================================== body */

.intro {
  padding: clamp(64px, 7.5vw, 104px) 0 clamp(72px, 8vw, 120px);
}

.section-title {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 2.4vw, 2.1rem);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.15;
  margin-bottom: 22px;
}

.intro-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(36px, 4.5vw, 80px);
  align-items: start;
}

.intro-action { margin-top: 20px; }

.text-link {
  color: var(--accent);
  font-weight: 550;
  transition: color 0.15s var(--ease);
}

.text-link:hover {
  color: var(--accent-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.text-link-arrow {
  display: inline-block;
  margin-left: 0.45em;
  transition: transform 0.15s var(--ease);
}

.text-link:hover .text-link-arrow { transform: translateX(2px); }

.intro-lede {
  max-width: 54ch;
  font-size: clamp(1.02rem, 1.15vw, 1.16rem);
  line-height: 1.72;
  color: var(--text-dim);
  text-wrap: pretty;
}

/* ------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: 0 24px;
  border-radius: var(--radius-btn);
  border: 1px solid transparent;
  font-size: 14.5px;
  font-weight: 550;
  letter-spacing: -0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
    color 0.15s var(--ease), transform 0.15s var(--ease);
}

.btn:active { transform: translateY(1px); }

.btn--lg {
  min-height: 60px;
  padding: 0 34px;
  font-size: 1rem;
  border-radius: 12px;
}

.btn--primary {
  background: var(--accent-solid);
  color: var(--accent-on-solid);
}

.btn--primary:hover { background: var(--accent-solid-hover); }

.btn--ghost {
  border-color: var(--line);
  color: var(--text);
  background: var(--btn-ghost-bg);
}

.btn--ghost:hover {
  border-color: var(--text-faint);
  background: var(--btn-ghost-bg-hover);
}

/* ================================================== designed for artists */

.artists {
  border-top: 1px solid var(--line);
  padding: clamp(56px, 6.5vw, 92px) 0 clamp(72px, 8vw, 118px);
}

.artists-copy {
  margin-top: clamp(36px, 4vw, 56px);
  background: var(--card-bg);
  border: 1px solid var(--card-line);
  border-radius: 16px;
  padding: clamp(26px, 3vw, 44px);
}

.artists-copy .section-title { margin-bottom: 20px; }

/* Full bleed to the content margins — the long measure is the point, setting
   this block apart from the two-column one above. */
.artists-paras {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.artists-paras .intro-lede { max-width: none; }

/* ---- the stacked screenshots ---- */

.shot-stack {
  position: relative;
  width: calc(100% - var(--shots-inset) * 2);
  margin: 0 auto;
}

/* Both windows are the same size. The front is offset up and to the left so it
   covers all of the back's repository panel bar a sliver of the file tree's
   right-hand columns, and leaves the graph panel whole. */
.shot-stack-inner {
  position: relative;
  width: 1704px;
  height: 870px;
  transform-origin: top left;
}

/* Compounded with .app-mock so these beat its own 1440×900 design size —
   app.css loads after this file. */
.shot {
  position: absolute;
  border-radius: 10px;
  /* Each window must be its own stacking context. Without one, the panel
     border (z-index 55) and the workspace tab strip (z-index 10) inside the
     BACK window escape into the shared context and paint over the front one. */
  isolation: isolate;
}

/* Back: the revision page with the graph open. The graph panel is never
   covered, and a band of the file tree stays visible below the front window. */
.app-mock.shot--back {
  right: 0;
  bottom: 0;
  width: 1160px;
  height: 720px;
  z-index: 1;
}

/* Front: the same project's organization page, overlapping the back window's
   top-left corner. */
.app-mock.shot--front {
  left: 0;
  top: 0;
  width: 1160px;
  height: 720px;
  z-index: 2;
}

/* ============================================================= downloads */

/* Sprite of platform glyphs, inlined at the top of the page. */
.icon-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.band--slim {
  --rings-x: 50%;
  --rings-y: -30%;
}

/* Mid-page band, about half the header's height. */
.band--strip {
  --rings-x: 50%;
  --rings-y: -70%;
  border-top: 1px solid var(--band-edge);
}

/* The field is declared once in a hidden sprite and <use>d by each band. */
.ring-sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Heading and sentence sit side by side, which is what keeps this band to
   half the page header's height. */
.page-strip {
  position: relative;
  z-index: 1;
  padding: clamp(24px, 2.4vw, 34px) 0;
}

.page-strip .shell {
  display: flex;
  align-items: baseline;
  gap: clamp(20px, 2.6vw, 48px);
  flex-wrap: wrap;
}

.strip-title {
  flex: none;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.8vw, 1.6rem);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.2;
}

.strip-text {
  flex: 1 1 420px;
  max-width: 90ch;
  color: var(--text-dim);
  line-height: 1.68;
  text-wrap: pretty;
}

/* The band already draws its own edge; a second one under it doubles up. */
.band + .shell > .guide:first-child { border-top: 0; }

.page-hero {
  position: relative;
  z-index: 1;
  padding: clamp(44px, 5.5vw, 78px) 0 clamp(44px, 5vw, 72px);
}

/* The button shares the title's row so it sits level with the word, with the
   version line dropping beneath both. */
.page-hero-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(24px, 4vw, 56px);
  flex-wrap: wrap;
}

.page-version {
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  color: var(--text-faint);
}

.page-title {
  font-family: var(--font-display);
  font-size: clamp(2.1rem, 3.4vw, 3rem);
  font-weight: 500;
  letter-spacing: -0.028em;
  line-height: 1.1;
}

.page-lede {
  margin-top: 16px;
  max-width: 58ch;
  font-size: clamp(1rem, 1.1vw, 1.1rem);
  line-height: 1.68;
  color: var(--text-dim);
  text-wrap: pretty;
}

.release {
  padding: clamp(36px, 4vw, 56px) 0;
  border-top: 1px solid var(--line);
}

/* Same three columns as the option grid below, so the licensing note starts
   on the "Compile From Source" column and tops out level with the blurb. */
.release-head {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: clamp(24px, 3vw, 44px);
  /* No row gap: the blurb and the note both carry their own top margin, which
     is what keeps their first lines level. */
  row-gap: 0;
  align-items: start;
}

.release-head > .release-title { grid-column: 1 / -1; }
.release-head > .release-blurb { grid-column: 1 / 3; }
.release-head > .release-note { grid-column: 3; }

/* A documentation-style hint inside an option grid, spanning the last two
   columns and sitting in the space their one-row groups leave. .doc-note does
   the dressing; this only places it.

   The first group takes both rows so nothing overlaps — without that the hint's
   row would push the tall Installers list around. `align-self: end` closes the
   box up against the bottom of that list rather than leaving it adrift.

   Scoped through the grid rather than left bare because `.doc-note` is declared
   later in this file and would win the margin-top on equal specificity. */
.option-grid:has(> .option-hint) > .option-group:nth-of-type(1) {
  grid-column: 1;
  grid-row: 1 / span 2;
}

.option-grid:has(> .option-hint) > .option-group:nth-of-type(2) {
  grid-column: 2;
  grid-row: 1;
}

.option-grid:has(> .option-hint) > .option-group:nth-of-type(3) {
  grid-column: 3;
  grid-row: 1;
}

.option-grid > .option-hint {
  grid-column: 2 / -1;
  grid-row: 2;
  align-self: end;
  margin-top: 0;
  font-size: 0.92rem;
}

/* Links inside a hint take the hint's own colour. No underline at rest — the
   box is already the loudest thing in its column, and underlining inside it
   only adds noise. The hover rule is there for whoever goes looking. */
.doc-note a { color: var(--hint); }

/* The one link in a hint that is the recommended way out. Underlined where its
   neighbours are not, so the eye lands on it first. */
.doc-note a.is-cue {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.doc-note a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}


.release-title {
  font-family: var(--font-display);
  font-size: clamp(1.45rem, 2.1vw, 1.9rem);
  font-weight: 500;
  letter-spacing: -0.024em;
  line-height: 1.2;
}

.release-version {
  font-family: var(--font-mono);
  font-size: 0.6em;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-faint);
  margin-left: 0.45em;
  vertical-align: 0.12em;
}

.release-note {
  margin-top: 12px;
  font-style: italic;
  font-size: 0.92rem;
  line-height: 1.62;
  color: var(--text-faint);
  text-wrap: pretty;
}

.release-blurb {
  margin-top: 12px;
  max-width: 62ch;
  color: var(--text-dim);
  line-height: 1.68;
  text-wrap: pretty;
}

.release code,
.guide code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--accent-soft);
  border-radius: 4px;
  padding: 0.1em 0.34em;
}

.option-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 44px);
  margin-top: clamp(26px, 3vw, 38px);
}

.option-heading {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.option-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 11px 0;
  border-bottom: 1px solid var(--line-soft);
}

/* Every value in a list starts on the same line. Each row is its own box, so
   with plain flex each one measures its own label and the value column comes
   out ragged — "Download" landing in a different place on every line. Subgrid
   hands the rows the list's column widths instead, sized to the longest label
   in that list and no wider, which a fixed width could not do without either
   clipping a long label or wasting space in front of a short one. Without
   subgrid support the flex rules above still apply, ragged but intact. */
@supports (grid-template-columns: subgrid) {
  .option-list {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
  }

  .option {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    column-gap: 14px;
  }
}

.option-label {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
  min-width: 7rem;
  font-size: 0.82rem;
  color: var(--text-faint);
}

/* Rows without a glyph (Homebrew, "All platforms") keep the empty slot so
   every column's labels start on the same line. */
.option-icon {
  width: 15px;
  height: 15px;
  flex: none;
  display: block;
  color: var(--text-faint);
}

.option-link {
  color: var(--accent);
  font-weight: 550;
  font-size: 0.95rem;
  transition: color 0.15s var(--ease);
}

.option-link:hover {
  color: var(--accent-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Keeps the pip on the same line as the link it qualifies, so the warning is
   plainly about that build and not the row below it. */
.option-value {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

/* The client's own Modified glyph, in the hint orange, as a link and nothing
   else. Never underlined: it is an icon, and an underline under a 15px circle
   reads as a rendering fault. */
.warn-pip {
  display: inline-flex;
  align-items: center;
  flex: none;
  color: var(--hint);
  text-decoration: none;
  transition: opacity 0.15s var(--ease);
}

.warn-pip svg {
  display: block;
  width: 15px;
  height: 15px;
  fill: currentColor;
}

.warn-pip:hover { opacity: 0.72; }

/* In running prose it sits on the text baseline rather than the line box. */
.doc-table .warn-pip,
.docs-article .warn-pip {
  vertical-align: -2px;
  margin-left: 2px;
}

.option-soon {
  font-size: 0.95rem;
  color: var(--text-faint);
}

/* The Intel builds: still shipped, no longer the ones to reach for. Dimmed as a
   whole — label, glyph and the Download link with them — and back to full
   strength on hover or keyboard focus, so the row is never dim while it is the
   thing being used. */
.option--legacy {
  opacity: 0.6;
  transition: opacity 0.15s var(--ease);
}

.option--legacy:hover,
.option--legacy:focus-within {
  opacity: 1;
}

/* The label carries no action, so it can take a little more than the link. */
.option--legacy .option-label { opacity: 0.9; }

/* ---- install commands ----
   The CLI's one-liners. Each sits in the value column of an ordinary option row,
   standing where the Download link stands in the other sections — so the rows,
   the hairlines and the label column are the list's, not this control's, and the
   field is only ever as wide as that column. Dressed like a documentation code
   block, because it is the same kind of object: something you read and then run.
   Compact enough to read as a control rather than a code listing. */

.install-cmd {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 5px 5px 5px 11px;
  /* One hairline all the way round. Every code box used to carry a 2px accent
     edge on the left, which made them all asymmetric for no reason. */
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--code-bg);
}

/* Scoped one level deeper than `.release code`, which would otherwise win on
   specificity and paint its green inline-code pill inside the card. */
.install-cmd .install-cmd-line {
  flex: 1;
  min-width: 0;
  background: none;
  border-radius: 0;
  padding: 0 0 2px;
  overflow-x: auto;
  white-space: pre;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--text);
  /* Left at its default the thumb is a bright bar directly under the command
     and reads as an underline. Toned to the hairline colour it reads as what it
     is: a hint that the line continues. */
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

.install-cmd .install-cmd-line::-webkit-scrollbar { height: 5px; }
.install-cmd .install-cmd-line::-webkit-scrollbar-track { background: transparent; }

.install-cmd .install-cmd-line::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 3px;
}

.install-cmd-copy {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}

.install-cmd-copy:hover {
  color: var(--text);
  border-color: var(--accent-line);
}

.install-cmd-copy-icon {
  display: block;
  width: 13px;
  height: 13px;
  fill: currentColor;
}

/* js/site.js sets data-done for a moment after a copy; the tick replaces the
   glyph rather than the label, since the button has no text to swap. */
.install-cmd-copy-icon--done { display: none; }
.install-cmd-copy[data-done] { color: var(--accent); border-color: var(--accent-line); }
.install-cmd-copy[data-done] .install-cmd-copy-icon { display: none; }
.install-cmd-copy[data-done] .install-cmd-copy-icon--done { display: block; }

.guide {
  padding: clamp(32px, 3.6vw, 52px) 0;
  border-top: 1px solid var(--line);
}

.guide:last-child { padding-bottom: clamp(64px, 8vw, 110px); }

.guide-title {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 1.7vw, 1.5rem);
  font-weight: 500;
  letter-spacing: -0.02em;
}

.guide-note {
  margin-top: 12px;
  max-width: 62ch;
  color: var(--text-faint);
  font-style: italic;
  line-height: 1.68;
}

.steps {
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 74ch;
}

.step {
  counter-increment: step;
  position: relative;
  padding-left: 40px;
}

.step::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-faint);
}

.step-text {
  color: var(--text-dim);
  line-height: 1.62;
  text-wrap: pretty;
}

.step-code {
  margin: 10px 0 0;
  padding: 12px 14px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--code-bg);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--text);
  white-space: pre;
}

/* The block already carries the code background; the inline-code chip rule
   must not paint a second one inside it. */
.step-code code {
  font: inherit;
  background: none;
  padding: 0;
  border-radius: 0;
}

/* ========================================================= documentation */

.docs-grid {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: clamp(32px, 4vw, 72px);
  align-items: start;
  padding-top: clamp(40px, 4.5vw, 64px);
  padding-bottom: clamp(72px, 8vw, 118px);
}

/* ---- sidebar ---- */

/* overflow-y on one axis makes the other axis clip too, which was shearing the
   left corners off the link highlight (it bleeds 10px past the text). The box
   is widened leftwards by exactly that bleed and the padding puts the content
   back where it was, so nothing moves and nothing is cut. */
.docs-nav {
  position: sticky;
  top: calc(var(--topbar-height) + 28px);
  align-self: start;
  max-height: calc(100vh - var(--topbar-height) - 56px);
  overflow-y: auto;
  margin-left: -10px;
  padding-left: 10px;
}

.docs-nav-heading {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding-bottom: 9px;
  border-bottom: 1px solid var(--line);
}

.docs-nav-heading + .docs-nav-list { margin-bottom: 26px; }

.docs-nav-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
}

.docs-nav-link {
  display: block;
  padding: 6px 10px;
  margin-left: -10px;
  border-radius: 7px;
  font-size: 0.9rem;
  line-height: 1.4;
  color: var(--text-dim);
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}

.docs-nav-link:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 6%, transparent);
}

.docs-nav-link--current {
  color: var(--accent);
  font-weight: 550;
  background: var(--accent-soft);
}

/* ---- article ---- */

.docs-article {
  min-width: 0;
  max-width: 78ch;
  padding-left: 0.4em;
}

.doc-title {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 2.4vw, 2.15rem);
  font-weight: 500;
  letter-spacing: -0.024em;
  line-height: 1.15;
}

.doc-lead {
  margin-top: 12px;
  font-size: clamp(1.02rem, 1.15vw, 1.14rem);
  line-height: 1.66;
  color: var(--text-dim);
  text-wrap: pretty;
}

.doc-heading {
  font-family: var(--font-display);
  font-size: clamp(1.12rem, 1.5vw, 1.32rem);
  font-weight: 500;
  letter-spacing: -0.018em;
  margin-top: 40px;
}

.docs-article p {
  margin-top: 16px;
  line-height: 1.72;
  color: var(--text-dim);
  text-wrap: pretty;
}

.docs-article strong { color: var(--text); font-weight: 600; }

.doc-list,
.doc-steps {
  margin: 16px 0 0;
  padding-left: 1.35rem;
  display: flex;
  flex-direction: column;
  gap: 10px;
  line-height: 1.7;
  color: var(--text-dim);
}

.doc-list { list-style: none; padding-left: 0; }

.doc-list > li {
  position: relative;
  padding-left: 1.35rem;
}

.doc-list > li::before {
  content: "";
  position: absolute;
  left: 0.1rem;
  top: 0.85em;
  width: 0.5rem;
  height: 1px;
  background: var(--accent);
  opacity: 0.75;
}

.doc-steps > li::marker {
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 0.85em;
}

/* The chip's padding sits outside the text origin, so a code span at the start
   of a line had its left corner shaved off. The article is nudged in by that
   much, and `clone` gives both halves of a wrapped span their own corners. */
.docs-article code {
  font-family: var(--font-mono);
  font-size: 0.86em;
  background: var(--accent-soft);
  border-radius: 4px;
  padding: 0.1em 0.34em;
  word-break: break-word;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.doc-code {
  margin: 14px 0 0;
  padding: 13px 15px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--code-bg);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.65;
  color: var(--text);
  white-space: pre;
}

.doc-code code {
  font: inherit;
  background: none;
  padding: 0;
  border-radius: 0;
}

/* ---- callout ----
   Orange throughout, so a hint is never mistaken for a code block: those keep
   the grey card background and the green edge, and these carry neither. */

.doc-note {
  display: block;
  margin-top: 22px;
  padding: 16px 18px;
  border: 1px solid var(--hint-line);
  border-radius: 12px;
  background: var(--hint-soft);
  font-size: 0.95rem;
  line-height: 1.66;
  color: var(--hint);
}

/* The bolded lead-in would otherwise revert to --text and break the wash. */
.doc-note strong { color: var(--hint); }

/* Inline code inside a hint: the green tint fights the orange. */
.doc-note code {
  background: var(--hint-soft);
  color: var(--hint);
}

/* ---- tables ---- */

.doc-table-wrap {
  margin-top: 20px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
}

.doc-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.doc-table th,
.doc-table td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
  line-height: 1.6;
}

.doc-table thead th {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  background: var(--card-bg);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

.doc-table td { color: var(--text-dim); }
.doc-table tbody tr:last-child td { border-bottom: none; }
.doc-table td:first-child { color: var(--text); }

/* ---- status chips ---- */

.doc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-weight: 500;
}

.doc-chip svg {
  width: 13px;
  height: 13px;
  flex: none;
}

.doc-table td:first-child:has(.doc-chip) { white-space: nowrap; }

/* ---- screenshot placeholders ---- */

.doc-shot {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 22px 0 0;
  padding: clamp(48px, 7vw, 84px) 20px;
  border: 1px solid var(--card-line);
  border-radius: 16px;
  background: var(--shot-ph-bg);
  text-align: center;
}

.doc-shot-label {
  font-size: 0.88rem;
  font-style: italic;
  color: var(--text-faint);
}

/* A real screenshot in the same frame the placeholder occupied: the same
   border and radius, none of the padding it needed to have a height of its
   own. No shadow — these sit inside a column of prose, not on a hero band, and
   the border is what separates the app's dark chrome from the page.

   overflow: hidden is what makes the radius apply to the image; the img alone
   would square off the corners inside it. */
.doc-shot--image {
  display: block;
  padding: 0;
  overflow: hidden;
  background: none;
  /* Not the placeholder's 16px. This frame is around a picture of a desktop
     window, and the window inside it has a 10px radius of its own (.app-mock
     in app.css, matching the real client) — anything rounder stops reading as
     a window and starts reading as a card with a window printed on it. */
  border-radius: 10px;
}

.doc-shot-img {
  display: block;
  width: 100%;
  height: auto;
}

/* =================================================================== footer */

/* Monospace throughout — the wordmark is artwork and keeps its own face. */
.site-footer {
  border-top: 1px solid var(--line);
  padding: 22px 0;
  font-family: var(--font-mono);
}

/* Brand, links and copyright share one line, so the footer is only as tall as
   the brand lockup. The links used to be two headed columns stacked under
   nothing, which made the footer three times the height it needed and left the
   middle of the row empty. */
.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(20px, 3vw, 48px);
  flex-wrap: wrap;
}

/* ---- the lockup (footer and Ridgeway home both) ----
   Range and wordmark side by side, reading as one object because they start and
   stop on exactly the same two lines: the mark is set to the wordmark's cap
   height, and baseline alignment drops its base onto the baseline. Nothing has
   to be fudged at either end — the artwork's ink touches all four edges of its
   frame, so the image's box and the mountain's silhouette are the same thing,
   and RIDGEWAY in small caps has no descender, so its ink bottom is the
   baseline.

   The size lives on the container rather than the wordmark, so the mark's `em`
   height resolves against the same font-size the text is set in. */

.footer-brand,
.home-brand {
  display: flex;
  align-items: baseline;
  gap: 0.3em;
  width: max-content;
  max-width: 100%;
}

/* 0.676em is Spectral SC's cap height, measured off a render rather than taken
   from the font's metrics. A replaced element's baseline is its bottom margin
   edge, which is what lands the range's base on the wordmark's baseline. */
.footer-brand .ridgeway-mark,
.home-brand .ridgeway-mark {
  height: 0.676em;
  width: auto;
}

.footer-wordmark,
.home-wordmark {
  font-size: 1em;
  font-family: var(--font-wordmark);
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.08em;
  /* Letter-spacing lands after the last glyph too. Taking it back off the right
     keeps the lockup's box tight to the Y instead of trailing a space. */
  margin-right: -0.08em;
  white-space: nowrap;
  color: var(--text);
}

.footer-brand { font-size: 22px; }

/* ---- links ----
   One row, two groups. The hairline does the job the column headings used to:
   it keeps Projects and Resources reading as two sets. */

.footer-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}

.footer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: clamp(16px, 1.8vw, 26px);
}

.footer-list + .footer-list {
  margin-left: clamp(16px, 1.8vw, 26px);
  padding-left: clamp(16px, 1.8vw, 26px);
  border-left: 1px solid var(--line);
}

/* Three element types, one look. The spans are the entries that name a
   destination which does not exist yet — see the note on the footer markup —
   and they have to sit in the row as though they belonged to it, because they
   do. What they must not do is claim to be clickable: the pointer cursor and
   the hover colour are the two things that make that claim, so both are
   scoped to the elements that actually do something. */
.footer-list a,
.footer-list button,
.footer-list span {
  font-family: inherit;
  font-size: 0.8rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  transition: color 0.15s var(--ease);
}

.footer-list a,
.footer-list button { cursor: pointer; }

.footer-list a:hover,
.footer-list button:hover { color: var(--text); }

.footer-legal {
  margin: 0;
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--text-faint);
  text-align: right;
}

/* ============================================================= ridgeway home */

/* Brand on the top quarter line, quote on the bottom third, nothing between. */
.home {
  min-height: 100vh;
  display: grid;
  grid-template-rows: 25vh auto 1fr auto 25vh;
  justify-items: center;
  padding: 0 var(--shell-pad);
  text-align: center;
}

/* Shape and lockup tuning are shared with the footer brand, up in the footer
   block; only the grid placement and the size are the home page's own. The
   lockup is a single nowrap row now, so it is the width the page has to hold —
   hence a clamp rather than a fixed size. */
.home-brand {
  grid-row: 2;
  font-size: clamp(28px, 5.4vw, 46px);
}

.home-tagline {
  grid-row: 4;
  align-self: end;
  margin: 0;
  font-family: var(--font-mono);
  font-size: clamp(0.86rem, 1.1vw, 1rem);
  line-height: 1.6;
  letter-spacing: 0.01em;
  color: var(--text-dim);
}

/* A page that has nothing on it yet: one centred line under the band, with
   enough room above and below that the footer does not ride up to meet it. */
.page-message {
  padding: clamp(72px, 11vw, 150px) 0;
  text-align: center;
  color: var(--text-dim);
  font-size: clamp(1rem, 1.2vw, 1.15rem);
  line-height: 1.68;
  text-wrap: pretty;
}

/* ================================================================ source
   The two license summaries and the full text under them. Everything above
   them on that page is the downloads page's own release/option grid.      */

/* Two cards, one per license, on the same margins as the option grid. */
.license-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 44px);
  margin-top: clamp(26px, 3vw, 38px);
}

.license-card {
  background: var(--card-bg);
  border: 1px solid var(--card-line);
  border-radius: 16px;
  padding: clamp(22px, 2.4vw, 32px);
}

.license-kicker {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.license-name {
  margin-top: 8px;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 1.5vw, 1.3rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.25;
}

.license-basis {
  margin-top: 8px;
  font-size: 0.9rem;
  font-style: italic;
  color: var(--text-faint);
}

/* "You may" / "You may not" — these carry the meaning of the ✓ and ✗ marks
   below them, which are decoration and are not read out. */
.license-group {
  margin-top: 22px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.license-points {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
}

.license-point {
  display: flex;
  gap: 10px;
  padding: 7px 0;
  font-size: 0.93rem;
  line-height: 1.55;
  color: var(--text-dim);
  text-wrap: pretty;
}

.license-point::before {
  flex: none;
  width: 1em;
  font-weight: 700;
}

.license-point--yes::before { content: "✓"; color: var(--accent); }
.license-point--no::before { content: "✗"; color: var(--stamp); }

/* ---- the full license text ---- */

.license-doc {
  margin-top: clamp(20px, 2.4vw, 30px);
  max-width: 78ch;
}

.license-doc p {
  margin-top: 14px;
  font-size: 0.94rem;
  line-height: 1.72;
  color: var(--text-dim);
  text-wrap: pretty;
}

.license-doc strong { color: var(--text); font-weight: 600; }

/* The one heading inside the license text — Permitted use. The terms above it
   run as plain paragraphs, the way MIT does. */
.license-clause {
  margin-top: 28px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.license-list {
  margin: 14px 0 0;
  padding-left: 0;
  list-style: none;
}

.license-list > li {
  position: relative;
  padding-left: 20px;
  margin-top: 10px;
  font-size: 0.94rem;
  line-height: 1.72;
  color: var(--text-dim);
  text-wrap: pretty;
}

.license-list > li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: var(--text-faint);
}

/* The warranty and liability disclaimers, which are conventionally set apart
   from the rest of the terms. */
.license-disclaimer {
  font-style: italic;
  color: var(--text-faint);
}

/* ============================================================ responsive */

@media (max-width: 900px) {
  .intro-grid { grid-template-columns: minmax(0, 1fr); }

  .docs-grid { grid-template-columns: minmax(0, 1fr); }

  .docs-nav {
    position: static;
    max-height: none;
  }

  .release-head { grid-template-columns: minmax(0, 1fr); }
  .release-head > .release-blurb,
  .release-head > .release-note { grid-column: 1; }

  /* Two columns here, one under 560px, so the explicit placement has to come
     off: a group pinned to column 3 would conjure a third column, and the hint
     would keep a row it no longer shares. It becomes the last full-width thing
     in the grid instead. */
  /* `:nth-of-type(n)` matches every group, and exists purely to equal the
     specificity of the three nth-of-type rules above — without it they win and
     a group stays pinned to a column three that no longer exists, conjuring
     one. */
  .option-grid:has(> .option-hint) > .option-group:nth-of-type(n) {
    grid-column: auto;
    grid-row: auto;
  }

  .option-grid > .option-hint {
    grid-column: 1 / -1;
    grid-row: auto;
    align-self: auto;
    margin-top: 22px;
  }
  .page-hero-inner { align-items: stretch; }
  .option-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .license-grid { grid-template-columns: minmax(0, 1fr); }

  .hero-head {
    flex-direction: column;
    align-items: stretch;
    gap: 30px;
  }
}

@media (max-width: 720px) {
  .topbar-inner { gap: 16px; }

  .nav-toggle { display: flex; }

  /* Absolutely positioned against the sticky bar, so opening the menu lays a
     second bar over the page instead of shoving the page down under a header
     that does not move. */
  .topnav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 4px var(--shell-pad) 10px;
    /* Opaque, unlike the bar above it. The header can be translucent because
       there is only ever a sliver of page behind it; a panel this tall with the
       page reading through it is just illegible. */
    background: var(--ground);
    border-bottom: 1px solid var(--line-soft);
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.22);
  }

  .topbar[data-nav="open"] .topnav { display: flex; }

  /* Full-width rows: the whole line is the target, not just the word. */
  .topnav a {
    padding: 12px 0;
    border-bottom: 1px solid var(--line-soft);
  }

  .topnav a:last-child { border-bottom: 0; }

  /* Too narrow for one line: stack the three, and drop the divider so the two
     link groups don't start a wrapped row with a rule hanging off the left. */
  .footer-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
  }

  .footer-links { gap: 10px clamp(16px, 4vw, 26px); }

  .footer-list + .footer-list {
    margin-left: 0;
    padding-left: 0;
    border-left: 0;
  }

  .footer-legal { text-align: left; }

  .brand-icon { width: 32px; height: 32px; }
  .brand-name { font-size: 24px; }
}

@media (max-width: 560px) {
  .option-grid { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

