/* IBM Plex, served by us.
 *
 * Loading these from fonts.googleapis.com sent every visitor's IP address to
 * Google on every page load, signed in or not -- a disclosure the privacy
 * policy would have had to make, and one a German court has held to breach
 * GDPR without consent. It also meant a 782-byte marketing page opened
 * connections to two Google origins to fetch fonts larger than itself.
 *
 * Latin subsets only: the interface is English, and shipping Cyrillic, Greek
 * and Vietnamese would multiply the payload for glyphs never drawn.
 */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  /* One variable file covers the whole range; Google serves the same bytes
     for 400, 500, 600 and 700, so shipping four copies was 137 KB wasted. */
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/IBMPlexSans.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/IBMPlexMono-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/IBMPlexMono-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/IBMPlexMono-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}

:root {
	--bg: #e2e7ef;
	--grid: #d4dbe5;
	--surface: #fcfdff;
	--surface-2: #f1f4f9;
	--ink: #131820;
	--ink-2: #48525f;
	--ink-3: #79838f;
	--line: #c2ccda;
	--line-2: #a9b6c6;
	--edge: #9faec2;
	--accent: #1f5fd0;
	--accent-soft: #dce7fb;
	--accent-ink: #ffffff;
	--danger: #b4322b;
	--danger-soft: #fae8e6;
	--ok: #1d7a4d;
	--shadow:
		0 1px 2px rgba(19, 24, 32, 0.08), 0 10px 28px -16px rgba(19, 24, 32, 0.3);
	--shadow-lg:
		0 2px 5px rgba(19, 24, 32, 0.1), 0 28px 60px -24px rgba(19, 24, 32, 0.42);
	--dom-l: 52%;
	--dom-c: 0.17;
	--dom-l2: 38%;
	--dom-c2: 0.13;
	--r: 7px;
}

/* Surfaces the browser draws that no design system claims by default.
   Selection, the caret and the native control accent all ship in the UA's own
   blue, which is not this app's blue -- and a schema editor is mostly text
   selection, so the first one shows constantly. */
::selection {
	background: color-mix(in srgb, var(--accent) 30%, transparent);
	color: var(--ink);
}
:root {
	accent-color: var(--accent);
}
input,
textarea {
	caret-color: var(--accent);
}
@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) {
		--bg: #0c1014;
		--grid: #171c23;
		--surface: #141a21;
		--surface-2: #1a2028;
		--ink: #e7ecf3;
		--ink-2: #a3aebc;
		--ink-3: #798492;
		--line: #252d38;
		--line-2: #323c49;
		--edge: #4c5b6e;
		--accent: #5b8ff9;
		--accent-soft: #182540;
		--accent-ink: #0b0e13;
		--danger: #f1786e;
		--danger-soft: #331a18;
		--ok: #4fbf87;
		--shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 24px -14px rgba(0, 0, 0, 0.7);
		--shadow-lg:
			0 2px 6px rgba(0, 0, 0, 0.6), 0 24px 56px -22px rgba(0, 0, 0, 0.85);
		--dom-l: 74%;
		--dom-c: 0.15;
		--dom-l2: 59%;
		--dom-c2: 0.13;
	}
}
:root[data-theme="dark"] {
	--bg: #0c1014;
	--grid: #171c23;
	--surface: #141a21;
	--surface-2: #1a2028;
	--ink: #e7ecf3;
	--ink-2: #a3aebc;
	--ink-3: #798492;
	--line: #252d38;
	--line-2: #323c49;
	--edge: #4c5b6e;
	--accent: #5b8ff9;
	--accent-soft: #182540;
	--accent-ink: #0b0e13;
	--danger: #f1786e;
	--danger-soft: #331a18;
	--ok: #4fbf87;
	--shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 24px -14px rgba(0, 0, 0, 0.7);
	--shadow-lg:
		0 2px 6px rgba(0, 0, 0, 0.6), 0 24px 56px -22px rgba(0, 0, 0, 0.85);
	--dom-l: 74%;
	--dom-c: 0.15;
	--dom-l2: 59%;
	--dom-c2: 0.13;
}
* {
	box-sizing: border-box;
}
html,
body {
	height: 100%;
}
body {
	margin: 0;
	background: var(--bg);
	color: var(--ink);
	font-family:
		"IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
		sans-serif;
	font-size: 14px;
	line-height: 1.45;
	overflow: hidden;
	-webkit-font-smoothing: antialiased;
}

