﻿/*
 * Controls: groups, sliders, selects, buttons, and the hover reference panel.
 *
 * ONE HEIGHT FOR EVERYTHING INTERACTIVE. Buttons, selects and colour swatches
 * all sit on --row-h (28px). That single rule is most of what makes a column of
 * mixed controls read as one list rather than a pile of differently-sized
 * boxes. Do not give a control a height of its own.
 *
 * THE UI MUST NOT RESIZE OR JUMP WHILE A CONTROL IS BEING DRAGGED, which is
 * enforced by .control-readout having a fixed width and tabular figures, so "8"
 * and "88888" occupy the same space and the row never reflows.
 */

/* ------------------------------------------------- collapsible groups */

.group { border-bottom: 1px solid var(--line-soft); }
.group:last-of-type { border-bottom: none; }
.group[hidden] { display: none; }

/*
 * Level 2 of the panel hierarchy — see the note in tokens.css. Smaller than the
 * options it contains, but uppercase, letterspaced and grey, which reads as a
 * divider rather than as content and lets it organise the column without
 * competing with the controls inside it.
 */
.group-summary {
	display: flex;
	align-items: center;
	gap: 7px;
	padding: 9px 0 7px;
	cursor: pointer;
	list-style: none;
	user-select: none;
	font-size: var(--fs-section);
	font-weight: 700;
	letter-spacing: 0.09em;
	text-transform: uppercase;
	color: var(--text-dim);
}
/* Both are needed: ::-webkit-details-marker for Safari and older Chrome,
 * `list-style: none` above plus ::marker for everything current. */
.group-summary::-webkit-details-marker { display: none; }
.group-summary::marker { content: ""; }
.group-summary:hover { color: var(--text); }
.group-summary:focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; border-radius: 3px; }

/* Disclosure triangle, drawn rather than typed so it rotates smoothly and
 * inherits the summary's colour. */
.group-summary::before {
	content: "";
	width: 0;
	height: 0;
	flex: none;
	border-left: 4.5px solid currentColor;
	border-top: 3.2px solid transparent;
	border-bottom: 3.2px solid transparent;
	transition: transform 120ms ease;
	transform-origin: 30% 50%;
}
.group[open] > .group-summary::before { transform: rotate(90deg); }


.group-name { flex: 1 1 auto; }

/* Shown only while collapsed — an expanded group already shows what is in it,
 * so the number would sit next to its own answer. Emptied by panel.js on open. */
.group-count {
	min-width: 15px;
	padding: 1px 5px;
	border-radius: 3px;
	background: var(--control);
	color: var(--text-dim);
	font-size: var(--fs-micro);
	font-weight: 400;
	letter-spacing: 0;
	text-align: center;
}
.group-count:empty { display: none; }

.group-body { padding: 1px 0 11px; }

/* --------------------------------------------------------------- controls */

.control { margin-bottom: 10px; }
.control:last-child { margin-bottom: 0; }
.control[hidden] { display: none; }

.control-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	margin-bottom: 4px;
}

/* Level 3 of the panel hierarchy — regular weight, so it never outranks the
 * uppercase section heading above it. See the note in tokens.css. */
.control-label { font-size: var(--fs-base); font-weight: 400; color: var(--text); }

/* The lead control (Method) sits above the groups with a rule under it, so it
 * reads as the decision the groups below depend on rather than as a stray. */
#lead-controls { padding-bottom: 11px; border-bottom: 1px solid var(--line-soft); }

.control-readout {
	flex: none;
	width: var(--readout-width);
	text-align: right;
	font-family: var(--font-mono);
	font-size: var(--fs-micro);
	font-variant-numeric: tabular-nums;
	color: var(--text-dim);
	/* Never wrap: a two-line readout would make the row taller mid-drag. */
	white-space: nowrap;
	overflow: hidden;
}

/*
 * Descriptions are NOT rendered inline — they live in the shared hover tooltip
 * (ui/tooltip.js). A row carrying one gets a faint dot after its label so it is
 * discoverable rather than hidden, and the cursor confirms it.
 */
