/*
* Jonathan Kim's links page: every style, in page order.
* Started from LittleLink (https://littlelink.io, MIT, see LICENSE.md);
* the parts this page still uses live in "Base" below.
*
* Sections:
*   1. Base             reset, font, sizes
*   2. Theme            colors, theme switch animation
*   3. Page layout      the still screen (header, list window, bottom bar)
*   4. Header           name, bio, theme button
*   5. Project list     the scrolling window and its card spacing
*   6. Project cards    image, bar, name, GitHub button
*   7. First card       "Projects" label and the walking kitten
*   8. Sky window       easter egg above the first card
*   9. End page         GitHub button and credit after the last card
*  10. Bottom bar       line, page dots, hold-to-scrub
*
* The tall layout (screens at least 31rem high) pins the screen and moves
* only the list. Shorter screens (phones turned sideways) scroll normally.
* Each section keeps its tall-layout rules in its own @media (min-height:31rem).
*/


/* 1. Base
–––––––––––––––––––––––––––––––––––––––––––––––––– */
*,
*::before,
*::after {
	box-sizing:border-box;
}
body, h1, h2, p {
	margin:0;
}
input,
button {
	font:inherit;
}
a {
	-webkit-touch-callout:none;   /* no link preview popup on a long press (iPhone) */
}
:focus-visible {
	outline:2px solid var(--accent);
	outline-offset:2px;
}
/* Reduce Motion: no CSS animations or transitions anywhere
   (js/site.js turns its own glides off too) */
@media (prefers-reduced-motion:reduce) {
	*,
	*::before,
	*::after {
		animation-duration:0.01ms !important;
		animation-iteration-count:1 !important;
		transition-duration:0.01ms !important;
	}
}

/* JetBrains Mono (SIL Open Font License, see fonts/OFL-JetBrainsMono.txt).
   One variable font file covers every weight from 400 to 800. Latin characters only. */
@font-face {
	font-display:swap;
	font-family:'JetBrains Mono';
	font-style:normal;
	font-weight:400 800;
	src:url('../fonts/jetbrains-mono-latin.woff2') format('woff2');
	unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
	font-size:16px;           /* 1rem = 16px: --card-h below mixes px and rem */
	--spacing-xs:0.5rem;      /* 8px */
	--spacing-s:1rem;         /* 16px */
	--spacing-m:1.5rem;       /* 24px */
	--spacing-l:2rem;         /* 32px */
	--spacing-xl:3rem;        /* 48px */
	--spacing-xxl:4rem;       /* 64px */
	--radius:1rem;            /* card corners */
}

body {
	min-height:100vh;
	font-family:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
	line-height:1.6;
	text-rendering:optimizeSpeed;
	-webkit-font-smoothing:antialiased;
	-moz-osx-font-smoothing:grayscale;
}


