/* RXXR manual page.
   Tokens come from tokens.css (shared with the builder).
   This file is standalone — style.css is NOT loaded here. */

* { box-sizing: border-box; }

html {
  color-scheme: dark;
  scrollbar-width: thin;
  scrollbar-color: var(--line) var(--panel);
  scroll-behavior: smooth;
}

body.manual-page {
  margin: 0;
  min-height: 100%;
  font-family: "Roboto Mono", ui-monospace, monospace;
  font-size: 13px;
  line-height: 1.75;
  /* Flat, unlike the builder's diagonal texture — this page is long-form
     reading and the texture only added noise behind the type. */
  background: var(--black);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ============================ view transitions ============================ */
/* Enter-only. The outgoing view is replaced instantly; the incoming one slides
   in along the chapter order — forward when going deeper, back when returning. */

:root { --dur-page: 260ms; }

@keyframes m-view-in {
  from { opacity: 0; transform: translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes m-view-fwd {
  from { opacity: 0; transform: translate3d(18px, 0, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes m-view-back {
  from { opacity: 0; transform: translate3d(-18px, 0, 0); }
  to   { opacity: 1; transform: none; }
}
@keyframes m-stagger-in {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}

/* Swapping a whole view out from under the viewport is exactly the case
   scroll anchoring tries to "help" with, and it fights our own positioning. */
#manualRoot, .m-body { overflow-anchor: none; }

.m-view { animation: m-view-in var(--dur-page) var(--ease-out) both; }
.m-view--fwd { animation-name: m-view-fwd; }
.m-view--back { animation-name: m-view-back; }

/* cards, list rows and sections cascade in behind the view itself */
.m-stagger {
  animation: m-stagger-in var(--dur-page) var(--ease-out) both;
  animation-delay: var(--stagger, 0ms);
}

/* The sidebar exists only inside a chapter, so it re-enters from display:none
   and its animation replays. Opacity only — a transform would fight `sticky`. */
@keyframes m-fade-in { from { opacity: 0; } to { opacity: 1; } }
.m-toc { animation: m-fade-in var(--dur-page) var(--ease-out) both; }

/* Sections of the open chapter arrive one at a time, as the reader gets to
   them — the whole block moves as one piece, nothing inside it animates on its
   own. The hidden state lives only under .m-reveal, which JS adds: without
   scripting, or with reduced motion, the page stays fully visible. */
.m-reveal .m-section {
  opacity: 0;
  transform: translate3d(0, 28px, 0);
  transition:
    opacity 460ms var(--ease-out) var(--reveal-delay, 0ms),
    transform 460ms var(--ease-out) var(--reveal-delay, 0ms);
}
.m-reveal .m-section.is-in { opacity: 1; transform: none; }


@media (prefers-reduced-motion: reduce) {
  .m-view, .m-stagger, .m-toc { animation: none; }
  .m-reveal .m-section { opacity: 1; transform: none; }
}

::selection { background: var(--accent-subtle); color: var(--text); }

a { color: inherit; }

/* ============================ top bar ============================ */

.m-top {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: var(--header-h);
  padding: 0 var(--sp-3);
  background: var(--black);
  border-bottom: 1px solid var(--line);
}

.m-brand { display: inline-flex; align-items: center; flex: 0 0 auto; }
.m-brand-logo { display: block; height: 20px; width: auto; }

.m-top-title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
  padding-left: var(--sp-1);
  border-left: 1px solid var(--line);
  flex: 0 0 auto;
}

/* search */
.m-search {
  position: relative;
  display: flex;
  align-items: center;
  margin-left: auto;
  width: min(280px, 40vw);
  flex: 0 1 auto;
}
.m-search-icon {
  position: absolute;
  left: 9px;
  color: var(--muted);
  pointer-events: none;
}
.m-search-input {
  width: 100%;
  height: 32px;
  padding: 0 28px 0 28px;
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--dur-ui) var(--ease-out);
  -webkit-appearance: none;
  appearance: none;
}
.m-search-input::placeholder { color: var(--muted); }
.m-search-input::-webkit-search-cancel-button { display: none; }
.m-search-input:hover { border-color: var(--line-hot); }
.m-search-input:focus { outline: none; border-color: var(--accent-focus); }
.m-search-clear {
  position: absolute;
  right: 4px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: inherit;
  font-size: 15px;
  line-height: 1;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.m-search-clear:hover { color: var(--text); }

/* language selector — mirrors .lang-select in style.css */
.lang-select {
  align-self: center;
  height: 32px;
  padding: 0 26px 0 10px;
  font: inherit;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--muted);
  background-color: transparent;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%237d7d80' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  flex: 0 0 auto;
  -webkit-appearance: none;
  appearance: none;
  transition: color var(--dur-ui) var(--ease-out), border-color var(--dur-ui) var(--ease-out);
}
.lang-select:hover { color: var(--text); border-color: var(--line-hot); }
.lang-select:focus { outline: none; border-color: var(--accent-focus); color: var(--text); }
.lang-select option { background: var(--black); color: var(--text); }
/* Once dropdown.js runs, the native select is hidden and .lang-dropdown (styled
   in dropdown.css) stands in its place. The header owns the spacing here, so
   the builder's own trailing margin comes off. */
.m-top .lang-dropdown { margin-right: 0; }

.m-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--muted);
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  flex: 0 0 auto;
  transition: color var(--dur-ui) var(--ease-out), border-color var(--dur-ui) var(--ease-out);
}
.m-back:hover { color: var(--text); border-color: var(--line-hot); }
.m-back svg { flex: 0 0 auto; display: block; }

/* ============================ shell ============================ */

/* Full-bleed: the sidebar is pinned to the left edge of the viewport, not
   floated inside a centred column. Reading width is capped further down,
   on the content blocks themselves. */
.m-shell {
  --toc-w: 264px; /* also the offset the reading column's break-out budgets for */
  display: grid;
  grid-template-columns: var(--toc-w) minmax(0, 1fr);
  gap: 0;
  margin: 0;
  padding: 0;
  align-items: start;
}

/* ---- table of contents ---- */

.m-toc-toggle { display: none; }

/* Always exactly the height of the viewport below the header, so the divider
   runs edge to edge instead of stopping where the list happens to end. */
.m-toc {
  position: sticky;
  top: var(--header-h);
  /* Above a hovered tile (z-index 3), below the top bar (30) — a clip that
     grows past the reading column must not cover the chapter list. */
  z-index: 10;
  background: var(--black);
  height: calc(100vh - var(--header-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-3) var(--sp-2) var(--sp-3) var(--sp-3);
  border-right: 1px solid var(--line);
}

.m-toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: toc;
}

.m-toc-item + .m-toc-item { margin-top: 1px; }

.m-toc-link {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 5px 10px;
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--muted);
  text-decoration: none;
  border-left: 2px solid transparent;
  border-radius: 0 var(--radius) var(--radius) 0;
  transition: color var(--dur-ui) var(--ease-out), background-color var(--dur-ui) var(--ease-out);
}
.m-toc-link:hover { color: var(--text); background: rgba(255,255,255,0.04); }
.m-toc-link.active {
  color: var(--text);
  border-left-color: var(--accent);
  background: var(--accent-subtle);
}

.m-toc-num {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 700;
  color: var(--line-hot);
  opacity: 0.45;
  font-variant-numeric: tabular-nums;
}
.m-toc-link.active .m-toc-num { color: var(--accent); opacity: 1; }
.m-toc-label { flex: 1 1 auto; }

.m-toc-group {
  margin: var(--sp-3) 0 6px;
  padding: 0 10px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
  opacity: 0.6;
}
.m-toc-group:first-child { margin-top: 0; }

.m-toc-link--home {
  margin-bottom: var(--sp-1);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1.5px;
}

/* sub-sections of the chapter you are currently reading */
.m-toc-sub {
  position: relative;
  list-style: none;
  margin: 2px 0 6px;
  padding: 0 0 0 22px;
}

/* One accent stripe that drives to the active item, instead of every row
   repainting its own border the instant the scroll spy switches. */
.m-toc-rail {
  position: absolute;
  left: 22px; /* the .m-toc-sub padding — the rail rides the sublinks' own line */
  top: 0;
  width: 1px;
  height: var(--rail-h, 0px);
  background: var(--accent);
  opacity: 0;
  transform: translate3d(0, var(--rail-y, 0px), 0);
  transition:
    transform 240ms var(--ease-in-out),
    height 240ms var(--ease-in-out),
    opacity var(--dur-ui) var(--ease-out);
  pointer-events: none;
}
.m-toc-rail.is-on { opacity: 1; }
.m-toc-sublink {
  display: block;
  padding: 3px 10px;
  font-size: 10px;
  letter-spacing: 0.5px;
  color: var(--muted);
  text-decoration: none;
  border-left: 1px solid var(--line);
  transition: color var(--dur-ui) var(--ease-out), border-color var(--dur-ui) var(--ease-out);
}
.m-toc-sublink:hover { color: var(--text); border-left-color: var(--line-hot); }
.m-toc-sublink.active { color: var(--accent); border-left-color: transparent; }

/* ---- content column ---- */

/* Sidebar hugs the viewport edge, but the text does not: one reading column,
   centred in whatever space is left over. Prose sets the measure and everything
   else lines up on it — headings, media and the slider share the same two edges.
   The three grid blocks are the exception: they break out of the measure
   symmetrically (see --bleed), so a four-across grid stays legible without
   dragging the prose out to a 140-character line. */
.m-body {
  --measure: 880px;
  /* Whatever is left beside the measure once the sidebar and the page padding
     are paid for, capped so a break-out block never grows past 1520px. Goes to
     0 on its own when there is no room, which is what narrow screens want. */
  --bleed: clamp(
    0px,
    calc((100vw - var(--toc-w) - var(--measure)) / 2 - var(--sp-3)),
    320px
  );
  min-width: 0;
  width: 100%;
  max-width: calc(var(--measure) + var(--sp-3) * 2);
  margin: 0 auto;
  padding: var(--sp-3) var(--sp-3) 64px var(--sp-3);
}

.m-body > * { max-width: none; }

.m-params,
.m-shots,
.m-gradients { margin-inline: calc(-1 * var(--bleed)); }

.m-banner {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 0 0 var(--sp-3);
  padding: 10px 12px;
  font-size: 11px;
  line-height: 1.6;
  color: var(--text);
  background: var(--accent-subtle);
  border: 1px solid rgba(187,234,85,0.25);
  border-radius: var(--radius);
}
/* display:flex outranks the UA rule for [hidden] — say it again here */
.m-banner[hidden] { display: none; }
.m-banner svg {
  flex: 0 0 auto;
  margin-top: 2px;
  color: var(--accent);
}


.m-empty {
  padding: 40px 0;
  font-size: 12px;
  color: var(--muted);
  text-align: center;
}

/* ============================ home: chapter cards ============================ */

/* The home screen is the chapter grid at full width — the sidebar and the
   760px reading column only make sense once you are inside a chapter. */
body.is-home .m-shell {
  grid-template-columns: minmax(0, 1fr);
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 var(--sp-3);
}
body.is-home .m-toc,
body.is-home .m-toc-toggle { display: none; }
body.is-home .m-body { max-width: none; padding-left: 0; padding-right: 0; padding-bottom: 80px; }
body.is-home .m-body > * { max-width: none; }
body.is-home .m-banner { max-width: 720px; }
body.is-home .m-foot { max-width: none; }

.m-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 32px var(--sp-3);
}

