/* ============ Home grid ,  native fallback ============
   The tool cards were laid out entirely by Tailwind utility classes
   (grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4).  If Tailwind's
   CDN is blocked ,  Edge Tracking Prevention, ad-blockers, corporate DNS , 
   the cards collapse into a plain vertical list.  This fallback gives the
   same layout in plain CSS, using the same breakpoints, so the page looks
   correct with or without Tailwind.  Because it uses the same specificity
   as Tailwind's utility classes and sits earlier in the cascade, Tailwind
   wins once it loads; if it never loads, this rule keeps the grid. */
.fallback-grid{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:1rem;
  margin-bottom:1.5rem;
}
@media (min-width:768px){
  .fallback-grid{grid-template-columns:repeat(3,minmax(0,1fr));}
}
@media (min-width:1024px){
  .fallback-grid{grid-template-columns:repeat(4,minmax(0,1fr));}
}
/* ============ Critical utility fallbacks ============
   Every Tailwind utility class that is used above the fold on the home view
   is mirrored here with byte-identical values. The page therefore renders
   pixel-for-pixel the same whether or not the Tailwind CDN has finished
   loading. Because Tailwind injects its own stylesheet later in the cascade,
   its utilities take over as soon as they arrive ,  but there is nothing to
   change, because the declarations are the same. */
.w-full{width:100%}
.max-w-5xl{max-width:64rem}
.mx-auto{margin-left:auto;margin-right:auto}
.px-6{padding-left:1.5rem;padding-right:1.5rem}
.pb-16{padding-bottom:4rem}
.pt-10{padding-top:2.5rem}
.pb-2{padding-bottom:.5rem}
.mb-6{margin-bottom:1.5rem}
.text-center{text-align:center}
.flex-1{flex:1 1 0%}
.overflow-hidden{overflow:hidden}
:root{
  color-scheme:dark;
  --banner-h:0px;

  /* ============ Lumen palette ,  restored pre-overhaul scheme ============ */
  --paper:#0d0d10;--ink:#f3f1ea;--soft:#8f8a80;--dim:#7d766a;
  --panel:#121216;--panel2:#1b1b20;--panel3:#232329;
  --bg:radial-gradient(circle at 18% 8%,#1b1a24 0%,#0c0c10 45%);
  --accent:#ff5e3a;
  --accent-h:#ff7a5c;
  --accent2:#7a2e12;
  --accent3:#ff9d7a;
  --accent-ink:#fff;
  --accent-glow:rgba(255,94,58,.45);
  --ok:#4dbb7d;--err:#e0645a;--err2:#7c231d;--warn:#e0a93f;--warn2:#7c5200;
  --focus:#ff7a5c;

  /* ============ Borders ============ */
  --border:#26262c;
  --border-s:rgba(255,255,255,.12);
  --border-strong:#3a3a42;
  --hairline:rgba(255,255,255,.04);
  --line:rgba(255,255,255,.09);--line2:rgba(255,255,255,.22);

  /* ============ Surfaces ============ */
  --surface:#15151a;--surface-2:#1b1b20;--surface-3:#222228;--surface-4:#2a2a32;
  --fg:#f5f3ef;--muted:#8b8794;

  --serif:Georgia, 'Times New Roman', serif;
  --sans:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --mono:ui-monospace,'SFMono-Regular',Consolas,'Liberation Mono',monospace;
  --read:'Source Serif 4',Georgia,serif;

  /* ============ Elevation ,  old shadow values, full scale kept ============ */
  --shadow-xs:0 1px 2px rgba(0,0,0,.25);
  --shadow-sm:0 2px 6px rgba(0,0,0,.28),0 8px 22px -6px rgba(0,0,0,.36);
  --shadow-md:0 10px 26px -8px rgba(0,0,0,.6),0 3px 8px -3px rgba(0,0,0,.4);
  --shadow:0 12px 34px -14px rgba(0,0,0,.65),0 24px 74px -22px rgba(255,94,58,.16);
  --shadow-lg:0 30px 80px -20px rgba(0,0,0,.72);
  --shadow-xl:0 40px 96px -24px rgba(0,0,0,.8),0 16px 36px -12px rgba(0,0,0,.55);
  --shadow-glow:0 0 0 1px rgba(255,94,58,.22),0 12px 40px -8px rgba(255,94,58,.38);
  --shadow-inset:inset 0 1px 0 rgba(255,255,255,.04);

  /* ============ Radii ============ */
  --r-xs:6px; --r-sm:9px; --r-md:13px; --r-lg:18px; --r-xl:24px; --r-full:999px;

  /* ============ Motion ============ */
  --ease-out:cubic-bezier(.2,.8,.2,1);
  --ease-spring:cubic-bezier(.2,.8,.2,1);
  --ease-in-out:cubic-bezier(.4,0,.2,1);
  --dur-instant:.1s; --dur-fast:.15s; --dur-base:.24s; --dur-slow:.42s;

  /* ============ Type scale ============ */
  /* Every size is derived from --fs-scale so the Text size setting moves the
     whole interface together instead of leaving stray hard-coded sizes behind.
     The user can set this from the Display settings dialog. */
  --fs-scale:1;
  --fs-2xs:calc(10px * var(--fs-scale)); --fs-xs:calc(11px * var(--fs-scale));
  --fs-sm:calc(12.5px * var(--fs-scale)); --fs-base:calc(14px * var(--fs-scale));
  --fs-md:calc(15px * var(--fs-scale)); --fs-lg:calc(17px * var(--fs-scale));
  --fs-xl:calc(20px * var(--fs-scale)); --fs-2xl:calc(26px * var(--fs-scale));
  --fs-3xl:calc(34px * var(--fs-scale));
  /* Height of the fixed top row. Also a setting; everything that offsets itself
     below the bar reads this rather than repeating the number. It follows
     --nav-h, which is the value the nav's own height rule actually uses, so
     there is one number controlling the bar rather than two that can disagree. */
  --topbar-h:var(--nav-h,56px);

  /* ============ Focus ring ============ */
  --ring:0 0 0 3px rgba(255,122,92,.34);
  --ring-soft:0 0 0 2px rgba(255,122,92,.22);

  --viewer-bg:var(--bg);
}

/* ============ Base typography: crisper across the whole app ============ */
html,body{
  font-feature-settings:"ss01" on,"cv11" on,"ss03" on;
  letter-spacing:-.005em;
}
body{background:var(--bg);}
body::after{
  /* Very subtle radial highlight at the top of the viewport, a hallmark
     of apps like Linear. Cheap (one paint) and adds real depth. */
  content:'';
  position:fixed;
  inset:0;
  pointer-events:none;
  z-index:0;
  background:
    radial-gradient(1200px 500px at 50% -10%, rgba(255,94,58,.07), transparent 60%),
    radial-gradient(900px 400px at 90% 5%, rgba(110,168,254,.05), transparent 65%);
}/* ============ Refined scrollbar ,  Linear/Vercel feel ============ */
*{scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{
  background:var(--scroll-thumb);
  border-radius:99px;
  border:3px solid transparent;
  background-clip:padding-box;
  transition:background var(--dur-fast);
}
::-webkit-scrollbar-thumb:hover{
  background:var(--scroll-thumb-hover);
  background-clip:padding-box;
  border-width:2px;
}
::-webkit-scrollbar-corner{background:transparent}

/* ============ Better selection ============ */
::selection{
  background:rgba(255,94,58,.32);
  color:var(--fg);
}/* Defeat Samsung Internet / forced-dark auto-inversion by explicitly declaring
   a light page and a media-query guard. Chrome, Safari and Firefox ignore
   these because the theme is already light; Samsung Internet respects them. */
@media (prefers-color-scheme: dark){
  html[data-skin="paper"],
  html[data-skin="comfy"]{
    color-scheme:only light !important;
    background:#f3eddd !important;
  }
  html[data-skin="paper"] body,
  html[data-skin="comfy"] body{
    background:var(--bg) !important;
    color:var(--fg) !important;
  }
}*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--fg);font-family:var(--sans);-webkit-font-smoothing:antialiased;touch-action:manipulation}
/* The UI face comes from --sans, which every theme sets through --t-sans. It was
   hardcoded here to 'Space Grotesk', which meant the token was defined all the
   way down in tokens.css and then thrown away at the root: no aesthetic could
   change the page's typeface, and the cat mode's own font swap did nothing at all
   because the page never asked for the token it had just changed. */
/* Text is selectable again. `user-select:none` on html/body made EVERY word in
   the app unselectable, which is why the browser's own Search/Copy callout was
   the only selection UI that ever appeared and why highlighting felt broken.
   Chrome then draws that native bubble over the page on mobile, which cannot be
   suppressed by script ,  allowing selection is what lets Lumen draw its own
   menu instead. Chrome that genuinely must not be selectable (board canvas,
   toolbar chrome, drag surfaces) opts back in locally. */
::selection{background:color-mix(in srgb,var(--accent) 34%,transparent);color:var(--ink)}
input,textarea,button,.btn,[data-nodrag],.ls-tools,.ls-zoom,.vbar,.top-contextual,
.ls-hl-bar,#rTools,#rName,.lsk-overlay,.lsk-menu,.lsq,.wb-canvas,.ls-board,
.no-select,body.dragging,body.wb-chat-dragging{user-select:none}
input,textarea,select,.tl,.md,.dtext,.narticle,.dx,.msg,.bubble,.rqc-msg,.wb-chat-msg,.dg-msg,.dg-doc{user-select:text}
#space-canvas{position:fixed;inset:0;z-index:0;pointer-events:none}

.view{position:fixed;inset:0;z-index:1;display:none;overflow:hidden;flex-direction:column;
  padding-top:calc(var(--topbar-actual, var(--topbar-h,56px)) + env(safe-area-inset-top,0px) + var(--banner-h));
  padding-left:env(safe-area-inset-left,0px);
  padding-right:env(safe-area-inset-right,0px);
  padding-bottom:env(safe-area-inset-bottom,0px)}
.view.active{display:flex;animation:viewIn .3s cubic-bezier(.2,.8,.2,1)}
.view.scroll{overflow-y:auto}
@keyframes viewIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@supports (padding-top:env(safe-area-inset-top,0px)){
  #view-study .topbar{min-height:calc(58px + env(safe-area-inset-top,0px));padding-top:calc(env(safe-area-inset-top,0px)*0.15)}
  .tool-head{min-height:calc(var(--topbar-h) + env(safe-area-inset-top,0px));padding-top:calc(env(safe-area-inset-top,0px)*0.35)}
}
[hidden]{display:none!important}

button:focus,.btn:focus,.ibtn:focus,.micbtn:focus,.nplay:focus,.rail button:focus,.rtabs button:focus,.seg button:focus{outline:none!important;box-shadow:none!important}
.btn:focus-visible,.ibtn:focus-visible,.micbtn:focus-visible,.nplay:focus-visible{outline:2px solid var(--accent);outline-offset:2px;box-shadow:none!important}
.pomo-btn,.pomo-btn *,.pomo-btn *::before,.pomo-btn *::after{outline:none!important;box-shadow:none!important}
.pomo-btn:focus,.pomo-btn:focus-visible,.pomo-btn:active,.pomo-btn:hover,
.pomo-btn *:focus,.pomo-btn *:focus-visible,.pomo-btn *:active{outline:none!important;box-shadow:none!important}
.pomo-btn::-moz-focus-inner{border:0!important}
/* ============ Buttons ,  refined, layered, alive ============ */
.btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:8px 14px;
  border-radius:var(--r-sm);
  border:1px solid var(--secondary-border);
  /* Flat surface base ,  every theme gets its own surface colour, no
     gradient mixing that goes muddy in the paper theme. The depth is
     added by an inset highlight below, which works on any colour. */
  background:var(--secondary-bg);
  box-shadow:
    var(--secondary-shadow);
  color:var(--secondary-fg);
  font:500 var(--fs-sm)/1 var(--sans);
  letter-spacing:-.005em;
  cursor:pointer;
  white-space:nowrap;flex:none;
  transition:
    transform var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
  user-select:none;
  -webkit-tap-highlight-color:transparent;
}
.btn:hover{
  border-color:var(--secondary-hover-border);
  background:var(--secondary-hover-bg);
  box-shadow:
    var(--secondary-hover-shadow);
  color:var(--secondary-hover-fg);
}
.btn:active{
  transform:translateY(.5px) scale(.985);
  box-shadow:var(--shadow-inset),var(--shadow-xs);
}
.btn:focus-visible{
  outline:none;
  box-shadow:var(--ring),var(--shadow-inset);
}
.btn:disabled{
  opacity:.4;
  pointer-events:none;
  filter:grayscale(.3);
}
.btn svg{width:14px;height:14px;flex:none}

/* ============ Primary ,  theme-aware and depth-first ============
   The gradient used to run top→bottom through --accent-h. In the
   noir theme that reads as a lighter-to-saturated orange, but in
   the paper theme --accent-h aliases to --accent2 which is DARKER
   than --accent, so the button went brown on top and terracotta on
   the bottom. That is why it looked wrong.
   Fix: run the gradient in the other direction ,  bottom->top pulls
   the pure accent colour upward, and use --accent-h only as a
   hover/prelight so the terracotta is never mixed with a darker
   alias. */
.btn.primary{
  background:var(--primary-bg);
  border-color:var(--primary-border);
  color:var(--primary-fg);
  box-shadow:
    var(--primary-shadow);
  font-weight:600;
}
.btn.primary:hover{
  background:var(--primary-hover-bg);
  border-color:var(--primary-hover-border);
  box-shadow:
    var(--primary-hover-shadow);
}
.btn.primary:active{
  background:var(--primary-active-bg);
  box-shadow:
    var(--primary-active-shadow);
}.btn.danger{
  border-color:var(--danger-border);
  color:var(--danger-fg);
  background:var(--danger-bg);
  box-shadow:var(--danger-shadow);
}
.btn.danger:hover{
  background:var(--danger-hover-bg);
  border-color:var(--danger-hover-border);
  color:var(--danger-hover-fg);
}

.btn.sm{padding:6px 11px;font-size:var(--fs-xs);border-radius:var(--r-xs);gap:5px}
.btn.sm svg{width:12px;height:12px}
.btn.icon{padding:0;width:36px;height:36px}
.btn.icon svg{width:16px;height:16px}
.btn.ghost{background:transparent;border-color:transparent;box-shadow:none}
.btn.ghost:hover{background:var(--surface-2);box-shadow:none;border-color:var(--border)}

/* ============ Icon buttons ,  theme-safe ============ */
.ibtn{
  position:relative;
  width:36px;height:36px;
  border-radius:var(--r-sm);
  border:1px solid var(--ctl-border);
  background:var(--ctl-bg);
  color:var(--ctl-fg);
  cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;
  box-shadow:
    var(--ctl-inset),
    var(--ctl-shadow);
  transition:
    color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.ibtn:hover{
  color:var(--ctl-hover-fg);
  border-color:var(--ctl-hover-border);
  background:var(--ctl-hover-bg);
  box-shadow:
    var(--ctl-hover-inset),
    var(--ctl-hover-shadow);
}.ibtn:active{transform:scale(.94)}
.ibtn:focus-visible{outline:none;box-shadow:var(--ring),var(--shadow-inset)}
.ibtn.active{
  background:var(--ctl-active-bg);
  border-color:var(--ctl-active-border);
  color:var(--ctl-active-fg);
  box-shadow:
    var(--ctl-active-inset),
    var(--ctl-active-shadow);
}
.ibtn svg{width:17px;height:17px}
.ibtn.disabled{opacity:var(--ibtn-disabled-opacity);pointer-events:none}

/* Chip ,  refined */
.chip{
  display:inline-flex;align-items:center;gap:8px;
  border:1px solid var(--chip-border);
  background:var(--chip-bg);
  border-radius:var(--r-full);
  padding:6px 12px 6px 10px;
  font:500 var(--fs-xs)/1 var(--sans);
  color:var(--chip-fg);
  cursor:pointer;
  max-width:230px;flex:none;
  box-shadow:
    var(--chip-shadow);
  transition:all var(--dur-fast) var(--ease-out);
}
.chip:hover{
  border-color:var(--chip-hover-border);
  background:var(--chip-hover-bg);
  box-shadow:
    var(--chip-hover-shadow);
}.chip .dot{
  width:7px;height:7px;border-radius:50%;
  background:var(--dim);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--dim) 22%,transparent);
  transition:all var(--dur-fast);
  flex:none;
}
.chip .dot.on{
  background:var(--ok);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 24%,transparent);
}
input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url],input[type=time],input[type=date],input[type=datetime-local],input:not([type]),textarea,select{
  background:var(--input-bg);
  border:1px solid var(--input-border);
  border-radius:var(--r-sm);
  color:var(--input-fg);
  font-family:inherit;
  font-size:var(--fs-base);
  padding:10px 12px;
  outline:none;
  resize:vertical;
  width:100%;
  box-shadow:var(--shadow-inset);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
input::placeholder,textarea::placeholder{color:var(--dim);opacity:.85}
input:hover:not(:focus):not(:disabled),textarea:hover:not(:focus):not(:disabled),select:hover:not(:focus):not(:disabled){
  border-color:var(--border-s);
  background:var(--surface-2);
}
input:focus,textarea:focus,select:focus{
  border-color:var(--input-focus-border);
  background:var(--input-bg);
  box-shadow:var(--input-focus-shadow);
}
input:disabled,textarea:disabled,select:disabled{
  opacity:.55;
  cursor:not-allowed;
  background:var(--surface);
}
textarea{line-height:1.55}

/* Select: arrow refinement */
select{
  appearance:none;
  -webkit-appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238f8d99' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat:no-repeat;
  background-position:right 10px center;
  padding-right:32px;
}

/* Range: refined thumb and track */
input[type=range]{
  -webkit-appearance:none;appearance:none;
  height:6px;background:var(--surface-3);
  border-radius:99px;outline:none;cursor:pointer;flex:none;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.35);
}
input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;
  width:18px;height:18px;border-radius:50%;
  background:linear-gradient(180deg,var(--accent-h),var(--accent));
  cursor:pointer;
  border:2px solid var(--surface);
  box-shadow:
    0 0 0 1px var(--accent),
    0 4px 12px -2px var(--accent-glow);
  transition:transform var(--dur-fast) var(--ease-out);
}
input[type=range]::-webkit-slider-thumb:hover{transform:scale(1.1)}
input[type=range]::-moz-range-thumb{
  width:16px;height:16px;border-radius:50%;
  background:linear-gradient(180deg,var(--accent-h),var(--accent));
  border:2px solid var(--surface);
  box-shadow:0 0 0 1px var(--accent),0 4px 12px -2px var(--accent-glow);
}

/* Checkbox */
input[type=checkbox]{
  accent-color:var(--accent);
  width:16px;height:16px;
  cursor:pointer;
}

/* Color */
input[type=color]{
  width:38px;height:32px;padding:3px;
  border-radius:var(--r-xs);
  border:1px solid var(--border);
  background:var(--surface-2);
  cursor:pointer;
}

.toast{
  position:fixed;
  bottom:calc(28px + env(safe-area-inset-bottom,0px));
  left:50%;
  transform:translateX(-50%) translateY(18px) scale(.96);
  color:var(--fg);
  padding:12px 18px;
  border-radius:var(--r-full);
  font:500 var(--fs-sm) var(--sans);
  letter-spacing:-.002em;
  border:1px solid var(--glass-border);
  background:var(--glass-bg);
  -webkit-backdrop-filter:var(--glass-blur);
  backdrop-filter:var(--glass-blur);
  box-shadow:
    var(--glass-shadow);
  z-index:10002;
  opacity:0;pointer-events:none;
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
  text-align:center;
  white-space:nowrap;max-width:90vw;
  display:flex;align-items:center;gap:10px;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .toast{background:var(--surface)}
}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
.toast.ok{border-color:color-mix(in srgb,var(--ok) 60%,transparent);color:var(--ok)}
.toast.err{border-color:color-mix(in srgb,var(--err) 60%,transparent);color:var(--err)}
.toast svg{width:17px;height:17px;flex:none}.top-nav{
  position:fixed;top:0;left:0;right:0;box-sizing:border-box;
  background:var(--nav-bg);
  -webkit-backdrop-filter:var(--nav-blur);
  backdrop-filter:var(--nav-blur);
  border-bottom:1px solid var(--nav-border);
  box-shadow:var(--nav-shadow);
/* min-height, not height.

     --topbar-h is the height the student asked for, but the row is allowed to
     be taller than that when the controls wrap onto a second line. A fixed
     height here put the wrapped line outside the bar and on top of the page,
     because the row grew downwards from a box that no longer contained it. The
     slider therefore sets a floor, and the row takes whatever it actually
     needs. --topbar-actual is written back from the measured height so the rest
     of the shell can sit below whatever the row really is. */
  min-height:calc(var(--topbar-h,56px) + env(safe-area-inset-top,0px));
  /* Only a view that asked for a tall toolbar makes the header tall.

     The header height was being set from the whiteboard's size setting for the
     whole application, so choosing Medium made every view humongous ,  a
     whiteboard preference leaked into the reader, the quiz and everything else.
     The height now grows only while a scrolling feature is the one on screen. */
  padding-top:0;
  padding:env(safe-area-inset-top,0px)
           calc(14px + env(safe-area-inset-right,0px))
           0
           calc(14px + env(safe-area-inset-left,0px));
  z-index:50;
  display:flex;align-items:center;gap:14px;
  /* The chrome keeps the top line; the toolbar gets a line of its own underneath
     it, across the full width.

     The bar used to be a flex item sitting BETWEEN the wordmark and the
     right-hand cluster, so its width was only the leftover middle. Making it
     taller then wrapped its controls inside that narrow strip ,  a row and a bit
     of buttons with empty header either side and empty page underneath ,  and
     the last row was always cut part way through, because an arbitrary pixel
     height has no relationship to the height of a row of buttons.

     The wordmark, timer, search and settings stay exactly where they were; the
     bar takes the space below them instead of fighting them for the middle. */
  flex-wrap:nowrap;
  /* Chrome stays on ONE line, with the wordmark.

     This was `wrap`, and it was the root of the header breaking on any view
     whose contextual bar is wide. A wide bar plus the chrome cluster exceeded
     the row, the nav wrapped, and the whole chrome cluster ,  Athena, the timer,
     the model picker, trash, search, settings, undo/redo ,  dropped onto a
     second line beneath the toolbar. That had been happening for some time and
     was invisible, because the header was also pinned to a single row's height
     by a clamp elsewhere in the cascade, which simply cropped the overflow.
     Letting the header grow (lumen-mobile.css) exposed it.

     Nowrap puts it right at the source. .top-contextual can shrink ,  it has
     min-width:0 and overflow-x:auto ,  and the overflow pass parks whatever will
     not fit behind More, so a feature with many controls costs it a More menu
     rather than a displaced header. The whiteboard is unaffected: it already
     gives its toolbar its own full-width row, which needs no wrap. */
  align-items:center;
  row-gap:0;
  /* Fallback for browsers that do not support backdrop-filter */
  box-shadow:0 1px 0 rgba(255,255,255,.02) inset;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .top-nav{background:var(--surface)}
}
.ai-model-select{width:auto;max-width:250px;font-size:12px;padding:7px 9px;flex:none}
.brand{
  font-family:var(--serif);
  font-size:30px;
  line-height:1;
  color:var(--fg);
  cursor:pointer;
  display:flex;align-items:center;
  height:56px;
  letter-spacing:-.02em;
  transition:opacity var(--dur-fast);
  /* The wordmark holds still so the space to its right is a fixed number the
     overflow algorithm can measure against. Allowed to shrink, it changed width
     with the bar's contents, so the budget moved every time a control moved. */
  flex:none;
  white-space:nowrap;
}
.brand:hover{opacity:.85}
.brand em{
  color:var(--accent);
  font-style:italic;
  /* Animated accent dot ,  subtle breathing, a hallmark detail */
  animation:brandPulse 4s var(--ease-in-out) infinite;
  display:inline-block;
  margin-left:1px;
}
@keyframes brandPulse{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.75;transform:scale(.88)}
}
.brand .brand-back{
  width:22px;height:22px;margin-right:8px;
  color:var(--muted);
  transition:color var(--dur-fast),transform var(--dur-fast);
  flex:none;
}
.brand:hover .brand-back{color:var(--accent);transform:translateX(-2px)}
.mono{font-family:var(--mono)}.dim{color:var(--dim)}.spacer{flex:1}
.fld{display:flex;flex-direction:column;gap:5px;min-width:0}
.fld>span{font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft)}
.lbl{font:600 10px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--soft)}
.hint{font-size:12.5px;color:var(--soft);line-height:1.55}
.guide{margin:4px 0 0;border:1px dashed var(--line2);border-radius:8px;padding:8px 12px;font-size:12.5px;color:var(--soft);line-height:1.6}
.guide summary{cursor:pointer;font:600 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--dim);list-style:none}
.guide summary::before{content:'▸ '}.guide[open] summary::before{content:'▾ '}
.guide ol{margin:7px 0 0;padding-left:1.35em}.guide li{margin:.3em 0}
.hint a{color:var(--accent)}
.sw{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:12.5px;color:var(--soft);user-select:none}
.sw input{position:absolute;opacity:0;width:0}
.sw .knob{width:30px;height:17px;border:1px solid var(--line2);border-radius:99px;position:relative;flex:none;transition:.18s}
.sw .knob::after{content:'';position:absolute;top:2px;left:2px;width:11px;height:11px;border-radius:50%;background:var(--soft);transition:.18s}
.sw input:checked+.knob{border-color:var(--accent)}
.sw input:checked+.knob::after{left:15px;background:var(--accent)}
.card{background:var(--panel);border:1px solid var(--line);border-radius:12px}
.chip span.ellip{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dot{width:7px;height:7px;border-radius:50%;background:var(--dim);flex:none}
.dot.on{background:var(--ok)}
.pad{padding:16px}
.seg{display:inline-flex;border:1px solid var(--line2);border-radius:8px;overflow:hidden;flex:none}
.seg button{padding:5px 11px;font:600 10px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--seg-fg);background:var(--seg-bg);border:none;cursor:pointer}
.seg button.on{background:var(--seg-on-bg);color:var(--seg-on-fg)}
.seg button:not(.on):hover{color:var(--ink)}

/* Nothing in the row may be squeezed.
   Every item here has a width the layout decided on ,  a 36px button, a 26px
   dial, the wordmark. Flex will still shrink them to fit, and their contents
   do not shrink with them, so the timer and the status badge turned into
   unreadable smears and the wordmark into a column of squeezed letters. The
   only two things allowed to give are the flexible spacer, which is meant to,
   and the contextual bar, which is measured before it is given anything. */
.top-nav > *{flex:none}
.top-nav > .flex-1{flex:1 1 auto;min-width:0}
/* The whiteboard is the one view with more controls than a single row can hold.

   It fills the gap beside the chrome and wraps onto as many lines as it needs,
   with no cap: the bar showed two lines while the tools wanted three, so half the
   shapes were simply not there. Everything visible, nothing clipped, and the
   space beside the wordmark is toolbar rather than a gap.

   Every other view keeps the bar inline where it has always been. */
/* The palette stays in the toolbar.

     Moving it up to the chrome line beside the wordmark was tried and reverted:
     the top line is a flex row whose order changes with the header's layout, so
     there is no reliable "just after the wordmark" to anchor to ,  Athena docks
     there too, and whichever was measured second ended up drawn over the other.
     The palette and Athena both live on the chrome line, and the toolbar keeps
     the palette until that line can be given a fixed, reserved slot. */
