/* bomb.sh-style neon + deep black + minimal restyle of the harness. Everything new lives here — demo.css stays untouched for the other demo pages (this sheet is only linked from harness.html). Dependency-free plain DOM; every control keeps an id or data-attribute anchor so tests and humans target the same things. Palette (neon on deep black): surface #000000 · panel #0a0a0a · hairline #1c1c1c text #e0e0e0 · muted #6a6a6a · hover surface #111111 accent neon #00ff87 (mint) · secondary neon #00e5ff (cyan) */ /* display:flex/grid below would beat the UA's [hidden] rule otherwise. */ [hidden] { display: none !important; } /* ---- shell (overrides the shared demo.css body chrome) ---- */ html, body { height: 100%; } body { margin: 0; background: #000000; color: #e0e0e0; font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace; font-size: 13px; overflow: hidden; } #app { display: grid; height: 100vh; grid-template-columns: 44px 250px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto 24px; grid-template-areas: "activity sidebar main" "activity sidebar output" "statusbar statusbar statusbar"; } /* Explorer toggled off from the activity bar: the sidebar column collapses. */ #app.sidebar-hidden { grid-template-columns: 44px 0 minmax(0, 1fr); } /* Thin dark scrollbars, neon thumb on hover. */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-thumb { background: #1c1c1c; } ::-webkit-scrollbar-thumb:hover { background: #00ff87; } ::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: #000000; } /* ---- base controls (scoped to this page's roots) ---- */ #app button, #context-menu button { font: inherit; color: #e0e0e0; background: #0a0a0a; border: 1px solid #1c1c1c; border-radius: 0; padding: 3px 10px; cursor: pointer; transition: color .15s ease, border-color .15s ease, background .15s ease; } #app button:hover:not(:disabled) { color: #00ff87; border-color: #00ff87; background: #000000; } #app button:focus-visible, #context-menu button:focus-visible { outline: 1px solid #00e5ff; outline-offset: 0; } #app button:disabled { opacity: 0.35; cursor: default; } #app input { font: inherit; color: #e0e0e0; background: #000000; border: 1px solid #1c1c1c; border-radius: 0; padding: 3px 8px; } #app input:focus { outline: none; border-color: #00ff87; } /* ---- activity bar: two icon buttons, Explorer (default active) + Output ---- */ #activity-bar { grid-area: 1 / 1 / 3 / 2; background: #000000; border-right: 1px solid #1c1c1c; display: flex; flex-direction: column; align-items: center; gap: 2px; padding-top: 2px; } .activity-btn { position: relative; width: 44px; height: 44px; display: grid; place-items: center; background: none !important; border: none !important; border-radius: 0 !important; color: #6a6a6a; } .activity-btn:hover { color: #00ff87 !important; background: #0a0a0a !important; } .activity-btn:focus-visible { outline: 1px solid #00e5ff !important; outline-offset: -2px; } .activity-btn.active { color: #00ff87; } /* bomb.sh active-view indicator: a 2px neon bar hugging the left edge. */ .activity-btn.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 2px; background: #00ff87; } /* files icon: three stacked lines of decreasing width */ .icon-files { display: block; width: 16px; height: 16px; background: linear-gradient(currentColor, currentColor) 1px 2px / 14px 2px no-repeat, linear-gradient(currentColor, currentColor) 1px 7px / 10px 2px no-repeat, linear-gradient(currentColor, currentColor) 1px 12px / 12px 2px no-repeat; } /* output icon: terminal chevron + underscore, pure CSS */ .icon-output { position: relative; display: block; width: 16px; height: 16px; } .icon-output::before { content: ""; position: absolute; left: 3px; top: 4px; width: 7px; height: 7px; border-top: 2px solid currentColor; border-right: 2px solid currentColor; transform: rotate(-45deg); /* the ">" chevron */ } .icon-output::after { content: ""; position: absolute; left: 4px; bottom: 3px; width: 9px; height: 2px; background: currentColor; /* the "_" underscore */ } /* ---- main pane: the app grid's middle column. Rows: 35px tab bar, the editor (flexible), and the OUTPUT panel docked at the bottom (collapses to zero when toggled off from the activity bar). Anchors the #status toast (absolute, top: 35px). */ #main { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 35px minmax(0, 1fr) auto; position: relative; min-height: 0; min-width: 0; } /* ---- tabs + editor pane: one tab per open file; the editor is gutter + textarea. Tab styling: active tab #000000 (merges with editor bg) with a 1px top accent (#00ff87), inactive #0a0a0a; dirty indicator (● dot) when the textarea content differs from the saved value (also in the tab title). */ /* #editor-body is a transparent wrapper: display:contents promotes the gutter, the highlight mirror, and the textarea into grid items of #editor (placed explicitly below). [hidden] still collapses it because display:none !important wins. */ #editor-body { display: contents; } #file-note { grid-row: 1; grid-column: 1 / -1; } #gutter { grid-row: 2; grid-column: 1; } #hl-wrap, #content { grid-row: 2; grid-column: 2; } #tab-bar { grid-row: 1; display: flex; align-items: stretch; background: #0a0a0a; border-bottom: 1px solid #1c1c1c; min-width: 0; } #tabs { flex: 1; display: flex; min-width: 0; overflow-x: auto; scrollbar-width: none; } #tabs::-webkit-scrollbar { display: none; } .tab { display: flex; align-items: center; gap: 6px; max-width: 240px; padding: 0 8px 0 14px; background: #0a0a0a; border-right: 1px solid #1c1c1c; border-top: 2px solid transparent; color: #6a6a6a; cursor: pointer; user-select: none; white-space: nowrap; } .tab.open { background: #000000; color: #e0e0e0; } .tab.active { background: #000000; color: #00ff87; border-top: 2px solid #00ff87; } .tab:hover { background: #111111; } .tab-label { max-width: 180px; overflow: hidden; text-overflow: ellipsis; pointer-events: none; } /* ● dirty marker: rendered on every tab, shown only while buffer ≠ saved */ .tab-dirty-dot { display: none; width: 8px; height: 8px; border-radius: 50%; background: #00e5ff; flex: none; } .tab.dirty .tab-dirty-dot { display: block; } .tab-close { background: none !important; border: none !important; padding: 0 2px !important; color: inherit; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 0 !important; } .tab-close:hover { color: #ff3860; } #tab-actions { display: flex; align-items: center; gap: 4px; padding: 0 6px; } #btn-save, #btn-editor-delete { background: none !important; border: none !important; padding: 4px 6px !important; color: #6a6a6a; } #btn-save:hover, #btn-editor-delete:hover { color: #00ff87; } /* ---- editor: gutter + textarea, monospace on pure black ---- */ #editor { grid-row: 2; display: grid; grid-template-columns: 4ch minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); /* file-note banner row, then the edit area */ overflow: hidden; position: relative; min-height: 0; background: #000000; } #gutter { overflow: hidden; padding: 6px 0; text-align: right; color: #1f3d2e; background: #000000; border-right: 1px solid #1c1c1c; user-select: none; } #gutter-inner { padding-right: 8px; transform: translateY(0); will-change: transform; } #content { font: inherit; color: #e0e0e0; background: #000000; border: none; outline: none; resize: none; padding: 6px 0 24px 12px; white-space: pre; overflow: auto; caret-color: #00ff87; } #content::selection { background: rgba(0, 255, 135, 0.25); color: #e0e0e0; } /* ---- output panel: guest runner, bottom dock (inside #main's third row; grid-area 3/1 replaces the stale 2/3/3/4 — #output lives inside #main, not directly in #app's grid. [hidden] still wins via !important. */ #output { grid-area: 3 / 1; display: grid; grid-template-rows: 35px minmax(0, 1fr); border-top: 1px solid #1c1c1c; background: #0a0a0a; min-height: 140px; } #output-header { display: flex; align-items: center; gap: 10px; padding: 0 12px; background: #000000; border-bottom: 1px solid #1c1c1c; } #output-label { color: #00ff87; text-transform: uppercase; letter-spacing: 2px; font-size: 11px; font-weight: bold; } #entry { flex: 1; max-width: 400px; } #btn-run { color: #00ff87; border-color: #00ff87; font-weight: bold; } #btn-run:hover:not(:disabled) { background: #00ff87 !important; color: #000000 !important; } #exit-badge { color: #6a6a6a; font-size: 11px; } #output-log { overflow: auto; padding: 8px 12px; font-size: 12px; line-height: 1.5; } #stdout { white-space: pre-wrap; color: #e0e0e0; } #stdout:empty::after { content: "— no output —"; color: #333333; font-style: italic; } #stderr { white-space: pre-wrap; color: #ff3860; margin-top: 4px; } /* ---- status bar (bomb.sh style: neon accents on black) ---- */ #statusbar { grid-area: 3 / 1 / 4 / 4; display: flex; align-items: center; gap: 16px; padding: 0 12px; background: #000000; border-top: 1px solid #1c1c1c; color: #6a6a6a; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; } #sb-left, #sb-right { display: flex; align-items: center; gap: 12px; } #sb-left { flex: 1; } #sb-right { justify-content: flex-end; } #sb-brand { color: #00ff87; font-weight: bold; } #sb-boot { color: #e0e0e0; } #sb-boot.error { color: #ff3860; } #sb-encoding { color: #00e5ff; } #sb-exit.ok { color: #00ff87; } #sb-exit.err { color: #ff3860; } #sb-selected { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---- inline prompt row: one shared row for new-file/new-folder/rename ---- Auto-placed into the sidebar's third row; the tree flows to the 1fr row. */ /* Two-row grid: the label gets a full-width line of its own and the input shares the second line with the buttons. One flex line can't fit label (~112px basis) + buttons (138px) + a usable input in 225px — with the input at flex-basis 0% it wins no shrink and no grow, ending at 18px of pure padding/border. Stacking the label above the controls gives the input ~57px of real typing width and keeps the path context visible. */ #inline-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 4px 6px; align-items: center; padding: 6px 12px; background: #0a0a0a; border-bottom: 1px solid #00ff87; } #inline-label { grid-column: 1 / -1; color: #00e5ff; font-size: 11px; text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* min-width:0 lets the input shrink inside the 250px sidebar — without it the row overflows under #editor and swallows clicks meant for its buttons */ #inline-input { grid-column: 1; min-width: 0; } #inline-ok { grid-column: 2; color: #00ff87; border-color: #00ff87; } #inline-cancel { grid-column: 3; color: #ff3860; border-color: #1c1c1c; } /* ---- confirmation row (destructive ops) ---- */ #confirm-row { display: flex; align-items: center; gap: 8px; padding: 6px 12px; background: #0a0a0a; border-bottom: 1px solid #ff3860; } #confirm-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #ff3860; font-size: 11px; text-transform: uppercase; } #confirm-yes { color: #ff3860; border-color: #ff3860; } #confirm-yes:hover { background: #ff3860 !important; color: #000000 !important; } #confirm-no { color: #6a6a6a; border-color: #1c1c1c; } /* ---- status line (kept for tests; styled as a toast near the editor top) ---- */ #status { position: absolute; top: 35px; left: 50%; transform: translateX(-50%); background: #0a0a0a; border: 1px solid #ff3860; color: #ff3860; padding: 6px 14px; z-index: 100; font-size: 12px; max-width: 80%; text-align: center; /* passive toast — must never swallow clicks aimed at tabs/editor underneath */ pointer-events: none; } #status:empty { display: none; } /* ---- context menu ---- */ #context-menu { position: fixed; z-index: 200; background: #0a0a0a; border: 1px solid #00ff87; min-width: 160px; padding: 4px 0; box-shadow: 0 0 20px rgba(0, 255, 135, 0.15); } #context-menu button { display: block; width: 100%; text-align: left; background: none !important; border: none !important; padding: 6px 16px; color: #e0e0e0; } #context-menu button:hover { background: #00ff87 !important; color: #000000 !important; } #context-menu .menu-sep { height: 1px; background: #1c1c1c; margin: 4px 0; } /* ---- sidebar (explorer) ---- */ #sidebar { grid-area: 1 / 2 / 3 / 3; display: grid; /* Explicit single column pinned to the sidebar's width. Without it the implicit "auto" column track grows to the max-content of its rows — e.g. the 470px-wide inline/confirm rows overflow into #editor, which then intercepts the pointer over the row's buttons (Playwright + humans both hit the editor instead of Create/Delete). minmax(0,1fr) lets flex rows shrink; .tree-row ellipsizes and #tree scrolls. */ grid-template-columns: minmax(0, 1fr); /* title | section title | inline create row | confirm row | tree — the inline/confirm rows are [hidden] most of the time and collapse to 0. */ grid-template-rows: 35px auto auto auto minmax(0, 1fr); background: #050505; border-right: 1px solid #1c1c1c; min-width: 0; } #sidebar-title { display: flex; align-items: center; padding: 0 16px; color: #6a6a6a; text-transform: uppercase; letter-spacing: 2px; font-size: 11px; border-bottom: 1px solid #1c1c1c; } #section-title { padding: 8px 16px 4px; color: #00ff87; text-transform: uppercase; letter-spacing: 1px; font-size: 12px; font-weight: bold; } #tree { grid-row: 5; overflow: auto; padding-bottom: 20px; } /* ---- tree rows ---- */ .tree-row { display: flex; align-items: center; gap: 4px; height: 24px; padding: 0 8px; cursor: pointer; color: #e0e0e0; white-space: nowrap; } .tree-row:hover { background: #111111; } .tree-row.selected { background: rgba(0, 255, 135, 0.08); color: #00ff87; } .tree-row .row-icon { margin-right: 4px; } .tree-row .twisty { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; color: #6a6a6a; font-size: 10px; transition: transform .15s ease; } .tree-row .twisty.open { transform: rotate(90deg); } .tree-label { overflow: hidden; text-overflow: ellipsis; } /* file icons — colored dots/diamonds for a minimal look */ .row-icon { width: 8px; height: 8px; display: inline-block; flex: none; transform: rotate(45deg); } .icon-folder { transform: none; background: #6a6a6a; width: 12px; height: 10px; border-bottom: 2px solid currentColor; } .ext-js, .ext-cjs, .ext-mjs { background: #ffd700; } /* amber */ .ext-ts, .ext-mts, .ext-cts { background: #00e5ff; } /* neon cyan */ .ext-json { background: #ffbf00; } .ext-md { background: #b3b3ff; } .ext-txt { background: #00ff87; } /* mint */ .ext-other { background: #444444; } /* ---- empty editor state ---- */ #editor-empty { position: absolute; inset: 0; display: grid; place-items: center; color: #333333; font-size: 14px; letter-spacing: 1px; text-transform: uppercase; pointer-events: none; } #empty-title { color: #1c1c1c; font-size: 24px; font-weight: bold; } #file-note { color: #6a6a6a; font-size: 11px; margin-top: 8px; } /* ---- download button (neon outline) ---- */ #btn-download { color: #00ff87; border-color: #00ff87; background: #000000; font-weight: bold; } #btn-download:hover:not(:disabled) { background: #00ff87 !important; color: #000000 !important; box-shadow: 0 0 12px rgba(0, 255, 135, 0.4); } /* ---- microlighter highlight mirror (code files) ---- A
 sits in #content's grid cell BEHIND the textarea: identical
   font, line-height, padding and white-space, scroll synced via its own
   scrollTop/scrollLeft. Tokens are painted through the CSS Custom Highlight
   API, so the mirror's plain text color matches the textarea's and the
   textarea text goes transparent (caret stays mint) while hl-on. */
#hl-wrap, #hl-mirror {
  margin: 0;
  border: 0;
  padding: 0;
  font: inherit;
  line-height: 1.5;
  white-space: pre;
  tab-size: 8;
}
#hl-wrap {
  overflow: hidden;
  padding: 6px 0 24px 12px;
  color: #e0e0e0;
  background: #000000;
  pointer-events: none;
  user-select: none;
}
#hl-mirror { display: block; }
/* While the mirror is live, the textarea renders nothing but the caret and
   the native selection band — the mirror supplies every glyph. */
