/*
  Charty documentation.

  Monochrome by design. A charting library's pages are full of colour already — every screenshot is
  six or seven hues — and a coloured interface competes with the thing it is presenting. So the
  chrome is black, white and grey, structure comes from hairline borders and space rather than from
  tinted panels, and the only colour on any page belongs to the charts.

  Both themes are declared here as variables and every rule below is written once against them, so
  neither is the special case. The attribute is set by an inline script before the first paint; a
  class toggled after load would flash the wrong colours on every navigation.

  Code blocks are the deliberate exception: they stay dark in both themes, because syntax colours are
  built for a dark ground and a block that inverts with the page makes one snippet look like two
  different things.
*/

:root {
    --bg: #ffffff;
    --surface: #ffffff;
    --surface-container-low: #fafafa;
    --surface-container: #f5f5f5;
    --surface-container-high: #ededed;
    --surface-container-highest: #e3e3e3;
    --outline: #a1a1a1;
    --outline-variant: #eaeaea;
    --text: #000000;
    --text-muted: #525252;
    --text-faint: #8f8f8f;
    /* "Primary" is ink, not a hue: a filled control is black on white and white on black. */
    --primary: #000000;
    --on-primary: #ffffff;
    --primary-container: #f2f2f2;
    --on-primary-container: #000000;
    --secondary-container: #f2f2f2;
    --on-secondary-container: #000000;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 24px rgba(0, 0, 0, 0.06);
    --sidebar-width: 268px;
    --toc-width: 224px;
    --topbar-height: 64px;
    --content-max: 780px;
    --radius: 12px;
    --radius-sm: 8px;
    --radius-pill: 999px;
}

:root[data-theme="dark"] {
    --bg: #000000;
    --surface: #000000;
    --surface-container-low: #0a0a0a;
    --surface-container: #111111;
    --surface-container-high: #1a1a1a;
    --surface-container-highest: #262626;
    --outline: #6f6f6f;
    --outline-variant: #262626;
    --text: #ffffff;
    --text-muted: #a1a1a1;
    --text-faint: #7d7d7d;
    --primary: #ffffff;
    --on-primary: #000000;
    --primary-container: #1a1a1a;
    --on-primary-container: #ffffff;
    --secondary-container: #1a1a1a;
    --on-secondary-container: #ffffff;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 8px 24px rgba(0, 0, 0, 0.5);
}

/*
  Code follows the theme like everything else.

  It was fixed dark at first, on the reasoning that syntax palettes are designed for a dark ground.
  That is true and beside the point: a slab of black halfway down a white page is the loudest thing
  on it, and readers who chose a light theme chose it for the code as much as the prose. Both
  palettes below are checked against their own background rather than borrowed from the other, since
  a colour that reads well on near-black is usually too pale on near-white.
*/
:root {
    --code-bg: #fbfbfc;
    --code-bg-header: #f1f1f3;
    --code-border: #e2e2e6;
    --code-text: #16161a;
    --code-gutter: #6f6f7a;
    --code-chrome-text: #55555f;
    --tok-keyword: #7326c9;
    --tok-string: #0a6c41;
    --tok-comment: #6b6b76;
    --tok-number: #9a4200;
    --tok-type: #0b539e;
    --tok-annotation: #7d4f00;
}

:root[data-theme="dark"] {
    --code-bg: #16161a;
    --code-bg-header: #1f1f24;
    --code-border: #32323a;
    --code-text: #f7f7f8;
    --code-gutter: #9797a3;
    --code-chrome-text: #a5a5b2;
    --tok-keyword: #e0b6ff;
    --tok-string: #7dfab5;
    --tok-comment: #9b9baa;
    --tok-number: #ffbc8a;
    --tok-type: #9fd4ff;
    --tok-annotation: #ffd98a;
}

* {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* Anchored headings otherwise land underneath the sticky top bar. */
    scroll-padding-top: calc(var(--topbar-height) + 16px);
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    font-size: 16px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--primary);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

.skip {
    position: absolute;
    left: -9999px;
}

.skip:focus {
    left: 12px;
    top: 12px;
    z-index: 100;
    background: var(--primary);
    color: var(--on-primary);
    padding: 10px 16px;
    border-radius: 8px;
}

/* ---------- top bar ---------- */

.topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    height: var(--topbar-height);
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 0 20px;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: saturate(180%) blur(12px);
    border-bottom: 1px solid var(--outline-variant);
}

.brand {
    display: flex;
    align-items: center;
    gap: 9px;
    font-weight: 700;
    font-size: 17px;
    color: var(--text);
    letter-spacing: -0.01em;
}

.brand:hover {
    text-decoration: none;
}

.brand-mark {
    width: 22px;
    height: 22px;
    border-radius: 6px;
    background: linear-gradient(140deg, #6650a4, #9a7fd8);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
}

.menu-toggle {
    display: none;
    background: none;
    border: 0;
    color: var(--text);
    font-size: 20px;
    cursor: pointer;
    padding: 4px 8px;
}

.search {
    position: relative;
    flex: 1;
    max-width: 460px;
    margin: 0 auto;
}

.search input {
    width: 100%;
    height: 44px;
    padding: 0 42px 0 42px;
    border-radius: var(--radius-pill);
    border: 1px solid transparent;
    background: var(--surface-container-high);
    color: var(--text);
    font: inherit;
    font-size: 14.5px;
}

.search input::placeholder {
    color: var(--text-faint);
}

.search-icon {
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 18px;
    color: var(--text-faint);
    pointer-events: none;
}

.search-hint {
    position: absolute;
    right: 14px;
    top: 50%;
    transform: translateY(-50%);
    padding: 1px 7px;
    border-radius: 6px;
    background: var(--surface-container-highest);
    color: var(--text-faint);
    font-size: 11.5px;
    font-family: inherit;
    pointer-events: none;
}

.search input:focus + .search-hint,
.search input:not(:placeholder-shown) + .search-hint {
    opacity: 0;
}

.search input:focus {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

.search-results {
    position: absolute;
    top: 46px;
    left: 0;
    right: 0;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--surface-container);
    border: 1px solid var(--outline-variant);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 6px;
}

.search-results a {
    display: block;
    padding: 9px 12px;
    border-radius: 8px;
    color: var(--text);
}

.search-results a:hover,
.search-results a.is-active {
    background: var(--secondary-container);
    text-decoration: none;
}

.search-results .r-section {
    display: block;
    font-size: 12px;
    color: var(--text-faint);
}

.search-results .r-empty {
    padding: 12px;
    color: var(--text-muted);
    font-size: 14px;
}

.topbar-links {
    display: flex;
    align-items: center;
    gap: 18px;
    font-size: 14px;
    font-weight: 500;
}

.topbar-links a {
    color: var(--text-muted);
}

.topbar-links a:hover {
    color: var(--text);
}

.theme-toggle {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid var(--outline);
    background: var(--surface-container-low);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    color: var(--text);
}

/* ---------- layout ---------- */

.layout {
    display: grid;
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr) var(--toc-width);
    gap: 40px;
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 24px;
    align-items: start;
}

