/* ── Theme tokens (light mode) ─────────────────────────────────────────
   All chrome colours route through these custom properties so the dark
   media query below can swap the theme without touching every rule.
   SVG fill/stroke use `currentColor` (set in JS) so they inherit from
   the SVG element's `color`, which we point at --tree-stroke. */
:root {
	--bg:             #fff;
	--bg-panel:       #fff;
	--bg-elevated:    #fff;
	--text:           #333;
	--text-dim:       #777;
	--text-link:      #36a;
	--border:         #ccc;
	--border-strong:  #888;
	--tree-stroke:    #333;
	--annot-support:  #222;
	--annot-conflict: #a33;
	--hover-bg:       #d6e6f8;
	--hover-text:     #14437a;
	--ext-id-color:   #888;
	--peek-bg:        rgba(255,255,255,0.78);
	--spinner-track:  rgba(150,150,150,0.3);
	--spinner-tip:    #333;
	--panel-shadow:   rgba(0,0,0,0.08);
	--halo-color:     #c33;
}

@media (prefers-color-scheme: dark) {
	:root {
		--bg:             #1a1a1a;
		--bg-panel:       #242424;
		--bg-elevated:    #2c2c2c;
		--text:           #ddd;
		--text-dim:       #999;
		--text-link:      #6aa3df;
		--border:         #444;
		--border-strong:  #666;
		--tree-stroke:    #88b1d8;   /* desaturated blue, readable on the dark bg */
		--annot-support:  #ccc;
		--annot-conflict: #e88;
		--hover-bg:       #2c4060;
		--hover-text:     #d6e6ff;
		--ext-id-color:   #888;
		--peek-bg:        rgba(40,40,40,0.85);
		--spinner-track:  rgba(180,180,180,0.25);
		--spinner-tip:    #ddd;
		--panel-shadow:   rgba(0,0,0,0.5);
		--halo-color:     #ff6b6b;
	}
}

html, body { height: 100%; margin: 0; overflow: hidden; }
body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
	padding: 0.5em 2em;
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	background: var(--bg);
	color: var(--text);
}
svg {
	border: 1px solid var(--border);
	display: block;
	flex: 1 1 0;
	min-height: 0;
	width: 100%;
	color: var(--tree-stroke);    /* fill/stroke inherit via currentColor */
}

/* ── Interactive viewer layout (index.php) ─────────────────────────────
   body is a column flex (top → bottom):
     #search-bar    — taxon name search input + dropdown of hits
     #nav-history   — collapsible breadcrumb / hoptree area
     #main          — relative-positioned wrapper containing the SVG and
                      (overlaid on the right) the info panel
   The info panel is hidden by default; .open shows it. transition.html
   doesn't include these elements so the rules are no-ops there. */

/* Top navbar: Home (left) | search (with dropdown) | About (right). */
#navbar {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	gap: 1em;
	padding: 0.4em 0;
	margin-bottom: 0.3em;
	border-bottom: 1px solid var(--border);
}
.nav-link {
	color: var(--text-link);
	text-decoration: none;
	padding: 0.2em 0.4em;
	font-size: 0.95em;
}
.nav-link:hover { text-decoration: underline; }
.nav-about { margin-left: auto; }

/* Inline interaction hint, deliberately understated. Sits in the nav so
   it adds no height of its own. Hidden on narrow screens where it would
   wrap awkwardly — touch users learn the gestures from the issue #1
   pattern (tap to highlight, double-tap to navigate) anyway. */
.nav-hint {
	color: var(--text-dim);
	font-size: 0.85em;
	white-space: nowrap;
}
@media (max-width: 720px) {
	.nav-hint { display: none; }
}

/* Featured-taxa dropdown — <details>/<summary> for native disclosure,
   absolutely-positioned panel below so it overlays the tree. The
   summary marker is suppressed so the trigger reads as a plain link
   matching the other nav items. */
