/* SpellTrainer — the whole of the site's styling.
 *
 * One same-origin stylesheet, no framework, no icon font, no build step
 * (REQ-W-050, REQ-W-052). Every colour here is from design/brand.md, which measured them
 * off the app icon and calculated the contrast rather than estimating it.
 *
 * The one to remember: the brand blue is 2.37:1 against white and cannot carry white
 * text. Deep is what white sits on. */

:root {
	color-scheme: light dark;

	--ink: #10193a;
	--slate: #4a5568;
	--deep: #005c96;
	--link: #0a6fb5;
	--sky: #00b3fe;
	--amber: #feb401;
	--paper: #ffffff;
	--tint: #f4f9ff;
	--rule: #dce6f2;

	--page: var(--paper);
	--text: var(--ink);
	--muted: var(--slate);
	--band: var(--tint);
	--accent: var(--link);
	--heading: var(--ink);

	--measure: 38rem;
	--wide: 64rem;
	--radius: 14px;
}

/* Ink becomes the page and Paper the text, as design/brand.md sets out. The ratios were
 * recalculated after inverting rather than assumed to carry over: the light secondary is
 * 9.1:1 on Ink, and Sky light — which cannot carry text on white — is 7.7:1 here. */
@media (prefers-color-scheme: dark) {
	:root {
		--page: #0c1430;
		--text: #f2f5fb;
		--muted: #b8c2d9;
		--band: #131d42;
		--accent: #57cdff;
		--heading: #ffffff;
		--rule: #263258;
	}
}

*,
*::before,
*::after {
	box-sizing: border-box;
}

html {
	/* REQ-W-061 — nothing below 16px, anywhere. */
	font-size: 100%;
}

body {
	margin: 0;
	/* REQ-W-042 — system fonts. A downloaded face is a request to somebody's server. */
	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
	font-size: 1.0625rem;
	line-height: 1.65;
	color: var(--text);
	background: var(--page);
	-webkit-text-size-adjust: 100%;
}

/* REQ-W-066 — a focus ring that is actually visible, on everything reachable by tab. */
:focus-visible {
	outline: 3px solid var(--accent);
	outline-offset: 3px;
	border-radius: 4px;
}

a {
	color: var(--accent);
	text-underline-offset: 0.15em;
}

a:hover {
	text-decoration-thickness: 2px;
}

img,
video {
	max-width: 100%;
	height: auto;
}

/* ---- The frame -------------------------------------------------------------------- */

.skip {
	position: absolute;
	left: -9999px;
}

.skip:focus {
	position: static;
	display: inline-block;
	margin: 0.75rem;
}

.bar {
	border-bottom: 1px solid var(--rule);
	background: var(--page);
}

.bar__inner,
.band__inner,
main {
	max-width: var(--wide);
	margin: 0 auto;
	padding: 0 1.25rem;
}

/* Two widths, not one.
 *
 * Running text wants a 38rem measure and the container is 64rem, so a band of nothing but
 * paragraphs left two fifths of a tinted rectangle empty — which reads as a section
 * somebody did not finish rather than as a margin. Text bands are now a column; the bands
 * carrying a screenshot keep the full width, because a 1600-pixel picture of an iPad has
 * something to do with the room. */
.band--text .band__inner {
	max-width: 48rem;
}

.bar__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
	padding-block: 0.85rem;
}

.wordmark {
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	font-weight: 700;
	font-size: 1.15rem;
	color: var(--heading);
	text-decoration: none;
}

.wordmark img {
	width: 34px;
	height: 34px;
	border-radius: 8px;
}

.bar nav {
	display: flex;
	gap: 1.25rem;
}

/* ---- Rhythm ----------------------------------------------------------------------- */

.band {
	padding-block: 3.5rem;
}

.band--tint {
	background: var(--band);
}

.band__inner > * + * {
	margin-top: 1.1rem;
}

h1,
h2,
h3 {
	color: var(--heading);
	line-height: 1.2;
	text-wrap: balance;
	margin: 0;
}

h1 {
	font-size: clamp(2rem, 6vw, 2.9rem);
	letter-spacing: -0.02em;
}

h2 {
	font-size: clamp(1.4rem, 3.4vw, 1.85rem);
	letter-spacing: -0.01em;
}

h3 {
	font-size: 1.15rem;
}

p,
li {
	max-width: var(--measure);
}

.lede {
	font-size: 1.2rem;
	color: var(--muted);
}

/* An amber rule under a section heading — the one place the accent colour appears, and
 * never as text (design/brand.md: amber is not a text colour). */
.band h2::after {
	content: "";
	display: block;
	width: 3rem;
	height: 4px;
	margin-top: 0.7rem;
	border-radius: 2px;
	background: var(--amber);
}

/* ---- The hero --------------------------------------------------------------------- */

.hero {
	padding-block: 3.5rem 3rem;
}

.hero__grid {
	display: grid;
	gap: 2.5rem;
	align-items: center;
}