button,
input,
select,
textarea {
	font: inherit;
	color: inherit;
}
:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
	border-radius: 4px;
}
.mono {
	font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---------- marketing ---------- */
:root {
	color-scheme: light dark;
}

body {
	margin: 0;
	min-height: 100vh;
	overflow: auto;
	background: var(--bg);
	color: var(--ink);
	font-family:
		"IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
		sans-serif;
}

body::before {
	content: '';
	position: fixed;
	inset: 0;
	pointer-events: none;
	background-image:
		linear-gradient(to right, var(--grid) 1px, transparent 1px),
		linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
	background-size: 80px 80px;
}

main {
	position: relative;
	z-index: 1;
	min-height: 100vh;
	display: flex;
	justify-content: center;
	padding: 40px 24px 28px;
}

.page {
	width: min(740px, 100%);
	display: grid;
	gap: 24px;
}

.hero {
	display: grid;
	gap: 12px;
}

h1 {
	margin: 0;
	max-width: 12ch;
	font-size: clamp(2.7rem, 7vw, 5.2rem);
	line-height: 0.92;
	letter-spacing: -0.04em;
	text-wrap: balance;
}

.lede,
.meta,
.section p,
.footer {
	font-size: 0.98rem;
	line-height: 1.65;
}

.lede,
.meta {
	margin: 0;
	max-width: 60ch;
	color: var(--ink-2);
}

.article {
	display: grid;
	gap: 18px;
	padding: 24px;
	background: var(--surface);
	border: 1px solid color-mix(in srgb, var(--line) 82%, transparent);
	border-radius: var(--r);
	box-shadow: var(--shadow);
}

.section {
	display: grid;
	gap: 8px;
}

.section h2 {
	margin: 0;
	font-size: 1.02rem;
	line-height: 1.3;
	letter-spacing: -0.01em;
}

.section p,
.section ul {
	margin: 0;
	color: var(--ink-2);
}

.section ul {
	padding-left: 1.15rem;
	display: grid;
	gap: 8px;
}

.section li::marker {
	color: var(--ink-3);
}

.footer {
	display: flex;
	flex-wrap: wrap;
	gap: 12px 16px;
	align-items: center;
	color: var(--ink-2);
}

.footer a {
	color: inherit;
	text-decoration-color: color-mix(in srgb, var(--ink-3) 70%, transparent);
	text-underline-offset: 0.16em;
}

.footer a:hover {
	color: var(--ink);
	text-decoration-color: var(--ink);
}

.footer-support {
	margin-left: auto;
	color: var(--ink-3);
}

@media (max-width: 560px) {
	.footer-support {
		margin-left: 0;
		flex-basis: 100%;
	}
}


/* ---------- landing ---------- */

/* The embed page shares this token block rather than copying it. It is the same
   drawing on the same sheet, just framed on its own page instead of in a hero,
   and a second copy of these values would drift the first time one changed. */
body.landing,
body.embed {
  --bp-ground: #eef2f7;
  --bp-sheet: #f7f9fc;
  --bp-ink: #0c1622;
  --bp-ink-2: #46586c;
  --bp-ink-3: #7d8ea3;
  --bp-line: #0b6fa8;
  --bp-line-soft: rgba(11, 111, 168, 0.16);
  --bp-line-faint: rgba(11, 111, 168, 0.075);
  --bp-accent: #0b6fa8;
  --bp-mark: #a35a00;
  --bp-border: rgba(11, 111, 168, 0.32);

  /* The hero drawing's own palette. The exported SVG ships hard-coded hexes for
     a white document; the prerenderer swaps them for these so the drawing is
     themed rather than inverted. See scripts/prerender.mjs. */
  --dw-card: #ffffff;
  --dw-ink: #131820;
  --dw-ink-2: #48525f;
  --dw-ink-3: #79838f;
  --dw-line: #d2d9e3;
  --dw-edge: #9faec2;
  --dw-accent: #1f5fd0;

  margin: 0;
  overflow-x: hidden;
  background: var(--bp-ground);
  color: var(--bp-ink);
  font-size: 16px;
  line-height: 1.58;
  -webkit-font-smoothing: antialiased;
}

@media (prefers-color-scheme: dark) {
  body.landing,
  body.embed {
    --bp-ground: #070c12;
    --bp-sheet: #0b1119;
    --bp-ink: #dbe6f2;
    --bp-ink-2: #8fa2b8;
    --bp-ink-3: #5f7288;
    --bp-line: #4cc2ff;
    --bp-line-soft: rgba(76, 194, 255, 0.2);
    --bp-line-faint: rgba(76, 194, 255, 0.085);
    --bp-accent: #4cc2ff;
    --bp-mark: #ffb347;
    --bp-border: rgba(76, 194, 255, 0.36);

    /* Cards a shade above the sheet, ink and wires stepped down to sit on it.
       Not the light values negated -- a negated white card is pure black. */
    --dw-card: #111a25;
    --dw-ink: #dce7f3;
    --dw-ink-2: #93a6bb;
    --dw-ink-3: #6c7f94;
    --dw-line: #24313f;
    --dw-edge: #435769;
    --dw-accent: #4cc2ff;
  }
}

/* The generic marketing grid belongs to neither: the landing page draws its
   own lattice, and an embed sits inside someone else's page. */
body.landing::before,
body.embed::before { display: none; }

.landing a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  text-decoration-color: var(--bp-line-soft);
  transition: color 160ms ease, text-decoration-color 160ms ease;
}

