/* findastra: everything in blacks and dark grays; the projects are the only color: neon tape on the Polaroids, and stars.
   Each section sets its own gray palette (.dark .light .mid .dots) and its own pattern. White is only a small accent. */
:root {
  --mono: "JetBrains Mono", ui-monospace, monospace;
  --wrap: max(20px, calc((100% - 1140px) / 2));
  --bg: #0b0b0b; --ink: #f2f2f2; --muted: #9b9b9b; --panel: #151515; --panel2: #222; --line: #3d3d3d;
}
.dark  { --bg: #0b0b0b; --ink: #f2f2f2; --muted: #9b9b9b; --panel: #151515; --panel2: #222;    --line: #3d3d3d; }
.light { --bg: #1b1b1b; --ink: #e4e4e4; --muted: #8e8e8e; --panel: #262626; --panel2: #303030; --line: #444; }
.mid   { --bg: #2a2a2a; --ink: #e4e4e4; --muted: #9a9a9a; --panel: #181818; --panel2: #333;    --line: #000; }
.dots  { --bg: #141414; --ink: #e4e4e4; --muted: #8a8a8a; --panel: #222;    --panel2: #2e2e2e; --line: #555; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 64px; }
/* three fonts across the page (Astra, 2026-10-29: "font styling, like consistency"): JetBrains Mono for nearly all of it, Permanent
   Marker for the Polaroids' handwriting, Inter only for the big titles */
body { margin: 0; color: var(--ink); font: 15px/1.65 var(--mono); overflow-x: hidden; }
/* the room for the pole scroll is inside main (and the header), so the banners can reach under the pole to the screen's edge */
main { padding-left: var(--rail); }
/* the frame (Astra, 2026-10-29): the pole on the left and the header are one black bezel around a screen where the code rain
   falls, with one gray inner edge. The sections are sheets of paper floating on that screen, the Polaroids taped to them.
   Color means one thing: the section you are in. */
/* the pole three quarters of what it was (Astra, 2026-10-29) */
/* --float: the room between the paper and the pole, and the same on the right (Astra, 2026-10-29: "a little bit more space") */
/* --rail: the pole's width, back to the 2026-10-28 chrome pole's (Astra, 2026-10-29) */
:root { --rail: 36px; --frame: #050505; --edge: #3d3d3d; --now: #ff2e4d; --float: 20px; }
/* the pole on the left, from Mommy's website: the chrome pole and a floor label per section */
/* black, with the frame's gray line on its right; no code rain behind the pole scroll (Astra, 2026-10-29) */
#pole { position: fixed; left: 0; top: 0; bottom: 0; z-index: 40; width: var(--rail); pointer-events: none; background: var(--frame); border-right: 2px solid var(--edge); border-left: 2px solid var(--edge); }
/* the frame all the way round (Astra, 2026-10-29): the pole's gray line on both its sides, down the window's right edge and
   along its bottom, where the pole ends */
/* (Astra, 2026-10-29) one square all the way round the window, the same light gray line on all four sides */
.bezel { position: fixed; inset: 0; z-index: 41; pointer-events: none; border: 2px solid var(--edge); }
/* but not along the very top and bottom (Astra, 2026-10-29): on a phone the browser bar slides in and out as you scroll and
   the fixed line dragged behind it, like a box stuck on the page; off on the computer too, the same everywhere */
.bezel { border-top: 0; border-bottom: 0; }
/* the matte chrome pole back as it was on 2026-10-28 (Astra, 2026-10-29: "put this back on instead of the other one"):
   white and gray, rounded at its ends */
#pole::before { content: ""; position: absolute; top: 0; bottom: 0; left: calc(50% - 4px); width: 8px; border-radius: 8px;
  background: linear-gradient(90deg, #5c5c5c 0%, #f4f4f4 28%, #a9a9a9 46%, #e2e2e2 62%, #6e6e6e 82%, #3a3a3a 100%);
  box-shadow: 0 0 10px rgba(255, 255, 255, .18); }
/* the pole's sparkle is the scroll bar (Astra, 2026-10-29): it rides the pole with the page, glowing in the color of the
   section you are in; drag it to scroll. The browser's own scroll bar is hidden */
/* the page's own pastel sparkle (Astra, 2026-10-29: "the exact same pastel sparkle"): the same four points, a pastel of the
   section you are in, drifting round the hues like the rest */
.rail-spark { --pale: color-mix(in srgb, var(--now) 65%, #fff); --pale: hsl(from var(--now) h 55% 80%); position: absolute; left: 50%; width: 30px; height: 30px; translate: -50% 0;
  filter: drop-shadow(0 0 1px #000) drop-shadow(0 0 6px var(--pale)) drop-shadow(0 0 14px var(--pale)); transition: filter .4s; animation: rainbow-drift 28s linear infinite; }
.rail-spark::before { content: ""; position: absolute; inset: 0; background: var(--pale); transition: background .4s;
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); animation: twinkle 4.8s ease-in-out infinite; }
.rail-spark::after { content: none; }
@keyframes sb-spin { from { rotate: 0deg; } to { rotate: 720deg; } }
@media (prefers-reduced-motion: reduce) { .rail-spark, .rail-spark::before { animation: none; } }
#bead { top: 0; z-index: 2; pointer-events: auto; cursor: grab; touch-action: none; }
#bead.held { cursor: grabbing; }
html { scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }
/* the words shrink with the window so there is always room between them, and the first and last sit the same
   distance from the top and bottom (Astra, 2026-10-29) */
.floors { --pad: 28px; position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: space-between; align-items: center; gap: 14px; padding: var(--pad) 0; }
.floors a { pointer-events: auto; cursor: pointer; text-decoration: none; min-height: 44px; min-width: 22px; display: flex; align-items: center; justify-content: center;
  font: 700 clamp(8px, calc((100vh - 230px) / 28), 16px)/1 var(--mono); letter-spacing: .1em; color: var(--c, #c9c9c9); writing-mode: vertical-rl; text-orientation: mixed; transform: rotate(180deg); padding: 6px 2px;
  transition: opacity .25s, color .25s, text-shadow .25s; }
/* each floor its own color (Astra, 2026-10-27: "I want each section to have a color") */
/* Astra, 2026-10-29: VR in VRChat's blue, Discord's purple, Web pink, PetOS orange, Arcade yellow, Contact green; the
   Terminal takes the red that was left over */
:root { --c-top: #ff2e4d; --c-vr: #1fa4ff; --c-discord: #8c6bff; --c-web: #ff2bd6; --c-pets: #ff7a1a; --c-arcade: #fff01f; --c-contact: #39ff14; }
.floors a:nth-child(1) { --c: var(--c-top); } .floors a:nth-child(2) { --c: var(--c-vr); } .floors a:nth-child(3) { --c: var(--c-discord); } .floors a:nth-child(4) { --c: var(--c-web); }
.floors a:nth-child(5) { --c: var(--c-pets); } .floors a:nth-child(6) { --c: var(--c-arcade); } .floors a:nth-child(7) { --c: var(--c-contact); }
/* the labels back as they were on 2026-10-28 (Astra, 2026-10-29: "put this back on"): capitals in the mono type, each in its
   first neon (Terminal pink, VR red, Discord orange, Web yellow, PetOS green, Arcade blue, Contact purple), on a small dark
   block across the pole, dimmed; the one you are on lit up and glowing */
.floors a:nth-child(1) { --c: #ff2bd6; } .floors a:nth-child(2) { --c: #ff2e4d; } .floors a:nth-child(3) { --c: #ff7a1a; } .floors a:nth-child(4) { --c: #fff01f; }
.floors a:nth-child(5) { --c: #39ff14; } .floors a:nth-child(6) { --c: #1f8fff; } .floors a:nth-child(7) { --c: #b14bff; }
/* the pole's words, the tape and the title tags a fifth softer, pastel rather than neon (Astra, 2026-10-29) */
.floors a { --c-soft: color-mix(in oklab, var(--c) 78%, #e2e2e2); }
.floors a { font-family: var(--mono); font-weight: 700; letter-spacing: .1em; color: var(--c-soft); background: #0b0b0b; opacity: .55; min-width: 28px; }
.floors a:hover, .floors a:focus-visible { opacity: 1; background: #0b0b0b; color: var(--c-soft); }
.floors a.on { opacity: 1; color: var(--c-soft); text-shadow: 0 0 8px var(--c-soft), 0 0 14px color-mix(in srgb, var(--c-soft) 70%, transparent); }
/* film grain over everything */
body::after { content: ""; position: fixed; inset: 0; z-index: 60; pointer-events: none; opacity: .09;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
a { color: var(--ink); text-decoration-thickness: 2px; text-underline-offset: 3px; }
a:hover { background: var(--ink); color: var(--bg); text-decoration: none; }
img { max-width: 100%; display: block; }
.mono { font-family: var(--mono); font-size: 13px; }
.muted { color: var(--muted); }
.defs { position: absolute; width: 0; height: 0; overflow: hidden; }
main { overflow-x: clip; }

/* header: the top of the frame, its inner edge joining the rails' (Astra, 2026-10-29); "find /astra /" and the page search
   after it, like a label printed on the case (Astra, 2026-10-28) */
.top { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: .6ch; padding: 12px 20px 12px calc(var(--rail) + var(--float));
  background: var(--frame); border-bottom: 2px solid var(--edge); color: #f2f2f2; }
/* both banners have a stripe above and below (Astra, 2026-10-29), right against the hero, with no black edge, so the rain
   shows all the way to it */
/* just two rows of checks, nothing else (Astra, 2026-10-29: "it's just supposed to be two squares stacked"): bigger squares so
   they stay sharp on a phone, and no gray line along their outside edge, which read as a broken third row */
.race-stripe { height: 8px; background: repeating-conic-gradient(#5a5a5a 0 25%, #000 0 50%) 0 0 / 8px 8px; }
.brand { font: 700 18px var(--mono); color: #f2f2f2; text-decoration: none; white-space: nowrap; }
.brand span { color: #777; }
.brand:hover { background: none; color: #fff; }
/* the search reads as the next part of the path: the same font as "find /astra /", a touch smaller, about as tall as the
   slash, right after it (Astra, 2026-10-29) */
#search { flex: 0 1 190px; min-width: 0; box-sizing: border-box; height: 1.4em; font: 700 16px/1 var(--mono); color: #f2f2f2; background: #0a0a0a; border: 1px solid #555; padding: 0 4px; }
/* a hair closer to the last /, so it reads as find /astra /search (Astra, 2026-10-29) */
#search { margin-left: -4px; }
#search::placeholder { color: #777; }
#search:focus { outline: 2px dashed #8a8a8a; outline-offset: 3px; }
#search.none { border-style: dashed; color: #8a8a8a; }
/* the search's drop-down (Astra, 2026-10-29): what matches, under the box; pick one and the page goes there */
.search-list { position: fixed; z-index: 70; margin: 0; padding: 4px; list-style: none; max-height: min(60vh, 420px); overflow-y: auto; background: #0a0a0a; border: 1px solid #555; box-shadow: 6px 6px 0 #000; font: 13px/1.3 var(--mono); }
.search-list[hidden] { display: none; }
.search-list li { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; align-items: baseline; padding: 6px 8px; cursor: pointer; color: #f2f2f2; }
.search-list li small { grid-column: 2; color: #8a8a8a; font-size: 11px; }
.search-list li i { font-style: normal; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: #8a8a8a; min-width: 6ch; }
.search-list li[aria-selected="true"], .search-list li:hover { background: #2a2a2a; }
/* where the search took you, outlined a moment */
.found { outline: 3px dashed #f2f2f2; outline-offset: 6px; transition: outline-color 1s; }
/* every match on the page, and the one it is on */
::highlight(page-find) { background: #5a5a5a; color: #fff; }
::highlight(page-find-now) { background: #f2f2f2; color: #000; }

/* buttons */
.btn { display: inline-block; font: 13px var(--mono); color: var(--ink); background: transparent; border: 2px solid var(--ink); padding: 6px 14px; cursor: pointer; text-decoration: none; border-radius: 0; box-shadow: 4px 4px 0 var(--muted); transition: transform .12s, box-shadow .12s; }
.btn:hover, .btn:focus-visible { background: var(--ink); color: var(--bg); transform: translate(-2px, -2px); box-shadow: 6px 6px 0 var(--muted); outline: none; }
.btn.hot { background: var(--ink); color: var(--bg); }
.btn.hot:hover, .btn.hot:focus-visible { background: var(--bg); color: var(--ink); }

/* sections */
/* the same room below each section as above its title (Astra, 2026-10-29). Each section is a sheet of paper floating on the
   screen, set in from the frame like the banners, with the Polaroids' hard shadow under it (Astra, 2026-10-29) */
/* --sheet: the words sit as far in from the paper's left edge as from its top, on every section (Astra, 2026-10-29) */
.block { --sheet: 38px; position: relative; isolation: isolate; padding: var(--sheet); margin-inline: var(--float); box-shadow: 8px 8px 0 #000; background: var(--bg); color: var(--ink); }
.kicker { display: inline-block; font: 13px var(--mono); background: var(--ink); color: var(--bg); padding: 3px 10px; margin: 0 0 14px; transform: rotate(-2deg); }
/* written as they are, "Discord", "Web & More" (Astra, 2026-10-29), and a fifth smaller than they first were */
h2 { font: 900 clamp(2.25rem, 7.2vw, 4.8rem)/.95 Inter, sans-serif; letter-spacing: -.03em; margin: 0 0 28px; }
h3 { display: inline-block; font: 700 12px var(--mono); text-transform: uppercase; letter-spacing: .14em; color: var(--ink); border-bottom: 3px solid var(--ink); margin: 20px 0 10px; padding-bottom: 2px; }
.lead { max-width: 70ch; color: var(--muted); margin-top: -12px; }

/* each section is its own kind of gray */
.light { background: linear-gradient(#262626 1px, transparent 1px) 0 0 / 100% 28px, linear-gradient(90deg, #262626 1px, transparent 1px) 0 0 / 28px 100%, #1b1b1b; }.mid { background: repeating-linear-gradient(-45deg, #242424 0 16px, #2d2d2d 16px 32px); }
.dots { background: radial-gradient(circle, #2f2f2f 1.5px, transparent 2px) 0 0 / 13px 13px, linear-gradient(#161616, #0c0c0c); }
.checker { background: repeating-conic-gradient(#111 0 25%, #1c1c1c 0 50%) 0 0 / 44px 44px; }
/* stripes big enough to see on a phone (Astra, 2026-10-29: "not visible from the human eye on the phone") */
.lines { background: repeating-linear-gradient(#151515 0 7px, #262626 7px 9px); }
/* sections are separated by an open gap one grid square tall, where the code rain falls through (Astra, 2026-10-29);
   each has its own pattern */
.block + .block, .ticker + .block, .race-stripe + .block { margin-top: 28px; }
.weave { background: repeating-linear-gradient(45deg, #1d1d1d 0 2px, transparent 2px 14px), repeating-linear-gradient(-45deg, #1d1d1d 0 2px, transparent 2px 14px), #121212; }
/* every section's title written like the Arcade's (Astra, 2026-10-29): a gray shadow and a black one behind it */
.block h2 { text-shadow: 4px 4px 0 #555, 8px 8px 0 #000; }
/* each section's little tag above its title, in that section's color, black writing on it so it reads (Astra, 2026-10-29) */
#vr { --tag: var(--c-vr); } #discord { --tag: var(--c-discord); } #web { --tag: var(--c-web); } #pets { --tag: var(--c-pets); }
#arcade { --tag: var(--c-arcade); } #contact { --tag: var(--c-contact); }
/* a little less saturated than the neon (Astra, 2026-10-29) */
.block .kicker { background: color-mix(in oklab, var(--tag, var(--ink)) 54%, #c4c4c4); color: #000; font-weight: 700; box-shadow: 3px 3px 0 #000, 0 0 10px color-mix(in srgb, var(--tag, transparent) 30%, transparent); }
/* "Astra's PetOS" as written, so the capitals do not run together (Astra, 2026-10-29: in all capitals it read wrong) */

/* tickers: what changed lately, right under the header, and what is being worked on now, under the hero. Both are the same
   slim, straight news strip; working on runs the other way (Astra, 2026-10-28: "make it straight", "the same size as the other one") */
.ticker { position: relative; z-index: 2; padding: 7px 0; overflow: hidden; white-space: nowrap; background: #030303; color: #d6d6d6;
  border-block: 1px solid #262626; font: 500 13px/1.2 var(--mono); }
.tick-label { position: absolute; z-index: 1; left: 0; top: 0; bottom: 0; display: grid; place-items: center; padding: 0 12px; background: #2a2a2a; color: #d6d6d6;
  font: 700 10px var(--mono); text-transform: uppercase; letter-spacing: .14em; box-shadow: 6px 0 10px #000; }
/* A stationary pixel matrix across the introduction; only its lit cells change. */
.rabbit-lane { position: absolute; z-index: 3; left: 20px; right: 20px; bottom: 6px; height: 54px; overflow: hidden; }
.rabbit-grid { display: block; width: 100%; height: 54px; padding: 0; border: 0; background: #080a0b; cursor: pointer; }
.rabbit-grid canvas { display: block; width: 100%; height: 54px; image-rendering: pixelated; }
.rabbit-grid:focus-visible { outline: 2px solid #e4e4e4; outline-offset: -2px; }
.rabbit-burst-cv { position: fixed; z-index: 45; pointer-events: none; }
.rabbit-burst { inset: auto; width: 52px; height: 52px; translate: -50% -50%; z-index: 3; }
.ticker .track { display: inline-flex; animation: tick var(--dur, 90s) linear infinite; }
.ticker.flip .track { animation-direction: reverse; }
.ticker:hover .track, .ticker:focus-within .track { animation-play-state: paused; }
.ticker .item { display: inline-flex; align-items: baseline; gap: .55em; padding-right: 2.4em; }
/* the words after a project name: a pale, almost white tint of its color (Astra, 2026-10-27) */
.ticker .item > span:not([class]) { color: color-mix(in srgb, var(--c, #fff) 28%, #e6e6e6); }
.ticker .item::after { content: "✦"; color: #9a9a9a; margin-left: 1.8em; }
.ticker .repo { color: var(--c, #fff); font-weight: 700; text-decoration: none; }
.ticker .repo:hover { background: var(--c, #fff); color: #000; }
.ticker .when { color: #7a7a7a; }
.ticker .pet { color: #9a9a9a; }
@keyframes tick { to { transform: translateX(-50%); } }

/* hero */
/* the terminal and the portrait: two squares the same size, straight, snug side by side (Astra, 2026-10-28); about as
   much room above them as below, one dot row less (Astra, 2026-10-29) */
/* on a narrow window the squares stay well inside it, with the code rain showing all round them (Astra, 2026-10-29) */
.hero { --sq: min(340px, 100% - 88px); position: relative; isolation: isolate; display: grid; grid-template-columns: repeat(2, var(--sq)); justify-content: center; gap: 48px;
  align-items: center; padding: 48px var(--wrap) 66px; /* one dot row less on top, and one less below (Astra, 2026-10-29) */
  /* no glow behind the portrait: only its own square, the code rain clear around it (Astra, 2026-10-29) */
  background: none; color: var(--ink); }
/* the rain and the dot grid sit behind the whole page; the hero is see-through, and the gaps between sections show it too */
body { background: radial-gradient(#272727 1px, transparent 1.6px) 0 0 / 18px 18px fixed, #070707; }
#rain { position: fixed; inset: 0; width: 100%; height: 100%; opacity: .7; z-index: 0; pointer-events: none; }
main { position: relative; z-index: 1; }
.term { position: relative; display: flex; flex-direction: column; aspect-ratio: 1; box-sizing: border-box; background: #000; border: 2px solid var(--ink); }
.term::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: translate(14px, 14px); background: repeating-linear-gradient(45deg, var(--ink) 0 2px, transparent 2px 8px); }
.term-bar { display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: repeating-linear-gradient(#f2f2f2 0 2px, #9c9c9c 2px 3px); border-bottom: 2px solid #f2f2f2; font: 12px var(--mono); color: #000; }
/* the hero terminal as a Linux one (Astra, 2026-10-29): a flat dark title bar with its title in the middle, like GNOME's
   Terminal, the lights still on the left; and the shell's colored prompt */
.term > .term-bar { position: relative; justify-content: flex-start; padding: 7px 10px; background: #2d2d2d; border-bottom: 1px solid #111; color: #e6e6e6; }
.term > .term-bar #term-title { position: absolute; left: 50%; translate: -50% 0; margin: 0; padding: 0; background: none; color: #e6e6e6; font-weight: 700; white-space: nowrap; pointer-events: none; }
.term .ps-user { color: #5fd35f; font-weight: 700; }
.term .ps-path { color: #5c9dff; font-weight: 700; }
.term .ps { color: #e6e6e6; font-weight: 400; }
#term-prompt .ps-user, #term-prompt .ps-path { font-weight: 700; }
/* the user@findastra part shifts slowly through the neon colors (Astra, 2026-10-29: "noticeable, but not too fast"), and the
   path a step behind it */
@keyframes ps-neon { 0%, 100% { color: #39ff88; } 17% { color: #39e6ff; } 33% { color: #4f7dff; } 50% { color: #b84dff; } 67% { color: #ff3fb4; } 83% { color: #ffe14d; } }
/* three times slower (Astra, 2026-10-29) */
.term .ps-user { animation: ps-neon 36s linear infinite; text-shadow: 0 0 6px currentColor; }
.term .ps-path { animation: ps-neon 36s linear infinite -6s; }
@media (prefers-reduced-motion: reduce) { .term .ps-user, .term .ps-path { animation: none; } }
/* the paired lights from the pet program pages' tool panels (astras-pet-os pet-program-page-20261018.css, Unity Stable):
   left ON/OFF (green, red), right checked and works or report issues (blue, yellow). A click moves a light to its next state
   and a bubble says what it means; nothing on hover (Astra, 2026-10-29) */
.term-bar .lights { display: inline-flex; gap: 7px; }
.term .light, .hack-term .light { width: 11px; height: 11px; padding: 0; border-radius: 50%; border: 1px solid #111; cursor: pointer; flex: none;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255, 255, 255, .35); transition: transform .08s ease-out; }
.term .light:hover, .hack-term .light:hover { transform: scale(1.18); }
.term .light:active, .hack-term .light:active { transform: scale(.9); box-shadow: inset 0 2px 2px rgba(0, 0, 0, .4); }
.term .light.onoff, .hack-term .light.onoff { background: #4fb35a; } .term .light.onoff.is-off, .hack-term .light.onoff.is-off { background: #d9534f; }
.term .light.care, .hack-term .light.care { background: #6cc7e8; } .term .light.care.is-fix, .hack-term .light.care.is-fix { background: #e8c547; }
.light-bubble { position: absolute; z-index: 50; max-width: 280px; padding: 6px 12px; border: 1px solid #e3e9f3; border-radius: 14px;
  background: #0a0d13; color: #e3e9f3; font: 13px/1.4 var(--mono); box-shadow: 0 6px 18px rgba(0, 0, 0, .45); pointer-events: none; }
.light-bubble[hidden] { display: none; }
/* switched off: the screen goes dark until it is switched back on */
.term.is-off .term-body, .term.is-off .term-input, .term.is-off .term-actions { opacity: .08; pointer-events: none; transition: opacity .3s; }
#term-title { margin-left: 8px; background: #f2f2f2; padding: 0 8px; }
.term-body { position: relative; flex: 1; min-height: 210px; padding: 12px 14px; font: 12.5px/1.55 var(--mono); color: #e4e4e4; white-space: pre-wrap; }
.term-body::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: repeating-linear-gradient(transparent 0 2px, rgba(255, 255, 255, .035) 2px 3px); }
.term-body .dim { color: #7c7c7c; }
.term-body .ok { color: #fff; font-weight: 700; }
.term-body .hot { display: table; background: #f2f2f2; color: #000; padding: 0 6px; }
.term-input { display: flex; align-items: center; gap: 8px; margin: 0; padding: 4px 14px 8px; font: 12.5px var(--mono); color: #fff; }
.term-input label { font-weight: 700; white-space: nowrap; }
#term-in { flex: 1; width: 0; min-width: 0; font: inherit; color: #e4e4e4; background: transparent; border: 0; border-bottom: 1px dashed #4a4a4a; padding: 2px 0; caret-color: #fff; }
#term-in:focus { outline: none; border-bottom-color: #f2f2f2; }
.term-body { min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #5a5a5a #000; }
/* a slim gray scroll bar in a black channel, like the rest of the terminal (Astra, 2026-10-29: "looks kind of weird") */
.term-body::-webkit-scrollbar { width: 6px; }
.term-body::-webkit-scrollbar-track { background: #000; }
.term-body::-webkit-scrollbar-thumb { background: #5a5a5a; }
.term-body::-webkit-scrollbar-button { display: none; }
.term-actions { display: flex; gap: 8px; flex-wrap: nowrap; padding: 10px 14px 14px; /* one row, on every screen (Astra, 2026-10-29) */ --ink: #f2f2f2; --bg: #000; --muted: #5c5c5c; }
/* the buttons' edges a shade darker; the title on the striped bar sits on its own solid strip so it reads (Astra, 2026-10-29) */
.term-actions .btn { border-color: #9a9a9a; flex: 0 1 auto; min-width: 0; white-space: nowrap; }
.term-actions .btn.hot { border-color: #6a6a6a; }
.term-bar #term-title, .term-bar > span:not(.lights) { background: #f2f2f2; color: #000; font-weight: 700; padding: 0 6px; }
.who { display: flex; flex-direction: column; justify-content: center; text-align: center; }
.portrait { margin: 0; width: 100%; aspect-ratio: 1; position: relative; }
/* a little see-through, hiding among the falling numbers and the sparkles (Astra, 2026-10-29) */
.portrait img { width: 100%; height: 100%; object-fit: cover; border: 3px solid var(--ink); opacity: .6; }
/* the portrait's own colors twinkle on it (cyber.js samples them), rainbow: each one a little off its color, and all of
   them drifting round the hues (Astra, 2026-10-27); much more saturated and further round the rainbow (Astra, 2026-10-29) */
.portrait .glitter { inset: 3px; z-index: 1; animation: rainbow-drift 28s linear infinite; }
@keyframes rainbow-drift { 0%, 100% { filter: hue-rotate(-70deg); } 50% { filter: hue-rotate(70deg); } }
/* one more veil over the picture (Astra, 2026-10-29): fine diagonal lines the other way from the stripes behind, and a
   halftone of dots, so she keeps her shape but hides a little more */
/* unmasked, she turns to her own negative: every color inverted, the hues turned back round so the tints still read
   (Astra, 2026-10-29) */
body.unmasked .portrait img { filter: invert(1) hue-rotate(180deg) saturate(1.3); }
/* unmasked, every sparkle turns dark: a black star inside its pastel, which stays round it as a glowing edge (Astra, 2026-10-29) */
body.unmasked .sparkles i::after, body.unmasked .jar-spark::after, body.unmasked .fuzz-trail::after { content: ""; position: absolute; inset: 24%; background: #0b0b0b;
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); }
.portrait::after { content: ""; position: absolute; inset: 3px; z-index: 0; pointer-events: none;
  background: repeating-linear-gradient(45deg, rgba(5, 5, 5, .28) 0 1.5px, transparent 1.5px 6px), radial-gradient(rgba(5, 5, 5, .22) 1px, transparent 1.6px) 0 0 / 5px 5px; }
.portrait::before { content: ""; position: absolute; inset: 0; z-index: -1; transform: translate(-16px, 16px); background: repeating-linear-gradient(-45deg, #8c8c8c 0 3px, transparent 3px 8px); }
/* Astra's hand lettering: transparent ink masks painted in --ink, so they follow the hero (white, then black when unmasked) */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.lettering { margin: 0 auto 14px; width: 100%; max-width: 416px; outline: none; }
.lettering:focus-visible { outline: 2px dashed var(--ink); outline-offset: 6px; }
/* as tall as the piece showing (lettering.js sets it), so a short one has no empty band above and below it
   (Astra, 2026-10-28: "too much space between the hand text above and below"); contain keeps the slides side by side from
   making the column wider than a phone's screen */
.letter-track { display: flex; align-items: center; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none;
  contain: inline-size; transition: height .3s ease; }
.letter-track::-webkit-scrollbar { display: none; }
/* small, about a third of what it was (Astra, 2026-10-27: "make my handwriting like a third of the size"; it was 84–118px tall),
   then all of it 20% smaller, with the same words on each line (Astra, 2026-10-28: "shrink it by 20%") */
.letter { flex: 0 0 100%; scroll-snap-align: center; display: grid; place-items: center; min-height: clamp(32px, 8vw, 40px); padding: 3px 6px; box-sizing: border-box; }
/* each picture cut into its words, laid out large in rows; a line of writing is --line tall (Astra, 2026-10-27) */
.letter-words { --line: clamp(24px, 4.8vw, 33.6px); display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 1.6px 9.6px; max-width: 100%; }
.letter-word { width: calc(var(--w) * var(--line)); height: calc(var(--h) * var(--line)); background: var(--ink); transition: background .4s;
  -webkit-mask: var(--src) center / 100% 100% no-repeat; mask: var(--src) center / 100% 100% no-repeat; }
/* a picture shown whole, when its words could not be cut apart */
.letter-ink { width: 100%; height: clamp(67.2px, 16vw, 94.4px); background: var(--ink);
  -webkit-mask: var(--src) center / contain no-repeat; mask: var(--src) center / contain no-repeat; transition: background .4s; }
/* on a narrow phone the count moves under the dots instead of pushing the page wider than the screen */
.letter-nav { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 10px; margin-top: 6px; }
.letter-btn { font: 700 20px/1 var(--mono); width: 34px; height: 30px; color: var(--ink); background: transparent; border: 2px solid var(--ink); cursor: pointer; box-shadow: 3px 3px 0 var(--muted); }
.letter-btn:hover, .letter-btn:focus-visible { background: var(--ink); color: var(--bg); outline: none; }
.letter-dots { display: flex; gap: 2px; }
.letter-dot { width: 22px; height: 22px; padding: 0; border: 0; background: transparent; cursor: pointer; display: grid; place-items: center; }
.letter-dot::before { content: ""; width: 9px; height: 9px; border: 2px solid var(--ink); transform: rotate(45deg); }
.letter-dot[aria-current="true"]::before { background: var(--ink); }
.letter-dot:focus-visible { outline: 2px solid var(--ink); }
.letter-count { font: 11px var(--mono); letter-spacing: .12em; color: var(--muted); min-width: 4ch; }
/* the lettering's credit, under the slider: its words are in the lettering itself */
.quote { margin: 6px auto 0; font: 14px var(--mono); color: var(--muted); }
.quote::before { content: "— "; }

/* the unmask (Astra, 2026-10-27): the hero turns white, its falling code black and glowing in its colors; everywhere else the
   grays take on a soft tint of their section's color */
body.unmasked .hero { --bg: #f7f7f7; --ink: #070707; --muted: #4a4a4a;
  background: none; }
body.unmasked { background: radial-gradient(#dcdcdc 1px, transparent 1.6px) 0 0 / 18px 18px fixed, #f4f4f4; }
body.unmasked #rain { opacity: .8; }
body.unmasked .block::before, body.unmasked .ticker::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: var(--tint, #8a8fff);
  mix-blend-mode: color; opacity: .6; }
body.unmasked .ticker { isolation: isolate; }
body.unmasked #vr { --tint: var(--c-vr); } body.unmasked #discord { --tint: var(--c-discord); } body.unmasked #web { --tint: var(--c-web); }
body.unmasked #pets { --tint: var(--c-pets); } body.unmasked #arcade { --tint: var(--c-arcade); } body.unmasked #contact { --tint: var(--c-contact); }
body.unmasked #tick-updates { --tint: var(--c-top); } body.unmasked #tick-now { --tint: var(--c-top); }
/* on the tinted panels the titles stay black, outlined in white, never colored (Astra, 2026-10-27) */
/* the white outline drawn round the letters, not as a stroke over them, so tight letters no longer cut into each other's
   corners (Astra, 2026-10-29) */
body.unmasked .block h2 { color: #050505; -webkit-text-stroke: 0; background: none;
  text-shadow: 2px 0 #f2f2f2, -2px 0 #f2f2f2, 0 2px #f2f2f2, 0 -2px #f2f2f2, 1.5px 1.5px #f2f2f2, -1.5px 1.5px #f2f2f2, 1.5px -1.5px #f2f2f2, -1.5px -1.5px #f2f2f2; }
.glitch { animation: glitch .8s steps(2) 1; }
@keyframes glitch {
  20% { filter: invert(1) contrast(2); transform: translate(-5px, 2px) skewX(6deg); }
  40% { filter: contrast(3) brightness(1.4); transform: translate(4px, -2px); }
  60% { filter: invert(1); transform: translate(-2px, 3px) skewX(-5deg); }
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0; margin: 0; list-style: none; }
.chips li { font: 13px var(--mono); border: 2px solid var(--ink); background: var(--panel); padding: 2px 9px; }
.chips li:nth-child(3n+2) { background: var(--ink); color: var(--bg); }
.chips li:nth-child(3n) { background: #444; }
.list { padding-left: 1.4em; margin: 0; list-style: "✦  "; }
.list li { margin-bottom: 4px; }

/* projects: VR & VRChat, Discord and Web & More as Polaroids, Astra's PetOS as the star jar */
.tag { display: inline-block; font: 11px var(--mono); text-transform: uppercase; letter-spacing: .12em; background: #000; color: #cfcfcf; padding: 2px 8px; }

/* sparkles: one kind on the whole page, the portrait's (Astra, 2026-10-29): four-point twinkles in soft pastels, each a
   little round the wheel from the next, all drifting through the hues, in front of what they ring */
.sparkles { position: absolute; inset: 0; pointer-events: none; z-index: 6; animation: rainbow-drift 28s linear infinite; }
.sparkles.field { inset: 0; z-index: 3; }
.sparkles i { position: absolute; left: var(--x); top: var(--y); width: var(--s); aspect-ratio: 1; translate: -50% -50%; background: var(--c);
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
  animation: twinkle 4.8s ease-in-out var(--d) infinite; }
@supports (color: hsl(from red h s l)) {
  .sparkles i { background: hsl(from var(--c) calc(h + var(--dh)) s min(calc(l + var(--l)), 92)); }
}
/* each one fades all the way out between twinkles, so they come and go instead of staying put (Astra, 2026-10-29) */
@keyframes twinkle { 0%, 100% { transform: scale(.2) rotate(0deg); opacity: 0; } 50% { transform: scale(1.15) rotate(45deg); opacity: 1; } }

/* the pet windows: fat stars */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(310px, 1fr)); gap: 0 22px; margin-top: 26px; }
/* rows tuck into each other: each star's top point rises into the notch between the legs above it */
.grid .card { margin-bottom: -5%; }
/* the star tilts, grows and turns over as .turn; the card itself stays put, so its sparkles do not swing with it
   (Astra, 2026-10-29) */
.card { position: relative; container-type: inline-size; width: 100%; max-width: 420px; aspect-ratio: 229 / 220; justify-self: center; color: #000; perspective: 1400px; }
.card:hover, .card:focus-within { z-index: 3; }
.card .turn { position: absolute; inset: 0; transform: rotate(var(--tilt, 0deg)); transform-style: preserve-3d; transition: transform .55s cubic-bezier(.3, 1.4, .5, 1); }
.card:hover .turn, .card:focus-within .turn { transform: rotate(0deg) scale(1.06); }
.card.flipped .turn { transform: rotate(0deg) rotateY(180deg); }
.card.flipped:hover .turn, .card.flipped:focus-within .turn { transform: rotate(0deg) scale(1.06) rotateY(180deg); }
.card .face, .card .back { position: absolute; inset: 0; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.card .back { transform: rotateY(180deg); display: grid; place-content: center; justify-items: center; gap: 2.2cqw; }
/* the back for now: the same star, empty */
.card .back.star-back { display: block; }
/* its spec sheet, in the middle of the star: the four levels in a white box, then code and stage */
.card .sheet { position: absolute; left: 45.5%; top: 62%; transform: translate(-50%, -50%); width: 46%; display: grid; gap: 1.6cqw; }
.card .levels-box { margin: 0; padding: 1.6cqw 2cqw; display: grid; grid-template-columns: auto 1fr; gap: .7cqw 2cqw; background: #fff; border: 2px solid #000;
  box-shadow: 1cqw 1cqw 0 #000; font: 600 3cqw/1.2 var(--mono); color: #000; text-align: left; }
.card .levels-box dt { font-weight: 800; }
.card .levels-box dd { margin: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card .levels-box dd span { display: block; overflow: hidden; text-overflow: ellipsis; }
.card .sheet .spec { position: static; transform: none; width: auto; }
.card .spec { position: absolute; left: 50%; top: 56%; transform: translate(-50%, -50%); width: 52%; margin: 0; display: grid; grid-template-columns: auto 1fr;
  gap: .9cqw 2.4cqw; font: 600 3.3cqw/1.2 var(--mono); color: #000; text-align: left; }
.card .spec dt { font-weight: 400; opacity: .7; }
.card .spec dd { margin: 0; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 700; }
/* the pet and its program are links, in the star's own black */
/* underlined, so they read as links (Astra, 2026-10-29); the arrow is balanced by an unseen one on the left, so the words
   themselves sit in the middle of the star */
.card .body a { text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: .16em; }
.card .body a::before { content: "↗\00a0"; visibility: hidden; font-weight: 700; }
/* the little arrow at the end, so people know to click (Astra, 2026-10-29) */
.card .body a::after { content: "\00a0↗"; font-weight: 700; }
.card .body a:hover, .card .body a:focus-visible { text-decoration-thickness: 2.5px; outline: none; }
/* the back: the pet program as one big square in the star's color */
.card .square { position: relative; display: grid; place-items: center; width: 72cqw; aspect-ratio: 1; padding: 0; overflow: hidden; cursor: zoom-in;
  background: #0b0b0b; border: 3px solid var(--c); box-shadow: 0 0 14px var(--c), 8px 8px 0 #000; }
.card div.square { cursor: default; background: color-mix(in srgb, var(--c) 22%, #0b0b0b); }
.card .square img { width: 100%; height: 100%; object-fit: cover; }
.card .square img.pix { width: 70%; height: 70%; object-fit: contain; image-rendering: pixelated; }
.card .square .soon { position: absolute; bottom: 6%; font: 3.4cqw/1 var(--mono); color: #cfcfcf; }
.card .back-links { display: flex; gap: 2cqw; align-items: center; }
.card .back-links a, .card .turn-btn { font: 700 3.2cqw/1 var(--mono); color: #000; background: var(--c); border: 2px solid #000; padding: .5em .8em; border-radius: 99px;
  text-decoration: none; cursor: pointer; box-shadow: 0 0 10px var(--c); }
.card .back-links a:hover, .card .turn-btn:hover, .card .back-links a:focus-visible, .card .turn-btn:focus-visible { background: #fff; color: #000; outline: none; }
/* the flip button in the star's top point: the turn-over arrows in a circle (Astra, 2026-10-29) */
/* just a small black icon, no circle behind it (Astra, 2026-10-29) */
/* on the star's right-hand point, inside its pale edge (Astra, 2026-10-29) */
/* in the middle of the right-hand point, inside the inner line (the shine) that runs round inside the star */
.card .tip.turn-btn { left: 74%; top: 46%; padding: 0; width: 7%; border: 0; border-radius: 0; background: none; box-shadow: none; color: #000; cursor: pointer; z-index: 2; }
.card .tip.turn-btn svg { width: 100%; height: 100%; }
.card .back-links .turn-btn { display: grid; place-items: center; width: 8cqw; height: 8cqw; padding: 0; border-radius: 50%; }
.card .back-links .turn-btn svg { width: 62%; height: 62%; }
.card .tip.turn-btn:hover, .card .tip.turn-btn:focus-visible { background: none; color: #fff; outline: none; }
/* the System Map button on the left arm, across from the flip (Astra, 2026-10-29) */
.card .tip.map-btn { left: 23%; top: 46%; width: 7%; color: #000; z-index: 2; }
.card .tip.map-btn svg { width: 100%; height: 100%; overflow: visible; }
.card .tip.map-btn circle { fill: none; stroke: currentColor; stroke-width: 7; }
.card .tip.map-btn circle.core, .card .tip.map-btn circle.node { fill: currentColor; stroke: none; }
.card .tip.map-btn:hover, .card .tip.map-btn:focus-visible { color: #fff; outline: none; }
.card.dark-star .tip.map-btn { color: #fff; }
.card .shape { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
/* thin strokes now, so the outline stays the Photostar's own rounded star, not puffed out by the stroke (Astra, 2026-10-29) */
.card .s-shadow { fill: #000; stroke: #000; stroke-width: 12; transform: translate(7px, 7px); transition: transform .3s; }
.card:hover .s-shadow { transform: translate(12px, 12px); }
/* a pale glowing edge in the star's own pastel, the same as the little stars in the jar, so one turns into the other
   smoothly (Astra, 2026-10-29) */
.card .s-line { fill: color-mix(in srgb, var(--c) 22%, #fff); stroke: color-mix(in srgb, var(--c) 22%, #fff); stroke-width: 12; }
.card .s-fill { fill: var(--c); stroke: var(--c); stroke-width: 4; }
.card .s-shine { fill: none; stroke: rgba(255, 255, 255, .45); stroke-width: 3; transform: scale(.86); }
.card .sparkles { inset: 2% 0 0; }
/* a small star in the top point */
.card .tip { position: absolute; left: 48.47%; top: 19.09%; width: 13.1%; aspect-ratio: 1; transform: translate(-50%, -50%); display: grid; place-items: center; font: 400 7cqw/1 var(--mono); }
/* the window: the pet, big, where the picture box was, then its name and the flip button */
/* the pet a little bigger and lower now the flip button is up in the point, still well inside the star's pale edge */
.card .win { position: absolute; left: 20.96%; width: 55.02%; top: 24%; height: 64%; display: grid; grid-template-rows: 68fr 32fr; row-gap: .6cqw; }
/* the pet half as big, so its name, program and what it does fit under it (Astra, 2026-10-29) */
.card .win { top: 25%; height: 66%; grid-template-rows: 34fr 66fr; }
/* a little smaller and lower, clear of the star's edge (Astra, 2026-10-29: "sitting a little bit too close to the star"), with
   room for five or six points */
.card .win { top: 27%; height: 64%; grid-template-rows: 27fr 73fr; }
.card .pet-big { position: relative; display: grid; place-items: center; min-height: 0; font: 400 9cqw/1 var(--mono); }
.card .pet-big img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated; filter: drop-shadow(0 3px 0 rgba(0, 0, 0, .35)); }
/* a drawing cut to its paint: whole, a little in from the window's sides and top, standing on the window's bottom, so every
   pet sits the same way above its name */
/* a fifth smaller, still standing at the bottom of its window (Astra, 2026-10-29: "a little bit too big ... like 20% smaller") */
.card .pet-big img.inked { inset: 26.4% 16.4% 0; width: 67.2%; height: 73.6%; object-position: 50% 100%; }
.card .body { display: flex; flex-direction: column; align-items: center; text-align: center; gap: .4cqw; min-height: 0; overflow: hidden; }
/* big enough to read (Astra, 2026-10-29) */
.card .name { font: 900 5.2cqw/1.1 Inter, sans-serif; letter-spacing: -.01em; color: #000; max-width: 100%;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden; }
.card .pet { font: 600 3.8cqw/1.25 Inter, sans-serif; color: rgba(0, 0, 0, .86); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* nothing cut off: the name and program wrap onto a second line when they need to */
.card .name, .card .pet { white-space: normal; -webkit-line-clamp: 2; display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
/* the front in the back's font (Astra, 2026-10-29: "I really like that"): the name and program centered, what it does
   left-aligned under them */
.card .name { font: 800 4.2cqw/1.15 var(--mono); letter-spacing: -.02em; }
.card .pet { font: 700 3.1cqw/1.25 var(--mono); }
.card .does { align-self: center; margin: .8cqw 0 0; padding: 0; list-style: none; text-align: left; font: 600 2.7cqw/1.3 var(--mono); color: rgba(0, 0, 0, .8); }
/* five or six points, lined up further left, each on one line (Astra, 2026-10-29) */
.card .does { align-self: stretch; padding-left: 9%; font-size: 2.5cqw; line-height: 1.28; }
.card .does li { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* a dark star (navy, dark blue) writes in white */
.card.dark-star .name, .card.dark-star .pet, .card.dark-star .does, .card.dark-star .body a, .card.dark-star .tip.turn-btn { color: #fff; }
.card.dark-star .spec { color: #fff; }
.card .does li::before { content: "• "; }

/* the chapters: read top to bottom like the GitHub profile README, a dashed rail down the left joining them (Astra, 2026-10-27) */
.contents { display: flex; flex-wrap: wrap; gap: 8px; }
.contents a { font: 13px var(--mono); text-decoration: none; border: 2px solid #555; padding: 5px 10px; background: #0a0a0a; box-shadow: 3px 3px 0 #000; }
.contents a b { color: #8a8a8a; margin-right: 4px; }
.contents a:hover, .contents a:focus-visible { background: #e4e4e4; color: #000; border-color: #e4e4e4; outline: none; }
/* each project group is its own section (Astra, 2026-10-27); hidden while a search finds nothing in it */
.chapter { scroll-margin-top: 60px; }
.chapter.empty { display: none; }
.lede { max-width: 70ch; margin: -14px 0 26px; color: var(--muted); }
/* Astra's PetOS: its four levels, one a line */
.levels-title { margin: -8px 0 10px; font: 700 15px var(--mono); letter-spacing: .04em; }
.levels { list-style: none; padding: 0; display: grid; gap: 4px; margin-top: 0; }
.levels b { display: inline-block; min-width: 3ch; color: var(--ink); }
.levels .all { margin-top: 6px; }
#project-source { max-width: none; white-space: pre-line; line-height: 1.55; }
/* a little smaller, so it fits on one line more often than not (Astra, 2026-10-29) */
#discord .lede { font-size: .86em; }

/* Polaroids: every project; what each one is, written in marker on the white border (Astra, 2026-10-27) */
/* a short last row sits in the middle, not stacked to the left; each one pinned a little higher or lower than its neighbors,
   close enough that tilted corners just touch the white border next door, never the writing (Astra, 2026-10-29) */
/* a full row reaches --hang past the paper on both sides, so its first and last Polaroids just hang off the edges; cyber.js
   sizes them (--pw) so a row fills that width exactly (Astra, 2026-10-29). Tilted corners included, they hang off by less
   than one of the paper's squares (the smallest is 22px), so none is lost past the border (Astra, 2026-10-29) */
/* rows far enough apart that no tape or sticky note lands on the Polaroid above (Astra, 2026-10-29: "make sure they're all separate") */
.polaroids { --hang: 8px; display: flex; flex-wrap: wrap; justify-content: center; gap: 48px 32px; padding: 28px 0 0;
  margin-inline: calc(-1 * (var(--sheet, 0px) + var(--hang))); }
/* one Polaroid under the other (a phone): the wall stays on the paper instead of hanging just off it */
.polaroids.single { margin-inline: 0; }
.polaroid { position: relative; flex: 0 0 var(--pw, 250px); min-width: 0; text-align: center; translate: 0 var(--lift, 0px); }
.polaroid:nth-child(7n+1) { --lift: -8px; }
.polaroid:nth-child(7n+2) { --lift: 6px; }
.polaroid:nth-child(7n+3) { --lift: -2px; }
.polaroid:nth-child(7n+4) { --lift: 10px; }
.polaroid:nth-child(7n+5) { --lift: -12px; }
.polaroid:nth-child(7n+6) { --lift: 3px; }
.polaroid:nth-child(7n+7) { --lift: -5px; }
.polaroid:hover, .polaroid:focus-within { z-index: 4; }
.polaroid .sparkles { inset: -6% -4% 34%; }
/* the white border is drawn as strips round the photo, so the photo is a real window: a clear one shows the paper behind
   (Astra, 2026-10-29) */
.polaroid .frame { --edge-w: 12px; position: relative; width: 100%; margin: 0 auto; padding: var(--edge-w) var(--edge-w) 0; box-shadow: 8px 8px 0 #000;
  /* each strip a pixel wider than the border, so no hairline shows where they meet the photo when the frame is tilted */
  background: linear-gradient(#f2f2f2 0 0) top / 100% calc(var(--edge-w) + 1px) no-repeat, linear-gradient(#f2f2f2 0 0) left / calc(var(--edge-w) + 1px) 100% no-repeat,
    linear-gradient(#f2f2f2 0 0) right / calc(var(--edge-w) + 1px) 100% no-repeat;
  rotate: var(--tilt); transition: rotate .3s, transform .3s; }
.polaroid .photo.clear { background: none; }
.polaroid .photo.clear .developing { display: none; }
/* black masked away: what is bright stays, what is dark fades into the paper */
.polaroid .photo img.glow, .ai-card img.glow { -webkit-mask: var(--m) center / contain no-repeat; mask: var(--m) center / contain no-repeat; mask-mode: luminance; object-fit: contain;
  filter: brightness(1.9) saturate(1.2); }
/* AI Discord Presence: its two cards, one over the other */
/* a Discord profile's activity, slim like Discord draws it (Astra, 2026-10-29: "thinner and pancake-like"): "Playing" with
   the controller, then the app's own card art (its galaxy GIF, turning by itself as on Discord) beside its name, what it is
   using, what it is working on, and the elapsed time in green */
/* Discord activity previews, one over the other on the bare paper */
.ai-real { position: absolute; inset: 0; display: grid; align-content: center; gap: 10px; padding: 6%; }
/* Proportions checked against the desktop client's 312 by 132 activity card. */
.ai-real-card { display: block; min-width: 0; aspect-ratio: 312 / 132; border-radius: 3.2% / 7.6%; overflow: hidden; }
.ai-real-card { position: relative; container-type: inline-size; }
.ai-live { position: absolute; inset: 0; display: grid; grid-template-columns: 22.4cqw minmax(0, 1fr); grid-template-rows: auto 1fr; column-gap: 3.2cqw; padding: 3.2cqw;
  background: #212123; border-radius: inherit; color: #f2f3f5; font-family: 'Segoe UI', Arial, sans-serif; text-align: left; }
.ai-live .ai-head { grid-column: 1 / -1; display: flex; justify-content: space-between; margin-bottom: 3.2cqw; font: 500 4.5cqw/1.3 'Segoe UI', Arial, sans-serif; }
.ai-live .ai-head i { font-style: normal; color: #b5bac1; letter-spacing: .1em; }
.ai-live .ai-art { width: 100%; aspect-ratio: 1; border-radius: 2.5cqw; object-fit: cover; background: #000; }
.ai-live .ai-words { display: grid; align-content: start; gap: .55cqw; min-width: 0; font: 400 4.15cqw/1.3 'Segoe UI', Arial, sans-serif; color: #f2f3f5; }
.ai-live .ai-words > * { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-live .ai-words b { font: 600 4.8cqw/1.25 'Segoe UI', Arial, sans-serif; }
.ai-live .ai-time { display: flex; align-items: center; gap: 1.2cqw; color: #a5d4af; font: 400 4.15cqw/1.3 ui-monospace, Consolas, monospace; }
.ai-live .ai-time svg { width: 4.3cqw; height: 4.3cqw; fill: currentColor; flex: none; }
.polaroid:hover .frame { rotate: 0deg; transform: scale(1.03); }
/* a strip of neon tape holds each one up */
#vr .polaroid { --c: var(--c-vr) !important; }
#discord .polaroid { --c: var(--c-discord) !important; }
#web .polaroid { --c: var(--c-web) !important; }
#pets .polaroid { --c: var(--c-pets) !important; }
.polaroid .tape { position: absolute; z-index: 1; left: 50%; top: -13px; width: 90px; height: 26px; translate: -50% 0; rotate: calc(var(--tilt) * -2.5);
  /* a touch less saturated (Astra, 2026-10-29) */
  background: color-mix(in srgb, color-mix(in oklab, var(--c) 64%, #c8c8c8) 72%, transparent); box-shadow: 0 0 10px color-mix(in oklab, var(--c) 64%, #c8c8c8); }
.polaroid .photo { position: relative; display: grid; place-items: center; width: 100%; aspect-ratio: 1; padding: 0; border: 0; overflow: hidden; cursor: zoom-in;
  background: radial-gradient(circle at 30% 25%, color-mix(in srgb, var(--c) 55%, #000), #0b0b0b 72%); }
/* every picture fills the square exactly, so a tall or wide one never spills out and gets cut (Astra, 2026-10-27) */
.polaroid .photo > img, .polaroid .photo > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .4s; }
/* Zoom the actual spiral arms to the photo edges; unlit areas remain transparent. */
.polaroid .official-galaxy { position: absolute; inset: 0; display: block; background: transparent; }
.polaroid .official-galaxy > img, .polaroid .official-galaxy > canvas { position: absolute; inset: -30%; width: 160%; height: 160%; max-width: none; object-fit: contain; }
.polaroid .official-galaxy > canvas { visibility: hidden; }
.polaroid .official-galaxy.official-galaxy-ready > canvas { visibility: visible; }
.polaroid .official-galaxy.official-galaxy-ready > img { visibility: hidden; }
.polaroid .photo img[src$=".svg"] { object-fit: contain; background: #0b0b0b; }
/* a see-through one has no backing at all (the Photostar was sitting on this dark square) */
.polaroid .photo.clear img[src$=".svg"] { background: none; }
/* a live page (the Cooling Tower's Mini Model) drawn at twice the size and scaled down, so it fits the photo; it spins by itself
   and can be dragged around */
.polaroid .photo iframe.live { position: absolute; left: 0; top: 0; width: calc(var(--view, 2) * 100%); height: calc(var(--view, 2) * 100%); border: 0;
  transform: scale(calc(1 / var(--view, 2))); transform-origin: 0 0; background: #000; }
/* bare: the page's title strip cropped off the top, and the picture a little lower so the model sits in the middle */
.polaroid .photo iframe.live.bare { top: -10%; height: 300%; }
/* the PetOS Polaroids: every pet's drawing in a little grid; the pet you flip to on a light background */
.pet-grid { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(7, 1fr); align-content: center; gap: 2px; padding: 6px; }
.pet-grid img { width: 100%; aspect-ratio: 1; object-fit: contain; }
#petos-list { margin-bottom: 34px; }
/* a sticky note hanging off a Polaroid's corner (the pet's intelligence levels on "Meet every pet") */
/* a perfectly square note, its tape the same color as the strip holding up the Polaroid (Astra, 2026-10-29) */
/* up off the writing, so every pet's name on the border can be read (Astra, 2026-10-29) */
/* always exactly square, whatever is written on it (Astra, 2026-10-29): a fixed size, the writing a size to fit */
/* (not clipped, so its tape shows whole) */
/* (a little lower and smaller, Astra 2026-10-29) */
.polaroid .sticky { position: absolute; right: -14px; bottom: 98px; z-index: 3; width: 104px; height: 104px; display: grid; align-content: center; padding: 6px;
  background: #fff36b; color: #151515; font: 12.5px/1.22 "Permanent Marker", cursive; text-align: center; rotate: 7deg; box-shadow: 3px 4px 0 rgba(0, 0, 0, .55);
  /* a black edge, so it shows where it lies over the white border (Astra, 2026-10-29) */
  outline: 2px solid #111; }
/* a note naming things (the levels) gets a smaller hand, so long names fit inside the square */
/* both notes in the same hand, the levels note's (Astra, 2026-10-29), lined up on the left, every line on one line: a line too
   long for the square is drawn a little smaller to fit (fitLines in cyber.js), never wrapped */
.polaroid .sticky.names { font-size: 10px; line-height: 1.15; gap: 3px; text-align: left; justify-items: start; }
.polaroid .sticky.names > div { white-space: nowrap; }
.polaroid .sticky.levels-note { gap: 2px; }
.polaroid .sticky.levels-note > div { display: grid; grid-template-columns: auto auto 1fr; column-gap: 3px; width: 100%; white-space: normal; line-height: 1.1; }
.polaroid .sticky.levels-note > div > span:last-child { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.polaroid .sticky::before { content: ""; position: absolute; left: 50%; top: -7px; width: 40px; height: 13px; translate: -50% 0; rotate: -4deg;
  background: color-mix(in srgb, var(--c) 72%, transparent); box-shadow: 0 0 8px var(--c); }
.polaroid .sticky b { display: block; font: 700 9px var(--mono); letter-spacing: .1em; text-transform: uppercase; margin-bottom: 3px; }
/* "concept art" / "concept model", written at the top of the picture */
/* every one the same black sticker, on the white border at the top left by the title, a little crooked (Astra, 2026-10-29) */
.polaroid .note { position: relative; }
.polaroid .badge { position: absolute; left: calc(var(--edge-w) - 4px); top: -9px; z-index: 2; font: 12px/1 "Permanent Marker", cursive; color: #fff; background: #111;
  padding: 4px 8px; rotate: -5deg; pointer-events: none; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); max-width: 70%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.polaroid .badge[hidden] { display: none; }
/* a door's number: black tape at the photo's bottom right, clear of the words on the left (Astra, 2026-10-29), beside the
   flip button rather than on top of it */
.photo .door-no { position: absolute; right: 36px; bottom: 5px; z-index: 2; font: 12px/1 "Permanent Marker", cursive; color: #fff; background: #111; padding: 4px 7px;
  rotate: 4deg; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); pointer-events: none; }
.photo .door-no[hidden] { display: none; }
/* the back of a Polaroid: its note, handwritten, on the same white paper (Astra, 2026-10-29) */
.polaroid .pback { position: absolute; inset: 0; z-index: 3; display: none; align-content: center; justify-items: start; gap: 6px; padding: 26px 22px;
  background: #f2f2f2; color: #1d1d1d; font: 17px/1.2 "Permanent Marker", cursive; text-align: left;
  background-image: repeating-linear-gradient(transparent 0 31px, rgba(0, 0, 0, .07) 31px 32px); }
.polaroid .pback b { font: 13px/1 var(--mono); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #6a6a6a; margin-bottom: 6px; }
.polaroid .pback span { rotate: -1deg; }
.polaroid .frame.turned .pback { display: grid; }
.polaroid .frame.turned > :not(.pback):not(.pflip):not(.tape) { visibility: hidden; }
/* the button that turns it over: down on the white border above the title at the right, a square of black tape like the
   sticker on the left (Astra, 2026-10-29: "the flip needs to go on the bottom ... the same style with the square tape ...
   above the title"); it stays showing on the back */
.polaroid .pflip { position: absolute; right: calc(var(--edge-w) - 4px); top: -11px; z-index: 4; display: grid; place-items: center; width: 30px; height: 26px; padding: 5px 7px; border: 0;
  background: #111; color: #fff; cursor: pointer; rotate: 4deg; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); visibility: visible; }
.polaroid .pflip svg { width: 100%; height: 100%; }
/* the map's link and the flip button side by side there */
.polaroid .stickers { position: absolute; right: calc(var(--edge-w) - 4px); top: -11px; z-index: 4; display: flex; align-items: center; gap: 6px; visibility: visible; }
.polaroid .stickers .look, .polaroid .stickers .pflip { position: static; }
.polaroid .frame.turned .stickers .look { visibility: hidden; }
.polaroid .pflip:hover, .polaroid .pflip:focus-visible { background: #000; box-shadow: 0 0 0 2px var(--c), 0 0 10px var(--c); outline: none; }
/* on a wide screen, the pet jar stands to the right of the PetOS Polaroids instead of under them (Astra, 2026-10-29) */
@media (min-width: 1500px) {
  #pets { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 48px; align-items: start; }
  #pets > :not(#petos-list):not(#pet-jar) { grid-column: 1 / -1; justify-self: start; }
  #pets > #petos-list { grid-column: 1; min-width: 0; justify-content: flex-start; }
  #pets > #pet-jar { grid-column: 2; }
}
/* the sound button for a video, in the picture's bottom right corner (Astra, 2026-10-29: "in the corner of the picture
   panel ... not above the text"); it lives in the writing strip under the picture, pulled up into the corner */
.polaroid .sound { position: absolute; right: calc(var(--edge-w) + 6px); top: calc(var(--edge-w) + 6px); z-index: 2; width: 30px; height: 26px; padding: 0; border: 0; cursor: pointer;
  background: #111; color: #fff; font: 14px/26px system-ui, sans-serif; rotate: 4deg; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); }
/* no colored square round it (Astra, 2026-10-29) */
.polaroid .sound:hover, .polaroid .sound:focus, .polaroid .sound:focus-visible { background: #000; outline: none; }
.polaroid .sound { -webkit-tap-highlight-color: transparent; }
.polaroid .sound[hidden] { display: none; }
.polaroid .photo:hover img { transform: scale(1.06); }
/* the galaxies only grow with the Polaroid, not a second time under the pointer (Astra, 2026-10-29) */
.polaroid .photo:hover .official-galaxy > img { transform: none; }
.polaroid div.photo { cursor: default; }
.photo .developing { font: 1.3rem/1 "Permanent Marker", cursive; color: color-mix(in srgb, var(--c) 55%, #fff); text-shadow: 0 0 12px var(--c); rotate: -7deg; }
/* the little arrows snug in the white border's bottom corners, as far from the side as from the bottom, and "1 of 3"
   between them on the same line (Astra, 2026-10-29) */
/* --lift-arrows: the marker arrow's ink sits low in its box, so the box rides higher to leave the same gap under the arrow as
   beside it (Astra, 2026-10-29) */
.polaroid .frame { --corner: 5px; --flip: 28px; --lift-arrows: 6px; }
.polaroid .frame.flips .note { padding-bottom: calc(var(--flip) + var(--corner) + var(--lift-arrows) + 2px); }
/* "1 of 3" level with the middle of the arrows either side of it (Astra, 2026-10-29), measured on a straightened Polaroid */
.polaroid .count { position: absolute; left: 50%; bottom: calc(var(--corner) + var(--lift-arrows)); height: var(--flip); display: grid; place-items: center; translate: -50% 6.5px;
  font: 13px/1 "Permanent Marker", cursive; color: #6a6a6a; white-space: nowrap; rotate: -2deg; }
.polaroid .flip { position: absolute; bottom: calc(var(--corner) + var(--lift-arrows)); width: var(--flip); height: var(--flip); display: grid; place-items: center; padding: 0; border: 0; background: none;
  color: #151515; font: 34px/1 "Permanent Marker", cursive; cursor: pointer; }
.polaroid .flip.prev { left: var(--corner); } .polaroid .flip.next { right: var(--corner); }
.polaroid .flip:hover, .polaroid .flip:focus-visible { color: #151515; text-shadow: 0 0 6px var(--c), 0 0 12px var(--c); outline: none; }
/* the writing strip runs the frame's full width, under the side strips, so the sides are one white all the way down */
.polaroid .note { display: grid; align-content: center; gap: 3px; min-height: 86px; margin: -1px calc(-1 * var(--edge-w)) 0; padding: 11px calc(6px + var(--edge-w)) 14px; color: #151515; background: #f2f2f2; }
.note .title { font: 1.35rem/1.05 "Permanent Marker", cursive; color: #151515; text-decoration: none; overflow-wrap: anywhere; rotate: -1.5deg; }
/* the name links to GitHub: on hover it glows in the section's color, the tape's, with no box behind it (Astra, 2026-10-29) */
.polaroid a.title:hover, .polaroid a.title:focus-visible { background: none; color: color-mix(in srgb, var(--c) 78%, #000); outline: none;
  text-shadow: 0 0 6px var(--c), 0 0 14px var(--c), 0 0 24px color-mix(in srgb, var(--c) 60%, transparent); }
.note .kind { font: .9rem/1.15 "Permanent Marker", cursive; color: #5a5a5a; rotate: 1deg; }
.polaroid .info p { margin: 18px auto 6px; max-width: 32ch; font-size: 14.5px; color: #c8c8c8; }
/* the repo and site tags, small stamps under the code and date */
.polaroid .tags { display: flex; justify-content: center; gap: 6px; margin-top: 3px; }
.polaroid .tags:empty { display: none; }
.polaroid .tags a { font: 13px/1.2 "Permanent Marker", cursive; color: #151515; text-decoration: none; rotate: -2deg; }
.polaroid .tags a:hover, .polaroid .tags a:focus-visible { text-shadow: 0 0 6px var(--c), 0 0 12px var(--c); outline: none; }

/* the pet jar: filled to the top with small stars, each holding its pet's icon; shaking it lets one out full size, tipping it
   out lays out all of them */
.jar-stage { position: relative; display: grid; grid-template-columns: minmax(260px, 360px) 1fr; gap: 28px 40px; align-items: center; min-height: 480px; }
.jar-all { grid-column: 1 / -1; }
/* the column of stars that tip out, narrower: 260px instead of 420, about three fifths as wide on a computer; a phone is already that narrow (Astra, 2026-10-29: "like 1/2 to 2/3") */
.jar-all .card, .jar-all .slot { max-width: 260px; }
.jar-side { display: grid; justify-items: center; gap: 12px; }
.jar-buttons { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
/* the jar's name, on its lid */
.jar-label { position: absolute; left: 50%; top: 7.9%; translate: -50% -50%; z-index: 1; font: 700 13px var(--mono); text-transform: uppercase; letter-spacing: .16em;
  background: #d8d8d8; color: #000; border: 2px solid #000; padding: 1px 12px; rotate: -3deg; pointer-events: none; }
.jar { position: relative; width: min(340px, 78vw); aspect-ratio: 300 / 380; padding: 0; border: 0; background: none; cursor: grab; touch-action: pan-y; transform-origin: 50% 92%; }
.jar:active { cursor: grabbing; }
.jar:focus-visible { outline: 2px dashed #8a8a8a; outline-offset: 8px; }
.jar > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
/* a prettier jar (Astra, 2026-10-29): tinted glass with a soft rim, and a screw lid sitting over the glass, not under it */
.jar-lid { fill: #3a3a3a; stroke: #0a0a0a; stroke-width: 4; }
.jar-lid-lines { stroke: #5c5c5c; stroke-width: 3; fill: none; }
.jar-lid-g { transform-box: fill-box; transform-origin: center; }
.jar-body { fill: url(#jar-tint); stroke: #cfcfcf; stroke-width: 4; stroke-opacity: .8; }
.jar-rim { stroke: rgba(255, 255, 255, .35); stroke-width: 6; stroke-linecap: round; fill: none; }
.jar-shine path { fill: none; stroke: rgba(255, 255, 255, .3); stroke-width: 7; stroke-linecap: round; }
.jar-shine path.dot { stroke: rgba(255, 255, 255, .22); stroke-width: 6; }
/* the inside of the glass, where the stars pile up (matches the body path in index.html) */
.jar .jar-glints { inset: auto; left: 11.4%; top: 25.6%; width: 77.2%; height: 70.6%; transition: opacity .2s; }
.jar.shaking .jar-glints { opacity: 0; }
.jar-stars { position: absolute; left: 11.4%; top: 25.6%; width: 77.2%; height: 70.6%; border-radius: 6% 6% 11% 11% / 4% 4% 9% 9%; overflow: hidden; }
.mini { position: absolute; width: var(--s); height: var(--s); transform: translate(-50%, -50%) rotate(var(--r)); }
.mini svg, .flyer svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
/* white edges, so the stars in the jar look brighter (Astra, 2026-10-29) */
.mini use, .flyer use { fill: var(--c); stroke: color-mix(in srgb, var(--c) 22%, #fff); stroke-width: 7; }
.mini svg, .flyer svg { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--c) 60%, #fff)) drop-shadow(0 0 7px var(--c)); }
/* sparkles round the outside of the jar too */
.jar-wrap { position: relative; display: block; }
/* over the pile of stars (the glass's inside, from a little above the stars to the bottom), spilling a little past its sides */
.jar-wrap > .sparkles.ring { inset: 22% 2% 0; }
.jar-wrap > .sparkles.ring.back { z-index: 0; inset: 18% -4% -2%; }
.mini img, .flyer img { position: absolute; left: 50%; top: 53%; width: 58%; height: 58%; translate: -50% -50%; object-fit: contain; }
.mini.gone { visibility: hidden; }
.jar-count { margin: 0; color: #8a8a8a; }
.jar-out { position: relative; display: grid; place-items: center; min-height: 440px; }
.jar-out .card { width: min(100%, 420px); animation: popout .55s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes popout { from { transform: scale(.2) rotate(-30deg); opacity: 0; } }
/* the hint sits over the jar now; the room to its right is where the jar falls */
.jar-hint { margin: 0 0 18px; font: 700 clamp(1rem, 2vw, 1.25rem)/1.3 var(--mono); color: #9a9a9a; text-align: center; }
.jar { z-index: 2; }
.flyer { position: absolute; z-index: 30; pointer-events: none; width: var(--s); height: var(--s); }
.flyer .sparkles { inset: -30%; }
/* the sparkles that shake off the falling star and trail it: the page's one pastel sparkle */
.jar-spark { position: absolute; z-index: 31; width: var(--s); aspect-ratio: 1; background: var(--c); pointer-events: none;
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); }
/* the lid's name label comes off with it, out of sight, while the jar lies on its side */
.jar-label { transition: opacity .3s; }
.jar.tipped .jar-label { opacity: 0; }
.jar-all[hidden] { display: none; }
/* the row of star slots under the jar: each shake fills the dashed one and a new one waits after it (Astra, 2026-10-29) */
.jar-all .slot { position: relative; width: 100%; max-width: 420px; aspect-ratio: 229 / 220; justify-self: center; margin-bottom: -5%; }
.jar-all .slot svg { position: absolute; inset: 6%; width: 88%; height: 88%; overflow: visible; }
.jar-all .slot use { fill: none; stroke: #5a5a5a; stroke-width: 5; stroke-dasharray: 18 12; }
.jar-all .card.landed { animation: land .45s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes land { from { transform: scale(.9); } }
/* the stars, more neon: each glows in its own color (Astra, 2026-10-29) */
.card .shape { filter: drop-shadow(0 0 6px var(--c)) drop-shadow(0 0 16px color-mix(in srgb, var(--c) 60%, transparent)); }

/* everything else: the README's groups, each a wall of Polaroids */
.ticker[hidden] { display: none; }

/* contact: two cards, one per name */
.contacts { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; }
.contact { background: #111; border: 3px solid #000; padding: 22px 24px; box-shadow: 10px 10px 0 #000; }
.contact:nth-child(2) { box-shadow: -10px 10px 0 #000; }
.contact b { display: block; font: 900 1.6rem/1.1 Inter, sans-serif; }
.contact .role { margin: 6px 0 4px; font: 700 14px var(--mono); color: #cfcfcf; }
.contact .muted { margin: 0 0 12px; }
.contact .list { margin-top: 10px; }
/* spritzed up (Astra, 2026-10-29): a neon edge across the top, the name glowing a little, what she does as neon chips */
/* Audra J. Volk in yellow, Astra in white (Astra, 2026-10-29) */
.contact { --n: #57e389; position: relative; border-top: 4px solid var(--n); }
.contact.vr { --n: #f4f4f4; }
.contact b { text-shadow: 0 0 14px color-mix(in srgb, var(--n) 55%, transparent); }
.contact .role { color: color-mix(in oklab, var(--n) 60%, #f2f2f2); }
.contact .pitch { margin: 10px 0 12px; color: #e6e6e6; line-height: 1.5; }
.contact .chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; padding: 0; list-style: none; }
.contact .chips li { font: 700 11px/1 var(--mono); letter-spacing: .04em; text-transform: lowercase; padding: 6px 9px; color: #f2f2f2; background: #000;
  border: 1.5px solid var(--n); border-radius: 99px; box-shadow: 0 0 8px color-mix(in srgb, var(--n) 45%, transparent); white-space: nowrap; }

/* feeds: her three feeds as Polaroids side by side, Instagram in the middle, stacked on a phone; each one post at a time,
   going on by itself (Astra, 2026-10-29). A TikTok is tall, so its Polaroid is a longer one; the TikToks white, Instagram dark */
.feeds { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 40px 34px; margin: 44px auto 0; }
.feed { position: relative; flex: 0 1 270px; width: min(100%, 270px); margin: 0; padding: 12px 12px 0; background: #f2f2f2; box-shadow: 8px 8px 0 #000; rotate: -1.5deg; }
.feed.instagram { background: #161616; box-shadow: 8px 8px 0 #000, 0 0 0 1px #3a3a3a; rotate: 1.5deg; }
.feed + .feed + .feed { rotate: 2deg; }
.feed .shot { position: relative; overflow: hidden; aspect-ratio: 9 / 16; background: #000; }
.feed.instagram .shot { aspect-ratio: 1; }
.feed .shot iframe, .feed .shot img { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: cover; }
/* TikTok's player shown whole, like TikTok: her name at the top, likes, comments and shares down the side (Astra, 2026-10-29:
   "it can appear like an actual TikTok video") */
.feed .shot iframe { pointer-events: auto; }
.feed .plays { position: absolute; left: 8px; bottom: 8px; z-index: 1; font: 700 11px/1 Inter, sans-serif; color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .8); pointer-events: none; }
/* the saves, which TikTok's player leaves out, in its bookmark above its heart */
.feed .saves { position: absolute; right: 0; bottom: var(--saves-y, 168px); z-index: 1; width: 44px; display: grid; justify-items: center; gap: 2px;
  font: 700 10px/1 Inter, sans-serif; color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); pointer-events: none; }
.feed .saves svg { width: 22px; height: 22px; fill: #fff; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4)); }
.feed [hidden] { display: none; }
/* the writing on the border is handwritten, like the other Polaroids (Astra, 2026-10-29: "it needs to be writing, not text"):
   her Instagram caption, then her name on the feed */
.feed figcaption { display: grid; justify-items: center; gap: 4px; padding: 12px 0 4px; text-align: center; }
.feed.instagram .feed-caption { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; height: 62px; overflow: hidden; }
.feed .feed-recovery { position: absolute; inset: auto 8px 12px; z-index: 3; padding: 14px; background: #111e; color: #f2f2f2; font: 12px/1.5 var(--mono); text-align: center; }
.feed .feed-recovery[data-mode="unavailable"] { inset: 0; display: grid; place-content: center; gap: 12px; padding: 24px; background: #101215; }
.feed .feed-recovery[hidden] { display: none; }
.feed-retry, .feed-watch { display: inline-block; padding: 7px 9px; margin: 4px; border: 1px solid #aaa; background: #222; color: #fff; font: inherit; text-decoration: none; cursor: pointer; }
.feed-retry:focus-visible, .feed-watch:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.feed .feed-caption { font: 18px/1.15 "Permanent Marker", cursive; color: #f2f2f2; rotate: -1.5deg; }
.feed .feed-handle { font: 1.15rem/1.1 "Permanent Marker", cursive; color: #151515; text-decoration: none; rotate: -1.5deg; }
.feed.instagram .feed-handle { font-size: 14px; color: #9a9a9a; }
/* the bottom row, as on the other Polaroids: the little marker arrows in the corners, "1 of 10" between them */
.feed-row { display: flex; align-items: center; justify-content: center; gap: 10px; height: 30px; margin: 0 -8px 8px; position: relative; }
.feed .flip { position: absolute; top: 0; width: 28px; height: 28px; display: grid; place-items: center; padding: 0; border: 0; background: none;
  color: #151515; font: 34px/1 "Permanent Marker", cursive; cursor: pointer; }
.feed .flip.prev { left: 0; } .feed .flip.next { right: 0; }
.feed.instagram .flip, .feed.instagram .count { color: #c8c8c8; }
.feed .flip:hover, .feed .flip:focus-visible { text-shadow: 0 0 6px #ff4fd8, 0 0 12px #ff4fd8; outline: none; }
.feed .count { font: 13px/1 "Permanent Marker", cursive; color: #6a6a6a; white-space: nowrap; rotate: -2deg; }
/* All sound stickers sit in the picture's top-right corner. TikToks start muted. */
.feed .shot .plays { left: 8px; bottom: 13px; }
.feed .sound { position: absolute; right: 6px; top: 6px; z-index: 4; width: 30px; height: 26px; padding: 0; border: 0; cursor: pointer; background: #111; color: #fff; font: 14px/26px system-ui, sans-serif;
  rotate: 4deg; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); -webkit-tap-highlight-color: transparent; }
.feed .sound:hover, .feed .sound:focus, .feed .sound:focus-visible { background: #000; outline: none; }
.feed.locked figcaption { padding-bottom: 16px; }
/* An unavailable TikTok preview links to the profile without guessing its account settings. */
.feed .feed-locked { position: absolute; inset: 0; display: grid; place-content: center; gap: 10px; padding: 20px; text-align: center;
  background: repeating-linear-gradient(135deg, #111 0 14px, #161616 14px 28px); color: #c8c8c8; text-decoration: none; font: 12px/1.4 var(--mono); }
.feed .feed-locked b { font: 22px/1.1 "Permanent Marker", cursive; color: #f2f2f2; }
.feed .feed-open { justify-self: center; padding: 6px 10px; border: 2px solid #f2f2f2; color: #f2f2f2; font-weight: 700; }

/* arcade: black cabinets on white scanlines */
.arcade { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 30px; }
/* a cabinet never wider than its column, so none runs off the paper on a phone (Astra, 2026-10-29) */
.cab { min-width: 0; }
.cab { --bg: #0c0c0c; --ink: #f2f2f2; --muted: #8c8c8c; background: var(--bg); color: var(--ink); border: 3px solid #000; padding: 20px; box-shadow: 10px 10px 0 #3a3a3a; }
.cab:nth-child(even) { box-shadow: -10px 10px 0 #3a3a3a; }
.cab h3 { margin-top: 0; }
.cab .how { margin: -6px 0 14px; font: 13px/1.4 var(--mono); color: #b8b8b8; }
.cab .hot { color: #fff; letter-spacing: .1em; }
.cab .btn.hot { color: var(--bg); letter-spacing: 0; }
.cab .btn.hot:hover, .cab .btn.hot:focus-visible { color: var(--ink); }
/* Hack the terminal: inside a terminal window, its words spread out with a > prompt each */
/* drawn like the tool panels on the pet program pages: a thin square border with the panel's name set into its top edge,
   three lights to the left of the name (Astra, 2026-10-27) */
.hack-term { position: relative; flex: 1; display: flex; flex-direction: column; margin-top: 10px; border: 1px solid #5a5a5a; background: #050505; }
/* its bar is the terminal's own (Astra, 2026-10-29: "if it's going to be a tool panel ... it needs to have the buttons"): the
   striped bar with the paired lights left of its name, which stays on one line */
.hack-term .term-bar { white-space: nowrap; overflow: hidden; }
/* and matching the hero terminal, a Linux one (Astra, 2026-10-29): the flat dark bar, its name in the middle */
.hack-term > .term-bar { position: relative; padding: 7px 10px; background: #2d2d2d; border-bottom: 1px solid #111; color: #e6e6e6; }
.hack-term > .term-bar > span:not(.lights) { position: absolute; left: 50%; translate: -50% 0; max-width: calc(100% - 84px); margin: 0; padding: 0; background: none; color: #e6e6e6; font-weight: 700; pointer-events: none; }
.hack-term .term-bar > span:not(.lights) { overflow: hidden; text-overflow: ellipsis; }
.hack-term.is-off #hack { opacity: .08; pointer-events: none; transition: opacity .3s; }
#hack { flex: 1; display: flex; flex-direction: column; justify-content: space-evenly; padding: 12px 16px; background: repeating-linear-gradient(transparent 0 2px, rgba(255, 255, 255, .035) 2px 3px); }
#hack > p { margin: 4px 0; font: 13px var(--mono); }
.words { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 12px; margin: 14px 0; font: 15px var(--mono); }
.words button { font: inherit; text-align: left; background: transparent; color: var(--ink); border: 0; padding: 3px 6px; cursor: pointer; }
.words button:hover { background: var(--ink); color: var(--bg); }
.words button[disabled] { color: #5e5e5e; text-decoration: line-through; background: transparent; cursor: default; }
.hacklog p { margin: 2px 0; font: 13px var(--mono); }
.hacklog .granted { color: #fff; font-weight: 700; }
.hacklog .denied { color: #8a8a8a; }
.hacklog .locked { display: table; background: #f2f2f2; color: #000; padding: 0 6px; }
.board { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; margin: 10px 0 16px; }
.tile { aspect-ratio: 1; perspective: 600px; }
.tile button { position: relative; width: 100%; height: 100%; padding: 0; border: 0; background: none; cursor: pointer; transform-style: preserve-3d; transition: transform .35s; }
.tile.open button, .tile.done button { transform: rotateY(180deg); }
.tile .f { position: absolute; inset: 0; backface-visibility: hidden; display: grid; place-items: center; }
.tile .f1 { background: repeating-conic-gradient(#1b1b1b 0 25%, #2b2b2b 0 50%) 0 0 / 12px 12px; border: 1px solid #4a4a4a; color: #c8c8c8; font: 18px var(--mono); }
.tile .f2 { background: #e9e9e9; transform: rotateY(180deg); }
.tile .f2 img { width: 78%; height: 78%; object-fit: contain; object-position: 50% 50%; }
.tile.done .f2 { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #000; }
/* the newer games (games-20261027.js): gray cabinets; the Fuzzbois, sparkles and avatars inside keep their own colors */
/* Keep controls close to their game, without stretching shorter cabinets to their neighbor. */
.cab.spread { display: flex; flex-direction: column; justify-content: flex-start; gap: 14px; }
.cab.spread > * { margin-block: 0; }
.fz-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; margin: 12px 0; }
.fz-buttons { justify-content: center; }
/* the Fuzzboi Factory's six big code boxes, each with what it decides under it */
.fz-code { --fzh: 56px; display: flex; align-items: flex-start; gap: 8px; }
/* the # as tall as the number boxes and centered on them, not on the boxes and their labels together (Astra, 2026-10-29) */
.fz-hash { display: flex; align-items: center; height: var(--fzh); font: 700 40px/1 var(--mono); color: #8a8a8a; }
.fz-digits { flex: 1; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; min-width: 0; }
.fz-digit { display: grid; gap: 4px; justify-items: center; }
.fz-digit input { width: 100%; height: var(--fzh); min-width: 0; font: 700 30px var(--mono); text-align: center; text-transform: uppercase; padding: 8px 0; color: #f2f2f2; background: #000;
  border: 2px solid #5c5c5c; caret-color: #f2f2f2; }
.fz-digit input:focus { outline: none; border-color: #f2f2f2; }
.fz-digit span { font: 11px var(--mono); color: #8a8a8a; white-space: nowrap; }
.fz-out { display: grid; justify-items: center; gap: 14px; }
/* each trait's name beside its value, one per row */
.fz-traits { width: 100%; display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; align-items: center; }
#fz-cv { width: min(100%, 300px); aspect-ratio: 1; border: 2px solid #000; box-shadow: 6px 6px 0 #3a3a3a; background: #222; }
.fz-traits { margin: 0; font: 13px/1.4 var(--mono); }
.fz-traits dt { color: #8a8a8a; text-transform: uppercase; letter-spacing: .1em; font-size: 11px; margin-top: 0; }
.fz-traits dd { margin: 0; color: #f2f2f2; display: flex; align-items: center; gap: 6px; }
.fz-traits dd i { width: 14px; height: 14px; border: 2px solid #f2f2f2; }
#fz-note, #home-pick, #home-count { min-height: 1.4em; font-size: 12px; }
/* the traits sit as wide as the picture, lined up with it, a little apart from it and smaller; the long rarity note under
   them is gone (Astra, 2026-10-29: "that big bulky paragraph ... just get rid of that") */
.fz-out { gap: 20px; }
.fz-traits { width: min(100%, 300px); font-size: 11.5px; gap: 3px 12px; }
.fz-traits dt { font-size: 10px; }
#fz-note { display: none; }
.home { display: block; width: 100%; aspect-ratio: 1; cursor: pointer; touch-action: manipulation; }
.home.mending { touch-action: none; }
/* the Fuzzbois let out of the dome, loose over the whole screen; clicks go through to the page */
.fuzz-loose { position: absolute; left: 0; top: 0; width: 100%; z-index: 55; pointer-events: none; overflow: hidden; }
.fuzz-loose .fuzz { position: absolute; left: 0; top: 0; pointer-events: auto; cursor: grab; touch-action: none; will-change: transform; }
.fuzz-loose .fuzz.held { cursor: grabbing; filter: drop-shadow(0 0 10px hsl(320 55% 80%)); }
/* the pastel sparkles a loose Fuzzboi leaves behind: they twinkle once where they fell and fade */
.fuzz-trail { position: absolute; width: var(--s); aspect-ratio: 1; translate: -50% -50%; background: var(--c); pointer-events: none;
  clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); animation: trail-fade 3.6s ease-out forwards; }
@keyframes trail-fade { 0% { transform: scale(.3) rotate(0); opacity: 0; } 15% { transform: scale(1.1) rotate(30deg); opacity: 1; } 100% { transform: scale(.4) rotate(120deg); opacity: 0; } }
/* the Avatar Lab's girl, happy the loose Fuzzbois came to hang out with her: she hops and blinks (Astra, 2026-10-29) */
#labSvg.happy { animation: lab-hop .8s ease-in-out infinite; }
@keyframes lab-hop { 0%, 100% { translate: 0 0; } 35% { translate: 0 -14px; } 55% { translate: 0 0; } 72% { translate: 0 -5px; } }
#labSvg.happy g[fill="#2a1330"], #labSvg.happy g[fill="#fff"] { transform-box: fill-box; transform-origin: center; animation: lab-blink 1.9s infinite; }
@keyframes lab-blink { 0%, 84%, 100% { transform: scaleY(1); } 90% { transform: scaleY(.08); } }
@media (prefers-reduced-motion: reduce) { #labSvg.happy, #labSvg.happy g { animation: none; } }
/* how wild the dome is, it shows itself: games-20261027.js sets its rainbow glow and its shake as it fills (Astra, 2026-10-29) */
/* the counter under the dome: how many are in, how many more until it breaks, and a bar filling toward 67 */
.dome-meter { display: grid; gap: 10px; }
.dome-meter .row { --fill: 0; display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 4px 10px; font: 12px/1.3 var(--mono); color: #9a9a9a; }
.dome-meter b { font: 800 1.5rem/1 var(--mono); color: #f2f2f2; min-width: 2.2ch; }
.dome-meter i { grid-column: 1 / -1; height: 8px; border: 1px solid #555; background: linear-gradient(90deg, #39ff14, #fff01f 50%, #ff2e4d) 0 0 / 100% 100%;
  clip-path: inset(0 calc((1 - var(--fill)) * 100%) 0 0); transition: clip-path .4s; }
/* the sparkle meter fills in the page's pastels */
.dome-meter .spark i { background: linear-gradient(90deg, hsl(320 55% 80%), hsl(200 55% 80%), hsl(140 55% 80%), hsl(50 55% 80%), hsl(0 55% 80%)); }
.dome-score { margin: 0; font-size: 12px; color: #fff36b; text-shadow: 0 0 8px rgba(255, 243, 107, .4); }
.dome-meter .row.hot b { color: #ff5a6e; text-shadow: 0 0 10px rgba(255, 40, 60, .7); }
/* the dome's count sits right under its name */
.cab-head { display: grid; gap: 6px; }
.cab-head h3 { margin: 0; justify-self: start; }
.screen { position: relative; aspect-ratio: 4 / 3; border: 2px solid #000; box-shadow: 6px 6px 0 #3a3a3a; background: #060606; touch-action: manipulation; }
.screen canvas { display: block; width: 100%; height: 100%; }
.hud { display: flex; justify-content: space-between; margin: 12px 0; color: #8a8a8a; }
.hud b { color: #f2f2f2; }
/* the Sparkle Creator under the sparkle catcher */
.spark-lab { margin-top: 18px; padding-top: 14px; border-top: 2px dashed #3a3a3a; }
.spark-lab h4 { margin: 0 0 10px; font: 700 13px var(--mono); text-transform: uppercase; letter-spacing: .12em; color: #f2f2f2; }
.spark-row { display: grid; grid-template-columns: 112px 1fr; gap: 14px; align-items: center; }
#sparkPrev { width: 112px; height: 112px; border: 2px solid #000; box-shadow: 4px 4px 0 #3a3a3a; }
.spark-row .labrow { margin-top: 0; }
#sparkRand { margin-top: 12px; }
/* she stands a little lower, down on her pink shadow in the middle of the band (Astra, 2026-10-29) */
.labstage { display: flex; justify-content: center; padding: 22px 0 0; background: radial-gradient(circle at 50% 78%, #2e2e2e, transparent 66%); }
.labrow { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }
.lab-b { font: 700 12px var(--mono); text-align: left; color: #f2f2f2; background: #000; border: 2px solid #555; padding: 7px 9px; cursor: pointer; }
.lab-b:hover, .lab-b:focus-visible { border-color: #f2f2f2; outline: none; }
.lab-b small { display: block; font-weight: 400; font-size: 11px; color: #9b9b9b; margin-top: 2px; }
.lab-group { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 4px; font: 700 12px var(--mono); color: #f2f2f2; }
.lab-group select { font: 12px var(--mono); color: #f2f2f2; background: #000; border: 2px solid #555; padding: 6px; }
.lab-name { margin: 8px 0 0; font: 700 17px var(--mono); color: #f2f2f2; min-height: 1.4em; }
/* the Sparkle catcher's five best sets (Astra, 2026-10-29) */
.hiscores { margin: 0 0 12px; }
.hiscores h4 { margin: 0 0 6px; font: 700 11px var(--mono); letter-spacing: .12em; text-transform: uppercase; color: #8a8a8a; }
.hiscores ol { margin: 0; padding: 0 0 0 22px; color: #8a8a8a; font-size: 13px; }
.hiscores li { display: flex; justify-content: space-between; gap: 12px; padding: 2px 0; }
.hiscores li b { color: #f2f2f2; }
.hiscores li.new b { color: #ff2e7e; }
.hiscores li.none { list-style: none; margin-left: -22px; }
/* the avatar library: every saved look, small, in a grid; tap to wear it, ✕ to let it go */
.lab-lib { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 10px; margin-top: 12px; }
.lab-lib[hidden] { display: none; }
.lab-lib > p { grid-column: 1 / -1; margin: 0; }
.lib-look { position: relative; }
.lib-wear { display: grid; justify-items: center; gap: 4px; width: 100%; padding: 6px 4px 8px; border: 1px solid #3a3a3a; background: #0c0c0c; color: #f2f2f2; cursor: pointer; font: 11px/1.2 var(--mono); }
.lib-wear:hover, .lib-wear:focus-visible { border-color: #ff2e7e; outline: none; }
.lib-wear svg { width: 72px; height: 83px; }
.lib-drop { position: absolute; top: 2px; right: 2px; width: 22px; height: 22px; padding: 0; border: 0; background: transparent; color: #8a8a8a; cursor: pointer; font-size: 12px; }
.lib-drop:hover, .lib-drop:focus-visible { color: #ff2e7e; }
#labSvg { stroke-linecap: round; stroke-linejoin: round; max-width: 100%; height: auto; }
.avatar-sparkles { animation: lab-shimmer 4s ease-in-out infinite alternate; transform-box: fill-box; transform-origin: center; }
@keyframes lab-shimmer { from { opacity: .55; } to { opacity: 1; } }

/* lightbox */
.lightbox { border: 2px solid #f2f2f2; background: rgba(0, 0, 0, .96); color: #f2f2f2; padding: 0; max-width: min(1100px, 96vw); width: 96vw; border-radius: 0; box-shadow: 12px 12px 0 #555; }
.lightbox::backdrop { background: rgba(0, 0, 0, .78); backdrop-filter: blur(3px) grayscale(1); }
.lightbox figure { margin: 0; padding: 44px 16px 18px; }
.lightbox img { margin: 0 auto; max-height: 76vh; object-fit: contain; }
.lightbox.mono img { filter: grayscale(1) contrast(1.2); }
/* a live page's own link (the System Map), a sticker on the white border at the top right, tipped like the sound sticker */
.polaroid .look { position: absolute; right: calc(var(--edge-w) - 4px); top: -10px; z-index: 4; padding: 5px 8px; border: 0; background: #111; color: #fff; text-decoration: none;
  font: 11px/1 "Permanent Marker", cursive; rotate: 4deg; box-shadow: 2px 2px 0 rgba(0, 0, 0, .35); }
.polaroid .look:hover, .polaroid .look:focus-visible { background: #111; color: #fff; outline: 2px solid var(--c); }
/* the Rainbow Maker's turning video with its gray cut away, drawn frame by frame on a canvas */
.polaroid .photo .keyed { position: absolute; inset: 0; }
.polaroid .photo .keyed video { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.polaroid .photo .keyed canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
/* an SVG drawn into the page (the Photostar): it fills the photo; its neon edge runs round the colors */
.polaroid .photo .inline-svg { position: absolute; inset: 0; display: grid; place-items: center; }
.polaroid .photo .inline-svg svg, .polaroid .photo .inline-svg img { width: 100%; height: 100%; }
.rgb-edge { animation: rgb-edge 5s linear infinite; }
@keyframes rgb-edge { to { filter: hue-rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .rgb-edge { animation: none; } }
/* the sparkles the Rainbow Maker throws out over the whole page */
.rainbow-spray { position: fixed; inset: 0; z-index: 54; width: 100vw; height: 100vh; pointer-events: none; }
.lightbox figcaption { text-align: center; font: 13px var(--mono); color: #9b9b9b; margin: 10px 52px 0; min-height: 26px; }
.lb-close { position: absolute; top: 8px; right: 10px; }
/* the arrows sit in the bottom corners, either side of the caption (Astra, 2026-10-28) */
.lb-nav { position: absolute; bottom: 6px; font-size: 34px; width: 44px; height: 44px; line-height: 1; }
.lb-nav.prev { left: 6px; } .lb-nav.next { right: 6px; }
.lightbox button { background: transparent; color: #f2f2f2; border: 0; cursor: pointer; }
.lightbox button:hover { background: #f2f2f2; color: #000; }

/* as much room below the footer's line as above it (Astra, 2026-10-29) */
/* slimmer, the bottom trimmed more than the top (Astra, 2026-10-29); the top's 10px is the checker strip */
.foot { position: relative; padding: 24px var(--wrap) 12px; background: #000; color: var(--ink); text-align: center; font-size: 14px; }
.foot::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 10px; background: repeating-conic-gradient(#4a4a4a 0 25%, #000 0 50%) 0 0 / 10px 10px; }
/* the unified footer, as on the pet program pages: centered, its groups after a pink ✦, no list */
.astra-footer .footer-line { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0 18px; }
.astra-footer .footer-group { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0 10px; }
.footer-group::before, .footer-sparkle { content: "✦"; color: #d994c6; font-size: 12px; line-height: 1; margin-right: 8px; }
.footer-by::before { content: none; }
.footer-sparkle { margin: 0 -8px 0 0; }
.footer-title { font: 600 14px var(--mono); letter-spacing: .02em; }
.footer-icons { display: inline-flex; gap: 4px; }
.footer-icons a { display: inline-grid; place-items: center; width: 32px; height: 32px; color: var(--ink); text-decoration: none; }
.footer-icons a:hover, .footer-icons a:focus-visible { background: rgba(255, 255, 255, .12); }
.footer-icons svg { width: 22px; height: 22px; fill: currentColor; }
.footer-star { display: block; line-height: 0; }
.footer-star:hover { background: none; }
.footer-star img { width: 48px; height: 48px; }

@media (max-width: 900px) {
  .hero { grid-template-columns: var(--sq); gap: 32px; }
  .arcade, .contacts { grid-template-columns: minmax(0, 1fr); }
  .jar-stage { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .jar-out { display: none; }
  /* the holder takes the width and the jar fills it: a jar 100% of a holder that shrinks to the jar came out 0 wide, so the
     jar was gone on phones (Astra, 2026-10-29) */
  .jar-wrap { width: min(340px, 100%); }
  .jar { width: 100%; }
  .jar-out { min-height: 380px; }
}
@media (max-width: 520px) {
  body { font-size: 16px; }
  :root { --rail: 36px; }
  .floors a { font-size: clamp(8px, calc((100vh - 230px) / 28), 13px); letter-spacing: .08em; }
  .block { --sheet: 28px; }
  /* the terminal on a phone: everything in it a size smaller, so it still shows its words above the buttons, like on a
     computer (Astra, 2026-10-29: "everything should look pretty much the same") */
  .term-bar { padding: 5px 8px; font-size: 10px; }
  .term-body { padding: 8px 10px; font-size: 10.5px; line-height: 1.45; }
  .term-input { padding: 2px 10px 6px; font-size: 10.5px; }
  .term-actions { gap: 6px; padding: 6px 10px 10px; }
  .term-actions .btn { font-size: 10.5px; padding: 4px 8px; box-shadow: 3px 3px 0 var(--muted); }
  /* the three buttons on one row, so more of the words above show (Astra, 2026-10-29) */
  .term-actions { flex-wrap: nowrap; gap: 5px; padding: 6px 8px 10px; }
  .term-actions .btn { flex: 0 1 auto; min-width: 0; white-space: nowrap; padding: 4px 5px; font-size: 9.5px; }
  .board { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* the Sparkle creator on a phone: the sparkle on top, its four squares in a full-width two by two below, so the words fit
     (Astra, 2026-10-29: "those squares look a little weird here on the phone") */
  .spark-row { grid-template-columns: 1fr; justify-items: center; }
  .spark-row .labrow { width: 100%; }
  .lab-b { min-width: 0; overflow-wrap: anywhere; }
  /* the Avatar lab too: the hairstyle collection across the whole row so its choice isn't cut off, and randomize, save and
     library side by side */
  .lab-group { grid-column: 1 / -1; }
  /* the jar's two lines stay two lines on a phone (Astra, 2026-10-29) */
  .jar-hint { font-size: 12.5px; }
  /* the footer on a phone, never more than two rows (Astra, 2026-10-29): By Astra ✦ star ✦ Find Out More on the first, all
     the links on the second, and less room above the star; the two rows as close together as the sparkles are to the star
     (Astra, 2026-10-29), the icons' own padding counted in */
  .foot { padding-top: 16px; }
  .astra-footer .footer-line { flex-wrap: wrap; gap: 0 8px; }
  .astra-footer .footer-icons { margin-top: -3px; }
  .astra-footer .footer-more { display: contents; }
  .astra-footer .footer-title { font-size: 12px; white-space: nowrap; }
  .astra-footer .footer-icons { flex: 0 0 100%; justify-content: center; }
  .footer-group::before, .footer-sparkle { margin-right: 0; }
  .footer-sparkle { margin: 0; }
  .footer-star img { width: 40px; height: 40px; }
  .lab-group select { width: 100%; }
  #labRand, #labSave, #labLibBtn, #labPng { padding: 6px 7px; font-size: 10.5px; }
  #labRand { order: 0; }
  /* four buttons now (save png too), two by two on a phone */
  .cab .fz-row:has(#labSave) { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .fz-digit input { font-size: 22px; }
  /* on a phone a smaller #, and the words under the numbers small enough to fit under each one (Astra, 2026-10-29) */
  .fz-code { --fzh: 44px; gap: 6px; }
  .fz-hash { font-size: 28px; }
  .fz-digits { gap: 4px; }
  .fz-digit span { font-size: 8.5px; letter-spacing: -.02em; }
  .lightbox figure { padding: 44px 12px 12px; }
  .grid { grid-template-columns: 1fr; }
  .polaroids { gap: 44px 20px; }
  .polaroid .frame { --edge-w: 8px; }
  .note .title { font-size: 1.05rem; }
  .note .kind { font-size: .75rem; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .letter-ink, .glitch, .ticker .track, .jar-out .card, .sparkles i, .portrait .glitter, .polaroid .photo img { animation: none; }
  .ticker { overflow-x: auto; }
  .card, .card .turn, .card .s-shadow, .btn, .polaroid .frame, .polaroid .photo img, .tile button, .letter-track { transition: none; }
  .sparkles { animation: none; }
  #rain { display: none; }
}

/* GitHub totals and contribution chart, green at Astra's request (2026-10-30). */
.gh { margin-top: 34px; background: #101a14; border: 3px solid #000; border-top: 4px solid #57e389; padding: 20px 24px 16px; box-shadow: 10px 10px 0 #000; }
.gh h3 { margin-top: 0; }
.gh-tiles { list-style: none; display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin: 6px 0 18px; padding: 0; }
.gh-tiles li { border: 2px solid var(--line, #000); background: #1a1a1a; padding: 8px 10px; }
.gh-tiles b { display: block; font: 900 1.6rem/1.1 Inter, sans-serif; color: #8cf2ad; }
.gh-tiles span { display: block; font: 12px/1.3 var(--mono); color: #9a9a9a; }
.gh-chart { overflow-x: auto; }
.gh-chart svg { display: block; width: 100%; min-width: 640px; height: auto; }
.gh-lab { font: 9px var(--mono); fill: #8a8a8a; }
.gh-l0 { fill: #1e2922; background: #1e2922; } .gh-l1 { fill: #0e4429; background: #0e4429; } .gh-l2 { fill: #006d32; background: #006d32; }
.gh-l3 { fill: #26a641; background: #26a641; } .gh-l4 { fill: #39d353; background: #39d353; }
.gh-chart rect.gh-l0 { stroke: #2c2c2c; stroke-width: 1; }
.gh-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin: 10px 0 0; font: 12px var(--mono); color: #9a9a9a; }
.gh-legend { display: inline-flex; align-items: center; gap: 3px; margin-left: auto; }
.gh-legend i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
@media (max-width: 900px) { .gh-tiles { grid-template-columns: repeat(3, 1fr); } .gh-legend { margin-left: 0; } }
@media (max-width: 480px) { .gh { padding: 16px; } .gh-tiles { grid-template-columns: repeat(2, 1fr); } .gh-tiles li:last-child { grid-column: span 2; } }
