/* shell.css — app layout: sidebar, main scroller, FAB, lock screen,
 * modals, toasts, settings drawer. (Roadmap content styles live in roadmap.css.) */

/* ------------------------------------------------------------------ *
 *  App grid
 * ------------------------------------------------------------------ */
#app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  /* Cap the single implicit row at the viewport. Without this the row is
   * auto-sized and grows to the taller column's content, so #sidebar never gets
   * a bounded height and its inner list can't scroll (only #main, a scroll
   * container, coped). minmax(0,1fr) pins the row to 100dvh. */
  grid-template-rows: minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;
  transition: grid-template-columns var(--dur) var(--ease);
}
#app[data-sidebar="collapsed"] { grid-template-columns: var(--sidebar-w-collapsed) 1fr; }

/* Until unlocked, hide the app chrome behind the lock overlay. */
#app[hidden] { display: none; }

/* ------------------------------------------------------------------ *
 *  Sidebar
 * ------------------------------------------------------------------ */
#sidebar {
  grid-column: 1;
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;         /* allow the flex column to bound .sb-list so it scrolls */
  overflow: hidden;
  z-index: 30;
}
.sb-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s4); height: 60px; flex: none;
  border-bottom: 1px solid var(--border);
}
.sb-logo {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center; font-size: 17px;
  background: linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #7c3aed));
  color: #fff; box-shadow: var(--shadow-sm);
}
.sb-title { font-weight: 700; letter-spacing: -.01em; white-space: nowrap; }
.sb-title small { display: block; font-weight: 500; font-size: var(--t-xs); color: var(--text-3); }
.sb-collapse {
  margin-left: auto; width: 30px; height: 30px; border-radius: 8px; flex: none;
  color: var(--text-2); display: grid; place-items: center;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.sb-collapse:hover { background: var(--surface-3); color: var(--text); }
.sb-collapse svg { transition: transform var(--dur) var(--ease); }
#app[data-sidebar="collapsed"] .sb-collapse svg { transform: rotate(180deg); }

/* Scrollable list. Three things make a long list (all weeks of a roadmap)
 * actually scroll: (1) #app caps its row at the viewport and #sidebar has
 * min-height:0, so this flex child is bounded; (2) min-height:0 here lets it
 * shrink below content; (3) the children must NOT flex-shrink (below) or a
 * column flex container compresses them to fit instead of overflowing. */
.sb-list { flex: 1; min-height: 0; overflow-y: auto; padding: var(--s3) var(--s2); display: flex; flex-direction: column; gap: 2px; }
.sb-list > * { flex-shrink: 0; }   /* don't squish rows — let the list overflow & scroll */
.sb-section-label { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .08em;
  color: var(--text-3); padding: var(--s3) var(--s3) var(--s2); font-weight: 600; }

.sb-item {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3); border-radius: var(--r2);
  color: var(--text-2); position: relative;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast);
  width: 100%; text-align: left; min-width: 0;
}
.sb-item:hover { background: var(--surface-3); color: var(--text); transform: translateX(2px); }
.sb-item[aria-current="true"] { background: var(--accent-soft); color: var(--text); }
.sb-item[aria-current="true"]::before {
  content: ""; position: absolute; left: 0; top: 20%; bottom: 20%;
  width: 3px; border-radius: 3px; background: var(--accent);
}
.sb-ico { font-size: 18px; width: 22px; text-align: center; flex: none; }
.sb-item-body { flex: 1; min-width: 0; }
.sb-item-title { font-weight: 600; font-size: var(--t-sm); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.sb-mini { display: flex; align-items: center; gap: var(--s2); margin-top: 5px; }
.sb-mini .bar { flex: 1; height: 4px; border-radius: 3px; background: var(--track); overflow: hidden; }
.sb-mini .bar > i { display: block; height: 100%; border-radius: 3px; background: var(--accent);
  width: 0; transition: width var(--dur-slow) var(--ease); }
.sb-mini .pct { font-size: var(--t-xs); color: var(--text-3); font-variant-numeric: tabular-nums; }
.track-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--text-3); }
.track-dot.ahead { background: var(--ok); } .track-dot.on { background: var(--info); }
.track-dot.behind { background: var(--warn); }

