@import url('https://fonts.googleapis.com/css2?family=Amiko:wght@400;600;700&family=Bitcount+Prop+Double:wght@400;700&display=swap');

/* =========================================================
   recall — style.css
   ---------------------------------------------------------
   1.  tokens
   2.  reset + base
   3.  motion — hovers, presses, and things arriving
   4.  app shell — the bars around the page
   5.  page column — one layout for every screen
   6.  shared pieces — buttons and the deck dropdown
   7.  home — section tabs
   8.  home — deck bar
   9.  home — deck dropdown rows
   10. right bar — help box and the two tiles
   11. create screen
   12. practice screen
   13. right-click menu
   14. audio — layout
   15. audio — the video preview
   16. audio — sensing box and its settings
   17. audio — saved clips
   18. audio — the live row and its pulse
   19. small screens
   ========================================================= */

/* =========================================================
   1. tokens
   ========================================================= */
:root {
	--paper: #ffffff;
	--ink: #000;
	--muted: #8a8a8a;   /* placeholder text only */
	--hover: #f1f1f1;   /* tinted hover surface */
	--line: #000;

	--col: 34rem;      /* max width of the content column */
	--rail: 19rem;     /* width of both side bars */
	--topbar: 3.25rem; /* height of the top bar */
	--radius: 10px;
	--field: 42px;     /* height of every control in the deck bar */

	/* the spacing scale. nothing should use a one-off value. */
	--gap: 2rem;       /* corners, and between separate blocks */
	--group: 1rem;     /* a control bar and the list it feeds — every
	                      section uses this, cards, audio and whatever
	                      comes next */
	--tight: 0.5rem;   /* controls sitting side by side in one bar */
	--rail-pad: 1rem;  /* the side bars are narrow — they run tighter */

	--ease: cubic-bezier(0.22, 1, 0.36, 1);   /* soft, decelerating */
	--quick: 0.18s;    /* hovers and presses */
	--slow: 0.4s;      /* things arriving on screen */

	--font: 'Amiko', 'Segoe UI', system-ui, sans-serif;
	--title-font: 'Bitcount Prop Double', 'Courier New', monospace;
}

/* =========================================================
   2. reset + base
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

body {
	margin: 0;
	color: var(--ink);
	background: var(--paper);
	font-family: var(--font);
	font-size: 1rem;
	line-height: 1.5;
	-webkit-font-smoothing: antialiased;
}

button, input, textarea { font: inherit; color: inherit; }

h1, h2 { font-weight: 700; }
h1 {
	margin: 0;
	font-family: var(--title-font);
	font-size: 2rem;
	letter-spacing: 0;
}
h2 { margin: 0 0 0.75rem; font-size: 1.125rem; }

[hidden] { display: none !important; }

:focus-visible {
	outline: 2px solid var(--ink);
	outline-offset: 2px;
}

::selection { color: #fff; background: var(--ink); }

/* nothing on the page shows a scrollbar — a gray one would break the
   two-tone rule anyway. everything still scrolls. */
.app-content, .rail, .help-box, .deck-menu, #recordingList, .sense-controls {
	scrollbar-width: none;
	-ms-overflow-style: none;
}
.app-content::-webkit-scrollbar,
.rail::-webkit-scrollbar,
.help-box::-webkit-scrollbar,
.deck-menu::-webkit-scrollbar,
#recordingList::-webkit-scrollbar,
.sense-controls::-webkit-scrollbar { width: 0; height: 0; }

/* =========================================================
   3. motion — hovers, presses, and things arriving
   ========================================================= */
button, .deck-row, summary {
	transition:
		color var(--quick) var(--ease),
		background-color var(--quick) var(--ease),
		border-color var(--quick) var(--ease),
		transform var(--quick) var(--ease);
}
/* the press: a squash on the way down, a springy overshoot on the way
   back. it runs off a class, not :active, so the whole thing plays
   even on a quick tap. added on pointerdown in section 10 of script.js */
@keyframes button-boing {
	0%   { transform: scale(1, 1); }
	16%  { transform: scale(0.88, 0.82); }
	38%  { transform: scale(1.08, 1.12) translateY(-2px); }
	58%  { transform: scale(0.97, 0.99); }
	78%  { transform: scale(1.02, 1.01); }
	100% { transform: scale(1, 1); }
}
button.boing { animation: button-boing 0.42s var(--ease); }
/* icon buttons keep still — a 2px stroke shimmers when it scales, and
   it only renders crisply if the button lands on a whole pixel, which
   is why the rows above it have whole-pixel heights */
.square-button:active, .deck-card:active { transform: none; }

/* a screen arriving: rises, overshoots a hair, settles */
@keyframes rise-in {
	0%   { opacity: 0; transform: translateY(14px) scale(0.985); }
	60%  { opacity: 1; transform: translateY(-3px) scale(1.004); }
	100% { opacity: 1; transform: none; }
}
.screen:not([hidden]) { animation: rise-in var(--slow) var(--ease) backwards; }

/* moving between screens: the new one comes in from the side you're
   heading, overshoots slightly, settles. going back mirrors it. */
@keyframes screen-forward {
	0%   { opacity: 0; transform: translateX(34px) scale(0.985); }
	55%  { opacity: 1; transform: translateX(-6px) scale(1.004); }
	100% { opacity: 1; transform: none; }
}
@keyframes screen-back {
	0%   { opacity: 0; transform: translateX(-34px) scale(0.985); }
	55%  { opacity: 1; transform: translateX(6px) scale(1.004); }
	100% { opacity: 1; transform: none; }
}
.screen.enter-forward:not([hidden]) { animation: screen-forward 0.44s var(--ease) backwards; }
.screen.enter-back:not([hidden]) { animation: screen-back 0.44s var(--ease) backwards; }

