/* ===================== Numogram (Decimal Labyrinth) =====================
 *
 * Black apertures, toxic green currents and hot pink gates. The mathematics
 * is inherited; the visual language is Cyphers' own.
 *
 * Everything here is scoped under .numField and sets its own colours from
 * scratch. The site's theme variables deliberately do not reach in: the
 * artefact keeps its own palette whatever skin the rest of the calculator is
 * wearing.
 *
 * Five tokens carry the palette: field and bone establish the plate; current
 * and channel separate the two kinds of motion; gate ink keeps the small
 * aperture numerals legible in both readings. [data-invert="1"] keeps those
 * roles but moves them onto a bone field - no second stylesheet, no duplicated
 * diagram.
 */

/* The site's shared panel wrapper is padded 0.5em 0em 0.75em 1em - one em on
 * the left, nothing on the right - and sized with width:fit-content. On every
 * other panel that is invisible, because their background is the panel's own.
 * Here the field IS the artwork, so the missing right-hand padding read
 * as the field being cut off, and the 1em on the left as it being off-centre.
 * This panel supplies its own even box instead.
 */
.numogramBG {
	padding: 0.6em;
	width: fit-content;
	max-width: 100%;
	margin: 0.5em auto;
	box-sizing: border-box;
}

.numField {
	--num-field: #020303;
	--num-ink: #f1eadb;
	--num-bone: #f1eadb;
	--num-muted: #8d9891;
	--num-zone-text: #f1eadb;
	--num-glow: 0 0 7px rgba(57, 255, 20, 0.5);
	--num-svg-glow: none;
	--num-rule: rgba(57, 255, 20, 0.22);
	--num-panel: rgba(7, 11, 9, 0.82);

	/* The layers, rather than the individual zones, own the colour. Black zones
	 * are addresses; green currents and pink gate courses are motion. */
	--num-zone-fill: #000000;
	--num-current: #39ff14;
	--num-channel: #ff1493;
	--num-gate-ink: #ff1493;
	--num-faint: 0.34;             /* unselected line work sits back */
	background:
		radial-gradient(circle at 50% 43%, rgba(57, 255, 20, 0.045), transparent 46%),
		linear-gradient(rgba(255, 20, 147, 0.015), rgba(57, 255, 20, 0.012)),
		var(--num-field);
	color: var(--num-ink);
	padding: 1.5em;                /* even on all four sides */
	margin: 0 auto;
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
	/* A column: the head and the foot take what they need, the diagram takes
	 * the rest. numogramFit() sets the height from the space actually
	 * available, so the whole artefact fits the window without scrolling
	 * whatever the window is - which a vh unit cannot do from inside a
	 * scrolling container. */
	display: flex;
	flex-direction: column;
	min-height: 0;
	/* Monospace throughout: the figure is a table of numbers as much as it is
	 * a drawing, and a fixed advance keeps the zone and gate numerals on the
	 * same rhythm as the readings beside them. */
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	/* no radius, no border: it is a field, not a card - but the field itself
	 * carries a faint bloom so it reads as lit rather than printed */
	box-shadow: inset 0 0 130px rgba(57, 255, 20, 0.055), inset 0 0 1px rgba(255, 20, 147, 0.4);
}

/* The bone-field artefact, one click away on CONTRAST. Nothing glows here: it is
 * ink on paper, so the lit tokens are dropped rather than recoloured. */
.numField[data-invert="1"] {
	--num-field: #f1eadb;
	--num-ink: #050606;
	--num-bone: #050606;
	--num-muted: #38413c;
	--num-zone-text: #f1eadb;
	--num-glow: none;
	--num-svg-glow: none;
	--num-rule: rgba(0, 0, 0, 0.3);
	--num-panel: transparent;
	--num-zone-fill: #050606;
	--num-current: #087c27;
	--num-channel: #aa005f;
	--num-gate-ink: #ff1493;
	--num-faint: 1;
	background: var(--num-field);
	box-shadow: none;
}

/* ---- typography: two words, set small, out of the diagram's way -------- */

/* The name, then the two controls that set up a reading, on one line. Wraps
 * rather than squeezing: below about 700px the trace group drops to its own
 * row instead of the field shrinking to nothing. */
.numTitle {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.7em 1.1em;
	margin: 0 0 0.9em;
}
.numTitleCtl {
	display: flex;
	align-items: center;
	gap: 0.45em;
	flex: 0 1 auto;
	min-width: 0;
}
.numTitleTrace { flex: 1 1 auto; }
.numTitleCosmo { margin-left: auto; }
.numTitleLabel {
	font-size: 0.66em;
	font-weight: 700;
	letter-spacing: 0.2em;
	opacity: 0.6;
	white-space: nowrap;
}
.numTitleCosmo .numTitleLabel { color: var(--num-channel); opacity: 0.9; }
.numTitleTrace .numTitleLabel { color: var(--num-current); opacity: 0.9; }
.numTitleCosmo .numSelect { color: var(--num-channel); border-bottom-color: var(--num-channel); }
.numTitleTrace .numNumberInput {
	flex: 1 1 4em;
	min-width: 3.5em;
	max-width: 9em;
	color: var(--num-current);
	border-bottom-color: var(--num-current);
}
.numTitleTrace .numTraceMsg { margin: 0; font-size: 0.66em; white-space: nowrap; }
.numField[data-invert="1"] .numTitleLabel,
.numField[data-invert="1"] .numTitle .numSelect,
.numField[data-invert="1"] .numTitle .numNumberInput {
	color: var(--num-ink);
	border-bottom-color: var(--num-ink);
}
.numTitleMain {
	font-size: 1.05em;
	font-weight: 700;
	letter-spacing: 0.34em;
	color: var(--num-bone);
	text-shadow: -1.5px 0 var(--num-channel), 1.5px 0 var(--num-current), 0 0 9px rgba(57, 255, 20, 0.34);
}
.numTitleSub {
	font-size: 0.72em;
	font-weight: 600;
	letter-spacing: 0.3em;
	opacity: 0.62;
	color: var(--num-muted);
}

/* ---- the diagram ------------------------------------------------------
 *
 * The SVG keeps its 2:3 ratio at every width. It is given room rather than
 * stretched: the composition matters more than filling the panel.
 */

/* Diagram and annotation side by side. The figure is roughly 0.6 as wide as it
 * is tall, so standing the text in the space beside it is what makes the field
 * an even box instead of a wide one with a narrow drawing adrift in it. */
.numBody {
	display: flex;
	align-items: stretch;
	gap: 1.5em;
	flex: 1 1 auto;
	min-height: 0;
	/* Nothing in here may push the field taller than the height it was given:
	 * the panel is sized to the window once, and a column that outgrows it
	 * scrolls on its own. Without this the field was holding 1158px of content
	 * in 617px of box and the bottom was simply cut off. */
	flex-wrap: nowrap;
	overflow: hidden;
}
.numSide {
	min-width: 0;
	align-self: stretch;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-top: 0.2em;
	padding-right: 0.4em;
	text-align: left;
}
.numSide::-webkit-scrollbar { width: 4px; }
/* the thumb was full-strength neon and read as a bar of light down the
 * side of the panel rather than as a scrollbar */
.numSide::-webkit-scrollbar-thumb { background: var(--num-rule); border-radius: 2px; }

/* The standing right rail reads the plate. The left-named legacy surface is
 * now the auto-fit apparatus grid below the primary body; its class remains an
 * internal compatibility name, not a claim that it is a side column. */
.numSideLeft {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
	gap: 0.65em;
	width: 100%;
	max-width: none;
	max-height: 32vh;
	padding: 0.2em 0.25em 0.25em 0;
}
.numSideRight {
	flex: 0 1 28em;
	width: clamp(24em, 28vw, 30em);
	min-width: 20em;
	max-width: 30em;
	max-height: 100%;
	border-left: 1px solid rgba(255, 20, 147, 0.34);
	padding-left: 1.1em;
}
.numSideRight .numActionRow { margin-top: 0; margin-bottom: 0.7em; }
.numRailLabel {
	font-size: 0.62em;
	letter-spacing: 0.28em;
	color: var(--num-channel);
	margin: 0 0 0.45em;
}