.nav-dropdown-wrap { position: relative; }
.nav-dropdown-wrap > summary {
	cursor: pointer;
	list-style: none;
}
.nav-dropdown-wrap > summary::-webkit-details-marker { display: none; }
.nav-dropdown {
	display: grid;
	grid-template-columns: repeat(3, auto);
	gap: 0.5em;
	position: absolute;
	top: calc(100% + 0.3em);
	left: 0;
	background: var(--bg-panel);
	color: var(--text);
	border: 1px solid var(--border);
	border-radius: 4px;
	padding: 0.6em;
	box-shadow: 0 2px 12px var(--panel-shadow);
	z-index: 20;
}
.featured-item {
	display: flex;
	flex-direction: column;
	align-items: center;
	width: 80px;
	padding: 0.3em;
	border-radius: 4px;
	color: var(--text);
	text-decoration: none;
	font-size: 0.8em;
	line-height: 1.15;
	text-align: center;
}
.featured-item img {
	width: 64px;
	height: 64px;
	object-fit: contain;               /* preserve whole SVG; no cropping */
	border-radius: 4px;
	display: block;
	border: 1px solid var(--border);
	padding: 4px;                       /* small inset so SVG doesn't kiss the border */
	box-sizing: border-box;
	/* Phylopic silhouettes are hard-filled black, which disappears in
	   dark mode. brightness(0) flattens any source colour to black,
	   invert(0.5) lands on #808080 — a mid-gray that reads against
	   both white and dark backgrounds, so we don't need a separate
	   dark-mode rule. No background on the img means the filter only
	   affects the silhouette itself; the dropdown's panel bg shows
	   through the transparent SVG areas. */
	filter: brightness(0) invert(0.5);
}
.featured-item:hover {
	background: var(--hover-bg);
	color: var(--hover-text);
}
.featured-item:hover img {
	border-color: var(--text-link);
}
.featured-item span { margin-top: 0.35em; }

/* About modal — native <dialog>, deliberately understated. No border;
   a soft shadow does the lifting. Capped at 70vw so the dialog stays
   compact even on wide screens. Sits in the upper third of the
   viewport (margin-top:15vh) — the standard "above optical center"
   position so the eye lands on the content immediately. */
#about-dialog {
	max-width: min(32em, 70vw);
	margin-top: 15vh;
	margin-bottom: auto;
	background: var(--bg-panel);
	color: var(--text);
	border: none;
	border-radius: 4px;
	padding: 1.5em 1.9em 1.4em;
	box-shadow: 0 2px 14px var(--panel-shadow);
	outline: none;
}
#about-dialog::backdrop { background: rgba(0, 0, 0, 0.25); }
#about-dialog p { margin: 0.6em 0; line-height: 1.5; font-size: 0.95em; }
#about-dialog p:first-of-type { margin-top: 0; }
#about-dialog p:last-of-type  { margin-bottom: 0; }
#about-dialog a { color: var(--text-link); }
#about-dialog .close {
	position: absolute;
	top: 0.4em; right: 0.7em;
	color: var(--text-dim);
	cursor: pointer;
	font-size: 1em;
	line-height: 1;
	user-select: none;
}
#about-dialog .close:hover { color: var(--text); }

#search-bar {
	flex: 0 0 auto;
	position: relative;
}
#search-input {
	width: 18em;
	padding: 0.3em 0.55em;
	font: inherit;
	font-size: 0.95em;
	background: var(--bg-panel);
	color: var(--text);
	border: 1px solid var(--border-strong);
	border-radius: 3px;
	box-sizing: border-box;
}
#search-input::placeholder { color: var(--text-dim); }
#search-input:focus {
	outline: none;
	border-color: var(--text-link);
	box-shadow: 0 0 0 2px rgba(60,140,210,0.18);
}
#search-results {
	position: absolute;
	top: 100%;
	left: 0;
	width: 18em;
	max-height: 16em;
	overflow-y: auto;
	margin: 2px 0 0;
	padding: 0;
	list-style: none;
	background: var(--bg-panel);
	color: var(--text);
	border: 1px solid var(--border-strong);
	border-radius: 3px;
	box-shadow: 0 2px 8px var(--panel-shadow);
	z-index: 200;
	display: none;
}
#search-results.open { display: block; }
#search-results li {
	padding: 0.3em 0.6em;
	cursor: pointer;
	border-bottom: 1px solid var(--border);
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1.5em;
}
#search-results li:last-child { border-bottom: none; }
#search-results li:hover,
#search-results li.active {
	background: var(--hover-bg);
	color: var(--hover-text);
}
#search-results li.empty {
	color: var(--text-dim);
	font-style: italic;
	cursor: default;
}
#search-results li.empty:hover { background: transparent; color: var(--text-dim); }
/* Id keeps its dimmed colour even on hover so the eye still reads
   name-then-id. The horizontal gap to the matched label is provided by
   the row's flex `gap`. */
#search-results .ext-id {
	color: var(--ext-id-color);
	font-size: 0.85em;
}

#nav-history {
	flex: 0 0 auto;
	margin-bottom: 0.4em;
	font-size: 0.9em;
}
#nav-history summary {
	cursor: pointer;
	color: var(--text-dim);
	user-select: none;
}
#hoptree-container {
	/* Fixed height so the hoptree never shoves the main viewer up or
	   down — short hoptrees leave whitespace below; tall ones scroll
	   inside the strip rather than expanding it. */
	height: 130px;
	padding: 0.3em 0;
	overflow: auto;
	box-sizing: border-box;
}
#hoptree-container.empty {
	color: var(--text-dim);
	font-style: italic;
}