/* the title changes with the screen, so it swaps rather than cuts */
@keyframes title-swap {
	0%   { opacity: 0; transform: translateY(8px); }
	100% { opacity: 1; transform: none; }
}
.title-bar h1.swap { animation: title-swap 0.34s var(--ease) backwards; }

/* and the way back fades in behind it */
.back-button:not([hidden]) { animation: title-swap 0.34s var(--ease) 0.06s backwards; }

@keyframes fade-up {
	from { opacity: 0; transform: translateY(4px); }
	to   { opacity: 1; transform: none; }
}

/* panels inside a screen */
.deck-organizer:not([hidden]) { animation: fade-up 0.24s var(--ease) backwards; }

/* a panel arriving when you swap section. --from is set on it by
   switchSection(), so it comes in from the side you're heading;
   without it (a plain page load) it just rises. the pieces are
   staggered so the swap reads as a hand of cards rather than a blink */
@keyframes panel-in {
	from { opacity: 0; transform: translate(var(--from, 0px), 8px); }
	60%  { opacity: 1; }
	to   { opacity: 1; transform: none; }
}
.deck-bar:not([hidden]),
.deck-list-slot:not([hidden]),
.recorder-panel:not([hidden]),
.help-box:not([hidden]),
.quick-panel:not([hidden]) {
	animation: panel-in 0.4s var(--ease) backwards;
}
.deck-list-slot:not([hidden]) { animation-delay: 0.05s; }
.help-box:not([hidden]) { animation-delay: 0.08s; }
.quick-panel:not([hidden]) { animation-delay: 0.14s; }

/* inside the audio panel the bar leads and the clips follow */
.recorder-panel:not([hidden]) .audio-bar { animation: panel-in 0.4s var(--ease) 0.05s backwards; }
.recorder-panel:not([hidden]) .audio-body { animation: panel-in 0.4s var(--ease) 0.12s backwards; }
#studyQuestion { animation: rise-in 0.34s var(--ease) backwards; }

/* the answer buttons deal themselves out one after another */
@keyframes deal-in {
	from { opacity: 0; transform: translateY(10px) rotate(-1.5deg); }
	to   { opacity: 1; transform: none; }
}

/* right answer pops, wrong one shakes its head */
@keyframes pop {
	0%   { transform: none; }
	40%  { transform: scale(1.05); }
	100% { transform: none; }
}
@keyframes shake {
	0%, 100% { transform: none; }
	20% { transform: translateX(-5px); }
	40% { transform: translateX(4px); }
	60% { transform: translateX(-3px); }
	80% { transform: translateX(2px); }
}

/* the deck entries cascade in */
@keyframes slide-in {
	from { opacity: 0; transform: translateX(-10px); }
	to   { opacity: 1; transform: none; }
}
.deck-card:nth-child(-n+6) { animation: slide-in 0.3s var(--ease) backwards; }
.deck-card:nth-child(1) { animation-delay: 0.02s; }
.deck-card:nth-child(2) { animation-delay: 0.06s; }
.deck-card:nth-child(3) { animation-delay: 0.1s; }
.deck-card:nth-child(4) { animation-delay: 0.14s; }
.deck-card:nth-child(5) { animation-delay: 0.18s; }
.deck-card:nth-child(6) { animation-delay: 0.22s; }

/* the size change itself is one layout pass; switchSection() then
   animates each tab's transform from where it was to where it landed,
   which reads as a smooth grow/shrink and costs no layout per frame */
.section-tab { transform-origin: left bottom; }

/* a popup unfolding from the button it hangs off */
@keyframes drop-in {
	from { opacity: 0; transform: translateY(-6px) scale(0.97); }
	to   { opacity: 1; transform: none; }
}
.share-panel:not([hidden]), .deck-menu:not([hidden]) {
	animation: drop-in 0.22s var(--ease) backwards;
}
.share-panel { transform-origin: top right; }
.deck-menu { transform-origin: top left; }

/* help rows unroll instead of snapping open */
@keyframes unroll {
	from { opacity: 0; transform: translateY(-4px); }
	to   { opacity: 1; transform: none; }
}
.help-box details[open] p { animation: unroll 0.25s var(--ease) backwards; }

/* =========================================================
   4. app shell — the bars around the page
   ========================================================= */
/* left bar runs the full height; the top bar starts beside it and
   spans the right bar, which hangs below it. */
.app-shell {
	position: relative;
	display: flex;
	height: 100vh;
	overflow: hidden;
}

/* the scalloped edge is drawn on the shell, not on the bar itself.
   painting it on the bar forced overflow:visible there, which stopped
   the bar scrolling and made it paint its whole content height. */
.app-shell::after {
	content: '';
	position: absolute;
	top: 0;
	bottom: 0;
	left: calc(var(--rail) - 13px);
	z-index: 5;
	width: 26px;
	pointer-events: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='44' viewBox='0 0 26 44'%3E%3Cpath d='M0 0 H4 A 25 25 0 0 1 4 44 H0 Z' fill='%23fff'/%3E%3Cpath d='M4 0 A 25 25 0 0 1 4 44' fill='none' stroke='%23000' stroke-width='1'/%3E%3C/svg%3E");
	background-repeat: repeat-y;
	background-size: 26px 44px;
}

.app-right {
	display: flex;
	flex: 1;
	flex-direction: column;
	min-width: 0;

}

.rail {
	position: relative;
	z-index: 2;        /* over the top bar, which passes behind both */
	flex: 0 0 var(--rail);
	background: var(--paper);
	overflow-y: auto;
}

/* the bar scrolls its own content; its edge is drawn on the shell */
.rail-left { contain: layout paint; }
/* the right bar holds the two tiles and is always in. its edge is a
   column of round dots rather than a line — a real border can only
   do square dashes, so it's drawn as a repeating background. */