.m-card {
  display: flex;
  flex-direction: column;
  text-decoration: none;
}

.m-card-media {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform var(--dur-ui) var(--ease-out);
}
.m-card:hover .m-card-media { transform: translateY(-3px); }
.m-card:focus-visible { outline: none; }
.m-card:focus-visible .m-card-media { outline: 1px solid var(--accent-focus); outline-offset: 2px; }
.m-card-media video,
.m-card-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* no clip recorded yet — a deliberate slot, not a broken thumbnail.
   Kept flat: the page background is already a diagonal texture. */
.m-card-media--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel);
}
.m-card-num {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 38px;
  font-weight: 800;
  letter-spacing: 1px;
  color: var(--line-hot);
  opacity: 0.09;
  font-variant-numeric: tabular-nums;
  transition: opacity var(--dur-ui) var(--ease-out);
}
.m-card:hover .m-card-num { opacity: 0.16; }

.m-card-body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1 1 auto;
  padding: var(--sp-2) 2px 0;
}
.m-card-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.2px;
  color: var(--text);
  transition: color var(--dur-ui) var(--ease-out);
}
.m-card:hover .m-card-title { color: var(--accent); }
.m-card-blurb {
  margin: 0;
  flex: 1 1 auto;
  font-size: 11px;
  line-height: 1.65;
  color: var(--label);
}