.landing a:hover { color: var(--bp-accent); text-decoration-color: var(--bp-accent); }
.landing a:active { color: var(--bp-ink-2); }

.landing :focus-visible {
  outline: 2px solid var(--bp-accent);
  outline-offset: 3px;
}

.landing ::selection { background: var(--bp-line-soft); color: var(--bp-ink); }

/* Every figure, identifier and sheet label. Measurements, not body copy. */
.mono,
.landing code,
.sheet-label,
.dim-figure {
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
  font-variant-numeric: tabular-nums;
}

.sheet-label {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 20;
  padding: 10px 14px;
  background: var(--bp-ink);
  color: var(--bp-ground);
}
.skip-link:focus { left: 12px; top: 12px; }

/* ---------- the grid ---------- */

.landing-main {
  position: relative;
  display: block;
  min-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}

/* The lattice is the sheet the hero is drawn on, not a texture for the whole
   page. Carried on its own layer so it can be masked: it is fully present
   behind the drawing and gone by the time the reading starts, because a grid
   ruled through body copy and a pricing panel is something to read past rather
   than something that means anything. The 12-column half-tone layer is gone
   for the same reason -- at that pitch it was noise, not a measure. */
.landing-main::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--bp-line-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--bp-line-soft) 1px, transparent 1px);
  background-size:
    calc(100% / 4) 100%,
    100% 240px;
  background-position: -1px -1px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 34%, transparent 58%);
  mask-image: linear-gradient(to bottom, #000 0, #000 34%, transparent 58%);
}

.landing-main section {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

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

.landing-hero {
  align-content: center;
  min-height: min(92dvh, 880px);
  padding-block: clamp(40px, 6vw, 88px);
}

.hero-copy {
  position: relative;
  z-index: 2;
  grid-row: 1;
  grid-column: 1 / span 6;
  align-self: center;
  display: grid;
  gap: 22px;
}

.hero-copy h1 {
  max-width: 9ch;
  margin: 0;
  font-size: clamp(3rem, 8.6vw, 7.25rem);
  font-weight: 600;
  line-height: 0.88;
  letter-spacing: max(-0.045em, -5px);
  text-wrap: balance;
}

.hero-copy p,
.section-heading p,
.quiet p {
  max-width: 70ch;
  margin: 0;
  color: var(--bp-ink-2);
}

.hero-copy p {
  max-width: 40ch;
  font-size: clamp(1.05rem, 1.7vw, 1.2rem);
}

/* A real dimension line: two arrow terminators, a rule, and the figure it
   measures. It spans the headline, because that is what it is measuring. */
.dim {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 30ch;
  color: var(--bp-ink-3);
}

.dim-rule {
  position: relative;
  flex: 1;
  height: 1px;
  background: var(--bp-border);
  transform-origin: left center;
}

.dim-rule::before,
.dim-rule::after {
  content: "";
  position: absolute;
  top: -3px;
  width: 1px;
  height: 7px;
  background: var(--bp-border);
}
.dim-rule::before { left: 0; }
.dim-rule::after { right: 0; }

.dim-figure { font-size: 10.5px; letter-spacing: 0.08em; }

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 24px;
  margin-top: 6px;
  font-size: 0.92rem;
}

.primary-action {
  position: relative;
  display: inline-block;
  padding: 11px 20px 10px;
  border: 1px solid var(--bp-accent);
  color: var(--bp-accent) !important;
  text-decoration: none;
  overflow: hidden;
  transition: color 180ms ease;
}

/* The fill sweeps in from the left rather than snapping, which is the same
   gesture as a wire being routed. */
.primary-action::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bp-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
  z-index: -1;
}
.primary-action:hover { color: var(--bp-ground) !important; }
.primary-action:hover::before { transform: scaleX(1); }
.primary-action:active::before { background: var(--bp-ink); }

/* The title block, where a drawing carries its own metadata. */
.title-block {
  grid-row: 2;
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin-top: clamp(24px, 4vw, 48px);
  border: 1px solid var(--bp-border);
}

.title-block > div {
  flex: 1 1 170px;
  padding: 15px 20px 17px;
  border-left: 1px solid var(--bp-border);
}
.title-block > div:first-child { border-left: 0; }