.rail-right {
	display: flex;
	flex-direction: column;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='16' viewBox='0 0 2 16'%3E%3Ccircle cx='1' cy='8' r='1' fill='%23000'/%3E%3C/svg%3E");
	background-repeat: repeat-y;
	background-position: left top;
	background-size: 2px 16px;
	/* reaches 13px past the bar, matching the scallop's outermost bump */
	margin-left: -13px;
	background-clip: border-box;
	/* every inset in here is one value, including the one off the dots */
	gap: var(--rail-pad);
	padding: var(--rail-pad);
	/* starts right under the black bar */
	padding-top: calc(var(--topbar) + var(--rail-pad));

	overflow: hidden;   /* the help box scrolls, the bar doesn't */
}

/* the black strip at the very top, beside the left bar. it runs back
   under the scallop so the bumps bite into it — otherwise it stopped
   short of the left bar by the width of a bump. */
.topbar {
	position: absolute;
	top: 0;
	left: calc(var(--rail) - 13px);
	right: 0;
	z-index: 4;
	height: var(--topbar);
	background: var(--ink);
}

/* the screen's name, on a white bar directly under the black one. it
   runs the full window and both side bars pass over its ends, so it
   only shows across the middle. */
.title-bar {
	position: absolute;
	top: var(--topbar);
	left: 0;
	right: 0;
	z-index: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	height: var(--topbar);
	background-color: var(--paper);
	/* its edge is a row of round dots, the same ones the right bar
	   uses — a real border can only do square dashes */
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='2' viewBox='0 0 16 2'%3E%3Ccircle cx='8' cy='1' r='1' fill='%23000'/%3E%3C/svg%3E");
	background-repeat: repeat-x;
	background-position: left bottom;
	background-size: 16px 2px;
}
.title-bar h1 { font-size: 1.6rem; line-height: 1; }

.app-body {
	display: flex;
	flex: 1;
	min-width: 0;
	min-height: 0;
}

.app-content {
	flex: 1;
	min-width: 0;
	overflow-y: auto;
	/* clears the black bar and the white one under it; the screen's
	   own gap then sets how far off the line the tabs sit */
	padding-top: calc(var(--topbar) * 2);
}

/* =========================================================
   5. page column — one layout for every screen
   ========================================================= */
.screen {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--gap);
	width: 100%;
	max-width: var(--col);
	min-height: 100%;
	justify-content: safe center;
	margin: 0 auto;
	padding: var(--gap);
	text-align: center;
}

#homeScreen {
	max-width: none;
	height: 100%;
	min-height: 0;
	justify-content: flex-start;
	gap: var(--group);
	padding: var(--gap);
	padding-top: var(--group);
	overflow: hidden;     /* the page never scrolls — the boxes do */
	text-align: left;
}
#homeScreen .deck-organizer { flex: none; }

/* create and practice run on the same frame as home */
#makerScreen, #studyScreen {
	max-width: none;
	height: 100%;
	min-height: 0;
	justify-content: flex-start;
	gap: var(--group);
	padding: var(--gap);
	padding-top: var(--group);
	overflow: hidden;
	text-align: left;
}

/* sits in the top bar, so the title stays centred in the bar */
.back-button {
	position: absolute;
	top: 0;
	bottom: 0;
	/* the bar runs the full window now, so clear the left rail —
	   this lands it on the content column's own left edge */
	left: calc(var(--rail) + var(--gap));
	display: grid;
	place-items: center;
	width: 2rem;
	height: 2rem;
	margin: auto 0;        /* centred without a transform — the swap
	                          animation and :active both use transform */
	padding: 0;
	color: var(--ink);
	background: none;
	border: 0;
	font-size: 1.5rem;
	line-height: 1;
	cursor: pointer;
}

/* =========================================================
   6. shared pieces — buttons and the deck dropdown
   ========================================================= */

/* pill button */
.basic-button {
	align-self: center;
	padding: 0.65rem 1.5rem;
	color: #fff;
	background: var(--ink);
	border: 1px solid var(--ink);
	border-radius: 999px;
	font-weight: 600;
	cursor: pointer;
}

