/* apps.busyowl.co — hand-written, unminified. Production builds load style_minified.css instead. */

:root {
  /* --theme_color is the brand FILL: bars, glows, dots, solid buttons. It is not legible as
     type on white (3.2:1, under the 4.5:1 AA floor), so anything set in the brand colour as
     *text* uses --theme_ink — same hue, darkened to 4.9:1. */
  --theme_color: 255, 85, 47;
  --theme_color_2: 255, 140, 60;
  --theme_ink: 204, 61, 26;

  --bg: 255, 255, 255;
  --bg_alt: 250, 250, 251;
  --bg_lift: 255, 255, 255;
  --ink: 12, 13, 16;
  --ink_hover: 42, 44, 51;     /* solid buttons darken instead of going translucent */
  --ink_soft: 92, 95, 105;     /* 6.7:1 */
  --ink_faint: 110, 113, 121;  /* 4.9:1 — was 140,143,152, which failed AA at 3.3:1 */
  --border: 12, 13, 16;        /* used at low alpha as hairlines, never as a solid */

  --radius: 10px;
  --radius_sm: 6px;
  --radius_lg: 14px;

  /* One easing, three speeds. Seven ad-hoc durations is what makes motion feel unowned. */
  --ease: cubic-bezier(.22,.61,.36,1);
  --t_fast: .15s;
  --t_mid: .28s;
  --t_slow: .5s;

  --max_w: 1200px;
  --nav_h: 64px;

  /* The waveform motif, as data URIs so it costs no requests. It lives at :root rather than on
     .lp because it is the identity, not a landing-page decoration — the docs prose bullets and
     the changelog's year dividers draw from the same two shapes. */
  --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11'%3E%3Cg fill='%23000'%3E%3Crect x='0' y='4' width='2' height='3' rx='1'/%3E%3Crect x='3.5' y='1' width='2' height='9' rx='1'/%3E%3Crect x='7' y='3' width='2' height='5' rx='1'/%3E%3C/g%3E%3C/svg%3E");
  --wave_rule: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='16'%3E%3Cg fill='%230c0d10' opacity='.16'%3E%3Crect x='0' y='6' width='1.5' height='4' rx='.75'/%3E%3Crect x='8' y='3' width='1.5' height='10' rx='.75'/%3E%3Crect x='16' y='7' width='1.5' height='2' rx='.75'/%3E%3Crect x='24' y='1' width='1.5' height='14' rx='.75'/%3E%3Crect x='32' y='5' width='1.5' height='6' rx='.75'/%3E%3Crect x='40' y='7' width='1.5' height='3' rx='.75'/%3E%3Crect x='48' y='2' width='1.5' height='12' rx='.75'/%3E%3Crect x='56' y='6' width='1.5' height='4' rx='.75'/%3E%3Crect x='64' y='4' width='1.5' height='8' rx='.75'/%3E%3Crect x='72' y='7' width='1.5' height='2' rx='.75'/%3E%3Crect x='80' y='3' width='1.5' height='10' rx='.75'/%3E%3Crect x='88' y='6' width='1.5' height='5' rx='.75'/%3E%3Crect x='96' y='1' width='1.5' height='13' rx='.75'/%3E%3Crect x='104' y='5' width='1.5' height='6' rx='.75'/%3E%3Crect x='112' y='7' width='1.5' height='3' rx='.75'/%3E%3C/g%3E%3C/svg%3E");
}

/* Keyboard focus was absent site-wide. One ring, brand-coloured, everywhere. */
:focus-visible {
  outline: 2px solid rgb(var(--theme_color));
  outline-offset: 3px;
  border-radius: var(--radius_sm);
}

/* Anchored sections must clear the fixed nav when linked to. */
[id] { scroll-margin-top: calc(var(--nav_h) + 28px); }

/* Four pages stack a SECOND sticky bar under the nav — the demos filter, the changelog filter,
   the docs section jumper, the article breadcrumb. The rule above only clears the first one, so
   a jump landed the target underneath the bar: click "Content & media" and the heading you asked
   for is the one thing hidden. Each page publishes its bar height as --bar_h and the anchors are
   offset by both. This governs in-page clicks and a #fragment typed into the address bar alike,
   which is why it belongs on [id] rather than in the scroll handler. */
.dm, .cl, .dx, .da { --bar_h: 57px; }
.dm [id], .cl [id], .dx [id], .da [id] { scroll-margin-top: calc(var(--nav_h) + var(--bar_h) + 28px); }

/* The bars are also fragment targets in their own right — the filter chips link to them, because
   a chip is a real URL and following one is a full page load that would otherwise dump you at the
   very top of the new document. A sticky element aimed at itself wants to land exactly where it
   sticks, not below its own height: at the offset above, the bar would come to rest 85px down the
   page with a slice of the hero still showing over it. */
.dm_bar[id], .cl_bar[id], .dx_bar[id] { scroll-margin-top: var(--nav_h); }

* { box-sizing: border-box; }

html { scroll-behavior: smooth; background: rgb(var(--bg)); }

body {
  margin: 0;
  padding-top: var(--nav_h); /* clears the fixed nav */
  background: rgb(var(--bg));
  color: rgb(var(--ink));
  font-family: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

/* Display type is tight and relatively light — the authority comes from space, not weight. */
h1, h2, h3 { line-height: 1.08; margin: 0 0 .5em; font-weight: 600; letter-spacing: -.032em; }
h2, h3 { text-wrap: balance; }
h1 { font-size: clamp(2.4rem, 4.4vw, 3.5rem); }
h2 { font-size: clamp(1.8rem, 3.2vw, 2.6rem); }
p { margin: 0 0 1em; color: rgb(var(--ink_soft)); text-wrap: pretty; }

/* The micro-label end of the scale. The jump between this and display type is the point. */
.eyebrow {
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: rgb(var(--ink_faint));
}
.rule { height: 1px; background: rgba(var(--border), .09); border: 0; margin: 0; }

/* Visually hidden but still announced — keeps the headline's accessible name stable while
   the visible word cycles. */
.sr_only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

main { display: block; }

.container, .site_nav_inner, .site_footer_inner, .hero_inner, .store_promo_inner,
.page_head, .feature_section_inner {
  max-width: var(--max_w);
  margin: 0 auto;
  padding-left: 24px;
  padding-right: 24px;
}

/* Buttons — small radii, hairline borders, no drop shadows. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  padding: .6em 1.1em;
  border-radius: var(--radius_sm);
  font-weight: 500;
  font-size: .875rem;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease), color var(--t_fast) var(--ease);
  white-space: nowrap;
}
.btn_primary { background: rgb(var(--ink)); color: rgb(var(--bg)); }
.btn_primary:hover { background: rgb(var(--ink_hover)); }
.btn_ghost {
  background: transparent;
  color: rgb(var(--ink));
  border-color: rgba(var(--border), .16);
}
.btn_ghost:hover { border-color: rgba(var(--border), .38); background: rgba(var(--border), .04); }
.btn_accent { background: rgb(var(--theme_ink)); color: #fff; } /* white needs the darker token to clear AA */
.btn_accent:hover { background: rgb(var(--theme_ink)); }
.btn_lg { padding: .78em 1.5em; font-size: .95rem; }

/* Nav */
.site_nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: rgba(var(--bg), .78);
  backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgba(var(--border), .08);
}
.site_nav_inner {
  display: flex;
  align-items: center;
  gap: 28px;
  height: var(--nav_h);
}
.site_logo {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  font-weight: 600;
  font-size: .975rem;
  letter-spacing: -.01em;
  flex-shrink: 0;
}
.site_logo img { height: 26px; width: 26px; object-fit: contain; border-radius: var(--radius_sm); }

/* Desktop: invisible as a box — site_links and site_cta sit inside it only so the mobile
   dropdown can wrap them in one panel (see nav.php); as flex children of site_nav_inner they
   need an explicit order to land back in their original visual sequence around site_switcher,
   which lives outside this wrapper and would otherwise render after both of them. */
.site_nav_panel { display: contents; }

.site_links { display: flex; gap: 22px; margin-right: auto; order: 1; }
.site_links a { text-decoration: none; font-weight: 500; font-size: .875rem; color: rgb(var(--ink_soft)); }
.site_links a:hover { color: rgb(var(--theme_ink)); }
/* Current section: full-ink and slightly heavier — the nav gave no sense of place before. */
.site_links a[aria-current="page"] { color: rgb(var(--ink)); font-weight: 600; }

.site_switcher { position: relative; order: 2; }
.site_switcher_toggle {
  background: transparent;
  border: 1px solid rgba(var(--border), .14);
  border-radius: var(--radius_sm);
  padding: .45em .8em;
  font: inherit;
  color: rgb(var(--ink));
  font-weight: 500;
  font-size: .85rem;
  cursor: pointer;
}
.site_switcher_toggle:hover { border-color: rgba(var(--border), .32); }
.site_switcher_menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 200px;
  background: rgb(var(--bg_lift));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  box-shadow: 0 18px 44px -12px rgba(var(--border), .28);
  padding: 6px;
  display: none;
  flex-direction: column;
}
.site_switcher.open .site_switcher_menu { display: flex; }
.site_switcher_menu a, .site_switcher_menu .disabled {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: .6em .7em;
  border-radius: var(--radius_sm);
  text-decoration: none;
  font-weight: 500;
  font-size: .875rem;
  color: rgb(var(--ink));
}
.site_switcher_menu a:hover, .site_switcher_menu a.active { background: rgba(var(--border), .07); }
.site_switcher_menu img { height: 20px; width: 20px; border-radius: 5px; object-fit: contain; flex: 0 0 auto; }
/* TEMP DISABLED (2026-08-23): ramtv & xaztube paused — greyed out, unclickable. Remove to re-enable. */
.site_switcher_menu .disabled { color: rgba(var(--ink), .4); cursor: not-allowed; }
.site_switcher_menu .disabled img { filter: grayscale(1); opacity: .5; }

.site_cta { display: flex; gap: 10px; align-items: center; order: 3; }
.site_nav_toggle { display: none; order: 4; }/* ==========================================================================
   LANDING PAGE
   --------------------------------------------------------------------------
   Composition rules for this page:
   · Spacing is a scale with deliberate CONTRAST — tight clusters, then silence.
     Uniform gaps are what make a page read as generated.
   · The four showcases each get a DIFFERENT composition (wide / bleed / dense /
     centred) so the scroll never settles into a pattern.
   · The waveform is the identity. It returns as rules, bullets and the closer
     horizon rather than appearing once in the hero and disappearing.
   ========================================================================== */
.lp {
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 104px;
  --s7: 150px;
  --s8: 210px;
}
.lp_wrap { max-width: var(--max_w); margin: 0 auto; padding: 0 var(--s3); }

/* Section tag: a number and a name. Gives every block an address. */
.tag { display: flex; align-items: center; gap: 10px; }
.tag_num {
  font-variant-numeric: tabular-nums;
  font-size: .6875rem; font-weight: 600; letter-spacing: .06em;
  color: rgb(var(--theme_ink));
}
.tag_name {
  font-size: .6875rem; font-weight: 500; letter-spacing: .09em;
  text-transform: uppercase; color: rgb(var(--ink_faint));
}
.tag::after { content: ""; flex: 1; height: 1px; background: rgba(var(--border), .1); }

/* Waveform rule — the motif as a divider, used only at major transitions. */
.wave_rule { height: 16px; background: var(--wave_rule) repeat-x center; opacity: .9; }

/* Scroll reveal. Opt-in via .js_reveal so nothing is hidden if the script dies. */
.js_reveal .rv { opacity: 0; transform: translateY(14px); transition: opacity var(--t_slow) var(--ease), transform var(--t_slow) var(--ease); }
.js_reveal .rv.in { opacity: 1; transform: none; }

/* ==========================================================================
   HERO — a built object, not a template.
   72 bars struck around a lit record, drawn in CSS so it re-colours per project.
   ========================================================================== */
.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: 92px 0 0;
}
.hero_grid {
  position: absolute; inset: 0; z-index: -2;
  background-image:
    linear-gradient(rgba(var(--border), .04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--border), .04) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(78% 68% at 50% 40%, #000 18%, transparent 76%);
  -webkit-mask-image: radial-gradient(78% 68% at 50% 40%, #000 18%, transparent 76%);
}
.hero_bloom {
  position: absolute; z-index: -1;
  left: 50%; top: 40%;
  width: 820px; height: 820px;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(var(--theme_color), .28) 0%, rgba(var(--theme_color_2), .14) 36%, transparent 66%);
  filter: blur(24px);
  pointer-events: none;
}

.hero_inner {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) auto minmax(0, .78fr);
  align-items: center;
  gap: var(--s4);
  padding-bottom: var(--s6);
}
.hero_copy { position: relative; z-index: 3; min-width: 0; }
.hero_copy .eyebrow { display: block; margin-bottom: var(--s3); }
.hero_title {
  font-size: clamp(2.6rem, 4.5vw, 3.9rem);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -.042em;
  margin: 0 0 var(--s3);
  max-width: 13ch;
  /* Three lines are reserved up front: the cycling word changes length ("Beat" vs
     "Audiobook"), flipping the headline between two and three lines, which would otherwise
     shunt the lead, buttons and stat band up and down on every cycle. */
  min-height: 3.06em;
}
/* The cycling word. Rendered in the brand colour and re-typed by the script; the caret is
   the only thing that moves when the word is at rest. */
.hero_rotate { color: rgb(var(--theme_color)); }
.hero_caret {
  display: inline-block;
  width: 3px; height: .78em;
  border-radius: 2px;
  background: rgb(var(--theme_color));
  vertical-align: -.04em;
  margin-left: .04em;
  animation: caret_blink 1.05s steps(1) infinite;
}
@keyframes caret_blink { 50% { opacity: 0; } }

.hero_lead { font-size: 1.0625rem; line-height: 1.62; max-width: 40ch; margin: 0 0 var(--s4); color: rgb(var(--ink_soft)); }
.hero_actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* The price, on the first screen. The tagline promises "from $49" and the hero never showed it,
   so the opening screen left the most-asked pre-sale question unanswered. */
.hero_anchor {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: var(--s3) 0 0;
  font-size: .8125rem;
}
.hero_anchor b { font-size: .9375rem; font-weight: 600; color: rgb(var(--ink)); }
.hero_anchor span { color: rgb(var(--ink_faint)); }

/* Right rail sits low and is tied to a rule, so it reads as a caption not a column. */
.hero_meta { position: relative; z-index: 3; justify-self: end; align-self: end; text-align: right; padding-bottom: 6px; }
.hero_meta .eyebrow { display: block; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid rgba(var(--border), .12); }
.hero_meta ul { margin: 0; padding: 0; }
.hero_meta li { list-style: none; font-size: .875rem; color: rgb(var(--ink_soft)); padding: 5px 0; }

