.app-shell {
  position: relative;
  display: grid;
  grid-template-columns: var(--sidebar-size) minmax(0, 1fr);
  width: 100%;
  height: 100%;
  transition: grid-template-columns 180ms ease;
}

.app-stage {
  position: relative;
  min-width: 0;
  height: 100%;
  padding: var(--workspace-gutter) var(--workspace-gutter) var(--workspace-gutter) 0;
  overflow: hidden;
  background: var(--bg-primary);
  transition: padding 180ms ease;
}

.panel-resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 50;
  width: var(--resizer-hit-area);
  transform: translateX(-50%);
  border: 0;
  background: transparent;
  cursor: col-resize;
  touch-action: none;
  user-select: none;
}

.sidebar-resizer { left: var(--sidebar-size); }
.panel-resizer:focus-visible { outline-offset: -3px; }

/* The active separator becomes the pane's edge while it is being dragged.
 * This is deliberately a line rather than a shadow: one physical CSS pixel,
 * full accent at its centre, fading cleanly into the upper and lower edge. */
.panel-resizer.is-resizing::after {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: linear-gradient(
    to bottom,
    transparent 0%,
    color-mix(in srgb, var(--color-accent) 18%, transparent) 18%,
    color-mix(in srgb, var(--color-accent) 68%, transparent) 38%,
    var(--color-accent) 50%,
    color-mix(in srgb, var(--color-accent) 68%, transparent) 62%,
    color-mix(in srgb, var(--color-accent) 18%, transparent) 82%,
    transparent 100%
  );
  box-shadow: none;
  content: "";
  pointer-events: none;
}

/* Resting separators share the workspace frame's layer. Lift whichever one is
 * active so its moving one-pixel edge cannot alternate with that frame; this
 * also carries the AI highlight across its open-panel tab. */
.panel-resizer.is-resizing {
  z-index: 52;
}

html.is-resizing,
html.is-resizing * { cursor: col-resize !important; }

html.is-resizing-y,
html.is-resizing-y * { cursor: row-resize !important; }

html.is-resizing .app-shell,
html.sidebar-resize-settling .app-shell,
html.is-resizing .workspace-window { transition: none; }

/* Pointer release and the last grid-track update occur in the same event.
 * Leave the track transition disabled until that geometry has painted once;
 * otherwise Chromium can briefly composite the sidebar at its preceding clip
 * and leave a narrow strip of Library content over the workspace. */

/* Keep direct dragging crisp, but animate the discontinuous minimum-width
 * snap so the closing panel remains spatially legible. */
html.sidebar-snapping .app-shell {
  transition: grid-template-columns 180ms ease;
}

html.sidebar-snapping .sidebar-resizer {
  transition: left 180ms ease;
}

@media (prefers-reduced-motion: reduce) {
  html.sidebar-snapping .app-shell,
  html.sidebar-snapping .sidebar-resizer { transition: none; }
}

html.ai-snapping-shut .app-shell,
html.ai-snapping-shut .workspace-window {
  transition: grid-template-columns 140ms cubic-bezier(0.4, 0, 1, 1);
}

/* Closing from the tab is a deliberate act, so the panel takes a longer, eased
 * run at the edge it is about to become that tab on. */
html.ai-closing .app-shell,
html.ai-closing .workspace-window {
  transition: grid-template-columns 260ms cubic-bezier(0.55, 0, 0.28, 1);
}

html.ai-opening .workspace-window {
  transition: grid-template-columns 280ms cubic-bezier(0.22, 1, 0.36, 1);
}

html.sidebar-compact .brand-name,
html.sidebar-compact .new-canvas > span:last-child,
html.sidebar-compact .sidebar .nav-copy,
html.sidebar-compact .sidebar .nav-heading,
html.sidebar-compact .sidebar-library-chevron,
html.sidebar-compact .profile-copy { display: none; }

html.sidebar-compact .sidebar-library-body { display: none; }

/* Collapsed, the Library is one icon to open it by. Its name, its unsaved
 * marker and its actions all describe a Library that is not on screen, and in a
 * 68px rail they read as clutter beside the icon rather than as anything a
 * teacher can act on. The state they were reporting is still there when the
 * rail is opened, which is the same press that makes it legible again. */
html.sidebar-compact .sidebar-library-name,
html.sidebar-compact .sidebar-library-header .sidebar-library-row-menu-trigger,
html.sidebar-compact .sidebar-course-menu { display: none; }

/* Work in no file yet is the one thing the rail still has to be able to say, so
 * the marker moves onto the icon it was standing beside. It is drawn from the
 * same hidden state the full-width dot reads, rather than kept in step by hand,
 * and it is ringed in the sidebar's own ground so it stays a marker on the
 * glyph instead of another mark within it. */
html.sidebar-compact .sidebar-library-toggle:has(.worksave-unsaved:not([hidden])) .nav-icon-library::after {
  position: absolute;
  top: -2px;
  right: -3px;
  width: 6px;
  height: 6px;
  border: 1.5px solid var(--bg-primary);
  border-radius: 50%;
  background: var(--color-danger, #dc2626);
  content: "";
}

html.sidebar-compact .sidebar { padding-inline: 10px; }
html.sidebar-compact .brand-row { grid-template-columns: 1fr; justify-items: center; }

html.sidebar-compact .new-canvas,
html.sidebar-compact .sidebar .nav-item,
html.sidebar-compact .profile-row { justify-content: center; padding-inline: 0; }
html.sidebar-compact .recent-empty { justify-content: center; }

html.canvas-focus .app-shell {
  grid-template-columns: 0 minmax(0, 1fr);
}

html.canvas-focus .sidebar,
html.canvas-focus .sidebar-resizer,
html.canvas-focus .ai-pane,
html.canvas-focus .ai-resizer {
  visibility: hidden;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

html.canvas-focus .workspace-window {
  grid-template-columns: 0 minmax(0, 1fr);
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

html.canvas-focus .app-stage { padding: 0; }

.mobile-nav-toggle,
.mobile-scrim { display: none; }

.app-toast {
  position: absolute;
  right: 24px;
  bottom: 24px;
  z-index: 150;
  max-width: min(320px, calc(100% - 48px));
  padding: 10px 13px;
  transform: translateY(12px);
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--color-accent);
  color: var(--bg-secondary);
  font-size: 0.75rem;
  opacity: 0;
  pointer-events: none;
  box-shadow: 0 10px 28px rgba(42, 36, 34, 0.18);
  transition: opacity 160ms ease, transform 160ms ease;
}

.app-toast.is-visible {
  transform: translateY(0);
  opacity: 1;
}
