/* sprites.org — the site stylesheet, built on stylesheet.org.
 *
 * Sections 1–5 are the stylesheet.org framework verbatim (semantic HTML, the
 * row / column / grid / stretch layout, the sunshine-yellow theme and the shared
 * components). Section 6 adds what sprites.org needs on top: the retro sprite
 * editor — a dark, glowing green canvas with chunky bevelled tool buttons,
 * carried over from the original sprites site — plus its pixel grid.
 *
 * The file is in six sections. A theme only ever changes section 2.
 *   1. RESET          — constant
 *   2. THEME TOKENS   — :root variables (colour, type, space, shape)
 *   3. LAYOUT         — constant: row, column, grid, flow, stretch
 *   4. ELEMENTS       — constant: semantic tags styled directly
 *   5. COMPONENTS     — constant: panel, card, chip, buttons, status, paging, menu
 *   6. SPRITE EDITOR  — sprites.org only: the retro editor and pixel grid
 *
 * Layout model: children of .row / .column take their natural size and stack
 * from the start — a fixed item is just a width/height, no class. .stretch is
 * the one flexing element (empty = a spacer; with content = a flexible region;
 * .stretch-2 / .stretch-3 weight the shares).
 */

/* ============================================================
   1. RESET
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body { min-height: 100vh; overflow-x: hidden; display: flex; flex-direction: column; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
img, svg, video { max-width: 100%; height: auto; display: block; }

/* ============================================================
   2. THEME TOKENS  — the only section a theme changes  (white · green · grey)
   ============================================================ */
:root {
  /* colour */
  --bg: #f6f9f7;               /* near-white, faint green-grey page base */
  --surface: #ffffff;          /* panels, cards, the intro area */
  --surface-soft: #eef4f0;     /* nested panels / soft fills */
  --section-bg: #eef3f1;       /* pale grey-green */
  --band: #e4ece7;             /* slightly darker — the full-width section band */
  --pad: 28px;                 /* content side padding */
  --header-bg: #dcefe4;        /* pale green — the menu */
  --header-bg-2: #eaf5ef;      /* paler band for a stacked sub-nav */
  --header-bg-3: #f2f9f5;      /* very pale green — the third-level sub-nav, paler still */
  --fg: #26302a;               /* dark grey-green text */
  --heading: #14201a;
  --muted: #667a6f;
  --border: #dbe6e0;           /* soft grey-green border */
  --border-strong: #c0d3c9;
  --accent: #15803d;           /* green — buttons, links, active */
  --accent-hover: #116631;
  --accent-fg: #ffffff;
  --accent-tint: #d8f0e1;      /* light green — hovers, focus, quotes */
  --accent-2: #0d9488;         /* teal — for "active" chips/badges */
  --accent-2-tint: #d5f3ef;
  --code-bg: #1b2420;          /* dark grey-green code block */
  --code-fg: #e7efe9;
  /* syntax highlighting — bright tokens on the warm-dark code block */
  --hl-tag:     #7cc4ff;       /* html tags · css at-rules */
  --hl-attr:    #f0c66b;       /* html attributes · css properties */
  --hl-value:   #8fe0a0;       /* html attribute values · css values · js strings */
  --hl-comment: #9b9b9b;       /* comments, any language — grey */
  --hl-kw:      #cbb6f5;       /* js keywords */
  --hl-fn:      #7fd6e0;       /* js function and method names */
  --hl-num:     #f0a98a;       /* js numbers */
  --ok-fg: #04603e; --ok-bg: #e9fbf2; --ok-bd: #a6e9c8;
  --err-fg: #ad1b14; --err-bg: #fdf0ef; --err-bd: #f6c9c6;
  --info-fg: #2f4ad6; --info-bg: #eef1ff; --info-bd: #c5cefb;
  --warn-fg: #9a5a05; --warn-bg: #fff4d6; --warn-bd: #f3df9a;
  /* type */
  --font: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-logo: ui-monospace, 'SF Mono', 'JetBrains Mono', 'Fira Code', Menlo, Consolas, monospace;   /* the code-style wordmark / hero title */
  --mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  /* space + shape */
  --gap: 16px;                 /* the one spacing rhythm — content flow, rows, columns, grids */
  --sep-1: 30px;               /* section separation, by nesting depth — first level, level with the function sections… */
  --sep-2: 30px;               /*   …one level in… */
  --sep-3: 21px;               /*   …two levels in */
  --radius: 14px;
  --radius-sm: 9px;
  --radius-pill: 999px;
  --maxw: 1040px;
  --shadow-sm: 0 1px 2px rgba(30, 60, 45, 0.05), 0 1px 3px rgba(30, 60, 45, 0.07);
  --shadow-md: 0 6px 14px -4px rgba(30, 60, 45, 0.12), 0 2px 6px -2px rgba(30, 60, 45, 0.07);
  --shadow-lg: 0 20px 30px -12px rgba(30, 60, 45, 0.18);
}

/* ============================================================
   3. LAYOUT  — structure only; references --gap, never colour
   ============================================================ */