/* --- the object ---------------------------------------------------------- */
.hero_stage { position: relative; width: 400px; height: 400px; display: grid; place-items: center; z-index: 2; }
.hero_wave { position: absolute; inset: 0; display: grid; place-items: center; }
.hero_wave .wv {
  position: absolute;
  width: 3px; height: var(--h);
  border-radius: 2px;
  background: linear-gradient(to top, rgba(var(--theme_color), .22), rgb(var(--theme_color)));
  transform: rotate(var(--a)) translateY(calc(-1 * var(--r)));
  transform-origin: 50% 100%;
  animation: wv_pulse 2.8s ease-in-out infinite;
  animation-delay: var(--d);
}
@keyframes wv_pulse {
  0%, 100% { transform: rotate(var(--a)) translateY(calc(-1 * var(--r))) scaleY(.74); opacity: .72; }
  50%      { transform: rotate(var(--a)) translateY(calc(-1 * var(--r))) scaleY(1.16); opacity: 1; }
}
.hero_rings span {
  position: absolute; left: 50%; top: 50%;
  width: 200px; height: 200px; margin: -100px 0 0 -100px;
  border: 1px solid rgba(var(--theme_color), .45);
  border-radius: 50%;
  animation: ring_out 5s cubic-bezier(.22,.61,.36,1) infinite;
}
.hero_rings span:nth-child(2) { animation-delay: 1.66s; }
.hero_rings span:nth-child(3) { animation-delay: 3.33s; }
@keyframes ring_out {
  from { transform: scale(.6); opacity: .5; }
  to   { transform: scale(2.1); opacity: 0; }
}
.hero_rings {
      position: absolute;
    top: 36px;
    background: black;
    bottom: 0;
    margin: auto;
}
.hero_disc {
  position: relative;
  top: 18px;
  width: 182px; height: 182px;
  border-radius: 50%;
  background:
    repeating-radial-gradient(circle at 50% 50%, rgba(255,255,255,.04) 0 1px, transparent 1px 5px),
    radial-gradient(circle at 34% 28%, #34353d 0%, #131419 52%, #0a0b0e 100%);
  box-shadow: 0 34px 74px -14px rgba(var(--theme_color), .34), 0 8px 26px rgba(0,0,0,.3), inset 0 0 0 1px rgba(255,255,255,.06);
  display: grid; place-items: center;
}
.hero_disc::after {
  content: ""; width: 14px; height: 14px; border-radius: 50%;
  background: rgb(var(--bg)); box-shadow: inset 0 0 0 3px rgba(var(--theme_color), .85);
}
/* inset:0 + margin:auto centres it without needing a transform — the transform slot is
   already spoken for by the spin animation, so translate(-50%,-50%) gets overwritten on the
   first keyframe and the logo drifts off-centre. */
.hero_disc img { position: absolute; inset: 0; margin: auto; width: 54px; height: 54px; object-fit: contain; }
.hero_disc_spin { animation: disc_spin 30s linear infinite; }
@keyframes disc_spin { to { transform: rotate(360deg); } }

/* Stat band closes the hero: real figures, tabular, on a hairline grid. */
.stats {
  border-top: 1px solid rgba(var(--border), .1);
  border-bottom: 1px solid rgba(var(--border), .1);
  background: rgba(var(--bg_alt), .7);
}
.stats_grid { display: grid; grid-template-columns: repeat(4, 1fr); }
.stat { padding: var(--s4) var(--s3); border-left: 1px solid rgba(var(--border), .08); }
.stat:first-child { border-left: 0; }
.stat_value {
  font-size: clamp(1.75rem, 2.6vw, 2.4rem); font-weight: 500;
  letter-spacing: -.035em; line-height: 1;
  font-variant-numeric: tabular-nums;
  margin-bottom: 8px;
}
.stat_label { font-size: .8125rem; color: rgb(var(--ink_soft)); }

/* ==========================================================================
   SHOWCASES — four compositions, deliberately unalike.
   ========================================================================== */
.sc { position: relative; }
.sc_head { margin-bottom: var(--s5); }
.sc_head .tag { margin-bottom: var(--s3); }
/* Two registers, not one. Every h2 on the page had settled at the same 2.7rem, so nothing told
   the eye which sections carry the argument and which answer a question. The showcases and the
   price keep the display size; the sections that come after the decision — how to launch it,
   what people ask, the full capability list — step down. */
.sc_head h2 { margin: 0; font-size: clamp(1.9rem, 3.1vw, 2.7rem); }
.launch .sc_head h2,
.faq .sc_head h2,
.spec_head h2 { font-size: clamp(1.6rem, 2.3vw, 2.05rem); letter-spacing: -.028em; }
.sc_lead { font-size: 1.0625rem; color: rgb(var(--ink_soft)); margin: var(--s3) 0 0; max-width: 46ch; }

/* Centred section head. Pricing, launch and FAQ all want one, and each was carrying the same four
   inline style attributes to get it — this is that rule, named once. */
.sc_head_mid { text-align: center; margin-left: auto; margin-right: auto; }
.sc_head_mid .tag { justify-content: center; }
.sc_head_mid .tag::after { display: none; }
.sc_head_mid h2 { max-width: 20ch; margin-inline: auto; }
.sc_head_mid .sc_lead { margin-inline: auto; }

/* Shared frame treatment. Shadow is directional and soft — a surface, not a card. */
/* The frame is doing the work of a product shot, so it gets a product shot's light: a warm
   brand-tinted cast under a neutral one, which lifts a dark admin capture off a white page
   instead of letting it sit on it like a pasted-in document image. Two shadows, no filters —
   the cost is one extra shadow layer. */
.shot {
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  overflow: hidden;
  background: rgb(var(--bg_alt));
  box-shadow:
    0 2px 4px rgba(var(--border), .04),
    0 24px 48px -28px rgba(var(--border), .5),
    0 48px 90px -50px rgba(var(--theme_color), .3);
  transition: box-shadow var(--t_slow) var(--ease), transform var(--t_slow) var(--ease);
}
.shot:hover {
  transform: translateY(-2px);
  box-shadow:
    0 2px 4px rgba(var(--border), .05),
    0 30px 58px -28px rgba(var(--border), .55),
    0 60px 110px -50px rgba(var(--theme_color), .42);
}
.shot_chrome {
  display: flex; align-items: center; gap: 6px;
  padding: 10px 13px;
  border-bottom: 1px solid rgba(var(--border), .07);
  background: rgba(var(--border), .012);
}
.shot_chrome i { width: 8px; height: 8px; border-radius: 50%; background: rgba(var(--border), .13); }
.shot_chrome b { margin-left: 9px; font-weight: 400; font-size: .6875rem; color: rgb(var(--ink_faint)); }
.shot img { width: 100%; height: auto; display: block; }

/* Feature list — waveform ticks instead of dots or icons. */
.f_list { list-style: none; margin: 0; padding: 0; }
.f_list li {
  position: relative;
  font-size: .875rem; color: rgb(var(--ink_soft));
  padding: 10px 0 10px 22px;
  border-bottom: 1px solid rgba(var(--border), .07);
}
.f_list li::before {
  content: "";
  position: absolute; left: 0; top: 12px;
  width: 11px; height: 11px;
  background: rgb(var(--theme_color));
  -webkit-mask: var(--tick) no-repeat center / contain;
  mask: var(--tick) no-repeat center / contain;
}
.f_list.cols { columns: 3; column-gap: var(--s4); }
.f_list.cols li { break-inside: avoid; border-bottom: 0; padding-bottom: 4px; }

/* -- 1. WIDE: headline sits over the top-left of a near-full-bleed surface -- */
.sc_wide { padding: var(--s7) 0; }
/* The head is full width so the tag's trailing rule spans the section and frames it; the
   headline and lead stay short on their own. A rule that stops halfway reads like it ran out
   rather than like a decision. */
.sc_wide .sc_head { margin-bottom: calc(var(--s5) * -1); position: relative; z-index: 2; }
.sc_wide .sc_head h2 { max-width: 15ch; }
.sc_wide .sc_lead { max-width: 42ch; }
.sc_wide_shot { margin-top: var(--s6); }
.sc_wide .f_list { margin-top: var(--s4); display: grid; grid-template-columns: repeat(5, 1fr); gap: 0 var(--s3); }
.sc_wide .f_list li { border-bottom: 0; border-top: 1px solid rgba(var(--border), .1); padding-top: 14px; }
.sc_wide .f_list li::before { top: 16px; }

/* -- 2. BLEED: text left, surface runs off the right edge of the viewport --- */
.sc_bleed { padding: var(--s7) 0; overflow: hidden; }
.sc_bleed_grid {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  gap: var(--s5);
  align-items: center;
}
.sc_bleed .sc_head { margin-bottom: var(--s4); }
.sc_bleed_shot { width: calc(100% + 50vw - (var(--max_w) / 2) + var(--s3)); }

/* -- 3. DENSE: quiet, compact, numbers-led. The rhythm break. -------------- */
.sc_dense { padding: var(--s7) 0; background: rgb(var(--bg_alt)); border-top: 1px solid rgba(var(--border), .08); border-bottom: 1px solid rgba(var(--border), .08); }
.sc_dense_grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s6); align-items: center; }
.sc_dense .sc_head { margin-bottom: var(--s4); }
.sc_dense_metrics { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s4) var(--s3); margin: var(--s5) 0 0; }
.sc_metric_v { font-size: 1.6rem; font-weight: 500; letter-spacing: -.03em; font-variant-numeric: tabular-nums; line-height: 1; }
.sc_metric_l { font-size: .8125rem; color: rgb(var(--ink_soft)); margin-top: 6px; }

/* Gateway roster. Seventeen names set as a tile wall — the section's claim is a count, so the
   proof is the things being counted, not a screenshot of one of them. Deliberately unbranded:
   seventeen payment logos is a licensing problem and a visual mess of seventeen palettes, and
   the name is what someone is actually scanning for. */
.gw {
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  padding: var(--s3);
  box-shadow: 0 30px 60px -46px rgba(var(--border), .7);
}
.gw_head {
  display: flex; align-items: baseline; gap: 9px;
  padding: 0 4px var(--s3);
  font-size: .8125rem; color: rgb(var(--ink_soft));
}
.gw_count {
  font-size: 1.5rem; font-weight: 600; letter-spacing: -.03em; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--theme_ink));
}
.gw_grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.gw_grid li {
  padding: 10px 12px;
  border: 1px solid rgba(var(--border), .09);
  border-radius: var(--radius_sm);
  background: rgba(var(--border), .015);
  font-size: .8125rem; font-weight: 500;
  color: rgb(var(--ink_soft));
  text-align: center;
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease), background var(--t_fast) var(--ease);
}
.gw_grid li:hover { color: rgb(var(--ink)); border-color: rgba(var(--theme_color), .4); background: rgba(var(--theme_color), .05); }

/* -- 4. CENTRED: symmetrical breath before the spec wall ------------------- */
.sc_centre { padding: var(--s7) 0 var(--s6); text-align: center; }
.sc_centre .sc_head { margin-left: auto; margin-right: auto; }
.sc_centre .tag { justify-content: center; }
.sc_centre .tag::after { display: none; }
.sc_centre h2 { max-width: 18ch; margin-inline: auto; }
.sc_centre .sc_lead { margin-inline: auto; }
.sc_centre .f_list { margin-top: var(--s5); text-align: left; max-width: 900px; margin-inline: auto; }

/* ==========================================================================
   SPEC INDEX — the full capability list, numbered and navigable.
   ========================================================================== */
.spec { padding: var(--s6) 0 var(--s5); }
.spec_head { margin-bottom: var(--s4); display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s4); align-items: end; }
.spec_head h2 { margin: 0; max-width: 20ch; }
.spec_head p { margin: 8px 0 0; max-width: 46ch; }
.spec_count { text-align: right; }
.spec_count b { display: block; font-size: 2.6rem; font-weight: 500; letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums; }
.spec_count span { font-size: .8125rem; color: rgb(var(--ink_soft)); }

.spec_group { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: var(--s5); padding: var(--s5) 0; border-top: 1px solid rgba(var(--border), .1); }
.spec_group_label { align-self: start; }
.spec_group_label .tag { margin-bottom: 12px; }
.spec_group_label .tag::after { display: none; }
.spec_group_label h3 { font-size: 1.0625rem; font-weight: 600; margin: 0 0 6px; letter-spacing: -.015em; }
.spec_group_label p { font-size: .8125rem; margin: 0; color: rgb(var(--ink_faint)); }

.spec_group_count { font-size: .75rem; color: rgb(var(--ink_faint)); margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(var(--border), .1); font-variant-numeric: tabular-nums; }
/* 260px, not 220: the group is full width now that it is an accordion row, and at 220 the track
   fitted five columns and squeezed a two-line title into four.

   The hairline grid is drawn by a ring on each CELL rather than by a tinted container showing
   through 1px gaps. Under the old technique a group whose item count did not fill its last row
   left the container's grey visible as two or three empty blocks, which read as cells that had
   failed to render. With the ring, the container is plain and an unfilled row is simply page. */
.spec_items { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1px; background: transparent; border-radius: var(--radius_sm); }
.spec_item {
  padding: 16px 18px;
  background: rgb(var(--bg));
  box-shadow: 0 0 0 1px rgba(var(--border), .07);
  transition: background var(--t_fast) var(--ease), box-shadow var(--t_fast) var(--ease);
}
.spec_item:hover { background: rgb(var(--bg_alt)); box-shadow: 0 0 0 1px rgba(var(--border), .07), inset 2px 0 0 rgb(var(--theme_color)); }
/* The icon is a locator, not an illustration: it sits inline at the size of the text it labels
   so a reader scanning 57 cells can re-find one by shape, without the grid turning into a tray
   of icon cards. Brand colour, because it is the only mark in the cell that can carry it. */
.spec_item_title { display: flex; align-items: center; gap: 8px; font-size: .875rem; font-weight: 600; margin-bottom: 5px; letter-spacing: -.005em; }
.spec_item_i { flex: 0 0 auto; font-size: 1.05rem; line-height: 1; color: rgb(var(--theme_ink)); opacity: .82; transition: opacity var(--t_fast) var(--ease); }
.spec_item:hover .spec_item_i { opacity: 1; }
.spec_item_text { font-size: .8125rem; color: rgb(var(--ink_soft)); line-height: 1.55; }

/* The spec wall is a set of native <details>, collapsed past the first group so 51 capabilities
   no longer dominate the page on load. No JavaScript: it degrades to a plain expand/collapse
   either way. Marker suppression mirrors .da_tree_g in the docs sidebar.

   The row is full width, NOT the old 210px-label + content grid. Under that grid a closed group
   left the content column empty, so folding a group away produced ~200px of blank white that
   read as a section which had failed to load. As a row, closed is genuinely compact and the
   items get the whole width when open. */
.spec_group { display: block; padding: 0; border-top: 1px solid rgba(var(--border), .1); }
/* The summary row stays pinned while its items scroll past underneath, so the open group's
   heading (and the way out — click it again to collapse) is never scrolled out of reach on a
   57-item wall. It needs its own opaque backdrop for that: without one the item grid showed
   straight through it, which is what a sticky element with no background always does. */
.spec_group_label {
  position: sticky; top: var(--nav_h); z-index: 5;
  display: flex; align-items: center; gap: var(--s3);
  padding: 22px 4px;
  cursor: pointer; list-style: none;
  background: rgba(var(--bg), .92);
  backdrop-filter: blur(10px) saturate(160%);
  border-bottom: 1px solid transparent;
  transition: background var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.spec_group[open] .spec_group_label { border-bottom-color: rgba(var(--border), .1); }
.spec_group_label::-webkit-details-marker { display: none; }
.spec_group_label::marker { content: ""; }
.spec_group_label:hover { background: rgba(var(--border), .022); }
.spec_g_num {
  flex: 0 0 auto;
  font-size: .6875rem; font-weight: 600; letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--theme_ink));
}
.spec_g_txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; margin-right: auto; }
.spec_g_title { font-size: 1.0625rem; font-weight: 600; letter-spacing: -.015em; }
.spec_g_sub { font-size: .8125rem; color: rgb(var(--ink_faint)); }
/* Tabular count in a pill: the same figure in the same place on every row, so the eye can read
   down the column and see how big each group is without opening any of them. */
.spec_g_count {
  flex: 0 0 auto;
  min-width: 30px; padding: 3px 9px;
  border: 1px solid rgba(var(--border), .12); border-radius: 999px;
  font-size: .75rem; font-variant-numeric: tabular-nums; text-align: center;
  color: rgb(var(--ink_faint));
}
.spec_g_chev { flex: 0 0 auto; font-size: 1.15rem; color: rgb(var(--ink_faint)); transition: transform var(--t_mid) var(--ease); }
.spec_group[open] .spec_g_chev { transform: rotate(180deg); color: rgb(var(--theme_ink)); }
.spec_group .spec_items { margin: 0 0 var(--s5); }

/* ==========================================================================
   PRICING — the number the research found missing entirely. $__script and
   $__bundles are real rows from _pluger_plugins ( endpoint_landing.php ), not
   hand-typed figures, so the bundle contents can never drift out of sync with
   what the store actually charges for.
   ========================================================================== */
