:root {
  color-scheme: dark;
  --bg: #1e1e1e;
  --panel: #262626;
  --accent: #0078d7;
  --accent-hi: #1e90ff;
  --text: #e8e8e8;
  --muted: #9a9a9a;
  --err: #ff6b6b;
  --ok: #7ed07e;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 "Segoe UI", system-ui, sans-serif;
  max-width: 860px;
  margin: 0 auto;
  padding: 16px;
}

header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid #333;
  padding-bottom: 10px;
}

h1 { font-size: 20px; margin: 0; }
h1 span { color: var(--accent-hi); }
h2 { font-size: 14px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 18px 0 8px; }

.title-block { display: flex; flex-direction: column; align-items: flex-start; }
.subtitle {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 2px;
}

.meta { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); }
#nodeId { color: var(--text); }

/* Single header pill: connection status + mode (joining… → Public/Private),
   with errors in red and transient detail in the hover tooltip. */
.mode {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 3px 9px;
  border-radius: 999px;
  line-height: 1;
  white-space: nowrap;
  cursor: default;
}
.mode-public { color: #e6a23c; border: 1px solid #e6a23c; }
.mode-private { color: var(--ok); border: 1px solid var(--ok); }
.mode-joining { border-style: dashed; opacity: .75; }
.mode-err { color: var(--err); border: 1px solid var(--err); }

section {
  background: var(--panel);
  border: 1px solid #333;
  border-radius: 8px;
  padding: 12px 16px;
  margin-top: 12px;
}

.hint { color: var(--muted); font-size: 12.5px; margin: 0 0 10px; }
.hint code { color: var(--accent-hi); }

label { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 10px; }
input, textarea {
  display: block;
  width: 100%;
  margin-top: 4px;
  background: #1a1a1a;
  color: var(--text);
  border: 1px solid #444;
  border-radius: 6px;
  padding: 8px 10px;
  font: 13px/1.5 ui-monospace, Consolas, monospace;
}
input:focus, textarea:focus { outline: none; border-color: var(--accent); }
textarea { min-height: 150px; resize: vertical; }

/* Run Code editor (OverType): bordered panel matching the section; the
   invisible-textarea WYSIWYG renders fenced code as highlighted blocks.
   OverType's autoResize mode makes the wrapper grow with its content,
   so the panel expands instead of scrolling internally — it starts at
   min-height and only gets taller. */
#editor {
  height: auto;
  min-height: 220px;
  background: #1a1a1a;
  border: 1px solid #444;
  border-radius: 6px;
  overflow: auto;
}
#editor:focus-within { border-color: var(--accent); }
#editor .overtype-container { height: auto; min-height: 220px; }
/* Beat OverType's auto-resize floor (60px) so the empty editor still
   presents as a proper panel. */
#editor .overtype-wrapper { height: auto; min-height: 220px !important; }
/* Floating bottom dock: the language chips, Run/Share row and
   public-network note stick to the bottom of the viewport while the run
   section is in view, so the controls are always available — no matter
   where the code is scrolled. The editor itself grows with its content
   (OverType autoResize, no inner scrollbar), so there is ONE universal
   page scroll: the dock simply floats over the document and covers the
   code that scrolls beneath it. Negative margins stretch the dock over
   the section's padding so it reads as a solid, edge-to-edge bar. */
.run-dock {
  position: sticky;
  bottom: 0;
  z-index: 12;
  margin: 0 -16px -12px;
  padding: 8px 16px 12px;
  background: var(--panel);
  border-top: 1px solid #333;
  /* Match the card's rounded bottom corners (the dock is stretched to the
     card's edges by the negative margins above). */
  border-radius: 0 0 8px 8px;
}
/* "More code below" chevron: a tiny centered marker INSIDE the dock,
   padded 2px under its top border — the visible bottom edge of the
   entry box — and nowhere near the pills below it. JS toggles .visible
   while content is hidden beneath the fold; the marker eases in and
   out (opacity/transform only — display can't animate, so the element
   stays in the DOM and merely transparent when idle). Pure decoration
   — pointer-events: none so it never intercepts clicks. */