/* The film takes four tenths of the hero rather than a corner of it.
 *
 * At 22rem the tree sat in the middle of a pale green square with the film's own margin
 * all round it — a small tree in a large box, which is the exact defect the app's own tree
 * panel went through four revisions to fix. The film cannot be cropped without cropping
 * the caption it draws, so the column is what gives. */
@media (min-width: 56rem) {
	.hero__grid {
		grid-template-columns: 1.15fr 1fr;
	}
}

/* REQ-W-012 — the three things a parent must know before she reads anything else.
 * Not a footnote: a parent who reaches the bottom of the page and only then learns she
 * needs a pencil has been wasted. */
.needs {
	display: inline-block;
	max-width: 100%;
	margin: 0;
	padding: 0.7rem 1.1rem;
	border-radius: var(--radius);
	background: var(--deep);
	color: #fff;
	font-weight: 600;
}

@media (prefers-color-scheme: dark) {
	.needs {
		background: #0b4d7d;
	}
}

/* The idea the app is built on, under the requirement line in the hero. A sub-heading
 * the size of body text, bold, and a paragraph — not a second headline. */
.why {
	margin: 1.5rem 0 0;
	padding-left: 1rem;
	border-left: 4px solid var(--amber);
}

.why h2 {
	margin: 0 0 0.4rem;
	font-size: 1.15rem;
	font-weight: 700;
}

.why p {
	margin: 0;
	color: var(--muted);
}

.coming {
	font-weight: 600;
	color: var(--muted);
}

/* ---- The film --------------------------------------------------------------------- */

figure {
	margin: 0;
}

figcaption {
	margin-top: 0.7rem;
	font-size: 1rem;
	color: var(--muted);
	max-width: var(--measure);
}

.film {
	width: 100%;
	border-radius: var(--radius);
	background: #eaf1e6;
	display: block;
}

/* Stopping the film under `prefers-reduced-motion` is `calm.js`'s job, not this file's.
 *
 * CSS cannot stop a video autoplaying. A rule here used to claim it did — `transition:
 * none` under the media query, with a comment saying the film would not play itself —
 * which is the worst kind of wrong: the requirement looked met by anyone reading the
 * stylesheet, and the film autoplayed regardless. */

/* ---- Screenshots ------------------------------------------------------------------ */

.shots {
	display: grid;
	gap: 2rem;
	margin-top: 2rem;
}

@media (min-width: 44rem) {
	.shots--two {
		grid-template-columns: 1fr 1fr;
	}
}

.shot img {
	width: 100%;
	border-radius: var(--radius);
	border: 1px solid var(--rule);
	background: var(--paper);
}

/* ---- Cards ------------------------------------------------------------------------ */

.cards {
	display: grid;
	gap: 1.25rem;
	margin-top: 1.75rem;
}

@media (min-width: 44rem) {
	.cards--three {
		grid-template-columns: repeat(3, 1fr);
	}
}

.card {
	padding: 1.4rem;
	border: 1px solid var(--rule);
	border-radius: var(--radius);
	background: var(--page);
}

.card h3 {
	margin-bottom: 0.4rem;
}

.card p {
	margin: 0;
}

/* ---- Questions -------------------------------------------------------------------- */

.qa {
	margin-top: 1.5rem;
}

.qa > div + div {
	margin-top: 1.4rem;
	padding-top: 1.4rem;
	border-top: 1px solid var(--rule);
}

.qa h3 {
	margin-bottom: 0.3rem;
}

.qa p {
	margin: 0;
}

/* ---- Lists ------------------------------------------------------------------------ */

ul.plain {
	padding-left: 1.2rem;
}

ul.plain li + li {
	margin-top: 0.5rem;
}

/* ---- The privacy page ------------------------------------------------------------- */

/* REQ-W-023 — the styling is ours, the text is not. Nothing in here changes a word. */
.policy {
	max-width: var(--measure);
}

.policy h2 {
	margin-top: 2.5rem;
	font-size: 1.25rem;
}

.policy h2::after {
	display: none;
}

.policy ul {
	padding-left: 1.2rem;
}

.policy li {
	margin: 0.4rem 0;
}

/* ---- Footer ----------------------------------------------------------------------- */

footer.site {
	border-top: 1px solid var(--rule);
	padding-block: 2rem 3rem;
	color: var(--muted);
	font-size: 1rem;
}

footer.site nav {
	display: flex;
	gap: 1.25rem;
	flex-wrap: wrap;
	margin-bottom: 0.6rem;
}

/* ---- Wide things ------------------------------------------------------------------ */

/* Nothing here is wide enough to need it today, but a table or a code block added later
 * scrolls inside itself rather than making the whole page scroll sideways (REQ-W-061). */
.scrolls {
	overflow-x: auto;
}

pre {
	overflow-x: auto;
	padding: 1rem;
	border-radius: var(--radius);
	background: var(--band);
	font-size: 1rem;
}

/* ---- The demonstrations ----------------------------------------------------------- */
/* The app's own screens (img.demo__frame, stacked), with the child's taps and pencil
 * played over them by demo.js adding classes. Everything that moves is here. Without the
 * script, or under prefers-reduced-motion when the script stands down, `.is-live` is never
 * set: the frame marked `.is-last` shows, the overlays stay hidden, and the figure is a
 * complete picture of the screen the child ends up on (REQ-W-051, REQ-W-065). */

