* { box-sizing: border-box; }
html, body, #root { width: 100%; height: 100%; margin: 0; overflow: hidden; background: var(--surface-window); }
button, input, textarea, select { font: inherit; color: inherit; }
button { border: 0; cursor: pointer; transition: background-color var(--motion-fast), border-color var(--motion-fast), color var(--motion-fast); }
button:disabled { cursor: default; opacity: 0.45; }
input[type="search"] { -webkit-appearance: none; appearance: none; }
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
-webkit-appearance: none;
appearance: none;
}
input[type="search"]:focus { outline: none; }
input[type="search"]:focus-visible {
border-color: var(--border-strong);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--text-primary) 12%, transparent);
}
.copy-button-icon { position: relative; width: 14px; height: 14px; display: inline-grid; place-items: center; flex: none; }
.copy-button-icon > svg { position: absolute; transition: opacity var(--motion-moderate), transform var(--motion-moderate); }
.copy-icon-idle { opacity: 1; transform: scale(1) rotate(0); }
.copy-icon-success { opacity: 0; transform: scale(.85); color: var(--status-success); }
.copy-button.copied .copy-icon-idle { opacity: 0; transform: scale(.85); }
.copy-button.copied .copy-icon-success { opacity: 1; transform: scale(1) rotate(0); }
.root-error-page { min-height: 100vh; padding: var(--space-16); display: grid; place-items: center; background: var(--surface-app); color: var(--text-primary); }
.root-error-card { width: min(100%, 520px); padding: var(--space-16); border-radius: var(--radius-dialog); }
.root-error-card h1 { margin: 0; font-family: var(--type-display-font); font-size: var(--type-display-size); line-height: var(--type-display-line-height); font-weight: var(--type-display-weight); }
.root-error-card > p { margin: var(--space-6) 0 0; color: var(--text-secondary); font-size: var(--type-body-size); line-height: 1.6; }
.root-error-actions { margin-top: 22px; display: flex; flex-wrap: wrap; gap: var(--space-4); }
.root-error-card > .root-error-action-error { color: var(--status-danger); font-size: var(--type-caption-size); }
.root-error-details { margin-top: 22px; border-top: 1px solid var(--border-subtle); padding-top: 14px; color: var(--text-secondary); font-size: var(--type-caption-size); }
.root-error-details summary { width: max-content; cursor: pointer; font-weight: 600; }
.root-error-details pre { max-height: 180px; margin: var(--space-6) 0 0; padding: var(--space-6); overflow: auto; border-radius: var(--radius-control); background: var(--surface-panel); color: var(--text-primary); font-family: var(--editor-font); font-size: var(--type-micro-size); line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
/* A panel that crashed (telemetry/panel-error-boundary.tsx): its card fills the panel, centered, as an empty state does. */
.panel-error { box-sizing: border-box; height: 100%; padding: var(--space-8); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-4); overflow: auto; text-align: center; color: var(--text-secondary); font-size: var(--type-body-compact-size); }
.panel-error > strong { color: var(--text-primary); }
.panel-error > p { margin: 0; }
.panel-error > .root-error-details { width: min(100%, 420px); margin-top: var(--space-4); text-align: start; }
/* Focus, in two parts.
*
* WebKit paints a native blue halo on plain `:focus`, including pointer clicks,
* even where the product already shows the state with its own border and fill.
* That halo is suppressed.
*
* Keyboard focus is different: it must always be visible, so `:focus-visible`
* draws one product ring for controls in the Tab order. Not for focus a
* pointer caused, though: script focus after a click or a hover matches
* `:focus-visible` too, so components/ui/pointer-focus.ts marks such an element
* `data-pointer-focus` and the rule below hides every ring on it, this one
* and those components draw themselves (a checkbox, an outline row).
* Menu items (every `menuitem*` role), Radix's and the project tree's own row
* menu, expose focus through their active-row fill, while the project switcher
* and the Overleaf split-menu trigger use a chrome fill. Outlining those
* composite controls can leave a stray black frame when WebKit restores focus
* after app activation. Radix also temporarily focuses dialog surfaces with
* `tabindex="-1"`; those composites already expose focus through their active
* row or modal context. Text entry is excluded because a field already answers
* with its own border treatment (see the `--field-control-*` contract), and
* doubling the two reads as an error state. */
:where(button, a, input, textarea, select, summary, [role="button"], [tabindex]):focus {
outline: none;
}
:where(button:not(.project-title):not(.overleaf-toolbar-menu-button):not([role^="menuitem"]), a, select, summary, [role="button"], [tabindex]:not(.ProseMirror):not([tabindex="-1"]):not([role^="menuitem"])):focus-visible {
outline: var(--focus-ring-width) solid var(--focus-ring);
outline-offset: var(--focus-ring-offset);
}
/* Specific enough to beat a component's own ring (`.ui-checkbox:focus-visible`,
`.latex-selection-tool button:focus-visible`), so one rule covers them all.
A ring more specific than this, or drawn some other way (a fill, a shadow,
a parent's outline), checks the mark itself. */
:root [data-pointer-focus]:focus-visible { outline-color: transparent; }
.app-shell { height: 100%; background: var(--surface-window); display: flex; flex-direction: column; }
.titlebar { container: titlebar / inline-size; position: relative; z-index: var(--z-chrome); height: var(--titlebar-height); flex: none; display: flex; align-items: center; background: var(--surface-titlebar); box-shadow: inset 0 -1px 0 var(--titlebar-rule); user-select: none; }
.titlebar-sidebar { width: var(--titlebar-sidebar-width); min-width: 0; height: 100%; flex: none; display: flex; align-items: center; }
.titlebar-navigator { width: var(--titlebar-navigator-width); height: 100%; flex: none; display: flex; align-items: center; padding-right: var(--titlebar-leading-gap); }
.traffic-space { width: var(--titlebar-traffic-space-width); height: 100%; flex: none; }
/* No traffic lights beside the bar: a browser tab and the website's demo have
none, and full screen hides them. There the switcher starts at the bar's
own padding. In full screen AppKit shows them again only while the pointer
holds the menu bar down, in its own bar that slides in over this one. */
:is(.app-shell.browser-hosted, .app-shell.fullscreen, .titlebar-without-lights) .titlebar-navigator { width: var(--titlebar-navigator-width-browser); padding: 0 var(--space-4); }
:is(.app-shell.browser-hosted, .app-shell.fullscreen, .titlebar-without-lights) .traffic-space { width: 0; }
.icon-button { width: var(--titlebar-action-size); height: var(--titlebar-action-size); border-radius: var(--radius-chrome); background: transparent; display: grid; place-items: center; color: var(--text-secondary); }
.icon-button:hover { background: var(--chrome-hover-surface); color: var(--text-primary); }
/* Soften the light/dark switch on the big themed surfaces (a rare action, so a
short color transition here is cheap even on weak WebKit). */
.app-shell, .titlebar { transition: background-color var(--motion-slow), color var(--motion-slow); }
.project-switcher { min-width: 0; height: 100%; flex: none; position: relative; display: flex; align-items: center; overflow: hidden; }
.project-title { max-width: var(--titlebar-project-title-max-width); height: var(--titlebar-action-size); min-width: 0; flex: none; border-radius: var(--radius-chrome); padding: 0 var(--space-4); background: transparent; color: var(--text-chrome); display: flex; align-items: center; gap: var(--gap-inline); font-size: var(--type-subtitle-size); font-weight: var(--weight-semibold); text-align: left; }
.project-title:hover, .project-title:focus-visible, .project-title[aria-expanded="true"] { background: var(--chrome-hover-surface); }
.project-title:disabled { opacity: 1; cursor: default; }
.project-title > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-title > svg { flex: none; color: var(--text-tertiary); }
/* A long name clamps at the title's max width; the percentage cap this used to
carry is cyclic inside the content-sized switcher, so the unclamped name set
the switcher's width and pushed Panels away. The gap after it is fixed, so
the switcher stays centered between the lights and Panels. */
/* Near the window's minimum width a full-length name would starve the panel
controls beside it; measured on the bar itself so interface zoom and the
browser tab's missing traffic lights are already accounted for. */
@container titlebar (max-width: 800px) { .project-title { max-width: var(--titlebar-project-title-max-width-compact); } }
.titlebar-drag-area { width: var(--titlebar-trailing-gap); height: 100%; flex: none; }
/* Focus mode (focus-mode-bar.tsx): the bar keeps the window's controls, the
document's name centered on the window and the way out, on the ground the
panels sit on and without its rule, so the page is all that stands out. */
.titlebar.titlebar-focus { background: var(--surface-shell); box-shadow: none; }
.focus-bar { min-width: 0; height: 100%; flex: 1; padding-inline: var(--space-4); display: flex; align-items: center; justify-content: flex-end; animation: focus-bar-in var(--motion-fast) both; }
.focus-bar-title { position: absolute; left: 50%; max-width: min(40%, 420px); translate: -50% 0; overflow: hidden; color: var(--text-tertiary); font-size: var(--type-body-compact-size); line-height: var(--type-body-compact-line-height); font-weight: var(--weight-regular); text-overflow: ellipsis; white-space: nowrap; pointer-events: none; }
.focus-bar-actions { position: relative; display: flex; align-items: center; gap: var(--space-2); }
.focus-bar-exit .focus-bar-keys { margin-inline-start: var(--space-1); }
@keyframes focus-bar-in { from { opacity: 0; } }
.titlebar-main { min-width: 0; height: 100%; flex: 1; background: transparent; display: flex; align-items: center; overflow: hidden; }
.titlebar-main > .canvas-toolbar { min-width: max-content; width: auto; height: 100%; flex: none; padding: 0 var(--space-3); border: 0; background: transparent; display: flex; align-items: center; gap: var(--space-4); overflow: visible; container-type: normal; container-name: none; }
.titlebar-main > .canvas-toolbar .active-document { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.titlebar-main > .canvas-toolbar .canvas-actions { min-width: max-content; width: max-content; flex: none; overflow: visible; }
.spin { animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.compile-diagnostics { box-shadow: 0 0 0 1px var(--border-subtle), var(--elevation-floating-shadow); position: absolute; z-index: var(--z-diagnostics); top: 46px; right: var(--space-4); width: min(440px, calc(100% - 16px)); border-radius: var(--radius-panel); color: var(--text-secondary); overflow: hidden; }
.compile-diagnostics.error .compile-diagnostics-toggle > svg:first-child { color: var(--status-danger); }
.compile-diagnostics.warning .compile-diagnostics-toggle > svg:first-child { color: var(--status-warning); }
.compile-diagnostics-bar { min-height: 34px; display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-5) var(--space-2) var(--space-4); }
.compile-diagnostics-toggle { flex: 1; min-width: 0; height: var(--control-height-compact); border: 0; border-radius: var(--radius-icon); padding: 0 var(--space-3); background: transparent; color: inherit; display: flex; align-items: center; gap: var(--gap-inline); font-size: var(--type-caption-size); font-weight: var(--weight-regular); }
.compile-diagnostics-toggle:hover { background: var(--tone-hover-surface); }
.compile-diagnostics-toggle > svg:first-child { width: 15px; height: 15px; flex: none; }
.compile-diagnostics-toggle span { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: left; font-weight: 400; }
.compile-diagnostics-bar > button:last-child { width: var(--control-size-icon-compact); height: var(--control-size-icon-compact); border: 0; border-radius: calc(var(--radius-icon) * var(--corner-smoothing)); corner-shape: squircle; padding: 0; background: transparent; color: inherit; display: grid; place-items: center; }
.compile-diagnostics-bar > button:last-child:hover { background: var(--tone-hover-surface); }
.compile-repair-progress { display: flex; align-items: center; gap: var(--space-3); white-space: nowrap; font-size: var(--type-caption-size); }
.compile-repair-action { flex: none; gap: var(--space-3); }
.compile-repair-detail { margin: 0; padding: var(--space-2) var(--space-5) var(--space-4); font-size: var(--type-caption-size); overflow-wrap: anywhere; max-height: 120px; overflow-y: auto; }
.compile-repair-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4); padding: var(--space-2) var(--space-4); border-top: 1px solid var(--border-subtle); font-size: var(--type-caption-size); }
.compile-repair-footer > span { margin-right: auto; }
.compile-diagnostics-body { padding: 0 var(--space-4) var(--space-4); display: grid; gap: var(--space-3); }
.compile-diagnostics-switch { min-height: var(--control-height-compact); display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.compile-log-copy { margin-inline-start: auto; }
.compile-diagnostics-tabs .ui-segmented-tab { gap: var(--space-2); }
/* The list scrolls, so it clips: it reaches into the body's padding by as much
as it pads back in, which keeps the focus rings of its rows whole. */
.compile-diagnostics-list { list-style: none; margin: calc(-1 * var(--focus-ring-reach)); padding: var(--focus-ring-reach); max-height: calc(168px + 2 * var(--focus-ring-reach)); overflow: auto; display: grid; align-content: start; gap: var(--space-4); }
.compile-diagnostics-group { min-width: 0; display: grid; gap: var(--space-1); }
.compile-diagnostics-group > ul { list-style: none; margin: 0; padding: 0; display: grid; }
.compile-diagnostics-group > ul > li { min-width: 0; display: flex; align-items: center; gap: var(--gap-tight); }
/* A path wraps rather than truncates: its last segment is the part that matters. */
.compile-diagnostics-file { min-width: 0; padding: 0 var(--space-4); display: flex; align-items: baseline; gap: var(--space-3); color: var(--text-tertiary); font-size: var(--type-micro-size); font-weight: var(--weight-medium); overflow-wrap: anywhere; }
.compile-diagnostics-count { flex: none; font-weight: var(--weight-regular); font-variant-numeric: tabular-nums; opacity: .8; }
.compile-diagnostic-item { min-width: 0; flex: 1; min-height: 30px; border: 0; border-radius: var(--radius-chrome); padding: var(--space-3) var(--space-4); background: transparent; color: inherit; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--gap-inline); align-items: start; text-align: left; font-size: var(--type-caption-size); }
.compile-diagnostic-item:hover:not(:disabled) { background: var(--tone-hover-surface); }
.compile-diagnostic-item:disabled { opacity: 0.88; cursor: default; }
.compile-diagnostic-item.error > svg { color: var(--status-danger); }
.compile-diagnostic-item.warning > svg { color: var(--status-warning); }
.compile-diagnostic-message { min-width: 0; color: var(--text-primary); overflow-wrap: anywhere; line-height: 1.45; }
.compile-diagnostic-line { color: var(--text-tertiary); font-size: var(--type-micro-size); line-height: 1.6; white-space: nowrap; font-variant-numeric: tabular-nums; }
.compile-diagnostic-copy { width: var(--control-size-icon-compact); height: var(--control-size-icon-compact); flex: none; border: 0; border-radius: var(--radius-icon); padding: 0; background: transparent; color: inherit; display: grid; place-items: center; opacity: .72; transition: background-color var(--motion-fast), opacity var(--motion-fast); }
.compile-diagnostic-copy:hover { background: var(--tone-hover-surface); opacity: 1; }
/* An error the build explains (compile-explain.ts) opens into a card: the
headline row, an excerpt of the failing line, one sentence, then the fix
first and TeX's own words last, small at the end. */
.compile-diagnostics-group > ul > li.compile-explained { flex-direction: column; align-items: stretch; gap: var(--space-3); padding-bottom: var(--space-3); }
.compile-explained .compile-diagnostic-message { font-weight: var(--weight-medium); }
.compile-excerpt { margin: 0 var(--space-4); padding: var(--space-3) 0; border-radius: calc(var(--radius-control) * var(--corner-smoothing)); corner-shape: squircle; background: var(--editor-bg); box-shadow: inset 0 0 0 1px var(--border-subtle); color: var(--text-secondary); font-family: var(--editor-font); font-size: var(--type-micro-size); line-height: 20px; overflow: hidden; display: grid; }
.compile-excerpt-line { display: grid; grid-template-columns: 30px minmax(0, 1fr); padding-inline-end: var(--space-4); white-space: pre; }
.compile-excerpt-line[data-failing] { background: color-mix(in srgb, var(--status-danger) 7%, transparent); box-shadow: inset 2px 0 0 var(--status-danger); color: var(--text-primary); }
.compile-excerpt-number { padding-inline-end: var(--space-4); text-align: right; color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
.compile-excerpt-text { overflow: hidden; text-overflow: ellipsis; }
/* The marked line shrinks its end first and then its lead, which is clipped
from the start (its text is packed to the end), so the token never leaves. */
.compile-excerpt-marked { display: flex; }
.compile-excerpt-lead { flex: 0 1 auto; min-width: 0; display: flex; justify-content: flex-end; overflow: hidden; }
.compile-excerpt-lead > span { flex: none; }
.compile-excerpt-marked > mark { flex: none; }
.compile-excerpt-rest { flex: 0 100 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.compile-excerpt mark { background: none; color: var(--status-danger); text-decoration: underline wavy var(--status-danger); text-decoration-thickness: 1px; text-underline-offset: 2px; text-decoration-skip-ink: none; font-weight: var(--weight-semibold); }
.compile-explained-text { margin: 0; padding: 0 var(--space-4); color: var(--text-primary); font-size: var(--type-caption-size); line-height: 1.5; text-wrap: pretty; }
.compile-explained-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding-inline-start: var(--space-4); }
.compile-explained-actions code { font-family: var(--editor-font); }
/* An error just fixed from its card waits, checked off, for the build that confirms it. */
.compile-fixed .compile-diagnostic-item > svg { color: var(--status-success); }
.compile-fixed .compile-diagnostic-message { color: var(--text-secondary); text-decoration: line-through; text-decoration-color: var(--text-tertiary); }
.compile-fixed .compile-explained-text { color: var(--text-secondary); }
.compile-fixed code { font-family: var(--editor-font); }
.compile-explained-enter { opacity: .55; }
/* The row's Copy stands at its end, as on every other message row. */
.compile-explained-actions > .compile-diagnostic-copy { margin-inline-start: auto; }
/* An explained card is taller than a row: the list gets room for it and the rows after it. */
.compile-diagnostics-list:has(.compile-explained) { max-height: calc(320px + 2 * var(--focus-ring-reach)); }
.compile-log { display: block; box-sizing: border-box; width: 100%; field-sizing: content; min-height: 0; max-height: 220px; margin: 0; overflow: auto; resize: none; border: 1px solid var(--tone-border); border-radius: var(--radius-chrome); padding: var(--space-4) var(--space-5); background: color-mix(in srgb, var(--surface-panel-raised) 90%, transparent); color: var(--text-primary); font-family: var(--editor-font); font-size: var(--type-micro-size); line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Without field-sizing a textarea is two rows tall; give it the old cap. */
@supports not (field-sizing: content) { .compile-log { height: 220px; } }
/* Docked, the output takes a strip under the page instead of covering its
top. The strip's height is fixed while it is open, so switching tabs or a
new build's messages never resize the PDF; only opening and closing do,
and the viewer settles its refit after those. */
.compile-diagnostics.docked { order: 1; position: relative; z-index: auto; top: auto; right: auto; width: auto; border-top: 1px solid var(--border-subtle); border-radius: 0; background: var(--editor-bg); box-shadow: none; }
.compile-diagnostics.docked.expanded { height: min(240px, 40%); display: flex; flex-direction: column; }
.compile-diagnostics.docked.expanded > * { flex: none; }
.compile-diagnostics.docked.expanded > .compile-repair-detail { flex: 0 1 auto; min-height: 0; overflow-y: auto; }
.compile-diagnostics.docked.expanded > .compile-diagnostics-body { flex: 1 1 0; min-height: 80px; display: flex; flex-direction: column; }
.compile-diagnostics.docked .compile-diagnostics-switch { flex: none; }
.compile-diagnostics.docked .compile-diagnostics-list,
.compile-diagnostics.docked .compile-log { flex: 1 1 0; min-height: 0; max-height: none; }
.compile-diagnostics.docked .compile-log { field-sizing: fixed; }
.editor-status-bar {
container-type: inline-size;
flex: none;
height: var(--control-height-compact);
min-height: var(--control-height-compact);
/* Each item pads its own 6px, so the text still starts 12px in. */
padding: 0 var(--space-3);
border-top: 1px solid var(--border-subtle);
background: var(--editor-bg);
color: var(--text-secondary);
display: flex;
align-items: center;
gap: var(--space-1);
font-size: var(--type-caption-size);
line-height: var(--type-caption-line-height);
font-variant-numeric: tabular-nums;
flex-wrap: nowrap;
overflow: hidden;
}
.editor-status-bar > * { flex: none; white-space: nowrap; }
/* The bar clips its overflow and leaves 2-3px above and below a 22px item:
the ring hugs the item so it fits. */
.editor-status-bar .status-item:focus-visible { outline-offset: 0; }
/* Every item a pointer lands on is one slab: 22px on the 28px bar, the
control radius, the control hover and press fills, and the secondary ink
stepping up to the primary, as the titlebar's and toolbar's controls do. */
.editor-status-bar .status-item { height: 22px; min-width: 0; border: 0; border-radius: var(--radius-control); padding: 0 var(--space-3); background: transparent; color: var(--text-secondary); font: inherit; display: inline-flex; align-items: center; gap: var(--gap-inline-tight); cursor: pointer; transition: background-color var(--motion-fast), color var(--motion-fast); }
.editor-status-bar .status-item:hover { background: var(--control-hover-surface); color: var(--text-primary); }
.editor-status-bar .status-item:is(:active, [data-state="open"]) { background: var(--control-pressed-surface); color: var(--text-primary); }
.editor-status-bar .status-item > svg { flex: none; }
.editor-status-bar .status-item > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.editor-status-bar .status-caret-word { white-space: pre; }
/* Comments and TODOs travel together toward the counts at the end rather than
floating mid-bar whenever the hint and the breadcrumb are absent. */
.editor-status-bar .status-comments { margin-left: auto; }
/* An open count is the one coloured thing on the bar: the icon and the
number take the accent, the noun keeps the bar's ink. */
.editor-status-bar .status-count { font-weight: var(--weight-medium); }
.editor-status-bar .status-counter[data-active] :is(svg, .status-count) { color: var(--control-active); }
/* The count reads first and names its scope in the quieter ink after a gap;
the scope is the first thing to go. */
.editor-status-bar .status-body-words { margin-left: var(--space-2); gap: var(--space-2); }
.editor-status-bar .status-body-words .status-word-scope::after { content: "\00a0"; margin-left: var(--space-2); }
.editor-status-bar .status-body-words .status-word-count { color: var(--text-primary); }
.editor-status-bar .status-more { display: none; width: 22px; padding: 0; justify-content: center; }
.editor-status-bar .editor-breadcrumb { min-width: 0; flex: 1 1 auto; overflow: hidden; color: var(--text-secondary); text-align: center; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); flex-wrap: nowrap; }
.editor-status-bar .editor-breadcrumb button { border: 0; background: transparent; color: inherit; padding: 0; font: inherit; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: color var(--motion-fast); }
.editor-status-bar .editor-breadcrumb button:hover { color: var(--text-primary); }
.editor-status-bar .editor-breadcrumb i { flex: none; color: var(--text-tertiary); font-style: normal; }
/* Each crumb (separator and button) is the flex item, so it is the one that
must be allowed under its text's width: then the button ellipsizes, where a
crumb wider than the bar was cut at both ends of the centred row. */
.editor-status-bar .editor-breadcrumb > span { min-width: 0; display: flex; align-items: center; gap: var(--space-2); }
.editor-status-bar .status-vim-mode { min-width: max-content; flex: none; padding: 0 var(--space-3); color: var(--text-primary); font-size: var(--type-micro-size); font-weight: var(--weight-semibold); letter-spacing: .02em; font-variant-numeric: tabular-nums; }
.editor-status-bar .status-vim-compact { display: none; }
/* The save slot after the caret: tertiary ink while all is well, the
secondary ink and a warning-tinted icon offline, danger only for a write
that failed. The age trails the label after a space. */
.editor-status-bar .status-save { color: var(--text-tertiary); }
.editor-status-bar .status-save .status-save-age::before { content: "\00a0"; margin-inline-end: var(--space-2); }
.editor-status-bar .status-save .status-save-age { margin-inline-start: var(--space-2); }
.editor-status-bar .status-save[data-mode="offline"] { color: var(--text-secondary); }
.editor-status-bar .status-save[data-mode="offline"] > svg { color: var(--status-warning); }
.editor-status-bar .status-save[data-mode="error"] { color: var(--status-danger); }
/* The bar gives way in one order as it narrows, so what stays is what a
writer glances at most, and nothing ever wraps, overlaps or is cut:
1. the breadcrumb keeps only the current section, then goes;
2. the counters drop their nouns and keep icon and count;
3. the word count drops its scope, then folds with everything below it
into More (…), which lists the counters and every word count;
4. the caret shortens to line:column and Vim's mode to its initial;
5. the counters fold into More.
The save slot drops its age with the breadcrumb's parents (1), its label
with the breadcrumb (3), and keeps its icon, which still opens its details.
The caret, Vim's mode and More never leave. Each step is the measured
width of the English bar that stays (the longer language) with a 5-digit
line, a 3-digit column, two-digit counts and a 7-digit manuscript, plus the
gaps; the breadcrumb, the one item that shrinks rather than goes, absorbs
the slack between steps. Vim's mode is about 80px, so a bar showing it
takes each step that much sooner (the `:has` twins). Keep them here as one
ladder: a second copy elsewhere in the cascade silently wins and strands the
step you are reading. A Split source at its minimum (SPLIT_SOURCE_MIN_WIDTH)
leaves the bar 216px; a panel narrower than both Split minimums shrinks the
source below that, and the caret ellipsizes before anything spills. */
@container (max-width: 600px) { .editor-status-bar .status-save .status-save-age { display: none; } }
@container (max-width: 680px) { .editor-status-bar:has(.status-vim-mode) .status-save .status-save-age { display: none; } }
@container (max-width: 420px) { .editor-status-bar .status-save .status-save-label { display: none; } }
@container (max-width: 500px) { .editor-status-bar:has(.status-vim-mode) .status-save .status-save-label { display: none; } }
@container (max-width: 600px) {
.editor-status-bar .editor-breadcrumb > span:not(:last-child),
.editor-status-bar .editor-breadcrumb > span > i { display: none; }
}
@container (max-width: 680px) {
.editor-status-bar:has(.status-vim-mode) .editor-breadcrumb > span:not(:last-child),
.editor-status-bar:has(.status-vim-mode) .editor-breadcrumb > span > i { display: none; }
}
@container (max-width: 520px) { .editor-status-bar .status-counter .status-label { display: none; } }
@container (max-width: 600px) { .editor-status-bar:has(.status-vim-mode) .status-counter .status-label { display: none; } }
@container (max-width: 420px) {
.editor-status-bar .editor-breadcrumb { display: none; }
.editor-status-bar .status-body-words .status-word-scope { display: none; }
}
@container (max-width: 500px) {
.editor-status-bar:has(.status-vim-mode) :is(.editor-breadcrumb, .status-body-words .status-word-scope) { display: none; }
}
@container (max-width: 340px) {
.editor-status-bar .status-body-words { display: none; }
.editor-status-bar .status-more { display: inline-flex; margin-left: var(--space-2); }
}
@container (max-width: 420px) {
.editor-status-bar:has(.status-vim-mode) .status-body-words { display: none; }
.editor-status-bar:has(.status-vim-mode) .status-more { display: inline-flex; margin-left: var(--space-2); }
}
/* From here the caret may also shrink, with an ellipsis, so a bar narrower
than the last step never spills past its end. */
@container (max-width: 240px) {
.editor-status-bar .status-goto { flex: 0 1 auto; }
.editor-status-bar :is(.status-caret-word, .status-vim-full) { display: none; }
.editor-status-bar .status-caret-column::before { content: ":"; }
.editor-status-bar .status-vim-compact { display: inline; }
}
@container (max-width: 350px) {
.editor-status-bar:has(.status-vim-mode) .status-goto { flex: 0 1 auto; }
.editor-status-bar:has(.status-vim-mode) :is(.status-caret-word, .status-vim-full) { display: none; }
.editor-status-bar:has(.status-vim-mode) .status-caret-column::before { content: ":"; }
.editor-status-bar:has(.status-vim-mode) .status-vim-compact { display: inline; }
}
/* With the counters folded, More takes the end of the bar on its own. */
@container (max-width: 180px) {
.editor-status-bar .status-counter { display: none; }
.editor-status-bar .status-more { margin-left: auto; }
}
@container (max-width: 210px) {
.editor-status-bar:has(.status-vim-mode) .status-counter { display: none; }
.editor-status-bar:has(.status-vim-mode) .status-more { margin-left: auto; }
}
.word-count-popover { width: 284px; padding: var(--space-2); }
/* A filename is one unbroken token; the notes break it anywhere rather than
run out of the popover, and the label column may shrink below its text. */
.word-count-popover dl { margin: 0; display: grid; }
.word-count-popover dl > div { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: var(--space-1) var(--space-6); padding: var(--space-5) var(--space-6); }
.word-count-popover dl > div + div { border-top: 1px solid var(--border-subtle); }
.word-count-popover dt { color: var(--text-primary); font-size: var(--type-body-compact-size); font-weight: var(--weight-semibold); }
.word-count-popover dd { margin: 0; color: var(--text-primary); font-size: var(--type-body-compact-size); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; white-space: nowrap; }
.word-count-popover dd.word-count-note { grid-column: 1 / -1; color: var(--text-tertiary); font-size: var(--type-micro-size); font-weight: var(--weight-regular); line-height: var(--type-micro-line-height); white-space: normal; text-wrap: pretty; min-width: 0; overflow-wrap: anywhere; }
/* The save slot's details: a sentence or two above the rows when there is
something to say (offline, a failed write), and the actions under them. */
.save-state-popover { display: grid; gap: var(--space-2); }
.save-state-popover > strong { padding: var(--space-4) var(--space-6) 0; color: var(--text-primary); font-size: var(--type-body-compact-size); font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
.save-state-popover > p { margin: 0; padding: 0 var(--space-6); color: var(--text-secondary); font-size: var(--type-caption-size); line-height: var(--type-caption-line-height); text-wrap: pretty; }
.save-state-popover > p.save-state-reason { color: var(--text-primary); font-family: var(--editor-font); font-size: var(--type-micro-size); overflow-wrap: anywhere; }
.save-state-popover .save-state-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); padding: var(--space-2) var(--space-6) var(--space-4); }
.save-state-popover > dl + .save-state-actions { padding-top: 0; }
/* At the narrowest steps More lists what the bar folded: the counters as
rows that open their drawers, the open count at the end in the accent,
then the word counts by scope. */
.status-more-rows { --fluid-hover-fill: var(--control-hover-surface); position: relative; display: grid; padding-bottom: var(--space-2); border-bottom: 1px solid var(--border-subtle); }
.status-more-row { min-height: var(--row-height-compact); border: 0; border-radius: calc(var(--nested-radius) * var(--nested-corner-smoothing, 1)); corner-shape: var(--nested-corner-shape, round); padding: 0 var(--space-4); background: transparent; color: var(--text-primary); font: inherit; font-size: var(--type-body-compact-size); line-height: var(--type-body-compact-line-height); display: flex; align-items: center; gap: var(--space-4); text-align: left; cursor: pointer; }
.status-more-row > svg { flex: none; width: 14px; height: 14px; stroke-width: 1.5; color: var(--text-secondary); }
.status-more-row > span:first-of-type { flex: 1 1 auto; min-width: 0; }
.status-more-row .status-count { font-weight: var(--weight-medium); font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.status-more-row[data-active] :is(svg, .status-count) { color: var(--control-active); }
/* `.modal` (dialogs.css) loads later with its own width and padding, so these
carry the base class too or they silently lose to it. */
.modal.quick-open-modal { width: min(560px, calc(100vw - 40px)); padding: 0; overflow: hidden; }
.quick-open-header { position: relative; padding: var(--space-3) var(--space-4); display: flex; align-items: center; }
.quick-open-header::after { content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 1px; background: var(--border-subtle); }
/* The divider already frames the query, so the field drops its own box; it
stands a row taller than the results, so the query reads as the way in. */
.quick-open-header .ui-search-field { height: var(--row-height-default); border-color: transparent; background: transparent; }
.quick-open-header .ui-search-field-input { font-size: var(--type-body-size); line-height: var(--type-body-line-height); }
/* Pickers whose details are short (the command palette's shortcuts) keep each
row to one line, with the detail trailing at the end. */
.quick-open-modal[data-detail="end"] .quick-open-list button { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-8); }
.quick-open-modal[data-detail="end"] .picker-label { flex: 1 1 auto; }
.quick-open-modal[data-detail="end"] .picker-detail { flex: 0 1 auto; max-width: 45%; }
.quick-open-list { --surface-radius: var(--radius-dialog); --surface-inset: var(--space-3); padding: var(--surface-inset); display: grid; }
/* Arrowing to a row past the fold stops it clear of the edge fades. */
.quick-open-scroll [data-slot="scroll-area-viewport"] { max-height: min(360px, 50vh); scroll-padding-block: var(--scroll-fade-size); }
.quick-open-list button { min-height: var(--row-height-compact); border: 0; border-radius: calc(var(--nested-radius) * var(--nested-corner-smoothing, 1)); corner-shape: var(--nested-corner-shape, round); padding: var(--space-3) var(--space-5); background: transparent; color: var(--text-primary); text-align: left; font-size: var(--type-body-size); line-height: var(--type-body-line-height); display: flex; flex-direction: column; align-items: stretch; justify-content: center; gap: var(--gap-tight); min-width: 0; overflow: hidden; }
/* The pointer moves the current row too (on a real move), so `.active` is the
only highlight; a :hover fill would light a second row under a resting
pointer while the arrows move on. */
.quick-open-list { --fluid-hover-fill: var(--control-active-soft); }
.quick-open-list button.active { background: var(--control-active-soft); color: var(--text-primary); }
/* A block: labels are plain text, and an ellipsis draws only in a block
container, so a long file name ends in "…" rather than mid-letter. */
.quick-open-list .picker-label { min-width: 0; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One heading per section at the caption role, quiet enough that the rows
stay the thing to read. */
.quick-open-list .picker-section { padding: var(--space-6) var(--space-5) var(--space-2); color: var(--text-secondary); font-size: var(--type-caption-size); line-height: var(--type-caption-line-height); font-weight: var(--weight-medium); }
/* The first heading by class, not :first-of-type: the hover fill is a div that
mounts first in the list, and counting it moved every row 8px down under a
fill measured before it came. */
.quick-open-list .picker-section:not(.picker-section ~ .picker-section) { padding-top: var(--space-2); }
.quick-open-list .picker-file { min-width: 0; display: flex; align-items: center; gap: var(--space-4); }
.quick-open-list .picker-file-icon { flex: none; display: inline-flex; color: var(--text-secondary); }
.quick-open-list button.active .picker-file-icon { color: var(--text-primary); }
.quick-open-list .picker-file-name { flex: none; max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-primary); }
.quick-open-list button.active .picker-file-name { color: inherit; }
.quick-open-list .picker-file-folder { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-secondary); font-size: var(--type-caption-size); }
.quick-open-list .picker-detail { color: var(--text-secondary); font-size: var(--type-caption-size); font-style: normal; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A row with an icon or a shortcut: icon, then its text, then the keycaps at
the end, all on one line whatever the detail placement. */
.quick-open-list button:has(> .picker-text) { flex-direction: row; align-items: center; gap: var(--space-4); }
.quick-open-list .picker-icon { flex: none; display: inline-flex; color: var(--text-secondary); }
/* The palette's icon symbols: in the document for