/* Homepage projects: the four cards sit in a 2 x 2 grid, and circuits branch out from each card's outer
   edge into the space either side, ending in chips that say something the card doesn't. On wide
   screens the cards keep the size they'd have three across, centred, which leaves that space;
   circuits.js places the chips and draws the traces from the live layout, and only when there's room.
   Narrower screens get the plain grid (full width, then site.css's own layouts below 900px). */

@media (min-width: 901px) {
	.projects--four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1100px) {
	.projects--four { grid-template-columns: repeat(2, calc((100% - 2.5rem) / 3)); justify-content: center; }
}

.projects-section { position: relative; overflow-x: clip; }

.circuit-board { display: none; position: absolute; inset: 0; pointer-events: none; }
.circuit-board.is-on { display: block; }

.circuit-group { position: absolute; }
.circuit-group__label {
	margin-bottom: 0.55rem;
	font: 400 0.68rem/1 var(--mono);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	white-space: nowrap;
	color: var(--text-faint);
}
.circuit-group[data-side="left"] .circuit-group__label { text-align: right; }
.circuit-group ul { display: grid; gap: 0.5rem; }

.circuit__chip {
	display: grid;
	gap: 0.05rem;
	padding: 0.45rem 0.7rem;
	border: 1px solid rgba(var(--accent-rgb), 0.28);
	border-radius: 8px;
	background: var(--bg-card);
	pointer-events: auto;
	transition: border-color 0.25s, box-shadow 0.25s;
}
.circuit__chip.is-lit {
	border-color: rgba(var(--accent-rgb), 0.75);
	box-shadow: 0 0 18px rgba(var(--accent-rgb), 0.14);
}
.circuit__name { font: 500 0.75rem/1.3 var(--mono); color: var(--accent); overflow-wrap: anywhere; }
.circuit__detail { font-size: 0.75rem; line-height: 1.35; color: var(--text-muted); }
/* Narrow sides: smaller chips so the longest names still fit. */
.circuit-board.is-compact .circuit__chip { padding: 0.4rem 0.55rem; }
.circuit-board.is-compact .circuit__name { font-size: 0.68rem; }
.circuit-board.is-compact .circuit__detail { font-size: 0.7rem; }

/* Traces. pathLength="1" on each path makes dash lengths fractions of the path, whatever its size. */
.circuit-board__traces { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; overflow: visible; }
.trace {
	fill: none;
	stroke: rgba(var(--accent-rgb), 0.28);
	stroke-width: 1.5;
	stroke-linejoin: round;
	transition: stroke-dashoffset 1.1s ease, stroke 0.25s;
}
.trace.is-lit { stroke: rgba(var(--accent-rgb), 0.8); }
.trace-pulse {
	fill: none;
	stroke: var(--accent);
	stroke-width: 2.2;
	stroke-linecap: round;
	stroke-dasharray: 0.07 1;
	stroke-dashoffset: 1.07;
	filter: drop-shadow(0 0 3px rgba(var(--accent-rgb), 0.9));
	animation: current 3.4s linear infinite;  /* circuits.js sizes and times each pulse from its length */
}
.trace-pad { fill: var(--bg); stroke: rgba(var(--accent-rgb), 0.7); stroke-width: 1.5; }
@keyframes current { to { stroke-dashoffset: 0; } }  /* from the dashoffset set on each pulse */

