/* Prototype for extra.rexchekal.com. Spacing and the ink come from rexchekal.com's tokens
   (vendor/rexchekal/tokens.css, copied by scripts/sync-tokens.py). This file is every style on the site. Its :root sets the ground,
   a ten-step gray ramp and the color roles built on it, the faces, the frame's own sizes and motion, and the confetti colors.
   Type is eight named styles (H1 to H4, body large, body, body small, label), each listing everything that wears it.
   Black and white: the tokens' ink on a true white ground (Rex, 2026-10-24); the only color is the parade itself, plus the green motion switch.
   The wordmark is Rex's own drawing (extra-extra.svg); the FREE burst in the ticker is free.svg. */
:root {
  --ground: #FFFFFF;
  /* Grays, 100 to 1000, the ink mixed into the ground (Rex, 2026-10-24: the grays need real steps). Uses follow Geist's scale:
     100 to 300 backgrounds (rest, hover, pressed), 400 to 600 borders, 700 to 800 strong fills, 900 secondary text, 1000 the ink. */
  --gray-100: color-mix(in srgb, var(--ink) 5%, var(--ground));
  --gray-200: color-mix(in srgb, var(--ink) 9%, var(--ground));
  --gray-300: color-mix(in srgb, var(--ink) 13%, var(--ground));
  --gray-400: color-mix(in srgb, var(--ink) 18%, var(--ground));
  --gray-500: color-mix(in srgb, var(--ink) 26%, var(--ground));
  --gray-600: color-mix(in srgb, var(--ink) 38%, var(--ground));
  --gray-700: color-mix(in srgb, var(--ink) 50%, var(--ground));
  --gray-800: color-mix(in srgb, var(--ink) 58%, var(--ground));
  --gray-900: color-mix(in srgb, var(--ink) 68%, var(--ground));
  --gray-1000: var(--ink);
  /* The frame's only colors, both on the footer's motion switch (Rex, 2026-10-24): green when motion is on, red when it is off.
     The red is the FREE burst's #F31313 a step darker, so 12px text clears 4.5:1. Green 5.5:1, red 5.2:1 on the white ground. */
  --green: #0A7A35; --red: #DA1111;
  /* The tokens' roles, pointed at the ramp. */
  --panel: var(--gray-100); --ghost: var(--gray-200); --rule: var(--gray-400); --muted: var(--gray-900);
  --text: 'Geist', system-ui, sans-serif; --display: var(--text); --mono: 'Geist Mono', ui-monospace, monospace;
  --rail: 56px; --side: min(400px, 32vw); --foot: 48px;
  /* Frame motion: everything that moves in the frame uses these. The stage's art keeps its own timing (docs/DESIGN_SYSTEM.md). */
  --dur-move: 450ms; --ease-move: cubic-bezier(.2,.7,.2,1);
  /* Confetti, the stage's only color besides Rex's art: canvas-confetti's traditional colors. index.html and system.html read this list. */
  --confetti: #26ccff, #a25afd, #ff5e7e, #88ff5a, #fcff42, #ffa62d, #ff36ff;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; overflow: hidden; }
body { background: var(--ground); color: var(--ink); font-family: var(--text); }
a { color: var(--ink); }
a:hover { color: var(--muted); }
a:focus-visible, button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Type. Each style lists every place on the site that wears it; a new place joins a list rather than setting its own size. */
.t-h1, .about-take h2 { font-family: var(--display); font-weight: 400; font-size: 64px; line-height: 1; letter-spacing: -0.03em; margin: 0; }
.t-h2, .now, .app-take h2 { font-family: var(--display); font-weight: 400; font-size: 48px; line-height: 1.05; letter-spacing: -0.02em; margin: 0; }
.t-h3 { font-family: var(--display); font-weight: 400; font-size: 32px; line-height: 1.15; letter-spacing: -0.015em; margin: 0; }
.t-h4 { font-family: var(--display); font-weight: 400; font-size: 24px; line-height: 1.25; letter-spacing: -0.01em; margin: 0; }
.t-body-lg, .intro, .take p, .take .go, .about-take .links { font-size: 18px; line-height: 1.5; letter-spacing: 0; }
.t-body, .desc { font-size: 16px; line-height: 1.5; letter-spacing: 0; }
.t-body-sm { font-size: 14px; line-height: 1.45; letter-spacing: 0; }
.t-label, .label, .ticker-run span, .rail-run > span, .foot, .foot .motion { font-family: var(--mono); font-size: 12px; line-height: 1.4; letter-spacing: .06em; text-transform: uppercase; }

.frame { position: relative; display: grid; grid-template-columns: var(--side) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) var(--foot); height: 100vh; transition: grid-template-columns var(--dur-move) var(--ease-move); }
body.collapsed .frame { grid-template-columns: var(--rail) minmax(0, 1fr); }