.numApparatus {
	flex: 0 0 auto;
	margin-top: 0.8em;
	border-top: 1px solid rgba(57, 255, 20, 0.28);
	color: var(--num-muted);
}
.numApparatus > summary {
	display: flex;
	align-items: center;
	gap: 1em;
	min-height: 44px;
	list-style: none;
	cursor: pointer;
	font-size: 0.68em;
	letter-spacing: 0.24em;
	color: var(--num-current);
}
.numApparatus > summary::-webkit-details-marker { display: none; }
.numApparatus > summary::before {
	content: "+";
	font-size: 1.25em;
	color: var(--num-channel);
}
.numApparatus[open] > summary::before { content: "−"; }
.numApparatus > summary i {
	font-style: normal;
	letter-spacing: 0.08em;
	color: var(--num-muted);
	opacity: 0.72;
}
.numApparatus > summary:focus-visible { outline: 2px solid var(--num-current); outline-offset: 2px; }
.numApparatusInner {
	max-height: 40vh;
	overflow: auto;
	padding: 0.75em 0.25em 0.25em;
}
.numApparatusHead {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	padding: 0 0 0.7em;
	border-bottom: 1px solid rgba(255, 20, 147, 0.18);
}
.numApparatusHead .numCtlRow { margin: 0; }

/* ---- the lookup lists ---------------------------------------------------
 * Name, the pair it runs between, and where it lands. Zone addresses stay in
 * bone while current and gate names carry their layer colours.
 */
.numList { display: flex; flex-direction: column; }
.numListRow {
	display: flex;
	align-items: center;
	gap: 0.5em;
	width: 100%;
	background: none;
	border: none;
	padding: 0.26em 0;
	color: inherit;
	font: inherit;
	font-size: 0.78em;
	letter-spacing: 0.06em;
	cursor: pointer;
	text-align: left;
}
.numListRow:hover { background: rgba(255, 255, 255, 0.045); }
.numListRow:focus-visible { outline: 2px solid var(--num-ink); outline-offset: -2px; }
.numListName { flex: 1 1 auto; font-weight: 600; }
.numListCurrent { color: var(--num-current); }
.numListGate { color: var(--num-channel); }
.numListPair, .numListTo { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 0.18em; }
.numListPair i, .numListTo i { font-style: normal; opacity: 0.4; }

/* a zone number wherever it appears in prose or in a list */
.numTag { font-weight: 700; color: var(--num-bone); }

/* The left column stays on the left. An earlier rule here dropped it under
 * the figure below 1100px, which at a typical window width meant it was never
 * on the left at all - it just sat at the bottom. The columns narrow and
 * scroll inside themselves instead; nothing reorders. */

.numStage {
	display: flex;
	justify-content: center;
	align-items: stretch;
	gap: 1.4em;
	flex: 1 1 auto;
	min-width: 0;
	min-height: 0;
	max-height: 100%;
	overflow: hidden;
}

/* Two panels inside the stage: the canonical figure and the selected
 * cosmology beside it. Keeping the attribution panel outside the Numogram
 * prevents a secondary model from rewriting the canonical geometry. */
.numPanel {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	min-width: 0;
	min-height: 0;
	max-height: 100%;
	overflow: hidden;
}
.numPanelMain { flex: 1 1 auto; }
.numPanelGeo { flex: 0 0 34%; max-width: 22em; }
.numPanelGeo:empty { display: none; }

.numGeoSvg {
	display: block;
	width: 100%;
	height: 100%;
	max-height: 100%;
	overflow: visible;
	color: var(--num-ink);
}
.numSvg {
	display: block;
	/* Fill the stage and let preserveAspectRatio="xMidYMid meet" do the
	 * fitting: the drawing scales by the tighter of the two axes and stays
	 * centred, so it is never stretched on one axis and never clipped. */
	width: 100%;
	height: 100%;
	min-height: 0;
	overflow: visible;
	color: var(--num-ink);
}
/* The bloom is applied to the figure as a whole rather than per element: one
 * filter pass, and the overlapping channels light each other the way the
 * strokes actually cross rather than each carrying its own halo. */
.numSvg, .numGeoSvg { filter: var(--num-svg-glow); }

/* Thin technical line work across the canonical plate. Stroke widths are in
 * user units, so rendering and clearance proofs share one geometry and scale
 * together. */

/* ---- zones: black apertures, bone addresses ----------------------------- */
.numZone { --zc: var(--num-bone); }

.numZoneRing {
	fill: var(--num-zone-fill);
	stroke: none;
}
.numZoneBracket {
	fill: none;
	stroke: var(--num-bone);
	stroke-width: 2.4;
	stroke-linecap: square;
	stroke-linejoin: miter;
	opacity: 0.68;
}
.numGateRing {
	fill: var(--num-zone-fill);
	stroke: none;
}
.numGateLeader {
	fill: none;
	stroke: var(--num-channel);
	stroke-width: 3.5;
	stroke-linecap: square;
}

.numZoneText,
.numGateText {
	text-anchor: middle;
	dominant-baseline: central;
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-weight: 500;
	pointer-events: none;
}
.numZoneText { fill: var(--num-zone-text); }
.numGateText { fill: var(--num-gate-ink); font-size: 19px; font-weight: 700; }