/* Hoptree SVG — small cladogram of the spanning subtree of every visited
   node, drawn at pixel scale (sized to its content; the container scrolls
   horizontally if it overflows). Each node is a rounded rectangle with the
   name inside; edges are smooth Bezier curves between rectangles. Visited
   nodes get a tinted background; the current focal is solid filled. */
/* Override the general `svg { width: 100%; flex: 1 1 0; border: ... }`
   rule so the hoptree renders at its natural pixel size — left-to-right,
   no stretching, font stays at its intrinsic 11 px regardless of how
   wide the page is. The container scrolls horizontally when the
   hoptree gets wider than the viewport. */
#hoptree-container .hoptree-svg,
#hoptree-container .hoptree-svg:focus,
#hoptree-container .hoptree-svg :focus {
	display: block;
	flex: none;
	border: 0 none transparent;
	outline: 0 none transparent;
	min-height: 0;
	color: inherit;
	/* width / height set inline from JS in pixels — match the viewBox
	   so preserveAspectRatio is a no-op. */
}
#hoptree-container .hoptree-node-g,
#hoptree-container .hoptree-node-g:focus,
#hoptree-container .hoptree-node-g:focus-visible { outline: none; }

.hoptree-rect           { fill: var(--bg); stroke: var(--text-dim); stroke-width: 1.5; }
.hoptree-rect.visited   { fill: var(--hover-bg); stroke: var(--text-link); }
.hoptree-rect.focal     { fill: var(--text-link); stroke: var(--text-link); }
.hoptree-label          { fill: var(--text); pointer-events: none; }
.hoptree-label.focal    { fill: #fff; font-weight: 600; }
.hoptree-edge           { stroke: var(--text-dim); stroke-width: 1.2; fill: none; opacity: 0.6; }
.hoptree-node-g         { cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; }
.hoptree-node-g:hover .hoptree-rect { stroke: var(--text-link); stroke-width: 2.5; }

#info-content p     { margin: 0.3em 0; }
#info-content .key  { color: var(--text-dim); margin-right: 0.4em; }
#info-content a     { color: var(--text-link); }

/* Per-relation annotation sections (supported_by, conflicts_with, …).
   Each section is a <details> so the user can collapse long lists. */
.annotations           { margin-top: 0.7em; border-top: 1px solid var(--border); padding-top: 0.5em; }
.ann-section           { margin: 0.25em 0; }
.ann-section > summary { cursor: pointer; color: var(--text); user-select: none; padding: 0.1em 0; }
.ann-section > summary:hover { color: var(--text-link); }
.ann-list              { list-style: none; padding: 0.2em 0 0.3em 1em; margin: 0; font-size: 0.9em; }
.ann-list li           { padding: 0.05em 0; }
.ann-list a            { color: var(--text-link); }
.ann-list .tree-id     { color: var(--text-dim); margin-left: 0.3em; font-size: 0.9em; }
.ann-empty             { color: var(--text-dim); font-style: italic; font-size: 0.9em; margin: 0.5em 0; }

#main {
	flex: 1 1 0;
	min-height: 0;
	position: relative;       /* anchor for the absolutely-positioned info panel */
	display: flex;
	flex-direction: column;
}
#main > svg {
	flex: 1 1 0;
	min-height: 0;
	width: 100%;
}
/* Info panel is an OVERLAY over the right edge of the tree, so opening it
   doesn't shrink the tree. .open shows it; the close button hides it. A
   subtle drop shadow on the inside-edge sells the "floating over" feel. */
#info-panel {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: 22em;
	max-width: 35%;
	background: var(--bg-panel);
	color: var(--text);
	border: 1px solid var(--border);
	box-shadow: -2px 0 6px var(--panel-shadow);
	overflow-y: auto;
	padding: 0.6em 0.9em 0.8em;
	box-sizing: border-box;
	display: none;
	z-index: 10;
}
#info-panel.open { display: block; }
#info-panel .close {
	position: absolute;
	top: 0.15em; right: 0.4em;
	background: none; border: none;
	font-size: 1.3em; line-height: 1;
	cursor: pointer; color: var(--text-dim);
	padding: 0;
}
#info-panel .close:hover { color: var(--text); }
#info-content { font-size: 0.9em; line-height: 1.4; }
#info-content h3 { margin: 0 0 0.4em; font-size: 1.05em; }
.controls { margin: 0.5em 0; }
button { padding: 0.3em 1em; margin-right: 0.5em; }
input[type=range] { width: 300px; vertical-align: middle; }
/* font-size is set via attribute from JS (STYLE.labelFontSize) so it can
   scale proportionally with annotation sizes and offsets. */