.more-hint {
  position: absolute;
  /* 2px under the dock's top edge, i.e. just below the entry box, so the
     marker reads as a "continues below" tab rather than part of the row. */
  top: 2px;
  left: 0;
  right: 0;
  /* Zero height with no line box: an inline triangle otherwise rides the
     text baseline of a 21px strut, which painted it 9–15px below the
     border — right on top of the language pills. The triangle is a block,
     so its border box starts exactly at `top` and ends at `top` + 6px,
     leaving the 16px gap above .lang-status clear. */
  height: 0;
  line-height: 0;
  font-size: 0;
  text-align: center;
  pointer-events: none;
  z-index: 9;
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity .22s ease, transform .22s ease;
}
.more-hint.visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .more-hint { transition: none; }
}
.more-hint .more-tri {
  display: block;
  margin: 0 auto;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid var(--accent-hi);
  opacity: 1;
}
/* Placeholder: OverType's own div collapses newlines (white-space:normal)
   and the fenced-python hint needs them — force pre + monospace. */
#editor .overtype-placeholder {
  white-space: pre;
  font-family: ui-monospace, Consolas, monospace;
  color: var(--muted);
}
/* Bright highlight for fence header info-strings (language declaration +
   ephemeral parameters) and shebang lines inside code blocks. */
#editor .code-header,
#editor .shebang {
  color: #7cfc9a;
  font-weight: 600;
}
/* The language declaration mirrors the pill colors: blue when supported,
   orange when unknown (seed/file info-strings stay green). */
#editor .code-header.valid { color: var(--accent-hi); }
#editor .code-header.invalid { color: #ffb86b; }
#editor .shebang {
  display: inline;
  background: rgba(124, 252, 154, 0.08);
  border-radius: 3px;
}

/* Language badges under the editor: each declared fence language gets a
   chip — green when it's in the language map, amber when it isn't. */