/* The whiteboard's own toolbar rows.

   The board keeps its own header rather than lending its controls to the
   contextual bar, so the Compact / Medium setting ,  which writes --ctx-rows and
   data-rows ,  now lands here. Before this the row count governed
   .top-contextual, an element the whiteboard does not use, so the setting had no
   visible effect at all: the toolbar simply ignored it.

   Rows are whole, on real line height, exactly as the contextual bar does it:
   34px of control plus 6px between rows, no trailing gap. Medium (2) is what the
   toolbar actually needs; Compact (1) clips to a single line and is reached by
   scrolling, which is the honest version of "smaller" ,  a control hidden with no
   way back is not smaller, it is missing. */
body:has(#view-whiteboard.active) #wbToolHead .top-actions{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:6px;row-gap:6px;
  min-width:0;
  /* 40px, measured rather than guessed. The tiles are 38px tall ,  33 of the 36
     controls in this row measure 38 ,  and the line was 34px, so four pixels were
     being cut off the top and bottom of nearly every control, which is what made
     the pen nibs and the swatches read as tall ovals sliced flat. 40 is the tile
     plus two pixels so the active tool's ring and shadow are not shaved either.

     This is a constant, not a measurement, because a row's height has to be
     known before its contents are laid out. If the tiles change size this has to
     be re-measured ,  the comment is the record of where the number came from. */
  --wb-line:40px;
  max-height:calc(var(--ctx-rows,2) * var(--wb-line) + (var(--ctx-rows,2) - 1) * 6px);
  overflow-x:auto;overflow-y:auto;
  scrollbar-width:thin;
  /* Scrolling stops on a row, never inside one.

     Every control in a row shares that row's top edge, so snapping each control
     to `start` snaps the SCROLLER to a row boundary ,  there is no row element to
     target, because flex-wrap makes rows an emergent result rather than a node.

     `always` is what stops it parking halfway: without it a flick can settle
     between two snap points, which is precisely the half-row of clipped tiles
     this is here to prevent. The container height is an exact multiple of the
     row pitch, so there is no remainder for a snap to land in. */
  scroll-snap-type:y mandatory;
}
body:has(#view-whiteboard.active) #wbToolHead .top-actions > *{
  scroll-snap-align:start;
  scroll-snap-stop:always;
}
body:has(#view-whiteboard.active) #wbToolHead .top-actions::-webkit-scrollbar{height:8px;width:8px}
/* Compact mode is one continuous row rather than a clipped stack.

   The stacked version (max-height one line over a hidden vertical scroll) is
   what made the second row, and the settings button at its tail, effectively
   disappear: nothing on the page told the user there was anything below the
   first row to scroll to. A single horizontal strip puts every control on one
   line and offers a scrollbar to reach the rest, the same shape the bottom
   page bar already uses. */
body:has(#view-whiteboard.active) #wbToolHead[data-rows="1"] .top-actions{
  flex-wrap:nowrap;
  max-height:44px;
  overflow-x:auto;
  overflow-y:hidden;
  scroll-snap-type:none;
}
/* The header itself has no title line any more, so it must not reserve one. */
#wbToolHead{padding-top:0}

body:has(#view-whiteboard.active) .top-contextual{
  /* Across the whole header, every line of it, on its own line under the chrome.

     Inline, the bar was only as wide as the gap between the wordmark and the
     chrome, so the top line was filled but the lines below stopped short on both
     sides. This fills both sides of every toolbar row and keeps the chrome on
     the top line; the strip between the wordmark and the timer is the one piece
     of header that stays empty.

     Tried and rejected: `display:contents`, which would have let the buttons
     flow directly into the header so the first line could fill the middle AND
     the rows below run edge to edge. It did not take ,  the toolbar kept its own
     box, the chrome ended up stranded mid-header and most controls lost their
     layout ,  so it is not in the build. */
  order:99;flex:1 0 100%;
  /* The cap goes, because on this view the row is allowed to be as tall as it
     actually needs; --ctx-rows is what decides how many rows that is. */
  max-height:none;
  /* …but the SCROLL does not. `overflow:visible` here was read off the intent
     ("let the bar be as wide as it needs") rather than off the mechanism, and it
     is what actually broke the whiteboard toolbar.

     `overflow-x:auto` on .top-contextual is the only thing that lets a row wider
     than the screen be reached at all. Replacing it with `visible` removed the
     scroll AND let the box paint past the header, so every control past the
     right edge was drawn where nobody could see it and no gesture could retrieve
     it ,  the toolbar lost its tail, and the last button sat sliced in half
     against the window edge.

     Restoring `auto` on both axes costs nothing when the row fits (a scroll
     container with nothing to scroll behaves exactly like a visible one) and is
     the whole fix when it does not. The vertical axis is the one the
     row-at-a-time wheel handler in lumen-workflows.js scrolls, so that had been
     dead on this view too. */
  overflow-x:auto;overflow-y:auto;
}
/* The bar needs the nav to WRAP for `flex:1 0 100%` to mean anything.

   `order:99` + `flex-basis:100%` puts the bar on a line of its own only while
   `.top-nav` is `flex-wrap:wrap`. It is `nowrap` now ,  deliberately, so that a
   wide contextual bar can never drag the chrome cluster onto a second line ,  and
   against a one-line nav a 100% basis is simply added AFTER the chrome instead
   of starting below it. The bar's left edge landed under the settings gear, its
   box was the nav's full content width, and everything past the window edge was
   gone: eleven of the whiteboard's controls on a 1440px screen, fourteen at
   820px, with no way to scroll to them.

   Re-enabling wrap for this one view is what the rule above was written against,
   and it cannot make the chrome wrap on its own: every chrome child is
   `.top-nav > * { flex:none }`, so the chrome line only breaks if the chrome
   ALONE is wider than the screen ,  which is the phone layout's problem and is
   already handled there. */
body:has(#view-whiteboard.active) .top-nav{flex-wrap:wrap}
/* Compact is the one size that deliberately hides rows ,  it is the only way to
   ask for a single line, and the rest is reachable by scrolling. */
body:has(#view-whiteboard.active) .top-contextual[data-rows="1"]{
  max-height:38px;overflow-y:auto;
}
/* …and the spacer keeps its width, so the chrome stays right-aligned.

   The whiteboard used to collapse .flex-1 to width:0, which existed while the
   toolbar was an INLINE item filling the gap beside the wordmark. That is not
   how it is laid out any more ,  the rule above gives .top-contextual its own
   full-width row (order:99, flex:1 0 100%) ,  so collapsing the spacer no longer
   bought the toolbar any room. It only removed the gap between the wordmark and
   the controls, which is why this one view had its chrome crammed against the
   brand on the left while every other view pushes it to the right. */
.top-nav > .flex-1{flex:1 1 auto}
.top-contextual{display:flex;align-items:center;gap:6px;min-width:0;flex:0 1 auto;
  /* Every view except one: the bar is an inline item between the wordmark and
     the chrome, exactly as it always was.

     Making it a full-width line of its own for EVERY view is what broke the
     other apps ,  a feature's controls took a whole line of the header
     everywhere, pushing that app's own toolbar out of its row and past the
     edge. Only the whiteboard has enough controls to want the width, and only
it is given it, below. */
  --ctx-line:34px;--ctx-row-gap:6px;
  /* Whole rows only, and the arithmetic is the real one.

     A row is 34px of control plus a 6px gap to the next one ,  there is no gap
     after the last row ,  so N rows are 34N + 6(N-1), plus the 4px of padding the
     bar carries. Rounding that to N*40 is what produced the row and a bit: two
     rows are 74px, not 80, and a bar allowed 80px showed two rows and then six
     pixels of nothing. */
max-height:calc(var(--ctx-rows,2) * var(--ctx-line) + (var(--ctx-rows,2) - 1) * var(--ctx-row-gap) + 4px);
  max-width:100%;
  flex-wrap:wrap;
  align-content:flex-start;
  row-gap:6px;
  /* Air above and below the rows so the toolbar is not welded to the chrome
     above it or to the canvas below it. This is the same gap the tool cards
     have between them, applied to the bar as a whole. */
  padding:6px 0;
  overflow-x:auto;
  overflow-y:auto;
  scrollbar-width:none;
  -webkit-overflow-scrolling:touch;
  padding:2px 0;
  margin-left:0;
  scroll-padding-inline:8px;
}
.top-contextual::-webkit-scrollbar{display:none}
/* One line for every view except the whiteboard.

   The bar wrapped to a second row wherever a feature registered enough controls
   to need one ,  the quiz registers nine ,  and a second row inside an inline
   chrome item overflowed the header instead of making it taller, so it painted
   over the view below: its heading, its "no quiz loaded" line, and its own
   toolbar row.

   The header is now allowed to grow (see lumen-mobile.css), but growing is the
   wrong outcome anyway ,  a wrapped contextual bar makes the header a different
   height on different views, which is the inconsistency the header was unified
   to remove in the first place.

   One line instead. Whatever will not fit goes to the More menu, which
   ensureMore() already builds and which passes the feature's priority list
   through, so the controls that matter survive and the rest are one click away
   rather than drawn over the page. The whiteboard keeps its own rule below and
   its scroll-and-grow toolbar. */
body:not(:has(#view-whiteboard.active)) .top-contextual{--ctx-rows:1}
body:has(#view-reader.active) .top-contextual{--ctx-rows:2}
/* One sidebar control, not two.

   #railToggle ("Show / hide the sidebar") and #rPanel ("Toggle side panel") sat
   side by side in the study header doing two different jobs on the same rail , 
   one collapsed the study tabs, the other toggled the Reader's side panel. Two
   adjacent page-with-a-panel icons, one showing the panel on the left and one on
   the right, which is not a distinction anyone can act on at a glance.

   Only #rPanel is kept: it is the one whose meaning survives without a tooltip,
   and it is the control the Reader needs. #railToggle is hidden rather than
   removed from the markup, so its keyboard handler and the rail itself are
   untouched ,  this is a visibility change, not a deletion. */

.top-nav #railToggle{display:none !important}

/* …and the survivor is given enough weight to be legible on its own. It is a
   label-less icon in a row of labelled buttons, so at the default icon size it
   read as decoration rather than as the sidebar control. */
.top-nav #rPanel,
#rPanel{
  width:38px;height:38px;min-width:38px;
}
/* The two header rows under the bar.

   The first said "Home › Reader" and the second said "Workspace" - the
   same place, said twice, in a band between the bar and the work. The
   bar already says which view you are in, and the view's own toolbar is
   where the rest of it belongs, so both rows go. */
#breadcrumbs{display:none !important}
#view-study .study-view .vbar .ls-crumbs{display:none !important}

#rPanel svg{width:19px !important;height:19px !important}
#rPanel .when-open,#rPanel .when-closed{display:grid;place-items:center}

     The height used to be set from the whiteboard's size setting for the whole
     application, so choosing Medium made every view humongous ,  a whiteboard
     preference leaked into the reader, the quiz and everything else. Keying it
     off the view being open means it cannot: the header is tall while the
     whiteboard is on screen and is its normal size everywhere else, with no
     bookkeeping in between. */
body:has(#view-whiteboard.active) .top-nav{
  /* One chrome line. The toolbar sits inside it and wraps; the header is then
     exactly as tall as the toolbar actually needs, because nothing caps it.
     A fixed height here is what hid the shapes ,  the bar wanted three lines and
     the header only ever allowed two.

     lumen-mobile.css pins the header to `height: var(--nav-h) !important` for
     the phone layout, but it is not inside a media query, so it was clamping the
     header to a single 50px line on every screen: the toolbar's rows after the
     first were laid out but had nowhere to go, which is why the shapes simply
     were not there. `auto` lets the header be as tall as its contents. */
  height:auto !important;
  min-height:calc(46px + env(safe-area-inset-top,0px));
}
/* …and the view below starts under the real height, not the nominal one. */
body:has(#view-whiteboard.active) .view{
  padding-top:calc(var(--topbar-actual, var(--nav-h)) + env(safe-area-inset-top,0px) + var(--banner-h,0px)) !important;
}
/* …and a one-line toolbar is exactly the height of a row, with no slab. */

/* …and the chrome line is laid out exactly as it is on every other view.

   It used to be pinned to the top edge here and nowhere else:

     body:has(#view-whiteboard.active) .top-nav{align-items:flex-start}
     … > .brand, > .pomo, > .flex-1, > .ctx-grip{align-self:flex-start;margin-top:6px}

   on the reasoning that there was more than one line above it to sit on top of.
   There is not: .top-contextual is given its own full-width row below the chrome
   (`order:99; flex:1 0 100%`), so the chrome row is exactly the height it is
   everywhere else. Those overrides bought nothing and cost the placement ,  the
   wordmark, timer, model, search, gear and history were top-aligned and shoved
   down 6px on this view alone while every other view centres them. That is the
   whole reason this header never matched the home screen no matter what was
   changed elsewhere in it: the button set could be made identical, and the row
   they sat in still was not the same row. */
/* A row that scrolls says so. The scrollbar is hidden everywhere else, which is
   right for a row that never overflows and wrong for one that does: a control
   that can only be reached by scrolling, with no scrollbar shown, is a control
   that looks like it does not exist. */
.top-contextual.ctx-scroll{scrollbar-width:thin;scrollbar-color:var(--border-s,rgba(120,110,90,.55)) transparent}
.top-contextual.ctx-scroll::-webkit-scrollbar{display:block;height:6px}
.top-contextual.ctx-scroll::-webkit-scrollbar-track{background:transparent}
.top-contextual.ctx-scroll::-webkit-scrollbar-thumb{background:var(--border-s,rgba(120,110,90,.55));border-radius:3px}
.top-contextual.ctx-scroll::-webkit-scrollbar-thumb:hover{background:var(--accent,#c8503c)}
.top-contextual > *{flex:none;
  /* One height for every control, and room around it.

     The colour palette, the pen slots and the size slider are all naturally
     shorter than a tool button; left alone they made the lines ragged and a
     height chosen as a whole number of rows then cut through whichever line was
     tallest. The height is uniform so lines divide exactly, and the padding is
     what the gap between tool cards is ,  a control should sit in the row with
     air around it, not flush against its neighbours and against the whiteboard
     below. */
  height:34px;
  min-height:34px;
  max-height:34px;
  padding-block:0;
  box-sizing:border-box;
  display:flex;align-items:center}
/* Controls the bar has no room for are parked in the feature's overflow menu.
   They leave the bar entirely rather than sitting in it out of sight, so the
   row never looks full while hiding half its tools. */
.top-contextual > .ctx-parked{display:none!important}
.top-contextual .ibtn{
  width:34px;height:34px;min-width:34px;min-height:34px;
  display:flex;align-items:center;justify-content:center;
}
.top-contextual .ibtn svg{width:16px;height:16px}
/* The size slider was the one control that broke the line.

   `input[type=range]` sets its height to the 6px track. That selector is more
   specific than the row's one-height rule above, so the slider lost its height
   entirely and was propped back up by min-height alone ,  which .ctx-adopted had
   lowered to 32px. A 32px box centred in a 34px line sits a pixel low, and that
   single pixel was the ragged edge running along the whole toolbar.

   The track moves into the pseudo-element so the element itself is the same
   34px box as everything beside it and the row divides exactly. */
.top-contextual > #wbSize{
  height:34px !important;
  min-height:34px !important;
  max-height:34px !important;
  align-self:center;
  background:transparent;
  box-shadow:none;
}
.top-contextual > #wbSize::-webkit-slider-runnable-track{
  height:6px;border-radius:99px;
  background:var(--surface-3);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.35);
}
.top-contextual > #wbSize::-webkit-slider-thumb{margin-top:-6px}
.top-contextual > #wbSize::-moz-range-track{
  height:6px;border-radius:99px;
  background:var(--surface-3);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.35);
}
/* Pen slots are a control in this row like any other and were two pixels
   narrower than the buttons they sit between. */
/* The pen-slot chip is gone from both scripts that used to build it. This is the
   backstop: if anything else ever reintroduces #wbPenSlots or its toggle, it
   stays out of the row rather than reappearing beside the colours as a stray
   numbered button. The slot data itself is untouched ,  only the chip. */
.top-contextual > #wbPenSlots,
.top-contextual > #wbSlotsToggle,
#view-whiteboard #wbPenSlots,
#view-whiteboard #wbSlotsToggle{display:none !important}
.top-contextual .btn{padding:6px 10px;font-size:12px;min-height:32px}
/* An adopted control keeps its identity but presents as a compact bar button,
   so moving it up a row never makes the bar taller than the chrome around it. */
.top-contextual .ctx-adopted{min-height:32px}
/* Component styles are often scoped to their own view ,  `#view-praxis .px-btn`
   is the case that broke: once the button moved into the top row that selector
   stopped matching, so it fell back to an unstyled browser button and was then
   clipped by the bar.
   This applies only to real controls. Giving it to wrappers as well added a
   border and padding to things like the whiteboard's colour palette, which
   widened the row until it could no longer be made to fit. */
.top-contextual button.ctx-adopted,
.top-contextual a.ctx-adopted{
  display:inline-flex;align-items:center;gap:6px;
  padding:4px 10px;border-radius:var(--r-sm);
  border:1px solid var(--border);background:var(--surface-2);color:var(--fg);
  font:500 var(--fs-sm)/1 var(--sans);white-space:nowrap;
  box-shadow:var(--shadow-xs);
}
.top-contextual button.ctx-adopted svg,
.top-contextual a.ctx-adopted svg{width:15px;height:15px;flex:none;margin:0}
.top-contextual .ctx-adopted.primary{
  background:var(--accent);color:var(--paper);border-color:transparent;font-weight:600;
}
.top-contextual button.ctx-adopted.icon-only,
.top-contextual button.ctx-adopted.ibtn{width:34px;height:34px;padding:0;justify-content:center}
/* Centre the glyph itself, not just the button box.

   An adopted control is laid out by whatever styles it brought with it from its
   own view, and several of those centre the button while letting the icon sit
   on the text baseline or hang off by its own line-box. So the button measured
   correctly and the icon inside it did not, which is why the row looked
   aligned by its boxes and ragged by its pictures. The icon is made a block
   flex item of a centred flex button, so its position no longer depends on the
   font it inherited. */
.top-contextual button.ctx-adopted,
.top-contextual a.ctx-adopted{
  display:inline-flex;align-items:center;justify-content:center;
}
.top-contextual button.ctx-adopted>svg,
.top-contextual a.ctx-adopted>svg{
  display:block;flex:none;align-self:center;
  width:16px;height:16px;
}
/* An adopted control that is currently selected must still LOOK selected.

   `.top-contextual button.ctx-adopted` sets a background, and it is one class
   more specific than `.ibtn.active`, so every tool that reported itself active
   was painted with the ordinary button background the moment it moved into the
   row. In its own view the same button highlighted correctly, which is why the
   highlight looked like it worked everywhere except the toolbar. This repeats
   the selected styling one level deeper so the state is actually visible. */
.top-contextual button.ctx-adopted.active,
.top-contextual a.ctx-adopted.active,
.top-contextual .ibtn.active{
  background:linear-gradient(180deg,var(--accent-h),var(--accent));
  border-color:var(--accent);
  color:var(--accent-ink);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    0 6px 14px -4px var(--accent-glow);
}
.top-contextual button.ctx-adopted.icon-only{font-size:0}
.top-contextual button.ctx-adopted.icon-only svg{width:16px;height:16px}
/* A search field is a field, not a control that can be shrunk to an icon,
   so it keeps its text - but it does not need the whole row. The
   library's was 954 px of a 1440 px header, which is a third of the
   window spent on one box. */
.top-contextual input[type="search"]{max-width:240px}
/* Every control in the bar is an icon.

   A control that carried an icon printed its label next to it as well, so
   the row was twice as wide as it needed to be - and the row's width is the
   number that decides how many of the open tool's buttons stay on screen.
   The label moves into the tooltip and into the overflow sheet, which is
   where a name belongs once the button is a picture.

   The label is not removed from the markup: it is a span the bar hides, so
   the same control still shows its name in the sheet it is parked in, and
   still announces it to a screen reader. */
.top-contextual .ctx-label{display:none !important}
/* A control the icon pass could not give a picture to keeps its name.
   A button with neither a picture nor a word is an empty square: it
   looks like a mistake, and on a phone it is a target the size of a
   finger with nothing in it. */
.top-contextual .ctx-nopic .ctx-label{display:inline !important;font-size:11px;padding:0 6px}
.top-contextual .ctx-adopted.ctx-icon{width:34px;height:34px;padding:0;display:inline-flex;align-items:center;justify-content:center}
.top-contextual .ctx-adopted.ctx-icon svg{width:16px;height:16px}
.top-contextual .chip{padding:5px 10px;font-size:11px}
.top-contextual .seg{border-radius:6px}
.top-contextual .seg button{padding:4px 9px;font-size:10px;min-height:30px}
.top-contextual .fld{padding:0}
/* A field is label-above-control in a form, which is right there and wrong in a
   one-row bar: the stacked label pushed the control down, the row grew taller
   than the chrome around it, and the label text got clipped against the top
   edge. Inline puts the label beside its control so every bar item is the same
   height as the buttons next to it. */
.top-contextual .fld{
  flex-direction:row;align-items:center;gap:6px;
  min-height:32px;
}
.top-contextual .fld>span{font-size:9px;white-space:nowrap;flex:none}
/* A field's control keeps its intrinsic width: as a flex row item the select
   was free to shrink below its content, which cut the dropdown arrow off. */
.top-contextual .fld>select,
.top-contextual .fld>input,
.top-contextual .fld>.seg{flex:none}
/* The field must not shrink either. `flex:none` on the control stops the SELECT
   being squeezed inside its own field, but the field is itself a flex item of
   the bar and could still be given less room than it needs ,  the select then
   overflowed its field and its text ran over the control next to it. Measured on
   the quiz row: Type needed 200px and had 172, Count needed 100 and had 66,
   Difficulty needed 190 and had 126. A field is sized by its label plus its
   control, so it takes exactly that and gives way whole. */
.top-contextual .fld{flex:0 0 auto}
/* A field sized around a select needs the select's width decided, not derived.
   A `<select>` reports one width to the flex layout that decides how wide the
   field becomes, and paints at another that is driven by its widest OPTION , 
   measured on the quiz row, the field reserved 100px and the control painted
   130px, so the control borrowed the label's space and the two drew over each
   other. Deciding the width is what makes the field's arithmetic come out: hide
   the label and the field is exactly the control, force the control wider and
   the field grows by the same amount. Every option stays reachable in the
   dropdown; a long value ellipsises rather than spilling. */
/* Each to its own content, not one width for all of them: a single 126px made
   Count ("5") as wide as Type ("Assert-Reason") and left both looking half
   empty. */
.top-contextual #qzType{width:112px}
.top-contextual #qzCount{width:72px}
.top-contextual #qzLevel{width:112px}
.top-contextual #qzCompWrap>select{width:146px}
.top-contextual .lbl{font-size:9px}
.top-contextual select{padding:5px 8px;font-size:11px;min-height:30px}
.top-contextual input[type="range"]{width:80px}
/* More menu for controls the bar has no room for. It reuses .pop so it matches
   every other popup in the app; only the placement is fixed to the viewport
   because the bar itself scrolls and would clip a menu opened inside it. */
/* The focus timer is a setting, not a removal.

   Hiding it outright was wrong twice over: it took away a control the user
   reaches for, and the row's width is not the only thing that matters. Prefs.pomo
   now drives this (Settings -> Display & layout -> Taskbar), the class lands on
   <html> so nothing has to be re-hidden when the row re-mounts, and the timer
   keeps running either way ,  only its place in the row is at stake.

   #timebarBtn is the OTHER clock, and a separate one: it is not in the markup at
   all ,  js/study/270-settings.js creates it and appends it to .top-nav on boot , 
   so hiding #pomo left this stopwatch sitting in the row. It toggles the slim
   session-duration bar under the header (#lumen-timebar), which keeps its own
   setting in the same panel; only the taskbar button is suppressed here. */
html.hide-pomo .top-nav .pomo,
.top-nav #timebarBtn{display:none !important}
.ctx-more-btn{cursor:pointer}
.ctx-more-btn.ctx-idle{display:none!important}
/* The drag grip is disabled: it read as a stray vertical line next
   to the wordmark rather than as an affordance. Reinstating it needs a handle
   that looks deliberate, not a bare line. */
.ctx-grip{display:none!important}
/* The workspace's own header row is a duplicate of the top row on every device:
   its controls have been adopted into the bar (and its rail toggle and save
   chip moved into the settings cluster), so it is removed outright rather than
   only on phones. */
#view-study > .topbar{display:none!important}
/* The Whiteboard's floating tools and page strip are fixed to the window, so
   they stayed painted over other views when the board was not open. They belong
   to the board and are hidden whenever it is not the view on screen. */
#view-whiteboard:not(.active) .wb-quickbar,
#view-whiteboard:not(.active) .wb-pages-bar,
#view-whiteboard:not(.active) .wb-tool-head{display:none!important}
[data-ctx-adopted]{display:none!important}

.ctx-menu{position:fixed;left:0;top:0;right:auto;width:auto;min-width:210px;max-width:min(330px,92vw);padding:8px;gap:2px;z-index:130}
.ctx-menu>*{width:100%;justify-content:flex-start}
/* Inside the menu an adopted control is a named list row, not the square icon
   button it has to be in the bar. Keyed on the classes themselves rather than
   .ctx-icon: a control only gets that class when it was adopted icon-only, and
   the whiteboard's tools reach the bar as .ibtn squares instead ,  which left
   them zero-padded and butted against their own labels. */
.ctx-menu .ctx-adopted,
.ctx-menu .ibtn{
  width:auto;height:auto;min-width:0;min-height:30px;
  padding:5px 10px;display:flex;align-items:center;gap:10px;
  justify-content:flex-start;
}
.ctx-menu .ctx-adopted svg,
.ctx-menu .ibtn svg{width:16px;height:16px;flex:none;margin:0}
.ctx-menu .seg{width:100%}
.ctx-menu .seg button{flex:1}
.ctx-menu-label{
  font:500 var(--fs-sm)/1 var(--sans);
  color:var(--fg);letter-spacing:-.005em;
  padding-left:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ctx-menu-label{
  font:500 var(--fs-sm)/1 var(--sans);
  color:var(--fg);letter-spacing:-.005em;
  padding-left:2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* ---- The row on a phone ---- */
@media (max-width:820px){
  /* The spacer exists to push the icon cluster right on a desktop. With no room
     it was competing with the controls for the same pixels. */
  .top-nav>.flex-1{display:none}
  .top-contextual{
    /* Same rules as the desktop row, deliberately. The phone case used to give
       the bar `flex:1 1 auto` and `order:2`, which pushed it past the icon
       cluster ,  the controls for the tool you were in then sat to the right of
       the app's furniture, and the measured budget and the box the row actually
       laid out disagreed by the width of the whole cluster. The bar is the one
       flexible item in the row and the algorithm sizes it; ordering it last as
       well only put the two out of step. No minimum width either: when the row
       genuinely has no room the bar collapses to the menu that can reach
       everything, which is a state that reads correctly, rather than to 56px of
       a button that is cut off at the edge. */
    max-width:var(--ctx-avail,none);
  }
  .top-nav{gap:5px}
  /* At full size the timer was the single biggest thing in the row ,  wider than
     a third of a phone screen ,  so on a phone it keeps only the dial and the
     countdown. Tapping the time opens the timer settings, which is why the
     separate settings button is not needed here. */
  .top-nav .pomo{gap:5px;padding:0}
  .top-nav .pomo-btn{width:26px;height:26px}
  .top-nav .pomo-btn svg.ring{width:26px;height:26px}
  .top-nav .pomo-ic svg{width:11px;height:11px;display:block}
  .top-nav .pomo-meta b{font-size:12px}
  .top-nav #pomoCfgBtn{display:none}
  .top-nav .pomo-meta b{cursor:pointer}
  /* Pinned so the one control that reveals everything else is always on screen,
     however far the strip is scrolled. */
  .top-contextual>.ctx-more-btn{
    position:sticky;right:0;z-index:2;
    background:var(--surface);
    box-shadow:-8px 0 10px -8px rgba(0,0,0,.45);
  }
}

/* The 38vw ceiling that used to live here is gone: it hid controls on a tablet
   with room to spare, which is the same bug as the desktop cap. The bar now
   takes the width the header measures out for it at every size. */

/* A toolbar whose controls have all moved into the contextual bar collapses to
   nothing, so the feature gets the space back instead of showing an empty
   strip. */
.tool-head.ctx-collapsed,
.vbar.ctx-collapsed,
header.ctx-collapsed{display:none!important}
/* Keyed on the attribute as well as the class: the Whiteboard's own scripts
   re-show its toolbar, and losing that race is what made the bar look right for
   a moment and then snap back with a second toolbar underneath it. */
[data-ctx-adopted]{display:none!important}
@media (max-width:640px){
  /* No width cap here either. 26vw was a guess about how much a phone could
     spare, and it was a guess in the wrong direction: the row that was
     overflowing on a phone was this one. The bar keeps the width the header
     measures out for it, and only the control sizes inside it shrink. */
  .top-contextual{gap:4px}
  .top-contextual .ibtn{width:32px;height:32px;min-width:32px;min-height:32px}
  .top-contextual .btn{padding:5px 8px;font-size:11px}
  .top-contextual .chip{padding:4px 8px;font-size:10px}
}
@media (max-width:560px){
  /* The back arrow carries the wordmark's job once there is no room for both,
     and the arrow is there anyway on every screen but Home ,  so the name was
     being paid for twice over. With it gone the row has room for a control of
     the current view plus the menu that reaches the rest; with it kept, the row
     had 22px and not even the menu button fitted. This used to live at 400px,
     where it never actually worked: lumen-mobile.css pins .brand > span to
     display:inline-flex with !important, so the rule it was fighting lost at
     every width and the band between 400 and 560 was in the same state while
     looking handled. !important is what makes this one stick.
     The rest of this width is handled by the chrome tiers, which drop the same
     controls ,  but on the evidence of the row being short, not of a number
     typed into a stylesheet. */
  .top-nav .brand > span{display:none!important}
}

/* ============ Whiteboard Contextual Controls ============ */
.wb-ctx-controls{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}
.wb-ctx-controls .seg{border-radius:6px;border:1px solid var(--line2);background:var(--surface-2)}
.wb-ctx-controls .wb-ctx-tool{
  width:32px;height:32px;min-width:32px;min-height:32px;
  display:flex;align-items:center;justify-content:center;
  background:none;border:none;color:var(--ink);font:600 14px var(--sans);
  cursor:pointer;border-radius:4px;padding:0;
}
.wb-ctx-controls .wb-ctx-tool svg{width:16px;height:16px;flex:none}
.wb-ctx-controls .wb-ctx-tool:hover{background:color-mix(in srgb,var(--accent) 12%,var(--surface-2))}
.wb-ctx-controls .wb-ctx-tool.active{background:var(--accent);color:var(--accent-ink)}
.wb-ctx-controls .wb-ctx-shapes .wb-ctx-tool{font-size:13px}
.wb-ctx-controls .wb-ctx-colors{display:flex;gap:3px}
.wb-ctx-controls .wb-ctx-colors .swatch{width:20px;height:20px;min-width:20px;min-height:20px}
.wb-ctx-controls #wbTopSize{width:70px}
.wb-ctx-controls .wb-ctx-pages{display:flex;align-items:center;gap:4px}
.wb-ctx-controls .wb-ctx-btn{
  width:32px;height:32px;min-width:32px;min-height:32px;
  display:flex;align-items:center;justify-content:center;
  background:var(--surface-2);border:1px solid var(--line2);border-radius:4px;
  color:var(--ink);cursor:pointer;padding:0;
}
.wb-ctx-controls .wb-ctx-btn:hover{background:color-mix(in srgb,var(--accent) 12%,var(--surface-2));border-color:var(--accent)}
.wb-ctx-controls .wb-ctx-page-label{font:600 11px var(--mono);color:var(--ink);min-width:80px;text-align:center}
.wb-ctx-controls .ibtn{width:32px;height:32px;min-width:32px;min-height:32px}
.wb-ctx-controls .ibtn svg{width:16px;height:16px}
.wb-ctx-controls .btn{padding:6px 10px;font-size:12px}

/* Mobile adjustments */
@media (max-width:900px){
  .wb-ctx-controls .wb-ctx-shapes{display:none}
  .wb-ctx-controls .wb-ctx-page-label{min-width:60px;font-size:10px}
}
@media (max-width:640px){
  .wb-ctx-controls .wb-ctx-colors{display:none}
  .wb-ctx-controls #wbTopSize{display:none}
  .wb-ctx-controls .wb-ctx-btn{width:28px;height:28px;min-width:28px;min-height:28px}
  .wb-ctx-controls .wb-ctx-tool{width:28px;height:28px;min-width:28px;min-height:28px}
}

/* ============ Diagram Contextual Controls ============ */
.dg-ctx-controls{display:flex;align-items:center;gap:6px;flex-wrap:wrap;min-width:0}
.dg-ctx-controls .seg{border-radius:6px;border:1px solid var(--line2);background:var(--surface-2)}
.dg-ctx-controls .dg-ctx-btn{
  width:32px;height:32px;min-width:32px;min-height:32px;
  display:flex;align-items:center;justify-content:center;
  background:none;border:none;color:var(--ink);font:600 11px var(--mono);text-transform:uppercase;
  cursor:pointer;border-radius:4px;padding:0 8px;
}
.dg-ctx-controls .dg-ctx-btn:hover{background:color-mix(in srgb,var(--accent) 12%,var(--surface-2))}
.dg-ctx-controls .dg-ctx-btn.active{background:var(--accent);color:var(--accent-ink)}
.dg-ctx-controls .dg-ctx-btn.danger{color:var(--err)}
.dg-ctx-controls .dg-ctx-btn.danger:hover{background:color-mix(in srgb,var(--err) 12%,var(--surface-2))}
.dg-ctx-controls .dg-ctx-btn svg{width:14px;height:14px}
.dg-ctx-controls .dg-ctx-modes .dg-ctx-btn{font-size:10px;padding:0 6px}
.dg-ctx-controls .dg-ctx-zoom{display:flex;align-items:center;gap:4px}
.dg-ctx-controls .dg-ctx-zoomv{font:600 11px var(--mono);color:var(--ink);min-width:45px;text-align:center}
.dg-ctx-controls .dg-ctx-actions{display:flex;gap:2px}

/* Mobile adjustments */
@media (max-width:900px){
  .dg-ctx-controls .dg-ctx-modes{display:none}
  .dg-ctx-controls .dg-ctx-btn{font-size:10px;padding:0 6px}
}
@media (max-width:640px){
  .dg-ctx-controls .dg-ctx-zoom{display:none}
  .dg-ctx-controls .dg-ctx-btn{width:28px;height:28px;min-width:28px;min-height:28px}
}

.file-banner{position:fixed;top:calc(var(--topbar-h) + env(safe-area-inset-top,0px));left:env(safe-area-inset-left,0px);right:env(safe-area-inset-right,0px);z-index:49;display:none;align-items:center;gap:10px;padding:6px 14px;background:var(--accent);color:var(--accent-ink);font:600 11.5px var(--mono)}
.file-banner.show{display:flex}
.file-banner svg{flex:none}
.file-banner span{min-width:0;overflow-wrap:anywhere}
.file-banner button{margin-left:auto;flex:none;background:none;border:1px solid currentColor;color:inherit;border-radius:5px;padding:2px 9px;cursor:pointer;font:600 10px var(--mono);text-transform:uppercase;letter-spacing:.08em}

.cat-label{
  font-size:var(--fs-xs);
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--dim);
  margin-bottom:14px;
  margin-top:34px;
  display:flex;align-items:center;gap:12px;
}
.cat-label::after{
  content:'';
  flex:1;height:1px;
  background:linear-gradient(90deg,var(--border) 0%,transparent 100%);
}
.cat-label:first-of-type{margin-top:0}
body.has-bg{position:relative}
body.has-bg::before{content:'';position:fixed;inset:0;background:var(--home-bg,url('')) center/cover no-repeat;opacity:0.18;pointer-events:none;z-index:-1}
.tool-card{
  position:relative;
  overflow:hidden;
  background:
    linear-gradient(180deg,
      color-mix(in srgb,var(--surface-2) 62%,transparent),
      color-mix(in srgb,var(--surface) 82%,transparent));
  border:1px solid var(--border);
  border-radius:var(--r-md);
  padding:16px;
  cursor:pointer;
  display:flex;align-items:center;gap:14px;
  text-align:left;
  -webkit-backdrop-filter:blur(14px);
  backdrop-filter:blur(14px);
  box-shadow:
    var(--shadow-inset),
    0 4px 16px -12px rgba(0,0,0,.6);
  transition:
    transform var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    background var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.tool-card::before{
  content:'';
  position:absolute;inset:0;
  border-radius:inherit;
  pointer-events:none;
  background:radial-gradient(400px 200px at 20% 0%,
    color-mix(in srgb,var(--accent) 18%,transparent),
    transparent 55%);
  opacity:0;
  transition:opacity var(--dur-base) var(--ease-out);
}
.tool-card:hover{
  transform:translateY(-3px);
  border-color:color-mix(in srgb,var(--accent) 38%,var(--border));
  background:
    linear-gradient(180deg,
      color-mix(in srgb,var(--surface-3) 75%,transparent),
      color-mix(in srgb,var(--surface-2) 90%,transparent));
  box-shadow:
    var(--shadow-inset),
    0 20px 42px -20px color-mix(in srgb,var(--accent) 32%,transparent),
    0 6px 20px -10px rgba(0,0,0,.6);
}
.tool-card:hover::before{opacity:1}
.tool-card:active{transform:translateY(-1px) scale(.995)}

/* Icon badge ,  a hole in the card, ringed in the accent */
.tool-card .tc-icon{
  position:relative;
  width:42px;height:42px;
  border-radius:var(--r-sm);
  /* The colours come from --tc-icon-*, so the theme layer owns them, and
     each one carries a literal fallback. The fallback is the point: the fill
     and the outline used to be two colour-mix() calls over theme variables,
     and a colour-mix() with an unresolved variable is invalid at
     computed-value time ,  the background shorthand dropped to its initial
     value and the border colour fell back to currentColor, which this same
     rule sets to the accent. So on a load where those variables had not
     resolved yet the tile rendered as one button and, a moment later, the
     very same rule rendered it as a different one. A literal cannot collapse. */
  background:var(--tc-icon-bg, transparent);
  border:1px solid var(--tc-icon-border, currentColor);
  color:var(--accent);
  display:flex;align-items:center;justify-content:center;
  flex-shrink:0;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.08),
    0 4px 10px -4px rgba(0,0,0,.35);
  transition:
    transform var(--dur-base) var(--ease-spring),
    background var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}.tool-card .tc-icon svg{
  width:20px;height:20px;
  stroke-width:1.9;
  transition:transform var(--dur-base) var(--ease-spring);
}
.tool-card:hover .tc-icon{
  transform:rotate(-3deg) scale(1.05);
  background:var(--tc-icon-hover-bg, var(--accent));
  border-color:var(--tc-icon-hover-border, var(--accent));
  color:var(--tc-icon-hover-fg, var(--accent-ink));
  box-shadow:
    var(--tc-icon-hover-shadow, inset 0 1px 0 rgba(255,255,255,.3), 0 8px 20px -6px var(--accent-glow));
}
.tool-card:hover .tc-icon svg{transform:scale(1.08)}

.tool-card > div:last-child{min-width:0;flex:1}

.tool-card h3{
  font-size:var(--fs-base);
  font-weight:600;
  letter-spacing:-.008em;
  line-height:1.3;
  margin-bottom:3px;
  color:var(--fg);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:color var(--dur-fast);
}
.tool-card p{
  font-size:var(--fs-xs);
  color:var(--muted);
  line-height:1.45;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  letter-spacing:-.002em;
  transition:color var(--dur-fast);
}
.tool-card:hover p{color:color-mix(in srgb,var(--fg) 55%,var(--muted))}
.hero-title{
  font-family:var(--serif);
  font-weight:400;
  font-size:clamp(64px,9vw,116px);
  line-height:.9;
  letter-spacing:-.03em;
  /* Subtle gradient on the accent dot only ,  keeps the word clean */
}
.hero-title i{
  color:var(--accent);
  font-style:normal;
  display:inline-block;
  /* Halos the accent dot, a proper Linear-grade detail */
  text-shadow:0 0 40px color-mix(in srgb,var(--accent) 55%,transparent);
  animation:heroPulse 4s var(--ease-in-out) infinite;
}
@keyframes heroPulse{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.82;transform:scale(.94)}
}
.hero-sub{
  color:var(--muted);
  letter-spacing:-.005em;
  margin-top:18px;
  font-size:var(--fs-lg);
  font-weight:400;
  max-width:52ch;margin-left:auto;margin-right:auto;
  text-wrap:balance;
}
.hero-note{
  color:var(--dim);
  font:600 var(--fs-2xs) var(--mono);
  letter-spacing:.18em;
  text-transform:uppercase;
  margin-top:22px;
  display:inline-flex;align-items:center;gap:8px;
  justify-content:center;
  padding:5px 12px;
  border-radius:var(--r-full);
  background:color-mix(in srgb,var(--surface-2) 60%,transparent);
  border:1px solid var(--border);
}
.hero-note::before{
  content:'';
  width:5px;height:5px;border-radius:50%;
  background:var(--ok);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 22%,transparent);
}