.layout--wide {
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
}

.sidebar {
    position: sticky;
    top: var(--topbar-height);
    max-height: calc(100vh - var(--topbar-height));
    overflow-y: auto;
    padding: 28px 0 60px;
    font-size: 14.5px;
}

.nav-home {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    margin-bottom: 2px;
    border-radius: var(--radius-pill);
    color: var(--text);
    font-weight: 600;
    font-size: 15px;
}

.nav-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 8px;
    background: var(--primary-container);
    color: var(--on-primary-container);
    font-size: 12px;
}

/* Collapsible groups. The marker is drawn by us so it can sit on the right and rotate. */
.nav-section {
    margin-top: 6px;
}

.nav-section summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 9px 16px;
    border-radius: var(--radius-pill);
    cursor: pointer;
    list-style: none;
    font-size: 14.5px;
    font-weight: 650;
    color: var(--text);
}

.nav-section summary::-webkit-details-marker {
    display: none;
}

.nav-section summary::after {
    content: "⌄";
    font-size: 14px;
    color: var(--text-faint);
    transform: translateY(-2px);
    transition: transform 160ms ease;
}

.nav-section[open] summary::after {
    transform: translateY(1px) rotate(180deg);
}

.nav-section summary:hover {
    background: var(--surface-container-high);
}

.nav-section ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-link {
    display: block;
    padding: 8px 16px;
    margin: 1px 0;
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    font-size: 14px;
}

.nav-link:hover,
.nav-home:hover {
    background: var(--surface-container-high);
    color: var(--text);
    text-decoration: none;
}

/* The current page is a filled pill rather than tinted text: at a glance it is the one thing in the
   sidebar with a shape, which is far easier to find than a colour difference. */
.nav-link.is-active,
.nav-home.is-active {
    background: var(--secondary-container);
    color: var(--on-secondary-container);
    font-weight: 650;
}

.content {
    min-width: 0;
    padding: 34px 0 96px;
    max-width: var(--content-max);
}

.toc {
    position: sticky;
    top: var(--topbar-height);
    max-height: calc(100vh - var(--topbar-height));
    overflow-y: auto;
    padding: 34px 0 60px;
    font-size: 13.5px;
}

.toc h2 {
    margin: 0 0 8px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.toc ul {
    list-style: none;
    margin: 0;
    padding: 0;
    border-left: 1px solid var(--outline-variant);
}

.toc a {
    display: block;
    padding: 4px 0 4px 14px;
    margin-left: -1px;
    border-left: 2px solid transparent;
    color: var(--text-muted);
}

.toc a:hover {
    color: var(--text);
    text-decoration: none;
}

.toc a.is-active {
    color: var(--primary);
    border-left-color: var(--primary);
    font-weight: 600;
}

/* ---------- prose ---------- */

.content h1 {
    margin: 0 0 18px;
    font-size: 40px;
    line-height: 1.15;
    letter-spacing: -0.025em;
    font-weight: 800;
}

.content h2 {
    margin: 46px 0 14px;
    padding-top: 12px;
    font-size: 25px;
    letter-spacing: -0.015em;
    font-weight: 700;
    border-top: 1px solid var(--outline-variant);
}

.content h3 {
    margin: 30px 0 10px;
    font-size: 18.5px;
    font-weight: 700;
}

.content p {
    margin: 0 0 16px;
    color: var(--text);
}

.content ul,
.content ol {
    margin: 0 0 16px;
    padding-left: 22px;
}

.content li {
    margin: 5px 0;
}

.content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius);
    border: 1px solid var(--outline-variant);
    background: #fff;
    margin: 8px 0 20px;
}

/* Blockquotes are used in these docs as asides, so they are drawn as Material callouts: a tonal
   filled box with a label, rather than a bar in the margin. */
.content blockquote {
    position: relative;
    margin: 0 0 20px;
    padding: 44px 22px 6px;
    background: var(--surface-container);
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--text-muted);
}

.content blockquote::before {
    content: "◈  Note";
    position: absolute;
    top: 16px;
    left: 22px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--primary);
}

.content blockquote > :last-child {
    margin-bottom: 16px;
}

.anchor {
    margin-left: 10px;
    color: var(--text-faint);
    opacity: 0;
    font-weight: 400;
    text-decoration: none;
}

h1:hover .anchor,
h2:hover .anchor,
h3:hover .anchor,
.anchor:focus {
    opacity: 1;
}

/* Tables scroll inside their own box; the page itself must never scroll sideways. */
.content table {
    display: block;
    overflow-x: auto;
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 20px;
    font-size: 14.5px;
}

.content th,
.content td {
    border: 1px solid var(--outline-variant);
    padding: 9px 13px;
    text-align: left;
    vertical-align: top;
}

.content th {
    background: var(--surface-container-low);
    font-weight: 650;
}

code {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.875em;
}

:not(pre) > code {
    background: var(--code-bg);
    border: 1px solid var(--outline-variant);
    border-radius: 5px;
    padding: 1px 5px;
    color: var(--text);
}

.code-block {
    position: relative;
    margin: 0 0 20px;
}

.code-block pre {
    margin: 0;
    padding: 20px 20px 20px 0;
    overflow-x: auto;
    background: var(--code-bg);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    /* Looser than prose: code is read a line at a time, and tight leading makes a dense argument
       list read as a block of noise. */
    line-height: 1.78;
    color: var(--code-text);
    /* Tabular figures keep the line numbers in a true column instead of wobbling by digit. */
    font-variant-numeric: tabular-nums;
}

