/*
 * Builder stylesheet
 *
 * Import order is cascade order, general to specific:
 *   settings  – tokens as custom properties, produce no CSS
 *   base      – reset and element selectors, no classes
 *   blocks    – BEM blocks, one block per file (block, block__element, block--modifier)
 */

:root {
  color-scheme: dark;

  /*
   * Colours. Text tokens are checked with the WCAG 2 contrast formula against
   * --color-surface (#1e1f23), the background most text sits on:
   *   --color-text         #e6e6e9  14.1:1
   *   --color-text-muted   #9a9ca5   6.0:1
   *   --color-text-subtle  #868994   4.7:1 (5.1:1 on --color-bg) – the floor for readable text
   *   --color-text-faint   #6b6e77   3.2:1 – fails AA; decorative only, never for readable text
   *                                          (used for the line-number gutter, nothing else)
   */
  --color-bg: #16171a;
  --color-workspace: #0f1012;
  --color-surface: #1e1f23;
  --color-surface-raised: #26272c;
  --color-surface-hover: #2e3036;
  --color-border: #313339;
  --color-border-strong: #43464e;
  --color-text: #e6e6e9;
  --color-text-muted: #9a9ca5;
  --color-text-subtle: #868994;
  --color-text-faint: #6b6e77;
  --color-accent: #5b8cff;
  --color-accent-muted: rgb(91 140 255 / 0.16);
  /* elpresso Machine's accent, coffee brown, so a tab of the machine is told from one of the builder at a glance. */
  --color-machine: #c08552;
  --color-machine-muted: rgb(192 133 82 / 0.18);
  /*
   * The series of a chart, in this order and never cycled. The coffee accent reads grey as
   * a 2px line, so the first series is a warmer orange beside it. Checked as a pair on
   * --color-surface: both inside the lightness band, chroma above the floor, apart by ΔE
   * 27.9 for protanopia and 30.5 for normal vision, both above 3:1. The wash is the first
   * series under its line.
   */
  --color-chart-1: #d27a36;
  --color-chart-2: #5b8cff;
  --color-chart-1-wash: rgb(210 122 54 / 0.1);
  /*
   * Text on a filled accent surface. White on #5b8cff is only 3.2:1, so filled buttons use
   * dark text: relative luminance 0.0053 (#0f1012) vs 0.2734 (#5b8cff) gives
   * (0.2734 + 0.05) / (0.0053 + 0.05) = 6.0:1. The hover brightness filter only raises it.
   */
  --color-on-accent: #0f1012;
  --color-text-selection: rgb(91 140 255 / 0.35);
  /*
   * Components: a place, and the component being edited, are violet rather than blue, so
   * what belongs to the whole site is never mistaken for the page. 6.5:1 on --color-surface;
   * --color-on-accent on it is 7.4:1.
   */
  --color-component: #b98cff;
  --color-component-muted: rgb(185 140 255 / 0.16);
  /*
   * Templates: a page without an address, published once per entry, is teal, so it is told
   * apart from the page it looks like and from a component. 8.2:1 on --color-surface.
   */
  --color-template: #4cc9b8;
  --color-template-muted: rgb(76 201 184 / 0.16);
  /*
   * Frames: what surrounds every page, and the frame mode, are orange, so Save, Save frame
   * and Save component are three buttons at a glance. Kept apart from --color-condition by
   * form: a condition is a dashed outline and a flag, a frame a fill. 6.5:1 on
   * --color-surface; --color-on-accent on it is 7.5:1.
   */
  --color-frame: #f0883e;
  --color-frame-muted: rgb(240 136 62 / 0.16);
  /*
   * Conditions: an element the website shows only when its condition holds is marked
   * amber, in the structure and as its dashed outline on the canvas. 6.7:1 on --color-surface.
   */
  --color-condition: #d99a32;
  --color-danger: #ff7b72;
  /* Behind a danger icon under the pointer: the red stays readable on it */
  --color-danger-muted: rgb(255 123 114 / 0.14);
  /* Something is waiting to be done, not something is wrong: a page with unpublished changes */
  --color-pending: #e3b341;
  --color-pending-muted: rgb(227 179 65 / 0.16);
  /*
   * Standing of a page or an entry, as a filled tag: live is green, a draft rose, changes
   * waiting are --color-pending. The text on its own muted fill over --color-surface:
   * live 6.2:1, draft 5.4:1.
   */
  --color-live: #56d364;
  --color-live-muted: rgb(86 211 100 / 0.16);
  --color-draft: #ec8ec4;
  --color-draft-muted: rgb(236 142 196 / 0.16);
  --color-canvas: #ffffff;
  --color-backdrop: rgb(0 0 0 / 0.55);

  /* Fonts */
  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-code: ui-monospace, "Cascadia Code", "SF Mono", Consolas, monospace;

  /*
   * Fluid type after the Utopia method:
   * clamp(min, intercept + slope * 100vw, max), linear between 1024px and 1920px viewport.
   * The builder is a desktop tool, hence the narrow range.
   */
  --step--1: clamp(0.6875rem, 0.6161rem + 0.1116vw, 0.75rem);
  --step-0: clamp(0.8125rem, 0.7411rem + 0.1116vw, 0.875rem);
  --step-1: clamp(0.9375rem, 0.8661rem + 0.1116vw, 1rem);
  --step-2: clamp(1.0625rem, 0.9196rem + 0.2232vw, 1.1875rem);

  /*
   * Code: not fluid, so the line-number gutter, the highlighted <pre> and the textarea share
   * one exact line grid. rem, not px, so the editor still follows the user's font-size setting.
   */
  --code-font-size: 0.8125rem;
  --code-line-height: 1.25rem;

  /* Code colours after VS Code "Dark+" */
  --code-text: #d4d4d4;
  --code-comment: #6a9955;
  --code-string: #ce9178;
  --code-number: #b5cea8;
  --code-keyword: #c586c0;
  --code-storage: #569cd6;
  --code-function: #dcdcaa;
  --code-variable: #9cdcfe;
  --code-type: #4ec9b0;
  --code-regex: #d16969;
  /* Same value as --code-storage on purpose (Dark+ does the same); kept separate so tags can diverge later. */
  --code-tag: #569cd6;
  --code-selector: #d7ba7d;
  /* Fills behind a selector, id or preset badge above the CSS editor, like --color-template-muted */
  --code-selector-muted: rgb(215 186 125 / 0.16);
  --code-storage-muted: rgb(86 156 214 / 0.16);
  --code-type-muted: rgb(78 201 176 / 0.16);
  /* The same fills, deeper, for the badge whose CSS is open */
  --code-selector-strong: rgb(215 186 125 / 0.32);
  --code-storage-strong: rgb(86 156 214 / 0.32);
  --code-type-strong: rgb(78 201 176 / 0.32);
  --code-punctuation: #808080;

  /* Spacing */
  --space-3xs: 2px;
  --space-2xs: 4px;
  --space-xs: 8px;
  --space-s: 12px;
  --space-m: 16px;
  --space-l: 24px;
  --space-xl: 32px;

  /* Control sizes: heights of inputs, buttons and rows, so bars and their children line up */
  --size-control-s: 24px;
  --size-control-m: 28px;
  --size-control-l: 32px;

  /* Shape */
  --radius-s: 4px;
  --radius-m: 6px;
  --radius-l: 10px;
  --shadow-canvas: 0 0 0 1px rgb(0 0 0 / 0.4), 0 8px 32px rgb(0 0 0 / 0.35);
  --shadow-raised: 0 24px 80px rgb(0 0 0 / 0.6);
  --shadow-toast: 0 8px 32px rgb(0 0 0 / 0.45);

  /*
   * Stacking, from bottom to top. The backdrop dims the app, the overlay (maximized pane,
   * modal) sits on it, toasts stay above everything.
   */
  --z-resizer: 1;
  --z-backdrop: 10;
  --z-overlay: 11;
  --z-toast: 30;

  /* Motion */
  --duration-fast: 120ms;
  /* One beat of something that asks to be noticed, like the website button after a publish */
  --duration-attention: 1200ms;
  /* One breath of something that goes on, like the dot of an action that is running */
  --duration-pulse: 1600ms;

  /* Layout */
  --panel-header-height: 40px;
  --taskbar-height: 48px;
}

/* The machine's pages take its accent; the server writes data-product into <html>. */
:root[data-product="elpresso Machine"] {
  --color-accent: var(--color-machine);
  --color-accent-muted: var(--color-machine-muted);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
}

body {
  -webkit-font-smoothing: antialiased;
}

img,
picture,
svg,
video,
canvas {
  display: block;
  max-width: 100%;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

ul[role="list"],
ol[role="list"] {
  padding: 0;
  list-style: none;
}

[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
}

body {
  overflow: hidden;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: var(--step-0);
  line-height: 1.5;
}

/* Focus is always an outline: it survives forced colours, where box-shadow is dropped. */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

::selection {
  background: var(--color-text-selection);
}

/* Present for assistive technology only, e.g. the page heading. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/*
 * The two media queries the builder allows (see .claude/rules/css.md): both are user
 * preferences, not breakpoints, and both live only here.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms;
    animation-duration: 0.01ms;
    /* An endless animation at that speed would flicker; once, it ends where it rests */
    animation-iteration-count: 1;
  }
}

/*
 * Forced colours (Windows high contrast) drop backgrounds and box-shadows. Everything that
 * carries state only through them gets a border or outline; transparent ones in the blocks
 * become visible here, and system colours mark the active state.
 */
@media (forced-colors: active) {
  .switch {
    border-color: ButtonText;
  }

  .switch::before {
    background: ButtonText;
  }

  .switch:checked {
    border-color: Highlight;
  }

  .switch:checked::before {
    background: Highlight;
  }

  .tabs__tab--active {
    border-bottom-color: Highlight;
  }

  .resizer--horizontal {
    border-top: 1px solid CanvasText;
  }

  .resizer--vertical {
    border-inline-start: 1px solid CanvasText;
  }

  .structure__tree--drop-end::after,
  .structure__row--drop-before::before,
  .structure__row--drop-after::before {
    outline: 1px solid Highlight;
  }

  .structure__row--drop-inside {
    outline-color: Highlight;
  }

  .structure__row--selected {
    border-inline-start-color: Highlight;
  }

  .structure__item:focus-visible > .structure__row,
  .code-editor:focus-within {
    outline-color: Highlight;
  }

  .canvas__box--hover,
  .canvas__box--selected {
    outline-color: Highlight;
  }

  .element-panel__field:focus-within,
  .context-menu__item:focus-visible {
    outline-color: Highlight;
  }

  .combobox__option--active,
  .overview__range-option[aria-checked="true"] {
    border-color: Highlight;
  }
}

/*
 * App shell: canvas top left, code editor below it, sidebar (structure and element
 * settings) on the right, or on the left with --sidebar-start, taskbar for inserting
 * elements across the full width at the bottom.
 */
/*
 * Until main.ts knows who logged in, nothing shows: an editor must never see the owner's
 * tools flash up before they are taken away.
 */
.app--starting {
  visibility: hidden;
}

.app {
  --app-sidebar-width: 280px;
  --app-editor-height: 260px;
  --app-resizer-size: 1px;

  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--app-resizer-size) var(--app-sidebar-width);
  grid-template-rows: minmax(0, 1fr) var(--app-resizer-size) var(--app-editor-height) auto;
  grid-template-areas:
    "canvas         sidebar-resizer sidebar"
    "editor-resizer sidebar-resizer sidebar"
    "editor         sidebar-resizer sidebar"
    "taskbar        taskbar         taskbar";
  height: 100dvh;
  overflow: hidden;
}

/* The sidebar docked on the leading edge: the same grid, mirrored */
.app--sidebar-start {
  grid-template-columns: var(--app-sidebar-width) var(--app-resizer-size) minmax(0, 1fr);
  grid-template-areas:
    "sidebar sidebar-resizer canvas"
    "sidebar sidebar-resizer editor-resizer"
    "sidebar sidebar-resizer editor"
    "taskbar taskbar         taskbar";
}

.app__canvas {
  grid-area: canvas;
}

.app__editor-resizer {
  grid-area: editor-resizer;
}

.app__editor {
  grid-area: editor;
}

.app__sidebar-resizer {
  grid-area: sidebar-resizer;
}

.app__sidebar {
  grid-area: sidebar;
}

.app__taskbar {
  grid-area: taskbar;
}

/*
 * The manage panel lies over the whole app, taskbar included: Save, undo and the page
 * switcher belong to the page in the canvas, and leaving them under a panel about the
 * website invites people to press the wrong one.
 */
.app__manage {
  grid-area: 1 / 1 / 5 / 4;
  z-index: var(--z-overlay);
}

/* While a resizer or canvas handle is dragged: no text selection anywhere (see canvas.css for the iframe) */
.app--resizing {
  user-select: none;
}

/*
 * Button that shows only an icon (SVG or a text glyph); its name lives in title/aria-label.
 * Size modifiers set the two block-local properties; a block that needs positioning adds
 * its own class next to this one and keeps only positional rules there.
 */
.icon-button {
  --icon-button-size: var(--size-control-s);
  --icon-button-icon-size: 16px;

  display: grid;
  flex: none;
  place-items: center;
  width: var(--icon-button-size);
  height: var(--icon-button-size);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--step-2);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color var(--duration-fast),
    border-color var(--duration-fast),
    color var(--duration-fast);
}

.icon-button:hover:not(:disabled) {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.icon-button:active:not(:disabled) {
  background: var(--color-surface-raised);
}

.icon-button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Off but focusable, with its reason in the tooltip: only the icon fades */
.icon-button[aria-disabled="true"] {
  cursor: not-allowed;
}

.icon-button[aria-disabled="true"] > .icon-button__icon {
  opacity: 0.35;
}

.icon-button[aria-disabled="true"]:hover {
  background: transparent;
  color: var(--color-text-muted);
}

/* Takes something away for good: red at rest, so it is found without reading */
.icon-button--danger {
  color: var(--color-danger);
}

.icon-button--danger:hover:not(:disabled, [aria-disabled="true"]) {
  background: var(--color-danger-muted);
  color: var(--color-danger);
}

.icon-button--danger[aria-disabled="true"]:hover {
  color: var(--color-danger);
}

.icon-button--s {
  --icon-button-size: 22px;
  --icon-button-icon-size: 14px;
}

.icon-button--m {
  --icon-button-size: var(--size-control-m);
}

/* Taskbar style: wider than tall, with a border on hover */
.icon-button--l {
  --icon-button-size: var(--size-control-l);
  --icon-button-icon-size: 18px;

  width: 36px;
  border-radius: var(--radius-m);
}

.icon-button--l:hover:not(:disabled) {
  border-color: var(--color-border);
}

.icon-button__icon {
  width: var(--icon-button-icon-size);
  height: var(--icon-button-icon-size);
}

/* A tag beside a name, what it is or where it stands; one block for every list. */
/*
 * Kind (Home, Template, Frame) or standing (Live, Changed, Draft), so a colour means the
 * same thing wherever it shows. Facts like counts are text, never a badge.
 */
.badge {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--space-2xs);
  padding-inline: var(--space-xs);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text-muted);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  font-weight: 400;
  line-height: 1.6;
  white-space: nowrap;
}