/* floating white panel, used by both dropdowns */
.deck-menu {
	position: absolute;
	top: calc(100% + 0.375rem);
	z-index: 20;
	max-height: 13rem;
	padding: 0.25rem;
	overflow-y: auto;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.deck-menu { left: 0; width: max(100%, 15rem); }

.empty-message {
	padding: 0.9rem 0;
	color: var(--ink);
	text-align: center;
}

/* =========================================================
   7. home — section tabs
   ========================================================= */
.deck-organizer, .quick-panel, .recorder-panel { width: 100%; }
.deck-organizer {
	position: relative;
	z-index: 2;   /* dropdowns hang out of here, so it outranks the slot */
	display: flex;
	flex-direction: column;
	gap: var(--gap);
	text-align: left;
}

/* section tabs — "cards  audio". the active one is big in the
   title font, the others sit small on the same baseline. */
.section-tabs {
	contain: layout;   /* the size swap stays inside this row */
	display: flex;
	align-items: center;   /* the small word rides the big one's middle,
	                          not its baseline */
	gap: 1.25rem;
	height: 2.25rem;   /* whole pixels — see the note on .square-button */
	/* 4px down, 4px back off the bottom: the row lands where the small
	   word wants to be, and the bar below doesn't move */
	margin: 4px 0 -4px;
	/* the padding drops the pair halfway between the white bar above
	   and the record / deck bar below. the 1px clears the font's own
	   left bearing, so the first letter starts on the button edge
	   under it. */
	padding: 1.1rem 0 0 1px;
}

.section-tab {
	padding: 0;
	background: none;
	border: 0;
	font-family: var(--title-font);
	font-size: 1.1rem;
	font-weight: 700;
	line-height: 1.1;
	cursor: pointer;
	/* no transition here on purpose — the only thing that changes is
	   font-size, and animating that re-lays out the page on every
	   frame. the size snaps in one pass, and switchSection() animates
	   the difference with a transform instead. */
}
.section-tab.active { font-size: 2.25rem; cursor: default; }
/* centring the boxes still leaves the small word low — the letters
   don't fill their line the same way at the two sizes. this lifts it
   onto the big word's middle. it's `top`, not a transform, so the
   swap animation in switchSection() has the property to itself. */
.section-tab:not(.active) { position: relative; top: -4px; }
/* the big word carries more ink below its middle, so measured centre
   reads low. this is its own lift — the small one keeps its place. */
.section-tab.active { position: relative; top: -5px; }

/* =========================================================
   8. home — deck bar
   ========================================================= */
.deck-bar { display: flex; align-items: center; gap: var(--tight); }

.deck-picker { position: relative; z-index: 30; flex: none; }

/* square icon button — the deck smiley, import, export */
.square-button {
	display: grid;
	place-items: center;
	flex: none;
	width: var(--field);
	height: var(--field);
	padding: 0;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	line-height: 1;
	cursor: pointer;
}
.square-button:hover, .deck-toggle[aria-expanded="true"] {
	color: #fff;
	background: var(--ink);
	border-color: var(--ink);
}

.deck-form { display: flex; flex: 1; gap: var(--tight); }
.deck-form input {
	flex: 1;
	min-width: 0;
	height: var(--field);
	padding: 0 0.75rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
}
.deck-form input::placeholder { color: var(--muted); }
.deck-form .basic-button { flex: none; height: var(--field); padding: 0 1.1rem; }

/* deck codes — a popup hanging off the share button */
.share-picker { position: relative; z-index: 30; flex: none; }
.share-panel {
	position: absolute;
	top: calc(100% + 0.375rem);
	right: 0;
	z-index: 20;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	width: 28rem;
	max-width: 78vw;
	padding: var(--tight);
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
	text-align: left;
}
.share-panel label { font-weight: 700; font-size: 0.85rem; }
.share-panel label + .share-row { margin-bottom: 0.5rem; }

.share-row { display: flex; align-items: stretch; gap: var(--tight); }
.share-row textarea {
	flex: 1;
	min-width: 0;
	/* exactly two lines tall, so a long code never leaves half a line
	   showing at the bottom — the rest scrolls */
	height: calc(2 * 1.4em + 1rem + 2px);
	padding: 0.5rem 0.7rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-size: 0.8rem;
	line-height: 1.4;
	resize: vertical;
	overflow-wrap: anywhere;
}
.share-row textarea::placeholder { color: var(--muted); }
.share-row .basic-button { flex: none; align-self: stretch; padding: 0 1.1rem; }

.share-note { min-height: 1.2rem; margin: 0; font-size: 0.8rem; }

/* placeholder box for the deck list — a third of the width,
   filling the blank space under the deck bar */
.deck-list-slot {
	contain: layout paint;
	position: relative;
	z-index: 1;
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--tight);
	width: 33%;
	min-height: 12rem;
	padding: var(--tight);
	overflow-y: auto;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	scrollbar-width: none;
}
.deck-list-slot::-webkit-scrollbar { width: 0; }

