/* ═══════════════════════════════════════════════════════════════
   Sijil — visual system
   ═══════════════════════════════════════════════════════════════ */

:root{
  --a-violet:#8b5cf6; --a-cyan:#22d3ee; --a-amber:#fbbf24;
  --a-rose:#fb7185;   --a-emerald:#34d399; --a-blue:#60a5fa;
  --a-slate:#94a3b8;

  --r-card:18px; --r-chip:12px;
  --dock-h:64px;
  --ease-spring:cubic-bezier(.22,1.4,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --font:-apple-system,BlinkMacSystemFont,"Inter","Segoe UI",Roboto,system-ui,sans-serif;
}

[data-theme="dark"]{
  --bg:#0a0c11; --bg-2:#0e1118;
  --grid-dot:rgba(255,255,255,.062);
  --glass:rgba(22,26,36,.72);
  --glass-hi:rgba(255,255,255,.055);
  --stroke:rgba(255,255,255,.10);
  --stroke-2:rgba(255,255,255,.18);
  --text:#e9edf5; --text-2:#9aa4b8; --text-3:#5f6a80;
  --shadow-1:0 1px 2px rgba(0,0,0,.5), 0 8px 24px -8px rgba(0,0,0,.7);
  --shadow-2:0 2px 6px rgba(0,0,0,.45), 0 28px 60px -20px rgba(0,0,0,.85);
  --ink-default:#e9edf5;
  --glow:.55;
}
[data-theme="light"]{
  --bg:#f4f5f9; --bg-2:#eceef5;
  --grid-dot:rgba(20,26,45,.10);
  --glass:rgba(255,255,255,.80);
  --glass-hi:rgba(255,255,255,.6);
  --stroke:rgba(16,22,40,.10);
  --stroke-2:rgba(16,22,40,.18);
  --text:#141a2d; --text-2:#5a6379; --text-3:#8b93a7;
  --shadow-1:0 1px 2px rgba(20,26,45,.06), 0 8px 22px -10px rgba(20,26,45,.22);
  --shadow-2:0 2px 8px rgba(20,26,45,.08), 0 30px 60px -22px rgba(20,26,45,.34);
  --ink-default:#141a2d;
  --glow:.28;
}

*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}

/* The `hidden` attribute is only a UA-stylesheet `display:none`, so any author
   `display` rule silently beats it. Several overlays below set display, which
   left them permanently on screen. Make hidden actually mean hidden. */
[hidden]{display:none!important}
html,body{height:100%;margin:0;overflow:hidden;overscroll-behavior:none}
body{
  background:var(--bg); color:var(--text); font-family:var(--font);
  font-size:14px; -webkit-font-smoothing:antialiased;
  touch-action:none; user-select:none;
}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
svg{stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}

/* ── ambient light ─────────────────────────────────────────────── */
#app::before{
  content:""; position:fixed; inset:-20%; pointer-events:none; z-index:0;
  background:
    radial-gradient(38rem 30rem at 12% -6%, color-mix(in oklab,var(--a-violet) 26%, transparent), transparent 70%),
    radial-gradient(34rem 28rem at 88% 108%, color-mix(in oklab,var(--a-cyan) 20%, transparent), transparent 70%),
    radial-gradient(30rem 26rem at 96% 4%,  color-mix(in oklab,var(--a-rose) 14%, transparent), transparent 70%);
  opacity:var(--glow); filter:blur(28px);
  animation:drift 34s ease-in-out infinite alternate;
}
@keyframes drift{to{transform:translate3d(2.5%,-2%,0) scale(1.06)}}

/* ═══ board ════════════════════════════════════════════════════ */
#viewport{position:fixed;inset:0;z-index:1;overflow:hidden;cursor:default}
#viewport.panning{cursor:grabbing}
#viewport.tool-pen{cursor:crosshair}
#viewport.tool-erase{cursor:cell}
#viewport.tool-link{cursor:alias}

#grid{
  position:absolute; inset:0; pointer-events:none;
  background-image:radial-gradient(var(--grid-dot) 1.2px, transparent 1.2px);
  background-size:var(--gs,28px) var(--gs,28px);
  background-position:var(--gx,0) var(--gy,0);
  mask-image:radial-gradient(ellipse 130% 110% at 50% 45%,#000 55%,transparent 100%);
}
#world{position:absolute;top:0;left:0;width:0;height:0;transform-origin:0 0;will-change:transform}

#edges,#ink-live{position:absolute;left:-100000px;top:-100000px;width:200000px;height:200000px;overflow:visible;pointer-events:none}
/* Connectors sit between the two node bands: over the containers, under the
   cards they join. See #nodes below for why that is possible at all. */
#edges{z-index:-500}
#ink-live{z-index:9999}
#ink-live-path{fill:var(--ink-default);stroke:none}

/* Deliberately no z-index. With one, this became a stacking context and every
   card inside it painted above #edges whatever its own z said — so a connector
   was stuck behind a container it crossed, and no amount of ordering within
   this layer could lift it out. Left as `auto`, the cards and the connectors
   share one stacking context and interleave properly: containers at -1000,
   connectors at -500, cards at 1 and up.

   `body.leaving` fades this layer, and an opacity below 1 does make a stacking
   context — so during that fade a container briefly covers the connectors
   again. It lasts one navigation and nothing can be clicked mid-fade. */
#nodes{position:absolute;left:0;top:0}

#marquee{
  position:absolute;border:1.5px solid var(--a-violet);
  background:color-mix(in oklab,var(--a-violet) 14%,transparent);
  border-radius:8px;pointer-events:none;z-index:5000
}

/* ═══ nodes ════════════════════════════════════════════════════ */
.node{
  position:absolute; border-radius:var(--r-card);
  background:var(--glass);
  backdrop-filter:blur(20px) saturate(1.5); -webkit-backdrop-filter:blur(20px) saturate(1.5);
  border:1px solid var(--stroke);
  box-shadow:var(--shadow-1);
  color:var(--text);
  transition:box-shadow .25s var(--ease-out), border-color .2s, transform .34s var(--ease-spring);
  will-change:transform;
  --accent:var(--a-violet);
  contain:layout style;
}
.node::after{ /* accent rim light */
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 1px 0 var(--glass-hi), inset 0 0 0 1px color-mix(in oklab,var(--accent) 16%,transparent);
}
.node.sel{border-color:color-mix(in oklab,var(--accent) 70%,transparent);box-shadow:var(--shadow-2),0 0 0 3px color-mix(in oklab,var(--accent) 22%,transparent)}
.node.drag{box-shadow:var(--shadow-2);transition:none;z-index:9000!important}
/* NB: entrance animates `scale`, not `transform` — `transform` carries the
   node's world position, and a keyframe on it would yank every card to the
   origin for the duration of the animation. */
.node.pop{animation:pop .42s var(--ease-spring);transform-origin:center}
@keyframes pop{from{scale:.82;opacity:0}to{scale:1;opacity:1}}
.node.zap{animation:zap .6s var(--ease-out)}
@keyframes zap{0%{box-shadow:0 0 0 0 color-mix(in oklab,var(--accent) 60%,transparent)}100%{box-shadow:0 0 0 26px transparent}}

.node .bar{
  display:flex;align-items:center;gap:8px;padding:9px 11px 7px;
  border-bottom:1px solid var(--stroke);cursor:default;
  border-radius:var(--r-card) var(--r-card) 0 0;
}
.node .dot{width:9px;height:9px;border-radius:99px;background:var(--accent);flex:none;
  box-shadow:0 0 12px color-mix(in oklab,var(--accent) 70%,transparent)}