.numChLine,
.numBondLine,
.numCurrentFilament,
.numCurrentChevron {
	fill: none;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* Gate courses: hot pink, narrow, and set back. There are far more of these
 * than anything else in the figure, so the green current filaments must keep
 * the heavier visual rhythm and win at exact crossings. */
.numChLine {
	stroke: var(--num-channel);
	stroke-width: 6.5;
	opacity: 0.72;
}
.numChHead {
	fill: none;
	stroke: var(--num-channel);
	stroke-width: 6;
	stroke-linecap: round;
	stroke-linejoin: round;
	opacity: 0.82;
}

.numBondLine {
	stroke: var(--num-current);
	stroke-width: 3.8;
	opacity: 0.42;
}
.numCurrentFilament {
	stroke: var(--num-current);
	stroke-width: 6;
}
.numFilament0, .numFilament4 { opacity: 0.42; }
.numFilament1, .numFilament3 { opacity: 0.72; }
.numFilament2 { opacity: 1; }
.numCurrentChevron {
	stroke: var(--num-current);
	stroke-width: 8;
}
/* the bloom belongs to this layer alone, not to the whole drawing */
.numLayerCurrents { filter: drop-shadow(0 0 4px rgba(57, 255, 20, 0.46)); }
.numField[data-invert="1"] .numLayerCurrents { filter: none; }

/* Gate wells must fully occlude the channel beneath them at rest. Region and
 * reading focus may still dim the whole semantic layer intentionally. */
.numGate { opacity: 1; }

/* ---- zones: selectable, but never buttons ------------------------------ */

.numZone { cursor: pointer; }
.numZone:focus { outline: none; }

.numZone:hover .numZoneBracket,
.numZone:focus-visible .numZoneBracket {
	stroke: var(--num-current);
	stroke-width: 3.4;
	opacity: 1;
}
.numZone:focus-visible .numZoneBracket { stroke-dasharray: 5 4; }
.numZone.numPick .numZoneBracket,
.numZone.numPick .numZoneMark {
	stroke: var(--num-current);
	fill: var(--num-current);
	opacity: 1;
	filter: drop-shadow(0 0 7px rgba(57, 255, 20, 0.72));
}

.numGate.numOn .numGateRing { fill: var(--num-gate-ink); }
.numGate.numOn .numGateText { fill: var(--num-zone-fill); }

.numChannel.numOn .numChLine { stroke-width: 10.5; opacity: 1; }
.numCurrent.numOn .numCurrentFilament { stroke-width: 8.5; opacity: 1; }
.numCurrent.numOn .numBondLine { stroke-width: 6; opacity: 0.86; }

/* ---- dimming: selection, time system, layers ---------------------------
 *
 * Everything unselected recedes by losing opacity rather than changing hue,
 * so the black/green relationship holds at every state.
 */

.numSvg[data-mode="torque"] .numCurrent:not([data-sys="torque"]),
.numSvg[data-mode="torque"] .numChannel:not([data-sys="torque"]),
.numSvg[data-mode="torque"] .numGate:not([data-sys="torque"]),
.numSvg[data-mode="torque"] .numZone:not([data-sys="torque"]),
.numSvg[data-mode="warp"] .numCurrent:not([data-sys="warp"]),
.numSvg[data-mode="warp"] .numChannel:not([data-sys="warp"]),
.numSvg[data-mode="warp"] .numGate:not([data-sys="warp"]),
.numSvg[data-mode="warp"] .numZone:not([data-sys="warp"]),
.numSvg[data-mode="plex"] .numCurrent:not([data-sys="plex"]),
.numSvg[data-mode="plex"] .numChannel:not([data-sys="plex"]),
.numSvg[data-mode="plex"] .numGate:not([data-sys="plex"]),
.numSvg[data-mode="plex"] .numZone:not([data-sys="plex"]) {
	opacity: 0.32;
}

/* ---- a region, lit -------------------------------------------------------
 *
 * Narrowing to a time system used to do one thing: drop everything else to
 * 0.32. That reads as "the rest went quiet", not as "this one is live". The
 * parts that belong to the chosen region now carry its own colour as a bloom
 * and pulse gently, so choosing a region shows you the circuit rather than
 * just hiding its surroundings.
 */
.numSvg[data-mode="torque"] { --reg: var(--num-current); }
.numSvg[data-mode="warp"]   { --reg: var(--num-channel); }
.numSvg[data-mode="plex"]   { --reg: var(--num-bone); }

.numSvg[data-mode="torque"] .numCurrent[data-sys="torque"],
.numSvg[data-mode="torque"] .numZone[data-sys="torque"],
.numSvg[data-mode="warp"] .numCurrent[data-sys="warp"],
.numSvg[data-mode="warp"] .numZone[data-sys="warp"],
.numSvg[data-mode="plex"] .numCurrent[data-sys="plex"],
.numSvg[data-mode="plex"] .numZone[data-sys="plex"] {
	filter: drop-shadow(0 0 6px var(--reg));
	animation: numRegionBreathe 2.4s ease-in-out infinite;
}
@keyframes numRegionBreathe {
	0%, 100% { filter: drop-shadow(0 0 4px var(--reg)); }
	50%      { filter: drop-shadow(0 0 11px var(--reg)); }
}

/* the flat green artefact takes the narrowing without the light show */
.numField[data-invert="1"] .numSvg [data-sys] { animation: none; filter: none; }
@media (prefers-reduced-motion: reduce) {
	.numSvg [data-sys] { animation: none !important; }
}

/* the bars beside the region names, in the colours above */
.numLayerBar[data-region="torque"] { background: var(--num-current); }
.numLayerBar[data-region="warp"]   { background: var(--num-channel); }
.numLayerBar[data-region="plex"]   { background: var(--num-bone); }
.numLayerBar[data-region="all"]    { background: var(--num-current); }

.numSvg[data-layer-currents="0"] .numCurrentPath,
.numSvg[data-layer-gates="0"] .numLayerGates,
.numSvg[data-layer-gates="0"] .numLayerChannels,
.numSvg[data-layer-zones="0"] .numLayerZones { display: none; }

/* The source pair and its current are independently truthful layers. */
.numSvg[data-layer-syzygies="0"] .numSyzygyBond { display: none; }

/* ---- annotation: typography, not a panel ------------------------------- */

.numAnnot {
	margin: 0;
	max-width: 100%;
	font-size: 0.76em;
	font-weight: 500;
	letter-spacing: 0.15em;
	line-height: 1.5;
	color: var(--num-ink);
	min-height: 1.5em;
}
.numAnnotHead { font-weight: 700; letter-spacing: 0.24em; }
.numAnnotFoot { opacity: 0.6; }
.numAnnotIdle { opacity: 0.55; }

/* SELECTED / RELATION / ARITHMETIC / STRUCTURES. The label column is set
 * lighter than its value so the eye reads the values first. */
.numAnnotGroup { margin-bottom: 0.7em; }
.numAnnotLabel {
	font-size: 0.88em;
	font-weight: 600;
	letter-spacing: 0.2em;
	opacity: 0.55;
}
.numAnnotRow { display: flex; gap: 0.6em; }
.numAnnotRow > span:first-child { flex: 0 0 5.4em; opacity: 0.55; }
.numAnnotExp {
	margin-top: 0.5em;
	font-size: 0.86em;
	letter-spacing: 0.14em;
	opacity: 0.55;
}

/* ---- controls: text, set in a row, no containers ----------------------- */

/* Every visible HTML control in the Numogram meets the same target floor.
 * Keeping the rule at the feature boundary also covers model-specific cards,
 * list rows and future folded controls instead of certifying a hand-picked
 * subset. SVG targets have their own measured geometry in numogram.js. */
.numogramBG button,
.numogramBG input:not([type="hidden"]),
.numogramBG select,
.numogramBG summary {
	box-sizing: border-box;
	min-width: 44px;
	min-height: 44px;
}

.numCtlRow {
	display: flex;
	flex-wrap: wrap;
	gap: 0.9em 1.15em;
	max-width: 100%;
	margin: 1.1em 0 0;
}
.numCtl {
	background: none;
	border: none;
	box-sizing: border-box;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
	padding: 0 0.65em;
	margin: 0;
	font-family: inherit;
	font-size: 0.74em;
	font-weight: 600;
	letter-spacing: 0.19em;
	color: var(--num-ink);
	opacity: 0.45;
	cursor: pointer;
	text-transform: uppercase;
}
.numCtl:hover { opacity: 0.8; }
.numCtl.numCtlOn { opacity: 1; box-shadow: 0 2px 0 0 var(--num-ink); }
.numCtlPlain { opacity: 0.65; }
.numCtl:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 3px; }

.numNumberRow { align-items: baseline; gap: 0.7em; }
.numNumberInput {
	background: none;
	border: none;
	border-bottom: 2px solid var(--num-ink);
	color: var(--num-ink);
	font-family: inherit;
	font-size: 0.86em;
	font-weight: 600;
	letter-spacing: 0.2em;
	width: 6.5em;
	padding: 0.15em 0;
	border-radius: 0;
}
.numNumberInput:focus { outline: none; border-bottom-width: 4px; }
.numNumberOut { margin-top: 0.6em; }

/* ---- the source note ---------------------------------------------------
 * Outside the field, in the site's own voice, so it cannot be mistaken for
 * part of the diagram. */

/* The panel around this is sized to fit its contents, and a paragraph's
 * max-content width is the whole sentence on one line - which was stretching
 * that panel to the full width of the page and leaving the field adrift
 * in the middle of it. width:0 keeps the note out of the intrinsic size;
 * min-width:100% then fills whatever the field settled on. */