/* As each card scrolls in, its traces draw out from the card, then the pads, current and chips appear. */
.reveal-ready .circuit-board .trace { stroke-dasharray: 1; stroke-dashoffset: 1; }
.reveal-ready .circuit-board .is-visible .trace { stroke-dashoffset: 0; }
.reveal-ready .circuit-board .trace-pulse,
.reveal-ready .circuit-board .trace-pad { opacity: 0; transition: opacity 0.4s ease 1.1s; }
.reveal-ready .circuit-board .is-visible .trace-pulse,
.reveal-ready .circuit-board .is-visible .trace-pad { opacity: 1; }
.reveal-ready .circuit-group .circuit-group__label,
.reveal-ready .circuit-group .circuit__chip {
	opacity: 0;
	transform: translateY(6px);
	transition: opacity 0.5s ease, transform 0.5s ease, border-color 0.25s, box-shadow 0.25s;
}
.reveal-ready .circuit-group.is-visible .circuit-group__label,
.reveal-ready .circuit-group.is-visible .circuit__chip { opacity: 1; transform: none; }
.reveal-ready .circuit-group.is-visible .circuit-group__label { transition-delay: 0.5s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(1) { transition-delay: 0.6s, 0.6s, 0s, 0s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(2) { transition-delay: 0.7s, 0.7s, 0s, 0s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(3) { transition-delay: 0.8s, 0.8s, 0s, 0s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(4) { transition-delay: 0.9s, 0.9s, 0s, 0s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(5) { transition-delay: 1s, 1s, 0s, 0s; }
.reveal-ready .circuit-group.is-visible .circuit__chip:nth-child(6) { transition-delay: 1.1s, 1.1s, 0s, 0s; }

@media (prefers-reduced-motion: reduce) {
	.trace-pulse { display: none; }
}

/* Terminal: a fast-forward button on the right of the title bar, there while the intro types.
   Negative block margins keep the bar the same height as before the script adds it. */
.terminal__skip {
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	margin: -0.2rem 0 -0.2rem auto;
	padding: 0.1rem 0.55rem;
	border: 1px solid var(--border-strong);
	border-radius: 999px;
	background: transparent;
	color: var(--text-muted);
	font: 400 0.7rem/1.4 var(--mono);
	cursor: pointer;
	transition: color 0.2s, border-color 0.2s, opacity 0.3s, visibility 0s;
}
.terminal__skip svg { width: 0.8rem; height: 0.8rem; fill: currentColor; }
.terminal__skip:hover { color: var(--accent); border-color: rgba(var(--accent-rgb), 0.5); }
.terminal__skip.is-gone { opacity: 0; visibility: hidden; transition: opacity 0.3s, visibility 0s 0.3s; }

/* ------------------------------------------------------------------ 2026-10 refresh */

/* Hero: the name centred over a slow cyan light; under it a mouse that says there's more below, and the built-with
   strip along the bottom of the first screen. */
.hero--spot { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto auto; min-height: calc(100svh - 4.25rem); padding: clamp(3rem, 8vh, 6rem) 0 1.75rem; text-align: center; }
.hero--spot::before { background-attachment: fixed; }  /* lines its dots up with the pointer's dot grid */
.hero__glow { position: absolute; border-radius: 50%; pointer-events: none; filter: blur(70px); background: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.28), transparent); }
.hero__glow--a { width: 760px; height: 520px; left: 50%; top: -160px; margin-left: -380px; animation: hero-drift-a 16s ease-in-out infinite alternate; }
.hero__glow--b { width: 520px; height: 420px; left: 8%; top: 45%; opacity: 0.55; animation: hero-drift-b 20s ease-in-out infinite alternate; }
@keyframes hero-drift-a { to { transform: translate(90px, 50px) scale(1.12); } }
@keyframes hero-drift-b { to { transform: translate(260px, -60px) scale(0.9); } }
.hero__center { position: relative; align-self: center; display: grid; justify-items: center; }
.hero--spot .hero__name {
	padding-bottom: 0.06em; font-size: clamp(3.4rem, 0.8rem + 9.4vw, 8.75rem); line-height: 0.95; letter-spacing: -0.055em; font-weight: 800;
	background: linear-gradient(180deg, #ffffff 30%, #9d9da8 100%); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero--spot .hero__role { margin-top: 1.1rem; font-size: clamp(1.1rem, 1rem + 0.5vw, 1.4rem); }
.hero--spot .hero__lede { max-width: 40rem; margin-inline: auto; }
.hero--spot .hero__actions { justify-content: center; }
.hero__scroll { position: relative; justify-self: center; width: 26px; height: 42px; margin-top: 2.5rem; border: 1.5px solid var(--border-strong); border-radius: 14px; transition: border-color 0.2s; }
.hero__scroll:hover { border-color: var(--accent); }
.hero__scroll span { position: absolute; left: 50%; top: 8px; width: 4px; height: 8px; margin-left: -2px; border-radius: 2px; background: var(--accent); animation: hero-wheel 1.8s ease-in-out infinite; }
@keyframes hero-wheel { 0% { opacity: 0; transform: translateY(0); } 30% { opacity: 1; } 100% { opacity: 0; transform: translateY(14px); } }

/* What it's built with, drifting left to right (effects.js gives the strip a copy of itself to loop on). */
.stack { position: relative; min-width: 0; padding-top: 2.25rem; }  /* min-width: the looping strip is far wider than the screen */
.marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }
.marquee__track { display: flex; width: max-content; animation: marquee 40s linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__set { display: flex; align-items: center; gap: 2.75rem; padding-right: 2.75rem; }
.marquee__set li { font: 500 0.95rem/1 var(--mono); white-space: nowrap; color: var(--text-faint); transition: color 0.2s; }
.marquee__set li:hover { color: var(--accent); }
@keyframes marquee { from { transform: translateX(-50%); } to { transform: translateX(0); } }

/* Experience: the line fills as you read down it, and each stop lights as the line reaches it. */
.timeline { gap: 2.75rem; padding-left: 2.4rem; border-left: 0; }
.timeline::before, .timeline::after { content: ""; position: absolute; left: 7px; top: 0.55rem; bottom: 0.55rem; width: 2px; border-radius: 2px; }
.timeline::before { background: var(--border); }
.timeline::after { background: var(--accent); box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.6); transform-origin: 50% 0; transform: scaleY(var(--fill, 0)); }
.timeline__item::before {
	left: calc(-2.4rem + 2px); top: 0.4rem; z-index: 1; width: 12px; height: 12px; border: 2px solid var(--border-strong); border-radius: 50%; background: var(--bg);
	box-shadow: none; transition: background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
}
.timeline__item.is-lit::before { border-color: var(--accent); background: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.15), 0 0 16px rgba(var(--accent-rgb), 0.6); }

/* Contact: a big headline in the open, a light rising from the bottom. */
.reach { position: relative; overflow: hidden; padding: clamp(6rem, 12vw, 10rem) 0 clamp(7rem, 13vw, 11rem); text-align: center; }
.reach::before { content: ""; position: absolute; left: 50%; bottom: -260px; width: 900px; height: 520px; margin-left: -450px; border-radius: 50%; background: radial-gradient(closest-side, rgba(var(--accent-rgb), 0.25), transparent); filter: blur(40px); pointer-events: none; }
.reach__dots {
	position: absolute; inset: 0; pointer-events: none; background-image: radial-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px); background-size: 22px 22px; background-attachment: fixed;
	-webkit-mask-image: radial-gradient(ellipse 60% 70% at 50% 60%, #000 20%, transparent 100%); mask-image: radial-gradient(ellipse 60% 70% at 50% 60%, #000 20%, transparent 100%);
}
.reach__inner { position: relative; display: grid; justify-items: center; gap: 1rem; }
.reach h2 {
	padding-bottom: 0.05em; font-size: clamp(2.8rem, 1.1rem + 5.4vw, 5.25rem); line-height: 1.02; letter-spacing: -0.05em; font-weight: 800;
	background: linear-gradient(180deg, #ffffff 30%, #9d9da8 100%); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.reach p { max-width: 32rem; font-size: 1.075rem; color: var(--text-muted); }
.reach .btn { margin-top: 1rem; }

@media (prefers-reduced-motion: reduce) {
	.hero__glow, .hero__scroll span, .marquee__track { animation: none; }
}

/* Security practices: each card lights up where the pointer is, and each icon draws itself in as the section
   appears, then acts out its practice on hover: the padlock snaps shut, the key turns, a tick draws itself in the
   shield, the person's tick redraws, the gauge needle swings up and is held back, and the block sign shakes. */
#practices .practice { position: relative; isolation: isolate; transition: border-color 0.3s, translate 0.35s cubic-bezier(0.22, 1, 0.36, 1); }
#practices .practice:is(:hover, .is-focus) { border-color: var(--border-strong); translate: 0 -3px; }
#practices .spot::before, #practices .spot::after, #experience .quote::before, #experience .quote::after { content: ""; position: absolute; border-radius: inherit; pointer-events: none; opacity: 0; transition: opacity 0.35s; }
#practices .spot::before, #experience .quote::before { inset: 0; z-index: -1; background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%), rgba(var(--accent-rgb), 0.08), transparent 45%); }
#practices .spot::after, #experience .quote::after {
	inset: -1px; z-index: 2; padding: 1px; background: radial-gradient(300px circle at var(--mx, 50%) var(--my, 50%), rgba(var(--accent-rgb), 0.8), transparent 45%);
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
	mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
#practices [data-spotlight]:hover .spot::before, #practices [data-spotlight]:hover .spot::after,
#practices .spot.is-focus::before, #practices .spot.is-focus::after,
#experience .quote:hover::before, #experience .quote:hover::after { opacity: 1; }
#practices .practice__icon { transition: box-shadow 0.3s, border-color 0.3s, background-color 0.3s; }
#practices .practice:is(:hover, .is-focus) .practice__icon { border-color: rgba(var(--accent-rgb), 0.6); background: rgba(var(--accent-rgb), 0.14); box-shadow: 0 0 24px rgba(var(--accent-rgb), 0.35); }
#practices .practice__icon svg * { transform-box: view-box; }

/* draw in: every stroke is one unit long (pathLength="1"), so a dash of 1 offset by 1 is hidden and offset 0 is drawn */
#practices .practice__icon :is(path, rect, circle) { stroke-dasharray: 1; }
#practices .i-tick { stroke-dashoffset: 1; }
.reveal-ready #practices .practice__icon :is(path, rect, circle) { stroke-dashoffset: 1; transition: stroke-dashoffset 1.1s cubic-bezier(0.65, 0, 0.35, 1) var(--draw-delay, 0.35s); }
.reveal-ready #practices .practice.is-visible .practice__icon :is(path, rect, circle):not(.i-tick) { stroke-dashoffset: 0; }
#practices .practice:nth-child(2) { --draw-delay: 0.47s; }
#practices .practice:nth-child(3) { --draw-delay: 0.59s; }
#practices .practice:nth-child(4) { --draw-delay: 0.71s; }
#practices .practice:nth-child(5) { --draw-delay: 0.83s; }
#practices .practice:nth-child(6) { --draw-delay: 0.95s; }

/* on hover */
#practices .practice:is(:hover, .is-focus) .i-shackle { animation: icon-shackle 0.7s cubic-bezier(0.3, 1.6, 0.5, 1); }
#practices .i-turn { transform-origin: 8px 15px; }
#practices .practice:is(:hover, .is-focus) .i-turn { animation: icon-turn 0.9s ease-in-out; }
#practices .practice:is(:hover, .is-focus) .i-tick { animation: icon-draw 0.5s ease-out 0.15s both; }
#practices .practice:is(:hover, .is-focus) .i-redraw { animation: icon-draw 0.55s ease-out both; }
#practices .i-needle { transform-origin: 12px 18px; }
#practices .practice:is(:hover, .is-focus) .i-needle { animation: icon-needle 1s cubic-bezier(0.3, 0.7, 0.4, 1); }
#practices .practice:is(:hover, .is-focus) .i-shake { animation: icon-shake 0.5s ease-in-out 0.1s; }
@keyframes icon-shackle { 35% { transform: translateY(-3.5px); } 60% { transform: translateY(0.6px); } }
@keyframes icon-turn { 40% { transform: rotate(-35deg); } 70% { transform: rotate(8deg); } }
@keyframes icon-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes icon-needle { 0% { transform: rotate(-95deg); } 50% { transform: rotate(28deg); } 68% { transform: rotate(-8deg); } 84% { transform: rotate(5deg); } }
@keyframes icon-shake { 20% { transform: translateX(-2px); } 40% { transform: translateX(2px); } 60% { transform: translateX(-1.5px); } 80% { transform: translateX(1px); } }

/* The terminal, once fitted to the cards beside it (terminal.js): a fixed height, with the output scrolling inside. */
.terminal.is-fitted { display: flex; flex-direction: column; }
.terminal.is-fitted .terminal__body { flex: 1 1 0; min-height: 0; max-height: none; }

/* The hero rises in as the page loads. Under the intro loader it waits (paused on its first frame, so hidden), then
   rises as the loader slides away. rise-in is in transitions.css. */
.hero__center > *, .hero--spot .hero__scroll, .hero--spot .stack { animation: rise-in 0.8s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.hero__center > :nth-child(2) { animation-delay: 0.08s; }
.hero__center > :nth-child(3) { animation-delay: 0.16s; }
.hero__center > :nth-child(4) { animation-delay: 0.24s; }
.hero--spot .hero__scroll { animation-delay: 0.36s; }
.hero--spot .stack { animation-delay: 0.44s; }
.intro-loading .hero__center > *, .intro-loading .hero--spot .hero__scroll, .intro-loading .hero--spot .stack { animation-play-state: paused; }

/* The testimonial. The pointer lights its border like the practice cards (above); the quote lights up word by word as it scrolls up the screen (effects.js); and opening the full
   letter fades its paragraphs in one by one, then draws a cyan underline under its key phrases. */
#experience .quote { position: relative; isolation: isolate; transition: border-color 0.3s; }
#experience [data-read-along].is-reading .word { color: var(--text-faint); transition: color 0.3s ease; }
#experience [data-read-along].is-reading .word.is-lit { color: var(--text); }
/* The full letter reads like a document: its five paragraphs numbered and labelled on a line like the timeline above.
   Opening it fades the parts in one by one, lights their dots, and sweeps a cyan highlighter over the key phrases. */
#experience .quote__full summary {
	padding: 0.45rem 0.95rem; border: 1px solid rgba(var(--accent-rgb), 0.35); border-radius: 999px; background: rgba(var(--accent-rgb), 0.06);
	transition: background-color 0.2s, border-color 0.2s, box-shadow 0.25s;
}
#experience .quote__full summary:hover { border-color: rgba(var(--accent-rgb), 0.7); background: rgba(var(--accent-rgb), 0.12); box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.15); }
#experience .quote__full summary:focus-visible { outline-offset: 3px; border-radius: 999px; }
.letter { margin-top: 1.75rem; padding-top: 1.75rem; border-top: 1px solid var(--border); }
/* Each part: its number and label in a margin on the left, the paragraph on the right, a dotted line between them. */
.letter__body { --margin: 10rem; --gap: 3rem; position: relative; display: grid; gap: 1.75rem; }
.letter__body::before { content: ""; position: absolute; left: calc(var(--margin) + var(--gap) / 2); top: 0.45rem; bottom: 0.45rem; width: 1px; background: var(--border-strong); }
.letter__part { position: relative; display: grid; grid-template-columns: var(--margin) minmax(0, 1fr); column-gap: var(--gap); }
.letter__part::before {
	content: ""; position: absolute; left: calc(var(--margin) + var(--gap) / 2 - 4px); top: 0.4rem; width: 9px; height: 9px; border: 1.5px solid var(--border-strong);
	border-radius: 50%; background: var(--bg-card); transition: background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
}
#experience .quote__content .letter__label {
	margin: 0; padding-top: 0.2rem; display: flex; flex-direction: column; align-items: flex-end; gap: 0.3rem; text-align: right;
	font: 500 0.72rem/1.3 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint);
}
.letter__label span { font-size: 1.05rem; letter-spacing: 0.02em; color: var(--accent); }
#experience .quote__content .letter__part p:not(.letter__label) { max-width: 78ch; margin: 0; font-size: 1rem; line-height: 1.75; color: var(--text-muted); }
@media (max-width: 760px) {
	.letter__body { padding-left: 1.6rem; }
	.letter__body::before { left: 4px; }
	.letter__part { display: block; }
	.letter__part::before { left: calc(-1.6rem + 0.5px); top: 0.3rem; }
	#experience .quote__content .letter__label { flex-direction: row; align-items: baseline; gap: 0.6rem; padding: 0; text-align: left; }
	.letter__label span { font-size: inherit; letter-spacing: inherit; }
	#experience .quote__content .letter__part p:not(.letter__label) { margin-top: 0.45rem; font-size: 0.97rem; line-height: 1.7; }
}
#experience .quote__key {
	color: var(--text); background-image: linear-gradient(rgba(var(--accent-rgb), 0.24), rgba(var(--accent-rgb), 0.24)); background-repeat: no-repeat;
	background-position: 0 88%; background-size: 0% 42%; transition: background-size 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

/* opening */
#experience .quote__full[open]:not(.is-closing) .letter__part { animation: quote-paragraph 0.5s ease both; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(1) { animation-delay: 0.1s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(2) { animation-delay: 0.2s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(3) { animation-delay: 0.3s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(4) { animation-delay: 0.4s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(5) { animation-delay: 0.5s; }
@keyframes quote-paragraph { from { opacity: 0; transform: translateY(8px); } }
#experience .quote__full[open]:not(.is-closing) .letter__part::before { border-color: var(--accent); background: var(--accent); box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15), 0 0 12px rgba(var(--accent-rgb), 0.6); }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(1)::before { transition-delay: 0.35s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(2)::before { transition-delay: 0.45s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(3)::before { transition-delay: 0.55s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(4)::before { transition-delay: 0.65s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(5)::before { transition-delay: 0.75s; }
#experience .quote__full[open]:not(.is-closing) .quote__key { background-size: 100% 42%; transition-delay: 0.6s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(2) .quote__key { transition-delay: 0.7s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(3) .quote__key { transition-delay: 0.8s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(4) .quote__key { transition-delay: 0.9s; }
#experience .quote__full[open]:not(.is-closing) .letter__part:nth-child(5) .quote__key { transition-delay: 1s; }

/* ------------------------------------------------------------------ phones
   Touch screens have no hover, so the desktop's pointer effects get touch versions (effects.js): a practice card
   reaching the middle of the screen gets .is-focus, which lights it and plays its icon. Below 720px the projects
   become a row you swipe through, the practices a compact two-column grid, and a dock of the sections floats at
   the bottom. */
#practices .practice.is-focus { --mx: 50%; --my: 0%; }
.projects__bus, .dock { display: none; }
@media (max-width: 720px) {
	body { padding-bottom: 5.5rem; }  /* room for the dock at the end of the page */

	/* projects: swipe sideways, one card at a time */
	.projects--four { display: flex; gap: 0.85rem; margin: 0 -1rem; padding: 0.25rem 1rem 0.5rem; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-x: contain; }
	.projects--four::-webkit-scrollbar { display: none; }
	.projects--four > .project { flex: 0 0 86%; scroll-snap-align: center; }
	.projects--four .project__link { transition: transform 0.25s, border-color 0.35s, box-shadow 0.35s; }
	.projects--four .project.is-focus .project__link { border-color: rgba(var(--accent-rgb), 0.5); box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.12), 0 18px 40px rgba(0, 0, 0, 0.45), 0 0 34px rgba(var(--accent-rgb), 0.1); }
	/* under the row, a small circuit: the pad for the card in front lights up, with current running to it */
	.projects__bus { display: block; width: min(100%, 22.5rem); height: auto; margin: -0.55rem auto 0; overflow: visible; }  /* the trace starts at the card's bottom edge */
	.bus__trace { fill: none; stroke: rgba(var(--accent-rgb), 0.26); stroke-width: 1.5; stroke-linejoin: round; transition: stroke 0.3s; }
	.bus__branch.is-on { stroke: rgba(var(--accent-rgb), 0.75); }
	.bus__via { fill: var(--bg); stroke: rgba(var(--accent-rgb), 0.45); stroke-width: 1.2; }
	.bus__pad { fill: var(--bg); stroke: rgba(var(--accent-rgb), 0.45); stroke-width: 1.5; transition: fill 0.3s, stroke 0.3s, filter 0.3s; }
	.bus__pad.is-on { fill: var(--accent); stroke: var(--accent); filter: drop-shadow(0 0 5px rgba(var(--accent-rgb), 0.9)); }
	.bus__pulse { fill: none; stroke: var(--accent); stroke-width: 2.2; stroke-linecap: round; stroke-dasharray: 0.12 1.2; stroke-dashoffset: 0.12; opacity: 0; filter: drop-shadow(0 0 3px rgba(var(--accent-rgb), 0.9)); }
	.bus__pulse.is-on { opacity: 1; animation: bus-current 1.6s linear infinite; }
	@keyframes bus-current { to { stroke-dashoffset: -1.08; } }  /* the dash starts just before the path and leaves just after it */

	/* practices: a compact two-column grid */
	#practices .practices { grid-template-columns: 1fr 1fr; gap: 0.65rem; }
	#practices .practice { padding: 1rem 0.95rem 1.05rem; }
	#practices .practice__icon { width: 2.4rem; height: 2.4rem; margin-bottom: 0.8rem; }
	#practices .practice h3 { font-size: 0.92rem; line-height: 1.3; }
	#practices .practice p { font-size: 0.8rem; line-height: 1.5; }

	/* the dock */
	.dock {
		position: fixed; left: 50%; bottom: calc(0.85rem + env(safe-area-inset-bottom, 0px)); z-index: 60; display: flex; gap: 0.15rem; padding: 0.35rem;
		border: 1px solid var(--border-strong); border-radius: 999px; background: rgba(17, 16, 19, 0.82); -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%);
		box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55); transform: translate(-50%, 160%); transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
	}
	.dock.is-shown { transform: translate(-50%, 0); }
	.dock a { position: relative; z-index: 1; display: grid; justify-items: center; gap: 0.15rem; min-width: 4.4rem; padding: 0.45rem 0.6rem 0.4rem; border-radius: 999px; color: var(--text-muted); font-size: 0.66rem; font-weight: 500; transition: color 0.25s; }
	.dock a.is-active { color: var(--accent); }
	.dock svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
	.dock__pill { position: absolute; top: 0.35rem; bottom: 0.35rem; left: 0; width: 0; border: 1px solid rgba(var(--accent-rgb), 0.35); border-radius: 999px; background: rgba(var(--accent-rgb), 0.12); opacity: 0; transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), width 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s; }
	.dock__pill.is-on { opacity: 1; }
}
