@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.label.end { text-anchor: end } text.label.end:not(.turned) { transform: translate(calc(-10px / var(--scale, 1)), 0) }   /* a lifeline's name, left of its row (groups.js); the chord's, on the left half */
text.label.under { text-anchor: middle; transform: translate(0, calc(10px / var(--scale, 1))) }   /* a fingerprint's name, under its glyph (chord.js) */
.petal { stroke: var(--stock); stroke-width: calc(1px / var(--scale, 1)) } .petal.s0 { fill: var(--blue) } .petal.s1 { fill: var(--black); opacity: .75 } .petal.s2 { fill: var(--black); opacity: .5 } .petal.s3 { fill: var(--black); opacity: .3 }
.ribbon { fill: var(--black-30); stroke: var(--stock); stroke-width: calc(1px / var(--scale, 1)) } .ribbon.hot { fill: var(--blue); opacity: .75 } .chord-arc { fill: var(--black-60) } .chord-arc.hot { fill: var(--blue) }
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)) } circle.grid { fill: none } .band { fill: var(--stock-deep) }   /* the portrait's year rings (circle.js) */
.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.inferred { stroke-dasharray: calc(1px / var(--scale, 1)) calc(2px / var(--scale, 1)); stroke-linecap: round }   /* a measured rule's guess: dotted, its rate in the panel */
.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.beside { stroke: var(--black); stroke-dasharray: calc(3px / var(--scale, 1)) calc(3px / var(--scale, 1)); opacity: .6 }   /* a committee, referee, examiner, opponent or jury edge, beside the lineage (beside.js) */
.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)) }
/* The magnifier (stage.js magnify): a mark near the pointer grows by --zoom and its name with it, easing both ways. */
g.mark { --zoom: 1 } .node, .root, .mark rect, text.label { transition: r 120ms ease-out, width 120ms ease-out, height 120ms ease-out, font-size 120ms ease-out }
g.mark.hot .node { r: calc(var(--zoom) * 5px / var(--scale, 1)) } g.mark.hot .node.near, g.mark.hot .root { r: calc(var(--zoom) * 7px / var(--scale, 1)) } g.mark.hot .node.cluster { r: calc(var(--zoom) * 9px / var(--scale, 1)) }
g.mark.hot rect { --side: calc(var(--zoom) * 10px / var(--scale, 1)) } g.mark.hot rect.near, g.mark.hot rect.root { --side: calc(var(--zoom) * 13px / var(--scale, 1)) }
text.label.hot { font-size: calc(15px / var(--scale, 1)); font-weight: 700; paint-order: stroke; stroke: var(--stock); stroke-width: calc(3px / var(--scale, 1)) }
/* Where a line ends (fold.js stops): a cap, a bar under or over the disc, where the fetched record holds nobody past this person on that side; a stub, a short tick, where the bound cut a person with more in the record. */
.mark rect.cap { width: calc(10px / var(--scale, 1)); height: calc(1.5px / var(--scale, 1)); x: calc(-5px / var(--scale, 1)); y: calc(9.5px / var(--scale, 1)); fill: var(--black) }
.mark rect.stub { width: calc(1.5px / var(--scale, 1)); height: calc(6px / var(--scale, 1)); x: calc(-.75px / var(--scale, 1)); y: calc(8px / var(--scale, 1)); fill: var(--black) }
.mark rect.cap.above { y: calc(-11px / var(--scale, 1)) } .mark rect.stub.above { y: calc(-14px / var(--scale, 1)) }
/* The lenses and the narrow bar (lenses.js): a mark faded by the year range or the institution, the person found, an edge of a kind turned off; here, so the export carries them. */
.faded { opacity: .15 } g.found .node { stroke: var(--pink); stroke-width: calc(3px / var(--scale, 1)) } .edge.off { display: none }
.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 } .cell.inferred { stroke: var(--black); stroke-dasharray: 1 2 }
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. */
@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 { 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 { animation-name: fade; stroke-dasharray: calc(4px / var(--scale, 1)) calc(4px / var(--scale, 1)) }
.print-both .edge.extracted { 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.inferred { animation-name: fade; stroke-dasharray: calc(1px / var(--scale, 1)) calc(2px / var(--scale, 1)) }
.print-both .edge.authored, .print-both .edge.coauthored { animation-name: fade; stroke-dasharray: calc(1px / var(--scale, 1)) calc(3px / var(--scale, 1)) }
.print-both .edge.beside { animation-name: fade; stroke-dasharray: calc(3px / var(--scale, 1)) calc(3px / var(--scale, 1)) }
.print-both .cell { animation-name: fade; stroke-dasharray: none }
.print-both .node, .print-both .root { 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 { 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) }
.print-both .mark :is(.cap, .stub) { animation: fade 300ms ease-out both; animation-delay: calc(var(--generation) * var(--wave) + 300ms) }   /* the stops appear with the labels */
.print-both .down :is(.cap, .stub) { 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") }
