pub const STUDIO_STYLE: &str = r#"
/* ============================================ */
/* STUDIO PAGE - Design Tokens */
/* ============================================ */
:root {
--studio-bg: #0f1419;
--studio-panel-bg: #12161c;
/* The one surface every column paints — the editor's — and how far every column holds its
content from its edges. A column's scroll ends at its last line: nothing to scroll into. */
--studio-surface: #0f1419;
--studio-inset: 20px;
--editor-overscroll: var(--studio-inset);
--studio-elevated: #1a1f27;
--studio-border: rgba(255, 255, 255, 0.08);
/* The column bars' grey: opaque (the border over --studio-bg), so every bar reads the
same whatever sits behind it. */
--studio-resizer: #22272b;
--studio-border-hover: rgba(255, 255, 255, 0.15);
--studio-text: #e8eaed;
--studio-text-secondary: #9ca3af;
--studio-text-muted: #6b7280;
--studio-accent: #667eea;
/* The one syntax palette: a colour for each kind of thing a formula is made of (One Dark), worn
by the Syntax Tree's pills and legend. Kinds that share a colour share a value. */
--syntax-quantifier: #c678dd;
--syntax-connective: #c678dd;
--syntax-predicate: #98c379;
--syntax-individual: #e5c07b;
--syntax-role: #e5c07b;
--syntax-variable: #61afef;
--syntax-modal: #56b6c2;
--syntax-tense: #56b6c2;
/* The toolbar's one row on a desk. */
--toolbar-height: 49px;
}
/* ============================================ */
/* STUDIO PAGE - Desktop Layout */
/* ============================================ */
.studio-container {
display: flex;
flex-direction: column;
height: 100vh;
height: 100dvh;
background: var(--studio-bg);
color: var(--studio-text);
scrollbar-color: var(--studio-border-hover) transparent;
overflow: hidden;
}
/* Toolbar */
.studio-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: var(--toolbar-height);
padding: 0 16px;
background: var(--studio-panel-bg);
border-bottom: 1px solid var(--studio-border);
gap: 12px;
flex-shrink: 0;
}
.studio-toolbar-left {
display: flex;
align-items: center;
gap: 12px;
}
.studio-toolbar-right {
display: flex;
align-items: center;
gap: 8px;
}
/* A deep link to a file the prerendered copy does not show (`prerender_guard`): the copy's panels
keep their place unpainted until the app draws the file the link names. */
.studio-opens-elsewhere #main .studio-main {
visibility: hidden;
}
/* What stopped an action nobody expected to fail: one warning-orange line just left of the
buttons. The whole story — the full message and where it was raised — is in its tooltip. */
.studio-fault {
flex: 0 1 auto;
min-width: 0;
max-width: 44vw;
color: var(--color-warning);
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: help;
}
/* One square button whose drawn icon names what a press does: show the files, or fold them away. */
.sidebar-toggle-btn {
width: 32px;
height: 32px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 6px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.15s ease;
}
.sidebar-toggle-btn .toggle-icon {
display: flex;
}
/* Beside the folded file browser's toggle: what it opens, muted, set like the action buttons' text. */
.files-label {
color: var(--studio-text-muted);
font-size: 13px;
user-select: none;
}
.sidebar-toggle-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.9);
}
/* Main content area with optional sidebar */
.studio-content {
flex: 1 1 0;
min-height: 100px;
display: flex;
overflow: hidden;
position: relative;
}
/* Overlay to close sidebar when clicking outside (mobile) */
.sidebar-overlay {
display: none;
}
/* Where the files are a drawer (responsive::media::FILES_DRAWER), it opens over the panels, inside
the workbench's own content, and stays folded until the reader opens it: no tree covers the
editor in the first frame. */
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse), (max-height: 500px) and (orientation: landscape) {
.sidebar-overlay {
display: block;
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
.sidebar-overlay:not(.drawer-open),
.studio-sidebar:not(.drawer-open) {
display: none !important;
}
}
/* Sidebar wrapper for controlled width */
.studio-sidebar {
display: flex;
flex-shrink: 0;
overflow: hidden;
}
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse), (max-height: 500px) and (orientation: landscape) {
.studio-sidebar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
z-index: 100;
width: 280px !important;
min-width: 280px !important;
max-width: 280px !important;
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.3);
background: var(--studio-surface);
}
/* Text color overrides for Safari compatibility */
.studio-sidebar .file-tree-item {
color: rgba(255, 255, 255, 0.9) !important;
-webkit-text-fill-color: rgba(255, 255, 255, 0.9);
}
.studio-sidebar .file-tree-item.selected {
color: #00d4ff !important;
-webkit-text-fill-color: #00d4ff;
}
.studio-sidebar .file-tree-item .name {
color: inherit;
-webkit-text-fill-color: inherit;
}
}
/* Desktop: 3-column panel layout */
.studio-main {
flex: 1;
display: flex;
overflow: hidden;
gap: 1px;
background: var(--studio-border);
}
.studio-panel {
background: var(--studio-surface);
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* Desktop columns. Their widths come from the layout model (studio_layout.rs) as custom
properties on .studio-content, so a drag repaints them without re-rendering the page. */
@media (min-width: 769px) {
.studio-sidebar { width: var(--col-sidebar); }
.studio-main > .col-left { flex: 0 0 var(--col-left); }
.studio-main > .col-center { flex: 1 1 0; }
.studio-main > .col-right { flex: 0 0 var(--col-right); }
}
.studio-panel .panel-header {
padding: 0 20px;
height: 52px;
background: rgba(255, 255, 255, 0.02);
border-bottom: 1px solid var(--studio-border);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
/* The Studio's one title type: every panel header's title ("English Input", "Logic Output"), the
Files header and each collapsible block's heading ("Symbol Dictionary"). */
.studio-panel .panel-header,
.panel-title {
font-size: 16px;
font-weight: 600;
letter-spacing: 0.3px;
color: var(--studio-text);
}
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse) {
.studio-panel .panel-header,
.panel-title {
font-size: 14px;
}
}
.studio-panel .panel-content {
flex: 1;
min-height: 0;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
/* Panel Resizers (desktop only). The bar draws 6px (RESIZER in studio_layout.rs); its grab
area reaches 4px either side. */
.panel-resizer {
position: relative;
width: 6px;
background: var(--studio-resizer);
cursor: col-resize;
transition: background 0.2s ease;
flex-shrink: 0;
touch-action: none;
}
.panel-resizer::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: -4px;
right: -4px;
z-index: 1;
}
.panel-resizer:hover,
.panel-resizer.active {
background: var(--studio-accent);
}
/* Format Toggle (Unicode/LaTeX) */
.format-toggle {
display: flex;
gap: 4px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid var(--studio-border);
border-radius: 6px;
padding: 2px;
}
.format-btn {
padding: 4px 10px;
border: none;
background: transparent;
color: var(--studio-text-muted);
font-size: 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
line-height: 1;
}
.format-btn:hover {
color: var(--studio-text);
background: rgba(255, 255, 255, 0.04);
}
.format-btn.active {
background: rgba(255, 255, 255, 0.08);
color: var(--studio-text);
}
/* Guide Bar - above panels */
.studio-guide {
background: var(--studio-panel-bg);
border-bottom: 1px solid var(--studio-border);
flex-shrink: 0;
}
/* The dock bar: one amber line along the bottom, above the status bar, that says what to do next
and holds the way into the drawer that rises from it (Logic's proof, Code's debugger). */
.studio-dock {
display: flex;
align-items: center;
gap: 10px;
min-height: 40px;
padding: 4px 12px;
background: rgba(245, 158, 11, 0.10);
border-top: 1px solid rgba(245, 158, 11, 0.35);
color: #fbbf24;
font-size: 13px;
flex-shrink: 0;
}
.studio-dock-icon {
display: inline-flex;
font-size: 15px;
}
.studio-dock-text {
flex: 1 1 auto;
min-width: 0;
line-height: 1.4;
}
.studio-dock-btn {
flex: none;
min-height: 30px;
padding: 5px 12px;
border: 1px solid rgba(245, 158, 11, 0.45);
border-radius: 6px;
background: transparent;
color: #fbbf24;
font: 600 13px/1 inherit;
font-family: inherit;
cursor: pointer;
transition: background 0.15s ease;
}
.studio-dock-btn:hover:not(:disabled) { background: rgba(245, 158, 11, 0.18); }
.studio-dock-btn.primary {
background: #f59e0b;
border-color: #f59e0b;
color: #111;
}
.studio-dock-btn.primary:hover:not(:disabled) { background: #fbbf24; }
.studio-dock-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* Under a finger the dock's buttons are a finger tall. */
@media (pointer: coarse) {
.studio-dock-btn { min-height: 44px; }
}
/* Execute button for Code mode */
.execute-btn {
padding: 8px 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
color: white;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
}
/* Desktop: show short labels */
.execute-btn .mobile-label {
display: none;
}
.execute-btn .desktop-label {
display: inline;
}
.execute-btn:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
/* An action the open file cannot take: in its place, dimmed, its tooltip saying why. */
.execute-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.execute-btn:active {
transform: translateY(0);
}
/* Mobile execute buttons */
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse) {
.execute-btn {
padding: 6px 10px;
font-size: 12px;
white-space: nowrap;
}
}
@media (max-width: 480px) {
.execute-btn {
padding: 5px 8px;
font-size: 11px;
}
}
/* Output mode toggle for Code mode */
/* Labelled output on a panel's own surface — a small-caps label over flat code, set like the
code editor's text — and a verdict marked by its colour and a rule, not a box. */
.panel-section-label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--studio-text-secondary);
margin: 4px 0;
}
/* The first label starts at the panel's inset, like every column's first line. */
.panel-section-label:first-child {
margin-top: 0;
}
.panel-code {
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-word;
color: var(--studio-text);
margin: 0 0 12px;
}
.panel-verdict {
font-size: 13px;
line-height: 1.5;
color: var(--studio-text-secondary);
border-left: 2px solid var(--studio-border-hover);
padding: 2px 0 2px 10px;
margin: 0 0 12px;
}
/* A picture's caption: what the drawing shows, in the secondary voice. */
.panel-caption {
margin-top: 6px;
font-size: 12px;
line-height: 1.5;
color: var(--studio-text-secondary);
}
.panel-verdict.proved {
color: #4ade80;
border-left-color: #4ade80;
}
.panel-verdict.refuted {
color: #f87171;
border-left-color: #f87171;
}
/* The controls on a panel header's right: segmented toggles and one-off actions. */
.panel-header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.output-mode-toggle {
display: flex;
gap: 4px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid var(--studio-border);
border-radius: 6px;
padding: 2px;
}
.output-mode-btn {
padding: 4px 10px;
border: none;
background: transparent;
color: var(--studio-text-muted);
font-size: 12px;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
line-height: 1;
}
.output-mode-btn:hover {
color: var(--studio-text);
background: rgba(255, 255, 255, 0.04);
}
.output-mode-btn.active {
background: rgba(255, 255, 255, 0.08);
color: var(--studio-text);
}
/* Interpreter output display */
/* A panel's own content — a picture, a list — held at the Studio's inset; `wide` scrolls a
picture wider than its column instead of shrinking it. */
.panel-body {
padding: var(--studio-inset);
}
.panel-body.wide {
overflow-x: auto;
}
.interpreter-output {
padding: var(--studio-inset);
font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 14px;
line-height: 1.6;
}
/* A program's output keeps its spacing — padded columns and indents are what it printed. */
.interpreter-line {
margin-bottom: 4px;
color: #e8eaed;
white-space: pre-wrap;
}
.interpreter-error {
color: #e06c75;
padding: 12px;
background: rgba(224, 108, 117, 0.1);
border-radius: 6px;
margin-top: 8px;
}
.interpreter-empty {
color: rgba(255, 255, 255, 0.4);
text-align: center;
padding: 40px 20px;
}
/* ============================================ */
/* STUDIO PAGE - Mobile Overrides */
/* ============================================ */
/* Phones and upright tablets: the panels stack. */
@media (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse) {
.studio-toolbar {
flex-wrap: wrap;
height: auto;
padding: 10px 12px;
gap: 10px;
position: relative;
z-index: 101;
}
/* First row: file toggle */
.studio-toolbar-left {
flex-shrink: 0;
order: 1;
}
/* Second row: action buttons - full width */
.studio-toolbar-right {
flex-basis: 100%;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
order: 3;
padding-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
/* The fault note takes a line of its own above the buttons. */
.studio-fault {
flex-basis: 100%;
max-width: 100%;
text-align: center;
}
/* Full text on mobile buttons */
.execute-btn {
padding: 10px 16px;
font-size: 14px;
}
.files-label {
font-size: 14px;
}
.execute-btn .mobile-label {
display: inline;
}
.execute-btn .desktop-label {
display: none;
}
/* Hide mobile tab bar - panels stack instead */
.mobile-tabs {
display: none !important;
}
/* Hide desktop resizers */
.panel-resizer {
display: none;
}
/* Stacked vertical panel layout */
.studio-main {
flex-direction: column;
gap: 0;
background: var(--studio-bg);
}
/* Both panels visible, stacked vertically */
.studio-panel {
min-width: unset;
min-height: 0;
width: 100% !important;
}
.studio-panel.mobile-expanded {
flex: var(--panel-flex, 1);
overflow: hidden;
}
.studio-panel.mobile-collapsed {
flex: 0 0 auto;
}
.studio-panel.mobile-collapsed .panel-content {
display: none;
}
/* Show panel headers with collapse affordance */
.studio-panel .panel-header {
display: flex !important;
cursor: pointer;
padding: 0 14px;
height: 44px;
}
/* Collapse chevron indicator */
.studio-panel .panel-header::after {
content: '\25BC';
font-size: 10px;
color: rgba(255, 255, 255, 0.4);
margin-left: 8px;
transition: transform 0.15s ease;
}
.studio-panel.mobile-collapsed .panel-header::after {
content: '\25B6';
}
/* Divider between stacked panels */
.studio-panel + .studio-panel {
border-top: 1px solid var(--studio-border);
}
/* Hide Panel 3 (Console/Context/Graph) on mobile */
.studio-main > aside {
display: none !important;
}
/* Mobile-sized format toggle */
.format-toggle {
gap: 4px;
padding: 2px;
border-radius: 6px;
}
.format-btn {
padding: 6px 10px;
font-size: 12px;
border-radius: 4px;
min-height: 32px;
min-width: 32px;
display: flex;
align-items: center;
justify-content: center;
}
/* Footer constraints */
.studio-footer {
max-height: 30vh;
overflow: auto;
}
.mobile-panel-resizer {
display: flex;
align-items: center;
justify-content: center;
height: 16px;
cursor: row-resize;
flex-shrink: 0;
touch-action: none;
-webkit-tap-highlight-color: transparent;
z-index: 2;
position: relative;
}
.mobile-panel-resizer::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 48px;
height: 4px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.15);
transition: background 0.15s ease;
}
.mobile-panel-resizer:hover::after,
.mobile-panel-resizer.active::after {
background: rgba(102, 126, 234, 0.6);
}
}
/* Extra small screens */
@media (max-width: 480px) {
.format-btn {
padding: 4px 8px;
font-size: 11px;
}
}
/* Desktop: hide mobile resizer */
@media (min-width: 769px) {
.mobile-panel-resizer {
display: none;
}
}
/* Landscape mobile */
@media (max-height: 500px) and (orientation: landscape) {
.studio-footer {
max-height: 25vh;
}
}
/* ============================================ */
/* STUDIO PAGE - Phones, tablets and touch */
/* ============================================ */
/* Every rule from here on sits under a phone, tablet or touch query (responsive::media), so the
desktop Studio stays as it was (`the_desktop_studio_is_unchanged`). The bands:
phone portrait one pane at a time, chosen by the switch
phone landscape the editor beside the output, half and half
tablet portrait the editor over the output, the split bar between
tablet landscape the desktop's columns, for a finger
The workbench's wrapper lays nothing out of its own on a desk. */
.studio-workbench {
display: contents;
}
/* The switch between a phone's panes, and the lines that stand in for a hover, show only where
they are needed. */
.mobile-tabs,
.studio-reasons,
.studio-fault-detail {
display: none;
}
/* The switch: a row of tabs under the toolbar, each a finger wide. */
@media (max-width: 600px), (max-height: 500px) and (orientation: landscape), (min-width: 601px) and (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse) {
.mobile-tabs {
gap: 4px;
padding: 4px max(12px, var(--safe-right, 0px)) 6px max(12px, var(--safe-left, 0px));
background: var(--studio-panel-bg);
border-bottom: 1px solid var(--studio-border);
flex-shrink: 0;
}
.mobile-tabs .mobile-tab {
flex: 1 1 0;
min-width: 0;
min-height: 44px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 6px;
padding: 0 8px;
border: 1px solid transparent;
border-radius: 8px;
background: rgba(255, 255, 255, 0.04);
color: var(--studio-text-muted);
font-size: 13px;
font-weight: 500;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.mobile-tabs .mobile-tab.active {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
color: var(--studio-text);
}
.mobile-tabs .mobile-tab-icon {
font-size: 15px;
line-height: 1;
}
.mobile-tabs .mobile-tab-label {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
/* One row of toolbar on a phone, either way up: the files toggle on the left, the mode's actions on
the right under their short labels, every one a finger wide. */
@media (max-width: 600px), (max-height: 500px) and (orientation: landscape) {
.studio-toolbar {
flex-wrap: wrap;
height: auto;
padding: 8px max(12px, var(--safe-right, 0px)) 8px max(12px, var(--safe-left, 0px));
gap: 8px;
}
.studio-toolbar-right {
flex-basis: auto;
flex: 0 1 auto;
margin-left: auto;
order: 2;
justify-content: flex-end;
gap: 6px;
padding-top: 0;
border-top: none;
}
.execute-btn {
min-height: 44px;
padding: 0 10px;
font-size: 13px;
}
.execute-btn .mobile-label {
display: none;
}
.execute-btn .desktop-label {
display: inline;
}
.files-label {
font-size: 13px;
}
/* A pane's header folds nothing where the panes are fixed. */
.studio-panel .panel-header {
cursor: default;
}
.studio-panel .panel-header::after {
content: none;
}
.studio-panel + .studio-panel {
border-top: none;
}
.studio-panel.mobile-collapsed .panel-content {
display: block;
}
.studio-panel.mobile-collapsed .panel-content
.mobile-panel-resizer,
.panel-resizer {
display: none !important;
}
}
/* A phone held upright: the pane the switch names, alone, the whole height. */
@media (max-width: 600px) {
.mobile-tabs {
display: flex !important;
}
.studio-main > .studio-panel:not(.phone-shown) {
display: none !important;
}
.studio-main > .studio-panel.phone-shown {
display: flex !important;
flex: 1 1 0;
min-height: 0;
}
}
/* A phone on its side: the editor beside the output (or the third panel the switch names), half and
half, the status bar left out for the height, and the page's nav free to scroll away above a
workbench the window's height. */
@media (max-height: 500px) and (orientation: landscape) {
.studio-main {
flex-direction: row;
gap: 1px;
background: var(--studio-border);
}
.studio-main > .studio-panel {
flex: 1 1 0 !important;
min-width: 0;
width: auto !important;
}
.studio-main > .col-center:not(.phone-right),
.studio-main > .col-right:not(.phone-right) {
display: none !important;
}
.studio-main > .col-right.phone-right {
display: flex !important;
}
.mobile-tabs.has-third {
display: flex !important;
}
.mobile-tabs .mobile-tab:first-child {
display: none;
}
.studio-statusbar {
display: none;
}
.studio-container {
height: auto;
}
.studio-container > .studio-workbench {
display: flex;
flex-direction: column;
flex: none;
height: 100svh;
}
}
/* A tablet held upright: the editor over the lower pane, which shows the output or the third panel
the switch names; the split bar between them takes a finger. */
@media (min-width: 601px) and (max-width: 768px), (min-width: 769px) and (max-width: 1024px) and (orientation: portrait) and (pointer: coarse) {
.mobile-tabs.has-third {
display: flex !important;
}
.mobile-tabs .mobile-tab:first-child {
display: none;
}
.studio-main > .col-center:not(.phone-right) {
display: none !important;
}
.studio-main > aside.phone-right {
display: flex !important;
}
.mobile-panel-resizer {
display: flex;
}
.mobile-panel-resizer::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: -14px;
bottom: -14px;
}
}
/* Every control a finger wide wherever a finger, a phone or an upright tablet may press it: the
files toggle, the actions, the output and format toggles, the proof's tactics, the guide's hint,
the readings and New file. */
@media (max-width: 768px), (pointer: coarse), (max-height: 500px) and (orientation: landscape) {
.sidebar-toggle-btn {
width: 44px;
height: 44px;
}
.execute-btn {
min-height: 44px;
}
.output-mode-btn,
.format-btn {
min-height: 44px;
min-width: 44px;
}
.output-mode-toggle,
.format-toggle {
padding: 0;
}
.studio-panel .panel-header {
height: 52px;
}
.studio-workbench .tactic-btn,
.studio-workbench .guide-btn,
.studio-workbench .reading-btn,
.studio-workbench .file-browser-btn {
min-height: 44px;
min-width: 44px;
}
}
/* Under a finger: the bars' grab areas a finger wide around what they draw, the panes' scrolling
kept to the panes on the Studio page, and the page sized above an on-screen keyboard. */
@media (pointer: coarse) {
.panel-resizer::after {
left: -19px;
right: -19px;
}
.studio-container {
height: calc(100dvh - var(--keyboard-inset, 0px));
overscroll-behavior: none;
}
.studio-container .panel-content,
.studio-container .code-editor-textarea {
overscroll-behavior: contain;
}
.code-editor-textarea {
touch-action: pan-x pan-y;
}
}
/* Where nothing hovers: each disabled action says why on a line under the toolbar, and the fault
note wraps and opens its whole story to a tap. */
@media (hover: none) {
.studio-reasons {
display: block;
padding: 6px max(12px, var(--safe-right, 0px)) 6px max(12px, var(--safe-left, 0px));
background: var(--studio-panel-bg);
border-bottom: 1px solid var(--studio-border);
color: var(--studio-text-muted);
font-size: 12px;
line-height: 1.4;
flex-shrink: 0;
}
.studio-reasons > span {
display: block;
}
.studio-fault {
white-space: normal;
text-overflow: clip;
max-width: none;
}
.studio-fault-detail {
display: block;
padding: 6px max(12px, var(--safe-right, 0px)) 6px max(12px, var(--safe-left, 0px));
background: var(--studio-panel-bg);
border-bottom: 1px solid var(--studio-border);
color: var(--color-warning);
font-size: 12px;
line-height: 1.4;
white-space: pre-wrap;
flex-shrink: 0;
}
}
/* LLM Heal (work/LLM_HEAL.md): the blue button with the fairy, the fairy swirling in to the mended
line and resting there still, and her card, readable whole with Keep and Undo always in view. */
.heal-btn {
background: linear-gradient(135deg, var(--accent-primary, #00d4ff) 0%, #2563eb 100%);
display: inline-flex;
align-items: center;
gap: 6px;
}
.heal-btn-icon {
width: 18px;
height: 18px;
object-fit: contain;
image-rendering: pixelated;
}
.col-left { position: relative; }
.heal-overlay {
position: absolute;
inset: 0;
pointer-events: none;
z-index: 30;
}
.heal-fairy {
position: absolute;
width: 56px;
height: 56px;
object-fit: contain;
image-rendering: pixelated;
left: 8px;
top: calc(48px + var(--heal-line, 0) * 1.5em);
transform: translateY(-50%);
transition: top 0.7s ease;
filter: drop-shadow(0 0 10px rgba(0, 212, 255, 0.7));
}
/* The flight: one swirl from the toolbar's side down into the editor, then still. */
.heal-fairy.flying { animation: heal-swirl 1.1s cubic-bezier(0.22, 0.8, 0.3, 1) both; }
@keyframes heal-swirl {
0% { transform: translate(60vw, -60px) rotate(0deg) scale(0.4); opacity: 0; }
35% { transform: translate(28vw, 40px) rotate(-200deg) scale(0.8); opacity: 1; }
70% { transform: translate(8vw, -16px) rotate(-330deg) scale(1.05); }
100% { transform: translateY(-50%) rotate(-360deg) scale(1); }
}
.heal-fairy.failed { left: auto; right: 18px; top: 10px; transform: none; }
.heal-card {
position: absolute;
left: 70px;
right: 12px;
top: 44px;
bottom: 12px;
display: flex;
flex-direction: column;
pointer-events: auto;
background: #1b1f27;
border: 1px solid rgba(0, 212, 255, 0.45);
border-radius: 10px;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
padding: 12px 14px;
color: #e6edf3;
font-size: 13px;
}
.heal-card-cols {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 14px;
}
.heal-card-col { min-width: 0; }
.heal-card-col h3 { margin: 0 0 6px; font-size: 12px; letter-spacing: 0.04em; text-transform: uppercase; color: #9aa3b2; }
.heal-card pre { margin: 0; white-space: pre-wrap; word-break: break-word; font: 13px/1.6 var(--font-mono, ui-monospace, monospace); color: #e6edf3; }
.heal-before { color: #b7bec9; }
.heal-line.mended { background: rgba(0, 212, 255, 0.14); box-shadow: -4px 0 0 rgba(0, 212, 255, 0.7); border-radius: 2px; }
.heal-line.replaced { background: rgba(239, 68, 68, 0.14); box-shadow: -4px 0 0 rgba(239, 68, 68, 0.7); border-radius: 2px; text-decoration: line-through; text-decoration-color: rgba(239, 68, 68, 0.6); }
.heal-word { background: rgba(0, 212, 255, 0.35); color: #fff; border-radius: 2px; font-weight: 600; }
.heal-line.replaced .heal-word { background: rgba(239, 68, 68, 0.4); }
.heal-card-actions { flex: none; display: flex; align-items: center; gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.heal-card-note { flex: 1 1 auto; color: #9aa3b2; }
.heal-card .studio-dock-btn { border-color: rgba(0, 212, 255, 0.45); color: var(--accent-primary, #00d4ff); }
.heal-card .studio-dock-btn.primary { background: rgba(0, 212, 255, 0.16); }
.heal-card-failed { bottom: auto; max-width: 420px; }
.heal-card-failed p { margin: 0 0 10px; }
@media (max-width: 768px) {
.heal-card { left: 12px; }
.heal-card-cols { grid-template-columns: minmax(0, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
.heal-fairy { transition: none; animation: none; }
}
}
"#;Expand description
Studio-specific styles that extend the shared responsive styles