.numNote {
	text-align: left;
	width: 0;
	min-width: 100%;
	box-sizing: border-box;
	margin: 1.1em 0 0;
	padding: 0 0.2em;
	font-size: 0.82em;
	line-height: 1.6;
	color: var(--text-3, #a1a1a1);
}

/* ---- motion ------------------------------------------------------------
 * There is none by default. A lit current traces once when it is selected,
 * and that is the only animation in the feature. */

@keyframes numTrace {
	from { stroke-dashoffset: 320; }
	to { stroke-dashoffset: 0; }
}
.numCurrent.numOn .numFilament2,
.numChannel.numOn .numChLine {
	stroke-dasharray: 320;
	animation: numTrace 0.75s ease-out 1;
}

@media (prefers-reduced-motion: reduce) {
	.numCurrent.numOn .numFilament2,
	.numChannel.numOn .numChLine {
		stroke-dasharray: none;
		animation: none;
	}
}

/* ---- narrow screens ----------------------------------------------------
 *
 * The figure keeps its proportions and its vertical arrangement; it simply
 * gets smaller. Nothing is rearranged, nothing is dropped. Touch targets are
 * handled by the SVG scaling with the viewport rather than by enlarging the
 * circles, so the composition is unchanged.
 */

/* Narrow: the annotation goes back under the diagram, which is the only
 * arrangement that keeps the figure a usable size on a phone. */
@media (max-width: 760px) {
	/* A column, with an order set rather than left to the source. In source
	 * order the left-hand controls come first, which on a phone put five
	 * panels of switches above the figure they act on before you had seen it.
	 * Figure first, then what it says, then the controls. */
	.numBody {
		display: flex;
		flex-direction: column;
		overflow: visible;
		max-height: none;
		gap: 1.1em;
	}
	.numStage { order: 1; flex-direction: column; align-items: center; justify-content: center;
	            gap: 1.1em; max-height: none; overflow: visible; }
	.numSideRight { order: 2; border-left: none; border-top: 1px solid rgba(255, 20, 147, 0.34);
	                padding: 1em 0 0; }
	.numSideLeft { order: 3; padding-right: 0; }

	/* full width, and scrolling with the page rather than inside themselves -
	 * a panel that scrolls within a scrolling page is unusable on a phone */
	.numSide {
		flex: 0 0 auto;
		width: 100%;
		min-width: 0;
		max-width: none;
		max-height: none;
		overflow-y: visible;
		margin-top: 0;
	}
	.numPanel { max-height: none; overflow: visible; }
	.numPanelGeo { flex: 0 0 auto; width: 100%; max-width: 20em; }
	.numGeoSvg { height: auto; max-height: 46vh; }
	.numSvg { height: auto; width: 100%; max-height: 58vh; }

	/* the title's controls each take their own line rather than crowding */
	.numTitle { gap: 0.5em 0.8em; }
	.numTitleCosmo { margin-left: 0; }
	.numTitleCtl { flex: 1 1 100%; }
	.numTitleTrace .numNumberInput { max-width: none; }

	/* two-up, so five folded panels are not five full-width rows */
	.numSideLeft { display: flex; flex-wrap: wrap; gap: 0 0.6em; }
	.numSideLeft > .numGroupBox { flex: 1 1 13em; }
	.numApparatusInner { max-height: none; overflow: visible; }
	.numApparatusHead { align-items: flex-start; flex-direction: column; }

	/* the deck scrolls with the page here rather than inside itself */
	.numDeck { max-height: none; overflow-y: visible; padding-right: 0; }
}

@media (max-width: 560px) {
	.numField { padding: 1em; }
	.numSvg { height: 48vh; min-height: 260px; }
	.numCtlRow { gap: 0.9em; }
	.numAnnot { font-size: 0.72em; letter-spacing: 0.12em; }
	.numTitleMain { font-size: 0.95em; letter-spacing: 0.26em; }
	.numNote { padding: 0 1em; }
}

/* A short window - a laptop, or the panel opened low - shrinks the figure
 * rather than pushing the controls off the bottom. */
@media (max-height: 760px) {
	.numField { padding: 1em; }
	.numAnnot { font-size: 0.72em; }
	.numCtlRow { margin-top: 0.8em; }
}

/* ---- the reading: labels, values, provenance --------------------------
 *
 * Editorial annotation rather than an application panel: a small label, the
 * value under it, and a status line wherever the claim is not plain Numogram
 * structure. No boxes, no rules, no containers.
 */

.numAnnotBig {
	font-size: 1.6em;
	font-weight: 700;
	letter-spacing: 0.12em;
	line-height: 1.1;
	margin: 0.1em 0 0.15em;
}

/* Provenance. Three statuses, distinguished by weight and opacity only - the
 * field is green and black, and a status tag is not a reason to break that. */
.numStatus {
	font-size: 0.78em;
	font-weight: 600;
	letter-spacing: 0.16em;
	margin: -0.35em 0 0.7em;
	opacity: 0.5;
}
.numStatus-canonical { opacity: 0.5; }
.numStatus-attested { opacity: 0.66; }
.numStatus-experimental {
	opacity: 0.85;
	box-shadow: inset 0 -1px 0 0 currentColor;
	display: inline-block;
	padding-bottom: 1px;
}

/* AQ character values, set as a run rather than a table - it is a reading of
 * a word, so it should read along the line like one. */
.numChars { display: flex; flex-wrap: wrap; gap: 0.5em 0.75em; margin: 0.15em 0 0.4em; }
.numChar {
	font-size: 0.92em;
	letter-spacing: 0.06em;
	white-space: nowrap;
}
.numChar b { font-weight: 700; margin-left: 0.28em; }

/* ---- the trace input --------------------------------------------------- */

.numTraceHead { align-items: baseline; gap: 1em; margin-top: 1.4em; }
.numTraceHead .numAnnotLabel { margin: 0; }
.numTraceMsg {
	font-size: 0.74em;
	font-weight: 600;
	letter-spacing: 0.16em;
	margin-top: 0.4em;
	min-height: 1.1em;
	opacity: 0.8;
}

/* ---- recent traces ----------------------------------------------------- */

.numHistory { margin-top: 1.2em; display: flex; flex-wrap: wrap; gap: 0.5em 0.9em; align-items: baseline; }
.numHistory .numAnnotLabel { flex: 0 0 100%; margin-bottom: 0.1em; }
.numHistBtn {
	background: none;
	border: none;
	padding: 0;
	font-family: inherit;
	font-size: 0.74em;
	font-weight: 600;
	letter-spacing: 0.12em;
	color: var(--num-ink);
	opacity: 0.55;
	cursor: pointer;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.numHistBtn:hover { opacity: 0.9; }
.numHistBtn:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 2px; }

/* ---- sources, folded away ---------------------------------------------- */

/* The full-width neon rule that used to run above this was the brightest
 * thing in the panel and sat under the figure doing nothing but separating a
 * credit line. A hairline at a fraction of the weight does the same job. */
.numDetails {
	margin-top: 0.9em;
	border-top: 1px solid var(--num-rule);
	padding-top: 0.6em;
	color: var(--num-ink);
	opacity: 0.7;
}
.numDetails > summary {
	display: flex;
	align-items: center;
	cursor: pointer;
	font-size: 0.74em;
	font-weight: 600;
	letter-spacing: 0.22em;
	opacity: 0.7;
	list-style: none;
}
.numDetails > summary::-webkit-details-marker { display: none; }
.numDetails > summary::before { content: "+ "; }
.numDetails[open] > summary::before { content: "\2212 "; }
.numDetails > summary:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 3px; }
.numDetailsBody { margin-top: 0.9em; column-gap: 2.2em; }
.numSrc { margin-bottom: 1em; break-inside: avoid; }
.numSrcClaim { font-size: 0.82em; line-height: 1.5; }
.numSrcFrom { font-size: 0.78em; line-height: 1.5; opacity: 0.62; margin-top: 0.25em; }

@media (min-width: 760px) {
	.numDetailsBody { column-count: 2; }
}

/* ---- the geocentric overlay (experimental) ----------------------------
 *
 * Secondary to the Numogram throughout: thinner than any canonical line,
 * lower in contrast, and drawn underneath the zones so it can never cover
 * them. With the mode off none of this is rendered at all, so the canonical
 * diagram is exactly what it was.
 */

.numGeoSphere {
	fill: none;
	stroke: var(--num-ink);
	stroke-width: 1.2;
	opacity: 0.5;
	vector-effect: non-scaling-stroke;
}
.numGeoMark {
	fill: var(--num-field);
	stroke: var(--num-ink);
	stroke-width: 1.8;
	vector-effect: non-scaling-stroke;
}
.numGeoDot { fill: var(--num-ink); }
.numGeoLabel {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 21px;
	font-weight: 600;
	letter-spacing: 0.1em;
	opacity: 0.72;
	pointer-events: none;
}

/* The tie from a body to the zone the Planetwork gives it. Dashed, because it
 * is a correspondence this application is drawing between two systems - not a
 * current, not a channel, and not anything either system asserts. */
.numGeoTie {
	fill: none;
	stroke: var(--num-ink);
	stroke-width: 1.2;
	stroke-dasharray: 3 6;
	opacity: 0.45;
	vector-effect: non-scaling-stroke;
}

.numGeoBody { cursor: pointer; }
.numGeoBody:focus { outline: none; }
.numGeoBody:hover .numGeoSphere,
.numGeoBody:focus-visible .numGeoSphere { opacity: 0.9; stroke-width: 2; }
.numGeoBody:focus-visible .numGeoMark { stroke-dasharray: 3 3; }

.numGeoBody.numOn .numGeoSphere { opacity: 1; stroke-width: 2.4; }
.numGeoBody.numOn .numGeoMark { fill: var(--num-ink); }
.numGeoBody.numOn .numGeoLabel { opacity: 1; font-weight: 700; }
.numGeoBody.numOn .numGeoTie { opacity: 0.85; stroke-width: 2; }