/* one deck — the smiley's face, then the name and what's in it */
.deck-card {
	display: flex;
	flex: none;
	align-items: center;
	gap: 0.7rem;
	width: 100%;
	padding: 0.6rem 0.7rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	text-align: left;
	cursor: pointer;
}
.deck-card:hover { color: #fff; background: var(--ink); }
.deck-card.active-deck {
	color: #fff;
	background: var(--ink);
	border-color: var(--ink);
	cursor: default;
}

/* a deck's look — one of eight outline shapes. it takes the row's
   colour, so it flips to white on the active row. */
.deck-shape {
	display: grid;
	flex: none;
	place-items: center;
	width: 28px;
	height: 28px;
}

.deck-card-text { min-width: 0; }
.deck-card-text strong,
.deck-card-text small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.deck-card-text strong { font-size: 0.95rem; }
.deck-card-text small { font-size: 0.78rem; }

/* =========================================================
   9. home — deck dropdown rows
   ========================================================= */
.deck-row {
	display: flex;
	align-items: center;
	gap: 0.25rem;
	padding: 0.15rem;
	border-radius: 6px;
}
.deck-row + .deck-row { border-top: 1px solid var(--line); }
.deck-row:hover { background: var(--hover); }
.deck-row.active-deck { box-shadow: inset 3px 0 0 var(--ink); }
.deck-row.dragging { opacity: 0.4; }
.deck-row.drop-target { outline: 1px dashed var(--ink); }

/* three-line drag handle */
.deck-handle {
	display: flex;
	flex: none;
	flex-direction: column;
	justify-content: center;
	gap: 3px;
	width: 1.75rem;
	height: 2rem;
	padding: 0 0.5rem;
	background: none;
	border: 0;
	border-radius: 6px;
	cursor: grab;
}
.deck-handle:active { cursor: grabbing; }
.deck-handle span {
	display: block;
	height: 2px;
	background: var(--line);
	border-radius: 1px;
}
.active-deck .deck-handle span { background: var(--ink); }

.deck-select {
	flex: 1;
	min-width: 0;
	padding: 0.5rem 0.4rem;
	overflow: hidden;
	background: none;
	border: 0;
	border-radius: 6px;
	text-align: left;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-weight: 600;
	cursor: pointer;
}

.deck-count {
	flex: none;
	padding: 0 0.6rem;
	color: var(--ink);
	font-size: 0.85rem;
	font-variant-numeric: tabular-nums;
}
.active-deck .deck-count { color: var(--ink); }

.deck-inline-input {
	flex: 1;
	min-width: 0;
	padding: 0.5rem 0.4rem;
	background: #fff;
	border: 1px solid var(--ink);
	border-radius: 6px;
	font-weight: 600;
}

/* =========================================================
   10. right bar — help box and the two tiles

   the tiles are shared by the create/practice pair and the
   record tile, so all three keep the same height and face.
   ========================================================= */

/* fills the blank area above the tiles. it scrolls inside itself, so
   opening a row never moves the tiles. */
.help-box {
	contain: layout paint;
	flex: 1;
	min-height: 0;
	overflow-y: auto;
	padding: var(--tight);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-size: 0.78rem;
	line-height: 1.45;
	text-align: left;
}
.help-box h2 {
	margin: 0 0 0.4rem;
	font-family: var(--title-font);
	font-size: 1rem;
}
.help-box details { border-top: 1px solid var(--line); }
.help-box summary {
	display: flex;
	justify-content: space-between;
	gap: 0.5rem;
	padding: 0.4rem 0;
	font-weight: 700;
	list-style: none;
	cursor: pointer;
}
.help-box summary::-webkit-details-marker { display: none; }
.help-box summary::after { content: '+'; }
.help-box details[open] summary::after { content: '\2212'; }
.help-box summary:hover { text-decoration: underline; }
.help-box p { margin: 0 0 0.6rem; }
/* stacked at the bottom of the right bar. the bar supplies the
   edge inset, so this only has to match it between the tiles. */
.quick-panel {
	display: flex;
	flex: none;
	flex-direction: column;
	gap: var(--rail-pad);
}

.quick-action {
	display: flex;
	flex: none;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 0.3rem;
	min-height: 10.5rem;
	padding: 1.5rem 0.75rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	cursor: pointer;
}
.quick-action.primary { color: #fff; background: var(--ink); border-color: var(--ink); }

/* hover lifts the tile and wakes its face up — the colours stay put */
.quick-action { transition: transform var(--quick) var(--ease); }
.quick-action:hover { transform: translateY(-4px); }
.quick-action:active { transform: translateY(-1px); }
.quick-action small { transition: transform 0.25s var(--ease); }
.quick-action:hover small { animation: face-wiggle 0.5s var(--ease); }

@keyframes face-wiggle {
	0%   { transform: none; }
	25%  { transform: translateY(-3px) rotate(-6deg); }
	55%  { transform: translateY(0) rotate(5deg); }
	80%  { transform: translateY(-1px) rotate(-2deg); }
	100% { transform: none; }
}

.quick-action .action-icon { color: var(--ink); font-size: 0.85rem; }
.quick-action.primary .action-icon { color: #fff; }

.quick-action strong, .quick-action small { display: block; }
.quick-action small {
	order: -1;
	margin-bottom: 0.3rem;
	font-size: 1.15rem;
	line-height: 1.2;
}
.quick-action strong { font-size: 0.95rem; font-weight: 700; }
.quick-action b { display: none; }

/* =========================================================
   11. create screen
   ========================================================= */

.form-note:empty { display: none; }

.maker-bar {
	position: relative;   /* the note hangs off this */
	display: flex;
	align-items: flex-end;
	flex: none;
	gap: var(--tight);
	width: 100%;
}
.maker-field { display: flex; flex: 1; flex-direction: column; gap: 0.25rem; min-width: 0; }
.maker-field span { font-size: 0.85rem; font-weight: 700; }
.maker-field textarea {
	width: 100%;
	height: 4.5rem;
	padding: 0.6rem 0.75rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	resize: none;
}
.maker-field textarea::placeholder { color: var(--muted); }
.maker-bar .basic-button { flex: none; height: 4.5rem; align-self: flex-end; padding: 0 1.5rem; }

/* the note sits over the bar's corner, so nothing shifts for it */
.form-note {
	position: absolute;
	right: 0;
	bottom: calc(100% + 0.25rem);
	margin: 0;
	font-weight: 700;
	font-size: 0.85rem;
}

/* the two scrolling lists share their frame and scrollbar */
#cardList, #recordingList {
	contain: layout paint;
	width: 100%;
	margin: 0;
	padding: 0 0.25rem 0 0.25rem;
	overflow-y: auto;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	list-style: none;
	text-align: left;
	scrollbar-width: none;
}
#cardList::-webkit-scrollbar,
#recordingList::-webkit-scrollbar { display: none; }

#cardList {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--tight);
	min-height: 0;
	padding: var(--tight);
}
#cardList .empty-message { padding: 1.25rem 0; }
#recordingList { display: flex; flex-direction: column; gap: var(--tight); padding: var(--tight); }
#recordingList .empty-message { padding: 1.25rem 0; }

/* download-all shows a pause while it's working its way down the list */
.square-button .icon-pause { display: none; }
.square-button.is-packing .icon-download { display: none; }
.square-button.is-packing .icon-pause { display: block; }

/* nothing to act on: a dashed outline rather than a faded one, since
   a faded black is just grey */
.square-button:disabled {
	cursor: default;
	border-style: dashed;
}
.square-button:disabled:hover { color: var(--ink); background: #fff; }

#cardList .card-item {
	display: flex;
	gap: 0.75rem;
	margin: 0;
	padding: 0.55rem 0.6rem;
	border-radius: 6px;
	cursor: context-menu;
}
#cardList .card-item:hover { background: var(--hover); }
#cardList .card-text {
	flex: 1;
	min-width: 0;
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

/* =========================================================
   12. practice screen
   ========================================================= */
.question-box { position: relative; flex: none; width: 100%; }

#studyQuestion {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 8rem;
	margin: 0;
	padding: 1.5rem 1.25rem;
	overflow-y: auto;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-size: clamp(1.3rem, 4vw, 2.1rem);
	line-height: 1.3;
	text-align: center;
	overflow-wrap: anywhere;
}
/* nothing to ask — it's a note, not a question, so it drops the
   heading weight and most of the size */