.row, .column { display: flex; gap: var(--gap); }
.row { flex-direction: row; }
.column { flex-direction: column; }
.row > *, .column > * { flex: 0 0 auto; min-width: 0; min-height: 0; }   /* natural size, from the start */
.stretch { flex: 1 1 0; min-width: 0; min-height: 0; }                    /* eats / shares the remainder */
.stretch-2 { flex-grow: 2; }
.stretch-3 { flex-grow: 3; }
.grid { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.flow { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
/* explicit grids shed a column at a time as the viewport narrows */
@media (max-width: 1100px) { .grid.cols-4 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 980px) { .grid.cols-3, .grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px) { .grid.cols-2, .grid.cols-3, .grid.cols-4 { grid-template-columns: 1fr; } }

/* ============================================================
   4. ELEMENTS  — semantic tags styled directly
   ============================================================ */
body {
  background: var(--surface);   /* white hero: the intro area sits on a flat white ground across the site */
  color: var(--fg);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.65;
}

/* typography */
h1, h2, h3, h4, h5, h6 { color: var(--heading); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; }
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
/* a function signature line, shown in full under the plain function name in a reference heading */
p.signature { margin-top: 6px; }
h3 { font-size: 1.2rem; letter-spacing: -0.01em; }
h4 { font-size: 1.02rem; letter-spacing: 0; }
h5, h6 { font-size: 0.9rem; letter-spacing: 0; }
/* Headings carry no spacing of their own. Within a part everything is one gap; the only
   margin in the flow is a section's own top margin (see the sections rules below), and a
   heading is always its section's first child, so it sits flush with nothing to reset. */
a { color: var(--accent); text-decoration: none; transition: color 0.15s ease; }
a:hover { color: var(--accent-hover); text-decoration: underline; }
strong { font-weight: 650; color: var(--heading); }
small { color: var(--muted); font-size: 0.875em; }
ul, ol { padding-left: 1.4em; }
li { margin: 5px 0; }
li::marker { color: var(--accent); }
/* description list — term → description pairs, laid out as two columns */
dl { display: grid; grid-template-columns: max-content 1fr; gap: 8px 18px; }
dt { font-weight: 600; color: var(--heading); }
dd { color: var(--fg); }
hr { border: none; border-top: 1px solid var(--border); }
code { font-family: var(--mono); font-size: 0.88em; background: var(--surface-soft); border: 1px solid var(--border); padding: 0.1em 0.4em; border-radius: 6px; }
pre { background: var(--code-bg); color: var(--code-fg); border-radius: var(--radius-sm); padding: 16px 18px; overflow-x: auto; box-shadow: var(--shadow-sm); font-size: 0.85rem; line-height: 1.6; width: 100%; max-width: 54rem; margin-inline: auto; }   /* one consistent width (fills to the cap, not to its content), centred, a touch narrower than the content — and it fills once the screen itself is narrower */
/* a captioned code block: the figure is the centred, capped unit; the code fills it and the short caption sits beneath, aligned to the code's left edge */
figure:has(> pre) { width: 100%; max-width: 54rem; margin-inline: auto; }
figure:has(> pre) > pre { max-width: 100%; }
pre code { background: none; border: none; padding: 0; color: inherit; font-size: inherit; }
/* syntax highlighting: a language tab pinned to the top-right, and per-token colours.
   Spans are baked into the markup — <code class="html|css|js"> with .tag/.attr/.value/.comment inside. */
pre { position: relative; }                             /* positioning context for the language tab */
pre > code[class] { display: block; padding-top: 14px; }  /* reserve room so the tab clears the first line */
pre > code[class]::before {
  position: absolute; top: 0; right: 0; padding: 3px 11px;
  font-family: var(--font); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--code-fg); background: rgba(255, 255, 255, 0.06);
  border-left: 1px solid rgba(255, 255, 255, 0.14);
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 0 var(--radius-sm) 0 var(--radius-sm);
}
pre > code.html::before { content: "html"; }
pre > code.css::before  { content: "css"; }
pre > code.js::before   { content: "javascript"; }
code.html         { color: #f5f5f5; }   /* html: plain element text reads white, not the warm cream */
pre code .tag     { color: var(--hl-tag); }
pre code .attr    { color: var(--hl-attr); }
pre code .value   { color: var(--hl-value); }
pre code .comment { color: var(--hl-comment); }
pre code .kw      { color: var(--hl-kw); }
pre code .fn      { color: var(--hl-fn); }
pre code .num     { color: var(--hl-num); }
pre > code.json::before { content: "json"; }

/* platform reference value cards. .panel is the tabset content box with no tab,
   for a plain value. .swatches is the palette preview row; .sw is an inline
   colour box beside a colour in a JSON listing. */
.panel { background: var(--surface); border: 2px solid var(--accent); border-radius: var(--radius-sm); padding: var(--gap); }
.panel > * + * { margin-top: var(--gap); }
.swatches { display: flex; flex-wrap: wrap; gap: 2px; }
.swatches > span { width: 18px; height: 18px; border-radius: 3px; border: 1px solid rgba(0, 0, 0, 0.2); }
.sw { display: inline-block; width: 0.85em; height: 0.85em; vertical-align: -0.1em; margin-left: 0.6em; border-radius: 2px; border: 1px solid rgba(255, 255, 255, 0.3); }
blockquote { padding: 10px 18px; border-left: 3px solid var(--accent); background: var(--accent-tint); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; color: var(--fg); }
/* figure: a block (image, code) tied to its caption — the caption is part of the
   component, sitting tight at a fixed distance, outside the flow rhythm */
figure { display: flex; flex-direction: column; gap: 6px; }
figcaption { color: var(--muted); font-size: 0.9em; text-align: center; }   /* caption centred beneath the block */

/* header: the site chrome — a bright sunshine-yellow menu, sticky, stacked bars */
header {
  position: sticky; top: 0; z-index: 10;
  background: var(--header-bg);
  border-bottom: 2px solid rgba(0, 0, 0, 0.08);
  box-shadow: 0 2px 10px rgba(30, 90, 55, 0.16);
}
nav { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; }
nav > a, nav > span { font-weight: 700; color: var(--heading); letter-spacing: -0.01em; }   /* a bar's title */
nav > a:hover { text-decoration: none; }
nav > ul { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin: 0; padding: 0; list-style: none; }
nav > ul > li { margin: 0; }
nav ul a { display: block; padding: 7px 13px; border-radius: 8px; white-space: nowrap; color: var(--fg); font-size: 0.95rem; font-weight: 500; text-decoration: none; transition: background 0.15s, color 0.15s; }
nav ul a:hover { background: rgba(0, 0, 0, 0.07); color: var(--heading); text-decoration: none; }
nav a[aria-current] { background: var(--accent); color: #fff; }
nav a[aria-current]:hover { background: var(--accent); color: #fff; }
header > nav { padding: 12px 28px; }
header > nav:first-of-type > a { display: inline-flex; align-items: center; gap: 9px; font-family: var(--font-logo); font-weight: 700; font-size: 1.4rem; letter-spacing: -0.02em; line-height: 1; }   /* brand: sprite icon + bold monospace code-style wordmark */
header > nav:first-of-type > a img { width: 32px; height: 32px; display: block; }
header > nav:first-of-type > ul { margin-left: auto; }                        /* brand left, links right */
header > nav ~ nav { border-top: 1px solid rgba(0, 0, 0, 0.08); background: var(--header-bg-2); padding-top: 9px; padding-bottom: 9px; }
header > nav ~ nav > span { font-size: 0.85rem; color: var(--accent-hover); text-transform: uppercase; letter-spacing: 0.05em; }
header > nav ~ nav ul a { font-size: 0.9rem; }

/* third-level sub-nav (e.g. the library part bar): a third white bar that carries
   the header's closing edge — the same drop shadow the header itself uses — and reads
   like the bars above it, with the standard link highlight (accent fill for the current
   page, a soft grey wash on hover). */
header:has(nav ~ nav ~ nav) { border-bottom: none; box-shadow: none; }   /* the closing edge moves down to the 3rd bar */
header > nav ~ nav:has(+ nav) { border-bottom: none; box-shadow: none; } /* the 2nd bar becomes a plain middle bar */
header > nav ~ nav ~ nav {                       /* the 3rd bar: very pale green, and it carries the closing shadow */
  position: relative; z-index: 1;
  background: var(--header-bg-3);
  border-bottom: 2px solid rgba(0, 0, 0, 0.08);
  box-shadow: 0 2px 10px rgba(30, 90, 55, 0.16);
  transition: transform 0.25s ease, opacity 0.25s ease;
}
/* hide on scroll down, show on scroll up */
header.nav-hidden > nav:nth-of-type(3) { transform: translateY(-100%); opacity: 0; pointer-events: none; }

/* breadcrumb: the wayfinding trail at the top of a deep page, in place of a
   third header bar. Links climb the tree; the last crumb is the current page.
   It sits in the page area and scrolls, so the header stays two bars deep. */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; margin: 0 0 var(--gap); font-size: 0.9rem; color: var(--muted); }
.crumbs a { color: var(--muted); text-decoration: none; transition: color 0.15s; }
.crumbs a:hover { color: var(--accent-hover); text-decoration: underline; }
.crumbs > * + *::before { content: "›"; margin: 0 10px; color: var(--muted); opacity: 0.6; }
.crumbs [aria-current] { color: var(--heading); font-weight: 600; }

/* nav dropdowns (no JS): a nested <ul> in <details> (click) or bare (hover/focus) */
nav li { position: relative; }
nav li > ul, nav details > ul {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  display: flex; flex-direction: column; gap: 2px;
  min-width: 210px; margin: 0; padding: 6px; list-style: none;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--shadow-md);
}
nav li > ul li, nav details > ul li { margin: 0; }
nav summary { display: block; padding: 7px 13px; border-radius: 8px; cursor: pointer; color: var(--fg); font-size: 0.95rem; font-weight: 500; list-style: none; }
nav summary:hover { background: rgba(0, 0, 0, 0.07); color: var(--heading); }
nav summary::-webkit-details-marker { display: none; }
nav summary::after { content: "  ▾" / ""; color: var(--muted); }
nav details[open] > summary { background: rgba(0, 0, 0, 0.07); color: var(--heading); }
nav details[open] > summary::after { content: "  ▴" / ""; }
nav li:has(> ul) > a::after { content: "  ▾" / ""; color: var(--muted); }
nav li > ul { visibility: hidden; opacity: 0; transform: translateY(-4px); transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s; }
nav li:hover > ul, nav li:focus-within > ul { visibility: visible; opacity: 1; transform: none; }

/* main: a white intro with a warm radial glow; sections sit on pale yellow */
main {
  flex: 1 0 auto;                /* grow to fill: footer sticks to the bottom */
  width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 44px var(--pad) 0;
  display: flex; flex-direction: column; gap: 0;
  /* no background here — the intro shows the body glow through, full-width with no edge */
}
/* darker band filler — keeps short pages banded all the way down to the footer */
main::after { content: ""; flex: 1 0 auto; margin-inline: calc(var(--pad) * -1); background: var(--band); box-shadow: 0 0 0 100vw var(--band); clip-path: inset(0 -100vw); }
main > h1 { font-size: clamp(1.9rem, 4vw, 2.5rem); letter-spacing: -0.025em; line-height: 1.12; }
main > h1 + p { max-width: 72ch; font-size: 1.15rem; color: var(--muted); }
/* a top-level feature list (e.g. the homepage goals): roomy, a lead line each */
main > ul { padding-left: 1.3em; max-width: 70ch; }
main > ul > li { margin: 0 0 18px; }
main > ul > li:last-child { margin-bottom: 0; }
main > ul > li > strong { font-size: 1.05rem; }
main > ul > li > p { margin-top: 4px; color: var(--muted); }

/* A section is the always-on structural unit — one around each heading and its content,
   nested to mirror the outline. Flow spacing is two rules and nothing else:
     • content rides one --gap, imposed by the container on its direct children (any element);
     • a subsection carries the between-part margin --sep, which tightens with nesting depth.
   Only sections set a margin; a first child is always flush. */
section { display: flex; flex-direction: column; }

main > *, main section > *                       { margin-top: var(--gap); }   /* content rhythm */
main > :first-child, main section > :first-child { margin-top: 0; }            /* first child flush */

main > *                  { --sep: var(--sep-1); }   /* separation tightens with depth */
main section > *          { --sep: var(--sep-2); }
main section section > *  { --sep: var(--sep-3); }

main section > section    { margin-top: var(--sep); }   /* a subsection sits --sep apart */

/* Backgrounds are a styling layer applied by position (or the user's own selector) — no
   class. The default theme paints the first-level sections as full-width bands: content
   stays centred while the background bleeds edge-to-edge via a clipped spread shadow. The
   first band keeps its --sep above it; the rest run flush. Nested sections stay transparent. */
main > section, main > .band               { margin-top: 0; margin-inline: calc(var(--pad) * -1); padding: var(--sep) var(--pad); background: var(--band); box-shadow: 0 0 0 100vw var(--band); clip-path: inset(0 -100vw); }
main > section:first-of-type, main > .band:first-of-type { margin-top: var(--sep); }
main > section > h2          { padding-bottom: 8px; border-bottom: 2px solid var(--border-strong); }
/* a .band stands in for a first-level section on layout pages (menu + content), so the
   real content sections inside it read as top level and their titles carry the underline */
main > .band .row > .column > section > h2 { padding-bottom: 8px; border-bottom: 2px solid var(--border-strong); }
main > .band > .row { gap: calc(var(--gap) * 1.5); }   /* the menu sits a little further from the content; deeper spacing stays the same */
main > :last-child           { padding-bottom: calc(var(--sep-1) * 6); }   /* extra breathing room below the last block */

/* tables: a white card boxed by a thin accent-green outline (the same green as a
   selected top-menu item), with the cell padding as the inner spacing. Rows carry
   no hover — they are read, not chosen. */
table { width: 100%; border-collapse: separate; border-spacing: 0; background: var(--surface); border: 2px solid var(--accent); border-radius: var(--radius-sm); overflow: hidden; }
caption { text-align: left; padding: 8px 0; font-weight: 600; color: var(--muted); }
th, td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--border); }
thead th { font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); background: var(--surface); border-bottom: 2px solid var(--accent); }
tbody tr:last-child td { border-bottom: none; }