#editor-body.hl-on #content { background: transparent; color: transparent; }
#editor-body.hl-on #content::selection { background: rgba(0, 255, 135, 0.25); color: transparent; }
/* Line-height on both surfaces (and the gutter) keeps glyph rows aligned. */
#content, #gutter { line-height: 1.5; }

/* ---- neon ::highlight() theme (our own; no stock microlighter theme) ----
   microlighter flattens TextMate scopes into these categories and registers
   each as a CSS Custom Highlight over the mirror text. Palette: mint
   keywords, cyan strings, dim italic comments, grey punctuation/numbers,
   pale mint/cyan tints for functions/types. */
::highlight(comment), ::highlight(quote) { color: #4a4a4a; font-style: italic; }
::highlight(keyword), ::highlight(storage), ::highlight(at-rule), ::highlight(doctype),
::highlight(important), ::highlight(section) { color: #00ff87; }
::highlight(operator) { color: #b0b0b0; }
::highlight(punctuation) { color: #6a6a6a; }
::highlight(string), ::highlight(regexp), ::highlight(attribute-value), ::highlight(link),
::highlight(raw), ::highlight(character-entity) { color: #00e5ff; }
::highlight(numeric), ::highlight(boolean), ::highlight(constant), ::highlight(symbol),
::highlight(anchor), ::highlight(entity) { color: #e0e0e0; }
::highlight(function), ::highlight(decorator), ::highlight(animation),
::highlight(selector) { color: #7dffc4; }
::highlight(type), ::highlight(support) { color: #86e3f2; }
::highlight(variable), ::highlight(interpolation) { color: #e0e0e0; }
::highlight(property), ::highlight(key), ::highlight(attribute-name) { color: #00e5ff; }
::highlight(tag) { color: #00ff87; }
::highlight(inserted) { color: #00ff87; }
::highlight(deleted) { color: #ff3860; }