/* Function schematics as HTML (see schematic-html.js). Self-contained: colors are
   tokens on .sx, with a dark palette that follows the page (prefers-color-scheme,
   or data-theme on the root element). */

.sx {
  --sx-fg: #1f2937; --sx-muted: #566070; --sx-faint: #9ca3af; --sx-stroke: #cbd3de; --sx-title: #0f172a; --sx-ct-add: #1C6B45; --sx-ct-del: #A8321C;
  --sx-d0: #ffffff; --sx-d1: #eef4ff; --sx-d2: #fff4dc; --sx-d3: #ffe6d1; --sx-d4: #ffd6d6; --sx-d5: #ffc4c4; --sx-d6: #ffb3b3;
  --sx-bar-if: #1d4ed8; --sx-bar-loop: #6d28d9; --sx-bar-callback: #52606f; --sx-bar-try: #475569; --sx-bar-catch: #b91c1c; --sx-bar-switch: #0369a1; --sx-bar-with: #0e6b64; --sx-bar-type: #115e59;
  --sx-rail-branch: #8D8F94; --sx-rail-loop: #7465B0; --sx-rail-try: #4F6D8F; --sx-rail-held: #8E4F8A; --sx-rail-inner: #8D8F94;
  --sx-catch-head: #fff1f2;
  --sx-arm-bg: #dbeafe; --sx-arm-line: #93c5fd; --sx-arm-kw: #1d4ed8;
  --sx-case-bg: #e0f2fe; --sx-case-line: #bae6fd; --sx-case-fg: #0c4a6e; --sx-switch-kw: #075985;
  --sx-guard-kw: #92400e; --sx-guard: #f59e0b;
  --sx-x-return: #334155; --sx-x-return-bg: #e2e8f0; --sx-x-throw: #b91c1c; --sx-x-throw-bg: #fee2e2;
  --sx-x-jump: #6d28d9; --sx-x-jump-bg: #ede9fe; --sx-x-end: #526071; --sx-x-end-bg: #f1f5f9;
  --sx-t-conc: #be185d; --sx-t-conc-bg: #fce7f3; --sx-t-defer: #475569; --sx-t-defer-bg: #e2e8f0;
  --sx-t-db: #0f766e; --sx-t-db-bg: #ccfbf1; --sx-t-dbw: #9a3412; --sx-t-dbw-bg: #ffedd5;
  --sx-t-http: #1d4ed8; --sx-t-http-bg: #dbeafe; --sx-t-await: #6b21a8; --sx-t-await-bg: #f3e8ff;
  --sx-good: #166534; --sx-good-bg: #dcfce7; --sx-warn: #92400e; --sx-warn-bg: #fef3c7; --sx-bad: #991b1b; --sx-bad-bg: #fee2e2;
  --sx-plain: #334155; --sx-plain-bg: #e2e8f0;
  --sx-rail: #94a3b8; --sx-rail-return: #64748b; --sx-rail-throw: #dc2626; --sx-rail-end: #94a3b8; --sx-rail-stop: #334155;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sx {
    --sx-fg: #e6edf3; --sx-muted: #9aa4b2; --sx-faint: #6b7480; --sx-stroke: #2f3742; --sx-title: #f1f5f9; --sx-ct-add: #4ade80; --sx-ct-del: #f87171;
    --sx-d0: #161b22; --sx-d1: #18233a; --sx-d2: #2b2614; --sx-d3: #33250f; --sx-d4: #3a1d1d; --sx-d5: #451a1c; --sx-d6: #521719;
    --sx-bar-if: #60a5fa; --sx-bar-loop: #a78bfa; --sx-bar-callback: #8392a8; --sx-bar-try: #a3b1c4; --sx-bar-catch: #f87171; --sx-bar-with: #2dd4bf; --sx-bar-type: #14b8a6; --sx-catch-head: #3a1517;
    --sx-rail-branch: #6B7480; --sx-rail-loop: #A79BE0; --sx-rail-try: #8FB0D4; --sx-rail-held: #D08FCB; --sx-rail-inner: #6B7480;
    --sx-arm-bg: #172a4a; --sx-arm-line: #1e3a8a; --sx-arm-kw: #93c5fd;
    --sx-case-bg: #0c2a3d; --sx-case-line: #0e4a6b; --sx-case-fg: #bae6fd; --sx-switch-kw: #7dd3fc;
    --sx-guard-kw: #fbbf24;
    --sx-x-return: #cbd5e1; --sx-x-return-bg: #273142; --sx-x-throw: #fca5a5; --sx-x-throw-bg: #3f1717;
    --sx-x-jump: #c4b5fd; --sx-x-jump-bg: #2e2350; --sx-x-end: #94a3b8; --sx-x-end-bg: #1f2630;
    --sx-t-conc: #f9a8d4; --sx-t-conc-bg: #3d1530; --sx-t-defer: #cbd5e1; --sx-t-defer-bg: #273142;
    --sx-t-db: #5eead4; --sx-t-db-bg: #0f2f2c; --sx-t-dbw: #fdba74; --sx-t-dbw-bg: #3a210f;
    --sx-t-http: #93c5fd; --sx-t-http-bg: #172a4a; --sx-t-await: #d8b4fe; --sx-t-await-bg: #2c1840;
    --sx-good: #86efac; --sx-good-bg: #14361f; --sx-warn: #fcd34d; --sx-warn-bg: #3a2a0a; --sx-bad: #fca5a5; --sx-bad-bg: #3d1515;
    --sx-plain: #cbd5e1; --sx-plain-bg: #273142;
    --sx-rail: #64748b; --sx-rail-return: #94a3b8; --sx-rail-throw: #f87171; --sx-rail-end: #64748b; --sx-rail-stop: #cbd5e1;
  }
}
:root[data-theme="dark"] .sx {
  --sx-fg: #e6edf3; --sx-muted: #9aa4b2; --sx-faint: #6b7480; --sx-stroke: #2f3742; --sx-title: #f1f5f9; --sx-ct-add: #4ade80; --sx-ct-del: #f87171;
  --sx-d0: #161b22; --sx-d1: #18233a; --sx-d2: #2b2614; --sx-d3: #33250f; --sx-d4: #3a1d1d; --sx-d5: #451a1c; --sx-d6: #521719;
  --sx-bar-if: #60a5fa; --sx-bar-loop: #a78bfa; --sx-bar-callback: #8392a8; --sx-bar-try: #a3b1c4; --sx-bar-catch: #f87171; --sx-bar-with: #2dd4bf; --sx-bar-type: #14b8a6; --sx-catch-head: #3a1517;
  --sx-rail-branch: #6B7480; --sx-rail-loop: #A79BE0; --sx-rail-try: #8FB0D4; --sx-rail-held: #D08FCB; --sx-rail-inner: #6B7480;
  --sx-arm-bg: #172a4a; --sx-arm-line: #1e3a8a; --sx-arm-kw: #93c5fd;
  --sx-case-bg: #0c2a3d; --sx-case-line: #0e4a6b; --sx-case-fg: #bae6fd; --sx-switch-kw: #7dd3fc;
  --sx-guard-kw: #fbbf24;
  --sx-x-return: #cbd5e1; --sx-x-return-bg: #273142; --sx-x-throw: #fca5a5; --sx-x-throw-bg: #3f1717;
  --sx-x-jump: #c4b5fd; --sx-x-jump-bg: #2e2350; --sx-x-end: #94a3b8; --sx-x-end-bg: #1f2630;
  --sx-t-conc: #f9a8d4; --sx-t-conc-bg: #3d1530; --sx-t-defer: #cbd5e1; --sx-t-defer-bg: #273142;
  --sx-t-db: #5eead4; --sx-t-db-bg: #0f2f2c; --sx-t-dbw: #fdba74; --sx-t-dbw-bg: #3a210f;
  --sx-t-http: #93c5fd; --sx-t-http-bg: #172a4a; --sx-t-await: #d8b4fe; --sx-t-await-bg: #2c1840;
  --sx-good: #86efac; --sx-good-bg: #14361f; --sx-warn: #fcd34d; --sx-warn-bg: #3a2a0a; --sx-bad: #fca5a5; --sx-bad-bg: #3d1515;
  --sx-plain: #cbd5e1; --sx-plain-bg: #273142;
  --sx-rail: #64748b; --sx-rail-return: #94a3b8; --sx-rail-throw: #f87171; --sx-rail-end: #64748b; --sx-rail-stop: #cbd5e1;
}