/* secondary chapters — no card, just a list */
.m-home-more-label {
  margin: 56px 0 var(--sp-1);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
}
.m-more {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 2px;
}
.m-more-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-1);
  padding: 12px var(--sp-2);
  font-size: 11px;
  letter-spacing: 0.5px;
  color: var(--label);
  text-decoration: none;
  background: var(--panel);
  border-radius: var(--radius);
  transition: color var(--dur-ui) var(--ease-out), background-color var(--dur-ui) var(--ease-out);
}
.m-more-link:hover { color: var(--accent); background: var(--panel-2); }
.m-more-link svg { flex: 0 0 auto; opacity: 0.5; }

/* ============================ chapter page ============================ */

.m-crumb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--sp-3);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
  text-decoration: none;
  transition: color var(--dur-ui) var(--ease-out);
}
.m-crumb:hover { color: var(--accent); }
.m-crumb svg { flex: 0 0 auto; }

.m-page-title {
  margin: 0 0 var(--sp-2);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.5px;
}
.m-page > .m-section:first-of-type { border-top: 0; padding-top: var(--sp-2); }

/* prev / next */
.m-pagenav {
  display: flex;
  gap: var(--sp-2);
  margin-top: 48px;
}
.m-pagenav-link {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex: 1 1 0;
  min-width: 0;
  padding: var(--sp-2);
  text-decoration: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--dur-ui) var(--ease-out);
}
.m-pagenav-link:hover { border-color: var(--line-hot); }
.m-pagenav-link svg { flex: 0 0 auto; color: var(--muted); }
.m-pagenav-link--next { margin-left: auto; justify-content: flex-end; text-align: right; }
.m-pagenav-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.m-pagenav-dir {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
}
.m-pagenav-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.m-pagenav-link:hover .m-pagenav-title { color: var(--accent); }