/* Home search ,  Linear-grade command palette feel */
.home-search{
  display:flex;align-items:center;gap:10px;
  max-width:600px;margin:36px auto 0;
  padding:12px 14px 12px 16px;
  background:color-mix(in srgb,var(--surface) 78%,transparent);
  -webkit-backdrop-filter:saturate(180%) blur(16px);
  backdrop-filter:saturate(180%) blur(16px);
  border:1px solid var(--border);
  border-radius:var(--r-md);
  box-shadow:
    var(--shadow-inset),
    0 12px 32px -16px rgba(0,0,0,.6);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.home-search:focus-within{
  border-color:color-mix(in srgb,var(--accent) 45%,var(--border));
  box-shadow:
    var(--ring-soft),
    var(--shadow-inset),
    0 16px 44px -18px var(--accent-glow);
}
.home-search svg{width:18px;height:18px;color:var(--muted);flex:none;transition:color var(--dur-fast)}
.home-search:focus-within svg{color:var(--accent)}
.home-search input{
  background:none;border:none;box-shadow:none;padding:0;
  font-size:var(--fs-md);
  flex:1;min-width:0;
  letter-spacing:-.005em;
}
.home-search input:focus{border-color:transparent;box-shadow:none}
.home-search kbd{
  font:600 var(--fs-2xs) var(--mono);
  letter-spacing:.06em;
  border:1px solid var(--border-s);
  background:var(--surface-2);
  border-radius:var(--r-xs);
  padding:3px 7px;
  color:var(--muted);
  flex:none;
  box-shadow:0 1px 0 rgba(255,255,255,.04) inset,0 1px 2px rgba(0,0,0,.25);
}
.mobile-note{display:none;align-items:center;gap:10px;margin:18px auto 0;max-width:560px;padding:10px 14px;border:1px solid var(--line2);border-radius:10px;background:color-mix(in srgb,var(--accent) 8%,transparent);font-size:12.5px;color:var(--soft);line-height:1.5}
.mobile-note svg{flex:none;color:var(--accent)}
.mobile-note button{margin-left:auto;flex:none;background:none;border:1px solid var(--line2);border-radius:6px;padding:3px 10px;color:var(--soft);cursor:pointer;font:600 10px var(--mono);text-transform:uppercase}
@media(max-width:720px){.mobile-note.show{display:flex}}

.tool-head{display:flex;align-items:center;gap:14px;padding:11px 20px;border-bottom:1px solid var(--border);flex:none;flex-wrap:wrap;min-height:56px}
.tool-title{font-family:var(--serif);font-weight:400;font-size:27px;line-height:1.05}
.tool-sub{font-size:12px;color:var(--muted);margin-top:2px}
.tool-body{flex:1;overflow:auto;padding:24px 22px}
.twrap{max-width:880px;margin:0 auto}
.guide-wrap{max-width:860px;margin:0 auto;padding:24px 22px 60px;overflow:auto}
.guide-wrap h2{font:italic 600 28px var(--serif);margin:24px 0 8px;color:var(--ink)}
.guide-wrap h2:first-child{margin-top:0}
.guide-wrap p,.guide-wrap li{font-size:14px;line-height:1.65;color:var(--soft)}
.guide-wrap ul,.guide-wrap ol{padding-left:24px;margin:8px 0 18px}
.guide-wrap code{font:12px var(--mono);color:var(--ink);background:var(--panel2);border:1px solid var(--line);border-radius:5px;padding:2px 5px;overflow-wrap:anywhere}
.guide-code{display:block;margin:10px 0 18px;padding:12px 14px;background:var(--panel2);border:1px solid var(--line);border-radius:8px;color:var(--ink);font:12px/1.6 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;user-select:text}
.library-tools{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:18px}
.library-tools input{flex:1;min-width:220px}
.library-tools .lib-toggle{display:inline-flex;align-items:center;gap:7px;font:12px var(--mono);color:var(--soft);cursor:pointer;white-space:nowrap}
.library-tools .lib-toggle input{flex:0 0 auto;min-width:0;width:15px;height:15px;accent-color:var(--accent)}
.library-item .lib-badge{position:absolute;left:8px;top:8px;padding:3px 8px;border-radius:20px;background:rgba(0,0,0,.6);color:#fff;font:600 10px var(--mono);letter-spacing:.06em;text-transform:uppercase;backdrop-filter:blur(4px)}
.library-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(410px,100%),1fr));gap:16px}
.library-grid .lib-sub{grid-template-columns:repeat(auto-fill,minmax(min(410px,100%),1fr));gap:16px}
.library-grid .lib-sub .library-item{padding:16px}
.lib-section{margin-bottom:6px}
.lib-section h3{display:flex;align-items:baseline;gap:8px;font:600 10px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--dim);margin:16px 0 10px;padding-bottom:6px;border-bottom:1px solid var(--line)}
.lib-section h3:first-child{margin-top:0}
.lib-section h3 .count{color:var(--accent)}
.library-item{display:flex;flex-direction:column;gap:11px;padding:16px;background:var(--panel);border:1px solid var(--line);border-radius:12px;min-width:0}
.library-item .lib-icon{height:150px;display:grid;place-items:center;background:var(--panel2);border-radius:9px;color:var(--accent);font:italic 600 27px var(--serif);overflow:hidden}
.library-item .lib-icon img,.library-item .lib-icon video{width:100%;height:100%;object-fit:cover}
.library-item .lib-icon.has-thumb{position:relative}
.library-item .lib-icon .thumb-x{position:absolute;top:5px;right:5px;width:22px;height:22px;border:none;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;font:600 14px/1 var(--mono);cursor:pointer;opacity:0;transition:opacity .15s}
.library-item .lib-icon.has-thumb:hover .thumb-x{opacity:1}
.library-item .lib-icon .thumb-x:hover{background:rgba(0,0,0,.75)}
.library-item .lib-name{font:600 14px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.library-item .lib-meta{font:11px var(--mono);color:var(--soft)}
.library-item .lib-actions{display:flex;gap:6px;margin-top:auto;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;padding-bottom:2px}
.library-item .lib-actions::-webkit-scrollbar{display:none}
.library-item .lib-actions .btn{flex:0 0 auto;justify-content:center;font-size:11px;padding:7px 11px;min-width:0;white-space:nowrap}
.library-grid .lib-sub .notebook-item{flex-direction:row;align-items:stretch}
.notebook-item .lib-icon{width:190px;height:auto;min-height:138px;flex:none;border-radius:9px}
.notebook-item .lib-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px;align-items:stretch}
.notebook-item .lib-name{font-size:15px}
.notebook-item .lib-sets{display:flex;flex-direction:column;gap:6px;min-width:0;margin-top:2px}
.notebook-item .lset{display:flex;align-items:center;justify-content:space-between;gap:8px;background:var(--panel2);border-radius:7px;padding:7px 10px;font:12px var(--mono);color:var(--soft);min-width:0}
.notebook-item .lset b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-weight:600}
.notebook-item .lset span{flex:none;color:var(--dim)}
.notebook-item .lset .btn{flex:none;font-size:10px;padding:4px 10px}
.notebook-item .lib-actions{margin-top:auto}
@media(max-width:700px){
  .library-grid .lib-sub .notebook-item{flex-direction:column}
  .notebook-item .lib-icon{width:100%;min-height:150px}
}
@media(pointer:coarse){.library-item .lib-icon.has-thumb .thumb-x{opacity:1}}
.notebar{display:flex;align-items:center;gap:10px;padding:8px 14px;border-bottom:1px solid var(--line);background:var(--panel2);flex:none}
.notebar .nb-label{font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft);flex:none}
.notebar .seg{flex:1;min-width:0;overflow-x:auto;scrollbar-width:none}
.notebar .seg::-webkit-scrollbar{display:none}
.rpSpin{width:13px;height:13px;border:2px solid color-mix(in srgb,var(--accent) 30%,transparent);border-top-color:var(--accent);border-radius:50%;display:inline-block;animation:rspin .8s linear infinite;vertical-align:-3px;margin-right:3px}
#rpGenMode button.spinning{color:var(--accent);pointer-events:none}
.top-actions{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  /* One toolbar that gets taller, never a stack of separate rows.

     The controls wrap onto the next line as the bar is given more height, so
     "taller" means "room for a few more buttons" rather than "a new row appears
     with its own furniture". align-content pins the wrapped lines to the top so
     the block grows downwards from a fixed edge instead of centring itself and
     moving as lines are added, and row-gap is set so a wrapped line is spaced
     like any other gap in the row. */
  align-content:flex-start;row-gap:6px;
  /* Nothing here may draw a line between wrapped lines ,  no borders, no
     background banding, no per-line separators. */
  border:0;box-shadow:none;background:none}