/* An icon mark replaces the dot entirely — same accent, no disc behind it. */
.node .dot.glyph{width:15px;height:15px;background:none;box-shadow:none;color:var(--accent);
  display:grid;place-items:center;margin:-3px -1px -3px -2px}
.node .dot.glyph svg{width:15px;height:15px;stroke-width:2}
.node .name{
  font-size:12.5px;font-weight:600;letter-spacing:.01em;color:var(--text-2);
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;outline:none
}
.node .name{cursor:text}             /* it is a text field; the grip does the moving */
.node .name.editing{cursor:text;color:var(--text);
  background:var(--glass-hi);border-radius:6px;margin:-2px -4px;padding:2px 4px}
.node .name:focus{color:var(--text)}
/* The only handle that moves a card, so it is always there to be aimed at:
   faint until the card is hovered rather than absent until then, and a 24px
   target around a 17px glyph. It used to be 13px and invisible, which is why
   the header had to be draggable and the title could not simply be a title. */
/* Roll-up. Sits beside the grip and keeps the same manners: faint until the
   card is reached for, so a board at rest is not a field of buttons. */
.node .bar .roll{flex:none;display:grid;place-items:center;width:22px;height:22px;
  border-radius:7px;color:var(--text-3);opacity:.45;cursor:pointer;
  transition:opacity .18s,background .15s,color .15s}
.node:hover .bar .roll,.node.sel .bar .roll{opacity:1}
.node .bar .roll:hover{background:var(--glass-hi);color:var(--text)}
.node .bar .roll svg{width:15px;height:15px}

.node .bar .grab{flex:none;display:grid;place-items:center;width:24px;height:24px;margin:-3px -5px -3px 0;
  border-radius:7px;color:var(--text-3);opacity:.45;cursor:grab;transition:opacity .18s,background .15s,color .15s}
.node:hover .bar .grab,.node.sel .bar .grab{opacity:1}
.node .bar .grab:hover{background:var(--glass-hi);color:var(--text)}
.node .bar .grab:active{cursor:grabbing}

.node .body{padding:11px 13px 13px;overflow:auto;height:calc(100% - 36px);
  scrollbar-width:thin;scrollbar-color:var(--stroke-2) transparent}
.node .body::-webkit-scrollbar{width:6px}
.node .body::-webkit-scrollbar-thumb{background:var(--stroke-2);border-radius:9px}

/* rich text */
.rt{outline:none;line-height:1.58;font-size:14px;min-height:100%;user-select:text;-webkit-user-select:text}
.rt:empty::before{content:attr(data-ph);color:var(--text-3)}
.rt h1{font-size:19px;margin:.2em 0 .35em;letter-spacing:-.01em}
.rt h2{font-size:16px;margin:.2em 0 .3em}
/* A contenteditable makes a `<div>` per line, not a `<p>`, so without this the
   lines someone types sit flush against each other while anything that came in
   as a paragraph is spaced — the same note, two different rhythms. */
.rt p,.rt div{margin:0 0 .55em}
.rt ul,.rt ol{margin:.2em 0 .6em;padding-left:1.15em}
.rt li{margin:.15em 0}
.rt code{background:var(--glass-hi);padding:.1em .35em;border-radius:6px;font-size:.9em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace}
.rt blockquote{margin:.3em 0;padding-left:.7em;border-left:2px solid var(--accent);color:var(--text-2)}
.rt a{color:var(--a-cyan)}
.rt img{max-width:100%;border-radius:10px;display:block;margin:.4em 0}
.rt hr{border:0;border-top:1px solid var(--stroke);margin:.7em 0}

/* task node */
.task-row{display:flex;align-items:flex-start;gap:9px;padding:5px 2px}
.task-box{
  width:19px;height:19px;flex:none;margin-top:1px;border-radius:7px;
  border:1.8px solid var(--stroke-2);position:relative;transition:.2s var(--ease-spring);cursor:pointer
}
.task-box:hover{border-color:var(--accent)}
.task-row.done .task-box{background:var(--accent);border-color:var(--accent)}
.task-box::after{content:"";position:absolute;left:5px;top:2px;width:5px;height:9px;
  border:2.4px solid #0a0c11;border-top:0;border-left:0;transform:rotate(42deg) scale(0);
  transition:transform .24s var(--ease-spring)}
.task-row.done .task-box::after{transform:rotate(42deg) scale(1)}
.task-row .t{flex:1;outline:none;user-select:text;-webkit-user-select:text;line-height:1.5}
.task-row.done .t{color:var(--text-3);text-decoration:line-through}
.task-add{color:var(--text-3);font-size:12.5px;padding:6px 2px;cursor:pointer}
.task-add:hover{color:var(--accent)}

/* An item with nothing written in it says so, and its tick box says what will
   happen: ticking an empty item removes it rather than completing it. */
.task-row:has(.t:empty) .task-box{border-style:dashed}
.task-row:has(.t:empty) .task-box:hover{border-color:var(--a-rose);border-style:solid;
  background:color-mix(in oklab,var(--a-rose) 18%,transparent)}
.task-meter{height:3px;border-radius:9px;background:var(--stroke);margin:8px 2px 2px;overflow:hidden}
.task-meter i{display:block;height:100%;background:var(--accent);width:0;transition:width .45s var(--ease-out)}

/* container / frame */
.node.frame{
  background:color-mix(in oklab,var(--accent) 7%,transparent);
  backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);
  border:1.5px dashed color-mix(in oklab,var(--accent) 42%,transparent);
  box-shadow:none;
}
.node.frame::after{box-shadow:none}
.node.frame > .bar{border-bottom:0;background:none}
.node.frame > .bar .name{color:color-mix(in oklab,var(--accent) 85%,var(--text));font-size:12px;
  text-transform:uppercase;letter-spacing:.09em}
.node.frame.hot{
  background:color-mix(in oklab,var(--accent) 16%,transparent);
  border-color:var(--accent);border-style:solid;
  box-shadow:0 0 0 3px color-mix(in oklab,var(--accent) 22%,transparent)
}
.node.frame .count{font-size:11px;color:var(--text-3);padding:0 4px}
/* A lawh about to swallow what is held over it. */
.node.lawh.hot .lawh-face{background:color-mix(in oklab,var(--accent) 24%,transparent);
  box-shadow:0 0 0 3px color-mix(in oklab,var(--accent) 34%,transparent)}
.node.lawh.hot .lawh-glyph{transform:scale(1.12);transition:transform .12s var(--ease-out)}

/* ink + media */
.node.ink,.node.media{background:transparent;border-color:transparent;box-shadow:none;backdrop-filter:none}
/* Ink is canvas marks, not a card, and it has no header at all — see
   createNodeEl. It drags from anywhere on the drawing, so the bar was only ever
   a strip of glass laid over the strokes it was supposed to label. */