/* divider + label between the main roadmaps and the "Side track" section */
.sb-sep { height: 1px; margin: var(--s3) var(--s3) var(--s2);
  background: linear-gradient(90deg, transparent, var(--border-2) 20%, var(--border-2) 80%, transparent); }
.sb-section-label.side { color: var(--text-3); display: flex; align-items: center; gap: 6px; }
.sb-section-label.side::before { content: "🧱"; font-size: 12px; filter: grayscale(.2); }

/* global cross-roadmap Today link (top of the sidebar) */
.sb-today { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s3);
  border-radius: var(--r2); color: var(--text-2); position: relative; width: 100%;
  transition: background var(--dur-fast), color var(--dur-fast), transform var(--dur-fast); }
.sb-today:hover { background: var(--surface-3); color: var(--text); transform: translateX(2px); }
.sb-today.active { background: var(--accent-soft); color: var(--text); }
.sb-today.active::before { content: ""; position: absolute; left: 0; top: 20%; bottom: 20%;
  width: 3px; border-radius: 3px; background: var(--accent); }
.sb-today-label { font-weight: 600; font-size: var(--t-sm); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }

/* jump list under an expanded sidebar item. Bounded height + its own scroll so a
 * long roadmap (many weeks) doesn't push the whole sidebar; overscroll-behavior
 * keeps a scroll gesture inside it from also scrolling the outer list. */
.sb-sub { display: flex; flex-direction: column; gap: 1px; padding: 2px 0 var(--s2) 42px;
  max-height: 42vh; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.sb-sub::-webkit-scrollbar { width: 7px; }
.sb-sub::-webkit-scrollbar-track { background: transparent; }
.sb-sub::-webkit-scrollbar-thumb { background: var(--border); border-radius: 6px;
  border: 2px solid transparent; background-clip: padding-box; }
.sb-sub::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
.sb-sub a { font-size: var(--t-sm); color: var(--text-3); padding: 4px var(--s2); border-radius: 6px;
  transition: color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast); }
.sb-sub a:hover { color: var(--text); background: var(--surface-3); transform: translateX(2px); }

/* Roadmap switch: the quick-jump list eases in and its links slide up one after
   another. Applied by renderSidebar only when the expanded roadmap changes (class
   .enter), so it never replays on progress re-renders or in-roadmap jumps.
   Transform + opacity only (compositor-only, no per-frame layout) — the height is
   reserved instantly and fades in over it, which is what keeps it buttery instead
   of the janky max-height tween. Both motion gates zero animation-duration, so
   reduced-motion is honored for free. */
@keyframes sbSubOpen {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}
@keyframes sbSubLink {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: none; }
}
.sb-sub.enter { animation: sbSubOpen .24s var(--ease-out) both; }
.sb-sub.enter a { animation: sbSubLink .26s var(--ease-out) both; animation-delay: var(--sd, 0ms); }

/* collapsed rail: hide text, center icons */
#app[data-sidebar="collapsed"] .sb-title,
#app[data-sidebar="collapsed"] .sb-item-body,
#app[data-sidebar="collapsed"] .sb-section-label,
#app[data-sidebar="collapsed"] .sb-today-label,
#app[data-sidebar="collapsed"] .sb-sub,
#app[data-sidebar="collapsed"] .sb-sep { display: none; }
#app[data-sidebar="collapsed"] .sb-item,
#app[data-sidebar="collapsed"] .sb-today { justify-content: center; padding: var(--s3) 0; }
/* collapsed rail (60px): stack logo over the expand button, both centred and
   in normal flow — never absolutely positioned (that escaped to the viewport). */
#app[data-sidebar="collapsed"] .sb-head { flex-direction: column; justify-content: center;
  height: auto; padding: var(--s3) 0; gap: var(--s2); }
#app[data-sidebar="collapsed"] .sb-collapse { margin: 0; }