/* ============================ search results ============================ */

.m-results-label {
  margin: 0 0 var(--sp-2);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--muted);
}
.m-result-page {
  display: inline-block;
  margin-bottom: 8px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1.5px;
  color: var(--accent);
  text-decoration: none;
}
.m-result-page:hover { text-decoration: underline; }

/* ---- section ---- */

.m-section {
  padding: 44px 0;
  border-top: 1px solid var(--line);
  scroll-margin-top: calc(var(--header-h) + var(--sp-2));
}
.m-section[hidden] { display: none; }

.m-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 var(--sp-2);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.2px;
}

.m-anchor {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-ui) var(--ease-out), color var(--dur-ui) var(--ease-out);
}
.m-section:hover .m-anchor,
.m-anchor:focus-visible { opacity: 1; }
.m-anchor:hover { color: var(--accent); }
.m-anchor.copied { opacity: 1; color: var(--accent); }

.m-lead {
  margin: 0 0 28px;
  font-size: 13px;
  color: var(--label);
}

/* ---- blocks ---- */

.m-text { margin: 0 0 var(--sp-2); color: var(--text); }
.m-text:last-child { margin-bottom: 0; }

.m-list {
  margin: 0 0 var(--sp-2);
  padding-left: 18px;
  color: var(--text);
}
.m-list li { margin-bottom: 4px; }
.m-list li::marker { color: var(--accent); }

