/* Image gallery. Layout matches the login and customize pages: a
   dark header bar at the top with the page title, a scrollable main
   area for the image tiles, an optional status row just above the
   bottom toolbar, and a bottom toolbar with the same look/feel as the
   login page (dev action on the left, primary actions on the right). */

h1,
h2,
h3,
h4,
h5,
h6,
p,
input {
  font-family: var(--editor-font-family, Arial, Helvetica, sans-serif);
}

/* Outer page: a flex column that fills the viewport so the header
   stays pinned at the top and the gallery tiles scroll inside the
   main area. */
#image-gallery-div.image-gallery-div {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  background-color: var(--gallery-content-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  text-align: left;
  overflow: hidden;
}

/* Top header bar — same dimensions/style as .customize-header. */
.image-gallery-heading-div {
  display: flex;
  align-items: center;
  flex: none;
  padding: 0.75rem 1.5rem;
  background-color: var(--top-toolbar-background-color);
  color: var(--toolbar-text-color);
  border-bottom: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

.image-gallery-heading {
  margin: 0;
  padding: 0;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: normal;
  color: var(--header-1-color);
  background-color: transparent;
}

/* Main scrollable area: image grid lives here. */
.gallery-images-container {
  flex: 1 1 auto;
  overflow: auto;
  padding: 2.25rem 2rem 1.5rem;
}

.gallery-images-div {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 0.75rem;
}

/* Image tile: a rounded clickable card, same look/feel as the home
   project tile. */
.gallery-image-group-div {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 14px 12px 12px;
  border-radius: var(--corner-radius);
  border: 1px solid var(--editor-control-border-color);
  background-color: var(--gallery-tile-bg, var(--editor-input-bg, #ffffff));
  max-width: 150px;
  cursor: default;
}

.gallery-image-group-div:focus {
  outline: 2px solid var(--editor-selected-blue);
  outline-offset: -2px;
}

.gallery-image-div {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 110px;
  height: 110px;
}

/* ── The non-picture assets ──
   The gallery holds sounds and videos on the same terms as pictures (the store
   never filtered on type). Each kind fills the same 110px cell, so the grid stays
   a grid whatever is in it. */

/* A video shows its own first frame — a <video> told to preload metadata draws it
   without playing anything — so it is sized exactly like a picture. */
.gallery-image-div .gallery-asset-video {
  display: block;
  max-width: 110px;
  max-height: 110px;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
  background: var(--editor-inset-bg, rgba(0, 0, 0, 0.06));
}

/* A sound has nothing to draw: a glyph plate with the browser's own player under
   it, which is the one preview a sound actually has. */
.gallery-asset-audio,
.gallery-asset-other {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 110px;
  height: 110px;
  box-sizing: border-box;
  padding: 6px;
  border-radius: 6px;
  background: var(--editor-inset-bg, rgba(0, 0, 0, 0.06));
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
}

.gallery-asset-glyph {
  font-size: 28px;
  line-height: 1;
  color: var(--editor-text-color);
  opacity: 0.75;
}

/* The native player, shrunk to the cell. Its own height is fixed by the browser,
   so only the width is ours to set. */
.gallery-asset-audio audio {
  width: 100%;
  max-width: 100px;
  height: 32px;
}

.gallery-image-div img {
  display: block;
  max-width: 110px;
  max-height: 110px;
  /* Double-clicking to pick an image must register as a dblclick, not start a
     native image-drag or select the picture (which would swallow the second
     click and leave the gallery open). */
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  cursor: pointer;
}

#editing-input {
  width: 110px;
  margin: 5px 0 0;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: 0.85rem;
}

.image-name-p {
  display: block;
  width: 110px;
  margin: 6px 0 0;
  overflow: hidden;
  font-size: 0.85rem;
  text-align: center;
  text-overflow: ellipsis;
  user-select: none;
  white-space: nowrap;
  color: var(--editor-text-color);
}

/* Status row — its own section, sitting just above the bottom toolbar
   so the toolbar reads as a clean action row. */
#status {
  flex: none;
  margin: 0;
  padding: 0.5rem 1.5rem;
  background-color: transparent;
  color: var(--editor-text-color);
  font-size: 0.95rem;
  font-weight: 500;
  text-align: center;
}

/* Bottom action toolbar — same look/feel as the login page's
   `.login-button-div`: dev action on the left, spacer, primary
   actions on the right. */
.image-gallery-footer-div {
  position: relative;
  display: flex;
  align-items: center;
  flex: none;
  gap: 0.75rem;
  padding: 0.75rem 1.5rem;
  background-color: transparent;
  border-top: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

.image-gallery-dev-buttons {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.image-gallery-footer-spacer {
  flex: 1 1 auto;
}

.buttons {
  min-width: auto;
  margin: 0;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background-color: var(--button-bg);
  color: var(--button-text);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}

.buttons:hover:not(:disabled) {
  background-color: var(--button-bg-hover);
}

.ok-button:disabled,
.image-gallery-footer-div .buttons:disabled {
  border-color: var(--editor-control-border-color);
  background-color: var(--editor-panel-separator-color);
  color: var(--editor-muted-text-color);
  cursor: not-allowed;
  opacity: 0.45;
}

/* Delete action in manage mode — red tint to set it apart from the
   non-destructive upload/download/rename buttons. */
.gallery-delete-button:not(:disabled) {
  border-color: rgba(220, 38, 38, 0.55);
  background-color: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}

.gallery-delete-button:not(:disabled):hover {
  background-color: rgba(220, 38, 38, 0.2);
}

/* Light-mode: main gallery area matches the editor's canvas surface;
   image tiles use the white "clickable surface" so they pop against
   the gray page. */
html[data-theme="light"] {
  --gallery-content-bg: var(--canvas-background-color);
  --gallery-tile-bg: #ffffff;
}

/* Dark-mode: gallery area uses the panel surface (same as customize
   and home); tiles use the top-toolbar surface so they read as
   elevated controls. */
html[data-theme="dark"] {
  --gallery-content-bg: var(--editor-panel-background-color);
  --gallery-tile-bg: var(--top-toolbar-background-color);
}

/* Status bar. Used on the Home, Customize, Login, Signup, Images, and
   editor pages. Sits flush with the bottom of the page; in the editor
   it pins below the canvas/panels and spans the full viewport. */

.status-bar {
  display: flex;
  align-items: center;
  flex: none;
  gap: 0.75rem;
  height: 26px;
  padding: 0 0.9rem;
  background-color: var(--status-bar-bg, var(--top-toolbar-background-color));
  color: var(--status-bar-text, var(--toolbar-text-color));
  border-top: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  user-select: none;
  box-sizing: border-box;
}

.status-bar-spacer {
  flex: 1 1 auto;
}

.status-bar-left,
.status-bar-right {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.status-bar-right {
  justify-content: flex-end;
  color: var(--status-bar-muted, var(--editor-muted-text-color));
}

.status-bar-error {
  color: #c0392b;
}

/* Clickable version read-out in the editor's right status bar — opens the
   What's New dialog. Dimmed at rest, brightens to full on hover. */
.status-bar-version {
  cursor: pointer;
  color: var(--status-bar-text, var(--toolbar-text-color));
  opacity: 0.5;
}
.status-bar-version:hover {
  opacity: 1;
}

/* Editor variant pins to the bottom of the viewport. The editor
   renders two of these — one spanning the left toolbar + layers tree,
   one spanning the details panel — and positions each via an inline
   `left`/`right`/`width` style so the canvas region in between is left
   uncovered. The left toolbar, tree, and details panels stop at
   --status-bar-height (see App.css) so they don't overlap. */
.status-bar-editor {
  position: fixed;
  bottom: 0;
  z-index: 15;
}

/* Light-mode: a softer surface than the dark top-toolbar so the bar
   doesn't read as a black slab against the canvas-gray pages. */
html[data-theme="light"] {
  --status-bar-bg: #e1e1e6;
  --status-bar-text: #46454e;
  --status-bar-muted: #6a6973;
}

/* Dark-mode: matches the top-toolbar surface. */
html[data-theme="dark"] {
  --status-bar-bg: var(--top-toolbar-background-color);
  --status-bar-text: var(--editor-text-color);
  --status-bar-muted: var(--editor-muted-text-color);
}

/* Generic loading spinner for async screens (image gallery, project list).
   Bundled globally like the app's other CSS; class names are prefixed so
   they don't collide. */
.app-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    width: 100%;
    padding: 48px 0;
    color: var(--editor-text-color, #5a5964);
}

.app-spinner {
    width: 36px;
    height: 36px;
    border: 3px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
    border-top-color: var(--editor-selected-blue, #3b82f6);
    border-radius: 50%;
    animation: app-spin 0.8s linear infinite;
}

.app-loading-label {
    font-size: 14px;
    opacity: 0.75;
}

@keyframes app-spin {
    to { transform: rotate(360deg); }
}

/* Small inline spinner for buttons mid-request (e.g. login). Sized in em
   so it tracks the button's font size.

   Drawn in the button's OWN text colour. It was a fixed white ring — right for the solid
   accent buttons it was written for, and invisible on every other kind: the Build Guide's
   "Generate (deterministic)" is a ghost button, so its spinner was white-on-near-white and
   the button simply looked frozen while it worked. currentColor covers both, and the
   transparent quadrant is what reads as motion.

   The animation is a pure transform, so the compositor keeps it turning even while a
   synchronous build (generateAppDesign lays out dozens of screens) holds the main thread. */
.btn-spinner {
    display: inline-block;
    width: 1em;
    height: 1em;
    margin-right: 0.5em;
    vertical-align: -0.15em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    opacity: 0.8;
    border-radius: 50%;
    animation: app-spin 0.7s linear infinite;
}

/* Full-screen dim overlay shown the instant a slow navigation starts
   (e.g. opening a project into the editor) so the click registers
   immediately even while the fresh record is still being fetched. */
.app-loading-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 18, 24, 0.55);
}
.app-loading-overlay .app-loading-label {
    color: rgba(255, 255, 255, 0.85);
    opacity: 1;
}

/* Authentication pages (login + signup). Layout mirrors the customize
   page: a dark header bar at the top with the page title, then a
   scrollable main area below that centers the form on the canvas
   surface. All colors come from editor variables so the pages match
   the active theme. */

/* All element rules below are scoped to the login + signup page
 * containers. Unscoped versions previously leaked into the rest of the
 * app (centering all <p>, restyling all <h2>) once UserAccountForm was
 * loaded once, since CSS imports in this project are global. */

.login-page h1,
.login-page h2,
.login-page h3,
.login-page h4,
.login-page h5,
.login-page h6,
.login-page p,
.login-page input,
.login-page select,
.signup-page h1,
.signup-page h2,
.signup-page h3,
.signup-page h4,
.signup-page h5,
.signup-page h6,
.signup-page p,
.signup-page input,
.signup-page select {
  font-family: var(--editor-font-family, Arial, Helvetica, sans-serif);
}

/* The .App element acts as our page shell. */
div.App {
  background-color: var(--editor-panel-background-color);
  color: var(--editor-text-color);
}

.login-page p,
.signup-page p {
  font-size: 1.25rem;
  text-align: center;
}

/* Outer page: a flex column that fills the viewport so the header
   stays pinned at the top and the form scrolls inside the main area. */
.login-page,
.signup-page {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  background-color: var(--login-content-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  overflow: hidden;
}

/* Forms fill the page and lay out as: heading bar, then the centered
   field block, then the action row at the bottom. */
.login-page > form,
.signup-page > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

/* Top header bar — same dimensions/style as .customize-header. */
.signup-heading-div {
  display: flex;
  align-items: center;
  flex: none;
  padding: 0.75rem 1.5rem;
  background-color: var(--top-toolbar-background-color);
  color: var(--toolbar-text-color);
  border-bottom: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

h2.signup-heading {
  margin: 0;
  padding: 0;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--header-1-color);
  background-color: transparent;
}

/* h2 fallback for any other usage on these pages. Scoped to login +
   signup so it doesn't bleed into other routes' headings. */
.login-page h2,
.signup-page h2 {
  margin: 0;
  padding: 8px;
  color: var(--toolbar-text-color);
  background-color: var(--top-toolbar-background-color);
}

/* Centered form-card area between the header and the action row.
   Takes the remaining vertical space inside the form and centers the
   user-fields card both horizontally and vertically. Provides its
   own scrolling for tall forms (signup). */
.login-div,
.signup-div {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  margin: 0;
  background: transparent;
  text-align: center;
  overflow: auto;
}

/* The signup/account form can be tall. Its card uses `margin: auto`
   (see .account-card) to center within this area while still scrolling
   fully when it overflows — so no align-items override is needed. */

.user-fields-div {
  display: inline-block;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
}

.user-data-div {
  padding: 5px 10px;
  text-align: left;
}

.user-data-label {
  display: inline-block;
  width: 160px;
  margin: 5px;
  font-size: 14px;
  text-align: left;
  color: var(--editor-text-color);
}

.user-data-input {
  box-sizing: border-box;
  width: 260px;
  padding: 0.35rem 0.55rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: 14px;
}

/* "Stay logged in" row, between the password field and the action row. */
.login-remember {
  display: flex;
  align-items: center;
}

.login-remember-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 5px;
  font-size: 0.85rem;
  color: var(--editor-text-color);
  cursor: pointer;
}

.login-remember-label input[type="checkbox"] {
  cursor: pointer;
}

/* Action row tucked inside the form card, below the password input.
   Links (Forgot password / Sign Up) sit to the left of a soft spacer,
   the primary Log In button anchors the right. The previous full-width
   toolbar at the bottom of the page is gone — keeping everything in the
   centered card means messages and actions live in the same visual
   region the user is already focused on. */
.login-card-actions {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 1rem 10px 0;
  padding-top: 0.9rem;
  border-top: 1px solid var(--editor-panel-separator-color, var(--editor-control-border-color));
}

.login-card-actions .login-button,
.login-card-actions .signup-button {
  margin-left: auto;
  /* Hold a stable footprint across the idle/submitting label swap. The busy
     state ("Logging in…" + spinner) is wider than the idle label, so without a
     reserved width the button — and the inline-block card around it — visibly
     resize the instant you click. Center the contents within that width. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 8.5rem;
}

/* Terms / Privacy consent line under the Sign Up action. */
.signup-legal-note {
  margin: 0.75rem 10px 0;
  font-size: 0.8rem;
  text-align: center;
  color: var(--editor-text-color);
  opacity: 0.75;
  line-height: 1.45;
}

.signup-legal-note a {
  color: var(--editor-selected-blue);
  text-decoration: none;
  font-weight: 600;
}

.signup-legal-note a:hover {
  text-decoration: underline;
}

/* Dev-only quick-fill row, currently only the Loren button on the Boss
   login. Dashed border + reduced opacity mark it as a non-production
   helper. Stripped from production builds via the NODE_ENV check in
   the Login component. */
.login-card-dev {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin: 0.9rem 10px 0;
  padding-top: 0.9rem;
  border-top: 1px dashed var(--editor-panel-separator-color, var(--editor-control-border-color));
}

.login-card-dev .buttons {
  opacity: 0.75;
}

.login-card-dev .buttons:hover {
  opacity: 1;
}

.login-link,
.signup {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--editor-selected-blue);
  text-decoration: none;
}

.login-link:hover,
.signup:hover {
  text-decoration: underline;
}

/* "Back to <app>" link rendered as a button — reset the native button surface
   and push it to the left so the rest of the actions stay grouped on the right. */
.login-landing-link {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  margin-right: auto;
  font-family: inherit;
}

/* Primary action button — same dimensions and surface as the customize
   page header buttons (.customize-back-button). */
.buttons {
  margin: 0;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background-color: var(--button-bg);
  color: var(--button-text);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}

.buttons:hover:not(:disabled) {
  background-color: var(--button-bg-hover);
}

/* Pressed-state feedback for the standard action buttons: a quick nudge
   down + dim while held so a click is visibly acknowledged. Mirrors the
   FS Tracker; the editor toolbar buttons already have their own :active
   states, so this is scoped to the shared .buttons class. */
.buttons {
  transition: transform 0.04s ease, filter 0.1s ease, background-color 0.1s ease;
}
.buttons:active:not(:disabled) {
  transform: translateY(1px);
  filter: brightness(0.92);
}

#status {
  display: block;
  margin: 0 1.5rem 1rem;
  color: var(--editor-text-color);
  font-size: 0.95rem;
}

/* Password field with a show/hide eye toggle (PasswordInput widget).
   The wrapper positions the eye button over the right edge of the
   input; the input gets extra right padding so its text never slides
   under the button. */
.password-input-wrap {
  position: relative;
  display: inline-block;
}

/* In the stacked account/signup fields the wrap fills the column so the
   100%-wide .account-control keeps its full width. */
.account-field .password-input-wrap {
  display: block;
}

.password-input-wrap input {
  padding-right: 2.1rem;
}

.password-toggle {
  position: absolute;
  top: 50%;
  right: 0.4rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px;
  border: none;
  background: transparent;
  color: var(--editor-text-color);
  opacity: 0.6;
  cursor: pointer;
}

.password-toggle:hover,
.password-toggle:focus-visible {
  opacity: 1;
}

.password-toggle svg {
  width: 18px;
  height: 18px;
  display: block;
}

/* Light-mode: main form area paints the canvas color, matching the
   home and customize pages. */
html[data-theme="light"] {
  --login-content-bg: var(--canvas-background-color);
}

/* ============================================================
   Account / signup form — grouped fields on a centered card
   ============================================================ */

/* The form card. `margin: auto` centers it within the scrollable
   main area when it fits, and — unlike align/justify-center — still
   lets the user scroll up to the top when the form overflows.
   Fluid width up to 960px: on a wide window it grows so the two
   columns get enough room for the controls to breathe (rather than
   pinning at a narrow 720px), and it falls back to the available
   width on smaller screens. */
.account-card {
  margin: auto;
  width: 100%;
  max-width: 960px;
  box-sizing: border-box;
  padding: 1.5rem 1.75rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  text-align: left;
}

/* The labelled field sections flow into two balanced columns. CSS
   multi-column (rather than a row-major grid) evens out the two columns'
   heights on its own — the old grid left the tall Address section alone
   in the left column while the right column ended short. `300px` caps it
   at two columns on the 720px card and collapses to one on narrow screens
   with no media query. */
.account-sections {
  columns: 300px 2;
  column-gap: 1.4rem;
}

/* Keep each section whole within a column, and restore the vertical
   gap between stacked sections (multi-column has no row `gap`, so the
   per-section bottom margin from .account-section does that job again). */
.account-sections .account-section {
  break-inside: avoid;
}

/* Soft "Alpha" banner anchored at the top of the signup / account
   card. The accent-tinted background reads as informative rather than
   alarming — bordered + filled with the same selected-blue used
   elsewhere, at low opacity so it reads in both light and dark
   themes. */
.alpha-banner {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0 0 1.4rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid color-mix(in srgb, var(--editor-selected-blue) 35%, transparent);
  border-radius: var(--corner-radius);
  background-color: color-mix(in srgb, var(--editor-selected-blue) 10%, transparent);
  color: var(--editor-text-color);
  font-size: 0.9rem;
  line-height: 1.4;
  text-align: left;
}

.alpha-banner-badge {
  flex: none;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background-color: var(--editor-selected-blue);
  color: #ffffff;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.alpha-banner-text {
  flex: 1 1 auto;
}

/* A labelled group of related fields. */
.account-section {
  margin-bottom: 1.4rem;
}

.account-section:last-child {
  margin-bottom: 0;
}

.account-section-heading {
  margin: 0 0 0.7rem;
  padding-bottom: 0.3rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--header-2-color);
  border-bottom: 1px solid var(--editor-panel-separator-color, var(--editor-control-border-color));
}

.account-section-note {
  margin: 0.55rem 0 0;
  font-size: 0.8rem;
  font-style: italic;
  text-align: left;
  color: var(--editor-text-color);
  opacity: 0.75;
}

/* Danger zone — the account-mode "Delete Account" section. The heading
   and buttons take a red accent so the irreversible action reads clearly
   apart from the ordinary save/cancel controls. */
.account-section-danger .account-section-heading {
  color: #b91c1c;
  border-bottom-color: rgba(220, 38, 38, 0.45);
}

.account-delete-actions {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 0.85rem;
}

.account-delete-button,
.account-delete-confirm {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border-color: rgba(220, 38, 38, 0.55);
  background-color: rgba(220, 38, 38, 0.10);
  color: #b91c1c;
}

.account-delete-button:hover:not(:disabled),
.account-delete-confirm:hover:not(:disabled) {
  background-color: rgba(220, 38, 38, 0.18);
}

/* Field grid within a section: paired fields (First/Last, City/State,
   Password/Confirm) sit two-up when the section is wide enough and stack
   to one column when it isn't — so on phones they don't get squeezed
   (e.g. the State select shrinking to nothing or the Zip placeholder
   truncating). `min(100%, 10rem)` lets a column fall below 10rem on very
   narrow cards rather than overflow. Wide fields keep spanning the full
   row via .account-field-wide (grid-column: 1 / -1). */
.account-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr));
  gap: 0.7rem 1rem;
}

/* One stacked label + control. */
.account-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

/* A field that spans the full width of the grid. */
.account-field-wide {
  grid-column: 1 / -1;
}

.account-field label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--editor-text-color);
}

/* Shared style for text inputs and the country/state <select>s. */
.account-control {
  box-sizing: border-box;
  width: 100%;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: 14px;
}

.account-control:focus {
  outline: 1px solid var(--editor-selected-blue);
  outline-offset: -1px;
}

/* One consent/preference checkbox row inside an account section. */
.account-checkbox {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.25rem 0;
  font-size: 0.9rem;
  color: var(--editor-text-color);
  cursor: pointer;
}

.account-checkbox input[type="checkbox"] {
  cursor: pointer;
}

/* Dev/test quick-fill buttons on the form's action row. The dashed
   border marks them as non-production helpers. */
.dev-fill-button {
  padding: 0.28rem 0.7rem;
  border: 1px dashed var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: transparent;
  color: var(--editor-text-color);
  font-size: 0.78rem;
  cursor: pointer;
  opacity: 0.65;
}

.dev-fill-button:hover {
  opacity: 1;
}

/* Informational (non-error) status message. */
.status-bar-ok {
  color: var(--editor-selected-blue);
}

/* Prominent inline error / status block, shown inside the form card
   above the inputs. Replaces stuffing errors into the StatusBar at the
   bottom corner of the screen — that strip is small and easy to miss,
   especially on tall monitors where the form is centered way above it. */
.login-page .login-form-status,
.signup-page .login-form-status {
  display: block;
  margin: 0 auto 1rem;
  padding: 0.65rem 1rem;
  border-radius: var(--corner-radius);
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  line-height: 1.4;
}

.login-page .login-form-status.is-error,
.signup-page .login-form-status.is-error {
  background-color: rgba(220, 38, 38, 0.10);
  border: 1px solid rgba(220, 38, 38, 0.45);
  color: #b91c1c;
}

.login-page .login-form-status.is-ok,
.signup-page .login-form-status.is-ok {
  background-color: rgba(34, 197, 94, 0.10);
  border: 1px solid rgba(34, 197, 94, 0.45);
  color: #15803d;
}

/* ALL-BUTTONS PRESS FEEDBACK
   Give every remaining button the same click response as the shared
   .buttons class and the marketing site: a quick nudge down + brightness
   dip while held. Additive to each button's own hover / background states.
   The color-picker drag handle and menu-row divs are intentionally left
   out (they have functional or list-row behaviors, not button presses). */
.home-buttons,
.home-icon-button,
.customize-reset-button,
.dev-fill-button,
.verify-banner-button,
.share-button,
.tt-btn,
.boss-icon-button,
.boss-delete-x,
.boss-primary,
.docs-back-button,
.docs-audience-button,
.docs-account-button,
.export-button,
.image-offset-reset,
.angle-reset,
.color-picker-close,
.color-picker-eyedropper-btn {
  transition: transform 0.04s ease, filter 0.1s ease, background-color 0.08s ease;
}
.home-buttons:active:not(:disabled),
.home-icon-button:active:not(:disabled),
.customize-reset-button:active:not(:disabled),
.dev-fill-button:active:not(:disabled),
.verify-banner-button:active:not(:disabled),
.share-button:active:not(:disabled),
.tt-btn:active:not(:disabled),
.boss-icon-button:active:not(:disabled),
.boss-delete-x:active:not(:disabled),
.boss-primary:active:not(:disabled),
.docs-back-button:active:not(:disabled),
.docs-audience-button:active:not(:disabled),
.docs-account-button:active:not(:disabled),
.export-button:active:not(:disabled),
.image-offset-reset:active:not(:disabled),
.angle-reset:active:not(:disabled),
.color-picker-close:active:not(:disabled),
.color-picker-eyedropper-btn:active:not(:disabled) {
  transform: translateY(1px);
  filter: brightness(0.92);
}

/* ── Agentic-authorization consent page (/approve) ───────────────────────── */
/* Reuses the login chrome (.login-page, .login-div, .user-fields-div, fields,
   .login-card-actions, .login-button). These add the consent-specific copy. */
.approve-lead {
  color: var(--editor-text-color);
  font-size: 0.95rem;
  line-height: 1.45;
  margin: 0 0 0.75rem 0;
}

.approve-note {
  color: var(--editor-text-color);
  opacity: 0.8;
  font-size: 0.85rem;
  line-height: 1.4;
  margin: 0.5rem 0 0.25rem 0;
}

.approve-consent {
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: 8px;
  padding: 0.85rem 1rem;
  margin: 0.25rem 0 0.5rem 0;
  background-color: var(--editor-input-bg, rgba(127, 127, 127, 0.08));
}

.approve-scope-list {
  margin: 0.15rem 0 0.5rem 1.1rem;
  padding: 0;
  color: var(--editor-text-color);
  opacity: 0.9;
  font-size: 0.85rem;
  line-height: 1.5;
}

/* A quieter, secondary action sitting next to the primary Approve button. */
.approve-deny-button {
  background-color: transparent;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.5));
  color: var(--editor-text-color);
}

/* ── Account Settings (tabbed dialog) ────────────────────────────────────── */
/* The Account Settings screen (Account.tsx) is a Customize-style page: a
   header with the back button, a tab strip, and a scrolling .customize-panel.
   The profile form and the Automation access section render embedded inside
   that panel — without their own full-screen .signup-page chrome — so the
   panel is the single scroll area and .account-card centres within it. */
.account-settings-page .account-embedded-form {
  display: block;
}

/* ── Automation access (Account page tokens section) ─────────────────────── */
.token-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.5rem 0;
}

.token-row {
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: 8px;
  padding: 0.6rem 0.8rem;
  background-color: var(--editor-input-bg, rgba(127, 127, 127, 0.06));
}

.token-row-main {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.token-row-name {
  font-weight: 600;
  color: var(--editor-text-color);
}

.token-row-prefix {
  font-family: monospace;
  opacity: 0.8;
  color: var(--editor-text-color);
}

.token-row-state {
  font-size: 0.75rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  border: 1px solid currentColor;
}

.token-row-state.is-active { color: #1f9d55; }
.token-row-state.is-revoked { color: #b04a4a; opacity: 0.85; }

.token-row-meta {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 0.35rem;
  font-size: 0.8rem;
  opacity: 0.75;
  color: var(--editor-text-color);
}

.token-row-actions {
  margin-top: 0.5rem;
}

.token-revoke-button {
  background-color: transparent;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.5));
  color: var(--editor-text-color);
}

.token-create {
  margin-top: 0.75rem;
}

.token-create-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

/* One-time reveal box for a freshly minted token. */
.token-reveal {
  border: 1px solid #1f9d55;
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
  margin: 0.5rem 0 0.75rem 0;
  background-color: rgba(31, 157, 85, 0.08);
}

.token-reveal-label {
  font-size: 0.85rem;
  color: var(--editor-text-color);
  margin-bottom: 0.5rem;
}

.token-reveal-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.token-reveal-value {
  font-family: monospace;
  font-size: 0.85rem;
  word-break: break-all;
  flex: 1 1 auto;
  padding: 0.35rem 0.5rem;
  border-radius: 6px;
  background-color: var(--editor-input-bg, rgba(127, 127, 127, 0.12));
  color: var(--editor-text-color);
}

.token-reveal-dismiss {
  display: inline-block;
  margin-top: 0.5rem;
}

/* App-switcher on the login form: pick which product to open after sign-in. */
.login-app-switch {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.login-app-options {
  display: flex;
  gap: 0.5rem;
}
.login-app-option {
  flex: 1;
  cursor: pointer;
}
.login-app-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.login-app-option span {
  display: block;
  text-align: center;
  padding: 0.5rem 0;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  font-weight: 600;
  color: var(--editor-text-color);
}
.login-app-option.is-selected span {
  border-color: var(--editor-selected-blue);
  background-color: color-mix(in srgb, var(--editor-selected-blue) 14%, transparent);
  color: var(--header-1-color);
}

/* The Boss dashboard styling. Anchored to the .App frame like the rest
 * of the editor pages so the table scrolls inside its own area rather
 * than pushing the page past the viewport.
 *
 * Colors reuse the editor theme variables so light/dark theming follows
 * the rest of the app. Modal sits above on a translucent backdrop.
 */

.boss-page {
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    display: flex;
    flex-direction: column;
    background-color: var(--editor-panel-background-color);
    color: var(--editor-text-color);
    font-family: var(--editor-font-family, Arial, Helvetica, sans-serif);
}

.boss-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    /* Padding + button sizing match .home-header-div so the Boss / Admin
       pages share the same toolbar height as the home page. */
    padding: 0.75rem 1.5rem;
    background-color: var(--top-toolbar-background-color);
    color: var(--toolbar-text-color);
    border-bottom: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

.boss-header h1 {
    margin: 0;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--header-1-color);
}

.boss-header-spacer { flex: 1 1 auto; }

/* Icon buttons in the Boss header (Add user, home, Log out boss,
   Log out all). Transparent so they read against the dark header
   surface, white-tint hover/active to match the home page header
   icon-button pattern. */
.boss-icon-button {
    background: transparent;
    border: none;
    margin: 0;
    padding: 4px;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    border-radius: var(--corner-radius);
    cursor: pointer;
    transition: background-color 80ms ease-out, opacity 80ms ease-out;
    /* Foreground-highlight rule: muted glyph at rest, full on hover, no bg box. */
    opacity: 0.8;
}

.boss-icon-button:hover {
    opacity: 1;
}

.boss-icon-button:active {
    opacity: 1;
}

/* Wider variant for the composite Log-out-boss button so the logout
   glyph plus the smaller crown badge fit comfortably without crowding. */
.boss-icon-button-wide {
    width: 54px;
}

.boss-icon-button-img {
    height: 22px;
    width: auto;
    user-select: none;
    pointer-events: none;
}

/* Smaller adornment used for the crown that sits next to the logout
   glyph on the Log-out-boss button. */
.boss-icon-button-img-sm {
    height: 14px;
    width: auto;
    user-select: none;
    pointer-events: none;
}

/* The email cell doubles as the row's edit affordance. Reset native
   button styling so it reads as a link, then add hover/focus underline
   so the cell visibly responds. */
.boss-email-link {
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    color: var(--editor-selected-blue);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.boss-email-link:hover,
.boss-email-link:focus-visible {
    text-decoration: underline;
}

/* Red × delete button matching the .color-picker-close pattern used
   elsewhere (e.g. ColorPicker, GradientEditor) — transparent
   background, red glyph at muted opacity, brighter + lightly tinted
   on hover/active. */
.boss-delete-x {
    background-color: transparent;
    border: none;
    border-radius: var(--corner-radius-sm);
    padding: 2px 8px;
    font-family: var(--editor-font-family);
    font-size: 1rem;
    font-weight: 700;
    color: #e53935;
    cursor: pointer;
    user-select: none;
    line-height: 1;
    opacity: var(--editor-icon-muted-opacity, 0.6);
    transition: background-color 80ms ease-out, opacity 80ms ease-out;
}

.boss-delete-x:hover {
    background-color: transparent;
    opacity: 1;
}

.boss-delete-x:active {
    background-color: rgba(128, 128, 128, 0.32);
    opacity: 1;
}

.boss-status-error {
    margin: 0;
    padding: 0.5rem 1.5rem;
    background-color: rgba(220, 38, 38, 0.10);
    color: #b91c1c;
    font-size: 0.9rem;
}

.boss-table-wrap {
    flex: 1 1 auto;
    overflow: auto;
    padding: 1rem 1.5rem 2rem;
}

.boss-table {
    border-collapse: collapse;
    width: 100%;
    font-size: 0.9rem;
}

.boss-table th, .boss-table td {
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--editor-panel-separator-color);
    vertical-align: middle;
}

.boss-table th {
    font-weight: 600;
    color: var(--header-2-color);
    background-color: rgba(0, 0, 0, 0.02);
    position: sticky;
    top: 0;
    z-index: 1;
}

.boss-row-actions {
    display: flex;
    gap: 0.4rem;
}

.boss-toggle input[type="checkbox"] { cursor: pointer; }

/* Inline role select in the Boss table. Sized to match surrounding
   text so the row stays on one line. */
.boss-role-select {
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--editor-control-border-color);
    border-radius: var(--corner-radius-sm, 4px);
    background-color: var(--editor-input-bg, var(--editor-panel-background-color));
    color: var(--editor-text-color);
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
}

/* ----- Modal ----- */
.boss-modal-backdrop {
    position: fixed;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 100;
}

.boss-modal {
    background-color: var(--editor-panel-background-color);
    color: var(--editor-text-color);
    border-radius: var(--corner-radius, 6px);
    padding: 1.5rem 1.6rem;
    max-width: 560px;
    width: calc(100% - 2rem);
    max-height: calc(100vh - 4rem);
    overflow-y: auto;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}

.boss-modal-title {
    margin: 0 0 1rem;
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--header-1-color);
}

.boss-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.7rem 1rem;
}

.boss-form-grid label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--editor-text-color);
}

.boss-form-grid input[type="text"],
.boss-form-grid input[type="email"],
.boss-form-grid input[type="password"],
.boss-form-grid input:not([type]) {
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--editor-control-border-color);
    border-radius: var(--corner-radius-sm, 4px);
    background-color: var(--editor-input-bg, var(--editor-panel-background-color));
    color: var(--editor-text-color);
    font-size: 0.9rem;
}

.boss-form-wide { grid-column: 1 / -1; }

.boss-form-check {
    flex-direction: row !important;
    align-items: center;
    gap: 0.5rem;
}

.boss-form-status {
    margin: 0.8rem 0 0;
    color: #b91c1c;
    font-size: 0.85rem;
}

.boss-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.2rem;
}

.boss-primary {
    background-color: var(--editor-selected-blue);
    color: #ffffff;
}

/* Delete-admin choice dialog. */
.boss-confirm {
    max-width: 460px;
}

.boss-confirm-text {
    margin: 0;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--editor-text-color);
}

.boss-danger {
    border-color: rgba(220, 38, 38, 0.55);
    background-color: rgba(220, 38, 38, 0.12);
    color: #b91c1c;
}

.boss-danger:hover:not(:disabled) {
    background-color: rgba(220, 38, 38, 0.2);
}

/* ---- Usage read-out (/theboss/usage) ---------------------------------- */
/* The screen's job is to make an UNTOUCHED surface as visible as a busy one, so
   the only real styling here is the cold row and the day bar. */
.boss-usage-note {
    margin: 12px 16px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--editor-muted-text-color, #888);
    max-width: 68ch;
}
.boss-usage-h2 {
    margin: 22px 16px 6px;
    font-size: 15px;
}
.boss-usage-empty { color: var(--editor-muted-text-color, #888); }
/* A group heading inside the table — the surface a set of events belongs to. */
.boss-usage-group td {
    padding-top: 14px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--editor-muted-text-color, #888);
}
/* Nobody has ever done this. Dimmed, never hidden: the absence IS the finding. */
.boss-usage-cold td { opacity: 0.55; }
.boss-usage-cold code { text-decoration: line-through; }
.boss-usage-details { margin-top: 3px; }
.boss-usage-chip {
    display: inline-block;
    margin: 2px 4px 0 0;
    padding: 1px 6px;
    font-size: 11px;
    border: 1px solid var(--editor-border-color, #4444);
    border-radius: 10px;
}
.boss-usage-chip em { font-style: normal; opacity: 0.7; }
.boss-usage-bar-cell { width: 30%; }
.boss-usage-bar {
    display: block;
    height: 8px;
    border-radius: 4px;
    background: var(--editor-selected-blue, #3b82f6);
    opacity: 0.7;
}
/* The selected time window. */
.boss-window-on {
    outline: 2px solid var(--editor-selected-blue, #3b82f6);
    outline-offset: -2px;
}

/* ---- Crash read-out (/theboss/errors) --------------------------------- */
/* Shares the usage screen's furniture (tables, day bars, the window buttons);
   what it adds is the two things a crash row needs and a usage row doesn't: a
   message you can click to open, and a stack that stays readable and
   selectable when you do. */
.boss-linkish {
    padding: 0;
    border: none;
    background: none;
    color: var(--editor-selected-blue, #3b82f6);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.boss-linkish:hover { text-decoration: underline; }
/* "render" beside a message — which handler saw it, when that is worth knowing. */
.boss-tag {
    margin-left: 6px;
    padding: 1px 6px;
    font-size: 11px;
    border-radius: 10px;
    border: 1px solid var(--editor-border-color, #4444);
    color: var(--editor-muted-text-color, #888);
}
/* The stack is the fix, so it is monospace, scrollable and selectable rather
   than tidy — it exists to be read and copied. */
.boss-stack {
    margin: 0 16px 12px;
    padding: 10px 12px;
    max-height: 22rem;
    overflow: auto;
    border: 1px solid var(--editor-border-color, #4444);
    border-radius: 8px;
    background: var(--editor-panel-background-color, #1e1e1e);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
    user-select: text;
}

/* Back control for full-screen pages: a single major back button.
   See BackControls.tsx. */
.back-controls {
  display: inline-flex;
  align-items: center;
}

/* Customize (preferences) page. Tabbed notebook with Theme, Layout,
   Keybindings, and Colors panels. Everything reads from the same CSS
   variables the editor uses, so the page itself respects the user's
   selected theme. */

.customize-page {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  background-color: var(--customize-page-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-family: var(--editor-font-family);
  overflow: hidden;
  /* One indentation step. Descriptions sit one step under their section
     header; the details below them sit one step further again. */
  --cz-indent: 1.5rem;
}

.customize-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem 1.5rem;
  border-bottom: 1px solid var(--panel-border-color);
  background-color: var(--top-toolbar-background-color);
  color: var(--toolbar-text-color);
}

.customize-header h1 {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--header-1-color);
}

.customize-header-spacer {
  flex: 1;
}

.customize-reset-button {
  padding: 0.4rem 0.9rem;
  font-size: 0.9rem;
  font-weight: 600;
  border-radius: var(--corner-radius);
  border: 1px solid var(--editor-control-border-color);
  background-color: var(--button-bg);
  color: var(--button-text);
  cursor: pointer;
}

.customize-reset-button:hover {
  background-color: var(--button-bg-hover);
}

.customize-tabs {
  display: flex;
  gap: 2px;
  padding: 0 1.5rem;
  background-color: var(--customize-tab-strip-bg, var(--editor-panel-separator-color));
  border-bottom: 1px solid var(--panel-border-color);
}

.customize-tab {
  padding: 0.6rem 1.25rem;
  margin: 0;
  border: none;
  background: var(--customize-tab-bg-inactive, transparent);
  color: var(--customize-tab-color, var(--editor-muted-text-color));
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  border-bottom: 2px solid transparent;
}

.customize-tab:hover {
  color: var(--customize-tab-color-active, var(--editor-text-color));
}

.customize-tab.active {
  color: var(--customize-tab-color-active, var(--editor-text-color));
  border-bottom-color: var(--editor-selected-blue);
  background-color: var(--customize-tab-bg-active, var(--editor-input-bg, var(--editor-panel-background-color)));
}

/* The tab NAME sits muted and brightens (foreground-highlight rule) on hover for
   an unselected, non-disabled tab — and is full when active. Opacity is used so
   it also lifts the accent-coloured Code tab, and so the per-tab background
   hierarchy (strip / inactive / active) is left untouched. */
.customize-tab-label {
  display: inline-block;
  opacity: 0.58;
  transition: opacity 0.1s ease;
}
.customize-tab:not(.active):not(:disabled):hover .customize-tab-label,
.customize-tab.active .customize-tab-label {
  opacity: 1;
}

.customize-panel {
  flex: 1;
  overflow: auto;
  padding: 1.5rem 2rem;
  /* Defensive: keep panel content left-aligned even if a sibling page's
     CSS were to set a wider text-align: center. */
  text-align: left;
}

.customize-section-title {
  margin: 0 0 0.5rem;
  font-size: 1rem;
  font-weight: 600;
  text-align: left;
  color: var(--header-2-color);
}

.customize-section-hint {
  /* Equal left/right margins so the description block is inset evenly. */
  margin: 0 var(--cz-indent) 1rem;
  color: var(--customize-hint-color, var(--editor-muted-text-color));
  font-size: 0.85rem;
  text-align: left;
}

/* ── Plugins inspector (Plugins tab) ── */
.plugins-list {
  list-style: none;
  margin: 0 var(--cz-indent);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: left;
}
.plugin-card {
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: 6px;
  padding: 10px 12px;
  background: var(--editor-input-bg, transparent);
}
.plugin-card-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.plugin-card-off {
  opacity: 0.55;
}
/* Enable control pinned to the right of the card head. */
.plugin-enable {
  margin-left: auto;
  align-self: center;
}
.plugin-toggle {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--editor-text-color);
}
.plugin-toggle input {
  margin: 0;
  cursor: pointer;
}
.plugin-core-badge {
  font-size: 0.72rem;
  color: var(--editor-muted-text-color);
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: 3px;
  padding: 0 5px;
  line-height: 1.6;
}
.plugin-name {
  font-weight: 600;
  color: var(--header-3-color, var(--editor-text-color));
}
.plugin-id {
  font-family: monospace;
  font-size: 0.78rem;
  color: var(--editor-muted-text-color);
}
.plugin-version {
  font-size: 0.78rem;
  color: var(--editor-muted-text-color);
}
.plugin-api-mismatch {
  font-size: 0.72rem;
  color: #b26a00;
  border: 1px solid currentColor;
  border-radius: 3px;
  padding: 0 4px;
}
.plugin-desc {
  margin: 6px 0 8px;
  font-size: 0.82rem;
  color: var(--editor-text-color);
}
.plugin-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.plugin-contribs {
  font-size: 0.8rem;
  color: var(--editor-muted-text-color);
}
.plugin-perms {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
}
.plugin-perm-chip {
  font-size: 0.72rem;
  color: var(--selection-color, #1097e2);
  border: 1px solid var(--selection-color, #1097e2);
  border-radius: 3px;
  padding: 0 5px;
  line-height: 1.5;
}

/* Two-column tab layout. Collapses to a single column when the window
   is too narrow to comfortably hold two. */
.customize-2col {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.75rem 2.5rem;
  align-items: start;
}

@media (min-width: 900px) {
  .customize-2col {
    grid-template-columns: 1fr 1fr;
  }
}

/* Engine Defaults groups flow into balanced columns that collapse to a
   single column when the window is too narrow. Multi-column (not grid) so
   groups pack tightly instead of row-aligning and leaving big vertical gaps. */
.customize-engine-cols {
  columns: 2 19rem;
  column-gap: 2.5rem;
}

.customize-engine-cols > .customize-group {
  break-inside: avoid;
  margin-bottom: 1.75rem;
}

/* The Keybindings / Colors group grid is indented one step so the
   group names sit under their tab's section header. */
.customize-2col-sub {
  margin-left: var(--cz-indent);
}

/* A section: the title stays centered (rule above); the description
   sits one indent step in (rule on .customize-section-hint), and the
   detail(s) below it sit one step further again. */
.customize-section {
  display: block;
}

/* Breathing room above each section header (not the first). */
.customize-section + .customize-section {
  margin-top: 1.5rem;
}

/* When one section holds several sub-headings (e.g. the reference settings:
   X/Y measurement + default reference point), the base title only carries a
   bottom margin, so the 2nd+ look cramped. Give them room above. */
.customize-section > .customize-section-title:not(:first-child) {
  margin-top: 1.5rem;
}

.customize-section > .customize-section-hint {
  margin-bottom: 0.75rem;
}

.customize-section > :not(.customize-section-title):not(.customize-section-hint) {
  margin-left: calc(2 * var(--cz-indent));
}

/* A Keybindings / Colors group within a column. The group name stays
   flush left; the group's details are indented by the standard amount,
   matching the Layout-tab sections. */
.customize-group {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.customize-group > :not(.customize-subhead) {
  margin-left: var(--cz-indent);
}

.customize-subhead {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-align: left;
  color: var(--header-2-color);
}

/* When a section stacks several labelled control groups (e.g. the
   color-picker magnifier settings), separate each subhead from the
   control above it. Margins collapse, so the first subhead keeps an
   even gap from the section hint too. */
.customize-section > .customize-subhead {
  margin-top: 1rem;
}

/* Theme tab */

.theme-options {
  display: grid;
  /* auto-fit so a 3- or 4-option list (theme picker, action announce,
     tooltip mode) lays out on a single row when there's horizontal
     room, and wraps gracefully on narrower viewports. */
  grid-template-columns: repeat(auto-fit, minmax(150px, 200px));
  gap: 1rem;
}

.theme-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 1rem;
  border: 2px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background: var(--theme-option-bg, var(--editor-panel-background-color));
  cursor: pointer;
  text-align: left;
  color: var(--theme-option-text, var(--editor-text-color));
  font-size: 0.95rem;
}

.theme-option.active {
  border-color: var(--editor-selected-blue);
}

.theme-option-swatch {
  width: 100%;
  height: 60px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  display: grid;
  grid-template-columns: 1fr 3fr;
}

.theme-option-swatch > span:first-child {
  border-right: 1px solid var(--editor-control-border-color);
}

/* Action-announcements swatches. Each one draws a tiny abstraction of the
 * fs-designer editor (top toolbar strip, left tools strip, canvas area)
 * and overlays the announcement element belonging to that mode:
 *   • toast — a rounded pill near the top of the canvas (matches the
 *             real canvas-notification position: top: 1.2rem, centred)
 *   • bar   — a thin status strip across the bottom of the editor
 *   • both  — the pill at top AND the bar at bottom
 *   • none  — ∅ glyph centred over a plain editor scene
 * Pseudo-elements layer on top of the base editor sketch so the swatch
 * conveys both the geometry of fs-designer and the announcement layout. */
.announce-swatch {
  display: block;
  position: relative;
  background-color: var(--editor-panel-separator-color);
  overflow: hidden;
}

/* Top-toolbar strip + left-tools strip drawn on every announce swatch so
 * the user recognises the rest of the editor regardless of mode. The
 * remaining area reads as the canvas. */
.announce-swatch::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 9px;
  background: var(--top-toolbar-background-color);
  border-bottom: 1px solid var(--editor-control-border-color);
}

/* Toast pill — small rounded rectangle just under the top-toolbar strip,
 * horizontally centred to mirror the real `.canvas-notification` at
 * top: 1.2rem. Reused for the "both" mode swatch (its bottom bar is
 * drawn via a background gradient since ::before/::after are taken). */
.announce-swatch-toast::after,
.announce-swatch-both::after {
  content: '';
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 46%;
  height: 9px;
  background: rgba(24, 24, 28, 0.82);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

/* Status bar strip pinned to the bottom of the editor sketch. */
.announce-swatch-bar::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 10px;
  background: var(--editor-panel-background-color);
  border-top: 1px solid var(--editor-control-border-color);
}

/* Both mode: the top-toolbar strip lives on `.announce-swatch::before`
 * and the toast pill on `::after` (above), which leaves no pseudo-element
 * for the bottom bar — so we paint it with a background gradient on the
 * swatch itself: a solid panel-bg band at the bottom 10px, transparent
 * above (so the base separator-color shows through as the canvas area). */
.announce-swatch-both {
  background:
    linear-gradient(to top,
      var(--editor-panel-background-color) 0,
      var(--editor-panel-background-color) 10px,
      transparent 10px,
      transparent 100%),
    var(--editor-panel-separator-color);
}

/* None: a centred ∅ over the plain editor scene. */
.announce-swatch-none::after {
  content: '∅';
  position: absolute;
  inset: 9px 0 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--editor-muted-text-color);
  font-size: 1.4rem;
  font-weight: 600;
}

/* Tooltip-mode swatches. Each sketches the editor layout — a top
 * toolbar strip across the top, narrow left strip (Left Toolbar +
 * Layers Panel) on the left, narrow right strip (Design Panel) on the
 * right, and the canvas in the middle. Each mode tints the regions
 * that surface tooltips in blue, the others in muted grey. */
.tooltip-swatch {
  display: block;
  position: relative;
  background-color: var(--editor-panel-separator-color);
  overflow: hidden;
}

/* Top toolbar strip (top 9px), drawn on every tooltip swatch via
 * ::before and tinted by mode below. */
.tooltip-swatch::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 9px;
  background: var(--editor-control-border-color);
  border-bottom: 1px solid var(--editor-control-border-color);
}

/* The left + right side strips drawn together via ::after using a
 * two-band radial-friendly trick: two vertical bands on each edge.
 * We use a layered background on the swatch itself so the inner area
 * (canvas) stays the swatch's default background. */
.tooltip-swatch::after {
  content: '';
  position: absolute;
  top: 9px;
  bottom: 0;
  left: 0;
  right: 0;
  background:
    linear-gradient(to right,
      var(--editor-control-border-color) 0,
      var(--editor-control-border-color) 14px,
      transparent 14px,
      transparent calc(100% - 14px),
      var(--editor-control-border-color) calc(100% - 14px),
      var(--editor-control-border-color) 100%);
}

/* Mode-specific highlights — paint the region that's enabled with the
 * selection blue so the user sees at a glance where tooltips appear. */
.tooltip-swatch-top::before {
  background: var(--editor-selected-blue);
}

.tooltip-swatch-panels::after {
  background:
    linear-gradient(to right,
      var(--editor-selected-blue) 0,
      var(--editor-selected-blue) 14px,
      transparent 14px,
      transparent calc(100% - 14px),
      var(--editor-selected-blue) calc(100% - 14px),
      var(--editor-selected-blue) 100%);
}

.tooltip-swatch-both::before {
  background: var(--editor-selected-blue);
}

.tooltip-swatch-both::after {
  background:
    linear-gradient(to right,
      var(--editor-selected-blue) 0,
      var(--editor-selected-blue) 14px,
      transparent 14px,
      transparent calc(100% - 14px),
      var(--editor-selected-blue) calc(100% - 14px),
      var(--editor-selected-blue) 100%);
}

/* None: dim editor + ∅. The ::before and ::after stay grey; the ∅
 * sits on the swatch background via an extra inset wrapper. */
.tooltip-swatch-none {
  position: relative;
}

.tooltip-swatch-none > * { display: none }

/* Use a CSS-only ∅ via the swatch's content-area: we layer the glyph
 * as a background using SVG data URI to avoid clobbering ::before /
 * ::after that already draw the toolbar + side strips. */
.tooltip-swatch-none {
  background-image:
    url("data:image/svg+xml;utf8,<svg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 100 100%27><text x=%2750%27 y=%2765%27 text-anchor=%27middle%27 font-size=%2752%27 font-weight=%27700%27 fill=%27%238e8c9e%27 font-family=%27Arial, sans-serif%27>%E2%88%85</text></svg>");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 36px;
}

/* Layout tab */

.layout-grid {
  display: grid;
  grid-template-columns: 140px 1fr 56px;
  gap: 0.7rem 0.7rem;
  align-items: center;
  max-width: 420px;
}

.layout-grid label {
  font-size: 0.95rem;
}

.layout-grid input[type="range"] {
  width: 100%;
}

.layout-grid input[type="number"] {
  width: 70px;
  padding: 0.25rem 0.4rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
}

.layout-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.95rem;
  user-select: none;
  cursor: pointer;
}

.layout-toggle input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
}

.customize-radios {
  display: flex;
  align-items: center;
  gap: 1.25rem;
}

.customize-slider-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
}

.customize-slider {
  width: 240px;
  max-width: 60%;
}

.customize-slider-value {
  font-size: 0.9rem;
  color: var(--customize-hint-color, var(--editor-muted-text-color));
  min-width: 3rem;
}

/* Keybindings tab */

.keybindings-table {
  /* Fills the group minus its indent (the table carries a margin-left
     of --cz-indent from the .customize-group rule). */
  width: calc(100% - var(--cz-indent));
  max-width: 720px;
  border-collapse: collapse;
  /* Fixed layout so the command column is a consistent width in every
     group's table and the shortcut boxes all line up. */
  table-layout: fixed;
}

.keybindings-table th,
.keybindings-table td {
  text-align: left;
  padding: 0.35rem 0.6rem;
  font-size: 0.95rem;
}

/* First cell (the command label) starts flush with the group name and
   has a fixed width, so the shortcut boxes in every group line up at
   the same x. Last cell (the shortcut box) has no trailing padding. */
.keybindings-table td:first-child {
  width: 150px;
  padding-left: 0;
}

.keybindings-table td:last-child {
  padding-right: 0;
}

.keybindings-table th {
  color: var(--editor-muted-text-color);
  font-weight: 600;
  letter-spacing: 0.03em;
  font-size: 0.8rem;
  text-transform: uppercase;
}

.keybindings-table tr:hover td {
  background-color: var(--editor-panel-separator-color);
}

.keybinding-input {
  width: 100%;
  max-width: 200px;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

.keybinding-input.capturing {
  border-color: var(--editor-selected-blue);
  outline: 1px solid var(--editor-selected-blue);
}

/* A captured chord that already belongs to another command in the same table — see
   `pendingConflict` in Customize.tsx. Sits as its own row right under the input's, so
   it reads as attached to the rebind that triggered it. */
.keybinding-conflict-row td {
  padding-top: 0;
  padding-bottom: 0.5rem;
}

.keybinding-conflict {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0.6rem;
  border-radius: var(--corner-radius-sm);
  background: color-mix(in srgb, var(--warning-color, #b26a00) 12%, transparent);
  color: var(--editor-text-color);
  font-size: 0.85rem;
}

.keybinding-conflict-overwrite,
.keybinding-conflict-cancel {
  flex: 0 0 auto;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: 0.8rem;
  cursor: pointer;
}

.keybinding-conflict-overwrite {
  border-color: var(--warning-color, #b26a00);
  color: var(--warning-color, #b26a00);
}

/* Colors tab */

/* Each color row: a clear gap between the description and its swatch
   (matching the keybindings layout), a generous row gap for space
   around each pair. */
.color-list {
  display: grid;
  grid-template-columns: auto auto;
  justify-content: start;
  gap: 1rem 1.5rem;
  align-items: center;
  padding: 0.25rem 0;
}

.color-row-label {
  font-size: 0.95rem;
  text-align: left;
}

.color-row-input {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

/* One-of-these group for enumerated engine defaults (e.g. Has on creation) — the row
   that holds a SegmentedChoice. */
.customize-radio-group {
  gap: 0.9rem;
}

.color-row-input input[type="color"] {
  width: 44px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background: transparent;
  cursor: pointer;
}

.color-row-input input[type="text"] {
  width: 120px;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

/* Numeric defaults on the Engine tab share the colour-row grid. */
.color-row-input input[type="number"] {
  width: 78px;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: 0.85rem;
}

/* Range slider used in the Engine Defaults grid (e.g. highlighter opacity). */
.engine-slider {
  width: 120px;
}

.engine-slider-value {
  min-width: 2.6rem;
  font-size: 0.9rem;
  color: var(--customize-hint-color, var(--editor-muted-text-color));
}

/* Engine tab: the "reset just these defaults" action sits below the
   groups, indented to line up with them. */
.customize-engine-reset {
  margin: 1.75rem 0 0 var(--cz-indent);
}

/* Each theme (light / dark) is its own section in the Colors tab. Its
   color rows are split into titled groups of related colors: the
   section title spans the column, each group's title sits one indent
   step in, and the swatches sit one step further (matching the
   Layout tab's section / detail hierarchy). */
.color-theme-section {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.color-theme-section > .customize-section-title {
  margin-bottom: 0.85rem;
  text-align: left;
}

.color-theme-section > .color-group {
  margin-left: var(--cz-indent);
}

.color-group {
  margin-bottom: 1.1rem;
}

.color-group:last-child {
  margin-bottom: 0;
}

/* Light-mode customizations:
   - Tab strip and tab titles a touch darker than the muted defaults
     so the navigation reads with a bit more weight.
   - Section hint text darker so descriptions don't look so faint.
   - Theme-option tiles keep their original near-white surface so they
     stand out against the now-grayish page background, but their
     label text is darkened slightly. */
html[data-theme="light"] {
  --customize-page-bg: var(--canvas-background-color);
  /* Three distinct levels in the tab strip:
     - selected tab is the brightest (true white)
     - unselected tabs sit a step darker
     - the strip area outside the tabs is darker still */
  --customize-tab-strip-bg: #b1afb8;
  --customize-tab-bg-inactive: #cfcdd5;
  --customize-tab-bg-active: #ffffff;
  --customize-tab-color: #46454e;
  --customize-tab-color-active: #2c2b33;
  --customize-hint-color: #6a6973;
  --theme-option-bg: #ffffff;
  --theme-option-text: #2c2b33;
}

/* ── Tools toolbar layout (Layout tab) ──
   The tool palette's own sections: drag one by its handle to reorder, clear a
   box to leave a section (or a single tool) off the palette. Rows read as
   cards like the Plugins list, so the two "what does this app show me"
   settings look like one another. */
.toolbar-sections {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 520px;
}
.toolbar-section-row {
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: 6px;
  padding: 8px 10px;
  background: var(--editor-input-bg, transparent);
}
/* A section that is switched off still lists (it is the only way back on), but
   it should not read as part of the palette. */
.toolbar-section-off {
  opacity: 0.55;
}
/* Where a dragged section would land. */
.toolbar-section-row.drop-onto {
  box-shadow: inset 0 0 0 2px var(--editor-selected-blue);
}
.toolbar-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.toolbar-section-handle {
  cursor: grab;
  user-select: none;
  color: var(--editor-muted-text-color);
  font-size: 0.95rem;
  line-height: 1;
  padding: 2px 2px;
}
.toolbar-section-handle:active {
  cursor: grabbing;
}
.toolbar-section-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  user-select: none;
}
.toolbar-section-toggle input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 0;
}
.toolbar-section-name {
  font-weight: 600;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
}
.toolbar-section-count {
  margin-left: auto;
  font-size: 0.78rem;
  color: var(--editor-muted-text-color);
}
.toolbar-section-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 6px 0 0 26px;
}
.toolbar-tool-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  cursor: pointer;
  user-select: none;
}
.toolbar-tool-toggle input[type="checkbox"] {
  width: 14px;
  height: 14px;
  margin: 0;
}
.toolbar-tool-toggle input:disabled {
  cursor: default;
}
/* The Selection tool can't be switched off — it reads as settled, not broken. */
.toolbar-tool-locked {
  color: var(--editor-muted-text-color);
  cursor: default;
}
.toolbar-sections-reset {
  margin-top: 10px;
  padding: 0.3rem 0.7rem;
  font-size: 0.85rem;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  color: var(--editor-text-color);
  cursor: pointer;
}
.toolbar-sections-reset:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Dark mode: same three-level hierarchy — the selected tab merges
   into the panel below, unselected tabs are a step darker, and the
   strip "frame" is darker still. Tile surfaces use the top-toolbar
   color so the clickable tiles read as elevated controls on the
   slightly brighter panel background. */
html[data-theme="dark"] {
  --customize-tab-strip-bg: #0c0e12;
  --customize-tab-bg-inactive: #14161b;
  --customize-tab-bg-active: var(--editor-panel-background-color);
  --theme-option-bg: var(--top-toolbar-background-color);
}

/* The consent step, which is the one screen in this app whose whole job is to be READ.
   Framed rather than inline: a person is being asked to agree to something, and a block that
   looks like part of the list around it reads as a list item rather than as a question. */
.plugin-consent {
    margin: 10px 0;
    padding: 12px 14px;
    border: 1px solid var(--editor-accent-color, #4a90d9);
    border-radius: 6px;
    background: var(--editor-panel-bg, rgba(127, 127, 127, 0.06));
}
.plugin-consent-title { margin: 0 0 4px; font-size: 13px; font-weight: 600; }
.plugin-consent-replaces { font-weight: 400; opacity: 0.8; }
.plugin-consent-meta { margin: 0 0 8px; font-size: 11.5px; opacity: 0.8; }
.plugin-consent-lead { margin: 8px 0 4px; font-size: 12px; font-weight: 600; }
/* The permissions themselves: spaced out and left-marked, so the list is countable at a
   glance. A person who cannot tell how much they are agreeing to agrees to everything. */
.plugin-consent-list { margin: 0 0 8px; padding-left: 18px; font-size: 12px; line-height: 1.5; }
.plugin-consent-list li { margin: 2px 0; }

/* Home page. Layout mirrors the customize page: a dark header bar
   pinned at the top with action buttons, then a scrollable main area
   below that holds the project grid. */

#home-div.home-div {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  margin: 0;
  padding: 0;
  background-color: var(--home-content-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  text-align: center;
  box-sizing: border-box;
}

/* Top action bar — same dimensions and surface as .customize-header. */
.home-header-div {
  position: relative;
  display: flex;
  align-items: center;
  flex: none;
  gap: 0.5rem;
  padding: 0.75rem 1.5rem;
  background-color: var(--top-toolbar-background-color);
  color: var(--toolbar-text-color);
  border-bottom: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

.home-header-spacer {
  flex: 1 1 auto;
}

/* Buttons styled to match .customize-back-button / .customize-reset-button. */
.home-buttons {
  margin: 0;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  background-color: var(--button-bg);
  color: var(--button-text);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  min-width: auto;
}

.home-buttons:hover:not(:disabled) {
  background-color: var(--button-bg-hover);
}

/* Icon-only header buttons (Add Project, Delete Project, Log Off).
   Transparent on the dark header surface, with a subtle hover/active
   tint so the click target is discoverable. The icon image is sized
   to read at toolbar scale. */
.home-icon-button {
  margin: 0;
  padding: 0;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--corner-radius);
  background-color: transparent;
  cursor: pointer;
  /* The standard toolbar glyph is 48px tall and centred; let it overflow the
     36px hit box the way the editor's .tt-btn does. */
  overflow: visible;
  transition: background-color 80ms ease-out, opacity 80ms ease-out;
  /* Icon-buttons highlight their FOREGROUND, never a background box: muted at
     rest, full-brightness glyph on hover (matches the editor rule app-wide). */
  opacity: 0.8;
}

.home-icon-button:disabled {
  cursor: default;
  opacity: 0.4;
}

.home-icon-button:hover:not(:disabled) {
  opacity: 1;
}

.home-icon-button:active:not(:disabled) {
  opacity: 1;
}

.home-icon-button-img {
  height: 28px;
  width: auto;
  user-select: none;
  pointer-events: none;
}

/* Larger variant: shared by the four primary home-page icon buttons
   (Add, Delete, Account, Log Off). Button hit area is 40x40 with 4px
   padding (32x32 inner). The account initial badge fills that inner
   exactly; plus/minus/logout sit at 22px inside the same inner with
   breathing room. */
.home-icon-button-lg {
  width: 40px;
  height: 40px;
  padding: 4px;
}

.home-icon-button-img-lg {
  height: 22px;
}

.account-initial-badge-lg {
  width: 32px;
  height: 32px;
  font-size: 16px;
}

/* Account icon-button (circular initial badge) + its dropdown menu.
   Pattern mirrors the editor TopToolbar's account button. The wrap
   provides a positioning context so the dropdown can be right-aligned
   to the button regardless of surrounding header layout. */
.home-account-wrap {
  position: relative;
  display: inline-flex;
}

.home-account-button {
  outline: 2px solid transparent;
  outline-offset: -2px;
  transition: background-color 80ms ease-out,
    outline-color 80ms ease-out;
}

.home-account-button-active,
.home-account-button-active:hover {
  outline-color: var(--editor-selected-blue);
  opacity: 1;
}

/* Reposition the shared `.account-menu-div` (defaulted for the editor
   TopToolbar at top:44 / right:194) so it drops directly under the
   home-page account button. Use a descendant selector to outrank the
   base single-class rule regardless of stylesheet load order. */
.home-account-wrap .account-menu-div,
.account-menu-div.home-account-menu {
  top: 100%;
  right: 0;
  left: auto;
  margin-top: 4px;
}

/* Main scrollable area: project grid lives here. Padding is symmetric
   on all four sides so the outer whitespace is balanced; tile-to-tile
   gap stays unchanged. */
.home-projects-div {
  flex: 1 1 auto;
  overflow: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-content: flex-start;
  gap: 0.75rem;
  padding: 1.25rem;
}

/* Legacy modifier — used to add header clearance when the header was
   fixed. The new layout pins the header with flex, so no extra padding
   is needed. Keeping the class as a no-op (matching the symmetric
   base padding) avoids touching the JSX. */
.home-projects-top {
  padding-top: 1.25rem;
}

.home-project-div {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Project tile: a clickable card. Light mode uses a true-white surface
   so the tile reads against the canvas-gray page; dark mode uses the
   top-toolbar surface so the tile reads as "elevated control" on the
   slightly brighter panel background. */
.home-project-group-div {
  display: block;
  /* +4px on the sides and bottom, +6px on the top, on top of the
     existing 8px base — gives the tile content a bit more room and
     a slightly larger headroom above the thumbnail. */
  padding: 14px 12px 12px;
  border-radius: var(--corner-radius);
  border: 1px solid var(--editor-control-border-color);
  background-color: var(--home-tile-bg, var(--editor-input-bg, #ffffff));
  cursor: default;
}

.home-project-group-div:focus {
  outline: 1px solid var(--editor-selected-blue);
  outline-offset: -1px;
}

.project-img {
  max-width: 220px;
  height: 150px;
  object-fit: contain;
  background-color: var(--home-project-img-bg, #f4f4f4);
}

.project-img-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 220px;
  height: 150px;
  color: var(--home-placeholder-text, #aaa);
  font-size: 0.95rem;
  font-style: italic;
  border: 2px dashed var(--home-placeholder-border, #ccc);
  border-radius: var(--corner-radius-sm);
  box-sizing: border-box;
}

.project-timestamp-p,
.project-descr-p {
  max-width: 220px;
  margin: 4px 4px 0;
  font-size: 0.85rem;
  text-align: left;
  user-select: none;
  word-wrap: break-word;
  color: var(--editor-text-color);
}

.home-status {
  margin: 0;
  padding: 0.75rem 1.5rem;
  color: #c0392b;
  flex: none;
  border-top: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
}

/* Light-mode: main project area matches the editor's canvas surface;
   project tiles use the white "clickable surface" so they pop against
   the gray page. */
html[data-theme="light"] {
  --home-content-bg: var(--canvas-background-color);
  --home-tile-bg: #ffffff;
}

/* Dark-mode: main project area uses the same panel surface as the
   customize page; project tiles take the top-toolbar surface so they
   match the elevated controls on the customize page. */
html[data-theme="dark"] {
  --home-content-bg: var(--editor-panel-background-color);
  --home-tile-bg: var(--top-toolbar-background-color);
  --home-project-img-bg: #14161b;
  --home-placeholder-text: #6f7383;
  --home-placeholder-border: #3a3d46;
}

/* The Open button on a project tile.
   Double-click used to be the only way into a project and nothing said so — the
   worst possible dead end, since it sits on the first screen of a brand-new
   account. The button is always in the DOM (screen readers and keyboard users
   get it either way, and the card itself takes Enter/Space); it becomes visible
   on hover or focus so a wall of tiles doesn't turn into a wall of buttons. */
.project-open-button {
  display: block;
  margin: 8px auto 0;
  opacity: 0;
  transition: opacity 120ms ease-in-out;
}
.home-project-group-div:hover .project-open-button,
.home-project-group-div:focus .project-open-button,
.home-project-group-div:focus-within .project-open-button {
  opacity: 1;
}
/* Keyboard focus must never be invisible — if someone tabs to the button it
   shows regardless of the hover rule above. */
.project-open-button:focus-visible {
  opacity: 1;
}

/* Bug-report / feedback dialog. Matches the app's modal house style
   (centered card on a dimmed backdrop) and is bundled globally via its
   component import. Class names are prefixed so they don't collide. */

.feedback-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 18, 24, 0.55);
    padding: 24px;
}

.feedback-modal {
    width: 100%;
    max-width: 520px;
    background: var(--editor-panel-background-color, #fff);
    color: var(--editor-text-color, #1a1a1f);
    border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
    border-radius: 12px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
    padding: 22px 24px;
    box-sizing: border-box;
}

.feedback-title {
    margin: 0 0 4px;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.feedback-subtitle {
    margin: 0 0 16px;
    font-size: 0.85rem;
    opacity: 0.7;
    line-height: 1.45;
}

/* Bug / Feedback segmented toggle. */
.feedback-kind {
    display: flex;
    gap: 8px;
    margin-bottom: 14px;
}
.feedback-kind-btn {
    flex: 1;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.18));
    border-radius: 8px;
    background: transparent;
    color: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.04s ease, filter 0.1s ease, background-color 0.1s ease;
}
.feedback-kind-btn:hover:not(.feedback-kind-active) {
    background-color: rgba(128, 128, 128, 0.12);
}
.feedback-kind-btn:active {
    transform: translateY(1px);
    filter: brightness(0.92);
}
.feedback-kind-active {
    background-color: var(--editor-selected-blue, #3b82f6);
    border-color: var(--editor-selected-blue, #3b82f6);
    color: #fff;
}

.feedback-label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 6px;
}

.feedback-textarea {
    width: 100%;
    min-height: 130px;
    resize: vertical;
    box-sizing: border-box;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.2));
    border-radius: 8px;
    background: var(--editor-input-bg, #fff);
    color: inherit;
    font-family: inherit;
    font-size: 0.92rem;
    line-height: 1.5;
}
.feedback-textarea:focus {
    outline: 2px solid var(--editor-selected-blue, #3b82f6);
    outline-offset: -1px;
}

/* Note that a diagnostic log will be attached (bug reports in the editor). */
.feedback-attach-note {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 12px 0 0;
    font-size: 0.8rem;
    opacity: 0.75;
    line-height: 1.4;
}

.feedback-status {
    margin: 12px 0 0;
    font-size: 0.85rem;
    line-height: 1.4;
}
.feedback-status.is-error { color: #dc2626; }
.feedback-status.is-ok { color: #16a34a; }

.feedback-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 20px;
}

/* Non-blocking thank-you toast shown after a successful send. Pinned to
   the bottom-center, above everything, and dismisses itself after a few
   seconds — no backdrop, no button. */
.feedback-toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    transform: translateX(-50%);
    z-index: 10001;
    max-width: min(90vw, 460px);
    padding: 0.7rem 1.1rem;
    border-radius: 10px;
    border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
    background: var(--editor-panel-background-color, #fff);
    color: var(--editor-text-color, #1a1a1f);
    box-shadow: 0 10px 32px rgba(0, 0, 0, 0.32);
    font-size: 0.9rem;
    font-weight: 600;
    text-align: center;
    cursor: default;
    animation: feedback-toast-in 0.18s ease-out;
}

@keyframes feedback-toast-in {
    from { opacity: 0; transform: translate(-50%, 8px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .feedback-toast { animation: none; }
}

/**
 * FS Designer public launch page. Replicates the FS Tracker landing styles
 * EXACTLY (lp-* rules from fs-tracker public/css/style.css) so the two product
 * landings match — same palette, buttons, spacing, cards. Everything is scoped
 * under .fs-landing (the SPA wrapper, which takes over the viewport) so none of
 * it leaks into the editor's own theming.
 */

.fs-landing {
    --bg: #0a1020;
    --surface: #131b2e;
    --text: #f1f5f9;
    --text-secondary: #cbd5e1;
    --text-muted: #94a3b8;
    --line: #1e293b;
    --line-strong: #334155;
    --accent: #3dcf5a;
    --accent-strong: #7fe095;
    --accent-soft: rgba(61, 207, 90, 0.13);
    --btn-primary: linear-gradient(180deg, #57e16d 0%, #2db546 100%);
    --btn-primary-hover: linear-gradient(180deg, #6be57e 0%, #36bf52 100%);
    --btn-primary-text: #1a472a;

    position: fixed;
    inset: 0;
    overflow-y: auto;
    z-index: 0;
    color: var(--text);
    background: linear-gradient(140deg, #1c2740 0%, #0a1020 100%) fixed;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
    -webkit-font-smoothing: antialiased;
}
.fs-landing h1, .fs-landing h2, .fs-landing h3 { font-weight: 700; }
.fs-landing p { margin: 0; }

.fs-landing .lp-wrap { max-width: 1100px; margin: 0 auto; padding: 0 1.5rem; }
.fs-landing .lp-nav { border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 50;
    background: rgba(10, 16, 32, 0.78); backdrop-filter: saturate(180%) blur(10px); }
.fs-landing .lp-nav-inner { display: flex; align-items: center; justify-content: space-between; gap: 2rem; height: 68px; }
.fs-landing .lp-brand { font-weight: 700; font-size: 1.15rem; letter-spacing: 0.01em; color: var(--text); text-decoration: none; }
.fs-landing .lp-nav-links { display: flex; align-items: center; gap: 1.6rem; }
.fs-landing .lp-nav-links a { color: var(--text-secondary); text-decoration: none; font-size: 0.95rem; }
.fs-landing .lp-nav-links a:hover { color: var(--text); }

.fs-landing .lp-btn { display: inline-flex; align-items: center; gap: 0.4rem; border-radius: 10px;
    padding: 0.6rem 1.1rem; font-weight: 600; text-decoration: none; border: 1px solid transparent;
    cursor: pointer; transition: background 140ms ease, border-color 140ms ease, transform 120ms ease, box-shadow 140ms ease; }
.fs-landing .lp-btn-primary { background: var(--btn-primary); color: var(--btn-primary-text);
    box-shadow: 0 8px 24px rgba(45, 181, 70, 0.28); }
.fs-landing .lp-btn-primary:hover { background: var(--btn-primary-hover); color: var(--btn-primary-text); transform: translateY(-1px); }
.fs-landing .lp-btn-ghost { background: transparent; color: var(--text); border-color: var(--text-secondary); }
.fs-landing .lp-btn-ghost:hover { background: var(--text); color: var(--bg); }
.fs-landing .lp-nav-links a.lp-btn-ghost { color: var(--text); }
.fs-landing .lp-nav-links a.lp-btn-ghost:hover { color: var(--bg); }
.fs-landing .lp-nav-links a.lp-btn-primary,
.fs-landing .lp-nav-links a.lp-btn-primary:hover { color: var(--btn-primary-text); }
.fs-landing .lp-btn-lg { padding: 0.85rem 1.5rem; font-size: 1.05rem; }

.fs-landing .lp-eyebrow { display: inline-block; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.12em;
    text-transform: uppercase; color: var(--accent-strong); margin-bottom: 0.9rem; }

.fs-landing .lp-badge { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0.85rem;
    border-radius: 999px; background: var(--accent-soft); color: var(--accent-strong);
    font-size: 0.85rem; font-weight: 600; margin-bottom: 1.4rem; }
.fs-landing .lp-badge .pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
    box-shadow: 0 0 0 0 rgba(61, 207, 90, 0.6); animation: lp-pulse 2s infinite; }
@keyframes lp-pulse {
    0% { box-shadow: 0 0 0 0 rgba(61, 207, 90, 0.5); }
    70% { box-shadow: 0 0 0 8px rgba(61, 207, 90, 0); }
    100% { box-shadow: 0 0 0 0 rgba(61, 207, 90, 0); }
}

.fs-landing .lp-hero { position: relative; padding: 6rem 0 4.5rem; text-align: center; overflow: hidden; }
.fs-landing .lp-hero::before { content: ''; position: absolute; top: -25%; left: 50%; transform: translateX(-50%);
    width: 900px; height: 560px; pointer-events: none;
    background: radial-gradient(closest-side, rgba(61, 207, 90, 0.16), rgba(61, 207, 90, 0) 70%); }
.fs-landing .lp-hero > .lp-wrap { position: relative; }
.fs-landing .lp-hero h1 { font-size: 3.1rem; line-height: 1.1; margin: 0 0 1.2rem; letter-spacing: -0.02em; font-weight: 700; }
.fs-landing .lp-hero h1 .lp-accent { color: var(--accent-strong); }
.fs-landing .lp-sub { font-size: 1.2rem; color: var(--text-secondary); max-width: 720px; margin: 0 auto 2rem; line-height: 1.55; }
.fs-landing .lp-cta { display: flex; gap: 0.9rem; justify-content: center; flex-wrap: wrap; }
.fs-landing .lp-cta-note { color: var(--text-muted); font-size: 0.9rem; margin-top: 1rem; }

.fs-landing .lp-features { padding: 4.5rem 0 5rem; border-top: 1px solid var(--line); }
.fs-landing .lp-features + .lp-features { padding-top: 3.5rem; }
.fs-landing .lp-features-head { text-align: center; margin-bottom: 2.6rem; }
.fs-landing .lp-features-head h2 { font-size: 2rem; margin: 0; letter-spacing: -0.01em; }
.fs-landing .lp-final h2 { font-size: 2rem; margin: 0 0 0.9rem; letter-spacing: -0.01em; }

.fs-landing .lp-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.4rem; }
.fs-landing .lp-card { background: var(--surface); border: 1px solid var(--line); border-radius: 16px; padding: 1.25rem;
    transition: border-color 160ms ease, transform 160ms ease; }
.fs-landing .lp-card:hover { border-color: var(--accent); transform: translateY(-3px); }
.fs-landing .lp-card h3 { margin: 0.2rem 0 0.5rem; font-size: 1.15rem; color: var(--text); }
.fs-landing .lp-card p { color: var(--text-secondary); margin: 0; line-height: 1.55; font-size: 0.97rem; }
.fs-landing .lp-shot { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; margin-bottom: 1.05rem;
    background: #0c1322; aspect-ratio: 1400 / 720; }
.fs-landing .lp-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.fs-landing .lp-card--text { display: flex; flex-direction: column; justify-content: center;
    background: linear-gradient(180deg, rgba(61, 207, 90, 0.06), transparent); }

/* A CTA that closes a section rather than opening the page: centred, with air
   above it, so a long page never leaves the reader scrolling back to act. */
.fs-landing .lp-cta-mid { margin-top: 2.6rem; }

/* The "why this one" reasons want a WIDER card than a feature does — four of
   them in the auto-fit grid leave one orphan on a second row, and these are the
   four sentences the page most wants read. Two up, one up on a phone. */
.fs-landing .lp-grid--two { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 880px; margin: 0 auto; }
@media (max-width: 720px) { .fs-landing .lp-grid--two { grid-template-columns: 1fr; } }

/* The standfirst under a section heading — the one place a section needs a
   sentence before its cards or figures (the "where it's going" caveat). */
.fs-landing .lp-lede { color: var(--text-secondary); max-width: 760px; margin: 0.9rem auto 0;
    line-height: 1.6; font-size: 1.05rem; }

/* Large editor showcases — we have only a few, so show them big (near full
   width) for a good look, instead of cramming them into the small cards. */
.fs-landing .lp-showcase { padding: 4.5rem 0; border-top: 1px solid var(--line); }
.fs-landing .lp-show { margin: 0 auto 3.2rem; }
.fs-landing .lp-show:last-child { margin-bottom: 0; }
.fs-landing .lp-show-frame { border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
    background: #0c1322; box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5); }
.fs-landing .lp-show-frame img { display: block; width: 100%; height: auto; }
.fs-landing .lp-show figcaption { text-align: center; margin-top: 1.15rem; }
.fs-landing .lp-show figcaption h3 { font-size: 1.3rem; margin: 0 0 0.4rem; }
.fs-landing .lp-show figcaption p { color: var(--text-secondary); max-width: 640px; margin: 0 auto; line-height: 1.55; }

.fs-landing .lp-final { background: var(--surface); border-top: 1px solid var(--line); padding: 4.5rem 0; text-align: center; }
.fs-landing .lp-final p { color: var(--text-secondary); margin: 0 0 1.8rem; font-size: 1.12rem; }

.fs-landing .lp-foot { border-top: 1px solid var(--line); padding: 1.75rem 0; color: var(--text-muted); font-size: 0.9rem; }
.fs-landing .lp-foot .lp-wrap { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
.fs-landing .lp-foot a { color: var(--text-secondary); text-decoration: none; }
.fs-landing .lp-foot a:hover { color: var(--text); }

@media (max-width: 720px) {
    .fs-landing .lp-hero { padding: 4rem 0 3rem; }
    .fs-landing .lp-hero h1 { font-size: 2.1rem; }
    .fs-landing .lp-sub { font-size: 1.08rem; }
}

/* Styles for the in-app documentation site at /docs/*.
 * Reuses the tokens defined in App.css so light/dark themes apply
 * automatically through the existing data-theme attribute. */

.docs-page {
    /* Fill the .App frame instead of claiming 100vh. .App is
       position:absolute; overflow:hidden, so a 100vh child would push
       content past the clip and the system chrome could overlap the
       bottom. Anchored fills give the inner .docs-content a bounded
       height to scroll within. */
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    background-color: var(--editor-panel-background-color, #fff);
    color: var(--editor-text-color, #333);
    font-family: var(--editor-font-family, "Arial", sans-serif);
}

/* ----- Header ----- */
.docs-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.9rem 1.4rem;
    border-bottom: 1px solid var(--editor-panel-separator-color, #ececec);
}

.docs-back-controls {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.docs-back-button {
    background: transparent;
    border: none;
    color: var(--editor-text-color);
    cursor: pointer;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    /* Driven by Customize > Corner roundness. --corner-radius spans 0px
       (square) to 16px (a full circle for this 32x32 button), so the
       button visibly follows the user's preference: subtly rounded at
       the default 0.5, fully circular at the max. */
    border-radius: var(--corner-radius, 8px);
    transition: opacity 80ms ease-out, color 80ms ease-out;
    /* Icon-button foreground-highlight rule: muted at rest, full on hover, no bg. */
    opacity: 0.8;
}

.docs-back-button:hover,
.docs-back-button:active {
    opacity: 1;
}

/* Granular back: smaller, lighter sibling of the major back button. Steps
   back through doc topics; disabled once we're at the entry topic. */
.docs-back-button-granular {
    width: 28px;
    height: 28px;
}

.docs-back-button-granular:disabled {
    opacity: 0.35;
    cursor: default;
}

.docs-back-button-granular:disabled:hover {
    background-color: transparent;
}

.docs-back-icon {
    display: block;
    pointer-events: none;
}

.docs-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--header-1-color, #2a2935);
}

/* Push the audience switcher to the right end of the header. */
.docs-header > .docs-audience-switch {
    margin-left: auto;
}

/* Three-segment audience switcher. */
.docs-audience-switch {
    display: inline-flex;
    border: 1px solid var(--editor-control-border-color, #d0d0d0);
    border-radius: var(--corner-radius, 6px);
    overflow: hidden;
}

.docs-audience-button {
    background: transparent;
    border: none;
    padding: 0.45rem 0.95rem;
    color: var(--editor-text-color);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    border-right: 1px solid var(--editor-control-border-color, #d0d0d0);
    transition: background-color 80ms ease-out, color 80ms ease-out;
}

.docs-audience-button:last-child {
    border-right: none;
}

.docs-audience-button:hover {
    background-color: rgba(16, 151, 226, 0.12);
    color: var(--editor-selected-blue, rgb(16, 151, 226));
}

.docs-audience-button:active {
    background-color: rgba(16, 151, 226, 0.22);
}

.docs-audience-button-active,
.docs-audience-button-active:hover {
    background-color: var(--editor-selected-blue, rgb(16, 151, 226));
    color: #fff;
}

.docs-audience-button-active:active {
    background-color: rgb(13, 121, 181);
}

/* ----- Account button + dropdown ----- */
.docs-account-button {
    background: transparent;
    border: none;
    padding: 4px;
    margin-left: 0.6rem;
    border-radius: var(--corner-radius, 6px);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: opacity 80ms ease-out, outline-color 80ms ease-out;
    outline: 2px solid transparent;
    outline-offset: -2px;
    /* Icon-button foreground-highlight rule: muted at rest, full on hover, no bg. */
    opacity: 0.8;
}

.docs-account-button:hover,
.docs-account-button:active {
    opacity: 1;
}

.docs-account-button-active,
.docs-account-button-active:hover {
    outline-color: var(--editor-selected-blue, rgb(16, 151, 226));
    opacity: 1;
}

/* Reposition the shared `.account-menu-div` for the docs header. Defaults
 * in App.css target the editor's TopToolbar; we want the menu to drop
 * under the account button at the right edge of the docs header. The
 * compound selector outranks the base single-class rule regardless of
 * stylesheet load order. */
.account-menu-div.docs-account-menu {
    top: 56px;
    right: 1.4rem;
    left: auto;
}

/* ----- Body grid ----- */
.docs-body {
    display: grid;
    grid-template-columns: 260px minmax(0, 1fr);
    gap: 0;
    flex: 1;
    min-height: 0;
}

/* ----- Sidebar ----- */
.docs-sidebar {
    padding: 1.2rem 1rem 2rem;
    border-right: 1px solid var(--editor-panel-separator-color, #ececec);
    overflow-y: auto;
}

.docs-sidebar-home {
    display: block;
    padding: 0.4rem 0.6rem;
    margin-bottom: 1rem;
    color: var(--editor-text-color);
    text-decoration: none;
    font-weight: 600;
    border-radius: var(--corner-radius-sm, 4px);
    transition: background-color 80ms ease-out, color 80ms ease-out;
}

.docs-sidebar-home:hover {
    background-color: rgba(16, 151, 226, 0.10);
    color: var(--editor-selected-blue, rgb(16, 151, 226));
}

.docs-sidebar-home:active {
    background-color: rgba(16, 151, 226, 0.20);
}

.docs-sidebar-home.active {
    background-color: rgba(16, 151, 226, 0.14);
    color: var(--editor-selected-blue, rgb(16, 151, 226));
}

.docs-sidebar-section {
    margin-bottom: 1.2rem;
}

.docs-sidebar-section-heading {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--header-3-color, #6f6e7c);
    padding: 0 0.6rem;
    margin-bottom: 0.35rem;
}

.docs-sidebar-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.docs-sidebar-link {
    display: block;
    padding: 0.35rem 0.6rem;
    color: var(--editor-text-color);
    text-decoration: none;
    font-size: 0.9rem;
    border-radius: var(--corner-radius-sm, 4px);
    transition: background-color 80ms ease-out, color 80ms ease-out;
}

.docs-sidebar-link:hover {
    background-color: rgba(16, 151, 226, 0.10);
    color: var(--editor-selected-blue, rgb(16, 151, 226));
}

.docs-sidebar-link:active {
    background-color: rgba(16, 151, 226, 0.22);
}

.docs-sidebar-link-active,
.docs-sidebar-link-active:hover {
    background-color: rgba(16, 151, 226, 0.14);
    color: var(--editor-selected-blue, rgb(16, 151, 226));
    font-weight: 600;
}

.docs-sidebar-link-active:active {
    background-color: rgba(16, 151, 226, 0.26);
}

/* ----- Content ----- */
.docs-content {
    padding: 2rem 2.4rem 4rem;
    overflow-y: auto;
}

.docs-article {
    max-width: 760px;
    line-height: 1.55;
}

.docs-article h1 {
    margin: 0 0 1.1rem;
    font-size: 1.9rem;
    font-weight: 700;
    color: var(--header-1-color, #2a2935);
}

.docs-article h2 {
    margin: 2rem 0 0.6rem;
    font-size: 1.3rem;
    font-weight: 600;
    color: var(--header-2-color, #3b3a45);
}

.docs-article h3 {
    margin: 1.4rem 0 0.45rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--header-2-color, #3b3a45);
}

.docs-article p {
    margin: 0.55rem 0 1rem;
}

.docs-article ul,
.docs-article ol {
    margin: 0.55rem 0 1rem;
    padding-left: 1.4rem;
}

.docs-article li {
    margin: 0.3rem 0;
}

.docs-article code {
    background-color: rgba(0, 0, 0, 0.05);
    padding: 0.05rem 0.35rem;
    border-radius: 3px;
    font-family: "Menlo", "Consolas", monospace;
    font-size: 0.88em;
}

.docs-article pre {
    background-color: rgba(0, 0, 0, 0.06);
    padding: 0.9rem 1rem;
    border-radius: var(--corner-radius-sm, 4px);
    overflow-x: auto;
    line-height: 1.4;
}

.docs-article pre code {
    background: none;
    padding: 0;
}

.docs-article kbd {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    background-color: var(--editor-panel-background-color, #fff);
    border: 1px solid var(--editor-control-border-color, #d0d0d0);
    border-bottom-width: 2px;
    border-radius: 3px;
    font-family: "Menlo", "Consolas", monospace;
    font-size: 0.82em;
    color: var(--editor-text-color);
}

/* Inline links inside article body — including the audience-mismatch
 * "Overview" link and any future cross-references. */
.docs-article a {
    color: var(--editor-selected-blue, rgb(16, 151, 226));
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    transition: color 80ms ease-out, background-color 80ms ease-out;
}

.docs-article a:hover {
    color: rgb(13, 121, 181);
    background-color: rgba(16, 151, 226, 0.08);
}

.docs-article a:active {
    color: rgb(9, 92, 138);
    background-color: rgba(16, 151, 226, 0.18);
}

/* ----- Landing-page extras ----- */
.docs-lead {
    font-size: 1.05rem;
    color: var(--editor-text-color);
}

.docs-audience-cards {
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem;
    margin: 1.5rem 0 2rem;
}

.docs-audience-card {
    flex: 1 1 0;
    min-width: 0;
    text-align: left;
    font: inherit;
    color: inherit;
    border: 1px solid var(--editor-panel-separator-color, #ececec);
    border-radius: var(--corner-radius, 6px);
    padding: 0.85rem 0.95rem;
    background-color: rgba(0, 0, 0, 0.015);
    cursor: pointer;
    transition: background-color 80ms ease-out, border-color 80ms ease-out,
        box-shadow 80ms ease-out;
}

.docs-audience-card:hover {
    background-color: rgba(16, 151, 226, 0.06);
    border-color: var(--editor-selected-blue, rgb(16, 151, 226));
}

.docs-audience-card:active {
    background-color: rgba(16, 151, 226, 0.14);
}

.docs-audience-card h3 {
    margin: 0 0 0.3rem;
    font-size: 1rem;
}

.docs-audience-card p {
    margin: 0;
    font-size: 0.9rem;
    color: var(--header-3-color, #6f6e7c);
}

.docs-audience-card-active,
.docs-audience-card-active:hover {
    border-color: var(--editor-selected-blue, rgb(16, 151, 226));
    background-color: rgba(16, 151, 226, 0.08);
}

.docs-topic-group {
    margin: 1rem 0 1.6rem;
}

.docs-topic-group-heading {
    margin: 0 0 0.4rem;
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--header-3-color, #6f6e7c);
    border-bottom: 1px solid var(--editor-panel-separator-color, #ececec);
    padding-bottom: 0.3rem;
}

.docs-topic-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.docs-topic-list li {
    padding: 0.35rem 0;
}

.docs-topic-list a {
    color: var(--editor-selected-blue, rgb(16, 151, 226));
    text-decoration: none;
    font-weight: 500;
    padding: 0.15rem 0.35rem;
    margin: 0 -0.35rem;
    border-radius: var(--corner-radius-sm, 4px);
    transition: background-color 80ms ease-out, color 80ms ease-out;
}

.docs-topic-list a:hover {
    text-decoration: underline;
    background-color: rgba(16, 151, 226, 0.08);
}

.docs-topic-list a:active {
    background-color: rgba(16, 151, 226, 0.18);
    color: rgb(13, 121, 181);
}

/* ----- Audience-mismatch hint ----- */
.docs-audience-mismatch {
    border: 1px dashed var(--editor-control-border-color, #d0d0d0);
    border-radius: var(--corner-radius, 6px);
    padding: 1rem 1.2rem;
    color: var(--header-3-color, #6f6e7c);
    background-color: rgba(0, 0, 0, 0.02);
}

.docs-audience-inline-switch {
    background: transparent;
    border: none;
    padding: 0;
    color: var(--editor-selected-blue, rgb(16, 151, 226));
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    text-decoration: underline;
    transition: color 80ms ease-out;
}

.docs-audience-inline-switch:hover {
    color: rgb(13, 121, 181);
}

.docs-audience-inline-switch:active {
    color: rgb(9, 92, 138);
}

/* ----- Tables (keyboard shortcuts) ----- */
.docs-shortcut-table {
    border-collapse: collapse;
    margin: 0.6rem 0 1.4rem;
    width: 100%;
}

.docs-shortcut-table th,
.docs-shortcut-table td {
    text-align: left;
    padding: 0.45rem 0.7rem;
    border-bottom: 1px solid var(--editor-panel-separator-color, #ececec);
    vertical-align: top;
}

.docs-shortcut-table th {
    font-weight: 600;
    color: var(--header-2-color, #3b3a45);
    background-color: rgba(0, 0, 0, 0.025);
}

/* ----- FAQ ----- */
.docs-faq {
    margin: 0.6rem 0 0;
}

.docs-faq-item {
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--editor-panel-separator-color, #ececec);
}

.docs-faq dt {
    font-weight: 600;
    color: var(--header-2-color, #3b3a45);
    margin-bottom: 0.35rem;
}

.docs-faq dd {
    margin: 0;
}

/* ----- Narrow viewports: collapse sidebar ----- */
@media (max-width: 760px) {
    .docs-body {
        grid-template-columns: 1fr;
    }
    .docs-sidebar {
        border-right: none;
        border-bottom: 1px solid var(--editor-panel-separator-color, #ececec);
        padding: 0.8rem 1rem;
    }
    .docs-content {
        padding: 1.4rem 1.2rem 3rem;
    }
    .docs-header {
        flex-wrap: wrap;
        gap: 0.7rem;
    }
}

/* Callout box in a docs article (e.g. an "Experimental" warning). */
.docs-callout {
    padding: 0.7rem 0.9rem;
    border-radius: 8px;
    line-height: 1.5;
}
.docs-callout-warn {
    color: var(--editor-text-color, #1a1a1f);
    background: color-mix(in srgb, #f5a623 12%, transparent);
    border: 1px solid color-mix(in srgb, #f5a623 42%, transparent);
}

/* The generated back-end × database grid (SupportGrid). Small, dense, and readable
   in both themes — it is a fact table, not a feature chart. */
.docs-support-grid { margin: 12px 0 18px; overflow-x: auto; }
.docs-support-grid table { border-collapse: collapse; font-size: 0.92rem; }
.docs-support-grid th,
.docs-support-grid td {
    padding: 6px 12px;
    text-align: center;
    border: 1px solid var(--editor-control-border-color, #3a3a3a);
}
.docs-support-grid thead th,
.docs-support-grid tbody th { text-align: left; font-weight: 600; }
.docs-support-note {
    margin-top: 6px;
    font-size: 0.85rem;
    opacity: 0.8;
    max-width: 70ch;
}
/* Visually hidden, still read aloud — the ticks above are decorative. */
.docs-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Verify-email banner anchored at the top of the viewport above all
 * other content. position: fixed + z-index keeps it above the editor
 * canvas and other absolute-positioned page chrome. */

.verify-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.45rem 0.9rem;
    background-color: #f59e0b;
    color: #1f1100;
    font-size: 0.88rem;
    font-weight: 500;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}

.verify-banner-text { flex: 1 1 auto; }

.verify-banner-button {
    appearance: none;
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: var(--corner-radius-sm, 4px);
    background-color: #0a1020;
    color: #ffffff;
    padding: 0.3rem 0.75rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.verify-banner-button:hover:not(:disabled) {
    background-color: #131b2e;
}

.verify-banner-button:disabled {
    opacity: 0.55;
    cursor: default;
}

.verify-banner-status {
    font-size: 0.82rem;
    opacity: 0.85;
}

/* Full-screen overlay shown when the backend server is unreachable.
   It sits above every route via a high z-index so it covers the editor,
   home page, etc. without unmounting them — the user's work is preserved
   underneath and reappears on reconnect. The background is fully opaque
   so the page behind it (login screen, editor, etc.) is hidden: it can't
   be interacted with while offline and is irrelevant until reconnect.
   Colors come from editor theme variables to match the active theme. */

.disconnected-page {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--editor-panel-background-color, #1e1e1e);
  color: var(--editor-text-color, #eee);
  font-family: var(--editor-font-family, Arial, Helvetica, sans-serif);
}

/* Content sits in a bordered box that is darker than the page behind
   it, so it reads as a distinct panel rather than blending into the
   surrounding background. */
.disconnected-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  width: 22rem;
  max-width: 90vw;
  padding: 2.5rem 2rem;
  border: 1px solid var(--editor-control-border-color, #444);
  border-radius: 12px;
  background-color: var(--editor-input-bg, #15171c);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  text-align: center;
}

/* Spinning accent ring — gives the screen motion and signals that
   reconnection is actively in progress. */
.disconnected-spinner {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 3px solid var(--editor-control-border-color, #444);
  border-top-color: var(--editor-selected-blue, #1097e2);
  animation: disconnected-spin 0.9s linear infinite;
}

@keyframes disconnected-spin {
  to { transform: rotate(360deg); }
}

.disconnected-heading {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
}

.disconnected-message {
  margin: 0;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--editor-muted-text-color, #8e8c9e);
}

/* Matches the standard `.buttons` style used on the login/signup pages. */
.disconnected-retry {
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  margin-top: 0.25rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius);
  cursor: pointer;
  background-color: var(--button-bg);
  color: var(--button-text);
}

.disconnected-retry:hover:not(:disabled) {
  background-color: var(--button-bg-hover);
}

.disconnected-retry:disabled {
  opacity: 0.55;
  cursor: default;
}

.disconnected-attempts {
  margin: 0;
  font-size: 0.8rem;
  color: var(--editor-muted-text-color, #8e8c9e);
}

/* Editor design tokens shared by toolbar, panels, and canvas layout. */
:root {
  --tree-width: 255px;
  --details-width: 350px;   /* the width it opens at (DETAILS_PANEL_START_WIDTH) */
  --standard-icon-height: 48px;
  --standard-icon-width: 44px;
  --top-toolbar-height: 48px;
  --left-toolbar-width: 82px;
  --chevron-left: 37px;
  --top-toolbar-left-of-zoom-icons: 120px;
  --top-toolbar-background-color: #282c34;
  --left-toolbar-background-color: #d8d6d6;
  --toolbar-text-color: #dedede;
  --editor-text-color: #5A5964;
  --editor-muted-text-color: #8e8c9e;
  /* header-1 is the most-prominent header; it must be DARKER than the
     panel background in light mode. Old #f4f4f6 was a stray dark-mode
     value that rendered tab text white-on-white. */
  --header-1-color: #2a2935;
  --header-2-color: #3b3a45;
  --header-3-color: #6f6e7c;
  --editor-panel-background-color: white;
  --editor-panel-separator-color: #ececec;
  --editor-control-border-color: #cccbd8;
  --editor-control-border: 1px solid var(--editor-control-border-color);
  --editor-selected-blue: rgb(16, 151, 226);
  /* Per-panel theme accents (each user-configurable in Customize): code green,
     data magenta, prototype amber. Lightened in dark mode for contrast. */
  --code-accent: #15825a;
  --data-accent: #b0208a;
  --prototype-accent: #e07b39;
  /* A filled solid code button uses --code-accent-btn (a muted green that never
     glares) with white --code-accent-fg text. --code-accent itself stays for accent
     TEXT / lines, which need to stay bright on the dark canvas in dark mode. */
  --code-accent-btn: #15825a;
  --code-accent-fg: #fff;
  /* Same split for prototype: --prototype-accent (a light amber) is used for
     accent TEXT / borders / icons, but white text on it fails contrast on a
     filled button — so filled prototype buttons use the darker
     --prototype-accent-btn with white --prototype-accent-fg text (≈4.8:1). */
  --prototype-accent-btn: #b8560f;
  --prototype-accent-fg: #fff;
  /* Subtle 1px hairline for code-view chrome (the editor ↔ console separator, etc.). */
  --editor-border-color: rgb(200, 200, 200);
  --editor-hover-color: #5fd8c0;
  --editor-constraint-line-color: #b2b2b2;
  /* Reference-point badge — kept in sync (by applyPreferences) with the same
     referenceBadgeColor / referenceBadgeAccent options that drive the on-canvas
     marker, so the panel mirror and the canvas badge always match. */
  --reference-badge-color: #29e06c;
  --reference-badge-accent: #1097e2;
  --tools-group-columns: 2;
  --canvas-background-color: var(--editor-panel-separator-color);
  --panel-border-color: rgb(200, 200, 200);
  --tree-selection-bg: rgb(194, 232, 255);
  /* Clickable / editable surface — true white in light mode so buttons
     and inputs stand out against the now-grayish page background.
     Dark mode redefines this further down. */
  --editor-input-bg: #ffffff;
  /* Code-editor pane syntax-highlight palette (light theme). */
  --syn-comment: #6a9955;
  --syn-keyword: #0f3fbd;
  --syn-string: #a3402a;
  --syn-number: #098658;
  --syn-boolean: #0f3fbd;
  --syn-tag: #7a1f1f;
  --syn-attr: #7a4a12;
  --syn-prop: #0451a5;
  --syn-punct: #6b6b76;
  --editor-font-family: Arial, Helvetica, sans-serif;
  --editor-control-font-size: 14px;
  /* Shared height for panel edit boxes + drop-downs: line-height + 2·pad-y +
     2·border ≈ 24px. Kept as line-height/padding (not an explicit `height`) so
     control widths and box-sizing are never disturbed. */
  --editor-control-line-height: 20px;
  --editor-control-pad-y: 2px;
  --editor-icon-muted-opacity: 0.6;
  --editor-small-icon-size: 18px;
  --editor-swatch-size: 18px;
  /* Design-panel row spacing. Rows HUG their content (whatever their tallest
     component needs); the space BETWEEN rows is standardized to --details-row-gap
     via symmetric top/bottom padding of half that on each row, so adjacent rows
     sit exactly --details-row-gap apart. EVERY stacked element must carry this
     half-padding — a row (or graphic block) that omits it only gets its
     neighbour's half, reading as gap/2. Set by the "Inter-row gap" Customize
     control (preferences.ts overrides --details-row-gap at runtime). */
  --details-row-gap: 12px;
  --details-row-pad: calc(var(--details-row-gap) / 2);
  /* List / tree-view row spacing — the same hug-plus-half-padding scheme as
     --details-row-gap, but a SEPARATE knob so dense name lists (Pages / Layers,
     the item / data / prototype / files trees, asset lists, prototype-panel
     lists) can be tighter than the design-panel form rows. Set by the "List &
     tree-view row gap" Customize control (preferences.ts overrides it at
     runtime); defaults a little tighter than --details-row-gap. */
  --list-row-gap: 8px;
  --list-row-pad: calc(var(--list-row-gap) / 2);
  --details-content-width: calc(var(--details-width) - var(--panel-indent));
  --panel-edge-gap: 8px;
  --panel-triangle-size: 12px;
  --panel-triangle-title-gap: 4px;
  /* Single indent step. Level N content lives at N * --panel-indent from the
     panel's left edge. Disclosure triangles for Level N occupy the band from
     (N-1)*panel-indent to N*panel-indent. The 12px image is offset 1px to
     the right of the band's natural center, so the left/right padding around
     it is 7/10 — visible room on each side, slight rightward weight. */
  --panel-indent: 28px;
  --panel-level-2: calc(2 * var(--panel-indent));
  --panel-level-3: calc(3 * var(--panel-indent));
  --panel-triangle-pad-left: 8px;
  --panel-triangle-pad-right: 8px;
  /* Legacy alias — now tracks the single inter-row gap so every row-spacing
     mechanism (flex row-gap, row padding) resolves to one value. */
  --panel-row-gap: var(--details-row-gap);
  --panel-control-gap: 6px;
  --panel-pair-gap: 20px;
  --panel-right-gutter: 12px;
  --panel-checkbox-label-gap: 8px;
  /* THE LABEL COLUMN for a labelled panel row — the left edge every control in a block
     starts from. One value, so rows written at different times still share an edge: the
     effect / texture / gradient detail rows (`.effect-detail-row`, a grid) and a stacked
     paint's Blend row (`.blend-row`, a flex row) both take their label width from here.
     A stacked fill's Blend used to carry its own 56px, which put its select and its
     opacity box on an edge of their own, half a character off the texture rows directly
     beneath it in the very same section. Don't re-state this number locally. */
  --panel-label-col: 6em;
  /* Default status-bar height. Overridden by preferences.ts: set to
     0px when the bar is hidden, ~26px when it's visible. Editor panels
     anchor their bottom edges to this value. */
  --status-bar-height: 26px;
  /* Justification of numeric edit boxes. Overridden by preferences.ts. */
  --number-input-align: right;
  /* Corner radii driven by the corner-roundness slider (preferences.ts);
     0px at the square end. */
  --corner-radius: 8px;
  --corner-radius-sm: 3px;
  /* Selection outline/border width for selectable icons. */
  --selection-border-width: 2px;
  /* Action-button surface. Light mode uses a light grey (not white)
     with dark-enough text; dark mode overrides below. */
  --button-bg: #e6e6ea;
  --button-bg-hover: #d8d8de;
  --button-text: #45444e;
}

/* Root application frame. */
p {
  font-size: var(--editor-control-font-size);
}

.App {
  position: absolute;
  overflow: hidden;
  /* The verify-email banner is position: fixed at the viewport top; push
     .App (and every absolute-positioned page inside it) down by the
     banner's height so it doesn't cover the home header / editor top
     toolbar. The variable is set by VerifyBanner when visible, removed
     when hidden — defaulting to 0 keeps every other page intact. */
  top: var(--verify-banner-height, 0px);
  bottom: 0px;
  left: 0px;
  right: 0px;
  background-color: var(--editor-panel-background-color);
}

/* The editor's chrome is an instrument, not a document.
 *
 * Dragging across a panel used to select its labels the way it would select
 * prose — you could highlight "SHAPES" in the tool rail, or half the project
 * name — and because nothing declared a cursor there, the browser showed an
 * I-beam over every one of them. Both symptoms were reported together, and
 * they are one cause: `user-select` was switched off piecemeal, on ~87
 * separate rules, so anything nobody remembered stayed selectable.
 *
 * Scoped to the editor's own regions rather than `.App`, which also wraps the
 * docs site, Home and the landing page — there, selecting text is the point.
 */
.top-toolbar,
.left-toolbar,
.tree-div,
.details-div,
.status-bar {
  user-select: none;
  -webkit-user-select: none;
  cursor: default;
}

/* Anything genuinely typed into, or meant to be copied, opts back in. A
 * cursor declared on the element itself beats the inherited one above, so
 * buttons and splitters keep the cursors their own rules already give them.
 *
 * `pre` and `code` are here because the panels really do show code the reader
 * needs to take away — the compiled flow in FlowDiagramsPanel, the read-only
 * source in CodeEditorPane, the console's output. That is content, not chrome,
 * and un-selecting it would trade one complaint for a worse one. `.allow-select`
 * is the escape hatch for anything else that turns out to be readable matter. */
:is(.top-toolbar, .left-toolbar, .tree-div, .details-div, .status-bar)
  :is(input, textarea, select, [contenteditable="true"], pre, code, .allow-select) {
  user-select: text;
  -webkit-user-select: text;
  cursor: auto;
}

/* Top toolbar: navigation, project metadata, zoom controls, and account actions. */
/* Home, Tracker and the logo flow relatively in the toolbar's flexbox (see the
   relative-positioning rule below); no per-button left offsets. */
.home-icon {
  position: relative;
}

.tracker-icon {
  position: relative;
}

.logo-icon {
  height: 30px;
  display: block;
}

/* File menu, opened by the top-toolbar logo button. Sits just below the
   toolbar, left-aligned under the logo. */
.logo-menu-div {
  position: absolute;
  top: 44px;
  left: 84px;
  width: 180px;
  z-index: 60;
  padding: 4px 0;
  color: var(--toolbar-text-color);
  background-color: var(--top-toolbar-background-color);
  border-radius: var(--corner-radius-sm);
  border: 1px solid rgba(0, 0, 0, 0.45);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.13), 0 4px 12px rgba(0, 0, 0, 0.35);
}

.logo-menu-item-div {
  padding: 6px 14px;
  cursor: pointer;
}

.logo-menu-item-div:hover {
  background-color: rgba(255, 255, 255, 0.14);
}

.logo-menu-item-div p {
  font-family: var(--editor-font-family);
  font-size: 10pt;
  margin: 0;
  text-align: left;
  user-select: none;
}

.top-toolbar-text {
  color: var(--toolbar-text-color);
  font-family: var(--editor-font-family);
  font-size: 10pt;
  margin: 0px;
}

/* Transparent full-screen layer behind an open top-toolbar menu — a click
   anywhere off the menu lands here and closes it. */
.menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 55;
}

/* Account dropdown, opened by the top-toolbar user icon. Sits just below
   the toolbar, horizontally centred under the user icon: the icon button
   spans right 266..302px (.user-icon right: 266px + .tt-btn 36px wide), so
   its centre is at right 284px; a 180px-wide menu centred there starts at
   right 194px. */
.account-menu-div {
  position: absolute;
  top: 44px;
  right: 194px;
  width: 180px;
  z-index: 60;
  padding: 4px 0;
  color: var(--toolbar-text-color);
  background-color: var(--top-toolbar-background-color);
  border-radius: var(--corner-radius-sm);
  border: 1px solid rgba(0, 0, 0, 0.45);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.13), 0 4px 12px rgba(0, 0, 0, 0.35);
}

.account-menu-item-div {
  padding: 6px 14px;
  cursor: pointer;
}

/* Version label at the foot of the account menu; clickable to open What's New. */
.account-menu-version {
  padding: 6px 14px 4px;
  margin-top: 4px;
  border-top: 1px solid var(--panel-border-color, rgba(255, 255, 255, 0.14));
  font-family: var(--editor-font-family);
  font-size: 9pt;
  text-align: left;
  opacity: 0.55;
  user-select: none;
  cursor: pointer;
}
.account-menu-version:hover {
  opacity: 0.9;
}

/* What's New dialog content (shell reuses the feedback modal). */
.whatsnew-list {
  max-height: 55vh;
  overflow-y: auto;
  margin: 10px 0;
}
.whatsnew-entry {
  margin-bottom: 14px;
}
.whatsnew-version {
  font-weight: 600;
  margin-bottom: 4px;
}
.whatsnew-notes {
  margin: 0;
  padding-left: 18px;
}
.whatsnew-notes li {
  margin: 3px 0;
}

.account-menu-item-div:hover {
  background-color: rgba(255, 255, 255, 0.14);
}

.account-menu-item-div p {
  font-family: var(--editor-font-family);
  font-size: 10pt;
  margin: 0;
  text-align: left;
  user-select: none;
}

.project-name-textbox {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  box-sizing: border-box;
  font-size: 13pt;
  font-weight: 600;
  padding: 0;
  background-color: var(--top-toolbar-background-color)
}

/* Project name flows in .project-info's flex column (justify-center), so it
   sits vertically centred in the toolbar. Bigger than the surrounding toolbar
   text to read as the document title. */
.project-name {
  position: relative;
  font-size: 13pt;
  font-weight: 600;
  line-height: 1.1;
}

.zoom-percentage {
  position: relative;
  user-select: none;
}

.zoom-percentage-textbox {
  /* In-flow (not absolute) so the flex cluster's align-items:center vertically
     centers the edit box in the toolbar — exactly like the static % readout it
     replaces. When absolute, the auto-height .zoom-text-div collapsed and the
     box hung below the centerline. */
  width: 100%;
  padding: 0;
  box-sizing: border-box;
  background-color: var(--top-toolbar-background-color);
  text-align: center;
}

/* Right-side toolbar cluster. Element right-edge offsets are tuned so the
   36px icon buttons, the share button, and the zoom read-out all sit 8px
   apart. Order from the right edge: zoom −, zoom %, zoom +, play, share,
   user, notifications, help. */
.zoom-text-div {
  position: absolute;
  top: 17px;
  right: 56px;
  text-align: center;
  width: 50px;
  height: 50px;
}

.zoom-minus-icon {
  position: absolute;
  right: 114px;
  user-select: none;
}

.zoom-plus-icon {
  position: absolute;
  right: 12px;
  user-select: none;
}

.play-icon {
  position: absolute;
  top: 0;
  right: 158px;
}
/* The inlined play glyph strokes with currentColor so it tracks the configurable Prototype
   accent — the one toolbar button with a colour of its own, because Play IS the prototype.
   Qualified with .tt-btn ON PURPOSE: .tt-btn sets `color` too (further down this file, so it
   wins at equal specificity) to stop the UA's default button colour reaching the glyphs, and
   that quietly turned the play triangle into an ordinary toolbar grey. */
.tt-btn.play-icon {
  color: var(--prototype-accent);
}

/* Share — the invite-link button, in the slot the right cluster always reserved for it
   (zoom +, zoom %, zoom −, play, SHARE, user, notifications, help). Plain icon button like
   its siblings; the .share-button rules that used to live here styled a text presentation
   that no longer exists anywhere. */
.share-icon {
  position: absolute;
  top: 0;
  right: 202px;
}

.user-icon {
  position: absolute;
  top: 0;
  right: 266px;
}

/* Circular initial badge inside the account button. Matches the size and
   fill of the previous static user.svg circle so the toolbar layout is
   unchanged, but the letter is the signed-in user's first initial. */
.account-initial-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  /* A hollow white circle at the standard toolbar-icon height and line width,
     like the other glyphs — with the initial letter also the standard white.
     The border matches the shared 3px glyph stroke. */
  width: 23px;
  height: 23px;
  border-radius: 50%;
  background-color: transparent;
  border: 3px solid var(--toolbar-text-color);
  color: var(--toolbar-text-color);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  user-select: none;
}

.notifications-icon {
  position: absolute;
  top: 0;
  right: 310px;
}

/* Help-mode toggle, sat just left of the notifications icon. */
.help-mode-icon {
  position: absolute;
  top: 0;
  right: 354px;
  cursor: pointer;
}

/* The Boss-area crown — only rendered when the signed-in user is a
   Boss, so the slot is empty for everyone else and the other right-
   cluster icons keep their existing positions. Sits just left of the
   help-mode toggle. */
.boss-icon {
  position: absolute;
  top: 0;
  right: 398px;
}

/* When help mode is active the question-mark button stays highlighted. */
/* Button-like container for the clickable top-toolbar icons. It is inset
   from the toolbar's top and bottom — leaving roughly equal space above and
   below — and rounds with the corner-roundness preference, like the
   left-toolbar tools. The icon image keeps its own size and is allowed to
   overflow the (transparent) button box. */
.tt-btn {
  /* A <button> does NOT inherit `color` — the UA sets its own (white under a dark
     colour-scheme, black under a light one). The inline SVG glyphs stroke with
     `currentColor`, so without this they came out #fff next to their #dedede
     siblings in dark mode, and BLACK on this permanently-dark toolbar in light
     mode. Anchor it to the toolbar's own text token so every glyph matches. */
  color: var(--toolbar-text-color);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  top: 6px;
  padding: 0;
  margin: 0;
  border: none;
  background-color: transparent;
  border-radius: var(--corner-radius);
  cursor: pointer;
  overflow: visible;
  -webkit-appearance: none;
  appearance: none;
  /* App-wide rule: icon-buttons highlight their FOREGROUND, never a background
     box. Muted at rest, full-brightness glyph on hover (matches the tool / panel
     / align icons). */
  opacity: 0.8;
  transition: opacity 0.1s ease;
}

.tt-btn > img {
  display: block;
}

/* Theme switcher, centred in the toolbar independent of the left/right group widths. */
.top-toolbar-center {
  position: absolute;
  left: 50%;
  top: 6px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
}
.tt-btn.tt-btn-disabled {
  opacity: 0.35;
  cursor: default;
}
.tt-btn.tt-btn-disabled:hover {
  opacity: 0.35;
}

.tt-btn:hover {
  opacity: 1;
}

/* A toggled top-toolbar button stays highlighted while its state is on,
   marked with a selection-coloured border like a selected tool — the
   help-mode toggle while help mode is on, and the account button while
   its menu is open. */
.help-mode-icon-active,
.help-mode-icon-active:hover,
.user-icon-active,
.user-icon-active:hover,
.logo-btn-active,
.logo-btn-active:hover {
  /* Toggled-on state: a selection-blue outline (structural), full-brightness
     glyph, and NO background fill — consistent with the foreground-only rule. */
  opacity: 1;
  outline: var(--selection-border-width) solid var(--editor-selected-blue);
  outline-offset: calc(-1 * var(--selection-border-width));
}

/* Logo button: an icon-button in the same family as the other top-toolbar
   buttons, but width-hugging the (wider-than-tall) logo image. Sits just
   right of the home button and opens the File menu. */
.logo-btn {
  position: relative;
  width: auto;
  padding: 0 8px;
}

/* Icon sizing for the small zoom buttons. */
.tt-zoom-icon {
  height: 16px;
  user-select: none;
}

.standard-top-toolbar-icon {
  /* Rendered at exactly the base height so the 48-unit viewBox maps 1 unit = 1px
     — a clean pixel grid, so axis-aligned strokes stay crisp. The larger "+2px"
     sizing is baked into each glyph's own coordinates (not a CSS scale), so no
     glyph lands on fractional pixels. */
  height: var(--standard-icon-height);
  user-select: none;
}
/* The bug glyph fills more of its 44x48 canvas than the sibling toolbar icons,
   so at the standard height it reads taller — trim it to match the others.
   The glyph also sits high in its canvas (more empty space below than above),
   so nudge it down ~1px to sit vertically centred in the button. */
.bug-icon img {
  height: calc(var(--standard-icon-height) * 0.84);
  transform: translateY(1px);
}

.home-div {
  position: absolute;
  top: 0;
  left: 0;
  user-select: none;
}

.top-toolbar {
  position: absolute;
  box-sizing: border-box;
  background-color: var(--top-toolbar-background-color);
  height: var(--top-toolbar-height);
  width: 100%;
  border-bottom: 1px solid var(--panel-border-color, var(--editor-panel-separator-color));
  /* Above the editor panels and their resize handles (z-index 10..30) so
     the toolbar's dropdown menus (logo / account) layer on top of them. */
  z-index: 40;
  /* Dynamic layout: a left group and a right group flow and space
     themselves instead of being pinned at fixed pixel offsets. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
  gap: 8px;
}

/* Left group (Home, Tracker, Logo, project info) and right group (Help,
   Notifications, Account, Share, Play, zoom). */
.top-toolbar-group {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* Device version picker (Desktop / Tablet / Phone), beside the theme switch.
   Single-choice, so it reads as a segmented bar — the same shape Look & Feel
   uses for its device preview, not a row of loose buttons. */
.device-view-selector {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--editor-control-border-color, var(--editor-border-color));
  border-radius: 6px;
  overflow: hidden;
}

.device-view-option {
  border: none;
  background: transparent;
  color: var(--editor-text-color);
  font-size: 12px;
  line-height: 1;
  padding: 5px 9px;
  cursor: pointer;
  white-space: nowrap;
}

.device-view-option:hover:not(.is-current) {
  background: var(--editor-hover-background-color, rgba(127, 127, 127, 0.18));
}

.device-view-option.is-current {
  background: var(--accent-color, #7a5cff);
  color: #fff;
}

/* Wraps a menu-trigger button (logo / account) so its dropdown anchors to it. */
.tt-menu-anchor {
  position: relative;
  display: flex;
  align-items: center;
}

/* Zoom out / readout / zoom in, kept together. */
.tt-zoom-cluster {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Project name + date stacked just right of the logo. */
.project-info {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  margin-left: 4px;
  min-width: 120px;
}

/* The left-side buttons and the project info text flow RELATIVELY in the
   flexbox now; clear any legacy absolute offsets. Higher specificity than the
   original per-element left:/right: rules. */
.top-toolbar-group .tt-btn,
.top-toolbar-group .share-button,
.top-toolbar .project-name {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
}

.top-toolbar .zoom-text-div {
  position: relative;
  top: auto;
  right: auto;
  height: auto;
}

/* Editable name textbox overlays the name within .project-info. */
.top-toolbar .project-name-textbox {
  top: 0;
  left: 0;
}

/* Dropdown menus anchor directly under their trigger button. */
.tt-menu-anchor .logo-menu-div {
  top: 100%;
  left: 0;
}

.tt-menu-anchor .account-menu-div {
  top: 100%;
  right: 0;
}

/* Left tool palette and resizable toolbar rail. */
.left-toolbar {
  position: absolute;
  background-color: var(--left-toolbar-background-color);
  top: var(--top-toolbar-height);
  left: 0px;
  width: var(--left-toolbar-width);
  bottom: var(--status-bar-height, 0px);
  display: flex;
  flex-direction: column;
  z-index: 10;
}

.left-toolbar-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* Always-on cursor-coordinate readout at the foot of the left toolbar. Compact
   grid (axis label + right-aligned value per row, unit below) so it fits the
   narrow toolbar. */
.cursor-coords-readout {
  flex: 0 0 auto;
  padding: 6px 5px;
  border-top: 1px solid var(--editor-control-border-color);
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1px 4px;
  align-items: baseline;
  font-size: 10px;
  color: var(--editor-muted-text-color);
}
/* Inline form for the editor status bar: one compact row, no border/padding. */
.cursor-coords-inline {
  display: flex;
  padding: 0;
  border-top: none;
  gap: 4px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.cursor-coords-inline .cursor-coords-val { min-width: 0; }
.cursor-coords-axis {
  text-align: left;
}
.cursor-coords-val {
  text-align: right;
  color: var(--editor-text-color);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cursor-coords-unit {
  grid-column: 1 / -1;
  text-align: right;
  opacity: 0.8;
}

/* Coordinate label pinned under the crosshair while the Coordinates tool is
   active. `left`/`top` are the cursor's screen point; the translate nudges the
   label below-right so it doesn't sit under the crosshair itself. */
.cursor-coords-canvas-label {
  position: absolute;
  transform: translate(18px, 18px);
  padding: 5px 10px;
  font-size: 16px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--editor-text-color);
  background: var(--editor-input-bg);
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  pointer-events: none;
  z-index: 20;
}

/* Ghost pin that replaces the cursor while placing a pin: anchored by its
   bottom-centre (the tip) at the cursor point. */
.pin-ghost-cursor {
  position: absolute;
  transform: translate(-50%, -100%);
  pointer-events: none;
  overflow: visible;
  z-index: 21;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
}

/* On-canvas tooltip for an annotation Pin's message, anchored above the marker
   (see PinTooltip). Dark chip so it reads over any design; never intercepts the
   pointer. */
.pin-tooltip {
  position: absolute;
  transform: translate(-50%, calc(-100% - 14px));
  max-width: 340px;
  padding: 9px 13px;
  font-size: 16px;
  line-height: 1.4;
  white-space: pre-wrap;
  color: #fff;
  background: rgba(28, 30, 36, 0.94);
  border-radius: 8px;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  z-index: 21;
}
/* The author suffix on a pin tooltip — dimmer than the message. */
.pin-tooltip-author {
  opacity: 0.7;
  font-style: italic;
}

/* On-canvas "fit height" button for a selected sticky Note (NoteFitButton), pinned just
   outside the note's top-right corner. Icon-button convention: no background box — the
   GLYPH brightens on hover (App.css's global active-translate gives the press nudge). The
   artwork is black strokes, so it inverts in dark mode like every other editor icon. */
.note-fit-btn {
  position: absolute;
  transform: translate(4px, -50%);
  padding: 3px;
  border: none;
  background: none;
  line-height: 0;
  cursor: pointer;
  z-index: 21;
}
.note-fit-btn img {
  display: block;
  width: 16px;
  height: 16px;
  opacity: var(--editor-icon-muted-opacity);
  /* The note under it can be any sticky colour, so the glyph carries its own separation. */
  filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.9));
}
.note-fit-btn:hover img {
  opacity: 1;
}
[data-theme="dark"] .note-fit-btn img {
  filter: invert(1) brightness(1.2) drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
}

/* Live collaborator cursors — a coloured pointer + name pill at the peer's
   world position. Non-interactive; sits above the canvas. */
.remote-cursor {
  position: absolute;
  transform: translate(-2px, -2px);
  pointer-events: none;
  z-index: 22;
  will-change: left, top;
}
.remote-cursor svg {
  display: block;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}
.remote-cursor-label {
  position: absolute;
  left: 14px;
  top: 12px;
  padding: 1px 6px;
  border-radius: 8px;
  font-size: 11px;
  line-height: 1.5;
  white-space: nowrap;
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* Collaborator strip in the toolbar centre (CollabPeers): one coloured initial per person —
   click to follow — and Follow Me. Only rendered in a live session. */
.collab-peers {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: 12px;
}
.collab-peer {
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid transparent;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
}
.collab-peer-following {
  border-color: #fff;
  box-shadow: 0 0 0 2px var(--peer-color);
}
.collab-follow-me {
  height: 26px;
  padding: 0 10px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--toolbar-text-color);
  background: transparent;
  color: var(--toolbar-text-color);
  font-size: 0.8rem;
  white-space: nowrap;
  cursor: pointer;
}
.collab-follow-me-active {
  border-color: var(--editor-selected-blue);
  background: var(--editor-selected-blue);
  color: #fff;
}

/* While following someone (or leading everyone): a frame in that person's colour round the
   canvas, and a banner with Stop. The frame passes clicks through; only the button takes one. */
.follow-frame {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  border: 3px solid var(--peer-color);
  pointer-events: none;
  z-index: 23;
}
.follow-banner {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 4px 3px 10px;
  border-radius: 12px;
  background: var(--peer-color);
  color: #fff;
  font-size: 12px;
  white-space: nowrap;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
  pointer-events: auto;
}
.follow-banner button {
  border: 0;
  border-radius: 9px;
  padding: 2px 9px;
  background: rgba(255, 255, 255, 0.28);
  color: #fff;
  font-size: 11px;
  cursor: pointer;
}

/* The outline over an item a collaborator has claimed — see ClaimOutlines. Covers the canvas
   and passes every event through: it is a label on somebody else's work, not a target. */
.claim-outlines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 22;
  overflow: visible;
}

/* THE MIRROR SHIELD. While following somebody, their screen is shown on yours and yours is
   read-only — so one pane covers the WHOLE window (toolbar, panels, trees and canvas alike)
   and takes every pointer event. It is nearly transparent rather than invisible: a screen you
   cannot type into has to LOOK like one, or the first thing that happens is somebody clicking
   at it and deciding the app has frozen. Only the banner inside it is clickable, because the
   way out of a mode must never be underneath the thing blocking you. */
.mirror-shield {
  position: fixed;
  inset: 0;
  z-index: 9990;
  cursor: not-allowed;
  border: 3px solid var(--peer-color);
  box-sizing: border-box;
  background: color-mix(in srgb, var(--peer-color) 7%, transparent);
}
.mirror-shield-banner {
  position: absolute;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 5px 4px 12px;
  border-radius: 13px;
  background: var(--peer-color);
  color: #fff;
  font-size: 12px;
  white-space: nowrap;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
  cursor: default;
}
.mirror-shield-banner button {
  border: 0;
  border-radius: 10px;
  padding: 3px 10px;
  background: rgba(255, 255, 255, 0.28);
  color: #fff;
  font-size: 11px;
  cursor: pointer;
}
.mirror-shield-banner button:hover {
  background: rgba(255, 255, 255, 0.42);
}
.mirror-shield-banner button:active {
  transform: translateY(1px);
}

/* Share dialog (invite links). Mirrors the feedback modal house style. */
.share-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 18, 24, 0.55);
  padding: 24px;
}
.share-modal {
  width: 100%;
  max-width: 460px;
  box-sizing: border-box;
  background: var(--editor-panel-background-color, #fff);
  color: var(--editor-text-color, #1a1a1f);
  border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  padding: 22px 24px;
}
.share-title { margin: 0 0 4px; font-size: 1.15rem; font-weight: 700; display: flex; align-items: center; gap: 8px; }

.share-subtitle { margin: 0 0 16px; font-size: 0.85rem; opacity: 0.7; line-height: 1.45; }
.share-create-row { display: flex; gap: 8px; align-items: center; }
.share-role-select {
  flex: 0 0 auto;
  padding: 6px 8px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
}
.share-create-btn, .share-copy-btn, .share-close-btn {
  padding: 6px 14px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-selected-blue);
  background: var(--editor-selected-blue);
  color: #fff;
  cursor: pointer;
  font-size: 0.85rem;
}
.share-create-btn:disabled { opacity: 0.6; cursor: default; }
.share-link-row { display: flex; gap: 8px; margin-top: 14px; }
.share-link-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 8px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  font-size: 0.8rem;
}
.share-copy-btn { flex: 0 0 auto; }
/* Email Link is an <a href="mailto:"> so the browser hands it to the mail app; dressed as its
   neighbouring button. */
.share-email-btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  white-space: nowrap;
}
.share-error { margin: 10px 0 0; font-size: 0.8rem; color: #e5484d; }
.share-list { margin-top: 18px; }
.share-list-head { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.6; margin-bottom: 6px; }
.share-list-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; font-size: 0.85rem; }
.share-list-role { flex: 0 0 auto; font-weight: 600; }
.share-list-meta { flex: 1 1 auto; opacity: 0.6; }
.share-revoke-btn {
  flex: 0 0 auto;
  padding: 3px 10px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  background: transparent;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 0.8rem;
}
.share-revoke-btn:hover { border-color: #e5484d; color: #e5484d; }
.share-actions { display: flex; justify-content: flex-end; margin-top: 20px; }
.share-close-btn { background: transparent; color: var(--editor-text-color); border-color: var(--editor-control-border-color); }

/* Version History dialog (File ▸ Version History). Mirrors the share dialog's
   modal chrome; the list is taller because history is the point of the dialog,
   so it scrolls inside the modal rather than growing the page. */
.vh-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 18, 24, 0.55);
  padding: 24px;
}
.vh-modal {
  width: 100%;
  max-width: 520px;
  max-height: 80vh;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background: var(--editor-panel-background-color, #fff);
  color: var(--editor-text-color, #1a1a1f);
  border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  padding: 22px 24px;
}
.vh-title { margin: 0 0 4px; font-size: 1.15rem; font-weight: 700; }
.vh-subtitle { margin: 0 0 14px; font-size: 0.85rem; opacity: 0.7; line-height: 1.45; }
.vh-error { margin: 0 0 10px; font-size: 0.8rem; color: #e5484d; }
.vh-empty { margin: 0 0 10px; font-size: 0.85rem; opacity: 0.7; }
.vh-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  border-top: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.12));
}
.vh-row {
  padding: 9px 6px;
  border-bottom: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.08));
  font-size: 0.85rem;
}
/* The live version is called out rather than merely un-restorable, so the list
   answers "where am I" without the user counting rows. */
.vh-row-current { background: color-mix(in srgb, var(--editor-selected-blue) 8%, transparent); }
.vh-row-main { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vh-row-time { font-weight: 600; }
.vh-row-label {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.75rem;
  color: var(--editor-text-color);
  background: color-mix(in srgb, var(--editor-selected-blue) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--editor-selected-blue) 45%, transparent);
}
.vh-row-current-badge {
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 7px;
  border-radius: 999px;
  color: #fff;
  background: var(--editor-selected-blue);
}
.vh-row-meta { display: flex; gap: 10px; margin-top: 2px; font-size: 0.78rem; opacity: 0.62; }
/* "What changed" — the semantic diff between a version and the one it replaced. The lines read
   as prose, so they are left aligned and unstyled beyond a quiet mark for added / removed. */
.vh-changes-toggle {
  margin-left: auto;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.vh-changes { margin: 6px 0 2px; padding-left: 10px; border-left: 2px solid currentColor; opacity: 0.85; }
.vh-changes-note { font-size: 0.78rem; opacity: 0.7; }
.vh-changes-group { margin-bottom: 6px; }
.vh-changes-area {
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.6;
  margin-bottom: 1px;
}
.vh-change { font-size: 0.8rem; line-height: 1.4; }
.vh-change-added::before { content: "+ "; opacity: 0.7; }
.vh-change-removed::before { content: "− "; opacity: 0.7; }
.vh-change-changed::before { content: "· "; opacity: 0.7; }
.vh-row-actions, .vh-row-confirm, .vh-row-name-edit {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 7px;
}
.vh-confirm-text { flex: 1 1 auto; font-size: 0.8rem; }
.vh-name-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 8px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  font-size: 0.8rem;
}
.vh-btn {
  flex: 0 0 auto;
  padding: 3px 12px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  background: transparent;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 0.8rem;
}
.vh-btn:hover { border-color: var(--editor-selected-blue); color: var(--editor-selected-blue); }
.vh-btn:disabled { opacity: 0.6; cursor: default; }
.vh-btn-primary {
  border-color: var(--editor-selected-blue);
  background: var(--editor-selected-blue);
  color: #fff;
}
.vh-btn-primary:hover { color: #fff; }
.vh-note { margin: 12px 0 0; font-size: 0.75rem; opacity: 0.55; line-height: 1.45; }
.vh-actions { display: flex; justify-content: flex-end; margin-top: 16px; }
.vh-close-btn {
  padding: 6px 14px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  background: transparent;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 0.85rem;
}

/* Save-status dot on the top-toolbar Save button (continuous autosave). */
.save-btn-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.save-status-dot {
  position: absolute;
  right: 1px;
  bottom: 3px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid var(--top-toolbar-background-color);
  background: #30a46c;              /* saved (green) */
  pointer-events: none;
}
.save-status-unsaved .save-status-dot { background: #f5a623; }   /* amber */
.save-status-offline .save-status-dot { background: #e5484d; }   /* red */
.save-status-saving .save-status-dot {
  background: #0091ff;                                            /* blue, pulsing */
  animation: save-dot-pulse 1s ease-in-out infinite;
}
@keyframes save-dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* Crash-recovery restore prompt (top-centre of the canvas). */
.restore-prompt {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 24;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 12px;
  font-size: 13px;
  border-radius: 10px;
  color: var(--editor-text-color);
  background: var(--editor-panel-background-color);
  border: 1px solid var(--editor-selected-blue);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
}
.restore-prompt-text { font-weight: 500; }
.restore-prompt-btn {
  padding: 4px 12px;
  border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  background: transparent;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 12px;
}
.restore-prompt-restore {
  border-color: var(--editor-selected-blue);
  background: var(--editor-selected-blue);
  color: #fff;
}

/* "View only" banner — shown on the canvas for a viewer-role collaborator. */
.view-only-banner {
  position: absolute;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 23;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 13px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 16px;
  color: #7a4b00;
  background: color-mix(in srgb, #f5a623 88%, #fff);
  border: 1px solid color-mix(in srgb, #f5a623 70%, #000 6%);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  pointer-events: auto;
}
.view-only-eye { font-size: 13px; }

/* On-canvas annotation author filter (top-left chip + popover). */
.annotation-filter {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 20;
}
.annotation-filter-chip {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 14px;
  border: 1px solid var(--editor-control-border-color);
  background: var(--editor-panel-background-color);
  color: var(--editor-text-color);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.annotation-filter-chip:hover { border-color: var(--editor-selected-blue); }
.annotation-filter-pop {
  margin-top: 6px;
  min-width: 180px;
  max-height: 260px;
  overflow-y: auto;
  padding: 6px;
  border-radius: 8px;
  border: 1px solid var(--editor-panel-separator-color);
  background: var(--editor-panel-background-color);
  box-shadow: 0 8px 28px -10px rgba(0, 0, 0, 0.4);
}
.annotation-filter-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--editor-text-color);
}
.annotation-filter-row:hover { background: color-mix(in srgb, var(--editor-selected-blue) 14%, transparent); }
.annotation-filter-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.annotation-filter-count { opacity: 0.6; font-variant-numeric: tabular-nums; }

/* Read-only "by <name>" attribution row in the annotation panels. */
.annotation-author-name {
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
.annotation-author-date {
  opacity: 0.75;
}

.left-toolbar-scroll,
.item-tree-scroll,
.panel-tab-scroll,
.details-scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.left-toolbar-scroll::-webkit-scrollbar,
.item-tree-scroll::-webkit-scrollbar,
.panel-tab-scroll::-webkit-scrollbar,
.details-scroll::-webkit-scrollbar {
  display: none;
}

/* The Project and Assets tabs scroll as a whole — no visible bar, same as every
   other panel scroller here. Each fills what the tab strip leaves (min-height:0
   so a flex child may actually shrink) and clips horizontally, so a wide field
   can never turn this into a two-axis scroller. */
.panel-tab-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.tools-group {
  display: grid;
  /* AUTO-FILL, so the buttons re-flow as the toolbar is dragged wider or narrower —
     live, without waiting for React. It used to be a fixed count from a preference, so
     dragging the handle changed the toolbar's WIDTH and left the buttons in two columns
     with dead space beside them. The drag snaps to whole columns (snapToolbarWidth) and
     writes the count it landed on back to the preference, so the two agree either way. */
  grid-template-columns: repeat(auto-fill, 38px);
  justify-content: start;
  padding-left: 2px;
  padding-right: 2px;
  padding-bottom: 4px;
  gap: 2px;
  border-width: 0 0 1px;
  border-bottom: 1px;
  border-style: solid;
  border-color: #b8b8b8;
}

.tools {
  min-height: min-content;
}

.left-toolbar-resize-handle {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 8px;
  cursor: col-resize;
  z-index: 30;
  touch-action: none;
}

/* Tool button grid and active/hover states. */
.hdr {
  grid-column: 1/-1;
  font: 600 10px/1.2 system-ui;
  letter-spacing: .04em;
  color: #8b93a0;
  opacity: var(--editor-icon-muted-opacity);
  text-transform: uppercase;
  text-align: center;
  margin: 2px 0 2px;
}

/* Tactile press feedback: an enabled button (and a tab) nudges down 1px while
   held, so a click reads as a physical press. Uses the independent `translate`
   property (not the `transform` shorthand) so it composes with any element that
   already sets a transform, and doesn't touch existing transitions. List / tree
   rows are deliberately excluded (they're selection + drag targets, not buttons). */
button:not(:disabled):active,
.tabs h3:active {
  translate: 0 1px;
}

/* Light mode: the left-toolbar section labels read a touch darker. */
[data-theme="light"] .hdr {
  color: #6e7480;
}

.tools button {
  aspect-ratio: 1/1;
  width: 38px;
  padding: 1px;
  display: grid;
  place-items: center;
  border-radius: var(--corner-radius);
  border: 0;
  background: #f4f3f3;
  opacity: 0.5;
}

/* Edit badge: a small "E" marker at the lower-right of a tool's icon while the
   matching item (a line, connection, or pen) is being edited. Colour is a CSS
   var, so it adapts to dark mode. */
.tool-edit-badge {
  position: absolute;
  right: 1px;
  bottom: 1px;
  width: 14px;
  height: 14px;
  display: grid;
  place-items: center;
  pointer-events: none;
  color: var(--editor-selected-blue, #1097e2);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}

.tool-icon {
  position: relative;
  width: 36px;
  margin: 0;
  padding: 0;
  /* Normal at rest; full brightness on hover and for the selected tool. */
  opacity: 0.9;
  border-radius: var(--corner-radius);
  user-select: none;
}

.tool {
  aspect-ratio: 1/1;
  border-radius: var(--corner-radius);
  display: grid;
  place-items: center;
  background: #ffffff14;
  /* default tile */
}

.tool:hover {
  outline: var(--selection-border-width) solid #6ea8ff;
  outline-offset: calc(-1 * var(--selection-border-width));
}

/* Selected tool: keep the icon glyph visible (don't flood the cell
   with a saturated color), and mark the state with a 2px selection
   outline at full opacity plus a slightly lifted neutral background. */
.tool[aria-pressed="true"] {
  background: var(--tool-active-bg, #e3e8f2);
  outline: var(--selection-border-width) solid var(--editor-selected-blue);
  outline-offset: calc(-1 * var(--selection-border-width));
  color: inherit;
}

.tool:focus-visible {
  outline: var(--selection-border-width) solid var(--input-focus-outline, #fff);
  outline-offset: calc(-1 * var(--selection-border-width));
}

.left-toolbar-item-div {
  position: relative;
  border-radius: var(--corner-radius);
}

/* Mouseover highlight for toolbar tools: brighten the glyph (foreground) only,
   no background box (see the .tool-icon opacity rule below). The selected tool
   keeps its .left-toolbar-item-active blue outline. */

/* Hovered and selected tools also get a full-brightness glyph. */
.left-toolbar-item-div:hover .tool-icon,
.left-toolbar-item-active .tool-icon {
  opacity: 1;
}

/* Currently-selected tool. Marked structurally by a 2px selection-blue outline
   and a full-brightness glyph — no background fill (foreground-only rule). */
.left-toolbar-item-active {
  outline: var(--selection-border-width) solid var(--editor-selected-blue);
  outline-offset: calc(-1 * var(--selection-border-width));
}

/* Left layers panel: page list and visual item tree. */
.tree-div {
  position: absolute;
  top: var(--top-toolbar-height);
  left: var(--left-toolbar-width);
  bottom: var(--status-bar-height, 0px);
  width: var(--tree-width);
  text-align: left;
  background-color: var(--editor-panel-background-color);
  border-right: 1px solid var(--panel-border-color);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 10;
}

/* Wrapper around the left panel's content (below the tabs). A flex column that
   fills the remaining height so the tree scroll areas inside still flex; also the
   element the auto-width measurement shrinks to read the content's natural extent. */
.left-panel-content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The Flow Diagrams panel is the Code-tab Assets pane and, unlike the other
   tabs, is rendered bare (not inside a plain .item-tree-scroll wrapper) — it owns
   its own header + scroll. It HUGS its content (flex-grow:0) so the Sequences /
   Use-cases / State-machines sections sit directly below it instead of being pushed
   to the bottom of the panel by a greedy fill. It stays a flex column that can still
   SHRINK (flex-shrink:1, min-height:0) when the panel is full, so its tree list
   (.item-tree-scroll, flex:1 1 auto; min-height:0) becomes height-bounded and scrolls
   with a hidden scrollbar rather than pushing the sections below it off-screen. */
.left-panel-content > .flow-diagrams,
.diagrams-panel-body > .flow-diagrams {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.left-panel-content > .flow-diagrams > .details-header,
.diagrams-panel-body > .flow-diagrams > .details-header {
  flex: 0 0 auto;
}

/* Diagrams sub-tabs: the strip stays fixed, the active type panel fills the rest and
   scrolls (the seq-style panels scroll as a whole; the flow panel bounds its own tree). */
.diagrams-panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.diagrams-panel-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.diagrams-panel-body > .seq-panel {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.page-div {
  width: 100%;
  color: var(--editor-text-color);
  flex: 0 0 auto;
  /* Bottom padding = half the inter-line gap so the last page row clears the
     divider/spacer below by the standard gap. */
  padding: 5px 0 var(--list-row-pad);
}

.page-triangle-img {
  padding: 4px var(--panel-triangle-pad-right) 4px var(--panel-triangle-pad-left);
  opacity: var(--editor-icon-muted-opacity);
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  vertical-align: middle;
  cursor: pointer;
  box-sizing: content-box;
}

/* Section (Level-1) disclosure triangle. Element fills the 0..panel-indent
   band; the 12px image sits at 7..19 within it (1px right of the band's
   natural center), with --panel-triangle-pad-right of space to the title. */
.section-triangle-img {
  padding: 4px var(--panel-triangle-pad-right) 4px var(--panel-triangle-pad-left);
  opacity: var(--editor-icon-muted-opacity);
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  vertical-align: middle;
  cursor: pointer;
  box-sizing: content-box;
  position: relative;
  z-index: 25;
}

.separator {
  border-color: var(--editor-panel-separator-color);
  border-width: 0px 0px 2px 0px;
  border-style: solid;
}

/* Header row holding the disclosure triangle, the "Page" title, and the
   add / delete icons — laid out with flex so everything sits on a shared
   vertical centre line. margin-right: auto on the title pushes the icons
   to the right. padding-right keeps them clear of the 12px-wide
   .left-panel-resize-handle that overlays the panel's right edge, so the
   icons stay hoverable / clickable instead of being captured by it. */
.page-header-row {
  display: flex;
  align-items: center;
  /* 10px right gap nudges the -/+ buttons right (was 14px, then 12px). */
  padding-right: 10px;
}

.page-heading {
  margin: 0 auto 0 0;
  user-select: none;
  vertical-align: middle;
  color: var(--header-2-color);
}

.page-generate-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.page-list-plus-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.minus-variable-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.page-list-minus-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.page-list-ul {
  list-style-type: none;
  margin: 0px;
  padding: 0px;
  /* The first page row's own top padding provides the gap under the header;
     no extra margin (that made the under-header gap too big). */
  margin-top: 0px;
  margin-bottom: 0px;
  padding-left: 3px;
  font-size: var(--editor-control-font-size);
}

/* Per-layer disclosure triangle — toggles that layer's opacity + blend sub-row.
   An IN-FLOW slot immediately left of the name (not pinned to the far-left
   gutter) so the triangle + name move together as one level: when group nesting
   adds levels, each row's indent shifts both. The slot is exactly one
   --panel-indent wide (the glyph aligned under the section triangle), so the name
   that follows lands at level 1 (--panel-indent) — its own left padding is zeroed
   (.layer-name below). */
.layer-disclosure {
  flex: 0 0 auto;
  width: var(--panel-triangle-size);
  height: var(--panel-triangle-size);
  margin-left: calc(var(--panel-triangle-pad-left) - 3px);
  margin-right: calc(var(--panel-indent) - var(--panel-triangle-pad-left) - var(--panel-triangle-size));
  opacity: var(--editor-icon-muted-opacity);
  cursor: pointer;
}
[data-theme="dark"] .layer-disclosure {
  filter: invert(1) brightness(1.2);
}
/* Layer name: the triangle slot supplies the level-1 indent, so drop the name's
   own left padding (keep the vertical row padding from .page-list-item-div). */
.page-list-item .layer-name {
  padding-left: 0;
}

/* Small type icon (folder / layer) between the disclosure slot and the name. */
.page-list-item .layer-type-icon {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin-right: 5px;
  opacity: var(--editor-icon-muted-opacity);
}
[data-theme="dark"] .page-list-item .layer-type-icon {
  filter: invert(1) brightness(1.2);
}

/* A layer's opacity + blend controls, revealed under its row by the disclosure
   triangle. Level 2 (the layer name is level 1): 2 × --panel-indent from the
   panel's left, minus the 3px the .page-list-ul already insets. */
.layer-props-sub {
  padding: 2px 12px 5px calc(2 * var(--panel-indent) - 3px);
  font-size: var(--editor-control-font-size);
}
.layer-props-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
}
.layer-props-row > label {
  flex: 0 0 auto;
  width: 46px;
  opacity: 0.7;
}
.layer-props-row .layer-opacity-range {
  flex: 1 1 auto;
  min-width: 0;
}
.layer-props-row .layer-opacity-val {
  flex: 0 0 auto;
  width: 34px;
  text-align: right;
  opacity: 0.7;
}
.layer-props-row .blend-mode-select {
  flex: 1 1 auto;
  width: auto;
}

.page-list-item-div {
  /* Space the page rows on the compact (dense-row) gap (was a fixed 4px). The
     left inset aligns the page / layer name under its section header — the
     header title sits one --panel-indent in (past its disclosure triangle),
     and the list is already inset 3px by .page-list-ul, so the name takes the
     remaining (--panel-indent − 3px). */
  padding: var(--list-row-pad) 0px var(--list-row-pad) calc(var(--panel-indent) - 3px);
}

/* Pages are shown as a GRID OF TILES — a fit-to-content thumbnail with the page
   name below — rather than a text list, so previews can be large without eating
   much vertical room. Own classes (NOT the shared .page-list-item*, which the
   Layers list reuses). The grid reflows by panel width: the min tile width sets
   how many columns fit, and 1fr lets them stretch to fill the row. */
.page-tiles-ul {
  list-style-type: none;
  margin: 0;
  padding: 6px 8px 4px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
  gap: 10px 8px;
  font-size: var(--editor-control-font-size);
}
.page-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  cursor: pointer;
}
.page-tile-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  box-sizing: border-box;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: var(--corner-radius-sm, 3px);
  /* The letterbox around a fit-to-content thumbnail. The image itself carries the page's
     canvas colour (renderPageThumbnail paints it); this matches, so the tile reads as one
     surface instead of a dark page floating on a white card. LayersPanel sets the variable
     from the live canvas colour, so it follows Customize and the theme. */
  background: var(--page-thumb-bg, #ffffff);
}
.page-tile-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  display: block;
}
.page-tile-empty {
  display: block;
  width: 100%;
  height: 100%;
}
.page-tile-name {
  margin-top: 3px;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--editor-text-color);
}
/* The ACTIVE (current) page: a blue ring on its thumbnail + a tinted name. */
.page-tile.active .page-tile-thumb {
  border-color: var(--editor-selected-blue);
  box-shadow: 0 0 0 1px var(--editor-selected-blue);
}
.page-tile.active .page-tile-name {
  color: var(--editor-selected-blue);
}
.page-tile.tile-dragging {
  opacity: 0.5;
}
/* Vertical insertion indicator in the gap on the left/right edge of the tile the
   cursor is over, while dragging a tile to reorder. */
.page-tile-drop-line {
  position: absolute;
  top: 2px;
  bottom: 2px;
  width: 2px;
  background: var(--editor-selected-blue);
  border-radius: 1px;
  pointer-events: none;
}
.page-tile-drop-line.before {
  left: -5px;
}
.page-tile-drop-line.after {
  right: -5px;
}

/* Page rows carry a visibility eye on the right, matching the Layers-tree eyes. */
.page-list-item {
  display: flex;
  align-items: center;
}
/* The ACTIVE layer/page — the insert target — is highlighted the same way the
   current item is highlighted in the item tree (--tree-selection-bg). */
.page-list-item.active-layer {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
  border-radius: 3px;
}
/* The active page/layer NAME is tinted with the selection colour for contrast —
   the subtle highlight background alone reads too faintly. Matches the active
   layer's name in the item tree. */
.page-list-item.active-layer .page-list-item-div {
  color: var(--editor-selected-blue);
}
.page-list-item.page-hidden .page-list-item-div,
.page-list-item.page-hidden .selected-icon,
.page-list-item.page-hidden .selected-line {
  opacity: 0.5;
}
.page-vis-eye {
  flex: 0 0 auto;
  margin-left: auto;
  /* Nudged right again (16px → 15px → 14px). */
  margin-right: 14px;
  width: 14px;
  height: 14px;
  opacity: 0.38;
  cursor: pointer;
}
.page-vis-eye.hidden {
  opacity: 0.8;
}
.page-vis-eye:hover {
  opacity: 1;
}
/* The last visible page/layer's eye can't be hidden (prevention) — dim it and
   show a not-allowed cursor so it reads as intentionally locked. */
.page-vis-eye.locked {
  opacity: 0.18;
  cursor: not-allowed;
}
.page-vis-eye.locked:hover {
  opacity: 0.18;
}
[data-theme="dark"] .page-vis-eye {
  filter: invert(1) brightness(1.2);
}

/* Layer lock — mirrors the visibility eye: a padlock with a diagonal slash when
   UNLOCKED (dim, like a shown layer's eye) and a solid padlock when locked
   (bright, like the crossed-out eye). The padlock is a smaller, denser shape
   than the sprawling eye, so the opacities are nudged past the eye's own values
   (unlocked below the eye's 0.38, locked to full) so the two read at the SAME
   apparent brightness. margin-left:auto right-aligns the lock+eye pair;
   black-stroke SVGs, inverted in dark mode like the eye. */
.page-lock-eye {
  flex: 0 0 auto;
  margin-left: auto;
  margin-right: 8px;
  width: 13px;
  height: 13px;
  /* Slashed (unlocked) lock matches a shown layer's eye brightness (0.38). */
  opacity: 0.38;
  cursor: pointer;
}
.page-lock-eye.on {
  opacity: 1;
}
.page-lock-eye:hover {
  opacity: 1;
}

/* ---- Blueprint page tree (reuses the standard tree classes: page-header-row,
   page-list-item, active-layer, layer-disclosure, layer-type-icon) ------------ */
.bp-tree-row { position: relative; cursor: pointer; user-select: none; padding-top: var(--list-row-pad); padding-bottom: var(--list-row-pad); padding-right: 6px; }
.bp-tree-empty { font-size: 11px; color: var(--editor-muted-text-color); font-style: italic; margin: 1px 0 3px; }
.bp-tree-row .bp-disclosure-spacer { flex: 0 0 auto; width: var(--panel-triangle-size); margin-left: calc(var(--panel-triangle-pad-left) - 3px); margin-right: calc(var(--panel-indent) - var(--panel-triangle-pad-left) - var(--panel-triangle-size)); }
.bp-tree-row .bp-row-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; }
.bp-tree-row.bp-form .bp-row-label { font-weight: 700; }
.bp-tree-row .bp-row-sub { color: var(--editor-muted-text-color); font-weight: 400; }
.bp-tree-row.active-layer .bp-row-label { color: var(--editor-selected-blue); }
.bp-row-badge { flex: 0 0 auto; font-size: 8.5px; font-weight: 700; color: var(--editor-muted-text-color); border: 1px solid var(--editor-control-border-color, var(--editor-border-color)); border-radius: 4px; padding: 0 4px; text-transform: uppercase; margin: 0 4px; }
.bp-start-badge { flex: 0 0 auto; font-size: 8.5px; font-weight: 700; color: var(--editor-muted-text-color); margin-right: 5px; }
.bp-row-btn { flex: 0 0 auto; width: 13px; height: 13px; margin-left: 6px; opacity: var(--editor-icon-muted-opacity); cursor: pointer; }
.bp-row-btn:hover { opacity: 1; }
/* The +/− glyphs are black artwork. Every other icon in the editor flips for dark mode;
   these were missed, so they sat black-on-dark (invisible). Same invert, and a touch more
   opacity than the muted default so a small 13px glyph still reads. */
[data-theme="dark"] .bp-row-btn { filter: invert(1) brightness(1.2); opacity: 0.75; }
[data-theme="dark"] .bp-row-btn:hover { opacity: 1; }
.bp-drop-line { position: absolute; left: 4px; right: 8px; height: 2px; background: var(--editor-selected-blue); pointer-events: none; z-index: 2; }
.bp-drop-line.before { top: -1px; }
.bp-drop-line.after { bottom: -1px; }
.bp-drop-into { position: absolute; inset: 0; border: 1px solid var(--editor-selected-blue); border-radius: 3px; pointer-events: none; }
[data-theme="dark"] .page-lock-eye {
  filter: invert(1) brightness(1.2);
}
/* The eye sits right after the lock, so the lock's margin-left:auto already
   right-aligns the pair. Cancel the eye's OWN margin-left:auto here — otherwise
   the free space splits between the two autos and the lock lands mid-row. */
.page-lock-eye + .page-vis-eye {
  margin-left: 0;
}

/* Per-row visibility eye for the Data-tab and Prototype-tab trees — matches the
   Layers-tree / pages eyes. */
.row-vis-eye {
  flex: 0 0 auto;
  margin-left: auto;
  /* Nudged right again (14px → 13px → 12px). */
  margin-right: 12px;
  width: 14px;
  height: 14px;
  opacity: 0.38;
  cursor: pointer;
}
.row-vis-eye.hidden {
  opacity: 0.8;
}
.row-vis-eye:hover {
  opacity: 1;
}
[data-theme="dark"] .row-vis-eye {
  filter: invert(1) brightness(1.2);
}

.item-tree-div {
  width: 100%;
  color: var(--editor-text-color);
}

.item-tree-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* The Structure tab scrolls as ONE column: pages, layers, top frames and the item tree each
   take the height they need, and the panel scrolls under the tabs. Sections with their own
   scrollers left you dragging four different scrollbars to see one page's worth of structure. */
.structure-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.structure-scroll::-webkit-scrollbar { display: none; }
/* Inside it, nothing scrolls on its own and nothing is pinned: each section is as tall as
   its content. */
.structure-scroll > * { flex: 0 0 auto; }

.item-tree-ul {
  list-style-type: none;
  margin: 0px;
  padding: 0px;
  font-size: var(--editor-control-font-size);
}

.item-tree-item p {
  margin: 0px;
  padding: 0px;
  user-select: none;
  display: inline-block;
  line-height: 1.2;
}

.item-tree-item input {
  padding: 0px;
  width: 140px;
  margin: 5px 3px;
  user-select: none;
}

.item-tree-item img {
  padding: 5px 5px 2px;
  /* the opacity is set in the javascript */
  height: 6px;
  width: 6px;
}

/* Structure-tree rows are interactive controls, not selectable content. Block
   native text/element SELECTION (so you can't rubber-band-highlight the icons /
   disclosure triangles and then drag that browser-selection) and native image
   DRAGGING (images are draggable by default, which otherwise lets you drag an
   item by its type icon even when the row itself isn't draggable — e.g. a locked
   layer's items). The row's own `draggable` attribute stays the ONLY drag source. */
.item-tree-row,
.item-tree-row * {
  user-select: none;
  -webkit-user-select: none;
}
.item-tree-row img {
  -webkit-user-drag: none;
  user-drag: none;
}

/* Shared detail-panel row controls and inline icon controls. */
.align-items-div {
  padding: 0px 6px;
}

.align-icon {
  padding: 2px;
  margin: 0 4px 0 0;
  opacity: 0.7;
  cursor: pointer;
  user-select: none;
  border: var(--selection-border-width) solid transparent;
  border-radius: var(--corner-radius);
  box-sizing: border-box;
}

.align-icon:hover {
  /* Brighten the glyph (foreground) on hover — no background box. */
  opacity: 1;
}

/* SOLID / DASHED ARE A PICTURE OF A LINE, so their buttons are the shape of one: a rectangle
   wider than it is tall, with the line running most of its length. A square button around a
   short line said "icon" where the thing being chosen is a stroke.

   EVEN ON ALL FOUR SIDES, which took two changes rather than one. The icons carry their own
   4-unit inset all round the ink (28×10 with the line at 4..24), and the cell adds the same
   padding everywhere — where `.align-icon`'s 4px RIGHT margin, meant for a row of separate
   glyphs, had been pushing the line off-centre inside its own button. */
.stroke-style-choice .align-icon-cell {
  padding: 3px;
}
.stroke-style-choice .align-icon {
  margin: 0;
  padding: 0;
}

/* Selectable alignment icons (image / text justification) sit inside an
   unfiltered cell, just like a line-end cap. The selection outline lives
   on this cell so it shows the true selection colour in dark mode (the
   icon itself is luminance-inverted), and the selected icon goes to full
   opacity — so a selected alignment icon reads exactly as bright as a
   selected cap. */
.align-icon-cell {
  display: inline-flex;
  /* Tight within-group spacing between adjacent align buttons. */
  margin: 0 2px 0 0;
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  outline: var(--selection-border-width) solid transparent;
  outline-offset: calc(-1 * var(--selection-border-width));
}

.align-icon-cell .align-icon {
  margin: 0;
}

.align-icon-cell-selected {
  background-color: var(--tool-active-bg, #e3e8f2);
  outline-color: var(--editor-selected-blue);
}

.align-icon-cell-selected .align-icon {
  opacity: 1;
}

/* An alignment used by SOME (not all) of a mixed selection — the same dimmed treatment
   `.ref-point-cell-mixed` gives a reference point in the same situation, so "some of the
   selection is here" reads the same way everywhere it can happen. */
.align-icon-cell-mixed {
  background-color: color-mix(in srgb, var(--editor-selected-blue) 30%, transparent);
  outline-color: color-mix(in srgb, var(--editor-selected-blue) 45%, transparent);
}

.align-icon-cell-mixed .align-icon {
  opacity: 0.8;
}

/* Point/area text-type switcher — a compact two-cell segmented control that
   reuses the align-cell selected highlight, but the cells hold a text label
   (Area / Point) instead of an icon, so they need their own padding. */
.text-type-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin-left: var(--panel-indent);
  margin-right: var(--panel-right-gutter);
}

.text-type-label {
  /* Fixed-width label column so the Type (Area/Point) and Height (Fixed/Auto)
     segmented controls start at the same X and line up as a stacked pair. */
  flex: 0 0 52px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}

/* A segmented row that runs out of width breaks BETWEEN cells, never inside one.
   A cell is a button naming one choice, so "Indented list" splitting across two lines
   inside its own outline reads as two choices; the row carrying its last cells to the
   next line reads as what it is. The row-gap keeps the carried line off the one above. */
.text-type-seg {
  flex-wrap: wrap;
  row-gap: 2px;
}

.text-type-cell {
  padding: 2px 10px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  white-space: nowrap;
}

.text-type-cell.align-icon-cell-selected {
  color: var(--editor-text-color);
}
/* A cell of a SegmentedChoice that is shown but can't be picked (the choice doesn't
   apply right now) — muted and inert, never hidden, so the row keeps its shape. */
.text-type-cell-disabled {
  opacity: var(--editor-icon-muted-opacity, 0.45);
  cursor: default;
}
/* Keyboard focus has to be visible on a cell that is a span playing the part of a radio. */
.text-type-cell:focus-visible,
.align-icon-cell[role="radio"]:focus-visible {
  outline-color: var(--editor-selected-blue);
}

.groups-add-remove-div {
  display: inline-flex;
  align-items: center;
  margin: 0 var(--panel-control-gap);
}

.add-group-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.remove-groups-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

/* Right details panel shell and resizer. */
.details-div {
  position: fixed;
  text-align: left;
  top: var(--top-toolbar-height);
  right: 0;
  bottom: var(--status-bar-height, 0px);
  width: var(--details-width);
  background-color: var(--editor-panel-background-color);
  color: var(--editor-text-color);
  border-left: 1px solid var(--panel-border-color);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  z-index: 10;
}

.details-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.left-panel-resize-handle,
.details-resize-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 12px;
  cursor: col-resize;
  z-index: 20;
  touch-action: none;
}

.left-panel-resize-handle {
  right: 0;
}

.details-resize-handle {
  left: 0;
}

/* Details panel form rows, inputs, and disclosure affordances. */
.section-content-row {
  margin-left: var(--panel-indent);
}

/* Content revealed by a secondary disclosure (stroke options, line options,
   corners) is indented to Level 2 so the visual hierarchy matches the
   disclosure tree. Both the disclosure-toggled title row and its expanded
   content land at the same X. */
.details-line-group,
.stroke-options-group {
  margin-left: var(--panel-level-2);
  /* Standard right gutter, so Stroke Join / Miter Limit / Align stop short of
     the panel edge like every other row instead of running flush to it. */
  margin-right: var(--panel-right-gutter);
}

/* Marker (pencil/highlighter) section: its Width + caps are level-1 content, so
   indent them one step like every other section's rows. The label is sized to
   its text (with the standard label gap) so the field sits right next to it. */
.marker-div {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

/* Note (sticky) styling section: a labelled row per control (Background / Text / Padding). */
.note-row {
  display: flex;
  align-items: center;
  /* Wrap so the swatch row can drop to its own full-width line under the
     label instead of squeezing beside it and orphaning a lone swatch. */
  flex-wrap: wrap;
  gap: 8px;
  box-sizing: border-box;
  max-width: 100%;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.note-label {
  flex: 0 0 auto;
  width: 72px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
/* ── The Records section (WHICH ROWS a bound list shows) ──
   One row shape for the section, at level 1 like every other type-specific panel:
   the label column, then the control. The Field and Value rows sit one indent
   FURTHER in, because they are the detail of the choice made above them — see the
   indent rule at the top of this file. */
.records-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  box-sizing: border-box;
  max-width: 100%;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.records-row-level1 {
  margin-left: calc(var(--panel-indent) * 2);
}
.records-label {
  flex: 0 0 auto;
  width: 44px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.records-field {
  flex: 1 1 auto;
  min-width: 0;
}
/* ── The Media section (a sound or a video source) ──
   One row shape for the whole section, at level 1 like every other type-specific
   panel: the label column, then the control. The three playback switches use the
   shared .proto-check-label row inside this same container, so they share the one
   left edge rather than each finding their own. */
.media-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  box-sizing: border-box;
  max-width: 100%;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.media-label {
  flex: 0 0 auto;
  width: 72px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
/* The chosen file, or a plain statement that there isn't one. Takes the room the
   label leaves and truncates rather than wrapping, since an asset id is long and
   uninteresting past its first few characters. */
.media-file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
  color: var(--editor-dim-text-color, var(--editor-text-color));
}
.media-url-input,
.media-poster-input {
  flex: 1 1 auto;
  min-width: 0;
}
.media-from-seg,
.media-preload-seg {
  flex: 1 1 auto;
  min-width: 0;
}

/* The swatch row: a custom picker (left, so its native popup opens inward) + preset swatches. */
.note-color-row {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  /* Full-width basis so the swatches take their own line (the whole panel
     width) rather than the narrow gap beside the label — avoids the orphan. */
  flex: 1 1 100%;
  min-width: 0;
}
.note-swatch {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.note-swatch.selected {
  outline: 2px solid var(--editor-selected-blue);
  outline-offset: 1px;
}
.note-color-custom {
  flex: 0 0 auto;
  width: 24px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background: transparent;
  cursor: pointer;
}
.note-pad-input {
  /* flex-basis pins the width so `.details-scroll input { width: 100% }`
     can't stretch it off the panel edge. */
  flex: 0 0 64px;
  width: 64px;
  box-sizing: border-box;
  padding: 3px 6px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  text-align: var(--number-input-align);
}
/* Emote section — a grid of preset emojis + a custom-emoji field. */
.emote-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 4px;
  box-sizing: border-box;
  max-width: 100%;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.emote-swatch {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  min-width: 0;
  padding: 0;
  font-size: 18px;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--corner-radius-sm);
  background: transparent;
  cursor: pointer;
}
.emote-swatch:hover {
  background: var(--editor-hover-bg, rgba(127, 127, 127, 0.18));
}
.emote-swatch.selected {
  outline: 2px solid var(--editor-selected-blue);
  outline-offset: -1px;
}
.emote-custom-input {
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  padding: 3px 6px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
}
.marker-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.marker-label {
  font-size: var(--editor-control-font-size);
  margin-right: var(--panel-checkbox-label-gap);
}

/* One (label + control) pair inside a stroke-options sub-section. Wrapper
   is --details-row-height tall; children stay at their natural height and
   are centered vertically. */
.stroke-options-div {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

/* Wrapper for the Dimensions grid (X/Y/W/H/Corners/Angle).
   Shifts the column-cell layout right so the first column aligns with
   title-text. The cells themselves remain inline-block at 40% width. */
.dim-grid {
  padding-left: var(--panel-indent);
  /* Standard right gutter so the rightmost fields (Y, H) stop 8px short
     of the panel edge rather than running flush against it. */
  padding-right: 8px;
}

/* X|Y and W|H pairs: the second field packs flush against the first
   instead of sitting out at a fixed column. */
.dim-row {
  display: flex;
  align-items: center;
  gap: var(--panel-pair-gap, 20px);
}

/* Wider X / Y edit boxes: tighten the column gap and lift the input cap so each
   field claims more of the row. */
.dim-row-xy {
  /* Between-group gap: the X and Y label+control groups sit further apart than
     a label sits from its own control (that inner gap is 4px, set below), and
     tighter than the 20px .dim-row default so both still fit. The inputs
     flex-shrink to absorb it. */
  gap: 12px;
}
/* Within-group gap: on the X/Y line a dimension label hugs its control (closer
   than the between-group gap above), overriding the wider
   --panel-checkbox-label-gap. Scoped to the X/Y row only — the W/H labels keep
   the standard gap so their edit boxes line up with the corner / angle boxes
   (which sit a full --panel-checkbox-label-gap past their 14px icon). */
.dim-row-xy .details-line-half-div p {
  margin-right: 4px;
}
/* Trim each X/Y control so the pair fits at the deeper level-2 indent (the X
   field now starts a full --panel-indent past the reference disclosure triangle)
   without X/Y crowding the position lock at the row's right edge. */
.dim-row-xy .details-line-half-div input {
  max-width: calc(100% - 28px);
}

/* Project section: a row with a label and a unit dropdown, under the
   "Project" disclosure header. Indented to match the rest of the design panel. */
.units-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  padding-left: var(--panel-indent);
  padding-right: var(--panel-right-gutter);
  gap: 8px;
}
.units-label {
  /* Fixed-width label column so each row's select starts at the same x —
     otherwise the wider "Units" label pushes its dropdown right of "Angle". */
  flex: 0 0 44px;
  font-weight: 600;
  font-size: var(--editor-control-font-size);
  color: var(--header-3-color);
  user-select: none;
  white-space: nowrap;
}
.units-select {
  flex: 0 0 auto;
}
/* The Units / Angle / Y-axis dropdowns share one width so they line up, and get
   the standard control height (line-height + vertical padding + border) instead
   of the browser's default select height. (The project-name input, which also
   carries .units-select, keeps its own width from .project-name-field.) */
select.units-select {
  box-sizing: border-box;
  width: 116px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  line-height: var(--editor-control-line-height);
  padding: var(--editor-control-pad-y) 4px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg);
}
/* Read-only value (Created / Last saved) shown beside its units-label. */
.units-value {
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  user-select: text;
  white-space: nowrap;
}
/* The built app's Window row: three checkbox switches on one line, each label beside its box,
   at the same left edge as every other value in the section. */
.app-window-row { flex-wrap: wrap; row-gap: 4px; }
.app-window-flag { display: inline-flex; align-items: center; gap: 4px; margin-right: 10px; white-space: nowrap; }
.app-window-flag input { margin: 0; }

/* App icon: a square chip showing the icon (or a + when there is none) that opens the file
   picker or takes a dropped picture, the file's name, and a Reset. Standard 26px control. */
.app-icon-chip {
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg);
  cursor: pointer;
  overflow: hidden;
}
.app-icon-chip:active { transform: translateY(1px); }
.app-icon-chip-over { outline: 2px solid var(--prototype-accent); }
.app-icon-img { width: 100%; height: 100%; object-fit: contain; }
.app-icon-empty { color: var(--editor-text-color); font-size: var(--editor-control-font-size); }
.app-icon-name { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.app-icon-reset {
  flex: 0 0 auto;
  height: 26px;
  box-sizing: border-box;
  padding: 0 8px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.app-icon-reset:active { transform: translateY(1px); }
.app-icon-error { font-size: var(--editor-control-font-size); color: var(--danger-color, #c0392b); }

/* Editable project-name field in the Project tab — a standard text box. */
.project-name-field {
  flex: 1 1 0;
  min-width: 0;
  box-sizing: border-box;
  padding: 2px 4px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
}

.dim-row .details-line-half-div {
  width: auto;
  /* Split the row evenly so each field (X|Y, W|H) — all formula-capable
     — claims half the available width and its input can grow into it. */
  flex: 1 1 0;
  min-width: 0;
}

/* The X/Y line carries a disclosure triangle for its reference sub-options.
   The triangle occupies the level-1 -> level-2 indent band (the row's base is
   level 1, the dim-grid padding), so the X field starts at the level-2
   boundary. W/H and corners have no sub-disclosure, so they stay at level 1. */
.dim-subtriangle {
  flex: 0 0 auto;
  width: var(--panel-triangle-size);
  height: var(--panel-triangle-size);
  cursor: pointer;
  /* Span one --panel-indent before X: triangle width + the row's 12px flex gap +
     this margin = --panel-indent, landing the X field at --panel-level-2. */
  margin-right: calc(var(--panel-indent) - var(--panel-triangle-size) - 12px);
}
/* The W/H row no longer aligns under X/Y — it sits at level 1 like corners — so
   its leading spacer contributes nothing. */
.dim-subtriangle-spacer {
  display: none;
}
/* Disclosed reference options (Relative / Bounding Box / Reference Point) line up
   under the X/Y fields at the level-2 boundary. The row already sits at level 1
   (dim-grid's padding), so one more --panel-indent lands it at --panel-level-2. */
.dim-row-indent {
  padding-left: var(--panel-indent);
}

/* Reference-point marker badge before the Relative / Bounding Box / Reference
   Point labels — mirrors the on-canvas badge (green ring + dot on white) so
   it reads as "reference point" and is distinct from the override dots. */
.ref-badge {
  display: inline-block;
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--reference-badge-accent);
  border: 1.5px solid var(--reference-badge-color);
  box-sizing: border-box;
  position: relative;
  vertical-align: middle;
  margin-right: 5px;
}
.ref-badge::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--reference-badge-color);
  transform: translate(-50%, -50%);
}
/* In the flex checkbox labels the row gap already spaces the badge. */
.ref-flag-option .ref-badge {
  margin-right: 0;
}

/* Reference-point picker — a small box with a clickable dot at each corner and
   the center. It sits inside .dim-grid, so it inherits the section's
   --panel-indent and lines up with the X/Y fields below it. */
.ref-point-row {
  /* Hug the reference-point grid, but carry the half-gap like a row so it sits
     a full inter-row gap from its neighbours. */
  min-height: 0;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
/* X/Y measurement checkboxes (Relative / Bounding Box) — per-item properties —
   sit on their own row above the reference-point picker. Laid out in a row that
   wraps if the panel is too narrow, so neither gets clipped. */
.ref-flags-row {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.ref-flags-group {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  gap: 4px 18px;
  width: 100%;
}
.ref-flag-option {
  display: flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  user-select: none;
  white-space: nowrap;
  cursor: pointer;
}
.ref-flag-option input {
  margin: 0;
  cursor: pointer;
}
.ref-point-group {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.ref-point-caption {
  /* Flex so the badge vertically centers with the label text on its line. */
  display: inline-flex;
  align-items: center;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  user-select: none;
  white-space: nowrap;
}
/* "Reference Point" label sitting to the left of the picker box. */
.ref-point-field {
  display: flex;
  align-items: center;
  gap: 10px;
}
.ref-point-box {
  display: block;
  overflow: visible;
}
/* The five cells (four L-shaped corners + a rectangular center) tile the box
   into one shape. A thin background-colored seam between them is drawn by each
   cell's own stroke, so the pieces read as separate buttons that fit together
   to form the rectangle. */
.ref-point-cell {
  cursor: pointer;
  outline: none;
  stroke: var(--editor-panel-background-color);
  stroke-width: 1.5;
  stroke-linejoin: round;
  transition: fill 0.1s ease;
  /* a faint fill so the tiling seams are visible at rest */
  fill: color-mix(in srgb, var(--toolbar-text-color) 18%, var(--editor-panel-background-color));
}
.ref-point-cell:hover {
  fill: color-mix(in srgb, var(--toolbar-text-color) 34%, var(--editor-panel-background-color));
}
.ref-point-cell.ref-point-cell-on {
  fill: var(--editor-selected-blue);
}
.ref-point-cell.ref-point-cell-on:hover {
  fill: var(--editor-selected-blue);
}
/* A reference point used by SOME (not all) of a mixed selection — dimmed. */
.ref-point-cell.ref-point-cell-mixed {
  fill: color-mix(in srgb, var(--editor-selected-blue) 45%, var(--editor-panel-background-color));
}
.ref-point-cell.ref-point-cell-mixed:hover {
  fill: color-mix(in srgb, var(--editor-selected-blue) 60%, var(--editor-panel-background-color));
}
.ref-point-cell:focus-visible {
  stroke: var(--editor-selected-blue);
  stroke-width: 2;
}

/* Flex row so children center on the geometric midline (matches every
   other panel row — .stroke-options-div, .color-details-div, etc.).
   Using line-height + vertical-align:middle here drifted the contents
   off-center relative to the surrounding flex rows. */
.details-line-div {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  width: 100%;
}

/* Multi-pair rows (west/east adornment selects, west/east cap selects,
   west/east size sliders). The label (icon) keeps its intra-pair gap to
   its control via its own margin-right; we add --panel-pair-gap as left
   margin to the start of each subsequent pair so the pairs visibly
   separate. */
.details-line-div > .arrow-icon ~ .arrow-icon,
.details-line-div > .stroke-cap ~ .stroke-cap,
.details-line-div > .line-cap ~ .line-cap,
.details-line-div > .adornment-size-div ~ .adornment-size-div {
  margin-left: var(--panel-pair-gap);
}

.details-line-half-div {
  display: inline-flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* Standalone rows (Angle, Corner Count, Corners) span the panel so the
     formula input can stretch; the X|Y / W|H pairs override this in the
     `.dim-row` rule above. */
  width: 100%;
  vertical-align: middle;
}

/* Inside a half-div, the .vi-dim wrapper would force a non-flex child box
   that prevents the label/input from centering directly. Collapse it with
   display: contents so the label + input become flex items of the
   .details-line-half-div and get vertically centered by align-items. */
.details-line-half-div > .vi-dim {
  display: contents;
}

.details-line-sides-div {
  display: inline-flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* Claim the row space left of the Equilateral checkbox (40%) so the
     formula-capable Side input has room to grow. */
  width: 58%;
  vertical-align: middle;
}

.details-line-checkbox {
  display: inline-block;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  width: 40%;
  line-height: normal;
  vertical-align: middle;
}

.details-line-star-div {
  /* Flex so the slider absorbs the remaining width and the text field keeps a
     fixed basis — otherwise `.details-scroll input { width: 100% }` stretches
     both the range and the text input to 100% and the row runs off the panel. */
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  line-height: normal;
}
.details-line-star-div > .star-depth-slider {
  flex: 1 1 0;
  min-width: 0;
}
.details-line-star-div > .star-depth {
  flex: 0 0 42px;
  width: 42px;
}

/* W / E adornment-size control: an E/W label + slider that line up
   with the line-end ADORNMENT row above — the label occupies the same
   width as the arrow icon (10px) and the slider the same width as the
   adornment select (86px), with the standard 8px gap between. */
.adornment-size-div {
  display: inline-flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.adornment-size-label {
  flex: none;
  width: 17px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  color: var(--editor-muted-text-color);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  user-select: none;
}

/* Inherits the label's currentColor, so it adapts to light/dark mode. */
.adornment-size-icon {
  display: block;
}

/* The W / E adornment-size sliders share the star-ratio slider class;
   size them to match the adornment select (86px). */
.adornment-size-div .star-depth-slider {
  flex: none;
  width: 86px;
  margin: 0;
}

.vi-dim {
  display: inline-block;
  vertical-align: middle;
}

/* Formula indicator (ƒ) shown on a Design-tab numeric field whose value is
   driven by a Code-tab formula. Small, green (code accent), muted until hover;
   clicking jumps to the Code tab. Rendered only when a formula is present. */
.formula-badge {
  flex: 0 0 auto;
  margin-left: 3px;
  padding: 0 3px;
  border: none;
  background: transparent;
  color: var(--code-accent);
  font-style: italic;
  font-weight: 700;
  font-size: 13px;
  line-height: 1;
  opacity: 0.75;
  cursor: pointer;
  user-select: none;
}
.formula-badge:hover {
  opacity: 1;
}

/* The single-letter dimension descriptor (X / Y / W / H) in VIDimension — a fixed
   14px column so the labels line up down the panel.
   Scoped to its own class ON PURPOSE. As a bare `.details-div p` this styled EVERY
   paragraph in the details panel: 14px wide, inline-block, muted. Long-form panel
   text then had to fight it — `.constraints-note-text`, `p.file-name-p` and
   `p.sides-p` each re-declare display / width / margin for no other reason — and
   any new paragraph (the Layout Grids empty state) came out as a 14px column
   wrapping one word per line, with no indent. */
.details-div p.dim-descr {
  display: inline-block;
  margin: 0px var(--panel-checkbox-label-gap) 0px 0;
  width: 14px;
  font-size: var(--editor-control-font-size);
  line-height: normal;
  text-align: left;
  color: var(--editor-muted-text-color);
  user-select: none;
  vertical-align: middle;
}

/* Collapsible "Scale / Skew" sub-section header in the dimensions panel. */
/* The Arc sub-section's header shares Scale / Skew's — they are siblings at the end of
   Dimensions and a second set of numbers for the same thing is how two headers drift apart. */
.arc-header,
.scale-skew-header {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.arc-title,
.scale-skew-title {
  font-weight: 600;
  font-size: var(--editor-control-font-size);
  color: var(--header-3-color);
  user-select: none;
}

.scale-skew-normal {
  margin-left: 6px;
  color: var(--editor-muted-text-color);
}

/* Scale / skew rows in the dimensions panel: label, slider, and a
   small percentage edit box, each control on its own line, indented
   one step (level 2) past the panel's level-1 content. */
.scale-skew-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* Right edge pinned 8px from the panel edge so the edit boxes line up there. */
  margin: 0 8px 0 var(--panel-indent);
  gap: var(--panel-checkbox-label-gap);
}

/* Reset (Scale / Skew → defaults) on its own line under the controls, left-
   aligned with the Skew label (inherits the control rows' margin-left, and the
   button drops .angle-reset's auto right margin). */
.scale-skew-reset-row .scale-skew-reset {
  margin-left: 0;
}

/* The same Reset for Outer Corners and Inner Corners, last in each group and on the
   group's own left edge (the .corner-sub-line indent), not pushed right by .angle-reset. */
.corner-reset-row .corner-reset {
  margin-left: 0;
}

/* Auto width so the gap after the label equals the gap after the
   slider (both are just the row's flex gap). */
.scale-skew-label {
  flex: none;
  /* Fixed width so every row's icon, slider and percentage input line up in a
     column. Holds the H / V / U / Skew direction icon, left-aligned. */
  display: flex;
  align-items: center;
  width: 24px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  user-select: none;
  white-space: nowrap;
}

.scale-skew-icon {
  width: 16px;
  height: 16px;
  display: block;
}

/* Rotate ±90° / Flip H / Flip V: plain icon-buttons (no background box), the same
   convention as the left toolbar's tools — the glyph brightens on hover, and a
   selected-blue OUTLINE (not a fill) marks state where one applies. None of these
   has an "is this on" state (each selected item rotates/toggles independently, so
   a multi-selection has no single answer) — they are ACTION buttons, not toggles.
   The row sits directly under the Angle field, whose value the rotates change. */
.scale-skew-flip-row { gap: 8px; }
/* The quarter-turn / mirror icons sit under the angle they change, and START WHERE THE ANGLE
   ICON STARTS — the row's own left edge, not one step in from it. It carried
   `margin-left: var(--panel-indent)` on top of a row that was already at the section's edge, so
   the four icons sat a full indent right of the control they belong to. The right margin is the
   panel's gutter variable rather than a hand-written 8px, which was 4px adrift of every
   neighbouring row. */
.rotate-flip-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin: 0 var(--panel-right-gutter) 0 0;
}
.flip-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border: none; border-radius: var(--corner-radius-sm);
  background: transparent; cursor: pointer;
}
.flip-btn .scale-skew-icon { opacity: 0.62; transition: opacity 90ms ease; }
.flip-btn:hover:not(:disabled) .scale-skew-icon { opacity: 1; }
.flip-btn:disabled { cursor: default; }
.flip-btn:disabled .scale-skew-icon { opacity: 0.3; }

.scale-skew-slider {
  /* Fixed 95px and NON-shrinking, so the width is actually honored: the slider
     keeps its size by taking room from the edit box. On a wider panel the box
     grows (the slider stays put); when the row is tight the box yields, not the
     slider. */
  flex: 0 0 auto;
  width: 95px;
  vertical-align: middle;
}

/* Design-panel range sliders tint with the editor selection color, so
   they restyle along with it when it is changed in Customize | Colors. */
.scale-skew-slider,
.star-depth-slider {
  accent-color: var(--editor-selected-blue);
}

.scale-skew-input {
  /* Grows with the panel on horizontal resize; the right edge stays pinned 8px
     from the panel edge via the row's right margin. Yields room to the slider
     (lower min-width) so the slider's width is honored on a tight panel. */
  flex: 1 1 auto;
  min-width: 44px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* Numeric edit boxes. Justification follows the --number-input-align
   preference (Customize → Layout); defaults to right so digits line up.
   The `input[type="number"]` selector applies it everywhere — including
   the Customize page's own number fields. */
input[type="number"],
.details-line-div input,
.details-line-half-div input,
.details-line-sides-div input,
.corner-sub-line input,
.details-line-star-div input,
.line-width-input,
.stroke-width-input,
.miter-limit-text-input,
.scale-skew-input,
.star-depth,
.alpha-input,
.color-part-input,
.gradient-stop-position-input,
.gradient-offset-input,
.shadow-offset-input,
.delay-input,
.font-div-size,
.stroke-dashes-text-input,
.line-dashes-text-input {
  text-align: var(--number-input-align, right);
}

/* Secondary (Level-2) disclosure triangle. The triangle element fills the
   panel-indent..panel-level-2 band (its parent row's margin-left is
   panel-indent, so the triangle starts there). Inside the element, the
   12px image sits at the same offset as the Level-1 section triangle —
   nudged 1px right of the band's natural center — and the title that
   follows starts at panel-level-2. */
.corners-triangle,
.arc-triangle,
.line-width-triangle {
  margin: 0;
  padding: 4px var(--panel-triangle-pad-right) 4px var(--panel-triangle-pad-left);
  box-sizing: content-box;
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  opacity: var(--editor-icon-muted-opacity);
  vertical-align: middle;
  cursor: pointer;
  position: relative;
  z-index: 25;
}

.section-triangle {
  margin: 0px 0px 0px 3px;
  /* opacity is set in the code */
  padding: 3px;
  height: 12px;
  width: 12px;
  position: relative;
  z-index: 25;
}

#item-dim-0 {
  width: 45px;
}

#item-dim-side {
  width: 45px;
}

/* The corner rows (VIDimensions): one setting on its own line, indented a step under
   the Outer Corners / Inner Corners header (level 2). Each line is the setting's name
   — Radius, Smooth %, Cut %, or "Corner N" in a per-corner list — followed by its
   slider (when it has one) and a roomy edit box. */
/* Everything in it comes from the panel's spacing variables — the row gap (the
   Customize "Inter-row gap") above and below, the label gap between a label and its
   field — and the label column is ONE variable, in em of the panel's control font, so
   a larger font widens it rather than letting "Smooth %" run into its field. Outer
   Corners (Radius, Smooth %, Cut %, the per-corner rows) and a star's Inner Corners
   both use these rows, so their fields share one column. */
.corner-sub-line {
  --corner-label-col: 5em;
  font-size: var(--editor-control-font-size);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
  padding-left: var(--panel-indent);
  padding-right: var(--panel-right-gutter);
  /* A ROW, not a block. Its `.vi-dim` is a flex box that fills the width, so anything
     BESIDE it — the spacing-token link on the Radius (Arc) row — was a block-level sibling
     with nowhere to go but the next line, and the little chain hung under the field. Every
     other row in this group has the `.vi-dim` alone and is unaffected. */
  display: flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
}
/* …and the field takes the slack, so the link sits at the row's end rather than pushing it. */
.corner-sub-line > .vi-dim {
  flex: 1 1 auto;
  min-width: 0;
}
.corner-sub-line .vi-dim {
  display: flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
}
.corner-sub-line .vi-dim input {
  flex: 1 1 0;
  min-width: 0;
  max-width: 150px;
  box-sizing: border-box;
  padding: var(--editor-control-pad-y) 4px;
  font-size: var(--editor-control-font-size);
  line-height: var(--editor-control-line-height);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}
/* THE PERCENTAGE SLIDER beside Smooth % and Cut %, and what it does to the row.
   The number field is the row's grower everywhere else; on a row that also carries a
   slider it gives that up — a fixed 3.5em is room for "100" and "Mixed" — so the slider
   takes the rest and is the part that grows when the panel is widened. */
.corner-sub-line .vi-dim input.corner-percent-slider {
  flex: 1 1 0;
  min-width: 40px;
  max-width: none;
  padding: 0;
  border: none;
  background: transparent;
  accent-color: var(--editor-selected-blue);
  vertical-align: middle;
}
.corner-sub-line .vi-dim:has(.corner-percent-slider) input:not(.corner-percent-slider) {
  flex: 0 0 3.5em;
  max-width: 3.5em;
}

/* "Smooth %" / "Corner N" / "Radius" text label — the label column, so the edit boxes
   line up; never narrower than its own text. No margins of its own: the row's padding
   and gap are the spacing. Beats the generic `.details-div p` via the two-class selector. */
.corner-sub-line .corner-sub-label {
  flex: 0 0 auto;
  width: var(--corner-label-col);
  min-width: max-content;
  margin: 0;
  font-size: var(--editor-control-font-size);
  line-height: normal;
  color: var(--editor-muted-text-color);
  user-select: none;
  white-space: nowrap;
}

.star-depth-slider[type="range"]::after,
.adornment-size-slider[type="range"]::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2px;
  height: 8px;
  transform: translate(-1px, -50%);
  background: currentColor;
  opacity: .35;
  pointer-events: none;
}

.details-line-star-div input {
  display: inline-block;
  position: relative;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
}

.star-depth {
  display: inline-block;
  width: 42px;
  margin-left: 10px;
}

.details-line-div input,
.details-line-half-div input,
.details-line-sides-div input {
  display: inline-block;
  width: 60px;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* Formula-capable dimension inputs (X / Y / W / H, angle, corner radius,
   corner count, side) grow to use leftover row width so a longer
   expression like 50{(count+5)*5} stays readable — but `max-width` caps
   the growth so a lone field in a wide row doesn't stretch the whole
   panel. A descendant selector is required: the VIDimension input sits
   inside a `display:contents` `.vi-dim` wrapper, so a `>` child
   combinator wouldn't match it. */
.details-line-half-div input,
.details-line-sides-div input {
  flex: 1 1 0;
  width: auto;
  min-width: 44px;
  max-width: 150px;
}

/* The Angle row's edit box grows with the panel (no 150px cap), so its right
   edge always sits 8px to the left of the Reset button's left edge. The Reset
   button keeps its place at the row's right; the box fills the gap up to it. */
.details-line-half-div.details-line-angle input {
  max-width: none;
}
.details-line-angle .angle-reset {
  margin-left: 8px;
}

/* W / H rows: the box grows to fill (no 150px cap) so the size-token link is
   pushed to the row's right edge, leaving the standard pair-gap between it and
   the brace — otherwise a capped input leaves a variable gap to the bracket. */
.details-line-half-div.dim-size-line input {
  max-width: none;
}
/* The token link's own right padding would otherwise sit ON TOP of the pair-gap,
   pushing the glyph ~3px further from the brace than the standard spacing. Trim
   it on the W/H rows so the link is flush at the lines' edge and there is only
   the standard pair-gap between it and the brace. */
.details-line-half-div.dim-size-line .token-link-btn {
  padding-right: 0;
}

/* Per-dimension lock toggle (X / Y / W / H / angle). Faded open padlock when
   unlocked; solid when locked. */
.dim-lock {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  margin-left: 2px;
  border: none;
  background: transparent;
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  opacity: 0.4;
  transition: opacity 0.1s ease, background-color 0.1s ease;
}

.dim-lock:hover {
  opacity: 1;
}

.dim-lock-on,
.dim-lock-on:hover {
  opacity: 1;
}

.dim-lock img {
  width: 14px;
  height: 14px;
  display: block;
}

/* The size padlock (lock.svg) ships as neutral grey #a8a8a8 and reads well in
   both themes. The aspect chain (linked/unlinked.svg) ships as BLACK, so it's
   low-contrast in dark mode. Recolour the chain to that same grey — invert(1)
   turns black to white, brightness(0.659) drops it to #a8a8a8 — so both locks
   match in light AND dark mode. */
.dim-aspect-lock img {
  filter: invert(1) brightness(0.659);
}

/* Size (W/H) row: the two dimension lines stack vertically (each full-width so both
   have room), and the size lock sits vertically centred to their right — kept at the
   same right edge as the X/Y position lock — with a brace showing it groups both. */
.dim-size-row {
  align-items: center;
}
.dim-size-lines {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--details-row-gap);
  /* W↔H are spaced by the row-gap above; the W/H lines drop their own padding.
     Carry the half-gap on the block's outer edges so the Reference-Point→W and
     H→Angle gaps are a full inter-row gap, not just the neighbour's half. */
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
/* Each W/H line now spans the whole lines column (overrides the side-by-side flex:1
   the plain `.dim-row .details-line-half-div` rule would otherwise apply). */
.dim-size-lines .details-line-half-div.dim-size-line {
  flex: 0 0 auto;
  width: 100%;
  /* The W/H rows are spaced by the .dim-size-lines row-gap, so drop their own
     row padding to avoid doubling it. */
  padding-top: 0;
  padding-bottom: 0;
}
.dim-size-lock {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1px;
  /* The row's flex gap is the wide --panel-pair-gap (for separating the X|Y /
     W|H pairs). The brace is not a pair separator — it hugs the size fields — so
     pull it in to the standard adjacent-control gap. The lines column (flex:1)
     absorbs the reclaimed width, so the link slides toward the brace while the
     lock stack stays right-aligned with the X/Y position lock above. */
  margin-left: calc(var(--panel-control-gap) - var(--panel-pair-gap));
}
.dim-size-brace {
  flex: 0 0 auto;
  color: var(--editor-muted-text-color);
}
/* Size padlock stacked over the aspect-ratio link, both centred on the brace. */
.dim-size-lock-stack {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
}

.details-div-heading,
.prototype-div-heading {
  text-align: left;
  margin: 0;
  user-select: none;
  display: inline-block;
  vertical-align: middle;
  color: var(--header-2-color);
}

/* Sections without a disclosure triangle render the heading as the first
   child of .details-header. Indent it to match the title-text position of
   triangled sections (the triangle is panel-indent wide, so triangled
   headings start at panel-indent automatically). */
.details-header > .details-div-heading:first-child,
.details-header > .prototype-div-heading:first-child {
  margin-left: var(--panel-indent);
}

.details-div-heading-descr {
  font-size: var(--editor-control-font-size);
  vertical-align: middle;
}

.details-div-group,
.prototype-div-group {
  padding: 10px 0;
  line-height: normal;
  /* Without this the whole prototype panel inherits the browser-default serif (Times New Roman)
     — the design panel sets it, the prototype panel didn't. */
  font-family: var(--editor-font-family);
}

/* Vertical breathing room between consecutive rows inside a section,
   so lines don't crowd against each other.
.details-div-group > .details-line-div,
.details-div-group > .details-line-half-div,
.details-div-group > .stroke-details-div,
.details-div-group > .line-details-div,
.details-div-group > .image-details-div,
.details-div-group > .stroke-options-group,
.details-div-group > .color-details-div,
.details-div-group > .shadow-div,
.details-div-group > .section-content-row {
  margin-top: var(--panel-row-gap);
}
*/
/* ---- ARC controls (an ellipse's start / sweep / hole) -------------------
   Sits with the other shape parameters in Dimensions, on the panel's own row
   rhythm. The label column is narrower than the section's standard 64px because
   these rows carry TWO label+field pairs on one line, the way the W/H row does. */
.arc-controls { width: 100%; }

/* EVERY ROW IN THE BLOCK SITS AT LEVEL 1, because the block is a SUB-SECTION now — it has a
   header with a disclosure triangle of its own, and the panel's rule (see --panel-indent at the
   top of this file) is that level N content lives at N × --panel-indent. Its two siblings at the
   end of Dimensions already do exactly this: `.scale-skew-row` and `.corner-sub-line` are both
   one --panel-indent in, under headers that look like this one.

   THE INDENT IS ON `.arc-row` ITSELF rather than on an extra class the rows opt into. There was
   an `.arc-row-level1` for the two rows that had no label, and when the header arrived the other
   three stayed behind at level 0 — a header whose contents do not indent under it. A class you
   have to remember to add is a class that gets forgotten; here there is nothing to forget. */
.arc-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
  padding-left: var(--panel-indent);
}

/* THE LABEL COLUMN IS PINNED, not merely given a basis. `flex: 0 0 38px` sets a basis and
   leaves min-width at `auto`, which for a flex item is its CONTENT size — so a label whose word
   is wider than the basis silently grows past it. "Sweep" is wider than "Start" at the panel's
   font, so the two fields beside them started at different x positions and the rows did not line
   up. An explicit min-width pins the column so every arc row's field begins in the same place,
   whatever the label says. */
.arc-label {
  flex: 0 0 48px;
  min-width: 48px;
  white-space: nowrap;
  color: var(--editor-muted-text-color);
}

.arc-input {
  flex: 1 1 auto;
  min-width: 0;
  border: var(--editor-control-border);
  border-radius: 3px;
  padding: var(--editor-control-pad-y) 4px;
  background-color: transparent;
  color: var(--editor-input-text-color, var(--editor-text-color));
  font-family: var(--editor-font-family);
}

.arc-hole-slider { flex: 1 1 auto; min-width: 0; }
.arc-hole-slider:disabled { opacity: 0.45; cursor: default; }
/* A control that does not apply says so by fading, the way a disabled button does — its label
   and its value go with it, or the row reads as half switched off. */
.arc-disabled { opacity: 0.45; }
.arc-hole-value { flex: 0 0 42px; color: var(--editor-muted-text-color); text-align: right; }


/* The pen editor's Join / Split row. Its own line under the sub-mode header, on the
   panel's row rhythm like every other stacked element in a section. The region-fill row
   rides the same class, so a picked room's colour lines up with the buttons above it. */
.pen-path-ops {
  display: flex;
  gap: 6px;
  align-items: center;
  padding: var(--details-row-pad) var(--panel-edge-gap) var(--details-row-pad) var(--panel-indent);
}

/* The label on the region-fill row — the panel's ordinary field label, not a heading. */
.pen-region-label {
  color: var(--editor-muted-text-color);
  white-space: nowrap;
}

/* ---- An INSTALLED PLUGIN'S PANEL ----------------------------------------
   A plugin describes a list of controls and the editor renders them (see
   plugins/install/installedPanels.tsx). Everything here exists so that a
   stranger's section reads as part of the panel it is in rather than as
   something bolted on: the same row rhythm (--details-row-pad, half above and
   half below, like every other stacked element in a section), the same indent,
   and the app's own controls inside. A plugin cannot bring styles of its own —
   there is nowhere in a node to put any — so this file is the whole of how one
   looks. */
.plugin-panel-body {
  padding: 0 var(--panel-edge-gap) 0 var(--panel-indent);
}

.plugin-panel-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
  width: 100%;
}

/* The label column, on the panel's own label width so a plugin's rows line up
   with the editor's — a section whose labels sat at their own widths would read
   as a different panel however good the control beside it looked. */
.plugin-panel-label {
  /* 64px, which is what the panel's own label columns use (see the Dimensions and
     Text rows) — a plugin's section whose labels sat at their own width would read
     as a different panel however good the control beside it looked. */
  flex: 0 0 64px;
  color: var(--editor-muted-text-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.plugin-panel-input { flex: 1 1 auto; min-width: 0; }
.plugin-panel-select { flex: 1 1 auto; min-width: 0; }
.plugin-panel-number { flex: 0 0 72px; }

.plugin-panel-text {
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}

.plugin-panel-divider {
  height: 1px;
  margin: var(--details-row-pad) 0;
  background-color: var(--editor-panel-separator-color);
}

.plugin-panel-group { padding-top: var(--details-row-pad); }

.plugin-panel-group-label {
  color: var(--editor-muted-text-color);
  font-weight: 600;
  padding-bottom: var(--details-row-pad);
}

.plugin-panel-check {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}

/* Floating toolbar submenus and editor canvas containers. */
.chevron-icon {
  position: absolute;
  top: 22px;
  left: var(--chevron-left);
  opacity: var(--editor-icon-muted-opacity);
  user-select: none;
}

.toolbar-submenu {
  position: absolute;
  left: var(--left-toolbar-width);
  background-color: var(--left-toolbar-background-color);
  height: var(--standard-icon-height);
  /* top is calculated in the javascript */
  z-index: 100;
}

#canvas-div {
  position: absolute;
  overflow: hidden;
  top: var(--top-toolbar-height);
  left: calc(var(--left-toolbar-width) + var(--tree-width));
  bottom: 0px;
  right: var(--details-width);
  z-index: 0;
  background-color: var(--canvas-background-color);
  /* The canvas is a drawing surface — never a text field. Without this, a
     click-drag on it (e.g. across an entity node's rendered text on the Data
     tab) starts a native browser text selection. Inherited by the <canvas>. */
  user-select: none;
}

/* Floating save/load status pill centered near the top of the canvas. */
.canvas-notification {
  position: absolute;
  top: 1.2rem;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(24, 24, 28, 0.80);
  color: #fff;
  padding: 0.4rem 1.1rem;
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
  z-index: 50;
  letter-spacing: 0.02em;
}

/* Blank-canvas message shown when no page is visible (all pages hidden, or the
   single active page is hidden). Centred over the empty canvas. */
.canvas-empty-message {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #555555;
  font-size: 1.25rem;
  font-weight: 700;
  pointer-events: none;
  z-index: 40;
  text-align: center;
}

#prototype-canvas-div {
  position: absolute;
  overflow-y: auto;
  overflow-x: auto;
  /* Anchor below the top toolbar (matches #canvas-div). Otherwise the
     canvas extends behind the toolbar, and a frame stretched to fill the
     canvas vertically tucks its top under the toolbar. */
  top: var(--top-toolbar-height);
  left: 0;
  bottom: 0;
  right: 0;
  background-color: black;
}

/* When the page hides the prototype top toolbar, the canvas takes the
   full viewport height (no toolbar offset to compensate for). Escape
   stops playback in this mode. */
.prototype-no-toolbar #prototype-canvas-div {
  top: 0;
}

.canvas,
.prototype-canvas {
  position: absolute;
  display: block;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  outline: none;
}

/* Frame-to-frame transition overlay: laid exactly over the playback canvas,
   click-through, shown only while a transition tween is running. */
.prototype-transition-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
}

/* Focus indicator: shows when the editor canvas has keyboard focus so the user
   knows that canvas-only shortcuts (arrow nudge, eyedropper Enter, etc.) will
   fire. The prototype-canvas is excluded — during playback the canvas is the
   "screen" the user is interacting with, and a focus ring around it reads as
   a stray editor chrome leak. outline-offset: -2px keeps the ring inside the
   canvas so layout doesn't shift. Color matches TColor.selection (paint.ts). */
.canvas:focus {
  outline: 2px solid var(--editor-selected-blue);
  outline-offset: -2px;
}

/* Hidden assets and custom cursor images used by the canvas. */
.selected-icon {
  display: inline-block;
  flex: 0 0 auto;
  width: 16px;
  height: 12px;
  background-color: var(--selection-color, var(--editor-selected-blue, #1097e2));
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}

/* Layers-mode indicator: a selection-coloured VERTICAL line filling the row's
   full height in the caret's 16px slot. Rows abut with no gap, so each row's
   segment joins its neighbours into ONE continuous line down the whole list.
   A direct flex child of .page-list-item (align-self stretch = full height). */
.selected-line {
  flex: 0 0 auto;
  align-self: stretch;
  width: 16px;
  position: relative;
  cursor: pointer;
}
.selected-line::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  transform: translateX(-50%);
  background-color: var(--selection-color, var(--editor-selected-blue, #1097e2));
  transition: width 0.08s ease, filter 0.08s ease;
}
/* Hovering the line over a row highlights just that row's segment — the bar
   brightens and thickens so you can see which layer you're pointing at. */
.selected-line:hover::before {
  width: 4px;
  filter: brightness(1.5);
}

#rotation-cursor {
  position: relative;
  left: -99999px;
  height: 20px;
  width: 20px;
}

#pen-cursor {
  position: relative;
  left: -99999px;
  height: 25px;
  width: 14px;
}

#eyedropper-cursor {
  height: 25px;
  user-select: none;
  z-index: 750;
}

/* Page, fill, stroke, gradient, behavior, and ring add/remove controls.
   The page add/delete divs are flex items of .page-header-row, centred on
   its line — no float. The right-edge gap comes from the row's
   padding-right; the plus (rightmost) needs no margin of its own. */
.page-add-div {
  display: flex;
  align-items: center;
  margin: 0;
}

.fill-add-div,
.stroke-add-div,
.gradient-add-div,
.grid-add-div {
  display: inline-flex;
  align-items: center;
  margin: 0 var(--panel-control-gap) 0 0;
}

.add-fill-icon,
.add-palette-color-icon,
.add-stroke-icon,
.add-variable-icon,
.add-grid-icon,
.unify-paint-icon,
.add-trigger-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.page-delete-div {
  display: flex;
  align-items: center;
  margin: 0 5px 0 0;
}

/* Regenerate-the-App-page sits LEFT of the delete/add (−/+) pair, with a gap after it:
   it acts on one particular page, where those two act on the list. */
.page-generate-div {
  display: flex;
  align-items: center;
  margin: 0 9px 0 0;
}

/* Extra layer tools (Group, Add-folder) sit left of the delete/add (−/+) pair;
   a right gap keeps them from reading as part of that pair. */
.layer-tool-div {
  margin-right: 11px;
}

.fill-delete-div,
.stroke-delete-div,
.gradient-delete-div,
.delete-gradient-color-div,
.delete-behavior-div,
.delete-ring-div {
  display: inline-flex;
  align-items: center;
  margin: 0 var(--panel-control-gap) 0 0;
}

.ring-shift-div {
  display: inline-flex;
  align-items: center;
  /* Up/down arrows start 8px to the right of the "Ring N" name. */
  margin: 0 var(--panel-control-gap) 0 8px;
}

/* The "Ring N" name sits right after the disclosure triangle, so it lands at level 1
   (one --panel-indent from the left) like every other section heading — no extra shift. */
.details-header > .ring-heading {
  margin-left: 0;
}

.add-behavior-div,
.variable-add-div,
.trigger-add-div {
  display: inline-flex;
  align-items: center;
  margin: 0 var(--panel-control-gap) 0 0;
}

.delete-color-icon,
.delete-stroke-icon,
.delete-gradient-icon,
.delete-gradient-color-icon,
.delete-behavior-icon,
.delete-ring-icon,
.variables-delete-icon,
.delete-trigger-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

.add-behavior-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  user-select: none;
  vertical-align: middle;
}

/* Standard accent add/remove button: an inline +/- glyph stroked in the theme
   accent (see AccentIcon.tsx) — only the glyph is coloured, not a box. Sized to
   the standard small-icon box so all +/- buttons match; muted at rest and
   brightened on hover, like the left-panel page-list +/- buttons. */
.accent-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--editor-small-icon-size);
  height: var(--editor-small-icon-size);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  vertical-align: middle;
  opacity: var(--editor-icon-muted-opacity);
}
/* Hover brightens the glyph itself (muted -> full), matching the left-panel
   page-list +/- buttons — not a painted background behind it. */
.accent-icon-btn:hover { opacity: 1; background-color: transparent; }
/* Nothing to act on: dimmer than muted, no hover lift, and the cursor says so. Same
   treatment a disabled icon-button gets elsewhere — the button is still there (so the
   section still reads as something you can add to) but it doesn't pretend to be live. */
.accent-icon-btn.disabled { opacity: 0.3; cursor: default; }
.accent-icon-btn.disabled:hover { opacity: 0.3; }
/* The glyph fills the standard small-icon box, so themed +/- match the size of
   the image-based add/remove icons everywhere. */
.accent-icon-btn > svg { display: block; width: var(--editor-small-icon-size); height: var(--editor-small-icon-size); }

/* Color, fill, stroke, line, and selection color controls. */
/* Color swatch that opens the color picker — sized and styled to match
   the gradient color-stop swatches. */
.color-div {
  height: 20px;
  width: 28px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  display: inline-block;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  flex: 0 0 auto;
  cursor: pointer;
}

.fill-details-div label {
  display: inline;
  margin: 0px;
  position: relative;
  font-size: 14pt;
  top: -10px;
  user-select: none;
}

.color-text-input {
  width: 72px;
  box-sizing: border-box;
  padding: 2px 4px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* Opacity %. Styled and behaving like the scale / skew percentage
   boxes — the "%" lives inside the box (no trailing label). */
.alpha-input {
  width: 52px;
  box-sizing: border-box;
  padding: 2px 4px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

.gradient-stop-position-input {
  position: relative;
  width: 25px;
  margin: 0 3px;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
}

/* Level-1 row: width is left auto so it fills the panel content area
   live as the panel resizes, keeping the right-aligned sight controls
   pinned to the panel's right edge. */
.color-details-div {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin-left: var(--panel-indent);
  position: relative;
}
/* The Selection Colors rows reuse .color-details-div, so they already inherit
   the design-panel inter-row gap (--details-row-pad) — the same as the Fill /
   Stroke colour rows. They are NOT treated as a dense name list (Pages /
   Layers), so they honour the standard inter-line gap, not --list-row-gap. */

/* Token link/unlink indicator. Absolutely placed (out of flow) so the colour
   swatch stays flush at panel-indent — lined up with the section heading text —
   while the icon is horizontally centred in the indent gutter between the panel
   edge and the swatch. High z-index so a slider handle never covers it.
   Faint when unlinked. */
.token-link-icon {
  position: absolute;
  left: calc(var(--panel-indent) / -2 - 6px);
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  opacity: 0.3;
  cursor: default;
  z-index: 30;
}
.token-link-icon.linked {
  opacity: 1;
  cursor: pointer;
}

/* Generic Level-1 row wrapper: --details-row-height tall (min), flex with
   align-items: center, indented to panel-indent. Use this for single-control
   rows and for icon rows whose icons fill the row height (like the Text
   align icons). The vertical padding adds breathing room above and below
   the children, so a row of full-height icons reads with the same visual
   spacing as a row of text + input. */
.section-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin-left: var(--panel-indent);
}

.stroke-details-div,
.line-details-div,
.image-details-div {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin-left: var(--panel-indent);
  /* Right gutter so the width input (which now grows to fill the row)
     stops short of the panel edge. */
  margin-right: var(--panel-right-gutter);
}

.fill-details-div p {
  width: 100%;
  text-align: left;
}

.stroke-details-div p {
  width: 100%;
  text-align: left;
}

.line-width-icon,
.stroke-width-icon {
  width: 16px;
  height: 16px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  opacity: var(--editor-icon-muted-opacity);
  vertical-align: middle;
}

.line-width-input,
.stroke-width-input {
  position: relative;
  /* Formula-capable — grow into spare row width but cap the growth so a
     lone width field doesn't stretch across the whole panel. */
  flex: 1 1 0;
  width: auto;
  min-width: 40px;
  max-width: 150px;
  margin: 0 var(--panel-control-gap) 0 0;
  vertical-align: middle;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
}

/* .line-width-triangle shares its layout rule with .corners-triangle above. */

.stroke-dashes-label,
.line-dashes-label {
  display: inline-block;
  width: auto;
  /* Keep the "D, Gap (unit)" label on one line — it sat in a flex row that let
     it shrink and wrap. The room it needs is taken off the input's left (see
     the narrower .*-dashes-text-input width below). */
  flex-shrink: 0;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  text-align: left;
  vertical-align: middle;
}

/* Dash / gap row: small label→input gap, modest input→Auto gap, and
   every item vertically centered. The shared is-auto-dashes rules add
   stray top offsets, so re-flatten them within the dash rows. */
.stroke-details-div .is-auto-dashes-label,
.line-details-div .is-auto-dashes-label {
  display: inline-flex;
  align-items: center;
  margin: 0 0 0 13px;
}

.stroke-details-div .is-auto-dashes-checkbox,
.line-details-div .is-auto-dashes-checkbox {
  position: static;
  top: 0;
  margin: 0 4px 0 0;
}

.stroke-dashes-text-input,
.line-dashes-text-input {
  position: relative;
  /* Narrowed from 110px to give the "D, Gap (unit)" label a little more room on
     its left so it never wraps; a dash/gap value is short and still fits. The
     label holds its width (flex-shrink: 0 above), so if a row is ever tighter
     still, this input yields rather than the label wrapping. */
  width: 100px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* Match count + sight icon, pushed to the right edge of the color row.
   Flex (not float) so the count and icon align on the row's centre line;
   flex: none so the group keeps its natural width and never clips. */
.sight-div {
  display: flex;
  align-items: center;
  /* Fill the row's free space (after the swatch + hex + alpha) so the count
     inside can right-align against the icon instead of sitting content-width. */
  flex: 1 1 auto;
  /* No vertical padding: the enclosing .color-details-div row already supplies
     the inter-row gap. Padding here made the sight-div the tallest child and
     inflated every selection-colour row by an extra 2 × --details-row-pad. */
}

.sight-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: 20px;
  /* Keep a comfortable click target horizontally, but don't let the vertical
     padding inflate the color row taller than the swatch/hex it sits beside. */
  padding: 2px 7px;
  user-select: none;
}

/* Clickable panel icons — the section plus / minus add & delete icons,
   the selection-colors targeting icon, the path-operation icons, and the
   pen-ring icons — get a top-toolbar-style hover background. It sits on
   the icon element itself so it hugs the icon's own box and never shifts
   the surrounding layout the way a padded toolbar button would. */
.add-fill-icon,
.add-palette-color-icon,
.add-stroke-icon,
.add-variable-icon,
.add-grid-icon,
.unify-paint-icon,
.add-trigger-icon,
.delete-color-icon,
.delete-gradient-color-icon,
.delete-gradient-icon,
.delete-ring-icon,
.delete-stroke-icon,
.variables-delete-icon,
.delete-trigger-icon,
.page-list-plus-icon,
.page-list-minus-icon,
.page-generate-icon,
.sight-icon,
.group-icon,
.union-icon,
.outline-icon,
.subtract-icon,
.exclude-icon,
.intersect-icon,
.mask-icon,
.pen-icon,
.ring-icon {
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}

.add-fill-icon:hover,
.add-palette-color-icon:hover,
.unify-paint-icon:hover,
.add-stroke-icon:hover,
.add-variable-icon:hover,
.add-grid-icon:hover,
.add-trigger-icon:hover,
.delete-color-icon:hover,
.delete-gradient-color-icon:hover,
.delete-gradient-icon:hover,
.delete-ring-icon:hover,
.delete-stroke-icon:hover,
.variables-delete-icon:hover,
.delete-trigger-icon:hover,
.page-list-plus-icon:hover,
.page-list-minus-icon:hover,
.page-generate-icon:hover,
.sight-icon:hover,
.group-icon:hover,
.union-icon:hover,
.outline-icon:hover,
.subtract-icon:hover,
.exclude-icon:hover,
.intersect-icon:hover,
.mask-icon:hover,
.pen-icon:hover,
.ring-icon:hover {
  background-color: transparent;
  opacity: 1;
}

/* Plus / minus add & delete icon-buttons: a slightly larger clickable area,
   and a hover that brightens only the glyph rather than painting a background. */
.add-fill-icon,
.add-palette-color-icon,
.unify-paint-icon,
.add-stroke-icon,
.add-behavior-icon,
.add-variable-icon,
.add-grid-icon,
.add-trigger-icon,
.delete-color-icon,
.delete-stroke-icon,
.delete-gradient-icon,
.delete-gradient-color-icon,
.delete-behavior-icon,
.delete-ring-icon,
.variables-delete-icon,
.delete-trigger-icon,
.page-list-plus-icon,
.page-list-minus-icon,
.page-generate-icon,
.minus-variable-icon,
.remove-groups-icon {
  box-sizing: content-box;
  padding: 1px;
  cursor: pointer;
}

.add-fill-icon:hover,
.add-palette-color-icon:hover,
.unify-paint-icon:hover,
.add-stroke-icon:hover,
.add-behavior-icon:hover,
.add-variable-icon:hover,
.add-grid-icon:hover,
.add-trigger-icon:hover,
.delete-color-icon:hover,
.delete-stroke-icon:hover,
.delete-gradient-icon:hover,
.delete-gradient-color-icon:hover,
.delete-behavior-icon:hover,
.delete-ring-icon:hover,
.variables-delete-icon:hover,
.delete-trigger-icon:hover,
.page-list-plus-icon:hover,
.page-list-minus-icon:hover,
.minus-variable-icon:hover,
.remove-groups-icon:hover,
.ring-icon:hover,
.sight-icon:hover {
  background-color: transparent;
  opacity: 1;
}

.corner-count-icon {
  opacity: var(--editor-icon-muted-opacity);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  height: 14px;
  user-select: none;
  vertical-align: middle;
}

/* Angle dimension glyph — replaces the old "A" text label. */
.angle-icon {
  opacity: var(--editor-icon-muted-opacity);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  width: 14px;
  height: 14px;
  user-select: none;
  vertical-align: middle;
}

.star-ratio-icon {
  opacity: var(--editor-icon-muted-opacity);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  height: 14px;
  user-select: none;
  vertical-align: middle;
}

/* Text, typography, media, alignment, and shape option controls. */
#text-area {
  font-family: var(--editor-font-family);
  position: absolute;
  display: block;
  border: none;
  outline: none;
  padding: 0;
  background: transparent;
  font-size: var(--editor-control-font-size);
  font-weight: normal;
  line-height: 1.25em;
  resize: none;
  color: black;
  z-index: 100;
  user-select: all;
  overflow: hidden;
  white-space: break-spaces;
  overflow-wrap: normal;
}

.font-div {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin-left: var(--panel-indent);
  /* Leave the standard right gutter so a row's last control (the size-unit combo,
     the Italic label, the Letter box) can't run to or past the panel edge. */
  margin-right: var(--panel-right-gutter);
}

.font-div label {
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}

.font-div input {
  border: var(--editor-control-border);
  outline-color: var(--editor-text-color);
  color: var(--editor-text-color);
}

.font-div input.font-div-font {
  margin-right: var(--panel-pair-gap);
}

.font-div input.font-div-size {
  margin-right: var(--panel-control-gap);
}

/* Line / letter spacing row. The Line label sits at the full level-1 indent
   (like the Font label), and the Letter label gets the same gap that separates
   the Font and Size boxes (--panel-pair-gap), placed before it via the line
   input's right margin. */
.text-spacing-div {
  margin-left: var(--panel-indent);
}
/* Keep the two-word labels ("¶ Space", "Indent", "Letter") on one line — a flex
   item shrinks to min-content by default, which broke "¶ Space" onto two lines. */
.text-spacing-div label {
  white-space: nowrap;
  flex: 0 0 auto;
}
.text-spacing-input {
  width: 52px;
}
.text-spacing-div input.line-spacing-input {
  margin-right: var(--panel-pair-gap);
}

/* The text alignment row's .section-row already provides the level-1 indent;
   cancel the nested .align-div's own indent so the icons start at level 1
   instead of stacking to level 2. The vertical-align group keeps its gap. */
.text-align-row > .align-div:first-child {
  margin-left: 0;
}
/* Between-group gap to the vertical-align group — matches the Image section's
   groups (was a full --panel-indent, which read as too large). */
.text-align-row > .align-div:last-child {
  margin-left: 16px;
}

.is-vertex-up-checkbox,
.is-equilateral-checkbox,
.is-even-odd-checkbox,
.is-closed-checkbox,
.is-auto-dashes-checkbox {
  position: relative;
  top: 2px;
  margin: 0 var(--panel-checkbox-label-gap) 0 0px;
}

.is-vertex-up-label,
.is-equilateral-label,
.is-even-odd-label,
.is-is-closed-label,
.is-auto-dashes-label {
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  margin: 0 0 0 3px;
  color: var(--editor-muted-text-color);
  user-select: none;
}

.is-italic-checkbox {
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  vertical-align: middle;
}

.is-radial-checkbox {
  position: relative;
  top: 2px;
  margin: 0 var(--panel-checkbox-label-gap) 0 12px;
}

#is-italic-label,
#is-radial-label {
  font-size: var(--editor-control-font-size);
  margin: 0;
  user-select: none;
  vertical-align: middle;
}

.font-div-font {
  width: 120px;
}

/* The font-family field fills its row (font names run long — "Franklin Gothic
   Medium", "Segoe UI Historic"), with the size box fixed on the right. The row
   leaves the standard right gutter, and the Style dropdown above fills to the
   same edge so the two wide controls line up. */
.font-family-row {
  margin-right: var(--panel-right-gutter);
}
.font-family-row .font-div-font {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}
/* Size box sits at the row's right edge — drop its trailing gap so its right
   edge lines up with the Style dropdown above (both at the gutter). Needs the
   `input` to out-specify `.font-div input.font-div-size`. */
.font-family-row input.font-div-size {
  margin-right: 0;
}

.font-div-size {
  width: 50px;
  /* flex-basis (not width) wins in the flex .font-div row, so the broad
     `.details-scroll input { width: 100% }` override can't stretch the
     size box off the panel edge. */
  flex: 0 0 50px;
}

/* Size combo: the number field + a caret that opens the common-sizes dropdown. */
.sizepicker {
  display: inline-flex;
  align-items: center;
  position: relative;
  flex: 0 0 auto;
  margin-right: var(--panel-control-gap);
}
/* No gap between the field and its caret — they read as one control. */
.font-div .sizepicker input.font-div-size {
  margin-right: 0;
}
.sizepicker-caret {
  flex: 0 0 auto;
  width: 16px;
  height: 22px;
  margin-left: 2px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 10px;
  line-height: 1;
}
.sizepicker-caret:hover { color: var(--editor-selected-blue); }
/* Right-align the numbers so they line up with the field's value. */
.sizepicker-item {
  justify-content: flex-end;
  font-variant-numeric: tabular-nums;
}
/* The size list is as wide as its numbers (see FontSizePicker), with a floor so a short list
   is still a target. The scrollbar's room is reserved rather than taken from the numbers —
   the list scrolls, and a gutter that appears inside a content-sized box would clip "96". */
.sizepicker-pop {
  width: max-content;
  min-width: 56px;
}
.sizepicker-pop .sizepicker-list {
  scrollbar-gutter: stable;
}

.font-weight-types {
  /* Fill the row and SHRINK if needed. flex-basis (in the shorthand) beats the
     `.details-scroll select { width: 100% }` rule, which would otherwise make
     this select 100% wide and push its row-mates (Italic, the decoration
     buttons) off the panel edge. */
  flex: 1 1 0;
  min-width: 0;
  margin-right: var(--panel-pair-gap);
  border: var(--editor-control-border);
  outline-color: var(--editor-text-color);
  color: var(--editor-text-color);
  vertical-align: middle;
}

/* Font-size unit combo — sits right after the size box on the Size line. Fixed width +
   no-grow so a long unit name can't stretch the Size row past the panel gutter.
   flex-basis (not `width`) pins it, since `.details-scroll select { width: 100% }`
   out-specifies a plain width and would otherwise blow the size unit off the edge. */
.font-unit-types {
  flex: 0 0 56px;
  box-sizing: border-box;
  min-width: 0;
  border: var(--editor-control-border);
  outline-color: var(--editor-text-color);
  color: var(--editor-text-color);
  vertical-align: middle;
}

/* ---- Measurement rows (Size / Line / Letter / ¶ Space / Indent) ----
   Each measurement sits on its OWN line as a 3-column grid — label | value |
   unit ("measurement type") — so the value fields and the unit controls line up
   down the whole group. Size uses a dropdown (many units); the others use a
   two-cell segmented toggle (like the Area/Point type switcher). Overrides the
   .font-div flex layout for these rows only. */
.measure-row.font-div {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr) 62px;
  align-items: center;
  column-gap: var(--panel-control-gap, 8px);
}
.measure-row > label {
  margin: 0;
  white-space: nowrap;
  justify-self: start;
}
.measure-row .measure-input {
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  /* Numeric field — honour the Number-alignment preference (Customize) like the
     other numeric edit boxes (the unit now lives in the toggle, so these hold a
     bare number). */
  text-align: var(--number-input-align, right);
}
select.measure-unit {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  margin: 0;
  border: var(--editor-control-border);
  outline-color: var(--editor-text-color);
  color: var(--editor-text-color);
  vertical-align: middle;
}
/* Segmented unit toggle (× / % and px / em) — two equal cells filling the unit
   column, reusing the align-cell selected highlight from the alignment icons. */
.measure-unit-seg {
  display: flex;
  width: 100%;
  gap: 2px;
}
.measure-unit-cell.align-icon-cell {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  padding: 1px 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  line-height: 1.4;
}
/* The Size row's FontSizePicker fills the value column like the plain inputs;
   its inner number field grows to fill the picker, leaving room for the caret. */
.measure-row .sizepicker {
  width: 100%;
  margin: 0;
}
.measure-row .sizepicker input.font-div-size {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}

/* ---- Font picker (replaces the old font-family datalist input) ---- */
/* Trigger looks like the other row controls and fills the family row, with the
   current family previewed in its own typeface. */
.fontpicker-trigger {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  height: var(--editor-control-height, 24px);
  padding: 0 6px;
  background: var(--editor-panel-background-color);
  border: var(--editor-control-border);
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  cursor: pointer;
  text-align: left;
}
.fontpicker-trigger.mixed .fontpicker-trigger-label { color: var(--editor-muted-text-color); }
.fontpicker-trigger-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.fontpicker-caret {
  flex: 0 0 auto;
  font-size: 10px;
  color: var(--editor-muted-text-color);
}

/* Popover (portaled to <body>, fixed-positioned to the trigger). */
.fontpicker-backdrop {
  position: fixed;
  inset: 0;
  z-index: 999;
}
.fontpicker-pop {
  z-index: 1000;
  display: flex;
  flex-direction: column;
  max-height: 330px;
  background: var(--editor-panel-background-color);
  border: 1px solid var(--editor-panel-separator-color);
  border-radius: 6px;
  box-shadow: 0 8px 28px -10px rgba(0, 0, 0, 0.4);
  overflow: hidden;
}
.fontpicker-search {
  flex: 0 0 auto;
  margin: 6px;
  padding: 5px 8px;
  border: var(--editor-control-border);
  border-radius: 4px;
  background: var(--editor-panel-background-color);
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  outline-color: var(--editor-selected-blue);
}
.fontpicker-list {
  flex: 1 1 auto;
  overflow-y: auto;
  list-style: none;
  margin: 0;
  padding: 0 0 6px;
}
.fontpicker-group { list-style: none; }
.fontpicker-group-label {
  position: sticky;
  top: 0;
  padding: 5px 10px 3px;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--editor-muted-text-color);
  background: var(--editor-panel-background-color);
}
.fontpicker-group-items { list-style: none; margin: 0; padding: 0; }
.fontpicker-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  cursor: pointer;
  color: var(--editor-text-color);
  font-size: 15px;
  line-height: 1.25;
}
.fontpicker-item:hover { background: color-mix(in srgb, var(--editor-selected-blue) 18%, transparent); }
.fontpicker-item.selected { background: color-mix(in srgb, var(--editor-selected-blue) 30%, transparent); }
.fontpicker-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.fontpicker-badge {
  flex: 0 0 auto;
  font-size: 9px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--editor-muted-text-color);
  font-family: var(--editor-font-family);
}
.fontpicker-empty { padding: 12px; color: var(--editor-muted-text-color); font-size: var(--editor-control-font-size); }
.fontpicker-spin {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border: 1.5px solid var(--editor-muted-text-color);
  border-top-color: transparent;
  border-radius: 50%;
  animation: fontpicker-spin 0.7s linear infinite;
}
.fontpicker-err { flex: 0 0 auto; color: #c0392b; font-weight: 700; }
.fontpicker-footer {
  flex: 0 0 auto;
  border-top: 1px solid var(--editor-panel-separator-color);
  padding: 6px;
}
.fontpicker-upload {
  width: 100%;
  padding: 6px 8px;
  background: transparent;
  border: 1px dashed var(--editor-panel-separator-color);
  border-radius: 4px;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  font-family: var(--editor-font-family);
  cursor: pointer;
}
.fontpicker-upload:hover:not(:disabled) { border-color: var(--editor-selected-blue); color: var(--editor-selected-blue); }
.fontpicker-upload:disabled { opacity: 0.6; cursor: default; }
@keyframes fontpicker-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .fontpicker-spin { animation: none; } }

/* Variable-weight slider + OpenType (small caps / kerning / width) rows. */
.variable-weight-row { gap: 8px; }
.variable-weight-slider {
  flex: 1 1 auto;
  min-width: 0;
  accent-color: var(--editor-selected-blue);
}
.variable-weight-value {
  flex: 0 0 auto;
  min-width: 28px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
.opentype-row .opentype-label {
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  user-select: none;
  margin: 0 14px 0 3px;
}
/* Wraps a checkbox + its label so the pair can carry one data-help entry (the
   Checkbox component itself drops unknown props). Inline-flex keeps the original
   inline layout; inner margins (checkbox gap, label 14px trailing) are unchanged. */
.opentype-toggle {
  display: inline-flex;
  align-items: center;
}
.deco-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: 8px;
  border: var(--editor-control-border);
  border-radius: 3px;
  cursor: pointer;
  font-size: 13px;
  color: var(--editor-text-color);
  user-select: none;
}
/* The B/I/U/S buttons are TOGGLES: a subtle resting state, and a clear filled
   "engaged" state (blue fill + white glyph) so an active style reads at a glance
   — the standard formatting-toggle convention. */
.deco-btn:hover:not(.deco-btn-on) {
  border-color: var(--editor-selected-blue);
  color: var(--editor-selected-blue);
}
.deco-btn-on {
  border-color: var(--editor-selected-blue);
  background: var(--editor-selected-blue);
  color: #fff;
}
.deco-btn-b { font-weight: 800; }

/* Rich-text formatting toolbar (B / I / U / S) — the visible entry point for
   styling text, or a selected range while editing on the canvas. */
.text-format-row { gap: 4px; }
.text-format-row .deco-btn { margin-left: 0; }
.text-rich-hint {
  flex: 1 1 0;
  min-width: 0;
  margin-left: 8px;
  font-size: 10.5px;
  line-height: 1.25;
  color: var(--editor-muted-text-color);
  user-select: none;
}

/* NOTE THE BAKED-IN INDENT. `.align-div` carries one --panel-indent of its own because it was
   born as a group standing alone on its row. Every placement that already provides the indent
   has to cancel it, and there are now four such cancellations below — the indent belongs to the
   ROW, not to the control, and this is the shape of that mistake. Left as-is because the class
   is used in many places; the cancellations are the safe half of the fix. */
.align-div {
  display: inline-flex;
  align-items: center;
  margin: 0 0 0 var(--panel-indent);
}

/* A SEGMENTED CONTROL THAT FOLLOWS A LABEL STARTS WHERE ITS ROW'S CONTROLS START. Both of these
   rows already sit at their own indent and put a label column before the control, so the extra
   --panel-indent above pushed the control a whole level right of everything it lines up with —
   and, in the Line Text Mode row, past the room left for it, so Basic / Advanced dropped onto a
   line of their own below the label. The arc's Wedge / Edge row had the same extra level, and so
   did the Shadow section's Cast row — which is why Outer / Inner started a level right of the
   Fill / Stroke icons on the Source row directly above it, when the two are a matched pair. */
.note-row .align-div,
.arc-row .align-div,
.shadow-type-row .align-div,
.effect-detail-row .align-div,
/* …and every row of the control panel, which is built the same way: a label column and then
   the control. The Style row (Look & Feel / D / Custom) was 5px short of its own width because
   of the extra level, so "Custom" dropped to a second line — the same failure the Line Text
   Mode row had, and the reason "Designer" was shortened to "D" in the first place. */
.control-prop-row .align-div {
  margin-left: 0;
}

.align-items-section {
  padding: 6px 0;
}
/* Both align groups (horizontal / vertical) sit on ONE line at the standard row
   height. A bit more space between the two groups than between their icons. */
.align-items-section .align-div {
  display: inline-flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.align-items-section .align-div + .align-div {
  margin-left: 8px;
}

.image-align-div {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* The X/Y offset row follows directly below, so no extra bottom margin
     is needed to separate the align icons from the next line. */
  margin: 0 4px 0 var(--panel-indent);
  display: block;
}

/* Frame Layout (prototype): give the align-icons row vertical breathing room,
   and widen the Width/Height label column so the labels clear their sliders —
   the flex-grow % edit box yields that width (so it shrinks). Scoped to the
   frame layout so the shared design-panel scale/skew rows are untouched. */
.frame-align-div {
  margin-top: 0;
  margin-bottom: 0;
}
.frame-scale-row .scale-skew-label {
  width: 52px;
  margin-right: 6px;
}

/* Import from Figma (Project tab). A drop target rather than a text box: the file is a
   megabyte of JSON nobody pastes by hand. */
.figma-import {
  padding-bottom: var(--details-row-pad);
}
.figma-drop {
  margin: var(--details-row-pad) 6px var(--details-row-pad) var(--panel-indent);
  padding: 14px 10px;
  border: 1px dashed var(--editor-control-border-color, var(--editor-border-color));
  border-radius: var(--corner-radius);
  text-align: center;
  cursor: pointer;
  user-select: none;
}
.figma-drop:hover,
.figma-drop-over {
  border-color: var(--editor-selected-blue, #3b82f6);
  background-color: var(--button-bg-hover);
}
.figma-drop-title {
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  color: var(--editor-text-color);
}
.figma-drop-sub {
  margin-top: 3px;
  font-size: 11px;
  color: var(--editor-muted-text-color);
}
.figma-drop-sub code {
  font-size: 10.5px;
}
/* One labelled row per field. The token box shows dots, so a mislabelled paste is
   invisible — the label has to be on screen, not just in a placeholder. */
.figma-field {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: var(--details-row-pad) 6px 0 var(--panel-indent);
}
.figma-label {
  flex: 0 0 42px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
.figma-input {
  flex: 1 1 120px;
  min-width: 0;
  box-sizing: border-box;
  padding: var(--editor-control-pad-y) 6px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg, var(--button-bg));
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
}
/* The "or" between the two routes — a divider, not a heading. */
.figma-or {
  margin: 6px 6px 0 var(--panel-indent);
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--editor-muted-text-color);
  opacity: 0.75;
}
.figma-note {
  margin: 4px 6px 0 var(--panel-indent);
  font-size: 11px;
  color: var(--editor-muted-text-color);
}
.figma-note-dim { opacity: 0.85; }
.figma-note-warn { color: #d64545; }
.figma-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 6px 6px 0 var(--panel-indent);
}
.figma-btn {
  flex: 0 0 auto;
  padding: var(--editor-control-pad-y) 10px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  line-height: var(--editor-control-line-height);
  color: var(--button-text);
  background-color: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.figma-btn:hover:not(:disabled),
.figma-btn:active:not(:disabled) { background-color: var(--button-bg-hover); }
.figma-btn:disabled { opacity: 0.45; cursor: default; }

/* Data panel — Database Schema. A wide, monospace SQL box (paste a schema.sql or a dump),
   its buttons, and the notes underneath. The width override is deliberate: the panel's
   `.details-scroll input { width: 100% }` rule doesn't reach a textarea, so this sets its own. */
/* Whose field this is (Data panel) — the assistant's, or the person's. Muted for "AI" and in
   the data accent for "You", because the second one is the state that changes what Generate
   will do. */
.data-source-badge {
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border: 1px solid currentColor;
  border-radius: var(--corner-radius-sm);
  padding: 0 4px;
  opacity: 0.7;
  color: var(--editor-text-color);
}
.data-source-badge.mine {
  color: var(--data-accent);
  opacity: 1;
}
.data-source-handback {
  flex: 0 0 auto;
  margin-left: 6px;
  padding: 0 6px;
  font-size: 10.5px;
  color: var(--editor-text-color);
  background-color: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}

/* Traceability (Data panel) — what depends on the selected entity. One indent in, like every
   other section's body; the rows are label + reason, and the reason wraps under it. */
.trace-body {
  margin-left: var(--panel-indent);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
  padding-right: 6px;
}
.trace-kind-head {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--data-accent);
  margin: 8px 0 2px;
}
.trace-line {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: baseline;
  font-size: 11.5px;
  line-height: 1.35;
  padding: 1px 0;
}
.trace-muted {
  color: var(--editor-muted-text-color, var(--editor-text-color));
  opacity: 0.75;
}
.trace-label {
  color: var(--editor-text-color);
}
.trace-why,
.trace-where,
.trace-by-name {
  color: var(--editor-muted-text-color, var(--editor-text-color));
  opacity: 0.75;
  font-size: 11px;
}
.trace-by-name {
  font-style: italic;
}

.db-schema-body {
  margin-left: var(--panel-indent);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
  padding-right: 6px;
}
.db-schema-input {
  box-sizing: border-box;
  width: 100%;
  resize: vertical;
  font-family: var(--editor-mono-font-family, monospace);
  font-size: 11px;
  line-height: 1.35;
  padding: 5px 6px;
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg, var(--button-bg));
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  white-space: pre;
  overflow: auto;
}
.db-schema-migration {
  margin-top: 6px;
}
.db-schema-row {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 6px;
}
.db-schema-btn {
  flex: 0 0 auto;
  padding: var(--editor-control-pad-y) 10px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  line-height: var(--editor-control-line-height);
  color: var(--button-text);
  background-color: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.db-schema-btn:hover:not(:disabled),
.db-schema-btn:active:not(:disabled) {
  background-color: var(--button-bg-hover);
}
.db-schema-btn:disabled {
  opacity: 0.45;
  cursor: default;
}
/* Which database the pasted schema is — sits in the same row as the buttons and wears the
   same clothes as the panel's other selects, so it reads as part of the row rather than as
   the browser's own control dropped into it. */
.db-schema-select {
  flex: 0 0 auto;
  padding: var(--editor-control-pad-y) 6px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  line-height: var(--editor-control-line-height);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
}
.db-schema-note {
  margin-top: 5px;
  font-size: 11px;
  color: var(--editor-muted-text-color);
}
.db-schema-skip {
  opacity: 0.85;
}

/* Prototype backdrop: one row per theme (light / dark). The label sits at the section's
   level-1 indent and the swatch keeps its own .color-details-div indent under it. */
.proto-bg-row {
  display: block;
}
/* Frame alignment X/Y offset row (prototype Frame Layout). */
.frame-offset-row {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  /* Standard inter-row gap (was a fixed 6px bottom margin, out of step with the
     other prototype rows). */
  margin: 0 4px 0 var(--panel-indent);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}
.frame-offset-field {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.frame-offset-label {
  color: var(--editor-muted-text-color);
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
}
.frame-offset-input {
  width: 52px;
  /* flex-basis (not width) survives the `.details-scroll input { width:
     100% }` override in the flex .frame-offset-field row. */
  flex: 0 0 52px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  text-align: var(--number-input-align, right);
  vertical-align: middle;
}

/* The Image section's align group sits inside .image-align-div, which
   already provides the Level-1 indent — cancel the shared .align-div indent
   here so the group starts at Level 1 rather than stacking to Level 2. */
.image-align-div .align-div {
  margin-left: 0;
}

/* The picture X/Y offset row. Sits at Level-1 indent like the rest of the
   Image section; labels and inputs use the panel's normal control font and
   the Reset button matches the other panel buttons (.export-button). */
.image-offset-div {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  column-gap: 14px;
  row-gap: 4px;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

.image-offset-field {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}

.image-offset-input {
  width: 44px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

.image-offset-reset,
.proto-bg-reset,
.line-text-offset-clear,
.effect-seed-new,
.angle-reset {
  margin-left: auto;
  flex: none;
  /* Size to the same height as the edit fields: same line-height + vertical
     padding + border, so the reset button and the number box line up. */
  padding: var(--editor-control-pad-y) 12px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  line-height: var(--editor-control-line-height);
  text-align: center;
  color: var(--button-text);
  background-color: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  user-select: none;
}

.image-offset-reset:hover,
.image-offset-reset:active,
.line-text-offset-clear:hover,
.line-text-offset-clear:active,
.proto-bg-reset:hover:not(:disabled),
.proto-bg-reset:active:not(:disabled),
.effect-seed-new:hover,
.effect-seed-new:active,
.angle-reset:hover:not(:disabled),
.angle-reset:active:not(:disabled) {
  background-color: var(--button-bg-hover);
}

.angle-reset:disabled,
.proto-bg-reset:disabled {
  opacity: 0.45;
  cursor: default;
}

/* The backdrop rows put their swatch and Reset on one line; the swatch keeps its own
   indent and Reset sits at the far end (margin-left: auto, from the shared rule). */
.proto-bg-row .color-details-div {
  padding-right: 4px;
}

.offscreen-image {
  position: relative;
  left: -99999px;
}

.video {
  position: absolute;
  top: 100px;
  left: 200px;
}

.arrow-types {
  vertical-align: middle;
}

.arrow-icon {
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  width: 10px;
  opacity: var(--editor-icon-muted-opacity);
  vertical-align: middle;
}

/* Line-end cap picker: three selectable icons (butt / round / square)
   replacing the old cap dropdown. The selection outline lives on the
   (unfiltered) cell so it shows the true selection colour — the icon
   image itself carries the dark-mode invert filter. */
.cap-selector {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  vertical-align: middle;
}

/* A cap picker for a line end that carries an adornment — the cap is
   irrelevant there, so it is shown greyed-out and non-interactive. */
.cap-selector-disabled {
  opacity: 0.35;
  pointer-events: none;
}

.cap-icon-cell {
  display: inline-flex;
  padding: 1px;
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  user-select: none;
  outline: var(--selection-border-width) solid transparent;
  outline-offset: calc(-1 * var(--selection-border-width));
}

.cap-icon-cell:hover {
  outline-color: var(--editor-control-border-color);
}

.cap-icon-cell-selected:hover {
  outline-color: var(--editor-selected-blue);
}

/* Selected cap: identical to a pressed left-toolbar tool — same active
   background and same selection outline. The outline lives on this
   (unfiltered) cell, so it shows the true selection colour in dark
   mode rather than the brightened tint the icon filter produced. */
.cap-icon-cell-selected {
  background-color: var(--tool-active-bg, #e3e8f2);
  outline-color: var(--editor-selected-blue);
}

.cap-icon-img {
  display: block;
  width: 18px;
  height: 18px;
  opacity: var(--editor-icon-muted-opacity);
}

.cap-icon-cell-selected .cap-icon-img {
  opacity: 1;
}

/* West-end caps: rotate the icon 180° so the end faces left. */
.cap-icon-west {
  transform: rotate(180deg);
}

/* Link / unlink toggle between west and east cap selectors. */
.cap-link-toggle {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin: 0 4px;
  opacity: var(--editor-icon-muted-opacity);
  cursor: pointer;
  user-select: none;
  border-radius: var(--corner-radius-sm);
  padding: 1px;
  box-sizing: content-box;
}

.cap-link-toggle:hover {
  background-color: transparent;
  opacity: 1;
}

.cap-link-toggle-disabled {
  opacity: 0.35;
  pointer-events: none;
}

/* (icon + cap selector) pair. The second pair on a row is nudged right
   so the two ends read as two distinct groups. */
.line-end-pair {
  display: inline-flex;
  align-items: center;
}

.line-end-pair ~ .line-end-pair {
  margin-left: var(--panel-pair-gap);
}

/* Aligned two-column rows for the line section's west/east controls: two EQUAL columns
   with the pair gap between them, so each end's control is the same width as the other's
   and every east control — and its icon — starts at the same x. The west column used to be
   a fixed 128px while the east one sized to its content, which made the west arrowhead
   select wider than the east one and ran it into the east arrow icon. */
.line-pair-row {
  display: flex;
  align-items: center;
  gap: var(--panel-pair-gap);
  width: 100%;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.adornment-autosize-label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 var(--panel-right-gutter) 0 0;
  /* Give the autosize toggle the standard inter-row gap; it had none. */
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  cursor: pointer;
  user-select: none;
}

.line-pair-row > .line-pair-col {
  display: flex;
  align-items: center;
  flex: 1 1 0;
  min-width: 0;
}

/* Each end's arrowhead select fills what its column leaves after the arrow icon — so the two
   are always the same width, and each box is a definite size its native dropdown opens
   against. `select.` on purpose: `.details-scroll select { width: 100% }` out-specifies a lone
   class, which is why the old `width: 86px` here never applied at all. */
.line-pair-col > select.arrow-types {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}

/* Cap row: two equal halves with the link/unlink toggle centred between them.
   Unlike the other line-pair-rows, this row does not use a fixed-width first
   column — both sides flex to equal width so the icon lands exactly between
   the two CapSelector groups. */
.cap-link-row {
  display: flex;
  align-items: center;
  width: 100%;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.cap-link-row > .cap-link-west,
.cap-link-row > .cap-link-east {
  flex: 1;
  display: flex;
  align-items: center;
}

/* In the line-end context only: inset matches arrow-icon (10px) +
   gap so cap icons left-align with the selects and sliders below. */
.details-line-group .cap-link-west,
.details-line-group .cap-link-east {
  padding-left: calc(10px + var(--panel-checkbox-label-gap));
}

/* Line style (solid / dashed) radio group, indented to level 1. */
.line-style-section {
  margin-left: var(--panel-indent);
}
/* The orthogonal-routing toggle is a bare radio directly under the section
   (unlike the Solid/Dashed pair, which sit in a padded .line-style-radios), so
   it had no vertical gap — give it the standard inter-row gap. */
.line-style-section > .line-style-radio {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

/* The dashes row nested under the (already level-1) line-style section
   adds one more indent step so the dash / gap line sits at level 2. */
.line-style-section .line-details-div {
  margin-left: var(--panel-indent);
}

.line-style-radios {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.line-style-radio {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  user-select: none;
  cursor: pointer;
}

/* Zero the browser-default margin on BOTH the radio and the checkbox that share
   this class (Solid/Dashed radios + the Orthogonal-routing checkbox), so every
   toggle control's left edge sits at the section's level-1 indent and lines up
   with each other and the labels. `input[type="radio"]` alone left the checkbox
   ~4px to the right. */
.line-style-radio input {
  margin: 0;
}

.stroke-cap,
.line-cap {
  margin: 0 var(--panel-control-gap) 0 0;
  vertical-align: middle;
}

.palette {
  position: relative;
  padding: 1px;
  height: 12px;
  width: var(--editor-swatch-size);
  opacity: var(--editor-icon-muted-opacity);
}

/* ── Item-tree drag and drop ──────────────────────────────────────────
   A blue line marks a sibling drop; a tinted, outlined row marks a drop
   *into* a container; the row being dragged is dimmed. */
.tree-drop-line {
  height: 2px;
  margin-right: 6px;
  background-color: var(--editor-selected-blue, #1097e2);
  border-radius: 1px;
  pointer-events: auto;
}

/* A small knob at the start of the insertion line for extra clarity. */
.tree-drop-line::before {
  content: '';
  display: block;
  width: 6px;
  height: 6px;
  margin: -2px 0 0 -3px;
  border-radius: 50%;
  background-color: var(--editor-selected-blue, #1097e2);
}

.tree-drop-into {
  background-color: rgba(16, 151, 226, 0.22) !important;
  outline: 1px solid var(--editor-selected-blue, #1097e2);
  outline-offset: -1px;
}

.tree-row-dragging {
  opacity: 0.4;
}

/* ── Help mode panel ──────────────────────────────────────────────────
   Shown in the left panel, in place of the layers tree, while help mode
   is on. Describes whatever element the pointer is over. */
.help-panel {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 12px 14px;
  color: var(--editor-text-color);
}

/* Help mode is view-only: force the question-mark help cursor everywhere
   so it's obvious the pointer describes things rather than operating them.
   The help-mode toggle button is the one exception — it stays a pointer
   so the user can click it to exit. */
body.help-mode,
body.help-mode * {
  cursor: help !important;
}

body.help-mode .help-mode-icon,
body.help-mode .help-mode-icon * {
  cursor: pointer !important;
}

/* "Help mode" header: a recessed band near the top of the panel, with a
   light background and an even 7px gap above, below, and on either side.
   The negative margins cancel the .help-panel padding (12px 14px) down to
   that 7px inset — hence -5px top and -7px sides. */
.help-panel-badge {
  display: block;
  margin: -5px -7px 7px -7px;
  padding: 8px 14px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--editor-muted-text-color);
  background-color: var(--editor-panel-separator-color);
}

.help-panel-title {
  margin: 0 0 6px 0;
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  color: var(--header-2-color);
}

/* Sub-heading inside the help panel — used for the "Customizable" block
   that names where a field's default lives in Customize → Engine Defaults. */
.help-panel-subtitle {
  margin: 10px 0 6px 0;
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  color: var(--header-2-color);
}

/* The description is indented under the title, left-aligned, with a
   selection-coloured accent line down its left edge. */
.help-panel-body {
  margin: 0 0 10px 0;
  padding-left: 12px;
  border-left: 2px solid var(--editor-selected-blue, #1097e2);
  font-size: 12.5px;
  line-height: 1.5;
  text-align: left;
  opacity: 0.85;
}

/* Floating help tooltip that follows the cursor in help mode. Shows the
   title and (when present) a body summary — the prototype canvas uses the
   body to describe the hovered item's type and interactions. */
.help-tooltip {
  position: fixed;
  left: -9999px;
  top: -9999px;
  z-index: 10000;
  max-width: 380px;
  padding: 8px 12px;
  font-size: 15px;
  line-height: 1.4;
  color: #ffffff;
  background-color: rgba(20, 22, 28, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--corner-radius-sm);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  pointer-events: none;
  white-space: normal;
  overflow-wrap: anywhere;
}

.help-tooltip-title {
  font-size: 16px;
  font-weight: 600;
}

.help-tooltip-body {
  margin-top: 5px;
  font-size: 14px;
  font-weight: 400;
  opacity: 0.85;
  white-space: pre-line;
}

/* Subhead inside the tooltip — used for "Customizable" before the
   default-location sentence. Sits above its own body block. */
.help-tooltip-subtitle {
  margin-top: 8px;
  font-size: 16px;
  font-weight: 600;
}

/* A second, distinct help entry below the first (e.g. an edit-badged tool
   describes the tool AND the badge), set off by a divider. */
.help-tooltip-extra {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--panel-border-color, #b8b8b8);
}

.help-tooltip-hidden {
  display: none;
}

.group-icon,
.union-icon,
.outline-icon,
.exclude-icon,
.intersect-icon,
.subtract-icon,
.mask-icon {
  margin: 0 3px 0 0;
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  vertical-align: middle;
}

/* Gap separating the regular group icon from the special-group icons,
   and the special-group cluster (mask is its last icon) from the add/remove
   icons. */
.group-icon {
  margin-right: 7px;
}

.mask-icon {
  margin-right: 7px;
}

.path-operations-div {
  display: inline-flex;
  align-items: center;
}

/* ── Grouping section (under Align): template / group / boolean / mask / ungroup.
   Icon-buttons sized like the Align buttons, with a gap between the logical
   clusters (template | group | boolean specials | ungroup). ── */
.grouping-div {
  margin-left: var(--panel-indent);   /* level-1 indent, like the other rows */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
/* Uniform 24px square boxes (a touch bigger, like the Align icons); the glyph
   scales to fit while keeping its aspect, so every button is the same size. */
.grouping-icon {
  width: 24px;
  height: 24px;
  padding: 1px;
  margin: 0 4px 0 0;
  opacity: 0.7;
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  vertical-align: middle;
  object-fit: contain;
}
/* Brighten the glyph (foreground) on hover — no background box. */
.grouping-icon:hover {
  opacity: 1;
}
/* The black glyph icons invert for dark mode; the template diamond carries its
   own accent colour, so it's excluded from the filter. */
[data-theme="dark"] .grouping-icon:not(.grouping-template) {
  filter: invert(0.94) hue-rotate(180deg) brightness(1.02);
}
/* Gaps separating the clusters: template | group | boolean specials | ungroup. */
.grouping-template,
.grouping-icon-group,
.grouping-icon-mask {
  margin-right: 12px;
}
/* Make-Template button: a purple diamond like the Layers tree's template badge
   ([[.tree-instance-badge]]), scaled up to the grouping-icon box. */
.grouping-template {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
}
.grouping-template-diamond {
  width: 14px;
  height: 14px;
  border: 2px solid var(--accent-color, #7a5cff);
  transform: rotate(45deg);
  box-sizing: border-box;
}
/* The diamond is already full-opacity, so brighten its foreground (the purple
   border) on hover rather than the opacity. */
.grouping-template:hover .grouping-template-diamond {
  border-color: #a58cff;
}
[data-theme="dark"] .grouping-template-diamond {
  border-color: #b9a7ff;
}
[data-theme="dark"] .grouping-template:hover .grouping-template-diamond {
  border-color: #d8ccff;
}

/* ── Type-header canvas-visibility (eye) toggle + Layers-tree hidden indicator ── */
.type-visibility-btn {
  order: 2;   /* pushed to the right of the header by the ::after spacer */
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  margin-left: 4px;
  margin-right: var(--panel-control-gap);   /* standard right gutter, like the header add/delete icons */
  border: none;
  background: transparent;
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  opacity: 0.38;   /* match the Layers-panel eyes' brightness */
}
.type-visibility-btn.hidden-on {
  opacity: 0.8;
}
.type-visibility-btn:hover {
  opacity: 1;
}
.type-visibility-icon {
  width: 16px;
  height: 16px;
  display: block;
}
[data-theme="dark"] .type-visibility-icon {
  filter: invert(1) brightness(1.2);   /* match the Layers-panel eyes in dark mode */
}
/* The eye-closed glyph shown at the right of a hidden Layers-tree row. Scoped
   under .item-tree-item so it beats the generic `.item-tree-item img { 6px }`
   rule (same trick as .tree-type-icon) — otherwise it renders at 6px. Sized like
   the type icon, vertically centred, full-contrast, with room from the row edge. */
/* Per-row visibility toggle: eye-open when shown (a quiet default), the slashed
   eye-closed when hidden (prominent). Scoped under .item-tree-item so it beats
   the generic `.item-tree-item img { 6px }` rule (like .tree-type-icon). */
.item-tree-item .tree-vis-eye {
  flex: 0 0 auto;
  display: block;
  align-self: center;
  margin-left: auto;
  /* Nudged right again (14px → 13px → 12px). */
  margin-right: 12px;
  padding: 0;
  width: 14px;
  height: 14px;
  opacity: 0.38;
  cursor: pointer;
}
.item-tree-item .tree-vis-eye.hidden {
  opacity: 0.8;
}
.item-tree-item .tree-vis-eye:hover {
  opacity: 1;
}
/* Full invert to white for solid contrast against the dark tree rows. */
[data-theme="dark"] .item-tree-item .tree-vis-eye {
  filter: invert(1) brightness(1.2);
}

/* Layer lock in the item tree — identical to the Layers-list lock
   (.page-lock-eye): a slashed padlock when unlocked, a solid padlock when
   locked. Its brightness tracks the tree eye — the slashed (unlocked) lock is
   the same 0.38 as a shown layer's eye; the solid (locked) lock is full, like
   the crossed-out eye. Sits just left of the tree eye; margin-left:auto
   right-aligns the lock+eye pair. */
.item-tree-item .tree-lock-eye {
  flex: 0 0 auto;
  display: block;
  align-self: center;
  margin-left: auto;
  margin-right: 8px;
  padding: 0;
  width: 13px;
  height: 13px;
  opacity: 0.38;
  cursor: pointer;
}
.item-tree-item .tree-lock-eye.on {
  opacity: 1;
}
.item-tree-item .tree-lock-eye:hover {
  opacity: 1;
}
/* The lock's margin-left:auto already right-aligns the pair, so cancel the
   eye's own margin-left:auto when it follows a lock (layer rows only). */
.item-tree-item .tree-lock-eye + .tree-vis-eye {
  margin-left: 0;
}
[data-theme="dark"] .item-tree-item .tree-lock-eye {
  filter: invert(1) brightness(1.2);
}

/* Section header group: disclosure triangle + title + (optional) descriptor
   + (optional) add/delete icon buttons. Everything lives on one row, full
   width, vertically centered. The triangle, title, and descriptor sit on
   the left at order 0; icon buttons (add/delete, ring shift, group ops)
   are tagged order 2 so they're pushed to the right; a ::after spacer at
   order 1 with flex: 1 absorbs all the free space in between. */
.details-header {
  display: flex;
  align-items: center;
  width: 100%;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.details-header::after {
  content: '';
  flex: 1 1 auto;
  order: 1;
}

/* Name section: its one field rides the header line, after the label, and takes the rest of
   it — so the header's push-right spacer is not wanted. flex-basis rather than a width:
   `.seq-name`'s width: 100% would otherwise shove the box onto its own line. */
.name-section-header::after { display: none; }
.name-section-header > .name-section-input {
  flex: 1 1 0;
  min-width: 0;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-control-gap);
}

.details-header > [class*="-add-div"],
.details-header > [class*="-delete-div"],
.details-header > .delete-ring-div,
.details-header > .sight-div,
.details-header > .groups-add-remove-div {
  order: 2;
  /* Keep the section's +/- centred on the header line. */
  align-self: center;
}

/* The Selection Colours target button moved from every colour row into the section
   HEADER (one button, acting on the picked row), so it sits with the other header
   icon-buttons instead of stretching to fill a row. */
.details-header > .sight-div {
  flex: 0 0 auto;
}

.palette-options {
  position: relative;
  display: inline-block;
  padding: 3px;
  margin: 0 3px;
  top: -9px;
  opacity: var(--editor-icon-muted-opacity);
}

.palette-options-menu {
  position: absolute;
  transition: 0s visibility;
  z-index: 50;
}

.palette-options-div {
  position: absolute;
  background-color: var(--top-toolbar-background-color);
  color: #fff;
  padding: 8px 12px;
  border-radius: 3px;
  width: 170px;
}

.palette-options-span {
  display: block;
  color: #fff;
  font-size: var(--editor-control-font-size);
  text-align: left;
  border-radius: var(--corner-radius-sm);
  padding: 1px 3px;
}

.inline-block {
  display: inline-block;
}

.palette-options-span:hover {
  color: #fff;
  background-color: var(--editor-selected-blue);
}

.palette-options-menu .palette-options-div::after {
  content: " ";
  position: absolute;
  bottom: 100%;
  right: 26px;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: transparent transparent var(--top-toolbar-background-color) transparent;
}

/* Pen mode toolbar and canvas context menu. Block-level with no side
   margin so the .separator bottom border spans the full panel width,
   matching the other design-panel sections. */
.pen-mode-div {
  display: block;
  /* Standard section padding so the header isn't crowded against the divider
     above it (matches .details-div-group). */
  margin: 0;
  padding: 10px 0;
}

/* Connector-insertion exit button: sits inline to the right of the section
   title (the details-header centers it vertically). */
.connector-create-cancel {
  margin-left: 8px;
}

.pen-mode-heading-div {
  /* The DisclosureTriangle before it already supplies the panel-indent, so the
     heading must NOT add another (it was double-indented vs other sections). */
  margin: 0 0px 0 0px;
  user-select: none;
  display: block;
}

.pen-icons-div {
  /* Flex row so the "Ring" name + prev/next triangles vertically center with
     the sub-mode icon buttons; wraps gracefully on a very narrow panel. */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

/* Pen editing rides its Select / Insert / Curve / Exit buttons on the header line. Like the
   standard +/- header slot, order 2 sits it AFTER the .details-header::after spacer, which
   grows and pushes it to the panel's right edge — with an 8px gutter so it doesn't butt
   against the edge. (margin-left:auto alone does NOT work: the flex-grow ::after eats the
   free space first, so the group would sit left, right after the title.) */
.pen-mode-header-icons {
  order: 2;
  margin-right: 8px;
  display: inline-flex;
  align-items: center;
}

.connector-mode-help {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  color: var(--editor-muted-text-color);
  font-size: var(--editor-control-font-size);
  line-height: 1.45;
}

.pen-icon {
  display: block;   /* drop the inline baseline gap so the wrapper's selection
                       border hugs the icon instead of leaving space below it */
  width: 24px;
  height: 24px;
  padding: 2px;
  opacity: 60%;
}

.pen-icon-disabled {
  opacity: 20%;
  pointer-events: none;
  cursor: default;
}

.wrapper {
  display: inline-block;
  /* A transparent border on every button keeps the row from shifting when the
     active button's blue selection border appears, and makes the selection box
     snug and identical for every sub-mode icon. */
  border: 1px solid transparent;
  border-radius: var(--corner-radius-sm);
  margin-right: 1px;
}

.canvas-context-menu-div {
  position: absolute;
}

.canvas-context-menu-ul {
  position: absolute;
  background-color: var(--top-toolbar-background-color);
  color: white;
  border-radius: 4px;
  width: 200px;
  margin: 10px;
  list-style: none;
  padding: 5px;
  z-index: 100;
  /* No overflow/scroll on the top-level menu: setting overflow-y also coerces
     overflow-x to `auto` (CSS spec), which would CLIP the flyout submenus that
     sit at `left: 100%` — showing a scrollbar'd sliver instead of the submenu.
     The menu itself is a short, bounded set of actions; the long LISTS live in
     the flyouts, which scroll on their own (.canvas-context-menu-submenu). The
     fixed position is already clamped to the viewport edges. */
}

.canvas-context-menu-li {
  background: none;
  height: 20px;
  width: 190px;
  margin: 0;
  padding: 5px;
  cursor: pointer;
  z-index: 150;
  font-size: 12pt;
}

.canvas-context-menu-li:hover {
  background: var(--editor-selected-blue);
}

/* Flyout sub-menu (e.g. Add control ▸) — opens to the right on hover. */
.canvas-context-menu-li.has-submenu {
  position: relative;
}
.canvas-context-menu-submenu {
  position: absolute;
  left: 100%;
  top: 0;
  margin: 0;
  padding: 5px;
  list-style: none;
  background-color: var(--top-toolbar-background-color);
  border-radius: 4px;
  width: 200px;
  display: none;
  z-index: 200;
  /* A long flyout (e.g. Add data with many entities) scrolls instead of
     running off the bottom of the page. */
  max-height: calc(100vh - 12px);
  overflow-y: auto;
}
/* When the parent menu sits on the right half of the screen it carries
   `submenu-left`, so its flyouts open to the LEFT and stay on-page. */
.canvas-context-menu-ul.submenu-left .canvas-context-menu-submenu {
  left: auto;
  right: 100%;
}
/* A row low on the screen carries `open-up` (set on hover in ContextMenu.tsx),
   so its flyout is bottom-aligned to the row and grows upward instead of off the
   bottom of the page. */
.canvas-context-menu-li.has-submenu.open-up > .canvas-context-menu-submenu {
  top: auto;
  bottom: 0;
}
.canvas-context-menu-li.has-submenu:hover > .canvas-context-menu-submenu {
  display: block;
}

.add-gradient-span {
  position: relative;
  top: -10px;
  font-size: var(--editor-control-font-size);
  background-color: var(--editor-muted-text-color);
  color: white;
  border-radius: 3px;
  padding: 0px 3px 1px;
  margin: 0 9px;
}

/* Gradient, shadow, image, export, and tab controls. */
.gradient-colors-heading {
  margin: 0 12px;
}

.gradient-color-div {
  margin: 0;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.color-picker-div {
  position: fixed;
  text-align: left;
  padding: 6px 11px 8px;
  border: 2px solid var(--editor-control-border-color);
  border-radius: 9px;
  background-color: var(--editor-panel-background-color);
  /* 210, not 190. The three readings need 173px at the default control font and had 170
     to put them in. Widening is what buys the row slack instead of spending the last of it. */
  width: 210px;
  z-index: 1000;
  user-select: none;
}

/* Thin drag-handle strip at the top of the floating picker.
   Extends wall-to-wall inside the border by negating the parent's padding.
   The ::before shows a row of dots so the user can see it is draggable. */
.color-picker-header {
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 0 -11px 0 -11px;
  padding: 4px 6px;
  border-radius: 7px 7px 0 0;
  background-color: rgba(128, 128, 128, 0.1);
  border-bottom: 1px solid var(--editor-control-border-color);
  cursor: grab;
  height: 18px;
  box-sizing: content-box;
}

.color-picker-title {
  font-size: var(--editor-control-font-size);
  /* Match the Design/Prototype tab heading colour. */
  color: var(--customize-tab-color-active, var(--editor-text-color));
  user-select: none;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  /* Keep the title clear of the centered drag-handle dots. */
  max-width: calc(50% - 26px);
  padding-right: 6px;
}

.color-picker-header::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 36px;
  height: 8px;
  background-image: radial-gradient(circle, currentColor 1.5px, transparent 1.5px);
  background-size: 6px 4px;
  opacity: 0.3;
  pointer-events: none;
}

.color-picker-header:active {
  cursor: grabbing;
}

/* Red × close button — styled like the Share toolbar button but in panel context. */
.color-picker-close {
  background-color: transparent;
  border: none;
  border-radius: var(--corner-radius-sm);
  padding: 1px 4px;
  font-family: var(--editor-font-family);
  font-size: 0.78rem;
  font-weight: 600;
  color: #e53935;
  cursor: pointer;
  user-select: none;
  flex-shrink: 0;
  -webkit-appearance: none;
  appearance: none;
  opacity: var(--editor-icon-muted-opacity);
}

.color-picker-close:hover {
  background-color: transparent;
  opacity: 1;
}

.color-picker-close:active {
  background-color: rgba(128, 128, 128, 0.32);
  opacity: 1;
}

.gradient-location-div {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

#gradient-start-label,
#gradient-end-label {
  display: inline-block;
  font-size: var(--editor-control-font-size);
  width: 30px;
  margin: 0 0 0 12px;
  user-select: none;
}

.gradient-offset-label {
  display: inline-block;
  font-size: var(--editor-control-font-size);
  width: 14px;
  margin: 0 6px 0 2px;
  user-select: none;
}

.shadow-offset-label {
  display: inline-block;
  font-size: var(--editor-control-font-size);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  user-select: none;
  vertical-align: middle;
  text-align: left;
}

/* The single-cell shadow rows (Blur and Type) get a fixed-width label so
   their inputs line up at the same X. X/Y stay auto-width — they're
   intentionally not aligned with Blur/Type. */
.shadow-offset-row > .shadow-offset-div:only-child > .shadow-offset-label {
  width: 32px;
}
/* "Source" is a full word, so it keeps its natural width and gets the standard
   label→control gap before the Fill / Stroke toggle (not the 32px column the
   short X/Y/Blur labels use, which would crowd it). */
.shadow-type-row > .shadow-offset-label {
  margin-right: var(--panel-checkbox-label-gap);
}

/* X and Y labels (the multi-cell shadow row) get the standard
   --panel-checkbox-label-gap of right margin to their input — a bit wider
   than the default --panel-control-gap so the short labels don't crowd
   their fields. */
.shadow-offset-row > .shadow-offset-div:not(:only-child) > .shadow-offset-label {
  margin-right: var(--panel-checkbox-label-gap);
}

/* Wrapper for one (label + input) pair in a shadow offset row. The wrapper
   is --details-row-height tall and centers its children at their natural
   height — neither the label nor the input is forced to a row height. */
.shadow-offset-div {
  display: inline-flex;
  align-items: center;
  /* No vertical pad here — the enclosing .shadow-offset-row already supplies the
     single inter-line gap. Padding both would double it on the X/Y and Blur rows
     (they'd sit taller than the colour and Type rows). */
  margin: 0;
  /* Each offset cell shares the flex row so its formula input can grow. */
  flex: 1 1 0;
  min-width: 0;
}

.gradient-location-types {
  font-size: var(--editor-control-font-size);
  margin: 0 6px 0 6px;
  border: var(--editor-control-border);
  outline-color: var(--editor-text-color);
  color: var(--editor-text-color);
}

.gradient-offset-input {
  position: relative;
  width: 25px;
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  margin: 0 3px 0 0;
}

.shadow-offset-input {
  position: relative;
  /* Formula-capable (Blur, and the X/Y offsets) — grow into spare row
     width, capped so a lone Blur field doesn't span the whole panel. */
  flex: 1 1 0;
  width: auto;
  min-width: 35px;
  max-width: 150px;
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  margin: 0;
}
/* A stacked paint's Blend row is an ordinary panel row — a .color-details-div, the same
   left-aligned label+control row the colour lines use. Its label takes the SHARED column
   (--panel-label-col), so the select beside it starts on the same edge as the controls in the
   texture / gradient / image rows that follow it in the same section. */
.layer-ctrl-label {
  flex: 0 0 var(--panel-label-col);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  user-select: none;
}
/* BLEND AND ITS OPACITY SHARE ONE ROW, under the single label "Blend": the select takes the
   room left over, the percentage keeps its fixed width at the end. Two rows made the opacity
   read as an independent setting competing with the colour's own alpha.
   The gap is the label→control gap the effect rows use (their grid's column-gap), and the
   RIGHT GUTTER is the one every other flexed row reserves: .color-details-div sets only a
   margin-left, so the opacity box — fixed-width and last in the row — ended flush against the
   panel's content edge with nothing between it and the scrollbar. */
.blend-row {
  gap: var(--panel-checkbox-label-gap);
  margin-right: var(--panel-right-gutter);
}

/* Per-paint blend-mode picker (a fill, a stroke, or a pen ring's paint), filling the row
   past its label. */
.layer-blend-select {
  flex: 1 1 0;
  min-width: 0;
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  color: var(--editor-text-color);
  padding: 1px 2px;
  margin: 0;
}
/* Small fixed-width opacity field — pinned via flex-basis so the global
   `.details-scroll input { width: 100% }` can't blow it out to the panel edge. */
.layer-opacity-input {
  flex: 0 0 64px;
  min-width: 0;
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  margin: 0;
}

.image-div {
  display: inline-block;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* Between-group gap separating the vertical-align group from the horizontal
     one — clearly wider than the within-group spacing, but not a full indent. */
  margin-left: 16px;
}

.export-div {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  line-height: normal;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

/* The shadow X/Y + Blur rows live in this wrapper; it's just the indent — the
   rows inside set their own height, so they stack with normal spacing. */
.shadow-div {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

/* Shadow tree — three levels (Shadows root → item → shadow leaf). Rows use the
   customizable inter-row spacing (--details-row-pad); each level indents by 14px
   (set inline); the selected row gets the selection blue. */
ul.shadow-tree {
  list-style: none;
  /* Indent the whole tree to level 1 (one --panel-indent), like other nested
     panel content; per-row level nesting is added on top via inline paddingLeft. */
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  padding: 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.shadow-tree-row {
  display: flex;
  align-items: center;
  gap: 6px;
  /* 6px on the right to MATCH `.flow-tree-row`: the Fill / Stroke trees end their
     summaries 6px in, and these rows sit directly under them in the same panel — at 4px
     this list's right-hand values stood 2px past the section above. (The left inset is
     per-level, set inline via treeRowPad — the same shared-tree formula.) */
  padding: var(--details-row-pad) 6px;
  cursor: pointer;
}
.shadow-tree-row:hover {
  background: color-mix(in srgb, var(--editor-selected-blue) 10%, transparent);
}
.shadow-tree-row.selected {
  background: color-mix(in srgb, var(--editor-selected-blue) 16%, transparent);
  box-shadow: inset 2px 0 0 var(--editor-selected-blue);
}
.shadow-tree-row.drop-before { box-shadow: inset 0 2px 0 var(--editor-selected-blue); }
.shadow-tree-row.drop-after { box-shadow: inset 0 -2px 0 var(--editor-selected-blue); }
/* Dropping a dragged shadow ONTO a whole row (an item, or the Shadows root) to copy it. */
.shadow-tree-row.drop-onto {
  box-shadow: inset 0 0 0 2px var(--editor-selected-blue);
  background: color-mix(in srgb, var(--editor-selected-blue) 12%, transparent);
}
.shadow-tree-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Per-row colour swatch (its background colour is set inline to the canvas colour
   so a partially transparent shadow composites correctly).
   The `width` is NOT redundant with the flex-basis: in the Fill / Stroke trees the swatch
   sits INSIDE the row's glyph cell (an inline-flex span sized to its content), and Firefox
   computes that intrinsic width from the child's content size — an empty span with only a
   flex-basis measures 0 there, the cell collapsed, and the row's name drew OVER the
   overflowing swatch. Chrome reads the basis, which is why every headless check was green
   while the browser this app is actually used in showed the overlap. */
.shadow-row-swatch {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  border-radius: 3px;
  border: var(--editor-control-border);
}
/* A list row's swatch is also a BUTTON: it opens that row's colour picker straight away,
   the shortcut past picking the row and clicking the swatch in its details (useSwatchPicker).
   The hover ring says so — a swatch is a colour, so it can't brighten a glyph the way an
   icon button does. */
.row-swatch-shortcut {
  cursor: pointer;
}
.row-swatch-shortcut:hover {
  box-shadow: 0 0 0 2px var(--editor-selected-blue);
}
/* An IMAGE fill's swatch: a picture TYPE SYMBOL centred in the swatch box rather than a
   colour. The glyph is an inner <img> so its dark-mode invert doesn't touch the box border. */
.paint-image-swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.paint-image-symbol {
  width: 12px;
  height: 12px;
  object-fit: contain;
}
[data-theme="dark"] .paint-image-symbol {
  filter: invert(1) brightness(1.2);
}
/* A Fill / Stroke entry row with NO paint — a ring that inherits, a fill switched off.
   It takes the swatch's place and its box, but reads as a cancel glyph: an empty
   swatch just looks like a colour that matches the panel behind it. */
/* THE FILL / STROKE ENTRY TREE — the shared tree (see shared/tree/TreeView), in the panel's
   own indentation. It used to be a hand-rolled <ul> that borrowed the Shadow list's rows and
   indented by inline padding; the rows are the app's tree rows now, so the fold triangles, the
   keyboard walking and the ARIA come with them and only the placement is said here. */
/* `ul.` ON PURPOSE, and it is not decoration: `.flow-tree-ul { margin: 0 }` is declared
   further down this file, so a bare `.paint-entry-tree` ties with it on specificity and LOSES
   on order — the list then sat flush against the panel's left edge, a full --panel-indent out
   of line with every heading and row around it. Element+class beats class, which is how
   `ul.shadow-tree` (the list this replaced) always said it. */
ul.paint-entry-tree,
ul.effect-tree,
ul.corner-tree {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  /* THE PANEL'S OWN SELECTION COLOUR. The shared rows are drawn in --code-accent, which is the
     diagram panels' green; in the design panel a picked row has always been the selection
     blue, and a green one reads as some other kind of state. Redefining the accent for this
     tree carries hover, the focus ring and the drop highlight with it — the same one-line move
     `.tree-tree-view` and `.anim-tree` make. */
  --code-accent: var(--editor-selected-blue);
}
/* …and the PANEL's row rhythm, not the list one: --list-row-pad is 4px, --details-row-pad 6px,
   and every other row in these sections is spaced on the latter (see the panel row-spacing
   rules). Two pixels a row is what makes a section look like one thing or two. */
ul.paint-entry-tree .flow-tree-row,
ul.effect-tree .flow-tree-row,
ul.corner-tree .flow-tree-row {
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}
/* The glyph cell holds a SWATCH rather than a character, so it is the swatch's own size. */
.flow-tree-glyph.paint-row-glyph {
  width: auto;
  display: inline-flex;
  align-items: center;
}
/* A corner list's row is named by a PICTURE of its corner (see CornerList) — drawn in the
   text colour, not the tree's muted glyph colour, since it is the row's name — and the
   glyph cell is the picture's own size, as the Fill list's swatch cell is. */
.flow-tree-glyph.corner-row-glyph {
  width: auto;
  display: inline-flex;
  align-items: center;
  color: var(--editor-text-color);
}
.corner-glyph {
  flex: 0 0 auto;
  display: block;
}

.paint-row-none {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  opacity: var(--editor-icon-muted-opacity);
}

/* A Fill / Stroke entry row's value, after the item's name: "#3366ff 80%", "Linear
   gradient, 3 stops", "Image fill". Muted and pushed right, so the column of names
   stays the thing you scan. Truncates before the name does. */
.paint-row-summary {
  margin-left: auto;
  padding-left: 8px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0.7;
}

/* A paint that is completely covered by an opaque one above it in its stack: a warning
   glyph before the summary, in the customizable warning colour (a variable, so it reads in
   both themes). It takes the summary's push-right when present, and the summary follows it. */
.paint-row-hidden {
  margin-left: auto;
  padding-left: 8px;
  flex: 0 0 auto;
  color: var(--warning-color, #b26a00);
  cursor: help;
}
.paint-row-hidden + .paint-row-summary {
  margin-left: 0;
}
/* The same marker serves the Effects list: a background blur covered by the item's own opaque
   fill is the identical situation one row up — the thing is working and none of it shows. One
   class, so the two warnings cannot drift apart in colour, placement or cursor. */

/* Fill / Stroke source glyph — used both in a tree leaf and in the toggle cells. */
.shadow-source-icon {
  flex: 0 0 14px;
  width: 14px;
  height: 14px;
  opacity: 0.9;
}
[data-theme="dark"] .shadow-source-icon {
  filter: invert(1) brightness(1.2);
}

/* Fill / Stroke source — a two-cell segmented toggle of icons (reuses .align-icon-cell). */
.shadow-source-seg {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.shadow-source-cell {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border: var(--editor-control-border);
}

/* The Type-selector row: a standard label + control row (like every other
   panel row) — flex, centred, one row tall. */
.shadow-type-row {
  display: flex;
  align-items: center;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

/* Shadow X/Y row + Blur row: each row is a flex container so its items
   wrap if the panel is too narrow. The rows themselves are block-level,
   so Blur naturally lands on the line below X/Y. All rows align to the
   shadow-div's left content edge (panel-indent). */
.shadow-offset-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: calc(var(--panel-pair-gap) + 12px);
  row-gap: var(--panel-row-gap);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin: 0;
}

.get-image-div {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

.get-image-btn {
  margin: 0 0 0 12px;
  font-size: var(--editor-control-font-size);
}

/* Export action button — a subtle bordered button styled to match the
   share button on the top toolbar. The .export-div parent already
   supplies the panel indent / right gutter. */
.export-button {
  display: block;
  width: 100%;
  margin: 0 0 0 0;
  padding: 6px 12px;
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  line-height: normal;
  text-align: center;
  color: var(--button-text);
  background-color: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  user-select: none;
}

.export-button:hover,
.export-button:active {
  background-color: var(--button-bg-hover);
}

.export-button:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Export rows: name on its own row; format + size share a row. Proximity —
   each label hugs its own control with the tight --panel-checkbox-label-gap
   (like every other panel section), and the wider --panel-pair-gap is reserved
   for separating the Format | Size pair (see .export-field + .export-field). The
   same tight gap here means the Name input (row 1) and the Format select (row 2)
   still start at the same x. */
.export-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--panel-checkbox-label-gap);
  row-gap: var(--panel-row-gap);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}

/* A label + select pair that sits inline with its sibling pair. */
.export-field {
  display: inline-flex;
  align-items: center;
  column-gap: var(--panel-checkbox-label-gap);
}

/* The second pair on the Format | Size row sits a full pair-gap from the first,
   so the two groups read as distinct (the row's own gap is the tight label gap). */
.export-field + .export-field {
  margin-left: calc(var(--panel-pair-gap) - var(--panel-checkbox-label-gap));
}

.export-row select,
.export-field select {
  vertical-align: middle;
}

/* Natural-width labels that hug their own control (the tight label→control gap
   comes from the row / field column-gap). No fixed label column — aligning the
   Name and Format controls at a shared x would force a gap after the shorter
   labels, and proximity to the control matters more than that column. */
/* Name (row 1) and Format (row 2, first column) are aligned DOWN their column —
   the one case that earns a fixed-width label. 45px hugs the widest of the two,
   "Format" (~44px at Arial 14px), so the only separation left is the standard
   8px label→control gap and the Name input lines up under the Format select. */
.export-label {
  display: inline-block;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 45px;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
}
/* Size is the SECOND pair on the Format | Size row — it aligns with nothing
   below it, so its label stays natural width and hugs its own control. */
.export-field + .export-field .export-label {
  width: auto;
}

/* The name field stretches to fill the rest of its row, and is styled to
   match the other edit fields in the design panel. */
.export-name-input {
  /* flex-basis 0 (not auto) so it grows into the leftover row width instead of
     inheriting the global `.details-scroll input { width: 100% }` as its basis —
     that 100% basis wouldn't fit beside the label and wrapped the input to the
     next line. min-width 0 lets it shrink rather than wrap on a narrow panel. */
  flex: 1 1 0;
  min-width: 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background-color: var(--editor-input-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* SVG-only "Rasterize text" toggle row. Inherits the export-div indent;
   the label wraps the checkbox + text with a small gap between them. */
.export-check-label {
  display: inline-flex;
  align-items: center;
  column-gap: 6px;
  font-size: var(--editor-control-font-size);
  cursor: pointer;
  user-select: none;
}

.export-check-label input {
  margin: 0;
  vertical-align: middle;
}

/* Resulting pixel dimensions, shown below the size selector. */
.export-dims {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  line-height: normal;
  font-size: var(--editor-control-font-size);
  opacity: 0.7;
}

/* Multi-selection export: one file-name field per selected item, stacked. The
   other parameters (format, size, rasterize) stay single below the list. */
.export-names {
  display: flex;
  flex-direction: column;
}
.export-names-head {
  padding-top: var(--details-row-pad);
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}

/* Design / Prototype tab strip. Styled to match the Customize page tabs
   exactly: a flat strip with an underline-style active tab. Each tab is
   only as wide as its label. */
.tabs {
  display: flex;
  gap: 2px;
  padding: 0 1.2rem;
  background-color: var(--customize-tab-strip-bg, var(--editor-panel-separator-color));
  border-bottom: 1px solid var(--panel-border-color);
  flex: 0 0 auto;
}

/* Editor tabs (Design/Prototype + the left panel's Layers/Assets/Project) are
   sized ~20% smaller than the default so a third tab fits without crowding. */
.tabs h3 {
  padding: 0.48rem 1rem;
  margin: 0;
  border: none;
  background: var(--customize-tab-bg-inactive, transparent);
  /* The Design / Prototype tabs are the panel's top-level headers. Unselected
     tabs sit muted (opacity, so it composes with the per-tab accent colours);
     the active tab and a hovered unselected one go full. */
  color: var(--header-1-color);
  font-size: 0.76rem;
  font-weight: 600;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  user-select: none;
  opacity: 0.62;
  transition: opacity 0.1s ease;
}

/* Unselected, non-disabled tab: brighten the label (foreground) on hover — same
   foreground-highlight rule as the icon-buttons, no background tint. */
.tabs h3:not(.active):not(.disabled):hover {
  opacity: 1;
}

.tabs .active,
.tabs .active:hover {
  color: var(--header-1-color);
  opacity: 1;
  border-bottom-color: var(--editor-selected-blue);
  background-color: var(--customize-tab-bg-active, var(--editor-input-bg, var(--editor-panel-background-color)));
}

/* Diagrams tab sub-tabs — one per diagram type (Flows / Sequences / Use cases /
   State machines). A compact, wrapping second-level strip below the main tab row;
   the active tab underlines in the code-accent green (the Diagrams tab's colour). */
.diagram-subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 4px;
  padding: 4px 6px 0;
  border-bottom: 1px solid var(--panel-border-color);
  flex: 0 0 auto;
}
.diagram-subtab {
  padding: 0.28rem 0.5rem;
  margin: 0;
  border: none;
  background: transparent;
  color: var(--code-accent);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  user-select: none;
  opacity: 0.6;
  transition: opacity 0.1s ease;
}
.diagram-subtab:not(.active):hover { opacity: 1; }
.diagram-subtab.active {
  opacity: 1;
  border-bottom-color: var(--code-accent);
}

/* The left panel's Layers / Assets / Project tab group is centred within the
   panel; the tabs themselves stay left-to-right (left-aligned) within it. */
/* The left panel's tabs (Structure / Assets / Project, etc.) fill the panel width so
   the tab strip's right edge lines up with the list below it — instead of a centred
   group that stopped short of the panel's right edge. The 6px side padding matches the
   list rows' inset so the edges line up. */
.left-panel-tabs {
  justify-content: stretch;
  padding: 0 6px;
}
.left-panel-tabs h3 {
  flex: 1 1 0;
  min-width: 0;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  text-align: center;
  /* Keep each label inside its own equal-width cell: a long single-word label
     (Variables / Diagrams) must not overflow and overlap a short neighbour like
     Files — clip with an ellipsis instead, staying centred. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Prototype interaction controls. */
.activation-types {
  display: inline-block;
  font-size: 13px;
  margin: 0 0px 0 12px;
  user-select: none;
}

.action-types {
  display: inline-block;
  font-size: 13px;
  margin: 0 6px 0 6px;
  user-select: none;
}

/* Plain-text stand-in for the action dropdown on a Connection row.
   Connections only run Navigate, so the dropdown becomes a label that
   sits in the same row position as the trigger row's select. */
.action-label {
  display: inline-block;
  font-size: 13px;
  margin: 0 6px 0 6px;
  user-select: none;
}

/* Connections / Triggers section content: indented to Level 1, under
   the section heading. */
.connections-content,
.triggers-content {
  margin-left: var(--panel-indent);
}

/* Triggers are grouped by item; leave a gap before each new item group so
   the groups don't run together (one full inter-row gap, a touch more than
   the gap between rows inside a group). */
.trigger-group:not(:first-child) {
  margin-top: var(--details-row-gap);
}

.interaction-start-end-heading {
  display: flex;
  align-items: center;
  margin: 0 0px 0 0px;
  user-select: none;
  /* A connection/trigger GROUP label (e.g. "Frame 1 → Frame 6"), not a section heading — it's an
     <h4>, so without an explicit size it falls back to the browser's big/bold default and reads
     as a major heading. Only .trigger-group styled it before, leaving the Connections list big. */
  font-size: var(--editor-control-font-size);
  font-weight: 600;
  color: var(--header-3-color);
  /* THE STANDARD ROW RHYTHM, above and below — the same half-gap every stacked element in these
     panels carries, so the label sits one customizable inter-row gap clear of the section
     heading above it and of its own first behaviour row below. Only the Triggers copy had it,
     which left a connection's "Rectangle 2 → Pentagon 1" jammed against its neighbours while
     the identical label under Triggers breathed. Padding rather than margin, like the rest of
     the rhythm: two adjacent rows then contribute a half-gap each and add up to one. */
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}

/* Trigger item-name heading: same structural role as the connection start→end heading, and
   nothing left to say about it — the size, the weight, the colour and the standard row padding
   are all on the shared rule now. This block used to restate every one of them, which is how
   the Connections list came to be the only place without the padding: the value lived in the
   copy rather than in the thing both copies are. */

/* In a trigger row, the activation-types select sits flush with the
   row's left edge — no need for the connection rows' 12px indent to
   line up under a separate start→end heading. */
.trigger-group .activation-types {
  margin-left: 0;
}

/* Heading text + arrow as one flex item; takes the available width so
   the add-behavior control is pushed to the right edge of the panel. */
.interaction-title {
  flex: 1;
  min-width: 0;
  display: inline-flex;
  align-items: center;
}

/* Connector arrow between the start and end item names. */
.interaction-arrow {
  flex: none;
  width: 22px;
  height: 8px;
  margin: 0 6px;
  fill: none;
  stroke: var(--editor-muted-text-color, #8e8c9e);
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Add-behavior (+) and delete-connection (-) controls: grouped at the
   right edge of the connection heading, vertically centered. */
.connection-controls {
  display: inline-flex;
  align-items: center;
  gap: var(--panel-control-gap);
  margin-left: auto;
  margin-right: var(--panel-control-gap);
}

.connection-controls .routing-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: var(--header-2-color);
  cursor: pointer;
  white-space: nowrap;
}

.connection-controls .routing-toggle input {
  margin: 0;
  cursor: pointer;
}

/* Section-wide routing toggle at the end of the Connections list —
   flips every selected connection between orthogonal and straight. */
.connections-routing-all {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--header-2-color);
  cursor: pointer;
  white-space: nowrap;
  margin-top: 0;
  padding-top: 8px;
  border-top: 1px solid var(--panel-border-color, rgba(128, 128, 128, 0.2));
}

.connections-routing-all input {
  margin: 0;
  cursor: pointer;
}

/* Behavior row: activation + action selects, with the delete (-)
   control pushed to the right edge and vertically centered. */
.interaction-behavior-row {
  display: flex;
  align-items: center;
  /* Standard gap between the row's controls (activation select, action
     select/label, delete) so a trigger's two adjacent selects aren't flush. */
  column-gap: var(--panel-control-gap);
  /* Same inter-row gap the other prototype rows use, so the activation/action row
     lines up with the property rows below it. */
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}
/* The activation / action selects must shrink (not overflow) in a narrow panel. */
.interaction-behavior-row > select {
  min-width: 0;
  max-width: 100%;
}

.interaction-behavior-row .delete-behavior-div {
  margin-left: auto;
  flex: 0 0 auto;
}

.miter-limit-text-input {
  /* flex-basis pins it — `.details-scroll input { width: 100% }` was stretching
     this number box across the whole row, which is what made the Miter Limit
     line too wide. A number keeps its own size; only the drop-downs fill. */
  flex: 0 0 64px;
  width: 64px;
  margin: 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
}

/* ONE label column for Stroke Join / Miter Limit / Align, so the three rows read
   as a grid rather than three controls starting wherever their own label ended.
   Fixed width because these labels ALIGN with each other (the only reason a panel
   label is ever fixed); nowrap because "Miter Limit" wrapped to two lines inside
   the old 74px and pushed its row taller than its neighbours. */
.stroke-options-label {
  flex: 0 0 84px;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
  margin: 0 var(--panel-checkbox-label-gap) 0 0;
  text-align: left;
}

/* The drop-downs take what the label column leaves and stop at the group's right
   gutter. They keep the shared `width: 100%` as their flex basis and simply
   shrink into place — min-width:0 is what lets a flex item shrink below the
   intrinsic width of its longest option. */
#stroke-join,
.stroke-align-select {
  flex: 1 1 auto;
  min-width: 0;
}

#stroke-join {
  margin: 0;
  vertical-align: middle;
}

p.file-name-p {
  display: block;
  line-height: normal;
  margin: 0 0 0 6px;
  text-align: left;
  word-wrap: normal;
  width: 140px;
}

/* Detailed color editor and constraint controls. */
/* Row that holds R/G/B inputs on the left and the palette area on the right. */
/* Stack the picker's lower sections vertically: RGB row, then the palette,
   then the colour tokens. Tight gap so the RGB sits close to the palette. */
.color-picker-bottom-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  margin-top: 0;
}

.color-picker-palette-area {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* RGB across, SHARING THE ROW rather than adding up to it.
   This row has been widened twice by arithmetic — "the readings need 173px, so the picker is
   210" — and it collided again anyway: three boxes at 4.6ch plus their labels plus two 10px
   gaps come to 192px in the 188px the picker actually leaves, so the last box sat past the
   border and each label was pressed flat against the box before it ("0G", "0B"). A sum that
   has to be re-derived every time a font, a label or a gap changes is a sum that will be
   wrong again.
   So the three readings DIVIDE the row instead: each takes an equal share of whatever is
   there, and the box inside it takes what the label leaves. Nothing to re-tune — it fits at
   any control font, in any reading, and the numbers stay right-aligned and legible. */
.color-parts-div {
  display: flex;
  flex-direction: row;
  justify-content: center;
  /* A PIXEL MORE THAN THE ROW'S OWN GAP. This gap falls between one reading's box and the
     next one's letter — "255" then "G" — and at 6px they read as one run of characters.
     One pixel is enough to part them, and is why this is 7 where the gap inside a reading
     (letter to box) is 4. */
  gap: 7px;
  width: 100%;
  /* AND IF THEY STILL DON'T FIT, THE ROW BREAKS — between readings, never inside one.
     Dividing the row equally (below) fixed the arithmetic at the DEFAULT control font and
     left it to be re-derived at every other one: the font size is a Customize setting, and
     at 17px each box hits its own 3.4ch minimum, spills out of its share, and lands ON TOP
     of the next reading's letter — measured at −6.3px of gap, which is "100H" with the H
     underneath the 0. A reading is a letter and its box and means nothing split up, so when
     the four of them want more width than the picker has, one moves to a second line. Same
     rule the segmented rows already follow (see .text-type-seg), and nothing to re-tune. */
  flex-wrap: wrap;
  row-gap: 4px;
}

/* WHICH READING the three boxes show — RGB / HSB / HSL. The app's pick-one control,
   sized to the picker's content column. */
/* WHICH READING the three boxes show — HEX / RGB / HSB / HSL. The app's pick-one control,
   on the picker's own content column: it used to be inset 10px on each side while the boxes
   below it were not, so the two rows that belong together started at different places. */
.color-space-choice {
  justify-content: center;
  margin: 0 0 4px;
}

/* Colour-tokens section at the bottom of the picker. The swatch grid wraps to
   only as many rows as needed. The margin-top adds room between the palette
   (colours) line above and this section's divider. */
.color-picker-tokens {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 0;
  border-top: 1px solid var(--editor-panel-separator-color, var(--editor-control-border-color));
  padding-top: 6px;
}
.color-picker-tokens-label {
  font-size: var(--editor-control-font-size);
  opacity: 0.7;
}
.color-picker-token-link {
  display: flex;
  align-items: center;
  gap: 6px;
}
/* One reading: its letter, then its box. An equal share of the row (see .color-parts-div) —
   but never LESS than the letter and the box's own minimum, which is what turns "too many
   readings for this width" from an overlap into a line break. `min-width: 0` was the
   opposite instruction: shrink past your contents, and let them hang out of you. */
.color-part-div {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1 1 auto;
  min-width: min-content;
  font-size: var(--editor-control-font-size);
}

/* SIZED TO ITS OWN LETTER, not to 12px.
   A fixed width here was a bet that the label's glyph would stay narrower than the number
   it stands beside — and "G" at the default size draws 10.11px into a 12px box, so the bet
   was nearly lost before anything changed. Labels get a fixed width in this app only to
   align them down a column; these three sit on one line and have nothing to align with. */
.color-part-label {
  flex-shrink: 0;
  text-align: right;
  font-size: var(--editor-control-font-size);
}

.color-part-percentage-label {
  flex-shrink: 0;
  font-size: var(--editor-control-font-size);
}

/* WHAT ITS LABEL LEAVES of its share of the row (see .color-part-div).
   It used to be a fixed 4.6ch — "three digits plus a real margin" — which is the right width
   for the number and the wrong way to get it: three fixed boxes plus labels plus gaps have to
   add up to less than the picker, and they did not. A minimum keeps it honest at any width;
   below that the row would rather be scrolled than lie about the number. */
.color-part-input {
  flex: 1 1 0;
  min-width: 3.4ch;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
}

/* A DEFINITE WIDTH, so the reading it sits in can say how narrow it is willing to be.
   `.color-part-div` is `min-width: min-content`, which is what turns "too many readings for
   this picker" into a line break instead of one box lying on top of the next letter — but
   min-content asks this box its smallest honest size, and an `<input>` told `width: 100%`
   by the panel's own rule answers with its DEFAULT SIZE, about twenty characters. Every
   reading then claimed 150px and the row broke onto a line each.

   THREE CLASSES because that is what it takes: the panel rule is
   `.details-scroll input:not([type])`, and a class-and-a-half loses to it — which is the
   same specificity fight the hex and opacity boxes below already had, and lost the first
   time. The flex basis still decides the real size; this only settles the minimum. */
.color-picker-bottom-row .color-part-div .color-part-input {
  width: 3.4ch;
}

/* THE SWATCH THAT REPLACED `<input type="color">`.
   Sized like the native input it stands in for, so the rows it sits in keep their
   proportions — but themed, and it opens our own picker rather than the OS's. */
.color-swatch-field {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
}
.color-swatch-field-btn {
  width: 34px;
  height: 22px;
  padding: 0;
  cursor: pointer;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  /* The colour is painted through `background` inline; the checker beneath shows through a
     translucent one, which is the whole reason a swatch beats a solid rectangle. */
  background-image:
    linear-gradient(45deg, rgba(128, 128, 128, 0.35) 25%, transparent 25%),
    linear-gradient(-45deg, rgba(128, 128, 128, 0.35) 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, rgba(128, 128, 128, 0.35) 75%),
    linear-gradient(-45deg, transparent 75%, rgba(128, 128, 128, 0.35) 75%);
  background-size: 8px 8px;
  background-position: 0 0, 0 4px, 4px -4px, -4px 0;
}
.color-swatch-field-btn:disabled {
  cursor: default;
  opacity: 0.5;
}
/* Enabled controls nudge while held — the app's global press rule. */
.color-swatch-field-btn:not(:disabled):active {
  transform: translateY(1px);
}

/* The picker itself, floating under the swatch. Same padding and border as the design
   panel's floating picker so the two read as one control in two places. */
.color-swatch-field-pop {
  z-index: 1000;
  width: 210px;
  padding: 8px 11px;
  border: 2px solid var(--editor-control-border-color);
  border-radius: 9px;
  background-color: var(--editor-panel-background-color);
  user-select: none;
}

.color-picker-div .add-palette-color-icon {
  height: 12px;
}

/* Palette swatches are a uniform 14px swatch size across the picker. */
.palette-color-div {
  height: 14px;
  width: 14px;
  flex-shrink: 0;
  border: 2px solid var(--editor-control-border-color);
  border-radius: 2px;
}

.palette-colors-div {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  position: relative;
  margin: 0;
  width: 100%;
}

.missing-triangle-div {
  display: inline-block;
  height: 10px;
  width: 8px;
}

.constraints-div {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Layout / Override chooser shown when the selected item is inside an auto-layout
   frame — sits above the constraint grid, spanning the panel width. */
.constraints-layout-note {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}
.constraints-mode-radio {
  display: flex;
  align-items: center;
  gap: 16px;
}
/* Was scoped past the panel-wide `.details-div p` rule, which used to force every
   panel <p> to a 14px inline-block glyph label and collapsed this caption to one word
   per line. That rule is now scoped to `p.dim-descr`, so the display/width below just
   restore the defaults — kept because they say what this caption needs to be. */
.constraints-layout-note .constraints-note-text {
  display: block;
  width: auto;
  margin: 0 0 0 0;
  color: var(--editor-muted-text-color);
  font-size: var(--editor-control-font-size);
  line-height: 1.35;
  text-align: left;
}
/* Per-child auto-layout sizing (Fill/Stretch + Fill limits), under the Layout/Override
   radio. Wide gap BETWEEN the two field groups; tight gap between each label + control. */
.layout-child-sizing-row {
  display: flex;
  align-items: center;
  gap: 22px;
  margin-top: 0;
}
.layout-child-field {
  display: flex;
  align-items: center;
  gap: 5px;
}
.layout-child-field label {
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  white-space: nowrap;
}
.layout-child-sizing-row .layout-input {
  width: 48px;
}

/* 3×3 grid: corner cells empty, edge cells hold the constraint handles,
   center cell holds the inner bordered box. */
.constraints-outer-box {
  flex-shrink: 0;
  box-sizing: border-box;
  width: 60px;
  height: 57px;
  border: 2px solid var(--editor-control-border-color);
  display: grid;
  grid-template-columns: 1fr 32px 1fr;
  grid-template-rows: 1fr 31px 1fr;
}

.constraints-top-box {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: 1px;
  cursor: pointer;
}

.constraints-top {
  width: 3px;
  height: 9px;
  background-color: var(--editor-constraint-line-color);
}

.constraints-bottom-box {
  grid-column: 2;
  grid-row: 3;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  padding-bottom: 1px;
  cursor: pointer;
}

.constraints-bottom {
  width: 3px;
  height: 9px;
  background-color: var(--editor-constraint-line-color);
}

.constraints-left-box {
  grid-column: 1;
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding-left: 1px;
  cursor: pointer;
}

.constraints-left {
  height: 3px;
  width: 9px;
  background-color: var(--editor-constraint-line-color);
}

.constraints-right-box {
  grid-column: 3;
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 1px;
  cursor: pointer;
}

.constraints-right {
  height: 3px;
  width: 9px;
  background-color: var(--editor-constraint-line-color);
}

.constraints-inner-box {
  grid-column: 2;
  grid-row: 2;
  box-sizing: border-box;
  border: 2px solid var(--editor-control-border-color);
  position: relative;
}

/* Vertical and horizontal center handles overlap inside the inner box.
   Each is absolutely positioned to occupy its own axis-centered strip,
   with a shared 3px bar in the middle rendered by the inner element. */
.vertical-center-box {
  position: absolute;
  inset: 0 35% 0 35%;
  display: flex;
  justify-content: center;
  cursor: pointer;
}

.vertical-center {
  width: 3px;
  background-color: var(--editor-constraint-line-color);
}

.horizontal-center-box {
  position: absolute;
  inset: 35% 0 35% 0;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.horizontal-center {
  width: 100%;
  height: 3px;
  background-color: var(--editor-constraint-line-color);
}

/* Label + select pairs laid out as a two-column grid. */
.constraints-details-div {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 4px 6px;
}

.constraints-label {
  font-family: var(--editor-font-family);
  color: var(--editor-muted-text-color);
  font-size: var(--editor-control-font-size);
}

#horizontal-constraint,
#vertical-constraint {
  color: var(--editor-text-color);
  width: 106px;
}

/* Eyedropper and color-picker mode controls. */
.eyedropper {
  height: 20px;
  display: block;
}

.color-picker-mode-cancel-icon {
  position: relative;
  top: -2px;
  height: 25px;
}

.color-picker-eyedropper-btn {
  background: none;
  border: none;
  cursor: pointer;
  border-radius: var(--corner-radius-sm);
  padding: 3px 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: var(--editor-icon-muted-opacity);
  margin: 0 0 3px;
}

.color-picker-eyedropper-btn:hover {
  background-color: transparent;
  opacity: 1;
}

.color-picker-eyedropper-active {
  background-color: var(--tool-active-bg, #e3e8f2);
  outline: var(--selection-border-width) solid var(--editor-selected-blue);
  outline-offset: calc(-1 * var(--selection-border-width));
  opacity: 1;
}

/* Interaction targets, modifiers, variables, and conditional fields.
   Labels in this group baseline-align with their adjacent text input via
   `vertical-align: middle` (the inputs already use it from the shared
   text-input rule), so the label text sits centered to the input box. */
.drop-target-label,
.scroll-target-label,
.delay-label,
.set-to-label {
  vertical-align: middle;
  font-size: var(--editor-control-font-size);
  margin: 0 6px 0 12px;
}

.drop-targets,
.scroll-targets {
  font-size: var(--editor-control-font-size);
}

/* NO VERTICAL NUDGES IN THIS BLOCK. `.additional-ib-properties` is a flex row with
   `align-items: center`, so every item on it is already centred on the row's tallest control —
   and the 1-2px `position: relative; top:` offsets these labels, checkboxes and the Delay box
   carried (from when the row was a line of inline-blocks aligned on a text baseline) now push
   them OFF that centre, each by a different amount. The Delay line was the tell: label, box and
   checkbox each sat at a slightly different height. */
.snap-back-label,
.select-target-label,
.show-toolbar-label,
.allow-zoom-label,
.allow-scroll-label {
  font-size: var(--editor-control-font-size);
}

/* A [checkbox][label] pair on one of this section's rows — Dim behind, Click outside closes,
   Disabled.
   ONE CLASS for all three, because they had none between them: the overlay pair's class carried
   no rule at all and Disabled borrowed `.shape-text-check` from the Shape Text section, so all
   three missed the control font size every other label on these rows has and drew a size larger
   than the row they sit on. Borrowing another section's class is what made that invisible.
   No left margin and no vertical nudge: `.additional-ib-properties` is a flex row with
   `align-items: center` that already carries the level-1 indent and the inter-row gap, so the
   pair sits on the block's own left edge, centred — see the NO VERTICAL NUDGES note above. */
.proto-check-label {
  display: inline-flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
}
/* THE BOX STARTS ON THE ROW'S LEFT EDGE, like every other checkbox in this section.
   The loose pairs on these rows (Snap Back, Select, Shift…) set their own `margin: 0 gap 0 12px`
   and `.additional-ib-properties > :first-child` zeroes that 12px for the first one in a row,
   so their box sits hard on the block's indent. Wrapping the input INSIDE the label puts it out
   of that rule's reach — it is a child of the label, not of the row — so it kept the browser's
   default checkbox margin (~4px left, 3px right) and drew both the box and its words a few
   pixels right of every row above it. The flex `gap` is the only space before the words. */
.proto-check-label > input[type="checkbox"] {
  margin: 0;
}

.snap-back-checkbox,
.select-target-checkbox,
.show-toolbar-checkbox,
.allow-zoom-checkbox,
.allow-scroll-checkbox {
  margin: 0 var(--panel-checkbox-label-gap) 0 12px;
}

.hovering-select-checkbox,
.ctrl-checkbox {
  margin: 0 var(--panel-checkbox-label-gap) 0 12px;
}

.ctrl-checkbox,
.shift-checkbox,
.alt-checkbox,
.caps-lock-checkbox,
.num-lock-checkbox {
  margin: 0 var(--panel-checkbox-label-gap) 0 12px;
}

/* When a checkbox starts a new group on the same row, widen its left margin
   so adjacent [checkbox][label] pairs read as visually separated groups
   rather than a continuous strip. The first checkbox in each row keeps its
   smaller leading indent (above). */
.ctrl-checkbox,
.alt-checkbox,
.num-lock-checkbox,
.select-target-checkbox {
  margin-left: var(--panel-pair-gap);
}

.additional-ib-properties,
.additional-var-properties {
  /* Indent the rows to level 1, under the section heading, and space them on
     the shared inter-row gap (was a fixed 3px bottom margin). A flex row so a
     wide text field fills the remaining width and shrinks instead of running off
     the panel; wrap keeps multi-checkbox rows tidy when the panel is narrow. */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 2px;
  margin: 0 var(--panel-right-gutter, 12px) 0 var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
/* ONE LEFT EDGE FOR THE BLOCK. The row class above already puts these at level 2, and
   whatever STARTS a row begins on that edge — so the leading indents written into the
   individual labels and checkboxes (`margin-left: 12px`, over and over) are cancelled here,
   in one place, for whichever element happens to come first. They are kept on the elements
   that FOLLOW another on the same row, where the same 12px is a gap between two pairs rather
   than an indent. Two left edges in a block is already suspect: it is what left the Set Style
   rows, which follow the indent formula, 12px out of line with Delay, Link and the rest. */
.additional-ib-properties > :first-child {
  margin-left: 0;
}

/* The single wide field in a property row (a URL, an expression, a target select)
   fills the space left of the label and shrinks with the panel — no overflow. */
.additional-ib-properties > .link-input,
.additional-ib-properties > .set-to-text-input,
.additional-ib-properties > .if-condition,
.additional-ib-properties > .then-statement,
.additional-ib-properties > .else-statement,
.additional-ib-properties > .drop-targets,
.additional-ib-properties > .scroll-targets {
  flex: 1 1 90px;
  min-width: 0;
  width: auto;
  box-sizing: border-box;
}

/* Shared visual styling for editable text inputs in the prototype panel —
   matches the design-panel text inputs (color hex, stroke width, etc.).
   Width and margin remain per-instance since they're content-specific. */
.delay-input,
.key-down-input,
.playback-zoom-input,
.user-variable-name-input,
.user-variable-initial-value-input,
.link-input,
.set-to-text-input,
.if-condition,
.then-statement,
.else-statement {
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  vertical-align: middle;
  font-size: var(--editor-control-font-size);
}

.delay-input {
  width: 40px;
  /* flex-basis pins the width against the `.details-scroll input { width:
     100% }` override in the flex .additional-ib-properties row. */
  flex: 0 0 40px;
}

.star-depth-slider {
  display: inline-block;
  position: relative;
  margin: 0 var(--panel-control-gap) 0 0;
  width: 100px;
  vertical-align: middle;
}

.hovering-select-label {
  font-size: var(--editor-control-font-size);
}

/* Match the variable-name label column (fixed width, inline-block) so the
   key input lines up under the variable inputs above it. */
.key-down-label {
  display: inline-block;
  margin: 0 3px 0 12px;
  width: 42px;
  font-size: var(--editor-control-font-size);
}

/* The Playback section's two label + control rows ("Device frame", "Zoom %").
   Both share one label column so their controls line up with each other — the
   only reason to pin a label width; elsewhere a label hugs its own text. Wider
   than the standard 42px column because "Device frame" doesn't fit it, and
   no-wrap so the control isn't pushed onto a second line. */
.playback-zoom-label,
.device-frame-label {
  display: inline-block;
  margin: 0 3px 0 12px;
  width: 84px;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
}

/* The device-shell picker. flex-basis pins the width against the
   `.details-scroll select { width: 100% }` override, which would otherwise
   stretch it past the panel edge. */
.device-frame-select {
  flex: 0 0 110px;
  width: 110px;
  box-sizing: border-box;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}

.key-down-input,
.playback-zoom-input {
  margin: 0 3px;
  width: 70px;
  /* flex-basis pins the width against the `.details-scroll input { width:
     100% }` override in the flex .additional-ib-properties row. */
  flex: 0 0 70px;
  box-sizing: border-box;
}

.ctrl-label,
.shift-label,
.alt-label,
.caps-lock-label,
.num-lock-label {
  font-size: var(--editor-control-font-size);
}

/* Frame-transition sub-group (navigate / back behaviors). Selects are fluid so
   the `.details-scroll select { width: 100% }` override can't push them off the
   panel; the duration box pins a small flex-basis (flex-basis beats width). */
.transition-label {
  margin: 0 3px 0 12px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.transition-type-select,
.transition-dir-select,
.transition-easing-select {
  flex: 1 1 90px;
  min-width: 0;
  max-width: 100%;
}
/* Wide enough for "1000ms" — the unit lives in the box now, so the trailing "ms" label this
   block used to carry is gone (a bare number with its unit off to the side is not how any
   measured field in this panel reads). */
.transition-duration-input {
  flex: 0 0 72px;
  width: 72px;
  box-sizing: border-box;
}

/* Animate action (property animation) controls. Numeric boxes pin a small
   flex-basis so the width override can't stretch them; the easing select is
   fluid. */
.animate-check {
  flex: 0 0 auto;
  margin: 0 2px 0 12px;
}
/* Track label after a checkbox (Fade to / Move to / …): standard gap to its
   control on the right; small gap after the checkbox on the left. */
.animate-label {
  margin: 0 var(--panel-control-gap) 0 4px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
/* A leading label with no checkbox (Duration / Easing) lines up with the
   checkbox column above it rather than the row's left edge. */
.animate-lead-label {
  margin-left: 12px;
}
.animate-axis-label {
  margin: 0 2px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
.animate-num-input {
  flex: 0 0 48px;
  width: 48px;
  box-sizing: border-box;
  text-align: var(--number-input-align, right);
}
.animate-easing-select {
  flex: 1 1 90px;
  min-width: 0;
  max-width: 100%;
}

/* Move-to coordinates: x and y each on their own line, indented one level
   under the "Move to" checkbox. */
.animate-subrow {
  margin-left: calc(var(--panel-indent) * 2);
}
.animate-sub-label {
  margin-right: var(--panel-control-gap);
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
/* Coordinate box holds a value WITH its unit as part of the control
   (e.g. "120 mm"), so it's wider; honors the number-alignment preference. */
.animate-move-input {
  flex: 0 0 96px;
  width: 96px;
  box-sizing: border-box;
  text-align: var(--number-input-align, right);
}

/* Preview progress: a temporary status bar shown only while a preview plays. */
.animate-preview-status {
  align-items: center;
}
.animate-preview-track {
  flex: 1 1 auto;
  min-width: 0;
  height: 6px;
  margin-left: 12px;
  background: var(--editor-input-bg);
  border: 1px solid var(--editor-control-border-color);
  border-radius: 3px;
  overflow: hidden;
}
.animate-preview-fill {
  height: 100%;
  background: var(--prototype-accent);
  transition: width 0.05s linear;
}
.animate-preview-status-label {
  flex: 0 0 auto;
  margin: 0 12px 0 8px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}

/* Preview button: plays the animation once in the editor. Sits at the row's
   left edge under the animate tracks. */
.animate-preview-btn {
  flex: 0 0 auto;
  margin-left: 12px;
  padding: 2px 10px;
  font-size: var(--editor-control-font-size);
  color: var(--prototype-accent);
  background: transparent;
  border: 1px solid var(--prototype-accent);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.animate-preview-btn:hover {
  background: color-mix(in srgb, var(--prototype-accent) 12%, transparent);
}

.link-label {
  margin: 0 var(--panel-control-gap) 0 12px;
  font-size: var(--editor-control-font-size);
}

.link-input {
  position: relative;
  margin: 0 3px;
  width: 180px;
}

.user-variable-list,
.user-variable-type-list {
  max-width: 160px;
  margin: 0 3px;
  font-size: var(--editor-control-font-size);
}

.user-variable-name-label,
.user-variable-list-label,
.user-variable-type-label,
.user-variable-initial-value-label {
  display: inline-block;
  margin: 0 3px 0 12px;
  width: 42px;
  font-size: var(--editor-control-font-size);
}

.user-variable-name-input {
  margin: 0 3px;
  width: 140px;
}

.user-variable-initial-value-input {
  margin: 0 3px;
  width: 140px;
}

/* ── Variables section (prototype panel) ─────────────────────────────────
   Row-per-variable layout. The header has a "+" button to add; each row
   carries inputs for name, type, initial value, and a delete button. */
.variables-empty {
  margin: 0 0 0 var(--panel-indent);
  margin-right: 12px;
  font-size: var(--editor-control-font-size);
  line-height: 1.35;
  opacity: 0.7;
}

.variables-table {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0 12px 0 var(--panel-indent);
}

.variables-row {
  display: grid;
  /* Fixed delete column (icon box = 18px + 1px padding) so the header row's
     empty delete cell and the data rows' minus button share the same width —
     otherwise the `auto` column is 0 in the header and the columns don't line
     up with the inputs below. */
  grid-template-columns: 1fr 84px 1fr 20px;
  column-gap: 6px;
  align-items: center;
}

.variables-row > .variables-delete-icon {
  justify-self: end;
}

.variables-header-row {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--editor-muted-text-color);
  text-transform: uppercase;
  margin-bottom: 0;
}

.variables-name-input,
.variables-value-input {
  width: 100%;
  min-width: 0;
  padding: 1px 4px;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  box-sizing: border-box;
}

.variables-type-select {
  width: 100%;
  min-width: 0;
  font-size: var(--editor-control-font-size);
  box-sizing: border-box;
}

.set-to-text-input {
  width: 180px;
}

/* The guard beside the activation ("Only if"). Natural width, hugging its field — it is one
   label on one row, so there is nothing to align a column with, and the If / Then / Else column
   below is sized to "Then" and wrapped this onto two lines. */
.guard-label {
  flex: 0 0 auto;
  margin: 0 var(--panel-control-gap) 0 0;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
}

.conditional-label {
  display: inline-block;
  width: 32px;
  margin: 0 var(--panel-control-gap) 0 12px;
  font-size: var(--editor-control-font-size);
}

.if-condition,
.then-statement,
.else-statement {
  width: 170px;
}

.vi-textarea-text {
  display: block;
  box-sizing: border-box;
  width: calc(100% - var(--panel-indent) - var(--panel-right-gutter));
  font-family: var(--editor-font-family);
  font-size: var(--editor-control-font-size);
  /* Carry the half inter-row gap top/bottom so the text box isn't cramped
     against the align row above and the next section below. */
  margin: var(--details-row-pad) var(--panel-right-gutter) var(--details-row-pad) var(--panel-indent);
}

/* The Text panel's rich-text editing area: a contentEditable that renders the
   item's styled runs so you can select a range and restyle just that part. */
.vi-rich-text {
  display: block;
  box-sizing: border-box;
  width: calc(100% - var(--panel-indent) - var(--panel-right-gutter));
  min-height: 3.5em;
  max-height: 220px;
  overflow-y: auto;
  padding: 6px 8px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  /* Fallback; the component sets the exact canvas colour inline (ge.vc.backgroundColor). */
  background: var(--canvas-background-color);
  white-space: pre-wrap;
  word-break: break-word;
  outline: none;
  margin: var(--details-row-pad) var(--panel-right-gutter) var(--details-row-pad) var(--panel-indent);
}
.vi-rich-text:focus {
  border-color: var(--editor-selected-blue);
}
/* Placeholder for an empty editor (contentEditable has no native placeholder). */
.vi-rich-text:empty::before {
  content: attr(data-placeholder);
  color: var(--editor-text-muted-color, #888);
  pointer-events: none;
}

/* Pen ring controls and generic rotation helpers. */
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

.rings-div {
  /* Relative flow (not absolutely nudged) so the prev/next triangles line up
     inline with the ring name and the rest of the pen-mode row. */
  display: inline-flex;
  align-items: center;
  margin-left: 5px;
}

.ring-descr-div {
  display: inline-flex;
  align-items: center;
  /* Prev/next triangles sit well clear of the "Ring" label. */
  margin-right: 18px;
}

#ring-p {
  font-size: 12px;
  font-weight: bold;
  margin: 0;
}

.ring-icon {
  display: inline-block;
  width: var(--editor-small-icon-size);
  /* Dim at rest, brighten on hover — same as the plus/minus icon-buttons
     (the :hover rule already sets opacity:1). */
  opacity: var(--editor-icon-muted-opacity);
}

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

.rotate-180 {
  transform: rotate(180deg);
}

.rotate-270 {
  transform: rotate(270deg);
}

.stroke-align-select {
  margin-left: 0;
  vertical-align: middle;
}

p.sides-p {
  width: 35px;
}

.sides-dim {
  width: 100px;
}

/* Dark theme overrides. Colors come from CSS variables that the
   PreferencesProvider sets on :root. The non-color tweaks here adjust
   the few hard-coded swatches, borders, and form-control surfaces that
   wouldn't track those variables on their own. */
[data-theme="dark"] {
  color-scheme: dark;
  --canvas-background-color: #0c0e12;
  --panel-border-color: #2a2c33;
  --code-accent: #4ec78e;
  --data-accent: #e05cc0;
  --prototype-accent: #f0a35e;
  /* Buttons use a MUTED green here (not the bright text accent above) with white
     text, so a filled code button doesn't glare on the dark canvas. */
  --code-accent-btn: #1a805a;
  --code-accent-fg: #fff;
  /* Darker amber fill so white text passes contrast on the dark canvas
     (the light --prototype-accent above stays for accent text/lines). */
  --prototype-accent-btn: #bd5a12;
  --prototype-accent-fg: #fff;
  --editor-border-color: #2a2c33;
  /* Code-editor pane syntax-highlight palette (dark theme). */
  --syn-comment: #6a9955;
  --syn-keyword: #569cd6;
  --syn-string: #ce9178;
  --syn-number: #b5cea8;
  --syn-boolean: #569cd6;
  --syn-tag: #569cd6;
  --syn-attr: #9cdcfe;
  --syn-prop: #9cdcfe;
  --syn-punct: #b8b8c0;
  /* Form-control surface and a section-title color that sits midway
     between the bright body text and the muted labels so titles read
     as a quieter "structural" element instead of glare. */
  --editor-input-bg: #15171c;
  --editor-input-bg-disabled: #1f2126;
  --editor-input-text-color: #b9b7c5;
  --header-1-color: #d9d8e3;
  --header-2-color: #c9c7d6;
  --header-3-color: #8f8d9e;
  --tree-list-bg: #0e1014;
  --tool-active-bg: #383b44;
  --tool-button-bg: #34373f;
  --tree-selection-bg: rgba(16, 151, 226, 0.30);
  /* Focused-control ring. Sits clearly above the input-bg / panel
     surfaces but well below body text, so the focus state reads as
     "marked" without the bright white glare the browser default
     produces on top of the dark panels. */
  --input-focus-outline: #4a4d57;
  /* Dark-mode action-button surface. */
  --button-bg: #23252b;
  --button-bg-hover: #2e3038;
  --button-text: var(--editor-text-color);
}

[data-theme="dark"] .tools-group {
  border-color: #2f323a;
}

[data-theme="dark"] .tools button {
  background: var(--tool-button-bg);
}

[data-theme="dark"] .tool {
  background: #ffffff08;
}

[data-theme="dark"] #text-area {
  color: #e8e8ee;
}

[data-theme="dark"] .hdr {
  color: #6c707b;
  opacity: 1;
}

/* Tree panel reads as the darkest "structural" surface so its content
   stands out from the brighter details panel. */
[data-theme="dark"] .tree-div {
  background-color: var(--tree-list-bg);
}

/* Tool/details/tree icons ship as dark glyphs on a light surface. On
   dark theme, invert their luminance so the strokes become near-white
   against the now-dark panels, with a hue-rotate to keep colored
   accents roughly where they were. Top-toolbar icons (play, share,
   user, notifications, zoom-plus/minus, etc.) are NOT inverted —
   they're already drawn as light glyphs and the top toolbar stays
   dark in both themes. */
[data-theme="dark"] .tool-icon,
[data-theme="dark"] .pen-icon,
[data-theme="dark"] .align-icon,
[data-theme="dark"] .arrow-icon,
[data-theme="dark"] .cap-icon-img,
[data-theme="dark"] .corner-count-icon,
[data-theme="dark"] .angle-icon,
[data-theme="dark"] .star-ratio-icon,
[data-theme="dark"] .line-width-icon,
[data-theme="dark"] .stroke-width-icon,
[data-theme="dark"] .add-fill-icon,
[data-theme="dark"] .unify-paint-icon,
[data-theme="dark"] .paint-row-none,
[data-theme="dark"] .add-palette-color-icon,
[data-theme="dark"] .add-stroke-icon,
[data-theme="dark"] .add-behavior-icon,
[data-theme="dark"] .add-variable-icon,
[data-theme="dark"] .add-grid-icon,
[data-theme="dark"] .add-trigger-icon,
[data-theme="dark"] .add-group-icon,
[data-theme="dark"] .remove-groups-icon,
[data-theme="dark"] .delete-color-icon,
[data-theme="dark"] .delete-stroke-icon,
[data-theme="dark"] .delete-gradient-icon,
[data-theme="dark"] .delete-gradient-color-icon,
[data-theme="dark"] .delete-behavior-icon,
[data-theme="dark"] .delete-ring-icon,
[data-theme="dark"] .variables-delete-icon,
[data-theme="dark"] .delete-trigger-icon,
[data-theme="dark"] .page-list-plus-icon,
[data-theme="dark"] .page-list-minus-icon,
[data-theme="dark"] .page-generate-icon,
[data-theme="dark"] .templates-add-icon,
[data-theme="dark"] .templates-delete-icon,
[data-theme="dark"] .minus-variable-icon,
[data-theme="dark"] .page-triangle-img,
[data-theme="dark"] .section-triangle-img,
[data-theme="dark"] .dim-subtriangle,
[data-theme="dark"] .tree-triangle-img,
[data-theme="dark"] .tree-type-icon,
[data-theme="dark"] .corners-triangle,
[data-theme="dark"] .arc-triangle,
[data-theme="dark"] .line-width-triangle,
[data-theme="dark"] .sight-icon,
[data-theme="dark"] .eyedropper,
[data-theme="dark"] .color-picker-mode-cancel-icon,
[data-theme="dark"] .ring-icon,
[data-theme="dark"] .group-icon,
[data-theme="dark"] .union-icon,
[data-theme="dark"] .outline-icon,
[data-theme="dark"] .exclude-icon,
[data-theme="dark"] .intersect-icon,
[data-theme="dark"] .subtract-icon,
[data-theme="dark"] .mask-icon,
[data-theme="dark"] .palette,
[data-theme="dark"] .selected-icon,
[data-theme="dark"] .scale-skew-icon,
[data-theme="dark"] .cap-link-toggle,
[data-theme="dark"] .token-link-icon {
  filter: invert(0.94) hue-rotate(180deg) brightness(1.02);
}

/* ── Standard control sizing (Design + Prototype panels) ───────────────────
   Every edit box (text/number) and drop-down in the right-hand panels shares
   ONE height and ONE text size, so rows read as a consistent grid. Widths are
   deliberately left per-control (a font name needs more room than a px value),
   so this rule never touches `width` or `box-sizing` — height is pinned by
   font-size + line-height + vertical padding + border, which lands at the same
   outer height whether a control was content-box or border-box. Selects that
   were unstyled (native) also pick up the shared border/radius here. Excludes
   checkbox/radio/color/range (not listed) and textarea (multi-line). */
.details-scroll input[type="text"],
.details-scroll input[type="number"],
.details-scroll input[type="search"],
.details-scroll input:not([type]),
.details-scroll select,
/* Left-panel edit boxes that need the SAME flat control look (the flat border/
   radius is otherwise scoped to .details-scroll, so these fell back to the
   browser's inset 3-D input) — e.g. the flow inspector's node / branch labels. */
.flow-node-label,
.flow-conn-label,
.flow-condition {
  /* Fill the inspector's width (leaving its padding as the right gutter) instead
     of the browser's intrinsic input width — matching every other panel field. */
  box-sizing: border-box;
  width: 100%;
  font-size: var(--editor-control-font-size);
  line-height: var(--editor-control-line-height);
  padding-top: var(--editor-control-pad-y);
  padding-bottom: var(--editor-control-pad-y);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
}

/* A labelled field in the flow inspector: the label BESIDE its control, like every
   other panel section. These were stacked — label on its own line above a full-width
   control — which is a shape the panel uses nowhere else, and it was never a decision:
   the rule above makes every input and select 100% wide, so a label beside one had no
   room. A fixed label column and a control that flexes into what's left fixes both.
   Two classes deep so it out-specifies that width. */
.flow-field-row {
  display: flex;
  align-items: center;
  gap: var(--panel-control-gap);
  margin-top: 8px;
}
.flow-field-label {
  flex: 0 0 84px;
  font-size: 11px;
  color: var(--editor-muted-text-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flow-field-row input,
.flow-field-row select {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}

/* The Diagrams-mode FlowNode inspector renders real paragraph captions (condition
   hints, variable lists, validation notes). These used to need exempting from the
   panel-wide `.details-div p` rule, which forced every panel <p> into a 14px
   inline-block glyph-label box and collapsed the captions to one word per line. That
   rule is now scoped to `p.dim-descr`; this just restores the defaults. */
.details-div .flow-node-section p {
  display: block;
  width: auto;
}

/* Segmented Manual / Automatic layout toggle in the diagram inspector (right panel). */
.flow-layout-toggle {
  display: flex;
  width: fit-content;
  border: 1px solid var(--editor-border-color);
  border-radius: 5px;
  overflow: hidden;
}
.flow-layout-btn {
  padding: 3px 12px;
  border: none;
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  background: transparent;
  color: var(--editor-text-color);
}
.flow-layout-btn.active {
  background: var(--code-accent-btn);
  color: var(--code-accent-fg);
}

/* Give EVERY left-panel (.tree-div) edit box the app's flat control border — the
   .details-scroll flat style above is right-panel-scoped, so left-panel inputs
   otherwise fall back to the browser's inset 3-D input. Border/radius only (no
   width or size assumptions), so it just flattens without reshaping any field. */
.tree-div input[type="text"],
.tree-div input[type="number"],
.tree-div input[type="search"],
.tree-div input:not([type]) {
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
}

/* Native <select> combo boxes render a hair taller than a text input with the
   same line-height/padding (UA min-block-size + dropdown metrics). Pin them to
   the exact standard control height so every edit box and combo box in the
   panel is the same size. Height = line-height + 2·pad-y + 2·1px border. */
.details-scroll select {
  box-sizing: border-box;
  height: calc(var(--editor-control-line-height) + 2 * var(--editor-control-pad-y) + 2px);
}

/* Form-control surface: every editable text/number input and select
   inside the editor uses the panel-input variable for a dark
   background. Input text is a touch dimmer than body text so the
   labels (which use --editor-muted-text-color) and section titles
   read with a clean hierarchy. Range sliders are excluded — the
   browser's color-scheme: dark on :root re-skins them automatically.

   `.on-canvas-field` IS EXCLUDED, and that is the whole point of the class.
   A field drawn over the ARTWORK — the box you type a sheet cell into — is not a
   panel control: it stands exactly where the canvas has already drawn that cell, and
   what it must look like is that cell. This rule reached it anyway (an element
   selector under an attribute one out-weighs the field's own class, so
   `.sheet-edit-input { background: transparent }` lost to it), which put a slab of
   panel grey — near-black, in dark mode — over the cell the moment you typed in one.
   Only in dark mode, because in light mode the panel input is white and most sheets
   are too, so it read as "fine". Excluding here rather than out-specifying there is
   deliberate: an exclusion cannot be undone by the order two rules happen to sit in. */
[data-theme="dark"] input[type="text"]:not(.on-canvas-field),
[data-theme="dark"] input[type="number"]:not(.on-canvas-field),
[data-theme="dark"] input[type="search"]:not(.on-canvas-field),
[data-theme="dark"] input[type="email"]:not(.on-canvas-field),
[data-theme="dark"] input[type="password"]:not(.on-canvas-field),
[data-theme="dark"] input:not([type]):not(.on-canvas-field),
[data-theme="dark"] textarea:not(.on-canvas-field),
[data-theme="dark"] .flow-node-label,
[data-theme="dark"] .flow-conn-label,
[data-theme="dark"] .flow-condition,
[data-theme="dark"] select {
  background-color: var(--editor-input-bg);
  color: var(--editor-input-text-color);
  border-color: var(--editor-control-border-color);
}

[data-theme="dark"] input[type="text"]:disabled,
[data-theme="dark"] input[type="number"]:disabled,
[data-theme="dark"] input:not([type]):disabled,
[data-theme="dark"] textarea:disabled,
[data-theme="dark"] select:disabled {
  background-color: var(--editor-input-bg-disabled);
  color: var(--editor-muted-text-color);
}

/* Color swatch borders need to read against the dark panel. */
[data-theme="dark"] .color-div,
[data-theme="dark"] .palette-color-div,
[data-theme="dark"] .constraints-outer-box,
[data-theme="dark"] .constraints-inner-box {
  border-color: #4a4d57;
}

/* Item-tree row text & file-name text — explicit color so it sits
   correctly on the darker tree panel surface. */
[data-theme="dark"] .item-tree-item p,
[data-theme="dark"] p.file-name-p {
  color: var(--editor-text-color);
}

/* Project-image placeholder background on home page. */
[data-theme="dark"] .project-img-placeholder {
  background-color: rgba(255, 255, 255, 0.03);
}

/* Top-toolbar glyphs ship in near-white (#DEDEDE) and read as overly
   loud against the very dark toolbar. Dim them a touch in dark mode
   so they sit at roughly the same visual weight as the rest of the
   chrome. The standard-top-toolbar-icon class is the umbrella used by
   most of these icons, so this single rule covers the bulk of them. */
[data-theme="dark"] .standard-top-toolbar-icon,
[data-theme="dark"] .tt-zoom-icon,
[data-theme="dark"] .logo-icon {
  opacity: 0.85;
}

/* Disclosure triangles originally use --editor-icon-muted-opacity (0.6);
   combined with the dark-theme invert filter that's too faint, so push
   them up to stay readable. The page-list add/remove icons deliberately
   stay at the muted 0.6 (via .page-list-plus-icon/.page-list-minus-icon)
   so they recede like the design-panel section icons and titles. */
[data-theme="dark"] .page-triangle-img,
[data-theme="dark"] .section-triangle-img,
[data-theme="dark"] .corners-triangle,
[data-theme="dark"] .arc-triangle,
[data-theme="dark"] .line-width-triangle,
[data-theme="dark"] .minus-variable-icon {
  opacity: 0.9;
}

/* Zoom percentage reads as toolbar text by default — very bright in
   dark mode. Match the design-panel input text color so it doesn't
   draw the eye as much as the rest of the toolbar chrome. */
[data-theme="dark"] .zoom-percentage,
[data-theme="dark"] .zoom-percentage-textbox {
  color: var(--editor-input-text-color);
}

/* Focused-control outline. Default browser behavior renders a bright
   ring; the editor's per-input rules also set outline-color to the
   bright editor-text-color. In dark mode, swap both to a softer
   mid-gray that reads as "focused" without glaring. Buttons get the
   same treatment so the focus ring across the Home / Customize /
   Login / Signup / Images pages stays consistent. */
[data-theme="dark"] input:focus,
[data-theme="dark"] input:focus-visible,
[data-theme="dark"] textarea:focus,
[data-theme="dark"] textarea:focus-visible,
[data-theme="dark"] select:focus,
[data-theme="dark"] select:focus-visible,
[data-theme="dark"] button:focus-visible {
  outline: 2px solid var(--input-focus-outline);
  outline-offset: -1px;
  border-color: var(--input-focus-outline);
}

/* Page-level surface. The .App element already paints the panel
   background across the whole viewport, but if any page is ever
   sized smaller than the window, html/body fall back to the same
   surface so the user never sees the browser's default white. */
html[data-theme="dark"],
html[data-theme="dark"] body {
  background-color: var(--editor-panel-background-color);
}

html[data-theme="light"],
html[data-theme="light"] body {
  background-color: var(--editor-panel-background-color);
}

/* ============================================================
   Fill / stroke gradient editor
   ============================================================ */

.gradient-editor {
  margin: 0 var(--panel-right-gutter, 12px) 0 var(--panel-indent);
  /* Carry the half-gap like a row so it sits a full inter-row gap from its
     neighbours (graphic blocks otherwise only get the neighbour's half). */
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  display: flex;
  flex-direction: column;
  gap: var(--details-row-gap);
}

/* Fill-type combo box, sat at the end of the color line. */
.gradient-type-select {
  /* flex-basis (max-content) pins the width to the combo's own content so the
     global `.details-scroll select { width: 100% }` rule can't stretch it to the
     full panel width (which pushed it off the edge). All four fill controls —
     swatch, hex, alpha, type — then fit on one line. */
  flex: 0 0 max-content;
  margin-left: auto;
  margin-right: 8px;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}

/* Image-fill editor (Fill section, when the fill type is Image): a fit-mode row and a
   "Select image" button that opens the gallery. A block below the colour line. */
.image-fill-editor {
  margin: 0 var(--panel-right-gutter, 12px) 0 var(--panel-indent);
  /* Carry the half-gap like a row (see .gradient-editor). */
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
.image-fill-fit-row {
  display: flex;
  align-items: center;
  gap: 8px;
  align-self: stretch;
}
.image-fill-fit-row label {
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.image-fill-fit {
  flex: 1 1 0;
  min-width: 0;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}
.image-fill-select { margin: 0; }

/* Brightness / contrast rows — the fill editor's and the Image section's alike.
   Rows reuse .scale-skew-* styles; the label is a text glyph in the icon slot,
   sized up a step so ☀ / ◐ read at panel scale in both themes (it inherits the
   muted text colour, like the H/V icons' grey). */
.image-fill-adjust {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-self: stretch;
}
.image-adjust-label {
  font-size: 14px;
  justify-content: center;
}
.image-adjust-div {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}
.image-adjust-div .scale-skew-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.image-adjust-div .scale-skew-input {
  flex: 0 0 44px;
  min-width: 0;
  width: auto;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  text-align: right;
}

/* Custom / Tile manual scale + offset controls. Scale rows reuse .scale-skew-* styles. */
.image-fill-xform {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-self: stretch;
}
.image-fill-offset-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.image-fill-offset-label {
  flex: 0 0 auto;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.image-fill-offset-axis {
  flex: 0 0 auto;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-muted-color, #888);
}
.image-fill-offset-in {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  text-align: right;
}

/* Preview bar + draggable stop markers. */
.gradient-bar {
  display: flex;
  flex-direction: column;
  /* Separate the ramp from the marker strip via flex gap — a margin here gets
     zeroed by the panel's blanket margin reset, so the markers would ride up
     onto the ramp. */
  gap: 3px;
}

/* Preview bar: a checkerboard backdrop with the live gradient on top so
   stop transparency reads correctly. */
.gradient-preview {
  position: relative;
  height: 18px;
  overflow: hidden;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: #ffffff;
  background-image:
    linear-gradient(45deg, #c8c8c8 25%, transparent 25%, transparent 75%, #c8c8c8 75%),
    linear-gradient(45deg, #c8c8c8 25%, transparent 25%, transparent 75%, #c8c8c8 75%);
  background-size: 10px 10px;
  background-position: 0 0, 5px 5px;
}

.gradient-preview-fill {
  position: absolute;
  inset: 0;
}

/* Strip below the preview bar holding the draggable stop markers. Height hugs
   the 12px marker so no dead space opens up before the first color row (the
   ramp↔marker gap lives on .gradient-bar). */
.gradient-marker-track {
  position: relative;
  height: 12px;
}

.gradient-marker {
  position: absolute;
  top: 0;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  box-sizing: border-box;
  border: 2px solid #ffffff;
  border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);
  cursor: ew-resize;
}

/* Indicator line spanning the full preview bar at each stop's location.
   Rendered inside .gradient-preview (which clips it) so it covers the
   whole bar height. */
.gradient-stop-indicator {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  margin-left: -1px;
  background-color: #ffffff;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}

.gradient-stops {
  display: flex;
  flex-direction: column;
  /* Stack the stop rows on the one inter-row gap (was a one-off 4px). */
  gap: var(--details-row-gap);
}

.gradient-stop-row {
  display: flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
}

/* The hex box carries a right margin for the non-flex solid color line; in
   the flex stop row that margin stacks on top of the gap, so zero it here. */
.gradient-stop-row .color-text-input {
  margin: 0;
}

.gradient-stop-color {
  flex: none;
  width: 28px;
  height: 20px;
  padding: 0;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background: none;
  cursor: pointer;
}

.gradient-stop-alpha {
  /* flex-basis (not width) so `.details-scroll input { width: 100% }` can't
     stretch it across the row. */
  flex: 0 0 52px;
  box-sizing: border-box;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}

.gradient-stop-range {
  flex: 1 1 auto;
  min-width: 0;
}

/* The hex field in a gradient stop row: pin its basis so it (like the alpha
   field) isn't stretched to 100% by `.details-scroll input`; the slider
   (flex 1 1) then takes the remaining width. */
.gradient-stop-row .color-text-input {
  flex: 0 0 64px;
  width: 64px;
}

.gradient-stop-pos {
  flex: none;
  width: 34px;
  text-align: right;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}

/* Same bare-glyph look/feel as the other delete (minus) icons via the
   .delete-color-icon class, at the standard small-icon size. */
.gradient-stop-remove {
  flex: none;
  height: var(--editor-small-icon-size);
}

.gradient-add-stop {
  align-self: flex-start;
  cursor: pointer;
}

.gradient-add-stop:hover {
  opacity: 1;
}

/* Numeric start/end endpoint controls (the panel twin of the on-canvas handles).
   Standard inter-row spacing: one --details-row-pad each side per row, plus one
   before the whole block so it's a full row gap from the add-stop button above. */
/* The Start / End rows sit as two children of the .gradient-editor flex column,
   which already puts one --details-row-gap between every block. So NO margin-top
   here (that stacked on the parent gap → extra space above Start) and NO per-row
   vertical padding (that stacked below End); the block's own `gap` spaces the two
   rows by the same single inter-row gap. */
.gradient-endpoints {
  display: flex;
  flex-direction: column;
  gap: var(--details-row-gap);
}
/* Proximity: the wide --panel-pair-gap separates the row's groups (the Start/End
   label and the two X | Y pairs), while each axis label hugs its own input with
   the tighter --panel-checkbox-label-gap below. Mirrors the .dim-row X/Y row. */
.gradient-endpoint-row {
  display: flex;
  align-items: center;
  gap: var(--panel-pair-gap);
}
.gradient-endpoint-label {
  flex: 0 0 40px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}
/* One (axis label + input) pair. Each cell shares the row equally and grows,
   so the two fields fill the extra width — mirrors the shadow X/Y offset row. */
.gradient-endpoint-cell {
  display: inline-flex;
  align-items: center;
  flex: 1 1 0;
  min-width: 0;
}
.gradient-endpoint-axis {
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  margin-right: var(--panel-checkbox-label-gap);
  user-select: none;
}
/* Grow into the spare row width (flex-basis 0 + grow), beating
   `.details-scroll input { width: 100% }` via flex. */
.gradient-endpoint-input {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
  box-sizing: border-box;
  padding: 2px 4px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}

/* The gradient endpoint handles are drawn directly on the canvas by the
   renderer (see render.ts drawGradientHandles) — no CSS overlay. */

/* ============================================================
   Color-picker eyedropper overlay & magnifier loupe
   ============================================================ */

/* Transparent layer shown while the color picker is in eyedropper
   mode. It covers only the canvas region (matching #canvas-div) so the
   toolbars and panels stay usable; sits below the toolbar dropdown
   menus (z-index 55–60) so those still open over it. */
.eyedropper-overlay {
  position: fixed;
  top: var(--top-toolbar-height);
  left: calc(var(--left-toolbar-width) + var(--tree-width));
  right: var(--details-width);
  bottom: 0;
  z-index: 50;
  background: transparent;
  cursor: crosshair;
}

/* The magnifier loupe — a zoomed view of the canvas pixels around the
   cursor. Positioned (in viewport coords) in a canvas corner; round or
   square per prefs. */
.eyedropper-loupe {
  position: fixed;
  box-sizing: content-box;
  border: 3px solid #ffffff;
  box-shadow:
    0 6px 20px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  pointer-events: none;
  background: #7a7a82;
}

.eyedropper-loupe.round {
  border-radius: 50%;
}

.eyedropper-loupe.square {
  border-radius: 10px;
}

.eyedropper-loupe canvas {
  display: block;
}

/* ---- Components section (composition layer) ---- */
.templates-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--panel-control-gap);
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}

.templates-btn {
  font-size: var(--editor-control-font-size);
  padding: 3px 10px;
  border: 1px solid var(--editor-border-color, rgba(128, 128, 128, 0.4));
  border-radius: 4px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.templates-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.templates-instance-label {
  margin: 0 0 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
  opacity: 0.85;
}

.templates-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0 12px 0 var(--panel-indent);
}

.templates-row {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  column-gap: 8px;
  align-items: center;
}

.templates-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.templates-count {
  opacity: 0.6;
  font-size: var(--editor-control-font-size);
}

.templates-delete-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  cursor: pointer;
  user-select: none;
}
/* Highlight the delete (−) on hover so it reads as an interactive button, across every
   library section (colours / text styles / layouts). */
.templates-delete-icon:hover {
  opacity: 1;
}

/* Component-instance badge (tree rows + library) */
/* Component/instance marker — a hollow diamond drawn as a rotated square so
   it centers exactly (a ◇ text glyph sits off its optical center). */
.tree-instance-badge {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin-right: 6px;
  border: 1.5px solid var(--accent-color, #7a5cff);
  transform: rotate(45deg);
  box-sizing: border-box;
  user-select: none;
}

/* In the tree, the diamond sits in a slot the same size as a type icon so
   every row's text lines up regardless of icon vs. diamond. */
.item-tree-item .tree-instance-slot {
  height: 16px;
  width: 16px;
  padding: 0 5px 0 0;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  user-select: none;
}
.item-tree-item .tree-instance-slot .tree-instance-badge {
  margin: 0;
}

/* The mid-purple is muddy against the near-black dark panels; lighten it
   so the marker keeps clear contrast. */
[data-theme="dark"] .tree-instance-badge {
  border-color: #b9a7ff;
}

/* A code-owned item (data node / UI control) uses the same diamond but in the
   code green (the data-binding green), not the template accent — lightened for dark. */
.tree-instance-badge.tree-data-node-badge {
  border-color: #15825a;
}
[data-theme="dark"] .tree-instance-badge.tree-data-node-badge {
  border-color: #4ec78e;
}
/* A UI CONTROL: a FILLED purple diamond — a template instance is the same purple,
   but outlined, so the two read apart at a glance. */
.tree-instance-badge.tree-control-badge {
  border: none;
  background-color: #7a5cff;
}
[data-theme="dark"] .tree-instance-badge.tree-control-badge {
  background-color: #b9a7ff;
}
/* A COMPONENT (a sealed chart/sheet/hierarchy root): a FILLED green diamond — the same
   fill/outline split as control/template-instance, in the data-node's code green rather
   than the control's purple, since a component is model-backed like a data node but reads
   as one item like a control. */
.tree-instance-badge.tree-component-badge {
  border: none;
  background-color: #15825a;
}
[data-theme="dark"] .tree-instance-badge.tree-component-badge {
  background-color: #4ec78e;
}

/* Templates library — left panel section (between Pages and the tree) */
.templates-panel-div {
  padding: 8px 0;
}

.templates-panel-header {
  display: flex;
  align-items: center;
}

.templates-panel-heading {
  margin: 0;
  user-select: none;
  display: inline-block;
  vertical-align: middle;
  color: var(--header-2-color);
}

.templates-add-div {
  margin-left: auto;
  /* Match the Pages header's right-edge gap (`.page-header-row` padding-right)
     so the Templates "+" lines up horizontally with the Pages "+" — both sit in
     the same tree panel, so a different gap left them visibly misaligned. */
  margin-right: 10px;
  display: inline-flex;
  align-items: center;
}

.templates-add-icon {
  opacity: var(--editor-icon-muted-opacity);
  height: var(--editor-small-icon-size);
  cursor: pointer;
  user-select: none;
}

.templates-add-icon.disabled {
  opacity: 0.25;
  cursor: default;
}
/* Highlight the add (+) on hover so it reads as an interactive button — across every library
   section (components / colours / text styles / layouts). Skips the disabled state. */
.templates-add-icon:not(.disabled):hover {
  opacity: 1;
}

.templates-empty {
  margin: 0 0 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
  opacity: 0.7;
}

.templates-panel-ul {
  list-style: none;
  /* Right margin matches the section "+" gap (.templates-add-div, 10px) so the
     row "−" lines up under the "+" — and with every other assets section. */
  margin: 0 10px 0 var(--panel-indent);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* AN ASSET ROW: a lead-in, a name that takes the slack, and then whatever trailing controls
   the row has — ending, in every section, with the "−".
 *
 * ONLY THE FIRST TWO TRACKS ARE NAMED, and the rest are created as the row's own children
 * need them (`grid-auto-flow: column`). Naming five tracks and letting a shorter row leave
 * the last ones empty is where the assets sections stopped lining up: an empty track is
 * still a track, its 8px COLUMN GAP is still spent, and the "−" on Colors, Effect Styles and
 * Spacing sat 8px left of the "−" on Text Styles and Layouts and of every section's "+"
 * above them — three columns where there should have been one. Counting is also the wrong
 * thing to depend on, because the count MOVES: the token-collection cell appears only once a
 * project has a second collection, so a template that fitted would stop fitting the first
 * time somebody added one. Implicit tracks can't be off by one. */
.templates-panel-row {
  display: grid;
  grid-auto-flow: column;
  grid-template-columns: auto 1fr;
  grid-auto-columns: auto;
  column-gap: 8px;
  align-items: center;
  /* Space the asset rows on the list / tree-view gap. */
  padding-top: var(--list-row-pad); padding-bottom: var(--list-row-pad);
}

/* Template rows read as clickable links — highlight the whole row on hover. */
.template-link-row {
  border-radius: var(--corner-radius-sm);
}
.template-link-row:hover {
  background-color: var(--editor-hover-bg, rgba(127, 127, 127, 0.15));
}

/* In the library grid the column-gap already spaces the badge from the name,
   so drop the badge's tree-row margin to tighten that gap. */
.templates-panel-row .tree-instance-badge {
  margin-right: 0;
}

/* Colour-token rows: swatch | name | then alpha and delete in implicit tracks — so the
   minus sits at the row's end like the other panels. The list's right gap matches the header's
   "+" gap (14px) so the row "−" lines up vertically with the section "+"; the
   name column (1fr) gives up the space. */
.color-tokens-ul {
  margin-right: 10px;
}
/* MODES section — one block per mode axis (collection): the axis name as a small heading
   with its ⋯ and +, then a LIST of its modes, one row each, the showing one marked by a
   filled dot. See TokenModesList.tsx. */
.token-modes {
  padding-bottom: 4px;
}
.token-mode-axis {
  display: block;
  padding: 2px 10px 4px var(--panel-indent);
}
.token-mode-axis-head {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding-top: var(--list-row-pad);
  padding-bottom: 2px;
}
/* The axis name is a heading over its modes, not another row: small caps-style, muted. */
.token-mode-axis-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: calc(var(--editor-control-font-size) - 1px);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--editor-muted-text-color);
  cursor: default;
}
.token-mode-axis-head .token-mode-rename {
  flex: 1 1 auto;
  min-width: 0;
}
/* The mode rows use the shared list row, in the panel's blue rather than the code green
   (Modes is not a code section) — the same one-line accent swap the corner list makes. */
.token-mode-list {
  --code-accent: var(--editor-selected-blue);
}
/* THE ROW'S EDGES ARE THE HEADING'S. The shared row pads 6px on its right, which put every
   row's − 6px left of the + in the heading above it and the section's + above that — three
   columns that should have been one. The rows now end where the heading ends, and the dot
   starts where the heading's name does. */
/* Element + two classes: `.flow-tree-row` is declared further down this file, so a single
   class here loses to it on order — which is exactly how the first attempt at this changed
   nothing. */
li.flow-tree-row.token-mode-row {
  padding-left: 0;
  padding-right: 0;
}
/* …and no left bar on the showing row: at the heading's edge it would sit on top of the dot,
   and the filled dot already says which mode is showing. The row keeps its tint. */
li.flow-tree-row.token-mode-row.selected {
  box-shadow: none;
}
.token-mode-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.token-mode-row .token-mode-rename {
  flex: 1 1 auto;
  min-width: 0;
}
/* The radio dot: hollow for a mode that isn't showing, filled for the one that is. */
.token-mode-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--editor-muted-text-color);
  box-sizing: border-box;
}
.token-mode-dot.on {
  border-color: var(--editor-selected-blue);
  background: var(--editor-selected-blue);
}
/* A row's − only shows on the row you point at (and the showing one), so a list of
   modes doesn't read as a column of delete buttons. */
.token-mode-row .token-mode-delete {
  visibility: hidden;
}
.token-mode-row:hover .token-mode-delete,
.token-mode-row.selected .token-mode-delete,
.token-mode-row:focus-within .token-mode-delete {
  visibility: visible;
}

/* Which axis a token varies along — the cell on a token row, shown only once the
   project has a second collection. Sized like the other small row controls. */
.token-collection-select {
  flex: 0 0 auto;
  min-width: 56px;
  max-width: 96px;
  box-sizing: border-box;
  padding: 2px 4px;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
}
.templates-panel-row.color-token-row {
  grid-template-columns: auto 1fr;
}
/* Opacity %. Matches the standard .alpha-input box (Fill/Stroke). */
.color-token-alpha {
  width: 44px;
  box-sizing: border-box;
  padding: 2px 4px;
  text-align: right;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  vertical-align: middle;
}
/* Colour swatch. Matches the standard .color-div swatch (Fill/Stroke). */
.color-token-swatch {
  width: 28px;
  height: 20px;
  padding: 0;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: none;
  cursor: pointer;
}
.color-token-swatch::-webkit-color-swatch-wrapper { padding: 0; }
.color-token-swatch::-webkit-color-swatch { border: none; border-radius: 2px; }
.color-token-name {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  min-width: 0;
  padding: 0;
}

/* HEX is ONE box where the numeric readings are three, so it takes the room they shared.
   OPACITY sits on the same line and needs only "100", so it takes a fixed sliver and leaves
   the rest to the colour itself.

   Both are written as compound selectors on purpose: the plain `.color-part-div` /
   `.color-part-input` rules above are single-class too, so a single-class rule here would be
   settled by WHICH CAME LAST IN THE FILE — which is how the hex box first shipped 0px wide
   while opacity took the whole row. */
.color-part-div.color-part-hex { flex: 1 1 auto; }
.color-part-hex .color-part-input { text-transform: uppercase; text-align: left; }
.color-part-div.color-part-alpha { flex: 0 0 auto; }
/* A DEFINITE FLEX BASIS, not a width. The panel's own `input` rule is an attribute+element
   selector and so outranks any class here — it sets width:100%, which is why the numeric
   boxes (flex-basis 0, width never consulted) behave while a `width` set here did not: the
   opacity box came out 203px wide and squeezed the hex box to nothing. A basis is the main
   size for a flex item, so it settles this without a specificity fight. */
.color-part-alpha .color-part-input { flex: 0 0 4.4ch; }

/* FOUR READINGS, not three: HEX / RGB / HSB / HSL have to share the picker's width or the
   last one wraps to a line of its own (it did). They divide the row instead of each asking
   for the width of its own word.

   THE STYLE ROW DOES NOT: Look & Feel / D / Custom are three words of very different
   lengths, and dividing the row evenly padded the short one out to the width of the long
   one — a "D" in the middle of a cell half of it empty. Each cell asks for its own word
   (the default `.text-type-cell`), and the row wraps between cells if it must. */
.color-space-choice { display: flex; }
.color-space-choice .align-icon-cell {
  flex: 1 1 0;
  min-width: 0;
  padding-left: 0;
  padding-right: 0;
  /* CENTRED BY THE FLEXBOX, not by `text-align`. A cell is an `inline-flex` (see
     .align-icon-cell), so its own text is an anonymous FLEX ITEM and `text-align` has
     nothing to say about it — the words sat hard against the left border of a cell half
     again as wide as they were. `justify-content` is the property that places a flex item,
     and `text-align` is kept for the wrapped line a long label would make. */
  justify-content: center;
  text-align: center;
}

/* ── Spacing/size token library rows ── */
.spacing-tokens-ul {
  margin-right: 10px;
}
/* Name | value | then the trailing controls. Name and value are both flexible so a panel
   resize splits 55/45 between them (11fr : 9fr); everything after them is an implicit track
   sized to itself (see .templates-panel-row). */
.templates-panel-row.spacing-token-row {
  grid-template-columns: 11fr 9fr;
}
.spacing-token-name {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  min-width: 0;
  padding: 0;
}
/* Value box. Fills its flexible grid track (so it grows/shrinks with the panel),
   with a floor wide enough to keep a value plus its unit suffix (e.g. "0.5 in"). */
.spacing-token-value {
  width: 100%;
  min-width: 68px;
  box-sizing: border-box;
  padding: 2px 4px;
  text-align: right;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  vertical-align: middle;
}

/* ── Spacing-token link control (design-panel numeric fields) ── */
/* A compact button that collapses to a link glyph; opens a portalled, viewport-
   clamped menu on click. Turns blue when bound, matching the other token UIs. */
/* ── Shared asset-link control (colour / text-style / spacing tokens) ──
   One button + portalled menu used by every token binding. See TokenLink.tsx. */
.token-link {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
}
/* Link-to-assets control: just the link glyph / token name — NO box (no border,
   no background fill). Follows the icon-button rule: the glyph is muted at rest
   and brightens on hover (opacity, so it lifts the 🔗 emoji and text alike). */
.token-link-btn {
  max-width: 84px;
  box-sizing: border-box;
  margin-left: 4px;
  padding: 1px 3px;
  text-align: center;
  color: var(--editor-muted-text-color);
  font-size: var(--editor-control-font-size);
  border: none;
  background: transparent;
  cursor: pointer;
  vertical-align: middle;
  line-height: 1.4;
  opacity: 0.72;
  transition: opacity 0.1s ease;
}
.token-link-btn .token-link-label {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Linked to a token: show its name in the accent colour. */
.token-link-btn.linked {
  color: var(--selection-color, #1097e2);
}
/* Hover brightens the glyph itself (foreground) — no background box. */
.token-link-btn:hover {
  opacity: 1;
}

/* A token-link sitting in a section header: standard horizontal spacing from
   the title, vertically centred (the header is already a centered flex row). */
.details-header > .token-link {
  margin-left: 6px;
}
.details-header > .token-link .token-link-btn {
  margin-left: 0;
}

/* Portalled menu (position:fixed set inline; clamped to the viewport in JS). */
.token-link-menu {
  position: fixed;
  z-index: 10000;
  min-width: 176px;
  max-height: 260px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--editor-panel-bg, var(--editor-input-bg, #fff));
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}
.token-link-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 8px;
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  white-space: nowrap;
}
.token-link-menu-item:hover {
  background: var(--editor-hover-bg, rgba(127, 127, 127, 0.18));
}
.token-link-menu-item.active {
  color: var(--selection-color, #1097e2);
  font-weight: 600;
}
.token-link-menu-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.token-link-menu-meta {
  flex: 0 0 auto;
  color: var(--editor-muted-text-color);
}
/* Colour-token swatch shown as a menu row's trailing meta. */
.token-link-swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.5));
}
.token-link-menu-new,
.token-link-menu-unlink {
  margin-top: 0;
  border-top: var(--editor-control-border);
  border-radius: 0;
}
.token-link-menu-empty {
  padding: 4px 8px;
  color: var(--editor-muted-text-color);
}

/* ── Text styles library rows ── */
.text-styles-ul {
  margin-right: 10px;
}
.templates-panel-row.text-style-row {
  grid-template-columns: auto 1fr;
  align-items: center;
}
/* "Ag" preview rendered in the style's own font; click applies to selected text. */
.text-style-preview {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 20px;
  padding: 0 5px;
  font-size: 13px;
  line-height: 1;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  cursor: pointer;
  user-select: none;
  overflow: hidden;
}
.text-style-name {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  min-width: 0;
  padding: 0;
}
/* Update-this-style-from-the-selected-text button (⟳). */
.text-style-update {
  background: none;
  border: none;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
  opacity: 0.7;
}
.text-style-update:hover {
  opacity: 1;
}

/* ── Paint assets library rows ── */
.paint-assets-ul {
  margin-right: 10px;
}
/* Six cells: swatch, name, what-it-is + how-many, then the three buttons. The button columns
   are `auto` and every row has all three, so they line up down the list — and the readout
   before them is a FIXED width for the same reason: "Gradient · 12" is wider than
   "Image · 1", and a content-width cell there would step the buttons in and out row by row.
   (Repeated icon buttons share one column — see the Dimensions +/− rows.) */
.templates-panel-row.paint-asset-row {
  grid-template-columns: auto 1fr 5.5em auto auto auto;
  align-items: center;
}
/* The asset's own picture, and a button: clicking it applies the asset to the current paint. */
.paint-asset-swatch {
  cursor: pointer;
}
.paint-asset-swatch:hover {
  box-shadow: 0 0 0 2px var(--editor-selected-blue);
}
/* "Texture · 3" — what the asset is, and how many paints wear it. Muted: it is a readout,
   not a control, and it must not compete with the name beside it. */
.paint-asset-uses {
  font-size: calc(var(--editor-control-font-size) - 2px);
  color: var(--editor-muted-text-color);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  user-select: none;
}
/* A ⟳ / ⧉ button with nothing to act on — no paint on screen carries a definition to
   capture. Matches `.templates-add-icon.disabled`, so the whole panel says "not now" one way. */
.text-style-update.disabled {
  opacity: 0.3;
  cursor: default;
}
.text-style-update.disabled:hover {
  opacity: 0.3;
}
/* The asset swatch inside the link MENU, beside each asset's name. */
.paint-asset-menu-swatch {
  flex: 0 0 14px;
  width: 14px;
  height: 14px;
}
/* The "Asset" link row in Fill / Stroke. It borrows .blend-row (shared label column, gap and
   right gutter); the link button itself takes the room left over, like the blend select does. */
.paint-asset-row-line .token-link {
  flex: 1 1 0;
  min-width: 0;
}

/* Layout presets ("Layouts" assets section): built-in one-click chips + saved-preset rows. */
.layout-preset-builtins {
  display: flex;
  flex-wrap: wrap;
  column-gap: 5px;
  row-gap: var(--list-row-gap);
  padding: var(--list-row-pad) var(--panel-right-gutter, 12px) var(--list-row-pad) var(--panel-indent);
}
.layout-preset-chip {
  padding: 3px 9px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background: var(--button-bg);
  border: var(--editor-control-border);
  border-radius: 10px;
  cursor: pointer;
  white-space: nowrap;
}
.layout-preset-chip:hover {
  border-color: var(--editor-selected-blue);
}
/* The built-in preset buttons live in the right-panel Layout section, indented to level 1. */
.layout-presets-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: 0 var(--panel-right-gutter, 12px) 0 var(--panel-indent);
}
/* The button matching the frame's current layout is highlighted. */
.layout-preset-chip.active {
  border-color: var(--editor-selected-blue);
  color: var(--editor-selected-blue);
  font-weight: 600;
}
/* Saved layout preset rows: name (1fr), then the delete in an implicit track — the minus
   sits at the row end like the colour/text-style rows, and the list's right gap lines the
   "−" up with the others and with every section's "+". */
.layout-presets-ul {
  margin-right: 10px;
}
.templates-panel-row.layout-preset-row {
  grid-template-columns: 1fr auto;
}
/* Normal-styled (bordered) rename box, filling its grid cell. */
.layout-preset-name {
  min-width: 0;
  width: 100%;
  box-sizing: border-box;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background: var(--editor-input-bg, var(--button-bg));
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  padding: 2px 6px;
}
.layout-preset-apply {
  background: none;
  border: none;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 4px 0 0;
  opacity: 0.7;
}
.layout-preset-apply:hover {
  opacity: 1;
}
/* Mask-mode picker (alpha / luminance / clip) shown for a selected mask group.
   Height/font/border come from the shared .details-scroll select rule. */
.mask-mode-row {
  margin-left: var(--panel-indent);
}
.mask-mode-select {
  width: 110px;
}
.blend-mode-row {
  margin-left: var(--panel-indent);
}
.blend-mode-select {
  width: 110px;
}

/* Auto-layout (stacks) panel section. Control height/font/border come from the
   shared .details-scroll select rule; these just lay the rows out. */
.layout-div {
  /* The `.font-div` rows inside ALREADY carry the level-1 indent (margin-left:
     --panel-indent), so this wrapper must NOT add another (that made it level-2 / too deep).
     Only add the right gutter so the flexed controls stop short of the panel edge. */
  margin-right: var(--panel-right-gutter, 12px);
}
.layout-row {
  display: flex;
  align-items: center;
  gap: 5px;
  /* No extra row margin — the standard .font-div row (30px, --details-row-height) already
     spaces itself; an added margin made the section looser than every other panel section. */
}
.layout-row label {
  flex: 0 0 76px;               /* fixed label column so every select/input lines up + equal width */
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
/* The padding row's "Padding" label spans its own line, so it opts out of the fixed column. */
.layout-pad-row label {
  flex-basis: 100%;
}
.layout-select {
  flex: 1 1 0;
  min-width: 0;
}
/* Flex the padding/gap boxes so a row of four shares the width instead of overflowing. */
.layout-input {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  padding: 2px 4px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg, transparent);
  text-align: right;
}

/* Padding row: 4 inputs (Top, Right, Bottom, Left). Put the label on its own line and let the
   inputs WRAP so each keeps enough width to show its value/formula instead of being squeezed
   onto one cramped line — 4-across when wide, 2×2 when the panel is narrow. */
.layout-pad-row {
  flex-wrap: wrap;
  height: auto;                 /* override .font-div's fixed height so wrapped inputs show */
  row-gap: 4px;
}
.layout-pad-row label {
  flex: 0 0 100%;               /* "Padding" on its own line; the 4 fields form a grid below */
  margin: 0;                    /* the row-gap spaces the label from the inputs — no extra */
  line-height: 1.3;
}
/* Header line = the "Padding" label + its token-link, spanning the full row width so the
   four padding inputs still wrap 4-across BELOW it (the link doesn't steal a field's slot). */
.layout-pad-header {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  gap: 6px;
}
.layout-pad-row .layout-pad-header label {
  flex: 0 1 auto;               /* not the full-width label rule — sits inline with the link */
}
/* Standard checkbox row (e.g. Wrap): box + label inline. */
.layout-checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}
.layout-checkbox input {
  margin: 0;
}
/* Each padding field = a side icon (top/right/bottom/left) + its input. Real data width →
   4-across when wide, 2×2 when narrow. */
.layout-pad-field {
  display: flex;
  align-items: center;
  gap: 3px;
  flex: 1 1 0;
  min-width: 62px;
}
.layout-pad-field .layout-input {
  flex: 1 1 auto;
  min-width: 0;
}
.layout-pad-glyph {
  flex: 0 0 auto;
  color: var(--editor-muted-text-color);   /* the box + edge use currentColor — themes itself */
}

/* Control inspector (Design panel): one label + control per row, at the standard
   panel control font size, with the normal inline checkbox format for booleans. */
.control-prop-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  /* Right gutter keeps flex-filled selects/inputs off the panel's right edge,
     matching the standard panel rows. */
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
}
.control-prop-row > .control-prop-label {
  flex: 0 0 76px;               /* fixed label column so every control lines up */
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.control-prop-row > .layout-select,
.control-prop-row > .control-prop-input {
  flex: 1 1 0;
  min-width: 0;
}
/* The one-line explanation under the Look switcher. A NOTE, not a tooltip: it says what the
   panel is doing by NOT showing things, which a control the user can't see can't explain. */
.control-prop-note {
  margin: 0 var(--panel-right-gutter) var(--details-row-pad) var(--panel-indent);
  font-size: calc(var(--editor-control-font-size) - 1px);
  color: var(--editor-muted-text-color);
  line-height: 1.35;
}
.control-prop-input {
  min-width: 0;
  padding: 1px 4px;
  color: var(--editor-text-color);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  outline-color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
  box-sizing: border-box;
}
/* Normal checkbox format: box then label, inline (matches .layout-checkbox). */
.control-prop-checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  margin-left: var(--panel-indent);
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
.control-prop-checkbox input {
  margin: 0;
}
/* A SUB-GROUP inside the Control section — a folding heading and its rows, the same shape the
   Sheet section's own sub-groups take. Every field the section has grown lives in one of these
   rather than at the section's top level. */
.control-sub-header {
  display: flex; align-items: center; gap: 2px;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.control-subtriangle {
  padding: 3px var(--panel-triangle-pad-right) 3px var(--panel-triangle-pad-left);
  opacity: var(--editor-icon-muted-opacity);
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  vertical-align: middle;
  cursor: pointer;
  box-sizing: content-box;
}
[data-theme="dark"] .control-subtriangle { filter: invert(1) brightness(1.2); }
.control-sub-heading {
  margin: 0; font-size: var(--editor-control-font-size); font-weight: 600; opacity: 0.85;
}
/* A sub-group's rows sit one level in from the section's own — see the panel indentation rule
   (level N content = N × --panel-indent). */
.control-sub-body > .control-prop-row { margin-left: var(--panel-level-2); }
.control-sub-body > .control-prop-checkbox { margin-left: var(--panel-level-2); }
/* The label-colour well: a bordered chip showing the colour, with the native picker filling it
   invisibly — the same shape the sheet's own colour buttons take. */
.control-prop-well {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 22px; padding: 0;
  border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm);
  cursor: pointer;
}
.control-prop-well input[type="color"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; border: none; padding: 0; cursor: inherit;
}
.control-prop-swatch {
  position: absolute; inset: 2px; border-radius: 1px;
  box-shadow: inset 0 0 0 1px var(--editor-border-color);
}
.control-prop-reset { flex: 0 0 auto; }

/* Data tab (left panel, Data mode): a tree of data stores + their fields. */
.data-tree { padding: 4px 0; }
.data-tree-ul, .data-tree-fields { list-style: none; margin: 0; padding: 0; }
.data-tree-store {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--list-row-pad) 10px var(--list-row-pad) 8px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  cursor: pointer;
}
.data-tree-store:hover { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.12)); }
.data-tree-store.selected { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2)); }
.data-tree-triangle {
  flex: 0 0 auto;
  width: 11px;
  height: 11px;
  opacity: var(--editor-icon-muted-opacity, 0.7);
  cursor: pointer;
}
[data-theme="dark"] .data-tree-triangle { filter: invert(1); }
/* Filled diamond glyph in the data accent — the data-store marker. */
.data-tree-badge {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  transform: rotate(45deg);
  background-color: var(--data-accent);
}
/* Intent action nodes read as actions, not stores — a round badge in the Data
   accent, matching the intent node's canvas title bar. */
.data-tree-badge.intent {
  border-radius: 50%;
  transform: none;
  background-color: var(--data-accent);
}
.data-tree-store-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-tree-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: var(--list-row-pad) 10px var(--list-row-pad) 34px;      /* indented under the store */
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  cursor: pointer;
}
.data-tree-field:hover { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.1)); }
.data-tree-field.selected {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
  box-shadow: inset 2px 0 0 var(--data-accent);
}
.data-tree-field-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-tree-field-type { flex: 0 0 auto; font-size: 11px; color: var(--editor-muted-text-color); }
.data-tree-empty {
  padding: 8px 12px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  line-height: 1.4;
}
/* Data bindings (connections) list, below the stores. */
.data-tree-bindings { margin-top: 6px; border-top: 1px solid var(--editor-border-color); }
.data-tree-bindings-header {
  padding: 5px 10px 3px 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--data-accent);
}
.data-tree-binding {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: var(--list-row-pad) 10px var(--list-row-pad) 12px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  cursor: pointer;
}
.data-tree-binding:hover { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.1)); }
.data-tree-binding.selected {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
  box-shadow: inset 2px 0 0 var(--data-accent);
}
.data-tree-binding-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-tree-binding-arrow { flex: 0 0 auto; color: var(--data-accent); }

/* Diagrams tab (left panel): the flow diagrams TREE — a nested list mirroring the
   Data tree's look (SVG disclosure triangle, indentation, hover/selected states) with
   ＋ (add sub-diagram) / − (delete) icon buttons to the side, in the code accent. */
/* "Generate with AI" — describe a diagram in words, AI builds the flowchart.
   Sits at the top of the Diagrams list; collapsed to a single accent toggle. */
/* No left padding: the ✨ Generate-with-AI toggle is a section header, so its caret must
   line up with the sibling .details-header caret (which sits at the panel's content edge). */
.flow-ai-gen { padding: 4px 10px 8px 0; border-bottom: 1px solid var(--editor-border-color); margin-bottom: 6px; }
.flow-ai-toggle {
  /* Caret + label geometry matches .details-header (caret then title, no gap, no left
     padding) so the two section headers line up exactly. */
  display: flex; align-items: center; gap: 0; width: 100%;
  padding: 4px 0; border: none; background: transparent; cursor: pointer;
  font-size: var(--editor-control-font-size); font-weight: 700; color: var(--code-accent); text-align: left;
}
.flow-ai-caret { width: 14px; display: inline-block; }
.flow-ai-body { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.flow-ai-modes { display: inline-flex; border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm); overflow: hidden; width: fit-content; }
.flow-ai-mode {
  padding: 3px 10px; border: none; background: transparent; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--editor-text-color);
}
.flow-ai-mode + .flow-ai-mode { border-left: 1px solid var(--editor-border-color); }
.flow-ai-mode.active { background: var(--code-accent-btn); color: var(--code-accent-fg); }
.flow-ai-input {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 48px;
  padding: 6px 8px; border: 1px solid var(--editor-control-border, var(--editor-border-color));
  border-radius: var(--corner-radius-sm); background: var(--editor-input-bg);
  color: var(--editor-text-color); font: inherit; font-size: 12.5px; line-height: 1.4;
}
.flow-ai-input:focus { outline: none; border-color: var(--code-accent); }
.flow-ai-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.flow-ai-run {
  display: inline-flex; align-items: center;
  padding: 4px 12px; border: 1px solid var(--code-accent); border-radius: var(--corner-radius-sm);
  background: var(--code-accent-btn); color: var(--code-accent-fg);
  font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.flow-ai-run:disabled { opacity: 0.5; cursor: default; }
/* While generating the button is disabled but stays fully opaque so its spinner
   reads clearly (matches the CodeDesigner / GenerateSection AI buttons). */
.flow-ai-run.working:disabled { opacity: 1; }
.flow-ai-status { font-size: 11.5px; color: var(--editor-muted-text-color); flex: 1 1 auto; min-width: 0; }

.flow-tree-ul { list-style: none; margin: 0; padding: 0; }
.flow-tree-li { margin: 0; }
.flow-tree-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--list-row-pad) 6px var(--list-row-pad) 8px;   /* left padding overridden inline per depth */
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  cursor: pointer;
}
.flow-tree-row:hover { background: color-mix(in srgb, var(--code-accent) 10%, transparent); }
/* THE KEYBOARD CURSOR HAS TO BE VISIBLE. The shared Tree makes these rows reachable by
   arrow key (see shared/tree/TreeView), and a focus you cannot see is the same as no
   keyboard navigation at all. `:focus-visible` rather than `:focus`, so a mouse click
   doesn't leave a ring behind it. */
.flow-tree-row:focus { outline: none; }
.flow-tree-row:focus-visible {
  outline: 2px solid var(--code-accent);
  outline-offset: -2px;
}
.flow-tree-row.selected {
  background: color-mix(in srgb, var(--code-accent) 16%, transparent);
  box-shadow: inset 2px 0 0 var(--code-accent);
}
/* A row lit from the CANVAS side of a two-way hover (a sheet's or a hierarchy's Structure
   tree, a corner list) — outlined the way the layers tree outlines the row of the item under
   the pointer: a 1px dashed line in the editor's hover colour. It was a wash in a hard-coded
   green, which followed no preference and matched no other hover in the panels. An outline,
   not a border, so the row keeps its size, and it sits on top of `selected` rather than
   replacing it — a picked row can still be the one pointed at. */
.flow-tree-row.hovered {
  outline: 1px dashed var(--editor-hover-color);
  outline-offset: -1px;
}
.flow-tree-triangle {
  flex: 0 0 auto;
  width: 11px;
  height: 11px;
  opacity: var(--editor-icon-muted-opacity, 0.7);
  cursor: pointer;
}
[data-theme="dark"] .flow-tree-triangle { filter: invert(1); }
/* Filled diamond in the code accent — the diagram marker (matches the data badge). */
.flow-tree-badge {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  transform: rotate(45deg);
  background-color: var(--code-accent);
}
.flow-tree-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.flow-tree-name.placeholder { color: var(--editor-muted-text-color); font-style: italic; }
/* Kind glyph on a shape (outline) row — muted, fixed width so labels line up. */
.flow-tree-glyph { flex: 0 0 auto; width: 12px; text-align: center; color: var(--editor-muted-text-color); font-size: 11px; }
/* Flow Variables tab: a variable row (name input + type/entity selects + delete). */
.flow-var-row { display: flex; align-items: center; gap: 4px; margin-bottom: 4px; }
.flow-var-name { flex: 1 1 auto; min-width: 40px; box-sizing: border-box; font-size: 12px; padding: 2px 5px; border: var(--editor-control-border); border-radius: var(--corner-radius-sm); }
.flow-var-type, .flow-var-ref { flex: 0 0 auto; max-width: 96px; font-size: 11px; }
.flow-var-add { border: none; background: none; cursor: pointer; color: var(--code-accent); font-size: 14px; line-height: 1; padding: 0; }
.flow-var-del { border: none; background: none; cursor: pointer; color: inherit; opacity: 0.55; font-size: 12px; padding: 0 2px; margin-left: auto; }
.flow-var-del:hover { opacity: 1; color: #c0442e; }
/* Generated-code preview for a flow — a compact read-only monospace block. */
.flow-code-pre {
  margin: 0;
  padding: 6px 8px;
  max-height: 260px;
  overflow: auto;
  font-family: var(--editor-mono-font-family, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11px;
  line-height: 1.45;
  white-space: pre;
  color: var(--editor-text-color);
  background: color-mix(in srgb, var(--code-accent) 7%, transparent);
  border: 1px solid var(--editor-border-color);
  border-radius: 5px;
}
/* The diagram-row kind marker (▭ process / ◇ decision) in the code accent. */
.flow-tree-diagram-glyph { color: var(--code-accent); font-size: 12px; }
/* Drag-drop drop-zone cues for diagram rows: an accent line before/after a sibling,
   a filled highlight when dropping INSIDE (nest as a sub-diagram). */
.flow-tree-row.drop-before { box-shadow: inset 0 2px 0 var(--code-accent); }
.flow-tree-row.drop-after { box-shadow: inset 0 -2px 0 var(--code-accent); }
.flow-tree-row.drop-inside { background: color-mix(in srgb, var(--code-accent) 22%, transparent); outline: 1px dashed var(--code-accent); outline-offset: -1px; }
.flow-tree-row .flow-rename { flex: 1 1 auto; min-width: 0; }

/* THE ANIMATIONS PAGE'S STEP TREE — the shared tree, in that page's own accent.
   Everything on the Animations page is drawn in --prototype-accent, and the shared rows
   are drawn in --code-accent (they came out of the diagram panels). Rather than a second
   set of rules saying the same things in a different colour, the page redefines what the
   accent IS for its own tree: hover, the drop lines and the nest highlight all follow. */
.anim-tree { --code-accent: var(--prototype-accent); }
/* The step's compact sequence label (1, 1a, 1a1) — the same token its bar carries on the
   timeline, which is how a row and a bar are recognisably one step. Wider than the shared
   glyph slot because it grows a character per level of nesting. */
.anim-tree .anim-seq-glyph {
  min-width: 18px;
  width: auto;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--prototype-accent);
}
/* A clickable validation issue in the Checks section (one that points at a shape). */
.flow-check.clickable:hover { background: color-mix(in srgb, var(--code-accent) 10%, transparent); }
/* The ＋ / − actions on the Diagrams section header reuse the standard page-list
   +/- icons (page-add-div / page-delete-div + page-list-plus/minus-icon), so they
   get the app's standard icon size, hover highlight, order, and positioning for
   free (the .details-header > [class*="-add-div"]/[class*="-delete-div"] rule).
   The rightmost icon (the ＋) carries the tree panels' standard 10px right-edge
   gap the SAME way .templates-add-div does — a margin on the icon div, NOT
   padding-right on the header: .details-header is width:100% content-box, so
   header padding-right overflows past the visible edge and the icons still sit
   flush. The margin lives inside the flex content box, so it actually shows. */
.flow-diagrams .page-add-div { margin-right: 10px; }
/* Sequences header (now a .details-header like Flow Diagrams): give its add (＋)
   the same 10px right-edge gap so the two peer headers align. */
.seq-header-add { margin-right: 10px; }
/* Group the two ADD icons (＋Process, ＋Decision) as a pair, and set the Delete
   (−) apart from them — otherwise the delete reads as grouped with the first add.
   Tight gap within the add pair; a wider gap separates the delete; the last add
   keeps the 10px right-edge gap (rule above). */
.flow-diagrams .page-add-div:not(:last-child) { margin-right: 4px; }
.flow-diagrams .page-delete-div { margin-right: 11px; }

/* Empty-state message on the blank Diagrams canvas — theme-adaptive colors with
   real contrast (a deep green on the light canvas; the accent green reads fine on
   the dark canvas), and body copy in the standard text colour, not a faint grey. */
.diagrams-empty-title { color: #0e5f3f; }
.diagrams-empty-body { color: var(--editor-text-color); }
[data-theme="dark"] .diagrams-empty-title { color: var(--code-accent); }

/* Prototype tab (left panel): connections + triggers lists. */
.proto-tree { padding: 4px 0; }
.proto-tree-section { margin-bottom: 2px; }
.proto-tree-ul { list-style: none; margin: 0; padding: 0; }
.proto-tree-header {
  padding: 5px 10px 3px 10px;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--editor-selected-blue);
}
.proto-tree-row {
  display: flex; align-items: center; gap: 5px;
  padding: var(--list-row-pad) 10px var(--list-row-pad) 12px;
  font-size: var(--editor-control-font-size); color: var(--editor-text-color); cursor: pointer;
}
.proto-tree-row:hover { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.1)); }
.proto-tree-row.selected {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
  box-shadow: inset 2px 0 0 var(--editor-selected-blue);
}
.proto-tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* On a trigger row the name GROWS to fill the row, so the count badge lands in a
   fixed column just left of the eye (aligned across rows) instead of floating
   right after the name — its old margin-left:auto fought the eye's own for the
   free space, so the badge's spot depended on the name's length. */
.proto-tree-name-fill { flex: 1 1 auto; min-width: 0; }
.proto-tree-arrow { flex: 0 0 auto; color: var(--editor-selected-blue); }
.proto-tree-count {
  flex: 0 0 auto;
  /* A centred pill: inline-flex + fixed height so the number sits on the row's
     midline (not floating high) and single digits read as a neat circle. */
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 16px; box-sizing: border-box;
  font-size: 11px; line-height: 1; padding: 0 5px; border-radius: 8px;
  color: var(--editor-muted-text-color); background: var(--tree-selection-bg, rgba(127, 127, 127, 0.15));
}
.proto-tree-empty {
  padding: 8px 12px; font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color); line-height: 1.4;
}

/* Customize → Theme → Data/Code accent-colour picker row. */
.accent-color-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.accent-color-row input[type="color"] {
  width: 44px; height: 30px; padding: 0; cursor: pointer; background: none;
  border: 1px solid var(--editor-control-border-color); border-radius: 6px;
}
.accent-color-row input[type="text"] {
  width: 150px; padding: 6px 8px; font-size: 13px; color: var(--editor-text-color);
  border: 1px solid var(--editor-control-border-color); border-radius: 6px;
  background: var(--editor-input-bg, #fff);
}
/* Read-only value (e.g. the store a button's connection points to). */
.control-prop-static {
  flex: 1 1 0;
  min-width: 0;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
}
/* Guidance line (draw-a-connection prompt, row-verb note). */
.control-prop-hint {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
  line-height: 1.4;
  color: var(--editor-muted-text-color);
}

/* Master name in the library is clickable to open the component for editing. */
.templates-name-edit {
  cursor: pointer;
}
.templates-name-edit:hover {
  text-decoration: underline;
}

/* On-canvas banner shown while editing a component master. Spans the canvas
   region (left/right set inline); sits just below the top toolbar. */
.template-edit-banner {
  position: fixed;
  top: var(--top-toolbar-height);
  z-index: 35;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 12px;
  background: var(--accent-color, #7a5cff);
  color: #ffffff;
  font-size: var(--editor-control-font-size);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

.template-edit-banner-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.template-edit-banner-actions {
  display: flex;
  gap: 8px;
  flex: 0 0 auto;
}

.template-edit-banner-btn {
  font-size: var(--editor-control-font-size);
  padding: 2px 12px;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 4px;
  background: transparent;
  color: #ffffff;
  cursor: pointer;
}

.template-edit-banner-btn.done {
  background: #ffffff;
  color: var(--accent-color, #7a5cff);
  border-color: #ffffff;
  font-weight: 600;
}

/* Subtle bottom-center hint for in-place editing (pen / line). Spans the canvas
   region (left/right inline); the wrap is click-through so it never blocks the
   canvas. */
.edit-hint-wrap {
  position: fixed;
  bottom: calc(var(--status-bar-height, 0px) + 12px);
  z-index: 35;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

.edit-hint-pill {
  background: rgba(20, 20, 24, 0.85);
  color: #ffffff;
  font-size: var(--editor-control-font-size);
  padding: 4px 12px;
  border-radius: 14px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  user-select: none;
}

.edit-hint-key {
  display: inline-block;
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: 3px;
  padding: 0 5px;
  margin: 0 1px;
  font-size: 0.9em;
}

/* Master-edit panel: the component name row. */
.component-name-row {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  align-items: center;
  margin: 0 12px 0 var(--panel-indent);
}
.component-name-label {
  font-size: var(--editor-control-font-size);
  opacity: 0.7;
}

.component-bind-heading {
  margin: 0 0 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
  opacity: 0.7;
}

/* Instance panel, Variants group: one row per appearance axis — its name and the
   pick-one row of its values. Level-2 content, so two panel indents in (see the
   indentation formula). */
.template-variant-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  margin: 0 var(--panel-right-gutter) 0 calc(var(--panel-indent) * 2);
}
.template-variant-name {
  flex: 0 0 auto;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
  opacity: 0.8;
}
.template-variant-choice {
  flex: 1 1 auto;
  min-width: 0;
}
/* Which combination is showing, and whether it has a look of its own. */
.template-variant-current {
  margin: 2px var(--panel-right-gutter) 0 calc(var(--panel-indent) * 2);
  font-size: var(--editor-control-font-size);
  opacity: 0.6;
}
.template-variant-edit-toggle {
  margin: 0 var(--panel-right-gutter) 0 calc(var(--panel-indent) * 2);
}
/* The axes themselves — shown only while editing properties. */
.template-variant-editor {
  margin: 2px var(--panel-right-gutter) 0 calc(var(--panel-indent) * 2);
}
.template-variant-prop-edit {
  margin: 0 0 4px;
}
.template-variant-prop-head,
.template-variant-value-edit {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
/* A value sits one step inside the property it belongs to. */
.template-variant-value-edit {
  padding-left: 14px;
}

/* Instance panel: a parameter name, its binding value, and (when overridden)
   a reset control. */
.cparam-bind-row {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  column-gap: 6px;
  align-items: center;
  margin: 0 12px 0 var(--panel-indent);
}
.cparam-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--editor-control-font-size);
}

/* Overridden parameter: accent name + a leading dot, with a reset button. */
.cparam-overridden {
  color: var(--accent-color, #7a5cff);
}
[data-theme="dark"] .cparam-overridden {
  color: #b9a7ff;
}
/* General override indicator for a design-panel control (instance differs
   from master). Clickable: reverts that property to the component default. */
/* Override dialog tabs — shown only when BOTH an instance override and a
   per-device change exist, so there's actually a choice to make. */
.override-tabs {
  display: flex;
  gap: 2px;
  margin: 0 0 10px;
  border-bottom: 1px solid var(--editor-border-color);
}

.override-tab {
  border: none;
  background: transparent;
  color: var(--editor-muted-text-color);
  font-size: 12.5px;
  font-weight: 600;
  padding: 6px 12px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.override-tab:hover:not(.is-current) {
  color: var(--editor-text-color);
}

.override-tab.is-current {
  color: var(--accent-color, #7a5cff);
  border-bottom-color: var(--accent-color, #7a5cff);
}

.override-badge {
  display: inline-block;
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--accent-color, #7a5cff);
  vertical-align: middle;
  margin: 0 4px;
  flex: 0 0 auto;
  cursor: pointer;
}
[data-theme="dark"] .override-badge {
  background: #b9a7ff;
}
.override-badge:hover {
  outline: 2px solid color-mix(in srgb, var(--accent-color, #7a5cff) 40%, transparent);
}

/* Override-report dialog: which section properties differ from the template,
   with Reset / Cancel. Matches the app's modal house style. */
.override-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 18, 24, 0.55);
  padding: 24px;
}
.override-modal {
  width: 100%;
  max-width: 480px;
  background: var(--editor-panel-background-color, #fff);
  color: var(--editor-text-color, #1a1a1f);
  border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.15));
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  padding: 22px 24px;
  box-sizing: border-box;
}
.override-title {
  margin: 0 0 4px;
  font-size: 1.1rem;
  font-weight: 700;
}
.override-subtitle {
  margin: 0 0 14px;
  font-size: 0.82rem;
  opacity: 0.7;
}
/* The report is an indented tree mirroring the item hierarchy: each component's
   name, its overridden properties, then its overridden descendants one level
   deeper. No dividers — indentation carries the structure. */
.override-tree {
  list-style: none;
  margin: 0;
  padding: 0;
}
.override-tree-root {
  margin: 0 0 18px;
  padding: 10px 0;
  max-height: 320px;
  overflow-y: auto;
  border-top: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.12));
  border-bottom: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.12));
}
/* Each nested level steps in; the root list itself is flush. */
.override-tree-node .override-tree {
  padding-left: 14px;
}
.override-tree-node {
  margin-top: 6px;
}
.override-tree-root > .override-tree-node:first-child {
  margin-top: 0;
}
.override-node-name {
  font-weight: 700;
  font-size: 0.85rem;
  margin-bottom: 2px;
}
.override-diff-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0 2px 14px;
  font-size: 0.85rem;
}
.override-diff-label {
  font-weight: 500;
  min-width: 0;
  opacity: 0.85;
}
.override-diff-values {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  text-align: right;
  white-space: nowrap;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.override-diff-current {
  font-weight: 600;
}
.override-arrow {
  opacity: 0.5;
}
.override-diff-master {
  opacity: 0.6;
}
.override-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.override-cancel,
.override-reset {
  padding: 0.45rem 0.85rem;
  border-radius: 8px;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--panel-border-color, rgba(0, 0, 0, 0.18));
}
.override-cancel {
  background: transparent;
  color: inherit;
}
.override-reset {
  background: var(--accent-color, #7a5cff);
  border-color: transparent;
  color: #fff;
}
.override-cancel:hover { filter: brightness(0.97); }
.override-reset:hover { filter: brightness(1.06); }

/* Override dot on a design-panel dimension field (W/H/angle on an instance).
   Clickable: reverts that field to the component default. */
.vi-dim-override-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--accent-color, #7a5cff);
  vertical-align: middle;
  margin-right: 3px;
  flex: 0 0 auto;
  cursor: pointer;
}
.vi-dim-override-dot:hover {
  outline: 2px solid color-mix(in srgb, var(--accent-color, #7a5cff) 40%, transparent);
}
[data-theme="dark"] .vi-dim-override-dot {
  background: #b9a7ff;
}

.cparam-override-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 5px;
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
}
.cparam-reset {
  border: none;
  background: transparent;
  color: inherit;
  opacity: 0.7;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0 4px;
}
.cparam-reset:hover {
  opacity: 1;
}

/* Tree rows: align the disclosure triangle, type icon, badge, and name on
   one centered line. */
.item-tree-row {
  display: flex;
  align-items: center;
  /* Vertical padding is set inline (paddingV in VisualItemTree / CodeFileTree)
     because it also compensates for the 1px hover border to avoid row jitter. */
}

/* A HIDDEN layer / item row is dimmed (its eye still reads the closed state and stays
   clickable at reduced alpha), matching the Layers panel so the tree stays in sync with it. */
.item-tree-row-hidden {
  opacity: 0.55;
}

/* Disclosure triangle — override the generic 6px `.item-tree-item img` size
   (which has higher specificity than a single class) so groups get a
   properly sized, clickable triangle. Use a FIXED width (not `auto`) so the
   non-square collapsed/expanded SVGs render at the same 12×12 box the design-
   panel section triangles use, instead of the expanded glyph spreading wider. */
.item-tree-item .tree-triangle-img {
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  box-sizing: content-box;
  padding: 0 4px 0 0;
  cursor: pointer;
}

/* Per-item type icon (rectangle, text, group, …) in front of each row.
   Opacity matches the toolbar's .tool-icon (0.9) so the dark-mode invert
   reads with the same contrast; 16px so the icons with internal padding
   don't look undersized at tree scale. */
.item-tree-item .tree-type-icon {
  height: 16px;
  width: 16px;
  padding: 0 5px 0 0;
  flex: 0 0 auto;
  opacity: 0.9;
}

/* Text items use a crisp glyph instead of the tiny "T" in text.svg (which
   floats in a large viewBox). Sized to the icon column; inherits the themed
   text color, so it stays high-contrast in both light and dark. */
.item-tree-item .tree-type-glyph {
  width: 16px;
  padding-right: 5px;
  flex: 0 0 auto;
  font-weight: 700;
  font-size: 13px;
  line-height: 1;
  text-align: center;
  opacity: 0.9;
  user-select: none;
}



/* ── UML Sequence diagrams (Sequences tab, Data mode) ─────────────────────── */
.seq-panel { padding: 6px 8px; display: flex; flex-direction: column; gap: 4px; }
/* The Generate-with-AI block already carries its own border + padding, so the panel's
   flex gap on top of its margin left too much air both ABOVE it (below the Sequences
   header) and below it (before the list). Drop the margin and keep only the small panel
   gap. */
.seq-panel .flow-ai-gen { margin-bottom: 0; }
.seq-empty { font-size: 12px; color: var(--editor-muted-text-color); padding: 2px; }
.seq-new-btn { align-self: flex-start; padding: 4px 10px; border: 1px solid var(--data-accent); color: var(--data-accent); background: transparent; border-radius: var(--corner-radius-sm); cursor: pointer; font-size: 12.5px; font-weight: 600; }
.seq-picker { display: flex; align-items: center; gap: 6px; }
.seq-picker select { flex: 1 1 auto; min-width: 0; }
.seq-name { width: 100%; box-sizing: border-box; font-size: 13.5px; font-weight: 600; padding: 4px 6px; border: var(--editor-control-border); border-radius: var(--corner-radius-sm); background: var(--editor-input-bg); color: var(--editor-text-color); }
.seq-canvas { overflow: auto; border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm); background: var(--editor-input-bg); max-height: 320px; padding: 4px; }
.seq-svg { display: block; max-width: none; }
.seq-section { display: flex; flex-direction: column; gap: 3px; }
.seq-section-head { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; color: var(--code-accent); text-transform: uppercase; letter-spacing: .04em; }
/* Right-aligned action cluster in a section header — holds the delete (−) then add (＋)
   icon-buttons in that order, so the minus always sits LEFT of the plus. */
.seq-head-actions { margin-left: auto; display: flex; align-items: center; }
.seq-row { display: flex; align-items: center; gap: 4px; }
.seq-row select { flex: 0 0 auto; max-width: 92px; font-size: 11px; }
.seq-inline-name { flex: 1 1 auto; min-width: 40px; box-sizing: border-box; font-size: 12px; padding: 2px 5px; border: var(--editor-control-border); border-radius: var(--corner-radius-sm); background: var(--editor-input-bg); color: var(--editor-text-color); }
.seq-arrow { flex: 0 0 auto; color: var(--editor-muted-text-color); font-size: 12px; }
.seq-mini { flex: 0 0 auto; width: 18px; height: 18px; padding: 0; border: 1px solid var(--editor-border-color); background: transparent; border-radius: 4px; cursor: pointer; font-size: 10px; color: var(--editor-text-color); line-height: 1; }
.seq-mini:disabled { opacity: 0.35; cursor: default; }
.seq-hint { font-size: 11px; color: var(--editor-muted-text-color); }

/* Sequence inspector — the RIGHT-panel section (a standard .flow-node-section, so it
   needs its own padding + column layout since that class carries none). */
.seq-inspector { display: flex; flex-direction: column; gap: 8px; padding: 12px 12px 14px; }
/* Lifeline rows stay on ONE line (name + kind + −); cap the kind select so the row
   fits the narrow panel without wrapping. */
.seq-inspector .seq-row select { max-width: 84px; }

/* Messages are a compact master-detail: a one-line-per-message LIST (summary + drag
   handle + delete) and a detail editor below for the SELECTED message. */
.seq-msg-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.seq-msg-row { display: flex; align-items: center; gap: 6px; padding: 3px 6px 3px 10px; cursor: pointer; font-size: 12px; color: var(--editor-text-color); border-radius: 3px; }
.seq-msg-row:hover { background: color-mix(in srgb, var(--code-accent) 10%, transparent); }
.seq-msg-row.selected { background: color-mix(in srgb, var(--code-accent) 16%, transparent); box-shadow: inset 2px 0 0 var(--code-accent); }
.seq-msg-idx { flex: 0 0 auto; color: var(--editor-muted-text-color); font-variant-numeric: tabular-nums; }
.seq-msg-summary { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seq-ll-kind { color: var(--editor-muted-text-color); font-size: 11px; }
.seq-msg-detail { display: flex; flex-direction: column; gap: 5px; margin-top: 6px; padding: 8px; border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm); background: color-mix(in srgb, var(--code-accent) 5%, transparent); }
.seq-detail-row { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.seq-detail-label { flex: 0 0 auto; font-size: 11px; color: var(--editor-muted-text-color); min-width: 28px; }
/* A second label in a detail row (e.g. "To" after From's control) starts a new
   field-group — give it extra left space so it doesn't read as part of the one before. */
.seq-detail-row .seq-detail-label ~ .seq-detail-label { margin-left: 12px; }
/* Detail-row selects fill to the panel's right edge (like the label/name inputs), so
   a lone From/To/Kind control hugs the right the same as everything else. */
.seq-detail-row select { flex: 1 1 60px; min-width: 0; }
.seq-detail-row .seq-inline-name { flex: 1 1 auto; }
/* …except a select that SHARES its row with a guard input (an alt operand's "at"
   picker) stays compact so it doesn't crowd the guard. */
.seq-detail-row .seq-op-select { flex: 0 1 auto; max-width: 96px; }
/* Line the row action icons up in one right-hand column: the shared .page-delete-div
   carries a 5px right margin that pushed the − left of the section-head ＋; zero it
   (and the add) inside the inspector so − and ＋ share the same right edge — even a
   lone icon. */
.seq-inspector .page-delete-div,
.seq-inspector .page-add-div { margin: 0; }
/* Drag-to-reorder lifeline rows. */
.seq-drag-row { cursor: default; }
.seq-drag-handle { flex: 0 0 auto; width: 12px; text-align: center; color: var(--editor-muted-text-color); cursor: grab; font-size: 12px; user-select: none; }
.seq-drag-row.drop-before { box-shadow: inset 0 2px 0 var(--code-accent); }
.seq-drag-row.drop-after { box-shadow: inset 0 -2px 0 var(--code-accent); }

/* ── Pen edit mode: unified Appearance master/detail list (reuses .shadow-tree) ── */
/* The − / + ride the standard header slot (order 2, pushed right by .details-header::after);
   an 8px right gutter keeps them off the panel edge (matching the pen sub-mode buttons).
   box-sizing: border-box is required — .details-header is width:100%, so a content-box
   padding-right would push 100%+8px wide (the padding hanging off-screen) instead of
   insetting the buttons. */
.pen-appearance .details-header {
  box-sizing: border-box;
  padding-right: 8px;
}
/* The ring rows line up with the "Rings" header title (one --panel-indent from the left):
   the shared .shadow-tree adds another level's worth on top, so pull the base back by the
   per-row level-0 pad (treeRowPad(0) = 8px, the shared tree's origin) to cancel it.
   Deeper levels still nest. */
.pen-appearance-tree {
  margin-left: calc(var(--panel-indent) - 8px);
}
/* A ring / All-rings group heading — bold, standard selection when picked. */
.pen-appearance-group .shadow-tree-label { font-weight: 700; }
/* No separation line between the list and the detail editor; hide the reused
   section's own header/separator (the tree row is the header now). */
.pen-appearance-detail {
  margin-top: var(--details-row-pad);
}
.pen-appearance-detail .details-div-group {
  border: none;
}
.pen-appearance-detail .details-header {
  display: none;
}

/* ── Database console (Project tab) ────────────────────────────────────────
   Reuses the Figma import section's field / note / button classes so the two
   read as one panel rather than two dialects. What's here is only what a
   console needs beyond that: a wrapping button row, the SQL box, and a result
   table that scrolls in its own box instead of widening the panel. */
.db-console .db-actions {
  flex-wrap: wrap;
  gap: 6px;
}

/* Rows-per-store: a small number beside the Seed button. flex-basis pins it,
   since `.details-scroll input { width: 100% }` would otherwise stretch it
   across the row (see the Miter Limit box for the same trap). */
.db-rows-input {
  flex: 0 0 44px;
  box-sizing: border-box;
  text-align: center;
}

/* The destructive one. Named by colour, not by position — it sits in a row of
   ordinary buttons and must not read as one of them. */
.figma-btn.db-danger {
  color: var(--error-color, #c62828);
  border-color: var(--error-color, #c62828);
}

.db-query {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}

.db-sql-input {
  box-sizing: border-box;
  width: 100%;
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--editor-control-font-size);
  line-height: 1.4;
  color: var(--editor-text-color);
  background: var(--editor-input-bg);
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  padding: 4px 6px;
}

.db-writes-label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
  white-space: nowrap;
}

/* Results scroll INSIDE their own box: a wide table must never turn the panel
   into a horizontal scroller. */
.db-result {
  max-height: 260px;
  overflow: auto;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  margin-top: var(--details-row-pad);
}

.db-result-table {
  border-collapse: collapse;
  font-size: var(--editor-control-font-size);
  white-space: nowrap;
}

.db-result-table th,
.db-result-table td {
  text-align: left;
  padding: 3px 8px;
  border-bottom: 1px solid var(--panel-border-color);
}

.db-result-table th {
  position: sticky;
  top: 0;
  background: var(--editor-panel-background-color);
  font-weight: 600;
}

/* Data tab: the store tree, then the database console beneath it. The tree keeps a
   bounded area of its own rather than growing without limit and pushing the console
   off the bottom; below that minimum the whole tab scrolls. */
.data-tab > .item-tree-scroll {
  flex: 0 1 auto;
  min-height: 120px;
  max-height: 45%;
}

/* ── SQL console (full page) ───────────────────────────────────────────────
   The editor is a transparent-text textarea sitting exactly on top of a div
   that draws the same text plus the ghosted suggestion. They must agree on
   every metric that affects where a glyph lands, or the ghost drifts off the
   caret — so the shared values live in one place and both sides read them. */
.db-console-page {
  --db-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --db-sql-size: 13.5px;
  --db-sql-line: 1.5;
  --db-sql-pad: 10px;
}

.db-console-page .db-page-sql {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: var(--db-sql-pad);
  border: 1px solid var(--panel-border-color);
  border-radius: var(--corner-radius-sm);
  font-family: var(--db-mono);
  font-size: var(--db-sql-size);
  line-height: var(--db-sql-line);
  letter-spacing: normal;
  tab-size: 4;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  overflow: auto;
}

.db-console-page .db-page-sql {
  background: transparent;
  color: var(--editor-text-color);
  resize: none;
  z-index: 1;
}

/* THE DIALECT CHECK — green, amber or red as you type. It stands where the completion
   buttons used to (see DatabaseConsolePage): beside the statement by default, so it can say
   a whole sentence without taking height from the results. */
.db-check {
  flex: 0 0 auto;
  padding: 6px 8px;
  border: 1px solid var(--panel-border-color);
  border-left-width: 3px;
  border-radius: var(--corner-radius-sm);
  font-size: 12px;
  line-height: 1.45;
  overflow-y: auto;
  max-height: 40vh;
}
.db-check-head { display: flex; align-items: center; gap: 6px; font-weight: 600; }
.db-check-dot {
  width: 8px; height: 8px; border-radius: 50%;
  flex: 0 0 auto;
  background: var(--editor-muted-text-color);
}
/* The three states. The border-left carries the colour as well as the dot, so the state is
   legible without relying on colour alone — and the words say it too. */
.db-check-ok { border-left-color: #2f9e5e; }
.db-check-ok .db-check-dot { background: #2f9e5e; }
.db-check-warning { border-left-color: #c8901f; }
.db-check-warning .db-check-dot { background: #c8901f; }
.db-check-error { border-left-color: #c9483f; }
.db-check-error .db-check-dot { background: #c9483f; }

.db-check-note { margin-top: 5px; color: var(--editor-text-color); }
.db-check-note-ok { color: var(--editor-muted-text-color); }
.db-check-note-warning::before { content: "▲ "; color: #c8901f; }
.db-check-note-error::before { content: "✕ "; color: #c9483f; }
/* What to write instead, on its own line — the fix is the useful half of a refusal. */
.db-check-fix {
  display: block;
  font-style: normal;
  color: var(--editor-muted-text-color);
}
.db-check-fix::before { content: "→ "; }

/* BESIDE the statement (the default): a narrow column that doesn't take height from the
   results. Under it, when a wide statement wants the width, it's a full-width strip. */
.db-check-side {
  order: -1;
  flex: 0 0 250px;
  min-width: 150px;
  max-width: 40%;
  resize: horizontal;
  overflow: auto;
}


.db-page-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.db-page-result {
  flex: 1 1 auto;
  min-width: 0;
  overflow: auto;
  border: 1px solid var(--panel-border-color);
  border-radius: var(--corner-radius-sm);
}

.db-page-log {
  flex: 0 0 300px;
  overflow: auto;
  border: 1px solid var(--panel-border-color);
  border-radius: var(--corner-radius-sm);
  padding: 6px;
}

.db-log-entry {
  padding: 6px 7px;
  border-bottom: 1px solid var(--panel-border-color);
  font-size: 13px;
  line-height: 1.45;
}

.db-log-bad .db-log-msg {
  color: var(--error-color, #c62828);
}

.db-log-msg {
  font-weight: 600;
  font-size: 13.5px;
}

.db-log-sql {
  font-family: var(--db-mono, monospace);
  font-size: 12px;
  color: var(--editor-muted-text-color);
  overflow-wrap: anywhere;
}

.db-log-ms {
  font-size: 11.5px;
  color: var(--editor-muted-text-color);
}

.db-empty {
  padding: 14px;
  font-size: 13px;
  color: var(--editor-muted-text-color);
}

/* SQL console: the operations in the header, and the schema column beside the results. */
.db-ops {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: 16px;
}

/* The schema reference: a tree with COLUMNS, a detail block for what's selected, and a
   legend. Wider than it was — four facts per row don't fit in a sidebar meant for names —
   and draggable when a long reference needs more. */
.db-page-schema {
  flex: 0 0 380px;
  min-width: 240px;
  max-width: 60%;
  resize: horizontal;
  overflow: auto;
  border: 1px solid var(--panel-border-color);
  border-radius: var(--corner-radius-sm);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.db-schema-table { margin-bottom: 8px; }

.db-schema-name,
.db-schema-col {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  padding: 1px 4px;
  font-family: var(--db-mono, monospace);
  font-size: 11.5px;
  cursor: pointer;
  border-radius: 3px;
}

.db-schema-name {
  color: var(--data-accent);
  font-weight: 700;
}

.db-schema-col {
  color: var(--editor-muted-text-color);
  padding-left: 12px;
}

.db-schema-name:hover,
.db-schema-col:hover { background: var(--editor-input-bg); }

/* The source choice: three radio chips, one of them lit.
   Level 1 — like every other row in this section, and like the panel's own rule that a
   section's content sits one --panel-indent in from the left, never flush against it. */
.db-source-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: var(--details-row-pad) 6px 0 var(--panel-indent);
}

.db-source {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  font-size: 11.5px;
  border: 1px solid var(--panel-border-color);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}

.db-source-on {
  border-color: var(--data-accent);
  color: var(--data-accent);
}

/* SQL console: ask-for-SQL row, and the saved-query strip. */
.db-ask-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.db-ask-input {
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  padding: 4px 8px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background: var(--editor-input-bg);
  border: 1px solid var(--panel-border-color);
  border-radius: var(--corner-radius-sm);
}

.db-saved {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
}

.db-saved-item {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--panel-border-color);
  border-radius: 999px;
  overflow: hidden;
}

.db-saved-run,
.db-saved-x {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-size: 11.5px;
  padding: 2px 8px;
  color: var(--editor-text-color);
}

.db-saved-x {
  padding-left: 2px;
  padding-right: 6px;
  color: var(--editor-muted-text-color);
}

.db-saved-run:hover { background: var(--editor-input-bg); }
.db-saved-x:hover { color: var(--error-color, #c62828); }

/* Schema reference: a column line carries its type and its constraints, because that's
   what you open a reference for. Badges stay quiet until you look at them. */
.db-schema-col {
  display: flex;
  align-items: baseline;
  gap: 5px;
  white-space: nowrap;
}

.db-col-name { color: var(--editor-text-color); }

.db-schema-type,
.db-schema-count {
  font-style: normal;
  font-size: 10.5px;
  color: var(--editor-muted-text-color);
}

.db-schema-count { margin-left: 6px; }

.db-badge {
  font-style: normal;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 0 3px;
  border-radius: 3px;
  border: 1px solid currentColor;
  color: var(--editor-muted-text-color);
  opacity: 0.85;
}

.db-badge-pk { color: var(--data-accent); }
.db-badge-u { color: var(--editor-selected-blue); }

/* A reference names where it points, which is the useful half. */
.db-badge-ref {
  border: 0;
  padding: 0;
  font-weight: 600;
  color: var(--code-accent);
}

/* Connected: the DATABASE accent, not green — green reads as the code theme, and this is
   the data layer. Cleared the moment any connection detail changes, so it can't lie. */
.figma-btn.db-connected {
  color: var(--data-accent);
  border-color: var(--data-accent);
  font-weight: 700;
}

/* ── Schema tree ───────────────────────────────────────────────────────────
   One grid for the head row and every tree row, so the columns line up
   without either knowing the other's widths. */
.db-tree-head,
.db-tree-row {
  display: grid;
  /* The NAME gets the room: it's the longest text here and the one you're reading. The flag
     columns are exactly as wide as "PK" and "NN"; References takes what's left and gives it
     back when there isn't any. */
  grid-template-columns: minmax(130px, 2.2fr) 68px 26px 26px minmax(0, 1fr);
  gap: 5px;
  align-items: center;
  padding: 2px 4px;
  font-size: 12px;
  font-family: var(--db-mono, monospace);
}

.db-tree-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--editor-panel-background-color);
  border-bottom: 1px solid var(--panel-border-color);
  font-family: var(--editor-font-family, inherit);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--editor-muted-text-color);
}

.db-tree {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

/* Below this the columns would start eating the names; scroll the tree instead of clipping
   every one of them to an ellipsis. */
.db-tree-head,
.db-tree-row { min-width: 300px; }

.db-tree-row {
  cursor: default;
  border-radius: 3px;
  user-select: none;
}

.db-tree-row:hover { background: var(--editor-input-bg); }

/* Standard selection: the same --tree-selection-bg every other tree in the editor uses,
   so a selected row here reads as selected everywhere else does. */
.db-tree-on,
.db-tree-on:hover {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
}

.db-tree-table .db-tree-label {
  font-weight: 700;
  color: var(--data-accent);
}

/* The column count belongs to the name, not to the Type column. */
.db-tree-count {
  font-size: 10.5px;
  color: var(--editor-muted-text-color);
}

.db-tree-c-name {
  display: flex;
  align-items: center;
  gap: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.db-tree-col .db-tree-c-name { padding-left: 16px; }

.db-tree-c-type,
.db-tree-c-key,
.db-tree-c-null,
.db-tree-c-ref {
  color: var(--editor-muted-text-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.db-tree-c-key,
.db-tree-c-null { font-weight: 700; }

/* The disclosure triangle: a target you can hit without selecting the row. */
.db-tri {
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0 2px;
  font-size: 10px;
  line-height: 1;
  color: var(--editor-muted-text-color);
}

/* The detail half — what doesn't fit a column. */
.db-detail {
  flex: 0 0 auto;
  border-top: 1px solid var(--panel-border-color);
  padding-top: 6px;
  font-size: 12px;
}

.db-detail-title {
  font-family: var(--db-mono, monospace);
  font-weight: 700;
  color: var(--data-accent);
  margin-bottom: 4px;
}

.db-detail-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 10px;
  margin: 0 0 6px;
}

.db-detail-list dt { color: var(--editor-muted-text-color); }
.db-detail-list dd { margin: 0; font-family: var(--db-mono, monospace); overflow-wrap: anywhere; }

.db-legend {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-top: 6px;
  border-top: 1px solid var(--panel-border-color);
  font-size: 11px;
  color: var(--editor-muted-text-color);
}

.db-legend b { color: var(--editor-text-color); }
.db-legend-tip { font-style: italic; }

/* Editor + suggestions as one area. Side-by-side by default: a column of buttons reads as a
   list you scan down, and it stops the strip taking height from the results every time the
   list grows a row. */
.db-editor-area {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 0 0 auto;
}

.db-editor-area-side {
  flex-direction: row;
  align-items: stretch;
}

/* Stacked, the editor is its own height and full width; beside the suggestions it takes
   whatever they leave. Both live here rather than in the component's inline style, which
   would win over these rules — that's exactly how the editor once collapsed to nothing. */
.db-editor-box {
  flex: 0 0 auto;
  width: 100%;
}

.db-editor-area-side .db-editor-box {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}

/* LEFT of the statement, in two narrow columns.
   Left because the eye is already there when it reaches the start of the next word; two
   columns because one long column of short words wastes half its width and pushes the list
   out of sight. It scrolls rather than growing the editor area, and can be dragged wider. */




/* Two narrow columns have no room for "— customers" after a name; the button's title still
   carries it, and the schema tree is where you go to browse rather than to pick. */

/* A message needs the whole width, not half of one column. */

/* Layout Grids section (design panel). One row per grid, fields beneath it —
   a frame can carry several grids, so this is a list, not a fixed form.

   The section's + carries NO rules of its own: `.add-grid-icon` joins the shared
   panel-icon families above (size, muted opacity, clickable padding, the hover
   that brightens the glyph, and the dark-mode invert a black SVG needs), and
   `.grid-add-div` joins the add-div family (inline-flex, centred, right gap).
   `.details-header` does the placing — its ::after spacer is order 1 and any
   [class*="-add-div"] is order 2, which is what puts it at the panel's right
   edge. Anything bespoke here is how it drifted from every other section. */

/* The empty state. Indented to level 1 like every other row in a section (it
   was flush to the panel edge), and allowed to use the full row width — as a
   flex item it shrank to min-content and wrapped one word per line. */
.grid-empty {
  margin: 0 var(--panel-right-gutter, 12px) 0 var(--panel-indent);
  padding: var(--details-row-pad) 0;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.8rem;
  opacity: 0.6;
}
.grid-row-group { padding: var(--details-row-pad) 0; }
.grid-row { display: flex; align-items: center; gap: 6px; padding: var(--details-row-pad) 0; }
.grid-field-label { font-size: 0.78rem; opacity: 0.7; flex: 0 0 auto; }
/* Pinned with flex-basis, not width: `.details-scroll input { width: 100% }`
   would otherwise stretch these off the panel edge. */
.grid-row .layout-input { flex: 0 0 56px; }
.grid-kind-select {
  flex: 0 0 auto;
  padding: 3px 6px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  font-size: 0.8rem;
}
.grid-color { flex: 0 0 26px; width: 26px; height: 20px; padding: 0; border: none; background: none; cursor: pointer; }
.grid-vis-btn, .grid-del-btn {
  flex: 0 0 auto;
  border: none;
  background: none;
  color: var(--editor-text-color);
  cursor: pointer;
  font-size: 0.9rem;
  opacity: 0.65;
}
.grid-vis-btn:hover, .grid-del-btn:hover { opacity: 1; color: var(--editor-selected-blue); }
.grid-warn { font-size: 0.72rem; color: #f5a623; }

/* Command palette (Ctrl+K). Sits high on screen like every other palette —
   the eye goes to where it expects the list, not to the middle of the window. */
.cp-backdrop {
  position: fixed; inset: 0; z-index: 10001;
  display: flex; align-items: flex-start; justify-content: center;
  background: rgba(15, 18, 24, 0.45); padding-top: 12vh;
}
.cp-modal {
  width: 100%; max-width: 520px; box-sizing: border-box;
  background: var(--editor-panel-background-color, #fff);
  color: var(--editor-text-color, #1a1a1f);
  border: 1px solid var(--panel-border-color, rgba(0,0,0,0.15));
  border-radius: 12px; box-shadow: 0 16px 48px rgba(0,0,0,0.4);
  overflow: hidden;
}
.cp-input {
  width: 100%; box-sizing: border-box; padding: 13px 16px;
  border: none; border-bottom: 1px solid var(--panel-border-color, rgba(0,0,0,0.12));
  background: transparent; color: var(--editor-text-color); font-size: 0.95rem; outline: none;
}
.cp-list { max-height: 46vh; overflow-y: auto; }
.cp-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; cursor: pointer; font-size: 0.86rem;
}
.cp-row-active { background: color-mix(in srgb, var(--editor-selected-blue) 16%, transparent); }
.cp-row-label { flex: 1 1 auto; }
.cp-row-keys { flex: 0 0 auto; font-size: 0.75rem; opacity: 0.6; font-variant-numeric: tabular-nums; }
.cp-empty { padding: 14px 16px; font-size: 0.85rem; opacity: 0.65; }

/* Layer-name filter at the top of the Structure tree. */
.layer-search-row { display: flex; align-items: center; gap: 6px; padding: 4px 6px 6px; }
.layer-search-input {
  flex: 1 1 auto; min-width: 0;
  padding: 4px 8px;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg);
  color: var(--editor-text-color);
  font-size: 0.8rem;
}
.layer-search-count { flex: 0 0 auto; font-size: 0.72rem; opacity: 0.6; }
/* The frame tools that sit beside the Find box. Icon-buttons: the GLYPH brightens on
   hover, never a background box (the app-wide rule for icon-buttons). */
.tree-tool-icon {
  flex: 0 0 auto; width: 16px; height: 16px; cursor: pointer;
  opacity: 0.62; transition: opacity 90ms ease;
}
.tree-tool-icon:hover { opacity: 1; }
/* Black artwork, so it flips in dark mode like every other editor icon. */
[data-theme="dark"] .tree-tool-icon { filter: invert(1) brightness(1.2); }

/* Find & replace (Ctrl+H). */
.fr-backdrop {
  position: fixed; inset: 0; z-index: 10001;
  display: flex; align-items: flex-start; justify-content: center;
  background: rgba(15,18,24,0.45); padding-top: 14vh;
}
.fr-modal {
  width: 100%; max-width: 380px; box-sizing: border-box; padding: 20px 22px;
  background: var(--editor-panel-background-color, #fff);
  color: var(--editor-text-color, #1a1a1f);
  border: 1px solid var(--panel-border-color, rgba(0,0,0,0.15));
  border-radius: 12px; box-shadow: 0 16px 48px rgba(0,0,0,0.4);
}
.fr-title { margin: 0 0 2px; font-size: 1.05rem; font-weight: 700; }
.fr-scope { margin: 0 0 14px; font-size: 0.78rem; opacity: 0.65; }
.fr-input {
  width: 100%; box-sizing: border-box; margin-bottom: 8px; padding: 7px 10px;
  border: var(--editor-control-border); border-radius: var(--corner-radius-sm);
  background: var(--editor-input-bg); color: var(--editor-text-color); font-size: 0.85rem;
}
.fr-check { display: flex; align-items: center; gap: 6px; font-size: 0.8rem; margin: 4px 0 10px; cursor: pointer; }
.fr-status { margin: 0 0 14px; font-size: 0.8rem; opacity: 0.75; min-height: 1.2em; }
.fr-actions { display: flex; justify-content: flex-end; gap: 8px; }
.fr-btn {
  padding: 6px 14px; border-radius: var(--corner-radius-sm);
  border: 1px solid var(--editor-control-border-color);
  background: transparent; color: var(--editor-text-color); cursor: pointer; font-size: 0.85rem;
}
.fr-btn-primary { border-color: var(--editor-selected-blue); background: var(--editor-selected-blue); color: #fff; }
.fr-btn:disabled { opacity: 0.5; cursor: default; }

/* Shared-layer badges in the Structure tree. A master and a shared copy must be
   tellable apart at a glance: editing a master changes every page. */
.tree-master-badge, .tree-shared-badge {
  flex: 0 0 auto;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.tree-master-badge {
  color: #fff;
  background: var(--editor-selected-blue);
}
.tree-shared-badge {
  color: var(--editor-selected-blue);
  background: color-mix(in srgb, var(--editor-selected-blue) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--editor-selected-blue) 45%, transparent);
  text-transform: none;
  letter-spacing: 0;
}

/* A shared layer whose master has been deleted: it draws nothing, and the badge
   says so rather than looking like a working copy. */
.tree-shared-badge { cursor: pointer; }
.tree-shared-badge.broken {
  color: #e5484d;
  background: color-mix(in srgb, #e5484d 14%, transparent);
  border-color: color-mix(in srgb, #e5484d 45%, transparent);
  cursor: default;
}
/* Rows listed under a shared layer belong to the master on another page — shown
   for context, inert to interaction. */
.item-tree-item.shared-context { opacity: 0.62; }

/* Badges inside the LAYERS panel adapt to the row instead of carrying their own
   colour. That panel highlights the active layer with the same selection blue
   the badges use, so a fixed blue chip either vanishes into the highlight or
   fights it. Drawing from currentColor means the badge stays legible whatever
   state the row is in — active, hidden, hovered — without a rule per state. */
.page-list-item .tree-master-badge,
.page-list-item .tree-shared-badge {
  color: inherit;
  background: transparent;
  border: 1px solid currentColor;
  opacity: 0.75;
}
.page-list-item .tree-master-badge { font-weight: 700; }
.page-list-item:hover .tree-master-badge,
.page-list-item:hover .tree-shared-badge { opacity: 1; }
/* A missing master stays red — it's a fault, not a state of the row, and it
   should read as one however the row is highlighted. */
.page-list-item .tree-shared-badge.broken {
  color: #e5484d;
  border-color: color-mix(in srgb, #e5484d 60%, transparent);
  opacity: 1;
}

/* Context-menu submenu (Add a shared layer ›). Opens on hover to the right, and
   flips to the left when there isn't room — a menu that opens off-screen is
   worse than a long label. */
/* One submenu look for every context menu: label left, chevron hard right. The
   older menus put a "▸" inline after the text, which drifted with the label
   length and read as punctuation rather than as an affordance. */
.cm-submenu-parent,
.canvas-context-menu-li.has-submenu { position: relative; display: flex; align-items: center; gap: 8px; }
.cm-submenu-label { flex: 1 1 auto; }
.cm-submenu-arrow { flex: 0 0 auto; opacity: 0.55; }
.cm-submenu {
  display: none;
  position: absolute;
  left: 100%; top: -5px;
  min-width: 190px;
  max-width: 280px;
}
.cm-submenu-parent:hover > .cm-submenu { display: block; }

/* The submenu ABUTS its parent menu — no gap to cross.
   `left: 100%` puts it at the row's right edge, which is inside the parent
   menu's padding and border, so it appeared a few pixels clear of the menu's
   visible edge. Pulling it back by that much makes the two panels touch, so
   moving the pointer across never leaves the menu. (An invisible hover bridge
   would paper over the gap; closing it is the actual fix.) */
.cm-submenu,
.canvas-context-menu-submenu { margin-left: -6px; }
.submenu-left .canvas-context-menu-submenu,
.cm-submenu-left { margin-left: 0; margin-right: -6px; }
/* Near the right edge the submenu would run off; open it leftward instead. */
@media (max-width: 900px) { .cm-submenu { left: auto; right: 100%; } }
.cm-submenu .canvas-context-menu-li { white-space: nowrap; }

/* Templates library — the ✨ AI generator beside the "+".
   The glyph is text, not an icon file, so it needs its own sizing rather than
   .templates-add-icon's height-based one. */
.templates-ai-icon {
  opacity: var(--editor-icon-muted-opacity);
  font-size: 13px;
  line-height: 1;
  margin-left: 6px;
  cursor: pointer;
  user-select: none;
}

.templates-ai-icon.disabled {
  opacity: 0.25;
  cursor: default;
}

.templates-ai-icon:not(.disabled):hover {
  opacity: 1;
}


/* ── HIERARCHY plugin panel section. Its own classes rather than the shared field
   grid: the section is contributed by a plugin, so it must not depend on the panel's
   internals to lay itself out. Colours are NOT here — they live in Fill / Stroke. ── */
/* The node count, reading as a subtitle to the section's own name. It needs a real gap:
   `margin-left: auto` was meant to give it one, but the +/− pair is what claims the header's
   free space, so the auto resolved to nothing and the count sat jammed against the title.
   The header's +/− are the app's standard page-list icons in the standard wrappers, so size,
   hover, order and placement all come from the shared rules. */
.tree-count {
  margin-left: 8px;
  margin-right: 6px;
  font-size: 0.72rem;
  opacity: 0.6;
}
.tree-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.tree-label { flex: 0 0 auto; min-width: 52px; font-size: var(--editor-control-font-size); opacity: 0.8; }
.tree-num { flex: 0 0 58px; width: 58px; }
/* The stretchy controls — the name field and the two dropdowns. Two classes deep on
   purpose: the panel's blanket `.details-scroll input / select { width: 100% }` out-
   specifies a plain width, so each of these demanded a whole row to itself and the
   wrapping row dropped it BELOW its label. That is where the stray label-on-top rows
   came from; a label belongs beside the control it names. */
.tree-row .tree-select,
.tree-row .tree-node-name { flex: 1 1 0; width: auto; min-width: 0; }
.tree-check { display: flex; align-items: center; gap: 4px; font-size: 0.74rem; opacity: 0.85; }
/* The line between the master/detail half (the tree and its picked node) and the settings
   that follow. It starts at LEVEL 1, like the content on either side of it, rather than
   running to the panel's edge — an edge-to-edge rule reads as the end of a section. */
/* The card a MULTI-item tree drag previews as: one row saying how many are coming,
   at the place they would land. Moving every row live instead made the tree jump
   under the cursor on each pointer move, which is what made the drop impossible to
   aim — the whole reason this exists. */
.tree-drop-card {
  margin: 1px 8px 1px var(--panel-indent);
  padding: 2px 8px;
  border: 1px dashed var(--editor-selected-blue);
  border-radius: var(--corner-radius-sm);
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2));
  font-size: var(--editor-control-font-size);
  opacity: 0.9;
  pointer-events: none;
  user-select: none;
}

/* THE PICKED NODE'S LINK — a sub-group, so its heading sits at LEVEL 1 with the rest of the
   section's content and its controls indent one level further. Same shape as Scale / Skew's
   header, for the same reason: the collapsed line has to be readable on its own. */
.tree-link-header {
  padding-top: var(--details-row-pad); padding-bottom: var(--details-row-pad);
}
.tree-link-title {
  font-weight: 600;
  font-size: var(--editor-control-font-size);
  color: var(--header-3-color);
  user-select: none;
}
.tree-link-row { padding-left: calc(var(--panel-indent) * 2); }
/* A folded node saying what it is hiding. A CAUTION rather than an error — the design is not
   wrong, it just cannot do the thing those items were given; the fix is one click above. */
.tree-link-folded {
  display: block;
  font-size: var(--editor-control-font-size);
  /* The CUSTOMIZABLE warning colour the sheet's own issue rings use (`warningColor` in
     preferences), not a new one invented here — and it is a variable rather than a literal so
     it answers to the theme in both light and dark. */
  color: var(--warning-color, #b26a00);
}
/* Two classes deep for the same reason the node name and the dropdowns are — the panel's
   blanket `.details-scroll input { width: 100% }` out-specifies a plain width and would put
   the address on its own line, below the label that names it. */
.tree-row .tree-link-url { flex: 1 1 0; width: auto; min-width: 0; }

.tree-divider {
  margin: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
  border-top: 1px solid var(--editor-panel-separator-color);
}
/* The TREE — the master half, at LEVEL 1 with the rest of the section's content. Rows
   indent further by their depth, so the list reads as the shape it draws; the picked row
   carries the same selection fill the other trees use. */
.tree-tree { padding: 2px 8px 6px var(--panel-indent); }
/* THE ROWS ARE THE SHARED TREE'S NOW (see shared/tree/TreeView) — the layout, the hover, the
   selection wash and the three drop cues all come from `.flow-tree-row`, which is where every
   other tree in the editor gets them. What is left here is the two things that are this
   panel's own: the class is still on every row so the panel's tests can find one to click, and
   the tree's accent is redefined to the blue this panel has always dropped in, rather than
   restating the same rules in a different colour (the same trick `.anim-tree` uses).

   The whole-hierarchy row above the tree is NOT one of the tree's rows — it wears
   `.flow-tree-row` to sit flush with them and keeps its own `.picked` below. */
.tree-tree-view { --code-accent: var(--editor-selected-blue); }
/* THE DESIGN PANEL'S LISTS SELECT THE WAY THE LAYERS TREE DOES — its selection background
   (`--tree-selection-bg`, the editor selection colour at the layers tree's alpha) and its
   selection-blue text — so a picked corner, fill, effect, hierarchy node, sheet row or list
   option reads as the same thing a selected layer does. The shared rows' own pick was a 16%
   wash of the tree's accent, which is the diagram panels' green in the Sheet and List trees
   and a lighter blue than the layers tree's everywhere else. The left-edge bar stays, in the
   selection blue. The diagram panels (flows, sequences, data…) keep their own accent. */
ul.paint-entry-tree, ul.effect-tree, ul.corner-tree, .tree-tree-view, .sheet-tree, .control-option-tree {
  --code-accent: var(--editor-selected-blue);
}
:is(ul.paint-entry-tree, ul.effect-tree, ul.corner-tree, .tree-tree-view, .sheet-tree, .control-option-tree)
  .flow-tree-row.selected {
  background: var(--tree-selection-bg);
  box-shadow: inset 2px 0 0 var(--editor-selected-blue);
}
:is(ul.paint-entry-tree, ul.effect-tree, ul.corner-tree, .tree-tree-view, .sheet-tree, .control-option-tree)
  .flow-tree-row.selected .flow-tree-name {
  color: var(--editor-selected-blue);
}
.tree-whole-row.picked { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.2)); border-radius: 3px; }
.tree-whole-row.hovered { outline: 1px dashed var(--editor-hover-color); outline-offset: -1px; border-radius: 3px; }
.tree-fold { flex: 0 0 auto; width: 9px; height: 9px; opacity: 0.7; cursor: pointer; }
.tree-fold:hover { opacity: 1; }
.tree-fold-spacer { flex: 0 0 auto; width: 9px; }
.tree-node-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE HIERARCHY ITSELF — the row above the root, standing for the whole drawing. Set apart
   from the nodes under it (it is not one) and carrying its node count on the right, the way
   the section header carries the drawing's. */
.tree-whole-row { font-weight: 600; }
.tree-whole-row .tree-count { margin-left: auto; margin-right: 4px; }
/* A SUB-GROUP inside the section: the app's own disclosure triangle in the level-1 band,
   with its rows stepping in one more level — the same shape every other section uses, and
   the reason there is no bespoke clickable-text header here any more. */
.tree-sub-header {
  display: flex; align-items: center; gap: 6px;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.tree-subtriangle { flex: 0 0 auto; width: 9px; height: 9px; }
/* Both triangles are BLACK artwork, so on a dark panel they sit black-on-dark and read as
   smudges — the sub-headers ("Generate with AI", "Paste a tree") most of all, since those
   are the only mark those rows have. Same invert the chart's and the sheet's sub-headers
   carry, applied to the tree's fold triangles too: it is one icon in two places. */
[data-theme="dark"] .tree-fold,
[data-theme="dark"] .tree-subtriangle { filter: invert(1) brightness(1.2); }
/* Says where the picked node's own settings are, rather than repeating them here. */
.tree-hint { opacity: 0.65; font-size: 0.72rem; line-height: 1.35; }
.tree-sub-heading {
  margin: 0; font-size: var(--editor-control-font-size); font-weight: 600; opacity: 0.85;
}
.tree-subgroup-row { padding-left: calc(var(--panel-indent) * 2); }
/* A plain worded button, sized and coloured like the panel's other text controls rather
   than like a browser button. `tree-edit-canvas` is the way INTO the drawing and shares
   the look but not the class: the two say entirely different things, and a test that means
   "the whole-hierarchy row offers no add-line control" has to be able to tell them apart. */
.tree-add-line,
.tree-edit-canvas {
  font: inherit; font-size: var(--editor-control-font-size);
  color: var(--editor-selected-blue); background: none; border: none; padding: 0; cursor: pointer;
}
.tree-add-line:hover, .tree-edit-canvas:hover { text-decoration: underline; }
.tree-add-line:disabled, .tree-edit-canvas:disabled { color: inherit; opacity: 0.4; cursor: default; text-decoration: none; }
/* The paste box. Full width of its level, monospace because what goes in it is INDENTED
   text and proportional type makes a two-space indent unreadable. */
.tree-paste {
  flex: 1 1 100%; width: auto; min-width: 0; resize: vertical;
  font-family: var(--editor-mono-font, monospace);
  font-size: var(--editor-control-font-size);
}
/* An inert tool icon (delete with an empty selection): visibly there, plainly not
   available — the row keeps its shape instead of reflowing as the selection changes. */
.tree-tool-icon.disabled { opacity: 0.25; cursor: default; }
.tree-tool-icon.disabled:hover { opacity: 0.25; }

/* ── CHART plugin panel section. Its own classes rather than the shared field grid, for
   the same reason the hierarchy's are: the section is contributed by a plugin, so it must
   not depend on the panel's internals to lay itself out. Colours are NOT here — they live
   in Fill / Stroke, one row per series. ─────────────────────────────────────────────── */
/* The row/series count, reading as a subtitle to the section's own name. */
.chart-count {
  margin-left: 8px;
  margin-right: 6px;
  font-size: 0.72rem;
  opacity: 0.6;
}
/* LEVEL 1 — a row of the section's own content, and the band a sub-group's triangle sits
   in. One row is one PARAMETER: a label and its single control. Checkboxes are the
   exception — a standalone one is still its own row (`.chart-check`), but a RELATED pair
   (X axis / Y axis) shares one (`.chart-check-first` / `checkPairRow`). `flex-wrap` is a
   safety net for a narrow panel, not something a row leans on the way it used to when two
   unrelated fields shared one. */
.chart-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
/* A row whose control is taller than one line (the SQL box, the field ticklist): its label
   belongs at the top of it, not floating in the middle. */
.chart-row-top { align-items: flex-start; }
/* A row whose control wants the panel's FULL width rather than what's left beside a fixed
   label column — the chart-kind tile grid, so far the only one. The label sits above it
   instead, at its own natural width rather than the shared label column's. */
.chart-row-stack { flex-direction: column; align-items: stretch; gap: 4px; }
.chart-row-stack > .chart-label { flex: 0 0 auto; }
/* A FIXED width, not `min-width`: every row is a label + one control now, so this is the one
   column every row's control lines up under — the same device `.layout-row label` uses.
   Sized to the longest label in the section ("Category gap", "Value prefix"); a label THAT
   long stays unwrapped rather than being squeezed into an ellipsis nobody asked for. */
.chart-label { flex: 0 0 100px; font-size: var(--editor-control-font-size); opacity: 0.8; white-space: nowrap; }
/* FIXED WIDTHS as `flex` and not as `width`: the panel's blanket
   `.details-scroll input { width: 100% }` out-specifies a plain width, and a field that
   claimed 100% would demand a whole row and drop BELOW its own label when the row wrapped. */
.chart-num { flex: 0 0 62px; }
.chart-text { flex: 1 1 60px; width: auto; min-width: 0; }
.chart-text.wide { flex: 1 1 100%; }
/* Two classes deep, for the same specificity reason. */
.chart-row .chart-select { flex: 1 1 0; width: auto; min-width: 0; }
.chart-check { display: flex; align-items: center; gap: 4px; font-size: 0.74rem; opacity: 0.85; }
/* Checkboxes are the one exception to "a row is one parameter" — a related pair (X axis / Y
   axis) reads as one decision and stays on one line (see `checkPairRow`). The FIRST checkbox
   of a pair still takes the label column's own width, so the second one starts where a
   field would on an ordinary row — the pair lines up with the rest of the section instead
   of drifting to wherever its own text happens to end. */
.chart-check-first { flex: 0 0 100px; }
/* A sub-group's heading. Its triangle belongs to the group's LEVEL-2 content, so it occupies
   the band from 1 to 2 * panel-indent — level 1, where the section's own rows are, rather
   than at the panel edge where a SECTION's triangle lives. The heading text then lands at
   level 2 with the rows it opens. (The sheet's sub-groups are the same control and read the
   same way.) */
.chart-sub-header {
  display: flex; align-items: center; gap: 2px;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.chart-subtriangle {
  padding: 3px var(--panel-triangle-pad-right) 3px var(--panel-triangle-pad-left);
  opacity: var(--editor-icon-muted-opacity);
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  vertical-align: middle;
  cursor: pointer;
  box-sizing: content-box;
}
.chart-sub-heading {
  margin: 0; font-size: var(--editor-control-font-size); font-weight: 600; opacity: 0.85;
}
/* LEVEL 2 — a sub-group's content. The extra indent is applied to the group rather than to
   every row inside it, so each row keeps the single level-1 padding it already has. */
.chart-sub-body > .chart-row { padding-left: var(--panel-level-2); }
.chart-sub-body > .chart-note,
.chart-sub-body > .chart-grid { margin-left: var(--panel-indent); }
/* A sentence the section says for itself — the SQL it would run, or why there is no chart
   to draw yet. Not a control, so it is quieter than one. */
.chart-note {
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
  font-size: 0.72rem; opacity: 0.7; line-height: 1.4; word-break: break-word;
}
.chart-note code { font-family: var(--code-font-family, monospace); font-size: 0.7rem; }
.chart-field-list { display: flex; flex-direction: column; gap: 2px; flex: 1 1 0; min-width: 0; }
/* A COMBO's per-series row inside that list: the series' own name, then its Bar/Line/Area
   picker. The name shrinks to an ellipsis before the picker ever does — a truncated name is
   still readable from the row above it; a squeezed select is not. */
.chart-combo-series-row { display: flex; align-items: center; gap: 6px; }
.chart-combo-series-name {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--editor-control-font-size); opacity: 0.85;
}
.chart-combo-series-row .chart-select { flex: 0 0 84px; }
.chart-sql {
  flex: 1 1 0; min-width: 0; resize: vertical;
  font-family: var(--code-font-family, monospace);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background: var(--editor-input-bg-color, transparent);
  border: 1px solid var(--editor-border-color);
  border-radius: var(--corner-radius-sm);
  padding: 4px 6px;
}
/* The chart-TYPE picker: a grid of tiles, each a real tiny render of the kind rather than a
   name to read — the same "thumbnail, not text" device the Pages panel uses for its own
   tiles (`.page-tile*`), with its own classes rather than sharing those: a chart's tile is a
   live `ChartPreview` SVG, not an `<img>`, and this grid sits inside a panel SECTION rather
   than filling one on its own, so it earns a size and a column count of its own. */
.chart-kind-grid {
  list-style-type: none;
  margin: 0; padding: 0;
  display: grid;
  /* 80px, FIXED rather than a `minmax(80px, 1fr)` that would stretch to fill the row: 80px
     is chosen against the panel's own MINIMUM width (`DETAILS_PANEL_MIN_WIDTH`,
     editorLayout.ts) — at that floor the row has ~272px to lay tiles out in after its
     padding and gaps, room for exactly 3 at this width and no more, so the panel's
     narrowest state is also the "at least 3 across" guarantee. Widening the panel then
     adds MORE tiles at the SAME size instead of enlarging the existing ones — a real
     drawing is worth a size that stays legible, not whatever's left over once the row is
     divided by however many kinds there are. */
  grid-template-columns: repeat(auto-fill, 80px);
  gap: 6px;
  min-width: 0;
  /* Height from the rows, and the rows from their content.
     `flex: 1 1 0` used to sit here, written as if this row laid its children out
     ACROSS — but `.chart-row-stack` is `flex-direction: column`, so that basis
     applied to HEIGHT, and `min-height: auto` floored the item at the grid's
     MIN-CONTENT height. Min-content width is one 80px column, so that floor was
     all ten tiles in a single column — ~727px. The grid then laid out 3-across in
     four rows inside a 727px box and `align-content: normal` (stretch) split the
     surplus between them: every tile 177px tall for ~70px of drawing and label,
     i.e. a ~110px dead band under every row of the picker. */
  flex: 0 0 auto;
  align-content: start;
}
.chart-kind-tile {
  display: flex; flex-direction: column; align-items: stretch;
  cursor: pointer;
}
.chart-kind-thumb {
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  box-sizing: border-box;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: var(--corner-radius-sm, 3px);
  background: #ffffff;   /* every thumbnail is drawn on the chart's own default white panel,
                             whatever the editor's theme, so its lines read against it */
}
.chart-kind-thumb .chart-preview-svg { display: block; width: 100%; height: 100%; }
.chart-kind-name {
  margin-top: 2px;
  text-align: center;
  font-size: 0.68rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--editor-text-color);
}
.chart-kind-tile.active .chart-kind-thumb {
  border-color: var(--editor-selected-blue);
  box-shadow: 0 0 0 1px var(--editor-selected-blue);
}
.chart-kind-tile.active .chart-kind-name {
  color: var(--editor-selected-blue);
}

/* ── RECORD FORM plugin panel section. Its own classes rather than the shared field grid,
   for the same reason the chart's and the hierarchy's are: contributed by a plugin, so it
   must not depend on the panel's internals to lay itself out. ─────────────────────────── */
.record-form-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.record-form-row-top { align-items: flex-start; }
.record-form-label { flex: 0 0 90px; font-size: var(--editor-control-font-size); opacity: 0.8; white-space: nowrap; }
.record-form-row .record-form-select { flex: 1 1 0; width: auto; min-width: 0; }
.record-form-row .record-form-text { flex: 1 1 0; width: auto; min-width: 0; }
/* The field checklist: one row per entity field, a checkbox and its name — the same
   scrollable, bordered look a sheet's own field ticklist uses. */
.record-form-fields {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 2px;
  max-height: 160px; overflow-y: auto;
  border: 1px solid var(--editor-control-border-color, rgba(127, 127, 127, 0.35));
  border-radius: var(--corner-radius-sm, 3px);
  padding: 4px 6px;
}
.record-form-field-row { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; }
.record-form-field-row input { width: auto; }

/* ── GRID TRACKS, in the Layout section. A track list is taller than one line, so the row
   aligns to the TOP rather than centring its label against a stack of fields. The fixed
   widths are pinned with `flex: 0 0` because `.details-scroll input { width: 100% }` would
   otherwise clobber a plain width. ───────────────────────────────────────────────────── */
.layout-row.layout-track-row { align-items: flex-start; }
.layout-tracks { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.layout-track { display: flex; align-items: center; gap: 4px; }
.layout-track .layout-input { flex: 0 0 56px; width: auto; }
.layout-track-unit { flex: 0 0 52px; width: auto; min-width: 0; }
.layout-track-remove, .layout-track-add {
  flex: 0 0 auto; font-size: 0.78rem; padding: 2px 6px;
}

/* ── The chart's TABLE, as a grid. One component in two homes (the panel and the full-page
   editor), so the only difference between them is `.compact`. ───────────────────────── */
.chart-grid { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* The panel copy is capped and scrolls: a forty-row table must not push every other section
   off the panel. The page copy takes whatever room the page has. */
.chart-grid-scroll { overflow: auto; min-width: 0; }
.chart-grid.compact .chart-grid-scroll { max-height: 190px; }
.chart-grid:not(.compact) .chart-grid-scroll { flex: 1 1 auto; min-height: 0; }
.chart-grid-table { border-collapse: collapse; font-size: var(--editor-control-font-size); }
.chart-grid-th, .chart-grid-td {
  padding: 1px 2px; border: 1px solid var(--editor-panel-separator-color); vertical-align: middle;
}
.chart-grid-th { position: sticky; top: 0; z-index: 2; background: var(--editor-bg-color); }
.chart-grid-th.label, .chart-grid-td.label { background: var(--tree-selection-bg, rgba(127, 127, 127, 0.08)); }
.chart-grid-th.narrow, .chart-grid-td.narrow { border: none; padding: 0 2px; }
/* The cells. Fixed widths as `flex`-free explicit rules on the INPUT, two classes deep so
   they beat the panel's blanket 100% — a grid whose every cell demanded the full width
   would be one column wide. */
.chart-grid-table .chart-grid-head-input,
.chart-grid-table .chart-grid-cell-input {
  width: 68px; min-width: 0; box-sizing: border-box;
  border: none; background: transparent; padding: 1px 3px;
  font-size: var(--editor-control-font-size); color: var(--editor-text-color);
}
.chart-grid:not(.compact) .chart-grid-table .chart-grid-head-input,
.chart-grid:not(.compact) .chart-grid-table .chart-grid-cell-input { width: 110px; }
.chart-grid-table .chart-grid-head-input { font-weight: 600; }
.chart-grid-table input:focus { outline: 1px solid var(--editor-selected-blue); border-radius: 2px; }
/* A column's or row's −. Brightens the GLYPH on hover, never a background box. */
.chart-grid-remove {
  width: 9px; height: 9px; cursor: pointer; vertical-align: middle;
  opacity: var(--editor-icon-muted-opacity);
}
.chart-grid-remove:hover { opacity: 1; }
/* The glyph is black artwork, which is invisible on a dark panel. */
[data-theme="dark"] .chart-grid-remove,
[data-theme="dark"] .chart-subtriangle { filter: invert(1) brightness(1.2); }
.chart-grid-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

/* The full-page data editor, and the SVG preview beside it. */
.chart-preview-svg {
  border: 1px solid var(--editor-panel-separator-color);
  border-radius: var(--corner-radius-sm);
  background: var(--editor-canvas-bg-color, transparent);
}
.chart-data-page .chart-grid-actions { padding-top: 6px; }

/* ── SHEET plugin panel section. Its own classes, like the hierarchy's and the chart's:
   a plugin's section must not depend on the panel's internals to lay itself out. The DATA
   is not here at all — it is edited on the sheet's own page. ───────────────────────── */
.sheet-count {
  margin-left: 8px;
  margin-right: 6px;
  font-size: 0.72rem;
  opacity: 0.6;
}
.sheet-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.sheet-row-top { align-items: flex-start; }
.sheet-label { flex: 0 0 auto; min-width: 62px; font-size: var(--editor-control-font-size); opacity: 0.8; }
/* Fixed widths as `flex`, never as `width`: the panel's blanket
   `.details-scroll input { width: 100% }` out-specifies a plain width, and a field that
   claimed the whole row would drop BELOW its own label when the row wrapped. */
.sheet-num { flex: 0 0 62px; }
.sheet-text { flex: 1 1 60px; width: auto; min-width: 0; }
.sheet-row .sheet-select { flex: 1 1 0; width: auto; min-width: 0; }
/* The table-shape buttons wrap: four of them never fit a panel column on one line. */
.sheet-table-ops { flex-wrap: wrap; gap: 4px; }
.sheet-clear-btn { flex: 0 0 auto; }
.sheet-check { display: flex; align-items: center; gap: 4px; font-size: 0.74rem; opacity: 0.85; }
/* A sub-group's heading. Its triangle belongs to the group's LEVEL-2 content, so it
   occupies the band from 1 to 2 * panel-indent — it starts at level 1, where the section's
   own rows are, rather than at the panel edge alongside a section triangle. The heading
   text then lands at level 2 with the rows it opens. */
.sheet-sub-header {
  display: flex; align-items: center; gap: 2px;
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
}
.sheet-subtriangle {
  padding: 3px var(--panel-triangle-pad-right) 3px var(--panel-triangle-pad-left);
  opacity: var(--editor-icon-muted-opacity);
  height: var(--panel-triangle-size);
  width: var(--panel-triangle-size);
  vertical-align: middle;
  cursor: pointer;
  box-sizing: content-box;
}
.sheet-sub-heading {
  margin: 0; font-size: var(--editor-control-font-size); font-weight: 600; opacity: 0.85;
}
.sheet-sub-body > .sheet-row { padding-left: var(--panel-level-2); }
.sheet-sub-body > .sheet-note { margin-left: var(--panel-indent); }
/* The Structure tree is the sub-body's other direct child (with `.sheet-row` and `.sheet-note`
   above) — same level-2 base so its root rows line up with them, before the tree's own
   per-depth indent (`treeIndent`) takes over for anything nested under a row or column. */
.sheet-sub-body > .sheet-tree { padding-left: var(--panel-level-2); }
.sheet-note {
  padding: var(--details-row-pad) 8px var(--details-row-pad) var(--panel-indent);
  font-size: 0.72rem; opacity: 0.7; line-height: 1.4; word-break: break-word;
}
.sheet-note code { font-family: var(--code-font-family, monospace); font-size: 0.7rem; }
/* THE TABLE'S TREE. Bounded and scrolled: a bound list can hold hundreds of rows, and a panel
   section that grows to the height of the data pushes every other section off the screen. The
   rows themselves are the shared `flow-tree-*` ones, so this only says how much of them shows. */
/* A radio group's choices, as the shared tree — the same shape the sheet's Structure takes,
   and capped for the same reason: a panel section that grows without limit pushes everything
   under it off the screen. A group is rarely long enough to scroll, which is the point. */
/* A slider's two ends sit on one row — small fixed boxes with a word between them, so the row
   reads as "0 to 100" rather than as two unrelated fields. Pinned with `flex` rather than a
   plain width: `.details-scroll input { width: 100% }` out-specifies the latter. */
.control-prop-num {
  flex: 0 0 56px;
  min-width: 0;
}
.control-prop-to {
  font-size: var(--editor-control-font-size);
  color: var(--editor-muted-text-color);
}

/* The Tabs section's list of tabs. A section's detail starts at LEVEL 1, so the tree takes the
   same left edge (and right gutter) as the Name / Style / Height rows under it — without this it
   sat flush against the panel while the rows below it were one indent in. */
.tabs-tree {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  max-height: 240px;
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: 4px;
}
/* A Radio Group's own tree — the group and its choices — for the same reason the Tabs tree
   above takes the same edge: a section's detail starts at LEVEL 1, so the tree lines up with
   the Style / Arrange / Choice rows beneath it instead of sitting flush against the panel
   while every row under it was one indent in. */
.control-option-tree {
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  max-height: 240px;
  overflow-y: auto;
  overflow-x: hidden;
  padding-bottom: 4px;
}

/* The Structure heading's − / + — the app's standard pair, pushed to the header's right edge
   the way every tree panel's are, with the same 10px right-edge gap. */
.sheet-structure-ops {
  margin-left: auto;
  display: flex;
  align-items: center;
  margin-right: 10px;
}

/* NO SCROLLER OF ITS OWN. A sheet's Structure takes the height it needs and the PANEL scrolls,
   like every other section in it — a box inside a scrolling panel is two scrollbars for one
   list, and the inner one traps the wheel over exactly the rows you are reading.
   That is also what retires the reason the branches were bounded separately: rows could only
   push the Columns branch out of sight while there was a box for it to be pushed out of.
   `overflow-x: hidden` stays — a long cell name must not widen the panel. */
.sheet-tree {
  overflow-x: hidden;
  padding-bottom: 4px;
}
.sheet-tree.sheet-tree-columns {
  padding-bottom: 6px;
}
.sheet-sql {
  flex: 1 1 0; min-width: 0; resize: vertical;
  font-family: var(--code-font-family, monospace);
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  background: var(--editor-input-bg-color, transparent);
  border: 1px solid var(--editor-border-color);
  border-radius: var(--corner-radius-sm);
  padding: 4px 6px;
}
[data-theme="dark"] .sheet-subtriangle { filter: invert(1) brightness(1.2); }

/* ── THE SHEET EDITOR (a whole page). A spreadsheet grid: a frozen heading row and
   row-number gutter, one thing selected at a time, and a drag that always shows where it
   would land. ───────────────────────────────────────────────────────────────────────── */
.sheet-page .sheet-count { margin-left: 0; font-size: 11.5px; }
.sheet-hint {
  margin-left: auto; font-size: 11.5px; opacity: 0.65; text-align: right;
}
.sheet-grid-scroll {
  position: relative; flex: 1 1 auto; min-width: 0; overflow: auto;
  border: 1px solid var(--editor-panel-separator-color);
  border-radius: var(--corner-radius-sm);
  outline: none;
}
/* While a drag is live nothing may be selected as text, or dragging a row highlights the
   whole table as if you were copying it. */
.sheet-grid-scroll.dragging { user-select: none; cursor: grabbing; }
.sheet-grid { display: grid; position: relative; }
.sheet-corner, .sheet-letter, .sheet-header, .sheet-rownum, .sheet-cell {
  display: flex; align-items: center; box-sizing: border-box;
  border-right: 1px solid var(--editor-panel-separator-color);
  border-bottom: 1px solid var(--editor-panel-separator-color);
  font-size: var(--editor-control-font-size);
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  user-select: none;
}
/* The header band stays put while the table scrolls under it — a spreadsheet whose
   headings scroll away is one you have to keep scrolling back up in. */
/* HEADER CHROME is a neutral tint, not the selection colour. Wearing the selection blue by
   default said "these are selected" about every heading and row number on the page, which left
   nothing for an actual selection to say — and made a sheet look permanently half-selected. A
   plain grey works over either theme and reads as chrome; being selected is what turns a header
   blue (see `.selected` below). */
/* THE LETTERED ROW and the corner: the editor's own handles for pointing at a column. Chrome,
   so they wear the neutral tint and are never drawn on the canvas. */
.sheet-corner, .sheet-letter {
  position: sticky; top: 0; z-index: 3;
  background: rgba(127, 127, 127, 0.12);
  font-weight: 600; padding: 0 6px; cursor: pointer;
  justify-content: center; font-size: 0.72rem; opacity: 0.75;
}
.sheet-corner { left: 0; z-index: 4; }
/* THE TITLE ROW is part of the TABLE, not chrome — it is drawn on the canvas and it is what a
   paired chart calls each series. So it reads as content: the sheet's own surface behind it,
   sitting under the letters as the first row of the grid. */
.sheet-header {
  position: sticky; top: 22px; z-index: 3;      /* under the lettered row, which is 22px tall */
  background: var(--editor-bg-color);
  font-weight: 600; padding: 0 6px; cursor: pointer;
}
.sheet-rownum {
  position: sticky; left: 0; z-index: 2;
  background: rgba(127, 127, 127, 0.12);
  justify-content: center; font-variant-numeric: tabular-nums;
  opacity: 0.75; cursor: grab;
}
/* ROW ZERO — a heading row's own number (0, or 0.1 / 0.2 … when there are several). Clickable
   like every other row number, and dimmer, because zero is a label for the headings rather than
   a row you can count. */
.sheet-rownum.title-num { cursor: pointer; opacity: 0.5; }
.sheet-rownum.title-num.selected, .sheet-rownum.title-num.lit { opacity: 1; }
/* A HEADING COLUMN — one of the leading columns that label their rows. Drawn as a heading here
   because that is how the canvas draws it: same band colour, same weight. */
.sheet-cell.heading-col {
  background: rgba(127, 127, 127, 0.10);
  font-weight: 600;
}
.sheet-letter.heading-col { font-weight: 700; }
.sheet-cell { padding: 0 6px; cursor: cell; background: var(--editor-bg-color); }
.sheet-cell .num { margin-left: auto; font-variant-numeric: tabular-nums; }
/* SELECTED — one thing at a time, so this is never a range.
   The TINT is here; the OUTLINE is composed per box in SheetPage, because a selected row is one
   band and only its outside edges are drawn. A single rule here would box every cell in the row
   separately, putting a rule between each of them — across the very direction the row is read
   in. Box-shadows can't be accumulated from several classes, so the composed value is inline. */
.sheet-corner.selected, .sheet-letter.selected, .sheet-header.selected,
.sheet-rownum.selected, .sheet-cell.selected {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.28));
}
/* LIT, NOT SELECTED — the letter and the number belonging to a selected cell.
   Deliberately weaker than a selection and with no outline: they say WHERE the selection is,
   which is how you read a cell's address in a wide table without counting in from the edge.
   Making them look selected would claim the letter is what you picked, and the − button would
   then be lying about what it is about to remove. */
.sheet-letter.lit, .sheet-rownum.lit {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.28));
  opacity: 1;
  color: var(--editor-selected-blue);
  font-weight: 700;
}
/* Being dragged: dimmed, so the eye follows the gap rather than the thing. */
.sheet-letter.dragging, .sheet-header.dragging, .sheet-rownum.dragging,
.sheet-cell.dragging { opacity: 0.4; }
/* The cell a dragged cell would trade places with. */
.sheet-cell.swap-target { box-shadow: inset 0 0 0 2px var(--editor-selected-blue); }
/* Where a dragged row or column would land. */
.sheet-drop-line {
  position: absolute; background: var(--editor-selected-blue);
  pointer-events: none; z-index: 5; border-radius: 1px;
}
/* A header selected ON ITS OWN — reached by double-clicking it. Nothing under it is selected,
   so the tint has to say "this box, not this column"; a slightly stronger fill plus the full
   box the page draws round it is the difference. */
.sheet-header.only, .sheet-rownum.only { font-weight: 700; }
/* The letters carry the column's resize handle, so they need a positioning context. */
.sheet-letter { position: sticky; }

/* RESIZE HANDLES. The grab strip is wider than the rule it sits on — a 1px target is one
   nobody hits — and it only shows itself on hover, so the header doesn't look like a row of
   controls when you aren't reaching for one. */
.sheet-col-resize, .sheet-row-resize {
  position: absolute; z-index: 5; background: transparent;
}
.sheet-col-resize { top: 0; right: -3px; width: 7px; height: 100%; cursor: col-resize; }
.sheet-row-resize { left: 0; bottom: -3px; height: 7px; width: 100%; cursor: row-resize; }
.sheet-header, .sheet-rownum { position: sticky; }   /* re-stated: the handles position inside */
.sheet-col-resize:hover, .sheet-row-resize:hover,
.sheet-col-resize.active, .sheet-row-resize.active {
  background: var(--editor-selected-blue);
  opacity: 0.55;
}
.sheet-col-resize.active, .sheet-row-resize.active { opacity: 1; }
/* While a size is being dragged nothing should look clickable or selectable. */
.sheet-grid-scroll.resizing { user-select: none; }

/* ── Editing a sheet ON THE CANVAS ───────────────────────────────────────
   The sheet itself is still drawn by the canvas underneath; everything here is chrome and
   hit area laid over it in the sheet's own layout units, inside one transformed surface. So
   sizes below are UNITLESS on purpose where they come from the component (it writes numbers,
   which CSS reads as px, and the surface's matrix scales them) — only colours and borders
   are stated here. */
/* The Structure tree's hover, drawn while a sheet is merely SELECTED (see `SheetHoverTint`) —
   the same coordinate-space-not-a-box shape as `.sheet-edit-surface`, positioned with the same
   kind of matrix, but decorative only: pointer-events stay off everything in it, on purpose —
   this must never compete with the canvas's own click/drag over a selected item. */
.sheet-hover-tint-surface {
  position: absolute; left: 0; top: 0;
  transform-origin: 0 0;
  z-index: 40;
  width: 0; height: 0;
  pointer-events: none;
}
.sheet-hover-tint-cell { position: absolute; pointer-events: none; }
.sheet-edit-dim {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* pointer-events: none is set on the element itself — see SheetCanvasEditor. */
  z-index: 55;
}
.sheet-edit-dim path { fill: rgba(0, 0, 0, 0.45); }
.sheet-edit-surface {
  position: absolute; left: 0; top: 0;
  transform-origin: 0 0;
  z-index: 56;
  /* No size and no background: it is a coordinate space, not a box. Its children carry the
     pointer targets, so the empty space between them stays the canvas's. */
  width: 0; height: 0;
  font-family: Inter, system-ui, sans-serif;
}
.sheet-edit-surface.dragging { cursor: grabbing; }
/*
 * NOTHING IN HERE IS TEXT YOU SELECT, and saying so is load-bearing rather than tidy.
 *
 * This used to be set only while a MOVE or a RESIZE was running, which left the gesture used
 * most — drag to select a range — competing with the browser's own text selection. The letters,
 * the numbers and row zero's label are real DOM text, so a drag across them started highlighting
 * them: the caret-drag steals the pointer, the chrome fills with selection blue, and on some
 * paths the gesture ends early. None of it shows in a jsdom test, which has no text selection to
 * fight with — it only shows in a browser, as a drag-select that "sometimes" misbehaves.
 *
 * `touch-action: none` for the same reason on the other kind of pointer: without it a touch drag
 * is claimed as a pan before the second move arrives, and the selection never starts at all.
 * The input you type into sets both back, because there the text IS the point.
 */
.sheet-edit-surface {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
.sheet-edit-input {
  user-select: text;
  -webkit-user-select: text;
  touch-action: auto;
}
/* The chrome: opaque, because it sits OUTSIDE the sheet, over whatever the canvas has there. */
/* The Filter-by tick list: one per column, wrapping, so a wide table doesn't run a single
   line off the panel's edge. */
.sheet-filter-cols {
  display: flex; flex-wrap: wrap; gap: 2px 10px;
  flex: 1 1 auto; min-width: 0;
}
.sheet-edit-corner, .sheet-edit-letter, .sheet-edit-rownum {
  display: flex; align-items: center; justify-content: center;
  box-sizing: border-box;
  /* OPAQUE. These sit outside the sheet, over whatever the canvas has there, so anything less
     than solid reads as a hole rather than as a label — which is exactly how row zero looked
     when the whole element carried an opacity instead of just its text. */
  background: var(--editor-panel-background-color);
  color: var(--editor-text-color);
  border: 1px solid var(--panel-border-color);
  overflow: hidden; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.sheet-edit-letter, .sheet-edit-rownum { cursor: grab; }
.sheet-edit-corner { cursor: pointer; }
/* A heading row's number (0, or 0.1 / 0.2 …) is dimmer: zero is a label for the headings
   rather than a row you can count. */
.sheet-edit-rownum.title-num { cursor: pointer; color: var(--editor-muted-text-color); }
/* Row zero's label is dimmed by default; when its band is what you have hold of it takes the
   same blue the other handles do, rather than merely un-dimming. */
.sheet-edit-rownum.title-num.selected, .sheet-edit-rownum.title-num.lit {
  color: var(--editor-selected-blue);
}
.sheet-edit-letter.heading-col { font-weight: 700; }
/* THE CHROME IS NEVER THE THING SELECTED. A letter, a number and the corner are HANDLES: they
   exist only while you are editing, are never drawn on the canvas, and point at what is
   selected rather than being it.
   So they say so IN THE TEXT and never with a fill. A wash behind a reference cell reads as
   that cell being selected — which is the one thing it can't be — and against the dark panel
   these sit on it read as a second, competing selection beside the real one out in the table.
   The letter goes blue and bold; the strip it lives in stays exactly as it was. */
.sheet-edit-corner.lit, .sheet-edit-letter.lit, .sheet-edit-rownum.lit,
.sheet-edit-letter.selected, .sheet-edit-rownum.selected {
  color: var(--editor-selected-blue);
  font-weight: 700;
}
/* A CELL's hit area is TRANSPARENT — the canvas is drawing the cell, and a second copy of it
   here is exactly the duplication this whole design avoids. Only the selection paints. */
.sheet-edit-cell {
  box-sizing: border-box;
  cursor: cell;
  background: transparent;
}
/* SELECTED — the same blue a selected row in a list wears in dark mode.
   NOT `var(--tree-selection-bg)`: that token is OPAQUE in light mode (a solid pale blue), which
   over a cell would hide the very value you selected it to look at. Its dark-mode value is
   translucent and works over anything — a dark cell reads as a selected dark row, a light cell
   as a pale tint, and the text stays readable either way. Stated once, the same in both themes,
   because what it has to sit on top of is the SHEET's colours and not the editor's. */
:root { --sheet-cell-selection: rgba(16, 151, 226, 0.30); }
.sheet-edit-cell.selected { background: var(--sheet-cell-selection); }
/* A CELL AT ODDS WITH ITS COLUMN. `outline`, not `box-shadow`, on purpose: the selection edges
   are drawn as an inline box-shadow, and a cell can be both selected and wrong at once. Two
   different properties means neither has to know about the other.
   The colours are the customizable ones — see `errorColor` / `warningColor` in preferences. A
   faint wash as well as the ring, because at a low zoom a 1.5px outline on a small cell is
   nearly invisible and the wash is what you notice from across the sheet. */
.sheet-edit-cell.issue-error,
.sheet-edit-cell.issue-warning {
  outline-style: solid;
  outline-width: 1.5px;
  outline-offset: -1.5px;
}
.sheet-edit-cell.issue-error {
  outline-color: var(--error-color, #c62828);
  background: color-mix(in srgb, var(--error-color, #c62828) 12%, transparent);
}
.sheet-edit-cell.issue-warning {
  outline-color: var(--warning-color, #b26a00);
  background: color-mix(in srgb, var(--warning-color, #b26a00) 12%, transparent);
}
/* Selection still wins the FILL — you have to be able to see what you have picked — but the
   ring stays, so selecting a wrong cell doesn't hide that it is wrong. */
.sheet-edit-cell.issue-error.selected,
.sheet-edit-cell.issue-warning.selected { background: var(--sheet-cell-selection); }
/* The ghost of what is being dragged, following the pointer. */
/* THE MOVE HANDLE — small, solid and straddling the corner, so it never covers a value. Its
   stacking sits with its geometry, in the component (`SheetCanvasEditor`): it has to be over
   every sibling, and that is a fact about where it is drawn, not about how it looks. */
.sheet-edit-move {
  background: var(--editor-selected-blue);
  border: 1px solid var(--editor-bg-color);
  border-radius: 2px;
  cursor: move;
  box-sizing: border-box;
}
/* The grab strips sit on the rule between one column (or row) and the next. Invisible until
   reached for — a table ringed with visible handles reads as a control panel. */
.sheet-edit-col-grab, .sheet-edit-row-grab { background: transparent; }
.sheet-edit-col-grab { cursor: col-resize; }
.sheet-edit-row-grab { cursor: row-resize; }
.sheet-edit-col-grab:hover, .sheet-edit-row-grab:hover {
  background: var(--editor-selected-blue); opacity: 0.55;
}
/* EVERY ROW RULE AT ONCE. One height governs all the data rows, so the affordance has to show
   all of them — `lit` is set on every rule while the pointer is on any one of them and for the
   whole of the drag (see SheetCanvasEditor). The column rules keep the plain :hover above,
   because dragging one of those really does resize one column. */
.sheet-edit-row-grab.lit {
  background: var(--editor-selected-blue); opacity: 0.55;
}
.sheet-edit-input {
  box-sizing: border-box; border: none; margin: 0;
  outline-style: solid; outline-color: var(--editor-selected-blue);
  /* TRANSPARENT. A cell that changes colour the moment you type in it reads as a different
     cell; the caret already says where you are, and the outline says which one. Nothing shows
     through because the canvas is drawing that cell BLANK while the input is open.
     This says it; `.on-canvas-field` on the element is what stops the dark-theme panel-input
     rule (far above) from saying otherwise. */
  background: transparent;
  font-family: inherit;
}

/* THE FORMATTING BAR — acts on the selection, so it lives on the page beside the grid. */
.sheet-format-bar, .sheet-fmt-row {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.sheet-format-bar.disabled { opacity: 0.45; }
.sheet-fmt-face { max-width: 130px; font-size: var(--editor-control-font-size); }
.sheet-format-bar .sheet-fmt-size {
  flex: 0 0 46px; width: 46px; min-width: 0; text-align: center;
}
.sheet-fmt-btn {
  min-width: 26px; height: 24px; padding: 0 6px;
  border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm);
  background: transparent; color: var(--editor-text-color);
  font-size: var(--editor-control-font-size); cursor: pointer;
}
.sheet-fmt-btn:nth-of-type(1) { font-weight: 700; }
.sheet-fmt-btn:disabled { cursor: default; }
.sheet-fmt-btn.on {
  background: var(--tree-selection-bg, rgba(127, 127, 127, 0.25));
  border-color: var(--editor-selected-blue);
}
/* Each button LOOKS like what it does — the fastest label there is. */
.sheet-format-bar .sheet-fmt-btn:nth-of-type(2) { font-style: italic; }
.sheet-format-bar .sheet-fmt-btn:nth-of-type(3) { text-decoration: underline; }
.sheet-format-bar .sheet-fmt-btn.strike { text-decoration: line-through; }
/* The alignment trio is a pick-one set, so none of the emphasis styling applies to it. */
.sheet-format-bar .sheet-fmt-btn.align { font-style: normal; text-decoration: none; font-weight: 400; }
/* COLOUR. The swatch IS the control's state; the native picker sits over it at zero size so
   clicking anywhere on the button opens it, which is the only way to get a system colour
   picker without shipping one. */
.sheet-fmt-color {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 24px; padding: 0 6px 3px;
  border: 1px solid var(--editor-border-color); border-radius: var(--corner-radius-sm);
  font-size: var(--editor-control-font-size); cursor: pointer;
}
.sheet-format-bar.disabled .sheet-fmt-color { cursor: default; }
.sheet-fmt-color input[type="color"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; border: none; padding: 0; cursor: inherit;
}
/* The bar under the glyph, as a word processor draws it. "transparent" reads as none, which is
   what an unset colour is. */
.sheet-fmt-swatch {
  position: absolute; left: 3px; right: 3px; bottom: 2px; height: 3px;
  border-radius: 1px; box-shadow: inset 0 0 0 1px var(--editor-border-color);
}

/* THE FOOTER'S PAGE ARROWS, while editing. Invisible until reached for, like the resize
   strips — a table ringed with visible controls reads as a control panel. Dimmed rather than
   removed at the ends, so the other one never jumps sideways under the pointer. */
.sheet-edit-page { background: transparent; cursor: pointer; }
.sheet-edit-page:hover { background: var(--editor-selected-blue); opacity: 0.35; }
.sheet-edit-page.off { cursor: default; }
.sheet-edit-page.off:hover { background: transparent; }

/* MIXED — the selection disagrees about this one.
   Deliberately not "off" and not a value: a control showing one member's answer for all of
   them says the selection is 11pt Arial when half of it is 14pt Georgia, and nothing on screen
   says otherwise. The hatch reads as "several", and pressing or typing settles it. */
.sheet-fmt-btn.mixed {
  border-style: dashed;
  border-color: var(--editor-selected-blue);
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 3px,
    var(--tree-selection-bg, rgba(127, 127, 127, 0.30)) 3px 6px);
}
.sheet-select.mixed, .sheet-num.mixed { font-style: italic; opacity: 0.85; }
/* The colour rows of a selection: the app's own mixed-colour list, plus the row that sets
   every part of the selection at once. */
.sheet-colors { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sheet-colors .selection-color-row { margin: 0; }
.sheet-color-all { display: flex; align-items: center; gap: 4px; }

/* Editing a cell: the input fills it, so the text doesn't jump as edit mode starts. */
.sheet-edit {
  width: 100%; min-width: 0; box-sizing: border-box;
  border: none; outline: 2px solid var(--editor-selected-blue); border-radius: 2px;
  background: var(--editor-bg-color); color: var(--editor-text-color);
  font-size: var(--editor-control-font-size); padding: 0 4px;
}

/* ── Shape text section ───────────────────────────────────────────────────
   Text belonging to a SHAPE (see sections/ShapeText.tsx). Only three classes:
   the section otherwise reuses the Note section's row/label layout and the
   established export-button / layout-select controls, so it sits in the panel
   like everything around it. */
/* Two classes deep so it beats `.details-scroll input { width: 100% }`: at 100% the
   field demanded the whole row, and `.note-row` wraps — which dropped it BELOW its own
   label. A label belongs beside the control it names. */
.note-row .shape-text-input {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}
/* ── Set Style rows (components/VisualDeltaRows.tsx) ──────────────────────────────────────────
   A label column and a control column, like the Effects detail: the labels are aligned ACROSS
   the rows, which is the one reason a label column exists, and it is a GRID column sized
   `max-content`, so it hugs the widest label by measurement. Each row is a subgrid of it.

   The block brings its OWN level-1 indent (--panel-indent), so its caller must not wrap it in
   another indented row — `.additional-ib-properties` around it is what put these rows a level
   deeper than the behaviour's other properties.

   Every control is the panel's standard size: edit boxes and combos are the 24px standard from
   the `.details-scroll` rules, the swatch is the design panel's `.color-div`, and Reset is the
   panel's Reset button (`.angle-reset`). The rules below only pin WIDTHS, element + two classes
   deep so they out-weigh `.details-scroll input / select { width: 100% }`.

   NOTHING HERE MAY EXCEED THE ROW. One field per row gives up width first (the hex, the
   number, the cursor combo); everything else is fixed. */
.cond-rows {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
}
.cond-row {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  column-gap: var(--panel-checkbox-label-gap);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}
.cond-label {
  white-space: nowrap;
  user-select: none;
  color: var(--editor-text-color);
}
/* Relative, because the colour picker a swatch here opens is rendered inside it. */
.cond-controls {
  display: flex;
  align-items: center;
  position: relative;
  min-width: 0;
}
.cond-controls input.color-text-input,
.cond-controls input.cond-input,
.cond-controls select.cond-select {
  flex: 1 1 0;
  width: auto;
  min-width: 0;
}
/* Room for an eight-digit hex, which is how a translucent colour reads here (no alpha box). */
.cond-controls input.color-text-input { max-width: 96px; }
.cond-controls input.cond-input { max-width: 84px; margin-right: var(--panel-checkbox-label-gap); }
.cond-controls select.cond-select { margin-right: var(--panel-checkbox-label-gap); }
.cond-controls select.cond-unit {
  flex: 0 0 auto;
  width: auto;
  min-width: 0;
  margin-right: var(--panel-checkbox-label-gap);
}
/* The no-colour swatch: the field is unset, so the item's own colour is used. The panel's
   "none" mark — a diagonal stroke across an empty swatch — rather than black, which would read
   as a colour that is set. */
.color-div.color-div-unset {
  background: linear-gradient(to top right,
    transparent calc(50% - 1px),
    var(--editor-muted-text-color) calc(50% - 1px),
    var(--editor-muted-text-color) calc(50% + 1px),
    transparent calc(50% + 1px));
}
/* The condition chips wrap — six of them will not sit on one line in a narrow panel, and a row
   that breaks between chips reads as what it is. This is the one row that may. */
.cond-pick-row { flex-wrap: wrap; row-gap: 2px; }
.cond-chip {
  padding: 2px 8px;
  font-size: var(--editor-control-font-size);
  color: var(--editor-text-color);
  user-select: none;
}
/* A chip whose condition HOLDS something is marked, so the whole set reads at a glance instead
   of one click at a time. A dot rather than a colour, which a theme can wash out. */
.cond-chip-set::after {
  content: "•";
  margin-left: 4px;
  color: var(--editor-accent-color, currentColor);
}

.shape-text-check {
  display: inline-flex;
  align-items: center;
  gap: var(--panel-checkbox-label-gap);
  white-space: nowrap;
}
/* ── Line Text section ────────────────────────────────────────────────────
   A label belonging to a LINE (see sections/LineText.tsx). Reuses the Note
   section's rows and the shape-text checkbox; what is its own is the POSITION
   track (a place along the route is a thing to point at, not three buttons)
   and the two length/measure fields beside their labels. */
/* The size box + caret and the unit dropdown share one line; the size box is the one that
   gives up width when the panel narrows. Same treatment `.measure-row` already gives its own
   size picker, for the same reason. Without these the picker's wrapper stretched across the
   row, pushing the unit onto a line of its own and the size list (right-aligned to the
   wrapper) off to the panel's far edge. */
/* Shape Text's Size and Weight rows are the same controls on another owner, and share these. */
.line-text-size-row, .shape-text-size-row { flex-wrap: nowrap; }
.line-text-size-row .sizepicker, .shape-text-size-row .sizepicker { flex: 1 1 auto; min-width: 0; margin-right: 0; }
.line-text-size-row .sizepicker input.font-div-size,
.shape-text-size-row .sizepicker input.font-div-size { flex: 1 1 auto; width: auto; min-width: 0; }
.line-text-unit, .shape-text-unit { flex: 0 0 62px; width: 62px; }
.line-text-weight, .shape-text-weight { flex: 1 1 0; min-width: 0; }
/* The track and its three named stops share one column, so the labels line up
   under the ends and the middle of the track itself. */
.line-text-pos {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.line-text-pos-track { width: 100%; margin: 0; }
.line-text-pos-stops {
  display: flex;
  justify-content: space-between;
  font-size: 0.68rem;
  opacity: 0.65;
}
.line-text-pos-stop { cursor: pointer; }
.line-text-pos-stop:hover { opacity: 1; text-decoration: underline; }
/* The stop the label is actually sitting on, so the track says where it is even
   when the handle is between two of them. */
.line-text-pos-stop.on { opacity: 1; color: var(--editor-selected-blue); }
/* Both fixed-basis, not plain width: `.details-scroll input { width: 100% }`
   out-specifies a width and would push the row off the panel edge. */
.line-text-pct { flex: 0 0 52px; width: 52px; }
/* The length field takes the rest of the line between Auto and Clear — it was a fixed 60px,
   too tight to show "13.1 px" comfortably. Shape Text's Inset (`.shape-text-inset`) wears the
   same class, so the two are the same control. */
.line-text-offset { flex: 1 1 0; width: auto; min-width: 0; }
/* Offset carries FOUR things on one line — the label, Auto, the length and Clear — where
   every other row here carries two. `.note-row` wraps, so at the section's usual label
   column Clear dropped to a line of its own. It runs narrower instead: a short label
   column (the word "Offset" needs nothing like 72px), tighter gaps, and no wrap, with the
   length field the only thing allowed to give. */
.line-text-offset-row { flex-wrap: nowrap; gap: 6px; }
.line-text-offset-row .note-label { width: 44px; }
/* Clear wears the reset button's look at a width that actually fits beside them. */
.line-text-offset-clear {
  flex: 0 0 auto;
  white-space: nowrap;
  padding-left: 7px;
  padding-right: 7px;
  margin-left: 0;
}
/* Advanced mode: a text BLOCK on the route. The body gets a real multi-line box, and
   the two spacing fields share a line the way the panel's paired fields do. */
/* Advanced: the panel's real rich-text editor, which brings its own look — this just
   gives it the row's remaining width. */
.line-text-rich {
  flex: 1 1 auto;
  min-width: 0;
}
/* …and inside it the editor is a CONTROL on a labelled row, not a row of its own. In the Text
   section it IS the row, so it carries the level-1 indent, the right gutter, a width that
   subtracts both, and the row's vertical spacing. Here the `.note-row` already gives all four,
   so they were applied twice: the box started a whole --panel-indent (28px) right of every other
   control in the section and stopped short of their right edge. */
.line-text-rich .vi-rich-text {
  width: 100%;
  margin: 0;
}
.line-text-align { flex: 0 0 118px; width: 118px; }
/* Mode: Basic / Advanced side by side on the label's line, at the width the two cells need.
   It shared the Align select's fixed 118px, which is narrower than the two cells with their
   padding (about 140px), so the segmented control wrapped Advanced onto a line of its own. */
.line-text-mode { flex: 0 0 auto; flex-wrap: nowrap; }
/* Justify: one choice of three, so the app's segmented control (as in Text -> Type)
   sitting on the label's line rather than under it. */
.line-text-justify { flex: 1 1 auto; min-width: 0; gap: 12px; }
.line-text-line-spacing, .line-text-letter-spacing { flex: 0 0 64px; width: 64px; }
/* The B / I / U / S group, shared with the Text section (.deco-btn / .text-rich-hint),
   spaced by the row's own gap rather than the buttons' stock left margin. */
.line-text-format-row { gap: 4px; }
.line-text-format-row .deco-btn { margin-left: 0; }
/* Nothing on the Offset line may shrink except the length field. */
.line-text-offset-row .shape-text-check { flex: 0 0 auto; }
/* Auto is choosing the number: show the one it chose, plainly not typed in. */
.line-text-offset.is-auto { opacity: 0.55; font-style: italic; }

/* The words don't fit and are therefore NOT drawn. Stated in the panel because
   the canvas can't say it — nothing appears there, which on its own reads as a
   bug rather than as a refusal. */
.shape-text-warning {
  color: var(--editor-danger-color, #d9534f);
  font-size: 11px;
}

/* The Structure tree's hover, drawn while a hierarchy is merely SELECTED (see
   `HierarchyHoverTint`) — decorative only, pointer-events stay off everything in it, on
   purpose: this must never compete with the canvas's own click/drag over a selected item. */
.hier-hover-tint-surface {
  position: absolute; left: 0; top: 0;
  transform-origin: 0 0;
  z-index: 40;
  width: 0; height: 0;
  pointer-events: none;
}
.hier-hover-tint-cell { position: absolute; pointer-events: none; }

/* ── EDITING A HIERARCHY ON THE CANVAS ───────────────────────────────────────────────────
   The hierarchy's own cells are drawn by the RENDERER, from the model, exactly as they are
   outside edit mode — these rules are only the wash over everything else and the transparent
   hit areas over each cell. Mirrors the sheet's editor, whose division of labour this copies. */
.hier-edit-dim {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* pointer-events: none is set on the element itself — see HierarchyCanvasEditor. */
  z-index: 55;
}
.hier-edit-dim path { fill: rgba(0, 0, 0, 0.45); }
.hier-edit-surface {
  position: absolute; left: 0; top: 0;
  transform-origin: 0 0;
  z-index: 56;
  /* No size and no background: a coordinate space, not a box. Its children carry the pointer
     targets, so the empty space between them stays the canvas's. */
  width: 0; height: 0;
}
/* The cell underneath is the drawing, and this is only where the pointer lands — so the only
   thing painted here is what the DRAWING has no opinion about: which node you are pointing at
   and which one you have picked. Anything more would be a second opinion about what a
   hierarchy looks like. */
.hier-edit-cell {
  position: absolute;
  box-sizing: border-box;
  cursor: pointer;
  border: 1px solid transparent;
}
/* HOVER, the way a running one lights a row: a tint over the cell, not just an outline, since
   an outline alone reads as a selection rather than as "this is what you would click". The
   same green `TColor.hover` tints the canvas with everywhere else a hover means "this, on the
   Structure tree too" — a sheet's cells, the layers tree's own preview — rather than the
   picked-a-thing-to-click blue this used to be its own colour for. */
.hier-edit-cell.hovered {
  background: rgba(41, 224, 108, 0.22);
  border-color: rgba(41, 224, 108, 0.6);
}
/* The one drilled open, and the one the right-hand panel is about. */
.hier-edit-cell.open { border-color: rgba(45, 140, 255, 0.5); }
.hier-edit-cell.picked { border-color: #2d8cff; border-width: 2px; }
/* THE DISCLOSURE TRIANGLE'S own target, over its row's. Bigger than the triangle it covers
   (see kMarkerPad) because a marker is around 11 units across and a missed one doesn't do
   nothing — it picks the node instead of folding it. */
.hier-edit-marker {
  position: absolute;
  box-sizing: border-box;
  cursor: pointer;
  border-radius: 3px;
}
.hier-edit-marker:hover { background: rgba(45, 140, 255, 0.22); }

/* THE EFFECTS DETAIL — Type / Subtype / Value for the effect picked in the tree above it.
   Level 1, like the tree, and one label column so the three controls start at one x: the
   labels are aligned ACROSS the rows, which is the one reason a label column exists. It is a
   GRID column sized `max-content`, so it hugs the widest label ("Subtype") by measurement
   rather than by a width somebody guessed; each row is a subgrid of it. */
/* THE EFFECT AND TEXTURE DETAIL: a label column and a control column, one row per setting.
   The label column is a FIXED width, not `max-content` shared through a subgrid. Shared, its
   width was the longest label the current texture happened to show, so switching textures moved
   the control column; the Texture row's buttons then wrapped onto a different number of lines,
   and Firefox kept the row at its old height, so the third line ("Dunes") sat over the Type row
   below. A fixed column means a texture change never moves a control, and each row is a plain
   grid that is always as tall as what it holds. 6em fits the longest label, "Perspective" (5.2em),
   and scales with the editor's font size. */
.effect-detail {
  /* Kept as a local alias so the many `var(--effect-label-col)` uses below read the same;
     the VALUE now lives once, at the top, as --panel-label-col. */
  --effect-label-col: var(--panel-label-col);
  display: flex;
  flex-direction: column;
  margin: 0 var(--panel-right-gutter) 0 var(--panel-indent);
  font-size: var(--editor-control-font-size);
}
.effect-detail-row {
  display: grid;
  grid-template-columns: var(--effect-label-col) minmax(0, 1fr);
  align-items: center;
  column-gap: var(--panel-checkbox-label-gap);
  padding-top: var(--details-row-pad);
  padding-bottom: var(--details-row-pad);
}
/* A control keeps its own width rather than stretching across the column. */
.effect-detail-row > :not(.effect-detail-label) {
  justify-self: start;
}
.effect-detail-label {
  white-space: nowrap;
  user-select: none;
}
/* THE SEVERAL-OF-THESE CONTROL (shared/SegmentedToggles): a short set of checkboxes on one
   row, reading as a sentence — which paints an effect is on, say. The checkbox row itself is
   the panel's own `.proto-check-label`; this only puts them side by side and keeps them from
   wrapping, since the set is the control and a set split over two lines reads as two. */
.toggle-set {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 10px;
  min-width: 0;
}
.toggle-set .toggle-cell {
  white-space: nowrap;
}

/* A SEGMENTED CHOICE IN THE EFFECT DETAIL whose cells are WORDS — a cloud's Cumulus / Stratus /
   Cirrus, a dune field's Rolling / Crescent / Ripples / Towering. Tighter side padding lets a row
   of names fit on one line in a panel of ordinary width. The row still WRAPS when the names don't
   fit: it used to be `nowrap` on the belief that the cells would shrink to their share, but a
   `white-space: nowrap` flex item never shrinks below its text, so the four dune names ran past
   the panel's right edge instead. Each cell is as wide as its own name — no growing to fill the
   line, which stretched a carried cell across the whole panel (Loren: "greedy buttons"). */
.effect-detail-row .effect-grain-seg {
  min-width: 0;
}
.effect-detail-row .effect-grain-seg .text-type-cell {
  padding-left: 6px;
  padding-right: 6px;
  text-align: center;
  justify-content: center;
}

/* A SLIDER AND ITS NUMBER in the effect detail grid — the pair the image adjustments use,
   in this grid's control column. The slider keeps its width and the box takes what is left,
   which is the rule `.scale-skew-slider` already states. */
.effect-slider-field {
  display: flex;
  align-items: center;
  gap: 6px;
}
.effect-detail-row input.blur-input.effect-slider-number {
  width: 44px;
}

/* MARBLE PRESETS — a row of stone/vein PAIRS, each swatch painted as the stone with a vein
   across it, so the pair can be judged without reading two hex codes. The selected outline is
   the align cell's, because "this is the one you are on" should look the same everywhere. */
.effect-preset-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.effect-preset-swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: var(--editor-control-border);
  border-radius: var(--corner-radius-sm);
  cursor: pointer;
  box-sizing: border-box;
  outline: var(--selection-border-width) solid transparent;
  outline-offset: calc(-1 * var(--selection-border-width));
}
.effect-preset-swatch-selected {
  outline-color: var(--editor-selected-blue);
}

/* The seed's Random button sits beside its field rather than at the row's far end: the
   detail grid's control column is sized to its content, so `margin-left: auto` (from the
   shared button rule) would have nothing to push against and the two would touch. */
.effect-seed-row {
  column-gap: var(--panel-checkbox-label-gap);
}
.effect-seed-row .effect-seed-field {
  display: flex;
  align-items: center;
  gap: 6px;
}
.effect-seed-row .effect-seed-new {
  margin-left: 0;
}

/* The value is one short number, so the field is pinned narrow. Element + two classes on
   purpose: `.details-scroll input { width: 100% }` out-weighs a lone class, and would
   stretch it across the panel. */
.effect-detail-row input.blur-input {
  width: 72px;
  min-width: 0;
}
/* The Subtype combo, pinned to its own content for the same reason — the global
   `.details-scroll select { width: 100% }`. */
.effect-detail-row select.effect-kind-select {
  width: max-content;
  padding: 2px 4px;
  border: 1px solid var(--editor-control-border-color);
  border-radius: var(--corner-radius-sm);
  background-color: var(--editor-input-bg, var(--editor-panel-background-color));
  color: var(--editor-text-color);
  font-size: var(--editor-control-font-size);
}
/* An effect's colour setting (Tint / Duotone): the panel's own swatch, hex and opacity row,
   in the detail grid's control column. `.color-details-div` is that row everywhere else, but
   it carries a level-1 indent and its own row padding, which the detail row already gives —
   so this is its layout without them. Relative, because the picker it opens anchors here. */
.effect-color {
  display: flex;
  align-items: center;
  position: relative;
  min-width: 0;
}

/* What a render crash looks like instead of a blank white page — see
   app/ErrorBoundary.tsx. Built to match `disconnected.css`, the app's other
   full-screen "something is wrong" surface: same editor theme variables, same
   panel-on-page construction, same z-index band. The two are the only screens
   that replace the whole app, and a person who has seen one should recognise
   the other.

   It is NOT an overlay. Disconnected covers a tree that is still mounted and
   still fine; this one is shown because the tree unmounted, so there is nothing
   underneath to preserve and nothing to reveal on recovery. */

.crash-screen {
  position: fixed;
  inset: 0;
  z-index: 9100;               /* above Disconnected: a crash outranks an outage */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  overflow: auto;
  background-color: var(--editor-panel-background-color, #1e1e1e);
  color: var(--editor-text-color, #eee);
  font-family: var(--editor-font-family, Arial, Helvetica, sans-serif);
}

.crash-card {
  width: 40rem;
  max-width: 100%;
  padding: 2.25rem 2rem;
  border: 1px solid var(--editor-control-border-color, #444);
  border-radius: 12px;
  background-color: var(--editor-input-bg, #15171c);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}

.crash-title {
  margin: 0 0 0.85rem;
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1.25;
}

.crash-body {
  margin: 0 0 1.4rem;
  line-height: 1.55;
  color: var(--editor-muted-text-color, #b9bec6);
}

.crash-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 1.4rem;
}

.crash-btn {
  padding: 0.55rem 1.05rem;
  border-radius: 8px;
  border: 1px solid var(--editor-control-border-color, #444);
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.crash-btn:hover { border-color: var(--editor-text-color, #eee); }

.crash-btn-primary {
  background-color: var(--editor-selected-blue, #1097e2);
  border-color: var(--editor-selected-blue, #1097e2);
  color: #fff;
  font-weight: 600;
}

.crash-btn-primary:hover { filter: brightness(1.08); }

/* The app's global active-translate convention: an enabled button nudges down
   1px while held. */
.crash-btn:active { transform: translateY(1px); }

.crash-detail-toggle {
  padding: 0;
  border: none;
  background: none;
  color: var(--editor-selected-blue, #1097e2);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* Monospace and scrollable, because this is the text a user is being asked to
   copy into a bug report — it has to be selectable and readable, not tidy. */
.crash-detail {
  margin: 0.9rem 0 0;
  padding: 0.85rem;
  max-height: 16rem;
  overflow: auto;
  border: 1px solid var(--editor-control-border-color, #444);
  border-radius: 8px;
  background-color: var(--editor-panel-background-color, #1e1e1e);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
  user-select: text;
}

.crash-foot {
  margin: 1.2rem 0 0;
  font-size: 0.85rem;
  color: var(--editor-muted-text-color, #b9bec6);
}

/* The small-screen interstitial that stands in for the editor. Deliberately plain:
   it is a message, not a screen anyone should want to linger on. */
/* IT MUST NOT REPEAT THE FAULT IT DESCRIBES. Centred with a min-height, this
   card was TALLER than a landscape phone's 390px viewport — and the page didn't
   scroll, so "Copy link" and both buttons sat below the fold, unreachable, on the
   exact device the screen exists for. Top-aligned and scrollable now. */
.wider-screen-page {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    height: 100%;
    max-height: 100vh;
    overflow-y: auto;
    padding: 16px;
    box-sizing: border-box;
}
.wider-screen-card {
    max-width: 46ch;
    padding: 22px 20px;
    border: 1px solid var(--editor-control-border-color, #3a3a3a);
    border-radius: var(--corner-radius, 8px);
    background-color: var(--editor-panel-background-color, #1e1e1e);
}
.wider-screen-card h1 {
    margin: 0 0 10px;
    font-size: 1.25rem;
}
.wider-screen-card p {
    margin: 0 0 12px;
    line-height: 1.5;
    color: var(--editor-muted-text-color, #aaa);
}
.wider-screen-lead { margin-bottom: 6px !important; }
/* The link has to be readable AND selectable on a phone — that's the whole point
   of showing it, so it can be sent to a machine with the room. */
.wider-screen-link {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}
.wider-screen-link code {
    display: block;
    padding: 8px 10px;
    font-size: 0.8rem;
    line-height: 1.4;
    overflow-wrap: anywhere;
    user-select: all;
    border: 1px solid var(--editor-control-border-color, #3a3a3a);
    border-radius: var(--corner-radius-sm, 4px);
    background-color: var(--editor-input-bg, #141414);
}
.wider-screen-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* A phone held sideways is ~390px tall, and most of that is this card. Tighten
   everything so the actions stay on screen without scrolling at all. */
@media (max-height: 520px) {
    .wider-screen-page { padding: 10px; }
    .wider-screen-card { padding: 14px 16px; }
    .wider-screen-card h1 { margin-bottom: 6px; font-size: 1.1rem; }
    .wider-screen-card p { margin-bottom: 8px; font-size: 0.9rem; line-height: 1.4; }
    .wider-screen-link { margin-bottom: 10px; }
    .wider-screen-link code { font-size: 0.75rem; padding: 6px 8px; }
}