.sb-foot { flex: none; padding: var(--s3); border-top: 1px solid var(--border);
  display: flex; align-items: center; gap: var(--s2); font-size: var(--t-xs); color: var(--text-3); }
.sync-pill { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.sync-pill span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sync-pill i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-3); flex: none; }
.sync-pill.online i { background: var(--ok); } .sync-pill.saving i { background: var(--warn); }
.sync-pill.offline i { background: var(--text-3); } .sync-pill.error i { background: var(--bad); }

/* companion-tutorials launcher — pinned to the footer's right edge */
.sb-tut { margin-left: auto; flex: none; width: 30px; height: 30px; border-radius: 8px;
  display: grid; place-items: center; color: var(--text-2);
  transition: background var(--dur-fast), color var(--dur-fast); }
.sb-tut:hover, .sb-tut[aria-expanded="true"] { background: var(--surface-3); color: var(--text); }
/* collapsed rail: stack the footer, hide the sync label, keep both icons centred */
#app[data-sidebar="collapsed"] .sb-foot { flex-direction: column-reverse; gap: var(--s3); }
#app[data-sidebar="collapsed"] #sync-pill-text { display: none; }
#app[data-sidebar="collapsed"] .sb-tut { margin: 0; }

/* the pop-up: fixed over the sidebar (so overflow:hidden can't clip it),
   anchored bottom-right just above the launcher, opening upward */
.tut-pop { position: fixed; z-index: 60; width: 232px; max-width: calc(100vw - 24px);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r3);
  box-shadow: var(--shadow-lg); padding: var(--s2); display: flex; flex-direction: column; gap: 2px;
  transform-origin: bottom right; opacity: 0; transform: translateY(8px) scale(.97);
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.tut-pop.open { opacity: 1; transform: none; }
.tut-pop-head { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-3); font-weight: 700; padding: var(--s2) var(--s2) var(--s1); }
.tut-item { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s2);
  border-radius: var(--r2); color: var(--text); transition: background var(--dur-fast); }
.tut-item:hover { background: var(--surface-3); }
.tut-ico { font-size: 18px; width: 24px; text-align: center; flex: none; }
.tut-tx { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.tut-label { font-size: var(--t-sm); font-weight: 600; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.tut-short { font-size: var(--t-xs); color: var(--text-3); }
.tut-ext { color: var(--text-3); flex: none; display: grid; place-items: center; }
.tut-item:hover .tut-ext { color: var(--accent); }

/* ------------------------------------------------------------------ *
 *  Main scroll region
 * ------------------------------------------------------------------ */
#main {
  grid-column: 2; position: relative; overflow-y: auto; overflow-x: hidden;
  scroll-behavior: smooth;
  /* slimmer, theme-tinted scrollbar (Firefox) */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
/* slimmer scrollbar (WebKit/Blink) — ~9px vs the ~15px OS default */
#main::-webkit-scrollbar { width: 9px; }
#main::-webkit-scrollbar-track { background: transparent; }
#main::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 8px;
  border: 2px solid transparent; background-clip: padding-box;
}
#main::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
#view { max-width: var(--maxread); margin: 0 auto; padding: var(--s6) var(--s5) var(--s8); }
@media (max-width: 720px) { #view { padding: var(--s4) var(--s4) var(--s8); } }

/* ------------------------------------------------------------------ *
 *  View crossfade (switching roadmap / Today)
 *  Only the content area animates: #view gets its own transition name so
 *  the sidebar/chrome (the "root" snapshot) swaps instantly underneath.
 *  Gated in JS on the motion setting + reduced-motion, so this only ever
 *  runs when a crossfade is actually wanted. */
#view { view-transition-name: rm-view; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
::view-transition-old(rm-view) {
  animation: viewOut 200ms var(--ease) both;
}
::view-transition-new(rm-view) {
  animation: viewIn 320ms var(--ease-out) both;
}
@keyframes viewOut { to { opacity: 0; transform: translateY(-8px); } }
@keyframes viewIn  { from { opacity: 0; transform: translateY(12px); } }

/* ------------------------------------------------------------------ *
 *  Mobile sidebar (slide-over)
 * ------------------------------------------------------------------ */
.sb-scrim { position: fixed; inset: 0; background: var(--scrim); z-index: 25; opacity: 0;
  pointer-events: none; transition: opacity var(--dur); }
#topbar { display: none; }

@media (max-width: 860px) {
  #app { grid-template-columns: 1fr; }
  #sidebar {
    position: fixed; inset: 0 auto 0 0; width: min(84vw, 320px);
    transform: translateX(-102%); transition: transform var(--dur) var(--ease);
    box-shadow: var(--shadow-lg);
  }
  #app[data-mobile="open"] #sidebar { transform: none; }
  #app[data-mobile="open"] .sb-scrim { opacity: 1; pointer-events: auto; }
  #main { grid-column: 1; }
  #topbar {
    display: flex; align-items: center; gap: var(--s3); height: 54px; padding: 0 var(--s4);
    position: sticky; top: 0; z-index: 20;
    background: color-mix(in srgb, var(--bg) 86%, transparent);
    backdrop-filter: saturate(1.4) blur(10px);
    border-bottom: 1px solid var(--border);
  }
  #topbar .menu-btn { width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;
    color: var(--text-2); }
  #topbar .menu-btn:hover { background: var(--surface-3); }
  #topbar .tb-title { font-weight: 700; }
  #view { padding-top: var(--s4); }
}