.vsep{width:1px;height:22px;background:var(--border);margin:0 4px;flex:none}
.minsel{padding:6px 8px;font-size:12px;width:auto;flex:none}
.drop{border:1.5px dashed var(--line2);border-radius:14px;padding:48px 20px;text-align:center;transition:.15s;cursor:pointer}
.rv-drop{position:relative}
.rv-drop:focus-within{outline:2px solid var(--accent);outline-offset:3px}
.rv-native-file{position:absolute;inset:0;width:100%;height:100%;box-sizing:border-box;margin:0;padding:0;border:0;opacity:.001;cursor:pointer;z-index:2;font-size:100px;-webkit-appearance:none;appearance:none;touch-action:manipulation}
.drop svg{width:30px;height:30px;color:var(--soft)}
.drop .serif{font:italic 600 25px var(--serif);margin:12px 0 8px}
.drop.over{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 7%,transparent)}
.ext{font:600 11px var(--mono);letter-spacing:.1em;background:var(--ink);color:var(--paper);padding:4px 9px;border-radius:6px;flex:none}
.frow{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--panel);margin-bottom:8px}
.frow img{width:46px;height:46px;object-fit:cover;border-radius:6px;border:1px solid var(--line);flex:none;background:var(--panel2)}
.frow .meta{flex:1;min-width:0}
.frow .nm{font:600 13px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.frow .sz{font:11px var(--mono);color:var(--soft)}
.frow .tb{display:flex;gap:6px;flex:none}
.mini-ic{width:26px;height:26px;border-radius:6px;border:1px solid var(--line);background:none;color:var(--soft);cursor:pointer;display:grid;place-items:center;flex:none}
.mini-ic:hover{color:var(--ink);border-color:var(--line2)}
.mini-ic svg{width:13px;height:13px}
.copt{display:flex;gap:16px;align-items:flex-end;padding:15px 18px;margin-top:14px;margin-bottom:10px;flex-wrap:wrap}
.copt.busy{opacity:.55;pointer-events:none}
.copt-title{font:italic 600 17px var(--serif)}
.copt-opts{display:flex;gap:14px;margin-top:9px;flex-wrap:wrap;align-items:flex-end}
.mini{display:flex;align-items:center;gap:7px;font:600 10px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--soft)}
.mini select{padding:4px 7px;font:12px var(--sans);width:auto}
.mini input[type=number]{width:74px;padding:4px 7px;text-align:center;font:12px var(--mono)}
.cprog{padding:16px 18px;margin:14px 0}
.pbar{height:7px;border:1px solid var(--line2);border-radius:99px;overflow:hidden;margin-top:9px}
.pbar i{display:block;height:100%;background:var(--accent);width:0;transition:width .25s}
.pbar.ind i{width:38%;animation:sweep 1.1s ease-in-out infinite alternate}
@keyframes sweep{from{margin-left:0;width:30%}to{margin-left:65%;width:35%}}
.cres{padding:18px;margin-top:14px}
.cres b.r-name{font:600 14px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cres .r-size{font:11.5px var(--mono);color:var(--soft);flex:none}
.cres img{max-width:100%;max-height:420px;display:block;border:1px solid var(--line);margin-top:12px}
.cres pre{font:12px var(--mono);max-height:260px;overflow:auto;white-space:pre-wrap;background:var(--paper);padding:12px;border:1px solid var(--line);border-radius:8px;margin-top:12px}
.res-meta{display:flex;align-items:center;gap:14px;flex-wrap:wrap}

/* whiteboard compact toolbar ,  slightly bigger per request */
.tool-head.compact{padding:7px 12px;gap:7px;min-height:52px}
.tool-head.compact .tool-title{font-size:19px;line-height:1.1}
.tool-head.compact .tool-sub{display:none}
.tool-head.compact .btn{padding:6px 11px;font-size:12.5px;gap:5px}
.tool-head.compact .ibtn{width:38px;height:38px;border-radius:8px}
.tool-head.compact .ibtn svg{width:18px;height:18px}
.tool-head.compact .vsep{height:18px;margin:0 1px}
.tool-head.compact .swatch{width:20px;height:20px;border-width:2px}
.tool-head.compact .mini{font-size:9.5px}
.tool-head.compact .top-actions{gap:6px}


#view-study{background:var(--paper)}
.vbar{display:flex;align-items:center;gap:10px;padding:9px 16px;border-bottom:1px solid var(--line);flex:none;min-height:54px;flex-wrap:wrap}
.vtitle{font:italic 600 20px var(--serif)}
.vbody{flex:1;overflow:auto;position:relative;padding:20px 22px 70px}
.rname{font:11.5px var(--mono);color:var(--soft);border-left:1px solid var(--line);padding-left:12px;max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#studyMain{flex:1;min-width:0;position:relative;overflow:hidden;background:var(--paper)}
.shell{flex:1;display:flex;min-height:0}
/* Ten destinations plus the calendar footer do not fit on short viewports, and
   overflow:hidden clipped the last entries with no way to reach them. Scroll
   the rail instead; the bar stays visually identical because the scrollbar is
   hidden until the pointer is actually over it. */
.rail{
  width:78px;border-right:1px solid var(--line);display:flex;flex-direction:column;
  padding:10px 0;gap:2px;flex:none;
  min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:transparent transparent;
}
.rail:hover{scrollbar-color:var(--line2) transparent}
.rail::-webkit-scrollbar{width:6px}
.rail::-webkit-scrollbar-track{background:transparent}
.rail::-webkit-scrollbar-thumb{background:transparent;border-radius:99px}
.rail:hover::-webkit-scrollbar-thumb{background:var(--line2)}
/* The footer pins to the bottom only while there is room for it. */
.rail .foot{flex:none;margin-top:auto}
.rail button{position:relative;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 2px 11px;background:none;border:none;color:var(--soft);cursor:pointer}
.rail button svg{width:19px;height:19px}
.rail button span{
  font:600 8px var(--mono);
  letter-spacing:.04em;
  text-transform:uppercase;
  max-width:100%;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
  line-height:1.4;
  text-align:center;
}
.rail button:hover{color:var(--rail-btn-hover-fg);background:var(--rail-btn-hover-bg);border-color:var(--rail-btn-hover-border)}
.rail button.on{color:var(--rail-btn-on-fg);background:var(--rail-btn-on-bg)}
.rail button.on::before{content:'';position:absolute;left:0;top:9px;bottom:9px;width:3px;background:var(--accent);border-radius:0 3px 3px 0}
.rail .foot{margin-top:auto;text-align:center;font:600 8.5px var(--mono);letter-spacing:.1em;color:var(--dim);padding:0 2px}
section.study-view{position:absolute;inset:0;display:flex;flex-direction:column}
#view-study .topbar{min-height:54px;display:flex;align-items:center;gap:14px;padding:0 14px 0 16px;border-bottom:1px solid var(--line);flex:none;flex-wrap:wrap}
#view-study .brand{font:italic 600 25px/1 var(--serif);position:relative;padding-bottom:4px;letter-spacing:-.01em;background:none;border:none;color:var(--ink);cursor:default}
#view-study .brand .scrawl{position:absolute;left:-1px;bottom:-2px;width:calc(100% - 25px);height:8px}
.pgnav,.zgroup{display:flex;align-items:center;gap:6px}
.pgnav input{width:44px;text-align:center;padding:5px 6px;font:600 12px var(--mono)}
.zgroup .dim{font:600 11px var(--mono);min-width:38px;text-align:center}
.pomo{display:flex;align-items:center;gap:9px;padding:4px 10px 4px 4px;border:1px solid var(--line2);border-radius:99px;position:relative}
.pomo-btn{position:relative;width:38px;height:38px;padding:0;border-radius:50%;background:none;border:none;cursor:pointer;display:grid;place-items:center;outline:none}
.pomo-btn svg.ring{position:absolute;inset:0;width:38px;height:38px;transform:rotate(-90deg)}
.pomo-btn .rbg{fill:none;stroke:var(--line);stroke-width:3}
.pomo-btn .rfg{fill:none;stroke:var(--accent);stroke-width:3;transition:stroke-dashoffset .3s linear}
.pomo-ic{display:grid;place-items:center}
.pomo-ic svg{width:13px;height:13px;display:block}
.pomo.running .pi-a{display:none}.pomo:not(.running) .pi-b{display:none}
.pomo-meta{display:flex;flex-direction:column;line-height:1.2}
.pomo-meta b{font:600 14px var(--mono)}
.pomo-meta .lbl{font-size:8.5px}
/* In the top row the timer is chrome competing with real controls for width, so
   it is compact: a smaller dial, no pill border, no separate settings button , 
   tapping the countdown opens the same panel, which is why the button was only
   ever a second way to reach a panel already one tap away. At full size it read
   as a second toolbar crammed into the header. */
.top-nav .pomo{gap:5px;padding:0 2px 0 0;border:none;background:none}
.top-nav .pomo-btn{width:26px;height:26px}
.top-nav .pomo-btn svg.ring{width:26px;height:26px}
.top-nav .pomo-ic svg{width:11px;height:11px;display:block}
.top-nav .pomo-meta b{font-size:11.5px;letter-spacing:-.01em}
.top-nav .pomo-meta .lbl{display:none}
.top-nav .pomo:hover .pomo-meta .lbl{display:block}
/* Not hidden, duplicated: #pomoTime opens #pomoPop on click. */
.top-nav #pomoCfgBtn{display:none}
/* ---- The row's own furniture, in its compact form ----
   The timer, the model picker and the undo strip are ranked below every control
   the open tool contributes, so these give up the row first when it runs short.
   That only works if they are cheap while they are still in it: the model
   picker used to carry its full name at 240px, which was four buttons' worth of
   width spent restating what the row already had a dot for. */
.top-nav .ai-model-btn{max-width:104px;padding:5px 8px;gap:5px;font-size:11px}
.top-nav .ai-model-btn .ai-model-label{letter-spacing:-.01em}
.top-nav .ai-model-btn .ai-model-chev{width:10px;height:10px}
/* Connection state is a dot and nothing else. The word beside it was the widest
   single label in the row, and the row's width is what decides how many
   controls of the tool you are in stay on screen. */
.top-nav .net-badge{
  padding:0;border:none;background:none;
  width:14px;height:14px;justify-content:center;
}
.net-badge{
  /* The connection dot lives in the very corner of the screen: out of the way of the
     toolbar, always in the same place, however the row is arranged below it. */
  position:fixed;top:6px;right:8px;z-index:60;
  margin:0;padding:4px;border:0;border-radius:50%;
  background:var(--net-badge-bg);cursor:pointer;line-height:0;
}
.top-nav .net-badge{position:fixed;top:6px;right:8px;width:14px;height:14px;
  padding:0;border:none;background:var(--net-badge-bg);justify-content:center}
.top-nav .net-badge .net-dot{width:7px;height:7px;box-shadow:0 0 0 3px color-mix(in srgb,var(--ok) 18%,transparent)}
.top-nav .net-badge:hover{border-color:var(--net-badge-hover-border);box-shadow:0 0 0 1px var(--border-s)}/* The notebook chip reports one workspace, so it is shown for that workspace
   and nowhere else ,  the same scoping as the rail toggle, and for the same
   reason: a status that means nothing on the screen you are looking at is just
   width spent. */
.top-nav #railToggle{display:none!important}
body.study-mode .top-nav #railToggle{display:flex!important}
body:not(.study-mode) #nbSaveChip{display:none!important}
.top-nav .nb-chip{padding:3px 8px;font-size:10.5px;flex:none}
.pop{
  position:absolute;
  top:calc(100% + 10px);
  right:0;
  z-index:80;
  width:262px;
  background:var(--glass-bg);
  border:1px solid var(--glass-border);
  border-radius:var(--r-md);
  box-shadow:
    var(--glass-shadow);
  padding:16px;
  display:flex;flex-direction:column;gap:11px;
  -webkit-backdrop-filter:var(--glass-blur);
  backdrop-filter:var(--glass-blur);
  animation:popIn var(--dur-base) var(--ease-out);
}@keyframes popIn{
  from{opacity:0;transform:translateY(6px) scale(.98)}
  to{opacity:1;transform:none}
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .pop{background:var(--surface)}
}
.pop-head{font:italic 600 17px var(--serif)}
.pop .row{display:flex;align-items:center;justify-content:space-between;gap:10px}
.pop .row input[type=number]{width:74px;text-align:center}
.pop .row2{display:flex;gap:8px}
.pop .row2 .btn{flex:1}
.dotsline{display:flex;align-items:center;gap:6px;font:12px var(--mono)}
.dotsline .mark{color:var(--accent);letter-spacing:2px}
.rbody{flex:1;display:flex;min-height:0;position:relative;overflow:hidden}
.rbody.night .pg{background:#141414}
.rbody.night .pg canvas{filter:invert(1) hue-rotate(180deg)}
.rbody.night .pg.dx{filter:invert(1) hue-rotate(180deg)}
/* FIX: hide the reader top toolbar AND the side panel together */
.study-view.ui-hidden .vbar{display:none}
.study-view.ui-hidden .rside{display:none}
#rRestore{display:none;position:absolute;top:14px;right:14px;z-index:8;opacity:.65;background:var(--surface)}
#rRestore:hover{opacity:1}
.rbody.ui-hidden #rRestore{display:flex}
#pages{flex:1;overflow:auto;padding:26px 18px 70px;display:block}
.pg{position:relative;margin:0 auto 24px;background:#fff;box-shadow:0 1px 0 rgba(0,0,0,.06),var(--shadow)}
.pg canvas{display:block}
.pg.flash{box-shadow:0 0 0 4px var(--accent),var(--shadow)}
.pg.dx{max-width:840px;padding:44px 52px;font-family:var(--read);font-size:15.5px;line-height:1.7;color:#161616;overflow-wrap:anywhere}
.pg.dx h1,.pg.dx h2,.pg.dx h3,.pg.dx h4{font-family:var(--serif);margin:.9em 0 .4em;line-height:1.25}
.pg.dx h1{font-size:24px}.pg.dx h2{font-size:20px}.pg.dx h3{font-size:17px}
.pg.dx p{margin:0 0 .85em}
.pg.dx ul,.pg.dx ol{padding-left:1.5em;margin:0 0 .9em}
.pg.dx li{margin:.25em 0}
.pg.dx table{border-collapse:collapse;margin:0 0 1em}
.pg.dx th,.pg.dx td{border:1px solid #bbb;padding:5px 9px;font-size:13px}
.pg.dx img{max-width:100%;height:auto;margin:.4em 0}
#rBody.docx .pgnav,#rBody.docx .zgroup{display:none}
.tl{position:absolute;inset:0;overflow:visible;line-height:1;cursor:text;user-select:text}
.tl span{position:absolute;color:transparent;white-space:pre;font-family:var(--read);transform-origin:0 0}
.tl span::selection{background:color-mix(in srgb,var(--accent) 35%,transparent);color:transparent}
.empty{max-width:440px;margin:9vh auto 0;text-align:center;padding:20px}
.empty h2{font:italic 600 34px/1.15 var(--serif);margin:14px 0 10px}
.empty p{color:var(--soft);font-size:14px;margin:0 0 22px}
.empty .inkart{width:130px;color:var(--soft);opacity:.85;margin:0 auto;display:block}
.rside{width:var(--rsideW,292px);flex:none;position:relative;border-left:1px solid var(--line);background:var(--panel);display:flex;flex-direction:column;transition:margin-right .28s cubic-bezier(.4,0,.2,1),opacity .28s,transform .28s;min-height:0}
#rSideGrip{position:absolute;left:-6px;top:0;bottom:0;width:12px;cursor:col-resize;z-index:6;background:none;border:none;padding:0;touch-action:none}
#rSideGrip::after{content:'';position:absolute;left:5px;top:0;bottom:0;width:2px;border-radius:2px;background:transparent;transition:background .15s}
#rSideGrip:hover::after,#rSideGrip.dragging::after{background:var(--accent)}
.rbody.side-closed #rSideGrip{display:none}
.rbody.side-closed .rside{margin-right:calc(-1*var(--rsideW,292px));opacity:0;pointer-events:none}
.rbody:not(.side-closed) .when-closed{display:none}
.rbody.side-closed .when-open{display:none}
/* One icon, never two.

   The panel toggle carries both of its states - the one for "the panel is
   open" and the one for "the panel is closed" - and the two rules above
   are meant to show one and hide the other. They did not: `#rPanel
   .when-open` is an ID selector and an ID beats three classes however
   many there are, so the state rules lost and the button drew BOTH
   glyphs side by side, twice as wide as every other control in the bar.

   The state lives on `.rbody` - but the control itself is ADOPTED into
   the top bar, so it is no longer a descendant of `.rbody` and a rule
   keyed on `.rbody` cannot reach it however specific it is. The body
   holds both, so the body is what asks. */
body:has(.rbody.side-closed) #rPanel .when-open{display:none}
body:has(.rbody:not(.side-closed)) #rPanel .when-closed{display:none}
.rbody:not(.side-closed) .when-closed{display:none}
.rbody.side-closed .when-open{display:none}
.rtabs{display:flex;border-bottom:1px solid var(--line);flex:none}
.rtabs button{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:11px 0;background:none;border:none;font:600 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--soft);cursor:pointer;box-shadow:inset 0 -2px transparent}
.rtabs button svg{width:13px;height:13px}
.rtabs button.on{color:var(--rtabs-btn-on-fg);box-shadow:inset 0 -2px var(--accent)}
.rtab-body{flex:1;overflow:auto;min-height:0}
#rtThumbs{display:grid;gap:14px;padding:14px;justify-items:center}
.thumb{width:138px;background:#fff;border:1px solid var(--line);position:relative;cursor:pointer}
.thumb:hover{border-color:var(--line2)}
.thumb.cur{outline:2px solid var(--accent);outline-offset:-1px}
.thumb canvas{width:100%;display:block}
.thumb span{position:absolute;right:4px;bottom:3px;font:600 9px var(--mono);background:var(--paper);padding:1px 5px;border-radius:4px}
.ol-item button{display:block;width:100%;text-align:left;background:none;border:none;color:var(--ink);font:13px var(--sans);padding:7px 12px 7px 10px;cursor:pointer;border-radius:6px;overflow-wrap:anywhere}
.ol-item button:hover{background:var(--panel2)}
.rt-search{padding:12px;border-bottom:1px solid var(--line)}
.hit{display:flex;gap:9px;align-items:flex-start;width:100%;text-align:left;background:none;border:none;border-bottom:1px solid var(--line);padding:10px 12px;cursor:pointer;font:12.5px var(--read);color:var(--ink)}
.hit:hover{background:var(--panel2)}
.hit .pgn{font:600 9.5px var(--mono);color:var(--accent);flex:none;margin-top:3px}
.hit mark{background:color-mix(in srgb,var(--accent) 28%,transparent);color:inherit}
.rside-foot{border-top:1px solid var(--line);padding:11px 12px;display:flex;gap:8px;flex:none}
.rside-foot .btn{flex:1;min-width:0;padding:8px 6px;font-size:11px;line-height:1.15;white-space:normal;text-align:center}
.selpop{position:fixed;z-index:500;display:flex;border:1px solid var(--line2);border-radius:9px;background:var(--panel);box-shadow:var(--shadow);overflow:hidden}
.selpop button{display:flex;gap:6px;align-items:center;padding:9px 13px;background:none;border:none;border-right:1px solid var(--line);font:600 10.5px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ink);cursor:pointer}
.selpop button:last-child{border-right:none}
.selpop button:hover{background:var(--ink);color:var(--paper)}
/* FIX: chat covers the page; optional Focus mode centers it */
.chat-wrap{flex:1;display:flex;flex-direction:column;min-height:0;width:100%}
.chat-wrap.focus{max-width:900px;margin:0 auto}
.msgs{flex:1;overflow:auto;padding:26px 28px;min-height:0;scroll-behavior:smooth}
.chat-empty{max-width:520px;margin:10vh auto 0;text-align:center}
.chat-empty h2{font:italic 600 32px/1.15 var(--serif);margin:0 0 10px}
.chat-empty p{color:var(--soft);margin:0 0 20px}
.sugs{display:flex;flex-direction:column;gap:9px;align-items:center}
.msg{
  margin:0 0 26px;
  max-width:78%;
  animation:msgIn var(--dur-base) var(--ease-out);
}
@keyframes msgIn{
  from{opacity:0;transform:translateY(8px)}
  to{opacity:1;transform:none}
}
.msg.user{margin-left:auto}
.msg .who{
  margin-bottom:5px;
  font:600 var(--fs-2xs) var(--mono);
  letter-spacing:.12em;
  text-transform:uppercase;
  color:var(--dim);
}
.msg.user .bubble{
  border:1px solid var(--bubble-user-border);
  background:var(--bubble-user-bg);
  padding:12px 15px;
  border-radius:var(--r-md);
  border-top-right-radius:6px;
  white-space:pre-wrap;
  font-size:var(--fs-base);
  box-shadow:
    var(--bubble-user-shadow);
}
.msg.ai .bubble{
  border-left:2px solid var(--bubble-ai-border);
  padding:2px 4px 2px 16px;
}
.msg.ai.err .bubble{border-left-color:var(--err);opacity:.8}
.msg.ai.typing .bubble::after{
  content:'';display:inline-block;
  width:13px;height:13px;
  border:2px solid color-mix(in srgb,var(--accent) 28%,transparent);
  border-top-color:var(--accent);
  border-radius:50%;
  animation:rspin .7s linear infinite;
  vertical-align:-2px;margin-left:6px;
}
.msg.typing .bubble::after{
  content:'';display:inline-block;
  width:13px;height:13px;
  border:2px solid color-mix(in srgb,var(--accent) 28%,transparent);
  border-top-color:var(--accent);
  border-radius:50%;
  animation:rspin .7s linear infinite;
  vertical-align:-2px;margin-left:6px;
}
@keyframes blink{50%{opacity:0}}
@keyframes rspin{to{transform:rotate(360deg)}}
.msg.err .bubble{border-left-color:var(--err)}
.quiz-card{border:1px solid var(--line2);border-radius:12px;background:var(--panel);padding:16px;max-width:620px}
.quiz-card h3{font:italic 600 21px var(--serif);margin:0 0 12px;line-height:1.25}
.quiz-options{display:grid;gap:8px;margin-top:14px}
.quiz-option{width:100%;text-align:left;border:1px solid var(--line);border-radius:8px;background:var(--surface-2);color:var(--fg);padding:10px 12px;font:14px var(--sans);cursor:pointer}
.quiz-option:hover{border-color:var(--accent)}
.quiz-option.correct{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 14%,var(--surface-2))}
.quiz-option.wrong{border-color:var(--err);background:color-mix(in srgb,var(--err) 14%,var(--surface-2))}
.quiz-option:disabled{opacity:.75;cursor:default}
.quiz-feedback{margin-top:12px;color:var(--soft);font-size:13px;line-height:1.5}
.qz-wrap{display:flex;flex-direction:column;align-items:center;padding-top:34px}
.qz-card{width:100%;max-width:680px;border:1px solid var(--line2);border-radius:14px;background:var(--panel);box-shadow:var(--shadow);padding:22px 24px}
.qz-card h3{margin:0 0 6px;font:italic 600 20px var(--serif);line-height:1.3}
.qz-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font:11px var(--mono);color:var(--soft);margin-bottom:14px;border-bottom:1px solid var(--line);padding-bottom:12px}
.qz-meta b{color:var(--accent)}
.qz-tag{border:1px solid var(--line2);border-radius:99px;padding:2px 9px;background:var(--surface-2)}
.qz-mc{display:grid;gap:9px;margin:14px 0 0}
.qz-fill{font:16px var(--read);line-height:1.75;color:var(--fg)}
.ar-blk{display:flex;flex-direction:column;gap:4px;padding:11px 13px;border:1px solid var(--line2);border-radius:9px;background:var(--surface-2);margin:0 0 9px}
.ar-blk b{font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--dim)}
.ar-blk span{font:15px var(--read);line-height:1.5;color:var(--fg)}
.qz-blank{display:inline-block;border:0;border-bottom:1.5px dashed var(--accent);background:transparent;color:var(--fg);font:inherit;min-width:130px;text-align:center;outline:none;padding:0 4px}
.qz-blank:focus{border-bottom-style:solid}
.qz-in{width:100%;resize:vertical;border:1px solid var(--line);border-radius:9px;background:var(--surface-2);color:var(--fg);padding:11px 13px;font:15px var(--read);min-height:64px}
.qz-in:focus{outline:none;border-color:var(--accent)}
.qz-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}
.qz-ans{background:color-mix(in srgb,var(--ok) 10%,var(--surface-2));border:1px solid var(--ok);border-radius:9px;padding:11px 13px;font:14px var(--read);line-height:1.55;margin-top:14px}
.qz-ans b{color:var(--ok)}
.qz-gpoints{font:600 13px var(--mono);color:var(--accent)}
.qz-missed .qz-ans b{color:var(--err)}
.qz-stream{font:12px var(--mono);color:var(--soft);margin-top:10px}
.fpop{position:fixed;inset:0;z-index:160;background:color-mix(in srgb,var(--ink) 22%,transparent);display:flex;align-items:flex-start;justify-content:center;padding:9vh 16px 20px}
.fpop .pop{position:static;width:min(780px,94vw);max-width:94vw;max-height:80vh;overflow:auto;box-shadow:var(--shadow);overscroll-behavior:contain;opacity:1}
.fpop[hidden]{display:none}
.fpop .saved-row{flex-wrap:wrap;gap:6px 9px}
.fpop .saved-row .t{flex:1 1 160px}
body:has(.fpop:not([hidden])){overflow:hidden}
.fpop.sidepane{justify-content:flex-end;align-items:stretch;padding:0;z-index:200;background:color-mix(in srgb,var(--ink) 34%,transparent)}
.fpop.sidepane .pop{width:min(400px,94vw);max-width:94vw;max-height:none;align-self:stretch;border-radius:0;border-left:1px solid var(--line2);background:var(--surface);box-shadow:var(--shadow);opacity:1}
@media (prefers-reduced-motion: reduce){.fpop .pop,.fpop.sidepane .pop{animation:none}}
.fpop.sidepane .pop-head{padding:12px 14px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--surface);z-index:1}
#qzSavedModal .pop-head{position:sticky;top:0;background:var(--surface);border-bottom:1px solid var(--line);padding:12px 14px;z-index:1}
#qzSavedModal #qzSavedRows{padding:6px 14px 12px}
#qzSavedModal .pop{min-height:168px}
#qzSavedModal.saved-z2{justify-content:center;align-items:flex-start}
.fpop.sidepane .hint{padding:10px 14px;line-height:1.5}
.saved-row, .set-row{display:flex;align-items:center;gap:9px;padding:9px 4px;border-bottom:1px solid var(--saved-row-border)}
.saved-row .t{flex:1;min-width:0;font:600 14px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.saved-row .sc, .set-row .sc{font:11px var(--mono);color:var(--soft);white-space:nowrap}
.saved-row .saved-acts{display:flex;gap:6px;flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;max-width:100%;-webkit-overflow-scrolling:touch}
.saved-row .saved-acts::-webkit-scrollbar{display:none}
.saved-row .saved-acts .btn{flex:0 0 auto}
.skillchip{font:600 11px var(--mono);white-space:nowrap}
.saved-state{font:600 11px var(--mono);color:var(--warn);white-space:nowrap;text-transform:uppercase;letter-spacing:.04em}
.saved-state.done{color:var(--ok)}
.saved-hist{padding:6px 4px 10px 18px;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:5px}
.saved-hist-row{display:flex;gap:12px;align-items:center;font:11px var(--mono);color:var(--soft)}
.saved-hist-row span:first-child{flex:1}
.saved-hist-row span:last-child{white-space:nowrap}
.nt-split{flex:1;display:flex;min-height:0;overflow:hidden}
.nt-pane{width:250px;border-right:1px solid var(--line);min-height:0;display:flex;flex-direction:column;overflow:hidden;background:var(--surface)}
.nt-pane-head{padding:10px 12px;border-bottom:1px solid var(--line)}
.nt-pane-rows{flex:1;overflow:auto;padding:2px 8px}
.nt-main{flex:1;min-width:0;display:flex;flex-direction:column}
#ntEmpty{flex:1;display:flex;flex-direction:column;justify-content:center;align-items:center;padding:24px}
#ntEditor{flex:1;display:flex;flex-direction:column;gap:8px;padding:14px 18px;min-height:0}
.nt-title{border:1px solid var(--line);border-radius:9px;background:var(--surface-2);color:var(--fg);font:700 18px var(--serif);padding:9px 12px;width:100%}
.nt-title:focus,.nt-body:focus{outline:none;border-color:var(--accent)}
.nt-body{flex:1;resize:none;border:1px solid var(--line);border-radius:9px;background:var(--surface-2);color:var(--fg);font:15px/1.7 var(--read);padding:12px 14px;min-height:180px}
.nt-row{cursor:pointer}
.nt-row.on{background:color-mix(in srgb,var(--accent) 10%,var(--surface-2));border-radius:8px}
@media (max-width:760px){.nt-split{flex-direction:column}.nt-pane{width:100%;max-height:34vh;border-right:none;border-bottom:1px solid var(--line)}}
.nb-title{flex:0 1 300px;border:1px solid var(--line);border-radius:8px;background:var(--surface-2);color:var(--fg);font:600 15px var(--serif);padding:6px 10px;min-width:120px}
.nb-title:focus{outline:none;border-color:var(--accent)}
.nb-pdf-host{flex:1;position:relative;min-height:0;display:flex;flex-direction:column}
.nb-pdf-host.dragover{outline:2px dashed var(--accent);outline-offset:-4px;background:var(--surface-2)}
.nb-quiz-host{flex:1;overflow:auto;padding:22px}
.nb-quiz-host .empty{width:100%;max-width:680px;margin:0 auto}
.nb-pdf-grid .empty, .nb-card-grid .empty{grid-column:1/-1;width:100%;max-width:680px;margin:24px auto;text-align:center}
.nb-qz-list{width:100%;max-width:680px;margin:0 auto;display:flex;flex-direction:column;gap:10px}
/* ---- Notebook Sources grid: PDF thumbnails, click to open ---- */
.nb-pdf-grid{display:flex;flex-wrap:nowrap;gap:16px;padding:12px;overflow-x:auto;overflow-y:hidden;flex:1;align-items:stretch}
.nb-pdf-grid .nb-pdf-thumb{flex:0 0 300px;width:300px}
.nb-pdf-thumb{display:flex;flex-direction:column;gap:8px;padding:12px;background:var(--panel);border:1px solid var(--line);border-radius:10px;cursor:pointer;transition:border-color .15s,box-shadow .15s,transform .05s;min-width:0;position:relative;flex:0 0 300px;width:300px}
.nb-pdf-thumb:hover{border-color:var(--accent);box-shadow:var(--shadow)}
.nb-pdf-thumb:active{transform:translateY(1px)}
.nb-pdf-thumb.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent)}
.nb-pdf-thumb .pdf-canvas{width:100%;aspect-ratio:3/4;background:var(--panel2);border-radius:6px;display:block}
.nb-pdf-thumb .pdf-name{font:600 13px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nb-pdf-thumb .pdf-meta{font:11px var(--mono);color:var(--soft)}
.nb-pdf-thumb .pdf-actions{display:flex;gap:6px;margin-top:auto}
.nb-pdf-thumb .pdf-actions .btn{flex:0 0 auto}
.nb-pdf-back{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;border-radius:8px;color:var(--soft);font:600 12px var(--sans);margin-bottom:10px;width:fit-content}
.nb-pdf-back:hover{background:var(--surface-2);color:var(--ink)}
.nb-switch-wrap select{max-width:230px;min-width:150px}
.nb-sel{max-width:230px;min-width:140px}

/* ---- Notebook header ----------------------------------------------------
   The notebook's own header: which notebook is open, what is in it, and the
   two things anyone actually comes here to do. It is a block of its own and
   not a .vbar, so the contextual top bar cannot hoist the picker away from
   the view that needs it. */
.nb-head{flex:0 0 auto;padding:14px 18px 12px;background:var(--surface);border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:8px}
.nb-head-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
.nb-head-label{font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft)}
.nb-head-grow{flex:1 1 auto;min-width:8px}
.nb-head-count{font:600 11px var(--mono);letter-spacing:.04em;color:var(--soft);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-full);padding:3px 10px;white-space:nowrap}
.nb-head-count:empty{display:none}
.nb-head-bar .btn{white-space:nowrap}
.nb-head-bar .btn[disabled]{opacity:.45;cursor:not-allowed}
.nb-head-main{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;min-width:0}
/* The app styles every text input with width:100%, and that rule wins over a
   plain class. The notebook title is sized to its own text, so it is given
   enough weight to say so. */