/* Line numbers come from a counter rather than from text, so they cannot be selected and a copied
   snippet is the code alone. */
.code-block code {
    counter-reset: line;
}

/*
  A line is a block so it can be numbered, and so hovering can pick one out. The highlight is the
  faintest thing that still works: reading code in a document usually means tracking one line across
  a wide argument list, and losing your place is the whole difficulty.
*/
.line {
    display: block;
    padding: 0 20px 0 0;
    min-height: 1.78em;
    transition: background 120ms ease;
}

.code-block:hover .line:hover {
    background: color-mix(in srgb, #ffffff 4%, transparent);
}

.line::before {
    counter-increment: line;
    content: counter(line);
    display: inline-block;
    width: 46px;
    padding-right: 18px;
    text-align: right;
    color: var(--code-gutter);
    /* Numbers are for orientation, not for reading, so they sit back a step from the code. */
    opacity: 0.75;
    user-select: none;
    -webkit-user-select: none;
}

.code-block pre code {
    background: none;
    border: 0;
    padding: 0;
}

.copy {
    position: absolute;
    top: 10px;
    right: 10px;
    z-index: 1;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 600;
    border-radius: var(--radius-pill);
    border: 1px solid var(--code-border);
    background: var(--code-bg-header);
    color: var(--code-chrome-text);
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease;
}

.code-block:hover .copy,
.copy:focus {
    opacity: 1;
}

.tok-keyword { color: var(--tok-keyword); font-weight: 600; }
.tok-string { color: var(--tok-string); }
.tok-comment { color: var(--tok-comment); font-style: italic; }
.tok-number { color: var(--tok-number); }
.tok-type { color: var(--tok-type); }
.tok-annotation { color: var(--tok-annotation); }

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

.landing {
    display: block;
}

.landing section {
    scroll-margin-top: var(--topbar-height);
}

.band-inner,
.hero,
.closing {
    max-width: 1180px;
    margin: 0 auto;
    padding-left: 24px;
    padding-right: 24px;
}

.band-inner.narrow {
    max-width: 900px;
}

/* Hero: the text carries the argument, the art carries the mood. Two columns where there is room,
   stacked where there is not, with the art second so it never pushes the headline below the fold. */
.hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
    gap: 56px;
    align-items: center;
    padding-top: 68px;
    padding-bottom: 64px;
}

.eyebrow {
    margin: 0 0 14px;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--primary);
}

.hero h1 {
    margin: 0 0 20px;
    font-size: clamp(36px, 4.6vw, 58px);
    line-height: 1.05;
    letter-spacing: -0.035em;
    font-weight: 800;
}

.lede {
    font-size: 18.5px;
    line-height: 1.6;
    color: var(--text-muted);
    margin: 0 0 30px;
    max-width: 60ch;
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 26px;
}

.button {
    display: inline-block;
    padding: 11px 22px;
    border-radius: 999px;
    border: 1px solid var(--outline);
    font-weight: 600;
    font-size: 15px;
    color: var(--text);
    background: var(--surface-container);
    transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
}

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

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

.button--primary:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}

/*
  Supporting actions, as chips rather than buttons.

  They belong in the hero — a reader who likes the project should not have to hunt for the way to say
  so — but they are not what the page is asking for. Lighter weight, smaller, and set below the two
  real calls to action, so the hierarchy still reads: try it, then, if you want, support it.
*/
.hero-support {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 24px;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 14px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--outline-variant);
    background: var(--bg);
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 500;
    transition: border-color 160ms ease, color 160ms ease, transform 160ms ease;
}

.chip:hover {
    text-decoration: none;
    border-color: var(--text);
    color: var(--text);
    transform: translateY(-1px);
}

/* Facts, stated small. A row of numbers answers "what is this" faster than another paragraph. */
.hero-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 22px;
    margin: 0 0 22px;
    padding: 0;
    list-style: none;
    font-size: 13.5px;
    color: var(--text-faint);
}

.hero-facts strong {
    color: var(--text);
    font-weight: 650;
    margin-right: 4px;
}

.hero-byline {
    margin: 0;
    font-size: 13.5px;
    color: var(--text-faint);
}

.hero-byline a {
    color: var(--text-muted);
}

.hero-byline a:hover {
    color: var(--text);
}

.hero-art .code-block {
    margin-bottom: 16px;
}

.hero-art .code-block pre {
    font-size: 13px;
}

.hero-banner {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
    border: 1px solid var(--outline-variant);
}

/*
  Two renderings of the same artwork, swapped by the theme rather than by a filter. Dimming a light
  image is not a dark version of it — the ground stays pale and merely gets duller. The dark file has
  the illustration's lightness inverted, so it is genuinely drawn on black.

  Swapped in CSS rather than with <picture>, because <picture> can only follow the operating system's
  preference and this site has a toggle of its own that must win.
*/
.hero-banner--dark {
    display: none;
}

:root[data-theme="dark"] .hero-banner--light {
    display: none;
}

:root[data-theme="dark"] .hero-banner--dark {
    display: block;
}

/*
  The banner is a designed asset with its own pale background, so it keeps that background in both
  themes rather than being inverted or tinted — a brand mark that changes colour with the theme stops
  being one. In dark mode it sits slightly dimmed on its own plate, so it reads as a deliberate panel
  rather than a rectangle of glare.
*/
.hero-art img {
    width: 100%;
    height: auto;
    border-radius: 18px;
    border: 1px solid var(--outline-variant);
    box-shadow: var(--shadow);
}

:root[data-theme="dark"] .hero-art img {
    filter: brightness(0.9);
}

/* Marquee: the charts, before any prose about them. */
.marquee {
    overflow: hidden;
    padding: 8px 0 64px;
    /* Fades the ends so the strip reads as continuous rather than as a row that got cut off. */
    mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}

.marquee-track {
    display: flex;
    gap: 16px;
    width: max-content;
    animation: marquee 90s linear infinite;
}

.marquee:hover .marquee-track {
    animation-play-state: paused;
}

.marquee img {
    height: 150px;
    width: auto;
    border-radius: 12px;
    border: 1px solid var(--outline-variant);
    background: #fff;
    display: block;
}

/* The track holds two identical copies, so translating by exactly half its width lands the second
   copy where the first started and the reset is invisible. */
@keyframes marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* Bands */
.band {
    padding: 76px 0;
}