.sx { color: var(--sx-fg); font: 13px/1.55 var(--font-mono, "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace); overflow-x: auto; font-synthesis: none; }
.sx *, .sx *::before { box-sizing: border-box; }
.sx-head { margin: 0 0 10px; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); }
.sx-title { color: var(--sx-title); font-size: 17px; font-weight: 600; letter-spacing: -0.012em; line-height: 1.35; overflow-wrap: anywhere; }
.sx-sub { color: var(--sx-muted); font-size: 12.5px; margin: 0 0 8px; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); }
.sx-head .sx-sub { margin: 2px 0 6px; }
.sx-chips, .sx-legend { display: flex; flex-wrap: wrap; gap: 4px 6px; }

/* The body leaves room on the right (and below) for the exit rail. */
/* As wide as its widest word chain (fit-content), never narrower than the card: a branch whose words do not fit widens the drawing, which scrolls sideways (.sx), and the exit rail stays at its right edge. */
.sx-body { position: relative; padding: 0 40px 30px 0; min-width: max(22ch, 100%); width: fit-content; box-sizing: border-box; }
.sx-tree { border: 1px solid var(--sx-stroke); }
.sx-tree > * + *, .sx-fbody > * + *, .sx-abody > * + *, .sx-cbody > * + *, .sx-held > * + *, .sx-fold > * + *, .sx-try > * + * { border-top: 1px solid var(--sx-stroke); }

.sx .d0 { background: var(--sx-d0); } .sx .d1 { background: var(--sx-d1); } .sx .d2 { background: var(--sx-d2); } .sx .d3 { background: var(--sx-d3); }
.sx .d4 { background: var(--sx-d4); } .sx .d5 { background: var(--sx-d5); } .sx .d6 { background: var(--sx-d6); }

.sx-row, .sx-exit, .sx-guard, .sx-fhead, .sx-ahead, .sx-clabel, .sx-fold > summary { padding: 2px 8px; min-height: 23px; }
.sx-steps { padding: 1px 0; }
.sx-steps.empty { color: var(--sx-faint); }
.sx-steps.collapsed { color: var(--sx-muted); }
/* Text is never cut. A row is a flex line: chips and keywords keep their size and the
   text takes the rest, so a long string wraps under itself, not under the chips. */
/* Lines break at spaces only, so an identifier is never split: a word wider than its
   column widens the column and the drawing scrolls sideways (.sx overflow-x). */
.sx-t { white-space: pre-wrap; }
.sx-nb { white-space: nowrap; }
.sx-row, .sx-exit, .sx-guard, .sx-fhead, .sx-ahead, .sx-clabel { display: flex; align-items: baseline; }
.sx-row > *, .sx-exit > *, .sx-guard > *, .sx-fhead > *, .sx-ahead > *, .sx-clabel > * { flex: none; }
.sx-row > .sx-t, .sx-exit > .sx-t, .sx-fhead > .sx-t, .sx-ahead > .sx-t, .sx-clabel > .sx-t { flex: 1 1 0; }
/* A guard's condition and exit label share the line and wrap in their own columns. */
.sx-guard { flex-wrap: wrap; }
.sx-guard > .sx-t { flex: 0 1 auto; max-width: 100%; }
.sx-guard > .sx-pill + .sx-t { flex: 1 1 16ch; }
.sx-guard > .sx-dia, .sx-guard > .sx-kw { align-self: baseline; }
/* A header that is only a keyword and a phrase (a switch head) wraps like text. */
.sx-fhead > .sx-kw:last-child { flex: 1 1 0; white-space: pre-wrap; }
.sx-clabel > .sx-was-label { margin-right: .6ch; }
.sx-clabel > s { flex: none; }