.pricing { padding: var(--s7) 0; background: rgb(var(--bg_alt)); border-top: 1px solid rgba(var(--border), .08); border-bottom: 1px solid rgba(var(--border), .08); }
/* align-items:start, not stretch: stretching made the short base card as tall as a sixteen-item
   Mega Bundle and left a void the height of a screen inside it. Each card is now its own height
   and the featured one is lifted instead. */
.price_grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4); margin-top: var(--s5); align-items: start; }
.price_card {
  position: relative;
  display: flex; flex-direction: column;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  padding: var(--s4);
  box-shadow: 0 30px 60px -46px rgba(var(--border), .7);
  transition: border-color var(--t_mid) var(--ease), box-shadow var(--t_mid) var(--ease), transform var(--t_mid) var(--ease);
}
.price_card:hover { border-color: rgba(var(--border), .2); box-shadow: 0 40px 80px -44px rgba(var(--border), .8); }
/* The one tier the page recommends. Brand hairline + a lift, no colour fill: a filled card would
   out-shout the hero, and the argument here is the savings summary, not the decoration. */
.price_card.is_feature {
  border-color: rgba(var(--theme_color), .55);
  box-shadow: 0 40px 80px -40px rgba(var(--theme_color), .45), 0 0 0 1px rgba(var(--theme_color), .12);
}
.price_card.is_feature:hover { transform: translateY(-3px); border-color: rgba(var(--theme_color), .8); }
.price_badge {
  position: absolute; top: 0; left: var(--s4); transform: translateY(-50%);
  background: rgb(var(--theme_color)); color: #fff;
  font-size: .625rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px;
  box-shadow: 0 6px 16px -6px rgba(var(--theme_color), .8);
}
.price_card_head { padding-bottom: var(--s3); margin-bottom: var(--s3); border-bottom: 1px solid rgba(var(--border), .09); }
.price_name { font-size: .8125rem; font-weight: 600; letter-spacing: -.005em; color: rgb(var(--ink_soft)); margin-bottom: 10px; }
.price_value {
  font-size: 2.9rem; font-weight: 500; letter-spacing: -.04em; line-height: 1;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
}
.price_value span { font-size: .75rem; font-weight: 500; color: rgb(var(--ink_faint)); letter-spacing: 0; }
.price_was { margin-top: 9px; font-size: .8125rem; color: rgb(var(--ink_faint)); }
.price_was s { text-decoration-color: rgba(var(--border), .4); }
.price_tagline { font-size: .875rem; font-weight: 500; color: rgb(var(--ink)); margin: 0 0 var(--s2); }
/* Base tier's contents, built on the same row rhythm as a bundle's item list so the three
   columns read as one table rather than three unrelated cards. */
.price_inc { list-style: none; margin: 0 0 var(--s3); padding: 0; flex: 1 1 auto; }
.price_inc li {
  position: relative;
  font-size: .8125rem; color: rgb(var(--ink_soft));
  padding: 7px 0 7px 20px;
  border-bottom: 1px solid rgba(var(--border), .07);
}
.price_inc li:last-child { border-bottom: 0; }
.price_inc li::before {
  content: ""; position: absolute; left: 0; top: 11px;
  width: 9px; height: 9px;
  background: rgb(var(--theme_color));
  -webkit-mask: var(--tick) no-repeat center / contain;
  mask: var(--tick) no-repeat center / contain;
}
.price_btn { margin-top: auto; align-self: stretch; justify-content: center; }
.price_foot { text-align: center; font-size: .8125rem; color: rgb(var(--ink_faint)); max-width: 60ch; margin: var(--s4) auto 0; }
/* Restates the terms where the decision is made. Hairline row, not a fourth card. */
.price_assure {
  list-style: none; margin: var(--s5) 0 0; padding: var(--s3) 0 0;
  border-top: 1px solid rgba(var(--border), .1);
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s2) var(--s5);
}
.price_assure li { display: flex; align-items: center; gap: 8px; font-size: .8125rem; color: rgb(var(--ink_soft)); }
.price_assure .mdi { color: rgb(var(--theme_ink)); font-size: 1.05rem; }

/* Bundle contents: this markup comes straight from the object layer ( pl_plugin "detail",
   for_store shape ) — an item list ( ._i / ._n / ._p / ._cc / ._l ) followed by a pricing summary
   ( ._ps / ._r / ._k / ._v, with ._final as the highlighted last row ). Styled to fit a card
   rather than restyled from scratch, so it stays correct if the admin edits a bundle's contents. */
.price_detail { flex: 1 1 auto; margin-bottom: var(--s3); font-size: .8125rem; }
.price_detail ._isb { display: block; font-weight: 600; font-size: .8125rem; color: rgb(var(--ink)); margin-bottom: 6px; }
.price_detail ._i { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid rgba(var(--border), .07); }
.price_detail ._i:last-child { border-bottom: 0; }
.price_detail ._n { color: rgb(var(--ink_soft)); flex: 1 1 auto; min-width: 0; }
.price_detail ._p { flex: 0 0 auto; font-weight: 600; font-variant-numeric: tabular-nums; margin-left: 10px; }
.price_detail ._cc { flex: 0 0 auto; font-size: .75rem; color: rgb(var(--ink_faint)); text-decoration: line-through; text-decoration-color: rgba(var(--border), .35); }
.price_detail ._cc ._l { display: none; } /* "On Envato" repeated per row is noise once the card states it once, up top */
.price_detail ._ps { margin-top: var(--s2); padding-top: var(--s2); border-top: 1px solid rgba(var(--border), .1); }
.price_detail ._r { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 4px 0; font-size: .75rem; color: rgb(var(--ink_faint)); }
.price_detail ._r ._v { font-variant-numeric: tabular-nums; }
.price_detail ._r._ccc { color: rgb(var(--theme_ink)); }
.price_detail ._r._final { font-size: .875rem; font-weight: 600; color: rgb(var(--ink)); padding-top: 9px; margin-top: 5px; border-top: 1px dashed rgba(var(--border), .18); }

/* The overflow rows. A Mega Bundle lists sixteen items; printed in full the card became a spec
   sheet, so the tail folds away and the savings summary — the part that actually argues for the
   bundle — stays on screen either way. */
.price_more { margin: 0; }
.price_more > summary {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  padding: 9px 0; cursor: pointer; list-style: none;
  font-size: .75rem; font-weight: 500;
  color: rgb(var(--theme_ink));
  border-bottom: 1px solid rgba(var(--border), .07);
}
.price_more > summary::-webkit-details-marker { display: none; }
.price_more > summary::marker { content: ""; }
.price_more > summary .mdi { font-size: 1rem; transition: transform var(--t_fast) var(--ease); }
.price_more[open] > summary .mdi { transform: rotate(180deg); }
.price_more[open] > summary { color: rgb(var(--ink_faint)); }
/* Once open the toggle sits beneath the rows it revealed, so the label has to change with it.
   Two spans swapped on [open] rather than one relabelled by a ::after — a generated-content
   label has to be positioned out of the flex flow to sit where the real one was, and an absolute
   box with no offsets resolves against the wrong containing block the moment anything around it
   moves. Swapping real elements keeps the summary a plain flex row in both states. */
.price_more_b { display: none; }
.price_more[open] > summary .price_more_a { display: none; }
.price_more[open] > summary .price_more_b { display: inline; }

/* ==========================================================================
   LAUNCH — "what it takes to go live".
   Three numbered steps and one callout. This was a two-column bullet list and
   read as a documentation page, which is exactly the impression the section
   exists to undo: its job is to make launching look like a short, known
   sequence rather than a list of prerequisites to worry about.
   ========================================================================== */
/* --------------------------------------------------------------------------
   Rhythm, restated.

   Every section had drifted to the same var(--s7) top and bottom, which is
   exactly what the note at the top of this file warns against: uniform gaps are
   what make a page read as generated. The scroll now has two registers —

     the ARGUMENT   ( showcases, pricing )  keeps --s7. These are the moments
                    the page is making a case, and they are allowed silence.
     the ANSWERS    ( launch, faq, spec )   tighten to --s6. Someone reading
                    this far has decided to look into it and is now scanning;
                    generous air here reads as padding, not as confidence.

   The spec wall also loses its --s8 tail. Collapsed, it left 210px of empty
   white before the closer — a full blank viewport, which read as the page
   having ended early.
   -------------------------------------------------------------------------- */
.launch { padding: var(--s6) 0; }
.launch_steps {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4);
  counter-reset: launch;
}
.launch_step {
  position: relative;
  padding: var(--s4) var(--s3) var(--s4) 0;
  border-top: 2px solid rgba(var(--theme_color), .28);
}
.launch_n {
  font-size: .75rem; font-weight: 600; letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--theme_ink));
  margin-bottom: 14px;
}
.launch_step h3 { font-size: 1.125rem; margin: 0 0 8px; letter-spacing: -.018em; }
.launch_step p { margin: 0; font-size: .875rem; line-height: 1.6; }

/* The licensing rule, stated before the sale. It is the biggest source of post-purchase friction
   in the support corpus and the rule itself is reasonable — it only breeds resentment when
   someone meets it for the first time after paying. */
.launch_note {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s3);
  align-items: start;
  margin-top: var(--s5);
  padding: var(--s4);
  background: rgb(var(--bg_alt));
  border: 1px solid rgba(var(--border), .1);
  border-left: 2px solid rgb(var(--theme_color));
  border-radius: var(--radius);
}
.launch_note_i {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(var(--theme_color), .1);
  color: rgb(var(--theme_ink));
}
.launch_note_i .mdi { font-size: 1.1rem; }
.launch_note h3 { font-size: .9375rem; margin: 6px 0 8px; letter-spacing: -.012em; }
.launch_note p { font-size: .875rem; line-height: 1.65; margin: 0 0 .7em; max-width: 74ch; }
.launch_note p:last-child { margin-bottom: 0; }
.launch_note_help { color: rgb(var(--ink_faint)); }

/* ==========================================================================
   FAQ — native <details>/<summary>, same pattern as the spec wall above.
   ========================================================================== */
/* Tinted ground. Without it, launch → faq → spec is 3,700px of unbroken white running into a
   black closer; the FAQ tint splits that run and gives the closer something to arrive from. */
.faq { padding: var(--s6) 0; background: rgb(var(--bg_alt)); border-top: 1px solid rgba(var(--border), .08); border-bottom: 1px solid rgba(var(--border), .08); }
.faq_list { max-width: 760px; margin: var(--s4) auto 0; }
.faq_item { border-top: 1px solid rgba(var(--border), .1); }
.faq_item:last-child { border-bottom: 1px solid rgba(var(--border), .1); }
.faq_item summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: 18px 2px;
  cursor: pointer;
  list-style: none;
  font-size: .9375rem; font-weight: 600; letter-spacing: -.008em;
  color: rgb(var(--ink));
}
.faq_item summary::-webkit-details-marker { display: none; }
.faq_item summary::marker { content: ""; }
/* A plus/minus drawn from two rules rather than the waveform tick used elsewhere. The tick is the
   site's bullet motif and reads as decoration here; an accordion has to say "this opens", and it
   is the one place on the page where the brand mark would be actively misleading. */
.faq_item summary::after,
.faq_item summary::before {
  content: "";
  position: absolute; right: 4px; top: 50%;
  width: 11px; height: 1.5px; border-radius: 1px;
  background: rgb(var(--ink_faint));
  transition: transform var(--t_mid) var(--ease), background var(--t_fast) var(--ease);
}
.faq_item summary::after { transform: translateY(-50%); }
.faq_item summary::before { transform: translateY(-50%) rotate(90deg); }
.faq_item[open] summary::before { transform: translateY(-50%) rotate(0deg); }
.faq_item summary { position: relative; padding-right: 34px; }
.faq_item summary:hover { color: rgb(var(--theme_ink)); }
.faq_item summary:hover::after, .faq_item summary:hover::before,
.faq_item[open] summary::after, .faq_item[open] summary::before { background: rgb(var(--theme_ink)); }
.faq_item > div { padding: 0 34px var(--s3) 2px; font-size: .875rem; line-height: 1.65; color: rgb(var(--ink_soft)); max-width: 68ch; }

/* ==========================================================================
   THE MOSAIC — "See it running".
   A varied-size tile wall rather than a uniform grid or a marquee: the work is
   large enough to actually read, and the changing tile proportions give the
   section rhythm on their own. Tiles are sized from a 12-column pattern that
   tiles exactly, so there are no ragged gaps.
   ========================================================================== */
/* Tighter on top than below: this follows the stat band, which is already a hard rule across the
   page, so a second full --s7 gap above it reads as a gap in the page rather than as air. */
.mosaic_sec { padding: var(--s6) 0 var(--s7); }
.mosaic_wrap { width: min(1360px, 100% - 48px); margin: 0 auto; }

.mosaic_head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s4); margin-bottom: var(--s5);
}
.mosaic_head h2 { margin: 0; }
.mosaic_head_meta { display: flex; align-items: baseline; gap: 10px; margin-top: 12px; }
.mosaic_head_count { font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; color: rgb(var(--theme_ink)); }
.mosaic_head p { margin: 0; }

/* Every column is exactly the same height; the variation is in how each column is DIVIDED.
   A 12-row grid with 1fr rows does this precisely: each column's row-spans sum to 12, so all
   four bottoms land on the same line while the tiles inside differ. Explicit placement (rather
   than auto-flow) is what makes that guarantee possible — and it lets the last tile span two
   columns. Heights only ever vary vertically: the sources are tall page captures, so a wide
   tile would crop them to a letterbox strip. */
/* 1320, down from 1520. This is the tallest block on the page and it lands at roughly 7% of the
   scroll; at the old height the proof section pushed the entire argument below it. The tiles are
   still large enough to read as real sites, which is the only thing this section has to do. */
.mosaic {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-template-rows: repeat(12, 1fr);
  gap: 14px;
  height: 1320px;
}

.tile {
  position: relative;
  display: block;
  grid-column: var(--c) / span var(--cs, 1);
  grid-row: var(--r) / span var(--rs);
  border-radius: var(--radius_lg);
  overflow: hidden;
  background: #0d0e12;
  border: 1px solid rgba(var(--border), .09);
  text-decoration: none;
  isolation: isolate;
  transition: box-shadow var(--t_slow) var(--ease), border-color var(--t_mid) var(--ease);
}
.tile:hover { border-color: rgba(var(--theme_color), .5); box-shadow: 0 30px 60px -30px rgba(var(--border), .65); }
.tile img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: top;
  transition: transform var(--t_slow) var(--ease);
}
.tile:hover img { transform: scale(1.04); }

/* Caption: always readable, but the detail only arrives on hover. */
.tile_cap {
  position: absolute; inset: auto 0 0 0;
  padding: 46px 18px 16px;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 12px;
  color: #fff;
  background: linear-gradient(to top, rgba(0,0,0,.94) 18%, rgba(0,0,0,.55) 58%, transparent);
}
.tile_name { font-size: 1rem; font-weight: 600; letter-spacing: -.012em; }
.tile_tool {
  font-size: .75rem; color: rgba(255,255,255,.62);
  margin-top: 3px;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height var(--t_mid) var(--ease), opacity var(--t_mid) var(--ease);
}
.tile:hover .tile_tool { max-height: 2em; opacity: 1; }
.tile_go {
  flex-shrink: 0;
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgb(var(--theme_color)); color: #fff;
  font-size: .85rem; line-height: 1;
  opacity: 0; transform: translateY(6px) scale(.9);
  transition: opacity var(--t_mid) var(--ease), transform var(--t_mid) var(--ease);
}
.tile:hover .tile_go { opacity: 1; transform: none; }

.mosaic_foot { margin-top: var(--s5); text-align: center; }

/* ==========================================================================
   CLOSER — plain by intent. The reel above is the loud moment; this is just a
   clear, well-set instruction to act.
   ========================================================================== */
.closer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  text-align: center;
  padding: var(--s8) var(--s3);
  background: #0b0c10;
  color: #fff;
}
/* The hero's bloom, reprised. The two ends of a thirteen-screen scroll should rhyme, and this is
   the only dark surface on the page — after eleven pale sections the change of ground is what
   makes the last screen read as an arrival rather than as one more block. */