/* A standing carries a dot as well, so it is not told by its colour alone */
.badge--live::before,
.badge--changed::before,
.badge--draft::before,
.badge--failed::before,
.badge--running::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

/* On the website as it is stored here */
.badge--live {
  background: var(--color-live-muted);
  color: var(--color-live);
}

/* Waiting for something: a publish, an approval */
.badge--changed {
  background: var(--color-pending-muted);
  color: var(--color-pending);
}

/* Only in the builder, never on the website */
.badge--draft {
  background: var(--color-draft-muted);
  color: var(--color-draft);
}

/* Stopped by an error and needs somebody: a service of the machine */
.badge--failed {
  background: var(--color-danger-muted);
  color: var(--color-danger);
}

/* Still under way, an action of the machine: the dot pulses until it has ended; the word says it without the motion */
.badge--running {
  background: var(--color-pending-muted);
  color: var(--color-pending);
}

.badge--running::before {
  animation: badge-pulse var(--duration-pulse) ease-in-out infinite;
}

@keyframes badge-pulse {
  50% {
    opacity: 0.25;
    scale: 0.6;
  }
}

/* The page the website's address opens, and the one every other address shows */
.badge--home,
.badge--not-found {
  background: var(--color-accent-muted);
  color: var(--color-text);
}

/* The one the builder has open, or the account that is you */
.badge--current {
  box-shadow: inset 0 0 0 1px var(--color-accent);
  background: transparent;
  color: var(--color-accent);
}

.badge--template {
  background: var(--color-template-muted);
  color: var(--color-template);
}

.badge--frame {
  background: var(--color-frame-muted);
  color: var(--color-frame);
}

.badge--component {
  background: var(--color-component-muted);
  color: var(--color-component);
}

/* Members only: a property of the page, outlined so it never reads as a standing */
.badge--members {
  box-shadow: inset 0 0 0 1px var(--color-border-strong);
  background: transparent;
}

/* Tighter, for the rows of the page switcher */
.badge--compact {
  padding-inline: var(--space-2xs);
}

/* The icon buttons at the end of a list row, in fixed slots, with Delete last and apart. */
.row-actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-3xs);
}

/* A slot this row has no action for: it keeps its width, so the columns line up */
.row-actions__gap {
  width: var(--size-control-m);
  height: var(--size-control-m);
}

/* Between the actions and Delete, which takes something away for good */
.row-actions__divider {
  width: 1px;
  height: var(--space-m);
  margin-inline: var(--space-2xs);
  background: var(--color-border);
}

/*
 * The canvas is an iframe: the customer site's CSS stays fully separated from the
 * builder's CSS, in both directions.
 *
 * Selection and hover are not drawn inside the page but as an overlay above the iframe,
 * so the builder changes neither the HTML nor the CSS of the site.
 */
.canvas {
  --canvas-handle-size: var(--space-s);
  /* Set by TypeScript while the preview is resized at the handles; 1fr fills the stage. */
  --canvas-width: 1fr;
  --canvas-height: 1fr;

  position: relative;
  display: flex;
  min-width: 0;
  min-height: 0;
  /* The size label sits in the top edge, the handles form the sides and the bottom. */
  padding: var(--space-l) 0 0;
  background: var(--color-workspace);
}

/* Current canvas width and height, shown in the top edge */
.canvas__size {
  position: absolute;
  inset: 0 0 auto;
  height: var(--space-l);
  font-size: var(--step--1);
  line-height: var(--space-l);
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--color-text-subtle);
  pointer-events: none;
}

/*
 * What the canvas shows, at the start of the top edge: the page switcher, then for a
 * template the entry chip. Half the width at most, so the size in the middle stays clear.
 */
.canvas__bar {
  position: absolute;
  inset-block-start: 1px;
  inset-inline-start: var(--canvas-handle-size);
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  max-width: calc(50% - 4rem);
}

/* The page name gives way before the entry chip does */
.canvas__pages {
  flex: 0 1 auto;
}

/*
 * Handles left, right and below the viewport. A px size caps its track, so the canvas
 * stays centred horizontally and keeps its top edge while it shrinks.
 */
.canvas__stage {
  display: grid;
  flex: 1;
  grid-template-columns: var(--canvas-handle-size) minmax(0, var(--canvas-width)) var(--canvas-handle-size);
  grid-template-rows: minmax(0, var(--canvas-height)) var(--canvas-handle-size);
  justify-content: center;
  align-content: start;
  min-width: 0;
  min-height: 0;
}

/* Responsive preview: the canvas is narrowed at the side handles and shortened at the bottom one. */
.canvas__handle {
  position: relative;
  cursor: col-resize;
  touch-action: none;
}

.canvas__handle::before {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: 4px;
  height: 48px;
  border-radius: 2px;
  background: var(--color-border-strong);
  translate: -50% -50%;
  transition: background-color var(--duration-fast);
}

.canvas__handle--left {
  grid-area: 1 / 1;
}

.canvas__handle--right {
  grid-area: 1 / 3;
}

.canvas__handle--bottom {
  grid-area: 2 / 2;
  cursor: row-resize;
}

.canvas__handle--bottom::before {
  width: 48px;
  height: 4px;
}

.canvas__handle:hover::before,
.canvas__handle:focus-visible::before,
.canvas__handle--active::before {
  background: var(--color-accent);
}

/* The focus ring stays inside the handle so it never overlaps the page */
.canvas__handle:focus-visible {
  outline-offset: -2px;
}

/*
 * Cross-block on purpose: the resizing state lives on the app root because every drag
 * (sidebar, editor, canvas handles) crosses the iframe, and the iframe must not swallow
 * pointer events while one is in progress. The canvas cannot know about the drag itself.
 */
.app--resizing .canvas__frame {
  pointer-events: none;
}

.app--resizing .canvas__size {
  color: var(--color-text);
}

.canvas__viewport {
  position: relative;
  grid-area: 1 / 2;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--radius-s);
  background: var(--color-canvas);
  box-shadow: var(--shadow-canvas);
}

.canvas__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: var(--color-canvas);
}

.canvas__overlay {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.canvas__box {
  position: absolute;
  box-sizing: border-box;
}

.canvas__box--hover {
  outline: 1px solid var(--color-accent);
  outline-offset: -1px;
}

.canvas__box--selected {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.canvas__box--component {
  outline-color: var(--color-component);
}

/* The placeholder where a frame's pages go */
.canvas__box--frame {
  outline-color: var(--color-frame);
}

/* Over an element the website leaves out: amber, with the reason in its top corner, inside
   the box, since the overlay clips what reaches above the canvas */
.canvas__box--condition {
  outline-color: var(--color-condition);
}

.canvas__box-reason {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  max-width: min(32rem, 100%);
  padding: 0 var(--space-2xs);
  overflow: hidden;
  border-radius: 0 0 var(--radius-s) 0;
  background: var(--color-condition);
  color: var(--color-on-accent);
  font-size: var(--step--1);
  line-height: 1.6;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * The sidebar: the structure tree on top, the settings of the selected element below it,
 * split by a resizer. Each section folds to its header; the other one then takes the room.
 */
.sidebar {
  /* Height of the element section; set by TypeScript from the resizer */
  --sidebar-panel-height: 320px;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--color-surface);
}

.sidebar__section {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.sidebar__section--structure {
  flex: 1 1 0;
}

.sidebar__section--element {
  flex: 0 0 var(--sidebar-panel-height);
}

/* The editor has no tree and no element settings, so this one section takes the sidebar */
.sidebar__section--content-fields {
  flex: 1 1 0;
  overflow-y: auto;
}

/* Folded to its header */
.sidebar__section--collapsed {
  flex: none;
}

/* The other section is folded: this one takes all the room */
.sidebar__section--fill {
  flex: 1 1 0;
}

.sidebar__resizer {
  flex: none;
  height: 1px;
}

.sidebar__header {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-2xs);
  height: var(--panel-header-height);
  padding-inline: var(--space-xs) var(--space-s);
  border-bottom: 1px solid var(--color-border);
}

.sidebar__section--collapsed .sidebar__header {
  border-bottom-color: transparent;
}

/* Every folded section but the last keeps a line below, so two headers do not merge */
.sidebar__section--collapsed:not(:last-child) {
  border-bottom: 1px solid var(--color-border);
}

.sidebar__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

/* The whole title is the disclosure button; the heading around it keeps the outline of the page */
.sidebar__toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  width: 100%;
  min-width: 0;
  height: var(--size-control-m);
  padding-inline: var(--space-2xs);
  border: 0;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.sidebar__toggle:hover {
  background: var(--color-surface-hover);
}

.sidebar__toggle:focus-visible {
  outline-offset: -2px;
}

.sidebar__toggle-icon {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--color-text-subtle);
  transition: transform var(--duration-fast);
}

.sidebar__toggle--expanded .sidebar__toggle-icon {
  transform: rotate(90deg);
}

.sidebar__title-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar__actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-3xs);
}

.sidebar__body {
  flex: 1;
  min-height: 0;
  padding: var(--space-m);
  overflow-x: hidden;
  overflow-y: auto;
}

/* Tree view of the inserted elements */
.structure {
  --structure-indent: 14px;
  /* Set by TypeScript per row; the root level is 1 */
  --structure-level: 1;
  /* Width of the selection mark on the row's leading edge */
  --structure-selection-width: 2px;

  display: flex;
  flex-direction: column;
  min-height: 100%;
}

.structure__empty {
  margin: auto 0;
  padding: var(--space-l) var(--space-s);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-m);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-align: center;
  text-wrap: balance;
}

.structure__tree,
.structure__group {
  padding: 0;
  list-style: none;
}

.structure__tree {
  position: relative;
}

/* The empty state and the tree itself receive focus programmatically; the ring stays inside them */
.structure__empty:focus-visible,
.structure__tree:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* Dropping at the end of the top level: line below the whole tree */
.structure__tree--drop-end::after {
  content: "";
  position: absolute;
  inset-inline: var(--space-2xs) 0;
  bottom: -2px;
  height: 2px;
  border-radius: 1px;
  background: var(--color-accent);
}

/* Focus is shown on the row (see below), not on the item that holds the children too */
.structure__item {
  outline: none;
}

/* The dragged element and its children stay visible but pale */
.structure__item--dragging {
  opacity: 0.4;
}

.structure__row {
  --structure-row-inset: calc(var(--space-2xs) + (var(--structure-level) - 1) * var(--structure-indent));
  /* The actions fade in over this colour; each state below sets its own */
  --structure-row-background: var(--color-surface);

  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  height: var(--size-control-m);
  padding-inline: var(--structure-row-inset) var(--space-2xs);
  border-radius: var(--radius-s);
  color: var(--color-text-muted);
  user-select: none;
  cursor: grab;
}

/* Dropping before/after, and where the taskbar would insert: line at the indent of this row (z-index keeps it above the row's children) */
.structure__row--drop-before::before,
.structure__row--drop-after::before {
  content: "";
  position: absolute;
  inset-inline: var(--structure-row-inset) 0;
  z-index: 1;
  height: 2px;
  border-radius: 1px;
  background: var(--color-accent);
  pointer-events: none;
}

.structure__row--drop-before::before {
  top: -1px;
}

.structure__row--drop-after::before {
  bottom: -1px;
}

/* Dropping inside: whole row marked as target, dashed so it differs from the solid focus ring */
.structure__row--drop-inside {
  background: var(--color-accent-muted);
  outline: 2px dashed var(--color-accent);
  outline-offset: -2px;
  color: var(--color-text);
}

/* While dragging, the row's children must not distort the drop target */
.structure__row > * {
  pointer-events: none;
}

.structure__row > .structure__toggle,
.structure__row > .structure__actions,
.structure__actions > .structure__action,
.structure__row > .structure__label,
.structure__row > .structure__name,
.structure__row > .structure__rename {
  pointer-events: auto;
}

.structure__row:hover {
  --structure-row-background: var(--color-surface-hover);

  background: var(--color-surface-hover);
  color: var(--color-text);
}

.structure__row:active {
  cursor: grabbing;
}

/*
 * Selected: tinted plus a bar on the leading edge, so selection is not colour-only and
 * differs from hover. The bar takes its width from the padding, so nothing shifts.
 */
.structure__row--selected,
.structure__row--selected:hover {
  --structure-row-background: var(--color-accent-muted);

  padding-inline-start: calc(var(--structure-row-inset) - var(--structure-selection-width));
  border-inline-start: var(--structure-selection-width) solid var(--color-accent);
  background: var(--color-accent-muted);
  color: var(--color-text);
}

/* A place of a component, and the component being edited: the component colour, not the page's */
/* Shown on a condition: its name and icon are amber, like its outline on the canvas */
.structure__row--condition > .structure__icon,
.structure__row--condition > .structure__name {
  color: var(--color-condition);
}

.structure__row--component > .structure__icon,
.structure__row--component > .structure__name {
  color: var(--color-component);
}

.structure__row--component.structure__row--selected,
.structure__row--component.structure__row--selected:hover {
  --structure-row-background: var(--color-component-muted);

  border-inline-start-color: var(--color-component);
  background: var(--color-component-muted);
}

/* @content, where a frame's pages go: the frame colour */
.structure__row--frame > .structure__icon,
.structure__row--frame > .structure__name {
  color: var(--color-frame);
}

.structure__row--frame.structure__row--selected,
.structure__row--frame.structure__row--selected:hover {
  --structure-row-background: var(--color-frame-muted);

  border-inline-start-color: var(--color-frame);
  background: var(--color-frame-muted);
}

/* The page around the component being edited: shown for orientation, not for editing */
.structure__row--outside {
  opacity: 0.4;
  cursor: default;
}

.structure__row--outside:hover {
  background: none;
  color: var(--color-text-muted);
}

.structure__item:focus-visible > .structure__row {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.structure__toggle {
  display: grid;
  flex: none;
  place-items: center;
  width: 16px;
  height: 16px;
  color: var(--color-text-subtle);
  cursor: pointer;
}

.structure__toggle--hidden {
  visibility: hidden;
}

.structure__toggle-icon {
  width: 12px;
  height: 12px;
  transition: transform var(--duration-fast);
}

.structure__toggle--expanded .structure__toggle-icon {
  transform: rotate(90deg);
}

.structure__icon {
  flex: none;
  width: 16px;
  height: 16px;
}

/* The tag is the secondary information: the readable name next to it carries the row */
.structure__label {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  color: var(--color-text-subtle);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.structure__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--color-text);
  font-size: var(--step--1);
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Renaming in place: the input keeps the row's height so nothing jumps while editing */
.structure__rename {
  flex: 1;
  min-width: 0;
  height: var(--size-control-s);
  padding-block: 0;
  padding-inline: var(--space-3xs);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  /* The row suppresses selection for dragging; inside the input text must stay selectable */
  user-select: text;
}

/* Editing the tag: code face, as narrow as the tag it replaces */
.structure__rename--tag {
  flex: none;
  font-family: var(--font-code);
}

.structure__rename:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* Hidden on the canvas, the element and everything in it: pale, and marked by the flag below */
.structure__row--hidden > .structure__icon,
.structure__row--hidden > .structure__label,
.structure__row--hidden > .structure__name,
.structure__row--hidden > .structure__preview {
  opacity: 0.5;
}

/* The struck-through eye on the hidden element itself; it stays when the row is not hovered */
.structure__flag {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--color-text-subtle);
}