.title-block dt {
  margin: 0 0 7px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
}

/* The value is the thing being read, so it carries the weight. At 0.95rem in
   the ink-2 grey it sat at the same visual level as its own label. */
.title-block dd {
  margin: 0;
  color: var(--bp-ink);
  font-size: 1.06rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.title-block .mark { color: var(--bp-mark); }


/* ---------- the sheet ---------- */

/* The drawing border, inset from the page edge the way a sheet border is, with
   a registration cross at each corner. Fixed, so it frames the viewport rather
   than scrolling away like content. */
.sheet-frame {
  position: fixed;
  inset: 14px;
  z-index: 6;
  border: 1px solid var(--bp-border);
  pointer-events: none;
}

.sheet-frame::before,
.sheet-frame::after {
  content: "";
  position: absolute;
  width: 13px;
  height: 13px;
  background:
    linear-gradient(var(--bp-border), var(--bp-border)) center / 100% 1px no-repeat,
    linear-gradient(var(--bp-border), var(--bp-border)) center / 1px 100% no-repeat;
}
.sheet-frame::before { top: -7px; left: -7px; }
.sheet-frame::after { bottom: -7px; right: -7px; }

.site-header,
.landing-main,
.landing-footer {
  max-width: 1360px;
  margin: 0 auto;
  padding-inline: clamp(28px, 4vw, 56px);
}

.site-header {
  position: relative;
  z-index: 7;
  display: flex;
  align-items: center;
  gap: 28px;
  padding-block: 22px;
}

.wordmark {
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  margin-right: auto;
}

.site-header nav {
  display: flex;
  align-items: center;
  gap: 26px;
  font-size: 0.86rem;
}

.site-header nav a { text-decoration: none; }
.site-header nav a:not(.open-link):hover { color: var(--bp-accent); }

.site-header .open-link {
  padding: 6px 13px 5px;
  border: 1px solid var(--bp-border);
  color: var(--bp-accent);
}
.site-header .open-link:hover {
  background: var(--bp-line-soft);
  color: var(--bp-accent);
}

/* ---------- the drawing ---------- */

.diagram-frame {
  position: relative;
  z-index: 1;
  grid-row: 1;
  grid-column: 7 / -1;
  align-self: center;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.diagram-zoom {
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
}

/* Real radios, kept off-screen but focusable and hit-testable. display:none or
   visibility:hidden would take them out of the tab order and the arrow-key
   group, which is the entire reason they are radios. */
.diagram-zoom > input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* The pan. Zooming past Fit makes the drawing wider than the frame, and this is
   what lets you move around inside it -- by drag-scroll, wheel, or the arrow
   keys once the container has focus. */
.diagram-scroll {
  overflow: auto;
  max-height: min(64vh, 520px);
  scrollbar-width: thin;
  scrollbar-color: var(--bp-line-soft) transparent;
  /* Panning the drawing must not turn into scrolling the page once you reach an
     edge, which is what makes a nested scroller feel broken on a trackpad. */
  overscroll-behavior: contain;
}

/* Only a grab cursor once there is something to grab. At Fit the drawing fills
   the frame and nothing pans, so offering the affordance there would be a lie. */
#hero-zoom-2x:checked ~ .diagram-scroll,
#hero-zoom-4x:checked ~ .diagram-scroll { cursor: grab; }

.diagram-scroll:focus-visible {
  outline: 2px solid var(--bp-accent);
  outline-offset: -2px;
}

.diagram-frame .schema-export {
  display: block;
  width: 100%;
  height: auto;
  mix-blend-mode: normal;
  transition: width 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* Multiples of the frame rather than of the drawing's own pixels: the export's
   natural size is not knowable from CSS, and a scale factor on the container is
   the thing that actually decides how much more you can read. */
#hero-zoom-2x:checked ~ .diagram-scroll .schema-export { width: 200%; }
#hero-zoom-4x:checked ~ .diagram-scroll .schema-export { width: 400%; }

@media (prefers-reduced-motion: reduce) {
  .diagram-frame .schema-export { transition: none; }
}

.zoom-controls {
  display: flex;
  gap: 0;
  border-top: 1px solid var(--bp-border);
}

.zoom-controls label {
  flex: 0 0 auto;
  padding: 7px 14px 6px;
  border-right: 1px solid var(--bp-border);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
  cursor: pointer;
  transition: color 140ms ease, background 140ms ease;
}

.zoom-controls label:hover { color: var(--bp-accent); background: var(--bp-line-faint); }

/* The selected step is filled, not merely tinted -- on a drawing the active
   view is the one that is called out. */
#hero-zoom-fit:checked ~ .zoom-controls label[for="hero-zoom-fit"],
#hero-zoom-2x:checked ~ .zoom-controls label[for="hero-zoom-2x"],
#hero-zoom-4x:checked ~ .zoom-controls label[for="hero-zoom-4x"] {
  background: var(--bp-accent);
  color: var(--bp-ground);
}

#hero-zoom-fit:focus-visible ~ .zoom-controls label[for="hero-zoom-fit"],
#hero-zoom-2x:focus-visible ~ .zoom-controls label[for="hero-zoom-2x"],
#hero-zoom-4x:focus-visible ~ .zoom-controls label[for="hero-zoom-4x"] {
  outline: 2px solid var(--bp-accent);
  outline-offset: -2px;
}

/* One relation is lit, the rest recede.
   The prerenderer tags a single wire hero-wire-lit; the rule that coloured it
   was lost in a rewrite, leaving the class inert and the drawing uniformly
   grey. Same discipline as the mark: one lit path through the lattice. */
.diagram-frame .hero-wire-lit {
  stroke: var(--bp-accent);
  stroke-width: 2.5;
}

.diagram-note {
  position: absolute;
  right: 10px;
  bottom: 8px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
}

/* ---------- the load sequence ---------- */

@keyframes bp-rise {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

@keyframes bp-extend {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@keyframes bp-wire-draw {
  to { stroke-dashoffset: 0; }
}

@keyframes bp-sheet-in {
  from { opacity: 0; clip-path: inset(0 100% 0 0); }
  to { opacity: 1; clip-path: inset(0 0 0 0); }
}

.hero-copy h1,
.hero-copy p,
.hero-actions,
.title-block {
  animation: bp-rise 760ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
}
.hero-copy h1 { animation-delay: 60ms; }
.dim { animation: bp-rise 760ms cubic-bezier(0.16, 1, 0.3, 1) 200ms backwards; }
.hero-copy p { animation-delay: 300ms; }
.hero-actions { animation-delay: 400ms; }
.title-block { animation-delay: 620ms; }

.dim-rule { animation: bp-extend 900ms cubic-bezier(0.16, 1, 0.3, 1) 280ms backwards; }

/* The sheet wipes open left to right, the direction the drawing is read. */
.diagram-frame { animation: bp-sheet-in 1s cubic-bezier(0.16, 1, 0.3, 1) 220ms backwards; }

.diagram-frame .hero-wire {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: bp-wire-draw 1.5s cubic-bezier(0.16, 1, 0.3, 1) 900ms forwards;
}

@media (prefers-reduced-motion: reduce) {
  .landing *,
  .landing *::before,
  .landing *::after {
    animation: none !important;
    transition-duration: 1ms !important;
  }
  .diagram-frame .hero-wire { stroke-dashoffset: 0; }
}

.workflow,
.formats,
.pricing {
  padding-block: clamp(72px, 8vw, 128px);
  row-gap: 0;
}

/* Each section opens with a section rule and its sheet number, the way a
   drawing numbers its views. The rule draws itself as the section arrives. */
.section-rule {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: clamp(28px, 3.5vw, 48px);
}

.section-rule .line {
  flex: 1;
  height: 1px;
  background: var(--bp-border);
  transform-origin: left center;
}

.section-heading {
  grid-column: 1 / span 10;
  align-self: start;
  max-width: min(52rem, 100%);
  margin-bottom: clamp(24px, 3vw, 40px);
}

.section-heading h2,
.quiet h2 {
  max-width: 20ch;
  margin: 0 0 14px;
  font-size: clamp(1.8rem, 3.2vw, 2.75rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: max(-0.035em, -2px);
  text-wrap: balance;
}

/* ---------- stages ---------- */

.stages {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.stages > div {
  position: relative;
  display: grid;
  align-content: start;
  gap: 9px;
  padding: 20px 22px 26px;
  border-left: 1px solid var(--bp-line-soft);
}
.stages > div:first-child { border-left: 0; }

/* A station mark on the rule above each stage, so the four read as points
   along one run rather than four boxes. */
.stages > div::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 22px;
  width: 5px;
  height: 5px;
  background: var(--bp-accent);
}
.stages > div:first-child::before { left: 22px; }

.stages dt {
  font-weight: 500;
  font-size: 0.98rem;
}

.stages dd {
  margin: 0;
  color: var(--bp-ink-2);
  font-size: 0.92rem;
}

.stages code {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.86em;
  color: var(--bp-accent);
}

/* ---------- formats ---------- */

.format-groups {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(24px, 4vw, 64px);
}

.format-group {
  padding: 2px 20px 8px;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.format-group h3 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 2px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--bp-border);
  font-size: 0.95rem;
  font-weight: 600;
}

.format-group h3 span {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
}

.format-group:last-child h3 { color: var(--bp-mark); }
.format-group:last-child h3 { border-bottom-color: var(--bp-mark); }

.format-list { display: grid; }

.format {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
  padding: 12px 0 12px 12px;
  border-bottom: 1px solid var(--bp-line-soft);
  transition: background 160ms ease, padding-left 160ms ease;
}

/* A leader line grows from the margin on hover -- the drawing's own way of
   pointing at a part. */
.format::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 8px;
  height: 1px;
  background: var(--bp-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1);
}

.format:hover { background: var(--bp-line-faint); padding-left: 16px; }
.format:hover::before { transform: scaleX(1); }

.format code {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.84rem;
  color: var(--bp-accent);
}

.format span { color: var(--bp-ink-2); font-size: 0.92rem; }
.format:last-child { border-bottom: 0; }

/* ---------- closing ---------- */

.quiet {
  padding-block: clamp(72px, 8vw, 120px);
}

.quiet h2 { grid-column: 1 / span 6; }
.quiet p { grid-column: 7 / -1; align-self: end; }

/* ---------- scroll reveals ---------- */

/* Driven by the scroll position, not a script -- the page is asserted to
   contain no script element. Where view() is unsupported the animation never
   engages and the finished state is what renders, which is also exactly what
   reduced motion gets. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes bp-reveal {
      from { opacity: 0; transform: translateY(18px); }
      to { opacity: 1; transform: none; }
    }

    @keyframes bp-draw-rule {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    .section-heading,
    .stages,
    .format-groups,
    .plan-cards,
    .quiet h2,
    .quiet p {
      animation: bp-reveal linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 26%;
    }

    .section-rule .line {
      animation: bp-draw-rule linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 22%;
    }
  }
}

/* ---------- responsive ---------- */

@media (max-width: 1080px) {
  .hero-copy { grid-column: 1 / span 7; }
  .diagram-frame { grid-column: 8 / -1; }
  .stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stages > div:nth-child(3) { border-left: 0; padding-left: 0; }
  .plan-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 820px) {
  .sheet-frame { inset: 8px; }
  .site-header nav a:not(.open-link) { display: none; }
  .landing-main::before {
    background-size:
      calc(100% / 2) 100%,
      100% 160px;
  }

  /* The hero is two columns of one row on a wide screen. Both have to leave
     that row here, or the drawing lands on top of the headline. */
  .landing-hero { min-height: 0; padding-block: clamp(32px, 7vw, 56px); }
  .hero-copy {
    grid-row: auto;
    grid-column: 1 / -1;
    padding-bottom: 36px;
  }
  .diagram-frame {
    grid-row: auto;
    grid-column: 1 / -1;
    align-self: stretch;
  }
  .title-block { grid-row: auto; }
  .title-block > div { flex-basis: 50%; border-top: 1px solid var(--bp-border); }
  .title-block > div:nth-child(-n+2) { border-top: 0; }
  .title-block > div:nth-child(odd) { border-left: 0; }

  .section-heading,
  .stages,
  .format-groups,
  .plan-cards { grid-column: 1 / -1; }

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

  .quiet h2,
  .quiet p { grid-column: 1 / -1; }
  .quiet p { margin-top: 12px; }

  .workflow,
  .formats,
  .pricing { padding-block: 68px; }

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

@media (max-width: 560px) {
  .stages { grid-template-columns: minmax(0, 1fr); }
  .stages > div {
    border-left: 0;
    padding: 18px 0 22px;
    border-top: 1px solid var(--bp-line-faint);
  }
  .stages > div:first-child { border-top: 0; }
  .stages > div::before { display: none; }
  .landing-footer > :last-child { margin-left: 0; flex-basis: 100%; }
}

/* ---------- pricing ---------- */

/* Three panels on the sheet, not a comparison grid. Data sits on an opaque
   surface: the lattice used to run straight through every row, so the eye had
   to separate a value from a grid line before it could read it, and a drawing
   is not transparent. */
.plan-cards {
  /* Full width on desktop, then collapses down. A label and its value have to
     stay in one glance; spread across the full width, "Schemas" and
     "2 schemas" sat a third of a screen apart, which is what made the old
     table unreadable. */
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px);
}

.plan-card {
  display: grid;
  align-content: start;
  padding: 0 22px 22px;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

/* The head is the offer; the spec below is the fine print. A rule between them
   is how a drawing separates a callout from its schedule. */
.plan-head {
  padding: 20px 0 18px;
  border-bottom: 1px solid var(--bp-border);
}

.plan-head h3 {
  margin: 0 0 12px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--bp-ink-3);
}

.plan-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 0;
}

/* The price is the thing being decided, so it is the largest figure in the
   section. In the table it was one cell of eighteen. */
.plan-figure {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: clamp(2.4rem, 4.4vw, 3.4rem);
  font-weight: 500;
  line-height: 0.9;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}

.plan-unit {
  color: var(--bp-ink-2);
  font-size: 0.86rem;
}

/* The mark goes on the top tier, not the middle one. It sat on Pro while Team
   was plain grey, which made the cheaper plan read as the premium one -- the
   opposite of what a price ladder is for. */
.plan-card-pro { border-color: var(--bp-mark); }
.plan-card-pro .plan-head { border-bottom-color: var(--bp-mark); }
.plan-card-pro .plan-head h3,
.plan-card-pro .plan-figure { color: var(--bp-mark); }

.plan-spec {
  display: grid;
  margin: 0;
  font-size: 0.93rem;
}

.plan-spec > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px 18px;
  padding: 12px 0;
  border-bottom: 1px solid var(--bp-line-soft);
  transition: background 140ms ease;
}
.plan-spec > div:last-child { border-bottom: 0; }
.plan-spec > div:hover { background: var(--bp-line-faint); }