#studyQuestion.is-empty {
	font-size: 1.15rem;
	font-weight: 400;
}

/* the four answers fill the rest, so the keyboard quadrants line up
   with where the buttons actually are */
.answer-options {
	display: grid;
	flex: 1;
	grid-template-columns: 1fr 1fr;
	grid-template-rows: 1fr 1fr;
	gap: var(--tight);
	min-height: 0;
	margin: 0;
}
.answer-button {
	display: flex;
	align-items: center;
	justify-content: center;
	animation: deal-in 0.3s var(--ease) backwards;
	min-width: 0;
	min-height: 0;
	padding: 1rem 1.25rem;
	font-size: 1.05rem;
	text-align: center;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-weight: 600;
	overflow-wrap: anywhere;
	hyphens: auto;
	cursor: pointer;
}
.answer-button:nth-child(1) { animation-delay: 0.02s; }
.answer-button:nth-child(2) { animation-delay: 0.07s; }
.answer-button:nth-child(3) { animation-delay: 0.12s; }
.answer-button:nth-child(4) { animation-delay: 0.17s; }
.answer-button:hover { color: #fff; background: var(--ink); }
.answer-button.correct:hover, .answer-button.incorrect:hover { color: inherit; background: inherit; }

.answer-button.correct { animation: pop 0.35s var(--ease); }
.answer-button.incorrect { animation: shake 0.4s ease-in-out; }

/* right fills in, wrong gets struck through — no colour needed */
.answer-button.correct {
	color: #fff;
	background: var(--ink);
	border-color: var(--ink);
}
.answer-button.incorrect {
	color: var(--ink);
	background: #fff;
	border-color: var(--ink);
	text-decoration: line-through;
}

/* sits over the question's corner — the grid below never moves */
#studyFeedback:empty { display: none; }
#studyFeedback {
	position: absolute;
	right: 0.75rem;
	bottom: 0.5rem;
	margin: 0;
	font-size: 0.85rem;
	font-weight: 700;
}

/* =========================================================
   13. right-click menu
   ========================================================= */
.context-menu {
	position: fixed;
	z-index: 30;
	width: 11rem;
	padding: 0.3rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 6px 20px rgba(0, 0, 0, 0.14);
}
.context-action {
	display: block;
	width: 100%;
	padding: 0.5rem 0.6rem;
	background: none;
	border: 0;
	border-radius: 6px;
	text-align: left;
	cursor: pointer;
}
.context-action:hover:not(:disabled) { background: var(--hover); }
.context-action:disabled { color: var(--ink); opacity: 0.35; cursor: not-allowed; }

/* =========================================================
   14. audio — layout
   ========================================================= */
.recorder-panel {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--group);
	min-height: 0;
	/* makes up whatever the screen gap above already supplied, so the
	   tabs always clear this panel by a full --gap */
	margin-top: calc(var(--gap) - var(--group));
}

/* the control row, same shape as the deck bar */
.audio-bar {
	position: relative;   /* the confirm popup hangs off it */
	display: flex;
	align-items: center;
	gap: var(--tight);
}
.audio-bar .basic-button { height: var(--field); padding: 0 1.4rem; }
.audio-bar .record-status { flex: 1; margin: 0 0 0 0.5rem; }

/* the ask before something that can't be taken back. a popup hanging
   off the button that started it, the same one the deck menus use. it
   drops itself after a moment if you ignore it. */
@keyframes confirm-in {
	from { opacity: 0; transform: translateY(-6px) scale(0.97); }
	to   { opacity: 1; transform: none; }
}
.confirm-pop {
	position: absolute;
	top: calc(100% + 0.375rem);
	right: 0;
	z-index: 20;
	display: flex;
	flex-direction: column;
	gap: var(--tight);
	width: 17rem;
	max-width: 78vw;
	padding: 0.8rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
	text-align: left;
	font-size: 0.9rem;
	transform-origin: top right;
	animation: confirm-in 0.22s var(--ease);
}
/* the two answers share the width under the question */
.confirm-answers { display: flex; gap: var(--tight); }
.confirm-answers .chip-button { flex: 1; }
.chip-button {
	padding: 0.25rem 0.85rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 999px;
	font-size: 0.85rem;
	font-weight: 700;
	cursor: pointer;
}
.chip-button:hover { color: #fff; background: var(--ink); }
#confirmChipYes { color: #fff; background: var(--ink); }
#confirmChipYes:hover { color: var(--ink); background: #fff; }

/* the button that asked stays lit while the chip is up */
.square-button.is-armed {
	color: #fff;
	background: var(--ink);
	border-color: var(--ink);
}

/* list on the left, everything live on the right */
.audio-body {
	display: flex;
	flex: 1;
	gap: var(--gap);
	min-height: 0;
}
#recordingList {
	position: relative;    /* so a row's offsetTop is its slot in here */
	width: 100%;           /* the whole row until there's a video beside it */
	flex: none;
	align-self: stretch;
	min-height: 0;
}

/* the stage only appears while you're aiming the sensing box */
.audio-stage {
	display: none;
	flex: 1;
	flex-direction: column;
	gap: var(--group);
	min-width: 0;
}
.recorder-panel.is-tuning .audio-stage { display: flex; }
#recordToggle.recording {
	color: var(--ink);
	background: #fff;
	border-color: var(--line);
}

/* the recorder sits in the bar between record and the options.
   idle it's a placeholder; live it's the pulse, level and switch count. */
.bar-meter { position: relative; flex: 1; min-width: 0; }