/* 2. Theme
   Light is the default. The theme button sets data-theme="dark" on <html>.
   The page color must match BAR_COLORS in js/site.js and the theme-color
   values in index.html.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
:root {
	color-scheme:light;
	--accent:#31A7EB;         /* sky blue: focus rings */
	--page-bg:#FAF9F6;        /* warm off-white */
	--card-bg:#FAF9F6;
	--card-border:#DCDCDC;
	--card-border-hover:#C4C4C4;
	--surface-hover:#F4F4F4;  /* button hover */
	--surface-active:#ECECEC; /* button press */
	--image-bg:#F2F2F2;       /* shows while a screenshot loads */
	--text:#111418;
	--text-muted:#5B6370;
	--shadow:0 1px 3px rgba(16,24,40,0.06);
	--shadow-hover:0 8px 24px rgba(16,24,40,0.10);
	--kitten-outline:none;
}
:root[data-theme="dark"] {
	color-scheme:dark;
	--page-bg:#1A1A1A;        /* soft black */
	--card-bg:#222222;        /* a step lighter than the page */
	--card-border:#2A2F37;
	--card-border-hover:#3D444F;
	--surface-hover:#20242A;
	--surface-active:#272C33;
	--image-bg:#1D2127;
	--text:#ECEEF1;
	--text-muted:#9AA3AE;
	--shadow:0 1px 3px rgba(0,0,0,0.5);
	--shadow-hover:0 10px 28px rgba(0,0,0,0.55);
	/* thin light outline so the black cat stays visible on the dark page */
	--kitten-outline:drop-shadow(1px 0 0 #8C949F) drop-shadow(-1px 0 0 #8C949F) drop-shadow(0 -1px 0 #8C949F);
}

html,
body {
	background-color:var(--page-bg);   /* on html too: Safari colors its top bar from it */
	color:var(--text);
}

/* No double-tap zoom anywhere, so tapping a button quickly (like the theme
   button) never zooms in. Pinch zoom still works. */
html {
	touch-action:manipulation;
}

/* Theme switch animation
   Modern browsers: the new theme spreads out in a circle from the theme button
   (View Transitions, started in js/site.js).
   iPhones, iPads and older browsers: colors fade over 0.4s (the .theme-fading
   class). Safari on iPhone dims the screen during View Transitions. */
::view-transition-old(root),
::view-transition-new(root) {
	animation:none;
	mix-blend-mode:normal;
}
/* The fade animates the color variables themselves, once, on <html>. Every
   element reads them, so all text, backgrounds and borders change in step.
   (Fading each element's own color made inherited text, like the name, lag:
   it kept restarting its fade as its parent's color moved.)
   @property makes the variables animatable as colors. Each initial-value is
   the light color above: keep them in sync. */
@property --page-bg          { syntax:"<color>"; inherits:true; initial-value:#FAF9F6; }
@property --card-bg          { syntax:"<color>"; inherits:true; initial-value:#FAF9F6; }
@property --card-border      { syntax:"<color>"; inherits:true; initial-value:#DCDCDC; }
@property --card-border-hover{ syntax:"<color>"; inherits:true; initial-value:#C4C4C4; }
@property --surface-hover    { syntax:"<color>"; inherits:true; initial-value:#F4F4F4; }
@property --surface-active   { syntax:"<color>"; inherits:true; initial-value:#ECECEC; }
@property --image-bg         { syntax:"<color>"; inherits:true; initial-value:#F2F2F2; }
@property --text             { syntax:"<color>"; inherits:true; initial-value:#111418; }
@property --text-muted       { syntax:"<color>"; inherits:true; initial-value:#5B6370; }
:root.theme-fading {
	transition:--page-bg 0.4s ease, --card-bg 0.4s ease, --card-border 0.4s ease,
		--card-border-hover 0.4s ease, --surface-hover 0.4s ease, --surface-active 0.4s ease,
		--image-bg 0.4s ease, --text 0.4s ease, --text-muted 0.4s ease;
}
/* Shadows aren't inherited, so each card can fade its own. Other per-element
   color transitions are switched off during the fade so nothing trails behind. */
.theme-fading .project-card {
	transition:box-shadow 0.4s ease;
}
.theme-fading .theme-toggle {
	transition:none;
}


/* 3. Page layout
   Narrower column: cards read better at phone-like widths, even on desktop.
   Tall layout: the screen stays still. The header sits at the top, the bottom
   bar at the bottom, and only the project list in between moves.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.container {
	width:100%;
	max-width:30rem;          /* 480px */
	margin:0 auto;
	/* clear of the iPhone notch and home bar */
	padding-top:max(var(--spacing-m), env(safe-area-inset-top));
	padding-left:max(var(--spacing-m), env(safe-area-inset-left));
	padding-right:max(var(--spacing-m), env(safe-area-inset-right));
	padding-bottom:env(safe-area-inset-bottom);
}

@media (min-height:31rem) {
	html, body {
		height:100%;
		overflow:hidden;
		overscroll-behavior:none;
	}
	.container {
		/* Nothing may stick out below the screen. iPhones (and Instagram's
		   browser) can still scroll a page with overflow:hidden when something
		   does, like the list while it is pulled down, which pushed the header
		   up. (Not position:fixed on body: that turned Safari's top bar white.) */
		overflow-y:clip;
		touch-action:pinch-zoom;  /* a drag on the header does nothing (pinch zoom still works) */
		display:flex;
		flex-direction:column;
		height:100vh;
		height:100dvh;            /* the visible screen, so nothing hides behind phone toolbars */
	}
	main {
		display:flex;
		flex-direction:column;
		flex:1;
		min-height:0;
	}
}


/* 4. Header: name and bio on the left, theme button on the right
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.profile {
	display:flex;
	align-items:center;
	gap:var(--spacing-s);
	padding-bottom:var(--spacing-m);
	margin-bottom:var(--spacing-m);
	border-bottom:1px solid var(--card-border);
}
@media (min-height:31rem) {
	.profile {
		flex-shrink:0;
		padding-bottom:var(--spacing-s);
		margin-bottom:var(--spacing-s);
	}
}
.profile-text {
	flex:1;
	min-width:0;
}
.profile h1 {
	font-size:1.375rem;       /* 22px */
	font-weight:800;
	line-height:1.2;
	letter-spacing:-0.03em;   /* tighter: monospace letters sit far apart at large sizes */
	overflow-wrap:break-word;
}
.profile p {
	margin-top:0.125rem;
	font-size:0.9375rem;      /* 15px */
	color:var(--text-muted);
	line-height:1.4;
}

/* Theme button: a moon in light mode, a sun in dark mode.
   The moon is a circle with a bite taken out of it (the mask circle).
   Switching to dark slides the bite away, shrinks the circle, and grows the rays. */
.theme-toggle {
	display:inline-flex;
	align-items:center;
	justify-content:center;
	align-self:flex-start;
	flex-shrink:0;
	width:2.5rem;             /* 40px */
	height:2.5rem;
	margin:0;
	padding:0;
	line-height:1;
	color:var(--text);
	background:transparent;
	border:1px solid var(--card-border);
	border-radius:0.5rem;
	cursor:pointer;
	-webkit-tap-highlight-color:transparent;
	transition:background-color 0.15s ease;
}
@media (hover:hover) {
	.theme-toggle:hover { background:var(--surface-hover); }
}
.theme-toggle:active { background:var(--surface-active); }
.theme-toggle svg {
	width:1.25rem;
	height:1.25rem;
	overflow:visible;
}
.theme-toggle .sun-core,
.theme-toggle .moon-bite,
.theme-toggle .sun-rays {
	transform-box:fill-box;
	transform-origin:center;
	transition:transform 0.5s cubic-bezier(0.5, 1.4, 0.4, 1), opacity 0.35s ease;
}
/* Light theme: show the moon */
.theme-toggle .sun-core  { transform:scale(1.5); }
.theme-toggle .moon-bite { transform:translate(0, 0); }
.theme-toggle .sun-rays  { transform:rotate(-45deg) scale(0.4); opacity:0; }
/* Dark theme: show the sun */
:root[data-theme="dark"] .theme-toggle .sun-core  { transform:scale(1); }
:root[data-theme="dark"] .theme-toggle .moon-bite { transform:translate(8px, -8px); }
:root[data-theme="dark"] .theme-toggle .sun-rays  { transform:rotate(0) scale(1); opacity:1; }


/* 5. Project list
   Each card can sit in the exact middle of the window, with a sliver of the
   previous and next cards peeking in at the edges. js/site.js moves the list
   one card per swipe and rolls the off-center cards back like a drum
   (rollCards sets each li's transform and opacity).
–––––––––––––––––––––––––––––––––––––––––––––––––– */
:root {
	--peek:3.25rem;           /* how much of the neighbor cards shows (less = more space between cards) */
	--edge-fade:2rem;         /* cards fade out over this distance at the top and bottom of the window */
}
.project-list {
	position:relative;        /* js/site.js measures cards from the list's top */
	display:flex;
	flex-direction:column;
	gap:var(--spacing-m);
	margin:0;
	padding:3.5rem 0 0;       /* top: room for the label and kitten (short screens) */
	list-style:none;
}
.project-list li {
	position:relative;
	margin:0;
}
/* Hidden unless the tall layout turns them on */
.haptic-switch { display:none; }

@media (min-height:31rem) {
	/* Holds the sky window, the list and the iPhone haptic switch */
	.project-window {
		position:relative;
		display:flex;
		flex-direction:column;
		flex:1;
		min-height:0;
	}
	.project-window .project-list { z-index:1; }   /* slides over the sky window */

	/* The scrolling window. container-type lets the cards size their spacing
	   from the window's height (cqh) and width (cqw). */
	.project-list {
		flex:1;
		min-height:0;
		container-type:size;
		overflow-y:auto;
		overscroll-behavior:contain;
		touch-action:none;        /* js/site.js moves the list one card per swipe */
		scrollbar-width:none;
		/* extend sideways so card shadows aren't cut off */
		margin:0 -1rem;
		padding:0 1rem;
		/* Edge fade with an eased curve (many stops), so it has no visible band */
		-webkit-mask-image:var(--edge-mask);
		        mask-image:var(--edge-mask);
		--edge-mask:linear-gradient(
			rgba(0,0,0,0.000) 0px,
			rgba(0,0,0,0.005) calc(var(--edge-fade) * 0.083),
			rgba(0,0,0,0.035) calc(var(--edge-fade) * 0.167),
			rgba(0,0,0,0.104) calc(var(--edge-fade) * 0.250),
			rgba(0,0,0,0.210) calc(var(--edge-fade) * 0.333),
			rgba(0,0,0,0.347) calc(var(--edge-fade) * 0.417),
			rgba(0,0,0,0.500) calc(var(--edge-fade) * 0.500),
			rgba(0,0,0,0.653) calc(var(--edge-fade) * 0.583),
			rgba(0,0,0,0.790) calc(var(--edge-fade) * 0.667),
			rgba(0,0,0,0.896) calc(var(--edge-fade) * 0.750),
			rgba(0,0,0,0.965) calc(var(--edge-fade) * 0.833),
			rgba(0,0,0,0.995) calc(var(--edge-fade) * 0.917),
			rgba(0,0,0,1.000) var(--edge-fade),
			rgba(0,0,0,1.000) calc(100% - var(--edge-fade)),
			rgba(0,0,0,0.995) calc(100% - var(--edge-fade) * 0.917),
			rgba(0,0,0,0.965) calc(100% - var(--edge-fade) * 0.833),
			rgba(0,0,0,0.896) calc(100% - var(--edge-fade) * 0.750),
			rgba(0,0,0,0.790) calc(100% - var(--edge-fade) * 0.667),
			rgba(0,0,0,0.653) calc(100% - var(--edge-fade) * 0.583),
			rgba(0,0,0,0.500) calc(100% - var(--edge-fade) * 0.500),
			rgba(0,0,0,0.347) calc(100% - var(--edge-fade) * 0.417),
			rgba(0,0,0,0.210) calc(100% - var(--edge-fade) * 0.333),
			rgba(0,0,0,0.104) calc(100% - var(--edge-fade) * 0.250),
			rgba(0,0,0,0.035) calc(100% - var(--edge-fade) * 0.167),
			rgba(0,0,0,0.005) calc(100% - var(--edge-fade) * 0.083),
			rgba(0,0,0,0.000) 100%);
		gap:0;
	}
	.project-list::-webkit-scrollbar { display:none; }
	/* Spacing is set on the cards, because cqw and cqh (the window's size)
	   only work inside the window. */
	.project-list li {
		/* card height: image (16:11 of the card width inside its 1px borders) + 52px of bar and borders */
		--card-h:calc((100cqw - 2px) * 11 / 16 + 52px);
		--center-space:calc((100cqh - var(--card-h)) / 2);
	}
	/* Space before the first card and after the last, so both can reach the middle */
	.project-list li:first-child { margin-top:max(1.5rem, var(--center-space)); }
	.project-list li:last-child  { margin-bottom:max(0px, var(--center-space)); }
	/* Gap between cards: just enough that a sliver of each neighbor peeks in */
	.project-list li + li { margin-top:max(var(--spacing-xs), calc(var(--center-space) - var(--peek))); }

	/* Invisible switches for iPhone haptics (js/site.js adds them): one over
	   the project window for swipes, one over the bottom bar for the dots */
	.project-window .haptic-switch,
	.bottom-space .haptic-switch {
		display:block;
		position:absolute;
		inset:0;
		width:100%;
		height:100%;
		margin:0;
		padding:0;
		border:0;
		opacity:0;
		-webkit-appearance:auto;
		        appearance:auto;
		touch-action:none;
		-webkit-tap-highlight-color:transparent;
	}
	.project-window .haptic-switch { z-index:5; cursor:inherit; }
	.bottom-space .haptic-switch   { z-index:1; }
}


/* 6. Project cards
   Big image on top, then a bar with the name (left) and GitHub link (right).
   The name link stretches over the whole card, so tapping anywhere opens
   the project. The GitHub link sits above that layer and opens the repo.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.project-card {
	position:relative;
	overflow:hidden;
	background:var(--card-bg);
	border:1px solid var(--card-border);
	border-radius:var(--radius);
	box-shadow:var(--shadow);
	transition:transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
@media (hover:hover) {
	.project-card:hover {
		border-color:var(--card-border-hover);
		box-shadow:var(--shadow-hover);
		transform:translateY(-2px);
	}
}
.project-card:active {
	transform:scale(0.985);
}
/* Focus ring only for keyboard users. (:focus-within also lit up after a tap on phones.) */
.project-card:has(.project-name:focus-visible) {
	outline:2px solid var(--accent);
	outline-offset:2px;
}

.project-image {
	display:block;
	width:100%;
	height:auto;              /* ignore the height attribute; aspect-ratio sets the height */
	aspect-ratio:16 / 11;     /* taller than the 16:10 screenshot, filling the space the short bar gave up */
	object-fit:cover;
	object-position:top center;
	background:var(--image-bg);
	border-bottom:1px solid var(--card-border);
}

.project-bar {
	display:flex;
	align-items:center;
	justify-content:space-between;
	gap:var(--spacing-s);
	min-height:3.0625rem;     /* 49px, same height with or without a GitHub button */
	padding:0.34375rem 1rem;  /* 5.5px top and bottom */
}

.project-name {
	/* Name on the first line, description under it. One line each (cut off
	   with "..." if too long), so the bar keeps its 49px height (--card-h
	   counts on it). */
	display:block;
	min-width:0;
	white-space:nowrap;
	overflow:hidden;
	text-overflow:ellipsis;
	font-size:0.95625rem;     /* 15.3px */
	font-weight:700;
	line-height:1.2;
	color:var(--text);
	text-decoration:none;
	-webkit-tap-highlight-color:transparent;
}
.project-name:focus-visible {
	outline:none;             /* the card shows the focus ring instead */
}
/* Stretch the name link over the whole card */
.project-name::after {
	content:"";
	position:absolute;
	inset:0;
	z-index:1;
}
.project-desc {
	display:block;
	overflow:hidden;
	text-overflow:ellipsis;
	font-size:0.75rem;        /* 12px: fits under the name next to the GitHub button */
	font-weight:400;
	line-height:1.25;
	color:var(--text-muted);
}

.project-repo {
	position:relative;
	z-index:2;                /* above the stretched link, so it gets its own taps */
	display:inline-flex;
	align-items:center;
	gap:0.375rem;
	flex-shrink:0;
	min-height:2.375rem;      /* 38px tap target */
	padding:0 0.75rem;
	font-size:0.875rem;       /* 14px */
	font-weight:700;
	color:var(--text);
	text-decoration:none;
	border:1px solid var(--card-border);
	border-radius:0.5rem;     /* box with slightly rounded corners */
	-webkit-tap-highlight-color:transparent;
}
/* Bigger invisible tap area than the button itself, so near misses still
   count: out to the card's right edge, and 12px above and below */
.project-repo::before {
	content:"";
	position:absolute;
	top:-12px;
	bottom:-12px;
	left:-14px;
	right:-1rem;
}
@media (hover:hover) {
	.project-repo:hover {
		background:var(--surface-hover);
	}
}
.project-repo:active {
	background:var(--surface-active);
}
.project-repo svg {
	width:1.125rem;
	height:1.125rem;
}
/* Very narrow phones: icon only, so the description has room */
@media (max-width:22.5rem) {
	.project-repo .repo-label { display:none; }
	.project-repo { padding:0 0.625rem; }
}


/* 7. First card: "Projects" label and the walking kitten
   Both sit just above the first card and scroll away with it, so they don't
   take room from the header. Move both to whichever project is first.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.section-label {
	position:absolute;
	left:0;
	bottom:calc(100% + 23px + 0.75rem);   /* above the kitten (23px tall) */
	margin:0;
	font-size:0.8125rem;      /* 13px */
	font-weight:700;
	letter-spacing:0.08em;
	text-transform:uppercase;
	color:var(--text-muted);
}

/* Pixel kitten that walks along the top edge of the first card
   (images/kitten.svg, made by tools/make_kitten.py) */
.kitten {
	--kitten-w:30.6px;        /* 16 x 12 pixel art at about 1.9x */
	position:absolute;
	bottom:100%;              /* paws stand exactly on top of the card's border */
	left:1rem;
	width:var(--kitten-w);
	height:calc(var(--kitten-w) * 0.75);
	image-rendering:pixelated;
	pointer-events:none;
	filter:var(--kitten-outline);
	animation:kitten-walk 18s linear infinite;
}
/* Walk right, pause, turn around, walk left, pause, turn again */
@keyframes kitten-walk {
	0%     { left:1rem;                                 transform:scaleX(1); }
	45%    { left:calc(100% - 1rem - var(--kitten-w));  transform:scaleX(1); }
	49.9%  { left:calc(100% - 1rem - var(--kitten-w));  transform:scaleX(1); }
	50%    { left:calc(100% - 1rem - var(--kitten-w));  transform:scaleX(-1); }
	95%    { left:1rem;                                 transform:scaleX(-1); }
	99.9%  { left:1rem;                                 transform:scaleX(-1); }
	100%   { left:1rem;                                 transform:scaleX(1); }
}


/* 8. Sky window (easter egg)
   A small arched window with two louvered shutters, above the first card.
   It shows only while you hold a pull down at the top. The shutters stay
   shut on a light pull and swing open (.is-open) on a hard pull. js/site.js
   places it, and sets the sky colors, sun, moon, stars and clouds as
   variables, and --reveal (0 to 1) as you pull.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.sky-window {
	display:none;             /* only in the tall layout (below) */
	--frame:#8C3A3A;          /* frame and shutters: painted red-brown wood */
	--louver:#6B2727;
	--line:#2E1414;           /* outlines */
	--reveal:0;
	--sky-scale:0.85;         /* overall size of the window (1 = the sizes below) */
	position:absolute;
	top:0.5rem;
	left:50%;
	z-index:0;
	width:4rem;               /* 64px: frame width; open shutters stick out the sides */
	pointer-events:none;
	opacity:var(--reveal);
	transform:translateX(-50%) scale(calc(var(--sky-scale) * (0.8 + 0.2 * var(--reveal))));
	transform-origin:center top;
	perspective:16rem;        /* gives the shutters a 3D swing */
	visibility:hidden;        /* js/site.js shows it while pulled */
	will-change:transform, opacity;   /* one layer, so the sill can't drift from the frame on iPhones */
}
@media (min-height:31rem) {
	.sky-window { display:block; }
}

/* Arched frame with the sky inside */
.sky-frame {
	position:relative;
	z-index:0;                /* keeps clouds and railing under the shutters */
	height:5.75rem;           /* 92px */
	overflow:hidden;
	background:linear-gradient(var(--sky-top, #4FA3E3), var(--sky-bottom, #A9D8F5));
	border:0.25rem solid var(--frame);
	border-radius:2rem 2rem 0.125rem 0.125rem;   /* round arch on top */
	box-shadow:0 0 0 1px var(--line), inset 0 0 0 1px rgba(0,0,0,0.25);
}
.sky-sill {
	height:0.3125rem;         /* 5px stone ledge */
	margin:-1px -0.375rem 0;
	background:linear-gradient(#B4B8BD, #8A8F96);
	border:1px solid #4A4E55;
	border-radius:2px;
}

/* Little iron railing across the bottom of the opening */
.sky-rail {
	position:absolute;
	left:0;
	right:0;
	bottom:0;
	z-index:3;
	height:0.75rem;           /* 12px */
	border-top:2px solid #2B3550;
	border-bottom:2px solid #2B3550;
	background:radial-gradient(circle, transparent 1.6px, #2B3550 1.6px 2.6px, transparent 2.8px) 0 50% / 7px 7px repeat-x;
}

/* Clouds: soft white puffs, dimmer at night. ::before adds a bump on each puff. */
.sky-clouds {
	--puffs:14px 14px 0 #fff, 32px 26px 0 -1px #fff, 6px 40px 0 #fff, 30px 54px 0 #fff, 12px 66px 0 -1px #fff;
	position:absolute;
	left:0;
	top:0;
	z-index:2;
	width:12px;
	height:5px;
	border-radius:5px;
	opacity:var(--clouds-on, 1);
	background:#fff;
	box-shadow:var(--puffs);
	transform:translate(4px, 6px);
}
.sky-clouds::before {
	content:"";
	position:absolute;
	left:3px;
	top:-3px;
	width:6px;
	height:6px;
	border-radius:50%;
	background:#fff;
	box-shadow:var(--puffs);
}

.sky-sun,
.sky-moon {
	position:absolute;
	z-index:1;
	border-radius:50%;
}
.sky-sun {
	left:var(--sun-x, 50%);
	top:var(--sun-y, 50%);
	width:0.75rem;            /* 12px */
	height:0.75rem;
	margin:-0.375rem 0 0 -0.375rem;   /* centered on its position */
	opacity:var(--sun-on, 1);
	background:#FFD45C;
	box-shadow:0 0 8px 2px rgba(255,200,80,0.65);
}
.sky-moon {
	left:var(--moon-x, 50%);
	top:var(--moon-y, 50%);
	width:0.625rem;           /* 10px */
	height:0.625rem;
	margin:-0.3125rem 0 0 -0.3125rem;
	opacity:var(--moon-on, 0);
	background:#F1EEDC;
	box-shadow:inset -3px -2px 0 rgba(0,0,0,0.12), 0 0 6px rgba(241,238,220,0.45);
}
.sky-stars {
	position:absolute;
	left:0;
	top:0;
	width:1px;
	height:1px;
	opacity:var(--stars-on, 0);
	background:#fff;
	box-shadow:
		7px 10px #fff, 18px 6px rgba(255,255,255,0.7), 31px 4px #fff, 44px 12px rgba(255,255,255,0.8),
		10px 26px #fff, 28px 22px rgba(255,255,255,0.6), 41px 34px rgba(255,255,255,0.8), 6px 44px #fff,
		22px 50px rgba(255,255,255,0.6), 36px 58px rgba(255,255,255,0.7), 12px 64px #fff, 48px 46px rgba(255,255,255,0.5);
}

/* Shutters: each covers half the opening, hinged on its outer edge.
   Louvers are thin dark slats; a rail splits each into two panels. */
.sky-shutter {
	position:absolute;
	top:0;
	z-index:1;
	width:50%;
	height:5.75rem;           /* same as the frame */
	background:
		linear-gradient(var(--frame), var(--frame)) 0 42% / 100% 3px no-repeat,
		repeating-linear-gradient(var(--louver) 0 1.5px, var(--frame) 1.5px 4px);
	border:3px solid var(--frame);
	box-shadow:0 0 0 1px var(--line);
	transition:transform 0.75s cubic-bezier(0.2, 0.9, 0.25, 1.08);
}
.sky-shutter-left {
	left:0;
	border-radius:2rem 0 0 0.125rem;
	transform-origin:left center;
}
.sky-shutter-right {
	right:0;
	border-radius:0 2rem 0.125rem 0;
	transform-origin:right center;
}
/* Small handle on the left shutter */
.sky-shutter-left::after {
	content:"";
	position:absolute;
	right:2px;
	top:60%;
	width:3px;
	height:5px;
	border-radius:1px;
	background:#C9B27A;
}
/* Open: swing out to the sides, like real shutters */
.sky-window.is-open .sky-shutter-left  { transform:rotateY(-160deg); }
.sky-window.is-open .sky-shutter-right { transform:rotateY(160deg); }


/* 9. End page: GitHub button and credit
   The last li of the list. In the tall layout it is a card-sized slot,
   spaced like a card, so the last project still peeks in above it.
–––––––––––––––––––––––––––––––––––––––––––––––––– */
@media (min-height:31rem) {
	.project-list li.list-end {
		display:flex;
		flex-direction:column;
		justify-content:center;
		flex-shrink:0;            /* keep the full slot height inside the flex list */
		height:var(--card-h);
	}
	.list-end .social { margin-top:0; }
	.list-end footer  { margin:var(--spacing-xs) 0 0; }
}
.social {
	margin-top:var(--spacing-l);
}
/* Styled like a card: page colors, thin border, soft shadow */
.button-github {
	display:inline-flex;
	align-items:center;
	justify-content:center;
	width:100%;
	min-height:3.25rem;       /* 52px */
	padding:0.75rem 1rem;
	font-size:1.125rem;       /* 18px */
	font-weight:700;
	line-height:1.3;
	color:var(--text);
	text-decoration:none;
	background-color:var(--card-bg);
	border:1px solid var(--card-border);
	border-radius:var(--radius);
	box-shadow:var(--shadow);
	cursor:pointer;
	-webkit-tap-highlight-color:transparent;
	transition:transform 0.2s ease;
}
@media (hover:hover) {
	.button-github:hover {
		background-color:var(--surface-hover);
		border-color:var(--card-border-hover);
		box-shadow:var(--shadow-hover);
		transform:translateY(-1px);
	}
}
.button-github:active {
	background-color:var(--surface-active);
	transform:scale(0.98);
}
.button-github .icon {
	flex-shrink:0;
	width:1.25rem;
	height:1.25rem;
	margin-right:0.5rem;
}

footer {
	margin:var(--spacing-xxl) 0;
	text-align:center;
	color:var(--text-muted);
	font-size:0.8125rem;
}
@media (max-width:34.375rem) {
	footer { margin:var(--spacing-xl) 0; }
}
footer a {
	color:var(--text-muted);
}


/* 10. Bottom bar: a thin line like the one under the header, then the page dots
   One dot per card plus the end page (js/site.js builds them). The current
   one stretches into a pill in the text color (dark on light, light on dark).
   Tap a dot to glide there. Hold the dots (.is-scrubbing) and they grow on a
   soft capsule that floats above the thumb; slide to scrub through the cards,
   and past the ends they rubber-stretch (.is-stretching).
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.bottom-space { display:none; }   /* only in the tall layout */
@media (min-height:31rem) {
	.bottom-space {
		display:flex;
		align-items:center;
		justify-content:center;
		flex-shrink:0;
		height:4.5rem;            /* smaller = the cards sit lower on the screen */
		border-top:1px solid var(--card-border);
		position:relative;        /* holds the iPhone haptic switch */
		z-index:2;                /* the lifted dots float over the cards */
		touch-action:none;        /* holding and sliding on the dots never scrolls the page */
		-webkit-user-select:none;
		        user-select:none;
		-webkit-touch-callout:none;
	}
}

.page-dots {
	display:flex;
	align-items:center;
	padding:0 0.25rem;
	border-radius:2rem;
	transition:background-color 0.2s ease, padding 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
/* Each button is taller and wider than its dot, so it is easy to tap */
.page-dot {
	display:flex;
	align-items:center;
	justify-content:center;
	height:2.75rem;           /* 44px tap area */
	padding:0 0.25rem;
	margin:0;
	border:0;
	background:none;
	cursor:pointer;
	transition:padding 0.2s ease;
	-webkit-tap-highlight-color:transparent;
}
.page-dot::before {
	content:"";
	display:block;
	width:0.4375rem;          /* 7px dot */
	height:0.4375rem;
	border-radius:1rem;
	background:var(--card-border-hover);
	transition:width 0.2s ease, height 0.2s ease, background-color 0.2s ease;
}
.page-dot.is-current::before {
	width:1.25rem;            /* 20px pill */
	background:var(--text);
}
.page-dot:focus-visible {
	outline:none;
}
.page-dot:focus-visible::before {
	outline:2px solid var(--accent);
	outline-offset:2px;
}

/* Held: the dots float up above the thumb so you can see them. Only the
   picture moves: the touch area stays on the bar. (js/site.js picks the dot
   from the finger's left-right position; moving up or down only stretches
   the dots.) The 0.9 scale must match DOT_HELD_SCALE in js/site.js. */
.is-scrubbing .page-dots {
	padding:0 0.75rem;
	background-color:var(--surface-active);
	/* 39px up, 10% smaller, plus the rubber-band stretch from js/site.js */
	transform:
		translate(var(--stretch-dx, 0px), calc(-2.43rem + var(--stretch-dy, 0px)))
		scale(calc(0.9 * var(--stretch-x, 1)), calc(0.9 * var(--stretch-y, 1)));
	box-shadow:var(--shadow-hover);
}
/* While the finger drags, the stretch follows it almost directly */
.is-scrubbing.is-stretching .page-dots {
	transition:background-color 0.2s ease, padding 0.2s ease, transform 0.05s linear, box-shadow 0.2s ease;
}
.is-scrubbing .page-dot {
	padding:0 0.5rem;
}
.is-scrubbing .page-dot::before {
	width:0.625rem;           /* 10px */
	height:0.625rem;
}
.is-scrubbing .page-dot.is-current::before {
	width:1.75rem;            /* 28px */
}