code, .m-code {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 0.92em;
  padding: 1px 5px;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

strong { font-weight: 700; color: var(--text); }

/* parameter rows */
.m-params {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  column-gap: 40px;
  row-gap: 10px;
  margin: 0 0 40px;
  border-top: 1px solid var(--line);
}
.m-param {
  display: flex;
  flex-direction: column;
  padding: 22px 0 26px;
  border-bottom: 1px solid var(--line);
}
.m-param[hidden] { display: none; }
.m-param-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}
/* control-type glyph in front of the name — slider / checkbox / popup /
   colour. Muted, so it marks the row without competing with the name. */
.m-param-type {
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  color: var(--muted);
}
.m-param-type svg { display: block; }

.m-param-name {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 14px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.2px;
}
.m-param-values {
  font-size: 10px;
  letter-spacing: 0.5px;
  color: var(--muted);
}
.m-inline-link {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
}
.m-inline-link:hover { border-bottom-color: var(--accent); }

.m-param-body { margin: 0; color: var(--label); }
.m-param-body code { color: var(--text); }

/* range + default, generated from the plugin source.
   Visual is the builder's tick slider (.tickslider in style.css) minus the
   fill — nothing on this page is set to anything, so the track stays neutral
   and only the pin says where the default sits. */
.m-slider {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 360px;
  margin-top: auto;
  padding-top: 22px;
}

.m-tickslider {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 100%;
  height: 22px;
}
.m-tickslider i {
  flex: 1;
  height: 13px;
  display: block;
  background: #1d1d1f;
}
.m-tickslider i.lead {
  height: 22px;
  background: linear-gradient(var(--pin), var(--pin)) center / 3px 100% no-repeat;
}

.m-slider-scale {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 9px;
  letter-spacing: 0.5px;
  font-variant-numeric: tabular-nums;
}
.m-slider-range { flex: 1; color: var(--muted); }

/* "DEFAULT" — one label for both spec lines, so a number and a mode read the
   same way down the column. */
.m-spec-label {
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--muted);
}
.m-slider-value {
  font-size: 10px;
  letter-spacing: 0;
  font-weight: 700;
  color: var(--text);
}

/* non-numeric parameters state their default next to a mock of their own
   control — a checkbox, a popup, a colour swatch — so the kind of control is
   visible before the line is read. Same measure and baseline as the slider
   spec, value flush right, so a column of mixed parameters still lines up. */
.m-default {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 360px;
  min-height: 22px;
  margin-top: auto;
  padding-top: 22px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
}
.m-default .m-spec-label { flex: 1; }
.m-default-value {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: 0;
  font-weight: 700;
  color: var(--text);
}
.m-default-value.is-on { color: var(--accent); }
.m-default-value.is-off { color: var(--muted); }

/* checkbox — off is an empty box, on is filled with the accent and ticked,
   the same read as the plugin's own panel. */
.m-check {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
}
.m-check.is-on {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--black);
}
.m-check svg { display: block; }

/* popup — a caret is what makes a value read as "one of a list". Borrows the
   builder's .dropdown-trigger feel without being interactive. */
.m-select {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 3px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
}
.m-select-caret {
  font-size: 9px;
  line-height: 1;
  color: var(--muted);
}

/* colour — the border keeps both #000000 and #FFFFFF readable on the page's
   own dark ground. */
.m-swatch {
  flex: 0 0 auto;
  display: block;
  width: 14px;
  height: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

/* tip / note callout — an accent rail instead of a filled strip: the manual
   leans on these often, and a full-width lime bar pulled more attention than
   the prose it annotates. The rail marks the block, the glyph carries the tone. */
.m-tip {
  margin: 0 0 var(--sp-3);
  padding: 12px 14px;
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--panel);
}
.m-tip-mark {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--label);
}
/* the quiet tone, for limitations that shouldn't spend the accent */
.m-tip--quiet { border-left-color: var(--line-hot); }
.m-tip-mark svg { flex: 0 0 auto; color: var(--accent); }
.m-tip--quiet .m-tip-mark svg { color: var(--text); }
.m-tip-body {
  margin: 0;
  font-size: 12px;
  line-height: 1.65;
  color: var(--label);
}