/* Deferent and epicycle, drawn for a selected superior planet. Schematic
 * geometry showing why the model produces an apparent reversal - not a
 * simulation, and not Ptolemy's own figures. */
.numGeoEpicycle {
	fill: none;
	stroke: var(--num-ink);
	stroke-width: 1.6;
	vector-effect: non-scaling-stroke;
}
.numGeoApparent {
	fill: none;
	stroke: var(--num-ink);
	stroke-width: 2.2;
	stroke-linecap: round;
	vector-effect: non-scaling-stroke;
}

/* Post-Ptolemaic bodies are held at arm's length even in the drawing. */
.numGeoBody[data-set="extended"] .numGeoSphere { stroke-dasharray: 8 7; }
.numGeoBody[data-set="extended"] .numGeoLabel { opacity: 0.55; }

/* ---- the two controls that lead the column -----------------------------
 *
 * COSMOLOGY and TRACE. Everything else in the side column operates inside
 * whichever cosmology is selected, so these two are set apart from the rest
 * rather than queued among it: full-strength labels against the 0.55 opacity
 * the other labels carry, and a rule under each block.
 */

.numPrimary {
	/* A lit block rather than a hairline-separated run of controls - the two
	 * things that steer the whole figure should look like instruments, not
	 * like the rest of the list. */
	background: var(--num-panel);
	border: 1px solid var(--num-rule);
	border-left: 2px solid var(--num-ink);
	padding: 0.75em 0.85em 0.85em;
	margin-bottom: 0.9em;
}
.numPrimary:first-child { margin-top: 0; }
.numPrimaryLabel {
	font-size: 0.95em;
	font-weight: 700;
	letter-spacing: 0.22em;
	opacity: 1;
	margin: 0;
	text-shadow: var(--num-glow);
}
/* The same instrument block at a lower weight: a hairline all round instead of
 * the lit left edge, for the groups that read the figure rather than change
 * which figure it is. The readout and RECENT take it too, so the column reads
 * as a stack of panels the way the rest of the drawing reads as one system. */
.numGroupBox {
	background: var(--num-panel);
	border: 1px solid var(--num-rule);
	padding: 0.15em 0.85em 0.6em;
	margin-bottom: 0.55em;
}
/* the reading and the history are inside a section now, so they no longer
 * carry a box of their own - that was a border inside a border */
.numGroupBox .numCtlRow:first-of-type { margin-top: 0; }
.numGroupLabel {
	font-size: 0.72em;
	font-weight: 700;
	letter-spacing: 0.22em;
	opacity: 0.55;
	margin-bottom: 0;
}

/* ---- folding sections ---------------------------------------------------
 * The whole header is the hit target, with the caret on the left and, while
 * the section is shut, a note on the right saying what it is holding - ALL,
 * 7/10, ZONE 4 - so folding never costs you the state. */
.numSectionHead {
	display: flex;
	align-items: center;
	gap: 0.55em;
	width: 100%;
	background: none;
	border: none;
	padding: 0.55em 0;
	color: inherit;
	font: inherit;
	cursor: pointer;
	text-align: left;
}
.numSectionHead:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 2px; }
.numSectionHead .numGroupLabel { flex: 1 1 auto; }
.numSectionCaret {
	flex: 0 0 auto;
	width: 0;
	height: 0;
	border-left: 4px solid currentColor;
	border-top: 3.5px solid transparent;
	border-bottom: 3.5px solid transparent;
	opacity: 0.6;
	transform: rotate(90deg);
	transition: transform 0.15s ease;
}
.numSectionShut .numSectionCaret { transform: rotate(0deg); }
.numSectionNote {
	flex: 0 0 auto;
	font-size: 0.68em;
	letter-spacing: 0.14em;
	opacity: 0;
	transition: opacity 0.15s ease;
}

/* ---- the right column, in colour ---------------------------------------
 *
 * Cosmology, Trace and Reading were three identical green headers over three
 * identical green boxes, which made the column read as one undifferentiated
 * block. Each takes a colour of its own now - the same hues the figure
 * already uses, so nothing new is introduced - and carries it in its header,
 * its caret, its left edge and its folded note. */
.numSideRight [data-section="reading"]   { --sec: var(--num-channel); }
.numSideLeft  [data-section="zones"]     { --sec: var(--num-bone); }
.numSideLeft  [data-section="layers"]    { --sec: var(--num-current); }
.numSideLeft  [data-section="regions"]   { --sec: var(--num-channel); }
.numSideLeft  [data-section="currents"]  { --sec: var(--num-current); }
.numSideLeft  [data-section="gates"]     { --sec: var(--num-channel); }

[data-section] .numGroupLabel { color: var(--sec, var(--num-ink)); opacity: 0.92; }
[data-section] .numSectionCaret { border-left-color: var(--sec, currentColor); opacity: 0.85; }
[data-section] .numSectionNote { color: var(--sec, var(--num-ink)); }
.numGroupBox[data-section] { border-left: 2px solid var(--sec, var(--num-rule)); }
.numSectionHead:hover .numGroupLabel { text-shadow: 0 0 7px var(--sec, var(--num-ink)); }

/* the trace field and its buttons pick up their section's colour too */
[data-section="trace"] .numNumberInput { border-bottom-color: var(--sec); color: var(--sec); }
[data-section="cosmology"] .numSelectPrimary { color: var(--sec); border-bottom-color: var(--sec); }

/* invert is ink on paper: one colour, no exceptions */
.numField[data-invert="1"] [data-section] { --sec: var(--num-ink); }
.numSectionShut .numSectionNote { opacity: 0.6; }
.numSectionBody { padding-bottom: 0.2em; }
.numSectionShut .numSectionBody { display: none; }
.numSectionShut { padding-bottom: 0.15em; }

/* ---- layer rows ---------------------------------------------------------
 * Name on the left behind a bar in the colour that layer is drawn in, state
 * on the right. The bar is the point: the panel is the figure's key as well
 * as its switchboard, so you can tell which colour you are turning off. */
.numLayerRow {
	display: flex;
	align-items: center;
	gap: 0.6em;
	width: 100%;
	background: none;
	border: none;
	padding: 0.3em 0;
	color: inherit;
	font: inherit;
	font-size: 0.82em;
	letter-spacing: 0.08em;
	cursor: pointer;
	text-align: left;
}
.numLayerBar {
	flex: 0 0 auto;
	width: 3px;
	height: 1.1em;
	background: var(--num-ink);
}
.numLayerBar[data-bar="currents"] { background: var(--num-current); }
.numLayerBar[data-bar="gates"]    { background: var(--num-channel); }
.numLayerBar[data-bar="syzygies"] { background: var(--num-bone); }
.numLayerName { flex: 1 1 auto; }
.numLayerState {
	flex: 0 0 auto;
	font-size: 0.82em;
	letter-spacing: 0.16em;
	opacity: 0.7;
}
.numLayerRow:hover .numLayerName { text-shadow: var(--num-glow); }
.numLayerRow.numLayerOff { opacity: 0.4; }
/* A switched-off LAYER greys its bar; a REGION that simply is not the current
 * one keeps its colour, because that bar is the only thing naming which part
 * of the figure "torque" or "plex" refers to. */
.numLayerRow.numLayerOff[data-layer] .numLayerBar { background: currentColor; opacity: 0.45; }
.numRegionRow.numLayerOff { opacity: 0.62; }
.numLayerRow:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 2px; }

/* ---- the zone switches --------------------------------------------------
 * Ten chips, each in its own zone's colour, so the control and the thing it
 * controls are the same colour in both places. */
.numZoneFilter {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3em;
	margin-bottom: 0.6em;
}
.numZoneChip {
	box-sizing: border-box;
	width: 44px;
	height: 44px;
	padding: 0;
	background: none;
	border: 1px solid var(--zc, var(--num-ink));
	border-radius: 50%;
	color: var(--zc, var(--num-ink));
	font: inherit;
	font-size: 0.78em;
	font-weight: 600;
	cursor: pointer;
	transition: opacity 0.15s ease, box-shadow 0.15s ease;
}
.numZoneChip { --zc: var(--num-bone); }
.numZoneChip:hover { box-shadow: 0 0 7px var(--zc, var(--num-ink)); }
.numZoneChip.numZoneChipOff { opacity: 0.28; }
.numZoneChip:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 2px; }