/* ------------------------------------------------------------------ *
 *  Settings FAB
 * ------------------------------------------------------------------ */
#fab {
  position: fixed; right: var(--s5); top: 50%; transform: translateY(-50%);
  width: 52px; height: 52px; border-radius: 50%; z-index: 40;
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow-lg); color: var(--text-2);
  display: grid; place-items: center; font-size: 22px;
  transition: transform var(--dur) var(--ease), color var(--dur-fast), background var(--dur-fast);
  touch-action: none;
}
#fab:hover { color: var(--accent); transform: translateY(-50%) scale(1.07) rotate(30deg); }
#fab:active { transform: translateY(-50%) scale(.95); }
#fab.dragging { transition: none; cursor: grabbing; }
@media (max-width: 860px) { #fab { right: var(--s4); top: auto; bottom: calc(var(--s5) + env(safe-area-inset-bottom)); transform: none; }
  #fab:hover { transform: scale(1.07) rotate(30deg); } }

/* Scroll-to-top button — bottom-right, revealed once the view is scrolled down.
 * On desktop the settings #fab sits mid-right, so bottom-right is clear; on
 * mobile the #fab moves to bottom-right, so we stack this above it. */
#gotop {
  position: fixed; right: var(--s5); bottom: calc(var(--s5) + env(safe-area-inset-bottom));
  width: 44px; height: 44px; border-radius: 50%; z-index: 39;
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-lg);
  color: var(--text-2); display: grid; place-items: center; cursor: pointer;
  opacity: 0; visibility: hidden; transform: translateY(10px);
  transition: opacity var(--dur-fast), transform var(--dur-fast), color var(--dur-fast),
    background var(--dur-fast), border-color var(--dur-fast);
}
#gotop.show { opacity: 1; visibility: visible; transform: none; }
#gotop:hover { color: var(--accent); border-color: var(--accent); background: var(--surface-2); }
#gotop:active { transform: scale(.94); }
@media (max-width: 860px) {
  #gotop { right: var(--s4); bottom: calc(var(--s5) + 52px + var(--s3) + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) { #gotop { transition: opacity var(--dur-fast); } }

/* ------------------------------------------------------------------ *
 *  Lock screen
 * ------------------------------------------------------------------ */
#lock {
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center; padding: var(--s5);
  background:
    radial-gradient(1000px 600px at 20% -10%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 60%),
    radial-gradient(900px 700px at 110% 110%, color-mix(in srgb, #7c3aed 20%, transparent), transparent 55%),
    var(--bg-2);
}
#lock[hidden] { display: none; }
#lock.leaving { animation: lockOut var(--dur-slow) var(--ease-out) forwards; }
@keyframes lockOut { to { opacity: 0; transform: scale(1.04); visibility: hidden; } }

.lock-card {
  width: min(400px, 100%); background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r4); box-shadow: var(--shadow-lg); padding: var(--s7) var(--s6) var(--s6);
  text-align: center; animation: pop var(--dur-slow) var(--ease-out);
}
.lock-badge {
  width: 62px; height: 62px; border-radius: 18px; margin: 0 auto var(--s4);
  display: grid; place-items: center; font-size: 30px; color: #fff;
  background: linear-gradient(135deg, var(--accent), #7c3aed); box-shadow: var(--shadow);
}
.lock-card h1 { font-size: var(--t-xl); letter-spacing: -.02em; }
.lock-card p { color: var(--text-2); font-size: var(--t-sm); margin-top: 4px; }
.lock-form { margin-top: var(--s5); display: flex; flex-direction: column; gap: var(--s3); }
.lock-input { position: relative; display: flex; align-items: center; }
.lock-input input {
  width: 100%; padding: var(--s3) var(--s6) var(--s3) var(--s4); border-radius: var(--r2);
  background: var(--surface-2); border: 1.5px solid var(--border-2); font-size: var(--t-md);
  transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.lock-input input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: none; }
.lock-input .peek { position: absolute; right: 8px; width: 34px; height: 34px; border-radius: 8px;
  color: var(--text-3); display: grid; place-items: center; }
.lock-input .peek:hover { color: var(--text); background: var(--surface-3); }
.lock-err { min-height: 18px; font-size: var(--t-sm); color: var(--bad); font-weight: 500;
  transition: opacity var(--dur-fast); }
.lock-err.warn { color: var(--warn); }
.lock-card.shake { animation: shake .4s var(--ease); }
@keyframes shake { 10%,90%{transform:translateX(-1px)} 20%,80%{transform:translateX(2px)}
  30%,50%,70%{transform:translateX(-5px)} 40%,60%{transform:translateX(5px)} }

/* ------------------------------------------------------------------ *
 *  Buttons
 * ------------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: var(--s3) var(--s4); border-radius: var(--r2); font-weight: 600; font-size: var(--t-sm);
  border: 1px solid var(--border-2); background: var(--surface-2); color: var(--text);
  transition: background var(--dur-fast), border-color var(--dur-fast), transform var(--dur-fast), box-shadow var(--dur-fast);
}
.btn:hover { background: var(--surface-3); border-color: var(--text-3); }
.btn:active { transform: scale(.97); }
.btn.primary { background: var(--accent); border-color: transparent; color: #fff; }
.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }
.btn.danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, var(--border-2)); }
.btn.danger:hover { background: color-mix(in srgb, var(--bad) 12%, transparent); }
.btn.ghost { background: none; border-color: transparent; color: var(--text-2); }
.btn.ghost:hover { background: var(--surface-3); color: var(--text); }
.btn.block { width: 100%; }
.btn.sm { padding: 7px var(--s3); font-size: var(--t-xs); }
.btn[disabled] { opacity: .55; pointer-events: none; }
.icon-btn { width: 36px; height: 36px; border-radius: var(--r2); display: grid; place-items: center;
  color: var(--text-2); border: 1px solid transparent; transition: background var(--dur-fast), color var(--dur-fast); }
.icon-btn:hover { background: var(--surface-3); color: var(--text); }

/* ------------------------------------------------------------------ *
 *  Modals
 * ------------------------------------------------------------------ */
#modal-root { position: fixed; inset: 0; z-index: 90; display: none; }
#modal-root.open { display: block; }
.modal-scrim { position: absolute; inset: 0; background: var(--scrim);
  opacity: 0; transition: opacity var(--dur); backdrop-filter: blur(2px); }
#modal-root.open .modal-scrim { opacity: 1; }
.modal-wrap { position: absolute; inset: 0; display: grid; place-items: center; padding: var(--s5);
  overflow-y: auto; }
.modal {
  width: min(460px, 100%); background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r3); box-shadow: var(--shadow-lg); overflow: hidden;
  transform: translateY(12px) scale(.98); opacity: 0;
  transition: transform var(--dur) var(--ease-out), opacity var(--dur);
}
.modal.wide { width: min(640px, 100%); }
#modal-root.open .modal { transform: none; opacity: 1; }
.modal-head { display: flex; align-items: flex-start; gap: var(--s3); padding: var(--s5) var(--s5) var(--s3); }
.modal-head .m-ico { font-size: 22px; flex: none; }
.modal-head h2 { font-size: var(--t-lg); letter-spacing: -.01em; }
.modal-head p { color: var(--text-2); font-size: var(--t-sm); margin-top: 2px; }
.modal-head .x { margin-left: auto; }
.modal-body { padding: 0 var(--s5) var(--s3); color: var(--text-2); font-size: var(--t-sm); }
.modal-body :is(input, textarea, select) {
  width: 100%; padding: var(--s3); border-radius: var(--r2); background: var(--surface-2);
  border: 1.5px solid var(--border-2); margin-top: var(--s2); }
.modal-body input:focus, .modal-body textarea:focus, .modal-body select:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: none; }
.modal-foot { display: flex; gap: var(--s2); justify-content: flex-end; padding: var(--s3) var(--s5) var(--s5); }
.modal-foot.spread { justify-content: space-between; }