/* The side column, top to bottom: an animation slot, the wordmark, the ticker, the intro, another animation slot. */
.side { position: relative; border-right: 1px solid var(--rule); display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.mark { flex: 1 1 auto; min-height: 120px; margin: 0; padding: var(--space-4); border-top: 1px solid var(--rule); }
/* The wordmark stretches to fill its box both ways at every size, as Hey News's does (Rex, 2026-10-24). */
.mark img { display: block; width: 100%; height: 100%; object-fit: fill; }
/* Grey slots where Rex's animations will go; placeholders until the art exists. */
.slot { flex: none; height: 56px; background: var(--panel); background-image: radial-gradient(var(--rule) 1.5px, transparent 1.6px); background-size: 24px 24px; background-position: 12px 12px; }
.slot-low { flex: 0 1 120px; min-height: 0; border-top: 1px solid var(--rule); }
/* The ticker: "Apps and agents" and the FREE burst, crawling left forever. */
.ticker { flex: none; height: 48px; border-top: 1px solid var(--rule); overflow: hidden; display: flex; align-items: center; }
.ticker-run { display: flex; align-items: center; flex: none; animation: crawl 30s linear infinite; }
.ticker-run span { flex: none; display: flex; align-items: center; gap: var(--space-3); padding-right: var(--space-3); white-space: nowrap; }
.ticker-run img { display: block; height: 28px; width: auto; }
@keyframes crawl { to { transform: translateX(-50%); } }
body.collapsed .ticker-run { animation-play-state: paused; } /* folded into the rail, nobody can see it */
body.still .ticker-run { animation-play-state: paused; } /* motion off: the footer toggle or the reduced-motion setting */
.intro { flex: none; margin: 0; padding: var(--space-4); border-top: 1px solid var(--rule); }
.intro p { margin: 0; }
.intro p + p { margin-top: var(--space-3); }
/* The column's contents keep their full width while it folds or opens, so they clip instead of re-wrapping word by word. */
.side > :not(.rail) { width: var(--side); }


/* Collapsed, the side column is a rail: the ticker turned on its side, read bottom to top and crawling up.
   Clicking it opens the column again. */
.rail { all: unset; cursor: pointer; display: none; position: absolute; inset: 0; flex-direction: column; align-items: center; overflow: hidden; }
/* The arrow square at the top of the rail says the column opens again; it lines up with the animation slot above the open column. */
.rail-open { flex: none; align-self: stretch; height: 56px; display: grid; place-items: center; border-bottom: 1px solid var(--rule); color: var(--ink); }
.rail-open svg { width: 20px; height: 20px; display: block; }
.rail:hover .rail-open { background: var(--ghost); }
.rail-tick { flex: 1 1 auto; min-height: 0; writing-mode: vertical-rl; transform: rotate(180deg); overflow: hidden; display: flex; }
.rail-run { display: flex; flex: none; animation: crawl-v 30s linear infinite; }
@keyframes crawl-v { from { transform: translateY(-50%); } to { transform: translateY(0); } } /* crawls up the rail */
.rail-run > span { flex: none; display: flex; align-items: center; gap: var(--space-3); padding-inline-end: var(--space-3); white-space: nowrap; }
/* The FREE burst turns with the words: a box as long as the art is wide, with the art rotated inside it. */
.rail-run .burst { position: relative; flex: none; inline-size: 48px; block-size: 28px; }
.rail-run .burst img { position: absolute; left: 50%; top: 50%; height: 28px; width: auto; transform: translate(-50%, -50%) rotate(90deg); }
body.still .rail-run { animation-play-state: paused; }
body.collapsed .side > :not(.rail) { visibility: hidden; }
body.collapsed .mark { visibility: visible; opacity: 0; } /* unseen, but the page keeps its heading for screen readers */
body.collapsed .rail { display: flex; }

/* The parade area: open, no grid, so each set (parade today, others later) can use it its own way. */
/* clip, not hidden: a hidden box can still be scrolled, and focusing an off-screen balloon used to slide the whole stage,
   takeovers included, out of view. The script brings a focused balloon into view itself. */
.show { position: relative; min-width: 0; overflow: hidden; overflow: clip; }
.set { position: absolute; left: var(--space-4); top: var(--space-4); z-index: 2; display: flex; flex-direction: column; gap: var(--space-2); pointer-events: none; }
/* The hovered balloon's name shows up top. Touch screens have no hover, so there each balloon wears its name. */
.now { min-height: 1em; }
.desc { max-width: 44ch; color: var(--muted); } /* the hovered app's one-line description */
.tag { display: none; position: absolute; left: 0; right: 0; top: var(--space-1); text-align: center; z-index: 1; }
@media (hover: none) { .tag { display: block; } .now, .desc { display: none; } }

.track { position: absolute; left: 0; bottom: 0; display: flex; align-items: flex-end; will-change: transform; }
/* Each box crops only its bottom edge (the strings); the top and sides stay open so a bob or tilt never clips the art. */
.balloon { all: unset; position: relative; flex: none; clip-path: inset(-100vh -50vw 0 -50vw); cursor: pointer; }
.balloon:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }
.bob { position: absolute; inset: 0; transform-origin: 50% 20%; animation: bob var(--d, 5s) ease-in-out var(--delay, 0s) infinite alternate; }
.bob img { position: absolute; display: block; max-width: none; pointer-events: none; user-select: none; }
.mirror { transform: scaleX(-1); }
@keyframes bob { from { transform: translateY(0) rotate(-1.4deg); } to { transform: translateY(-1.6%) rotate(1.4deg); } }