/* A filtered-out element leaves the figure entirely rather than dimming:
 * a half-visible zone reads as a state of that zone, which it is not. */
.numHidden { display: none; }

/* ---- holding zones ------------------------------------------------------
 *
 * With anything picked the figure goes into focus: the rest drops back far
 * enough to read as background but not so far as to vanish, because the
 * point of holding two zones is to see them AGAINST the figure, not instead
 * of it. Filtering (numHidden) removes; this only recedes.
 */
.numSvg[data-focus="1"] .numChannel:not(.numOn),
.numSvg[data-focus="1"] .numCurrent:not(.numOn),
.numSvg[data-focus="1"] .numGate:not(.numOn) {
	opacity: 0.16;
	transition: opacity 0.2s ease;
}
.numSvg[data-focus="1"] .numZone:not(.numPick) {
	opacity: 0.22;
	transition: opacity 0.2s ease;
}
.numSvg[data-focus="1"] .numZone.numPick {
	opacity: 1;
}
.numZone.numPick .numZoneBracket {
	stroke-width: 3.8;
	filter: drop-shadow(0 0 9px rgba(57, 255, 20, 0.72));
}

.numPrimaryHint {
	font-size: 0.7em;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	opacity: 0.6;
	margin-top: 0.35em;
}
/* the leading controls carry the weight their labels do */
.numSelectPrimary {
	font-size: 0.92em;
	border-bottom-width: 2px;
}
.numPrimary .numCtlRow { margin-top: 0; }
.numPrimary .numTraceHead { margin-top: 0; }
.numPrimary .numNumberRow { margin-top: 0.7em; }
.numPrimary .numGeoSetRow { margin-top: 0.8em; }

/* ---- information containers -------------------------------------------
 *
 * Quiet research sections, not dashboard cards: one hairline rule above each,
 * a small label, then the values. No fill, no radius, no shadow, no badge.
 */

.numBox {
	border-top: 1px solid currentColor;
	padding-top: 0.55em;
	margin-bottom: 0.9em;
	opacity: 1;
}
.numBox:first-child { border-top: none; padding-top: 0; }
.numBoxLabel {
	margin: 0 0 0.3em;
	font-size: 0.72em;
	font-weight: 600;
	letter-spacing: 0.2em;
	opacity: 0.55;
	text-transform: uppercase;
}
/* the rule is what separates sections, so a status tag sits tight under its
 * own box rather than floating between two */
.numBox + .numStatus { margin: -0.7em 0 0.9em; }

/* ---- the cosmology selector -------------------------------------------- */

.numSelect {
	background: none;
	border: none;
	border-bottom: 1px solid currentColor;
	border-radius: 0;
	color: var(--num-ink);
	font-family: inherit;
	font-size: 0.78em;
	font-weight: 600;
	letter-spacing: 0.12em;
	padding: 0.1em 1.2em 0.15em 0;
	cursor: pointer;
	/* the site's own select styling would bring a panel background in with it */
	-webkit-appearance: none;
	appearance: none;
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
	                  linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: right 4px center, right 0px center;
	background-size: 4px 4px, 4px 4px;
	background-repeat: no-repeat;
}
.numSelect:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 3px; }
/* the option list is drawn by the platform, so it needs real colours */
.numSelect option { background: var(--num-field); color: var(--num-bone); }

.numCosmoRow, .numGeoSetRow { align-items: baseline; gap: 0.9em; margin-top: 1.2em; }
.numCosmoRow .numAnnotLabel,
.numGeoSetRow .numAnnotLabel { margin: 0; }

/* ---- the direction marks ----------------------------------------------
 * Each small solid triangle points toward the zone's syzygetic twin. Purely
 * visual: it never takes a pointer event. */
.numZoneMark { fill: var(--num-bone); stroke: none; pointer-events: none; }
.numZone.numOn .numZoneMark { fill: var(--num-current); }

/* ---- hit areas ---------------------------------------------------------
 *
 * The visible linework is thin, and a thin stroke is a poor target. These sit
 * over the artwork, take the pointer events, and are invisible - the design
 * is unchanged, only the reach of the cursor is.
 */
.numHit {
	fill: transparent;
	stroke: none;
	pointer-events: all;
	cursor: pointer;
}
.numHitLine {
	fill: none;
	stroke: transparent;
	stroke-width: 16;
	stroke-linecap: round;
	pointer-events: stroke;
	cursor: pointer;
}
.numCurrentHit { stroke-width: 26; }

/* The visible parts no longer need to catch anything, so a click in the
 * middle of a zone cannot be swallowed by the ring or the numeral. */
.numZoneRing, .numZoneBracket, .numGateRing, .numGateLeader, .numZoneText, .numGateText,
.numBondLine, .numCurrentFilament, .numCurrentChevron,
.numChLine, .numChHead { pointer-events: none; }

.numGate, .numChannel, .numCurrent { cursor: pointer; outline: none; }
.numGate:focus-visible .numGateRing,
.numChannel:focus-visible .numChLine {
	filter: drop-shadow(0 0 5px rgba(255, 20, 147, 0.85));
	stroke-width: 12;
}
.numCurrent:focus-visible .numCurrentFilament,
.numCurrent:focus-visible .numBondLine {
	filter: drop-shadow(0 0 5px rgba(57, 255, 20, 0.85));
	stroke-width: 9;
}

/* ---- the cosmology panel: attributed bands ----------------------------
 *
 * One band per body. The Planetwork rises by solar zone; the selectable
 * Gnostic arrangement rises from the Void through its attributed bodies.
 *
 * Every stroke here is lighter than the canonical linework: this is the
 * secondary model and it must read as secondary.
 */

/* A rule under each row, not a track through it. The row used to be a
 * full-width line with a knob sitting at its midpoint, which is the shape of
 * a slider: it promised something to drag and had nothing. It is a table of
 * bodies, so it is set as one. */
.numBandRule {
	stroke: var(--num-ink);
	stroke-width: 1;
	opacity: 0.18;
	vector-effect: non-scaling-stroke;
}

.numBandName {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 22px;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-anchor: start;
	pointer-events: none;
}
.numBandRole {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 17px;
	font-weight: 500;
	letter-spacing: 0.08em;
	text-anchor: end;
	opacity: 0.55;
	pointer-events: none;
}

/* The zone address on every row uses the same bone registration ink as the
 * canonical figure. Individual zones do not acquire a second colour code. */
.numBandZone {
	fill: none;
	stroke: var(--zc, var(--num-ink));
	stroke-width: 2;
	vector-effect: non-scaling-stroke;
	pointer-events: none;
}
.numBandZoneText {
	fill: var(--zc, var(--num-ink));
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 18px;
	font-weight: 700;
	text-anchor: middle;
	dominant-baseline: central;
	pointer-events: none;
}
.numBandZone, .numBandZoneText { --zc: var(--num-bone); }

/* Retained for provenance-backed datasets with bodies that have no zone. A
 * row that lights nothing must say so rather than look broken. */
.numBandNoZone {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-anchor: middle;
	opacity: 0.38;
	pointer-events: none;
}

.numBandNote {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.16em;
	text-anchor: middle;
	opacity: 0.5;
	pointer-events: none;
}

.numBandEarth .numBandName { font-weight: 700; }
.numBandEarth .numBandRule { opacity: 0.4; }

.numBand { cursor: pointer; }
.numBand:focus { outline: none; }
.numBand:hover .numBandName { opacity: 1; font-weight: 700; text-shadow: var(--num-glow); }
.numBand:hover .numBandRule { opacity: 0.5; }
.numBand:hover .numBandRole { opacity: 0.95; }
.numBand:hover .numBandZone { stroke-width: 3; }
.numBand:focus-visible .numBandRule { opacity: 0.7; stroke-dasharray: 5 4; }
.numBand.numOn .numBandName { font-weight: 700; }
.numBand.numOn .numBandRule { opacity: 0.6; }
.numBand.numOn .numBandRole { opacity: 0.9; }
.numBand.numOn .numBandZone { fill: var(--zc, var(--num-ink)); }
.numBand.numOn .numBandZoneText { fill: var(--num-field); }

/* the one tie, drawn only for the selected body in cross-map */
.numBandTie {
	stroke: var(--num-ink);
	stroke-width: 1.6;
	fill: none;
	vector-effect: non-scaling-stroke;
}
.numBandTieText {
	fill: var(--num-ink);
	font-family: "Roboto Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 15px;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-anchor: end;
	pointer-events: none;
}