.node.ink::after,.node.media::after{box-shadow:none}
.node.ink.sel,.node.media.sel{border-color:color-mix(in oklab,var(--accent) 60%,transparent)}
.node.ink svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.node.ink svg path{stroke:none}
.node.media{overflow:hidden;background:var(--glass)}
.node.media img,.node.media video{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}
.node.media .cap{position:absolute;left:0;right:0;bottom:0;padding:16px 12px 9px;font-size:12px;
  background:linear-gradient(transparent,rgba(0,0,0,.72));color:#fff}

/* A picture is not a card either, so its header follows ink's rule rather than
   a note's: the file's name, its mark and the grip are laid over the top edge
   and appear when the card is reached for. Permanently visible, it would cover
   the one part of an image most likely to matter. Before this the bar was built
   but never seen — the image is 100% of the height, so the header was laid out
   past the bottom edge and clipped away by `overflow:hidden`. */
.node.media .bar{
  opacity:0;transition:opacity .18s;border-bottom:none;
  border-radius:var(--r-card) var(--r-card) 0 0;background:var(--glass);backdrop-filter:blur(20px);
  position:absolute;top:0;left:0;right:0;z-index:2
}
.node.media:hover .bar,.node.media.sel .bar,.node.media.drag .bar{opacity:1}

/* Playback over the foot of the video, on the same terms as the header. The
   video itself stays `pointer-events:none` so the card can still be dragged by
   its picture; only the controls take the pointer. */
.node.media .vctl{
  position:absolute;left:0;right:0;bottom:0;z-index:2;display:flex;align-items:center;gap:8px;
  padding:15px 10px 8px;opacity:0;transition:opacity .18s;
  background:linear-gradient(transparent,rgba(0,0,0,.78))
}
.node.media:hover .vctl,.node.media.sel .vctl{opacity:1}
.node.media .vbtn{width:26px;height:26px;flex:none;border-radius:8px;display:grid;place-items:center;
  color:#fff;background:rgba(255,255,255,.16);transition:background .15s}
.node.media .vbtn:hover{background:rgba(255,255,255,.34)}
.node.media .vbtn svg{width:15px;height:15px}
.node.media .vtime{font-size:10.5px;color:#fff;opacity:.92;flex:none;font-variant-numeric:tabular-nums}
.node.media .vscrub{flex:1;min-width:0;height:4px;appearance:none;-webkit-appearance:none;
  background:rgba(255,255,255,.32);border-radius:99px;cursor:pointer}
.node.media .vscrub::-webkit-slider-thumb{-webkit-appearance:none;width:11px;height:11px;
  border-radius:99px;background:#fff}
.node.media .vscrub::-moz-range-thumb{width:11px;height:11px;border:0;border-radius:99px;background:#fff}
/* A caption and the controls both want the foot of the card. */
.node.media.has-video .cap{bottom:38px}

/* Swallowed by a lawh. The card goes at once and the network catches up; if the
   move is refused it fades straight back in. Opacity only — the position is an
   inline transform, and animating that here would fight it. */
.node.into-lawh{opacity:0;pointer-events:none;transition:opacity .16s var(--ease-out)}

/* Between one lawh and the next. Brief and slight: enough to read as a change
   of place rather than a stutter. */
body.leaving #nodes,body.leaving #edge-layer{opacity:.35;transition:opacity .12s linear}

/* ═══ resolving a clash ═══════════════════════════════════════ */
.merge-gate{position:fixed;inset:0;z-index:120;display:grid;place-items:center;
  background:color-mix(in oklab,var(--bg) 72%,transparent);backdrop-filter:blur(6px);
  animation:palIn .28s var(--ease-spring)}
.merge-sheet{width:min(760px,94vw);max-height:88vh;display:flex;flex-direction:column}
.merge-sub{margin:2px 0 14px}
.merge-panes{display:grid;grid-template-columns:1fr 1fr;gap:12px;min-height:0;flex:1}
.merge-pane{display:flex;flex-direction:column;min-height:0;border:1px solid var(--stroke);
  border-radius:14px;background:color-mix(in oklab,var(--a-violet) 5%,transparent);overflow:hidden}
.merge-pane.theirs{background:color-mix(in oklab,var(--a-blue) 6%,transparent)}
.merge-head{font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text-3);padding:9px 12px 7px;border-bottom:1px solid var(--stroke)}
.merge-list{flex:1;min-height:120px;max-height:46vh;overflow-y:auto;padding:9px;
  display:flex;flex-direction:column;gap:7px;
  scrollbar-width:thin;scrollbar-color:var(--stroke-2) transparent}

/* A chip is a paragraph or a checklist item. The ones both sides agree on are
   quiet; the ones that differ carry the accent, so the eye goes to the choice
   that actually has to be made. */
.merge-chip{padding:8px 11px;border-radius:10px;font-size:12.5px;line-height:1.5;
  background:var(--glass);border:1px solid var(--stroke);cursor:grab;
  touch-action:none;user-select:none;-webkit-user-select:none;
  overflow-wrap:anywhere;transition:border-color .14s,background .14s,transform .1s}
.merge-chip.differs{border-color:color-mix(in oklab,var(--a-violet) 55%,transparent);
  background:color-mix(in oklab,var(--a-violet) 12%,transparent)}
.merge-pane.theirs .merge-chip.differs{border-color:color-mix(in oklab,var(--a-blue) 60%,transparent);
  background:color-mix(in oklab,var(--a-blue) 13%,transparent)}
.merge-chip.same{color:var(--text-2);opacity:.72}
.merge-chip.done{text-decoration:line-through;color:var(--text-3)}
.merge-chip:active{cursor:grabbing}
.merge-chip.lifting{opacity:.35}
/* What is about to happen: a chip lands on this one and replaces it… Scoped
   past `.merge-pane.theirs .merge-chip.differs`, which is more specific than a
   bare `.merge-chip.drop-into` and would otherwise keep its blue while the
   pointer sat on it. */
.merge-list .merge-chip.drop-into{border-color:color-mix(in oklab,var(--a-amber) 75%,transparent);
  background:color-mix(in oklab,var(--a-amber) 20%,transparent);transform:scale(1.02)}
/* …or the pieces part and leave a real space where it will land, so "between
   these two" is somewhere you can see rather than something you have to guess
   from where the pointer happens to be.

   The same amber as replacing, deliberately. Landing on a chip and landing
   between two chips are one idea — "the piece goes here" — and giving them a
   colour each read as two unrelated states. Amber because it is neither side's
   colour: violet is yours and blue is theirs, so a target in either of those
   would be saying something about ownership it does not mean. */
.merge-slot{flex:none;height:38px;border-radius:10px;
  border:2px dashed color-mix(in oklab,var(--a-amber) 62%,transparent);
  background:color-mix(in oklab,var(--a-amber) 12%,transparent);
  animation:slotOpen .13s var(--ease-out)}
@keyframes slotOpen{from{height:0;opacity:0}to{height:38px;opacity:1}}
.merge-chip.ghost{position:fixed;z-index:130;pointer-events:none;box-shadow:var(--shadow-2);
  opacity:.95;transform:rotate(-1.2deg)}

.merge-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:14px}
.merge-foot .muted{flex:1;min-width:0;margin:0;font-size:12px}
/* Scoped to the footer to outrank `.auth-go`, which is a full-width form button
   defined *later* in this file — a bare `.merge-go` ties on specificity and
   loses on source order, and the button then swallowed the footer and pushed
   the count onto one word per line. Specificity, not position: moving either
   block would silently undo it. */
.merge-foot .merge-go{flex:none;width:auto;padding:9px 20px;font-size:13px}
@media (max-width:640px){
  .merge-panes{grid-template-columns:1fr}
  .merge-list{max-height:26vh}
}

/* ═══ diagram shapes ══════════════════════════════════════════ */
/* No card behind it: the outline *is* the card, so the chrome that makes a note
   look like paper would show as a rectangle around a circle. */
.node.shape{background:transparent;border-color:transparent;box-shadow:none;backdrop-filter:none;overflow:visible}
.node.shape::after{box-shadow:none}
.node.shape > .body{padding:0;height:100%;overflow:visible;display:grid;place-items:center}
.shape-art{position:absolute;inset:0;width:100%;height:100%;overflow:visible;
  fill:color-mix(in oklab,var(--accent) 20%,transparent);
  stroke:var(--accent);stroke-width:1.6;stroke-linejoin:round}
/* The outline is stretched to the card's size, so without this a wide ellipse
   would have fat sides and thin ends. */
.shape-art,.shape-art *{vector-effect:non-scaling-stroke}
.shape-art .detail{fill:none}
.node.shape.sel{border-color:transparent}
.node.shape.sel .shape-art{stroke-width:2.4}
/* The label sits in the middle of the shape, not in a bar above it. */
.shape-label{position:relative;z-index:1;max-width:78%;text-align:center;font-size:13px;
  font-weight:600;color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere;outline:none;cursor:text}
.shape-label:empty::before{content:'Double-click to label';color:var(--text-3);font-weight:500;font-size:11.5px}
.shape-label.editing{background:var(--glass-hi);border-radius:6px;padding:2px 6px;margin:-2px -6px}
#viewport.tool-shape{cursor:copy}

/* ═══ the diagram picker ══════════════════════════════════════ */
.diagram-pick{
  position:fixed;z-index:70;padding:12px;border-radius:16px;width:max-content;max-width:min(94vw,340px);
  background:var(--glass);border:1px solid var(--stroke);backdrop-filter:blur(20px);
  box-shadow:var(--shadow-2);animation:rise-placed .22s var(--ease-spring)
}
.dp-group + .dp-group{margin-top:12px;padding-top:11px;border-top:1px solid var(--stroke)}
.dp-title{font-size:10px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--text-3);margin-bottom:8px}
.dp-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:5px}
.dp-cell{width:40px;height:38px;border-radius:10px;display:grid;place-items:center;
  color:var(--text-2);transition:.14s var(--ease-out)}
.dp-cell svg{width:26px;height:26px;fill:color-mix(in oklab,currentColor 22%,transparent);
  stroke:currentColor;stroke-width:1.7;stroke-linejoin:round}
.dp-cell svg .detail{fill:none}
.dp-cell:hover{background:var(--glass-hi);color:var(--text)}
.dp-cell.on{background:color-mix(in oklab,var(--accent) 20%,transparent);color:var(--accent)}
.dp-lines{display:flex;gap:5px}
.dp-line{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;padding:7px 4px 6px;
  border-radius:10px;color:var(--text-2);font-size:10.5px;font-weight:600;transition:.14s var(--ease-out)}
.dp-line svg{width:28px;height:22px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.dp-line:hover{background:var(--glass-hi);color:var(--text)}
.dp-line.on{background:color-mix(in oklab,var(--accent) 20%,transparent);color:var(--accent)}

/* ═══ rolled up like a scroll ═════════════════════════════════ */
/* Everything but the header is put away, and the card's own height shrinks with
   it, so what can be clicked is what can be seen.

   Deliberately after the ink and media rules rather than beside the other bar
   styling. A picture and a sketch each insist on filling their card, at exactly
   the same specificity as these — placed earlier, they won on source order and
   a rolled clip stayed on screen squashed into a 36px strip, with its header
   still invisible because the media bar only shows on hover. */
.node.rolled > .body,
.node.rolled img,.node.rolled video,.node.rolled .cap,.node.rolled .vctl,
.node.rolled svg[data-ink],
.node.rolled .hres{display:none}
.node.rolled .bar{opacity:1;border-bottom:none;border-radius:var(--r-card)}
.node.rolling{transition:height .22s var(--ease-out)}

/* handles */
.hres{position:absolute;right:-7px;bottom:-7px;width:22px;height:22px;border-radius:8px;
  background:var(--glass);border:1px solid var(--stroke-2);backdrop-filter:blur(8px);
  cursor:nwse-resize;opacity:0;transition:opacity .18s;touch-action:none;z-index:3}
.node:hover .hres,.node.sel .hres{opacity:1}
.hres::after{content:"";position:absolute;right:5px;bottom:5px;width:8px;height:8px;
  border-right:2px solid var(--text-3);border-bottom:2px solid var(--text-3);border-radius:0 0 3px 0}

.anchor{position:absolute;width:15px;height:15px;border-radius:99px;background:var(--bg);
  border:2.2px solid var(--accent);opacity:0;transition:opacity .18s,transform .18s var(--ease-spring);
  cursor:crosshair;touch-action:none;z-index:4}
.node:hover .anchor,.node.sel .anchor,body.linking .anchor{opacity:1}
.anchor:hover{transform:scale(1.45)}
.anchor.n{top:-8px;left:calc(50% - 7.5px)} .anchor.s{bottom:-8px;left:calc(50% - 7.5px)}
.anchor.w{left:-8px;top:calc(50% - 7.5px)}  .anchor.e{right:-8px;top:calc(50% - 7.5px)}

/* edges */
#edge-layer path.wire{stroke-width:2;fill:none;stroke-linecap:round;
  transition:stroke-width .2s,opacity .2s;pointer-events:stroke;cursor:pointer}
#edge-layer path.hit{stroke:transparent;stroke-width:20;fill:none;pointer-events:stroke;cursor:pointer}
#edge-layer g.on path.wire{stroke-width:3.2}
#edge-layer text{fill:var(--text-2);stroke:none;font:600 11px var(--font);text-anchor:middle;
  paint-order:stroke;stroke:var(--bg);stroke-width:5px;stroke-linejoin:round}
