@layer surfaces {

/* ============================================================================
   Editor v2 app frame: the editor page opts out of the global chrome (session
   bar, centered shell) and brings its own — a fixed 54px nav rail, a sticky
   52px top bar (title + type badge + view controls + sync), and a full-width
   workspace. The WINDOW stays the scroll container (the virtualized renderer
   measures window.scrollY); the frame feel comes from fixed/sticky chrome,
   never from an inner-scrolling canvas.
   ========================================================================= */
.app-shell--frame {
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
}

/* Typing at the viewport edges auto-scrolls the caret into view; scroll-padding keeps it
   clear of the sticky top bar and the pinned statusbar instead of hiding beneath them.
   --chrome-bottom is MEASURED (ui/chrome_metrics): where the bar actually ends, which is not
   its height whenever anything sits above it — the durability banner does. --topbar-h was
   never set by anything, so every consumer of it was silently a constant. */
html:has(.app-shell--frame) {
  scroll-padding-top: calc(var(--chrome-bottom, 3.25rem) + 1.5rem);
  scroll-padding-bottom: 5rem;
}

.editor-page--frame {
  max-width: none;
  margin: 0;
  padding-left: 3.375rem; /* clear the fixed nav rail */
  padding-right: 3.25rem; /* clear the fixed lens rail */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.editor-page--frame .editor-surface {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* The workspace (or board) absorbs the slack so the statusbar sits at the viewport
   bottom even when the document is short; sticky keeps it pinned once it scrolls. */
.editor-page--frame .editor-workspace,
.editor-surface[data-view-mode="board"] .document-board {
  flex: 1;
}

/* The global rail (left-matter model, 2026-07-23): the same fixed rail serves every
   signed-in page. Outside the editor it renders as a body-level sibling of main, so the
   sibling offsets below clear it; inside the editor it lives in .editor-surface and the
   workspace grid already accounts for it. */
.app-shell--rail {
  --rail-w: 3.375rem;
  /* .app-shell says width: min(100%, 72rem); with a forced left margin that overflows
     any viewport narrower than 72rem + the rail — the margin must come out of the width.
     And the margin must CENTER the shell in the space right of the rail: a bare
     margin-left overrides the shorthand's left `auto`, leaving the right margin to
     absorb all the slack — on an ultrawide the content hugged the rail (punch list,
     2026-08-06). */
  width: min(100% - var(--rail-w), 72rem);
  margin-left: calc(var(--rail-w) + (100% - var(--rail-w) - min(100% - var(--rail-w), 72rem)) / 2);
}

/* The sibling form covers rail-bearing pages; the :has form covers the editor, whose
   rail lives inside <main> and so is never the banner's sibling. */
.editor-navrail ~ .persist-banner,
body:has(.app-shell--frame) .persist-banner {
  margin-left: 3.375rem;
}

/* The dashboard's fixed drawer would otherwise run over the eviction warning. */
@media (min-width: 1025px) {
  body:has(.project-dashboard[data-drawer="open"]) .persist-banner {
    margin-left: calc(3.375rem + 14.75rem);
  }
}

.editor-navrail {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  width: 3.375rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 0.875rem 0;
  background: var(--c-gray-25);
  border-right: 1px solid var(--c-gray-200);
  z-index: 40;
}

.editor-navrail-mark {
  display: block;
  width: 15px;
  height: 15px;
  border-radius: 4px;
  background: var(--c-ink);
  margin-bottom: 0.625rem;
}

.editor-navrail-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--c-gray-400);
  cursor: pointer;
}

.editor-navrail-button:hover {
  color: var(--c-gray-700);
  background: var(--c-gray-100);
}

.editor-navrail-spacer {
  flex: 1;
}

.editor-navrail .theme-toggle {
  flex-direction: column;
}

/* Text (not an SVG icon), so it must clear the 4.5:1 AA contrast bar the icon buttons
   are exempt from — hence gray-600, not the rail's usual gray-400. */
.editor-navrail-button--palette {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--c-gray-600);
}

