@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;800&family=Space+Mono:wght@400;700&display=swap');

/* OVERPRINT (BLACKBOX/THEMES/OVERPRINT.md): the tokens and the sheet. Two drums on uncoated stock; the inks carry direction, the black drum does everything else.
   This file is inlined into the SVG export, so everything a printed figure needs lives here and nothing else does. */
:root {
  color-scheme: light;
  --stock: #efeadf; --stock-deep: #e6e0d2;
  --pink: #ff48b0; --blue: #3d5588; --black: #1f1d1b;
  --black-60: #6e6a63; --black-30: #b9b4a9;
  --blend: multiply;
  --rule-heavy: 2px solid var(--black); --rule-hair: 1px solid var(--black-30); --radius: 0;
  --font-text: 'Archivo', system-ui, sans-serif; --font-data: 'Space Mono', ui-monospace, monospace;
}
/* MASTER: the stencil on a light table. Light adds where ink subtracts, so the overprint is a screen.
   The system picks the stock until the page's theme button sets data-theme on the root; an exported SVG has no button and follows the system. */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) {
  color-scheme: dark; --stock: #1c1917; --stock-deep: #262220; --pink: #ff5cb8; --blue: #7f9bd6;
  --black: #ece7dc; --black-60: #a39d92; --black-30: #57524b; --blend: screen;
} }
:root[data-theme="dark"] {
  color-scheme: dark; --stock: #1c1917; --stock-deep: #262220; --pink: #ff5cb8; --blue: #7f9bd6;
  --black: #ece7dc; --black-60: #a39d92; --black-30: #57524b; --blend: screen;
}

/* The sheet. One figure, four registration marks, ink multiplied onto the stock behind it. */
#view { flex: 1; width: 100%; min-height: 0; background: var(--stock); isolation: isolate; touch-action: none; cursor: grab; user-select: none } #view:active { cursor: grabbing }
.reg { position: absolute; width: 12px; height: 12px; color: var(--black); pointer-events: none }
.pass, .root { mix-blend-mode: var(--blend) }