/* ---- information containers become real boxes ------------------------- */

.numBox {
	border: 1px solid currentColor;
	border-top: 1px solid currentColor;
	padding: 0.6em 0.75em 0.7em;
	margin-bottom: 0.8em;
}
.numBox:first-child { border-top: 1px solid currentColor; padding-top: 0.6em; }
.numBox + .numStatus { margin: -0.45em 0 0.8em 0.1em; }

/* The credit, set as one line rather than as a provenance apparatus. */
.numCredit {
	font-size: 0.72em;
	line-height: 1.6;
	letter-spacing: 0.04em;
	opacity: 0.8;
}
.numCredit i { font-style: italic; opacity: 0.95; }

/* ===================== the Pandemonium deck =============================
 *
 * Five cards, one per syzygy. Card-shaped on purpose - a border all round, a
 * rule under the head, the name set large - because the ask was for something
 * read like a deck rather than like another list. Scrolls in the panel
 * column; one card is about a third of its height.
 */
.numDeck {
	display: flex;
	flex-direction: column;
	gap: 0.7em;
	width: 100%;
	max-height: 100%;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-right: 0.3em;
}
.numDeck::-webkit-scrollbar { width: 4px; }
.numDeck::-webkit-scrollbar-thumb { background: var(--num-rule); border-radius: 2px; }

/* ---- the deck's head and its segments ----------------------------------- */
.numDeckHead { margin-bottom: 0.8em; }
.numDeckCount {
	font-size: 0.68em;
	font-weight: 700;
	letter-spacing: 0.24em;
	color: var(--num-ink);
	opacity: 0.8;
}
.numDeckNote {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.66em;
	line-height: 1.55;
	color: var(--num-muted);
	margin: 0.35em 0 0.6em;
}
.numDeckTabs { display: flex; flex-wrap: wrap; gap: 0.4em; }
.numDeckTabs .numCtl { font-size: 0.66em; }

.numDeckSeg { margin-bottom: 0.9em; }
.numDeckSegHead {
	display: flex;
	align-items: center;
	gap: 0.5em;
	padding-bottom: 0.3em;
	margin-bottom: 0.45em;
	border-bottom: 1px solid var(--num-rule);
}
.numDeckSegName {
	font-size: 0.66em;
	font-weight: 700;
	letter-spacing: 0.2em;
	opacity: 0.75;
}
.numDeckSegCount {
	font-size: 0.6em;
	opacity: 0.45;
	margin-left: auto;
}
.numDeckSegTag {
	font-size: 0.58em;
	font-weight: 700;
	letter-spacing: 0.16em;
	color: var(--num-channel);
}
/* the nine-sum segment is the one that matters most, so it is marked */
.numDeckSegKey .numDeckSegHead { border-bottom-color: var(--num-channel); }
.numDeckSegKey .numDeckSegName { opacity: 1; color: var(--num-channel); }

/* Forty of the forty-five carry arithmetic and nothing else, so they are set
 * as a compact row: two-up where there is width for it, and without the
 * padding a named card needs. */
.numDeckSeg { display: flex; flex-direction: column; gap: 0.4em; }
.numDeckSeg .numDeckSegHead { margin-bottom: 0; }
.numCardPlain { padding: 0.45em 0.6em; }
.numCardPlain .numCardTop { padding-bottom: 0.3em; margin-bottom: 0.3em; }
.numCardPlain .numCardFacts { margin-bottom: 0; font-size: 0.66em; }
.numCardSums {
	font-size: 0.62em;
	font-weight: 600;
	letter-spacing: 0.12em;
	opacity: 0.5;
}