/* TABLET rung and below: a fixed side column is too expensive, but hiding the rail with
   nothing in its place severs Projects/Account/theme entirely (that was the 2026-08-05
   tablet bug — Sign out was unreachable). The body-level rail every non-editor page
   carries turns into a static top bar; the editor's nested copy stands down because the
   editor topbar already owns that edge. */
@media (max-width: 760px) {
  .app-shell--rail {
    margin-left: 0;
    width: min(100%, 72rem);
  }

  .editor-navrail ~ .persist-banner,
  body:has(.app-shell--frame) .persist-banner {
    margin-left: 0;
  }

  body > .editor-navrail {
    position: static;
    width: auto;
    flex-direction: row;
    gap: 0.375rem;
    padding: 0.5rem 0.875rem;
    border-right: 0;
    border-bottom: 1px solid var(--c-gray-200);
  }

  body > .editor-navrail .editor-navrail-mark {
    margin-bottom: 0;
    margin-right: 0.5rem;
  }

  body > .editor-navrail .theme-toggle {
    flex-direction: row;
  }

  .editor-surface .editor-navrail {
    display: none;
  }

  .editor-page--frame {
    padding-left: 0;
  }
}

/* ---- Project drawer (editor-v3 workspace shell) ------------------------------------
   The binder promoted out of the outline column: a full-height fixed column beside the
   nav rail on desktop (the page pads left to clear it, same trick as the rail).
   data-drawer="closed" hides it and shows the top-bar reveal button instead. ≤1024px it
   flows statically below the workspace under the existing panels toggle. */
.editor-drawer {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 3.375rem;
  width: 14.75rem;
  display: flex;
  flex-direction: column;
  background: var(--c-gray-25);
  border-right: 1px solid var(--c-gray-200);
  z-index: 30;
}

.editor-drawer-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.9rem 0.875rem 0.75rem;
  border-bottom: 1px solid var(--c-gray-200);
}

.editor-drawer-heading {
  flex: 1;
  min-width: 0;
}

/* An <a> since 2026-07-23: the drawer title links to the project dashboard. */
.editor-drawer-title {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--c-gray-900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-decoration: none;
}

.editor-drawer-title:hover,
.editor-drawer-title:focus-visible {
  color: var(--c-accent);
}

.editor-drawer-kicker {
  display: block;
  margin-top: 0.125rem;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-accent);
}

.editor-drawer-collapse,
.editor-drawer-reveal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.625rem;
  height: 1.625rem;
  border: 1px solid var(--c-gray-300);
  border-radius: 7px;
  background: none;
  color: var(--c-gray-400);
  cursor: pointer;
}

.editor-drawer-collapse:hover,
.editor-drawer-reveal:hover {
  color: var(--c-gray-700);
  background: var(--c-gray-100);
}

.editor-drawer-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0.75rem 0.875rem 1rem;
}

/* The reveal button only exists as an affordance for the desktop fixed drawer. */
.editor-drawer-reveal {
  display: none;
}

/* The dashboard hosts the same drawer (left-matter model): same classes, same
   data-drawer mechanics, on the .project-dashboard root instead of .editor-surface.
   Below 1025px the dashboard hero already lists every document, so the drawer and its
   reveal stand down entirely. */
.project-dashboard .editor-drawer-title--static {
  cursor: default;
}

@media (max-width: 1024px) {
  .project-dashboard .editor-drawer,
  .project-dashboard .editor-drawer-reveal {
    display: none;
  }
}

@media (min-width: 1025px) {
  .project-dashboard[data-drawer="closed"] .editor-drawer {
    display: none;
  }

  .project-dashboard[data-drawer="closed"] .editor-drawer-reveal {
    display: block;
    position: fixed;
    left: 3.875rem;
    top: 0.875rem;
    z-index: 25;
    border: 1px solid var(--c-gray-300);
    border-radius: 6px;
    background: var(--c-white);
    color: var(--c-gray-600);
    cursor: pointer;
    padding: 0.3rem 0.4rem;
  }

  .project-dashboard[data-drawer="closed"] .editor-drawer-reveal:hover {
    color: var(--c-gray-900);
    border-color: var(--c-gray-400);
  }

  .project-dashboard[data-drawer="open"] {
    padding-left: 15.75rem;
  }
}