/* Takeovers fill the parade area: an app's own page, or the about. Each has its own address. */
.take { position: absolute; inset: 0; z-index: 5; background: var(--ground); display: none; overflow: auto; } /* scrolls on short screens */
.take.on { display: grid; }
.take:focus { outline: none; }
.take .close { all: unset; position: absolute; top: var(--space-3); right: var(--space-3); z-index: 2; width: 64px; height: 64px; display: grid; place-items: center; border-radius: var(--space-2); cursor: pointer; color: var(--ink); }
.take .close:hover { background: var(--ghost); }
  .take .close svg { width: 36px; height: 36px; }
.app-take { grid-template-columns: minmax(0, 1fr) minmax(320px, 40%); }
.app-take .pic { position: relative; border-right: 1px solid var(--rule); overflow: hidden; container-type: size; }
/* The art is sized against its panel (64% of the width, never taller than 86% of the height), so it grows with the panel
   while the column folds instead of jumping once the fold ends. */
.app-take .pic .box { position: absolute; left: 50%; bottom: calc(-1 * var(--space-3)); width: 64%; width: min(64cqw, 86cqh * var(--w-per-h)); aspect-ratio: var(--ar); transform: translateX(-50%); overflow: hidden; }
.app-take .pic .box img { position: absolute; max-width: none; }
.app-take .copy { padding: 64px var(--space-5) var(--space-5); display: flex; flex-direction: column; gap: var(--space-3); }
.take p { margin: 0; max-width: 44ch; }
.take .go { color: var(--ink); text-decoration: none; }
.take .go:hover { text-decoration: underline; }
.about-take { grid-column: 2 / 3; grid-row: 1 / 2; align-content: center; /* lies over the parade's cell of the frame; both lines named, or a positioned item runs to the frame's edge */ padding: 64px clamp(var(--space-4), 6vw, 96px); }
.about-take .inner { display: flex; flex-direction: column; gap: var(--space-4); max-width: 640px; }
.about-take .links { display: flex; gap: var(--space-4); }

/* The footer runs the full width under both columns, on its own line, each item in its own box: built-by on the left,
   then the motion switch and the copyright boxed at the right. The first box is as wide as the column above it, or as the
   built-by line (--built, measured once the font loads) if that is wider; it folds with the column. */