.sx-kw { font-weight: 600; }
/* Visually hidden text for screen readers (the + and − marks say added and removed). */
.sx-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.sx-tag, .sx-pill, .sx-chip, .sx-key, .sx-tern {
  display: inline-block; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); font-size: 12px; font-weight: 600; line-height: 16px; padding: 0 6px; border-radius: 999px;
  vertical-align: 1px; white-space: nowrap;
}
.sx .sx-tern { color: var(--sx-t-defer); background: var(--sx-t-defer-bg); }
.sx .t-conc { color: var(--sx-t-conc); background: var(--sx-t-conc-bg); } .sx .t-defer { color: var(--sx-t-defer); background: var(--sx-t-defer-bg); }
.sx .t-db { color: var(--sx-t-db); background: var(--sx-t-db-bg); } .sx .t-dbw { color: var(--sx-t-dbw); background: var(--sx-t-dbw-bg); }
.sx .t-http { color: var(--sx-t-http); background: var(--sx-t-http-bg); } .sx .t-await { color: var(--sx-t-await); background: var(--sx-t-await-bg); }
.sx .x-return { color: var(--sx-x-return); background: var(--sx-x-return-bg); } .sx .x-throw { color: var(--sx-x-throw); background: var(--sx-x-throw-bg); }
.sx .x-break, .sx .x-continue, .sx .x-goto, .sx .x-fallthrough { color: var(--sx-x-jump); background: var(--sx-x-jump-bg); }
.sx .x-end { color: var(--sx-x-end); background: var(--sx-x-end-bg); }
.sx-chip { font-size: 12px; font-weight: 600; line-height: 18px; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); }
.sx .c-good { color: var(--sx-good); background: var(--sx-good-bg); } .sx .c-warn { color: var(--sx-warn); background: var(--sx-warn-bg); }
.sx .c-bad { color: var(--sx-bad); background: var(--sx-bad-bg); } .sx .c-plain { color: var(--sx-plain); background: var(--sx-plain-bg); }

/* Stub (Outline): a run of statements is a bare bar, 6px plus 2px per extra statement; its IO chips sit to the left. */
.sx-bar { display: flex; align-items: center; gap: 4px; padding: 3px 8px; }
/* A short bar, not a rule across the block: it stands for statements, it does not divide them. */
.sx-bar::after { content: ""; flex: 0 1 160px; min-width: 16px; height: 6px; border-radius: 3px; background: var(--sx-faint); opacity: .45; }
.sx-bar .sx-tag { margin-right: 0; }
.sx-bar.h8::after { height: 8px; } .sx-bar.h10::after { height: 10px; } .sx-bar.h12::after { height: 12px; } .sx-bar.h14::after { height: 14px; }
.sx-bar.h16::after { height: 16px; } .sx-bar.h18::after { height: 18px; } .sx-bar.h20::after { height: 20px; } .sx-bar.h22::after { height: 22px; }

/* Guards: ◆ if … → exit, on one row. */
.sx-dia { display: inline-block; width: 8px; height: 8px; margin: 0 8px 1px 1px; background: var(--sx-guard); transform: rotate(45deg); }
.sx-guard > .sx-kw { color: var(--sx-guard-kw); }
.sx-arrow { color: var(--sx-muted); margin: 0 0 0 6px; }
/* The renderer follows keywords, tags and pills with a real space, but a flex row drops
   whitespace between its items: there the space is a margin. */
:is(.sx-row, .sx-exit, .sx-guard, .sx-fhead, .sx-ahead, .sx-clabel, .sx-bar) > .sx-kw { margin-right: .6ch; }
:is(.sx-row, .sx-exit, .sx-guard, .sx-fhead, .sx-ahead, .sx-clabel, .sx-bar) > :is(.sx-tag, .sx-tern) { margin-right: 4px; }
:is(.sx-row, .sx-exit, .sx-guard, .sx-fhead, .sx-ahead, .sx-clabel, .sx-bar) > .sx-pill { margin-right: 6px; }
.sx-guard > .sx-arrow { margin-right: 6px; }
.sx-exit .sx-t, .sx-guard .sx-pill + .sx-t { color: var(--sx-muted); }