.plan-spec dt { color: var(--bp-ink-2); }

.plan-spec dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.plan-action {
  margin: 22px 0 0;
  justify-self: start;
  font-size: 0.92rem;
}

/* Nothing to click on a plan that cannot be bought yet, so it is not styled as
   something to click. */
.plan-action-pending {
  color: var(--bp-ink-3);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

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

.landing-footer {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 26px;
  padding-block: 26px 34px;
  border-top: 1px solid var(--bp-border);
  color: var(--bp-ink-2);
  font-size: 0.84rem;
}

.landing-footer nav {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 20px;
}

.landing-footer > :last-child { margin-left: auto; color: var(--bp-ink-3); }


/* ---------- conversion pages ---------- */

.conversion-main {
  padding-bottom: clamp(56px, 8vw, 112px);
}

.conversion-hero {
  align-content: center;
  min-height: min(76dvh, 720px);
  padding-block: clamp(54px, 8vw, 108px);
}

.conversion-hero-copy {
  grid-column: 1 / span 8;
  display: grid;
  gap: 22px;
  align-content: center;
}

.conversion-hero h1 {
  max-width: 12ch;
  margin: 0;
  font-size: clamp(2.8rem, 7.4vw, 6.5rem);
  font-weight: 600;
  line-height: 0.92;
  letter-spacing: max(-0.045em, -4px);
  text-wrap: balance;
}

.conversion-lede {
  max-width: 60ch;
  margin: 0;
  color: var(--bp-ink-2);
  font-size: clamp(1rem, 1.5vw, 1.14rem);
}

.conversion-facts {
  grid-column: 10 / -1;
  align-self: center;
  margin: 0;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.conversion-facts div {
  padding: 16px 18px 18px;
  border-bottom: 1px solid var(--bp-line-soft);
}

.conversion-facts div:last-child { border-bottom: 0; }
.conversion-facts dt { margin-bottom: 5px; }
.conversion-facts dd {
  margin: 0;
  color: var(--bp-ink);
  font-size: 0.96rem;
  line-height: 1.4;
}
.conversion-facts .tier-value { color: var(--bp-mark); font-weight: 600; }

.conversion-note {
  grid-column: 1 / -1;
  margin: clamp(14px, 2.5vw, 28px) 0 0;
  padding: 15px 18px 16px;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
  color: var(--bp-ink-2);
  font-size: 0.9rem;
}

.conversion-section {
  padding-block: clamp(58px, 7vw, 96px);
  row-gap: 0;
}

.conversion-section + .conversion-section {
  border-top: 1px solid var(--bp-line-soft);
}

.conversion-section .section-heading { margin-bottom: clamp(22px, 3vw, 36px); }

.code-panel {
  position: relative;
  min-width: 0;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.code-panel figcaption {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px 20px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--bp-border);
  color: var(--bp-ink-3);
  font-size: 0.78rem;
}

.code-panel pre {
  max-height: 690px;
  margin: 0;
  overflow: auto;
  padding: clamp(18px, 2.6vw, 30px);
  scrollbar-width: thin;
  scrollbar-color: var(--bp-border) var(--bp-sheet);
}

.code-panel code {
  display: block;
  min-width: max-content;
  color: var(--bp-ink);
  font-size: clamp(0.74rem, 1vw, 0.86rem);
  line-height: 1.62;
  white-space: pre;
  tab-size: 2;
}

.output-wide { grid-column: 1 / span 9; }
.output-full { grid-column: 1 / -1; }

.annotation-rail {
  grid-column: 10 / -1;
  align-self: start;
  margin: 0;
  border-top: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.annotation-rail div {
  padding: 16px 0 18px;
  border-bottom: 1px solid var(--bp-line-soft);
}

.annotation-rail dt {
  margin-bottom: 6px;
  color: var(--bp-ink);
  font-weight: 500;
}

.annotation-rail dd {
  margin: 0;
  color: var(--bp-ink-2);
  font-size: 0.89rem;
}

.pipeline {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1.25fr 1fr;
  margin: 0 0 clamp(28px, 4vw, 48px);
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.pipeline div { padding: 18px 20px 22px; border-right: 1px solid var(--bp-line-soft); }
.pipeline div:last-child { border-right: 0; }
.pipeline dt { margin-bottom: 8px; font-weight: 500; }
.pipeline dd { margin: 0; color: var(--bp-ink-2); font-size: 0.9rem; }

.contract-map,
.dialect-map {
  grid-column: 1 / span 4;
  align-self: start;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}

.contract-map table,
.dialect-map table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
  font-size: 0.88rem;
}

.contract-map caption,
.dialect-map caption {
  padding: 13px 16px;
  border-bottom: 1px solid var(--bp-border);
  text-align: left;
}

.contract-map th,
.contract-map td,
.dialect-map th,
.dialect-map td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--bp-line-soft);
}

.contract-map tr:last-child th,
.contract-map tr:last-child td,
.dialect-map tr:last-child th,
.dialect-map tr:last-child td { border-bottom: 0; }

.contract-map th,
.dialect-map th { font-weight: 500; }
.contract-map td,
.dialect-map td { color: var(--bp-ink-2); }
.contract-output,
.dialect-output { grid-column: 5 / -1; }

.dbml-ledger {
  grid-column: 9 / -1;
  align-self: start;
  padding: 22px;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}
.dbml-output { grid-column: 1 / span 8; }
.dbml-ledger h3 { margin: 0 0 12px; font-size: 1rem; }
.dbml-ledger ul { margin: 0; padding-left: 1.1rem; color: var(--bp-ink-2); }
.dbml-ledger li + li { margin-top: 8px; }

.relation-run {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-bottom: clamp(26px, 4vw, 46px);
  padding: 22px;
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
  text-align: center;
}
.relation-run .node { padding: 12px; border: 1px solid var(--bp-line-soft); }
.relation-run .edge { color: var(--bp-accent); }

.limitation-panel {
  grid-column: 1 / span 9;
  padding: clamp(20px, 3vw, 30px);
  border: 1px solid var(--bp-border);
  background: var(--bp-sheet);
}
.limitation-panel p { margin: 0; color: var(--bp-ink-2); }
.limitation-panel p + p { margin-top: 10px; }
.limitation-panel code { color: var(--bp-ink); font-size: 0.82em; }

.caveat-list {
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--bp-line-soft);
}
.caveat-list li {
  padding: 11px 0;
  border-bottom: 1px solid var(--bp-line-soft);
  color: var(--bp-ink-2);
}