.meter-idle,
.live-meter {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	width: 100%;
	height: var(--field);
	padding: 0 0.75rem;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	font-size: 0.85rem;
}
.meter-idle { justify-content: center; background: #fff; }
.live-meter { color: #fff; background: var(--ink); }
.live-meter #levelCanvas { flex: 1; min-width: 0; height: 10px; }
.live-meter .clip-label { flex: none; font-size: 0.78rem; }
.live-meter .switch-count {
	flex: none;
	padding: 0.1rem 0.5rem;
	color: var(--ink);
	background: #fff;
	border-radius: 999px;
	font-size: 0.72rem;
	font-weight: 700;
	white-space: nowrap;
}

/* whichever of the two fits the moment */
.recorder-panel.is-live .meter-idle { display: none; }
.recorder-panel:not(.is-live) .live-meter { display: none; }

/* the video only shows while you're aiming the sensing box */
.preview-wrap:not(.showing-video) { display: none; }

/* a message covers the meter rather than pushing anything down.
   it's the same box, so the page never moves for it. */
.record-status {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: none;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	padding: 0 0.75rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	margin: 0;
	font-size: 0.85rem;
	text-align: center;
}
.record-status:not(:empty) { display: flex; }
.record-status.is-error { font-weight: 700; }

/* =========================================================
   15. audio — the video preview

   the video sits in this box so pixels can be sampled, and is only
   shown while the settings panel is open.
   ========================================================= */
.preview-wrap {
	position: relative;
	display: flex;
	align-items: center;
	width: 100%;
	height: 3.25rem;
	padding: 0 0.6rem;
	background: var(--ink);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	overflow: hidden;
}

.preview-wrap video {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	opacity: 0;
	pointer-events: none;
}
.preview-wrap.showing-video {
	height: auto;
	padding: 0;
	line-height: 0;
}
.preview-wrap.showing-video video { position: static; display: block; opacity: 1; }
/* =========================================================
   16. audio — sensing box and its settings
   ========================================================= */
.preview-wrap:not(.showing-video) .sense-box { display: none; }

.sense-box {
	position: absolute;
	z-index: 4;
	cursor: move;
	touch-action: none;
	/* sits over arbitrary video, so it's a white frame with a black
	   outline either side — readable on any footage */
	border: 2px solid #fff;
	box-shadow: 0 0 0 1px var(--ink), inset 0 0 0 1px var(--ink);
	background: rgba(255, 255, 255, 0.16);
	border-radius: 3px;
}

.sense-box.is-triggered { background: rgba(255, 255, 255, 0.5); }

.sense-controls {
	position: absolute;
	top: 2.9rem;
	right: 0.5rem;
	z-index: 2;
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	width: min(20rem, calc(100% - 1rem));
	padding: 0.6rem;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
	text-align: left;
	line-height: 1.5;
	font-size: 0.85rem;
}
.sense-controls label {
	display: grid;
	grid-template-columns: 5.5rem 1fr 2.6rem;
	align-items: center;
	gap: 0.4rem;
}
.sense-controls output {
	text-align: right;
	font-variant-numeric: tabular-nums;
}
.sense-controls input[type="range"] {
	width: 100%;
	height: 1.25rem;
	margin: 0;
	background: none;
	-webkit-appearance: none;
	appearance: none;
	cursor: pointer;
}
.sense-controls input[type="range"]::-webkit-slider-runnable-track {
	height: 6px;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 999px;
}
.sense-controls input[type="range"]::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 1rem;
	height: 1rem;
	margin-top: -6px;
	background: var(--ink);
	border: 1px solid var(--line);
	border-radius: 999px;
}
.sense-controls input[type="range"]::-moz-range-track {
	height: 6px;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 999px;
}
.sense-controls input[type="range"]::-moz-range-thumb {
	width: 1rem;
	height: 1rem;
	background: var(--ink);
	border: 1px solid var(--line);
	border-radius: 999px;
}

/* the change/trigger numbers, only shown while the settings are open */
.sense-readout {
	display: none;
	margin: 0;
	text-align: center;
	font-size: 0.85rem;
	font-variant-numeric: tabular-nums;
}
.preview-wrap.showing-video ~ .sense-readout { display: none; }
.recorder-panel.is-tuning #recordingList { display: none; }
.recorder-panel.is-tuning .record-status { display: none; }

.text-button {
	align-self: center;
	padding: 0.2rem 0.3rem;
	background: none;
	border: 0;
	font-size: 0.85rem;
	text-decoration: none;
	cursor: pointer;
}
.text-button:hover { text-decoration: underline; }
.panel-handle {
	height: 0.6rem;
	margin: -0.2rem 0 0.15rem;
	background:
		repeating-linear-gradient(90deg, var(--ink) 0 3px, transparent 3px 6px)
		center / auto 2px no-repeat;
	cursor: grab;
	touch-action: none;
}
.panel-handle:active { cursor: grabbing; }

/* =========================================================
   17. audio — saved clips

   one row per clip: play, scrubber, label, discard. the
   native audio element is kept in the dom for playback only,
   since its own controls can't be recolored.
   ========================================================= */
/* the clip list fills its column now, so it no longer needs a cap —
   and it always holds at least the empty line */
#recordingList:empty { display: none; }

.recording-item {
	display: flex;
	align-items: center;
	gap: 0.4rem;
	margin: 0;
	padding: 0.75rem 0.6rem;
	border-radius: 6px;
}
.recording-item audio { display: none; }

/* three lines to grab, the same grip the deck rows have. the list
   scrolls itself if you drag a clip against either end. */
.clip-handle {
	display: flex;
	flex: none;
	flex-direction: column;
	justify-content: center;
	gap: 3px;
	width: 1.25rem;
	height: 1.5rem;
	padding: 0 0.3rem;
	background: none;
	border: 0;
	cursor: grab;
	touch-action: none;   /* a drag here moves the clip, not the list */
}
.clip-handle:active { cursor: grabbing; }
.clip-handle span {
	display: block;
	height: 1px;
	background: var(--line);
}
/* while a clip is in hand, nothing else on the page takes the drag —
   no text selecting itself as the cursor sweeps past */
.sorting-clips, .sorting-clips * { user-select: none; }
.sorting-clips { cursor: grabbing; }

/* the row in your hand: it's picked up off the page, so it outlines
   itself. its slot stays behind as the space it would drop into. */
.recording-item.is-lifted {
	position: relative;
	z-index: 2;
	background: #fff;
	outline: 1px solid var(--line);
	outline-offset: -1px;
	cursor: grabbing;
	will-change: transform;   /* its own layer — the list isn't repainted
	                             every time it moves a pixel */
}

/* play / pause */
.clip-play {
	display: grid;
	place-items: center;
	flex: none;
	width: 1.5rem;
	height: 1.5rem;
	padding: 0;
	margin-left: -0.1rem;
	margin-right: 0.1rem;
	color: var(--ink);
	background: none;
	border: 0;
	font-size: 0.75rem;
	line-height: 1;
	cursor: pointer;
}
/* playing reads off the glyph alone now — there's no ring to fill */
.is-playing .clip-play { color: var(--ink); }

/* scrubber */
.clip-track {
	position: relative;
	flex: 1;
	min-width: 0;
	height: 15px;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 999px;
	overflow: hidden;
	cursor: pointer;
}
.clip-fill {
	width: 0;
	height: 100%;
	background: var(--ink);
}

/* sits over the fill; difference blending flips it black-on-white
   and white-on-black as the bar sweeps past */
.clip-text {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, calc(-50% + 0.5px));
	color: #fff;
	mix-blend-mode: difference;
	font-size: 9px;
	line-height: 1;
	white-space: nowrap;
	user-select: none;
	pointer-events: none;
}