.band--alt {
    background: var(--surface-container-low);
    border-top: 1px solid var(--outline-variant);
    border-bottom: 1px solid var(--outline-variant);
}

.section-title {
    margin: 0 0 10px;
    font-size: clamp(26px, 3vw, 34px);
    letter-spacing: -0.025em;
    font-weight: 800;
}

.section-lede {
    margin: 0 0 34px;
    font-size: 17px;
    color: var(--text-muted);
    max-width: 62ch;
}

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
    gap: 12px;
}

.card {
    display: block;
    padding: 18px 18px 20px;
    border-radius: var(--radius-sm);
    color: var(--text);
}

.card:hover {
    text-decoration: none;
    border-color: var(--primary);
    transform: translateY(-3px);
    box-shadow: var(--shadow);
}

.card h3 {
    margin: 0 0 7px;
    font-size: 15px;
    font-weight: 650;
    letter-spacing: -0.01em;
}

.card p {
    margin: 0;
    color: var(--text-muted);
    font-size: 13.5px;
    line-height: 1.55;
}

.card code {
    font-size: 0.9em;
}

/* Catalog */
.catalog-group {
    margin-bottom: 44px;
}

.catalog-heading {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 16px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.count {
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--secondary-container);
    color: var(--primary);
    font-size: 11.5px;
    letter-spacing: 0;
}

.chart-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(184px, 1fr));
    gap: 14px;
}

.chart-card {
    display: block;
    border: 1px solid var(--outline-variant);
    border-radius: 12px;
    overflow: hidden;
    background: var(--surface-container);
    color: var(--text);
    transition: border-color 150ms ease, transform 150ms ease;
}

.chart-card:hover {
    text-decoration: none;
    border-color: var(--primary);
    transform: translateY(-2px);
}

.chart-card-art {
    aspect-ratio: 16 / 10;
    background: #fff;
    overflow: hidden;
}

.chart-card-art img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center top;
    display: block;
}

.chart-card-name {
    display: block;
    padding: 10px 12px;
    font-size: 14px;
    font-weight: 600;
    border-top: 1px solid var(--outline-variant);
}

/* Platforms */
.platforms {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
    margin-bottom: 40px;
}

.platform {
    padding: 18px 20px;
    border: 1px solid var(--outline-variant);
    border-radius: 12px;
    background: var(--surface-container);
}

.platform strong {
    display: block;
    font-size: 16px;
    margin-bottom: 3px;
}

.platform span {
    font-size: 13.5px;
    color: var(--text-muted);
}

.install {
    max-width: 660px;
}

.note {
    color: var(--text-muted);
    font-size: 14.5px;
    line-height: 1.6;
}

/* Comparison */
.table-scroll {
    overflow-x: auto;
    margin-bottom: 60px;
}

.compare {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
}

.compare th,
.compare td {
    padding: 11px 14px;
    border-bottom: 1px solid var(--outline-variant);
    text-align: center;
}