.closer_bloom {
  position: absolute; z-index: -1;
  left: 50%; top: 46%;
  width: 900px; height: 900px;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(var(--theme_color), .42) 0%, rgba(var(--theme_color_2), .18) 38%, transparent 68%);
  filter: blur(30px);
  pointer-events: none;
}
.closer_inner { position: relative; z-index: 1; max-width: 700px; margin: 0 auto; }
.closer .eyebrow { display: block; margin-bottom: var(--s3); color: rgba(255, 255, 255, .55); }
.closer h2 { font-size: clamp(2.1rem, 4.2vw, 3.1rem); margin: 0 auto var(--s3); max-width: 18ch; color: #fff; }
.closer p { max-width: 52ch; margin: 0 auto var(--s4); color: rgba(255, 255, 255, .74); }
.closer_actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
/* On dark ground the filled button has to be the light one — ink on near-black disappears — and
   the ghost border comes off white rather than off the ink hairline token. Same inversion the
   /demos hero already makes over the brand colour. */
.closer .btn_primary { background: #fff; color: rgb(var(--ink)); }
.closer .btn_primary:hover { background: rgba(255, 255, 255, .87); }
.closer .btn_ghost { color: #fff; border-color: rgba(255, 255, 255, .3); }
.closer .btn_ghost:hover { color: #fff; border-color: rgba(255, 255, 255, .8); background: rgba(255, 255, 255, .1); }
.closer_assure {
  list-style: none; margin: var(--s5) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px var(--s3);
}
.closer_assure li {
  display: flex; align-items: center; gap: 7px;
  font-size: .8125rem; color: rgba(255, 255, 255, .62);
}
.closer_assure li::before {
  content: ""; width: 9px; height: 9px; flex: 0 0 auto;
  background: rgb(var(--theme_color));
  -webkit-mask: var(--tick) no-repeat center / contain;
  mask: var(--tick) no-repeat center / contain;
}

/* ==========================================================================
   Landing responsive
   ========================================================================== */
@media (max-width: 1080px) {
  .hero_inner { grid-template-columns: minmax(0, 1fr) auto; }
  .hero_meta { grid-column: 1 / -1; justify-self: start; text-align: left; }
  .hero_meta .eyebrow { border-bottom: 0; padding-bottom: 0; }
  .sc_bleed_shot { width: 100%; }
  /* Explicit placement is released below the wide breakpoint: the 4x12 arrangement only
     balances at four columns. Tiles flow naturally at a fixed height instead. */
  .mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: none; grid-auto-rows: 420px; height: auto; }
  .tile { grid-column: auto / span 1; grid-row: auto; }
  .tile:last-child { grid-column: auto / span 2; }
  .sc_bleed_grid, .sc_dense_grid { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .sc_wide .f_list { grid-template-columns: repeat(2, 1fr); }
  .f_list.cols { columns: 2; }
  .spec_group_label { position: static; }
  /* Two columns, base licence first and full width: comparing a $49 tier against two bundles
     side by side is the whole point, and at this width three columns squeeze every item row. */
  .price_grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .price_grid > .price_card:first-child { grid-column: 1 / -1; }
  .launch_steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); }
}
@media (max-width: 860px) {
  .lp { --s6: 72px; --s7: 88px; --s8: 110px; }
  .hero { padding-top: 56px; }
  /* minmax(0, 1fr), never a bare 1fr: 1fr means minmax(auto, 1fr), whose automatic minimum
     is the item's min-content, so the track refuses to shrink and pushes the grid past the
     viewport. This was overflowing the whole page horizontally on phones. */
  .hero_inner { grid-template-columns: minmax(0, 1fr); padding-bottom: var(--s6); }
  .hero_stage { width: min(300px, 78vw); height: auto; aspect-ratio: 1; justify-self: center; order: -1; }
  .hero_disc { width: 140px; height: 140px; }
  .hero_title { max-width: none; }
  .stats_grid { grid-template-columns: repeat(2, 1fr); }
  .stat { border-top: 1px solid rgba(var(--border), .08); }
  .stat:nth-child(-n+2) { border-top: 0; }
  .stat:nth-child(odd) { border-left: 0; }
  .sc_wide .sc_head { margin-bottom: var(--s4); }
  .sc_wide_shot { margin-top: 0; }
  .sc_wide .f_list { grid-template-columns: minmax(0, 1fr); }
  .sc_wide .f_list li { border-top: 0; border-bottom: 1px solid rgba(var(--border), .08); padding-top: 10px; }
  .sc_wide .f_list li::before { top: 12px; }
  .f_list.cols { columns: 1; }
  .mosaic_head { flex-direction: column; align-items: flex-start; }
  .mosaic { grid-template-columns: minmax(0, 1fr); grid-auto-rows: 380px; }
  .tile:last-child { grid-column: auto / span 1; }
  .spec_head { grid-template-columns: minmax(0, 1fr); }
  .spec_count { text-align: left; }
  .price_grid { grid-template-columns: minmax(0, 1fr); }
  .price_grid > .price_card:first-child { grid-column: auto; }
  .launch_steps { grid-template-columns: minmax(0, 1fr); gap: 0; }
  /* Stacked, the steps read as one numbered run, so only the first keeps the brand rule on top
     and the rest divide with a hairline — three heavy rules in a column looks like three
     unrelated sections. */
  .launch_step { border-top-color: rgba(var(--border), .1); border-top-width: 1px; padding: var(--s3) 0; }
  .launch_step:first-child { border-top: 2px solid rgba(var(--theme_color), .28); }
  .launch_note { grid-template-columns: minmax(0, 1fr); }
  .spec_g_sub { display: none; }
  .spec_group_label { gap: var(--s2); padding: 18px 2px; }
  .closer { padding: var(--s7) var(--s3); }
}
/* ==========================================================================
   DEMOS — the catalogue.
   --------------------------------------------------------------------------
   This page is an index, not a slideshow. Someone arrives to compare eleven
   things and pick one, so:
   · the filter is a real URL, sticky, with live counts;
   · the row shape changes as you scroll (spread / pair / reversed / triple) so
     the eye keeps re-anchoring instead of switching off;
   · each entry carries a spec block, because prose can't be compared at a
     glance and eleven paragraphs of it can't be read;
   · exactly one entry gets a filled button. Eleven identical primary CTAs is
     the same as none.
   The identity is the landing page's: white, hairlines, tabular figures,
   brand colour as a fill and --theme_ink as type.
   ========================================================================== */
.dm {
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 104px;
  --s7: 150px;
}
.dm code { font-family: "SFMono-Regular", Consolas, monospace; font-size: .8125rem; }

/* --- Hero ----------------------------------------------------------------
   A wall of the actual work, full bleed and drifting. Columns alternate
   direction and no two share a speed, so the surface never resolves into one
   sliding sheet — it reads as a lot of things happening at once, which is the
   claim the page is making.

   The captures are laid over the brand colour with a luminosity blend: their
   light and shade survive, their own colours do not. Fifty unrelated palettes
   become one surface instead of a collage, and white type stays legible on top
   of arbitrary screenshots without a slab of scrim over everything. */
/* Pulled up under the fixed nav and padded back down by the same amount, so the wall runs to
   the very top of the window. The nav is translucent with a backdrop blur, so instead of a
   white bar sitting on top of the colour it becomes a frosted band of it — which is the whole
   reason the original read as full bleed. Body padding is what is being cancelled here; the
   value has to stay tied to --nav_h rather than a number. */
.dm_hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  margin-top: calc(var(--nav_h) * -1);
  padding-top: var(--nav_h);
  min-height: calc(660px + var(--nav_h));
  display: flex;
  align-items: center;
  background: linear-gradient(158deg, rgb(var(--theme_color)) 0%, rgb(var(--theme_color_2)) 58%, rgb(var(--theme_color)) 100%);
}
.dm_wall {
  position: absolute; inset: -12% 0;
  z-index: -1;
  display: flex; gap: 14px;
  padding: 0 14px;
  mix-blend-mode: luminosity;
  opacity: .44;
  pointer-events: none;
}
.dm_wall_col { flex: 1 1 0; min-width: 0; margin-top: var(--off, 0); }
.dm_wall_track {
  display: flex; flex-direction: column; gap: 14px;
  animation: dm_drift var(--dur, 80s) linear infinite;
  will-change: transform;
}
/* Half the columns run the other way. Reversing the same keyframe is seamless because the
   track holds two identical passes — translateY(0) and translateY(-50%) show the same thing. */
.dm_wall_col.is_down .dm_wall_track { animation-direction: reverse; }
.dm_wall_track img {
  width: 100%; display: block;
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
}
@keyframes dm_drift {
  from { transform: translateY(0); }
  to   { transform: translateY(-50%); }
}

/* Deepens under the copy only, so the wall keeps its detail out at the edges. The bottom stays
   crisp: the fact band below is a hairline grid on white, and a colour that fades out before it
   would leave the two looking like they missed each other. */
.dm_hero_scrim {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(ellipse 60% 66% at 50% 52%, rgba(84, 16, 2, .5) 0%, rgba(84, 16, 2, .16) 55%, transparent 78%),
    linear-gradient(to bottom, rgba(84, 16, 2, .3) 0%, transparent 26%);
}

.dm_hero_copy {
  position: relative; z-index: 2;
  max-width: 46rem; margin: 0 auto;
  padding: var(--s7) 0;
  text-align: center;
  color: #fff;
}
.dm_hero_copy .tag { justify-content: center; margin-bottom: var(--s3); }
.dm_hero_copy .tag::after { display: none; }
.dm_hero_copy .tag_name { color: rgba(255, 255, 255, .72); }
.dm_hero_title {
  font-size: clamp(2.6rem, 5.4vw, 4.2rem);
  font-weight: 600; line-height: 1.02; letter-spacing: -.045em;
  margin: 0 0 var(--s3);
  text-shadow: 0 2px 30px rgba(70, 14, 0, .3);
}
.dm_hero_lede {
  font-size: 1.0625rem; line-height: 1.62;
  max-width: 48ch; margin: 0 auto var(--s4);
  color: rgba(255, 255, 255, .88);
}
.dm_hero_acts { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* On this ground the filled button has to be the light one — ink on brand is a muddy pair, and
   the ghost border needs to come off white rather than off the ink hairline token. */
.dm_hero .btn_primary { background: #fff; color: rgb(var(--ink)); }
.dm_hero .btn_primary:hover { background: rgba(255, 255, 255, .87); }
.dm_hero .btn_ghost { color: #fff; border-color: rgba(255, 255, 255, .42); }
.dm_hero .btn_ghost:hover { color: #fff; border-color: rgba(255, 255, 255, .85); background: rgba(255, 255, 255, .12); }

/* Fact band — same hairline-grid treatment as the landing page's stat band. */
.dm_facts { border-top: 1px solid rgba(var(--border), .1); }
.dm_facts_inner { max-width: var(--max_w); margin: 0 auto; padding: 0 var(--s3); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.dm_fact { padding: var(--s4) var(--s3) var(--s4) 0; border-left: 1px solid rgba(var(--border), .08); padding-left: var(--s3); }
.dm_fact:first-child { border-left: 0; padding-left: 0; }
.dm_fact_v {
  display: block;
  font-size: clamp(1.75rem, 2.6vw, 2.4rem); font-weight: 500;
  letter-spacing: -.035em; line-height: 1;
  font-variant-numeric: tabular-nums;
  margin-bottom: 8px;
}
.dm_fact_l { display: block; font-size: .8125rem; color: rgb(var(--ink_soft)); }

/* --- Index bar ------------------------------------------------------------
   Sticky under the nav. Each chip is an href, so the filter survives a curl,
   a bookmark and a browser back button.

   The changelog and the docs index have the same job — a long list that needs a filter and a
   sense of how much of it there is — so they wear the same bar rather than three near-identical
   ones. Only the trailing margin differs, which is why it is set separately below. */
.dm_bar, .cl_bar, .dx_bar {
  position: sticky; top: var(--nav_h); z-index: 30;
  background: rgba(var(--bg), .86);
  backdrop-filter: blur(14px) saturate(160%);
  border-top: 1px solid rgba(var(--border), .1);
  border-bottom: 1px solid rgba(var(--border), .1);
}
.dm_bar { margin-bottom: var(--s6); }
.dm_bar_inner, .cl_bar_inner, .dx_bar_inner {
  max-width: var(--max_w); margin: 0 auto; padding: 11px var(--s3);
  display: flex; align-items: center; gap: var(--s3);
}
.dm_bar_count { flex: 0 0 auto; font-size: .8125rem; color: rgb(var(--ink_faint)); white-space: nowrap; }
.dm_bar_count strong { color: rgb(var(--ink)); font-weight: 600; font-variant-numeric: tabular-nums; }
.dm_chips { display: flex; gap: 6px; margin-left: auto; overflow-x: auto; scrollbar-width: none; }
.dm_chips::-webkit-scrollbar { display: none; }
.dm_chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px;
  border: 1px solid rgba(var(--border), .12);
  border-radius: 999px;
  font-size: .8125rem; font-weight: 500;
  color: rgb(var(--ink_soft));
  text-decoration: none; white-space: nowrap;
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease), background var(--t_fast) var(--ease);
}
.dm_chip i { font-style: normal; font-size: .6875rem; font-variant-numeric: tabular-nums; color: rgb(var(--ink_faint)); }
.dm_chip:hover { color: rgb(var(--ink)); border-color: rgba(var(--border), .34); }
.dm_chip.is_on { background: rgb(var(--ink)); border-color: rgb(var(--ink)); color: rgb(var(--bg)); }
.dm_chip.is_on i { color: rgba(255, 255, 255, .58); }

/* --- The grid -------------------------------------------------------------
   Twelve columns, filled by whole rows. Widths come from the row template in
   the template file, which guarantees every row adds up to twelve even after a
   filter cuts the list down. */
.dm_grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s6) var(--s3); }
.dm_card { min-width: 0; }
.dm_card.is_lead, .dm_card.is_wide { grid-column: span 12; display: grid; gap: var(--s5); align-items: center; }
.dm_card.is_lead { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.dm_card.is_wide { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
/* Reversed spread: the surface returns on the right so two full-width entries never
   read as the same block twice. */
.dm_card.is_wide .dm_body { order: 1; }
.dm_card.is_wide .dm_shot { order: 2; }
.dm_card.is_half { grid-column: span 6; }
.dm_card.is_third { grid-column: span 4; }

/* Screenshot frame. The sources are full-length page captures, so the frame shows the top
   and the image travels up on hover — the whole page without the user scrolling it, and a
   reason to hover that isn't decoration.

   Frames stay portrait or near-square at every size. A wide frame turns a full-page capture
   into a letterbox strip of its header, which shows nothing and looks like a mistake. */
.dm_shot {
  --shot_h: 500px;
  position: relative; display: block; overflow: hidden;
  height: var(--shot_h);
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  background: rgb(var(--bg_alt));
  box-shadow: 0 40px 90px -50px rgba(var(--border), .75);
  transition: border-color var(--t_mid) var(--ease), box-shadow var(--t_slow) var(--ease);
}
.is_lead .dm_shot  { --shot_h: 560px; }
.is_wide .dm_shot  { --shot_h: 500px; }
.is_third .dm_shot { --shot_h: 420px; }
.dm_shot:hover { border-color: rgba(var(--theme_color), .45); box-shadow: 0 40px 80px -44px rgba(var(--border), .8); }
.dm_shot img {
  width: 100%; height: auto;
  /* min-height + cover keeps a short capture filling the frame instead of leaving a gap,
     and makes the translate below a no-op for it rather than a jump. */
  min-height: 100%;
  object-fit: cover; object-position: top;
  display: block;
  transition: transform 3.4s var(--ease);
}
.dm_shot:hover img, .dm_shot:focus-visible img { transform: translateY(calc(-100% + var(--shot_h))); }
.dm_shot_go {
  position: absolute; top: 12px; right: 12px;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgb(var(--theme_color)); color: #fff;
  font-size: 1rem; line-height: 1;
  opacity: 0; transform: translateY(-5px) scale(.9);
  transition: opacity var(--t_mid) var(--ease), transform var(--t_mid) var(--ease);
  pointer-events: none;
}
.dm_shot:hover .dm_shot_go, .dm_shot:focus-visible .dm_shot_go { opacity: 1; transform: none; }

.dm_body { min-width: 0; }
.dm_card.is_half .dm_body, .dm_card.is_third .dm_body { padding-top: var(--s3); }
.dm_head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.dm_num {
  flex: 0 0 auto;
  font-size: .6875rem; font-weight: 600; letter-spacing: .08em;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--theme_ink));
}
.dm_title { margin: 0; font-size: 1.25rem; letter-spacing: -.022em; }
.is_lead .dm_title { font-size: clamp(1.9rem, 3vw, 2.5rem); }
.is_wide .dm_title { font-size: clamp(1.55rem, 2.4vw, 2rem); }
.dm_flag {
  font-size: .625rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--theme_ink));
  border: 1px solid rgba(var(--theme_color), .3);
  border-radius: 999px; padding: 2px 8px;
}
.dm_lede { margin: 10px 0 0; font-size: .9375rem; }
.dm_lede b { font-weight: 600; color: rgb(var(--ink)); }
.is_lead .dm_lede { font-size: 1.0625rem; max-width: 44ch; }
.dm_note { margin: var(--s2) 0 0; font-size: .9375rem; max-width: 52ch; color: rgb(var(--ink_soft)); }

