/* The Hologram pages (board, home, changelog): layout only, after ui.css and links.css (ADR 0023 U2).
   The tokens, the font, the focus ring, reduced motion and forced colours are ui.css's. */
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; font-size: 13px; }
/* No page zoom on a board: a pinch off the canvas pans a panel at most (the canvas has its own
   touch-action). Home and the changelog zoom like any page (ADR 0023 decision 7). */
html:has(.app), html:has(.app) body, .app { touch-action: pan-x pan-y; }
html { -webkit-tap-highlight-color: transparent; }
a { color: var(--accent); }
.app { position: relative; height: 100vh; height: 100dvh; width: 100vw; overflow: hidden; }
.stage { position: absolute; inset: 0; }
.board { position: absolute; inset: 0; width: 100%; height: 100%; background-color: var(--bg); touch-action: none; user-select: none; cursor: default; }
/* The first camera (ADR 0023 §6, the plan's departure 12): the server paints the default camera and
   the boot moves it, so the board stays hidden until data-ready, 800 ms at most. A timer, not
   motion: it keeps its delay under reduced motion, which zeroes every other one. */
#board[data-ready="0"] { visibility: hidden; animation: board-show 0s 800ms forwards !important; }
@keyframes board-show { to { visibility: visible; } }
/* The dot grid lives in screen space: board.js publishes one tile (--grid-size, the
   coarse pitch) for four dot layers, their positions (--grid-pos: three fine layers at
   the half offsets, then the coarse one), how far the fine dots have faded in (--af)
   and whether the grid shows at all (--ag), so the dots keep their size at every zoom. --dot is
   a colour (ui.css), and the two fades mix into its alpha. */
.board { --fine-dot: radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--dot) calc(var(--ag, 1) * var(--af, 1) * 100%), transparent) 1px, transparent 1.25px);
  background-image: var(--fine-dot), var(--fine-dot), var(--fine-dot), radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--dot) calc(var(--ag, 1) * 100%), transparent) 1px, transparent 1.25px);
  background-size: var(--grid-size, 48px 48px);
  background-position: var(--grid-pos, 24px 0, 0 24px, 24px 24px, 0 0); }