/* double-click puts just this one span back in the event path */
.clip-text.is-editing {
	user-select: text;
	pointer-events: auto;
	cursor: text;
	outline: none;
}

.clip-label {
	flex: none;
	font-size: 0.85rem;
	font-variant-numeric: tabular-nums;
}
.recording-item .clip-label { margin-top: 2px; }

/* download and discard */
.clip-download, .clip-discard {
	flex: none;
	width: 1rem;
	height: 1rem;
	padding: 0;
	color: var(--ink);
	background: none;
	border: 0;
	border-radius: 2px;
	line-height: 1;
	cursor: pointer;
}
.clip-download { font-size: 0.9rem; margin-top: 3px; }
.clip-discard { font-size: 1.1rem; margin-top: 1px; }

/* =========================================================
   18. audio — the live row and its pulse
   ========================================================= */
.live-meter .clip-track {
	background: var(--ink);
	border-color: #fff;
}

/* the blinking dot standing in for the play button */
.clip-pulse {
	flex: none;
	width: 1.5rem;
	height: 1.5rem;
	background: radial-gradient(circle at center, #fff 0 26%, transparent 27%);
	border: 1px solid #fff;
	border-radius: 999px;
	animation: clip-pulse 1.2s ease-in-out infinite;
}
@keyframes clip-pulse {
	0%, 100% { opacity: 1; }
	50% { opacity: 0.35; }
}

/* the bar marches while the clip fills up */
.clip-fill.live {
	width: 100%;
	background: repeating-linear-gradient(
		90deg,
		#fff 0 6px,
		var(--ink) 6px 12px
	);
	animation: clip-sweep 0.9s linear infinite;
}
@keyframes clip-sweep {
	from { transform: translateX(0); }
	to { transform: translateX(12px); }
}

/* =========================================================
   19. small screens
   ========================================================= */
/* no room for a right bar — it becomes a strip under the content,
   so the two tiles stay reachable */
@media (max-width: 1000px) {
	.app-body { flex-direction: column; }
	.rail-right {
		flex: none;
		background-repeat: repeat-x;
		background-size: 16px 2px;
		background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='2' viewBox='0 0 16 2'%3E%3Ccircle cx='8' cy='1' r='1' fill='%23000'/%3E%3C/svg%3E");
		margin-left: 0;
	}
	.quick-panel { flex-direction: row; flex: none; }
	.quick-action { flex: 1; }
	/* the strip takes at most a third, so the page above it still has
	   room — the help box scrolls inside whatever is left */
	.rail-right { max-height: 38%; overflow: hidden; padding-top: var(--rail-pad); }
	.help-box { flex: 1; min-height: 0; overflow-y: auto; }
}

@media (max-width: 760px) {
	.rail-left { display: none; }
	.app-shell::after { display: none; }   /* its edge goes with it */
	.topbar { left: 0; }                   /* no scallop to reach under */
	.back-button { left: var(--gap); }     /* no rail to clear */
}

@media (max-width: 560px) {
	.screen { gap: 1.25rem; padding: 2.5rem 1rem 2.5rem; }
	h1 { font-size: 1.6rem; }
	.deck-bar { flex-direction: column; }
	.deck-picker { align-self: flex-start; }
	.deck-form { width: 100%; }
	.answer-options { gap: 0.5rem; }
	.answer-button {
		min-height: 3.25rem;
		padding: 0.6rem 0.7rem;
		font-size: 0.9rem;
	}
	#studyQuestion { min-height: 6rem; padding: 1.25rem 1rem; }
}

@media (max-width: 380px) {
	.screen { padding: 2rem 0.75rem 2rem; }
	.answer-button { min-height: 3rem; font-size: 0.85rem; }
	.quick-action { min-height: 8rem; }
	.quick-action small { font-size: 1.25rem; }
}

@media (prefers-reduced-motion: reduce) {
	* { transition: none !important; animation: none !important; }
}