.conversion-action {
  grid-column: 10 / -1;
  align-self: end;
  display: grid;
  gap: 12px;
}
.conversion-action p { margin: 0; color: var(--bp-ink-2); font-size: 0.9rem; }
.conversion-action .primary-action { justify-self: start; }

@media (max-width: 900px) {
  .conversion-hero-copy { grid-column: 1 / span 8; }
  .conversion-facts { grid-column: 9 / -1; }
  .output-wide,
  .dbml-output { grid-column: 1 / span 8; }
  .annotation-rail,
  .dbml-ledger { grid-column: 9 / -1; }
  .limitation-panel { grid-column: 1 / span 8; }
  .conversion-action { grid-column: 9 / -1; }
}

@media (max-width: 720px) {
  .conversion-hero { min-height: 0; }
  .conversion-hero-copy,
  .conversion-facts,
  .conversion-note,
  .output-wide,
  .annotation-rail,
  .contract-map,
  .contract-output,
  .dbml-output,
  .dbml-ledger,
  .dialect-map,
  .dialect-output,
  .limitation-panel,
  .conversion-action { grid-column: 1 / -1; }
  .conversion-facts { margin-top: 32px; }
  .pipeline { grid-template-columns: 1fr; }
  .pipeline div { border-right: 0; border-bottom: 1px solid var(--bp-line-soft); }
  .pipeline div:last-child { border-bottom: 0; }
  .annotation-rail,
  .dbml-ledger { margin-top: 20px; }
  .contract-output,
  .dialect-output { margin-top: 20px; }
  .relation-run { grid-template-columns: 1fr; text-align: left; }
  .relation-run .edge { transform: rotate(90deg); justify-self: center; }
  .conversion-action { margin-top: 24px; }
}