.control[data-tip] { cursor: help; }
.control[data-tip] .control-label::after {
	content: "";
	display: inline-block;
	width: 3.5px;
	height: 3.5px;
	margin-left: 5px;
	margin-bottom: 2px;
	border-radius: 50%;
	background: var(--line);
	vertical-align: middle;
}
.control[data-tip]:hover .control-label::after,
.control[data-tip]:focus-within .control-label::after { background: var(--pink); }

/* ---------------------------------------------------------------- slider */

input[type="range"] {
	-webkit-appearance: none;
	appearance: none;
	display: block;
	width: 100%;
	height: 18px;
	background: transparent;
	cursor: pointer;
	margin: 0;
}

input[type="range"]::-webkit-slider-runnable-track {
	height: 3px;
	border-radius: 2px;
	/* The filled portion is a background gradient whose stop is set from script
	 * (--fill). A separate filled element would be another thing to keep in
	 * sync with the value. */
	background: linear-gradient(to right,
		var(--pink) 0 var(--fill, 0%), var(--control) var(--fill, 0%) 100%);
}
input[type="range"]::-moz-range-track {
	height: 3px;
	border-radius: 2px;
	background: linear-gradient(to right,
		var(--pink) 0 var(--fill, 0%), var(--control) var(--fill, 0%) 100%);
}

input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 12px;
	height: 12px;
	margin-top: -4.5px;
	border-radius: 50%;
	background: var(--text);
	border: 2px solid var(--pink);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
input[type="range"]::-moz-range-thumb {
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: var(--text);
	border: 2px solid var(--pink);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
input[type="range"]:focus-visible { outline: 2px solid var(--pink); outline-offset: 3px; border-radius: 3px; }

/* ---------------------------------------------------------------- select */

select {
	width: 100%;
	height: var(--row-h);
	padding: 0 26px 0 9px;
	background-color: var(--control);
	/* Chevron drawn inline so the control needs no image request. */
	background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%23a8a8b4' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
	background-repeat: no-repeat;
	background-position: right 8px center;
	background-size: 11px;
	color: var(--text);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-family: inherit;
	font-size: var(--fs-base);
	appearance: none;
	cursor: pointer;
}
select:hover { background-color: var(--control-hover); }
select:focus-visible { outline: 2px solid var(--pink); outline-offset: 1px; }

/* ----------------------------------------------------------------- color */

input[type="color"] {
	width: 100%;
	height: var(--row-h);
	padding: 2px;
	background: var(--control);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	cursor: pointer;
}
input[type="color"]:focus-visible { outline: 2px solid var(--pink); outline-offset: 1px; }

/* --------------------------------------------------------------- buttons */

.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	height: var(--row-h);
	padding: 0 11px;
	background: var(--control);
	color: var(--text);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-family: inherit;
	font-size: var(--fs-base);
	line-height: 1;
	cursor: pointer;
	white-space: nowrap;
}
.button:hover:not(:disabled) { background: var(--control-hover); }
.button:focus-visible { outline: 2px solid var(--pink); outline-offset: 1px; }
.button:disabled { opacity: 0.4; cursor: not-allowed; }

/*
 * The primary action. Taller than the standard row on purpose — it is the one
 * thing on the page you press to make something happen, and 18px at weight 900
 * also clears the WCAG "large text" threshold (18.66px bold is the bar for
 * bold), which is what makes near-black ink on the brand pink legitimate at its
 * measured 5.68:1. At a normal text size that pairing would fail, so DO NOT
 * shrink this button without changing its colours too.
 */
.button-primary {
	width: 100%;
	height: 38px;
	background: var(--pink);
	border-color: var(--pink);
	color: var(--pink-ink);
	font-family: var(--font-display);
	font-weight: 900;
	font-size: 19px;
	letter-spacing: 0.01em;
	text-transform: lowercase;
}
.button-primary:hover:not(:disabled) { background: var(--pink-hover); border-color: var(--pink-hover); }

.button-small { height: var(--row-h-sm); padding: 0 9px; font-size: var(--fs-small); }

.button-row { display: flex; flex-wrap: wrap; gap: 6px; }
.button-row .button { flex: 1 1 auto; }