.compare thead th {
    font-size: 13px;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

.compare tbody th {
    text-align: left;
    font-weight: 500;
    white-space: nowrap;
}

.compare .yes { color: #2e9e63; font-size: 17px; }
.compare .partial { color: #c98a1e; font-size: 15px; }
.compare .no { color: var(--text-faint); font-size: 17px; }

.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 14px;
}

.stat {
    padding: 20px;
    border: 1px solid var(--outline-variant);
    border-radius: 12px;
    background: var(--surface-container);
}

.stat strong {
    display: block;
    font-size: 26px;
    font-weight: 800;
    letter-spacing: -0.02em;
    margin-bottom: 4px;
}

.stat span {
    font-size: 13.5px;
    color: var(--text-muted);
}

/* Closing */
.closing {
    text-align: center;
    padding-top: 84px;
    padding-bottom: 84px;
}

.closing h2 {
    margin: 0 0 10px;
    font-size: clamp(26px, 3vw, 36px);
    letter-spacing: -0.025em;
    font-weight: 800;
}

.closing p {
    margin: 0 0 26px;
    color: var(--text-muted);
    font-size: 17px;
}

.closing .hero-actions {
    justify-content: center;
    margin-bottom: 0;
}

/* Footer */
.footer {
    border-top: 1px solid var(--outline-variant);
    background: var(--surface-container-low);
    padding: 52px 0 60px;
}

.footer-inner {
    max-width: 1180px;
    margin: 0 auto;
    padding: 0 24px;
    display: grid;
    grid-template-columns: 1.6fr repeat(3, 1fr);
    gap: 32px;
}

.footer .brand-mark {
    display: inline-block;
    vertical-align: -4px;
    margin-right: 8px;
}

.footer strong {
    font-size: 17px;
}

.footer p {
    margin: 12px 0 0;
    font-size: 14px;
    color: var(--text-muted);
    max-width: 34ch;
}

.footer-license {
    font-size: 13px !important;
    color: var(--text-faint) !important;
}

.footer h3 {
    margin: 0 0 12px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.footer a {
    display: block;
    padding: 3px 0;
    font-size: 14px;
    color: var(--text-muted);
}

.footer a:hover {
    color: var(--primary);
}

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

    .band {
        padding: 56px 0;
    }

    .footer-inner {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (prefers-reduced-motion: reduce) {
    .marquee-track {
        animation: none;
    }
}

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

@media (max-width: 1180px) {
    .layout {
        grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    }

    .toc {
        display: none;
    }
}

@media (max-width: 900px) {
    .menu-toggle {
        display: block;
    }

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

    .sidebar {
        display: none;
        position: fixed;
        top: var(--topbar-height);
        left: 0;
        bottom: 0;
        width: 290px;
        max-height: none;
        z-index: 25;
        padding: 20px 16px 40px;
        background: var(--surface-container);
        border-right: 1px solid var(--outline-variant);
        box-shadow: var(--shadow);
    }

    .sidebar.is-open {
        display: block;
    }

    .topbar-links a {
        display: none;
    }

    .hero h1 {
        font-size: 36px;
    }

    .content h1 {
        font-size: 31px;
    }
}

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

    * {
        transition: none !important;
    }
}

/* ---------- previous / next ---------- */

.page-nav {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
    margin-top: 56px;
}

/*
  Compact enough to be a footer rather than a second navigation screen: the label is small, the title
  carries the weight, and the arrow is its own element so it can move on hover — the cheapest way to
  say "this goes somewhere" without adding another word.
*/
.page-nav-card {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 13px 16px;
    border-radius: var(--radius-sm);
    color: var(--text);
}

.page-nav-card--next {
    justify-content: flex-end;
    text-align: right;
}

.page-nav-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.page-nav-label {
    font-size: 12px;
    font-weight: 500;
    color: var(--text-faint);
    letter-spacing: 0.02em;
}

.page-nav-title {
    font-size: 14.5px;
    font-weight: 600;
    letter-spacing: -0.01em;
    /* A long chart name should not force the pair of cards to different heights. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.page-nav-arrow {
    flex: none;
    color: var(--text-faint);
    font-size: 15px;
    transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1), color 200ms ease;
}

.page-nav-card:hover {
    text-decoration: none;
}

.page-nav-card:hover .page-nav-arrow {
    color: var(--text);
}

.page-nav-card:hover .page-nav-arrow:first-child {
    transform: translateX(-3px);
}

.page-nav-card:hover .page-nav-arrow:last-child {
    transform: translateX(3px);
}

/* ---------- topbar and surfaces, Material tone ---------- */

.topbar {
    background: color-mix(in srgb, var(--surface) 82%, transparent);
    border-bottom: 1px solid var(--outline-variant);
}

.theme-toggle {
    width: 40px;
    height: 40px;
    border: 0;
    background: var(--surface-container-high);
}

.theme-toggle:hover {
    background: var(--surface-container-highest);
}

.search-results {
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--surface-container-high);
    padding: 8px;
}

.search-results a {
    border-radius: var(--radius-sm);
    padding: 10px 14px;
}

.search-results a:hover,
.search-results a.is-active {
    background: var(--secondary-container);
    color: var(--on-secondary-container);
}

/* Documentation headings no longer need a rule above them; the tonal surfaces do the separating. */
.content h2 {
    border-top: 0;
    padding-top: 0;
    margin-top: 52px;
}

.content table {
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--outline-variant);
}

.content th,
.content td {
    border: 0;
    border-bottom: 1px solid var(--outline-variant);
}

.content th {
    background: var(--surface-container);
}

.content img {
    border-radius: var(--radius-sm);
    border: 1px solid var(--outline-variant);
}

:not(pre) > code {
    background: var(--surface-container-high);
    border: 0;
    border-radius: 6px;
    padding: 2px 6px;
}

.card,
.platform,
.stat,
.chart-card {
    border: 0;
    background: var(--surface-container);
}

.card:hover,
.chart-card:hover {
    background: var(--surface-container-high);
}

.chart-card-name {
    border-top: 1px solid var(--outline-variant);
}

.button {
    border: 0;
    background: var(--surface-container-high);
    padding: 13px 26px;
}

.button:hover {
    background: var(--surface-container-highest);
    color: var(--text);
}

.button--primary,
.button--primary:hover {
    background: var(--primary);
    color: var(--on-primary);
}

.band--alt {
    background: var(--surface-container-low);
    border-top: 0;
    border-bottom: 0;
}

.footer {
    background: var(--surface-container-low);
    border-top: 0;
}

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

    .search-hint {
        display: none;
    }
}

/* ---------- sponsor ---------- */

.button--sponsor {
    background: var(--primary-container);
    color: var(--on-primary-container);
}

.button--sponsor:hover {
    background: var(--primary-container);
    color: var(--on-primary-container);
    filter: brightness(0.96);
}

.sponsor-link {
    padding: 7px 14px;
    border-radius: var(--radius-pill);
    background: var(--primary-container);
    color: var(--on-primary-container) !important;
    font-weight: 600;
}

.sponsor-link:hover {
    text-decoration: none;
    filter: brightness(0.96);
}

.heart {
    color: #e0455f;
}

:root[data-theme="dark"] .heart {
    color: #ff8fa3;
}

/* ---------- monochrome refinements ---------- */

/*
  Structure comes from hairline borders and space, not from tinted panels. Cards get a border and the
  page background rather than a fill, which is what keeps a grid of chart thumbnails reading as
  images on a page instead of as tiles in an app.
*/
.card,
.platform,
.stat,
.chart-card,
.page-nav-card {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
    border-radius: var(--radius-sm);
}

.card:hover,
.chart-card:hover,
.page-nav-card:hover {
    background: var(--bg);
    border-color: var(--text);
}

.band--alt {
    background: var(--surface-container-low);
}

.footer {
    background: var(--bg);
    border-top: 1px solid var(--outline-variant);
}

.brand {
    gap: 8px;
    font-size: 17px;
    letter-spacing: -0.02em;
}

.logo-mark {
    display: block;
    color: var(--text);
}

.footer-brand {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 17px;
}

.footer-brand strong {
    letter-spacing: -0.02em;
}

/* Buttons are ink on paper: the filled one is solid black on white and solid white on black. */
.button {
    border: 1px solid var(--outline-variant);
    background: var(--bg);
    border-radius: var(--radius-sm);
    padding: 11px 20px;
    font-size: 14.5px;
}

.button:hover {
    background: var(--bg);
    border-color: var(--text);
    color: var(--text);
}

.button--primary,
.button--primary:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}

.button--primary:hover {
    opacity: 0.85;
}

.button--sponsor,
.button--sponsor:hover {
    background: var(--bg);
    color: var(--text);
    filter: none;
}

.sponsor-link {
    background: transparent;
    color: var(--text-muted) !important;
    border: 1px solid var(--outline-variant);
    padding: 6px 13px;
    border-radius: var(--radius-sm);
    font-weight: 500;
}

.sponsor-link:hover {
    border-color: var(--text);
    color: var(--text) !important;
    filter: none;
}

.heart,
:root[data-theme="dark"] .heart {
    color: var(--text);
}

/* Search: a bordered field rather than a tonal pill. */
.search input {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
    border-radius: var(--radius-sm);
    height: 40px;
}

.search input:focus {
    outline: none;
    border-color: var(--text);
}

.search-results {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
    border-radius: var(--radius-sm);
}

.search-results a:hover,
.search-results a.is-active {
    background: var(--surface-container);
    color: var(--text);
}

.search-hint {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
}

.theme-toggle {
    border: 1px solid var(--outline-variant);
    background: var(--bg);
    border-radius: var(--radius-sm);
    width: 36px;
    height: 36px;
}