.numCard {
	display: block;
	width: 100%;
	text-align: left;
	background: var(--num-panel);
	border: 1px solid var(--num-rule);
	border-radius: 3px;
	padding: 0.7em 0.8em 0.75em;
	color: inherit;
	font: inherit;
	cursor: pointer;
	transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.numCard:hover { border-color: var(--reg-c, var(--num-ink)); }
.numCard:focus-visible { outline: 2px solid var(--num-ink); outline-offset: 2px; }
.numCard.numCardOn {
	border-color: var(--reg-c, var(--num-ink));
	box-shadow: 0 0 12px -2px var(--reg-c, var(--num-ink));
}
.numCard[data-sys="torque"] { --reg-c: var(--num-current); }
.numCard[data-sys="warp"]   { --reg-c: var(--num-channel); }
.numCard[data-sys="plex"]   { --reg-c: var(--num-bone); }
/* a pair that is not a syzygy belongs to no region, so it takes no region
 * colour - the deck should not imply a membership the arithmetic denies */
.numCard[data-sys="none"]   { --reg-c: var(--num-muted); }

.numCardTop {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.6em;
	padding-bottom: 0.45em;
	margin-bottom: 0.5em;
	border-bottom: 1px solid var(--num-rule);
}
.numCardPair { font-size: 0.86em; font-weight: 700; }
.numCardPair i { font-style: normal; opacity: 0.4; }
.numCardRegion {
	font-size: 0.62em;
	font-weight: 700;
	letter-spacing: 0.2em;
	color: var(--reg-c, var(--num-ink));
	opacity: 0.9;
}
.numCardName {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 1.32em;
	font-weight: 700;
	letter-spacing: 0.02em;
	color: var(--num-bone);
	line-height: 1.1;
}
.numCardEpithet {
	font-size: 0.62em;
	font-weight: 700;
	letter-spacing: 0.24em;
	color: var(--reg-c, var(--num-ink));
	opacity: 0.75;
	margin-top: 0.15em;
	margin-bottom: 0.6em;
}
.numCardFacts {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.12em 0.7em;
	margin: 0 0 0.6em;
	font-size: 0.7em;
}
.numCardFacts dt { opacity: 0.5; letter-spacing: 0.1em; }
.numCardFacts dd { margin: 0; color: var(--num-bone); }
.numCardReads {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.72em;
	line-height: 1.55;
	color: var(--num-muted);
}
.numCardTag {
	margin-top: 0.5em;
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.58em;
	font-weight: 600;
	letter-spacing: 0.14em;
	opacity: 0.42;
}
.numField[data-invert="1"] .numCardName,
.numField[data-invert="1"] .numCardFacts dd,
.numField[data-invert="1"] .numCardReads { color: var(--num-ink); }
.numField[data-invert="1"] .numCard { --reg-c: var(--num-ink); }

/* ===================== the reading, in more than one colour ==============
 *
 * It was a single column of one green at one weight, so a zone, its
 * arithmetic and its provenance all looked like the same kind of statement.
 * Each block takes a colour for its label and a rule in the same colour;
 * values are set near-white so they read as answers rather than as more
 * chrome; and the prose lines drop to a sans face, which separates what the
 * app is saying from what the figure is.
 */
.numBox[data-box] { --bx: var(--num-ink); }
.numBox[data-box="zone"]             { --bx: var(--num-bone); }
.numBox[data-box="syzygy"]           { --bx: var(--num-current); }
.numBox[data-box="arithmetic"]       { --bx: var(--num-bone); }
.numBox[data-box="structures"]       { --bx: var(--num-current); }
.numBox[data-box="current"],
.numBox[data-box="currents"]         { --bx: var(--num-current); }
.numBox[data-box="gate"],
.numBox[data-box="gates"]            { --bx: var(--num-channel); }
.numBox[data-box="phase"]            { --bx: var(--num-bone); }
.numBox[data-box="quasiphonic"]      { --bx: var(--num-current); }
.numBox[data-box="system-notes"]     { --bx: var(--num-muted); }
.numBox[data-box="held-zones"]       { --bx: var(--num-bone); }
.numBox[data-box="zones"]            { --bx: var(--num-bone); }
.numBox[data-box="reduction"]        { --bx: var(--num-channel); }
.numBox[data-box="aq-values"]        { --bx: var(--num-bone); }
.numBox[data-box="number-trace"],
.numBox[data-box="word-trace"],
.numBox[data-box="planet-trace"]     { --bx: var(--num-current); }
.numBox[data-box="body"]             { --bx: var(--num-bone); }
.numBox[data-box="geocentric"]       { --bx: var(--num-channel); }
.numBox[data-box="ccru-planetwork"],
.numBox[data-box="geocentric-zone"]  { --bx: var(--num-current); }

.numBox[data-box] { border-top-color: var(--bx); }
.numBoxLabel { color: var(--bx); opacity: 0.85; }

/* Values read as answers: near-white, and a size up from the label. */
.numAnnotLine { color: var(--num-bone); }
.numAnnotRow > span:first-child { color: var(--bx); opacity: 0.7; }
.numAnnotRow > span:last-child { color: var(--num-bone); }

/* The big numeral stays in the figure's bone registration ink. */
.numAnnotBig { font-size: 1.9em; font-weight: 700; line-height: 1.1; }

/* A demon's name is a name, not a value - it gets the block's colour and
 * italics rather than the white the numbers take. */
.numAnnotName {
	color: var(--bx);
	font-style: italic;
	letter-spacing: 0.06em;
}

/* Prose, in a sans at a smaller size: this is the app explaining itself, and
 * it should not look like part of the figure's own notation. */
.numAnnotFoot,
.numAnnotIdle,
.numStatus {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	letter-spacing: 0.02em;
}
.numAnnotFoot { color: var(--num-muted); opacity: 1; font-size: 0.92em; }
.numAnnotIdle { color: var(--num-muted); opacity: 1; }
.numStatus { opacity: 0.6; }

.numPhaseEntry {
	display: grid;
	grid-template-columns: 3.5em minmax(0, 1fr) auto;
	gap: 0.55em;
	align-items: baseline;
	padding: 0.15em 0;
	border-top: 1px solid color-mix(in srgb, var(--bx) 18%, transparent);
}
.numPhaseSpan { color: var(--num-bone); font-weight: 700; }
.numPhaseName { min-width: 0; color: var(--num-bone); overflow-wrap: anywhere; }
.numPhaseRole { color: var(--num-channel); font-size: 0.78em; letter-spacing: 0.1em; text-align: right; }
.numSourceLine {
	margin-top: 0.45em;
	color: var(--bx);
	font-size: 0.68em;
	letter-spacing: 0.16em;
	opacity: 0.62;
}

/* invert is ink on paper: one colour, no exceptions */
.numField[data-invert="1"] .numBox[data-box] { --bx: var(--num-ink); }
.numField[data-invert="1"] .numAnnotLine,
.numField[data-invert="1"] .numAnnotRow > span:last-child,
.numField[data-invert="1"] .numAnnotFoot,
.numField[data-invert="1"] .numAnnotIdle { color: var(--num-ink); }

/* ---- the trace field's own clear ---------------------------------------
 * In the field's corner rather than on the row: the row is in the title bar
 * and had no width to give a fourth control. Shows only when there is
 * something to clear, and comes up to full strength on hover. */
.numTraceField { position: relative; display: inline-flex; flex: 1 1 9em; min-width: 7em; }
.numTraceField .numNumberInput {
	width: 100%;
	box-sizing: border-box;
	min-height: 44px;
	padding-right: 0;
	/* smaller than the rest of the title row, and with the row's wide tracking
	   dropped: the placeholder is a phrase, and at the row's own size and
	   letter-spacing it needed 101px in a 98px field */
	font-size: 0.64em;
	letter-spacing: 0.02em;
}
.numTraceField .numNumberInput:not(:placeholder-shown) { padding-right: 44px; }
/* The right end of the field, invisible until the field is hovered. The input
 * reserves the same width, so the target never covers typed text. */
.numTraceClear {
	position: absolute;
	right: 0;
	top: 0;
	width: 44px;
	height: 44px;
	min-width: 44px;
	min-height: 44px;
	padding: 0;
	line-height: 1;
	background: var(--num-field);
	border: none;
	border-radius: 50%;
	color: inherit;
	font: inherit;
	font-size: 0.78em;
	opacity: 0;
	cursor: pointer;
	transition: opacity 0.15s ease;
	z-index: 2;
}
.numTraceField:hover .numTraceClear,
.numTraceField:focus-within .numTraceClear,
.numTraceClear:focus-visible { opacity: 0.8; }
.numTraceClear:hover { opacity: 1; }
.numTraceClear:focus-visible { outline: 1px solid currentColor; outline-offset: 1px; }

/* ---- a Lemur card, and a Lemur reading --------------------------------- */
.numCardMesh {
	font-size: 0.6em;
	font-weight: 700;
	letter-spacing: 0.14em;
	opacity: 0.45;
}
.numCardAlias {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.66em;
	font-style: italic;
	color: var(--num-muted);
	margin-top: 0.1em;
}
.numCardClass {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.66em;
	line-height: 1.45;
	color: var(--num-muted);
	margin-top: 0.4em;
}
.numCard .numCardRegion { display: block; margin-top: 0.45em; }

/* routes: the path, then what CCRU reads in it */
.numRt { display: flex; gap: 0.55em; margin-bottom: 0.3em; align-items: baseline; }
.numRtPath {
	flex: 0 0 auto;
	font-weight: 700;
	color: var(--bx, var(--num-ink));
	font-size: 0.9em;
}
.numRtText {
	flex: 1 1 auto;
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.86em;
	line-height: 1.45;
	color: var(--num-muted);
}
.numBox[data-box="lemur"]       { --bx: var(--num-channel); }
.numBox[data-box="pandemonium"] { --bx: var(--num-current); }
.numBox[data-box="routes"]      { --bx: var(--num-bone); }

/* ---- Divination -------------------------------------------------------- */
.numDivPair { display: flex; gap: 0.6em; margin-bottom: 0.6em; }
.numDivSide { flex: 1 1 0; min-width: 0; }
.numDivLabel {
	display: block;
	font-size: 0.6em;
	font-weight: 700;
	letter-spacing: 0.22em;
	opacity: 0.55;
	margin-bottom: 0.25em;
}
.numDivInput { width: 100%; }
.numDivOut { margin-top: 0.3em; font-size: 0.76em; font-weight: 700; }
.numDivErr {
	margin-top: 0.3em;
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.66em;
	color: var(--num-channel);
}
.numDivResult {
	margin-top: 0.9em;
	padding-top: 0.7em;
	border-top: 1px solid var(--num-rule);
}
.numDivHead {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 1.25em;
	font-weight: 700;
	color: var(--num-bone);
	line-height: 1.15;
}
.numDivHead i { font-style: italic; font-size: 0.7em; color: var(--num-muted); }
.numDivEpithet {
	font-size: 0.62em;
	font-weight: 700;
	letter-spacing: 0.2em;
	color: var(--num-channel);
	margin: 0.2em 0 0.6em;
}
.numDivBody {
	font-family: "Montserrat", "Helvetica Neue", Arial, sans-serif;
	font-size: 0.72em;
	line-height: 1.55;
	color: var(--num-muted);
	margin-top: 0.5em;
}
.numDivRoutes { margin-top: 0.7em; }
.numField[data-invert="1"] .numDivHead,
.numField[data-invert="1"] .numDivBody { color: var(--num-ink); }

/* ---- Divination takes the room -----------------------------------------
 *
 * Two name fields, their reductions, the Lemur they land on and its routes
 * do not fit the narrow column the planet lineup uses. In this cosmology the
 * panel takes the width of the stage and the READING section stands down -
 * the divination IS the reading here, and having both was two readings of
 * the same pair competing for a column each.
 */
.numField[data-cosmo="divine"] .numPanelGeo {
	flex: 1 1 62%;
	max-width: none;
}
.numField[data-cosmo="divine"] .numPanelMain { flex: 0 1 38%; }
.numField[data-cosmo="divine"] [data-section="reading"] { display: none; }
.numField[data-cosmo="divine"] .numSideRight { display: none; }

/* with the room, the two fields sit side by side at a readable size */
.numField[data-cosmo="divine"] .numDivine { font-size: 1.06em; }
.numField[data-cosmo="divine"] .numDivHead { font-size: 1.5em; }
.numField[data-cosmo="divine"] .numDivPair { gap: 1em; }

@media (max-width: 760px) {
	.numField[data-cosmo="divine"] .numPanelGeo,
	.numField[data-cosmo="divine"] .numPanelMain { flex: 0 0 auto; max-width: none; }
	.numField[data-cosmo="divine"] .numSideRight { display: none; }
}