/* ------------------------------------------------------------------ *
 *  Toasts
 * ------------------------------------------------------------------ */
#toast-root { position: fixed; left: 50%; bottom: var(--s6); transform: translateX(-50%);
  z-index: 95; display: flex; flex-direction: column; gap: var(--s2); align-items: center;
  pointer-events: none; width: max-content; max-width: 92vw; }
.toast {
  pointer-events: auto; display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); border-radius: var(--r-pill);
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-lg);
  font-size: var(--t-sm); font-weight: 500;
  animation: toastIn var(--dur) var(--ease-out);
}
.toast.leaving { animation: toastOut var(--dur) var(--ease) forwards; }
.toast .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
.toast.ok .dot { background: var(--ok); } .toast.bad .dot { background: var(--bad); }
.toast.warn .dot { background: var(--warn); }
.toast .undo { margin-left: var(--s2); color: var(--accent-ink); font-weight: 700; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px) scale(.96); } }
@keyframes toastOut { to { opacity: 0; transform: translateY(8px) scale(.97); } }

/* ------------------------------------------------------------------ *
 *  Settings drawer
 * ------------------------------------------------------------------ */
/* The root stays rendered (visibility, not display:none) so the drawer can slide
 * in/out — a display:none→block flip in the same frame as .open would snap the
 * transform to its end state with no transition. Visibility hides the closed
 * drawer without pulling it out of layout; the delay keeps it around long enough
 * for the slide-out to finish before it's hidden. */