/* Segmented control in the viewport toolbar. */
.segment {
	height: 22px;
	padding: 0 9px;
	background: transparent;
	color: var(--text-dim);
	border: none;
	border-radius: 3px;
	font-family: inherit;
	font-size: var(--fs-small);
	line-height: 1;
	cursor: pointer;
	white-space: nowrap;
}
.segment:hover { color: var(--text); background: var(--control); }
/* --pink-solid, not --pink: small text on a pink fill. See tokens.css. */
.segment[aria-pressed="true"] { background: var(--pink-solid); color: var(--pink-ink); font-weight: 600; }
.segment:focus-visible { outline: 2px solid var(--pink); outline-offset: 1px; }


/* ------------------------------------------------------- reference panel */

/*
 * Shortcuts, formats, the privacy claim and the credit plate are read once and
 * then never again, so they live behind the "Runs locally" badge rather than
 * taking permanent vertical space.
 *
 * Opens on :focus-within as well as :hover, or it is unreachable by keyboard
 * and invisible on touch where hover does not exist.
 */
.reference { position: relative; display: inline-block; }

/*
 * Bridge so the pointer can cross the gap to the panel without the hover
 * dropping. It is on the CONTAINER, never on the panel: an absolutely
 * positioned descendant of a scrolling box still counts towards its
 * scrollHeight, which invents a scrollbar on content that fits.
 */
.reference::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	top: 100%;
	height: 10px;
	pointer-events: none;
}
.reference:hover::after, .reference:focus-within::after { pointer-events: auto; }

.reference-panel {
	position: absolute;
	z-index: 20;
	left: 0;
	top: calc(100% + 9px);
	width: 264px;
	padding: 12px 13px;
	background: var(--bg);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
	font-size: var(--fs-small);
	line-height: 1.55;
	color: var(--text-dim);
	opacity: 0;
	visibility: hidden;
	transition: opacity 110ms ease;
}
.reference:hover .reference-panel,
.reference:focus-within .reference-panel { opacity: 1; visibility: visible; }

.reference-panel dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; }
.reference-panel dt { color: var(--text); font-family: var(--font-mono); font-size: var(--fs-micro); }
.reference-panel dd { margin: 0; }

/*
 * Provenance stamped small, like the print on the back of a phone — findable
 * rather than announced.
 *
 * ONE GREY FOR THE WHOLE PLATE. No underline, no brighter link colour, no
 * weight change. Both were tried on an earlier project and rejected: they cut a
 * single stamp into three pieces and turn it into a sentence with hyperlinks in
 * it. Hover and focus reveal the links, and the pointer cursor does the rest.
 *
 * Sized to fit the whole stamp on ONE line — it is a single mark, not three
 * items in a list, and wrapping it breaks that reading. If the panel ever
 * narrows, shrink the letter-spacing before the font size.
 */
.plate {
	margin: 12px 0 0;
	padding-top: 10px;
	border-top: 1px solid var(--line-soft);
	text-align: center;
	font-family: var(--font-mono);
	font-size: 8.8px;
	letter-spacing: 0.07em;
	white-space: nowrap;
	text-transform: uppercase;
	color: var(--text-faint);
}
.plate a { color: inherit; text-decoration: none; }
.plate a:hover { color: var(--pink-text); }
.plate a:focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; border-radius: 2px; }

/* --------------------------------------------------------------- utility */

.visually-hidden {
	position: absolute; width: 1px; height: 1px;
	margin: -1px; padding: 0; overflow: hidden;
	clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
	}
}

/* ------------------------------------------------------- outcome buttons */

/*
 * A row of mutually exclusive choices, used for the fit presets. Wraps rather
 * than scrolls: four labels do not fit one panel width, and a row you have to
 * scroll hides the very options it exists to offer.
 */
/*
 * A fixed 2x2 grid, not a wrapping row.
 *
 * Four labels of very different lengths ("Loose" against "Skin tight") do not
 * fit one 288px column, and flex-wrap put three on the first line and one
 * stranded on the second — which reads as a mistake rather than as a set. A
 * grid gives four equal cells whatever the labels say, and stays symmetrical if
 * a preset is ever renamed.
 */
.buttonset {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 3px;
	padding: 3px;
	background: var(--control);
	border: 1px solid var(--line);
	border-radius: var(--radius);
}
.buttonset .segment { width: 100%; }