.nb-head .nb-head-title{flex:0 1 auto;width:auto;max-width:min(560px,72%);min-width:170px;field-sizing:content;font:700 22px var(--serif);background:none;border:1px solid transparent;border-radius:var(--r-sm);padding:3px 8px;margin-left:-8px;color:var(--fg);box-shadow:none}
.nb-head .nb-head-title::placeholder{color:var(--dim);font-style:italic}
.nb-head .nb-head-title:hover:not(:focus){border-color:var(--line);background:none}
.nb-head .nb-head-title:focus{outline:none;border-color:var(--accent);background:var(--surface-2);box-shadow:none}
.nb-head-meta{font:500 12px var(--sans);color:var(--soft);min-width:0}

/* ---- Source cards -------------------------------------------------------
   A picture of the document is the way into the document, so every source
   carries a preview of its first page rather than a generic "PDF" badge. */
.nb-pdf-thumb{padding:0;overflow:hidden;gap:0;background:var(--panel);display:flex;flex-direction:column}
.nb-pdf-preview{position:relative;width:100%;aspect-ratio:3/4;background:var(--panel2);border-bottom:1px solid var(--line);overflow:hidden}
.nb-pdf-preview .pdf-canvas{width:100%;height:100%;aspect-ratio:auto;object-fit:cover;object-position:top center;display:block;opacity:0;transition:opacity .25s}
.nb-pdf-preview .nb-src-img{width:100%;height:100%;object-fit:contain;display:block;background:var(--panel2)}
.nb-pdf-preview .pdf-canvas.ready{opacity:1}
.nb-pdf-skel{position:absolute;inset:0;display:grid;place-items:center;color:var(--dim)}
.nb-pdf-skel svg{width:28px;height:28px;stroke-width:1.5}
.nb-pdf-thumb .pdf-canvas.ready ~ .nb-pdf-skel{opacity:0}
.nb-pdf-badge{position:absolute;top:8px;left:8px;font:700 9px var(--mono);letter-spacing:.1em;color:var(--ink);background:rgba(0,0,0,.62);border:1px solid var(--line);border-radius:5px;padding:2px 6px;pointer-events:none}
.nb-pdf-thumb .pdf-name{padding:9px 10px 0;font:600 13px var(--sans)}
.nb-pdf-thumb .pdf-meta{padding:2px 10px 0;font:11px var(--mono);color:var(--soft)}
.nb-pdf-thumb .pdf-actions{padding:9px 10px 10px;gap:5px}
.nb-pdf-thumb .pdf-actions .btn{padding:4px 9px;font-size:11px}
.nb-pdf-thumb .pdf-actions .btn.icon-only{padding:4px 7px}
.nb-pdf-thumb:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---- Empty states ------------------------------------------------------- */
.nb-empty{display:flex;flex-direction:column;align-items:center;gap:14px;padding:44px 20px !important;text-align:center}
.nb-empty-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:center}
.nb-empty-picks{display:flex;flex-direction:column;gap:7px;width:min(420px,100%)}
.nb-pick{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;background:var(--panel);border:1px solid var(--line);border-radius:var(--r-sm);color:var(--fg);cursor:pointer;text-align:left;transition:border-color .12s,background .12s}
.nb-pick:hover{border-color:var(--accent);background:var(--surface-2)}
.nb-pick .nb-pick-name{flex:1 1 auto;min-width:0;font:600 13px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nb-pick svg{width:15px;height:15px;flex:0 0 auto;color:var(--soft)}

@media (max-width:720px){
  .nb-head{padding:11px 12px 10px}
  .nb-head .nb-head-title{font-size:18px;max-width:100%}
  .nb-head-bar .btn{padding:6px 9px}
}
/* ---- Icon-only notebook tab bar ---- */
.nb-tab-icons{display:flex;gap:2px;align-items:center}
.nb-tab-icons button{display:grid;place-items:center;width:34px;height:30px;padding:0;border-radius:7px;background:none;border:none;color:var(--soft);cursor:pointer;transition:background .12s,color .12s;position:relative}
.nb-tab-icons button:hover{background:var(--surface-2);color:var(--ink)}
.nb-tab-icons button.on{background:var(--accent);color:#000}
.nb-tab-icons button svg{width:15px;height:15px;stroke-width:2}
/* Tooltip on hover */
.nb-tab-icons button::after{content:attr(title);position:absolute;top:calc(100% + 6px);left:50%;transform:translateX(-50%);white-space:nowrap;font:600 10px var(--mono);letter-spacing:.06em;text-transform:uppercase;background:var(--ink);color:var(--paper);padding:3px 8px;border-radius:5px;pointer-events:none;opacity:0;transition:opacity .12s;z-index:100}
.nb-tab-icons button:hover::after{opacity:1}
/* ---- Study plan item list inside plan card ---- */
.nb-plan-list{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:6px}
.nb-plan-list li{display:flex;align-items:baseline;gap:8px;font:13px var(--sans);padding:6px 8px;border-radius:7px;background:var(--surface-2)}
.nb-plan-num{font:600 11px var(--mono);color:var(--accent);min-width:18px;flex:none}
.nb-plan-text{flex:1;min-width:0}
.nb-plan-dur{font:11px var(--mono);color:var(--soft);white-space:nowrap}
.nb-plan-done{color:var(--accent);font-size:11px;flex:none}
/* ---- Generic notebook card grids (quizzes, notes, cards...) ---- */
.nb-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(240px,100%),1fr));gap:12px;padding:12px;overflow:auto;flex:1;align-content:start}
.nb-card{display:flex;flex-direction:column;gap:8px;padding:14px;background:var(--panel);border:1px solid var(--line);border-radius:10px;min-width:0;position:relative}
.nb-card .card-icon{height:44px;display:grid;place-items:center;border-radius:8px;background:var(--surface-2);color:var(--accent);font:italic 600 18px var(--serif)}
.nb-card .card-title{font:600 14px var(--sans);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nb-card .card-meta{font:11px var(--mono);color:var(--soft)}
.nb-card .card-actions{display:flex;gap:6px;margin-top:auto}
.nb-card .card-actions .btn{flex:0 0 auto}
.nb-card.empty{text-align:center;justify-content:center;color:var(--soft);border-style:dashed}
#readerHold{display:none}
#qzPasteWrap{width:min(760px,calc(100% - 40px));margin:12px auto 0}
#qzPasteWrap:not([hidden]){display:block}
.qz-prompt{border:1px solid var(--line);border-radius:14px;background:var(--panel);padding:14px 16px;box-shadow:var(--shadow)}
.qz-prompt-head{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-bottom:10px}
.qz-prompt-title{display:flex;align-items:baseline;gap:10px;font:600 13px var(--sans)}
.qz-prompt-head label.chk{font-size:12px;color:var(--mut);white-space:nowrap}
.qz-prompt textarea{width:100%;min-height:76px;background:var(--bg);border:1px solid var(--line);border-radius:10px;padding:10px 12px;font:13px/1.5 var(--sans);color:var(--fg);resize:vertical;outline:none}
.qz-prompt textarea:focus{border-color:var(--accent)}
.qz-prompt-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}
#qzSavedModal .hint{padding:10px 16px;line-height:1.5}
.md{font-family:var(--read);font-size:15.5px;line-height:1.68;word-wrap:break-word}
.md p{margin:.5em 0}.md h1,.md h2,.md h3{font-family:var(--serif);margin:.9em 0 .4em;line-height:1.2}.md h1{font-size:22px}.md h2{font-size:19px}.md h3{font-size:16px}
.md ul,.md ol{padding-left:1.35em;margin:.5em 0}.md li{margin:.22em 0}
.md code{font:12.5px var(--mono);background:var(--panel2);padding:1px 5px;border-radius:4px}
.md pre{background:var(--ink);color:var(--paper);padding:13px 15px;border-radius:9px;overflow:auto;margin:.6em 0}
.md pre code{background:none;color:inherit;font-size:12.5px;padding:0}
.md blockquote{border-left:2px solid var(--accent);padding-left:13px;color:var(--soft);margin:.6em 0;font-style:italic}
.md table{border-collapse:collapse;margin:.6em 0}.md th,.md td{border:1px solid var(--line2);padding:4px 9px;font-size:13px}
.md hr{border:none;border-top:1px solid var(--line)}
.md a{color:var(--accent)}
.md .dimline{color:var(--dim);font-style:italic}
/* Sandboxed chat visualisations ,  ```viz fenced blocks become live cards */
.sim-card{border:1px solid var(--line);border-radius:12px;background:var(--surface);margin:.8em 0;overflow:hidden;max-width:640px}
.sim-card .sim-top{display:flex;align-items:center;gap:10px;padding:8px 12px;border-bottom:1px solid var(--line)}
.sim-card .sim-title{font:600 13px var(--ui,var(--sans));letter-spacing:.02em;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sim-card .sim-kind{font:600 9px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--dim);border:1px solid var(--line);border-radius:99px;padding:2px 8px;white-space:nowrap}
.sim-card .sim-act{display:flex;gap:4px}
.sim-card .sim-mini{font:600 10px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--soft);background:none;border:1px solid var(--line);border-radius:6px;padding:2px 8px;cursor:pointer}
.sim-card .sim-mini:hover{color:var(--accent);border-color:var(--accent)}
.sim-card .sim-frame{position:relative}
.sim-card .sim-frame iframe{display:block;width:100%;height:320px;border:0;background:#fff;color:#17130c}
.sim-card.sim-fullscreen{position:fixed;inset:0;z-index:9999;max-width:none;border-radius:0;display:flex;flex-direction:column;background:var(--surface);overflow:hidden}
.sim-card.sim-fullscreen .sim-frame{flex:1;min-height:0}
.sim-card.sim-fullscreen .sim-frame iframe{height:100%}
.sim-card.sim-fullscreen .sim-ctrls{flex:none}
body.sim-fullscreen-lock{overflow:hidden}
.sim-card.sim-fullscreen .sim-top{padding:12px 16px}
.sim-card.sim-fullscreen .sim-title{font-size:15px}
.sim-card .sim-ctrls{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:8px 12px;border-top:1px solid var(--line)}
.sim-card .sim-ctrls label{display:flex;align-items:center;gap:6px;font:600 10px var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--dim)}
.sim-card .sim-ctrls input[type=range]{width:110px}
.sim-card .sim-ctrls b{font:600 11px var(--mono);color:var(--fg);min-width:20px;text-align:right}
.sim-card .sim-more{display:flex;gap:4px;margin-left:auto}
.sim-card .sim-err{display:none;padding:10px 14px;font:12px var(--mono);color:var(--dim);line-height:1.5}
.sim-card.bad .sim-frame{display:none}
.sim-card.bad .sim-err{display:block}
.sim-card.collapsed .sim-frame,.sim-card.collapsed .sim-ctrls{display:none}
.sim-card .sim-state{font:600 11px var(--mono);color:var(--accent);min-width:34px;text-align:center;text-transform:uppercase;letter-spacing:.06em}
.sp-widget{position:fixed;z-index:300;left:50%;top:50%;transform:translate(-50%,-50%);background:var(--surface);border:1px solid var(--border-s);box-shadow:0 24px 70px rgba(0,0,0,.45);border-radius:14px;padding:16px 18px;min-width:240px;max-width:80vw;font:500 14px var(--font)}
.sp-widget h4{margin:0 0 6px;font:600 15px var(--serif);color:var(--fg)}
.sp-widget p{margin:0 0 12px;color:var(--fg2);font-size:13px;line-height:1.45}
.sp-widget .wbtn{background:var(--accent);color:#fff;border:none;border-radius:8px;padding:7px 14px;font:600 13px var(--font);cursor:pointer}
.sp-widget .wbtn.ghost{background:transparent;color:var(--fg2);margin-left:8px}
/* Both the class and the property. Several controls here are switched off with
   `el.disabled = true`, which sets the pseudo-class rather than the class, and
   the property is what the whiteboard uses while a save is in flight. Matching
   only the class left those buttons looking perfectly live while refusing to
   do anything. */
.ibtn.disabled,
.ibtn:disabled{opacity:.38;pointer-events:none}

.errline{color:var(--err);font-family:var(--sans);font-size:13px}
.md-tools{display:flex;gap:6px;margin-top:10px;flex-wrap:wrap}
.mini-btn{display:inline-flex;align-items:center;gap:5px;font:600 10px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--soft);background:none;border:1px solid var(--line);border-radius:6px;padding:4px 8px;cursor:pointer}
.mini-btn:hover{color:var(--ink);border-color:var(--line2)}
.tp-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.tp-panes{display:grid;grid-template-columns:1fr 1fr;gap:14px;align-items:start;min-width:0}
.tp-pane{display:flex;flex-direction:column;gap:6px;min-width:0}
.tp-prev{border:1px solid var(--line);border-radius:9px;background:var(--surface-2);padding:16px 18px;overflow:auto;min-height:420px;max-height:620px;scrollbar-gutter:stable}
@media (max-width:880px){.tp-panes{grid-template-columns:1fr}}
.composer{
  border-top:1px solid var(--border);
  padding:12px 28px 16px;
  flex:none;
  /* Slightly milky glass ,  composer is a foreground layer */
  background:color-mix(in srgb,var(--surface) 68%,transparent);
  -webkit-backdrop-filter:saturate(160%) blur(18px);
  backdrop-filter:saturate(160%) blur(18px);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .composer{background:var(--surface)}
}
.sysdet{margin-bottom:10px}
.sysdet summary{cursor:pointer;font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft);list-style:none}
.sysdet summary::before{content:'▸ '}.sysdet[open] summary::before{content:'▾ '}
.sysdet textarea{margin-top:8px}
.ctxrow{display:flex;align-items:center;gap:14px;margin-bottom:10px;flex-wrap:wrap}
.inputrow{
  display:flex;gap:10px;align-items:flex-end;
}
#chatIn{
  flex:1;
  min-height:52px;max-height:200px;
  border-radius:var(--r-lg);
  background:color-mix(in srgb,var(--surface-3) 40%,var(--surface));
  color:var(--fg);
  border:1px solid var(--border-s);
  padding:14px 16px;
  font-size:var(--fs-base);
  line-height:1.5;
  letter-spacing:-.005em;
  box-shadow:
    var(--shadow-inset),
    inset 0 2px 4px rgba(0,0,0,.18);
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
#chatIn:hover:not(:focus){border-color:var(--border-strong)}
#chatIn:focus{
  border-color:color-mix(in srgb,var(--accent) 55%,var(--border));
  box-shadow:var(--ring-soft),var(--shadow-inset);
}
#chatSend{
  width:44px;height:44px;
  border-radius:var(--r-md);
  border:none;
  background:linear-gradient(180deg,var(--accent-h) 0%,var(--accent) 100%);
  color:var(--accent-ink);
  cursor:pointer;
  display:grid;place-items:center;
  flex:none;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.3),
    inset 0 -1px 0 rgba(0,0,0,.14),
    0 8px 20px -6px var(--accent-glow);
  transition:
    transform var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
#chatSend svg{width:18px;height:18px}
#chatSend:hover{
  transform:translateY(-1px);
  background:linear-gradient(180deg,color-mix(in srgb,var(--accent-h) 92%,white 8%) 0%,var(--accent-h) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.36),
    0 12px 28px -8px var(--accent-glow);
}
#chatSend:active{transform:translateY(1px) scale(.96)}
#chatSend:focus-visible{outline:none;box-shadow:var(--ring);border-radius:var(--r-md)}