.foot { grid-column: 1 / -1; border-top: 1px solid var(--rule); display: grid; grid-template-columns: max(var(--side), calc(var(--built, 0px) + 2 * var(--space-4) + 1px)) 1fr auto auto; transition: grid-template-columns var(--dur-move) var(--ease-move); }
body.collapsed .foot { grid-template-columns: var(--rail) 1fr auto auto; }
.foot > span { display: flex; align-items: center; padding: 0 var(--space-4); min-width: 0; overflow: hidden; white-space: nowrap; }
.foot .left { border-right: 1px solid var(--rule); }
.foot .cell { border-left: 1px solid var(--rule); }
.foot .switch { grid-column: 3; }
.foot .copy { grid-column: 4; }
/* The motion switch: green label text that names the current state; clicking flips it. */
.foot .motion { background: none; border: 0; padding: 0; cursor: pointer; color: var(--green); }
.foot .motion:hover { color: var(--ink); }
body.still .foot .motion { color: var(--red); }
body.still .foot .motion:hover { color: var(--ink); }
.foot .motion:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
body.collapsed .foot .left { justify-content: center; padding: 0; }
/* "Created by Rex Chekal", an info icon in front, the whole thing one link to the about, never underlined (Rex, 2026-10-24).
   Folded, only the icon shows; the words stay for screen readers. Pressed again while the about is open, it closes it. */
.foot .rex { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink); text-decoration: none; }
.foot .rex:hover { color: var(--muted); }
.foot .rex svg { flex: none; width: 16px; height: 16px; display: block; }
body.collapsed .foot .rex svg { width: 20px; height: 20px; }
body.collapsed .foot .built { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* While the about is open, the folded square is on: filled with the ink, its icon in the ground color. */
body.collapsed.about-open .foot .left { background: var(--ink); }
body.collapsed.about-open .foot .rex { color: var(--ground); }

body.still .bob { animation-play-state: paused; }
body.still .frame, body.still .foot { transition: none; }
@media (max-width: 760px) {
  /* On a phone the page scrolls: the column at full length, then the parade at most of the screen's height, then the footer.
     Nothing scrolls inside the column. */
  :root { --side: 100%; --foot: auto; }
  html, body { height: auto; overflow: visible; }
  .frame { height: auto; }
  .t-h1, .about-take h2 { font-size: 40px; }
  .t-h2, .now, .app-take h2 { font-size: 32px; }
  .frame { grid-template-columns: 1fr; grid-template-rows: auto 80vh auto auto; grid-template-rows: auto 80svh auto auto; }
  body.collapsed .frame { grid-template-columns: 1fr; }
  .side { border-right: 0; border-bottom: 1px solid var(--rule); }
  /* On a phone the column has no height of its own to fill, so the box takes Hey News's proportions (664 by 705). */
  .mark { flex: none; height: auto; }
  .mark img { height: auto; aspect-ratio: 664 / 705; }
  .slot-low { display: none; }
  /* About one balloon shows at a time, so the name and line of the balloon in the middle show up top and the touch-screen
     name tags are off. Sideways swipes on the parade go to the page's script, which drags it; up and down still scroll. */
  .show { touch-action: pan-y; }
  .tag { display: none; }
  .now, .desc { display: block; }
  .set { right: var(--space-4); }
  body.collapsed .side > :not(.rail) { visibility: visible; }
  body.collapsed .mark { opacity: 1; }
  body.collapsed .rail { display: none; }
  /* Built-by across the top; the motion switch and the copyright side by side under it. */
  .foot, body.collapsed .foot { grid-template-columns: 1fr 1fr; }
  .foot > span { padding: var(--space-3) var(--space-4); }
  /* The about is a section of its own under the parade, always showing, so the footer drops its link to it. */
  .about-take { display: grid; position: static; grid-column: 1; grid-row: 3; border-top: 1px solid var(--rule); padding: var(--space-7) var(--space-4); }
  .about-take .close { display: none; }
  .foot { grid-row: 4; }
  .foot .left { display: none; }
  .foot .switch { grid-column: 1; border-left: 0; }
  .foot .copy { grid-column: 2; }
  .app-take { grid-template-columns: 1fr; grid-template-rows: 45% 1fr; }
  .app-take .pic { border-right: 0; border-bottom: 1px solid var(--rule); }
}