@media (min-width: 1025px) {
  .editor-surface[data-drawer="closed"] .editor-drawer {
    display: none;
  }

  .editor-surface[data-drawer="closed"] .editor-drawer-reveal {
    display: inline-flex;
  }
}

/* Wide desktops: the open drawer pushes the workspace aside (page padding clears rail +
   drawer). Focus mode wins (its padding-left: 0 rule targets the same element), so
   exclude it here — later rules of equal specificity would override it. */
@media (min-width: 1361px) {
  .editor-page--frame:has(.editor-surface[data-drawer="open"]:not([data-focus="true"])) {
    padding-left: calc(3.375rem + 14.75rem);
  }
}

/* Smaller desktops (1025–1360px): all three side surfaces plus the drawer can't share the
   width without starving the prose column, so an open drawer OVERLAYS the workspace
   (temporary-drawer pattern) instead of pushing it — the shadow says "floating". JS
   defaults the drawer closed at these widths; opening it is a deliberate act. */
@media (min-width: 1025px) and (max-width: 1360px) {
  .editor-surface[data-drawer="open"] .editor-drawer {
    box-shadow: 0 0 0 1px var(--c-gray-200), 0 12px 32px var(--c-shadow);
  }
}

.editor-topbar {
  position: sticky;
  top: 0;
  z-index: 25;
  display: flex;
  align-items: center;
  gap: 0.8rem;
  /* The one honest use of --topbar-h: this is the bar's own HEIGHT. Everything that needs to
     sit below it wants --chrome-bottom instead, which is measured from this element. */
  min-height: var(--topbar-h, 3.25rem);
  padding: 0 1.25rem;
  background: var(--c-white);
  border-bottom: 1px solid var(--c-gray-200);
}

.editor-topbar .document-title {
  margin: 0;
  min-width: 0;
  flex-shrink: 1;
}

.editor-topbar .document-title-display,
.editor-topbar .document-title-input {
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}

.editor-doc-type-badge {
  flex: none;
  font-size: 0.59375rem;
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--c-gray-500);
  border: 1px solid var(--c-gray-300);
  border-radius: 5px;
  padding: 0.125rem 0.375rem;
}

.editor-topbar-spacer {
  flex: 1;
}

/* The palette's visible affordance: a keycap chip naming the platform chord (the JS
   stamps ⌘K / Ctrl+K into [data-palette-hint]). gray-600, not lighter — small text
   must clear the AA contrast gate (axe). */
.editor-topbar-palette {
  flex: none;
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--c-gray-300);
  border-radius: 7px;
  background: none;
  padding: 0.25rem 0.5rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--c-gray-600);
  cursor: pointer;
}

.editor-topbar-palette:hover {
  color: var(--c-gray-900);
  border-color: var(--c-gray-400);
}

.editor-page--frame .editor-workspace {
  padding: 1.5rem 1.5rem 0;
}

/* Desktop only — the ≤1024px stacked layout (grid: 1fr) must keep winning.
   The prose column gets a hard floor (30rem) and the outline column absorbs the squeeze:
   with the project drawer open on a 1280px window all three side surfaces don't fit at
   full width, and a starved prose column wraps mid-word (garbling End-key caret placement,
   caught by the live-collab e2e). Writing space wins over nav chrome.

   The workspace itself is capped at the triad's natural width (tracks + gaps + padding,
   tracking the margin's collapsed state via --margin-col) and centered, so on wide screens
   the outline hugs the prose's left edge instead of stranding itself beside the drawer —
   outline and margin belong to the content, not the viewport edges (editor-v3). Capping
   the workspace (not the middle track) leaves narrow-width distribution untouched: at
   1280 the outline keeps its full 13rem, where a narrower column pushed the hover actions
   over the node titles and swallowed outline clicks (caught by the outline e2e). */