#drawer-root { position: fixed; inset: 0; z-index: 80; visibility: hidden;
  transition: visibility 0s linear var(--dur-slow); }
#drawer-root.open { visibility: visible; transition-delay: 0s; }
.drawer-scrim { position: absolute; inset: 0; background: var(--scrim); opacity: 0;
  pointer-events: none; backdrop-filter: blur(2px);
  transition: opacity var(--dur-slow) var(--ease); }
#drawer-root.open .drawer-scrim { opacity: 1; pointer-events: auto; }
.drawer {
  position: absolute; top: 0; right: 0; height: 100%; width: min(380px, 100%);
  background: var(--surface); border-left: 1px solid var(--border); box-shadow: var(--shadow-lg);
  transform: translateX(102%); transition: transform var(--dur-slow) var(--ease-out);
  display: flex; flex-direction: column;
}
#drawer-root.open .drawer { transform: none; }
/* staggered reveal of the setting groups as the drawer slides in */
#drawer-root.open .drawer-body > * { animation: drawerItem .42s var(--ease-out) both; }
#drawer-root.open .drawer-body > *:nth-child(2) { animation-delay: .05s; }
#drawer-root.open .drawer-body > *:nth-child(3) { animation-delay: .09s; }
#drawer-root.open .drawer-body > *:nth-child(4) { animation-delay: .13s; }
#drawer-root.open .drawer-body > *:nth-child(5) { animation-delay: .17s; }
#drawer-root.open .drawer-body > *:nth-child(n+6) { animation-delay: .21s; }
@keyframes drawerItem { from { opacity: 0; transform: translateY(10px); } }
.drawer-head { display: flex; align-items: center; padding: var(--s5); border-bottom: 1px solid var(--border); }
.drawer-head h2 { font-size: var(--t-lg); }
.drawer-head .x { margin-left: auto; }
.drawer-body { flex: 1; overflow-y: auto; padding: var(--s4) var(--s5); display: flex;
  flex-direction: column; gap: var(--s6); }