/* symptom → fix, collapsed until asked for */
.m-faq {
  margin: 0 0 var(--sp-3);
  border-top: 1px solid var(--line);
}
.m-faq-item { border-bottom: 1px solid var(--line); }
.m-faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 14px 2px;
  font-size: 13px;
  font-weight: 700;
  color: var(--label);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-ui) var(--ease-out);
}
.m-faq-q::-webkit-details-marker { display: none; }
.m-faq-q:hover { color: var(--text); }
.m-faq-item[open] .m-faq-q { color: var(--accent); }
.m-faq-q svg {
  flex: 0 0 auto;
  opacity: 0.5;
  transition: transform var(--dur-ui) var(--ease-out);
}
.m-faq-item[open] .m-faq-q svg { transform: rotate(180deg); }
.m-faq-body { margin: 0 0 10px; color: var(--label); }
.m-faq-item .m-list { margin-top: 0; padding-bottom: 6px; }

/* gradient presets — swatches built from the plugin's own stop table.
   Laid out as a hairline table like .m-params, and the ramp keeps the
   proportions of the plugin's own preset preview (.gradient-option-preview):
   a thin strip, not a color card. */
.m-gradients {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  column-gap: 40px;
  row-gap: 0;
  margin: 0 0 40px;
  border-top: 1px solid var(--line);
}
.m-gradient {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 0;
  padding: 13px 0;
  border-bottom: 1px solid var(--line);
}
.m-gradient-name {
  flex: 0 0 auto;
  width: 108px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.m-gradient-bar {
  flex: 1 1 auto;
  min-width: 0;
  height: 12px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-sm);
}

/* video example */
.m-video {
  margin: 0 0 var(--sp-3);
  padding: 0;
}
.m-video-frame {
  position: relative;
  display: block;
  width: 100%;
}
.m-video-clip {
  display: block;
  width: 100%;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.m-video-clip video {
  display: block;
  width: 100%;
  height: auto;
}
.m-video-caption {
  margin: 8px 0 0;
  font-size: 11px;
  color: var(--muted);
}

/* before / after: both shots stacked, the "after" one clipped at --pos.
   --pos is a bare 0–100 number so the label fades can do maths on it. */
.m-compare {
  --pos: 50;
  /* Matches the parameter table's trailing space — a media block needs more
     air under it than the 16px prose rhythm, or the next paragraph sits on it. */
  margin: 0 0 40px;
  padding: 0;
}
.m-compare-frame {
  position: relative;
  display: block;
  width: 100%;
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: ew-resize;
  touch-action: pan-y; /* vertical scrolling still belongs to the page */
  user-select: none;
  -webkit-user-select: none;
}
.m-compare-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
}
.m-compare-after {
  position: absolute;
  inset: 0;
  clip-path: inset(0 0 0 calc(var(--pos) * 1%));
}
.m-compare-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--pos) * 1%);
  width: 1px;
  background: var(--accent);
  pointer-events: none;
}
.m-compare-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
  width: 34px;
  height: 34px;
  margin: 0;
  padding: 0;
  color: var(--ink);
  background: var(--accent);
  border: none;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  cursor: ew-resize;
  pointer-events: auto;
  transition: transform var(--dur-ui) var(--ease-out);
}
.m-compare-handle svg { display: block; }
.m-compare-frame:hover .m-compare-handle { transform: translate(-50%, -50%) scale(1.08); }
.m-compare-frame.is-dragging .m-compare-handle { transform: translate(-50%, -50%) scale(0.94); }
.m-compare-handle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Side labels sit clear of the divider and fade out once it reaches them. */
.m-compare-tag {
  position: absolute;
  top: 10px;
  padding: 4px 8px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--text);
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  pointer-events: none;
}
.m-compare-tag--before { left: 10px; opacity: clamp(0, calc((var(--pos) - 14) / 6), 1); }
.m-compare-tag--after { right: 10px; opacity: clamp(0, calc((86 - var(--pos)) / 6), 1); }

@media (prefers-reduced-motion: reduce) {
  .m-compare-handle { transition: none; }
}

/* ===================== hover: zoom + lime caption plate =====================
   Any clip on the page behaves the same under the cursor: the frame grows and
   the caption it already carries is repeated on a lime plate that hangs off
   the frame's bottom edge — never over the picture, which is the thing the
   cursor came to look at. The plate lives outside the clip, so `top: 100%`
   in the frame's own coordinates lands on the *visual* bottom edge of the
   zoomed frame for free; the counter-scale keeps its type at natural size. */