@media (min-width: 1025px) {
  .editor-page--frame .editor-workspace {
    --outline-col: 13rem;
    grid-template-columns: minmax(0, var(--outline-col)) minmax(30rem, 1fr) var(--margin-col, 19.25rem);
    gap: 1.75rem;
    max-width: calc(var(--outline-col) + var(--prose-w) + var(--margin-col, 19.25rem) + 3.5rem + 3rem);
    margin-inline: auto;
  }

  /* Lens drawer open: a fourth track joins and the cap widens with it. The outline
     column absorbs the squeeze on tight viewports (writing space keeps its 30rem floor).
     "story" belongs here too — 140 opens the drawer for it like the other three, but it was
     missing from this list, so the making-of panel appeared with no column of its own.

     And the page holds still while it happens. The cluster is centred, so every pixel the
     right side gains slides the prose LEFT by half of it: the drawer swaps a 15.125rem
     gutter for pins + gap + drawer, a net 7.625rem, and the writer's paragraph jumped 61px
     at every width (measured, punch list 2026-08-07). Padding the START by that gain grows
     the box evenly on both sides, so the centring arithmetic lands where it began — and the
     cap grows with it, or border-box would take the padding out of the columns instead.

     Paid only out of slack the window actually has. Buying more starves the outline, which
     absorbs every squeeze here and at half width covers its own node titles with hover
     actions (the outline e2e caught that once). So: still on a roomy screen, a partial
     nudge on a tight one, and on a cramped one the old shift — which is the cheaper loss. */
  .editor-page--frame .editor-surface:is([data-side-panel="scene"], [data-side-panel="versions"], [data-side-panel="cues"], [data-side-panel="story"]) .editor-workspace {
    --drawer-shift: calc(1.75rem + 1.75rem + 19.25rem - 15.125rem);
    /* The narrowest the open cluster can be: the middle track's FLOOR (30rem), not
       --prose-w — the measure only caps the canvas inside a track that is 1fr. Using the cap
       here overstated the cluster by 216px, so the slack below always came out zero and the
       compensation never paid. */
    --drawer-natural-w: calc(var(--outline-col) + 30rem + var(--margin-col, 19.25rem) + 19.25rem + 5.25rem + 3rem);
    --drawer-pay: min(var(--drawer-shift), max(0px, 100% - var(--drawer-natural-w)));
    grid-template-columns: minmax(0, var(--outline-col)) minmax(30rem, 1fr) var(--margin-col, 19.25rem) 19.25rem;
    padding-inline-start: calc(1.5rem + var(--drawer-pay));
    max-width: calc(var(--outline-col) + var(--prose-w) + var(--margin-col, 19.25rem) + 19.25rem + 5.25rem + 3rem + var(--drawer-pay));
  }

  /* Comfortable workspace (ultrawide preference): spend the wings on content — a wider
     outline and a roomier margin-card column — instead of empty paper. Only wings that
     EXIST get spent: at 1800–2199px the expansion yields while a lens drawer holds the
     fourth column (the grid would over-budget and squeeze prose to its floor); from
     2200px there is room for everything. Pins mode keeps winning at any width — a
     collapsed margin is a deliberate act. */
  @media (min-width: 1800px) {
    .editor-surface[data-workspace="comfortable"]:not([data-margin-collapsed="true"]):not([data-side-panel="scene"]):not([data-side-panel="versions"]):not([data-side-panel="cues"]) .editor-workspace {
      --outline-col: 16rem;
      --margin-col: 24rem;
      gap: 2.25rem;
    }
  }

  @media (min-width: 2200px) {
    .editor-surface[data-workspace="comfortable"]:not([data-margin-collapsed="true"]) .editor-workspace {
      --outline-col: 16rem;
      --margin-col: 24rem;
      gap: 2.25rem;
    }
  }

  /* The storyboard replaces the triad with a single full-bleed track — no cap. */
  .editor-surface[data-view-mode="board"] .editor-workspace {
    max-width: none;
  }
}

/* The writing column centers itself inside the flexible middle track. */
.editor-page--frame .editor-canvas {
  max-width: var(--prose-w);
  width: 100%;
  margin-inline: auto;
}

.editor-page--frame .editor-statusbar,
.editor-page--frame .document-board {
  padding-left: 1.25rem;
  padding-right: 1.25rem;
}

/* The sticky song dock rides below the sticky top bar — below where it ENDS, measured, not
   below where its height says it should end. */
.editor-page--frame .editor-track-dock {
  top: var(--chrome-bottom, 3.25rem);
}

}