/* Shown on a condition: amber like its outline on the canvas */
.structure__flag--condition {
  color: var(--color-condition);
}

/* The actions float over the flag: their eye button says the same while they are shown */
.structure__row:hover > .structure__flag,
.structure__row--selected > .structure__flag {
  visibility: hidden;
}

.structure__preview {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * Duplicate and delete buttons (.icon-buttons): only shown while the row is hovered or
 * selected. They float over the end of the row, fading in over its background, so the
 * name is not cut short to make room for buttons that are not there.
 */
.structure__actions {
  position: absolute;
  inset-block: 0;
  inset-inline-end: var(--space-2xs);
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
  padding-inline-start: var(--space-m);
  /* The row's tint is translucent on a selected row: the panel's own colour underneath keeps
     the name or text behind the buttons from showing through. */
  background:
    linear-gradient(to right, transparent, var(--structure-row-background) var(--space-m)),
    linear-gradient(to right, transparent, var(--color-surface) var(--space-m));
  opacity: 0;
  pointer-events: none;
}

.structure__row:hover .structure__actions,
.structure__row--selected .structure__actions {
  opacity: 1;
  pointer-events: auto;
}

/* Settings of the selected element below the structure: one card per value, label above it */
.element-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.element-panel__empty {
  margin: auto 0;
  padding: var(--space-l) var(--space-s);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-m);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-align: center;
  text-wrap: balance;
}

.element-panel__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.element-panel__group-title {
  margin: 0;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* A card with the reason its value was refused below it */
.element-panel__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

/* A card: the label on top, the value below; the whole card shows the focus of its field */
.element-panel__field {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  padding: var(--space-xs) var(--space-s);
  border: 1px solid transparent;
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
}

.element-panel__field:focus-within {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.element-panel__field--invalid,
.element-panel__field--invalid:focus-within {
  border-color: var(--color-danger);
  outline-color: var(--color-danger);
}

/* The field for a new attribute: dashed like the button below it, so it is not taken for one that exists */
.element-panel__field--new {
  border: 1px dashed var(--color-accent);
  background: transparent;
}

/* Room at the end of the card for its remove button or the arrow of its suggestions */
.element-panel__field--with-action {
  padding-inline-end: calc(var(--space-s) + 22px);
}

.element-panel__label {
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Attribute names are code: same face as in the HTML editor */
.element-panel__label--code {
  font-family: var(--font-code);
}

/* The card carries the focus ring, so the field inside it draws none of its own */
.element-panel__input,
.element-panel__select,
.element-panel__textarea {
  width: 100%;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: var(--step-0);
}

.element-panel__input:focus-visible,
.element-panel__select:focus-visible,
.element-panel__textarea:focus-visible {
  outline: none;
}

.element-panel__input::placeholder,
.element-panel__textarea::placeholder {
  color: var(--color-text-subtle);
}

.element-panel__input--code {
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.element-panel__select {
  cursor: pointer;
}

.element-panel__textarea {
  min-height: 5lh;
  resize: vertical;
}

/* A value that cannot be edited here, e.g. the preset marker */
.element-panel__value {
  overflow: hidden;
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Remove button (an .icon-button) in the top corner of a card */
.element-panel__field-action {
  position: absolute;
  inset-block-start: var(--space-2xs);
  inset-inline-end: var(--space-2xs);
}

/* The reason a value was not taken, right below its card */
.element-panel__message {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

/* The selected element is a place of a component: what it is and the two ways to change it */
.element-panel__place {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-s);
  border: 1px solid var(--color-component);
  border-radius: var(--radius-m);
  background: var(--color-component-muted);
}

/* @content of a frame: the same box in the frame colour */
.element-panel__place--frame {
  border-color: var(--color-frame);
  background: var(--color-frame-muted);
}

.element-panel__place-text {
  margin: 0;
  color: var(--color-text);
  font-size: var(--step--1);
}

.element-panel__place-actions {
  display: flex;
  gap: var(--space-xs);
}

/* A warning below a card that does not refuse the value, e.g. a collection that does not exist */
.element-panel__note {
  margin: 0;
  color: var(--color-pending);
  font-size: var(--step--1);
}

/* What a value does, in words, e.g. the entries a filter keeps */
.element-panel__note--info {
  color: var(--color-text-subtle);
}

/* A label and a switch on one line */
.element-panel__switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  padding: var(--space-xs) var(--space-s);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step-0);
  cursor: pointer;
}

.element-panel__hints {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  margin: 0;
  padding: var(--space-xs) var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-inline-start: 2px solid var(--color-accent);
  border-radius: var(--radius-m);
  list-style: none;
}

.element-panel__hint {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* "Add attribute" below the list, as a dashed card that is not there yet */
.element-panel__add {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: var(--size-control-l);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-m);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--step-0);
  cursor: pointer;
}

.element-panel__add:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.element-panel__kbd {
  color: var(--color-text-subtle);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.element-panel__add-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Rows over a filter or condition in the element panel: one bordered fieldset per clause, narrow enough for the sidebar */
.condition-editor {
  display: grid;
  gap: var(--space-xs);
  margin-top: var(--space-3xs);
}

.condition-editor__rows {
  display: grid;
  gap: var(--space-xs);
}

.condition-editor__row {
  display: grid;
  gap: var(--space-2xs);
  min-width: 0;
  margin: 0;
  padding: var(--space-2xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
}

/* The field to compare and the button that removes the row */
.condition-editor__head,
.condition-editor__value {
  display: flex;
  gap: var(--space-2xs);
  align-items: center;
  min-width: 0;
}

.condition-editor__values {
  display: grid;
  gap: var(--space-2xs);
}

.condition-editor__select,
.condition-editor__input {
  flex: 1;
  min-width: 0;
  height: 24px;
  padding: 0 var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--step--1);
}

/* The date kind is short; the value takes the rest of the line */
.condition-editor__value > .condition-editor__select {
  flex: none;
  width: auto;
}

.condition-editor__select:focus-visible,
.condition-editor__input:focus-visible {
  border-color: var(--color-accent);
  outline: 1px solid var(--color-accent);
  outline-offset: -1px;
}

/* The value to compare with the arrow of its suggestions inside its end */
.condition-editor__field {
  --combobox-toggle-block-end: 1px;
  --combobox-toggle-inline-end: 1px;

  position: relative;
  display: flex;
  flex: 1;
  min-width: 0;
}

.condition-editor__field > .condition-editor__input {
  padding-inline-end: 24px;
}

/* A field that names nothing a condition can compare, e.g. a placeholder with a fallback */
.condition-editor__input[aria-invalid="true"] {
  border-color: var(--color-danger);
}

.condition-editor__remove {
  flex: none;
}

.condition-editor__footer {
  display: flex;
  gap: var(--space-s);
  align-items: center;
  justify-content: space-between;
}

/* Text buttons: they add or switch, the rows themselves are the content */
.condition-editor__add,
.condition-editor__or,
.condition-editor__mode {
  justify-self: start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent);
  font: inherit;
  font-size: var(--step--1);
  cursor: pointer;
}

.condition-editor__mode {
  margin-inline-start: auto;
  color: var(--color-text-muted);
}

.condition-editor__add:hover,
.condition-editor__or:hover,
.condition-editor__mode:hover {
  text-decoration: underline;
}

.condition-editor__mode:disabled {
  color: var(--color-text-subtle);
  text-decoration: none;
  cursor: default;
}

.condition-editor__add:focus-visible,
.condition-editor__or:focus-visible,
.condition-editor__mode:focus-visible {
  border-radius: var(--radius-s);
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Suggestions below a text field; fixed and outside the panel, so no scrolling box clips them */
.combobox__list {
  /* Set by TypeScript from the field's position */
  --combobox-top: 0px;
  --combobox-left: 0px;
  --combobox-width: 160px;

  position: fixed;
  inset-block-start: var(--combobox-top);
  inset-inline-start: var(--combobox-left);
  z-index: var(--z-overlay);
  width: var(--combobox-width);
  max-height: 240px;
  margin: 0;
  padding: var(--space-2xs);
  overflow-y: auto;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-toast);
  list-style: none;
}

.combobox__option {
  padding: var(--space-3xs) var(--space-xs);
  /* A long placeholder wraps rather than scrolling the list sideways */
  overflow-wrap: anywhere;
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: var(--step--1);
  line-height: var(--code-line-height);
  cursor: pointer;
}

/* The value after a suggestion's label: what the field will hold, e.g. a collection's slug */
.combobox__value {
  margin-inline-start: var(--space-xs);
  color: var(--color-text-subtle);
}

/* The option the arrow keys are on: the focus stays in the field, so the option carries the ring */
.combobox__option--active {
  border-color: var(--color-accent);
  background: var(--color-accent-muted);
}

/* The value the field holds right now */
.combobox__option--current {
  font-weight: 600;
}

/* The arrow button at the end of the field; the field's card leaves room for it and may move it */
.combobox__toggle {
  position: absolute;
  inset-block-end: var(--combobox-toggle-block-end, var(--space-xs));
  inset-inline-end: var(--combobox-toggle-inline-end, var(--space-2xs));
}

.combobox__toggle-icon {
  rotate: 90deg;
}

/* Menu at the pointer or the focused row; fixed, so no scrolling container can clip it */
.context-menu {
  /* Set by TypeScript: where the menu opens, kept inside the window */
  --context-menu-top: 0px;
  --context-menu-left: 0px;

  position: fixed;
  inset-block-start: var(--context-menu-top);
  inset-inline-start: var(--context-menu-left);
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  min-width: 220px;
  padding: var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-toast);
}

.context-menu:focus-visible {
  outline-offset: -2px;
}

.context-menu__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  height: var(--size-control-m);
  padding-inline: var(--space-xs);
  border: 0;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  font-size: var(--step--1);
  text-align: start;
  cursor: pointer;
}

.context-menu__item:hover,
.context-menu__item:focus-visible {
  background: var(--color-surface-hover);
}

.context-menu__item:focus-visible {
  outline-offset: -2px;
}

.context-menu__item--danger {
  color: var(--color-danger);
}

.context-menu__shortcut {
  color: var(--color-text-subtle);
  font-family: var(--font-code);
}

.context-menu__separator {
  height: 1px;
  margin-block: var(--space-2xs);
  background: var(--color-border);
}

/*
 * Three areas: settings and the page switcher on the start side, the elements to insert
 * in the middle, publish and save at the end. The sides take what they need; the middle
 * gets the rest and scrolls when that is not enough.
 */
.taskbar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-m);
  height: var(--taskbar-height);
  padding-inline: var(--space-s);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
}

.taskbar__start,
.taskbar__end {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  /* A flex item's minimum is its content; without this the group would overflow into the elements column */
  min-width: 0;
}

.taskbar__end {
  justify-content: flex-end;
}

/* Speaks only while the site styles travel, fail or have just arrived */
.taskbar__styles {
  overflow: hidden;
  margin: 0 var(--space-xs) 0 0;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.taskbar__styles--failed {
  color: var(--color-danger);
}

/* Between undo/redo and the rest of the start group */
.taskbar__divider {
  width: 1px;
  height: var(--space-m);
  margin-inline: var(--space-2xs);
  background: var(--color-border);
}

/*
 * The toolbar scrolls sideways when the window is too narrow. The bar has a fixed height,
 * so the scrollbar must fit into the slack around the buttons: full height, thin scrollbar.
 */
.taskbar__elements {
  display: flex;
  align-self: stretch;
  align-items: center;
  /* "safe" keeps the first buttons reachable when the row is narrower than its content */
  justify-content: safe center;
  gap: var(--space-xs);
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
}

.taskbar__group {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
}

.taskbar__item {
  flex: none;
}

/* The opener of the manage panel carries the count of waiting comments in its corner */
.taskbar__manage {
  position: relative;
}

.taskbar__badge {
  position: absolute;
  inset-block-start: -2px;
  inset-inline-end: -2px;
  min-width: 16px;
  padding-inline: var(--space-3xs);
  border-radius: 8px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
}

/* Page switcher on the canvas's top edge: a select over the pages of the site, a settings button per row */
.pages {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-width: 0;
}

/* Holds the list, which opens downwards over the canvas */
.pages__switcher {
  position: relative;
  min-width: 0;
}

.pages__current {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  /* As wide as the name: a short one leaves the edge to the entry chip */
  max-width: 100%;
  height: 22px;
  padding-inline: var(--space-xs) var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
}

.pages__current:hover {
  background: var(--color-surface-hover);
}

/* The focus ring sits on the border, which turns accent at the same time */
.pages__current:focus-visible,
.pages__current[aria-expanded="true"] {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.pages__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pages__badges {
  display: flex;
  flex: none;
  gap: var(--space-3xs);
}

.pages__chevron {
  flex: none;
  width: 12px;
  height: 12px;
  margin-inline-start: auto;
  color: var(--color-text-muted);
}

.pages__list {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-start: 0;
  z-index: var(--z-overlay);
  display: grid;
  /* minmax(0, …): a long address is cut with an ellipsis instead of widening the list */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2xs);
  width: 28rem;
  max-width: calc(100vw - 2 * var(--space-m));
  max-height: 60vh;
  padding: var(--space-2xs);
  overflow-y: auto;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-toast);
}

/* The list keeps the focus while the arrow keys walk it; the active option carries the ring */
.pages__list:focus-visible {
  outline: none;
}

.pages__group {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

.pages__group-label {
  padding: var(--space-3xs) var(--space-xs);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* One line per page: the name with its badges, the address to the right */
.pages__option {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-3xs) var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
}

.pages__option:hover {
  background: var(--color-surface-hover);
}

.pages__option--active {
  border-color: var(--color-accent);
  background: var(--color-accent-muted);
}

/*
 * The button to a page's settings: in the row's layout from the start, so the address does
 * not move, and shown where the pointer or the arrow keys are.
 */
.pages__option-edit {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--size-control-s);
  height: var(--size-control-s);
  margin-block: calc(-1 * var(--space-3xs));
  border-radius: var(--radius-s);
  color: var(--color-text-muted);
  visibility: hidden;
}

.pages__option:hover .pages__option-edit,
.pages__option--active .pages__option-edit {
  visibility: visible;
}