#chatSend[disabled]{
  opacity:.4;
  pointer-events:none;
  filter:saturate(.5);
}
.nwrap{max-width:860px;margin:0 auto}
.nctl{padding:16px 18px;display:flex;flex-direction:column;gap:14px;margin-bottom:20px}
.nbanner{font-size:13px;line-height:1.55;color:var(--accent-ink);background:linear-gradient(135deg,var(--accent),var(--accent2));border-radius:8px;padding:10px 14px}
.nctl-row{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.nplay{width:56px;height:56px;border-radius:50%;border:none;background:var(--accent);color:var(--accent-ink);cursor:pointer;display:grid;place-items:center;flex:none;box-shadow:var(--shadow)}
.nplay:hover{background:var(--accent2)}
.nplay:disabled{opacity:.4;pointer-events:none}
.nplay svg{width:20px;height:20px}
.nplay .pb{display:none}.nctl-row.playing .nplay .pb{display:block}.nctl-row.playing .nplay .pa{display:none}
.nprog{flex:1;min-width:220px}
.nprog-meta{display:flex;justify-content:space-between;margin-top:4px;font:600 11px var(--mono);color:var(--soft)}
.nctl-row2{display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end}
.nctl-row2 .fld{min-width:180px;flex:1}
.nctl-row2 .fld.slim{flex:0 0 200px}
#nWpmV{color:var(--accent)}
  /* Neural voice panel: deliberately dismissible, because the engine and
     speaker controls are secondary to reading and playback. */
  .n-neural-panel{flex:1 0 100%;display:flex;gap:14px;align-items:flex-end;flex-wrap:wrap;
    margin-top:10px;padding:10px 12px;border:1px solid var(--line2);border-radius:10px;background:var(--surface-2)}
  .n-neural-panel[hidden]{display:none}
  .n-neural-head{display:flex;align-items:center;gap:8px;flex:0 0 auto}
  .n-neural-head strong{font:700 12px var(--sans)}
  .n-neural-note{font:500 11px var(--sans);color:var(--soft)}
  .n-x{margin-left:4px;width:24px;height:24px;line-height:1;border-radius:6px;border:1px solid var(--line2);
    background:transparent;color:var(--soft);font-size:17px;cursor:pointer;flex:none}
  .n-x:hover{border-color:var(--accent);color:var(--ink)}
  .n-neural-panel .fld{flex:0 0 210px;min-width:0}
  #nSpkWrap{flex:1 1 260px}
  #nSpkWrap input[type=range]{width:100%}
  #nSpkVal{color:var(--accent);font-variant-numeric:tabular-nums}
  .n-narrstat{font:500 11px var(--mono);color:var(--soft);flex:1 1 100%}
  /* Model-download progress. The three local voices range from 75 MB to
     1.4 GB, and on a cold cache that is a wait long enough that a button that
     simply stops responding reads as "broken" rather than "busy". A real bar,
     with the byte count and the current stage, makes the wait legible. */
  .n-narrload{display:flex;align-items:center;gap:8px;min-width:0}
  .n-narrload[hidden]{display:none}
  .n-narrload-bar{position:relative;flex:1 1 120px;min-width:90px;max-width:260px;height:6px;
    border-radius:99px;background:var(--panel2);overflow:hidden}
  .n-narrload-bar i{position:absolute;inset:0 auto 0 0;width:0%;border-radius:99px;
    background:linear-gradient(90deg,var(--accent),color-mix(in srgb,var(--accent) 55%,#fff));
    transition:width .18s ease}
  .n-narrload-bar.indet i{width:35%!important;animation:n-narrload-slide 1.1s ease-in-out infinite}
  @keyframes n-narrload-slide{0%{transform:translateX(-100%)}100%{transform:translateX(320%)}}
  .n-narrload-txt{font:500 11px var(--mono);color:var(--soft);white-space:nowrap;
    overflow:hidden;text-overflow:ellipsis}
  .n-narrload.err .n-narrload-bar i{background:var(--danger,#c0392b)}
  .n-narrload.done .n-narrload-txt{color:var(--accent)}
.npaste{display:flex;gap:10px;align-items:flex-end}
.npaste .fld{flex:1}
.narticle{font-family:var(--read);font-size:17px;line-height:1.9;max-width:720px;margin:0 auto;padding:8px 6px 40px}
.narticle p{margin:0 0 1em}
.snt{cursor:pointer;border-radius:3px}
.snt:hover{background:var(--panel2)}
.snt.speaking{background:color-mix(in srgb,var(--accent) 16%,transparent);box-shadow:0 1px 0 color-mix(in srgb,var(--accent) 45%,transparent)}
.snt.hdg{display:block;font-family:var(--serif);font-weight:700;color:var(--ink);margin:.45em 0 .2em;line-height:1.25}
.snt.hdg.h1{font-size:23px;margin-top:.75em}
.snt.hdg.h2{font-size:20px}
.snt.hdg.h3{font-size:17px}
.snt.hdg.h4{font-size:15px}
.snt.hdg.h5,.snt.hdg.h6{font-size:14px}
.snt.hdg.speaking{color:var(--accent)}
/* dictation ,  transcript front and center, diagnostics collapsed */
.dict-wrap{max-width:860px;margin:0 auto;display:flex;flex-direction:column;gap:14px}
.dict-stage{display:flex;align-items:center;gap:18px;padding:18px;flex-wrap:wrap}
.dict-stage h3{font:italic 600 21px var(--serif);margin:0 0 4px}
.micbtn{width:72px;height:72px;border-radius:50%;border:1.5px solid var(--line2);background:var(--paper);cursor:pointer;display:grid;place-items:center;position:relative;flex:none;color:var(--ink)}
.micbtn svg{width:26px;height:26px}
.micbtn:hover{border-color:var(--ink)}
.micbtn.rec{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.micbtn.rec::after{content:'';position:absolute;inset:-8px;border:1.5px solid var(--accent);border-radius:50%;animation:ringp 1.5s ease-out infinite}
/* Loading state: a spinning ring around the mic, so the user knows the
   model is downloading or the audio device is initialising. Clicks are
   ignored while this class is present ,  see the click handler. */
.micbtn.loading{opacity:.9;cursor:wait}
.micbtn.loading::after{content:'';position:absolute;inset:-8px;border:2px solid var(--accent);border-top-color:transparent;border-radius:50%;animation:rspin .8s linear infinite}
.micbtn.loading .loadlbl{
  position:absolute;bottom:-22px;left:50%;transform:translateX(-50%);
  font:600 10px var(--mono);letter-spacing:.06em;text-transform:uppercase;
  color:var(--accent);white-space:nowrap;
}
@keyframes ringp{0%{transform:scale(.92);opacity:.9}100%{transform:scale(1.28);opacity:0}}
.dlive{font:italic 17px var(--read);color:var(--accent);margin-top:6px;min-height:26px;overflow-wrap:anywhere}
#dMeter{display:block;margin-top:8px;width:200px;height:14px;flex:none}
.dta{font-size:17px;line-height:1.6;min-height:230px}
.dact{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.diagrows{display:flex;flex-direction:column;gap:4px;margin-top:8px;font-size:11.5px}
.rd-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(128px,1fr));gap:12px;margin-top:16px}
.rd-item{position:relative;border:1px solid var(--line);border-radius:9px;overflow:hidden;background:var(--panel);cursor:grab;display:flex;flex-direction:column;align-items:center;padding-top:6px}
.rd-item.dragging{opacity:.35}
.rd-item.over{outline:2px solid var(--accent);outline-offset:-2px}
.rd-item canvas{max-width:100%;height:118px;display:block}
.rd-num{position:absolute;left:6px;top:6px;font:600 10px var(--mono);background:var(--paper);color:var(--soft);padding:1px 6px;border-radius:5px;border:1px solid var(--line)}
.rd-acts{position:absolute;right:4px;top:4px;display:flex;gap:4px;opacity:0;transition:.15s}
.rd-item:hover .rd-acts{opacity:1}
.ptwrap{max-width:940px;margin:0 auto}
.ptcfg{padding:18px;display:flex;flex-direction:column;gap:12px}
.ptrow{display:flex;align-items:center;gap:6px;min-width:0}
.ptrow input[type="number"]{width:74px}
.ptrow select{width:auto;flex:none}
.ptrow .dim{color:var(--dim);font:12px var(--mono)}
#ptPreview,#ptTilerPreview{padding:20px;margin-top:18px}
#ptCv{max-width:100%;display:block;margin:0 auto;border:1px solid var(--line);background:#fff}
#ptSum{margin-top:12px;font:11.5px var(--mono);color:var(--soft);text-align:center}
#ptWarn{margin:8px 0 0;font-size:12.5px;color:var(--accent);text-align:center}
#ptLayout{font:600 12px var(--mono);color:var(--ink);padding:9px 0}
#ptOrientNote{margin:0}
#ivStage{flex:1;position:relative;overflow:hidden;background:var(--viewer-bg);touch-action:none}
#ivCv{position:absolute;left:0;top:0;transform-origin:0 0;background:var(--viewer-bg);box-shadow:0 0 0 1px rgba(128,128,128,.15),var(--shadow)}
#ivOv{position:absolute;inset:0;pointer-events:none}
.iv-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--viewer-bg)}
.ivbar{display:flex;align-items:center;gap:16px;padding:8px 16px;border-bottom:1px solid var(--border);flex:none;flex-wrap:wrap}
#ivCropBar{position:absolute;left:50%;bottom:18px;transform:translateX(-50%);display:flex;gap:8px;align-items:center;background:var(--surface);border:1px solid var(--border-s);border-radius:10px;padding:8px 10px;box-shadow:var(--shadow);z-index:5}
.swatch{width:22px;height:22px;border-radius:50%;border:2px solid var(--line2);cursor:pointer;flex:none}
.swatch.on{outline:2px solid var(--accent);outline-offset:2px}
#view-viewer.simple .iv-edit{display:none!important}
#view-viewer.simple .ivbar{display:none}
#view-viewer.hide-ui .tool-head{display:none}
#view-viewer.hide-ui .ivbar{display:none}
#ivRestore{display:none;position:absolute;top:12px;right:12px;z-index:6;opacity:.4;background:var(--surface)}
#ivRestore:hover{opacity:1}
#view-viewer.hide-ui #ivRestore{display:flex}
#wbStage{flex:1;position:relative;overflow:hidden;background:var(--bg)}
/* A hair, not a bead.
   The dot was a hard-stopped 1px circle, which renders as a two-pixel blob on a
   HiDPI screen ,  the grid read as a field of beads rather than as ruled paper.
   Sub-pixel radius keeps it a dot at every density, and the alpha keeps it from
   competing with the ink on top of it. */
/* Grid dots. The radius was .55px of `var(--line2)` ,  about 1.1px of ink at
   22% alpha ,  which is present in the computed style and invisible on screen.
   The toggle reported the grid as on while nothing could be seen, and "no dots
   even with grid on" was the honest reading of that. 0.85px of a slightly
   stronger mix is the smallest size that still reads as a dot at 100% zoom
   without turning the board into graph paper; it stays far quieter than a
   ruled line, which is what a snapping grid has to be while you are drawing on
   top of it. */
#wbGrid{position:absolute;inset:0;background-image:radial-gradient(circle,color-mix(in srgb,var(--fg) 26%,transparent) .85px,transparent 1.15px);background-size:24px 24px;pointer-events:none}
#wbCv{position:absolute;inset:0;touch-action:none;cursor:crosshair}
.wb-label-inline{position:absolute;display:flex;align-items:center;justify-content:center;text-align:center;overflow:hidden;z-index:120;cursor:text;white-space:pre-wrap;word-break:break-word;outline:1.5px dashed var(--accent);outline-offset:3px;border-radius:6px;background:transparent;font-family:var(--sans)}
.wb-label-inline:focus{outline:2px solid var(--accent);outline-offset:2px;background:rgba(0,0,0,.03)}
.wb-tbl-inline{position:absolute;z-index:120;overflow:hidden;background:var(--bg)}
.wb-tbl-inline table{width:100%;height:100%;table-layout:fixed;border-collapse:collapse;font-family:var(--sans)}
.wb-tbl-inline td{position:relative;border:1px solid var(--tblb,#555);padding:3px 5px;text-align:center;vertical-align:middle;overflow:hidden;color:var(--tblc,#333);cursor:text;white-space:pre-wrap;word-break:break-word}
.wb-tbl-inline td.hdr{background:rgba(0,0,0,.06);font-weight:600}
.wb-tbl-inline td:focus{outline:2px solid var(--accent);outline-offset:-1px;background:rgba(0,0,0,.03);z-index:1}
.wb-tbl-inline td.editing{outline:2px solid var(--accent);outline-offset:-1px;background:rgba(0,0,0,.03)}
.wb-tbl-inline td.rg{user-select:none;cursor:pointer;color:var(--soft);border-style:dashed;opacity:.75;font-size:13px}
.wb-tbl-inline td.rg:hover{opacity:1;color:var(--accent);background:rgba(0,0,0,.04)}
.wb-tbl-inline .addcol{width:22px;min-width:22px;max-width:22px}
.wb-tbl-inline .addrow{height:20px;min-height:20px;text-align:center}
#view-whiteboard.hide-ui .tool-head{display:none}
#view-whiteboard.hide-ui #wbStage{padding-top:0}
#wbRestore{display:none;position:absolute;right:14px;top:14px;z-index:8;opacity:.65;background:var(--surface)}
#wbRestore:hover{opacity:1}
#view-whiteboard.hide-ui #wbRestore{display:flex}
.wb-pages-bar{position:absolute;right:14px;bottom:14px;z-index:8;display:flex;gap:6px}
#wbPages{background:var(--surface);opacity:.85;gap:6px}
#wbPages:hover{opacity:1}
#wbPages svg{width:13px;height:13px}
#wbPages b{min-width:14px;text-align:center;font:600 11px var(--mono);background:var(--accent);color:var(--on-accent);border-radius:8px;padding:1px 5px}
.wb-pages-bar .wb-pg{padding:0 8px;background:var(--surface);opacity:.85}
.wb-pages-bar .wb-pg:hover{opacity:1}
.wb-pages-bar .wb-pg svg{width:13px;height:13px}
/* "Open PDF" carries a label as well as an icon, so it needs room for text and
   a gap between the two. The other .wb-pg buttons are icon-only. */
.wb-pages-bar #wbOpenPdf{display:inline-flex;align-items:center;gap:6px;padding:0 10px;font:600 11.5px var(--sans);color:var(--ink)}
.wb-pages-bar #wbOpenPdf svg{width:14px;height:14px;flex:none}
.dg-split{display:grid;grid-template-columns:230px 1fr;gap:0;height:76vh;border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--panel)}
.dg-pane{display:flex;flex-direction:column;border-right:1px solid var(--line);min-height:0;background:var(--surface)}
.dg-pane .nt-pane-head{flex:none}
.dg-main{display:flex;flex-direction:column;min-width:0;min-height:0}
.dg-toolbar{flex:none;display:flex;align-items:center;gap:7px;padding:9px 12px;border-bottom:1px solid var(--line);flex-wrap:wrap}
.dg-toolbar .hint{color:var(--soft)}
.dg-canvas{flex:1;position:relative;overflow:hidden;background:var(--bg);min-height:0}
.dg-canvas svg{position:absolute;inset:0;width:100%;height:100%;display:block;cursor:grab;touch-action:none;user-select:none}
.dg-canvas svg.dg-drag{cursor:grabbing}
.dg-box{stroke:#1d2a22;stroke-width:2.2;rx:10}
.dg-node{stroke:#1d2a22;stroke-width:2.2}
.dg-leg{fill:none;stroke:#1d2a22;stroke-width:2.2}
.dg-lab{font:600 14px var(--sans);fill:#10211a;paint-order:stroke;stroke:rgba(255,255,255,.75);stroke-width:4px;user-select:none;pointer-events:none}
.dg-inf{font:500 11px var(--sans);fill:#55624d;pointer-events:none}
.dg-selbox{stroke:#0f6fc0;stroke-width:2.4}
.dg-selnode{stroke:#0f6fc0;stroke-width:2.4}
.dg-good{stroke:#2fa05c !important;stroke-width:3}
.dg-bad{stroke:#d44c40 !important;stroke-width:3}
.dg-info{flex:none;font:400 12px/1.5 var(--sans);color:var(--ink);padding:9px 14px;border-top:1px solid var(--line);background:var(--surface);max-height:120px;overflow:auto}
.dg-bar{display:flex;align-items:center;gap:8px;max-width:860px;margin:0 auto;padding:0 22px 14px;flex-wrap:wrap}
.dg-topic{flex:1;min-width:200px;padding:9px 12px;border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--ink);font:500 13px var(--sans)}
.dg-topic:focus{outline:none;border-color:var(--line2)}
.dg-row{padding:10px 12px;border-bottom:1px solid var(--line);cursor:pointer}
.dg-row:hover{background:var(--panel)}
.dg-row.cur{background:rgba(87,184,132,.12)}
.dg-row b{display:block;font:600 12.5px var(--sans);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dg-row span{display:block;font:400 10.5px var(--mono);color:var(--soft);margin-top:2px}
.dg-row .dg-del{float:right;border:none;background:none;color:var(--soft);cursor:pointer;font:600 12px var(--sans);padding:2px 6px;border-radius:6px}
.dg-row .dg-del:hover{color:var(--err);background:rgba(212,76,64,.12)}
.dg-toolbar .seg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden}
.dg-toolbar .seg button{padding:5px 12px;border:none;background:var(--surface);color:var(--soft);font:500 12px var(--sans);cursor:pointer;border-right:1px solid var(--line);transition:background .12s,color .12s}
.dg-toolbar .seg button:last-child{border-right:none}
.dg-toolbar .seg button.on{background:var(--seg-on-bg);color:var(--seg-on-fg)}
.dg-toolbar .seg button:hover:not(.on){background:var(--panel)}
.dg-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:auto;z-index:10}
.dg-overlay[hidden]{display:none}
.dg-recall-card{background:var(--panel);border:1px solid var(--line2);border-radius:14px;box-shadow:0 12px 40px -8px rgba(20,15,5,.25);padding:24px 32px;text-align:center;max-width:420px;width:90%}
.dg-recall-card .q{font:600 16px var(--sans);color:var(--ink);margin-bottom:14px;min-height:44px}
.dg-recall-card input{width:100%;padding:10px 14px;border:1px solid var(--line);border-radius:9px;background:var(--bg);color:var(--ink);font:500 14px var(--sans);margin-bottom:10px}
.dg-recall-card input:focus{outline:none;border-color:var(--line2)}
.dg-recall-card .acts{display:flex;gap:8px;justify-content:center;flex-wrap:wrap}
.dg-recall-card .acts button{padding:7px 16px;border-radius:8px;border:1px solid var(--line);background:var(--surface);color:var(--ink);font:500 12.5px var(--sans);cursor:pointer}
.dg-recall-card .acts button.primary{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.dg-recall-card .score{font:600 28px var(--sans);color:var(--accent);margin-bottom:8px}
.dg-recall-card .score-sub{font:400 13px var(--sans);color:var(--soft)}
.dg-node.dg-highlight{stroke:var(--accent)!important;stroke-width:4;filter:drop-shadow(0 0 8px rgba(87,184,132,.4))}
.dg-node.dg-dim{opacity:.3}
.dg-box.dg-highlight{stroke:var(--accent)!important;stroke-width:4;filter:drop-shadow(0 0 8px rgba(87,184,132,.4))}
.dg-box.dg-dim{opacity:.3}
.dg-leg.dg-dim{opacity:.15}
.dg-leg.dg-highlight{stroke:var(--accent)!important;stroke-width:3}
.wb-pop{position:fixed;z-index:500;background:var(--panel);border:1px solid var(--line2);border-radius:14px;box-shadow:0 18px 50px -10px rgba(20,15,5,.45);width:260px;max-height:76vh;overflow:auto;padding:8px 0}
/* Full-width menu rows. Scoped away from .wb-pages-panel, whose buttons are
   square icon controls and were being forced to display:block;width:100%
   with a 10px/16px padding by this rule - which is what left every icon
   sitting low and off-centre in its box. */
.wb-pop:not(.wb-pages-panel) button{display:block;width:100%;text-align:left;padding:10px 16px;background:none;border:none;font:500 12.5px var(--sans);color:var(--ink);cursor:pointer}
.wb-pop:not(.wb-pages-panel) button:hover{background:var(--ink);color:var(--paper)}
.wb-pop:not(.wb-pages-panel) button .sub{display:block;font:400 10.5px var(--sans);color:var(--soft);margin-top:2px}
.wb-pop-head{padding:10px 16px 6px;font:600 11px var(--mono);color:var(--soft);text-transform:uppercase;letter-spacing:.08em;border-bottom:1px solid var(--line)}
.wb-ctx{position:fixed;z-index:600;background:var(--panel);border:1px solid var(--line2);border-radius:12px;box-shadow:0 16px 48px -8px rgba(20,15,5,.5);min-width:200px;max-width:280px;padding:6px 0;user-select:none;animation:ctxFadeIn .12s ease}
/* ==========================================================================
   Whiteboard: toolbar visibility, tool context menu, settings
   ========================================================================== */
/* A control the user has turned off. The `!important` is the whole point.

   The toolbar is fitted by a pass that hides what will not fit and, on the
   next pass, restores every element's previous inline `display` so it can
   measure again. A control hidden with `style.display = 'none'` was therefore
   handed back its visibility by the next refit ,  which is why unticking the
   purple swatch made the focus timer and the keybind gear reappear, and why
   those two looked like they had a mind of their own.

   A class with `!important` cannot be outranked by the inline style the fit
   writes, so turning something off is final and touches nothing else. */
.wb-off{display:none!important}
/* One eraser button, two behaviours.

   The two modes live in a small panel that drops out of the button rather than
   in two buttons on the bar: the row was spending two of its slots ,  two of the
   fewest slots there are ,  to say something a menu says better, and those slots
   are worth more to the palette. */
.wb-tool-wrap{position:relative;display:inline-flex}
.wb-eraser-menu{
  position:absolute;top:calc(100% + 6px);left:0;z-index:60;
  min-width:198px;padding:4px;border-radius:10px;
  background:var(--surface-2,var(--surface));border:1px solid var(--border,var(--border-s));
  box-shadow:0 10px 28px rgba(0,0,0,.28);
  display:flex;flex-direction:column;gap:2px;
}
.wb-eraser-menu[hidden]{display:none!important}
.wb-eraser-menu>button{
  display:grid;grid-template-columns:20px 1fr;grid-template-rows:auto auto;
  align-items:center;gap:0 8px;text-align:left;
  padding:6px 8px;border:0;border-radius:7px;background:none;cursor:pointer;
  color:var(--ink);min-height:0;width:100%;
}
.wb-eraser-menu>button:hover{background:var(--surface-3,rgba(127,127,127,.12))}
.wb-eraser-menu>button.on{background:color-mix(in srgb,var(--accent) 18%,transparent)}
.wb-eraser-menu>button>svg{grid-row:1/3;width:16px;height:16px;align-self:center}
.wb-eraser-menu>button>span{font:600 12px var(--body,sans-serif)}
.wb-eraser-menu>button>small{font:400 10.5px var(--mono);color:var(--soft);opacity:.85}

.wb-tool-ctx{max-height:min(70vh,520px);overflow-y:auto;overscroll-behavior:contain}
.wb-ctx-hint{padding:6px 12px;font-size:11px;color:var(--soft);line-height:1.45}
.wb-ctx-swatches{display:flex;flex-wrap:wrap;gap:4px;padding:2px 12px 8px}
.wb-ctx-swatches button{
  width:20px;height:20px;border-radius:50%;border:1px solid var(--line2);cursor:pointer;padding:0;
}
.wb-ctx-swatches button.on{box-shadow:0 0 0 2px var(--accent)}
.wb-ctx-toggle{display:flex;gap:6px;padding:2px 12px 8px}
.wb-ctx-toggle button{
  flex:1;padding:6px 8px;border-radius:8px;cursor:pointer;font-size:11px;
  border:1px solid var(--line2);background:var(--surface-2);color:var(--fg);
}
.wb-ctx-toggle button.on{background:var(--accent);color:var(--accent-ink);border-color:transparent}
.wb-ctx-row{display:flex;align-items:center;gap:8px;padding:2px 12px 8px}
.wb-ctx-row label{font-size:11px;color:var(--soft);flex:none}
.wb-ctx-row input[type=range]{flex:1;min-width:0}
.wb-ctx-row .val{font:600 10px var(--mono);color:var(--soft);min-width:22px;text-align:right}
.wb-ctx-label{padding:4px 12px 6px;font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft)}
.wb-ctx-sep{height:1px;background:var(--line2);margin:4px 0}

.wb-settings{
  position:fixed;inset:5vh 5vw;z-index:900;max-width:900px;margin:0 auto;
  display:flex;flex-direction:column;
  background:var(--panel);border:1px solid var(--line2);border-radius:16px;
  box-shadow:0 32px 90px -12px rgba(0,0,0,.6);
}
.wb-settings[hidden]{display:none}
.wbs-head{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--line2)}
.wbs-head h2{margin:0;font:700 18px var(--serif);flex:1}
.wbs-body{flex:1;min-height:0;overflow-y:auto;padding:8px 20px 20px}
.wbs-sec{padding:16px 0;border-bottom:1px solid var(--line2)}
.wbs-sec:last-child{border-bottom:none}
.wbs-sec h3{margin:0 0 4px;font:700 14px var(--serif)}
.wbs-sec h4{margin:14px 0 6px;font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft)}
.wbs-note{margin:0 0 10px;font-size:11.5px;color:var(--soft);line-height:1.5}
.wbs-grid{display:flex;flex-wrap:wrap;gap:6px}
.wbs-check{display:flex;align-items:center;gap:6px;font-size:11.5px;
  padding:5px 9px;border:1px solid var(--line2);border-radius:8px;background:var(--surface-2);cursor:pointer}
/* The mark beside a name is borrowed from the control it names, so the
   panel and the toolbar cannot disagree about what a control looks like.
   It is a picture, not a word: it is drawn at the size of the other marks
   in the panel and it never takes a line of its own. */
.wbs-ic{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:15px;height:15px;color:var(--soft)}
.wbs-ic svg{display:block;width:15px;height:15px}
.wbs-tx{min-width:0}
.wbs-chip{display:flex;align-items:center;gap:6px;font-size:11px;
  padding:4px 8px 4px 5px;border:1px solid var(--line2);border-radius:999px;background:var(--surface-2);cursor:pointer}
.wbs-chip i{width:14px;height:14px;border-radius:50%;background:var(--chip);display:block;border:1px solid var(--line2)}
.wbs-seg{display:inline-flex;gap:4px;padding:3px;border-radius:10px;border:1px solid var(--line2);background:var(--surface-2)}
.wbs-segbtn{padding:6px 12px;border-radius:8px;cursor:pointer;font-size:11.5px;border:none;background:transparent;color:var(--fg)}
.wbs-segbtn.on{background:var(--accent);color:var(--accent-ink)}
.wbs-field{display:flex;align-items:center;gap:10px;margin:10px 0 0}
.wbs-field span{font-size:11.5px;color:var(--soft);min-width:110px}
.wbs-field select{flex:1;max-width:220px}
.wbs-keys{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}
.wbs-key{display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:8px;
  border:1px solid var(--line2);background:var(--surface-2);color:var(--fg);cursor:pointer;font-size:11.5px}
/* Read the chip as "what it does", then "which key does it".

   The order was left to the DOM, and it was showing the key first ,  a row of
   chips that opened on a column of bare letters, so nothing announced which tool
   any of them belonged to until you read the word after it. The order is pinned
   here rather than in the markup so a theme or a later `row-reverse` cannot put
   the key back in front. */
.wbs-key span{order:1}
.wbs-key kbd{order:2}
.wbs-key kbd{font:700 10px var(--mono);background:var(--surface);border:1px solid var(--line2);
  border-radius:5px;padding:2px 6px;color:var(--accent)}
.wbs-foot{display:flex;align-items:center;gap:10px;padding:14px 20px;border-top:1px solid var(--line2)}
.wbs-spacer{flex:1}
@media (max-width:760px){
  .wb-settings{inset:2vh 2vw}
}
@keyframes ctxFadeIn{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}
.wb-ctx-item{display:flex;align-items:center;gap:8px;padding:7px 14px;font:500 12.5px var(--sans);color:var(--ink);cursor:pointer;border:none;background:none;width:100%;text-align:left}
.wb-ctx-item:hover{background:var(--ink);color:var(--paper)}
.wb-ctx-item svg{width:15px;height:15px;flex:none;opacity:.6}
.wb-ctx-sep{height:1px;background:var(--line);margin:4px 10px}
.wb-ctx-label{padding:5px 14px 2px;font:600 9.5px var(--mono);color:var(--soft);text-transform:uppercase;letter-spacing:.06em}
.wb-ctx-swatches{display:flex;gap:4px;padding:4px 14px 6px;flex-wrap:wrap}
.wb-ctx-swatches button{width:22px;height:22px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;flex:none}
.wb-ctx-swatches button:hover{transform:scale(1.15)}
.wb-ctx-swatches button.on{border-color:var(--fg);box-shadow:0 0 0 2px var(--paper)}
.wb-ctx-row{display:flex;align-items:center;gap:6px;padding:4px 14px}
.wb-ctx-row label{font:500 11px var(--sans);color:var(--soft);min-width:44px}
.wb-ctx-row input[type=range]{flex:1;height:4px;accent-color:var(--accent)}
.wb-ctx-row input[type=color]{width:28px;height:28px;border:none;border-radius:6px;padding:0;cursor:pointer;background:none}
.wb-ctx-row .val{font:600 10px var(--mono);color:var(--soft);min-width:26px;text-align:right}
.wb-ctx-toggle{display:flex;align-items:center;gap:6px;padding:5px 14px}
.wb-ctx-toggle button{border:1px solid var(--line2);background:var(--surface);color:var(--ink);font:600 11px var(--sans);padding:4px 10px;border-radius:6px;cursor:pointer}
.wb-ctx-toggle button.on{background:var(--accent);color:#fff;border-color:var(--accent)}
.wb-ctx-toggle button:hover:not(.on){background:var(--surface-2)}
.wb-tpl-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;padding:8px}
.wb-tpl-card{border:1px solid var(--line);border-radius:8px;padding:8px;text-align:center;cursor:pointer;background:var(--surface);transition:all .15s}
.wb-tpl-card:hover{border-color:var(--accent);background:var(--surface-2);transform:translateY(-1px)}
.wb-tpl-card svg{width:32px;height:32px;margin:0 auto 4px;display:block}
.wb-tpl-card span{font:500 10px var(--sans);color:var(--soft);display:block}
.wb-tpl-cats{display:flex;gap:4px;padding:6px 8px;border-bottom:1px solid var(--line)}
.wb-tpl-cats button{border:1px solid var(--line2);background:var(--surface);color:var(--ink);font:600 10px var(--sans);padding:4px 10px;border-radius:20px;cursor:pointer}
.wb-tpl-cats button.on{background:var(--accent);color:#fff;border-color:var(--accent)}
.wb-tbl-editor{position:fixed;z-index:250;display:flex;flex-direction:column;background:var(--wb-tbl-editor-bg);border:2px solid var(--wb-tbl-editor-border);border-radius:10px;box-shadow:var(--wb-tbl-editor-shadow);overflow:hidden;min-width:200px}
.wb-tbl-editor table{border-collapse:collapse;width:100%}
.wb-tbl-editor td,.wb-tbl-editor th{border:1px solid var(--line2);padding:4px 8px;font:400 13px var(--sans);min-width:60px;min-height:28px;cursor:text;position:relative}
.wb-tbl-editor th{background:var(--wb-tbl-editor-th-bg);font-weight:600;color:var(--wb-tbl-editor-th-fg)}
.wb-tbl-editor td:focus{outline:2px solid var(--accent);outline-offset:-2px}
.wb-tbl-editor .col-resize{position:absolute;right:-3px;top:0;width:6px;height:100%;cursor:col-resize;z-index:2}
.wb-tbl-editor .row-resize{position:absolute;bottom:-3px;left:0;width:100%;height:6px;cursor:row-resize;z-index:2}
.wb-tbl-editor .row-resize:hover{background:var(--accent);opacity:.3}
.wb-tbl-editor td.rg,.wb-tbl-editor th.rg{position:relative;width:14px;min-width:14px;max-width:14px;padding:0;background:var(--surface-2)}
.wb-tbl-editor th.rg{cursor:default}
.wb-tbl-editor tr[data-row]{height:32px}
.wb-pages-panel{width:min(560px,calc(100vw - 16px));max-height:min(82vh,720px);display:flex;flex-direction:column;padding:0;overflow:hidden}
.wb-pages-head{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--line);flex:none}
.wb-pages-head b{font:600 13px var(--sans);color:var(--ink)}
.wb-pages-count{font:600 11px var(--mono);color:var(--soft)}
.wb-pages-x{width:26px;height:26px;border-radius:8px;border:1px solid var(--line);background:transparent;color:var(--soft);cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}
.wb-pages-x svg{display:block;flex:none}
.wb-pages-x:hover{background:var(--surface-2);color:var(--ink)}
.wb-pages-grid{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:12px;display:grid;grid-template-columns:repeat(auto-fill,minmax(168px,1fr));grid-auto-rows:max-content;gap:12px;align-content:start}
/* `grid-auto-rows:max-content` above is load-bearing, and it is here because
   of this rule.

   `.wb-page-card` has overflow:hidden, so as a grid item its automatic minimum
   size is 0 rather than its content height. The grid is therefore free to
   compress every row to fit the panel's height, and it does: a row measured
   88px against a natural 138px. The card's own overflow:hidden then clipped
   the 29px caption row off the bottom of each tile, with no scrollbar and
   nothing to indicate anything was missing - which is exactly the "bottom text
   is cut in the preview" report.

   Rows are pinned to max-content so a card is always frame + caption tall, and
   the grid scrolls when the pages outnumber the space. `min-height:0` on the
   grid is what lets it scroll at all: without it the grid also refuses to
   shrink and the panel's overflow:hidden clips the overflow instead. */
.wb-page-card{min-height:min-content}

/* The frame fixes the tile's aspect so the grid does not reflow as you move
   between pages with differently shaped content. */
.wb-page-card{position:relative;border:1px solid var(--line);border-radius:10px;overflow:hidden;cursor:pointer;background:var(--surface);transition:border-color .15s,box-shadow .15s}
.wb-page-card:hover{border-color:var(--accent)}
.wb-page-card:focus-within{border-color:var(--accent)}
.wb-page-card.cur{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
.wb-page-frame{position:relative;aspect-ratio:20/13;background:var(--bg);overflow:hidden}
/* contain, not cover: cover crops the drawing to fill the box, which cut the
   last stroke off at the tile edge. The tile already shows the whole page. */
.wb-page-img{display:block;width:100%;height:100%;object-fit:contain}
.wb-page-num{position:absolute;left:6px;top:6px;min-width:18px;height:18px;padding:0 5px;border-radius:9px;background:rgba(0,0,0,.55);color:#fff;font:600 10px/18px var(--mono);text-align:center;backdrop-filter:blur(3px)}
.wb-page-cur{position:absolute;right:6px;top:6px;height:auto;padding:2px 6px;border-radius:5px;background:var(--accent);color:var(--accent-ink);font:600 9px var(--sans);text-transform:uppercase;letter-spacing:.04em}

/* Controls sit above the preview. They are always in the DOM and always
   clickable - they are revealed on hover and on keyboard focus so they do not
   permanently cover the drawing, but they are never display:none, which is
   what made the panel look like it had no buttons at all. */
.wb-page-acts{position:absolute;right:6px;bottom:6px;top:auto;height:24px;display:flex;align-items:center;gap:4px;opacity:0;transition:opacity .15s;z-index:2}
.wb-page-card:hover .wb-page-acts,.wb-page-card:focus-within .wb-page-acts{opacity:1}
/* Icons are inline SVG on the same box as the button, so they are centred by
   construction. The `display:flex` centring is for the box; `line-height:1`
   is belt-and-braces against any inherited line box nudging a fallback glyph. */
.wb-page-ic{width:24px;height:24px;border-radius:7px;border:1px solid var(--line2);background:rgba(8,11,18,.82);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;line-height:1}
.wb-page-ic svg{display:block;flex:none}
.wb-page-ic:hover{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);transform:translateY(-1px)}
.wb-page-ic.danger{color:#ff8a80}
.wb-page-ic.danger:hover{background:var(--err);color:#fff;border-color:var(--err)}

.wb-page-meta{display:flex;align-items:center;gap:6px;padding:7px 9px;border-top:1px solid var(--line)}
.wb-page-meta b{font:600 11px var(--sans);color:var(--ink)}
.wb-page-meta .sub{margin-left:auto;font:500 10px var(--mono);color:var(--soft)}

.wb-page-add{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:13px;border:none;border-top:1px solid var(--line);background:var(--surface);color:var(--accent);font:600 12.5px var(--sans);cursor:pointer;flex:none;line-height:1}
.wb-page-add svg{display:block;flex:none}
.wb-page-add:hover{background:var(--surface-2)}

/* Touch devices have no hover, so the controls stay visible there. */
@media (hover:none){.wb-page-acts{opacity:1}}

/* Older rules that are no longer used by the single panel implementation. */
.wb-page-del{display:none}
.wb-tbl-editor .col-resize:hover{background:var(--accent);opacity:.3}
.wb-tbl-editor .row-ops{display:flex;gap:2px;padding:4px 8px;border-top:1px solid var(--line);background:var(--surface)}
.wb-tbl-editor .row-ops button{border:1px solid var(--line2);background:var(--surface);color:var(--ink);font:600 10px var(--sans);padding:3px 8px;border-radius:5px;cursor:pointer}
.wb-tbl-editor .row-ops button:hover{background:var(--accent);color:#fff;border-color:var(--accent)}
.tbl-toolbar{display:flex;align-items:center;gap:6px;padding:6px 8px;border-bottom:1px solid var(--line);background:var(--surface);flex:none}
.tbl-title{font:600 11px var(--sans);color:var(--soft);flex:1}
.tbl-ops{display:flex;gap:4px}
.tbl-ops button{border:1px solid var(--line2);background:var(--surface);color:var(--ink);font:600 10px var(--sans);padding:3px 8px;border-radius:5px;cursor:pointer;transition:background .12s}
.tbl-ops button:hover{background:var(--accent);color:#fff;border-color:var(--accent)}
.tbl-color{font-size:0;cursor:pointer}
.tbl-color input[type=color]{width:22px;height:22px;border:1px solid var(--line2);border-radius:4px;padding:0;background:var(--surface);cursor:pointer}
.tbl-scroll{overflow:auto;flex:1;min-height:0}
.tbl-cell-menu{position:fixed;z-index:700;background:var(--tbl-cell-menu-bg);border:1px solid var(--tbl-cell-menu-border);border-radius:8px;padding:4px 0;box-shadow:var(--tbl-cell-menu-shadow);min-width:150px}
.tbl-cell-menu button{display:block;width:100%;text-align:left;border:none;background:none;padding:6px 14px;font:400 12px var(--sans);color:var(--ink);cursor:pointer}
.tbl-cell-menu button:hover{background:var(--accent);color:#fff}
.wb-tbl-editor .rg{position:relative;width:26px;min-width:26px;max-width:26px;padding:0;background:var(--surface-2);font:600 10px var(--sans);color:var(--soft);text-align:center;cursor:default;overflow:visible}
.wb-tbl-editor .rg.b{color:var(--accent);font-size:13px}
.wb-tbl-editor .row-handle{cursor:default}
.wb-tbl-editor .col-handle{width:26px;min-width:26px;max-width:26px;text-align:center;color:var(--accent);font:600 13px var(--sans);cursor:pointer}
.wb-tbl-editor td.hdr{background:rgba(0,0,0,.04);font-weight:600}
.wb-tbl-editor td.editing{outline:2px solid var(--accent);outline-offset:-2px;background:rgba(255,255,255,.06);z-index:1}
.wb-tbl-editor .addrow td{padding:0}
.row-plus{height:14px;display:flex;align-items:center;justify-content:center;font:600 11px var(--sans);color:var(--accent);cursor:pointer;transition:background .12s;border-top:1px dashed var(--line2)}
.row-plus:hover{background:var(--row-plus-hover-bg)}
.cal-wrap{flex:1;display:flex;gap:0;height:100%;overflow:hidden}
.cal-grid{flex:1;display:grid;grid-template-columns:repeat(7,1fr);grid-auto-rows:minmax(74px,1fr);gap:1px;background:var(--line2);border:1px solid var(--line2);border-radius:12px;overflow:hidden;padding:1px}
.cal-side{width:280px;border-left:1px solid var(--line);padding:14px 16px;overflow-y:auto;flex-shrink:0;background:color-mix(in srgb,var(--surface) 55%,transparent);display:flex;flex-direction:column;gap:8px}
.cal-dow{display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:6px 0;background:var(--cal-dow-bg)}
.cal-cell{background:var(--cal-cell-bg);min-height:74px;padding:6px;position:relative;display:flex;flex-direction:column;gap:3px;border-radius:2px}
.cal-cell.off{opacity:.38}
.cal-cell.today{border:1px solid var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.cal-cell .cday{font-size:12.5px;font-weight:600;color:var(--ink);display:flex}
.cal-cell.today .cday{color:var(--accent)}
.cal-cell button.cday{padding:2px 7px;border-radius:20px;align-self:flex-start;background:transparent;border:0;color:inherit;cursor:pointer;font:inherit}
.cal-cell.today button.cday{background:var(--accent);color:#fff}
.cal-cell .cev{font-size:11px;line-height:1.25;padding:2px 5px;border-radius:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
.cal-cell .cev.cev-task{background:#eef1f6;border-left:3px solid #8ab4f8}
.cal-cell .cev.cev-red{background:#fdecea;border-left:3px solid #f28b82}
.cal-cell .cev.cev-amber{background:#fef7e0;border-left:3px solid #fdd663}
.cal-cell .cev.cev-green{background:#e6f4ea;border-left:3px solid #81c995}
.cal-cell .cev.cev-done{text-decoration:line-through;opacity:.55}
.cal-cell .cev button{margin-left:auto;background:none;border:0;color:var(--muted);cursor:pointer;font-size:12px;padding:0 2px;line-height:1}
.cal-side h4{font-size:12.5px;font-weight:700;color:var(--ink);display:flex;align-items:center;justify-content:space-between;margin:2px 0 2px}
.cal-side h4 .hint{font-weight:400}
.cal-side .up-item{display:flex;flex-direction:column;gap:2px;padding:6px 8px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}
.cal-side .up-item b{font-size:12.5px;color:var(--ink)}
.cal-side .up-item .hint{font-size:11px}
.cal-side .up-item.urgent{border-color:#f28b82;background:#fdecea}
.cal-side .up-item .row{display:flex;gap:6px;align-items:center}
.cal-side .up-item button{margin-left:auto;background:var(--surface-2);border:1px solid var(--line2);color:var(--ink);border-radius:6px;font-size:11px;padding:3px 8px;cursor:pointer}
.cal-side .empty2{padding:10px 2px;color:var(--muted);font-size:12px}
.cal-add-form{display:flex;flex-direction:column;gap:8px;border:1px dashed var(--line2);border-radius:10px;padding:10px;background:var(--surface)}
.cal-add-form input,.cal-add-form select{border:1px solid var(--line2);background:var(--surface);color:var(--ink);border-radius:6px;padding:6px 8px;font:400 13px var(--sans);outline:none}
.cal-add-form input:focus,.cal-add-form select:focus{border-color:var(--accent)}
.cal-add-form label{font-size:11px;color:var(--muted)}
.cal-add-form .row{display:flex;gap:6px}
.cal-add-form .row>*{flex:1}
.cal-add-actions{display:flex;gap:6px}
.cal-add-actions .btn{flex:1}
.ts-wrap{display:flex;flex-direction:column;gap:0;padding:0 16px;overflow:hidden;height:100%}
.ts-addbar{display:flex;gap:6px;padding:10px 0;align-items:center;border-bottom:1px solid var(--line);flex-shrink:0}
.ts-addbar input,.ts-addbar select{border:1px solid var(--line2);background:var(--surface);color:var(--ink);border-radius:6px;padding:6px 8px;font:400 13px var(--sans);outline:none}
.ts-addbar input:focus{border-color:var(--accent)}
#tsNewTask{flex:1;min-width:120px}
#tsDue{width:160px;flex-shrink:0}
#tsPriority{width:90px;flex-shrink:0}
.ts-list{overflow-y:auto;flex:1;padding:6px 0;display:flex;flex-direction:column;gap:0}
.ts-item{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid transparent;border-radius:6px;transition:background .12s,border-color .12s}
.ts-item:hover{border-color:var(--line2);background:rgba(0,0,0,.03)}
.ts-item input[type=checkbox]{accent-color:var(--accent);width:16px;height:16px;flex-shrink:0;cursor:pointer}
.ts-item .ts-txt{flex:1;cursor:text;word-break:break-word;transition:opacity .2s}
.ts-item.done .ts-txt{text-decoration:line-through;opacity:.55}
.ts-item .ts-due{font:400 11px var(--sans);color:var(--soft);flex-shrink:0;white-space:nowrap}
.ts-item .ts-due.overdue{color:var(--err);font-weight:600}
.ts-item .ts-pri{border:1px solid var(--line2);border-radius:4px;padding:0 6px;font:600 10px var(--sans);color:var(--soft);flex-shrink:0}
.ts-item .ts-pri[data-v="1"]{border-color:#e08a3e;color:#e08a3e}
.ts-item .ts-pri[data-v="2"]{border-color:var(--err);color:var(--err)}
.ts-item .ts-del{background:none;border:none;color:var(--soft);cursor:pointer;padding:2px 4px;font-size:14px;line-height:1;opacity:0;transition:opacity .15s}
.ts-item:hover .ts-del{opacity:1}
.ts-empty{padding:30px 0;color:var(--soft);font:400 13px var(--sans);text-align:center}
.ts-filters{display:flex;gap:6px;padding:8px 0;align-items:center;flex-wrap:wrap;border-bottom:1px solid var(--line);margin-bottom:6px}
.ts-filter{border:1px solid var(--line2);background:transparent;color:var(--ink);font:600 11px var(--sans);padding:5px 10px;border-radius:20px;cursor:pointer}
.ts-filter.on{background:var(--accent);color:#fff;border-color:var(--accent)}
.ts-search{flex:1;min-width:140px;max-width:260px;margin-left:auto;padding:5px 10px;font-size:12px}
.ts-tag{display:inline-flex;font:600 10px var(--mono);color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);border-radius:99px;padding:1px 7px}
.ts-item.dragging{opacity:.4}
.ts-item.over{border-top:2px solid var(--accent)}
.ts-timer{position:fixed;bottom:16px;right:16px;z-index:500;background:var(--ts-timer-bg);border:2px solid var(--accent);border-radius:12px;padding:10px 14px;box-shadow:var(--ts-timer-shadow);width:180px;display:none}
.ts-timer.on{display:block}
.ts-timer-head{display:flex;align-items:center;justify-content:space-between;font:600 12px var(--sans);margin-bottom:6px;color:var(--soft)}
.ts-close-ts{background:none;border:none;color:var(--soft);cursor:pointer;font-size:16px;padding:0 2px}
.ts-timer-display{font:700 36px var(--mono);text-align:center;letter-spacing:2px}
.ts-timer-label{font:400 11px var(--sans);text-align:center;color:var(--soft);margin:4px 0 8px}
.ts-timer-btns{display:flex;flex-wrap:wrap;gap:4px;justify-content:center}
.ts-timer-btns .btn{font-size:11px;padding:3px 6px}
.ts-timer-btns .on{background:var(--accent);color:#fff;border-color:var(--accent)}
.ts-sticky-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;padding:12px 0}
.ts-sticky{background:#f5f06f;border-radius:3px;min-height:110px;position:relative;box-shadow:2px 3px 8px rgba(0,0,0,.15);font:400 13px var(--sans);overflow:hidden}
.ts-sticky-title{border:none;border-bottom:1px solid rgba(0,0,0,.08);padding:8px 10px 4px;font:600 13px var(--sans);background:transparent;width:100%;color:#333;outline:none}
.ts-sticky-body{border:none;border:none;padding:4px 10px 20px;background:transparent;width:100%;min-height:70px;resize:vertical;font:400 13px var(--sans);color:#333;outline:none;overflow:hidden}
.ts-sticky-del{position:absolute;top:3px;right:4px;background:none;border:none;color:rgba(0,0,0,.3);cursor:pointer;font-size:16px;line-height:1}
.ts-sticky-del:hover{color:rgba(0,0,0,.8)}
.ts-sticky select.ts-sticky-color{position:absolute;bottom:3px;left:4px;font-size:10px;border:none;background:transparent;color:rgba(0,0,0,.3);cursor:pointer}

/* --- tasks polish --- */
.ts-item:focus-within{border-color:color-mix(in srgb,var(--accent) 45%,transparent);background:color-mix(in srgb,var(--accent) 4%,transparent)}
.ts-item .ts-due.overdue{text-decoration:underline;text-underline-offset:2px}
.ts-item.done .ts-due{opacity:.45}
.ts-item.done .ts-pri{opacity:.45;border-color:var(--line2);color:var(--soft)}
.ts-filter{transition:background .12s,color .12s,box-shadow .12s}
.ts-filter:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.ts-addbar input:focus-visible,.ts-addbar select:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.ts-sticky{transition:transform .15s,box-shadow .15s}
.ts-sticky:hover{transform:translateY(-2px);box-shadow:2px 5px 12px rgba(0,0,0,.2)}
.ts-empty{opacity:.7}
.ts-timer{backdrop-filter:blur(14px)}
.ts-timer .btn{transition:background .12s,color .12s}
.ts-timer-btns .on{box-shadow:0 0 0 1px var(--accent)}
dialog{
  border:1px solid var(--border-s);
  border-radius:var(--r-lg);
  padding:0;
  background:
    linear-gradient(180deg,
      color-mix(in srgb,var(--surface-2) 40%,transparent),
      color-mix(in srgb,var(--surface) 90%,transparent));
  color:var(--ink);
  width:min(680px,94vw);
  max-height:86vh;
  display:flex;flex-direction:column;
  box-shadow:
    var(--shadow-inset),
    var(--shadow-xl);
  margin:auto;
  box-sizing:border-box;
  -webkit-backdrop-filter:blur(20px) saturate(180%);
  backdrop-filter:blur(20px) saturate(180%);
  animation:dialogIn var(--dur-base) var(--ease-out);
}
@keyframes dialogIn{
  from{opacity:0;transform:translateY(12px) scale(.985)}
  to{opacity:1;transform:none}
}
dialog::backdrop{
  background:color-mix(in srgb,var(--bg) 78%,transparent);
  -webkit-backdrop-filter:blur(6px) saturate(.9);
  backdrop-filter:blur(6px) saturate(.9);
  animation:backdropIn var(--dur-base) var(--ease-out);
}@keyframes backdropIn{from{opacity:0}to{opacity:1}}
dialog:not([open]){display:none}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  dialog{background:var(--surface)}
}
.dlg-head{
  flex:none;
  display:flex;align-items:center;justify-content:space-between;
  padding:18px 24px 14px;
  border-bottom:1px solid var(--border);
}
.dlg-title{
  font:italic 600 clamp(20px,2.4vw,24px) var(--serif);
  flex:1;
  letter-spacing:-.005em;
}
.dlg-hint{font-size:12px;line-height:1.45;color:var(--soft);background:var(--surface-2);padding:10px 14px;border-radius:9px;margin:12px 20px 4px;border:1px solid var(--line);flex:none}
.prov-guide{flex:none;margin:6px 20px 4px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);overflow:hidden}
.prov-guide summary{cursor:pointer;padding:9px 14px;font:600 12px var(--sans);color:var(--fg);list-style:none;display:flex;align-items:center;gap:8px;user-select:none}
.prov-guide summary::before{content:'▶';font-size:9px;color:var(--muted);transition:transform .2s}
.prov-guide[open] summary::before{transform:rotate(90deg)}
.prov-guide-badge{font:600 10px var(--mono);background:color-mix(in srgb,var(--accent) 14%,var(--surface-3));color:var(--accent);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);border-radius:20px;padding:1px 7px;letter-spacing:.04em;text-transform:uppercase}
.prov-guide-body{padding:10px 14px 14px;display:flex;flex-direction:column;gap:12px}
.prov-guide-section{border:1px solid var(--line);border-radius:8px;padding:10px 12px;background:var(--surface)}
.prov-guide-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.prov-guide-logo{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:6px;font:700 12px sans-serif;color:#fff;flex:none}
.prov-guide-tag{font:600 10px var(--mono);border-radius:20px;padding:1px 7px;text-transform:uppercase;letter-spacing:.04em}
.prov-guide-tag.free{background:color-mix(in srgb,var(--ok) 14%,var(--surface-3));color:var(--ok);border:1px solid color-mix(in srgb,var(--ok) 28%,transparent)}
.prov-guide-tag.paid{background:color-mix(in srgb,var(--warn) 14%,var(--surface-3));color:var(--warn);border:1px solid color-mix(in srgb,var(--warn) 28%,transparent)}
.prov-guide-section ol{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:4px}
.prov-guide-section li{font-size:12px;line-height:1.5;color:var(--fg)}
.prov-guide-section a{color:var(--accent);text-decoration:underline}
.prov-guide-section code{font:11px var(--mono);background:var(--surface-2);padding:1px 5px;border-radius:4px}
.prov-guide-note{font:12px var(--sans);color:var(--soft);margin:6px 0 0;line-height:1.45}
.dlg-body{flex:1 1 auto;min-height:140px;max-height:55vh;overflow-y:auto;padding:12px 20px;display:flex;flex-direction:column;gap:12px}
.dlg-foot{flex:none;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:16px 20px;border-top:1px solid var(--line);background:var(--panel)}
.dlg-foot .btn{padding:11px 16px;font-size:13px}
.dlg-foot .primary{min-width:170px;justify-content:center}
/* ---- Settings: one panel, arranged as a grid ----
   The three categories the app actually has are three cards, each carrying its
   own current value. A tab strip hid the value and made you go look for it;
   a card that says "midnight" or "gemini-2.5-flash" answers the question the
   moment the panel opens. The chosen card's controls sit directly below, so
   there is no second screen and nothing to navigate back from. */
.pref-grid{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:10px;flex:none;
}
.pref-card{
  display:flex;align-items:center;gap:11px;
  padding:11px 13px;border-radius:12px;
  border:1px solid var(--border);background:var(--surface);
  color:var(--fg);cursor:pointer;text-align:left;min-width:0;
  transition:border-color var(--dur-fast),background var(--dur-fast);
}
.pref-card:hover{border-color:color-mix(in srgb,var(--accent) 40%,var(--border))}
.pref-card.on{
  border-color:color-mix(in srgb,var(--accent) 62%,transparent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));
}
.pref-card-ic{
  display:flex;align-items:center;justify-content:center;flex:none;
  width:32px;height:32px;border-radius:9px;
  background:var(--surface-2);color:var(--soft);
  border:1px solid var(--line);
}
.pref-card-ic svg{width:16px;height:16px}
.pref-card.on .pref-card-ic{
  color:var(--accent);
  border-color:color-mix(in srgb,var(--accent) 40%,transparent);
  background:color-mix(in srgb,var(--accent) 12%,var(--surface-2));
}
.pref-card-txt{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.pref-card-txt b{font:600 var(--fs-sm)/1.25 var(--sans);letter-spacing:-.005em}
.pref-card-sum{
  font:11px/1.35 var(--sans);color:var(--soft);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* The chevron says which card is open, and points the way the content moves. */
.pref-card-go{width:14px;height:14px;flex:none;color:var(--dim);transition:transform var(--dur-fast)}
.pref-card.on .pref-card-go{transform:rotate(180deg);color:var(--accent)}

/* The aesthetics as cards: you can see what you have and what the
   alternatives look like before clicking, instead of cycling blind. */
.skin-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:9px}
.skin-card{
  display:flex;flex-direction:column;gap:7px;align-items:stretch;
  padding:9px;border-radius:11px;cursor:pointer;
  border:1px solid var(--border);background:var(--surface);color:var(--fg);
  transition:border-color var(--dur-fast),background var(--dur-fast);
}
.skin-card:hover{border-color:color-mix(in srgb,var(--accent) 40%,var(--border))}
.skin-card.on{
  border-color:color-mix(in srgb,var(--accent) 62%,transparent);
  background:color-mix(in srgb,var(--accent) 8%,var(--surface));
}
.skin-sw{
  display:flex;height:32px;border-radius:7px;overflow:hidden;
  border:1px solid var(--line);
}
.skin-sw i{flex:1}.skin-name{font:500 11.5px/1.2 var(--sans);color:var(--fg);text-align:center}

.pref-pane{display:flex;flex-direction:column;gap:16px}
.pref-pane[hidden]{display:none}
.pref-body{
  flex:1 1 auto;min-height:220px;max-height:min(66vh,620px);overflow-y:auto;
  padding:16px 20px 18px;display:flex;flex-direction:column;gap:16px;
}
.pref-sec{display:flex;flex-direction:column;gap:8px}
.pref-sec h4{
  margin:0;font:600 10px var(--mono);letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted);padding-bottom:6px;border-bottom:1px solid var(--line);
}
.pref-row{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.pref-row>label{font:500 var(--fs-sm)/1.2 var(--sans);color:var(--fg)}
.pref-ctl{display:flex;align-items:center;gap:10px;flex:none}
/* The bar-height control is a slider now, so it carries its own row: the track
   needs the room, and the reading beside it has to stay put while the track
   grows under the cursor. */
.pref-ctl-row{display:flex;align-items:center;gap:10px;flex:1;min-width:0}
.pref-ctl-row input[type="range"]{flex:1;min-width:120px;accent-color:var(--accent)}
.pref-out{font:600 var(--fs-sm) var(--mono);color:var(--accent);min-width:48px;text-align:right;flex:none}
.pref-ctl input[type="range"]{width:190px}
.pref-ctl output{font:600 var(--fs-sm) var(--mono);color:var(--accent);min-width:46px;text-align:right}
.pref-note{margin:0;font:12px/1.5 var(--sans);color:var(--soft);max-width:62ch}
/* The borrowed model select keeps the app's own control styling but is allowed
   the width the row has, rather than the 250px cap it carried in the top bar. */
#prefAiModelSlot .ai-model-select{
  width:auto;max-width:min(320px,52vw);min-width:180px;
  font-size:var(--fs-sm);padding:7px 9px;
}
@media (max-width:560px){
  .pref-grid{grid-template-columns:1fr}
  .pref-card-go{display:none}
  #prefAiModelSlot .ai-model-select{max-width:none;width:100%;min-width:0}
  .pref-body{padding:14px 14px 16px}
}
/* Reduce motion: nothing moves position or changes content, it simply stops
   animating. Set from the display settings. */
[data-motion="off"] *,[data-motion="off"] *::before,[data-motion="off"] *::after{
  animation-duration:.001ms!important;animation-iteration-count:1!important;
  transition-duration:.001ms!important;scroll-behavior:auto!important;
}
.dlg-foot .hint{flex:1;min-width:0}
.prov{border:1px solid var(--line);border-radius:12px;padding:14px;display:flex;flex-direction:column;gap:11px;background:var(--surface);box-shadow:0 2px 8px rgba(0,0,0,.04)}
.prov-top{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.radio{position:relative;display:inline-flex;cursor:pointer;flex:none}
.radio input{position:absolute;opacity:0;width:0}
.radio .mark{width:18px;height:18px;border:1.5px solid var(--line2);border-radius:50%;display:inline-grid;place-items:center}
.radio input:checked+.mark::after{content:'';width:8px;height:8px;border-radius:50%;background:var(--accent)}
/* A label, not an input.

   The provider name was an editable text field styled to look exactly like a
   button ,  which is how a stale one came to sit there reading "OpenAI" above an
   Ollama base URL, looking like a control to press. Nothing kept it in step
   with the kind, so it was editable and wrong at the same time. It is derived
   from the kind now and cannot drift. */
.prov-name{
  max-width:200px;font-weight:600;color:var(--fg);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.prov-kind{font:600 9px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--dim);border:1px solid var(--line);padding:2px 6px;border-radius:5px}
.prov-status{margin-left:auto;font:11px var(--mono);color:var(--soft);display:flex;gap:7px;align-items:center}
.prov-status .dot{width:8px;height:8px}
.prov-status.ok .dot{background:var(--ok)}.prov-status.err .dot{background:var(--err)}.prov-status.testing .dot{background:var(--warn)}
.prov-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}
.prov-actions{display:flex;align-items:center;gap:10px;margin-top:2px}
.keyrow{display:flex;gap:8px}
.keyrow .fld{flex:1}
.keyeye{flex:none}
.pmrow{display:flex;gap:8px;align-items:center}
.pmrow .pm{flex:1;min-width:0}

.flipwrap{width:100%;max-width:620px;perspective:1400px}
.flipcard{position:relative;width:100%;cursor:pointer}
.flip-inner{position:relative;width:100%;min-height:340px;transition:transform .55s cubic-bezier(.2,.8,.2,1);transform-style:preserve-3d}
.flipcard.flipped .flip-inner{transform:rotateY(180deg)}
.flip-face{position:absolute;inset:0;backface-visibility:hidden;-webkit-backface-visibility:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:28px 30px;background:var(--panel);border:1px solid var(--line);border-radius:16px;box-shadow:var(--shadow);overflow:auto}
.flip-face.back{transform:rotateY(180deg);justify-content:flex-start;align-items:stretch}
.flip-q{font-family:var(--read);font-size:20px;line-height:1.55;text-align:center;overflow-wrap:anywhere}
.flip-tag{font:600 10px var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--soft);margin-bottom:14px;flex:none;text-align:center;overflow:hidden;max-height:2.8em}
.flip-a{font-family:var(--read);font-size:17px;line-height:1.65;overflow-wrap:anywhere}

@media (max-width:720px){
  .ai-model-select{max-width:150px;font-size:11px;padding:6px 7px}
  .dlg-foot{align-items:stretch;flex-wrap:wrap}
  .dlg-foot .primary{min-width:0;flex:1}
  .dlg-foot .hint{flex-basis:100%;text-align:center}
  .rside{position:absolute;right:0;top:0;bottom:0;z-index:45;width:var(--rsideW,330px);max-width:330px;box-shadow:var(--shadow)}
  #rSideGrip{display:none}
  .rbody.side-closed .rside{margin-right:0;transform:translateX(108%)}
  .rail{width:60px}
  .rail button{padding:10px 0 9px}
  .rail button span{font-size:7px;letter-spacing:.03em}
  .tool-head{padding:8px 10px;gap:8px}
  .tool-head>div:not(.top-actions){min-width:0;flex:1}
  .tool-title{font-size:19px}
  .tool-sub{display:none}
  .top-actions{width:100%;overflow-x:auto;flex-wrap:nowrap;margin-left:0;padding-bottom:4px;scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .top-actions::-webkit-scrollbar{display:none}
  .top-actions .ibtn{width:32px;height:32px}
  .top-actions .ibtn svg{width:15px;height:15px}
  .top-actions .btn{padding:6px 10px;font-size:12px}
  .tool-head.compact .ibtn{width:40px;height:40px}
  .ivbar{overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none}
  .ivbar::-webkit-scrollbar{display:none}
  #view-study .topbar{gap:8px;padding:0 10px}
  #view-study .brand{font-size:19px}
  #view-study .brand .scrawl{display:none}
  .pomo{gap:6px}
  .pomo-meta b{font-size:12px}
  .vbar{padding:8px 10px;gap:8px}
  .rname{max-width:150px}
  .pg.dx{padding:26px 20px}
  #pages{padding:16px 10px 60px}
  .dict-stage{flex-direction:column;text-align:center;gap:14px}
  .home-search{margin:20px auto 0}
  .flip-inner{min-height:380px}
  .chat-wrap .msgs{padding:16px 12px}
  .composer{padding:10px 12px 12px}
  .tool-body{padding:16px 12px}
  .tool-card{backdrop-filter:none;background:var(--surface)}
}
/* ============ Interactive Diagrams ,  redesign ============ */
.dg-shell{display:flex;flex-direction:column;flex:1;min-height:0;background:var(--bg);overflow:hidden}
.dg-hero{padding:16px 22px 14px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,color-mix(in srgb,var(--accent) 6%,transparent),transparent);flex:none}
.dg-hero-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap;max-width:1180px;margin:0 auto}
.dg-hero-secondary{margin-top:8px;gap:8px}
.dg-hero-icon{width:34px;height:34px;border-radius:9px;background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent);display:flex;align-items:center;justify-content:center;flex:none}
.dg-topic{flex:1;min-width:220px;padding:11px 16px;border:1px solid var(--line2);border-radius:11px;background:var(--surface);color:var(--ink);font:500 14.5px var(--sans);box-shadow:inset 0 1px 0 rgba(255,255,255,.02);transition:border-color .15s,box-shadow .15s}
.dg-topic:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 16%,transparent)}
.dg-pill{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line2);border-radius:99px;padding:7px 14px;background:transparent;font:600 12px var(--sans);color:var(--ink);cursor:pointer;transition:background .15s,border-color .15s,transform .1s;white-space:nowrap}
.dg-pill:hover{background:var(--surface-2);border-color:var(--accent)}
.dg-pill:active{transform:scale(.97)}
.dg-pill.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);padding:9px 20px;font-size:13px}
.dg-pill.primary:hover{background:var(--accent-h);border-color:var(--accent-h)}
.dg-pill.primary:disabled{opacity:.55;cursor:default}
.dg-export{font-size:11.5px;padding:6px 12px}
.dg-stream{font:600 11.5px var(--mono);color:var(--accent);letter-spacing:.02em}
.dg-split{flex:1;display:grid;grid-template-columns:252px 1fr;min-height:0;overflow:hidden}
.dg-pane{display:flex;flex-direction:column;border-right:1px solid var(--line);background:var(--surface);min-height:0;overflow:hidden}
.dg-pane-head{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--line);flex:none}
.dg-pane-title{font:600 10.5px var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--soft)}
.dg-pane-count{font:600 10px var(--mono);background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent);border-radius:99px;padding:2px 8px;min-width:20px;text-align:center}
.dg-rows{flex:1;overflow:auto;padding:6px 8px}
.dg-empty-hint{padding:20px 16px;font:12.5px var(--sans);color:var(--soft);line-height:1.6;text-align:center}
.dg-row{padding:10px 12px;border-radius:9px;cursor:pointer;transition:background .12s;border:1px solid transparent}
.dg-row:hover{background:var(--panel2)}
.dg-row.cur{background:color-mix(in srgb,var(--accent) 10%,transparent);border-color:color-mix(in srgb,var(--accent) 32%,transparent)}
.dg-row b{display:block;font:600 13px var(--sans);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:3px}
.dg-row span{display:block;font:11px var(--mono);color:var(--soft)}
.dg-row .dg-del{float:right;border:none;background:none;color:var(--soft);cursor:pointer;font:600 14px var(--sans);padding:2px 6px;border-radius:6px;line-height:1;margin-top:-2px}
.dg-row .dg-del:hover{color:var(--err);background:rgba(212,76,64,.12)}
.dg-main{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--bg);position:relative}
.dg-toolbar{flex:none;display:flex;align-items:center;gap:9px;padding:10px 16px;border-bottom:1px solid var(--line);background:var(--surface);flex-wrap:wrap}
.dg-seg{display:inline-flex;border:1px solid var(--line2);border-radius:9px;overflow:hidden;background:var(--panel)}
.dg-seg button{padding:6px 14px;border:none;background:transparent;color:var(--soft);font:600 11.5px var(--sans);cursor:pointer;transition:background .12s,color .12s}
.dg-seg button.on{background:var(--accent);color:var(--accent-ink)}
.dg-seg button:not(.on):hover{color:var(--ink);background:var(--panel2)}
.dg-zoomgrp{display:inline-flex;align-items:center;gap:2px;border:1px solid var(--line2);border-radius:9px;overflow:hidden;background:var(--panel)}
.dg-ibtn{width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--soft);cursor:pointer;transition:color .12s,background .12s}
.dg-ibtn:hover{color:var(--ink);background:var(--panel2)}
.dg-zoomv{min-width:46px;text-align:center;font:600 11.5px var(--mono);color:var(--ink);padding:0 4px}
.dg-canvas{flex:1;position:relative;overflow:hidden;background:radial-gradient(ellipse at 50% 40%,color-mix(in srgb,var(--accent) 4%,transparent),transparent 65%),var(--bg);min-height:0}
.dg-canvas::before{content:"";position:absolute;inset:0;pointer-events:none;background-image:radial-gradient(var(--line2) 1px,transparent 1px);background-size:22px 22px;opacity:.35}
.dg-canvas svg{position:absolute;inset:0;width:100%;height:100%;display:block;cursor:grab;touch-action:none;user-select:none}
.dg-canvas svg.dg-drag{cursor:grabbing}
.dg-box{stroke-width:1.5;stroke-linejoin:round;transition:filter .15s}
.dg-node{stroke-width:1.5;transition:filter .15s}
.dg-leg{fill:none;stroke:#a49c8c;stroke-width:1.8;stroke-linecap:round;stroke-dasharray:none;transition:opacity .2s}
.dg-lab{font:600 14px var(--sans);fill:#10211a;paint-order:stroke;stroke:rgba(255,255,255,.75);stroke-width:4px;user-select:none;pointer-events:none}
.dg-inf{font:500 11px var(--sans);fill:#55624d;pointer-events:none}
.dg-selbox{stroke:var(--accent)!important;stroke-width:2.6!important;stroke-dasharray:6 4}
.dg-selnode{stroke:var(--accent)!important;stroke-width:2.6!important;stroke-dasharray:6 4}
.dg-good{stroke:#2fa05c!important;stroke-width:3!important}
.dg-bad{stroke:#d44c40!important;stroke-width:3!important}
.dg-node.dg-highlight,.dg-box.dg-highlight{stroke:var(--accent)!important;stroke-width:3.5!important;filter:drop-shadow(0 0 10px color-mix(in srgb,var(--accent) 65%,transparent))}
.dg-node.dg-dim,.dg-box.dg-dim{opacity:.22}
.dg-leg.dg-dim{opacity:.12}
.dg-leg.dg-highlight{stroke:var(--accent)!important;stroke-width:3!important}
.dg-info{flex:none;font:400 13px/1.55 var(--sans);color:var(--ink);padding:11px 18px;border-top:1px solid var(--line);background:var(--surface);max-height:120px;overflow:auto}
.dg-info b{color:var(--accent);font-weight:600}
@media(max-width:820px){
  .dg-split{grid-template-columns:1fr;grid-template-rows:minmax(0,148px) 1fr}
  .dg-pane{border-right:none;border-bottom:1px solid var(--line)}
  .dg-hero-row{flex-wrap:wrap}
  .dg-topic{min-width:0}
  .dg-export{display:none}
}
.dg-scene{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:18px;overflow:hidden}
.dg-scene svg{width:100%;height:100%;max-width:100%;max-height:100%;display:block}
.dg-scene [data-part]{transition:filter .2s}
.dg-canvas .dg-loader{position:absolute;inset:0;z-index:6;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;background:color-mix(in srgb,var(--bg) 88%,transparent)}
.dg-canvas .dg-loader[hidden]{display:none}
.dg-loader-spin{width:40px;height:40px;border:3px solid color-mix(in srgb,var(--accent) 26%,transparent);border-top-color:var(--accent);border-radius:50%;animation:dgSpin .8s linear infinite}
.dg-loader-msg{font:600 13px var(--sans);color:var(--soft);letter-spacing:.02em;text-align:center;padding:0 16px}
@keyframes dgSpin{to{transform:rotate(360deg)}}
.dg-scene [data-part]:hover{filter:drop-shadow(0 0 6px color-mix(in srgb,var(--accent) 60%,transparent))}
/* ============ Whiteboard pages panel ============ */

.wb-page-ic{width:24px;height:24px;border-radius:7px;border:1px solid var(--line2);background:rgba(8,11,18,.82);color:#fff;font:600 13px/1 var(--sans);cursor:pointer;display:grid;place-items:center;padding:0;backdrop-filter:blur(8px);transition:background .12s,color .12s,border-color .12s,transform .1s}
.wb-page-ic:hover{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);transform:translateY(-1px)}
.wb-page-ic.danger{color:#ff8a80}
.wb-page-ic.danger:hover{background:var(--err);color:#fff;border-color:var(--err)}

.wb-page-add{width:100%;padding:13px;border:none;background:transparent;color:var(--accent);font:600 12.5px var(--sans);cursor:pointer;border-top:1px solid var(--line);transition:background .15s;letter-spacing:.02em}
.wb-page-add:hover{background:color-mix(in srgb,var(--accent) 9%,transparent)}
.wb-page-card{transition:border-color .15s,transform .12s}
.wb-page-card:hover{border-color:var(--accent);transform:translateY(-1px)}
/* ============ Diagram Save to Library ============ */
.dg-save{background:color-mix(in srgb,var(--accent) 12%,transparent);border-color:color-mix(in srgb,var(--accent) 42%,transparent);color:var(--accent);font-size:11.5px;padding:6px 13px;font-weight:600}
.dg-save:hover{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
/* ============ Reader quick-chat popup ============ */
#rqc{position:fixed;right:18px;bottom:calc(18px + env(safe-area-inset-bottom,0px));width:min(360px,calc(100vw - 32px));z-index:95;box-shadow:var(--shadow)}
#rqc[hidden]{display:none!important}
#rqc .rqc-card{background:var(--surface);border:1px solid var(--line2);border-radius:14px;overflow:hidden;display:flex;flex-direction:column;max-height:min(70vh,520px)}
#rqc .rqc-head{display:flex;align-items:center;gap:10px;padding:11px 12px;border-bottom:1px solid var(--line);font:600 13px var(--sans);color:var(--ink);flex:none}
#rqc .rqc-sub{margin-left:auto;font:600 9.5px var(--mono);color:var(--soft);text-transform:uppercase;letter-spacing:.08em}
#rqc .rqc-head .ibtn{width:28px;height:28px}
#rqc .rqc-msgs{flex:1;overflow:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:90px}
#rqc .rqc-msg{max-width:92%;padding:8px 11px;border-radius:12px;font:14px/1.5 var(--read);white-space:pre-wrap;word-wrap:break-word}
#rqc .rqc-msg.user{align-self:flex-end;background:var(--accent);color:var(--accent-ink)}
#rqc .rqc-msg.ai{align-self:flex-start;background:var(--panel2);color:var(--ink)}
#rqc .rqc-msg.ai .md p{margin:0 0 .4em}
#rqc .rqc-msg.ai .md p:last-child{margin-bottom:0}
#rqc .rqc-msg.typing{color:var(--soft);font-style:italic}
#rqc .rqc-msg.placeholder{color:var(--soft);text-align:center;font-style:italic;background:none}
#rqc .rqc-msg.err .errline{color:var(--err);font:12px var(--sans)}
#rqc .rqc-in{display:flex;gap:8px;border-top:1px solid var(--line);padding:10px 12px;flex:none}
#rqc .rqc-in input{flex:1;min-width:0;font-size:16px}
@media (max-width:600px){#rqc{right:8px;bottom:calc(8px + env(safe-area-inset-bottom,0px));width:calc(100vw - 16px)}}
/* ============ Mobile / iPad polish ============ */
@media (max-width:900px){
  html{touch-action:manipulation}
  .btn{min-height:42px;min-width:44px;padding:10px 15px;font-size:13.5px}
  .btn.sm{min-height:38px;min-width:0;padding:8px 13px}
  .ibtn,.btn.icon{width:44px;height:44px;min-width:44px;min-height:44px}
  .ibtn svg,.btn.icon svg{width:19px;height:19px}
  input,select,textarea{font-size:16px!important}
  .tool-head{min-height:52px;padding:8px 12px}
  #view-study .topbar{min-height:52px;padding:0 10px}
  .vbar{min-height:50px;padding:8px 10px}
  .rail .rail-group{flex:1;overflow-y:auto}
  .rail button{min-height:44px;padding:12px 0}
  .rail button span{font-size:7.5px}
  .composer textarea{font-size:16px}
  .msgs .msg{font-size:15.5px}
  .chat-wrap .msgs{padding:14px 12px}
  .twrap{max-width:100%}
  .dg-hero-row{gap:6px}
  .dg-toolbar{padding:8px 10px;gap:7px}
  .dg-toolbar>*{flex:none}
  .dg-pane-head{padding:10px 12px}
  .dg-rows{padding:4px 6px}
  .tabs-scroll,.ivbar{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .msg-actions{flex-wrap:wrap}
  .guide-wrap{padding:20px 16px 60px}
}
@media (min-width:901px) and (max-width:1280px) and (pointer:coarse){
  .rail{width:72px}
  .dg-split{grid-template-columns:220px 1fr}
}
@media (max-width:820px){.dg-save{font-size:11px;padding:5px 10px}}
/* ===== Whiteboard AI chat panel ===== */
.wb-chat-fab{position:absolute;right:18px;bottom:64px;z-index:7;width:44px;height:44px;border-radius:50%;border:1px solid var(--border-s);background:var(--surface);color:var(--fg);display:grid;place-items:center;cursor:pointer;box-shadow:var(--shadow);transition:border-color .15s,color .15s,transform .12s;touch-action:none;user-select:none}
.wb-chat-fab:active{cursor:grabbing}
.wb-chat-fab.dragging{transform:scale(1.06);box-shadow:0 10px 28px -6px rgba(0,0,0,.55)}
.wb-chat.dragging{box-shadow:0 24px 60px -10px rgba(0,0,0,.55)}
.wb-chat .wb-chat-head{cursor:grab;user-select:none;touch-action:none}
.wb-chat.dragging .wb-chat-head{cursor:grabbing}
body.wb-chat-dragging,body.wb-chat-dragging *{user-select:none!important}
body.wb-chat-dragging .wb-chat-fab,body.wb-chat-dragging .wb-chat{transition:none!important}
.wb-chat-fab:hover{border-color:var(--accent);color:var(--accent)}
.wb-chat-fab svg{width:20px;height:20px}
.wb-chat{position:absolute;right:18px;bottom:64px;z-index:9;width:min(380px,calc(100% - 36px));max-height:min(560px,calc(100% - 80px));background:var(--surface);border:1px solid var(--line2);border-radius:14px;box-shadow:var(--shadow);display:flex;flex-direction:column;overflow:hidden}
.wb-chat[hidden]{display:none!important}
.wb-chat-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--line);font:600 13px var(--sans);color:var(--ink);flex:none}
.wb-chat-head span{flex:1}
.wb-chat-head button{background:none;border:1px solid var(--line);color:var(--soft);border-radius:6px;width:24px;height:24px;cursor:pointer;font-size:14px;line-height:1;padding:0}
.wb-chat-head button:hover{color:var(--accent);border-color:var(--accent)}
.wb-chat-msgs{flex:1;overflow:auto;padding:10px 12px;display:flex;flex-direction:column;gap:8px;min-height:120px}
.wb-chat-msg{max-width:92%;padding:8px 11px;border-radius:12px;font:13.5px/1.5 var(--read);white-space:pre-wrap;word-wrap:break-word}
.wb-chat-msg.user{align-self:flex-end;background:var(--accent);color:var(--accent-ink)}
.wb-chat-msg.ai{align-self:flex-start;background:var(--panel2);color:var(--ink)}
.wb-chat-msg.ai.typing{color:var(--soft);font-style:italic}
.wb-chat-msg.ai .md p:first-child{margin-top:0}
.wb-chat-msg.ai .md p:last-child{margin-bottom:0}
.wb-chat-msg.ai .md pre{margin:.4em 0}
.wb-chat-msg.err .errline{color:var(--err);font:12px var(--sans)}
.wb-chat-in{display:flex;gap:8px;border-top:1px solid var(--line);padding:10px 12px;flex:none}
.wb-chat-in input{flex:1;min-width:0;font-size:14px}
@media(max-width:600px){
  .wb-chat{right:8px;left:8px;width:auto;bottom:56px}
  .wb-chat-fab{right:12px;bottom:56px}
}
/* ============ P9 ,  Micro-interactions, flat surfaces, misc ============ */

.view.active{animation:viewInPremium var(--dur-base) var(--ease-out)}
@keyframes viewInPremium{
  from{opacity:0;transform:translateY(6px)}
  to{opacity:1;transform:none}
}

/* ============ Tool-card stagger ,  first impressions matter ============ */
#view-home .grid > .tool-card:nth-child(1){animation:cardIn var(--dur-slow) var(--ease-out) both}
#view-home .grid > .tool-card:nth-child(2){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.04s}
#view-home .grid > .tool-card:nth-child(3){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.08s}
#view-home .grid > .tool-card:nth-child(4){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.12s}
#view-home .grid > .tool-card:nth-child(5){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.16s}
#view-home .grid > .tool-card:nth-child(6){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.2s}
#view-home .grid > .tool-card:nth-child(7){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.24s}
#view-home .grid > .tool-card:nth-child(8){animation:cardIn var(--dur-slow) var(--ease-out) both;animation-delay:.28s}
@keyframes cardIn{
  from{opacity:0;transform:translateY(14px) scale(.97)}
  to{opacity:1;transform:none}
}

/* ============ Focus shapes ============ */
.btn:focus-visible,.ibtn:focus-visible{
  box-shadow:var(--ring);
  border-color:transparent;
}
.btn.primary:focus-visible{
  box-shadow:var(--ring);
}
input:focus-visible,textarea:focus-visible,select:focus-visible{
  border-color:var(--accent);
  box-shadow:var(--ring);
}

/* ============ Reduced motion ============ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
}

/* ============ Shimmer ============ */
.shimmer{
  position:relative;
  overflow:hidden;
}
.shimmer::before{
  content:'';
  position:absolute;
  top:0;left:-150%;
  width:120%;height:100%;
  background:linear-gradient(100deg,transparent 30%,rgba(255,255,255,.14) 50%,transparent 70%);
  transform:skewX(-18deg);
  animation:shimmerSweep 2.8s var(--ease-in-out) infinite;
  pointer-events:none;
}
@keyframes shimmerSweep{
  0%{left:-150%}
  55%,100%{left:150%}
}

/* ============ Overscroll ============ */
.rbody,.msgs,.vbody,.tool-body,.guide-wrap,.dash-grid,.library-grid,.ts-list,.cal-side,.chat-nav-list,.dg-rows,.htmlview-wrap{
  overscroll-behavior:contain;
}

/* ============ Primitives ============ */
kbd{
  font:600 var(--fs-2xs) var(--mono);
  border:1px solid var(--border-s);
  border-radius:var(--r-xs);
  background:var(--surface-2);
  color:var(--soft);
  padding:2px 6px;
  box-shadow:0 1px 0 rgba(255,255,255,.05) inset;
}
hr{border:none;border-top:1px solid var(--border);margin:10px 0}
a,a:visited{color:var(--accent-h);text-decoration:none}
a:hover{color:var(--accent);text-decoration:underline}

/* ============ Drop zones ============ */
.drop{
  transition:border-color var(--dur-fast),background var(--dur-fast),box-shadow var(--dur-fast);
}
.drop.serif{
  font-family:var(--serif);
  font-style:italic;
}
.drop svg{color:var(--accent)}
.drop.dragover{
  border-color:var(--accent);
  background:color-mix(in srgb,var(--accent) 8%,transparent);
  box-shadow:var(--ring-soft),var(--shadow-inset);
}

/* ============ Empty ============ */
.empty h2{
  font-size:var(--fs-2xl);
  font-weight:500;
  letter-spacing:-.02em;
}
.empty p{
  color:var(--muted);
  max-width:46ch;
  margin-left:auto;margin-right:auto;
  font-size:var(--fs-sm);
  font-weight:400;
  line-height:1.55;
}

/* ============ Rail ============ */
.rail{
  width:52px;
  display:flex;flex-direction:column;align-items:center;
  gap:8px;padding:10px 0;
  border-right:1px solid var(--border);
  background:color-mix(in srgb,var(--surface) 70%,transparent);
  -webkit-backdrop-filter:saturate(160%) blur(14px);
  backdrop-filter:saturate(160%) blur(14px);
}
.rail button{
  position:relative;
  width:38px;height:38px;
  border:1px solid transparent;
  border-radius:var(--r-sm);
  background:transparent;
  color:var(--muted);
  cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  transition:all var(--dur-fast) var(--ease-out);
}
.rail button:hover{
  color:var(--fg);
  background:var(--surface-2);
  border-color:var(--border);
  transform:translateX(1px);
}
.rail button.active,
.rail button.on{
  background:linear-gradient(180deg,var(--accent-h),var(--accent));
  border-color:var(--accent);
  color:var(--accent-ink);
  box-shadow:0 6px 16px -4px var(--accent-glow);
}
.rail button[disabled]{opacity:.35;pointer-events:none}
.rail button svg{width:17px;height:17px}
.rail button.on::before{
  content:'';
  position:absolute;
  left:-10px;top:50%;
  transform:translateY(-50%);
  width:3px;height:16px;
  border-radius:99px;
  background:var(--accent);
}

/* ============ Bars ============ */
.vbar,.tool-head{
  background:var(--bar-bg);
  -webkit-backdrop-filter:var(--bar-blur);
  backdrop-filter:var(--bar-blur);
  border-bottom:1px solid var(--bar-border);
  box-shadow:var(--bar-shadow);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .vbar,.tool-head{background:var(--surface)}
}

/* ============ P10 ,  Mobile + final polish ============ */

@media (max-width:720px){
  .hero-title{font-size:clamp(40px,14vw,56px)}
  .hero-sub{font-size:var(--fs-base);margin-top:12px}
  .hero-note{font-size:var(--fs-2xs);letter-spacing:.12em;margin-top:16px}
  .tool-card{padding:13px}
  .tool-card p{font-size:var(--fs-xs)}
  .tool-card .tc-icon{
    box-shadow:0 4px 10px -4px rgba(0,0,0,.4);
  }
  .tool-card:hover{
    transform:translateY(-2px);
    box-shadow:0 14px 30px -18px color-mix(in srgb,var(--accent) 40%,transparent);
  }
  .home-search{margin-top:28px;padding:11px 12px 11px 14px}
  .btn:hover{transform:none}
  .ibtn:hover{transform:none}
  .btn:active{transform:translateY(.5px) scale(.985)}
  .ibtn:active{transform:scale(.94)}
  .toast{bottom:16px;width:calc(100vw - 32px);max-width:none;font-size:var(--fs-sm)}
  dialog{width:min(560px,96vw);max-height:92vh;border-radius:var(--r-lg)}
  .dlg-head{padding:16px 18px 12px}
  .dlg-title{font-size:19px}
  .dlg-body{padding:14px 16px;gap:10px}
  .dlg-foot{padding:14px 16px}
}

@media (max-width:400px){
  .hero-title{font-size:clamp(34px,13vw,44px)}
  .tool-card h3{font-size:var(--fs-base)}
  .tool-card .tc-icon{width:36px;height:36px}
}

/* ============ View-chrome entrance ============ */
.view.active > header,
.view.active > .topbar,
.view.active > .vbar{
  animation:chromeIn var(--dur-base) var(--ease-out);
}
@keyframes chromeIn{
  from{opacity:0;transform:translateY(-4px)}
  to{opacity:1;transform:none}
}
.view.active > .tool-body,
.view.active > .vbody,
.view.active > .guide-wrap{
  animation:contentIn var(--dur-base) var(--ease-out);
}
@keyframes contentIn{
  from{opacity:0;transform:translateY(6px)}
  to{opacity:1;transform:none}
}

/* ============ Text rendering ============ */
body{
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}

/* ============ Shared form controls inherit type ============ */
input,textarea,select,button{
  font-family:inherit;
  font-feature-settings:inherit;
}

/* ============ Every button is an app button, styled or not ============
   A bare <button> is rendered by the platform as an OS widget: grey chrome,
   the system font, its own padding and radius. Any component that forgot to
   style one - the AI assist panel's close and tab buttons were the visible
   case - then looks broken beside the buttons that were styled, which reads
   as a layout bug rather than a missing rule. This gives every button a
   neutral app surface first; .btn and component rules layer their own look
   on top, so only the previously-unstyled ones change appearance. */
button{
  appearance:none;
  -webkit-appearance:none;
  font:inherit;
  color:inherit;
  background:var(--secondary-bg,transparent);
  border:1px solid var(--secondary-border,transparent);
  border-radius:var(--r-sm,8px);
  padding:6px 12px;
  cursor:pointer;
  transition:background var(--dur-fast,.12s) var(--ease-out,ease),
             border-color var(--dur-fast,.12s) var(--ease-out,ease);
}
button:hover:not(:disabled){border-color:var(--line,#3a3a4a)}
button:disabled{opacity:.55;cursor:not-allowed}

/* ============ Model selector polish ============ */
.ai-model-select{
  max-width:none;
  font-size:var(--fs-sm);
  padding:7px 12px;
  border-radius:var(--r-md);
  background:var(--ai-model-select-bg);
  border:1px solid var(--ai-model-select-border);
  color:var(--fg);
  cursor:pointer;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.ai-model-select:hover:not(:focus){border-color:var(--border-strong)}.ai-model-select:focus-visible{
  border-color:color-mix(in srgb,var(--accent) 55%,var(--border));
  box-shadow:var(--ring-soft);
}

/* Optional top-bar controls, toggled in Settings -> Display & layout.

   The undo/redo/history cluster and the connection dot are on by default because
   they are real affordances. A person who never touches them is paying width for
   them on every screen, so both can be turned off once here. Driven by a class
   on <html> rather than by hiding the elements, so nothing has to be re-hidden
   when the row re-mounts. */
html.hide-undo-bar .lsk-undo-bar{display:none}
html.hide-net-badge #netBadge{display:none}

/* Model selector in the TOP BAR: icon only.

   In the header the select rendered the raw model id ("gpt-oss:120b-cloud"),
   which was the widest control in the row and pushed the open tool's own
   buttons off screen while reading as debug output. It now collapses to a
   fixed-width chip carrying a bot glyph; the model name lives in the title and
   the aria label.

   The <select> is still a real select - the same node app.js binds onchange to,
   and the same node Settings borrows and hands back - so picking a model is
   unchanged, and when Settings moves it into its dialog it leaves .top-nav and
   these rules fall away so it renders full width again. The chip and the glyph
   are drawn by the wrapper, which is why the select itself is transparent. */
.top-nav .ai-model-wrap{
  position:relative;display:inline-grid;place-items:center;
  width:34px;height:34px;flex:none;color:var(--muted);
}
.top-nav .ai-model-wrap::before{
  content:'';position:absolute;inset:0;border-radius:var(--r-md);
  background:var(--ai-model-select-bg);border:1px solid var(--ai-model-select-border);
  transition:border-color var(--dur-fast) var(--ease-out),box-shadow var(--dur-fast) var(--ease-out);
}
.top-nav .ai-model-wrap::after{
  content:'';position:absolute;width:17px;height:17px;pointer-events:none;
  background:currentColor;
  -webkit-mask:var(--lumen-bot-glyph) center/contain no-repeat;
          mask:var(--lumen-bot-glyph) center/contain no-repeat;
}
.top-nav .ai-model-wrap:hover::before{border-color:var(--border-strong)}
.top-nav .ai-model-wrap:has(.ai-model-select:focus-visible)::before{
  border-color:color-mix(in srgb,var(--accent) 55%,var(--border));
  box-shadow:var(--ring-soft);
}
.top-nav .ai-model-wrap:has(.ai-model-select:focus-visible){color:var(--fg)}
.top-nav .ai-model-wrap:has(> .ai-model-select[hidden]){display:none}
.top-nav .ai-model-select{
  position:relative;z-index:1;
  width:100%;height:100%;min-width:0;max-width:none;
  padding:0;border:0;background:transparent;box-shadow:none;
  opacity:0;cursor:pointer;text-indent:-9999px;
  -webkit-appearance:none;appearance:none;
}
:root{
  --lumen-bot-glyph:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 8V4H8'/%3E%3Crect width='16' height='12' x='4' y='8' rx='2'/%3E%3Cpath d='M2 14h2'/%3E%3Cpath d='M20 14h2'/%3E%3Cpath d='M15 13v2'/%3E%3Cpath d='M9 13v2'/%3E%3C/svg%3E");
}/* ============ Study rail ,  stacked icon + label, no overlap ============
   The premium pass compacted the rail to 52px with 38px icon squares,
   which crushed the stacked icon+label buttons into overlapping text.
   Widen just enough for icon + one-line label and let each button size
   to its content. Labels already use nowrap+ellipsis. */
.rail{
  width:88px;
  align-items:stretch;
}
.rail button{
  width:auto;
  height:auto;
  min-height:48px;
  flex-direction:column;
  gap:4px;
  padding:8px 2px;
  border-radius:var(--r-sm);
}
.rail button svg{width:19px;height:19px}
.rail button span{
  max-width:calc(100% - 4px);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.rail button.on::before{
  left:-10px;
  top:10px;
  bottom:10px;
  height:auto;
  transform:none;
}
.rail button.icon-only{
  gap:0;
  min-height:44px;
  width:calc(100% - 12px);
  margin:4px 6px 8px;
}
@media (max-width:720px){
  .rail{width:80px}
}

/* The whiteboard's own settings button, on the chrome line.
   It belongs to the chrome while the whiteboard is open and to nothing the
   rest of the time, so it is shown only for that view rather than left
   sitting in every other one. */
.wb-chrome-btn{display:none !important}
body:has(#view-whiteboard.active) .wb-chrome-btn{
  display:inline-flex !important;align-self:flex-start;margin-top:6px}