.m-hint {
  position: absolute;
  top: 100%;
  /* Laid out `--zoom` times too wide and then counter-scaled by the same
     number, the plate renders at exactly the zoomed frame's width — the two
     edges have to line up, or the plate reads as a stray tag under the clip.
     The extra width is split either side so the centres stay put. */
  left: calc(50% - 50% * var(--zoom));
  width: calc(100% * var(--zoom));
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 10px;
  background: var(--accent);
  color: var(--ink);
  pointer-events: none;
  opacity: 0;
  transform-origin: top center;
  transform: scale(calc(1 / var(--zoom))) translate3d(0, 4px, 0);
  transition: opacity 260ms var(--ease-out),
              transform 260ms var(--ease-out);
}
.m-hint-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
}
.m-hint-note {
  font-size: 11px;
  line-height: 1.45;
  opacity: 0.78;
}
/* The growing frame belongs inside a chapter, where the tiles are the subject
   of the page. The home grid keeps its old, quiet lift. */
.m-shot,
.m-video {
  position: relative;
  --zoom: 1.15; /* one number for the zoom and the plate's counter-scale */
}
/* The frame outranks its own caption — the grown tile must not end up under
   the label that belongs to it. */
.m-shot-frame,
.m-video-frame { z-index: 2; }
.m-shot-cap,
.m-video-caption { position: relative; z-index: 1; }

@media (hover: hover) and (pointer: fine) {
  .m-shot-frame,
  .m-video-frame { transition: transform 260ms var(--ease-out); }
  .m-shot-clip,
  .m-video-clip { transition: box-shadow 260ms var(--ease-out); }

  /* Driven from the block, not the frame: the caption belongs to the tile,
     so crossing it must not drop the tile back down. */
  .m-shot:hover .m-shot-frame,
  .m-video:hover .m-video-frame {
    transform: scale(var(--zoom));
  }
  .m-shot:hover .m-shot-clip,
  .m-video:hover .m-video-clip { box-shadow: 0 28px 70px rgba(0, 0, 0, 0.7); }

  /* The hovered tile sits over everything around it. */
  .m-shot:hover,
  .m-video:hover { z-index: 3; }

  /* The rest of the grid drops out of focus. `:has` keeps the gaps neutral —
     the blur belongs to a hovered tile, not to the grid. */
  .m-shots-grid .m-shot { transition: filter 260ms var(--ease-out); }
  .m-shots-grid:has(.m-shot:hover) .m-shot:not(:hover) {
    filter: blur(4px) brightness(0.5);
  }

  .m-shot:hover .m-hint,
  .m-video:hover .m-hint {
    opacity: 1;
    transform: scale(calc(1 / var(--zoom)));
  }

  /* The plate says the same thing as the caption under the tile, and it sits
     right on top of it — one copy at a time, or the tail of the caption reads
     as a second, ragged line under the lime. */
  .m-shot-cap,
  .m-video-caption { transition: opacity 260ms var(--ease-out); }
  .m-shot:hover .m-shot-cap,
  .m-video:hover .m-video-caption { opacity: 0; }
}


/* Reduced motion keeps the frame at rest; the plate still appears under it,
   full tile width, and still clears the picture and the native controls. */
@media (prefers-reduced-motion: reduce) {
  .m-shot,
  .m-video { --zoom: 1; }
  .m-hint { transform: none; }
  .m-shot:hover .m-shot-frame,
  .m-video:hover .m-video-frame { transform: none; }
  .m-shots-grid:has(.m-shot:hover) .m-shot:not(:hover) { filter: none; }
}

/* shot grid: one variant per tile, breaking out past the reading measure.
   Two across is the default because the tiles carry fine texture — a dither
   pattern read at 270px is just noise, at ~670px it is the actual pattern. */