.pages__option-edit:hover {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.pages__option-edit-icon {
  width: 16px;
  height: 16px;
}

/* The page the builder has open */
.pages__option[aria-selected="true"] .pages__option-name {
  font-weight: 600;
}

.pages__option-line {
  display: flex;
  flex: 0 1 auto;
  align-items: center;
  gap: var(--space-2xs);
  min-width: 0;
}

.pages__option-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pages__option-path {
  flex: 1 1 0;
  min-width: 4rem;
  margin-inline-start: auto;
  overflow: hidden;
  text-align: end;
  color: var(--color-text-muted);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The entry a template shows on the canvas: step through the entries or search one */
.entry-chip {
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
  min-width: 0;
}

.entry-chip[hidden] {
  display: none;
}

/* Which collection's entries, for a template that serves several */
.entry-chip__collection {
  flex: none;
  max-width: 10rem;
  height: 22px;
  padding-inline: var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step--1);
}

.entry-chip__collection:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.entry-chip__step--previous .icon-button__icon {
  rotate: 180deg;
}

.entry-chip__current {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-width: 0;
  height: 22px;
  padding-inline: var(--space-xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
}

.entry-chip__current:hover {
  background: var(--color-surface-hover);
}

.entry-chip__current:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.entry-chip__current[aria-expanded="true"] {
  border-color: var(--color-accent);
}

.entry-chip__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Not on the website: the canvas shows it, visitors will not see it */
.entry-chip__badge {
  flex: none;
  padding: 0 var(--space-2xs);
  border-radius: var(--radius-s);
  background: var(--color-pending-muted);
  color: var(--color-pending);
  font-size: var(--step--1);
  line-height: 1.5;
}

.entry-chip__badge[hidden] {
  display: none;
}

.entry-chip__count {
  flex: none;
  color: var(--color-text-subtle);
  font-variant-numeric: tabular-nums;
}

.entry-chip__popover {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-start: 0;
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  width: 20rem;
  max-width: calc(100vw - 2 * var(--space-m));
  padding: var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-toast);
}

.entry-chip__popover[hidden] {
  display: none;
}

.entry-chip__search {
  height: var(--size-control-m);
  padding-inline: var(--space-xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--step--1);
}

.entry-chip__search:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.entry-chip__list {
  max-height: 240px;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.entry-chip__option {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-3xs) var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
}

.entry-chip__option:hover {
  background: var(--color-surface-hover);
}

/* The option the arrow keys are on: the focus stays in the search, so the option carries the ring */
.entry-chip__option--active {
  border-color: var(--color-accent);
  background: var(--color-accent-muted);
}

/* The entry the canvas shows right now */
.entry-chip__option[aria-selected="true"] {
  font-weight: 600;
}

.entry-chip__option-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entry-chip__note {
  margin: 0;
  padding-inline: var(--space-xs);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

.entry-chip__note:empty {
  display: none;
}

/* The frame the open page stands in, above the canvas: shaped like the page switcher, the pencil leads into the frame mode */
.frame-chip {
  display: flex;
  flex: none;
  min-width: 0;
}

.frame-chip[hidden] {
  display: none;
}

.frame-chip__edit {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  max-width: 16rem;
  height: 22px;
  padding-inline: var(--space-xs) var(--space-2xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
}

.frame-chip__edit:hover {
  background: var(--color-surface-hover);
}

/* The focus ring sits on the border, which turns accent at the same time, as on the switcher */
.frame-chip__edit:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.frame-chip__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The same badge the switcher shows on a frame */
.frame-chip__badge {
  flex: none;
  padding: 0 var(--space-2xs);
  border-radius: var(--radius-s);
  background: var(--color-frame-muted);
  color: var(--color-frame);
  line-height: 1.6;
}

.frame-chip__icon {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--color-text-muted);
}

.frame-chip__edit:hover .frame-chip__icon {
  color: var(--color-text);
}

/* Publish group at the end of the taskbar: preview, export download, the website, publish */
.publish {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin-inline-end: var(--space-xs);
}

/* Links styled as buttons keep the button's colour and get no underline */
.publish__preview,
.publish__export {
  text-decoration: none;
}

/* Before the first publish there is nothing to preview */
.publish__preview[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* Just published: the way to the website glows twice, so it is found */
.publish__site--fresh {
  animation: publish-site-glow var(--duration-attention) ease-in-out 2;
}

@keyframes publish-site-glow {
  50% {
    background: var(--color-accent-muted);
    color: var(--color-accent);
  }
}

/*
 * Code editors for the selected element. The header carries only the tabs and, with tabs,
 * the classes, and at its end the layout and maximize buttons: side by side it is hidden,
 * because every pane has its own header and the first one takes the layout button. The
 * footer shows only while there is a parse error.
 */
.code-panel {
  /* Set by TypeScript per pane while the splitters are dragged; equal shares by default */
  --code-panel-pane-grow: 1;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--color-surface);
}

.code-panel__header {
  display: flex;
  flex: none;
  height: var(--panel-header-height);
  padding-inline: var(--space-xs);
  border-bottom: 1px solid var(--color-border);
}

/* Every child may shrink, so the classes list truncates instead of overflowing */
.code-panel__header > * {
  min-width: 0;
}

/* Layout and maximize at the end of the panel header */
.code-panel__actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-2xs);
  margin-inline-start: auto;
}

/* Layout and maximize buttons in a header (an .icon-button): only its position lives here */
.code-panel__action {
  align-self: center;
}

.code-panel__body {
  position: relative;
  display: flex;
  flex: 1;
  min-height: 0;
}

/*
 * Each editor sits in a pane. With tabs, the active pane fills the space and has no header.
 * With .code-panel--split all three stand side by side.
 */
.code-panel__pane {
  display: flex;
  /* The width is a share of the space, not a fixed value: set while dragging the splitters */
  flex: var(--code-panel-pane-grow) 1 0;
  flex-direction: column;
  min-width: 0;
  background: var(--color-surface);
}

.code-panel__pane-header {
  display: none;
  flex: none;
  align-items: center;
  height: var(--size-control-l);
  padding-inline: var(--space-m) var(--space-xs);
  border-bottom: 1px solid var(--color-border);
}

/* Never shrinks: the three titles are short, and "CSS" must not end up as "C…" */
.code-panel__pane-title {
  flex: none;
  margin-inline-end: auto;
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.code-panel__pane-body {
  position: relative;
  flex: 1;
  min-height: 0;
}

.code-panel--split .code-panel__pane-header,
.code-panel__pane--maximized .code-panel__pane-header {
  display: flex;
}

/* Splitter between two panes (mixed with .resizer): drag it to resize the panes. */
.code-panel__splitter {
  flex: none;
  width: 1px;
}

/* Maximized pane: large in the middle of the screen, the rest dimmed by the backdrop. */
.code-panel__pane--maximized {
  position: fixed;
  inset: 8dvh 10vw;
  z-index: var(--z-overlay);
  overflow: hidden;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-raised);
}

.code-panel__backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-backdrop);
  background: var(--color-backdrop);
}

.code-panel__empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-l);
  background: var(--color-bg);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-align: center;
}

.code-panel__footer {
  display: flex;
  flex: none;
  align-items: center;
  height: var(--size-control-m);
  padding-inline: var(--space-m);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

.code-panel__error {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--color-danger);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A hint about HTML that was applied anyway, e.g. a line break that became a space */
.code-panel__error--note {
  color: var(--color-text-muted);
}

/* Tab strip in the code-panel header; the active tab carries a real bottom border, not only a colour */
.tabs {
  display: flex;
  gap: var(--space-3xs);
}

.tabs__tab {
  position: relative;
  display: flex;
  align-items: center;
  padding-inline: var(--space-m);
  border: 0;
  /* Transparent here so the active state does not shift the label; forced colours make it visible */
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color var(--duration-fast);
}

.tabs__tab:hover:not(:disabled) {
  color: var(--color-text);
}

.tabs__tab:disabled {
  color: var(--color-text-subtle);
  cursor: default;
}

.tabs__tab:focus-visible {
  outline-offset: -4px;
}

.tabs__tab--active {
  border-bottom-color: var(--color-accent);
  color: var(--color-text);
}

/*
 * Classes of the selected element as badges: pick the active class whose CSS is edited,
 * rename one in place, take it off with its ×, delete it with its trash, add one with the
 * + at the end (it opens the add-class dialog).
 */
.class-switcher {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-width: 0;
  padding-inline: var(--space-s);
}

/*
 * The chips scroll sideways, but the scrollbar is never drawn: inside a header this low it
 * would take the height the chips need. Scroll with the wheel; tabbing to a chip brings it
 * into view.
 */
/*
 * Holds the scrolling chips and draws the edges over them. The edges live here and not on
 * the list, because anything inside the list would scroll away with the chips.
 */
.class-switcher__strip {
  /* Set by TypeScript: the edge grows where chips are hidden, 0 where they are not */
  --class-switcher-fade-start: 0px;
  --class-switcher-fade-end: 0px;

  position: relative;
  display: flex;
  min-width: 0;
}

.class-switcher__strip::before,
.class-switcher__strip::after {
  content: "";
  position: absolute;
  inset-block: 0;
  z-index: var(--z-resizer);
  pointer-events: none;
}

.class-switcher__strip::before {
  inset-inline-start: 0;
  width: var(--class-switcher-fade-start);
  background: linear-gradient(to right, var(--color-border-strong), transparent);
}

.class-switcher__strip::after {
  inset-inline-end: 0;
  width: var(--class-switcher-fade-end);
  background: linear-gradient(to left, var(--color-border-strong), transparent);
}

.class-switcher__list {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  touch-action: pan-x;
  mask-image: linear-gradient(
    to right,
    transparent 0,
    black var(--class-switcher-fade-start),
    black calc(100% - var(--class-switcher-fade-end)),
    transparent 100%
  );
}

/*
 * A chip is a badge like the page badges (Template, Draft): a tinted fill and text in one
 * hue, nothing else, a little lower than those. The active chip gets a deeper fill. The
 * outline stays transparent and is never drawn; it is there for forced colours, which would
 * otherwise lose the chip, and unlike a border it takes no height.
 */
.class-switcher__item {
  display: flex;
  flex: none;
  align-items: stretch;
  border-radius: var(--radius-s);
  outline: 1px solid transparent;
  outline-offset: -1px;
  background: var(--code-selector-muted);
  color: var(--code-selector);
  font-size: var(--step--1);
  line-height: 1.4;
  transition: background-color var(--duration-fast);
}

.class-switcher__item--active {
  background: var(--code-selector-strong);
}

/* An id is set apart from the classes by the blue of the style manager */
.class-switcher__item--id {
  background: var(--code-storage-muted);
  color: var(--code-storage);
}

.class-switcher__item--id.class-switcher__item--active {
  background: var(--code-storage-strong);
}

/* The marker chip of an element preset: the default look of every such element, in teal like the Template badge */
.class-switcher__item--element {
  background: var(--code-type-muted);
  color: var(--code-type);
}

.class-switcher__item--element.class-switcher__item--active {
  background: var(--code-type-strong);
}

.class-switcher__class,
.class-switcher__action {
  border: 0;
  border-radius: var(--radius-s);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* Negative offsets keep the focus ring inside the badge, clear of the neighbouring button */
.class-switcher__class:focus-visible,
.class-switcher__action:focus-visible {
  outline-offset: -2px;
}

/* Same colour a selector has in the CSS editor, because a chip is a selector */
.class-switcher__class {
  padding-inline: var(--space-2xs);
  font-family: var(--font-code);
}

.class-switcher__item--element .class-switcher__class {
  font-family: var(--font-ui);
}

/* Name field while renaming, in place of the label */
.class-switcher__rename {
  min-width: 0;
  padding-inline: var(--space-2xs);
  border: 0;
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: inherit;
  line-height: inherit;
  user-select: text;
}

/*
 * × takes the class off the element, the trash deletes it everywhere. They stay dim until
 * the chip is pointed at, focused inside or active, so a row of badges reads calm; dim, not
 * hidden, so Tab still finds them.
 */
.class-switcher__action {
  display: grid;
  place-items: center;
  padding-inline: var(--space-3xs);
  opacity: 0.45;
  transition:
    opacity var(--duration-fast),
    color var(--duration-fast);
}

.class-switcher__action:last-child {
  padding-inline-end: var(--space-2xs);
}

.class-switcher__item:hover .class-switcher__action,
.class-switcher__item:focus-within .class-switcher__action,
.class-switcher__item--active .class-switcher__action {
  opacity: 1;
}

.class-switcher__action:hover {
  color: var(--color-text);
}

.class-switcher__action--delete:hover {
  color: var(--color-danger);
}

.class-switcher__icon {
  width: 12px;
  height: 12px;
}

/* Only a strip with hidden chips can be dragged, so only then does the pointer say so */
.class-switcher__list--scrollable {
  cursor: grab;
}

/* While dragging, the chips under the pointer must not look clickable or get selected */
.class-switcher__list--dragging,
.class-switcher__list--dragging .class-switcher__class,
.class-switcher__list--dragging .class-switcher__action {
  cursor: grabbing;
  user-select: none;
}

/* The + that opens the add-class dialog (an .icon-button): it never scrolls away */
.class-switcher__add {
  flex: none;
}

/* The dialog behind the + in the class row: one field for one or more class names */
.add-class {
  display: grid;
  gap: var(--space-xs);
  min-width: min(26rem, 80vw);
}

.add-class__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.add-class__input {
  height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--code-selector);
  font-family: var(--font-code);
  font-size: var(--step-0);
}

.add-class__input::placeholder {
  color: var(--color-text-subtle);
}

/* The focus ring sits on the border, which turns accent at the same time */
.add-class__input:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.add-class__input--invalid,
.add-class__input--invalid:focus-visible {
  border-color: var(--color-danger);
  outline-color: var(--color-danger);
}

/* Why a name was not accepted */
.add-class__hint {
  color: var(--color-danger);
  font-size: var(--step--1);
}

.add-class__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/*
 * Highlighted code as a <pre> behind a transparent textarea. Both need the same font,
 * padding, wrapping and scrollbar gutter, otherwise text and caret drift apart.
 *
 * Long lines wrap instead of scrolling sideways, so a line number cannot live in a column
 * of its own: every line is a block that draws its number into its own start padding.
 */
.code-editor {
  /* Room for a three-digit line number plus the gap to the code */
  --code-gutter-width: 36px;

  position: absolute;
  inset: 0;
  background: var(--color-bg);
  font-family: var(--font-code);
  font-size: var(--code-font-size);
  line-height: var(--code-line-height);
}

.code-editor__highlight,
.code-editor__input {
  position: absolute;
  inset: 0;
  margin: 0;
  border: 0;
  font: inherit;
  line-height: inherit;
  /* hidden across: lines wrap. The stable gutter keeps both boxes equally wide. */
  overflow: hidden auto;
  scrollbar-gutter: stable;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  tab-size: 2;
}

.code-editor__highlight {
  padding: var(--space-xs) var(--space-m) var(--space-xs) 0;
  color: var(--code-text);
  /* The gutter is reserved like the textarea's, the bar itself is never drawn */
  scrollbar-color: transparent transparent;
  pointer-events: none;
}

.code-editor__code {
  display: block;
  font: inherit;
}

/* One source line. Its number sits in the start padding and stays put when the line wraps. */
.code-editor__row {
  position: relative;
  display: block;
  min-height: var(--code-line-height);
  padding-inline-start: var(--code-gutter-width);
}

/* Line numbers are decorative (the active one is readable), so the faint colour is allowed here */
.code-editor__row::before {
  content: attr(data-line);
  position: absolute;
  inset-inline-start: 0;
  width: calc(var(--code-gutter-width) - var(--space-xs));
  color: var(--color-text-faint);
  text-align: end;
  user-select: none;
}

.code-editor__row--active::before {
  color: var(--color-text-muted);
}

/* Focus is shown on the whole editor (below), not on the transparent textarea */
.code-editor__input {
  width: 100%;
  height: 100%;
  padding: var(--space-xs) var(--space-m) var(--space-xs) var(--code-gutter-width);
  outline: 0;
  resize: none;
  background: transparent;
  color: transparent;
  caret-color: var(--code-text);
}

.code-editor__input::selection {
  background: var(--color-text-selection);
  color: transparent;
}

.code-editor__token--comment { color: var(--code-comment); }
.code-editor__token--string { color: var(--code-string); }
.code-editor__token--number { color: var(--code-number); }
.code-editor__token--keyword { color: var(--code-keyword); }
.code-editor__token--storage { color: var(--code-storage); }
.code-editor__token--function { color: var(--code-function); }
.code-editor__token--variable { color: var(--code-variable); }
.code-editor__token--type { color: var(--code-type); }
.code-editor__token--regex { color: var(--code-regex); }
.code-editor__token--tag { color: var(--code-tag); }
.code-editor__token--attr { color: var(--code-variable); }
.code-editor__token--property { color: var(--code-variable); }
.code-editor__token--selector { color: var(--code-selector); }
.code-editor__token--value { color: var(--code-string); }
.code-editor__token--punctuation { color: var(--code-punctuation); }

/* Cursor line in the bottom corner, only while the editor is being typed in */
.code-editor__position {
  position: absolute;
  inset-block-end: var(--space-2xs);
  inset-inline-end: var(--space-m);
  display: none;
  padding: 0 var(--space-2xs);
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text-subtle);
  font-family: var(--font-ui);
  font-size: var(--step--1);
  pointer-events: none;
}