.demo {
	margin: 2rem auto;
	max-width: 44rem;
}

.demo--wide { max-width: 56rem; }

/* The hero's tree: square, drawn on a canvas by tree.js over the app's own still, which is
 * what shows without scripting. */
.demo--tree { max-width: 34rem; margin-top: 0; }
.demo--tree .demo__stage { aspect-ratio: 1 / 1; }
.demo--tree .tree-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: none; }
.demo--tree.is-live .tree-canvas { display: block; }
.demo--tree .demo__strip { text-align: center; }

/* A still shown once, narrower than the demonstrations beside it. */
.shot--narrow { max-width: 34rem; margin: 1.5rem auto 0; }

.demo__stage {
	position: relative;
	width: 100%;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	border-radius: var(--radius);
	border: 1px solid var(--rule);
	background: var(--paper);
}

.demo__frame {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0;
	transition: opacity 0.4s ease;
}

.demo:not(.is-live) .demo__frame.is-last,
.demo .demo__frame.on { opacity: 1; }

/* The tree's frames cross-fade a little slower, so a leaf arrives rather than blinks. */
.demo--tree .demo__frame { transition-duration: 0.55s; }

/* The overlay: the pencil's strokes, in the still's own coordinate space. */
.demo__over {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

.demo:not(.is-live) .demo__over,
.demo:not(.is-live) .demo__tap,
.demo:not(.is-live) .demo__sound,
.demo:not(.is-live) .demo__typed { display: none; }

.demo__over .ink {
	fill: none;
	stroke: #1c1c1e;
	stroke-width: 9;
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-dasharray: 1;
	stroke-dashoffset: 1;
	opacity: 0;
	transition: stroke-dashoffset 0.5s linear, opacity 0s linear;
}

.demo__over .ink.on { stroke-dashoffset: 0; opacity: 1; }

/* A touch: a ring growing from the fingertip. A press stays down a moment longer. */
.demo__tap {
	position: absolute;
	width: 5.5%;
	aspect-ratio: 1;
	border-radius: 50%;
	transform: translate(-50%, -50%) scale(0.3);
	background: rgba(0, 179, 254, 0.28);
	border: 2px solid var(--deep);
	opacity: 0;
	pointer-events: none;
}

.demo__tap.on { animation: demo-tap 0.6s ease-out forwards; }
.demo__tap.is-press.on { animation: demo-press 0.9s ease-out forwards; }

@keyframes demo-tap {
	0% { opacity: 1; transform: translate(-50%, -50%) scale(0.3); }
	100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); }
}

@keyframes demo-press {
	0% { opacity: 1; transform: translate(-50%, -50%) scale(0.3); }
	40% { opacity: 0.9; transform: translate(-50%, -50%) scale(0.9); }
	100% { opacity: 0; transform: translate(-50%, -50%) scale(1.4); }
}

/* The word being spoken: three rings leaving the control. */
.demo__sound {
	position: absolute;
	width: 7%;
	aspect-ratio: 1;
	border-radius: 50%;
	transform: translate(-50%, -50%);
	border: 3px solid var(--deep);
	opacity: 0;
	pointer-events: none;
}

.demo__sound.on { animation: demo-sound 1.4s ease-out 2; }

@keyframes demo-sound {
	0% { opacity: 0.9; transform: translate(-50%, -50%) scale(0.4); }
	100% { opacity: 0; transform: translate(-50%, -50%) scale(1.6); }
}

/* Letters typed into a field: a box laid over the field in the still. */
.demo__typed {
	position: absolute;
	transform: translateY(-50%);
	padding: 0 0.6em;
	font: 600 clamp(14px, 2.2vw, 22px)/1.9 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
	color: var(--ink);
	background: #fff;
	border-radius: 0.5em;
	pointer-events: none;
	white-space: nowrap;
}

.demo__typed::after {
	content: "";
	display: inline-block;
	width: 2px;
	height: 1.1em;
	margin-left: 2px;
	vertical-align: -0.2em;
	background: var(--deep);
	animation: demo-blink 1s steps(2, start) infinite;
}

@keyframes demo-blink { to { visibility: hidden; } }

/* The words under the screen. Kept at a fixed height so the page does not jump. */
.demo__strip {
	min-height: 2.4em;
	margin: 0.7rem 0 0;
	font-size: 1.05rem;
	font-weight: 600;
	color: var(--text);
	opacity: 0;
	transition: opacity 0.3s ease;
}

.demo__strip.on { opacity: 1; }
.demo:not(.is-live) .demo__strip { display: none; }

.demo figcaption {
	margin-top: 0.6rem;
	font-size: 0.95rem;
	color: var(--muted);
}

/* Snapping back at the top of a loop: no transitions for one frame. */
.demo.is-resetting * { transition: none !important; animation: none !important; }

@media (prefers-reduced-motion: reduce) {
	.demo *, .demo *::before, .demo *::after { animation: none !important; transition: none !important; }
}