#edge-ghost path{stroke:var(--a-violet);stroke-width:2.4;stroke-dasharray:7 7;fill:none;opacity:.9}
.pulse{fill:var(--a-cyan);stroke:none;filter:drop-shadow(0 0 7px var(--a-cyan))}

/* ═══ chrome ═══════════════════════════════════════════════════ */
#topbar{
  position:fixed;top:0;left:0;right:0;height:56px;z-index:60;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 14px 0 12px;padding-top:env(safe-area-inset-top);
  background:linear-gradient(var(--bg) 30%,transparent);
  pointer-events:none;
}
#topbar>*{pointer-events:auto}
.brand{display:flex;align-items:center;gap:10px;min-width:0}
/* The emblem paints its body with `currentColor` and cuts the inscription out
   in `--ink`, which tracks the background — so the ruling reads as knocked out
   of the scroll in either theme rather than as a second colour on top of it.
   Sized 24x26 because the artwork is 40x44, not square. */
.mark{width:24px;height:26px;flex:none;stroke:none;
  color:var(--a-violet);--ink:var(--bg);
  filter:drop-shadow(0 0 12px color-mix(in oklab,var(--a-violet) 60%,transparent))}
.doc-title{
  background:none;border:0;outline:none;color:var(--text);font:600 15px/1 var(--font);
  letter-spacing:-.01em;padding:7px 9px;border-radius:10px;min-width:0;width:min(46vw,320px);
  transition:background .2s
}
.doc-title:hover{background:var(--glass-hi)}
.doc-title:focus{background:var(--glass-hi);box-shadow:0 0 0 1px var(--stroke-2)}

.top-actions{display:flex;gap:7px;align-items:center}
.chip{
  display:flex;align-items:center;gap:7px;height:36px;padding:0 13px;border-radius:var(--r-chip);
  background:var(--glass);border:1px solid var(--stroke);backdrop-filter:blur(14px);
  color:var(--text-2);font-size:13px;font-weight:550;transition:.18s var(--ease-out)
}
.chip svg{width:16px;height:16px}
.chip:hover{color:var(--text);border-color:var(--stroke-2);transform:translateY(-1px)}
.chip:active{transform:translateY(0) scale(.97)}
.chip.icon{padding:0;width:36px;justify-content:center}
.chip.on{color:var(--a-cyan);border-color:color-mix(in oklab,var(--a-cyan) 45%,transparent)}
/* Offline is a state, not a failure: amber and calm, never alarming. Work is
   safe on the device and the pill says so on hover. */