.code-editor:focus-within .code-editor__position {
  display: block;
}

/*
 * Focus ring drawn inside the editor: the inset shadow is the visible ring, the transparent
 * outline is what forced colours render instead.
 */
.code-editor:focus-within {
  outline: 2px solid transparent;
  outline-offset: -2px;
  box-shadow: inset 0 0 0 2px var(--color-accent);
}

/*
 * Visibly only 1px wide. ::after enlarges the hit area, ::before shows a wider accent
 * line on hover and while dragging.
 */
.resizer {
  position: relative;
  z-index: var(--z-resizer);
  background: var(--color-border);
  touch-action: none;
}

.resizer::before,
.resizer::after {
  content: "";
  position: absolute;
}

.resizer::before {
  background: transparent;
  transition: background-color var(--duration-fast);
}

.resizer:hover::before,
.resizer:focus-visible::before,
.resizer--active::before {
  background: var(--color-accent);
}

/* The ring hugs the accent line; the outline itself comes from base.css */
.resizer:focus-visible {
  outline-offset: 1px;
}

.resizer--horizontal {
  cursor: row-resize;
}

.resizer--horizontal::before {
  inset: -1px 0;
}

.resizer--horizontal::after {
  inset: -4px 0;
}

.resizer--vertical {
  cursor: col-resize;
}

.resizer--vertical::before {
  inset: 0 -1px;
}

.resizer--vertical::after {
  inset: 0 -4px;
}

/* Button with a text label */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--size-control-l);
  padding-inline: var(--space-m);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: transparent;
  color: var(--color-text);
  font-size: var(--step--1);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--duration-fast),
    border-color var(--duration-fast),
    filter var(--duration-fast);
}

.button:hover:not(:disabled, [aria-disabled="true"]) {
  background: var(--color-surface-hover);
}

/* Off for a reason the surrounding text gives: muted, whatever the modifier. aria-disabled
   is the same look for a button that stays focusable so its tooltip can say why. */
.button:disabled,
.button[aria-disabled="true"] {
  border-color: var(--color-border);
  background: transparent;
  color: var(--color-text-subtle);
  cursor: not-allowed;
  filter: none;
}

/* Filled: dark text on the accent, see --color-on-accent for the contrast figure */
.button--primary {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.button--primary:hover:not(:disabled, [aria-disabled="true"]) {
  background: var(--color-accent);
  filter: brightness(1.1);
}

.button--primary:active:not(:disabled, [aria-disabled="true"]) {
  filter: brightness(0.95);
}

/* Saves a component rather than the page: the component colour says which one it is */
.button--component {
  border-color: var(--color-component);
  background: var(--color-component);
  color: var(--color-on-accent);
}

.button--component:hover:not(:disabled) {
  background: var(--color-component);
  filter: brightness(1.1);
}

/* Saves a frame rather than the page: the frame colour says which one it is */
.button--frame {
  border-color: var(--color-frame);
  background: var(--color-frame);
  color: var(--color-on-accent);
}

.button--frame:hover:not(:disabled) {
  background: var(--color-frame);
  filter: brightness(1.1);
}

/* An action that cannot be undone: outlined in the danger colour, filled on hover */
.button--danger {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.button--danger:hover:not(:disabled) {
  background: var(--color-danger);
  color: var(--color-on-accent);
}

/* Short messages at the bottom centre, above the taskbar. They disappear on their own. */
.toast {
  position: fixed;
  inset: auto 0 calc(var(--taskbar-height) + var(--space-m));
  z-index: var(--z-toast);
  display: grid;
  justify-items: center;
  gap: var(--space-xs);
  pointer-events: none;
}

.toast__item {
  padding: var(--space-xs) var(--space-m);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step-0);
  box-shadow: var(--shadow-toast);
  transition:
    opacity var(--duration-fast),
    translate var(--duration-fast);
}

/* State before fading in and after fading out */
.toast__item--hidden {
  opacity: 0;
  translate: 0 8px;
}

/* The one tooltip, in the browser's top layer, so no scrolling list cuts it off; tooltip.ts places it. */
.tooltip {
  --tooltip-top: 0;
  --tooltip-left: 0;

  position: fixed;
  inset: var(--tooltip-top) auto auto var(--tooltip-left);
  width: max-content;
  max-width: 18rem;
  margin: 0;
  padding: var(--space-2xs) var(--space-xs);
  border: 0;
  border-radius: var(--radius-s);
  background: var(--color-workspace);
  box-shadow: 0 0 0 1px var(--color-border-strong), var(--shadow-toast);
  color: var(--color-text);
  font-size: var(--step--1);
  line-height: 1.4;
  pointer-events: none;
}

/* Modal built on <dialog>. The reset removes the dialog's margin, so auto is restored here to centre it. */
.modal {
  width: min(480px, calc(100vw - 2 * var(--space-l)));
  max-height: calc(100dvh - 2 * var(--space-l));
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-l);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-raised);
}

/* For content that lists things side by side, e.g. the widgets */
.modal--wide {
  width: min(44rem, calc(100vw - 2 * var(--space-l)));
}

.modal[open] {
  display: flex;
  flex-direction: column;
}

.modal::backdrop {
  background: var(--color-backdrop);
}

/* Title at the start, close button (an .icon-button) at the end */
.modal__header {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  height: 48px;
  padding-inline: var(--space-l) var(--space-s);
  border-bottom: 1px solid var(--color-border);
}

.modal__title {
  margin: 0;
  font-size: var(--step-1);
  font-weight: 600;
}

.modal__body {
  flex: 1;
  min-height: 0;
  padding: var(--space-l);
  overflow-y: auto;
}

/* Settings: what belongs to the person, not the website — groups with a title, rows with a label and a switch */

.settings {
  display: grid;
  gap: var(--space-xl);
}

.settings__title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.settings__hint {
  margin: var(--space-2xs) 0 var(--space-s);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.settings__list {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-bg);
}

.settings__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding: var(--space-s) var(--space-m);
  font-size: var(--step-0);
  cursor: pointer;
}

.settings__row + .settings__row {
  border-top: 1px solid var(--color-border);
}

/* Who is logged in, and the way out */
.settings__account {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
}

.settings__email {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Toggle switch: a checkbox with role="switch", drawn entirely in CSS. Focus comes from base.css. */
.switch {
  position: relative;
  flex: none;
  width: 36px;
  height: 20px;
  margin: 0;
  appearance: none;
  /* Transparent here; forced colours turn it into the visible track outline */
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--color-border-strong);
  cursor: pointer;
  transition: background-color var(--duration-fast);
}

.switch::before {
  content: "";
  position: absolute;
  inset-block-start: 1px;
  inset-inline-start: 1px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-text);
  transition: translate var(--duration-fast);
}

.switch:checked {
  background: var(--color-accent);
}

.switch:checked::before {
  translate: 16px 0;
}

.switch:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/*
 * Style manager: a section of the manage panel, a list of every class on the left and the
 * CSS of the picked one on the right. Its tabs pick selectors, stylesheets or variables.
 */
.style-manager {
  --style-manager-list-width: 360px;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--color-bg);
}