/* Spec block — the reason eleven demos can be compared without reading eleven paragraphs. */
.dm_specs { margin: var(--s3) 0 0; }
.dm_specs > div {
  display: grid; grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  gap: 12px; padding: 8px 0;
  border-top: 1px solid rgba(var(--border), .07);
  font-size: .8125rem;
}
.dm_specs dt { color: rgb(var(--ink_faint)); }
.dm_specs dd { margin: 0; color: rgb(var(--ink)); }
.is_lead .dm_specs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--s4); }

.dm_tools { list-style: none; display: flex; flex-wrap: wrap; gap: 5px; margin: var(--s3) 0 0; padding: 0; }
.dm_tools li {
  font-size: .6875rem; color: rgb(var(--ink_soft));
  border: 1px solid rgba(var(--border), .1);
  background: rgba(var(--border), .022);
  border-radius: 999px; padding: 3px 9px;
}

/* One filled button on the page, on the lead entry. Everything else is a text action —
   present, findable, and not competing. */
.dm_acts { display: flex; align-items: center; flex-wrap: wrap; gap: 10px var(--s3); margin-top: var(--s4); }
.dm_open {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .8125rem; font-weight: 500; text-decoration: none;
  color: rgb(var(--ink));
  border-bottom: 1px solid rgba(var(--border), .2);
  padding-bottom: 2px;
  transition: border-color var(--t_fast) var(--ease);
}
.dm_open .mdi { font-size: .875rem; color: rgb(var(--ink_faint)); }
.dm_open:hover { border-bottom-color: rgb(var(--theme_color)); }
.dm_keys {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .8125rem; font-weight: 500; text-decoration: none;
  color: rgb(var(--ink_soft));
  transition: color var(--t_fast) var(--ease);
}
.dm_keys .mdi { color: rgb(var(--theme_ink)); font-size: .95rem; }
.dm_keys:hover { color: rgb(var(--ink)); }

.dm_empty { text-align: center; padding: var(--s5) 0 var(--s7); }
.dm_empty p { margin-bottom: var(--s3); }

/* --- Back-end -------------------------------------------------------------
   A screenshot of a website can't show the half of the product that does the
   work, so the admin panel gets its own section and its own credentials. */
.dm_backend {
  margin-top: var(--s7);
  padding: var(--s7) 0;
  background: rgb(var(--bg_alt));
  border-top: 1px solid rgba(var(--border), .08);
  border-bottom: 1px solid rgba(var(--border), .08);
}
.dm_backend_head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 340px); gap: var(--s5); align-items: start; margin-bottom: var(--s5); }
.dm_backend_intro h2 { margin: var(--s3) 0 0; max-width: 17ch; }
.dm_backend_intro p { margin: var(--s3) 0 0; max-width: 50ch; }

.dm_keycard {
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: 0 30px 60px -46px rgba(var(--border), .7);
}
.dm_keycard_head {
  display: flex; align-items: center; gap: 8px;
  font-size: .6875rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
  margin-bottom: 10px;
}
.dm_keycard_head .mdi { color: rgb(var(--theme_ink)); font-size: .95rem; }
.dm_keycard dl { margin: 0; }
.dm_keycard dl > div {
  display: grid; grid-template-columns: 5rem minmax(0, 1fr);
  align-items: center; gap: 10px; padding: 8px 0;
  border-top: 1px solid rgba(var(--border), .07);
}
.dm_keycard dt, .dm_creds dt { font-size: .75rem; color: rgb(var(--ink_faint)); }
.dm_keycard dd, .dm_creds dd { margin: 0; display: flex; align-items: center; gap: 8px; min-width: 0; }
.dm_keycard code, .dm_creds code { overflow-wrap: anywhere; }

/* Copy buttons do nothing without a clipboard, so they only exist once the script says so. */
.dm_copy { display: none; }
.js_copy .dm_copy {
  display: inline-block;
  margin-left: auto; flex: 0 0 auto;
  border: 1px solid rgba(var(--border), .14);
  background: transparent;
  border-radius: var(--radius_sm);
  font: inherit; font-size: .6875rem; font-weight: 500;
  padding: 2px 8px;
  color: rgb(var(--ink_soft));
  cursor: pointer;
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.js_copy .dm_copy:hover { color: rgb(var(--ink)); border-color: rgba(var(--border), .34); }
.js_copy .dm_copy.is_done { color: rgb(var(--theme_ink)); border-color: rgba(var(--theme_color), .5); }

.dm_admin_grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s5) var(--s3); }
.dm_admin_item { margin: 0; }
.dm_admin_shot {
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  overflow: hidden;
  background: rgb(var(--bg));
  box-shadow: 0 30px 60px -48px rgba(var(--border), .8);
}
.dm_admin_shot img { width: 100%; height: auto; display: block; transition: transform var(--t_slow) var(--ease); }
.dm_admin_item:hover .dm_admin_shot img { transform: scale(1.03); }
.dm_admin_item figcaption { margin-top: 12px; }
.dm_admin_t { display: block; font-size: .875rem; font-weight: 600; letter-spacing: -.008em; }
.dm_admin_d { display: block; font-size: .8125rem; color: rgb(var(--ink_soft)); margin-top: 2px; }

/* --- Closer --------------------------------------------------------------- */
.dm_closer { text-align: center; padding: var(--s7) 0; }
.dm_closer h2 { font-size: clamp(2rem, 3.8vw, 2.9rem); max-width: 20ch; margin: 0 auto var(--s3); }
.dm_closer p { max-width: 48ch; margin: 0 auto var(--s4); }
.dm_closer_acts { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* --- Access dialogs -------------------------------------------------------
   Server-rendered into the page and opened by :target, so a browser with no
   JavaScript still gets every URL and credential. The script upgrades them to
   proper modals (no hash, Esc to close, scroll lock, focus returned). */
.dm_dlg { position: fixed; inset: 0; z-index: 200; display: none; align-items: center; justify-content: center; padding: var(--s3); }
.dm_dlg:target, .dm_dlg.is_open { display: flex; }
.dm_dlg_scrim { position: absolute; inset: 0; background: rgba(8, 9, 12, .5); backdrop-filter: blur(3px); }
.dm_dlg_card {
  position: relative;
  width: min(680px, 100%);
  max-height: calc(100vh - var(--s5));
  overflow-y: auto;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  box-shadow: 0 50px 100px -30px rgba(0, 0, 0, .45);
  padding: var(--s4);
}
.dm_dlg_x {
  position: absolute; top: 14px; right: 14px;
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  text-decoration: none; color: rgb(var(--ink_soft));
  border: 1px solid rgba(var(--border), .12);
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease), background var(--t_fast) var(--ease);
}
.dm_dlg_x:hover { color: rgb(var(--ink)); border-color: rgba(var(--border), .32); background: rgba(var(--border), .04); }
.dm_dlg_head { margin-bottom: var(--s3); padding-right: 44px; }
.dm_dlg_head h2 { margin: 6px 0 0; font-size: 1.5rem; }
.dm_dlg_head p { margin: 10px 0 0; font-size: .875rem; }

.dm_ends {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: rgba(var(--border), .09);
  border: 1px solid rgba(var(--border), .09);
  border-radius: var(--radius); overflow: hidden;
}
.dm_end { background: rgb(var(--bg)); padding: 18px; }
.dm_end.is_off { background: rgb(var(--bg_alt)); }
.dm_end h3, .dm_testcard h3 {
  display: flex; align-items: center; gap: 8px; margin: 0 0 12px;
  font-size: .6875rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
}
.dm_end_i { color: rgb(var(--theme_ink)); font-size: .95rem; }
.dm_end_url {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .875rem; font-weight: 600; text-decoration: none;
  color: rgb(var(--ink));
  overflow-wrap: anywhere;
  border-bottom: 1px solid rgba(var(--theme_color), .4);
  transition: border-color var(--t_fast) var(--ease);
}
.dm_end_url:hover { border-bottom-color: rgb(var(--theme_color)); }
.dm_end_url .mdi { font-size: .8rem; color: rgb(var(--ink_faint)); }
.dm_end p { margin: 12px 0 0; font-size: .8125rem; }
.dm_end_none { color: rgb(var(--ink_soft)); }

.dm_creds { margin: 14px 0 0; }
.dm_creds > div { display: grid; grid-template-columns: 4.6rem minmax(0, 1fr); gap: 8px; align-items: center; padding: 7px 0; border-top: 1px solid rgba(var(--border), .07); }
.dm_creds.is_row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); }
.dm_creds.is_row > div { grid-template-columns: minmax(0, 1fr); gap: 3px; padding: 0; border-top: 0; align-items: start; }

.dm_testcard {
  margin-top: var(--s3);
  border: 1px solid rgba(var(--theme_color), .22);
  background: rgba(var(--theme_color), .04);
  border-radius: var(--radius);
  padding: 18px;
}
.dm_testcard p { margin: 0; font-size: .8125rem; }

.dm_dlg_foot { display: flex; align-items: center; flex-wrap: wrap; gap: 10px var(--s3); margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid rgba(var(--border), .08); }
.dm_dlg_foot_l { font-size: .6875rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--ink_faint)); }
.dm_dlg_foot .dm_tools { margin: 0; }

/* While a dialog is open the page behind it must not scroll. */
.dlg_open, .dlg_open body { overflow: hidden; }

/* --- Demos responsive ----------------------------------------------------- */
@media (max-width: 1180px) {
  .dm_admin_grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 980px) {
  .dm_card.is_third { grid-column: span 6; }
  .dm_card.is_lead, .dm_card.is_wide { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .dm_card.is_wide .dm_body { order: 2; }
  .dm_card.is_wide .dm_shot { order: 1; }
  .is_lead .dm_specs { grid-template-columns: minmax(0, 1fr); }
  .dm_backend_head { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
}
@media (max-width: 980px) {
  /* Seven columns on a phone is 65px each, which turns the screenshots into noise. Dropping
     the last three keeps the tiles wide enough to still read as screens. */
  .dm_wall_col:nth-child(n+5) { display: none; }
  .dm_hero { min-height: calc(520px + var(--nav_h)); }
}
@media (max-width: 760px) {
  .dm { --s5: 44px; --s6: 64px; --s7: 88px; }
  .dm_wall_col:nth-child(n+4) { display: none; }
  .dm_facts_inner { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dm_fact { border-left: 0; padding-left: 0; padding-right: var(--s3); border-top: 1px solid rgba(var(--border), .08); }
  .dm_fact:nth-child(-n+2) { border-top: 0; }
  .dm_card.is_half, .dm_card.is_third { grid-column: span 12; }
  .dm_admin_grid { grid-template-columns: minmax(0, 1fr); }
  .dm_bar_inner { flex-wrap: wrap; gap: 10px; }
  .dm_chips { margin-left: 0; width: 100%; }
  .dm_ends { grid-template-columns: minmax(0, 1fr); }
  .dm_dlg { padding: 0; align-items: flex-end; }
  .dm_dlg_card { max-height: 88vh; border-radius: var(--radius_lg) var(--radius_lg) 0 0; padding: var(--s4) var(--s3) var(--s3); }
  .dm_creds.is_row { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .dm_closer_acts .btn, .dm_hero_acts .btn { width: 100%; justify-content: center; }
}
/* Store promo */
.store_promo { background: rgb(var(--bg_alt)); text-align: center; }
.store_promo_inner > p { max-width: 44em; margin-left: auto; margin-right: auto; }
.bundle_cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px; margin-top: 32px; }
.bundle_card { background: rgb(var(--bg)); border-radius: var(--radius); padding: 26px; display: flex; flex-direction: column; align-items: center; gap: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.05); }
.bundle_card img { height: 44px; width: auto; object-fit: contain; }
.bundle_card_title { font-weight: 700; }
.bundle_price_now { font-size: 1.6rem; font-weight: 800; color: rgb(var(--theme_color)); }
.bundle_price_was { display: block; font-size: .78rem; color: rgb(var(--ink_soft)); text-decoration: line-through; }
.store_promo_actions { margin-top: 26px; }

/* ==========================================================================
   DOCS INDEX.
   --------------------------------------------------------------------------
   Headed grids, flat and wide. Every article is an icon, a title and a line
   saying what it is for; nothing is in a box.

   Two earlier versions put the articles inside something — first a hairline
   table, then bordered cards — and both made the page about the containers.
   The job here is to let someone find one line in fifty-three at a glance, and
   borders do not help with that. Icons give each row a shape to recognise, the
   description says whether it is the right row, and the air between sections
   does the work the borders were doing badly.
   ========================================================================== */
.dx {
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 104px;
  --s7: 150px;
  --bar_h: 57px;   /* measured height of .dx_bar — sticky offsets hang off it */
}

/* --- Hero ---------------------------------------------------------------- */
.dx_hero {
  position: relative;
  padding: var(--s6) 0 var(--s5);
  text-align: center;
}
.dx_hero_bg { position: absolute; inset: 0; overflow: hidden; isolation: isolate; pointer-events: none; }
/* Weaker than the overview's bloom and pushed up, because there it lights an object and here
   it would tint the headline. */
.dx_hero_bloom {
  position: absolute; z-index: -1;
  left: 50%; top: -300px;
  width: 1100px; height: 740px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at center, rgba(var(--theme_color), .15) 0%, rgba(var(--theme_color_2), .07) 44%, transparent 70%);
  filter: blur(26px);
  pointer-events: none;
}
.dx_hero_inner { position: relative; z-index: 2; }
.dx_hero_inner .tag { justify-content: center; margin-bottom: var(--s3); }
.dx_hero_inner .tag::after { display: none; }
.dx_hero_title {
  font-size: clamp(2.4rem, 4.8vw, 3.6rem);
  font-weight: 600; line-height: 1.03; letter-spacing: -.042em;
  /* 16ch, not 20: at 20 the line broke before the last word and left "down." on a line of its
     own. This forces the break at the comma, where the sentence already pauses. */
  margin: 0 auto var(--s3); max-width: 16ch;
}
.dx_hero_lede { font-size: 1.0625rem; line-height: 1.62; max-width: 54ch; margin: 0 auto var(--s5); }

/* The widest single element in the hero on purpose — it is the one control on the page, and
   everything below it exists so that you do not have to use it. */
.dx_search {
  position: relative;
  display: flex; align-items: center;
  width: min(620px, 100%);
  margin: 0 auto;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .14);
  border-radius: var(--radius_lg);
  box-shadow: 0 22px 44px -34px rgba(var(--border), .75);
  transition: border-color var(--t_fast) var(--ease), box-shadow var(--t_fast) var(--ease);
}
.dx_search:focus-within {
  border-color: rgba(var(--theme_color), .55);
  box-shadow: 0 0 0 4px rgba(var(--theme_color), .09), 0 22px 44px -34px rgba(var(--border), .75);
}
.dx_search_i { position: absolute; left: 20px; font-size: 1.25rem; color: rgb(var(--ink_faint)); pointer-events: none; }
.dx_search_in {
  flex: 1; min-width: 0;
  border: 0; background: transparent;
  font: inherit; font-size: 1rem;
  color: rgb(var(--ink));
  padding: 1.05em 3.6em 1.05em 3.4em;
  text-align: left;
}
.dx_search_in::placeholder { color: rgb(var(--ink_faint)); }
.dx_search_in:focus { outline: none; }
.dx_search_in::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.dx_search_k {
  position: absolute; right: 16px;
  font-family: inherit; font-size: .75rem; font-weight: 600;
  color: rgb(var(--ink_faint));
  border: 1px solid rgba(var(--border), .14);
  border-radius: 5px; padding: 2px 8px;
  background: rgba(var(--border), .022);
}
.dx_search:focus-within .dx_search_k { opacity: 0; }