/* forms */
form { display: grid; gap: 15px; }
fieldset { padding: 16px; display: grid; gap: 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); }
legend { font-weight: 600; padding: 0 6px; color: var(--heading); }
label > p { font-weight: 550; font-size: 0.9rem; margin-bottom: 6px; color: var(--heading); }
input, select, textarea { width: 100%; padding: 9px 12px; font: inherit; color: var(--fg); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); transition: border-color 0.15s, box-shadow 0.15s; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
textarea { min-height: 96px; resize: vertical; }
input[type="checkbox"], input[type="radio"] { width: auto; margin-right: 7px; accent-color: var(--accent); }
/* The 100% width above is for a stacked form (form / fieldset are grids). In a row
   a field is a flex item: let it shrink so it shares the free space and fits the
   flow instead of forcing 100% and pushing the row wider. .row > * already sets
   min-width: 0, so it can shrink cleanly; checkboxes and radios are width: auto,
   so they have nothing to shrink and stay their natural size. */
.row > :is(input, select, textarea) { flex-shrink: 1; }

/* buttons (the element) + link buttons */
button, input[type="submit"], input[type="reset"], input[type="button"], a.button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: auto; padding: 9px 18px; font: inherit; font-weight: 550; line-height: 1.4;
  cursor: pointer; text-align: center; text-decoration: none;
  background: var(--accent); color: var(--accent-fg);
  border: 1px solid var(--accent); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm); transition: background 0.15s, box-shadow 0.15s, transform 0.1s;
}
button:hover, input[type="submit"]:hover, input[type="button"]:hover, a.button:hover {
  background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-fg);
  box-shadow: var(--shadow-md); text-decoration: none; transform: translateY(-1px);
}
button:disabled, a.button.disabled { opacity: 0.5; box-shadow: none; pointer-events: none; }   /* muted, no hover, unclickable */
/* button variants */
.button.secondary, input[type="reset"], button.secondary { background: var(--border-strong); color: var(--fg); border-color: var(--border-strong); box-shadow: none; }   /* same grey-green face as an unselected tab */
.button.secondary:hover, input[type="reset"]:hover, button.secondary:hover { background: var(--border-strong); color: var(--accent-hover); border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.button.ghost, button.ghost { background: transparent; color: var(--accent); border-color: transparent; box-shadow: none; }
.button.ghost:hover, button.ghost:hover { background: var(--accent-tint); color: var(--accent-hover); }
.button.small, button.small { padding: 5px 12px; font-size: 0.9rem; }
.button.large, button.large { padding: 12px 24px; font-size: 1.05rem; }
/* a selected button — a pressed, active look, independent of primary/secondary,
   enabled/disabled. Used by Button.Option and anything with the class. */
button.selected, .button.selected { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-fg); box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.28); }
button.secondary.selected, .button.secondary.selected { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-hover); box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.12); }
/* a button group — buttons sit flush with a hairline seam between them, and the
   group rounds its outer corners to its own silhouette. It clips to its rounded
   box, so Row, Column and Grid all round in the right place with no per-button
   rule: the seam is a 1px gap that shows the group's own colour, and the corners
   follow the clip. Row goes across, Column down, Grid fills columns. The styling
   assumes only buttons inside. */