.theme-toggle:hover {
    background: var(--bg);
    border-color: var(--text);
}

/* Sidebar: the current page is marked with weight and a rule, not a coloured pill. */
.nav-link,
.nav-home {
    border-radius: var(--radius-sm);
}

.nav-link:hover,
.nav-home:hover,
.nav-section summary:hover {
    background: var(--surface-container);
}

.nav-link.is-active,
.nav-home.is-active {
    background: var(--surface-container);
    color: var(--text);
    font-weight: 600;
}

.nav-icon {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
    color: var(--text);
}

.nav-section summary {
    border-radius: var(--radius-sm);
}

/* Prose */
.eyebrow {
    color: var(--text-muted);
}

.content blockquote {
    background: var(--bg);
    border: 1px solid var(--outline-variant);
    border-radius: var(--radius-sm);
}

.content blockquote::before {
    color: var(--text);
}

:not(pre) > code {
    background: var(--surface-container);
    border: 1px solid var(--outline-variant);
}

.content th {
    background: var(--surface-container-low);
}

.count {
    background: var(--surface-container);
    color: var(--text-muted);
}

.page-nav-label {
    color: var(--text-muted);
    font-weight: 500;
}

.hero-art img,
.content img,
.chart-card-art,
.marquee img {
    background: #ffffff;
}

.stat strong {
    letter-spacing: -0.03em;
}

/* Charts are white-background images; on a black page they need a border to sit on, not to float. */
.marquee img,
.hero-art img {
    border: 1px solid var(--outline-variant);
}

/* ---------- code contrast ---------- */

/*
  On the dark theme the page is pure black, so a near-black code block dissolves into it. The block
  is lifted a few steps and given a border, which is what makes it read as a distinct object rather
  than as a region where the text happens to be monospaced.
*/
.code-block pre {
    border: 1px solid var(--code-border);
}

.code-block code {
    font-size: 13.5px;
    letter-spacing: 0.002em;
}

.line::before {
    color: var(--code-gutter);
}

/* ---------- marquee labels ---------- */

.marquee a {
    position: relative;
    display: block;
    flex: 0 0 auto;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.marquee img {
    border-radius: 0;
    border: 0;
}

/*
  Every thumbnail says what it is. A strip of unlabelled charts is decoration; the same strip with
  names is a contents page, and the reader can tell whether the library draws the thing they came
  looking for.
*/
.marquee-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 16px 12px 8px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0));
    color: #ffffff;
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-align: left;
}

.marquee a {
    border: 1px solid var(--outline-variant);
}

/* ---------- motion ---------- */

/*
  Sections rise into place the first time they are scrolled to. The transform is small and the
  duration short: enough to make the page feel assembled rather than dumped, not so much that a
  reader waits for it. Everything below is disabled outright under prefers-reduced-motion.
*/
/*
  The hidden state is applied by script, never by this stylesheet, and only to sections that are
  below the fold when the page loads. Authoring `opacity: 0` as the resting state means any failure
  — a script error, an old browser, a blocked file — leaves the page blank rather than unanimated.
  This way the worst case is that nothing moves.
*/
.reveal.is-pending {
    opacity: 0;
    transform: translateY(14px);
}

.reveal {
    transition: opacity 520ms cubic-bezier(0.22, 1, 0.36, 1),
                transform 520ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Staggered so a grid arrives as a sequence rather than as one block. */
.cards .card,
.chart-grid .chart-card,
.platforms .platform,
.stats .stat {
    transition: opacity 420ms ease, transform 420ms cubic-bezier(0.22, 1, 0.36, 1),
                border-color 160ms ease, background 160ms ease;
}

.hero-text > * {
    animation: rise 620ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.hero-text > *:nth-child(1) { animation-delay: 40ms; }
.hero-text > *:nth-child(2) { animation-delay: 90ms; }
.hero-text > *:nth-child(3) { animation-delay: 150ms; }
.hero-text > *:nth-child(4) { animation-delay: 210ms; }
.hero-text > *:nth-child(5) { animation-delay: 270ms; }

.hero-art {
    animation: rise 720ms cubic-bezier(0.22, 1, 0.36, 1) 180ms both;
}

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

.chart-card-art img {
    transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
}

.chart-card:hover .chart-card-art img {
    transform: scale(1.04);
}

.button,
.nav-link,
.nav-home,
.search input,
.theme-toggle {
    transition: background 160ms ease, border-color 160ms ease, color 160ms ease,
                opacity 160ms ease, transform 160ms ease;
}

.button:active {
    transform: scale(0.98);
}

@media (prefers-reduced-motion: reduce) {
    .reveal,
    .reveal.is-pending,
    .hero-text > *,
    .hero-art {
        opacity: 1 !important;
        transform: none !important;
        animation: none !important;
        transition: none !important;
    }

    .chart-card:hover .chart-card-art img {
        transform: none;
    }
}

/* ---------- gradients and card motion ---------- */

/*
  A gradient edge that appears on hover, drawn as a masked pseudo-element rather than a border
  image: `border-image` cannot follow a rounded corner, and a gradient background under a padded
  child would sit behind the text. Masking a one-pixel frame out of a gradient gives a real gradient
  border on a rounded box, with the card's own background untouched.
*/
.card,
.chart-card,
.stat,
.platform,
.page-nav-card {
    position: relative;
    isolation: isolate;
    background:
        linear-gradient(180deg,
            color-mix(in srgb, var(--text) 3%, var(--bg)) 0%,
            var(--bg) 60%);
    border: 1px solid var(--outline-variant);
    transition:
        transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
        border-color 200ms ease,
        opacity 380ms ease;
}

.card::before,
.chart-card::before,
.stat::before,
.platform::before,
.page-nav-card::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--text) 55%, transparent),
        color-mix(in srgb, var(--text) 8%, transparent) 45%,
        color-mix(in srgb, var(--text) 40%, transparent));
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    opacity: 0;
    transition: opacity 260ms ease;
}

.card:hover,
.chart-card:hover,
.stat:hover,
.platform:hover,
.page-nav-card:hover {
    transform: translateY(-2px);
    border-color: transparent;
}

.card:hover::before,
.chart-card:hover::before,
.stat:hover::before,
.platform:hover::before,
.page-nav-card:hover::before {
    opacity: 1;
}