.chip.net{
  color:var(--a-amber);border-color:color-mix(in oklab,var(--a-amber) 40%,transparent);
  background:color-mix(in oklab,var(--a-amber) 12%,var(--glass));cursor:default
}
.chip.net.syncing{color:var(--a-cyan);border-color:color-mix(in oklab,var(--a-cyan) 40%,transparent);
  background:color-mix(in oklab,var(--a-cyan) 12%,var(--glass))}
@media (max-width:720px){.chip span{display:none}.chip{padding:0;width:36px;justify-content:center}}

#rail{
  position:fixed;right:12px;top:50%;transform:translateY(-50%);z-index:60;
  display:flex;flex-direction:column;gap:2px;padding:5px;border-radius:16px;
  background:var(--glass);border:1px solid var(--stroke);backdrop-filter:blur(14px);
  box-shadow:var(--shadow-1)
}
.rail-btn{width:34px;height:32px;border-radius:10px;color:var(--text-2);font-size:15px;
  display:grid;place-items:center;transition:.15s}
.rail-btn:hover{background:var(--glass-hi);color:var(--text)}
#zoom-label{font-size:10.5px;color:var(--text-3);text-align:center;padding:2px 0;font-variant-numeric:tabular-nums;
  display:block;width:100%;background:none;border:0;font-family:inherit;cursor:pointer;border-radius:8px;transition:background .12s,color .12s}
#zoom-label:hover{background:var(--glass-hi);color:var(--text)}
@media (max-height:560px){#rail{display:none}}

#dock{
  position:fixed;left:50%;bottom:max(14px,env(safe-area-inset-bottom));transform:translateX(-50%);
  z-index:60;display:flex;align-items:center;gap:3px;padding:6px;border-radius:20px;
  background:var(--glass);border:1px solid var(--stroke);
  backdrop-filter:blur(24px) saturate(1.6);-webkit-backdrop-filter:blur(24px) saturate(1.6);
  box-shadow:var(--shadow-2);max-width:calc(100vw - 20px);overflow-x:auto;scrollbar-width:none
}
#dock::-webkit-scrollbar{display:none}
.dock-sep{width:1px;height:26px;background:var(--stroke);margin:0 4px;flex:none}
.tool{
  position:relative;display:flex;flex-direction:column;align-items:center;gap:3px;
  min-width:56px;padding:8px 6px 6px;border-radius:15px;color:var(--text-2);
  transition:.2s var(--ease-out);flex:none
}
.tool svg{width:21px;height:21px}
.tool b{font-size:9.5px;font-weight:600;letter-spacing:.03em;opacity:.85}
.tool:hover{color:var(--text);background:var(--glass-hi)}
.tool:active{transform:scale(.93)}
.tool[aria-pressed="true"]{
  color:var(--text);
  background:linear-gradient(color-mix(in oklab,var(--a-violet) 30%,transparent),color-mix(in oklab,var(--a-violet) 14%,transparent));
  box-shadow:inset 0 1px 0 var(--glass-hi),0 0 22px -6px var(--a-violet)
}
@media (max-width:560px){.tool b{display:none}.tool{min-width:46px;padding:9px 6px}}

#ink-bar{
  position:fixed;left:50%;bottom:calc(max(14px,env(safe-area-inset-bottom)) + var(--dock-h) + 22px);
  transform:translateX(-50%);z-index:60;display:flex;align-items:center;gap:12px;padding:8px 12px;
  border-radius:16px;background:var(--glass);border:1px solid var(--stroke);
  backdrop-filter:blur(20px);box-shadow:var(--shadow-1);animation:rise .3s var(--ease-spring)
}
@keyframes rise{from{opacity:0;transform:translate(-50%,14px)}to{opacity:1;transform:translate(-50%,0)}}
/* Same entrance for panels that compute their own `left` (#hud, #fmt) rather
   than sitting at left:50%. Reusing `rise` shifted them half their own width
   to the left for the length of the animation, then snapped them into place —
   barely visible on a narrow bar, glaring once the HUD grew a mark picker. */
@keyframes rise-placed{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
.swatches{display:flex;gap:6px}

/* Mark picker in the selection HUD. */
.marks,.hud-acts{display:flex;gap:2px;align-items:center}
.mk{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;
  color:var(--text-3);transition:.15s}
.mk svg{width:16px;height:16px;stroke-width:1.9}
.mk i{width:9px;height:9px;border-radius:99px;background:currentColor;display:block}
.mk:hover{background:var(--glass-hi);color:var(--text)}
.mk.on{background:var(--glass-hi);color:var(--a-violet)}
.sw{width:24px;height:24px;border-radius:99px;border:2px solid transparent;transition:.18s var(--ease-spring);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.25)}
.sw:hover{transform:scale(1.18)}
.sw.on{border-color:var(--text);transform:scale(1.12)}
.sizes{display:flex;gap:3px}
.sizes button{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;transition:.15s}
.sizes button i{width:var(--d);height:var(--d);border-radius:99px;background:var(--text-2)}
.sizes button.on{background:var(--glass-hi)}
.sizes button.on i{background:var(--text)}
.ink-mode{width:30px;height:28px;border-radius:9px;color:var(--text-3);font-size:15px}
.ink-mode.on{background:var(--glass-hi);color:var(--a-amber)}

#hud{
  /* One row on a desktop; wraps group-by-group on a phone. Each group is its
     own flex item so a row never breaks mid-group. */
  position:fixed;z-index:62;display:flex;align-items:center;gap:8px;padding:6px 9px;border-radius:14px;
  flex-wrap:wrap;justify-content:center;max-width:min(94vw,640px);
  background:var(--glass);border:1px solid var(--stroke);backdrop-filter:blur(20px);
  box-shadow:var(--shadow-2);animation:rise-placed .25s var(--ease-spring)
}
#hud button{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;color:var(--text-2);transition:.15s}
#hud button svg{width:16px;height:16px}
#hud button:hover{background:var(--glass-hi);color:var(--text)}
#hud button.danger:hover{background:color-mix(in oklab,var(--a-rose) 22%,transparent);color:var(--a-rose)}
.hud-sep{width:1px;height:20px;background:var(--stroke)}
#hud .sw{width:20px;height:20px}
/* The mark picker and its divider travel together, so hiding one hides the
   rule that would otherwise be left dangling beside the swatches. */
.hud-group{display:flex;align-items:center;gap:8px}
.hud-group[hidden]{display:none}

/* radial */
#radial{position:fixed;z-index:70;width:0;height:0}
#radial .ri{
  position:absolute;width:52px;height:52px;margin:-26px;border-radius:99px;
  background:var(--glass);border:1px solid var(--stroke-2);backdrop-filter:blur(18px);
  box-shadow:var(--shadow-2);display:grid;place-items:center;color:var(--text);
  transition:transform .34s var(--ease-spring),opacity .2s;transform:scale(.2);opacity:0
}
#radial .ri svg{width:20px;height:20px}
#radial .ri.in{transform:scale(1);opacity:1}
#radial .ri:hover{background:color-mix(in oklab,var(--a-violet) 35%,var(--glass))}
#radial .rl{position:absolute;transform:translate(-50%,26px);font-size:10px;color:var(--text-3);white-space:nowrap}