.button-group { gap: 1px; background: var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; }
.button-group > button, .button-group > .button { border: none; border-radius: 0; box-shadow: none; }
.button-group > button:hover, .button-group > .button:hover { transform: none; box-shadow: none; }
.button-group-row { display: inline-flex; flex-direction: row; }
.button-group-column { display: inline-flex; flex-direction: column; }
.button-group-column > button, .button-group-column > .button { width: 100%; }
.button-group-grid { display: inline-grid; }

/* images — a canvas (Bitmap) or SVG (Vector) drawing surface. A frame is the
   positioned box a Vector fills and a PanAndScale overlay covers. */
.image-frame { position: relative; overflow: hidden; background: var(--surface-soft); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.image-bitmap { display: block; }
.image-vector { display: block; position: relative; width: 100%; height: 100%; }   /* positioned so it stacks above a checkerboard backdrop placed before it */
.image-panzoom { position: absolute; inset: 0; cursor: grab; touch-action: none; }
.ui-size { position: relative; width: 100%; height: 100%; }
.ui-touch { position: absolute; inset: 0; cursor: crosshair; touch-action: none; }

/* live examples — the Preview panel of a reference example, mounted from its own
   code by examples-live.js. Pixel grid and overlay area are shared demo pieces. */
.example-live { display: flex; flex-direction: column; gap: var(--gap); }   /* mount is a column, like Layout.Column: rows fill width, readouts sit below with the gap */
.tabset .example-live > * + * { margin-top: 0; }                            /* the gap spaces children; drop the panel's stacked margin so element and text rows space alike */
.pixel-grid { display: grid; gap: 1px; width: max-content; padding: 1px; background: var(--border-strong); border-radius: var(--radius-sm); }
.pixel { width: 20px; height: 20px; background: var(--surface); cursor: pointer; }
.pixel:hover { outline: 2px solid var(--accent); outline-offset: -2px; }
.overlay-demo-area { min-height: 160px; display: flex; flex-direction: column; gap: var(--gap); align-items: flex-start; justify-content: center; padding: 16px; background: var(--surface-soft); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.image-panzoom:active { cursor: grabbing; }

/* menu: a sidebar nav. align-self keeps it only as tall as its own contents,
   so in a .row beside a long article it does not stretch to the page foot. */
menu { display: block; align-self: flex-start; margin: 0; padding: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm); }
menu ol, menu ul { list-style: none; margin: 0; padding: 0; }
menu li { margin: 2px 0; }
menu p { margin: 0; padding: 5px 12px; font-size: 0.82rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
menu p:not(:first-child) { margin-top: 14px; }
menu a { display: block; padding: 7px 12px; color: var(--fg); border-radius: 8px; transition: background 0.15s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* a name wider than the menu clips to an ellipsis */
menu a:hover { background: var(--surface-soft); text-decoration: none; }
menu a[aria-current] { background: var(--accent-tint); color: var(--accent-hover); font-weight: 600; box-shadow: inset 2px 0 0 var(--accent); }

/* ============================================================
   5. COMPONENTS
   ============================================================ */
/* panel — a soft surface (a neutral skin that composes with layout classes).
   card — a styling class, not an element: a bordered, padded surface you add to
   whatever already carries the meaning (article, section, p, div, a). It only
   paints — children flow at the system --gap, so adding or removing .card never
   reflows them. <article> is not boxed on its own; add .card when you want the
   surface. */
.panel { display: block; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow-sm); }
.panel .panel { background: var(--surface-soft); box-shadow: none; }
.card { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: var(--gap); transition: box-shadow 0.18s ease, border-color 0.18s ease, transform 0.12s ease; }
.card .card { background: var(--surface-soft); box-shadow: none; }   /* a nested card is an inset surface — same gap, just softer */
/* a card title takes no special colour: a plain heading stays the heading colour; a
   heading that is a link gets the link colour from the <a>, so accent follows the link. */
/* card links — two ways to make a whole card clickable.
   boxed link (a.card): the whole card is one <a>. Simplest, but it can hold no other links.
   stretched title link (.stretch-link): a card whose heading is a link; the link is stretched
   over the whole surface, so the card opens from anywhere while inner links/buttons still work
   and the card keeps its hover. Trade-off: card text isn't selectable (the overlay is the link). */
a.card { color: inherit; }
a.card:hover, .grid > a.card:hover { text-decoration: none; box-shadow: var(--shadow-md); border-color: var(--accent); transform: translateY(-2px); }
.stretch-link { position: relative; }
.stretch-link :is(h1, h2, h3, h4, h5, h6) a::after { content: ""; position: absolute; inset: 0; }
.stretch-link a:not(:is(h1, h2, h3, h4, h5, h6) a), .stretch-link button { position: relative; z-index: 1; }
.stretch-link:hover { text-decoration: none; box-shadow: var(--shadow-md); border-color: var(--accent); transform: translateY(-2px); }
.stretch-link:hover :is(h1, h2, h3, h4, h5, h6) a { color: var(--accent-hover); text-decoration: underline; }   /* the title link fills the card, so its lit state follows the CARD's hover — steady wherever the pointer is, even over a sub-link */

/* platform cards — each editor card leads with a small line-icon of the machine.
   The icon draws in currentColor, so it takes the title's accent and lights with it on hover. */
.platforms .card h3 { display: flex; align-items: center; gap: 10px; }
.platforms .card h3 svg { flex: none; width: 26px; height: 26px; }

/* homepage editor cards — each title carries the same line-icon, drawn inside the
   title link so it takes the link's accent and its underline stays on the text. */
#editors .card h3 a { display: inline-flex; align-items: center; gap: 10px; text-decoration-line: none; }
#editors .card h3 a > span { text-decoration: underline; }
#editors .card h3 svg { flex: none; width: 26px; height: 26px; }

/* homepage platforms list — each machine leads with a small line-icon, drawn inside
   the link so it takes the link's accent; the underline stays on the name. */
.platform-list li a { display: inline-flex; align-items: center; gap: 8px; text-decoration-line: none; }
.platform-list li a > span { text-decoration: underline; }
.platform-list li svg { flex: none; width: 18px; height: 18px; }

/* platform media cards — each media type leads its card with a line-icon in the
   title link, over a list of bullet links into that media page's sections. */
.media-grid .card h3 a { display: inline-flex; align-items: center; gap: 10px; text-decoration-line: none; }
.media-grid .card h3 a > span { text-decoration: underline; }
.media-grid .card h3 svg { flex: none; width: 24px; height: 24px; }

/* thumb — a small line-diagram of a component page, sitting atop an overview card.
   A wireframe in the site's three tones only: the darker yellow (#ffd21a) draws every
   outline, the lighter yellow (#fff6cf) is the one fill, everything else stays white.
   No frame and no vertical padding — each SVG's viewBox is cropped to its content, so it
   sits tight top-and-bottom; the side padding keeps it narrower than the card. */
.thumb { display: block; width: 100%; height: auto; padding: 0 12px; }
.thumb .box { fill: #fff; stroke: var(--header-bg); stroke-width: 2; }               /* a plain surface — outline only */
.thumb .bar { fill: var(--surface-soft); stroke: var(--header-bg); stroke-width: 2; } /* a filled bar / nav */
.thumb .at  { fill: var(--surface-soft); stroke: var(--header-bg); stroke-width: 2; } /* a highlighted / active surface */
.thumb .a   { fill: var(--surface-soft); stroke: var(--header-bg); stroke-width: 2; } /* headings, buttons, the brand mark — a filled accent */
.thumb .m   { fill: none; stroke: var(--header-bg); stroke-width: 1.5; }              /* text lines — outlined */
.thumb .ln  { stroke: var(--header-bg); stroke-width: 1.5; }                          /* table rules */

/* chip — an inline pill label; a button/a chip is interactive */
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; font-size: 0.85rem; font-weight: 550; line-height: 1.5; color: var(--fg); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); }
button.chip, a.chip { cursor: pointer; text-decoration: none; transition: background 0.15s, border-color 0.15s, color 0.15s; }
button.chip:hover, a.chip:hover { background: var(--accent-tint); border-color: var(--accent); color: var(--accent-hover); text-decoration: none; }
.chip.accent { background: var(--accent-tint); border-color: transparent; color: var(--accent-hover); }
.chip.teal { background: var(--accent-2-tint); border-color: transparent; color: var(--accent-2); }

/* buttons — a group of actions */
.buttons { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* tabset — an in-page tab set, no JavaScript and no IDs. Inside .tabset a <label> holding a
   radio is a tab, and the element immediately after it is that tab's content. The radios
   share a name, so exactly one is ever chosen; :has() hides every unchosen tab's content
   and leaves the chosen one showing. Anything else you drop in (a lead line, a note) is
   neither a tab nor hidden, so it rides along above the strip. The radios hold the state,
   so a script can read the choice from a radio's value via its change event. */
/* the tabs ride along the top; the chosen tab's content joins flush beneath as one
   white box drawn with the same thin accent-green outline as the tables. Tabs are
   rounded on top, flat on the bottom, and sit right up against the box. */
.tabset { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 4px; padding: 0; background: none; border: none; border-radius: 0; }
/* a tab — a label styled like a tab; its radio is taken out of sight but stays focusable.
   An unchosen tab takes the header's medium-green fill and a thin accent-green outline on
   its top and sides, open at the bottom so it joins the strip; the chosen tab turns solid
   accent-green (the border blends into its fill) and laps -2px onto the box to cover the seam. */
.tabset > label { order: 1; position: relative; z-index: 0; display: inline-flex; align-items: center; gap: 7px; margin-bottom: -2px; padding: 7px 16px; cursor: pointer; font-weight: 550; line-height: 1.4; color: var(--fg); background: var(--border-strong); border: none; border-radius: var(--radius-sm) var(--radius-sm) 0 0; transition: background 0.15s, color 0.15s; }
.tabset > label:hover { color: var(--accent-hover); }
.tabset > label:has(input:checked) { z-index: 2; background: var(--accent); color: var(--accent-fg); }
.tabset > label:has(input:checked):hover { color: var(--accent-fg); }
.tabset > label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.tabset > label > input { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; clip: rect(0 0 0 0); overflow: hidden; }
/* the content — the element straight after a tab. It fills the row below the tabs as a
   white box with the same 2px green outline as the tables; its inner padding is the layout gap. */
.tabset > label:has(input) + * { order: 2; z-index: 1; position: relative; flex: 0 0 auto; width: 100%; padding: var(--gap); background: var(--surface); border: 2px solid var(--accent); border-radius: 0 var(--radius-sm) var(--radius-sm) var(--radius-sm); }
.tabset > label:has(input) + * > * + * { margin-top: var(--gap); }        /* content inside the panel rides the one gap */
.tabset > label:has(input:not(:checked)) + * { display: none; }           /* hide every unchosen tab's content */
/* vertical — the tabs stack in a column and the chosen content fills the space beside them */
.tabset.vertical { display: grid; grid-template-columns: max-content minmax(0, 1fr); row-gap: 0; column-gap: 8px; align-content: start; }
.tabset.vertical > label { grid-column: 1; width: 100%; margin-bottom: 6px; justify-content: flex-start; }
.tabset.vertical > label:has(input) + * { grid-column: 2; grid-row: 1 / span 50; width: auto; margin: 0; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); }

/* reference module pages: a fixed-width side menu wide enough for full function
   names, and a larger rhythm between stacked function sections (padding above each
   heading after the first, on top of the column's base gap). */
.row > menu { flex: 0 0 17rem; width: 17rem; }
main .row > .column > section:not(:first-child) > h2 { padding-top: var(--sep); }

/* accordion — a stack of disclosure panels built on the native <details> element. Each
   row is a <details>; its <summary> is the always-visible header and the rest of the
   content shows when the row is open. No JS — the browser owns open/closed. Give every
   <details> the same name to make a single-open set (opening one closes the siblings);
   leave the name off and any number of rows can stand open at once. Wrapping the rows in
   .accordion fuses them into one bordered unit with shared dividers. */
.accordion { background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden; }
.accordion > details + details { border-top: 1px solid var(--border-strong); }   /* a divider between rows */
.accordion > details > summary { display: flex; align-items: center; gap: 10px; padding: 14px 18px; font-weight: 600; color: var(--heading); background: var(--surface); cursor: pointer; list-style: none; transition: background 0.15s; }
.accordion > details > summary:hover { background: var(--surface-soft); }
.accordion > details > summary::-webkit-details-marker { display: none; }         /* drop the native triangle; the chevron below replaces it */
.accordion > details > summary::after { content: "▾"; margin-left: auto; color: var(--muted); transition: transform 0.2s ease; }
.accordion > details[open] > summary { background: var(--surface-soft); border-bottom: 1px solid var(--border-strong); }
.accordion > details[open] > summary::after { transform: rotate(180deg); }
.accordion > details > :not(summary) { padding-inline: 18px; }                    /* body content keeps the header's side inset */
.accordion > details > summary + :not(summary) { padding-top: 14px; }             /* first body block clears the header */
.accordion > details > :last-child:not(summary) { padding-bottom: 16px; }         /* last body block leaves a base */
.accordion > details > :not(summary) + :not(summary) { margin-top: var(--gap); }  /* the content rhythm between body blocks */

/* status — a banner / live region */
.status { display: block; padding: 11px 15px; border-radius: var(--radius-sm); border: 1px solid var(--border); font-weight: 500; }
.status.success { background: var(--ok-bg); border-color: var(--ok-bd); color: var(--ok-fg); }
.status.error   { background: var(--err-bg); border-color: var(--err-bd); color: var(--err-fg); }
.status.info    { background: var(--info-bg); border-color: var(--info-bd); color: var(--info-fg); }
.status.warning { background: var(--warn-bg); border-color: var(--warn-bd); color: var(--warn-fg); }

/* paging — a Pagination nav */
.paging { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; }
.paging a, .paging span { display: inline-block; padding: 7px 13px; border-radius: 8px; color: var(--fg); text-decoration: none; }
.paging a:hover { background: var(--surface); box-shadow: var(--shadow-sm); text-decoration: none; }
.paging a[aria-current] { background: var(--accent); color: var(--accent-fg); }
.paging span { color: var(--muted); }

/* footer — full-width bar; content constrained and centred, copyright left / credit right */
footer { flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border-strong); color: var(--muted); }
footer > .row { width: 100%; padding: 22px var(--pad); align-items: center; flex-wrap: wrap; }   /* full window width, not the content column */
footer a { color: var(--muted); text-decoration: underline; }