.label { }

/* Node circles are the hit target: cursor flips. Hover affordance is a
   concentric ring (.tree-node-halo) drawn behind/around each node — see
   below. We avoid animating SVG `r` via CSS because Safari's
   implementation is glitchy (the circle can flash in and out of view).
   Animating opacity on a halo is universal. */
.tree-node {
	cursor: pointer;
	/* iOS: opt out of double-tap-to-zoom so two quick taps both deliver
	   `click` events (the second is otherwise eaten by the zoom gesture).
	   Also removes the legacy 300 ms click delay so the in-JS double-tap
	   window in handleNodeClick is reachable on touch devices. */
	touch-action: manipulation;
	user-select: none;
	-webkit-user-select: none;
}
/* Hollow tree-node markers (other_* placeholders) — fill matches the
   page background so the interior reads as transparent in any theme,
   while the click target stays solid (vs `fill: none`, which would
   make only the stroke clickable). */
.tree-node.is-hollow {
	fill: var(--bg);
	stroke: currentColor;
}
.tree-node-halo {
	fill: none;
	stroke: var(--halo-color);
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.12s ease;
}
.tree-node:hover ~ .tree-node-halo {
	opacity: 1;
}
.legend { font-size: 13px; margin-top: 0.5em; }
.legend span { display: inline-block; width: 12px; height: 12px; border-radius: 50%;
               vertical-align: middle; margin-right: 4px; }

/* Peek overlay: rendered inside the SVG as a polytomy fanning out from the
   other_ node. A backdrop rect sits under the text labels so they stay legible
   over the tree. Color matches the "other" convention used elsewhere.
   Opacity + positions are animated per-frame from JS, not CSS, so nothing
   conflicts when renderPeek redraws on every tick of the main animation. */
/* Peek edges, circles, and labels share the tree's persist colour (#333)
   so the list reads as part of the cladogram. peek-label has
   pointer-events: none so clicks fall through to the .peek-hit rect
   underneath. Hover bolds the label as the only interactive cue. */
.peek-edge     { stroke: var(--tree-stroke); stroke-width: var(--ott-stroke, 1); fill: none; }
.peek-label    { fill: var(--tree-stroke); pointer-events: none; }
.peek-hit      { fill: transparent; cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; }
.peek-hit:hover + .peek-label { font-weight: 600; }
.peek-backdrop { fill: var(--peek-bg); stroke: none; }
.peek-node-solid  { fill: var(--tree-stroke); }
.peek-node-hollow { fill: var(--bg-panel); stroke: var(--tree-stroke); stroke-width: var(--ott-hollow-stroke, 1); }
/* Scroll indicator triangles — shown when the peek member list is
   windowed (more rows above / below than fit in the visible band). */
.peek-scroll-indicator { fill: var(--tree-stroke); opacity: 0.5; pointer-events: none; }

/* Node annotations: "supported" above the incoming edge, "conflicts"
   below it, both centered just left of the node circle. Mirrors the OTT
   convention. Hidden when both counts are zero.
   font-size comes from STYLE.annotFontSize in JS. */
.annot-support  { fill: var(--annot-support); }
.annot-conflict { fill: var(--annot-conflict); }

/* Internal-clade brackets: vertical bar + label in the gutter to the
   right of the tip labels. Both share the link colour so they read as
   "annotations" layered over the tree, distinct from the tip text. */
.bracket-line   { stroke: var(--text-link); fill: none; }
.bracket-label  { fill: var(--text-link); font-weight: 600; }

/* In-flight tree fetch indicator: a centred spinning ring while a request
   is on the wire. Toggled via body.loading. Translucent track so the tree
   underneath stays visible. Centring uses negative margins (not
   `transform: translate`) because the spin animation owns `transform` —
   sharing it with translate makes Safari / Firefox interpolate the
   translate to (0,0) and the element drifts off-centre. */
#loading {
	position: fixed;
	top: 50%;
	left: 50%;
	width: 64px;
	height: 64px;
	margin: -32px 0 0 -32px;
	border: 5px solid var(--spinner-track);
	border-top-color: var(--spinner-tip);
	border-radius: 50%;
	animation: spin 0.8s linear infinite;
	pointer-events: none;
	display: none;
	z-index: 1000;
}
body.loading #loading { display: block; }
@keyframes spin { to { transform: rotate(360deg); } }