/* palette */
#palette{position:fixed;inset:0;z-index:80;background:color-mix(in oklab,var(--bg) 62%,transparent);
  backdrop-filter:blur(10px);display:grid;place-items:start center;padding-top:min(18vh,140px);
  animation:fade .18s var(--ease-out)}
@keyframes fade{from{opacity:0}to{opacity:1}}
.pal-card{width:min(620px,92vw);border-radius:20px;background:var(--glass);border:1px solid var(--stroke-2);
  backdrop-filter:blur(30px) saturate(1.6);box-shadow:var(--shadow-2);overflow:hidden;
  animation:palIn .3s var(--ease-spring)}
@keyframes palIn{from{opacity:0;transform:translateY(-14px) scale(.97)}to{opacity:1;transform:none}}
#pal-input{width:100%;padding:18px 20px;background:none;border:0;outline:none;color:var(--text);
  font-size:16px;border-bottom:1px solid var(--stroke)}
#pal-results{max-height:min(52vh,420px);overflow:auto;padding:6px}
.pal-row{display:flex;align-items:center;gap:11px;padding:10px 13px;border-radius:12px;cursor:pointer}
.pal-row .k{width:26px;height:26px;border-radius:8px;display:grid;place-items:center;flex:none;
  background:var(--glass-hi);color:var(--text-2)}
.pal-row .k svg{width:15px;height:15px}
.pal-row .tt{flex:1;min-width:0}
.pal-row .tt b{display:block;font-weight:550;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pal-row .tt s{display:block;text-decoration:none;color:var(--text-3);font-size:11.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pal-row.on{background:color-mix(in oklab,var(--a-violet) 24%,transparent)}
.pal-empty{padding:26px;text-align:center;color:var(--text-3);font-size:13px}

/* share */
#share,#about,#wipe,#mode-pick{position:fixed;inset:0;z-index:80;background:color-mix(in oklab,var(--bg) 62%,transparent);
  backdrop-filter:blur(10px);display:grid;place-items:center;padding:16px;animation:fade .18s}
.sheet{position:relative;width:min(460px,94vw);border-radius:22px;padding:22px;background:var(--glass);
  border:1px solid var(--stroke-2);backdrop-filter:blur(30px);box-shadow:var(--shadow-2);
  animation:palIn .3s var(--ease-spring)}
/* Keep the heading clear of the close button in the corner. */
.sheet h3{margin:0 0 4px;font-size:17px;letter-spacing:-.01em;padding-right:34px}
.muted{color:var(--text-3);font-size:12.5px;margin:0 0 16px;line-height:1.5}
.share-row{display:flex;gap:7px;margin-bottom:12px}
.share-row input,.share-row select,.share-link code{
  background:var(--glass-hi);border:1px solid var(--stroke);border-radius:11px;padding:9px 11px;
  color:var(--text);outline:none;font:inherit;font-size:13px}
.share-row input{flex:1;min-width:0}
.share-row select{color:var(--text-2)}
.share-row button,#share-copy{background:var(--a-violet);color:#fff;border-radius:11px;padding:9px 14px;
  font-size:13px;font-weight:600;transition:.15s}
.share-row button:hover,#share-copy:hover{filter:brightness(1.12)}
#share-list{list-style:none;margin:0 0 14px;padding:0;display:flex;flex-direction:column;gap:6px}
#share-list li{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:12px;background:var(--glass-hi)}
#share-list .av{width:28px;height:28px;border-radius:99px;display:grid;place-items:center;
  font-size:11.5px;font-weight:700;color:#0a0c11;flex:none}
#share-list .nm{flex:1;font-size:13px}
#share-list .rl{font-size:11.5px;color:var(--text-3)}
#share-list .rm{color:var(--text-3);padding:2px 6px;border-radius:7px}
#share-list .rm:hover{color:var(--a-rose);background:color-mix(in oklab,var(--a-rose) 16%,transparent)}
.share-link{display:flex;gap:7px;align-items:center;margin-bottom:16px}
.share-link code{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:ui-monospace,monospace;font-size:11.5px;color:var(--text-2)}
/* Dialogs dismiss from the top right. House rule: never a cancel button along
   the bottom — the X is the only way out besides Escape and the backdrop. */
.sheet-x{position:absolute;top:12px;right:12px;width:32px;height:32px;border-radius:10px;
  display:grid;place-items:center;color:var(--text-3);transition:.16s var(--ease-out)}
.sheet-x svg{width:17px;height:17px}
.sheet-x:hover{background:var(--glass-hi);color:var(--text)}
.sheet-x:active{transform:scale(.92)}

/* toasts + hint */
#toasts{position:fixed;left:50%;top:70px;transform:translateX(-50%);z-index:90;
  display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none}
.toast{padding:9px 15px;border-radius:13px;background:var(--glass);border:1px solid var(--stroke-2);
  backdrop-filter:blur(20px);box-shadow:var(--shadow-2);font-size:13px;font-weight:500;
  animation:toastIn .34s var(--ease-spring)}
@keyframes toastIn{from{opacity:0;transform:translateY(-12px) scale(.94)}to{opacity:1;transform:none}}
.toast.out{animation:toastOut .3s var(--ease-out) forwards}
@keyframes toastOut{to{opacity:0;transform:translateY(-8px) scale(.96)}}

.hint{position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(max(14px,env(safe-area-inset-bottom)) + var(--dock-h) + 20px);
  z-index:55;font-size:11.5px;color:var(--text-3);pointer-events:none;
  background:var(--glass);border:1px solid var(--stroke);border-radius:99px;padding:6px 14px;
  backdrop-filter:blur(14px);transition:opacity .5s;white-space:nowrap;max-width:94vw;overflow:hidden;text-overflow:ellipsis}
.hint.gone{opacity:0}
.hint b{color:var(--text-2)}

/* floating format bar */
#fmt{position:fixed;z-index:75;display:flex;gap:2px;padding:5px;border-radius:13px;
  background:var(--glass);border:1px solid var(--stroke-2);backdrop-filter:blur(22px);
  box-shadow:var(--shadow-2);animation:rise-placed .2s var(--ease-spring)}
#fmt button{width:29px;height:29px;border-radius:8px;color:var(--text-2);font-size:13px;
  display:grid;place-items:center;transition:.14s}
#fmt button:hover{background:var(--glass-hi);color:var(--text)}
#fmt button svg{width:15px;height:15px}

@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;transition-duration:.06s!important}}

/* ═══ sign-in gate (takhzeen adapter only) ════════════════════ */
.auth-gate{
  position:fixed;inset:0;z-index:200;display:grid;place-items:center;padding:18px;
  background:var(--bg);animation:fade .3s var(--ease-out)
}
.auth-gate::before{
  content:"";position:absolute;inset:-20%;pointer-events:none;
  background:
    radial-gradient(34rem 28rem at 22% 8%, color-mix(in oklab,var(--a-violet) 30%,transparent),transparent 70%),
    radial-gradient(30rem 26rem at 80% 96%, color-mix(in oklab,var(--a-cyan) 22%,transparent),transparent 70%);
  filter:blur(30px);opacity:var(--glow);animation:drift 30s ease-in-out infinite alternate
}
.auth-gate.out{animation:fade .3s var(--ease-out) reverse forwards}
.auth-card{
  position:relative;width:min(400px,100%);padding:30px 28px 24px;border-radius:24px;
  background:var(--glass);border:1px solid var(--stroke-2);
  backdrop-filter:blur(30px) saturate(1.6);-webkit-backdrop-filter:blur(30px) saturate(1.6);
  box-shadow:var(--shadow-2);animation:palIn .42s var(--ease-spring)
}
/* The emblem fills whatever box it is given, so each placement sets its own
   size and nothing has to be told twice. */