/* A wash behind the hero, so the page opens with depth instead of a flat field of black. */
.hero {
    position: relative;
}

.hero::before {
    content: "";
    position: absolute;
    inset: -10% -20% 20% -20%;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(60% 55% at 20% 25%,
            color-mix(in srgb, var(--text) 9%, transparent) 0%,
            transparent 70%);
}

/*
  Cards inside a section that has not arrived yet start low and faded, then land one after another.
  The delay is per position rather than per card, so a grid reads as a sequence and the eye is led
  across it instead of being handed eight things at once.
*/
.reveal.is-pending .card,
.reveal.is-pending .chart-card,
.reveal.is-pending .stat,
.reveal.is-pending .platform {
    opacity: 0;
    transform: translateY(10px);
}

.cards .card:nth-child(1), .chart-grid .chart-card:nth-child(1) { transition-delay: 0ms; }
.cards .card:nth-child(2), .chart-grid .chart-card:nth-child(2) { transition-delay: 45ms; }
.cards .card:nth-child(3), .chart-grid .chart-card:nth-child(3) { transition-delay: 90ms; }
.cards .card:nth-child(4), .chart-grid .chart-card:nth-child(4) { transition-delay: 135ms; }
.cards .card:nth-child(5), .chart-grid .chart-card:nth-child(5) { transition-delay: 180ms; }
.cards .card:nth-child(6), .chart-grid .chart-card:nth-child(6) { transition-delay: 225ms; }
.cards .card:nth-child(7), .chart-grid .chart-card:nth-child(7) { transition-delay: 270ms; }
.cards .card:nth-child(8), .chart-grid .chart-card:nth-child(8) { transition-delay: 315ms; }

/* Hover must never wait for an entrance delay. */
.card:hover,
.chart-card:hover,
.stat:hover,
.platform:hover {
    transition-delay: 0ms;
}

@media (prefers-reduced-motion: reduce) {
    .card,
    .chart-card,
    .stat,
    .platform,
    .reveal.is-pending .card,
    .reveal.is-pending .chart-card,
    .reveal.is-pending .stat,
    .reveal.is-pending .platform {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* ---------- code window ---------- */

.code-block {
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--code-border);
    background: var(--code-bg);
}

.code-chrome {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 12px;
    height: 38px;
    background: var(--code-bg-header);
    border-bottom: 1px solid var(--code-border);
}

/*
  The three lights are the one place colour is allowed back in. They are not decoration borrowed for
  its own sake: they are the universally understood mark of "this is a window with code in it", which
  is exactly what the block is.
*/
.code-lights {
    display: inline-flex;
    gap: 7px;
}

.code-lights i {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: #ff5f57;
}

.code-lights i:nth-child(2) {
    background: #febc2e;
}

.code-lights i:nth-child(3) {
    background: #28c840;
}

.code-lang {
    flex: 1;
    text-align: center;
    font-size: 12px;
    font-weight: 500;
    color: var(--code-chrome-text);
    letter-spacing: 0.02em;
}

/* The copy button lives in the title bar now, so it no longer floats over the first line. */
.code-block .copy {
    position: static;
    opacity: 1;
    padding: 3px 11px;
    font-size: 11.5px;
    border-radius: 6px;
    border: 1px solid var(--code-border);
    background: transparent;
    color: var(--code-chrome-text);
}

.code-block .copy:hover {
    color: var(--code-text);
    border-color: var(--code-gutter);
}

.code-block pre {
    border: 0;
    border-radius: 0;
    background: var(--code-bg);
}

.hero-code .code-block,
.install .code-block {
    box-shadow: var(--shadow);
}


/* ---------- inline code inside cards ---------- */

/*
  Cards are already a box; a second box around a type name inside them reads as clutter and pushes
  the line height around. In prose the chip earns its place by separating code from the sentence —
  in a four-line card the monospace face alone is enough.
*/
.card :not(pre) > code,
.card code {
    background: none;
    border: 0;
    padding: 0;
    color: var(--text);
    font-size: 0.92em;
}

/* ---------- reveal slider ---------- */

/*
  Two layers of the same size, each clipped to one side of a divider the reader drags: code on the
  left, the charts on the right.

  The handle is a real <input type="range"> sitting invisibly over the whole box. That is what makes
  it work with a keyboard, with touch, and with a screen reader without any of it being written by
  hand — and it means that if the script fails, the box still shows both halves at the starting split
  instead of showing nothing.
*/
.compare {
    --split: 50%;
    position: relative;
    /* The banner's own ratio. Any other value crops it, and the artwork is the point. */
    aspect-ratio: 2 / 1;
    border-radius: var(--radius-sm);
    overflow: hidden;
    border: 1px solid var(--outline-variant);
    background: var(--bg);
    touch-action: pan-y;
}

.compare-layer {
    position: absolute;
    inset: 0;
}

/* Each side is clipped rather than hidden, so both are always laid out and neither reflows on drag. */
.compare-layer--code {
    clip-path: inset(0 calc(100% - var(--split)) 0 0);
}

.compare-layer--art {
    clip-path: inset(0 0 0 var(--split));
}

/*
  The window keeps its own height rather than being stretched to fill the panel.

  Stretched, six lines of Kotlin sat above a field of empty dark that read as a code block someone
  forgot to fill. At its natural size, centred, it reads as what it is: a window resting on the
  panel, the same size it would be anywhere else on the site.
*/
.compare-layer--code {
    display: flex;
    align-items: center;
    padding: 0 clamp(8px, 2.5%, 22px);
}

.compare-layer--code .code-block {
    width: 100%;
    margin: 0;
    box-shadow: var(--shadow);
}

.compare-layer--code pre {
    overflow: hidden;
}

.compare-layer--art img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* contain, not cover: the box already matches the artwork's ratio, and contain guarantees that
       stays true if either ever changes — a cropped banner is worse than a letterboxed one. */
    object-fit: contain;
    border: 0;
    border-radius: 0;
    margin: 0;
}

.compare-divider {
    position: absolute;
    z-index: 4;
    top: 0;
    bottom: 0;
    left: var(--split);
    width: 2px;
    margin-left: -1px;
    background: rgba(255, 255, 255, 0.85);
    pointer-events: none;
}