/* page layouts: normal is the default main; full fills the window for editors */
main.full { max-width: none; }
main.full::after { display: none; }
main.full > section { margin-inline: 0; padding: 0; background: none; box-shadow: none; clip-path: none; }

/* full-height editor page: header, a hero at the top, a stage that fills the
   rest, footer pinned, and no page scroll. The stage clips, so however tall it
   gets it never pushes the footer off the bottom. */
body:has(main.full.fill) { height: 100vh; overflow: hidden; }
main.full.fill { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding-top: var(--pad); padding-bottom: 0; }   /* even top padding holds the title rows off the header; the stage below runs full-bleed to the screen edges */
main.full.fill > * { flex: 0 0 auto; }
main.full.fill > .editor-stage { flex: 1 1 0; min-height: 0; }
main.full.fill > :last-child { padding-bottom: 0; }

/* the sprite stage: a checkerboard backdrop shows through the transparent Vector,
   which fills the stage while its rounded corners clip it. */
.editor-stage { position: relative; overflow: hidden; background: #ffffff; border-top: 2px solid var(--accent); margin-inline: calc(var(--pad) * -1); }   /* full-bleed below the title rows: a green top rule, no box, running to the left, right and bottom screen edges */

/* a fully linked card shows its title like a link: green with an underline */
a.card :is(h1, h2, h3, h4, h5, h6) { color: var(--accent); text-decoration: underline; }
a.card:hover :is(h1, h2, h3, h4, h5, h6) { color: var(--accent-hover); }
/* a card whose title is itself a link shows that title underlined too */
.card :is(h1, h2, h3, h4, h5, h6) a { text-decoration: underline; }

/* window: a titled panel of content, floating over the page or on an overlay.
   A window is flush by default: its body has no padding and its layout has no
   gaps, so a Content.Area sets its own edges and a divider can run edge to edge.
   Wrap parts in a Content.Area for padding and gaps; stack areas for dividers. */
.window { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-lg); overflow: hidden; }
.window-bar { display: flex; align-items: center; gap: 8px; padding: 10px 14px; font-weight: 600; color: var(--heading); background: var(--surface-soft); border-bottom: 1px solid var(--border); }
.window-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.window-close { flex: 0 0 auto; border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: 1.2rem; line-height: 1; padding: 2px 8px; border-radius: var(--radius-sm); }
.window-close:hover { background: var(--border); color: var(--heading); }
.window-body { padding: 0; overflow: auto; }
.window-body > * + * { border-top: 1px solid var(--border); }              /* a divider between stacked areas and the footer */
.window-footer { display: flex; justify-content: flex-end; gap: var(--gap); padding: 12px 16px; background: var(--surface-soft); }
.window-floating { position: fixed; top: 80px; left: 50%; transform: translateX(-50%); min-width: 260px; max-width: calc(100vw - 32px); max-height: calc(100vh - 32px); }
.window-floating .window-bar { cursor: move; user-select: none; touch-action: none; }
/* resizeable: the body fills the frame so the footer sits at the bottom, and
   eight transparent grips over the edges and corners drag the reactive size. */