.mark.inscribed{display:block;width:100%;height:100%}
/* Big on the front door. It is the one screen with nothing else on it, and the
   kufic needs room — much under 60px and the letters close up into a smudge. */
.auth-mark{width:78px;height:86px;color:var(--a-violet);--ink:var(--bg);stroke:none;margin:0 auto 14px;
  filter:drop-shadow(0 0 20px color-mix(in oklab,var(--a-violet) 55%,transparent))}
.auth-card h2{margin:0 0 5px;font-size:22px;letter-spacing:-.02em}
.auth-card .muted{margin-bottom:20px}
.auth-card label{display:block;margin-bottom:13px}
.auth-card label span{display:block;font-size:11.5px;font-weight:600;letter-spacing:.05em;
  text-transform:uppercase;color:var(--text-3);margin-bottom:6px}
.auth-card input{
  width:100%;padding:11px 13px;border-radius:12px;font:inherit;font-size:14px;color:var(--text);
  background:var(--glass-hi);border:1px solid var(--stroke);outline:none;transition:.18s
}
.auth-card input:focus{border-color:var(--a-violet);box-shadow:0 0 0 3px color-mix(in oklab,var(--a-violet) 20%,transparent)}
.auth-go{
  width:100%;margin-top:6px;padding:12px;border-radius:13px;font-size:14.5px;font-weight:650;color:#fff;
  background:linear-gradient(135deg,var(--a-violet),color-mix(in oklab,var(--a-violet) 60%,var(--a-cyan)));
  box-shadow:0 8px 24px -8px var(--a-violet);transition:.18s var(--ease-out)
}
.auth-go:hover:not(:disabled){transform:translateY(-1px);filter:brightness(1.08)}
.auth-go:active{transform:translateY(0) scale(.985)}
.auth-go:disabled{opacity:.6;cursor:default}
.auth-err{
  padding:9px 12px;border-radius:11px;font-size:12.5px;margin-bottom:12px;
  background:color-mix(in oklab,var(--a-rose) 18%,transparent);
  border:1px solid color-mix(in oklab,var(--a-rose) 40%,transparent);color:var(--a-rose)
}
.auth-swap{margin-top:16px;text-align:center;font-size:12.5px;color:var(--text-3)}
.auth-swap button{color:var(--a-cyan);font-weight:600;padding:2px 4px}
.auth-swap button:hover{text-decoration:underline}
.auth-provider{
  width:100%;padding:11px;border-radius:12px;font-size:13.5px;font-weight:600;margin-bottom:9px;
  background:var(--glass-hi);border:1px solid var(--stroke-2);transition:.16s
}
.auth-provider:hover{border-color:var(--text-3)}
.auth-or{display:flex;align-items:center;gap:11px;margin:4px 0 16px;color:var(--text-3);font-size:11.5px}
.auth-or::before,.auth-or::after{content:"";flex:1;height:1px;background:var(--stroke)}

.chip.avatar{
  font-weight:700;font-size:13px;color:#fff;
  background:linear-gradient(135deg,var(--a-violet),var(--a-cyan));border-color:transparent;
  position:relative;overflow:hidden;padding:0
}
.chip.avatar img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  border-radius:inherit;opacity:0;transition:opacity .2s
}
/* Set only once the photo has decoded, so a failed load keeps the initial. */
.chip.avatar.has-photo img{opacity:1}
.chip.avatar.has-photo{color:transparent}

/* ═══ top-bar menus ════════════════════════════════════════════ */
.menu-card{
  position:fixed;z-index:88;min-width:236px;max-width:min(300px,92vw);padding:6px;
  border-radius:15px;background:var(--glass);border:1px solid var(--stroke-2);
  backdrop-filter:blur(24px);box-shadow:var(--shadow-2);
  animation:rise-placed .16s var(--ease-out)
}
.menu-head{padding:9px 11px 10px;display:flex;flex-direction:column;gap:3px}
.menu-head b{font-size:13px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.menu-head i{font-style:normal;font-size:11.5px;line-height:1.5;color:var(--text-3)}
.menu-divider{height:1px;background:var(--stroke);margin:5px 4px}
.menu-item{
  display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;
  padding:9px 11px;border-radius:10px;font-size:13px;font-weight:550;
  color:var(--text-2);text-align:left;transition:.14s
}
.menu-item:hover{background:var(--glass-hi);color:var(--text)}
.menu-item kbd{
  font:inherit;font-size:10.5px;color:var(--text-3);padding:1px 5px;border-radius:5px;
  background:var(--glass-hi);border:1px solid var(--stroke)
}
.menu-item.danger:hover{background:color-mix(in oklab,var(--a-rose) 16%,transparent);color:var(--a-rose)}

/* ═══ storage mode chooser ═════════════════════════════════════ */
/* No X and no dismiss: this one is a fork in the road, not a dialog you
   can put aside — the app cannot boot until it knows where data lives. */
#mode-pick{z-index:95}
.mode-sheet{width:min(440px,94vw);text-align:center}
.mode-sheet .auth-mark{width:31px;height:34px;color:var(--a-violet);--ink:var(--bg);stroke:none;
  filter:drop-shadow(0 0 16px color-mix(in oklab,var(--a-violet) 55%,transparent))}
.mode-sheet h3{margin:10px 0 4px;padding-right:0}
.mode-sheet .muted{margin-bottom:18px}
.mode-card{
  display:flex;align-items:flex-start;gap:13px;width:100%;text-align:left;
  padding:14px;border-radius:15px;margin-bottom:10px;
  background:var(--glass-hi);border:1px solid var(--stroke);transition:.16s var(--ease-out)
}
.mode-card:hover{border-color:var(--a-violet);transform:translateY(-1px)}
.mode-card:active{transform:translateY(0) scale(.99)}
.mode-ico{flex:none;width:36px;height:36px;border-radius:11px;display:grid;place-items:center;
  background:color-mix(in oklab,var(--a-violet) 16%,transparent);color:var(--a-violet)}
.mode-ico svg{width:19px;height:19px}
.mode-text{display:flex;flex-direction:column;gap:3px;min-width:0}
.mode-text b{font-size:13.5px;font-weight:650}
.mode-text i{font-style:normal;font-size:12px;line-height:1.5;color:var(--text-3)}

/* Where your boards live, and how to take them with you. In About because it
   is reachable by tapping the app icon — the palette needs a keyboard nobody
   has on a phone, and a backup you cannot find is not a backup. */
.about-data{border-top:1px solid var(--stroke);margin-top:16px;padding-top:14px}
.about-data-head{margin:0;font-size:11px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--text-3)}
.about-data-note{margin:6px 0 11px;font-size:12.5px;color:var(--text-2);line-height:1.5}
.about-data-actions{display:flex;gap:8px;flex-wrap:wrap}
.about-data-actions button{
  flex:1;min-width:120px;padding:9px 12px;border-radius:11px;font-size:12.5px;font-weight:600;
  background:var(--glass-hi);border:1px solid var(--stroke);transition:.16s
}
.about-data-actions button:hover{border-color:var(--a-violet);color:var(--text)}

/* ═══ destructive confirm ══════════════════════════════════════ */
.danger-box{display:flex;gap:11px;align-items:flex-start;padding:12px 13px;border-radius:14px;
  margin-bottom:14px;font-size:12.5px;line-height:1.5;
  background:color-mix(in oklab,var(--a-rose) 13%,transparent);
  border:1px solid color-mix(in oklab,var(--a-rose) 34%,transparent);color:var(--text)}