/* Marks keep their printed size at any zoom: every dimension is divided by --scale, the scene's scale. Zooming spreads the figure, it does not fatten the ink. */
text { font-family: var(--font-text); font-size: calc(12px / var(--scale, 1)); fill: var(--black); pointer-events: none }
text.label:not(.turned) { transform: translate(calc(10px / var(--scale, 1)), 0); dominant-baseline: middle } text.near { font-weight: 500 }
text.fixed { font-size: 11px; transform: none; dominant-baseline: auto }
.axis { font-family: var(--font-data); font-size: calc(10px / var(--scale, 1)); fill: var(--black-60); dominant-baseline: hanging }
.grid { stroke: var(--black-30); stroke-width: calc(.5px / var(--scale, 1)) } .band { fill: var(--stock-deep) }
.edge { fill: none; stroke-width: calc(var(--weight) * 1px / var(--scale, 1)) } .edge.pink { stroke: var(--pink) } .edge.blue { stroke: var(--blue) }
.edge.cited { stroke-dasharray: calc(4px / var(--scale, 1)) calc(4px / var(--scale, 1)) }
.edge.extracted { stroke-dasharray: calc(6px / var(--scale, 1)) calc(3px / var(--scale, 1)) calc(1px / var(--scale, 1)) calc(3px / var(--scale, 1)) }
.edge.authored, .edge.coauthored { stroke: var(--black); stroke-dasharray: calc(1px / var(--scale, 1)) calc(3px / var(--scale, 1)); stroke-linecap: round } .cell.black { fill: var(--black-60) }
.edge.sub, .cell.sub { opacity: .5 }
.edge.lit { stroke-width: calc(var(--weight) * 2px / var(--scale, 1)) } .dimmed .edge:not(.lit) { stroke-width: calc(var(--weight) * .35px / var(--scale, 1)) }
.hit { fill: transparent; r: calc(12px / var(--scale, 1)) }
.node { fill: var(--stock); stroke: var(--black); stroke-width: calc(1.5px / var(--scale, 1)); r: calc(5px / var(--scale, 1)) } .node.near, .root { r: calc(7px / var(--scale, 1)) }
.root.pink { fill: var(--pink) } .root.blue { fill: var(--blue) }   /* registered, the root reads as plum */
/* A work prints as a square and a term as a diamond, sized like a disc of the same tier; a root keeps both inks in either shape. */
.mark rect { --side: calc(10px / var(--scale, 1)); width: var(--side); height: var(--side); x: calc(var(--side) / -2); y: calc(var(--side) / -2) }
.mark rect.near, .mark rect.root { --side: calc(13px / var(--scale, 1)) } .mark rect.term { transform: rotate(45deg) }
.node.cluster { fill: var(--stock-deep); r: calc(9px / var(--scale, 1)); stroke-dasharray: calc(3px / var(--scale, 1)) calc(2px / var(--scale, 1)) }   /* folded marks, tap to open */
.node.outside { stroke-dasharray: calc(1px / var(--scale, 1)) calc(2px / var(--scale, 1)) } g.picked .node { stroke-width: calc(3px / var(--scale, 1)) }
.cell { stroke: none } .cell.pink { fill: url(#pink-60) } .cell.blue { fill: url(#blue-60) } .cell.pink.lit { fill: var(--pink) } .cell.blue.lit { fill: var(--blue) }
.cell.cited { stroke: var(--black); stroke-dasharray: 4 4 } .cell.extracted { stroke: var(--black); stroke-dasharray: 6 3 1 3 }
text.colophon { font: .6875rem var(--font-data); fill: var(--black-60) }

/* The passes travel along the lineage: each edge draws itself one wave after the mark it grows from, the disc lands, the name follows.
   Pink prints first, from the root upward; blue follows, from the root downward. The second print (print-blue) moves only the blue drum. */
@keyframes draw { from { stroke-dashoffset: var(--length) } to { stroke-dashoffset: 0 } }
@keyframes pop { from { r: 0; opacity: 0 } }   /* a square has no r, so it lands by opacity alone */
@keyframes fade { from { opacity: 0 } }
.print-both .edge, .print-blue .edge.down { animation: draw 300ms ease-out both; animation-delay: calc(var(--generation) * var(--wave)); stroke-dasharray: var(--length) }
.print-both .edge.down { animation-delay: calc((var(--generation) + var(--depth-up)) * var(--wave)) }
.print-both .edge.cited, .print-blue .edge.cited.down { animation-name: fade; stroke-dasharray: calc(4px / var(--scale, 1)) calc(4px / var(--scale, 1)) }
.print-both .edge.extracted, .print-blue .edge.extracted.down { animation-name: fade; stroke-dasharray: calc(6px / var(--scale, 1)) calc(3px / var(--scale, 1)) calc(1px / var(--scale, 1)) calc(3px / var(--scale, 1)) }
.print-both .edge.authored, .print-blue .edge.authored.down, .print-both .edge.coauthored { animation-name: fade; stroke-dasharray: calc(1px / var(--scale, 1)) calc(3px / var(--scale, 1)) }
.print-both .cell, .print-blue .cell.down { animation-name: fade; stroke-dasharray: none }
.print-both .node, .print-both .root, .print-blue .down .node, .print-blue .root.down { animation: pop 200ms ease-out both; animation-delay: calc(var(--generation) * var(--wave) + 200ms) }
.print-both .down .node, .print-both .root.down { animation-delay: calc((var(--generation) + var(--depth-up)) * var(--wave) + 200ms) }
.print-both .label, .print-blue .label.down { animation: fade 300ms ease-out both; animation-delay: calc(var(--generation) * var(--wave) + 300ms) }
.print-both .label.down { animation-delay: calc((var(--generation) + var(--depth-up)) * var(--wave) + 300ms) }
.still .label { animation: fade 300ms ease-out both }
@media (prefers-reduced-motion: reduce) { #scene [class] { animation: none } }
:root[data-motion="still"] #scene [class] { animation: none }   /* the footer's still button; the export has no attribute and keeps the media query above */

/* Paper tooth: five percent of noise over the sheet, the one raster on the stock. It sits under the cards and never enters the export. */
.stage::after { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .05; mix-blend-mode: var(--blend); background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E") }