.window-resizeable .window-body { flex: 1 1 auto; display: flex; flex-direction: column; }
.window-resizeable .window-footer { margin-top: auto; }
.window-grip { position: absolute; touch-action: none; }
.window-grip-n { top: 0; left: 14px; right: 14px; height: 6px; cursor: ns-resize; }
.window-grip-s { bottom: 0; left: 14px; right: 14px; height: 6px; cursor: ns-resize; }
.window-grip-e { right: 0; top: 14px; bottom: 14px; width: 6px; cursor: ew-resize; }
.window-grip-w { left: 0; top: 14px; bottom: 14px; width: 6px; cursor: ew-resize; }
.window-grip-ne { top: 0; right: 0; width: 14px; height: 14px; cursor: nesw-resize; }
.window-grip-nw { top: 0; left: 0; width: 14px; height: 14px; cursor: nwse-resize; }
.window-grip-se { bottom: 0; right: 0; width: 14px; height: 14px; cursor: nwse-resize; }
.window-grip-sw { bottom: 0; left: 0; width: 14px; height: 14px; cursor: nesw-resize; }
.window-modal { max-width: calc(100vw - 32px); max-height: calc(100vh - 32px); }

/* a content area: padding on the inside, and its own layout regains its gaps.
   The window resets layout gaps to zero, so only areas and footers space out. */