.m-shots {
  --cols: 2;
  margin: 0 0 40px; /* same trailing air as the compare block and the param table */
  padding: 0;
}
.m-shots-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  /* The caption sits under its own tile, so the row gap has to beat the
     caption's own offset by a wide margin — otherwise a label reads as if it
     belonged to the tile below it. */
  column-gap: 10px;
  row-gap: 28px;
}
.m-shot-frame { position: relative; }
.m-shot-clip {
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.m-shot-clip img,
.m-shot-clip video,
/* drawn tiles (grid markers) scale exactly like a still */
.m-shot-clip svg {
  display: block;
  width: 100%;
  height: auto;
}
.m-shot-cap {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 6px;
}
.m-shot-labelrow {
  display: flex;
  align-items: center;
  gap: 6px;
}
.m-shot-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--text);
}
.m-shot-new {
  flex: none;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--accent-subtle);
  color: var(--accent);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 1px;
  line-height: 1.5;
}
.m-shot-note {
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted);
}
.m-shots-caption {
  margin: 12px 0 0;
  font-size: 11px;
  color: var(--muted);
}

/* the marker gallery: vector tiles with a 2x2 patch of cells in each. They
   stay legible at thumbnail size, so they run as one dense row instead of
   eating a stills-sized grid — and they keep that row on narrow screens. */
.m-shots--compact .m-shots-grid {
  grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  column-gap: 8px;
  row-gap: 14px;
}

/* a grid living inside a parameter row: the row gives it the full table width,
   the figure drops the trailing air a standalone block needs */
.m-param--wide { grid-column: 1 / -1; }
/* A card with examples breaks out to the whole grid width, so its spec has to
   follow: clipped at the 360px column measure the default hung in the middle
   of the card instead of lining up with the edge of the tiles under it. */
.m-param--wide .m-slider,
.m-param--wide .m-default { max-width: none; }
.m-shots--inline { margin: 16px 0 0; }
.m-shots--inline .m-shots-caption,
.m-shots--inline .m-compare-caption { margin-top: 10px; }

/* Once the break-out is gone the pair would be back to postage stamps, so the
   tiles go single file and keep their size instead. */
@media (max-width: 900px) {
  .m-shots-grid { grid-template-columns: minmax(0, 1fr); }
  .m-shots--compact .m-shots-grid {
    grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
  }
}

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

.m-foot {
  display: flex;
  gap: var(--sp-3);
  margin-top: 48px;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
}
.m-foot-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--muted);
  text-decoration: none;
  transition: color var(--dur-ui) var(--ease-out);
}
.m-foot-link svg { flex: 0 0 auto; display: block; opacity: 0.6; }
.m-foot-link:hover { color: var(--accent); }
.m-foot-link:hover svg { opacity: 1; }

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

@media (max-width: 1000px) {
  .m-shell {
    grid-template-columns: minmax(0, 1fr);
    padding: 0 var(--sp-2);
  }

  .m-toc-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    margin-top: var(--sp-2);
    padding: 10px 12px;
    font: inherit;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 2px;
    color: var(--muted);
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    cursor: pointer;
  }
  .m-toc-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
  .m-toc-toggle svg { transition: transform var(--dur-menu) var(--ease-out); }

  .m-toc {
    position: static;
    height: auto;
    max-height: 50vh;
    padding: var(--sp-2) 0;
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .m-toc[hidden] { display: none; }

  .m-body { max-width: none; padding: var(--sp-3) 0 48px; }
  .m-body > * { max-width: none; }
}

@media (max-width: 720px) {
  .m-top {
    height: auto;
    flex-wrap: wrap;
    padding: var(--sp-1) var(--sp-2);
    row-gap: var(--sp-1);
  }
  .m-top-title { order: 2; }
  .lang-select,
  .m-top .lang-dropdown { order: 3; margin-left: auto; }
  .m-back { order: 4; }
  .m-search { order: 5; width: 100%; margin-left: 0; }

  .m-toc { max-height: 60vh; }
  .m-page-title { font-size: 21px; }
  .m-section { padding: 26px 0; }
  .m-section-head { font-size: 17px; }

  .m-cards { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-2); }
  .m-card-num { font-size: 32px; }
  .m-pagenav { flex-direction: column; }
  .m-pagenav-link--next { margin-left: 0; }
}