.danger-box svg{width:18px;height:18px;flex:none;color:var(--a-rose);margin-top:1px}
.danger-box b{color:var(--a-rose)}

/* House style: switches are toggles, never tickboxes. */
.switch-row{display:flex;align-items:center;gap:14px;justify-content:space-between;
  padding:11px 13px;border-radius:14px;background:var(--glass-hi);
  border:1px solid var(--stroke);margin-bottom:14px;cursor:pointer}
.switch-row span{display:flex;flex-direction:column;gap:3px;min-width:0}
.switch-row b{font-size:13px;font-weight:600}
.switch-row i{font-style:normal;font-size:11.5px;color:var(--text-3);line-height:1.45}
.switch{width:42px;height:25px;border-radius:99px;flex:none;padding:0;position:relative;
  background:var(--stroke-2);transition:background .18s var(--ease-out)}
.switch i{position:absolute;top:3px;left:3px;width:19px;height:19px;border-radius:99px;
  background:var(--text);transition:transform .2s var(--ease-spring)}
.switch[aria-checked="true"]{background:var(--a-rose)}
.switch[aria-checked="true"] i{transform:translateX(17px);background:#fff}

.sheet-go{width:100%;padding:12px;border-radius:13px;font-weight:650;font-size:13.5px;
  background:var(--glass-hi);border:1px solid var(--stroke-2);transition:.16s}
.sheet-go.danger{background:color-mix(in oklab,var(--a-rose) 20%,transparent);
  border-color:color-mix(in oklab,var(--a-rose) 46%,transparent);color:var(--a-rose)}
.sheet-go.danger:hover{background:color-mix(in oklab,var(--a-rose) 32%,transparent);color:#fff}
.sheet-go[disabled]{opacity:.55;pointer-events:none}

/* ═══ about ════════════════════════════════════════════════════ */
/* The brand mark is the button, so it carries no chrome of its own until
   it is reached for — the top bar should still read as a title, not a
   toolbar. */
#about-open{padding:0;border-radius:11px;display:flex;align-items:center;
  flex:none;transition:.18s var(--ease-out)}
#about-open:hover{background:var(--glass-hi);transform:translateY(-1px)}
#about-open:active{transform:translateY(0) scale(.94)}

.about-sheet{width:min(420px,94vw)}
.about-head{display:flex;align-items:center;gap:13px;margin-bottom:14px}
/* Larger than the top-bar mark on purpose: this and the front door are the only
   places that carry the square kufic, and below about 60px the letters close up
   into a smudge — measured, not guessed. */
.about-mark{width:62px;height:68px;flex:none;color:var(--a-violet);--ink:var(--bg);stroke:none;
  filter:drop-shadow(0 0 14px color-mix(in oklab,var(--a-violet) 45%,transparent))}
.about-head h3{margin:0}
.about-ver{margin:2px 0 0;font-size:12.5px;color:var(--text-3);font-variant-numeric:tabular-nums}

/* Release notes scroll: the list only grows, and the sheet should not. */
.about-notes{max-height:min(46vh,320px);overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--stroke-2) transparent}
.about-notes::-webkit-scrollbar{width:6px}
.about-notes::-webkit-scrollbar-thumb{background:var(--stroke-2);border-radius:9px}
.about-rel{padding:11px 0;border-top:1px solid var(--stroke)}
.about-rel:first-child{border-top:0;padding-top:0}
.about-rel-head{display:flex;align-items:baseline;gap:8px;margin-bottom:7px}
.about-rel-v{font-size:13px;font-weight:650;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.about-rel.now .about-rel-v{color:var(--a-violet)}
.about-rel-when{font-size:11px;color:var(--text-3)}
.about-rel ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
.about-rel li{position:relative;padding-left:15px;font-size:12.5px;line-height:1.5;color:var(--text-2)}
.about-rel li::before{content:"";position:absolute;left:3px;top:7px;width:5px;height:5px;
  border-radius:99px;background:var(--a-violet);opacity:.75}

/* view-only members: hide every authoring affordance */
body.read-only #dock,body.read-only #hud,body.read-only .hres,body.read-only .anchor{display:none}
body.read-only .rt,body.read-only .t,body.read-only .name{pointer-events:none}

/* ═══ lawh: a card that is another board ══════════════════════ */
/* No card chrome: a lawh has nothing to show, so there is nothing to put in a
   card. What is left is an icon — and the icon is the button that opens it. */
.node.lawh{--accent:var(--a-blue);background:transparent;border-color:transparent;
  box-shadow:none;backdrop-filter:none}
.node.lawh::after{box-shadow:none}
.node.lawh.sel{border-color:color-mix(in oklab,var(--accent) 60%,transparent)}
.node.lawh .body{padding:0;display:flex;height:100%;overflow:visible}
.lawh-face{
  flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;padding:12px 8px;text-align:center;position:relative;border-radius:var(--r-card);
  transition:background .15s
}
.node.lawh:hover .lawh-face{background:color-mix(in oklab,var(--accent) 10%,transparent)}
.lawh-face:active{transform:scale(.98)}
.lawh-glyph{
  width:min(46px,42%);height:auto;aspect-ratio:1;color:var(--accent);flex:none;
  filter:drop-shadow(0 0 12px color-mix(in oklab,var(--accent) 50%,transparent))
}
/* The board's own title, mirrored here. Renaming it inside the lawh from the top
   bar flows back; a double click on this renames it from the outside. */
.lawh-label{
  flex:none;max-width:100%;font-size:12.5px;font-weight:600;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;outline:none;cursor:text
}
.lawh-label:empty::before{content:'Untitled lawh';color:var(--text-3)}
.lawh-label.editing{cursor:text;color:var(--text);background:var(--glass-hi);
  border-radius:6px;padding:1px 5px;margin:-1px -5px;white-space:pre;overflow:visible}
.lawh-sub{display:block;text-decoration:none;font-size:10.5px;color:var(--text-3)}
/* The mark sits over the icon's shoulder rather than in a header row. */
.lawh-face > .dot{position:absolute;top:8px;left:8px}
/* No grip and no resize corner: the icon is the handle, so grabbing it anywhere
   carries it. `grab` rather than `pointer` says so before the press happens. */
.lawh-face{cursor:grab}
.lawh-face:active{cursor:grabbing}

/* breadcrumb back button */
#crumb{
  width:32px;height:32px;flex:none;border-radius:10px;display:grid;place-items:center;
  color:var(--text-2);background:var(--glass);border:1px solid var(--stroke);
  backdrop-filter:blur(14px);transition:.16s var(--ease-out)
}
#crumb svg{width:17px;height:17px}
#crumb:hover{color:var(--text);border-color:var(--stroke-2);transform:translateX(-2px)}
#crumb:active{transform:translateX(-2px) scale(.94)}

/* social sign-in buttons */
.auth-provider{display:flex;align-items:center;justify-content:center;gap:10px}
.auth-provider svg{flex:none}
#go-home{
  width:32px;height:32px;flex:none;border-radius:10px;display:grid;place-items:center;
  color:var(--text-2);background:var(--glass);border:1px solid var(--stroke);
  backdrop-filter:blur(14px);transition:.16s var(--ease-out)
}
#go-home svg{width:16px;height:16px}
#go-home:hover{color:var(--text);border-color:var(--stroke-2)}
#go-home:active{transform:scale(.94)}