/* Live results, dropped below the field — never a filter over the index underneath, so that
   stays fully browsable even when a query comes up empty. */
.dx_search_results {
  position: absolute; top: calc(100% + 10px); left: 0; right: 0;
  max-height: min(420px, 60vh);
  overflow-y: auto; overscroll-behavior: contain;
  background: rgb(var(--bg_lift));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  box-shadow: 0 24px 50px -20px rgba(var(--border), .5);
  padding: 8px;
  z-index: 40; /* above .dx_bar's sticky 30, below .site_nav's 50 */
  text-align: left;
}
.dx_sr_item {
  display: flex; align-items: center; gap: var(--s3);
  padding: .7em .8em;
  border-radius: var(--radius_sm);
  text-decoration: none;
  color: rgb(var(--ink));
}
.dx_sr_item:hover, .dx_sr_item.is_active { background: rgba(var(--border), .07); }
.dx_sr_i { flex: 0 0 auto; font-size: 1.1rem; color: rgb(var(--theme_ink)); opacity: .85; }
.dx_sr_b { min-width: 0; flex: 1 1 auto; }
.dx_sr_t { font-size: .9375rem; font-weight: 600; letter-spacing: -.005em; }
.dx_sr_t mark { background: rgba(var(--theme_color), .22); color: inherit; border-radius: 3px; padding: 0 1px; }
.dx_sr_tag {
  display: inline-block; margin-left: 6px;
  font-size: .6875rem; font-weight: 600; letter-spacing: .02em;
  color: rgb(var(--ink_faint));
  border: 1px solid rgba(var(--border), .14); border-radius: 999px;
  padding: 1px 7px;
}
.dx_sr_d {
  display: block; margin-top: 2px;
  font-size: .8125rem; color: rgb(var(--ink_soft));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dx_sr_d mark { background: rgba(var(--theme_color), .18); color: inherit; border-radius: 3px; padding: 0 1px; }
.dx_sr_g {
  flex: 0 0 auto;
  font-size: .75rem; color: rgb(var(--ink_faint));
  max-width: 30%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dx_sr_empty { padding: 1.4em 1em; text-align: center; font-size: .875rem; color: rgb(var(--ink_faint)); }

/* --- The four doors ------------------------------------------------------
   The only boxed things on the page, which is what makes them read as the way
   in. They answer "where do I start", which is a different question from
   "which page covers X" — so each opens one article, not another index. */
.dx_start { padding: 0 0 var(--s6); }
.dx_start_grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); }
.dx_door {
  display: block;
  padding: 22px;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius_lg);
  text-decoration: none;
  box-shadow: 0 30px 60px -50px rgba(var(--border), .8);
  transition: border-color var(--t_mid) var(--ease), box-shadow var(--t_mid) var(--ease);
}
/* No lift on hover. Nothing else in this design moves under the pointer — cards, screenshots,
   tiles and rows all answer with colour — and one component that does reads as borrowed. */
.dx_door:hover {
  border-color: rgba(var(--theme_color), .4);
  box-shadow: 0 34px 64px -42px rgba(var(--border), .85);
}
.dx_door_i {
  display: grid; place-items: center;
  width: 40px; height: 40px; margin-bottom: 16px;
  border-radius: 11px;
  background: rgba(var(--theme_color), .09);
  color: rgb(var(--theme_ink));
  font-size: 1.25rem;
  transition: background var(--t_mid) var(--ease);
}
.dx_door:hover .dx_door_i { background: rgba(var(--theme_color), .16); }
.dx_door_t {
  display: flex; align-items: center; gap: 7px;
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -.018em;
  color: rgb(var(--ink));
  margin-bottom: 6px;
}
.dx_door_go {
  font-size: .9rem; color: rgb(var(--theme_ink));
  opacity: 0; transform: translateX(-4px);
  transition: opacity var(--t_fast) var(--ease), transform var(--t_fast) var(--ease);
}
.dx_door:hover .dx_door_go { opacity: 1; transform: none; }
.dx_door_d { display: block; font-size: .875rem; line-height: 1.55; color: rgb(var(--ink_soft)); }

/* Every description on these pages is a block <span> rather than a <p>, so none of them picked
   up the `p { text-wrap: pretty }` the rest of the site relies on — which is why "code." and
   "schedule." were each sitting alone on a third line. Same rule, applied where it was missed. */
.dx_door_d, .dx_item_d, .dx_step_d, .dx_help_d, .da_pager_g, .dm_admin_d { text-wrap: pretty; }

/* --- Sections ------------------------------------------------------------ */
.dx_sec { padding: 0 0 var(--s6); }
.dx_sec_head { margin-bottom: var(--s4); }
/* Two vertical lines govern this page: icons at the container edge, text 35px in. The section
   icon therefore sits in the same 22px box the article icons use, with the same 13px gap, and
   the section description is indented to meet the heading rather than starting back at the
   container edge. Before this, section titles, section descriptions and article titles each
   began at a different x — three edges where there should be two. */
.dx_sec_head h2 {
  display: flex; align-items: center; gap: 13px;
  font-size: clamp(1.4rem, 2.2vw, 1.75rem);
  letter-spacing: -.028em; margin: 0;
}
.dx_sec_i {
  flex: 0 0 auto;
  width: 22px; text-align: center;
  font-size: 1.3rem; line-height: 1;
  color: rgb(var(--theme_ink));
}
.dx_sec_n {
  font-size: .8125rem; font-weight: 500;
  color: rgb(var(--ink_faint)); font-variant-numeric: tabular-nums;
  align-self: center;
}
.dx_sec_head p { margin: 10px 0 0 35px; font-size: .9375rem; max-width: 58ch; }

/* Three across, no boxes. The negative inset lets the hover state bleed into the gutter so it
   reads as the row lighting up rather than as a card appearing under the pointer. */
.dx_items { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px var(--s3); margin: 0 -12px; }
.dx_item {
  display: flex; align-items: flex-start; gap: 13px;
  padding: 13px 12px;
  border-radius: var(--radius);
  text-decoration: none;
  transition: background var(--t_fast) var(--ease);
}
.dx_item:hover { background: rgba(var(--border), .04); }
.dx_item_i {
  flex: 0 0 auto;
  width: 22px; margin-top: 1px;
  font-size: 1.15rem; line-height: 1.2;
  color: rgb(var(--ink_faint));
  transition: color var(--t_fast) var(--ease);
}
.dx_item:hover .dx_item_i { color: rgb(var(--theme_ink)); }
.dx_item_b { min-width: 0; }
.dx_item_t {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: .9375rem; font-weight: 600; letter-spacing: -.012em;
  color: rgb(var(--ink));
  margin-bottom: 3px;
}
.dx_item_tag {
  flex: 0 0 auto;
  font-size: .5625rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
  border: 1px solid rgba(var(--border), .12);
  border-radius: 999px; padding: 1px 7px;
}
.dx_item_d { display: block; font-size: .8125rem; line-height: 1.5; color: rgb(var(--ink_soft)); }

/* --- Install path --------------------------------------------------------
   A sequence, so it keeps the numbered treatment rather than being folded into
   the topic grids, where the order would simply vanish. Top rules rather than
   boxes, the same device the overview gives its wide feature row. */
.dx_path .dx_sec_head { margin-bottom: var(--s5); }
.dx_steps {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0 var(--s3);
}
.dx_steps a {
  display: block; text-decoration: none;
  padding: 15px 0 0;
  border-top: 1px solid rgba(var(--border), .13);
  transition: border-color var(--t_mid) var(--ease);
}
.dx_steps a:hover { border-top-color: rgb(var(--theme_color)); }
.dx_step_n {
  display: block; margin-bottom: 9px;
  font-size: .6875rem; font-weight: 600; letter-spacing: .08em;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--theme_ink));
}
.dx_step_t { display: block; font-size: .9375rem; font-weight: 600; letter-spacing: -.012em; color: rgb(var(--ink)); }
.dx_step_d { display: block; margin-top: 5px; font-size: .8125rem; line-height: 1.5; color: rgb(var(--ink_soft)); }

/* --- Host guides ---------------------------------------------------------
   Deliberately NOT the numbered treatment above. These are alternatives, not
   steps: exactly one of them applies to any given reader, and numbering them
   would suggest all nine have to be worked through. Boxed and logo-led instead,
   because the reader is scanning for a mark they recognise — the logo is the
   thing being searched for, so it leads the card rather than sitting beside the
   title as it does in the topic grids. */
.dx_hosts_head { margin: var(--s5) 0 var(--s3); }
.dx_hosts_head h3 {
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -.018em;
  color: rgb(var(--ink)); margin-bottom: 6px;
}
.dx_hosts_head p { font-size: .875rem; line-height: 1.6; color: rgb(var(--ink_soft)); max-width: 62ch; }
.dx_hosts {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s2);
}
.dx_host {
  display: flex; align-items: flex-start; gap: 13px;
  padding: 16px 18px;
  text-decoration: none;
  background: rgb(var(--bg_alt));
  border: 1px solid rgba(var(--border), .09);
  border-radius: var(--radius);
  transition: border-color var(--t_mid) var(--ease), background var(--t_mid) var(--ease);
}
a.dx_host:hover { border-color: rgba(var(--theme_color), .38); background: rgb(var(--bg)); }
.dx_host_i { flex: 0 0 auto; font-size: 1.375rem; line-height: 1.25; color: rgb(var(--theme_ink)); }
.dx_host_b { min-width: 0; }
.dx_host_t {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: .9375rem; font-weight: 600; letter-spacing: -.012em;
  color: rgb(var(--ink)); margin-bottom: 3px;
}
.dx_host_d { display: block; font-size: .8125rem; line-height: 1.5; color: rgb(var(--ink_soft)); }

/* --- Planned, not published ----------------------------------------------
   A row that is listed so the shape of the library is legible, but has nothing
   behind it yet. It must not read as broken or as a disabled control — it is a
   promise — so the icon dims and the badge does the talking, rather than the
   half-opacity-everything treatment that makes a page look like it failed. */
.dx_soon {
  flex: 0 0 auto;
  font-size: .5625rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--theme_ink));
  background: rgba(var(--theme_color), .1);
  border-radius: 999px; padding: 2px 7px;
}
.dx_host.is_soon, .dx_item.is_soon { cursor: default; }
.dx_host.is_soon .dx_host_i, .dx_item.is_soon .dx_item_i { opacity: .4; }
.dx_host.is_soon .dx_host_t, .dx_item.is_soon .dx_item_t { color: rgb(var(--ink_soft)); }
.dx_host.is_soon { background: transparent; border-style: dashed; }
/* Nothing happens on click, so nothing may happen on hover either — the hover states above are
   inherited by tag name and would otherwise promise a link that is not there. */
.dx_item.is_soon:hover { background: none; }
.dx_item.is_soon:hover .dx_item_i { color: rgb(var(--ink_faint)); }

.dx_index { padding-top: var(--s6); }

/* --- Help ---------------------------------------------------------------- */
.dx_help { padding: var(--s5) 0 var(--s7); }
.dx_help_grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
.dx_help_card {
  display: block; padding: 28px;
  background: rgb(var(--bg_alt));
  border: 1px solid rgba(var(--border), .09);
  border-radius: var(--radius_lg);
  text-decoration: none;
  transition: border-color var(--t_mid) var(--ease), background var(--t_mid) var(--ease);
}
.dx_help_card:hover { border-color: rgba(var(--theme_color), .35); background: rgb(var(--bg)); }
.dx_help_i { display: block; font-size: 1.5rem; color: rgb(var(--theme_ink)); margin-bottom: 14px; }
.dx_help_t { display: block; font-size: 1.25rem; font-weight: 600; letter-spacing: -.024em; color: rgb(var(--ink)); margin-bottom: 8px; }
.dx_help_d { display: block; font-size: .9375rem; line-height: 1.6; color: rgb(var(--ink_soft)); max-width: 44ch; }
.dx_help_go {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 18px;
  font-size: .875rem; font-weight: 500;
  color: rgb(var(--ink));
  border-bottom: 1px solid rgba(var(--theme_color), .4);
  padding-bottom: 2px;
  transition: border-color var(--t_fast) var(--ease);
}
.dx_help_card:hover .dx_help_go { border-bottom-color: rgb(var(--theme_color)); }
.dx_help_go .mdi { font-size: .85rem; color: rgb(var(--theme_ink)); }

.dx_none { padding: var(--s6) 0; text-align: center; }
.dx_none p { margin-bottom: var(--s3); }

/* Defensive: flex/grid display rules elsewhere in this section can otherwise override the
   browser's default [hidden] styling. Covers the search field pre-JS and its results panel. */
.dx [hidden] { display: none !important; }