/* The tabs (a .tabs block) sit in a bar of their own, the active border on its lower edge */
.style-manager__bar {
  display: flex;
  flex: none;
  align-items: stretch;
  gap: var(--space-m);
  padding-inline: var(--space-m);
  height: var(--panel-header-height);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.style-manager__tabs {
  min-width: 0;
}

/* Where the site styles stand; they save themselves, so this is all there is to press */
.style-manager__saved {
  align-self: center;
  overflow: hidden;
  margin: 0 0 0 auto;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.style-manager__saved--failed {
  color: var(--color-danger);
}

.style-manager__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

/* ---- Selectors ---- */

.style-manager__selectors {
  display: grid;
  flex: 1;
  grid-template-columns: var(--style-manager-list-width) minmax(0, 1fr);
  min-height: 0;
}

/* ---- List ---- */

.style-manager__list {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-inline-end: 1px solid var(--color-border);
  background: var(--color-surface);
}

/* Search and buttons in one row while they fit; the buttons drop to a second row when not */
.style-manager__toolbar {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  gap: var(--space-xs);
  padding: var(--space-s) var(--space-s) var(--space-xs);
}

.style-manager__search {
  flex: 1 1 8rem;
  min-width: 0;
  height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--step-0);
}

.style-manager__search::placeholder {
  color: var(--color-text-subtle);
}

.style-manager__search:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

/* Two rows of filters, the kind and the state; the rows share the tinted background of the list */
.style-manager__filters {
  display: grid;
  flex: none;
  gap: var(--space-3xs);
  padding: var(--space-2xs) var(--space-s) var(--space-xs);
}

.style-manager__filter-group {
  display: flex;
  gap: var(--space-3xs);
}

/* Segmented filter: the pressed one is filled */
.style-manager__filter {
  flex: none;
  height: var(--size-control-s);
  white-space: nowrap;
  padding-inline: var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-weight: 600;
  cursor: pointer;
  transition:
    background-color var(--duration-fast),
    color var(--duration-fast);
}

.style-manager__filter:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.style-manager__filter[aria-pressed="true"] {
  border-color: var(--color-border-strong);
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.style-manager__items {
  flex: 1;
  min-height: 0;
  margin: 0;
  padding: 0 var(--space-xs) var(--space-xs);
  overflow-y: auto;
  list-style: none;
}

.style-manager__option {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  height: var(--size-control-m);
  padding-inline: var(--space-xs);
  border-radius: var(--radius-s);
  color: var(--color-text-muted);
  cursor: pointer;
  user-select: none;
}

.style-manager__option:hover {
  background: var(--color-surface-hover);
}

.style-manager__option:focus-visible {
  outline-offset: -2px;
}

/* Picked: tinted plus a bar on the leading edge, like the selected row in the structure */
.style-manager__option--selected,
.style-manager__option--selected:hover {
  padding-inline-start: calc(var(--space-xs) - 2px);
  border-inline-start: 2px solid var(--color-accent);
  background: var(--color-accent-muted);
  color: var(--color-text);
}

/* Same colour a selector has in the CSS editor */
.style-manager__selector {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--code-selector);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.style-manager__uses {
  flex: none;
  min-width: 1.5em;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.style-manager__uses--none {
  font-style: italic;
}

/* "no CSS" or a problem mark; the problem keeps its meaning without colour through the "!" */
.style-manager__flag {
  flex: none;
  padding-inline: var(--space-2xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  line-height: 1.4;
}

.style-manager__flag--problem {
  min-width: 1.4em;
  border-color: var(--color-danger);
  color: var(--color-danger);
  font-weight: 700;
  text-align: center;
}

.style-manager__empty {
  margin: auto var(--space-s);
  padding: var(--space-l) var(--space-s);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-m);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-align: center;
  text-wrap: balance;
}

/* The strip along the bottom of a pane: the list's summary on the left and the editor's
   problem line on the right are the same bar, so they share their geometry here. Sized
   apart they would sit at different heights and their top borders would not meet. */
.style-manager__summary,
.style-manager__footer {
  display: flex;
  flex: none;
  align-items: center;
  height: var(--size-control-m);
  padding-inline: var(--space-m);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

/* The height is fixed, so a long summary is cut off instead of wrapping out of the bar. */
.style-manager__summary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- Editor ---- */

.style-manager__editor {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

/* The name field on the left, the actions on the right */
.style-manager__editor-header {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-s);
  height: 48px;
  padding-inline: var(--space-m);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.style-manager__name-field {
  display: flex;
  flex: 1;
  align-items: center;
  min-width: 0;
  color: var(--code-selector);
  font-family: var(--font-code);
  font-size: var(--step-1);
}

/*
 * The kinds read apart by colour: classes in the selector yellow, ids in blue, element
 * presets in teal, each the colour the CSS editor gives the same kind of token.
 */
.style-manager__selector--id,
.style-manager__name-field--id {
  color: var(--code-storage);
}

.style-manager__selector--element,
.style-manager__name-field--element {
  color: var(--code-type);
  font-family: var(--font-ui);
}

/* The name is an input that looks like text until it is hovered or focused */
.style-manager__name {
  flex: 1;
  min-width: 0;
  height: var(--size-control-l);
  /* No start padding: the dot before the field must read as part of the selector */
  padding-inline: 0 var(--space-2xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: inherit;
  font: inherit;
}

.style-manager__name:hover {
  border-color: var(--color-border-strong);
}

.style-manager__name:focus-visible {
  border-color: var(--color-accent);
  background: var(--color-bg);
  outline-offset: -1px;
}

.style-manager__name--invalid,
.style-manager__name--invalid:focus-visible {
  border-color: var(--color-danger);
  outline-color: var(--color-danger);
}

/* Why the typed name is refused */
.style-manager__hint {
  flex: none;
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

.style-manager__actions {
  display: flex;
  flex: none;
  gap: var(--space-xs);
}

/* The elements that carry the class, as chips that jump to the element */
.style-manager__usage {
  display: flex;
  flex: none;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-xs) var(--space-m);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.style-manager__usage-label {
  margin-inline-end: var(--space-2xs);
}

.style-manager__element {
  height: var(--size-control-s);
  padding-inline: var(--space-xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font-size: var(--step--1);
  cursor: pointer;
  transition:
    background-color var(--duration-fast),
    border-color var(--duration-fast);
}

.style-manager__element:hover {
  border-color: var(--color-border-strong);
  background: var(--color-surface-hover);
}

/* The code editor positions itself absolutely inside */
.style-manager__code {
  position: relative;
  flex: 1;
  min-height: 0;
}

.style-manager__footer {
  gap: var(--space-m);
}

.style-manager__problem {
  min-width: 0;
  overflow: hidden;
  color: var(--color-danger);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* No class picked: one sentence in the middle of the editor area */
.style-manager__blank {
  display: grid;
  flex: 1;
  place-items: center;
  margin: 0;
  padding: var(--space-l);
  color: var(--color-text-subtle);
  font-size: var(--step-0);
  text-align: center;
  text-wrap: balance;
}

/* ---- Stylesheets ---- */

/* The stylesheet list has no search, so its one button sits at the end of the toolbar */
.style-manager__toolbar--end {
  justify-content: flex-end;
}

/* Stylesheet names are prose, not selectors */
.style-manager__sheet-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: var(--color-text);
  font-size: var(--step-0);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.style-manager__name-field--text {
  color: var(--color-text);
  font-family: var(--font-ui);
}

/* The base layer's name is fixed: the field stays a label, no border on hover */
.style-manager__name:read-only,
.style-manager__name:read-only:hover {
  border-color: transparent;
  cursor: default;
}

/* Move up and down (icon-buttons): the chevron points the way */
.style-manager__move-icon {
  rotate: -90deg;
}

.style-manager__move-icon--down {
  rotate: 90deg;
}

/* ---- Variables ---- */

.style-manager__variables {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

/* Hint on the left, the button on the right */
.style-manager__variables-header {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding: var(--space-s) var(--space-m);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.style-manager__variables-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* Table of the custom properties: name and value are inputs that look like text until focused */
.variables {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step-0);
}

.variables__head {
  position: sticky;
  top: 0;
  background: var(--color-surface);
}

.variables__heading {
  padding: var(--space-xs) var(--space-s);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-align: start;
}

.variables__row:hover {
  background: var(--color-surface-hover);
}

.variables__cell {
  padding: var(--space-3xs) var(--space-s);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

.variables__value {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.variables__cell--sheet {
  color: var(--color-text-muted);
  white-space: nowrap;
}

.variables__cell--uses {
  width: 6em;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.variables__cell--none {
  font-style: italic;
}

.variables__cell--actions {
  width: 0;
}

.variables__input {
  width: 100%;
  height: var(--size-control-m);
  padding-inline: var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.variables__input:hover {
  border-color: var(--color-border-strong);
}

.variables__input:focus-visible {
  border-color: var(--color-accent);
  background: var(--color-bg);
  outline-offset: -1px;
}

/* Same colour a variable has in the CSS editor */
.variables__input--name {
  color: var(--code-variable);
}

/* The colour a value stands for; the value is set by TypeScript, only when it is a colour */
.variables__swatch {
  --variables-swatch: transparent;

  flex: none;
  width: 16px;
  height: 16px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--variables-swatch);
}

/* Body of the confirmation dialog: the question, then Cancel and the action side by side */
.confirm {
  display: grid;
  gap: var(--space-m);
}

.confirm__message {
  margin: 0;
  color: var(--color-text);
  font-size: var(--step-0);
  text-wrap: pretty;
}

.confirm__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
}

/* The export dialog: what goes into the zip, and which pages that turns out to be */
.export {
  display: grid;
  gap: var(--space-xs);
  min-width: min(30rem, 84vw);
}

.export__scope {
  display: grid;
  gap: var(--space-2xs);
  padding: var(--space-xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
}

.export__legend,
.export__label {
  padding-inline: var(--space-2xs);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.export__check {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--step--1);
}

/* The list scrolls rather than pushing the buttons off a small screen */
.export__pages {
  display: grid;
  gap: var(--space-3xs);
  max-height: 14rem;
  overflow-y: auto;
  padding: var(--space-2xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  background: var(--color-bg);
}

.export__item {
  list-style: none;
}

.export__summary {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* What ticking a members-only page gives away, in the colour of a caution, not an error */
.export__warning {
  color: var(--color-pending);
  font-size: var(--step--1);
}

.export__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* The check before publishing: what the website would get that is worth a second look, per page */
.publish-check {
  display: grid;
  gap: var(--space-s);
}

.publish-check__intro {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  text-wrap: pretty;
}

/* The pages scroll rather than pushing the buttons off a small screen */
.publish-check__pages {
  display: grid;
  gap: var(--space-s);
  max-height: 22rem;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.publish-check__page {
  display: grid;
  gap: var(--space-2xs);
}

.publish-check__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs);
}

.publish-check__path {
  color: var(--code-selector);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.publish-check__items {
  display: grid;
  gap: var(--space-3xs);
  margin: 0;
  padding-inline-start: var(--space-m);
  color: var(--color-text);
  font-size: var(--step--1);
}

/* Notes sit below the problems and say less loudly what is worth knowing */
.publish-check__notes-title {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-weight: 600;
}

.publish-check__pages--notes {
  max-height: 10rem;
}

.publish-check__items--note {
  color: var(--color-text-muted);
}

.publish-check__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
}

/* The dialog for a new page and for page settings: title, slug, the collections of a template and the reasons something was refused */
.page-form {
  display: grid;
  /* The form scrolls once a template lists its collections; without this the grid squeezes
     the textarea, a scroll container whose automatic minimum height is zero */
  grid-auto-rows: max-content;
  gap: var(--space-xs);
  min-width: min(26rem, 80vw);
}

.page-form__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.page-form__input {
  height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--step-0);
}

/* A text of a few lines, like the description, grows downwards instead of scrolling one line */
.page-form__input--area {
  height: auto;
  padding-block: var(--space-2xs);
  resize: vertical;
  font-family: inherit;
  line-height: 1.4;
}

.page-form__input--code {
  color: var(--code-selector);
  font-family: var(--font-code);
}

.page-form__input::placeholder {
  color: var(--color-text-subtle);
}

/* The focus ring sits on the border, which turns accent at the same time */
.page-form__input:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.page-form__input--invalid,
.page-form__input--invalid:focus-visible {
  border-color: var(--color-danger);
  outline-color: var(--color-danger);
}

/* Label, input and hint that come and go together, like the slug a template does not have */
.page-form__group {
  display: grid;
  gap: var(--space-xs);
}

/* Where a template gets its addresses from, in place of the slug it does not have */
.page-form__note {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* A button inside a group, as wide as its words rather than the column */
.page-form__action {
  justify-self: start;
}

/* Why a slug or a description was not accepted */
.page-form__hint {
  color: var(--color-danger);
  font-size: var(--step--1);
}

/* A checkbox reads as one line with its words, unlike the stacked label and input above */
.page-form__check {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.page-form__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* The collections a template shows, one row each: name, address and a way out */
.page-form__rows {
  display: grid;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.page-form__row {
  display: grid;
  grid-template-columns: minmax(5rem, auto) 1fr auto;
  align-items: center;
  gap: var(--space-xs);
}

.page-form__row-name {
  color: var(--color-text);
  font-size: var(--step--1);
}

/* Why an address was not accepted, under the address it belongs to */
.page-form__row-hint {
  grid-column: 2 / -1;
}

/* A value to copy, a one-time link: the field and its Copy button on one line */
.page-form__copy {
  display: flex;
  gap: var(--space-xs);
}

/* The value to copy takes the row's whole width, so a long link shows in full. */
.page-form__copy > .page-form__input {
  flex: 1 1 auto;
  min-width: 0;
}

/* The Auto-BEM dialog: the block name, and every class it would add before anything changes */
.auto-bem {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.auto-bem__intro {
  margin: 0 0 var(--space-xs);
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.auto-bem__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.auto-bem__input {
  height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--code-selector);
  font-family: var(--font-code);
  font-size: var(--step-0);
}

.auto-bem__input:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.auto-bem__input--invalid,
.auto-bem__input--invalid:focus-visible {
  border-color: var(--color-danger);
  outline-color: var(--color-danger);
}

.auto-bem__hint {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

.auto-bem__summary {
  margin: var(--space-xs) 0 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.auto-bem__preview {
  max-height: 16rem;
  margin: 0;
  padding: var(--space-xs) var(--space-s);
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  list-style: none;
}

.auto-bem__entry {
  display: flex;
  justify-content: space-between;
  gap: var(--space-m);
  font-size: var(--step--1);
  line-height: var(--code-line-height);
}

.auto-bem__element {
  min-width: 0;
  overflow: hidden;
  color: var(--color-text-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.auto-bem__class {
  flex: none;
  color: var(--code-selector);
  font-family: var(--font-code);
}

.auto-bem__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* The uploads section: the table of uploads, and the whole section takes a drop of files. */
.assets {
  min-width: min(40rem, 80vw);
}

/* Says that a drop works anywhere here; lights up while files are dragged over the section */
.assets__drop {
  margin: 0;
  padding: var(--space-xs);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-m);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-align: center;
  transition: border-color var(--duration-fast), background-color var(--duration-fast);
}

.assets__drop--over {
  border-color: var(--color-accent);
  background: var(--color-surface-hover);
  color: var(--color-text);
}

/* The native file input stays for assistive technology; Upload in the toolbar opens it */
.assets__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.assets__status {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/*
 * Content: a section of the manage panel, the collections on the left and the entries or
 * fields of the picked one on the right. The entries table is the data-table block's.
 */
.content {
  --content-side-width: 280px;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--color-bg);
}

/* auto, not a fixed track: a hidden column takes no width at all (see content__side) */
.content__body {
  display: grid;
  flex: 1;
  grid-template-columns: auto minmax(0, 1fr);
  min-height: 0;
}

.content__side {
  display: flex;
  width: var(--content-side-width);
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-m);
  border-inline-end: 1px solid var(--color-border);
  overflow-y: auto;
}

.content__collections {
  display: grid;
  gap: var(--space-3xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.content__collection-button {
  display: grid;
  width: 100%;
  padding: var(--space-xs) var(--space-s);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.content__collection-button:hover {
  background: var(--color-surface-hover);
}

.content__collection-button[aria-current="true"] {
  border-color: var(--color-border-strong);
  background: var(--color-surface-raised);
}

.content__collection-slug {
  color: var(--color-text-subtle);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.content__main {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: var(--space-m) var(--space-l);
  overflow-y: auto;
}

.content__blank {
  margin: 0;
  color: var(--color-text-muted);
}

.content__main-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  margin-block-end: var(--space-m);
}

.content__main-title {
  margin: 0;
  font-size: var(--step-1);
  font-weight: 600;
}

/* The identifiers of a saved entry: three read-only fields in its folded Identifiers */
.content__identifiers {
  display: grid;
  gap: var(--space-s);
}

/* A read-only field with its copy button inside, at the end */
.content__readonly {
  position: relative;
}

.content__readonly-input {
  padding-inline-end: calc(var(--space-s) + 22px);
  border-color: var(--color-border);
  background: var(--color-surface-raised);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.content__readonly-copy {
  position: absolute;
  inset-block: 0;
  inset-inline-end: var(--space-2xs);
  margin-block: auto;
}

/* ---- Forms ---- */

.content__form {
  display: grid;
  gap: var(--space-s);
  width: min(40rem, 100%);
}

/* An image field: the thumbnail that opens the picker, the upload's name, Upload */
.content__image {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
}

.content__image-thumb {
  display: grid;
  flex: none;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  color: var(--color-text-subtle);
  cursor: pointer;
}

.content__image-thumb:hover {
  border-color: var(--color-accent);
}

.content__image-preview {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.content__image-icon {
  width: 20px;
  height: 20px;
}

.content__image-name {
  flex: 1;
  min-width: 0;
}

/* The file input behind Upload: the button opens it, so it takes no room */
.content__image-file {
  display: none;
}

/* An entry field: the slug with the list of the collection's entries, the title below */
.content__reference-box {
  display: grid;
  gap: var(--space-3xs);
}

.content__reference {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
}

.content__reference-input {
  flex: 1;
  min-width: 0;
}

/* The form's title with "View on website" at its end */
.content__form-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
}

.content__form-title {
  margin: 0 0 var(--space-2xs);
  font-size: var(--step-0);
  font-weight: 600;
}

/* The identifiers of a saved entry, folded away below its values */
.content__more {
  display: grid;
  gap: var(--space-s);
  padding-block-start: var(--space-xs);
  border-top: 1px solid var(--color-border);
}

.content__more[open] {
  padding-block-end: var(--space-xs);
}

.content__more-summary {
  width: fit-content;
  border-radius: var(--radius-s);
  color: var(--color-text-muted);
  font-size: var(--step--1);
  cursor: pointer;
}

.content__more[open] > .content__more-summary {
  margin-block-end: var(--space-s);
}

.content__control {
  display: grid;
  gap: var(--space-3xs);
}

/* An action under a field, "Give Termine pages": as wide as its words, not as the field */
.content__control-action {
  justify-self: start;
  margin-top: var(--space-2xs);
}

.content__control--inline {
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--space-xs);
}

.content__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.content__input,
.content__textarea {
  width: 100%;
  min-width: 0;
  padding: var(--space-2xs) var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--color-text);
  font: inherit;
  font-size: var(--step-0);
}

.content__input {
  height: var(--size-control-l);
}

.content__input--code {
  font-family: var(--font-code);
  font-size: var(--step--1);
}

.content__textarea {
  resize: vertical;
}

/* The focus ring sits on the border, which turns accent at the same time */
.content__input:focus-visible,
.content__textarea:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

.content__checkbox {
  width: var(--space-m);
  height: var(--space-m);
  accent-color: var(--color-accent);
}

.content__fields {
  display: grid;
  gap: var(--space-2xs);
}

/* Label, key, type and the remove button of one field in a row */
.content__field {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 2fr) minmax(0, 2fr) auto;
  align-items: center;
  gap: var(--space-2xs);
}

/* An entry field's collection, on a line of its own under the field's type */
.content__field-collection {
  grid-column: 3 / 4;
}

.content__hint {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

/* What the entries of a collection are ordered by, on the website */
.content__order {
  display: grid;
  gap: var(--space-2xs);
}

.content__order-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-2xs);
}

/* Whether the entries are pages of their own: the template and the address they get */
.content__detail {
  display: grid;
  gap: var(--space-2xs);
}

.content__hint-text {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* A slug no entry has, below an entry field */
.content__hint-text--problem {
  color: var(--color-danger);
}

.content__actions {
  display: flex;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* The editor's sidebar: the page's texts and pictures as fields, grouped per element */
.content-fields {
  display: grid;
  gap: var(--space-s);
  padding: var(--space-s);
}

.content-fields__empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Where the entries behind a list are edited, below the fields of its part of the page */
.content-fields__note {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.content-fields__link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* The table's Columns button: icon and word, at the end of the toolbar */
.content__columns-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
}

.content__columns-icon {
  width: 16px;
  height: 16px;
}

/* Under a collection's name: its slug, and how many entries it has at the end */
.content__collection-meta {
  display: flex;
  justify-content: space-between;
  gap: var(--space-xs);
}

.content__collection-count {
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
}

/* The choice of an entry table's columns: one row per column, a tick and two moves */
.columns {
  display: grid;
  gap: var(--space-xs);
}

.columns__intro,
.columns__note {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* The list scrolls rather than pushing the buttons off a small screen */
.columns__list {
  display: grid;
  gap: var(--space-3xs);
  max-height: 22rem;
  margin: 0;
  padding: var(--space-2xs);
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-s);
  background: var(--color-bg);
}

.columns__item {
  display: flex;
  align-items: center;
  gap: var(--space-3xs);
  list-style: none;
}

.columns__check {
  display: flex;
  flex: 1;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--step--1);
}

.columns__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* Reset stands apart from the pair that closes the dialog */
.columns__reset {
  margin-inline-end: auto;
}

/* Widgets dialog: the ready-made elements as cards, each with the line that says what it does */
.widgets {
  display: grid;
  gap: var(--space-m);
}

.widgets__intro {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.widgets__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-xs);
}

.widgets__item {
  display: flex;
  align-items: start;
  gap: var(--space-s);
  padding: var(--space-s);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.widgets__item:hover:not(:disabled) {
  border-color: var(--color-border-strong);
  background: var(--color-surface-hover);
}

.widgets__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.widgets__icon {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--color-accent);
}

.widgets__text {
  display: grid;
  gap: var(--space-3xs);
  min-width: 0;
}

.widgets__label {
  font-size: var(--step-0);
  font-weight: 600;
}

.widgets__description {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Components dialog: the site's components as cards, inserted with one click */
.components {
  display: grid;
  gap: var(--space-m);
}

.components__intro,
.components__empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.components__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-xs);
}

.components__item {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-s);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.components__item:hover:not(:disabled) {
  border-color: var(--color-component);
  background: var(--color-surface-hover);
}

.components__item:focus-visible {
  outline: 2px solid var(--color-component);
  outline-offset: 2px;
}

.components__item:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.components__icon {
  flex: none;
  width: 20px;
  height: 20px;
  color: var(--color-component);
}

.components__text {
  display: grid;
  gap: var(--space-3xs);
  min-width: 0;
}

.components__label {
  overflow: hidden;
  font-size: var(--step-0);
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.components__tag {
  color: var(--color-text-muted);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

/* Image picker: the website's pictures as a grid of thumbnails, one picked for an image field */
.image-picker {
  display: grid;
  gap: var(--space-s);
}

.image-picker__empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.image-picker__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  gap: var(--space-xs);
  max-height: 60vh;
  overflow-y: auto;
}

.image-picker__item {
  display: grid;
  gap: var(--space-3xs);
  padding: var(--space-2xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  color: var(--color-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.image-picker__item:hover {
  border-color: var(--color-accent);
  background: var(--color-surface-hover);
}

/* The picture the field holds now */
.image-picker__item[aria-pressed="true"] {
  border-color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

.image-picker__thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-s);
  background: var(--color-surface);
  object-fit: cover;
}

.image-picker__name {
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * Manage panel: everything about the website that is not the page in the canvas. Covers
 * canvas, code editor and sidebar (the taskbar stays), a nav of the website's parts on the
 * left and the picked section on the right. Where it sits in the app grid is app.css's
 * business.
 */
.manage {
  --manage-nav-width: 200px;

  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--color-bg);
}

/* Back button, title and the line that says what the section is, in one row */
.manage__header {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-m);
  height: var(--panel-header-height);
  padding-inline: var(--space-s) var(--space-m);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.manage__title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.manage__intro {
  margin: 0;
  overflow: hidden;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.manage__body {
  display: grid;
  flex: 1;
  grid-template-columns: var(--manage-nav-width) minmax(0, 1fr);
  min-height: 0;
}

/* ---- The nav: categories, each a vertical tablist over its sections ---- */

.manage__nav {
  padding: var(--space-s) var(--space-s) var(--space-l);
  border-inline-end: 1px solid var(--color-border);
  overflow-y: auto;
}

.manage__group-title {
  margin: 0 0 var(--space-2xs);
  padding-inline: var(--space-s);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.manage__group {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

/* A rule above every category but the first: the groups read as blocks, not as one list */
.manage__group + .manage__group-title {
  margin-top: var(--space-m);
  padding-top: var(--space-m);
  border-top: 1px solid var(--color-border);
}

.manage__nav-button {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  width: 100%;
  min-height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 0;
  border-radius: var(--radius-s);
  background: none;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--step-0);
  text-align: start;
  cursor: pointer;
}

.manage__nav-button:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.manage__nav-button--active,
.manage__nav-button--active:hover {
  background: var(--color-accent-muted);
  color: var(--color-text);
  font-weight: 600;
}

/* A link after a group's tabs leads to another place in a new tab; it looks like a tab and is never active. */
.manage__nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  width: 100%;
  min-height: var(--size-control-l);
  padding-inline: var(--space-s);
  border-radius: var(--radius-s);
  color: var(--color-text-muted);
  text-decoration: none;
  font-size: var(--step-0);
}

.manage__nav-link:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.manage__nav-icon {
  width: 14px;
  height: 14px;
  margin-inline-start: auto;
  flex: none;
}

.manage__nav-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* How many wait for a decision, for now only the comments */
.manage__badge {
  flex: none;
  min-width: 18px;
  padding-inline: var(--space-2xs);
  border-radius: 9px;
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: var(--step--1);
  font-weight: 600;
  text-align: center;
}

/* ---- The section on the right ---- */

.manage__main {
  display: grid;
  min-width: 0;
  min-height: 0;
}

.manage__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-m);
  grid-area: 1 / 1;
  padding: var(--space-l);
  overflow-y: auto;
}

/* A section that brings its own layout and scrolling: the style manager, the content panel */
.manage__section--flush {
  gap: 0;
  padding: 0;
  overflow: hidden;
}

.manage__toolbar {
  display: flex;
  flex: none;
  gap: var(--space-xs);
}

.manage__empty {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.manage__status {
  margin: 0;
  min-height: 1lh;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* ---- Forms inside a section ---- */

.manage__form {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--space-xs);
  margin: 0;
  max-width: 560px;
}

.manage__form--row {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  max-width: none;
}

.manage__label {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.manage__input {
  width: 100%;
  max-width: 320px;
  height: var(--size-control-m);
  padding-inline: var(--space-xs);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--step-0);
}

.manage__input--short {
  max-width: 160px;
}

.manage__input--lines {
  height: auto;
  padding-block: var(--space-2xs);
  resize: vertical;
}

.manage__input:disabled {
  color: var(--color-text-muted);
  background: var(--color-surface-raised);
}

.manage__fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: var(--space-s);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
}

.manage__legend {
  padding-inline: var(--space-2xs);
  font-size: var(--step--1);
  font-weight: 600;
}

.manage__hint {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* A part of a section with a name of its own, below the section's own heading */
.manage__subheading {
  margin: 0;
  color: var(--color-text);
  font-size: var(--step-0);
  font-weight: 600;
}

.manage__url {
  color: var(--color-accent);
  font-size: var(--step-0);
  word-break: break-all;
}

/* The one-time setup: shown above the form until the website has its domain. */
.manage__notice {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  max-width: 560px;
  padding: var(--space-s);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-m);
  background: var(--color-surface);
}

.manage__notice-title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.manage__notice-text {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* The console of elpresso Machine: the manage panel as the whole page instead of an overlay. */

/* Nothing shows until the login is known, so the console never flashes before a redirect. */
.console--starting {
  visibility: hidden;
}

.console {
  display: grid;
  height: 100dvh;
}

/* .manage sizes itself to its parent, which the app grid gives it in the builder. */
.console__panel {
  min-height: 0;
}

/* One number of the machine at a glance: its label, the value, what it is out of, and a sparkline of the chosen range. */
.stat-tile {
  --stat-tile-share: 0;

  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-width: 0;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface);
}

.stat-tile__label {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Proportional figures: a lone large number looks loose in tabular ones */
.stat-tile__value {
  margin: 0;
  overflow: hidden;
  color: var(--color-text);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stat-tile__detail {
  margin: 0;
  overflow: hidden;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* What asks to be noticed, a disk full in a few days; the pending colour on the surface reads at 8.5:1 */
.stat-tile__note {
  margin: 0;
  color: var(--color-pending);
  font-size: var(--step--1);
}

/* A share of a limit; the track is the hairline colour, so the fill reads across the whole bar */
.stat-tile__meter {
  height: 4px;
  margin-block: var(--space-3xs);
  overflow: hidden;
  border-radius: 2px;
  background: var(--color-border);
}

.stat-tile__meter-fill {
  display: block;
  width: calc(var(--stat-tile-share) * 100%);
  height: 100%;
  border-radius: inherit;
  background: var(--color-accent);
}

/* Three quarters used is worth a look, nine tenths worth acting on; the percentage says it in words */
.stat-tile__meter-fill--high {
  background: var(--color-pending);
}

.stat-tile__meter-fill--full {
  background: var(--color-danger);
}

/* Stretched to the tile's width; the stroke keeps its thickness however wide that is */
.stat-tile__spark {
  display: block;
  width: 100%;
  height: 32px;
  margin-top: auto;
  overflow: visible;
}

.stat-tile__spark-base {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.stat-tile__spark-line {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* The machine's overview around its stat tiles: the range of the sparklines, the grid of tiles and the facts of the host. */
.overview__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
}

/* Two choices in one box, like the filter chips of a list, but one is always checked */
.overview__range {
  display: inline-flex;
  flex: none;
  gap: var(--space-3xs);
  padding: var(--space-3xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-workspace);
}

.overview__range-option {
  height: var(--size-control-s);
  padding-inline: var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--step--1);
  font-weight: 500;
  cursor: pointer;
}

.overview__range-option:hover {
  color: var(--color-text);
}

.overview__range-option[aria-checked="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.overview__range-option:focus-visible {
  outline-offset: -1px;
}

.overview__sampled {
  margin: 0;
  margin-inline-start: auto;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

.overview__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-s);
}

/* One card per part of the server, as many in a row as fit */
.overview__cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--space-s);
}

/* Two charts side by side where they still read, one under the other where not */
.overview__charts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: var(--space-s);
}

.overview__host {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs) var(--space-l);
  margin: 0;
  font-size: var(--step--1);
}

.overview__fact {
  display: flex;
  gap: var(--space-2xs);
}

.overview__term {
  color: var(--color-text-subtle);
}

.overview__detail {
  margin: 0;
  color: var(--color-text);
}

/* A service's journal: monospace lines that scroll inside the section, so the controls above them stay in view. */
.log-view {
  flex: 1;
  min-height: 0;
  overflow: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-workspace);
}

.log-view:focus-visible {
  outline-offset: -2px;
}

.log-view__text {
  margin: 0;
  padding: var(--space-s);
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: var(--code-font-size);
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* One line per element, so an error can carry its colour */
.log-view__line {
  display: block;
}

/* Priority err and worse; the "!" before the message says it without the colour */
.log-view__line--error {
  color: var(--color-danger);
}

/* What an action of the Machine console printed, in a dialog that grows while it runs: the log scrolls, the lines above it stay in view. */
.job-output {
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  /* Tall enough to read apt-get, short enough that the dialog never leaves the window */
  height: min(36rem, calc(100dvh - 8rem));
}

.job-output__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* What went wrong, over the output that shows where */
.job-output__message {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
  overflow-wrap: anywhere;
}

.job-output__log {
  min-height: 0;
}

/* One part of the server at a glance on the Machine overview, a button that opens its section; the edge's colour repeats what the words say. */
.summary-card {
  display: grid;
  gap: var(--space-3xs);
  min-width: 0;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--color-border);
  border-inline-start-width: 4px;
  border-radius: var(--radius-m);
  background: var(--color-surface);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.summary-card:hover {
  background: var(--color-surface-hover);
}

.summary-card:focus-visible {
  outline-offset: 2px;
}

.summary-card__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.summary-card__value {
  color: var(--color-text);
  font-size: var(--step-1);
  font-weight: 600;
}

.summary-card__detail {
  overflow: hidden;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.summary-card--ok {
  border-inline-start-color: var(--color-live);
}

.summary-card--warning {
  border-inline-start-color: var(--color-pending);
}

.summary-card--critical {
  border-inline-start-color: var(--color-danger);
}

/* A line chart of the Machine console: one SVG scaled to the card's width, a hairline grid, thin lines, and a readout beside the crosshair. */
.chart {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  min-width: 0;
  margin: 0;
  padding: var(--space-s) var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface);
}

.chart__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2xs) var(--space-s);
}

.chart__title {
  margin: 0;
  color: var(--color-text);
  font-size: var(--step-0);
  font-weight: 600;
}

.chart__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin: 0;
  padding: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  list-style: none;
}

.chart__legend-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
}

/* A short stroke of the line's colour, never a filled box: it keys a line */
.chart__key {
  flex: none;
  width: 12px;
  height: 2px;
  border-radius: 1px;
}

.chart__key--1 {
  background: var(--color-chart-1);
}

.chart__key--2 {
  background: var(--color-chart-2);
}

/* The plot takes the focus for the arrow keys; the ring sits inside, clear of the next card */
.chart__plot {
  --chart-tip-x: 1;

  position: relative;
  border-radius: var(--radius-s);
}

.chart__plot:focus-visible {
  outline-offset: 2px;
}

.chart__svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

.chart__gridline {
  stroke: var(--color-border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* Tabular figures, so the numbers of one axis line up */
.chart__label {
  fill: var(--color-text-subtle);
  font-family: var(--font-ui);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.chart__label--y {
  text-anchor: end;
  dominant-baseline: middle;
}

.chart__label--x {
  text-anchor: middle;
}

.chart__area {
  fill: var(--color-chart-1-wash);
}

.chart__line {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.chart__line--1 {
  stroke: var(--color-chart-1);
}

.chart__line--2 {
  stroke: var(--color-chart-2);
}

.chart__cursor {
  stroke: var(--color-border-strong);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

/* A ring of the surface around each dot keeps it clear where it crosses a line */
.chart__dot {
  stroke: var(--color-surface);
  stroke-width: 2;
}

.chart__dot--1 {
  fill: var(--color-chart-1);
}

.chart__dot--2 {
  fill: var(--color-chart-2);
}

/* Beside the crosshair, on the side with more room; it never takes the pointer */
.chart__tip {
  position: absolute;
  top: 0;
  left: calc(var(--chart-tip-x) * 100%);
  translate: var(--space-s) 0;
  display: grid;
  gap: var(--space-3xs);
  min-width: max-content;
  padding: var(--space-xs) var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-raised);
  pointer-events: none;
}

.chart__tip--left {
  translate: calc(-100% - var(--space-s)) 0;
}

.chart__tip-time {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.chart__tip-row {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0;
  font-size: var(--step--1);
}

/* The value leads, the line's name follows */
.chart__tip-value {
  color: var(--color-text);
  font-weight: 600;
}

.chart__tip-label {
  color: var(--color-text-muted);
}

/* The backup settings of the Machine console: a switch, the time, the days and how many copies are kept, each with its problem under it. */
.backup-form {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--space-s);
  max-width: 640px;
  margin: 0;
  padding: var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
}

.backup-form__title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.backup-form__field {
  display: grid;
  gap: var(--space-2xs);
}

.backup-form__label {
  padding: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.backup-form__group {
  display: grid;
  gap: var(--space-xs);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.backup-form__days {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-s);
}

.backup-form__keep {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
}

.backup-form__number {
  max-width: 96px;
}

/* A box and its words, the whole of it the click target */
.backup-form__check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--color-text);
  cursor: pointer;
}

.backup-form__box {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}

.backup-form__box:focus-visible {
  outline-offset: 2px;
}

.backup-form__problem {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

.backup-form__problem:empty {
  display: none;
}

.backup-form__hint {
  margin: 0;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

.backup-form__actions {
  display: flex;
  gap: var(--space-xs);
}

/* One part of the server's protection in the Machine console: what it is, how it stands, its settings and their save; the pending one asks to be confirmed. */
.guard-card {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: var(--space-s);
  padding: var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
}

/* A change of the firewall that waits for its confirmation, before anything else on the page */
.guard-card--pending {
  border-color: var(--color-pending);
  border-inline-start-width: 4px;
}

.guard-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}

.guard-card__title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.guard-card__line {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Tabular figures, so the seconds of the countdown do not make the line jump */
.guard-card__countdown {
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.guard-card__fields {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-s);
}

.guard-card__field {
  display: grid;
  gap: var(--space-2xs);
}

.guard-card__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.guard-card__input {
  max-width: 220px;
}

.guard-card__input--number {
  max-width: 96px;
}

/* A box and its words, the whole of it the click target */
.guard-card__check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  color: var(--color-text);
  cursor: pointer;
}

.guard-card__box {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-accent);
}

.guard-card__box:focus-visible {
  outline-offset: 2px;
}

.guard-card__problem {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

.guard-card__problem:empty {
  display: none;
}

/* A table inside a card takes the card's width */
.guard-card__table {
  align-self: stretch;
}

.guard-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

/* The part's switch stands at the end of its head, across from the name. */
.guard-card__switch {
  margin-inline-start: auto;
}

/* Terms and what they say, one under the other: a security check's why, its fix and where its setting lives. */
.fact-list {
  display: grid;
  gap: var(--space-s);
  margin: 0;
}

.fact-list__item {
  display: grid;
  gap: var(--space-3xs);
}

.fact-list__term {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.fact-list__detail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin: 0;
  color: var(--color-text);
  overflow-wrap: anywhere;
}

/* The updates box of the Machine console: what Barista runs, the newest build and the button that installs it. */
.update-box {
  display: grid;
  gap: var(--space-xs);
  padding: var(--space-m);
  margin-block-end: var(--space-m);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
}

.update-box__title {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
}

.update-box__line {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.update-box__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-block-start: var(--space-xs);
}

/* Every list of the manage panel as one table: a toolbar, sortable headings, rows with their icon actions. */
.data-table {
  --data-table-menu-top: 0;
  --data-table-menu-left: 0;

  display: grid;
  gap: var(--space-s);
  min-width: 0;
}

/* New on the left, then search and filters, the count, and what a section adds at the end */
.data-table__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
}

.data-table__hint {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.data-table__search {
  position: relative;
  flex: 1 1 12rem;
  max-width: 20rem;
}

.data-table__search-icon {
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--space-xs);
  width: 16px;
  height: 16px;
  margin-block: auto;
  color: var(--color-text-subtle);
  pointer-events: none;
}

.data-table__search-input {
  width: 100%;
  height: var(--size-control-l);
  padding-inline: calc(var(--space-xs) + 16px + var(--space-2xs)) var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-workspace);
  color: var(--color-text);
  font: inherit;
  font-size: var(--step-0);
}

.data-table__search-input:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

/* A segmented group of filters: the pressed one is filled, each with how many it keeps */
.data-table__filters {
  display: inline-flex;
  flex: none;
  gap: var(--space-3xs);
  padding: var(--space-3xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-workspace);
}

.data-table__filter {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  height: var(--size-control-s);
  padding-inline: var(--space-xs);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--step--1);
  font-weight: 500;
  cursor: pointer;
}

.data-table__filter:hover {
  color: var(--color-text);
}

.data-table__filter[aria-pressed="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.data-table__filter:focus-visible {
  outline-offset: -1px;
}

.data-table__filter-count {
  color: var(--color-text-subtle);
  font-variant-numeric: tabular-nums;
}

/* The count ends the free space, so what a section adds sits at the far end */
.data-table__count {
  margin-inline-start: auto;
  color: var(--color-text-muted);
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Many columns scroll sideways instead of squeezing every value into a narrow stack */
.data-table__scroll {
  overflow-x: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface);
}

.data-table__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step-0);
}

/* The heading row on the raised surface, set apart from the section behind and the rows below */
.data-table__head {
  padding: var(--space-xs) var(--space-s);
  border-bottom: 1px solid var(--color-border-strong);
  background: var(--color-surface-raised);
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-align: start;
  text-transform: uppercase;
  white-space: nowrap;
}

/* A heading that sorts the table: the whole label is the button */
.data-table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.data-table__sort:hover {
  color: var(--color-text);
}

.data-table__sort-mark {
  color: var(--color-accent);
}

.data-table__row:hover > .data-table__cell {
  background: var(--color-surface-hover);
}

.data-table__row:last-child > .data-table__cell {
  border-bottom: 0;
}

.data-table__cell {
  padding: var(--space-xs) var(--space-s);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

/* The row the builder has open: a line at its start in the accent */
.data-table__row--current > .data-table__cell:first-child {
  box-shadow: inset 3px 0 0 var(--color-accent);
}

/* One line, cut short with an ellipsis, so a row keeps its height however many columns show */
.data-table__cell--clip {
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Numbers as narrow as their digits, lined up at the end */
.data-table__head--number,
.data-table__cell--number {
  width: 1%;
  font-variant-numeric: tabular-nums;
  text-align: end;
  white-space: nowrap;
}

.data-table__cell--number {
  color: var(--color-text-muted);
}

.data-table__cell--code {
  color: var(--color-text-muted);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

/* As narrow as what it holds: a date, a badge, a thumbnail */
.data-table__head--shrink,
.data-table__cell--shrink {
  width: 1%;
  white-space: nowrap;
}

.data-table__cell--actions {
  width: 1%;
  white-space: nowrap;
}

.data-table__cell--actions > .row-actions {
  justify-content: flex-end;
}

/*
 * The column a row is read by stays at the start while the others scroll past, so a value
 * far to the right still says whose it is. Opaque, or the scrolled columns would show through.
 */
.data-table__head--sticky,
.data-table__cell--sticky {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
}

.data-table__cell--sticky {
  max-width: 22rem;
  background: var(--color-surface);
}

/* The dividing line only while the table scrolls: without columns passing beneath, it marks nothing */
.data-table__scroll--overflowing .data-table__head--sticky,
.data-table__scroll--overflowing .data-table__cell--sticky {
  box-shadow: inset -1px 0 0 var(--color-border);
}

/* A name with its badges on one line and its address or kind below */
.data-table__name {
  display: grid;
  gap: var(--space-3xs);
  min-width: 0;
}

.data-table__name-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs) var(--space-xs);
}

.data-table__name-text {
  color: var(--color-text);
  font-weight: 600;
}

/* The name as the row's main action: it does what the pencil does */
.data-table__name-button {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text);
  font: inherit;
  font-weight: 600;
  text-align: start;
  cursor: pointer;
}

.data-table__name-button:hover {
  color: var(--color-accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.data-table__sub {
  overflow: hidden;
  color: var(--color-text-muted);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A placeholder of an address pattern: filled per entry, in the colour of templates */
.data-table__token {
  color: var(--color-template);
}

/* A fact in a cell, quieter than the name: "13 pages", "No frame" */
.data-table__meta {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Longer text, a comment, cut after two lines; the whole text opens on demand */
.data-table__text {
  display: -webkit-box;
  max-width: 40rem;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  white-space: pre-line;
}

.data-table__thumb {
  display: grid;
  place-items: center;
  width: 44px;
  height: 32px;
  overflow: hidden;
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--color-text-subtle);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-transform: uppercase;
}

.data-table__thumb-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.data-table__empty {
  margin: 0;
  padding: var(--space-l);
  color: var(--color-text-muted);
  text-align: center;
}

.data-table__pager {
  display: flex;
  align-items: center;
  gap: var(--space-m);
}

.data-table__pager-label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* Table or grid, at the end of the toolbar */
.data-table__views {
  display: inline-flex;
  flex: none;
  gap: var(--space-3xs);
}

.data-table__views > [aria-pressed="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.data-table__checkbox {
  width: var(--space-m);
  height: var(--space-m);
  margin: 0;
  accent-color: var(--color-accent);
  cursor: pointer;
}

.data-table__row--selected > .data-table__cell {
  background: var(--color-accent-muted);
}

/* What is selected and what can be done to it, between the toolbar and the rows */
.data-table__selection {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-s);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-m);
  background: var(--color-accent-muted);
}

.data-table__selection-count {
  margin-inline-end: auto;
  color: var(--color-text);
  font-size: var(--step--1);
  font-weight: 600;
}

/* Cards for rows best told apart by eye, as many as fit in a row */
.data-table__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-s);
}

.data-table__card {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--space-2xs);
  padding-block-end: var(--space-2xs);
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-m);
  background: var(--color-surface);
}

.data-table__card:hover {
  border-color: var(--color-border-strong);
}

.data-table__card--selected {
  border-color: var(--color-accent);
  background: var(--color-accent-muted);
}

.data-table__card > .row-actions {
  justify-self: end;
  padding-inline: var(--space-2xs);
}

/* The checkbox stands on the picture, where it is found without reading */
.data-table__card-check {
  position: absolute;
  inset-block-start: var(--space-xs);
  inset-inline-start: var(--space-xs);
  z-index: 1;
}

/* Hidden overflow keeps the 4:3: without it a tall picture stretches the box to its own height */
.data-table__card-media {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--color-bg);
  color: var(--color-text-subtle);
  font-family: var(--font-code);
  font-size: var(--step--1);
  text-transform: uppercase;
}

.data-table__card-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.data-table__card-text {
  display: grid;
  gap: var(--space-3xs);
  min-width: 0;
  padding-inline: var(--space-xs);
}

.data-table__card-body {
  display: grid;
  gap: var(--space-xs);
  min-width: 0;
}

/* A badge in a card is as wide as its words, not the card */
.data-table__card-text > .badge {
  justify-self: start;
}

/* A name with its checkbox in front, as in the entries table */
.data-table__pick {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  min-width: 0;
}

.data-table__head-check {
  margin-inline-end: var(--space-s);
  vertical-align: middle;
}

/* The Columns button: icon and word, like the one of the entries table */
.data-table__columns {
  flex: none;
}

.data-table__columns-button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
}

.data-table__columns-icon {
  width: 16px;
  height: 16px;
}

/* The Columns menu, in the top layer under its button; data-table.ts places it */
.data-table__menu {
  position: fixed;
  inset: var(--data-table-menu-top) auto auto var(--data-table-menu-left);
  min-width: 12rem;
  margin: 0;
  padding: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-m);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-raised);
  color: var(--color-text);
}

.data-table__menu-group {
  display: grid;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  border: 0;
}

.data-table__menu-title {
  margin-block-end: var(--space-2xs);
  padding: 0;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.data-table__menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--step-0);
  cursor: pointer;
}

/* A comment's whole text, once opened */
.data-table__text--full {
  display: block;
  -webkit-line-clamp: none;
}

/* Opens or closes a long text in place */
.data-table__more {
  justify-self: start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-accent);
  font: inherit;
  font-size: var(--step--1);
  cursor: pointer;
}

.data-table__more:hover {
  text-decoration: underline;
}

/* Every address of the website in one table; a collection's pattern is indented under the page above it by --routes-depth. */
.routes {
  --routes-depth: 0;
  --routes-indent: 20px;
}

/* The table is the data-table block's; it keeps the addresses whole rather than squeezing them */
.routes__table {
  min-width: 720px;
}

/* The row of notes and addresses under a route belongs to it, so no line between them */
.routes__row:has(+ .routes__details:not([hidden])) .routes__cell {
  border-bottom-color: transparent;
}

.routes__where {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  padding-inline-start: calc(var(--routes-depth) * var(--routes-indent));
  white-space: nowrap;
}

/* A route without a toggle keeps its address in line with those that have one */
.routes__where:not(:has(.routes__toggle)) {
  padding-inline-start: calc(var(--routes-depth) * var(--routes-indent) + var(--size-control-s) + var(--space-2xs));
}

.routes__toggle {
  display: grid;
  flex: none;
  place-items: center;
  width: var(--size-control-s);
  height: var(--size-control-s);
  border: 0;
  border-radius: var(--radius-s);
  background: none;
  color: var(--color-text-muted);
  cursor: pointer;
}

.routes__toggle:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.routes__chevron {
  transition: rotate var(--duration-fast);
}

.routes__toggle[aria-expanded="true"] .routes__chevron {
  rotate: 90deg;
}

.routes__address {
  color: var(--color-text);
  font-family: var(--font-code);
  font-size: var(--step--1);
}

/* A placeholder of a collection's pattern: filled per entry, in the colour of templates */
.routes__token {
  color: var(--color-template);
}

.routes__shows {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs);
}

.routes__name {
  color: var(--color-text);
  font-weight: 600;
}

.routes__kind {
  color: var(--color-text-subtle);
  font-size: var(--step--1);
}

.routes__none-mark {
  color: var(--color-text-subtle);
}

/* Lined up under the address: past the indent and the toggle */
.routes__more {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding-inline-start: calc(var(--routes-depth) * var(--routes-indent) + var(--size-control-s) + var(--space-2xs));
}

.routes__note {
  margin: 0;
  font-size: var(--step--1);
}

.routes__note--problem {
  color: var(--color-danger);
}

.routes__note--warning {
  color: var(--color-condition);
}

.routes__note--info {
  color: var(--color-text-muted);
}

.routes__examples {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.routes__example {
  color: var(--color-text-muted);
  font-family: var(--font-code);
  font-size: var(--step--1);
  user-select: text;
}

.routes__example--more {
  color: var(--color-text-subtle);
  font-family: var(--font-ui);
}

.routes__none {
  display: flex;
  flex-direction: column;
  flex: none;
  gap: var(--space-2xs);
}

.routes__title {
  margin: 0;
  color: var(--color-text-subtle);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.routes__intro {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.routes__none-list {
  display: flex;
  flex-direction: column;
  margin: var(--space-xs) 0 0;
  padding: 0;
  list-style: none;
}

.routes__none-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-s);
  padding: var(--space-xs);
  border-bottom: 1px solid var(--color-border);
}

.routes__none-text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs);
}

.routes__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: none;
  gap: var(--space-s);
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

/* The drafts among a collection's entries, beside its count of pages, in the colour of drafts */
.routes__drafts {
  color: var(--color-draft);
}

/* The list under a collection's route: its search, a page of addresses, and the pager */
.routes__addresses {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  max-width: 48rem;
}

/* The toolbar's search grows along its row; in this column it keeps its own height */
.routes__find {
  flex: none;
  width: 100%;
  max-width: 22rem;
}

/* A step of the pager, as small as the arrow it shows */
.routes__step {
  height: var(--size-control-m);
  min-width: var(--size-control-m);
  padding-inline: var(--space-xs);
  font-size: var(--step-0);
}

/* Login and registration: one card in the middle of the page */
.auth {
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: var(--space-l);
}

.auth__card {
  display: grid;
  gap: var(--space-xs);
  width: min(24rem, 100%);
  padding: var(--space-xl);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-l);
  background: var(--color-surface);
}

.auth__title {
  margin: 0;
  font-size: var(--step-2);
  font-weight: 600;
}

.auth__intro {
  margin: 0 0 var(--space-m);
  color: var(--color-text-muted);
}

.auth__label {
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.auth__input {
  height: var(--size-control-l);
  padding-inline: var(--space-s);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-s);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--step-0);
}

/* The focus ring sits on the border, which turns accent at the same time */
.auth__input:focus-visible {
  border-color: var(--color-accent);
  outline-offset: -1px;
}

/* The password rule, shown where a password is chosen */
.auth__hint {
  margin: 0;
  color: var(--color-text-muted);
  font-size: var(--step--1);
}

.auth__error {
  margin: 0;
  color: var(--color-danger);
  font-size: var(--step--1);
}

.auth__actions {
  display: flex;
  justify-content: space-between;
  gap: var(--space-xs);
  margin-block-start: var(--space-m);
}