/* Explains why a group is absent, so a conditional section does not read as
 * something having gone missing. */
#no-cage-note { margin: 10px 0 0; }

/*
 * The 2D wrap-behaviour pad (ui/panel.js `pad` builder).
 *
 * Grid rather than absolute positioning for the captions: they are real text
 * that has to stay legible and translatable, and a grid keeps them clear of the
 * plot instead of floating over it.
 */
.pad {
	display: grid;
	grid-template-columns: auto 1fr auto;
	grid-template-areas:
		".    top    ."
		"left plot   right"
		".    bottom .";
	align-items: center;
	gap: 4px;
}

.pad svg {
	grid-area: plot;
	width: 100%;
	aspect-ratio: 4 / 3;
	display: block;
	border-radius: 4px;
	cursor: crosshair;
	touch-action: none;   /* or a drag scrolls the panel on touch instead */
}
.pad svg:focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; }

.pad-cap {
	font-size: var(--fs-micro);
	color: var(--text-dim);
	white-space: nowrap;
}
.pad-cap-top    { grid-area: top;    justify-self: center; }
.pad-cap-bottom { grid-area: bottom; justify-self: center; }
/* The side captions are rotated so a 260px panel does not have to find room for
 * two words of horizontal text on each flank. */
.pad-cap-left   { grid-area: left;  writing-mode: vertical-rl; transform: rotate(180deg); }
.pad-cap-right  { grid-area: right; writing-mode: vertical-rl; }

.pad-bg   { fill: var(--control); }
.pad-grid { stroke: var(--line-soft); stroke-width: 1; }

/* The measured crumple zone. Deliberately drab rather than alarming — it is a
 * region that gives poor results, not an error. */
.pad-danger { fill: var(--line-soft); opacity: 0.55; }

.pad-marker {
	fill: var(--line);
	stroke: var(--panel);
	stroke-width: 1.5;
	cursor: pointer;
}
.pad-marker:hover { fill: var(--text-dim); }
.pad-marker.is-current { fill: var(--pink); }

.pad-cursor line { stroke: var(--pink); stroke-width: 1; opacity: 0.5; }
.pad-cursor circle { fill: var(--pink); stroke: var(--bg); stroke-width: 2; }

/*
 * RIGHT PANEL SECTIONS ARE LEVEL 1, NOT LEVEL 2.
 *
 * `.group-summary` above is styled as a divider — small, uppercase, grey —
 * because in the LEFT panel these are sub-groups sitting under a numbered step
 * ("Wrap cage" under "2 Prepare wrap"). In the right panel there is no step
 * above them: Wrap material, Wrap result, Imported model and Imported textures
 * ARE that panel's top level, and dividers reading as sub-headings of nothing
 * made the column look like a list of fragments.
 *
 * So inside .scene-body they take the same type as "1 Load model", and the
 * numbered badge becomes a dot — the right panel is a set of readings, not a
 * sequence, so numbering it would promise an order that is not there.
 *
 * A DOT AND NOT AN ICON, deliberately. A previous pass put glyphs here and they
 * were rejected as poorly drawn; a filled circle has no draughtsmanship to get
 * wrong and inherits the brand colour for free.
 */
.scene-body .group-summary {
	font-size: var(--fs-step);
	font-weight: 600;
	letter-spacing: -0.005em;
	text-transform: none;
	color: var(--text);
	gap: 8px;
	padding: 11px 0 8px;
}

/* The triangle is replaced by the dot, so the open/closed affordance moves to
 * the far right where it does not sit between the mark and the words. */
.scene-body .group-summary::before {
	width: 7px;
	height: 7px;
	margin: 0 4px;
	border: 0;
	border-radius: 50%;
	background: var(--pink-solid);
	transform: none;
}
.scene-body .group[open] > .group-summary::before { transform: none; }

.scene-body .group-summary::after {
	content: "";
	flex: none;
	width: 0;
	height: 0;
	border-left: 4.5px solid currentColor;
	border-top: 3.2px solid transparent;
	border-bottom: 3.2px solid transparent;
	opacity: 0.55;
	transition: transform 120ms ease;
	transform-origin: 30% 50%;
}
.scene-body .group[open] > .group-summary::after { transform: rotate(90deg); }