@media (max-width: 1080px) {
  .dx_start_grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dx_items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dx_steps { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); }
  .dx_hosts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
  .dx { --s5: 44px; --s6: 64px; --s7: 88px; }
  .dx_start_grid { grid-template-columns: minmax(0, 1fr); }
  .dx_items { grid-template-columns: minmax(0, 1fr); }
  .dx_steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dx_hosts { grid-template-columns: minmax(0, 1fr); }
  .dx_help_grid { grid-template-columns: minmax(0, 1fr); }
  .dx_search_in { font-size: .9375rem; padding: .95em 1.1em .95em 3.1em; }
  .dx_search_i { left: 16px; font-size: 1.1rem; }
  .dx_search_k { display: none; }
  .dx_sr_g { display: none; }
}
@media (max-width: 480px) {
  .dx_steps { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   DOCS ARTICLE — library, article, contents.
   --------------------------------------------------------------------------
   Three columns, the arrangement every good reference has settled on: the tree
   on the left so you can move without going back, the article in the middle at
   a readable measure, and the page's own shape on the right so a long article
   stays navigable. The rails are sticky and scroll independently; only the
   middle column belongs to the document.
   ========================================================================== */
.da {
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 104px;
  --bar_h: 49px;
  --side_w: 258px;
  --toc_w: 208px;
  --shell: min(1400px, 100% - 48px);
  /* One measure for the whole column. The prose was capped at 74ch while the header rule, the
     pager and the footer ran the full width of the grid track — so the widest thing on the page
     was a hairline, and nothing lined up on the right. Everything in the article now ends on the
     same edge. */
  --measure: 74ch;
}

/* --- Top bar: where you are, and the way out ----------------------------- */
.da_bar {
  position: sticky; top: var(--nav_h); z-index: 30;
  background: rgba(var(--bg), .86);
  backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid rgba(var(--border), .1);
}
.da_bar_inner { width: var(--shell); margin: 0 auto; height: var(--bar_h); display: flex; align-items: center; gap: 14px; min-width: 0; }
/* overflow:hidden is what lets the trailing crumb's ellipsis actually engage — without it the
   nowrap titles push the prev/next buttons off the end of the bar on a narrow screen. */
.da_crumbs { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; font-size: .8125rem; }
.da_crumbs a { color: rgb(var(--ink_soft)); text-decoration: none; white-space: nowrap; transition: color var(--t_fast) var(--ease); }
.da_crumbs a:hover { color: rgb(var(--theme_ink)); }
.da_crumb_sep { color: rgba(var(--border), .26); }
.da_crumb_here { color: rgb(var(--ink)); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.da_bar_acts { margin-left: auto; display: flex; gap: 6px; flex: 0 0 auto; }
.da_bar_step {
  width: 30px; height: 30px; border-radius: var(--radius_sm);
  display: grid; place-items: center;
  border: 1px solid rgba(var(--border), .12);
  color: rgb(var(--ink_soft)); text-decoration: none; font-size: 1.05rem;
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease), background var(--t_fast) var(--ease);
}
.da_bar_step:hover { color: rgb(var(--ink)); border-color: rgba(var(--border), .34); background: rgba(var(--border), .04); }
.da_bar_menu {
  display: none; align-items: center; gap: 7px;
  font: inherit; font-size: .8125rem; font-weight: 500;
  background: transparent; color: rgb(var(--ink));
  border: 1px solid rgba(var(--border), .14);
  border-radius: var(--radius_sm);
  padding: .35em .7em; cursor: pointer;
}

.da_shell {
  width: var(--shell); margin: 0 auto;
  display: grid; grid-template-columns: var(--side_w) minmax(0, 1fr);
  gap: var(--s5);
  align-items: start;
}
.da.has_toc .da_shell { grid-template-columns: var(--side_w) minmax(0, 1fr) var(--toc_w); }

/* --- Sidebar -------------------------------------------------------------
   Its own scroll container pinned under the bar, so a 53-item tree never drags
   the page height around and the article can be read without the tree moving. */
.da_side {
  position: sticky; top: calc(var(--nav_h) + var(--bar_h));
  height: calc(100vh - var(--nav_h) - var(--bar_h));
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--s4) var(--s3) var(--s5) 0;
  border-right: 1px solid rgba(var(--border), .08);
  scrollbar-width: thin;
}
.da_side_back {
  display: inline-flex; align-items: center; gap: 8px;
  margin-bottom: var(--s3);
  font-size: .8125rem; font-weight: 500; text-decoration: none;
  color: rgb(var(--ink_soft));
  transition: color var(--t_fast) var(--ease);
}
.da_side_back:hover { color: rgb(var(--theme_ink)); }

/* <details> rather than a script: the tree collapses with JavaScript switched off, and the
   group holding the current article is opened server-side. */
.da_tree_g { border-top: 1px solid rgba(var(--border), .07); }
.da_tree_g:first-of-type { border-top: 0; }
.da_tree_g > summary {
  display: flex; align-items: center; gap: 9px;
  padding: 10px 2px; cursor: pointer;
  list-style: none;
  font-size: .8125rem; font-weight: 600; letter-spacing: -.005em;
  color: rgb(var(--ink));
}
.da_tree_g > summary::-webkit-details-marker { display: none; }
.da_tree_g > summary::marker { content: ""; }
.da_tree_i { flex: 0 0 auto; font-size: .95rem; color: rgb(var(--ink_faint)); transition: color var(--t_fast) var(--ease); }
.da_tree_g[open] > summary .da_tree_i, .da_tree_g > summary:hover .da_tree_i { color: rgb(var(--theme_ink)); }
.da_tree_t { min-width: 0; }
.da_tree_c { margin-left: auto; font-size: .6875rem; color: rgb(var(--ink_faint)); font-variant-numeric: tabular-nums; }
.da_tree_g ul {
  list-style: none; margin: 0 0 10px 8px; padding: 0 0 0 16px;
  border-left: 1px solid rgba(var(--border), .09);
}
.da_tree_g li a {
  position: relative; display: block;
  padding: 5px 9px; border-radius: var(--radius_sm);
  font-size: .8125rem; line-height: 1.45;
  color: rgb(var(--ink_soft)); text-decoration: none;
  transition: color var(--t_fast) var(--ease), background var(--t_fast) var(--ease);
}
.da_tree_g li a:hover { color: rgb(var(--ink)); background: rgba(var(--border), .045); }
.da_tree_g li a.is_here { color: rgb(var(--ink)); font-weight: 600; background: rgba(var(--theme_color), .07); }
/* Sits exactly on the tree's indent guide, so "you are here" reads as a lit segment of the
   rule rather than as a second, competing line. */
.da_tree_g li a.is_here::before {
  content: ""; position: absolute;
  left: -17px; top: 4px; bottom: 4px; width: 2px;
  background: rgb(var(--theme_color)); border-radius: 1px;
}

/* --- The article --------------------------------------------------------- */
.da_main { min-width: 0; padding: var(--s5) 0 var(--s6); }
.da_head { max-width: var(--measure); padding-bottom: var(--s4); border-bottom: 1px solid rgba(var(--border), .09); margin-bottom: var(--s4); }
.da_head .tag { margin-bottom: 14px; }
.da_head .tag::after { display: none; }
.da_head h1 { font-size: clamp(2rem, 3.3vw, 2.85rem); line-height: 1.05; letter-spacing: -.036em; margin: 0; }
.da_lede { margin: 14px 0 0; font-size: 1.0625rem; line-height: 1.6; max-width: 58ch; }
.da_cover {
  max-width: var(--measure);
  margin: 0 0 var(--s4);
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  overflow: hidden;
  background: rgb(var(--bg_alt));
}

/* --- Prose ---------------------------------------------------------------
   Editor.js emits a wrapper div around everything; display:contents drops it out
   of the box tree so the measure and the vertical rhythm are set here, once. */
.docs_article_body { font-size: .9375rem; line-height: 1.72; color: rgb(var(--ink_soft)); max-width: var(--measure); }
.docs_article_body .editorjs_html_wrapper { display: contents; }
.docs_article_body p { margin: 0 0 1.15em; color: rgb(var(--ink_soft)); }
.docs_article_body :is(h2, h3, h4, h5) {
  color: rgb(var(--ink));
  font-weight: 600; line-height: 1.24; letter-spacing: -.022em;
  scroll-margin-top: calc(var(--nav_h) + var(--bar_h) + 24px);
}
.docs_article_body h2 { font-size: 1.5rem; margin: 2.1em 0 .55em; }
.docs_article_body h3 { font-size: 1.25rem; margin: 2em 0 .5em; }
.docs_article_body h4 { font-size: 1.0625rem; margin: 1.85em 0 .45em; }
.docs_article_body h5 { font-size: .9375rem; margin: 1.7em 0 .4em; }
.docs_article_body > :first-child :is(h2, h3, h4, h5):first-child { margin-top: 0; }

/* The heading's own address, revealed on approach. Added by the script, so it never appears
   without something to click. */
.docs_h_link {
  margin-left: .45em;
  font-size: .8em; font-weight: 400; text-decoration: none;
  color: rgb(var(--theme_ink));
  opacity: 0;
  transition: opacity var(--t_fast) var(--ease);
}
.docs_article_body :is(h2, h3, h4, h5):hover .docs_h_link, .docs_h_link:focus-visible { opacity: 1; }

.docs_article_body a {
  color: rgb(var(--ink)); text-decoration: none;
  border-bottom: 1px solid rgba(var(--theme_color), .42);
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.docs_article_body a:hover { color: rgb(var(--theme_ink)); border-bottom-color: rgb(var(--theme_color)); }

.docs_article_body ul { list-style: none; margin: 0 0 1.15em; padding-left: 1.5em; }
.docs_article_body ol { margin: 0 0 1.15em; padding-left: 1.35em; }
.docs_article_body li { margin-bottom: .4em; }
.docs_article_body ol li::marker { color: rgb(var(--ink_faint)); font-variant-numeric: tabular-nums; }
/* The waveform tick as the bullet — the same mark the overview page's feature lists use, so
   prose and marketing share one vocabulary instead of a disc and a tick. */
.docs_article_body ul > li { position: relative; }
.docs_article_body ul > li::before {
  content: ""; position: absolute; left: -1.5em; top: .5em;
  width: 11px; height: 11px;
  background: rgb(var(--theme_color));
  -webkit-mask: var(--tick) no-repeat center / contain;
  mask: var(--tick) no-repeat center / contain;
}
.docs_article_body ul ul { margin-bottom: .3em; }

.docs_article_body code {
  font-family: "SFMono-Regular", Consolas, monospace; font-size: .85em;
  background: rgba(var(--border), .05);
  border: 1px solid rgba(var(--border), .09);
  border-radius: 4px; padding: .1em .38em;
  color: rgb(var(--ink));
}
.docs_article_body pre {
  position: relative;
  background: rgb(var(--ink)); color: #e9eaef;
  padding: 18px 20px; margin: 0 0 1.4em;
  border-radius: var(--radius);
  overflow-x: auto;
}
.docs_article_body pre code { background: none; border: 0; padding: 0; color: inherit; font-size: .8125rem; line-height: 1.65; }
.code_copy_btn {
  position: absolute; top: 10px; right: 10px;
  font: inherit; font-size: .6875rem; font-weight: 500;
  padding: 3px 9px; border-radius: var(--radius_sm);
  border: 1px solid rgba(255, 255, 255, .2);
  background: transparent; color: rgba(255, 255, 255, .7);
  cursor: pointer; opacity: 0;
  transition: opacity var(--t_fast) var(--ease), color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.docs_article_body pre:hover .code_copy_btn, .code_copy_btn:focus-visible { opacity: 1; }
.code_copy_btn:hover { color: #fff; border-color: rgba(255, 255, 255, .45); }
.code_copy_btn.is_done { color: rgb(var(--theme_color)); border-color: rgba(var(--theme_color), .6); opacity: 1; }

/* Tables are wrapped by the script so a wide one scrolls inside its own box instead of
   pushing the whole article sideways. */
.docs_table { overflow-x: auto; margin: 0 0 1.4em; border-radius: var(--radius_sm); }
.docs_article_body table { width: 100%; border-collapse: collapse; margin: 0 0 1.4em; font-size: .875rem; }
.docs_table table { margin: 0; }
.docs_article_body :is(th, td) { border: 1px solid rgba(var(--border), .11); padding: 9px 12px; text-align: left; vertical-align: top; }
.docs_article_body thead :is(th, td) { background: rgb(var(--bg_alt)); color: rgb(var(--ink)); font-weight: 600; font-size: .8125rem; }

.docs_article_body figure { margin: 1.7em 0; }
.docs_article_body :is(img, video) {
  width: 100%; height: auto; display: block;
  border: 1px solid rgba(var(--border), .09);
  border-radius: var(--radius);
  background: rgb(var(--bg_alt));
}
.docs_article_body figcaption { margin-top: 10px; font-size: .8125rem; color: rgb(var(--ink_faint)); text-align: center; }

.docs_article_body :is(mark, .cdx-marker) {
  background: rgba(var(--theme_color), .16);
  color: rgb(var(--ink));
  border-radius: 3px; padding: .06em .26em;
}
.docs_article_body blockquote.quote_wrapper {
  border-left: 2px solid rgb(var(--theme_color));
  margin: 1.7em 0; padding: .1em 0 .1em 1.2em;
  color: rgb(var(--ink));
}
.docs_article_body .warning_wrapper {
  background: rgba(var(--theme_color), .045);
  border: 1px solid rgba(var(--theme_color), .2);
  border-radius: var(--radius);
  padding: 15px 18px; margin: 1.7em 0;
}
.docs_article_body .warning_wrapper > :last-child { margin-bottom: 0; }
.docs_article_body .delimiter { height: 16px; margin: 2.4em 0; background: var(--wave_rule) repeat-x center; }

/* --- Pager and foot ------------------------------------------------------ */
.da_pager {
  max-width: var(--measure);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3);
  margin-top: var(--s6); padding-top: var(--s4);
  border-top: 1px solid rgba(var(--border), .09);
}
.da_pager a {
  display: block; padding: 15px 18px;
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color var(--t_mid) var(--ease), background var(--t_fast) var(--ease);
}
.da_pager a:hover { border-color: rgba(var(--theme_color), .45); background: rgb(var(--bg_alt)); }
.da_pager_r { text-align: right; }
.da_pager_k {
  display: block; margin-bottom: 9px;
  font-size: .6875rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
}
.da_pager_t { display: block; font-size: .9375rem; font-weight: 600; letter-spacing: -.012em; color: rgb(var(--ink)); }
.da_pager_g { display: block; margin-top: 3px; font-size: .8125rem; color: rgb(var(--ink_soft)); }

.da_help {
  max-width: var(--measure);
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 10px var(--s3);
  margin-top: var(--s4); padding-top: var(--s4);
  border-top: 1px solid rgba(var(--border), .07);
}
.da_help p { margin: 0; font-size: .875rem; }

/* --- Contents ------------------------------------------------------------ */
.da_toc {
  position: sticky; top: calc(var(--nav_h) + var(--bar_h));
  align-self: start;
  max-height: calc(100vh - var(--nav_h) - var(--bar_h));
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--s5) 0 var(--s4);
  scrollbar-width: thin;
}
.da_toc .eyebrow { display: block; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid rgba(var(--border), .12); }
.da_toc ul { list-style: none; margin: 0; padding: 0; }
.da_toc li a {
  display: block; padding: 5px 0 5px 13px;
  font-size: .8125rem; line-height: 1.45;
  color: rgb(var(--ink_faint)); text-decoration: none;
  border-left: 1px solid rgba(var(--border), .1);
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.da_toc li a:hover { color: rgb(var(--ink)); border-left-color: rgba(var(--border), .34); }
.da_toc li a.is_on { color: rgb(var(--ink)); font-weight: 500; border-left-color: rgb(var(--theme_color)); }
.da_toc_d1 a { padding-left: 26px; }

/* --- Docs article responsive ---------------------------------------------
   The contents rail goes first: it is the most redundant of the three columns,
   since everything in it is also visible by scrolling. The tree goes second,
   becoming a panel the top bar opens. */
@media (max-width: 1240px) {
  .da.has_toc .da_shell { grid-template-columns: var(--side_w) minmax(0, 1fr); }
  .da_toc { display: none; }
}
@media (max-width: 960px) {
  .da { --shell: min(760px, 100% - 40px); }
  .da_shell, .da.has_toc .da_shell { grid-template-columns: minmax(0, 1fr); }
  .da_bar_menu { display: inline-flex; }
  .da_crumbs .da_crumb_sep:first-of-type, .da_crumbs a:first-child { display: none; }
  .da_side {
    position: fixed; inset: calc(var(--nav_h) + var(--bar_h)) 0 0 0; z-index: 29;
    display: none; height: auto;
    padding: var(--s3) var(--s3) var(--s5);
    background: rgb(var(--bg));
    border-right: 0;
  }
  .da_side.is_open { display: block; }
  .da_main { padding-top: var(--s4); }
}
@media (max-width: 620px) {
  .da_pager { grid-template-columns: minmax(0, 1fr); }
  .da_pager_r { text-align: left; }
  .da { --measure: none; }
}

/* ==========================================================================
   CHANGELOG — the release index.
   --------------------------------------------------------------------------
   Three hundred releases, and the median entry reads "Minor fixes". Cards would
   give a two-word note the weight of a feature announcement and stretch the
   archive over a mile of whitespace, so releases are rows on a hairline grid.
   The hero draws what a list cannot: two years of release counts as the site's
   own waveform, each bar a link into that month.
   ========================================================================== */
.cl {
  --s2: 16px;
  --s3: 24px;
  --s4: 40px;
  --s5: 64px;
  --s6: 104px;
  --s7: 150px;
  --bar_h: 57px;
}

.cl_hero { padding: var(--s6) 0 var(--s5); }
.cl_hero_copy { max-width: 44rem; }
.cl_hero .tag { margin-bottom: var(--s3); }
.cl_hero_title {
  font-size: clamp(2.4rem, 4.6vw, 3.5rem);
  font-weight: 600; line-height: 1.02; letter-spacing: -.042em;
  margin: 0 0 var(--s3);
}
/* Display-size, so the brand fill clears WCAG's 3:1 large-text floor — the same licence the
   demos hero count takes. */
.cl_hero_count { color: rgb(var(--theme_color)); font-variant-numeric: tabular-nums; }
.cl_hero_lede { font-size: 1.0625rem; line-height: 1.62; max-width: 54ch; margin: 0; }
.cl_hero_lede b { color: rgb(var(--ink)); font-weight: 600; font-variant-numeric: tabular-nums; }
.cl_hero_acts { display: flex; gap: 10px; flex-wrap: wrap; margin-top: var(--s4); }

/* --- Activity graph ------------------------------------------------------
   Bar height is that month's release count against the busiest month in view.
   Empty months are kept as a baseline mark rather than dropped: a quiet quarter
   is a fact about the project, and closing the gaps would draw a busier picture
   than the truth. */
.cl_graph { margin: var(--s5) 0 0; }
.cl_graph_bars {
  display: flex; align-items: flex-end; gap: 5px;
  height: 92px;
  border-bottom: 1px solid rgba(var(--border), .13);
}
.cl_gbar {
  position: relative; flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; align-items: flex-end;
  text-decoration: none;
}
.cl_gbar::after {
  content: ""; display: block; width: 100%;
  height: var(--v, 3px);
  border-radius: 3px 3px 0 0;
  background: linear-gradient(to top, rgba(var(--theme_color), .3), rgb(var(--theme_color)));
  opacity: .85;
  transition: opacity var(--t_fast) var(--ease);
}
.cl_gbar:hover::after { opacity: 1; }
.cl_gbar.is_none::after { height: 3px; border-radius: 2px; background: rgba(var(--border), .16); }
.cl_gbar i {
  position: absolute; bottom: calc(100% + 5px); left: 50%; transform: translateX(-50%);
  font-style: normal; font-size: .6875rem; font-weight: 600;
  font-variant-numeric: tabular-nums; color: rgb(var(--ink));
  opacity: 0; pointer-events: none;
  transition: opacity var(--t_fast) var(--ease);
}
.cl_gbar:hover i { opacity: 1; }
/* Year boundary, run past the axis so the graph has an x-scale without labelling 24 months. */
.cl_gbar.is_year::before {
  content: ""; position: absolute; left: -3px; top: 0; bottom: -8px; width: 1px;
  background: rgba(var(--border), .14);
}
.cl_graph_cap {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 11px;
  font-size: .6875rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
}
.cl_graph_cap_m { color: rgba(var(--ink_faint), .8); }

/* --- The releases -------------------------------------------------------- */
.cl_bar { margin-bottom: var(--s5); }
.cl_list { padding: 0 0 var(--s6); }

.cl_year { display: flex; align-items: center; gap: var(--s3); padding: var(--s5) 0 var(--s2); }
.cl_year:first-child { padding-top: 0; }
.cl_year_n {
  font-size: clamp(1.9rem, 3.2vw, 2.7rem); font-weight: 500;
  letter-spacing: -.045em; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.cl_year_rule { flex: 1; height: 16px; background: var(--wave_rule) repeat-x center; opacity: .85; }

.cl_month {
  display: grid; grid-template-columns: 196px minmax(0, 1fr);
  gap: var(--s5);
  padding: var(--s4) 0;
  border-top: 1px solid rgba(var(--border), .1);
}
.cl_month_label { position: sticky; top: calc(var(--nav_h) + var(--bar_h) + 30px); align-self: start; }
.cl_month_label h2 { font-size: 1.25rem; font-weight: 600; letter-spacing: -.02em; margin: 0; }
.cl_month_y { font-size: .8125rem; color: rgb(var(--ink_faint)); font-variant-numeric: tabular-nums; margin-top: 1px; }
.cl_month_c {
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid rgba(var(--border), .1);
  font-size: .75rem; color: rgb(var(--ink_faint));
  font-variant-numeric: tabular-nums;
}

/* The row is pulled left of its column and padded back, so the hover state can bleed into the
   gutter and read as a struck line rather than as a box that appeared. */
.cl_rel {
  display: grid;
  grid-template-columns: minmax(0, 196px) minmax(0, 1fr) minmax(0, 112px);
  gap: var(--s3);
  margin-left: -14px;
  padding: 13px 14px;
  border-top: 1px solid rgba(var(--border), .07);
  border-radius: 0 var(--radius_sm) var(--radius_sm) 0;
  transition: background var(--t_fast) var(--ease), box-shadow var(--t_fast) var(--ease);
}
.cl_rel:first-child { border-top: 0; }
.cl_rel:hover { background: rgb(var(--bg_alt)); box-shadow: inset 2px 0 0 rgb(var(--theme_color)); }

.cl_rel_id { min-width: 0; }
.cl_rel_plugin {
  display: block; font-size: .875rem; font-weight: 600; letter-spacing: -.008em;
  color: rgb(var(--ink)); text-decoration: none;
  transition: color var(--t_fast) var(--ease);
}
a.cl_rel_plugin:hover { color: rgb(var(--theme_ink)); }
.cl_rel_ver {
  display: block; margin-top: 2px;
  font-size: .75rem; font-weight: 500; letter-spacing: .015em;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--ink_faint));
}

.cl_rel_body { min-width: 0; font-size: .875rem; line-height: 1.6; color: rgb(var(--ink_soft)); }
.cl_rel_body .editorjs_html_wrapper { display: contents; }
.cl_rel_body p { margin: 0 0 .45em; }
.cl_rel_body > :last-child, .cl_rel_body .editorjs_html_wrapper > :last-child { margin-bottom: 0; }
.cl_rel_body :is(ul, ol) { margin: .3em 0 .5em; padding-left: 1.2em; }
.cl_rel_body li { margin-bottom: .2em; }
.cl_rel_body a {
  color: rgb(var(--ink)); text-decoration: none;
  border-bottom: 1px solid rgba(var(--theme_color), .42);
  transition: color var(--t_fast) var(--ease), border-color var(--t_fast) var(--ease);
}
.cl_rel_body a:hover { color: rgb(var(--theme_ink)); border-bottom-color: rgb(var(--theme_color)); }
.cl_rel_body :is(mark, .cdx-marker) { background: rgba(var(--theme_color), .16); color: rgb(var(--ink)); border-radius: 3px; padding: .05em .26em; }
.cl_rel_quiet { color: rgb(var(--ink_faint)); }

.cl_rel_when {
  text-align: right; white-space: nowrap;
  font-size: .75rem; color: rgb(var(--ink_faint));
  font-variant-numeric: tabular-nums;
}
.cl_rel_when .dm_flag { display: inline-block; margin-top: 6px; }

.dm_chip.is_more { border-style: dashed; }
.dm_chip.is_more .mdi { font-size: .8rem; }

/* Nine sections and forty-five plugins both overrun the bar at desktop widths. The row already
   scrolls; the fade is what makes the cut read as "there is more this way" rather than as a
   chip that got sliced in half by the viewport. Scoped to these two pages — the demos bar
   carries ten short chips and never reaches its edge. */
.dx_bar_inner .dm_chips, .cl_bar_inner .dm_chips {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
  padding-right: 6px;
}

/* --- Latest release card -------------------------------------------------
   The one release a returning reader actually came for, lifted out of a list of three hundred.
   Same construction as the demos key card. */
.cl_hero_grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 288px); gap: var(--s5); align-items: start; }
.cl_latest {
  justify-self: end; width: 100%;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .1);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: 0 30px 60px -46px rgba(var(--border), .7);
}
.cl_latest_h {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 14px; padding-bottom: 12px;
  border-bottom: 1px solid rgba(var(--border), .08);
  font-size: .6875rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase;
  color: rgb(var(--ink_faint));
}
.cl_latest_h .mdi { color: rgb(var(--theme_ink)); font-size: .95rem; }
.cl_latest_p { font-size: 1.0625rem; font-weight: 600; letter-spacing: -.018em; }
.cl_latest_v {
  margin-top: 2px; font-size: .875rem; font-weight: 500;
  font-variant-numeric: tabular-nums; color: rgb(var(--theme_ink));
}
.cl_latest_note { margin: 12px 0 0; font-size: .8125rem; color: rgb(var(--ink_soft)); }
.cl_latest_note b { color: rgb(var(--ink)); font-weight: 500; }