.compare-grip {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 38px;
    height: 38px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: #ffffff;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}

/* The two chevrons that say this thing moves. */
.compare-grip::before,
.compare-grip::after {
    content: "";
    position: absolute;
    top: 50%;
    width: 6px;
    height: 6px;
    border-top: 2px solid #111;
    border-right: 2px solid #111;
}

.compare-grip::before {
    left: 11px;
    transform: translateY(-50%) rotate(-135deg);
}

.compare-grip::after {
    right: 11px;
    transform: translateY(-50%) rotate(45deg);
}

/*
  The range covers the whole box so the reader can grab anywhere, not just the handle. Its own thumb
  and track are hidden because the divider above is the visible control; the input is here for the
  behaviour, not the appearance.
*/
/*
  The drag surface covers the panel, so the divider can be grabbed anywhere.

  The Copy button underneath it is unreachable as a result, which is a fair trade here: this is the
  hero's illustration, and the same snippet appears with a working Copy on the quick-start page. The
  alternative — carving the button's rectangle out of the drag surface — makes the slider dead in a
  patch of the panel for no clear reason.
*/
.compare-range {
    position: absolute;
    z-index: 2;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: ew-resize;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
}

.compare-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 44px;
    height: 100%;
    cursor: ew-resize;
}

.compare-range::-moz-range-thumb {
    width: 44px;
    height: 100%;
    border: 0;
    cursor: ew-resize;
}

/* Focus has to be visible on a control whose own appearance is hidden. */
.compare-range:focus-visible {
    outline: none;
}

.compare-range:focus-visible ~ .compare-divider .compare-grip {
    outline: 3px solid var(--primary);
    outline-offset: 3px;
}

.compare-hint {
    margin: 10px 0 0;
    font-size: 12.5px;
    color: var(--text-faint);
    text-align: center;
}

/* The ratio stays the artwork's at every width; changing it is what letterboxed the banner. */



/*
  No Copy button on the hero's snippet. The drag surface covers it, so it cannot be pressed, and a
  control that does nothing when clicked is worse than no control. The same snippet carries a working
  one on the quick-start page, which is where someone who wants to copy it will be.
*/
.compare .copy {
    display: none;
}


/* ---------- try it in the playground ---------- */

/*
  One link, two shapes. It starts as a card at the top of the page, and once the reader has scrolled
  past it, it lifts out and becomes a floating button that follows them down. It is deliberately the
  same element in both states rather than a card plus a duplicate button: two anchors to the same
  place would be announced twice by a screen reader and tabbed through twice.
*/
.try-it-slot {
    /* Holds the card's height so the page does not jump when the card lifts out of the flow. */
    min-height: 64px;
    margin: 0 0 26px;
}

.try-it {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    border-radius: var(--radius-sm);
    color: var(--text);
    position: relative;
    overflow: hidden;
    isolation: isolate;

    /* Glass: a translucent pane over whatever it covers, with a hairline lit edge. The blur is what
       makes it read as glass rather than as a flat panel with opacity. */
    background: color-mix(in srgb, var(--surface-container) 62%, transparent);
    backdrop-filter: blur(14px) saturate(150%);
    -webkit-backdrop-filter: blur(14px) saturate(150%);
    border: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
    box-shadow: inset 0 1px 0 color-mix(in srgb, var(--text) 8%, transparent);

    transition:
        border-color 200ms ease,
        transform 260ms cubic-bezier(0.22, 1, 0.36, 1),
        box-shadow 260ms ease;
}

/* The sheen: a narrow highlight that sweeps across the glass on hover, once. */
.try-it::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        linear-gradient(105deg,
            transparent 35%,
            color-mix(in srgb, var(--text) 10%, transparent) 48%,
            transparent 62%);
    transform: translateX(-120%);
    transition: transform 720ms cubic-bezier(0.22, 1, 0.36, 1);
}

.try-it:hover {
    text-decoration: none;
    border-color: color-mix(in srgb, var(--text) 28%, transparent);
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, var(--text) 14%, transparent),
        0 8px 30px color-mix(in srgb, var(--text) 7%, transparent);
}

.try-it:hover::after {
    transform: translateX(120%);
}

.try-it-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--text) 16%, transparent);
    background: color-mix(in srgb, var(--text) 5%, transparent);
    font-size: 11px;
    color: var(--text);
    transition: background 200ms ease, color 200ms ease, border-color 200ms ease;
}

.try-it:hover .try-it-icon {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}

.try-it-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.try-it-text strong {
    font-size: 15px;
    font-weight: 650;
    letter-spacing: -0.01em;
    white-space: nowrap;
}

.try-it-sub {
    font-size: 13px;
    color: var(--text-muted);
    white-space: nowrap;
}

.try-it-go {
    margin-left: auto;
    flex: none;
    color: var(--text-faint);
    font-size: 16px;
    transition: transform 240ms cubic-bezier(0.22, 1, 0.36, 1), color 200ms ease;
}

.try-it:hover .try-it-go {
    color: var(--text);
    transform: translateX(4px);
}

/* ---------- the floating state ---------- */

/*
  Once the card has scrolled away, the same element takes station at the bottom-right. It sheds its
  subtitle and its arrow on the way, because a floating button has to earn its space: the icon and
  three words say the whole thing.
*/
.try-it.is-floating {
    position: fixed;
    z-index: 40;
    right: 24px;
    bottom: 24px;
    margin: 0;
    padding: 12px 18px 12px 12px;
    border-radius: var(--radius-pill);
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, var(--text) 14%, transparent),
        0 10px 34px rgba(0, 0, 0, 0.28);
}

.try-it.is-floating .try-it-sub,
.try-it.is-floating .try-it-go {
    display: none;
}

.try-it.is-floating .try-it-text strong {
    font-size: 14px;
}

.try-it.is-floating:hover {
    transform: translateY(-2px);
}

@media (max-width: 700px) {
    .try-it.is-floating {
        right: 16px;
        bottom: 16px;
    }

    .try-it.is-floating .try-it-text strong {
        font-size: 13px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .try-it,
    .try-it::after,
    .try-it-go,
    .try-it-icon {
        transition: none;
    }

    .try-it:hover,
    .try-it.is-floating:hover {
        transform: none;
    }

    .try-it:hover::after {
        transform: translateX(-120%);
    }

    .try-it.is-floating {
        transition: none;
    }
}