.lang-status {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.lang-status[hidden] { display: none; }

/* Per-block run status lives INSIDE the language pills: grey + spinner while
   running, three dots while a chained block waits for the one above it, green
   check on success, yellow triangle on warnings (clean exit with stderr), red x
   on failure - see the .lang-chip.waiting/running/done/warn/err rules below.
   Every state fills the same fixed-width slot, so a pill never changes width
   as its block changes state.
*/
.lang-chip {
  font: 12px ui-monospace, Consolas, monospace;
  padding: 2px 8px;
  border-radius: 10px;
  border: 1px solid #444;
  background: #1a1a1a;
  color: var(--muted);
  /* Pills are one line tall, always: a wrapped two-line pill breaks the
     straight shared edge a .pill-group is built on, and ragged heights make
     the dock jump as blocks are typed. Long names are elided in JS
     (shortPillName) rather than wrapped here. */
  white-space: nowrap;
}
.lang-chip.valid { border-color: #1e4a6e; color: var(--accent-hi); }
.lang-chip.invalid { border-color: #8a5a1e; color: #ffb86b; }
.lang-chip.file { border-color: #444; color: var(--muted); }
.lang-chip.artifact { border-color: #1e4a6e; color: #6db3ff; }
/* An artifact pill is a promise before the run and a download after it, and
   its 📦 says which: grey while the file has not been generated, full colour
   once it has been. Only paint changes, never geometry — greyscale and opacity
   leave the glyph's advance alone, so a pill keeps one width through every
   state (the same rule the status slot below follows). A declared name whose
   run finished without producing it stays grey and is struck through: a pill
   for a file that is not there, rather than a promise that stays pending
   forever. */
.lang-chip.artifact-pending { color: var(--muted); }
.lang-chip.artifact-pending .pill-art {
  filter: grayscale(1);
  opacity: 0.45;
}
.lang-chip.artifact-missing .pill-art { opacity: 0.3; }
.lang-chip.artifact-missing { text-decoration: line-through; }

/* Pills that belong to one fence (the language declaration plus the
   artifact pills it writes) form a group: their shared edge is squared
   off and the 1px borders overlap, so the row reads as one block's
   declaration + outputs rather than as unrelated controls. A lone pill
   keeps its rounded shape; a middle pill in a three-pill group ends up
   rectangular, sharing both of its edges. */
.pill-group { display: inline-flex; }
.pill-group > .lang-chip { border-radius: 0; position: relative; }
.pill-group > .lang-chip + .lang-chip { margin-left: -1px; }
.pill-group > .lang-chip:first-child { border-radius: 10px 0 0 10px; }
.pill-group > .lang-chip:last-child { border-radius: 0 10px 10px 0; }
.pill-group > .lang-chip:only-child { border-radius: 10px; }
/* A group wider than the dock shrinks instead of spilling past its right
   edge: the row stays one line tall and the pills ellipsize in place (the
   full name is still in each pill's tooltip). min-width: 0 is what lets a
   flex item shrink below its content size at all. */
.lang-status > .pill-group { max-width: 100%; min-width: 0; }
.pill-group > .lang-chip {
  min-width: 3ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The overlapped border belongs to the pill under the pointer: keep it
   on top, or the neighbour paints its own border over the highlight. */
.pill-group > .lang-chip:hover,
.pill-group > .lang-chip:focus-visible { z-index: 1; }

/* Pills are affordances: a supported language pill re-runs just its block,
   and a ready artifact pill downloads that one file — both highlight their
   block in the editor while hovered/focused. */
.lang-chip.rerun,
.lang-chip.artifact-ready { cursor: pointer; }
/* A seedfile pill is the fold toggle for its own block. */
.lang-chip.foldable { cursor: pointer; }
.lang-chip.rerun:hover,
.lang-chip.rerun:focus-visible,
.lang-chip.artifact-ready:hover,
.lang-chip.artifact-ready:focus-visible,
.lang-chip.foldable:hover,
.lang-chip.foldable:focus-visible {
  border-color: var(--accent-hi);
  background: #14304a;
  color: #fff;
  outline: none;
}
.lang-chip[tabindex]:focus-visible { outline: 1px solid var(--accent-hi); }

/* The block behind the hovered pill, highlighted in place inside OverType's
   preview (the invisible textarea can't show a selection, so the rendered
   fence — opening line, code, closing line — gets the tint instead). The
   !important is forced: OverType paints its own fence backgrounds with
   !important, and #editor's id gives this rule the specificity to win. */
#editor .overtype-wrapper .overtype-preview .block-hover {
  background-color: rgba(30, 144, 255, 0.16) !important;
  box-shadow: inset 2px 0 0 var(--accent-hi) !important;
}

/* A folded seedfile body: the payload collapses to this one placeholder line
   in OverType's preview, tinted blue so it reads as a summary of staged content
   rather than as code. The !important is forced — OverType paints the fence
   backgrounds itself. */
#editor .overtype-preview pre.code-block.seed-fold {
  background: #0f2130 !important;
  border: 1px dashed #1e4a6e !important;
}
#editor .overtype-preview pre.code-block.seed-fold code {
  color: #6db3ff !important;
  font-style: italic;
}

/* Run-status overlay on the language pills while a run is in flight. */
.lang-chip.running { border-color: #555; color: var(--muted); background: #222; }
.lang-chip.done { border-color: #2d5a2d; color: var(--ok); }
.lang-chip.warn { border-color: #8a7a1e; color: #ffd76b; }
.lang-chip.err { border-color: #5a2d2d; color: var(--err); }

/* The status slot: a fixed 10px box at the head of the pill, so a block's pill
   keeps the same width as it moves waiting -> running -> done. A monospace
   `...` was wider than the spinner and the check mark it became, which made
   the whole dock reflow mid-run. The slot is only reserved once a block HAS a
   status - an idle pill never ran, so it carries no indicator and no dead
   space. */
.chip-run {
  display: none;
  width: 10px;
  height: 10px;
  margin-right: 4px;
  text-align: center;
  /* The glyph's own line box must not outgrow the 10px slot: a taller line box
     makes the chip 0.6px taller on ✓/⚠/✕ than on the spinner. */
  line-height: 10px;
  vertical-align: -1px;
}
.lang-chip.waiting .chip-run,
.lang-chip.running .chip-run,
.lang-chip.done .chip-run,
.lang-chip.warn .chip-run,
.lang-chip.err .chip-run { display: inline-block; }

/* A chained run queues blocks behind the one in flight. The three dots are
   drawn (2px each, 4px apart) rather than typed: they sit closer together than
   a monospace `...` and span exactly the 10px the spinner and the status
   glyphs occupy, so the waiting mark changes neither the mark's weight nor the
   pill's width. They blink while the block waits. */
.lang-chip.waiting { border-color: #3a3a3a; color: var(--muted); background: #1e1e1e; }
.lang-chip.waiting .chip-run {
  position: relative;
  animation: chip-wait 1.4s ease-in-out infinite;
}
.lang-chip.waiting .chip-run::before {
  content: "";
  position: absolute;
  top: 4px;
  left: 0;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 4px 0 0 currentColor, 8px 0 0 currentColor;
}
.lang-chip.running .chip-run {
  /* border-box, so the 2px ring stays inside the 10px slot the glyphs use. */
  box-sizing: border-box;
  border: 2px solid rgba(255, 255, 255, 0.25);
  border-top-color: var(--muted);
  border-radius: 50%;
  animation: chip-spin 0.8s linear infinite;
}
@keyframes chip-spin { to { transform: rotate(360deg); } }
@keyframes chip-wait { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* Disclaimer directly under the Run buttons. */
.run-note {
  font-size: 12px;
  color: var(--muted);
  margin: 10px 0 0;
  border-left: 3px solid var(--err);
  padding-left: 8px;
}
.run-note .caps {
  text-transform: uppercase;
  font-weight: 700;
  color: var(--err);
}
.run-note strong { color: var(--err); }
.run-note.private { border-left-color: var(--ok); }
.run-note.private .caps,
.run-note.private strong { color: var(--ok); }

/* Red note shown in the output only when `unsafe` was actually tried. */
.unsafe-note {
  color: var(--err);
  font-size: 12px;
  margin: 6px 0 0;
}

/* Artifacts: inline image previews with floating copy/download icons. */
.block.artifacts {
  /* Anchors the floating .artifact-tools over the preview. */
  position: relative;
  border-top: 1px dashed #333;
  margin-top: 8px;
  padding-top: 8px;
  white-space: normal;
}
.artifact-img {
  display: block;
  max-width: 100%;
  max-height: 320px;
  /* Keep genuinely tiny artifacts (e.g. a 1x1 test image) from rendering
     as an invisible dot at natural size. */
  min-width: 32px;
  min-height: 32px;
  border-radius: 6px;
  border: 1px solid #333;
  margin: 6px 0;
  background: #111;
}
/* Copy image / Download: the same floating top-right affordance as the
   per-code-block copy button, so artifacts carry no separate text buttons. */
.artifact-tools {
  position: absolute;
  top: 2px;
  right: 2px;
  display: flex;
  gap: 2px;
  opacity: .55;
  transition: opacity .12s ease;
}
.block.artifacts:hover .artifact-tools,
.artifact-tools:focus-within { opacity: 1; }
.artifact-tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .55);
  color: var(--muted);
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 4px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.artifact-tool:hover { background: rgba(0, 0, 0, .85); color: var(--text); }
.artifact-tool svg { width: 13px; height: 13px; }
.artifact-tool.ok { color: var(--ok); }
/* Touch devices have no hover: keep the icons permanently visible rather
   than fading a tap target out. */
@media (hover: none) {
  .artifact-tools { opacity: 1; }
}
.artifact-count { color: var(--muted); font-size: 12px; margin-top: 2px; }
/* Inline artifact filename caption (interleaved view) — the file name used
   to ride on the old action bar. */
.artifact-name {
  color: var(--muted);
  font-size: 12px;
  font-family: ui-monospace, Consolas, monospace;
  margin-top: 2px;
  overflow-wrap: anywhere;
}
/* Non-image artifact shown inline under its run result (interleaved view):
   the floating icons sit over its top-right, so leave them room. */
.artifact-file {
  color: #6db3ff;
  font-size: 13px;
  font-family: ui-monospace, Consolas, monospace;
  margin: 6px 0;
  padding-right: 64px;
}

.actions { display: flex; gap: 10px; margin-top: 10px; align-items: center; }
/* Spinner lives inside the Run button: centered over the label while a
   job is in flight. The button is disabled during a run, so this doubles
   as a visible debounce — a disabled button swallows clicks, so mashing
   Run can't queue up multiple jobs. */
.run-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 150px;
}
.run-btn .run-label { transition: opacity 0.15s; }
.run-btn .spinner {
  display: none;
  position: absolute;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
.run-btn.busy .run-label { opacity: 0.15; }
.run-btn.busy .spinner { display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }
button {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 8px 18px;
  font-size: 14px;
  cursor: pointer;
}
button:hover { background: var(--accent-hi); }
button.secondary { background: #3a3a3a; }
button.secondary:hover { background: #4a4a4a; }
.share-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: #3a3a3a;
  font-size: 12px;
  padding: 6px 12px;
}
.share-btn:hover { background: #4a4a4a; }
/* "Share link output" mode picker in the Run/Share row: None / Link only /
   Link + QR. A borderless inline control so the three options read as
   one compact group without native fieldset border artifacts. */
.print-link-opt,
fieldset.print-link-opt {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 10px;
  font-size: 11.5px;
  color: var(--muted);
  border: none !important;
  outline: none !important;
  background: transparent !important;
  margin: 0;
  padding: 0;
  user-select: none;
  -webkit-appearance: none;
  appearance: none;
}
.print-link-opt:hover,
fieldset.print-link-opt:hover { color: var(--text); }
.print-link-opt legend,
.print-link-opt .print-link-opt-legend {
  font-size: 11.5px;
  color: var(--muted);
  padding: 0;
  border: 0;
  float: left; /* keeps the legend inline with the options, not above */
}
.print-link-opt legend + label,
.print-link-opt .print-link-opt-legend + label { margin-left: 4px; }
.print-link-opt label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  cursor: pointer;
  white-space: nowrap;
}
.print-link-opt:hover label { color: var(--text); }
.print-link-opt input {
  margin: 0;
  accent-color: var(--accent-hi);
  cursor: pointer;
  vertical-align: middle;
}
/* The QR code image inside the .try-it-here footer (on screen and in
   print): modest size, white quiet zone so scanners lock on even in
   dark themes. */
.try-it-here .try-qr {
  display: block;
  width: 92px;
  height: 92px;
  margin-top: 8px;
  image-rendering: pixelated;
}
/* The QR <img> sits inside a link to the same share URL, so the printed
   paper/PDF keeps a second clickable target on top of the "Try it here"
   text link. Pure wrapper: inline-block so the anchor hugs the image
   with no extra spacing or color. */
.try-it-here a.try-qr-link {
  display: inline-block;
  line-height: 0;
  color: inherit;
}
.try-it-here a.try-qr-link:hover { text-decoration: none; }
/* On-screen mirror of the print "Try it here" footer: one element pinned
   at the end of the Output box while the checkbox is on, so the payload
   link is visible without printing (and the print snapshot already
   carries it). Same class as the print footer, so one set of rules
   covers both. Just the link — no bare URL text; the address lives in
   the href (clickable in printed PDFs) and the hover title. */
.try-it-here {
  margin-top: 14px;
  padding-top: 8px;
  border-top: 1px solid #333;
}
.try-it-here a {
  color: var(--accent-hi);
  font-weight: 600;
  text-decoration: none;
}
.try-it-here a:hover { text-decoration: underline; }

.cluster-list { list-style: none; margin: 0 0 10px; padding: 0; font-size: 12.5px; }
.cluster-list li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px dashed #333;
}
.cluster-list .empty { color: var(--muted); border: none; }
.cluster-list code { color: var(--accent-hi); }
.cluster-list .images {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  color: var(--muted);
}
/* Warm-image pills reuse the editor's language-chip look, slightly tighter
   for the dense peer list; the full image ref is on each pill's title. */
.cluster-list .images .lang-chip {
  font-size: 11px;
  padding: 1px 6px;
}
.cluster-list .rtt { color: var(--muted); white-space: nowrap; }
.tag {
  font-size: 10.5px;
  padding: 1px 6px;
  border-radius: 8px;
  text-transform: uppercase;
  letter-spacing: .05em;
}
.tag.seed { background: #2d4a2d; color: var(--ok); }
.count { color: var(--muted); }

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Sticky so the toolbar can float. The ROW is the sticky box, but in
     the stuck (.floating) state only the button cluster is visible: the
     section label fades out and the row itself stays transparent, so the
     code shows through and the top of the card stays "open" instead of
     gaining a second header bar. JS toggles .floating when the row is
     actually stuck, and a row pushed out of its containing block's end
     (scrolled past the last line) drops the state as it leaves.
     The containing block bounds the float: the run head sits in .run-body
     (head + editor only), so the buttons can't outlive the code area; the
     output head's block is the output box itself. */
  position: sticky;
  top: 0;
  z-index: 15;
}
.section-head h2 {
  margin: 18px 0 8px;
  transition: opacity .15s ease;
}
/* Stuck state: the buttons alone float — no background, no pill, just the
   icons, slightly translucent. A soft drop shadow keeps them legible over
   whatever code scrolls beneath them. pointer-events pass through the
   transparent row so clicks land on the code behind it; the buttons
   themselves stay clickable. */
.section-head.floating { pointer-events: none; }
.section-head.floating h2 { opacity: 0; }
.section-head.floating .head-actions {
  pointer-events: auto;
  opacity: .8;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .95));
}
.head-actions {
  display: flex;
  gap: 2px;
  margin-bottom: 6px;
  align-items: center;
  /* Pull the cluster in from the card's padding edge so the toolbars sit
     inside the code panel's own block background. OverType insets its
     rendered code block by 16px (wrapper padding) behind the editor's 1px
     border, so 17px lands the buttons on that block's right edge — in
     both the static and the floating state (sticky keeps the static
     horizontal alignment). Same offset on the Output head, which shares
     this rule. */
  margin-right: 17px;
}

/* Trailing (i) help pill in the language-chip row: lists every supported
   language (canonical + aliases) and the /output artifact contract on
   hover or click. The popover is an absolute overlay, so opening it never
   shifts the layout. */
.lang-help-chip {
  position: relative;
  cursor: help;
  font-weight: 700;
}
.lang-help-chip.active { border-color: var(--accent-hi); color: var(--accent-hi); }
.lang-help-pop {
  /* Anchored in place of the Run Code editor box (the JS positions it
     over the editor on open), slightly transparent so the underlying
     code stays visible, and never covering the header or the action
     buttons. */
  position: absolute;
  z-index: 20;
  background: rgba(30, 30, 30, .88);
  backdrop-filter: blur(4px);
  border: 1px solid #3a3a3a;
  border-radius: 8px;
  padding: 8px 10px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .5);
  white-space: normal;
  overflow: hidden;
}
.lang-help-pop[hidden] { display: none; }
.lang-help-title {
  font: 600 10px/1.4 ui-monospace, Consolas, monospace;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
  margin-bottom: 4px;
}
.lang-help-list {
  display: grid;
  /* auto-fill: the column count adapts to the popover width (which tracks
     the editor box), so entries stay readable on narrow boxes and the
     list stays short on wide ones. */
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  gap: 1px 10px;
  font: 10px/1.25 ui-monospace, Consolas, monospace;
}
.lang-help-item {
  /* Clickable: appends a demo snippet to the Run Code editor. */
  cursor: pointer;
  border-radius: 4px;
  padding: 1px 4px;
  margin: 0 -4px;
  outline-offset: 1px;
}
.lang-help-item:hover { background: rgba(255, 255, 255, .07); }
.lang-help-item:focus-visible {
  outline: 1px solid var(--accent-hi);
  background: rgba(255, 255, 255, .07);
}
.lang-help-item code { color: var(--text); }
.lang-help-item .als { color: var(--muted); }
.lang-help-note {
  margin-top: 6px;
  padding-top: 4px;
  border-top: 1px solid #333;
  font: 10px/1.35 ui-monospace, Consolas, monospace;
  color: var(--muted);
}
.lang-help-note code { color: var(--accent-hi); }
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--muted);
  border: none;
  border-radius: 4px;
  padding: 4px;
  cursor: pointer;
}
.icon-btn:hover { background: #333; color: var(--text); }
.icon-btn svg { width: 14px; height: 14px; }
.icon-btn.ok svg { color: var(--ok); }
.icon-btn.active { color: var(--accent-hi); background: #333; }
/* Interleave pip: the little corner dot reflects the effective view
   decision — grey = nothing has run yet (auto pre-execute, decision
   pending), blue = interleaved (auto saw prose, or pinned on), hollow =
   results-only (auto saw bare code, or pinned off). */
#interleave { position: relative; }
#interleave::after {
  content: "";
  position: absolute;
  right: 2px;
  bottom: 1px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted); /* grey: nothing run yet */
}
#interleave.pip-on::after { background: var(--accent-hi); } /* blue: interleaved */
#interleave.pip-off::after { background: transparent; border: 1px solid var(--muted); } /* hollow: results-only */
#interleave.mode-off { opacity: .5; }
/* The [hidden] attribute is defeated by .icon-btn's display:inline-flex,
   so re-assert it for the conditional warnings button. */
.icon-btn[hidden] { display: none; }
/* Warnings toggle: yellow in BOTH states (that's the presence signal —
   no badge needed); only appears on runs that produced exit-0 stderr. */
.icon-btn.warn { color: #ffb86b; border: 1px solid rgba(255, 184, 107, .5); }
.icon-btn.warn:hover { background: rgba(255, 184, 107, .14); color: #ffd9a8; }
.icon-btn.warn.on { background: rgba(255, 184, 107, .22); color: #ffe0b3; border-color: #ffb86b; }

details.advanced { margin-top: 14px; }
details.advanced > summary {
  cursor: pointer;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  user-select: none;
  list-style: none;
}
details.advanced > summary::-webkit-details-marker { display: none; }
details.advanced > summary::before { content: "▸ "; }
details.advanced[open] > summary::before { content: "▾ "; }
details.advanced > summary:hover { color: var(--text); }
details.advanced section { margin-top: 12px; }
details.advanced .advanced-meta { margin-top: 12px; }
details.advanced .advanced-meta code { color: var(--accent-hi); }

.output {
  background: #111;
  border-radius: 6px;
  padding: 10px;
  min-height: 140px;
  overflow: auto;
  font: 13px/1.55 ui-monospace, Consolas, monospace;
}
.line { margin: 0; white-space: pre-wrap; word-break: break-word; }
.line.log-stdout { color: var(--text); }
/* stderr is advisory during a run (warnings from interpreters like osabie
   print here with exit 0) — only the final exit code decides error red. */
.line.log-stderr { color: var(--muted); }
.line.stderr-muted { color: var(--muted); }
.line.done { color: var(--ok); }
.line.err { color: var(--err); }

/* Result blocks: headers + code fences as distinct, bounded panels so
   the code blocks clearly separate from the surrounding output.
   .source-head (the language tag above an interleaved source fence)
   shares this rule so the two block headings read identically. */
.block.result { white-space: normal; }
.result-title,
.source-head {
  color: var(--accent-hi);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  margin: 10px 0 6px;
}
.result-title.failed { color: var(--err); }
.result-step { color: var(--muted); font-size: 12px; margin: 6px 0 4px; }
.result-line { color: var(--text); white-space: pre-wrap; word-break: break-word; margin: 2px 0; }

/* Interleaved view: the original document with each block's result slotted
   in after it. (.source-head is styled with .result-title above.) */
.block.source { white-space: normal; }
.block.prose { white-space: normal; }
.interleave-h1, .interleave-h2, .interleave-h3 {
  color: var(--text);
  font-weight: 700;
  margin: 14px 0 4px;
}
.interleave-h1 { font-size: 15px; }
.interleave-h2 { font-size: 13px; }
.interleave-h3 { font-size: 12px; color: var(--muted); }

/* Lightweight Markdown rendering inside interleaved prose: callouts,
   rules, lists, tables, and inline spans. The callout echo of the
   editor's magenta quote styling keeps prose segments visually distinct
   from result panels. */
.block.prose > :first-child { margin-top: 0; }
.block.prose p { margin: 2px 0; }
.block.prose blockquote {
  margin: 8px 0;
  padding: 8px 12px;
  border-left: 3px solid #ff2ec4;
  background: #2a1630;
  border-radius: 0 6px 6px 0;
}
.block.prose blockquote p { margin: 2px 0; }
.block.prose hr {
  border: 0;
  border-top: 1px dashed #4a4a4a;
  margin: 12px 0;
}
.block.prose ul, .block.prose ol { margin: 6px 0 10px; padding-left: 24px; }
.block.prose li { margin: 2px 0; }
.block.prose code {
  background: rgba(255, 255, 255, .08);
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: 3px;
  padding: 1px 5px;
  font-family: ui-monospace, Consolas, monospace;
  font-size: 12px;
}
.block.prose a { color: var(--accent-hi); text-decoration: none; }
.block.prose a:hover { text-decoration: underline; }
.block.prose table { border-collapse: collapse; margin: 8px 0; }
.block.prose th, .block.prose td { border: 1px solid #3a3a3a; padding: 4px 10px; text-align: left; }
.block.prose th { background: #2c2c2c; }

/* Result panels get the blue tint in BOTH views; source panels (interleave
   only) keep the default near-black. Result blocks indent under source. */
.block.result .code-block {
  background: #131a22;
  border-color: #2a3644;
  /* The copy button floats over the top-right corner; once the text wraps
     to the right edge it would sit under it, so reserve its width. */
  padding-right: 34px;
}
/* Output wraps instead of panning sideways. A model reply or a wide print()
   arrives as one long fence inside a `.block.result`, and at these widths the
   overflow was only reachable by dragging a scrollbar — which also hid the
   panel's copy button. `anywhere` additionally splits single long tokens
   (URLs, base64, hashes) that `break-word` alone would still push out.
   Source fences are untouched: they stay `pre` so indentation lines up. */
.block.result .code-block {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
#output.interleaved .block.result {
  margin-left: 18px;
  border-left: 2px solid #2b2b2b;
  padding-left: 10px;
}
/* Live-preview placeholder under a block with no result (nothing has run
   yet, or its code changed since the last run). Same inset as a result
   panel so the document keeps its rhythm. */
#output.interleaved .block.pending {
  margin: 0 0 10px 18px;
  border-left: 2px solid #2b2b2b;
  padding-left: 10px;
  color: var(--muted);
  font-size: 12px;
  white-space: normal;
}
.code-block {
  background: #0c0c0c;
  border: 1px solid #2b2b2b;
  border-radius: 6px;
  padding: 10px 12px;
  margin: 6px 0 10px;
  overflow: auto;
  position: relative;
}
.code-block code {
  background: transparent;
  padding: 0;
  font: 13px/1.55 ui-monospace, Consolas, monospace;
}

/* Per-block copy button: small, top-right, copies only the code contents. */
.block-copy {
  position: absolute;
  top: 5px;
  right: 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .45);
  color: var(--muted);
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 3px;
  cursor: pointer;
  opacity: .5;
  transition: opacity .12s ease, color .12s ease;
}
pre.code-block:hover .block-copy,
.block-copy:hover { opacity: 1; color: var(--text); }
.block-copy svg { width: 12px; height: 12px; }
.block-copy.ok { color: var(--ok); opacity: 1; }

/* Unsupported-language reminder block in the output. */
.block.reminder {
  border: 1px solid #8a5a1e;
  background: #2a2215;
  border-radius: 6px;
  padding: 10px 12px;
  margin: 8px 0;
  white-space: normal;
}
.reminder-title { color: #ffb86b; font-weight: 600; margin-bottom: 6px; }
.reminder-body { font-size: 12.5px; line-height: 1.7; }
.reminder-als { color: var(--muted); font-size: 11.5px; }
.reminder-hint { color: var(--muted); font-size: 12px; margin-top: 6px; }
.reminder code {
  background: rgba(255, 255, 255, 0.07);
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--accent-hi);
  white-space: nowrap;
}


/* Bottom-right link back to the main repo page. */
.about-link {
  margin: 6px 0 0;
  text-align: right;
  font-size: 12px;
}
.about-link a {
  color: var(--muted);
  text-decoration: none;
}
.about-link a:hover {
  color: var(--accent-hi);
  text-decoration: underline;
}

/* ============================================================
   Mobile / narrow screens
   The desktop layout assumes ~860px: a side-by-side header, an
   860px-capped body, and a bottom dock whose row never needs to
   wrap. On a phone that reads as a zoomed-out desktop page — the
   viewport meta is already width=device-width, so the fixes are
   about reclaiming padding and letting rows wrap cleanly.
   ============================================================ */
@media (max-width: 640px) {
  /* Reclaim horizontal space: the desktop card padding (16px + 16px
     card padding) eats 25% of a 375px screen. */
  body { padding: 10px 8px; }
  section { padding: 10px 10px 12px; }

  /* Keep the browser from juggling font sizes on rotation / focus. */
  html { -webkit-text-size-adjust: 100%; }

  /* Header: row 1 has title on the left and status pill on the upper right;
     row 2 has the subtitle spanning all the way across on one line. */
  header {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    align-items: center;
    gap: 3px 8px;
  }
  .title-block { display: contents; }
  h1 {
    grid-column: 1;
    grid-row: 1;
    font-size: 17px;
    margin: 0;
  }
  .meta {
    grid-column: 2;
    grid-row: 1;
    margin-top: 0;
  }
  .subtitle {
    grid-column: 1 / -1;
    grid-row: 2;
    white-space: nowrap;
    font-size: clamp(8.5px, 2.6vw, 10.5px);
    letter-spacing: .08em;
    margin-top: 0;
  }

  /* Editor: the desktop body reads well at 14px mono, but a phone
     width fits only ~40 chars per line — drop to 12.5px for ~46.
     OverType sizes the textarea and preview from its own inline
     styles, so target both layers; line-height matches 1.6 for
     touch-accuracy when tapping to place the caret. */
  #editor textarea.overtype-input,
  #editor .overtype-preview {
    font-size: 12.5px !important;
    line-height: 1.6 !important;
  }

  /* Toolbar icon buttons: 14px glyphs are small for touch — bump
     hit areas to a comfortable 40px+ square. */
  .icon-btn { padding: 9px; }
  .icon-btn svg { width: 17px; height: 17px; }

  /* Bottom dock: Run button spans full width; Share URL and the
     compact radio options share the row below it. */
  .run-dock { padding: 6px 10px 10px; }
  .actions { flex-wrap: wrap; gap: 6px 8px; align-items: center; }
  .run-btn { flex: 1 1 100%; min-width: 0; }
  .share-btn { flex: 0 0 auto; padding: 6px 10px; }
  .print-link-opt legend,
  .print-link-opt .print-link-opt-legend {
    display: none;
  }
  .print-link-opt,
  fieldset.print-link-opt {
    display: inline-flex;
    align-items: center;
    flex: 1 1 auto;
    gap: 2px 8px;
    white-space: nowrap;
    border: none !important;
    outline: none !important;
  }
  .print-link-opt label {
    font-size: 11px;
    gap: 3px;
  }

  /* Output: mono text at 13px overflows a narrow panel — size down
     and let long tokens (hashes, URLs) break instead of scrolling. */
  .output { font-size: 12px; }
  .code-block code { font-size: 12px; }

  /* Language-help popover: it tracks the editor's width, which is
     fine, but its dense mono grid needs a smaller column minimum
     to avoid a single squished column on narrow screens. */
  .lang-help-list { grid-template-columns: repeat(auto-fill, minmax(86px, 1fr)); }
}

/* Very narrow screens (<360px): collapse the remaining margins. */
@media (max-width: 360px) {
  body { padding: 8px 4px; }
  .section-head h2 { margin-top: 12px; }
}