/* Framed blocks: a colored bar down the left, a header, then the body. */
.sx-frame { border-left: 12px solid var(--bar); }
.sx-frame > .sx-fhead > .sx-kw { color: var(--bar); }
.sx-fbody, .sx-cases { border-top: 1px solid var(--sx-stroke); }
.sx .f-if { --bar: var(--sx-bar-if); } .sx .f-loop { --bar: var(--sx-bar-loop); } .sx .f-callback { --bar: var(--sx-bar-callback); }
.sx .f-try, .sx .f-finally { --bar: var(--sx-bar-try); } .sx .f-with { --bar: var(--sx-bar-with); } .sx .f-type { --bar: var(--sx-bar-type); } .sx .f-block { --bar: var(--sx-bar-loop); } .sx .f-catch { --bar: var(--sx-bar-catch); } .sx .f-switch { --bar: var(--sx-bar-switch); }
.sx .f-catch > .sx-fhead { background: var(--sx-catch-head); }
.sx .f-switch > .sx-fhead > .sx-kw { color: var(--sx-switch-kw); }
.sx-frame.dashed { outline: none; background-image: linear-gradient(color-mix(in srgb, var(--bar, #94a3b8) 8%, transparent), color-mix(in srgb, var(--bar, #94a3b8) 8%, transparent)); }

/* if / else: arms side by side, each as wide as it needs. */
.sx-branch { display: flex; }
/* No min-width: an arm is at least as wide as its longest word, so nothing is cut; a wide branch scrolls. */
.sx-arm { flex: 1 1 12ch; display: flex; flex-direction: column; }
.sx-arm + .sx-arm { border-left: 1px solid var(--sx-arm-line); }
.sx-ahead { background: var(--sx-arm-bg); border-bottom: 1px solid var(--sx-arm-line); }
.sx-ahead > .sx-kw { color: var(--sx-arm-kw); }
.sx-abody { flex: 1; }

/* switch: a label column, then each case's body. */
.sx-case { display: grid; grid-template-columns: minmax(4ch, max-content) minmax(0, 1fr); }
.sx-case + .sx-case { border-top: 1px solid var(--sx-stroke); }
.sx-clabel { background: var(--sx-case-bg); color: var(--sx-case-fg); border-right: 1px solid var(--sx-case-line); max-width: 28ch; }

/* Folded blocks (very large functions): open to see what is inside. */
.sx-fold > summary { color: var(--sx-muted); cursor: pointer; list-style: none; }
.sx-fold > summary::-webkit-details-marker { display: none; }
.sx-fold > summary::before { content: "▸ "; }
.sx-fold[open] > summary::before { content: "▾ "; }
.sx-fold > summary:hover { color: var(--sx-fg); }

.sx-legend { margin-top: 8px; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); align-items: center; }
.sx .k-loop { color: var(--sx-x-jump); background: var(--sx-x-jump-bg); } .sx .k-guard { color: var(--sx-guard-kw); background: var(--sx-warn-bg); }
.sx .k-if { color: var(--sx-arm-kw); background: var(--sx-arm-bg); } .sx .k-switch { color: var(--sx-switch-kw); background: var(--sx-case-bg); }
.sx .k-callback { color: var(--sx-plain); background: var(--sx-x-end-bg); } .sx .k-type { color: var(--sx-t-db); background: var(--sx-t-db-bg); } .sx .k-conc { color: var(--sx-t-conc); background: var(--sx-t-conc-bg); }
.sx .k-defer { color: var(--sx-t-defer); background: var(--sx-t-defer-bg); } .sx .k-throw { color: var(--sx-x-throw); background: var(--sx-x-throw-bg); }
.sx .k-db { color: var(--sx-t-db); background: var(--sx-t-db-bg); } .sx .k-dbw { color: var(--sx-t-dbw); background: var(--sx-t-dbw-bg); }
.sx .k-http { color: var(--sx-t-http); background: var(--sx-t-http-bg); }
.sx-keynote { color: var(--sx-muted); font-size: 10px; margin-left: 4px; }

/* Diffs (difftree.js): + and − in a gutter, green and red. */
.sx { --sx-add: #147c3b; --sx-add-bg: rgba(34, 197, 94, .18); --sx-del: #b91c1c; --sx-del-bg: rgba(239, 68, 68, .16); --sx-mod: #b45309; --sx-mod-bg: rgba(245, 158, 11, .14); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sx { --sx-add: #4ade80; --sx-add-bg: rgba(34, 197, 94, .22); --sx-del: #f87171; --sx-del-bg: rgba(239, 68, 68, .22); --sx-mod: #fbbf24; --sx-mod-bg: rgba(245, 158, 11, .18); } }
:root[data-theme="dark"] .sx { --sx-add: #4ade80; --sx-add-bg: rgba(34, 197, 94, .22); --sx-del: #f87171; --sx-del-bg: rgba(239, 68, 68, .22); --sx-mod: #fbbf24; --sx-mod-bg: rgba(245, 158, 11, .18); }
.sx .chg-add { background-image: linear-gradient(var(--sx-add-bg), var(--sx-add-bg)); box-shadow: inset 3px 0 0 var(--sx-add); }
.sx .chg-del { background-image: linear-gradient(var(--sx-del-bg), var(--sx-del-bg)); box-shadow: inset 3px 0 0 var(--sx-del); }
.sx .chg-mod { background-image: linear-gradient(var(--sx-mod-bg), var(--sx-mod-bg)); box-shadow: inset 3px 0 0 var(--sx-mod); }
.sx-g.g-mod { color: var(--sx-mod); }
/* A statement that changed inside: its own lines, as a code diff under its row. */
.sx-code { padding: 2px 0; font: 13px/1.5 var(--font-mono, "IBM Plex Mono", ui-monospace, Menlo, monospace); }
.sx-cl { padding: 0 8px; white-space: pre-wrap; overflow-wrap: anywhere; }
.sx-cl code { font: inherit; }
.sx-cl.k-ctx { padding-left: calc(8px + 1.8ch); }
.sx-cl.k-add { background: var(--sx-add-bg); } .sx-cl.k-del { background: var(--sx-del-bg); }
.sx-cl.k-gap { padding-left: calc(8px + 1.8ch); font-style: italic; color: var(--sx-muted, #64748b); }
.sx-g { display: inline-block; width: 1.4ch; margin-right: .4ch; font-weight: 700; text-align: center; }
.sx-g.g-add { color: var(--sx-add); } .sx-g.g-del { color: var(--sx-del); }
.sx-removed { box-shadow: inset 3px 0 0 var(--sx-del); }
.sx-removed > * + * { border-top: 1px solid var(--sx-stroke); }
.sx-was-label { color: var(--sx-del); }
.sx-note { color: var(--sx-muted); font-style: italic; margin-left: 1ch; font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); font-size: 12px; }
.sx-same > summary { color: var(--sx-muted); cursor: pointer; list-style: none; padding: 2px 8px; font-style: italic; }
.sx-same > summary::-webkit-details-marker { display: none; }
.sx-same[open] > summary { border-bottom: 1px dashed var(--sx-stroke); }
.sx-tree > .sx-same + *, .sx-fbody > .sx-same + * { border-top: 1px solid var(--sx-stroke); }

/* Diff schematics: if / elif / else stacked like code, and neutral nesting tints. */
.sx-diff .sx-branch { flex-direction: column; }
.sx-diff .sx-arm + .sx-arm { border-left: 12px solid var(--sx-bar-if); border-top: 1px solid var(--sx-stroke); }
.sx-diff .sx-arm { border-left: 12px solid var(--sx-bar-if); }
.sx-diff .sx-ahead > .sx-kw { color: var(--sx-bar-if); }
.sx-diff .sx-ahead { background: transparent; border-bottom: 1px solid var(--sx-stroke); }
.sx-diff { --sx-rail-branch: #8A8C92; --sx-rail-loop: #8A8C92; --sx-rail-try: #8A8C92; --sx-rail-held: #8A8C92; --sx-rail-inner: #8A8C92; --sx-d1: #f8fafc; --sx-d2: #f1f5f9; --sx-d3: #f8fafc; --sx-d4: #f1f5f9; --sx-d5: #f8fafc; --sx-d6: #f1f5f9; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sx-diff { --sx-rail-branch: #6B7480; --sx-rail-loop: #6B7480; --sx-rail-try: #6B7480; --sx-rail-held: #6B7480; --sx-rail-inner: #6B7480; --sx-d1: #1a2029; --sx-d2: #1e2530; --sx-d3: #1a2029; --sx-d4: #1e2530; --sx-d5: #1a2029; --sx-d6: #1e2530; } }
:root[data-theme="dark"] .sx-diff { --sx-rail-branch: #6B7480; --sx-rail-loop: #6B7480; --sx-rail-try: #6B7480; --sx-rail-held: #6B7480; --sx-rail-inner: #6B7480; --sx-d1: #1a2029; --sx-d2: #1e2530; --sx-d3: #1a2029; --sx-d4: #1e2530; --sx-d5: #1a2029; --sx-d6: #1e2530; }
.sx-diff[data-look="code"] :is(.sx-arm, .sx-arm + .sx-arm) { border-left: 0; }

/* Rows and block headers that know their source lines: the page shows the code. */
.sx [data-lines] { cursor: help; }
.sx [data-lines]:hover, .sx [data-lines].sx-picked { outline: none; box-shadow: inset 0 0 0 100vmax color-mix(in srgb, var(--sx-pick, #2563eb) 16%, transparent); }

/* Exit rail, drawn by schematic-rail.js. */
.sx-rail { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.sx-rail .spine { stroke: var(--sx-rail); stroke-width: 2; }
.sx-rail .r-return { stroke: var(--sx-rail-return); fill: var(--sx-rail-return); }
.sx-rail .r-throw { stroke: var(--sx-rail-throw); fill: var(--sx-rail-throw); }
.sx-rail .r-end { stroke: var(--sx-rail-end); fill: var(--sx-rail-end); }
.sx-rail line[class^="r-"] { stroke-width: 1.6; opacity: .85; }
.sx-rail path, .sx-rail circle { stroke: none; }
.sx-rail .stop { fill: var(--sx-rail-stop); }
.sx-rail text { fill: var(--sx-muted); font: 600 11px var(--font-title, "IBM Plex Sans", "Segoe UI", sans-serif); }

/* What the function calls, grouped by where the callee comes from (callees.js). */
.sx-deps { margin-top: 10px; border-top: 1px solid var(--sx-stroke); padding-top: 6px; font-size: 12px; display: grid; gap: 3px; }
.sx-dephead { font-family: var(--font-title, "IBM Plex Sans", "Segoe UI", Helvetica, Arial, sans-serif); font-weight: 600; font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--sx-muted); }
/* Zoomed out the calls are one running line; it wraps, nothing is hidden. */
.sx-deps-line { display: block; font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, Menlo, monospace); color: var(--sx-muted); overflow-wrap: anywhere; }
.sx-deps-line .sx-dephead { margin-right: 2px; }
.sx-deprow { display: grid; grid-template-columns: minmax(90px, 26%) 1fr; gap: 8px; align-items: baseline; }
.sx-depfrom { color: var(--sx-muted); overflow-wrap: anywhere; }
.sx-depfrom b { font: 600 12px var(--font-mono, "IBM Plex Mono", ui-monospace, Menlo, monospace); padding: 0 4px; border-radius: 3px; background: var(--sx-chip, rgba(100,116,139,.14)); color: var(--sx-fg); }
.sx-deprow.o-class .sx-depfrom, .sx-deprow.o-file .sx-depfrom, .sx-deprow.o-project .sx-depfrom, .sx-deprow.o-package .sx-depfrom { color: var(--sx-fg); font-weight: 600; }
.sx-depnames { display: flex; flex-wrap: wrap; gap: 2px 10px; font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, Menlo, monospace); overflow-wrap: anywhere; min-width: 0; }
.sx-deprow.o-builtin .sx-depnames, .sx-deprow.o-value .sx-depnames, .sx-deprow.o-local .sx-depnames, .sx-deprow.o-rest .sx-depnames { color: var(--sx-muted); }
.sx-dep i { font-style: normal; color: var(--sx-muted); margin-left: 1px; }
.sx-calls { margin-left: 8px; color: var(--sx-muted); font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, Menlo, monospace); font-size: 13px; }
/* Where a callee in the footer is called: hover or click its name. */
.sx-dep[data-uses] { cursor: pointer; border-radius: 3px; padding: 0 2px; margin: 0 -2px; }
/* Targets of 24px at least: in the grid of calls, and inside the one-line list. */
.sx-depnames .sx-dep[data-uses] { display: inline-flex; align-items: center; min-height: 24px; }
.sx-deps-line .sx-dep[data-uses] { display: inline-block; padding-block: 4px; margin-block: -4px; }
.sx-dep[data-uses]:hover, .sx-dep[data-uses].on { background: var(--sx-uses-bg, rgba(250, 204, 21, .35)); }
.sx .sx-uses { outline: none; background-image: linear-gradient(var(--sx-uses-bg, rgba(250, 204, 21, .28)), var(--sx-uses-bg, rgba(250, 204, 21, .28))); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sx { --sx-uses: #facc15; --sx-uses-bg: rgba(250, 204, 21, .22); } }
:root[data-theme="dark"] .sx { --sx-uses: #facc15; --sx-uses-bg: rgba(250, 204, 21, .22); }
.sx-dep.chg-add { color: var(--sx-add); } .sx-dep.chg-del { color: var(--sx-del); text-decoration: line-through; text-decoration-thickness: 1px; }
.sx-dep .sx-g { width: auto; margin-right: 2px; }

/* Syntax highlighting (src/highlight.js, wrapped by src/highlight-dom.js): the
   token spans of the Denoised and Full levels, the same levels of diffs and the source
   inspector, which sit in .sx and in .inspect. The colors follow the page
   theme like the schematic tokens do. */
:root { --tok-k: #6d28d9; --tok-s: #166534; --tok-n: #92400e; --tok-c: #566070; --tok-f: #1d4ed8; --tok-t: #0e6b64; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --tok-k: #c4b5fd; --tok-s: #4ade80; --tok-n: #fbbf24; --tok-c: #9ca3af; --tok-f: #93c5fd; --tok-t: #5eead4; }
}
:root[data-theme="dark"] { --tok-k: #c4b5fd; --tok-s: #4ade80; --tok-n: #fbbf24; --tok-c: #9ca3af; --tok-f: #93c5fd; --tok-t: #5eead4; }
.tok-k { color: var(--tok-k); }
.tok-s { color: var(--tok-s); }
.tok-n { color: var(--tok-n); }
.tok-c { color: var(--tok-c); font-style: italic; }
.tok-f { color: var(--tok-f); }
.tok-t { color: var(--tok-t); }

/* ---------- code-shaped schematics (codelook.js) ----------
   Shared by the app and the landing page.
   Schematic, Minimal and Outline keep the code view's geometry: the same gutter of
   line numbers, nesting as indentation, rows in source order. A block is a thin rule
   in its colour beside its body instead of a 12px bar and a box, if/else arms stack
   as they do in code, and depth tints give way to a faint tint on block heads. So a
   zoom from Denoised to Schematic changes what each line says, not where it is. */
/* The gutter, left to right: sign (diffs only, 2ch) | line number (4ch, right-aligned) | mark (2ch) | 1ch lane | code.
   The lane is where a held region's rail sits at depth 0 (plan 2026-10-30, D3).
   Sign, number and mark are all placed against .sx-body, so nesting never moves them and no mark can touch a rail.
   A file is 7ch (number 0-4, mark 4-6, lane 6-7); a diff is 9ch (sign 0-2, number 2-6, mark 6-8, lane 8-9). The source view (style.css) has the same columns. */
.sx[data-look="code"] { --gut: 7ch; --ind: 2ch; }
.sx[data-look="code"] .sx-tree { border: 0; padding-left: var(--gut); }
.sx[data-look="code"] .sx-tree > * + *, .sx[data-look="code"] .sx-fbody > * + *, .sx[data-look="code"] .sx-abody > * + *,
.sx[data-look="code"] .sx-cbody > * + *, .sx[data-look="code"] .sx-held > * + *, .sx[data-look="code"] .sx-fold > * + *, .sx[data-look="code"] .sx-try > * + *,
.sx[data-look="code"] .sx-fbody, .sx[data-look="code"] .sx-cases, .sx[data-look="code"] .sx-case + .sx-case { border-top: 0; }
.sx[data-look="code"] [class*=" d"], .sx[data-look="code"] [class^="d"] { background: transparent; }
.sx[data-look="code"] .sx-row, .sx[data-look="code"] .sx-exit, .sx[data-look="code"] .sx-guard, .sx[data-look="code"] .sx-fhead,
.sx[data-look="code"] .sx-ahead, .sx[data-look="code"] .sx-clabel, .sx[data-look="code"] .sx-bar { min-height: 0; padding: 0 8px 0 0; }
/* The gutter: each line's first source line, where the code view puts its number. */
.sx[data-look="code"] [data-ln]::before { content: attr(data-ln); position: absolute; left: 0; width: calc(var(--gut) - 3ch); text-align: right; color: var(--sx-muted); opacity: .6; font-style: normal; font-weight: 400; user-select: none; }
/* Blocks: the head is a line of code; the body is indented with a rail whose shape says what the block is. */
.sx[data-look="code"] .sx-frame { border-left: 0; }
.sx[data-look="code"] .sx-fbody, .sx[data-look="code"] .sx-abody, .sx[data-look="code"] .sx-cases { margin-left: var(--ind); padding-left: calc(var(--ind) - 2px); }
.sx[data-look="code"] :is(.sx-abody, .sx-cases) { border-left: 1px solid var(--sx-rail-branch); }
.sx[data-look="code"] .f-loop > .sx-fbody { border-left: 2px solid var(--sx-rail-loop); }
.sx[data-look="code"] :is(.f-try, .f-catch, .f-finally) > .sx-fbody { border-left: 2px dashed var(--sx-rail-try); }
.sx[data-look="code"] .f-callback > .sx-fbody { border-left: 1px dotted var(--sx-rail-inner); }
.sx[data-look="code"] :is(.f-if, .f-with, .f-block, .f-type, .f-switch) > .sx-fbody { border-left: 1px solid var(--sx-rail-branch); }
.sx[data-look="code"] .f-with.held > .sx-fbody { border-left: 3px double var(--sx-rail-held); }
/* A synchronized or lock block is held like with lock:. */
.sx[data-look="code"] .f-block.held > .sx-fbody { border-left: 3px double var(--sx-rail-held); }
/* A lock taken and released by calls (core/held.ts, plan 2026-10-30 D3): a 3px double rail in the 1ch just left
   of the region's text, beside its rows, which keep their x (margin + border + padding = 0). At depth 0 the 1ch
   is the gutter's lane; deeper it is inside the parent's indent. Not positioned: the marks and numbers are placed
   against .sx-body, and a positioned wrapper would capture them. Ink on diffs through --sx-rail-held. */
.sx[data-look="code"] .sx-held { margin-left: calc((1ch + 3px) / -2); padding-left: calc((1ch - 3px) / 2); border-left: 3px double var(--sx-rail-held); }
/* if/else arms: one under the other, the else at the if's indentation. */
.sx[data-look="code"] .sx-branch { display: block; --bar: var(--sx-bar-if); }
.sx[data-look="code"] .sx-arm, .sx[data-look="code"] .sx-arm + .sx-arm { border-left: 0; border-top: 0; }
.sx[data-look="code"] .sx-ahead { border-bottom: 0; border-radius: 4px; }
.sx[data-look="code"] .sx-case { display: block; }
/* A chain (an if with an elif or else, a try with a handler, a switch) hangs on one spine, plan 2026-10-30 task 4.
   The spine is the arm's own left border, so every arm shares one column and nothing breaks at an arm head. It sits in
   the lane left of the heads (an if/try arm moves left by .85ch + the border and pads back by the same, so no text
   moves; a switch's spine stays in the rail column its cases already had, because their labels sit right of it).
   Each head after the first gets a tick off the spine, and the last arm ends in a corner; both are backgrounds,
   because a positioned arm would capture the line numbers, which are placed against .sx-body. The first arm draws
   its spine from under its head, the last ends at its last line, so those two draw it as a background too and
   leave their border transparent. --tk is the height of a line's middle. */
.sx[data-look="code"] .sx-chain { --spine: var(--sx-rail-branch); --sp: 1px; --run: .6ch; --tk: .5lh; --mark: linear-gradient(var(--spine), var(--spine)); }
.sx[data-look="code"] .sx-chain.dashed { --spine: var(--sx-rail-try); --sp: 2px; --dash: repeating-linear-gradient(to bottom, var(--spine) 0 4px, transparent 4px 7px); }
.sx[data-look="code"] .sx-cases.sx-chain { --run: calc(var(--ind) - 2px - .2ch); border-left: 0; padding-left: 0; }
.sx[data-look="code"] .sx-chain > :is(.sx-arm, .sx-case, .sx-frame) { background-origin: border-box; background-repeat: no-repeat; }
.sx[data-look="code"] .sx-chain > .sx-arm { margin-left: calc(-.85ch - 1px); padding-left: .85ch; border-left: 1px solid var(--sx-rail-branch); }
.sx[data-look="code"] .sx-chain > .sx-arm > .sx-abody { border-left: 0; padding-left: calc(var(--ind) - 1px); }
.sx[data-look="code"] .sx-chain > .sx-case { border-left: 1px solid var(--sx-rail-branch); padding-left: calc(var(--ind) - 2px); }
.sx[data-look="code"] .sx-chain.dashed > .sx-frame { margin-left: calc(-.85ch - 2px); padding-left: .85ch; border-left: 2px dashed var(--sx-rail-try); }
.sx[data-look="code"] .sx-chain.dashed > .sx-frame > .sx-fbody { border-left: 0; padding-left: var(--ind); }
/* a tick at every head after the first */
.sx[data-look="code"] .sx-chain > :is(.sx-arm, .sx-case, .sx-frame):not(.first) { background-image: var(--mark); background-position: 0 var(--tk); background-size: calc(var(--run) + var(--sp)) 1px; }
/* the first: the spine starts under the head (a switch has no head of its own in the chain, so it starts at its first case) */
.sx[data-look="code"] .sx-chain > :is(.sx-arm, .sx-frame).first { border-left-color: transparent; background-image: var(--dash, var(--mark)); background-position: 0 1lh; background-size: var(--sp) calc(100% - 1lh); }
.sx[data-look="code"] .sx-chain > .sx-case.first { border-left-color: transparent; background-image: var(--mark), var(--mark); background-position: 0 var(--tk), 0 var(--tk); background-size: calc(var(--run) + var(--sp)) 1px, var(--sp) calc(100% - var(--tk)); }
/* the last: the spine ends at the middle of its last line, in a corner */
.sx[data-look="code"] .sx-chain > :is(.sx-arm, .sx-case, .sx-frame).last { border-left-color: transparent; background-image: var(--mark), var(--dash, var(--mark)), var(--mark);
  background-position: 0 var(--tk), 0 0, 0 calc(100% - 1lh + var(--tk));
  background-size: calc(var(--run) + var(--sp)) 1px, var(--sp) calc(100% - 1lh + var(--tk) + 1px), calc(var(--run) + var(--sp)) 1px; }
/* diffs: ink-grey, like every rail */
.sx-diff .sx-chain { --sx-rail-branch: #8A8C92; --sx-rail-try: #8A8C92; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sx-diff .sx-chain { --sx-rail-branch: #6B7480; --sx-rail-try: #6B7480; } }
:root[data-theme="dark"] .sx-diff .sx-chain { --sx-rail-branch: #6B7480; --sx-rail-try: #6B7480; }
/* Outline's folded runs: a line of code that says what it hides and opens in place. */
.sx[data-look="code"] .sx-bar::after { display: none; }
.sx[data-look="code"] .sx-bar { font-size: .85em; line-height: 1.3; }
.sx[data-look="code"] .sx-foldtext { color: var(--sx-muted); font-style: italic; }
.sx[data-look="code"] .sx-bar[role="button"] { cursor: pointer; border-radius: 4px; }
.sx[data-look="code"] .sx-bar[role="button"]:hover, .sx[data-look="code"] .sx-bar[role="button"]:focus-visible { background: color-mix(in srgb, var(--accent, #5b45d6) 10%, transparent); }
.sx[data-look="code"] .sx-bar[aria-expanded="true"] .sx-foldtext { color: var(--accent, #5b45d6); }
.sx[data-look="code"] .sx-unfold { border-left: 2px dotted color-mix(in srgb, var(--accent, #5b45d6) 45%, transparent); margin-left: 2px; padding-left: calc(var(--ind) - 4px); }
/* A new drawing fades in, so a zoom reads as one picture changing, not a page swap. */
@keyframes sx-in { from { opacity: .35; } to { opacity: 1; } }
.sx[data-look="code"] .sx-tree, .sx-codeview pre { animation: sx-in .16s ease-out; }
@media (prefers-reduced-motion: reduce) { .sx[data-look="code"] .sx-tree, .sx-codeview pre { animation: none; } }
/* Statement marks: one 2ch column left of the text. Outside the code look the tag pills carry the same meaning. */
.sx-mk { display: none; }
.sx[data-look="code"] .sx-mk { display: block; position: absolute; left: calc(var(--gut) - 3ch); width: 2ch; text-align: center; color: var(--sx-muted); font-weight: 600; }
.sx[data-look="code"] .sx-mk.m-acquire > .mg::before { content: "⊏"; }
.sx[data-look="code"] .sx-mk.m-release > .mg::before { content: "⊐"; }
.sx[data-look="code"] .sx-mk.m-wait > .mg::before { content: "◷"; }
.sx[data-look="code"] .sx-mk.m-notify > .mg::before { content: "↗"; }
.sx[data-look="code"] .sx-mk.m-defer > .mg::before { content: "↧"; }
.sx[data-look="code"] .sx-mk.m-throw > .mg::before { content: "!"; }
.sx[data-look="code"] .sx-mk.m-write > .mg::before { content: "●"; }
.sx[data-look="code"] .sx-mk.m-write { color: var(--sx-fg); }
.sx[data-look="code"] .sx-mk:is(.m-acquire, .m-release) { color: var(--sx-mark-lock, #7A3F76); }
.sx[data-look="code"] .sx-mk.m-throw { color: var(--sx-mark-throw, #3F5B7D); }
.sx[data-look="code"] .sx-exit.m-throw .sx-t { color: var(--sx-mark-throw, #3F5B7D); font-weight: 500; }
.sx-diff[data-look="code"] .sx-mk, .sx-diff[data-look="code"] .sx-exit.m-throw .sx-t { color: var(--sx-muted); }
/* Tiers of ink (plan 2026-10-30, 1.8a): lines that leave, lock, wait, wake or change state carry the
   weight; structure is ink; local work is muted (5:1 or better, test/prominence-contrast.test.ts). The
   schematic levels only: Denoised and Full are the source view (web/public/style.css). */
.sx[data-look="code"] .tier-1 > .sx-t { font-weight: 500; }
.sx[data-look="code"] .tier-3 > .sx-t, .sx[data-look="code"] .tier-3 > .sx-t [class*="tok-"] { color: var(--sx-muted); }
.sx[data-look="code"] .cg-holds > .sx-t { color: var(--sx-mark-lock, #7A3F76); }
.sx[data-look="code"] .sx-hot { font-weight: 600; text-decoration: underline; text-decoration-color: var(--sx-faint); text-underline-offset: 3px; }
.sx-diff[data-look="code"] .cg-holds > .sx-t { color: var(--sx-fg); }
.sx[data-look="code"] .sx-mk.m-return > .mg::before { content: "⏎"; }
/* Guards (plan 2026-10-30, 1.8e): the margin says what the guard does; the amber diamond is a fourth hue. */
.sx[data-look="code"] .sx-mk.m-guard > .mg::before { content: "◇"; }
.sx[data-look="code"] .sx-guard > .sx-dia { display: none; }
.sx[data-look="code"] .sx-guard.tier-1 > .sx-pill ~ .sx-t { font-weight: 500; }
.sx[data-look="code"] .sx-guard.m-throw > .sx-pill ~ .sx-t { color: var(--sx-mark-throw, #3F5B7D); }
.sx-diff[data-look="code"] .sx-guard.m-throw > .sx-pill ~ .sx-t { color: var(--sx-fg); }
.sx[data-look="code"] .sx-mk.m-loop > .mg::before { content: "↻"; }
.sx[data-look="code"] .sx-mk.m-inner > .mg::before { content: "λ"; }
/* The diff sign has a 2ch column of its own at the gutter's left edge (the line number moves right of it); marks and rails keep theirs.
   Like the line number, it is placed against .sx-body, so nesting does not move it. */
.sx-diff[data-look="code"] { --gut: 9ch; }
.sx-diff[data-look="code"] [data-ln]::before { left: 2ch; width: calc(var(--gut) - 5ch); }
.sx[data-look="code"] .sx-g { position: absolute; left: 0; width: 2ch; margin: 0; }
.sx[data-look="code"] .sx-dep .sx-g { position: static; width: auto; margin-right: 2px; }
/* The mark says what the lock, wait or notify pill said, so the conc pills after it step aside. */
.sx[data-look="code"] .sx-mk:is(.m-acquire, .m-release, .m-wait, .m-notify, .m-defer) ~ .sx-tag:is(.t-conc, .t-defer), .sx[data-look="code"] .sx-mk.m-throw ~ .sx-pill.x-throw { display: none; }
/* Structure carries no fill in the code look: block heads are plain lines, the catch keyword is ink. */
.sx[data-look="code"] .sx-ahead, .sx[data-look="code"] .f-catch > .sx-fhead { background: transparent; }
.sx[data-look="code"] .f-catch > .sx-fhead > .sx-kw { color: var(--sx-fg); }
/* A diff's change bar would sit on the first letter (the sign column and the wash say the same). */
.sx-diff[data-look="code"] :is(.chg-add, .chg-del, .chg-mod, .sx-removed) { box-shadow: none; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .sx { --sx-mark-lock: #D08FCB; --sx-mark-throw: #8FB0D4; } }
:root[data-theme="dark"] .sx { --sx-mark-lock: #D08FCB; --sx-mark-throw: #8FB0D4; }

/* Landmarks (plan 2026-10-30, 1.8f): one tick per tier-1 line at the drawing's right edge. The hit area is 24px tall. */
.sx-codeview { position: relative; }
.sx-ticks { position: absolute; top: 0; right: 2px; width: 12px; height: 100%; pointer-events: none; }
.sx-tick { position: absolute; right: 0; width: 12px; height: 4px; margin: -2px 0 0; padding: 0; border: 0; border-radius: 2px; background: var(--sx-muted); cursor: pointer; pointer-events: auto; }
.sx-tick::after { content: ""; position: absolute; inset: -10px -4px; }
.sx-tick:hover, .sx-tick:focus-visible { transform: scaleX(1.5); outline: none; }
.sx-tick.cg-raises { background: var(--sx-mark-throw, #3F5B7D); } .sx-tick.cg-holds { background: var(--sx-mark-lock, #7A3F76); } .sx-tick.cg-changes { background: var(--sx-fg); }
.sx-diff .sx-tick, .sx-codediff .sx-tick { background: #8A8C92; }
.sx-diff .sx-tick.chg-add, .sx-codediff .sx-tick.chg-add { background: var(--sx-add); }
.sx-diff .sx-tick.chg-del, .sx-codediff .sx-tick.chg-del { background: transparent; box-shadow: inset 0 0 0 1.5px var(--sx-del); }
@media (max-width: 600px) { .sx-ticks { display: none; } }
@media (prefers-reduced-motion: reduce) { .sx-tick:hover { transform: none; } }