.set-group h3 { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-3); margin-bottom: var(--s3); font-weight: 700; }
.set-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s2) 0; }
.set-row .lbl { font-size: var(--t-sm); font-weight: 500; }
.set-row .lbl small { display: block; color: var(--text-3); font-weight: 400; font-size: var(--t-xs); }

/* segmented control */
.seg { display: inline-flex; background: var(--surface-3); border-radius: var(--r2); padding: 3px; gap: 2px; }
.seg button { padding: 5px 11px; border-radius: 8px; font-size: var(--t-xs); font-weight: 600; color: var(--text-2); }
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

/* toggle switch */
.switch { width: 42px; height: 25px; border-radius: 999px; background: var(--track); position: relative;
  transition: background var(--dur-fast); flex: none; }
.switch[aria-pressed="true"] { background: var(--accent); }
.switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 19px; height: 19px;
  border-radius: 50%; background: #fff; box-shadow: var(--shadow-sm); transition: transform var(--dur) var(--ease); }
.switch[aria-pressed="true"]::after { transform: translateX(17px); }

/* accent swatches */
.swatches { display: flex; gap: var(--s2); flex-wrap: wrap; justify-content: flex-end; max-width: 210px; }
.swatch { width: 26px; height: 26px; border-radius: 50%; border: 2px solid transparent;
  transition: transform var(--dur-fast) var(--ease-spring), box-shadow var(--dur-fast); }
.swatch:hover { transform: scale(1.15); }
.swatch[aria-pressed="true"] { border-color: var(--text); box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent-soft); }

/* custom-hue slider */
.hue-slider { -webkit-appearance: none; appearance: none; width: 170px; height: 12px; border-radius: var(--r-pill);
  background: linear-gradient(90deg, hsl(0 85% 60%), hsl(60 85% 60%), hsl(120 85% 60%),
    hsl(180 85% 60%), hsl(240 85% 60%), hsl(300 85% 60%), hsl(360 85% 60%)); cursor: pointer; }
.hue-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%;
  background: #fff; border: 3px solid var(--accent); box-shadow: var(--shadow); cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring); }
.hue-slider::-webkit-slider-thumb:hover { transform: scale(1.15); }
.hue-slider::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%; background: #fff;
  border: 3px solid var(--accent); box-shadow: var(--shadow); cursor: pointer; }

.hr { height: 1px; background: var(--border); border: 0; }

/* shared animation */
@keyframes pop { from { opacity: 0; transform: translateY(10px) scale(.97); } }
.spinner { width: 15px; height: 15px; border: 2px solid var(--track); border-top-color: var(--accent);
  border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* dev error banner (validator failure) */
#dev-error { position: fixed; left: 0; right: 0; top: 0; z-index: 120; background: #7f1d1d; color: #fee2e2;
  font-family: var(--font-mono); font-size: var(--t-sm); padding: var(--s3) var(--s4); display: none;
  box-shadow: var(--shadow-lg); max-height: 45vh; overflow: auto; white-space: pre-wrap; }
#dev-error.show { display: block; }