/* --- Plugin index -------------------------------------------------------- */
.cl_plugins {
  padding: var(--s6) 0;
  background: rgb(var(--bg_alt));
  border-top: 1px solid rgba(var(--border), .08);
  border-bottom: 1px solid rgba(var(--border), .08);
}
.cl_plugins_head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s4); align-items: end; margin-bottom: var(--s5); }
.cl_plugins_head h2 { margin: var(--s3) 0 0; max-width: 20ch; }
.cl_plugins_head p { margin: 8px 0 0; max-width: 46ch; }
.cl_plugins_count { text-align: right; }
.cl_plugins_count b { display: block; font-size: 2.6rem; font-weight: 500; letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums; }
.cl_plugins_count span { font-size: .8125rem; color: rgb(var(--ink_soft)); }

/* Same per-cell hairlines as the docs index, and for the same reason: forty-five plugins never
   divide evenly into the column count, so a tinted container would end on a grey stub. */
.cl_plugin_grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
  gap: 1px;
  background: rgb(var(--bg));
  border: 1px solid rgba(var(--border), .08);
  border-radius: var(--radius_sm);
  overflow: hidden;
}
.cl_plugin {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 3px 12px; align-items: center;
  padding: 13px 16px;
  background: rgb(var(--bg));
  text-decoration: none;
  box-shadow: -1px 0 0 rgba(var(--border), .08), 0 -1px 0 rgba(var(--border), .08);
  transition: background var(--t_fast) var(--ease), box-shadow var(--t_fast) var(--ease);
}
.cl_plugin:hover, .cl_plugin.is_on {
  background: rgb(var(--bg_alt));
  box-shadow: -1px 0 0 rgba(var(--border), .08), 0 -1px 0 rgba(var(--border), .08), inset 2px 0 0 rgb(var(--theme_color));
}
.cl_plugin_t {
  grid-column: 1; grid-row: 1;
  display: flex; align-items: center; gap: 7px; min-width: 0;
  font-size: .875rem; font-weight: 600; letter-spacing: -.008em;
  color: rgb(var(--ink));
}
.cl_plugin_dot { flex: 0 0 auto; width: 5px; height: 5px; border-radius: 50%; background: rgb(var(--theme_color)); }
/* The same mark inline in the section's lede, so the dot in the grid means something. */
.cl_plugins_head .cl_plugin_dot { display: inline-block; vertical-align: middle; margin: 0 1px; }
.cl_plugin_m {
  grid-column: 1; grid-row: 2;
  display: flex; align-items: baseline; gap: 8px;
  font-size: .75rem; color: rgb(var(--ink_faint));
}
.cl_plugin_m b { font-weight: 500; color: rgb(var(--ink_soft)); font-variant-numeric: tabular-nums; }
.cl_plugin_n {
  grid-column: 2; grid-row: 1 / span 2;
  font-size: .8125rem; color: rgb(var(--ink_faint));
  font-variant-numeric: tabular-nums; text-align: right;
}

.cl_none { padding: var(--s6) 0; text-align: center; }
.cl_none p { margin-bottom: var(--s3); }

.cl_closer { text-align: center; padding: var(--s6) 0 var(--s7); }
.cl_closer h2 { font-size: clamp(1.9rem, 3.4vw, 2.6rem); max-width: 20ch; margin: 0 auto var(--s3); }
.cl_closer p { max-width: 48ch; margin: 0 auto var(--s4); }
.cl_closer_acts { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* --- Changelog responsive ------------------------------------------------- */
@media (max-width: 980px) {
  .cl_hero_grid { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .cl_latest { justify-self: stretch; max-width: 340px; }
  .cl_month { grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
  .cl_month_label { position: static; display: flex; align-items: baseline; gap: 10px; }
  .cl_month_label .cl_month_c { margin: 0 0 0 auto; padding: 0; border-top: 0; }
  .cl_plugins_head { grid-template-columns: minmax(0, 1fr); }
  .cl_plugins_count { text-align: left; }
}
@media (max-width: 860px) {
  .cl { --s5: 44px; --s6: 64px; --s7: 88px; }
  .cl_graph_bars { height: 68px; gap: 3px; }
  .cl_rel { grid-template-columns: minmax(0, 1fr) auto; gap: 4px var(--s3); }
  .cl_rel_id { grid-column: 1; grid-row: 1; }
  .cl_rel_when { grid-column: 2; grid-row: 1; display: flex; align-items: center; gap: 8px; }
  .cl_rel_when .dm_flag { margin-top: 0; }
  .cl_rel_body { grid-column: 1 / -1; grid-row: 2; margin-top: 5px; }
  .cl_closer_acts .btn, .cl_hero_acts .btn { width: 100%; justify-content: center; }
}
@media (max-width: 560px) {
  .cl_graph_cap_m { display: none; }
}
/* Empty / 404 */
.not_found { text-align: center; padding: 100px 24px; }
.not_found h1 { font-size: 5rem; }

/* Footer */
/* The divider was a solid rgb(var(--border)) rule — i.e. near-black, full width, which read
   as a hard slab across the page. It is now a hairline that fades out at both ends and opens
   a gap in the middle for the brand mark. The mark is drawn with a mask rather than a coloured
   data URI so it re-tints per project along with --theme_color. */
.site_footer { position: relative; border-top: 0; padding: 48px 0; margin-top: 40px; }
.site_footer::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg,
    transparent,
    rgba(var(--border), .13) 16%,
    rgba(var(--border), .13) calc(50% - 34px),
    transparent calc(50% - 32px),
    transparent calc(50% + 32px),
    rgba(var(--border), .13) calc(50% + 34px),
    rgba(var(--border), .13) 84%,
    transparent);
}
.site_footer::after {
  content: "";
  position: absolute; top: -6px; left: 50%; transform: translateX(-50%);
  width: 15px; height: 13px;
  background: rgb(var(--theme_color));
  opacity: .8;
  -webkit-mask: var(--footer_mark) no-repeat center / contain;
  mask: var(--footer_mark) no-repeat center / contain;
}
:root {
  --footer_mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='13'%3E%3Cg fill='%23000'%3E%3Crect x='0' y='4' width='2.5' height='5' rx='1.25'/%3E%3Crect x='6' y='1' width='2.5' height='11' rx='1.25'/%3E%3Crect x='12' y='3' width='2.5' height='7' rx='1.25'/%3E%3C/g%3E%3C/svg%3E");
}
.site_footer_inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px; }
.site_footer_brand { display: flex; align-items: center; gap: 10px; font-weight: 700; }
.site_footer_brand img { height: 24px; width: 24px; border-radius: var(--radius_sm); object-fit: contain; }
.site_footer_links { display: flex; flex-wrap: wrap; gap: 18px; }
.site_footer_links a { text-decoration: none; color: rgb(var(--ink_soft)); font-size: .9rem; }
.site_footer_links a:hover { color: rgb(var(--theme_ink)); }
.site_footer_meta { width: 100%; font-size: .82rem; color: rgb(var(--ink_soft)); text-align: center; }

/* Responsive */
@media (max-width: 860px) {
  /* display: contents only matters while collapsed to nothing on desktop; below this width the
     panel becomes a real box again, so site_links and site_cta must go back to being ordinary
     block children instead of flex items reaching for the (now absent) desktop row. */
  .site_nav_panel { display: none; }
  .site_nav_toggle {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-left: auto;
    background: none;
    border: none;
    cursor: pointer;
    padding: 8px;
  }
  .site_nav_toggle span { width: 22px; height: 2px; background: rgb(var(--ink)); }
  /* One panel, one anchor point — site_links and site_cta stack inside it in normal flow instead
     of each becoming its own absolutely-positioned box pinned to the same top offset, which is
     what buried Overview/Demos/Docs under the shorter Support/Store box before. */
  .site_nav.open .site_nav_panel {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: var(--nav_h);
    left: 0;
    right: 0;
    background: rgb(var(--bg));
    border-bottom: 1px solid rgb(var(--border));
    padding: 20px 24px;
    gap: 20px;
  }
  .site_nav.open .site_links,
  .site_nav.open .site_cta {
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
}

@media (max-width: 560px) {
  .hero_actions .btn { width: 100%; justify-content: center; }
}