.board.hand { cursor: grab; }
.board.panning { cursor: grabbing; }
.board.tool { cursor: crosshair; }
/* the eraser in hand looks like one (09-A3), its hot spot where it meets the line; dark with a light edge on any paper */
.board.tool.eraser { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 256 256'%3E%3Cpath d='M225,80.4,183.6,39a24,24,0,0,0-33.94,0L31,157.66a24,24,0,0,0,0,33.94l30.06,30.06A8,8,0,0,0,66.74,224H216a8,8,0,0,0,0-16h-84.7L225,114.34A24,24,0,0,0,225,80.4ZM108.68,208H70.05L42.33,180.28a8,8,0,0,1,0-11.31L96,115.31,148.69,168Zm105-105L160,156.69,107.31,104,161,50.34a8,8,0,0,1,11.32,0l41.38,41.38a8,8,0,0,1,0,11.31Z' fill='%231f2937' stroke='%23fff' stroke-width='22' paint-order='stroke' stroke-linejoin='round'/%3E%3C/svg%3E") 6 21, crosshair; }
/* One cursor vocabulary. The select tool promises a move only where a drag
   would move something; a shape never contradicts the tool in hand, so the hand
   and the creation tools own the cursor over shapes too; and a live move drag
   holds grabbing across the whole stage, so it cannot flicker as the pointer
   crosses other shapes. */
.shape { cursor: default; }
.shape.selected { cursor: move; }
.shape.locked { cursor: default; }
.board.hand .shape, .board.hand .shape *, .board.tool .shape, .board.tool .shape * { cursor: inherit; }
.board.tool-text .shape.text-kind, .board.tool-text .shape.text-kind *, .board.tool-text .shape.markdown-kind, .board.tool-text .shape.markdown-kind *, .board.tool-text .shape.sticky-kind, .board.tool-text .shape.sticky-kind * { cursor: text; }
.stage.dragging, .stage.dragging * { cursor: grabbing !important; }
/* No blanket fill here: a CSS rule beats the fill attribute the page computes
   per shape, which is how every box ended up with #1f2937 text. */
.shape text { pointer-events: none; white-space: normal; }
.shape tspan { white-space: pre; }
/* Rich text: one block per source line, on the canvas (flat divs from Charta.Markdown)
   and in the editor (rich_editor.mjs) alike, with one set of metrics, so the box is
   the same height either way. Bullets, numbers, task boxes, line numbers and the
   language of a code block are drawn from CSS variables, not written as text. Only a
   link or the Copy label of a code block takes the pointer on the canvas. */
.md { box-sizing: border-box; width: 100%; line-height: 1.4; overflow-wrap: break-word; font-family: inherit; }
/* pre-wrap on the lines alone: the whitespace the template leaves between blocks must collapse */
.md > * { white-space: pre-wrap; }
.shape .md { pointer-events: none; }
/* A prototype page on the canvas: a title bar and the page itself, live but inert -
   every press goes to the board, so the card drags and selects like an image. */
.pgwrap { box-sizing: border-box; width: 100%; height: 100%; display: flex; flex-direction: column; overflow: hidden; border-radius: 4px; pointer-events: none; }
.pgbar { flex: none; height: 24px; line-height: 24px; padding: 0 8px; font-size: 12px; font-weight: 600; color: var(--muted); background: var(--hover); border-bottom: 1px solid var(--line); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pgwrap iframe.pg { flex: 1; width: 100%; border: 0; background: #fff; pointer-events: none; }
.shape .pgname { fill: var(--muted); font-weight: 600; pointer-events: none; }
/* A cell (ADR 0017): a card drawn from its data - a header with its actions, the
   output or the code, and a footer saying who ran it - over its body rect. Only
   the header buttons take the pointer; every other press goes to the shape. The
   left border is the state stripe: blue running, red failed, amber stale. */
.cellcard { box-sizing: border-box; width: 100%; height: 100%; display: flex; flex-direction: column; overflow: hidden; border-left: 3px solid transparent; border-radius: 8px 0 0 8px; pointer-events: none; color: #1f2937; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
.cellhead { flex: none; height: 28px; display: flex; align-items: center; gap: 6px; padding: 0 8px 0 7px; border-bottom: 1px solid #efede6; }
.celltitle { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #52514e; }
.cellhead button { pointer-events: auto; font: 500 11px/1 var(--font); padding: 4px 8px; min-height: 24px; }
.cellstate { font: 11px Inter, system-ui, sans-serif; color: #2563eb; white-space: nowrap; }
.cellbody { flex: 1; min-height: 0; overflow: hidden; padding: 6px 8px 0 7px; }
.cellpre, .cellcode { margin: 0; font: inherit; white-space: pre-wrap; overflow-wrap: anywhere; }
.cellcode { color: #52514e; }
.cellerr { color: #b91c1c; }
.cellmore { font: 11px Inter, system-ui, sans-serif; color: #6b7280; margin-bottom: 4px; }
.celltab { border-collapse: collapse; font-size: 11px; }
.celltab th, .celltab td { padding: 1px 10px 1px 0; border-bottom: 1px solid #efede6; text-align: left; white-space: nowrap; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }
.celltab th { font-weight: 600; color: #6b7280; }
.cellfoot { flex: none; padding: 4px 8px 6px 7px; font: 11px/1.3 Inter, system-ui, sans-serif; color: #6b7280; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shape .cellname { fill: var(--muted); font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; pointer-events: none; }
.shape .cellpic { pointer-events: none; }
.cellaccess .lead { margin: 2px 4px 6px; color: var(--muted); font-size: 12px; max-width: 260px; }
.cellaccess input:not([type=checkbox]), .cellaccess select { font-size: 12px; padding: 4px 6px; }
.cellaccess select { padding-right: 22px; }
.cellaccess input:not([type]) { width: 200px; }
.cellaccess input[type=number] { width: 56px; }
.cellaccess .row { gap: 6px; padding: 2px 4px; font-size: 12px; }
.md > * { margin: 0; min-height: 1.4em; }
.md .h1 { font-size: 1.6em; font-weight: 700; line-height: 1.2; min-height: 1.2em; }
.md .h2 { font-size: 1.3em; font-weight: 700; line-height: 1.25; min-height: 1.25em; }
.md .h3 { font-size: 1.1em; font-weight: 700; line-height: 1.3; min-height: 1.3em; }
/* Nothing inside a foreignObject may get a layer of its own: Safari 18.6 paints a
   positioned, masked or translucent element there without the board transforms, at the
   top left of the window and at 1x, and takes its clicks there. So the bullet, number,
   task box, line number and Copy label are floats with a margin box of no width, which
   leave the text where the old absolute ones did; a line holding one is its own block
   formatting context, so the float never reaches the next line; and a fade is a colour. */
.md .li { display: flow-root; padding-left: calc(var(--d, 0) * 1.5em + 1.5em); }
/* a number wider than its column (10.) stays on one line and grows to the left, as flex-end does; a list
   that counts to 100 (markdown.mjs `wide`) takes a wider column on every item, or at depth 0 the 1 of 100.
   would reach past the shape's padding and be cut (09-C1) */
.md .li::before { content: var(--m, '•'); float: left; display: flex; justify-content: flex-end; white-space: nowrap; width: 1.5em; margin-left: -1.5em; padding-right: 0.4em; box-sizing: border-box; color: color-mix(in srgb, currentColor 80%, transparent); }
.md .li.wide { padding-left: calc(var(--d, 0) * 1.5em + 2.2em); }
.md .li.wide::before { width: 2.2em; margin-left: -2.2em; }
.md .li.task::before { content: ''; width: 0.85em; height: 0.85em; margin: 0.3em 0.45em 0 -1.3em; padding: 0; border: 1.5px solid light-dark(#00000066, #ffffff80); border-radius: 3px; }
.md .li.task.done::before { background: color-mix(in srgb, var(--accent) 70%, transparent) url(data:image/svg+xml;utf8,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20viewBox=%270%200%2014%2014%27%3E%3Cpath%20d=%27M3.5%207.2l2.3%202.3%204.7-4.7%27%20fill=%27none%27%20stroke=%27%23fff%27%20stroke-width=%272%27/%3E%3C/svg%3E) center / 90% no-repeat; border-color: transparent; }
/* A fade is mixed into the fill of the box (--paper), so it stays one solid colour: a see-through
   line-through would darken where it crosses the see-through letters. --link fades every link
   in the line, the [[links]] board.js draws as well. */
.md .li.done { text-decoration: line-through; color: color-mix(in srgb, currentColor 70%, var(--paper, transparent)); --link: color-mix(in srgb, var(--accent) 70%, var(--paper, transparent)); }
.md .q { border-left: 3px solid light-dark(#0000002b, #ffffff38); padding-left: 0.7em; color: color-mix(in srgb, currentColor 85%, var(--paper, transparent)); --link: color-mix(in srgb, var(--accent) 85%, var(--paper, transparent)); }
.md .fence, .md .code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em; background: light-dark(#0000000d, #ffffff0f); padding: 0 0.6em; min-height: 1.4em; }
.md .fence.open { padding-top: 0.3em; border-radius: 4px 4px 0 0; color: light-dark(#6b7280, #a6a298); }
.md .fence.open::after { content: var(--lang, 'code'); font: 600 0.8em/1.75 system-ui, sans-serif; }
.md .fence.close { min-height: 0.5em; padding-bottom: 0.3em; border-radius: 0 0 4px 4px; }
/* the number is a column of its own, so a wrapped line continues under the text */
.md .code.numbered { display: flow-root; padding-left: 3.8em; }
.md .code.numbered::before { content: var(--ln); float: left; width: 2.6em; margin: 0 0.6em 0 -3.2em; text-align: right; color: color-mix(in srgb, currentColor 45%, transparent); }
/* the label floats right, and its margin-left of -100% leaves it a margin box of no width, so the language beside it never moves */
.md .fence > .m { float: right; margin: -0.1em -0.4em 0 -100%; font: 600 0.75em/1 system-ui, sans-serif; padding: 0.35em 0.55em; border-radius: 4px; background: light-dark(#ffffff8f, #0000004d); color: light-dark(#52514eb3, #bdb9afb3); pointer-events: auto; cursor: pointer; user-select: none; }
.md .fence > .m:hover { background: light-dark(#ffffffcc, #00000080); color: light-dark(#52514e, #bdb9af); }
/* the string and comment tokens share their class with struck text (.s) and inline code (.c); on a dark
   card each takes a lighter twin (report 10 section 4.4) */
.md .code .k { color: light-dark(#7c3aed, #c4b5fd); } .md .code .s { color: light-dark(#15803d, #86efac); text-decoration: none; } .md .code .c { color: light-dark(#6b7280, #a6a298); font-style: italic; font-size: inherit; background: none; padding: 0; }
.md .code .n { color: light-dark(#b45309, #fdba74); } .md .code .a { color: light-dark(#0e7490, #67e8f9); } .md .code .t { color: light-dark(#1d4ed8, #93c5fd); }
/* the rule is a 1px background at half the height of the line: no layout, no layer */
.md .hr { background: linear-gradient(light-dark(#0003, #fff3), light-dark(#0003, #fff3)) 0 calc(50% + 0.5px) / 100% 1px no-repeat; }
.md .b { font-weight: 700; }
.md .i { font-style: italic; }
.md .s { text-decoration: line-through; }
.md .c { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em; background: light-dark(#0000000d, #ffffff0f); border-radius: 3px; padding: 0 0.25em; }
.md a { color: var(--link, var(--accent)); text-decoration: underline; }
.shape .md a { pointer-events: auto; }
/* a [[link]] (ADR 0018) is text until board.js finds its note in the Space */
.shape .md a[data-wiki] { color: inherit; text-decoration: none; pointer-events: none; }
/* a task line takes the pointer so its box can be ticked; the press lands on the shape otherwise */
.shape .md .li.task { pointer-events: auto; }
.shape .md .li.task::before { cursor: pointer; }
/* A note placed on the board (ADR 0018): a header row of SVG above its text, the text cut at
   the bottom of the card under a fade and moved only by the wheel (board.js), a table or
   callout one quiet line whose other lines stay as hidden blocks, and far out the name at
   13 px on screen, the --iz trick of frame titles. Nothing in a card takes the pointer but
   Open and the name, whose presses board.js catches: its links read as text, the Copy label
   of a code block goes, and task boxes show the state of the note (the id outweighs the
   resolved-link rule board.js adopts later). As everywhere in a foreignObject nothing in the
   card is positioned or masked: the fade is an SVG gradient drawn over it, and the spacer
   lets the last lines scroll up out of the fade without adding a line. */
.shape .nglyph path { fill: var(--muted); }
.shape .nhead use, .shape .nplate use { font-weight: 600; fill: var(--ink); }
.shape .nname > rect { fill: none; pointer-events: all; }
.shape .nopen rect { fill: light-dark(#0000000a, #ffffff14); pointer-events: all; cursor: pointer; }
.shape .nopen:hover rect { fill: light-dark(#0000001a, #ffffff29); }
.shape .nopen text { fill: var(--accent); font-weight: 600; font-size: 0.85em; }
.shape .fade { pointer-events: none; }
.md.card { height: 100%; overflow: hidden; }
.md.card::after { content: ""; display: block; height: 2.5em; }
.md .quiet { color: var(--muted); font-style: italic; }
/* a note's ==highlight== on its card, as the reader marks it (markdown.mjs `card`) */
.md .h { background: var(--mark); border-radius: 2px; }
.md .gone { display: none; }
#board .shape .md.card a, #board .shape .md.card .li.task { pointer-events: none; }
#board .shape .md.card a { color: inherit; text-decoration: none; }
#board .shape .md.card .m { display: none; }
.shape .nplate { --on: clamp(0, calc((var(--h) * var(--cam-z, 1) - 24) * 100), 1); }
.shape .nplate use { font-size: 13px; transform-box: view-box; transform-origin: 0 0; transform: scale(calc(var(--iz, 1) * var(--on, 1))); }
/* The editor: as tall as its content, never shorter than the box it edits (the .editor
   rule further down fixes a height; two classes outweigh it). The line the caret is on
   shows its markers raw, and the bullet or label those stand for steps aside. */
.editor.md-editor { height: auto; min-height: calc(var(--eh) * var(--cam-z, 1) * 1px); overflow: visible; line-height: 1.4; outline: none; }
.editor.md-editor:focus { outline: none; }
.md-editor .mk { display: none; }
.md-editor .line.on .mk { display: inline; opacity: 0.45; }
.md-editor .line.on.fence::after, .md-editor .line.on.li::before { display: none; }
.md-editor .line.on.hr { background: none; }
.md-editor .line.on.li { padding-left: calc(var(--d, 0) * 0em); }
.md-editor .line.on.code .mk { opacity: 1; }
.md-tools { left: max(8px, min(calc((var(--ex) - var(--cam-x, 0)) * var(--cam-z, 1) * 1px), calc(100% - var(--tw, 0px) - 8px))); max-width: calc(100% - 16px); flex-wrap: wrap; top: calc((var(--ey) - var(--cam-y, 0)) * var(--cam-z, 1) * 1px - 42px); z-index: 6; }
.md-tools button { font-size: 12px; font-weight: 600; min-width: 28px; justify-content: center; padding: 6px 6px; }
.md-tools button.active { background: var(--accent-soft); color: var(--accent-strong); }
/* one toolbar above the box at a time: the shape toolbar waits while the text is edited */
.stage.md-editing > .ctx { display: none; }
/* A frame title is 14 px on screen at every zoom - 3.5 px at 25% is what
   a board of frames looked like right after Fit - with its baseline
   8 px above the top edge: the --iz trick the lock badge uses, scaled
   about the top-left corner of the frame. --on drops the title when the
   frame is under 24 px tall, the way the handles drop off, and the clip
   path in the markup holds it to the width of the frame. */
.shape.frame-kind .ftitle { font-weight: 600; fill: var(--muted);
  --on: clamp(0, calc((var(--h) * var(--cam-z, 1) - 24) * 100), 1);
  transform-box: view-box; transform-origin: 0 0;
  transform: scale(calc(var(--iz, 1) * var(--on, 1))); fill-opacity: var(--on, 1); }
.shape .body { transition: filter var(--fast); }
/* What the pointer is on, before the click: a thin outline and nothing else -
   no badges, no buttons. Only at rest, with the select tool in hand, and never
   on a touch screen, where there is no hover to speak of. */
@media (hover: hover) and (pointer: fine) {
  .board[data-drag=""]:not(.tool):not(.hand) g.shape:not(.selected):not(.locked):hover > .body { stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
  .board[data-drag=""]:not(.tool):not(.hand) g.shape.arrow:not(.selected):not(.locked):hover .line { stroke-width: 3; }
}
/* One shadow, for the life of the note: two filters on a wall of stickies
   is two composites each, and a selection that replaces it with a
   zero-blur nothing flattens the note the moment you click it - the ring
   and the handles already say what is selected. */
.shape.sticky-kind .body { filter: drop-shadow(0 1px 3px light-dark(#0000001f, #00000080)); }
/* A shape drawn at low detail (too small on screen for its shadow to
   read) composites nothing: a wall of tiny stickies is one paint. */
.shape.lo .body { filter: none; transition: none; }
/* A dragged shape lifts off the board: paint only, so its bounds - and
   everything read off them - are exactly what they were at rest. */
.shape.lifting .body { filter: drop-shadow(0 2px 4px #00000026) drop-shadow(0 12px 20px #0000001c); }
/* The copies an Alt-drag moves, until they are dropped. */
.shape.ghost { opacity: 0.5; }
.board[data-drag="move"] circle.port, .board[data-drag="resize"] circle.port { pointer-events: none; }
/* Selection chrome keeps its on-screen size at every zoom. board.js keeps the live
   camera on the stage as --cam-x, --cam-y, --cam-z and --iz (1 / zoom), updated every
   frame, so none of this waits for a Hologram render. Handles, endpoints and ports scale
   by --iz around their own centre; the lock badge around the corner of the shape. */
.handle, .shape .endpoint, .shape .port, .shape .waypoint, .shape .midpoint { transform-box: fill-box; transform-origin: center; transform: scale(var(--iz, 1)); }
/* A connection dot sits on the outline in board units and is pushed
   14 screen px out along its own normal from here, so half its target
   is not inside the shape: pressing the middle of an edge to move the
   shape used to start a connector instead, which is the most repeated
   complaint about connectors in Miro. */
.shape .port { transform: scale(var(--iz, 1)) translate(var(--ox, 0px), var(--oy, 0px)); }
.shape .port-t { --oy: -14px; }
.shape .port-r { --ox: 14px; }
.shape .port-b { --oy: 14px; }
.shape .port-l { --ox: -14px; }
.shape .lock { fill: var(--muted); transform-box: view-box; transform-origin: 0 0; transform: scale(var(--iz, 1)); }
/* The selection ring and highlight sit a fixed number of pixels (--gap) outside the
   shape: the rect has the bounds of the shape (--w, --h) and is stretched about its centre. */
.shape .ring { transform-box: fill-box; transform-origin: center; transform: scale(calc(1 + 2 * var(--gap) * var(--iz, 1) / var(--w)), calc(1 + 2 * var(--gap) * var(--iz, 1) / var(--h))); }
.shape .outline { --gap: 2; fill: none; stroke: var(--accent); stroke-width: 1.5; pointer-events: none; vector-effect: non-scaling-stroke; }
/* --- the transform box ------------------------------------------------------
   Four corner squares, each with a bigger transparent grip over it, and an
   invisible strip along each side. Board units in the markup, fixed screen
   sizes here: 10 px of drawing, 18 px of hit area (24 on a touch screen),
   and 10 px of strip straddling the side. --on is the level of detail,
   stepped off the live zoom board.js publishes as --cam-z: a side is worth
   a strip at 40 screen px, the three far corners at 16, the bottom-right
   one at 8, and under that the shape is move-only. Scaling to 0 takes the
   hit area with it; the opacity hides the stroke, which does not scale. */
.handle { fill: var(--panel); stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.grip { fill: transparent; stroke: none; pointer-events: all; transform-box: fill-box; }
.corner { --on: clamp(0, calc((min(var(--w), var(--h)) * var(--cam-z, 1) - 16) * 100), 1); }
.corner-br { --on: clamp(0, calc((min(var(--w), var(--h)) * var(--cam-z, 1) - 8) * 100), 1); }
.handle.corner { transform: scale(calc(var(--iz, 1) * var(--on, 1))) translate(var(--ox, 2px), var(--oy, 2px)); stroke-opacity: var(--on, 1); fill-opacity: var(--on, 1); }
/* --gk keeps the grip off the middle of a small shape: the 18 px hit
   area reaches 7 px inside the corner, which is more than half of a
   15 px shape, and then there is nothing left to grab it by. It goes
   back down to the 10 px of the drawing under 22 screen px. */
.grip.corner { --gk: clamp(0.5556, calc(min(var(--w), var(--h)) * var(--cam-z, 1) / 22), 1); --ck: calc(var(--iz, 1) * var(--on, 1) * var(--gk)); transform-origin: center; transform: scale(var(--ck)) translate(var(--ox, 2px), var(--oy, 2px)); }
@media (pointer: coarse) { .grip.corner { --ck: calc(var(--iz, 1) * var(--on, 1) * var(--gk) * 1.334); } }
.corner-tl { --ox: -2px; --oy: -2px; cursor: nwse-resize; }
.corner-tr { --ox: 2px; --oy: -2px; cursor: nesw-resize; }
.corner-bl { --ox: -2px; --oy: 2px; cursor: nesw-resize; }
.corner-br { --ox: 2px; --oy: 2px; cursor: nwse-resize; }
/* Each side is two strips, and --sh takes 10 screen px off the inner
   end of each: the 20 px in the middle belong to the connection dot,
   which board.js adds to a group the template also fills, where a
   later patch can put the strips back on top of it. --tk is the
   thickness, 10 screen px (14 on a touch screen). */
.grip.side { transform-origin: center; --tk: calc(var(--iz, 1) * var(--on, 1)); }
@media (pointer: coarse) { .grip.side { --tk: calc(var(--iz, 1) * var(--on, 1) * 1.4); } }
.side-t, .side-b { --on: clamp(0, calc((var(--w) * var(--cam-z, 1) - 40) * 100), 1); --sh: max(0, 1 - 20 / (var(--w) * var(--cam-z, 1))); cursor: ns-resize; }
.side-l, .side-r { --on: clamp(0, calc((var(--h) * var(--cam-z, 1) - 40) * 100), 1); --sh: max(0, 1 - 20 / (var(--h) * var(--cam-z, 1))); cursor: ew-resize; }
.side-t.half-a, .side-b.half-a { transform-origin: left center; transform: scaleY(var(--tk)) scaleX(var(--sh)); }
.side-t.half-b, .side-b.half-b { transform-origin: right center; transform: scaleY(var(--tk)) scaleX(var(--sh)); }
.side-l.half-a, .side-r.half-a { transform-origin: center top; transform: scaleX(var(--tk)) scaleY(var(--sh)); }
.side-l.half-b, .side-r.half-b { transform-origin: center bottom; transform: scaleX(var(--tk)) scaleY(var(--sh)); }
/* The cursor a resize started with holds for the whole drag, wherever the
   pointer goes - the trick .stage.dragging uses for a move. Which cursor is
   a variable, so the drag adds only one rule with a universal tail: those
   are matched against every element on the page on every style pass. */
.stage.resizing, .stage.resizing * { cursor: var(--rzc, default) !important; }
.stage.resizing-nwse { --rzc: nwse-resize; }
.stage.resizing-nesw { --rzc: nesw-resize; }
.stage.resizing-ns { --rzc: ns-resize; }
.stage.resizing-ew { --rzc: ew-resize; }
.shape .endpoint { fill: var(--panel); stroke: var(--accent); stroke-width: 2; cursor: crosshair; vector-effect: non-scaling-stroke; }
.shape .port { fill: var(--panel); stroke: var(--accent); stroke-width: 1.5; cursor: crosshair; vector-effect: non-scaling-stroke; }
.shape .port:hover { fill: var(--accent); }
/* waypoints (white, drag to move, double-click to remove) and span middles (blue, drag to add one) */
.shape .waypoint { fill: var(--panel); stroke: var(--accent); stroke-width: 2; cursor: move; vector-effect: non-scaling-stroke; }
.shape .midpoint { fill: var(--accent); stroke: #fff; stroke-width: 1.5; cursor: grab; vector-effect: non-scaling-stroke; }
.shape .midpoint:hover { fill: var(--accent-strong); }
/* While an arrow or an arrow end is dragged, arrows let the pointer through to the boxes
   beneath, and the box it would dock to is outlined. */
.board.arrow-drag .shape.arrow, .board.arrow-drag .shape.arrow * { pointer-events: none; }
.shape.drop-target .body { stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.shape.arrow .hit { fill: none; stroke: transparent; stroke-width: 16; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.shape.arrow .line { fill: none; stroke-linecap: round; stroke-linejoin: round; }
/* Ink (ADR 0009): the hit band is 8 px either side at any zoom, and the line
   itself lets the pointer through, so a press inside the box of a stroke but
   off the ink lands on the canvas. A selected stroke wears the same halo as a
   connector under its ring. */
.shape.ink-kind .hit { fill: none; stroke: transparent; stroke-width: 16; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* a stroke wider on screen than the band (a highlighter, a broad pen zoomed in) is hit anywhere on its ink, with 4 px of halo to spare */
.shape.ink-kind .hit { stroke-width: max(16px, calc(var(--sw, 0) * var(--cam-z, 1) * 1px + 8px)); }
.shape.ink-kind .line { fill: none; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
.shape.ink-kind.selected .hit { stroke: light-dark(#2563eb40, #6ea2ff40); }
/* A selected connector keeps the colour you gave it: the selection is the
   halo the hit path paints under the line, not a repaint of the line. */
.shape.arrow.selected .hit { stroke: light-dark(#2563eb40, #6ea2ff40); }
/* The label sits on the line, which is clipped out under it: no halo,
   because a halo can only be one colour and over a sticky, a frame or
   an image that colour is wrong. */
.shape.arrow .alabel { font-size: 12px; fill: light-dark(#52514e, #bdb9af); }
/* An agent highlight pulses three times and settles, so a long one never flashes forever. */
.hl { --gap: 5; fill: none; stroke: #f59e0b; stroke-width: 4; stroke-opacity: 0.85; pointer-events: none; animation: hlpulse 0.9s ease-in-out 3 alternate; vector-effect: non-scaling-stroke; }
@keyframes hlpulse { from { stroke-opacity: 0.25; } to { stroke-opacity: 1; } }
/* One dashed box marks a multi-selection as a single thing to grab: it sits
   4 screen px outside the union of what is selected (the --iz mechanism the
   rings use), takes no pointer events, and puts the per-shape rings back to a
   hint while it is up. */
.selbox { --gap: 4; fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-dasharray: 4 3; pointer-events: none; vector-effect: non-scaling-stroke;
  transform-box: fill-box; transform-origin: center;
  transform: scale(calc(1 + 2 * var(--gap) * var(--iz, 1) / var(--w)), calc(1 + 2 * var(--gap) * var(--iz, 1) / var(--h))); }
.board.multi .shape .outline { stroke-width: 1; stroke-opacity: 0.6; }
/* 15%: at 8% the box was the faintest of any board, and over a white shape
   it was not there at all. */
.marquee { fill: light-dark(#2563eb26, #6ea2ff26); stroke: var(--accent); stroke-width: 1; vector-effect: non-scaling-stroke; pointer-events: none; }
/* Cmd/Ctrl asks for full containment; the dashes say the box is picky now. */
.marquee.strict { stroke-dasharray: 4 3; fill: light-dark(#2563eb14, #6ea2ff14); }
#board[data-native-marquee="1"] .outline:not(.native-outline),
#board[data-native-marquee="1"] .handle,
#board[data-native-marquee="1"] .grip,
#board[data-native-marquee="1"] .endpoint,
#board[data-native-marquee="1"] .midpoint,
#board[data-native-marquee="1"] .waypoint,
#board[data-native-marquee="1"] > .selbox,
#board[data-native-marquee="1"] ~ .ctx { display: none; }
/* The connector a drag would leave behind IS the connector: same
   geometry, same stub, same head and width, in accent blue - dashed
   while the end is free, solid the moment it would attach. The mark
   on the attach point keeps one size on screen, like the dots. */
.preview { fill: none; stroke: var(--accent); stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
.attach { fill: var(--accent); stroke: #fff; stroke-width: 1.5; pointer-events: none; vector-effect: non-scaling-stroke;
  transform-box: fill-box; transform-origin: center; transform: scale(var(--iz, 1)); }
.cursors { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 3; }
/* Smart guides, in an overlay svg board.js keeps inside .cursors: every
   child of the board svg belongs to Hologram, and a re-render mid-drag
   would drop them. It shares the board viewBox, so the lines are drawn in
   board units and held at a pixel by non-scaling-stroke. */
#guides { position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; }
/* Copies a duplicate or a paste is about to make, drawn at once by board.js
   while the model and the canvas catch up behind them. */
#previews { position: absolute; inset: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; }
#previews .ghost { opacity: 0.85; }
#previews .ghost .outline, #previews .ghost .handle, #previews .ghost .grip, #previews .ghost .endpoint,
#previews .ghost .waypoint, #previews .ghost .midpoint, #previews .ghost .port, #previews .ghost .hl { display: none; }
#previews .sil { stroke-width: 1; vector-effect: non-scaling-stroke; }
#guides line { vector-effect: non-scaling-stroke; stroke-width: 1; }
#guides .gline, #guides .gmark { stroke: var(--accent); }
#guides .gline { stroke-dasharray: 5 4; }
/* equal gaps get their own tint, so a distance never reads as an alignment */
#guides .ggap { stroke: light-dark(#9333ea, #c084fc); }
/* What the box measures while it is being resized, in whole board units:
   an HTML pill in the same overlay, so it keeps one size at every zoom. */
.dims { position: absolute; left: 0; top: 0; padding: 2px 7px; border-radius: 9px; background: var(--accent); color: var(--on-accent); font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; box-shadow: 0 1px 3px #00000033; }
/* Remote cursors: board.js sets --cx/--cy (board units, registered as
   numbers) and the live camera does the rest, so they pan and zoom with
   the board in the same frame. Samples arrive every 60 ms or so; the
   transition on the two numbers slides a cursor from one to the next
   instead of jumping there, while the camera terms stay instant. */
.rcursor { position: absolute; left: 0; top: 0; display: flex; align-items: flex-start; gap: 2px; will-change: transform;
  transform: translate(calc((var(--cx, 0) - var(--cam-x, 0)) * var(--cam-z, 1) * 1px), calc((var(--cy, 0) - var(--cam-y, 0)) * var(--cam-z, 1) * 1px));
  transition: --cx 100ms linear, --cy 100ms linear; }
/* Less motion keeps this one: the slide only joins samples 60 ms apart, where the cursor goes anyway
   (ui.css zeroes every other transition). */
@media (prefers-reduced-motion: reduce) { .rcursor { transition-duration: 100ms !important; } }
.rcursor svg { display: block; overflow: visible; }
.rcursor .name { color: #fff; font-size: 12px; font-weight: 500; border-radius: 9px; padding: 1px 7px; margin-top: 10px; white-space: nowrap; max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
.rcursor.agent .dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--panel); margin: -7px 0 0 -7px; }
.rcursor.agent .name { margin-top: -9px; }
.shape .port { pointer-events: all; }
/* A file dragged over the board, and the space an image being uploaded
   will fill: board units from board.js, placed by the live camera the way
   the editor is. */
.board.file-over { outline: 2px dashed var(--accent); outline-offset: -10px; }
.holding { position: absolute; border: 2px dashed var(--accent); border-radius: 4px; background: light-dark(#2563eb14, #6ea2ff14);
  left: calc((var(--hx) - var(--cam-x, 0)) * var(--cam-z, 1) * 1px);
  top: calc((var(--hy) - var(--cam-y, 0)) * var(--cam-z, 1) * 1px);
  width: calc(var(--hw) * var(--cam-z, 1) * 1px);
  height: calc(var(--hh) * var(--cam-z, 1) * 1px); }

.float { position: absolute; background: var(--panel); border-radius: 10px; box-shadow: var(--shadow); display: flex; align-items: center; gap: 2px; padding: 4px; z-index: 5; }
.float button { border: 0; background: transparent; border-radius: 6px; padding: 6px 8px; display: inline-flex; align-items: center; gap: 6px; line-height: 1; min-height: 30px; }
@media (hover: hover) { .float button:hover { background: var(--hover); } }
@media (hover: none) { .float button:active { background: var(--hover); } }
/* a button that shows what it does: the line style it would set */
.float button svg { display: block; }
.float button.active { background: var(--accent-soft); color: var(--accent-strong); }
.float button.off { opacity: var(--disabled); cursor: default; }
.float .sep { width: 1px; height: 20px; background: var(--line); margin: 0 4px; }
.topleft { top: 12px; left: 12px; }
.topleft .logo { display: inline-flex; align-items: center; min-height: 30px; padding: 0 8px; text-decoration: none; }
.topleft input.title { border: 0; background: transparent; padding: 6px 8px; border-radius: 6px; width: 220px; font-size: 13px; font-weight: 500; text-overflow: ellipsis; }
.topleft input.title:focus { background: var(--hover); }
@media (hover: hover) { .topleft input.title:hover { background: var(--hover); } }
.topleft .home-space { display: inline-block; line-height: 30px; max-width: 180px; padding: 0 8px; border-radius: 6px; color: var(--muted); font-size: 13px; text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (hover: hover) { .topleft .home-space:hover { background: var(--hover); color: var(--ink); } }
.po { width: 320px; max-width: calc(100vw - 24px); background: var(--panel); color: var(--ink); border: 0; border-radius: 10px; box-shadow: var(--shadow); padding: 14px 16px 12px; font-size: 13px; line-height: 1.5; margin: 0; position: fixed; inset: auto; }
.po:popover-open { display: block; }
.po h4 { margin: 0 0 6px; font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 7px; }
.po h4 .ic { width: 16px; height: 16px; color: var(--green-ink); }
.po p { margin: 0 0 8px; color: var(--muted); }
.po .acts { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin-top: 10px; }
.po .lnk { color: var(--muted); font-size: 12.5px; }
/* ADR 0018 draws the board in a Space inside a frame, where a popover
   anchored to the top layer still belongs to this document. */
.po::backdrop { background: transparent; }

/* A board opened with a read link: the tools go away and one pill says why. */
.reading-only .tools, .reading-only .ctx, .reading-only #board-menu { display: none; }
/* nobody is in this board, so there is nothing here to share or to let a program into */
.shut #open-share, .shut #open-agents { display: none; }
/* below the bar, which a reading tab still has: Share, the title, who is here */
.reading { position: absolute; top: 62px; left: 50%; transform: translateX(-50%); z-index: 9; }
.reading button { display: inline-flex; gap: 8px; align-items: center; background: var(--ink); color: var(--on-ink); border: 0; border-radius: 999px; padding: 6px 14px; }
.reading u { text-underline-offset: 2px; }

.sealnote { top: 60px; left: 50%; transform: translateX(-50%); max-width: 560px; padding: 8px 14px; display: flex; gap: 8px; align-items: center; background: var(--danger-strong); color: var(--on-ink); font-weight: 500; }
.sealnote .ic { flex: none; width: 16px; height: 16px; }
/* ADR 0021 U7b: a change nobody who can write here signed. Amber, never red, because
   none of it is the reader fault and a red wall is what people learn to click past. */
.keptout { top: 60px; left: 50%; transform: translateX(-50%); max-width: 560px; padding: 8px 14px; display: flex; gap: 8px; align-items: center; background: var(--amber-soft); border: 1px solid var(--amber-line); color: var(--ink); font-weight: 500; z-index: 9; }
.keptout .ic { flex: none; width: 16px; height: 16px; }
.keptout span { flex: 1; min-width: 0; }
.keptout button { flex: none; padding: 4px 10px; min-height: 32px; }
/* ADR 0021 U8: a board nobody has been let into, which is every board made before it.
   The same one line as above, in the calm grey of a fact rather than the amber of a
   warning -- being open to whoever has the address is what this board always was. */
/* It sits over the canvas and it is there on every board nobody has been let into, so
   it must not swallow a drag that starts under it: the box is see-through to the
   pointer and its two answers are not. */
.loose { top: 60px; left: 50%; transform: translateX(-50%); max-width: 640px; padding: 8px 14px; display: flex; gap: 8px; align-items: center; background: var(--panel); border: 1px solid var(--line); color: var(--ink); z-index: 9; pointer-events: none; }
.loose button { pointer-events: auto; }
.loose .ic { flex: none; width: 16px; height: 16px; color: var(--muted); }
.loose span { flex: 1; min-width: 0; }
.loose button { flex: none; min-height: 32px; white-space: nowrap; display: inline-flex; align-items: center; }
.loose button.q { border-color: transparent; background: transparent; color: var(--muted); }
@media (hover: hover) { .loose button.q:hover { background: var(--hover); color: var(--ink); } }
/* ADR 0019, mechanism 8: the ends of an arrow, its waypoints and the
   connection dots of a shape were the last things on the board under
   24 px on a finger -- the grips were grown by the polish pass, these
   were not. They grow by their radius, which grows ink and hit area
   together, because a transparent stroke would fatten the visible ring.
   24 px is the floor WCAG 2.5.8 sets, and the honest number here: a
   44 px grip on a 60 px sticky leaves nothing to grab the sticky by. */
@media (pointer: coarse) {
  .shape .endpoint, .shape .waypoint, .shape .midpoint, .shape .port { r: 12; }
}
/* States that last (ADR 0023 §6, C8): the kit's .notice, stacked at the top of the canvas so two
   never cover each other. The page draws a new build's; board.js puts the offline one beside it.
   It sits where no chrome is, and under what a press opens: the chrome's z 5, after the rail in the
   page, so the selection bar (6), a panel (7), a menu or a pop (8) all win over it. */
.notices { position: absolute; top: 60px; left: 50%; transform: translateX(-50%); width: max-content; max-width: min(560px, calc(100% - 24px)); display: flex; flex-direction: column; gap: 8px; z-index: 5; pointer-events: none; }
.notices > .notice { box-shadow: var(--shadow); pointer-events: auto; }
/* a screen too short for the rail wraps it into a second column */
.tools { top: 72px; left: 12px; flex-direction: column; flex-wrap: wrap; max-height: calc(100% - 134px); }
.tools button { width: 40px; height: 40px; justify-content: center; font-size: 18px; padding: 0; position: relative; }
.tools button small { position: absolute; right: 3px; bottom: 2px; font-size: 9px; color: var(--muted); }
.tools .sep { width: 24px; height: 1px; margin: 4px 0; }
.topright { top: 12px; right: 12px; }
.zoom { bottom: 12px; right: 12px; }
/* shown when the board has scrolled out of view; above the toast lane it
   shares, so an agent message can never hide the way back */
.back { bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 9; }
.zoom .pct { min-width: 46px; text-align: center; color: var(--muted); }
.zoom .nav { color: var(--muted); font-size: 12px; }
/* a finger has no wheel: the switch between what a wheel does goes, and its divider with it */
@media (pointer: coarse) { .zoom .nav, .zoom .nav + .sep { display: none; } }
/* The one line telling a first-time visitor what to do, centred under the
   board so the Agents panel (top right) cannot cover it. */
.hint { position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%); max-width: min(620px, calc(100% - 150px)); text-align: center; color: var(--muted); font-size: 12px; pointer-events: none; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0; }
.hint kbd { margin: 0 4px 0 2px; line-height: 1.4; }
/* ADR 0019: while a shape is being written in, it wears a ring and a
   tag, so a shape that is selected and a shape being typed into are not
   the same picture. */
.writing .editing-tag { display: block; }
.editing-tag { display: none; position: absolute; font: 600 10.5px var(--font); color: var(--accent-strong); background: var(--accent-soft); border-radius: 4px; padding: 1px 6px; pointer-events: none; width: auto !important; height: auto !important; margin-top: -20px; }
/* the editor itself wears the ring: its own outline, kept at screen size */
.writing .editor { outline: 2px solid var(--accent); outline-offset: 3px; box-shadow: 0 0 0 5px light-dark(#2563eb22, #6ea2ff22); }
/* In a Space the board keeps one piece of chrome of its own, the Sealed
   mark, at the bottom left, so the line steps over it. */
.in-space .hint { bottom: 56px; }
/* a shut board has nothing to zoom and no tool to hint at (02-34) */
.app.shut .zoom, .app.shut .hint { display: none; }

/* ADR 0019: what an empty board says instead of a panel opening itself. */
/* It stands where the first shapes go, so a click on it is a click on the board. */
.start { position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%); width: min(420px, calc(100% - 120px)); text-align: center; color: var(--muted); font-size: 14px; line-height: 1.6; pointer-events: none; user-select: none; }
.start b { display: block; color: var(--ink); font-weight: 600; }
.start span { display: block; }
.start .go .ic { width: 16px; height: 16px; }
#open-agents .ic { width: 16px; height: 16px; }
/* the “?” is one glyph: the bar’s height as its width, so it is a target and not a letter */
#open-help { min-width: 30px; justify-content: center; }
/* ink: the line around it is muted, and the kit's button takes its text colour from where it sits */
.start .go { display: inline-flex; align-items: center; gap: 7px; margin-top: 14px; color: var(--ink); pointer-events: auto; }

/* ADR 0019: the rail names its tools while the board is empty -- the
   first session is where an icon on its own costs most. Each row is
   the icon, the name and the key after it, inside the rail, as the
   ADR drew it. They go when a tool is picked, so the named rail never
   covers the board a tool is about to click, and stay gone after the
   first shape; the tooltip says the same words. A phone or a finger keeps the icons (ADR 0023
   decision 7): 207 px of names covered the start block and the second header row there, and a tap
   on a tool names it in the hint line anyway. */
.tools .tn { display: none; }
@media (min-width: 601px) and (pointer: fine) {
  .tools.named { align-items: stretch; }
  .tools.named > .popwrap { display: flex; }
  .tools.named > button, .tools.named > .popwrap > button { width: 100%; justify-content: flex-start; gap: 10px; padding: 0 10px 0 9px; }
  .tools.named .tn { display: block; white-space: nowrap; color: var(--ink); font-size: 13px; font-weight: 500; }
  .tools.named > button small, .tools.named > .popwrap > button small { position: static; margin-left: auto; padding-left: 16px; font-size: 11px; font-weight: 500; }
  .tools.named > .sep { width: auto; margin: 4px 6px; }
  .tools.named button.off .tn { color: var(--muted); }
}
.peers { display: flex; align-items: center; padding: 0 4px; }
.avatar { width: 28px; height: 28px; border-radius: 50%; color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; margin-left: -6px; border: 2px solid var(--panel); }
.peers .avatar:first-child { margin-left: 0; }
.avatar.agent { border-radius: 8px; }
/* your own name, next to the avatars; a button because it changes the name */
.topright .me { font-weight: 500; max-width: 140px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: inline-block; }
/* In a Space (ADR 0018) the Space shows the name, path and menu of the board; the board
   keeps its tools, zoom, people, Agents, the question mark and the Sealed mark, and asks no name. */
/* one "?" a screen (ADR 0023 C10): in a Space it is the Space bar's, which opens this board's sheet */
.in-space .topleft .logo, .in-space .topleft .home-space, .in-space .topleft input.title, .in-space #open-share, .in-space #board-menu, .in-space #open-agents, .in-space .topright .me, .in-space #open-help { display: none; }
/* with Programs and the "?" gone, their divider goes too, and so does a pill with no face and no badge left in it */
.in-space .topright .sep, .in-space .topright:not(:has(.peers, .badge)) { display: none; }
.in-space .topleft:not(:has(.sealed)) { display: none; }
.in-space .topleft { top: auto; bottom: 12px; }
.in-space .tools { top: 12px; max-height: calc(100% - 74px); }
/* The connector toolbar is five groups of buttons wide - wider than a
   narrow window - so it wraps rather than running off the edge, where
   the last buttons could not be reached at all. */
.ctx { z-index: 6; left: 0; top: 0; will-change: transform; flex-wrap: wrap; justify-content: center; max-width: calc(100vw - 16px); }
.stage.panning, .stage.panning * { cursor: grabbing !important; }
/* no hinting: glyphs scale smoothly instead of snapping to pixels while zooming */
.board text { text-rendering: geometricPrecision; }
/* .pop in the selector outranks .ctx button and .float button, whose min-width,
   padding and border:0 stretched the dots into touching pills and hid white */
.pop .swatch { width: 24px; height: 24px; min-width: 0; min-height: 0; padding: 0; margin: 0; border-radius: 50%; border: 1px solid light-dark(#0002, #fff3); }
@media (hover: hover) { .pop .swatch:hover { transform: scale(1.12); } }
.pop .swatch.active { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The bar: 32 px targets, option sets behind popovers, kinds as chips */
.ic { width: 20px; height: 20px; fill: currentColor; display: block; flex: none; }
.tools .ic { width: 22px; height: 22px; }
.ctx .ic, .zoom .ic, .topleft .ic { width: 18px; height: 18px; }
.car .ic { width: 10px; height: 10px; }
.ctx button { min-width: 32px; min-height: 32px; }
.ctx button.open, .ctx button[aria-expanded="true"], .tools button[aria-expanded="true"] { background: var(--accent-soft); color: var(--accent-strong); }
.ctx .car { font-size: 9px; color: var(--muted); }
/* the colour on the bar's button (a span; the pop's own swatches are buttons) */
.ctx span.swatch { width: 18px; height: 18px; border-radius: 6px; border: 1px solid light-dark(#0002, #fff3); display: inline-block; }
.ctx span.swatch.mixed { background: linear-gradient(135deg, var(--mark) 0 48%, var(--muted) 48% 52%, var(--danger-soft) 52% 100%); }
.ctx button.chip { min-height: 24px; min-width: 0; height: 24px; padding: 0 9px; border-radius: 999px; background: var(--hover); font-size: 12px; gap: 4px; }
@media (hover: hover) { .ctx button.chip:hover { background: var(--pressed); } }
.ctx button.chip b { font-weight: 700; }
.popwrap { position: relative; display: inline-flex; }
/* the shapes flyout opens beside the rail, not under it */
.tools .pop { top: 0; left: calc(100% + 10px); flex-direction: row; flex-wrap: wrap; max-width: calc(100vw - 86px); }
.pop { position: absolute; top: calc(100% + 6px); left: 0; display: flex; align-items: center; gap: 2px; padding: 4px; background: var(--panel); border-radius: 10px; box-shadow: var(--shadow); z-index: 8; }
.pop.right { left: auto; right: 0; }
.pop.colors { display: grid; grid-template-columns: repeat(6, 24px); gap: 8px; padding: 10px; }
.pop.rows { flex-direction: column; align-items: stretch; }
/* the pen options: the seven dots, then the three widths, one row like the shapes flyout */
.pop.pen { gap: 8px; padding: 6px 8px; }
.pop.pen .sep { width: 1px; height: 20px; margin: 0 2px; }
.pop .row { display: flex; align-items: center; gap: 2px; }
.pop .row .t { width: 46px; padding-left: 4px; font-size: 11px; color: var(--muted); }
.menu { flex-direction: column; align-items: stretch; min-width: 220px; padding: 6px; z-index: 8; }
/* rows, not buttons: the weight of the one menu (ui.css .popmenu), not the kit button's 500 */
.menu button { justify-content: space-between; gap: 20px; padding: 0 10px; min-height: 30px; font-weight: 400; text-align: start; }
.menu button .k { color: var(--muted); font-size: 11px; }
.menu .div { height: 1px; background: var(--line); margin: 4px 4px; }

/* The ring is a shadow, so text in the editor gets the same width as on the canvas. */
.editor { position: absolute; margin: 0; border: 0; box-shadow: 0 0 0 2px var(--accent); border-radius: 2px; line-height: 1.25; resize: none; outline: none; overflow: hidden; z-index: 4; font-family: inherit; box-sizing: border-box; overflow-wrap: break-word;
  /* --ex/--ey/--ew/--eh/--ef/--ep are board units from the page; the camera maps them to pixels */
  left: calc((var(--ex) - var(--cam-x, 0)) * var(--cam-z, 1) * 1px);
  top: calc((var(--ey) - var(--cam-y, 0)) * var(--cam-z, 1) * 1px);
  width: max(calc(var(--ew) * var(--cam-z, 1) * 1px), var(--emin, 0px));
  height: calc(var(--eh) * var(--cam-z, 1) * 1px);
  font-size: calc(var(--ef) * var(--cam-z, 1) * 1px);
  padding: calc(var(--ep) * var(--cam-z, 1) * 1px);
  /* centred text: board.js measures the lines and sets --ed-pt */
  padding-top: var(--ed-pt, calc(var(--ep) * var(--cam-z, 1) * 1px)); }
/* A cell is edited as code: monospace, lines kept whole, scrolled rather than wrapped */
.editor.celledit { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; overflow: auto; overflow-wrap: normal; tab-size: 2; line-height: 1.45; }
/* SVG hangs text from its hanging baseline; board.js measures how far that sits from
   where an HTML line box puts the same glyphs, so the canvas and the editor agree. */
.shape text.tx { transform: translateY(calc(var(--text-shift, 0) * 1em)); }

.panel { position: absolute; top: 60px; right: 12px; width: 400px; max-width: calc(100% - 24px); max-height: calc(100% - 130px); overflow: auto; background: var(--panel); border-radius: 12px; padding: 16px 18px; box-shadow: var(--shadow); z-index: 7; font-size: 13px; }
.panel h3 { margin: 0 0 8px; font-size: 15px; display: flex; align-items: center; justify-content: space-between; }
.panel h4 { margin: 24px 0 6px; font-size: 15px; font-weight: 600; color: var(--ink); }
.panel p { margin: 6px 0; line-height: 1.45; }
.panel code, .panel pre { background: var(--hover); border-radius: 6px; font-size: 12px; }
.panel code { padding: 2px 5px; word-break: break-all; }
.panel pre { padding: 10px; overflow-x: auto; white-space: pre-wrap; margin: 6px 0; }
/* a link that reads as a button: the kit has none for an <a> that is not the primary */
.panel a.btn { display: inline-block; padding: 6px 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); color: var(--ink); font-weight: 500; text-decoration: none; }
@media (hover: hover) { .panel a.btn:hover { background: var(--hover); } }
.panel .row { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 6px 0; }
.panel .feed { list-style: none; margin: 0; padding: 0; }
.panel .feed li { padding: 5px 0; border-top: 1px solid var(--line); display: flex; gap: 8px; align-items: baseline; }
.panel .feed .who { font-weight: 600; white-space: nowrap; }

/* The name dialog: over everything, because until a name is set the tab
   only watches the board. */
.veil { position: absolute; inset: 0; background: var(--backdrop); display: flex; align-items: center; justify-content: center; z-index: 20; }
.dialog { background: var(--bg); color: var(--ink); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 24px 28px; width: 400px; max-width: calc(100% - 32px); box-sizing: border-box; box-shadow: var(--shadow-lg); font-size: 15px; line-height: 1.5; }
.dialog h3 { margin: 0 0 8px; font-size: 20px; font-weight: 600; }
.dialog p { margin: 6px 0; color: var(--muted); }
.dialog p.error { color: var(--danger); }
.dialog input { width: 100%; box-sizing: border-box; margin: 8px 0 2px; }
.dialog .row { display: flex; gap: 8px; margin-top: 16px; }

/* A page in full screen: the page under a bar of ours, so it can never pass for the
   browser or for Charta. Above the panels, under the name dialog. */
.page-veil { background: var(--ink); flex-direction: column; align-items: stretch; justify-content: flex-start; z-index: 15; }
.pagebar { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--panel); border-bottom: 1px solid var(--line); }
.pagebar .ptitle { font-weight: 600; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pagebar .pnote { color: var(--muted); font-size: 12px; margin-left: auto; white-space: nowrap; }
.pagebar button { display: inline-flex; align-items: center; gap: 6px; }
/* the key on the ink button: the kit mutes a key on a button, which ink would swallow */
.pagebar .primary kbd { color: inherit; opacity: 0.7; }
iframe.pagefull { flex: 1; width: 100%; border: 0; background: #fff; }
.pgblocked { flex: 1; display: grid; place-items: center; color: var(--on-ink); font-size: 15px; }
.panel.source { z-index: 21; width: 560px; }
.panel.source pre { max-height: 60vh; overflow: auto; white-space: pre; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* max-content: at left 50% the lane would only get the half of the screen right of the middle */
.toasts { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: max-content; z-index: 8; }
/* who said it: the name in the toast's own colour, their hue as a dot (on ink, a hue fails as text) */
.toast .who { font-weight: 600; white-space: nowrap; max-width: 160px; overflow: hidden; text-overflow: ellipsis; flex: none; }
.toast .who::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 6px; border-radius: 50%; background: var(--hue); vertical-align: 1px; }
/* the answer the panel opens with, before any list */
.panel.share .who { color: var(--ink); font-weight: 600; margin: 0 0 6px; }
/* a board's owed-keys row (share.mjs li.owed): the words take the line, the button its height
   (these were written for the old ul.owed and reach the li now; kept, so the row does not move) */
.panel.share .owed { margin: 0 -8px 8px; }
.panel.share .owed .grow { flex: 1 1 100%; min-width: 0; }
.panel.share .owed button { min-height: 32px; }
/* the dot on Share: one quiet mark, and its tooltip says what it is */
#open-share.asked::after { content: ""; display: inline-block; width: 7px; height: 7px; margin-left: 5px;
  border-radius: 50%; background: var(--amber-ink); vertical-align: 1px; }

/* Touch: fingers need 44 px, and nothing centred at the bottom may sit on the zoom bar. */
@media (pointer: coarse) {
  .float button, .zoom button { min-height: 44px; min-width: 44px; justify-content: center; }
  .tools button { width: 44px; height: 44px; }
  .menu button, .dialog button { min-height: 44px; }
  /* a row, not a centred button: the label left, the key right, as with a mouse */
  .menu button { justify-content: space-between; }
  .panel button, .panel a.btn, .reading button { min-height: 36px; }
  /* a fold and a field are pressed as much as a button is, and the top bar's two are the
     board's own name and the way home (verify-4) */
  .panel summary, .panel details > summary { min-height: 36px; display: flex; align-items: center; }
  .panel input { min-height: 36px; }
  .topleft input.title, .topleft .logo, #open-help { min-height: 44px; min-width: 44px; }
  .pop.colors { grid-template-columns: repeat(6, 32px); }
  .pop .swatch { width: 32px; height: 32px; }
  .tools button small { display: none; }
}
/* A short screen under a finger (a phone with its address bar, a board in a Space under two bars): the
   rail's fifteen rows at 44 px would wrap into a second column, under the header's second row; at 36 px
   they fit, and stay 44 px wide */
@media (pointer: coarse) and (max-height: 800px) { .tools button { height: 36px; min-height: 36px; } }
@media (max-width: 1100px) { .hint, .toasts, .back { bottom: 70px; } }
/* under 800 the lane spans the page, as a Space's does (verify-2): one lane, the height above the zoom bar the
   board's own; `width: auto` beats the manual popover's fit-content */
@media (max-width: 799px) { .toasts { left: 12px; right: 12px; width: auto; transform: none; align-items: stretch; } }
/* A phone: the title takes the first row, the people and Share the second. */
@media (max-width: 600px) {
  .topleft { right: 12px; }
  .topleft .logo .script { display: none; }
  .topleft .home-space { max-width: 30vw; flex: none; }
  /* Share and the ⋯ keep their size (a finger's min-width would let them be squeezed under their words):
     the name and the access line are what give */
  .topleft #open-share, .topleft #board-menu { flex: none; }
  .topright { top: 72px; }
  .panel, .sealnote, .keptout, .loose { top: 132px; }
  /* the one line spans the width here: the tool rail is behind it, and a box squeezed
     between the rail and the edge wraps to five lines nobody reads */
  .keptout, .loose { left: 8px; right: 8px; transform: none; max-width: none; }
  /* under both header rows and right of the rail, never over a tool; the rail that names its tools
     on an empty board is wider (its longest row, "Image or prototype", measured 206 px) */
  .notices { top: 132px; left: 72px; right: 12px; transform: none; width: auto; max-width: none; align-items: stretch; }
  /* a panel opens under the notices (board.js keeps --notices-h), so a lasting state stays in sight */
  .panel { top: calc(132px + var(--notices-h, 0px) + min(var(--notices-h, 0px), 8px)); }
  /* the empty board’s first lines stand right of the rail, not under it (a reader has no rail) */
  .app:not(.reading-only) .start { left: 72px; right: 12px; width: auto; transform: translateY(-50%); }
  /* the selection’s bar and the text’s bar wrap in the room right of the rail; its buttons sit closer, so a
     sticky's seven fit on one row at 390 (U7b) */
  .ctx { max-width: calc(100vw - 80px); }
  .ctx button { padding-inline: 5px; }
  .ctx .sep { margin: 0 2px; }
  .md-tools { left: max(72px, min(calc((var(--ex) - var(--cam-x, 0)) * var(--cam-z, 1) * 1px), calc(100% - var(--tw, 0px) - 8px))); max-width: calc(100% - 80px); }
  /* at a phone width the sentence and its two answers stack: three things on one
     line here is a button of four pixels */
  .loose { flex-wrap: wrap; }
  .loose span { flex: 1 0 100%; }
  .panel { max-height: calc(100% - 210px - var(--notices-h, 0px)); }
  .in-space .topleft { right: auto; } .in-space .topright { top: 12px; } .in-space .panel, .in-space .sealnote, .in-space .keptout, .in-space .loose { top: 60px; }
  /* the top is two rows of bars here, and the chip sat on the second one: it goes to the
     bottom, above the hint line, where nothing else is */
  .reading { top: auto; bottom: 110px; }
  /* a board nobody let you into has one short line: the pill hugs it, not the width */
  .shut .topleft { right: auto; }
  .shut .topleft input.title { max-width: none; }
}
/* under 360 px the way back to the Space goes (the lockup still goes home), or the menu fell off (04-5) */
@media (max-width: 359px) { .topleft .home-space { display: none; } }


/* Home: the boards this browser opened, and a search over them. */
.home { max-width: 520px; margin: 0 auto; padding-block: 14vh 0; padding-inline: 24px; font-size: 15px; }
.home header { gap: 8px; min-height: 31px; margin-bottom: 40px; }
.home header nav { display: flex; flex-wrap: wrap; justify-content: flex-end; margin-right: -8px; }
/* narrow, the nav wraps: the two New links first, and the logo on their line */
@media (max-width: 600px) { .home header { align-items: baseline; } .home .you { order: 1; } }
.home .pitch { margin: 0 0 40px; color: var(--muted); line-height: 1.5; }
.home .pitch a { color: var(--ink); }
.home .new:not(.primary) { color: var(--ink); text-decoration: none; font-weight: 500; padding: 6px 8px; border-radius: 8px; white-space: nowrap; }
.home .new:not(.primary, [aria-expanded=true]):hover { background: var(--hover); }
.home nav button.new { border: 0; background: none; }
/* the “?” is one glyph: a square target, not a 24 px sliver */
.home nav button.help { min-width: 32px; }
@media (pointer: coarse) { .home nav button.help { min-width: 44px; } }
/* open: a row's ⋯ while its menu does (the kit adds the ring) */
.home li .dots[aria-expanded=true] { color: var(--ink); }
/* no key yet, so no href: a press reads as waiting */
.home a[aria-disabled=true] { cursor: progress; }
/* the empty list: the two first presses side by side, each over its sentence */
.home .firsts { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin: 8px 0 0; }
.home .firsts p { margin: 0; }
.home .firsts .none { display: block; margin: 10px 0 0; line-height: 1.5; }
/* the second press: a link in the kit's secondary look, which the kit keeps for <button> */
.home .firsts a:not(.primary) { display: inline-flex; align-items: center; min-height: 32px; padding: 6px 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); font-size: 13px; }
.home .firsts a:not(.primary):hover { background: var(--hover); }
/* The page has a heading for a screen reader; on screen the logo and
   the list itself say where you are. */
.home h1.named { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* in flow, so it never covers the list or the changelog */
.home footer { margin-top: 64px; padding: 16px 0; text-align: center; }
.home footer nav { display: flex; justify-content: center; gap: 24px; }
.home footer a { padding-block: 3px; margin-block: -3px; color: var(--muted); text-decoration: none; }
.home footer a:hover, .home footer a[aria-current] { color: var(--ink); }
/* the changelog: one feature an entry, newest first, each with a picture or a clip of it */
.changelog h1 { font-size: 32px; letter-spacing: -0.03em; margin: 0 0 8px; }
.changelog .lede { margin: 0 0 56px; }
.changelog article { margin: 0 0 64px; scroll-margin-top: 24px; }
.changelog time { color: var(--muted); font-size: 13px; }
.home.changelog h2 { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; margin: 2px 0 8px; color: var(--ink); }
.changelog h2 a { color: inherit; text-decoration: none; }
.changelog h2 a:hover { text-decoration: underline; text-underline-offset: 3px; }
.changelog p { color: var(--muted); line-height: 1.5; margin: 0 0 16px; }
.changelog pre { margin: 0 0 16px; padding: 12px 14px; border-radius: var(--r); background: var(--panel); box-shadow: 0 0 0 1px var(--line); overflow-x: auto; font: 13px/1.5 var(--mono); color: var(--ink); }
.changelog figure { position: relative; margin: 0; }
/* a clip is pressed to pause it or play it again (home.mjs); with less motion asked for it waits, under a play badge */
.changelog video { cursor: pointer; }
.changelog video:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .changelog figure:has(video):not(.playing)::after { content: ""; position: absolute; inset: 0; margin: auto; width: 56px; height: 56px; border-radius: 50%; pointer-events: none;
    background: rgb(0 0 0 / 0.6) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 5v14l11-7z' fill='white'/%3E%3C/svg%3E") center / 24px no-repeat; }
}
/* a picture of the light app, framed as a light island on either ground rather than a slab with no edge;
   the width and height it carries keep its place before it loads */
.changelog figure :is(img, video) { display: block; max-width: 100%; height: auto; border-radius: 12px; box-shadow: 0 0 0 1px var(--line); background: var(--panel); }
/* the header row where every plain page has it: the lockup, Your list at the other end, then the page */
.home.changelog { max-width: 720px; padding-top: 20px; }
.home.changelog > header { margin-bottom: 8vh; }
.home input.q { width: 100%; border: 0; background: none; padding: 0 0 20px; font-size: clamp(24px, 7.5vw, 32px); font-weight: 600; letter-spacing: -0.03em; outline: none; appearance: none; }
.home input.q::-webkit-search-cancel-button { display: none; }
.home ol { list-style: none; margin: 0 -12px; padding: 0; }
.home li a { display: flex; align-items: baseline; gap: 24px; padding: 10px 12px; line-height: 22px; border-radius: 8px; color: var(--ink); text-decoration: none; }
.home.searching ol:first-of-type li:first-child { background: var(--hover); }
.home li span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home li .untitled, .home li time, .home .none { color: var(--muted); }
/* a board's row says so, as its row in a Space's tree does (space.css `.app .tree .row .k`): a board and a
   Space stop looking alike (02-5) */
.home li a > .k { flex: none; align-self: center; margin-left: -16px; font-size: 11px; line-height: 16px; color: var(--muted); border: 1px solid var(--line); border-radius: 5px; padding: 0 5px; }
/* right after the name with the tree's small gap (the row's 24 px less 16), the time at the row's end, and
   the access words give way before the name does, down to their ellipsis (verify-5 M3) */
.home li a > span:first-child:has(+ .k) { flex: none; min-width: 0; max-width: 60%; }
.home li a > .k + time { margin-left: auto; }
/* and where the name, its chip and the words do not fit one line, the words take the next, still at the end */
.home li a:has(> .k) { flex-wrap: wrap; row-gap: 2px; }
/* a phone's row puts the words on a line of their own (links.css): the name has the line but the chip */
@media (max-width: 600px) { .home li a > .k { margin-left: -4px; } .home li a > span:first-child:has(+ .k) { max-width: calc(100% - 60px); } }
.home li time { font-size: 13px; font-variant-numeric: tabular-nums; }
/* somebody has asked to be let in here and nobody has answered (ADR 0021 §8) */
.home li i.asked { flex: 1; color: var(--amber-ink); font-style: normal; font-size: 13px; white-space: nowrap; }
/* it belongs to the name, so the name stops where it ends and the time still sits right */
.home li:has(i.asked) > a > span:first-child { flex: 0 1 auto; }
.home .none { margin: 10px 0; }
.home .none a, .home .keeper a { color: var(--ink); }
.home h2 { margin: 28px 0 4px; font-size: 13px; font-weight: 500; color: var(--muted); }
.home li { position: relative; display: flex; align-items: center; border-radius: 8px; }
.home li > a { flex: 1; min-width: 0; }
.home li:focus-within { background: var(--hover); }
/* a row's own presses are ghosts at the row's size: Put back and its ⋯ (whose menu is menu.mjs's .popmenu) */
.home li button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; padding: 6px 10px; border-radius: 6px; }
@media (hover: hover) { .home li button:hover { background: var(--pressed); } }
.home li .dots, .home .putback { flex: none; }
.home li .dots { display: flex; justify-content: center; width: 36px; color: var(--muted); }
.home li .dots .ic { width: 18px; height: 18px; }
.home li:has(> .dots[aria-expanded=true]) { background: var(--hover); }
/* with a pointer a row lights up on hover (on touch that would stick), and its ⋯ shows on hover, on focus and while its menu is open; on touch it always shows */
@media (hover: hover) { .home li:hover { background: var(--hover); } .home li:not(:hover):not(:focus-within) .dots:not([aria-expanded=true]) { opacity: 0; } }
.home .putback { color: var(--muted); font-size: 13px; }
/* a forgotten row is muted, and Put back stands where its time was */
.home li:has(.putback) span { color: var(--muted); }
.home li:has(.putback) time { display: none; }
/* the join announcement (ADR 0021 §2): the amber of a row that asks something of you,
   and it stays until it is answered on /me — a toast that goes by itself would be the
   one detection a stolen recovery sheet leaves, lost in six seconds. */
.home .joined { margin: 0 0 20px; padding: 10px 12px; border-radius: 10px; background: var(--amber-soft); box-shadow: inset 0 0 0 1px var(--amber-line); color: var(--amber-ink); font-size: 13px; line-height: 1.5; }
.home .joined a { color: var(--amber-ink); font-weight: 500; }
.home .keeper { margin: 32px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.home .offer { margin: 0 -12px 32px; padding: 12px 11px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); line-height: 1.5; }
.home .offer p { margin: 0 0 8px; }
.home .offer p:last-child { margin: 0; display: flex; gap: 8px; align-items: center; }
.home .new.you { color: var(--muted); font-weight: 400; }
/* the toast lane: its look is the kit's */
.home .toasts { position: fixed; z-index: 3; left: 50%; bottom: 64px; transform: translateX(-50%); width: max-content; }
/* a toast here takes the pointer, so resting on it keeps it (the board's lets the canvas have it) */
.home .toasts > .toast { pointer-events: auto; }
/* "Move into a Space…" (home.mjs `pick`): sure()'s sheet (links.css gives its width and footer), the Spaces
   this list may write to as a menu's rows in one box, each pressed once: the name, its access line under it */
dialog.sheet.pick .spaces { display: grid; margin: 12px 0 0; padding: 6px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--panel); }
dialog.sheet.pick .spaces button { display: grid; gap: 2px; padding: 7px 10px; border: 0; border-radius: var(--r-sm); background: none; color: var(--ink); font-size: 14px; font-weight: 400; text-align: start; }
dialog.sheet.pick .spaces :is(b, i) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
dialog.sheet.pick .spaces b { font-weight: 500; }
dialog.sheet.pick .spaces i { color: var(--muted); font-size: 12px; font-style: normal; }
@media (hover: hover) { dialog.sheet.pick .spaces button:hover { background: var(--hover); } }
dialog.sheet.pick .spaces button:focus-visible { outline-offset: -2px; background: var(--hover); }
@media (pointer: coarse) { dialog.sheet.pick .spaces button { min-height: 44px; align-content: center; } }
/* on touch every target is at least 44px tall */
@media (pointer: coarse) {
  .home li a { padding-block: 11px; }
  .home .new, .home li button, .home .offer .go, .home .offer button { line-height: 32px; }
  .home li .dots { width: 44px; min-height: 44px; align-items: center; }
  .home .firsts a:not(.primary) { min-height: 44px; }
  .home footer { padding-block: 0; }
  .home footer a { line-height: 44px; }
}

/* canvas: what 1charta draws follows the theme */
/* ADR 0023 decision 11, report 10 option a+ (logs/10-canvas.css): a colour a person chose stays, what
   1charta draws on its own ground takes a twin. The rules match the colour attributes the board
   already writes, so no stored colour changes and a board edited in dark reopens in light as it was.
   light-dark() picks per the element's color-scheme, so none of this needs a media query, and a light
   island (color-scheme: light) keeps the light half. Colours match case-insensitively because an agent
   may write #1F2937. A rule over an attribute weighs nothing (:where), so a state rule (a drop target,
   a hover) still wins over it as it won over the attribute. The canvas rules that were already here
   (the sticky's shadow, the code colours, the frame title, the marquee) take their twins in place. */
/* 1. drawn on the ground: free text (and its editor, which reads --ed-ink), pen strokes, arrows and their
      far-zoom silhouettes */
:where(.shape.text-kind .tx[fill="#1f2937" i], #editor[data-ink="#1f2937" i]) { fill: light-dark(#1f2937, #e8e6e1); --ed-ink: light-dark(#1f2937, #e8e6e1); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#1f2937" i]) { stroke: light-dark(#1f2937, #e8e6e1); }
:where(.shape.text-kind .body[fill="#1f29372e" i]) { fill: light-dark(#1f29372e, #e8e6e12e); }
:where(.shape.text-kind .tx[fill="#6b7280" i], #editor[data-ink="#6b7280" i]) { fill: light-dark(#6b7280, #a6a298); --ed-ink: light-dark(#6b7280, #a6a298); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#6b7280" i]) { stroke: light-dark(#6b7280, #a6a298); }
:where(.shape.text-kind .body[fill="#6b72802e" i]) { fill: light-dark(#6b72802e, #a6a2982e); }
:where(.shape.text-kind .tx[fill="#52514e" i], #editor[data-ink="#52514e" i]) { fill: light-dark(#52514e, #bdb9af); --ed-ink: light-dark(#52514e, #bdb9af); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#52514e" i]) { stroke: light-dark(#52514e, #bdb9af); }
:where(.shape.text-kind .body[fill="#52514e2e" i]) { fill: light-dark(#52514e2e, #bdb9af2e); }
:where(.shape.text-kind .tx[fill="#dc2626" i], #editor[data-ink="#dc2626" i]) { fill: light-dark(#dc2626, #f87171); --ed-ink: light-dark(#dc2626, #f87171); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#dc2626" i]) { stroke: light-dark(#dc2626, #f87171); }
:where(.shape.text-kind .body[fill="#dc26262e" i]) { fill: light-dark(#dc26262e, #f871712e); }
:where(.shape.text-kind .tx[fill="#d97706" i], #editor[data-ink="#d97706" i]) { fill: light-dark(#d97706, #fbbf24); --ed-ink: light-dark(#d97706, #fbbf24); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#d97706" i]) { stroke: light-dark(#d97706, #fbbf24); }
:where(.shape.text-kind .body[fill="#d977062e" i]) { fill: light-dark(#d977062e, #fbbf242e); }
:where(.shape.text-kind .tx[fill="#16a34a" i], #editor[data-ink="#16a34a" i]) { fill: light-dark(#16a34a, #4ade80); --ed-ink: light-dark(#16a34a, #4ade80); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#16a34a" i]) { stroke: light-dark(#16a34a, #4ade80); }
:where(.shape.text-kind .body[fill="#16a34a2e" i]) { fill: light-dark(#16a34a2e, #4ade802e); }
:where(.shape.text-kind .tx[fill="#2563eb" i], #editor[data-ink="#2563eb" i]) { fill: light-dark(#2563eb, #6ea2ff); --ed-ink: light-dark(#2563eb, #6ea2ff); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#2563eb" i]) { stroke: light-dark(#2563eb, #6ea2ff); }
:where(.shape.text-kind .body[fill="#2563eb2e" i]) { fill: light-dark(#2563eb2e, #6ea2ff2e); }
:where(.shape.text-kind .tx[fill="#7c3aed" i], #editor[data-ink="#7c3aed" i]) { fill: light-dark(#7c3aed, #a78bfa); --ed-ink: light-dark(#7c3aed, #a78bfa); }
:where(.shape:is(.ink-kind, .arrow) :is(.line, .heads)[stroke="#7c3aed" i]) { stroke: light-dark(#7c3aed, #a78bfa); }
:where(.shape.text-kind .body[fill="#7c3aed2e" i]) { fill: light-dark(#7c3aed2e, #a78bfa2e); }
/* 2. 1charta's own furniture: the default frame and the default hairlines (a Black sticky keeps an edge) */
:where(.shape.frame-kind > .body[fill="#fafaf8" i]) { fill: light-dark(#fafaf8, #22211e); }
:where(.shape.frame-kind > .body[stroke="#d6d3cd" i]) { stroke: light-dark(#d6d3cd, #45423c); }
:where(.shape > .body[stroke="#00000014"]) { stroke: light-dark(#00000014, #ffffff29); }
:where(.shape > .body[stroke="#00000020"]) { stroke: light-dark(#00000020, #ffffff29); }
:where(.shape > .body[stroke="#00000030"]) { stroke: light-dark(#00000030, #ffffff38); }
/* 3. the default white rich-text card is 1charta's paper (the page writes var() inks for its text); a
      card whose fill a person chose is a light island, its note header and far plate too */
:where(.shape.markdown-kind > .body[fill="#ffffff" i]) { fill: var(--panel); }
:where(.shape.markdown-kind stop[stop-color="#ffffff" i]) { stop-color: var(--panel); }
.shape.markdown-kind:not(:has(> .body[fill="#ffffff" i])) { color-scheme: light; }
/* 4. light islands: documents drawn for a white page keep it (their bodies' #ffffff has no rule above) */
.cellcard, .pgwrap, .shape.image-kind { color-scheme: light; }
/* 5. the highlighter: multiply over a dark ground is invisible (1.01:1); normal at 20% (40% in light) lets
      the text under it keep 4:1 and more (red ink under the green band, the worst; 3.5:1 at 25%), and the band
      still stands 1.9:1 off the ground. The page writes multiply inline (only for a highlighter), hence
      the !important; scheme.js repeats this for a kept choice. */
@media (prefers-color-scheme: dark) { .shape.ink-kind .line[style*="multiply"] { mix-blend-mode: normal !important; opacity: .2; } }
/* 6. a swatch shows what it draws. Ink, pen and connector colours are drawn on the ground, so their swatches
      (a pop or the bar's own, marked .ink by the page) show the twin; a rich-text card's White is 1charta's
      paper (.paper); a sticky's swatch shows its fill. The page writes the colour inline, hence !important. */
:where(.ink > .swatch, .swatch.ink)[style="background:#1f2937" i] { background: light-dark(#1f2937, #e8e6e1) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#6b7280" i] { background: light-dark(#6b7280, #a6a298) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#52514e" i] { background: light-dark(#52514e, #bdb9af) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#dc2626" i] { background: light-dark(#dc2626, #f87171) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#d97706" i] { background: light-dark(#d97706, #fbbf24) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#16a34a" i] { background: light-dark(#16a34a, #4ade80) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#2563eb" i] { background: light-dark(#2563eb, #6ea2ff) !important; }
:where(.ink > .swatch, .swatch.ink)[style="background:#7c3aed" i] { background: light-dark(#7c3aed, #a78bfa) !important; }
:where(.paper > .swatch, .swatch.paper)[style="background:#ffffff" i] { background: var(--panel) !important; }
/* 7. Safari 18.6 draws no context-stroke, so an arrow head there was black whatever the arrow, and on
      a dark board it would vanish: the page's ink instead, in the browsers that cannot take the arrow's */
@supports not (fill: context-stroke) { marker path { fill: var(--ink); } }
/* end canvas */