.window .row, .window .column, .window .grid { gap: 0; }
.content-area { display: flex; flex-direction: column; gap: var(--gap); padding: 14px 16px; }
.content-area .row, .content-area .column, .content-area .grid { gap: var(--gap); }

/* overlays: a background and a stack of layers over an area or the whole window.
   A Container is the positioned area, clipping its layers; the window host is
   fixed over everything; a free host holds one non-modal floating window. The
   background dims; each layer fills the area and places its own content. */
.overlay-container { position: relative; overflow: hidden; }
.overlay-window, .overlay-free { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
.overlay-window > *, .overlay-free > * { pointer-events: auto; }
.overlay-background { position: absolute; inset: 0; background: rgba(20, 32, 26, 0.45); }
.overlay-layer { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 16px; }

/* ============================================================
   6. SPRITE EDITOR  — sprites.org only
   ------------------------------------------------------------
   The retro editor from the original sprites site, kept whole: a dark, glowing
   green console dropped onto the sunshine page. Its own tokens keep the green
   world sealed inside .editor, so the rest of the site stays on the framework
   theme. Layout still rides the framework — .row / .column / .stretch — so it
   reflows and stacks with everything else.
   ============================================================ */
.editor {
  --e-bg: #063006;        /* the deep console green behind the canvas */
  --e-surface: #0a3d0a;   /* the panel body */
  --e-line: #0a0;         /* grid lines */
  --e-line-bright: #0f0;  /* hover outline, brush cursor, glow */
  --e-fill: #0e0;         /* a set pixel */
  --e-fg: #dfffdf;        /* pale green text */
  --e-muted: #7fcf7f;
  /* bevelled button faces — light edge top/left, dark edge bottom/right */
  --e-btn: #060; --e-btn-lt: #080; --e-btn-dk: #040;
  --e-btn-hover: #090; --e-btn-hover-lt: #0b0; --e-btn-hover-dk: #060;
  --e-btn-on: #0c0; --e-btn-on-lt: #0e0; --e-btn-on-dk: #0a0;

  display: block;
  background: var(--e-surface);
  border: 1px solid #052605;
  border-radius: var(--radius);
  padding: 20px;
  box-shadow: var(--shadow-md), inset 0 0 0 1px rgba(0, 255, 0, 0.10);
  color: var(--e-fg);
  user-select: none;
}
/* the three-part editor row: tools · canvas · downloads, stacking when narrow */
.editor > .row { align-items: flex-start; gap: 22px; }
.editor .tools, .editor .downloads { flex: 0 0 140px; text-align: center; }
.editor .stage { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 12px; }
@media (max-width: 680px) {
  .editor > .row { flex-direction: column; align-items: stretch; }
  .editor .tools, .editor .downloads { flex: 0 0 auto; }
}

/* editor headings / help text, in the green world */
.editor h2, .editor h3 { color: var(--e-fg); letter-spacing: 0; }
.editor h3 { font-size: 1.05rem; }
.editor .tools p, .editor .downloads p { color: var(--e-muted); font-size: 0.85rem; margin: 4px 0 8px; }

/* the canvas — a dark square with the signature green glow */
.editor .canvas {
  width: 100%; max-width: 420px; height: auto; display: block;
  background: var(--e-bg);
  border-radius: var(--radius-sm);
  box-shadow: 0 0 50px 5px rgba(0, 255, 0, 0.30);
  touch-action: none;
}
.editor .canvas .frame { fill: none; stroke: var(--e-line); stroke-width: 1.5; }
.editor .canvas .pixel { fill: none; stroke: var(--e-line); stroke-width: 0.5; }
.editor .canvas .pixel.filled { fill: var(--e-fill); }
.editor .canvas .hover { fill: rgba(0, 0, 0, 0); stroke: none; }
.editor .canvas .hover:hover { stroke: var(--e-line-bright); stroke-width: 2; }
.editor .canvas .brush { fill: none; stroke: var(--e-line-bright); stroke-width: 1; }
.editor .canvas .hidden { display: none; }

/* the token readout beneath the canvas */
.editor .token {
  width: 100%;
  font-family: var(--mono); font-size: 0.72rem; line-height: 1.5;
  color: var(--e-muted); word-break: break-all; text-align: center;
  padding: 7px 10px; border: 1px solid #0a4d0a; border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.20);
}

/* bevelled retro buttons — overrides the framework button inside the editor only */
.editor button {
  display: block; width: 100%; margin: 8px 0 0;
  padding: 9px 0; font-weight: 600; font-size: 0.95rem;
  color: var(--e-fg); background: var(--e-btn);
  border-top: 4px solid var(--e-btn-lt); border-left: 4px solid var(--e-btn-lt);
  border-bottom: 4px solid var(--e-btn-dk); border-right: 4px solid var(--e-btn-dk);
  border-radius: 10px; cursor: pointer;
  box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.9);
  transition: none;
}
.editor button:hover {
  color: #fff; background: var(--e-btn-hover); text-decoration: underline; transform: none;
  border-top-color: var(--e-btn-hover-lt); border-left-color: var(--e-btn-hover-lt);
  border-bottom-color: var(--e-btn-hover-dk); border-right-color: var(--e-btn-hover-dk);
  box-shadow: 0 0 2px 2px rgba(0, 255, 0, 1);
}
.editor button:active { border-width: 4px; box-shadow: inset 0 0 6px 2px rgba(0, 0, 0, 0.6); }
/* the chosen tool stays pressed-in and bright */
.editor button[aria-pressed="true"], .editor button[aria-pressed="true"]:hover {
  color: #fff; background: var(--e-btn-on); cursor: default; text-decoration: none;
  border-top-color: var(--e-btn-on-lt); border-left-color: var(--e-btn-on-lt);
  border-bottom-color: var(--e-btn-on-dk); border-right-color: var(--e-btn-on-dk);
  box-shadow: 0 0 2px 2px rgba(0, 90, 0, 1);
}

/* Loading boundary: a spinner over content while a value under it is pending.
   The veil covers and dims the content; the content stays in place and runs. */
.ui-loading { position: relative; }
.ui-loading-veil {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--surface) 65%, transparent);
  border-radius: inherit;
}
/* the veil sets display, which would beat the browser's [hidden] rule, so hide
   it explicitly when the boundary drops the hidden attribute back on. */
.ui-loading-veil[hidden] { display: none; }
.ui-loading-spinner {
  width: 20px;
  height: 20px;
  border: 3px solid var(--border-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: ui-spin 0.7s linear infinite;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }
