/* Pro Engineer — the shared stylesheet for every page but the landing.
 *
 * The landing keeps its own copy inline. That is not an oversight: `index.html`
 * is the one file in `site/` that is also published to GitHub Pages, and it has
 * to render there with nothing beside it. Everything under this file is served
 * only from proengineer.app, where a second request costs nothing and one
 * stylesheet for six pages is worth having.
 *
 * The palette is the application's own default theme, from core/src/config.rs,
 * so the site and the program cannot drift apart in look without somebody
 * noticing.
 */

:root{
  --bg:#0a0a0f;
  --bg-raised:#101018;
  --text:#dcdce6;
  /* Lifted 8% from #6d7180, which measured 4.07:1 against this background —
     under the 4.5:1 a body of text needs. It is `.sub`, `.lede`, `.note` and
     the second column of every table, so almost all the prose here was below
     the line and read as washed out rather than as quiet. Same hue, 4.63:1. */
  --dim:#767a8a;
  --highlight:#00b4ff;
  --accent:#ffa500;
  --border:#3c4655;
  --good:#3fd07f;
  --warn:#ffc447;
  --bad:#ff5f56;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,"Cascadia Mono","JetBrains Mono","Fira Code",Menlo,Consolas,"DejaVu Sans Mono",monospace;
  --pad:20px;
  /* The strip's own height, named once so the landing offset cannot drift away
     from it. Every anchor clears it by the same amount. */
  --bar:46px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
section,#top{scroll-margin-top:calc(var(--bar) + 16px)}
body{
  margin:0;background:var(--bg);color:var(--text);
  font-family:var(--mono);font-size:15px;line-height:1.65;
  -webkit-font-smoothing:antialiased;overflow-wrap:break-word;
}
body::before{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:9;
  background:repeating-linear-gradient(180deg,rgba(255,255,255,.014) 0 1px,transparent 1px 3px);
}
a{color:var(--highlight);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1100px;margin:0 auto;padding:0 var(--pad)}

/* ── window chrome ───────────────────────────────────────────────────
   Drawn in two pieces with the tab strip between them, so the three read as
   one frame while the strip is a child of .wrap — whose height is the whole
   page, which is how far it has to stick. */
.term-head{
  border:1px solid var(--border);border-bottom:0;border-radius:8px 8px 0 0;
  background:var(--bg-raised);
}
.term-bar{
  display:flex;align-items:center;gap:8px;padding:9px 14px;
  background:#15151f;border-radius:7px 7px 0 0;
}
.term-body{
  border:1px solid var(--border);border-top:0;border-radius:0 0 8px 8px;
  background:var(--bg-raised);margin:0 0 4px;padding:26px 22px;
}
.dot{width:11px;height:11px;border-radius:50%;flex:none}
.dot.r{background:var(--bad)}.dot.y{background:var(--warn)}.dot.g{background:var(--good)}
.term-title{
  flex:1;text-align:center;color:var(--dim);font-size:12.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* The same mark as the favicon, inline so it costs no request and stays sharp
   at any size. Decorative — every page says its name in words. */
.mark{width:15px;height:15px;vertical-align:-3px;margin-right:7px;flex:none}
.mark.big{width:34px;height:34px;vertical-align:0;margin:0}
.sign{display:flex;gap:12px;align-items:flex-start}

/* ── the site's tab strip ────────────────────────────────────────────
   The application's own tab bar, carrying the site's pages instead of the
   program's screens. Which one is lit is decided in the markup, per page. */
nav.tabs{
  /* Wraps rather than scrolls. It used to be `overflow-x:auto`, which is a
     nicer strip on a phone and is also a clipping box — and a clipping box
     cannot hold a dropdown. See the menu below for what that cost. */
  display:flex;flex-wrap:wrap;gap:2px;padding:0 14px;background:#0d0d15;
  border-bottom:1px solid var(--border);
  position:sticky;top:0;z-index:20;
  border-left:1px solid var(--border);border-right:1px solid var(--border);
  box-shadow:0 10px 24px -18px #000;
}
nav.tabs::after{
  content:"";position:absolute;left:0;bottom:-1px;height:2px;width:100%;
  background:linear-gradient(90deg,var(--highlight),var(--accent));
  transform:scaleX(0);transform-origin:0 50%;
}
@supports (animation-timeline: scroll()){
  nav.tabs::after{animation:fill linear both;animation-timeline:scroll(root block)}
  @keyframes fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
}
nav.tabs::-webkit-scrollbar{display:none}
nav.tabs a{
  padding:11px 13px;color:var(--dim);font-size:13px;letter-spacing:.06em;
  border-bottom:2px solid transparent;white-space:nowrap;
}
nav.tabs a:hover{color:var(--text);text-decoration:none;border-bottom-color:var(--border)}
nav.tabs a.on{color:var(--highlight);border-bottom-color:var(--highlight)}
/* One tab that asks to be clicked first.
   The accent, and a dot before it — enough to catch the eye in a row of eight
   without turning the strip into a advertisement. It keeps the underline when
   it is also the page you are on, so the two states add up rather than one
   hiding the other. */
/* The same mark on the landing's card for it. */
.card.pick{border-color:color-mix(in srgb,var(--accent) 45%,var(--border))}
.card.pick h3{color:var(--accent)}
.card.pick .go{color:var(--accent)}
nav.tabs a.pick{color:var(--accent)}
nav.tabs a.pick::before{
  content:"";display:inline-block;width:5px;height:5px;border-radius:50%;
  background:var(--accent);margin-right:7px;vertical-align:2px;
}
nav.tabs a.pick:hover{color:var(--accent);border-bottom-color:var(--accent)}
nav.tabs a.pick.on{border-bottom-color:var(--accent)}

/* ── the games menu ──────────────────────────────────────────────────
   Two games fit in the strip; six do not, and a strip that scrolls sideways
   before the reader has done anything is a strip nobody reads to the end. So
   the games live under one item.

   No script, which forces two decisions. The trigger is a <button> rather than
   a link, because on a phone a tap has to open the menu instead of navigating
   somewhere first — and a focused button is what `:focus-within` reacts to.

   The menu used to be `position:fixed` with no offsets, to escape the strip's
   `overflow-x:auto` clipping: with its offsets left alone the browser places a
   fixed box at the position it would have had in flow. **That is the position
   it would have had in flow, and the strip is `position:sticky`** — so the
   moment a reader scrolled, the nav travelled with the viewport and the menu
   stayed behind at the top of the document. On a long page it opened
   somewhere far below the screen, which is exactly what it looked like: the
   games appearing at the bottom of the page.

   So the clipping went instead. The strip wraps rather than scrolls, nothing
   clips, and the menu is absolutely positioned against its own item — which
   moves *with* the sticky strip because it is inside it. */
/* Positioned, so the menu below has something of its own to hang from, and
   block flow rather than flex: a flex container puts an out-of-flow child at
   its own top-left corner regardless of the siblings, which lands the menu on
   top of the button. */
nav.tabs .has-menu{display:block;position:relative}
nav.tabs .games{
  appearance:none;background:none;border:0;border-bottom:2px solid transparent;
  font:inherit;font-size:13px;letter-spacing:.06em;cursor:pointer;
  padding:11px 13px;white-space:nowrap;
  /* Slightly forward of the other tabs and well behind the current one: this
     is a way in, not a destination. */
  color:var(--text);
}
nav.tabs .games::after{content:"▾";font-size:9px;opacity:.65;margin-left:6px}
nav.tabs .has-menu:hover .games,
nav.tabs .has-menu:focus-within .games{border-bottom-color:var(--border)}
nav.tabs .has-menu.on .games{color:var(--highlight);border-bottom-color:var(--highlight)}

nav.tabs .menu{
  /* Against the item, and directly under it. Absolute rather than fixed so it
     follows the sticky strip; see the note above for what fixed did. */
  position:absolute;top:100%;left:0;display:none;min-width:210px;
  background:#0d0d15;border:1px solid var(--border);border-top:0;
  border-radius:0 0 7px 7px;box-shadow:0 20px 34px -22px #000;
  padding:5px 0;z-index:30;
}
nav.tabs .has-menu:hover .menu,
nav.tabs .has-menu:focus-within .menu{display:block}
@media (prefers-reduced-motion:no-preference){
  /* The same entrance the folds use, so the menu belongs to the page rather
     than appearing on top of it. Opt-in, like every other movement here. */
  nav.tabs .has-menu:hover .menu,
  nav.tabs .has-menu:focus-within .menu{animation:foldin .16s ease both}
}
nav.tabs .menu a{
  display:flex;align-items:center;gap:8px;
  padding:9px 14px;border-bottom:0;font-size:13px;letter-spacing:.02em;
}
nav.tabs .menu a:hover{color:var(--text);background:#15151f;border-bottom:0}
nav.tabs .menu a.on{color:var(--highlight)}
/* Where each game stands, said in the menu rather than only on its page: a
   list of names reads as a list of things that work. */
nav.tabs .menu .tag{
  margin-left:auto;font-size:10px;letter-spacing:.07em;text-transform:uppercase;
  border:1px solid var(--border);border-radius:20px;padding:1px 7px;flex:none;
}
nav.tabs .menu .tag.full{color:var(--good);border-color:rgba(63,208,127,.4)}
nav.tabs .menu .tag.soon{color:var(--dim)}
/* The comparison at the foot of the menu is not a game, and should not read as
   one. */
nav.tabs .menu a.all{
  border-top:1px solid var(--border);margin-top:4px;padding-top:11px;
  color:var(--dim);font-size:12.5px;
}
nav.tabs .menu a.all:hover{color:var(--text)}

@media (max-width:640px){
  /* On a narrow screen the menu hangs from the *strip* rather than from the
     item, and spans it. Against the item it would run off whichever edge the
     item happens to be near — off the left when GAMES sits near the left, off
     the right when the strip has wrapped and put it near the right — and a
     dropdown that is half off the screen is worse than one that is plain.
     `position:static` here hands the job to `nav.tabs`, which is sticky and
     therefore a containing block, so `top:100%` is under the whole strip. */
  nav.tabs .has-menu{position:static}
  nav.tabs .menu{left:8px;right:8px;min-width:0}
}

/* ── page head ───────────────────────────────────────────────────────
   A sub-page opens with a breadcrumb and a real H1 rather than with the block
   logo. The logo is the landing's; repeating it on six pages makes six pages
   that all look like the front door. */
.crumbs{color:var(--dim);font-size:12.5px;letter-spacing:.06em;margin:0 0 12px}
.crumbs a{color:var(--dim)}
.crumbs a:hover{color:var(--highlight)}
.crumbs .sep{opacity:.5;padding:0 6px}
.h1{font-size:clamp(21px,3vw,27px);margin:0 0 10px;font-weight:700;color:var(--text);line-height:1.25}
.h1 span{color:var(--dim);font-weight:400;font-size:clamp(13px,1.6vw,15px);display:block;margin-top:4px}
.tagline{font-size:clamp(16px,2.4vw,21px);margin:0 0 6px}
.tagline b{color:var(--accent);font-weight:600}
.sub{color:var(--dim);margin:0 0 24px;max-width:66ch}
.sub b{color:var(--text);font-weight:600}
.prompt{color:var(--good)}

/* ── the phosphor look ─────────────────────────────────────────────
   A cheap, static glow on the two accent colours. Text-shadow does not animate
   here, so it costs one paint and nothing per frame. */
.h1,.stat b,.demo .hi{text-shadow:0 0 18px rgba(0,180,255,.28)}
h2{text-shadow:0 0 16px rgba(255,165,0,.22)}
.btn{box-shadow:0 0 0 0 rgba(0,180,255,0)}
.btn:hover{box-shadow:0 0 22px rgba(0,180,255,.25)}

/* A refresh line crossing the screen once every twelve seconds: one transformed
   element over a fixed layer, so nothing under it repaints. */
.sweep{position:fixed;inset:0;pointer-events:none;z-index:8;overflow:hidden}
.sweep::before{
  content:"";position:absolute;left:0;right:0;height:38vh;top:-40vh;
  background:linear-gradient(180deg,transparent,rgba(0,180,255,.045),transparent);
}
@media (prefers-reduced-motion:no-preference){
  .sweep::before{animation:sweep 12s linear infinite}
}
@keyframes sweep{from{transform:translateY(0)}to{transform:translateY(180vh)}}

/* ── the program's own output, on the page ─────────────────────────── */
.demo{border:1px solid var(--border);border-radius:6px;overflow:hidden;margin:0 0 22px;background:#07070c}
.demo-bar{
  background:#15151f;border-bottom:1px solid var(--border);padding:6px 12px;
  color:var(--dim);font-size:11.5px;letter-spacing:.08em;
}
.demo-body{padding:14px 16px;white-space:pre-wrap;font-size:13.5px;line-height:1.75;color:var(--text)}
.demo .c{color:var(--dim)}
.demo .w{color:var(--text)}
.demo .acc{color:var(--accent)}
.demo .hi{color:var(--good)}
.demo .md{color:var(--warn)}
.demo .lo{color:var(--bad)}
.cursor{
  display:inline-block;width:.6em;height:1.05em;background:var(--highlight);
  vertical-align:-2px;animation:blink 1.1s steps(1) infinite;
}
@keyframes blink{50%{opacity:0}}

/* Each line prints itself as the block is scrolled into view: nine staggered
   animations off the same scroll range, and no script. */
.demo-body .ln{display:block;min-height:1.75em}
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    .demo-body .ln{opacity:0;animation:printline linear both;animation-timeline:view()}
    .demo-body .ln:nth-child(1){animation-range:entry 6% entry 16%}
    .demo-body .ln:nth-child(2){animation-range:entry 12% entry 22%}
    .demo-body .ln:nth-child(3){animation-range:entry 17% entry 27%}
    .demo-body .ln:nth-child(4){animation-range:entry 22% entry 32%}
    .demo-body .ln:nth-child(5){animation-range:entry 27% entry 37%}
    .demo-body .ln:nth-child(6){animation-range:entry 32% entry 42%}
    .demo-body .ln:nth-child(7){animation-range:entry 37% entry 47%}
    .demo-body .ln:nth-child(8){animation-range:entry 42% entry 52%}
    .demo-body .ln:nth-child(9){animation-range:entry 47% entry 57%}
    @keyframes printline{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
  }
}

/* ── typing ──────────────────────────────────────────────────────────
   The default state is plain, fully visible text. The clipping and the zero
   width exist only inside the guards, because a heading that starts at
   `width:0` and never animates is a heading nobody can read. */
.tw{display:inline;--tw-adv:1ch}
/* A section heading is spaced out by .14em a letter, and each character takes
   that much room on top of its own 1ch. Without it the box stopped growing
   about a seventh of the way short and the end of every heading was clipped. */
h2 .tw{--tw-adv:calc(1ch + .14em)}
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    .tw{
      display:inline-block;overflow:hidden;white-space:nowrap;vertical-align:bottom;
      width:calc(var(--n) * var(--tw-adv));
      animation:typein linear both;animation-timeline:view();
      animation-range:entry 8% entry 34%;
    }
  }
}
@keyframes typein{from{width:0}to{width:calc(var(--n) * var(--tw-adv))}}

/* ── movement ────────────────────────────────────────────────────────
   Only opacity and transform, so every frame is on the compositor and nothing
   here can cause a relayout. */
.btn,.card,nav.tabs a,details.fold > summary,figure img{
  transition:transform .18s ease, border-color .18s ease,
             background-color .18s ease, color .18s ease, opacity .18s ease,
             box-shadow .18s ease;
}
/* Only things that go somewhere move under the pointer. This used to be
   `.card:hover`, which lifted and lit every tile on the page — including the
   nine that are descriptions and lead nowhere. A tile that behaves like a
   button and then does nothing teaches people not to trust the ones that do. */
a.card:hover{transform:translateY(-2px);border-color:var(--highlight)}
.btn:hover{transform:translateY(-1px)}
figure img:hover{border-color:var(--highlight)}
.stat{transition:background-color .18s ease}
.stat:hover{background:#15151f}
.btn:active,a.card:active{transform:translateY(1px)}
nav.tabs a:active{opacity:.7}

/* Keyboard users get the same answer the pointer gets. Without this the only
   indication of focus is the browser's default outline, which several of these
   backgrounds swallow entirely. */
a:focus-visible,.btn:focus-visible,a.card:focus-visible,
nav.tabs a:focus-visible,nav.tabs .games:focus-visible,
details.fold > summary:focus-visible{
  outline:2px solid var(--highlight);outline-offset:3px;border-radius:4px;
}

details.fold[open] > .fold-body{animation:foldin .2s ease both}
@keyframes foldin{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}

@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    h2::after{
      transform-origin:0 50%;animation:draw linear both;
      animation-timeline:view();animation-range:entry 0% entry 40%;
    }
    @keyframes draw{from{transform:scaleX(0)}to{transform:scaleX(1)}}

    section > h2,section > .lede,.card,.stats,.demo,figure,details.fold{
      animation:rise linear both;animation-timeline:view();
      animation-range:entry 0% entry 32%;
    }
    @keyframes rise{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

    /* A grid that arrives all at once is one event; the same cards a beat apart
       read as a list being filled in. */
    .grid .card:nth-child(1){animation-range:entry 2% entry 30%}
    .grid .card:nth-child(2){animation-range:entry 6% entry 34%}
    .grid .card:nth-child(3){animation-range:entry 10% entry 38%}
    .grid .card:nth-child(4){animation-range:entry 14% entry 42%}
    .grid .card:nth-child(5){animation-range:entry 18% entry 46%}
    .grid .card:nth-child(6){animation-range:entry 22% entry 50%}
    .grid .card:nth-child(7){animation-range:entry 26% entry 54%}
    .grid .card:nth-child(8){animation-range:entry 30% entry 58%}
    .grid .card:nth-child(9){animation-range:entry 34% entry 62%}

    .stats .stat{animation:rise linear both;animation-timeline:view()}
    .stats .stat:nth-child(1){animation-range:entry 4% entry 26%}
    .stats .stat:nth-child(2){animation-range:entry 8% entry 30%}
    .stats .stat:nth-child(3){animation-range:entry 12% entry 34%}
    .stats .stat:nth-child(4){animation-range:entry 16% entry 38%}
    .stats .stat:nth-child(5){animation-range:entry 20% entry 42%}
    .stats .stat:nth-child(6){animation-range:entry 24% entry 46%}

    /* A screenshot settles rather than slides — it is a picture of a screen,
       and a screen switching on is the right gesture for it. */
    figure img{animation:settle linear both;animation-timeline:view();
               animation-range:entry 0% entry 36%}
    @keyframes settle{from{opacity:0;transform:scale(.985)}to{opacity:1;transform:none}}
  }
}

/* A link's underline arrives from the left. */
.lede a,footer a,.note a,.card a{
  text-decoration:none;background-image:linear-gradient(var(--highlight),var(--highlight));
  background-size:0 1px;background-position:0 100%;background-repeat:no-repeat;
  transition:background-size .2s ease;
}
.lede a:hover,footer a:hover,.note a:hover,.card a:hover{background-size:100% 1px}

/* Landing on a section from a link should show you where you landed. */
section{position:relative}
section::before{
  content:"";position:absolute;left:calc(var(--pad) * -1 + 4px);
  top:38px;bottom:8px;width:2px;background:var(--highlight);
  opacity:0;border-radius:2px;
}
section:target::before{animation:mark 2.4s ease both}
@keyframes mark{
  0%{opacity:0;transform:scaleY(0)}
  12%{opacity:1;transform:scaleY(1)}
  70%{opacity:1;transform:scaleY(1)}
  100%{opacity:0;transform:scaleY(1)}
}
section:target > h2{animation:markh 2.4s ease both}
@keyframes markh{0%,70%{color:var(--highlight)}100%{color:var(--accent)}}

@media (prefers-reduced-motion:reduce){
  .cursor{animation:none}
  /* Named rather than left to the blanket rule below: an infinite animation
     with a 0.01 ms duration is still an animation, and a sweep crossing the
     screen a hundred times a second is worse than the one it replaced. */
  .sweep{display:none}
  .demo-body .ln{opacity:1;animation:none}
  section:target::before,section:target > h2,.stats .stat,figure img{animation:none}
  section:target > h2{color:var(--accent)}
  .tw{display:inline;width:auto;overflow:visible;animation:none}
  h2::after{animation:none;transform:none}
  nav.tabs::after{animation:none;transform:scaleX(1)}
  *{transition-duration:.01ms !important;animation-duration:.01ms !important}
  html{scroll-behavior:auto}
}

/* ── buttons, stats, sections ────────────────────────────────────── */
.cta{display:flex;flex-wrap:wrap;gap:10px;margin:24px 0 10px}
.fineprint{color:var(--dim);font-size:13px;margin:0;max-width:64ch}
/* Filled rather than outlined, faintly, so a button reads as a surface you can
   press instead of as a box with words in it — the outlined version was being
   mistaken for a label beside the two ghosts. */
.btn{
  border:1px solid var(--highlight);color:var(--highlight);
  background:rgba(0,180,255,.09);
  padding:10px 18px;border-radius:5px;font-size:14px;cursor:pointer;
}
.btn:hover{background:var(--highlight);color:#06060b;text-decoration:none}
.btn.ghost{border-color:var(--border);color:var(--text);background:var(--bg-raised)}
.btn.ghost:hover{border-color:var(--highlight);color:var(--highlight);background:#161624}

.stats{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
  gap:1px;background:var(--border);border:1px solid var(--border);
  border-radius:6px;overflow:hidden;margin:0 0 26px;
}
.stat{background:var(--bg-raised);padding:14px 12px;text-align:center}
.stat b{display:block;color:var(--highlight);font-size:23px;line-height:1.2}
.stat span{color:var(--dim);font-size:11.5px;letter-spacing:.06em}

section{padding:46px 0 8px}
h2{
  font-size:15px;letter-spacing:.14em;color:var(--accent);
  margin:0 0 6px;font-weight:600;display:flex;align-items:center;gap:12px;
}
h2::after{content:"";flex:1;height:1px;background:var(--border)}
.lede{color:var(--dim);margin:0 0 24px;max-width:74ch}
.lede b{color:var(--text);font-weight:600}
h3{font-size:15px;margin:22px 0 8px;color:var(--text)}

.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:14px}
.grid.three{grid-template-columns:repeat(3,1fr)}
@media (max-width:900px){.grid.three{grid-template-columns:repeat(2,1fr)}}
@media (max-width:620px){.grid.three{grid-template-columns:1fr}}
.card{
  border:1px solid var(--border);border-radius:6px;padding:16px 18px;
  background:var(--bg-raised);
}
.card h3{display:flex;align-items:baseline;gap:8px;margin:0 0 7px}
.card h3 .k{color:var(--highlight);flex:none}
.card h3 .n{margin-left:auto;color:var(--accent);font-size:12px;font-weight:400;flex:none}
.card p{margin:0;color:var(--dim);font-size:14px}

/* A card that is entirely a link — used for the page cards and the simulator
   picker. The whole tile is the target, which is what a thumb expects.

   It has to *say* so without being hovered: half the people reading this are on
   a phone, where there is no hover at all, and a tile that looks exactly like
   the tile beside it will not be tapped. Three permanent cues, none of which
   the static tiles have — a rail down the left edge in the accent colour, a
   heading in that colour, and a line at the bottom that ends in an arrow. */
a.card{
  display:block;color:inherit;cursor:pointer;
  border-left:2px solid var(--highlight);
  background:linear-gradient(180deg,#12121d,#0f0f17);
}
a.card:hover{text-decoration:none;box-shadow:0 8px 26px -18px var(--highlight)}
a.card h3{color:var(--highlight)}
a.card p{color:var(--dim)}
a.card .go{
  color:var(--highlight);font-size:12.5px;letter-spacing:.06em;
  margin-top:12px;display:block;
}
/* The arrow steps forward rather than the whole line moving: one transformed
   pseudo-element, and the text it belongs to stays where it was. */
a.card .go::after{
  content:" →";display:inline-block;transition:transform .18s ease;
}
a.card:hover .go::after{transform:translateX(4px)}

/* And the tiles that are only text say that too, by staying still and keeping
   the plain border on all four sides. */
.card:not(a):hover{border-color:var(--border)}

/* The simulator picker. One tile per game, and adding the next one is adding a
   tile — which is the whole point of the folder-per-game boundary underneath. */
.sims{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;margin:0 0 8px}
.sim-badge{
  display:inline-block;font-size:11.5px;letter-spacing:.08em;padding:2px 8px;
  border:1px solid var(--border);border-radius:20px;color:var(--dim);margin-left:auto;flex:none;
}
.sim-badge.live{color:var(--good);border-color:var(--good)}
.sim-badge.soon{color:var(--dim)}
a.card.pending{opacity:.72}
a.card.pending:hover{opacity:1}

/* A status table: one line per thing, and a mark that says where it stands.
   Prose describing the state of six items is six sentences nobody finishes; a
   column of marks is read at a glance and cannot hide a "mostly". The mark
   carries a letter as well as a colour, because a red and a green dot are the
   same dot to a colour-blind reader and to a printed page. */
table.status td:first-child{width:auto}
table.status td.mark{
  width:1%;white-space:nowrap;text-align:center;padding-right:14px;
  font-size:15px;font-weight:700;
}
table.status td.mark.yes{color:var(--good)}
table.status td.mark.wip{color:var(--warn)}
table.status td.mark.no{color:var(--bad)}
table.status td.what{color:var(--text);font-weight:600}
table.status td.note{color:var(--dim);font-size:13.5px}
table.status tr.is-no td.what{color:var(--dim);font-weight:500}

/* The capability matrix: one column per simulator, one row per thing the
   program does something with.

   A grid rather than a page of prose per game, because the question a reader
   arrives with is comparative — "does it do X on mine" — and prose makes that
   a search. The row label carries its own sentence underneath, so a cross is
   never just a cross: what is withheld and why is the whole point of the
   table, and a bare ✗ reads as a missing feature rather than as a measurement
   the game does not publish. */
table.matrix{width:100%;border-collapse:collapse}
table.matrix th,table.matrix td{
  border-bottom:1px solid var(--border);padding:11px 10px;vertical-align:top;
}
table.matrix thead th{
  color:var(--dim);font-size:12px;letter-spacing:.07em;text-transform:uppercase;
  font-weight:600;text-align:center;border-bottom:1px solid var(--border);
  position:sticky;top:44px;background:var(--bg);z-index:1;
}
table.matrix thead th:first-child{text-align:left}
table.matrix thead th a{border-bottom:0;color:var(--text)}
table.matrix thead th a:hover{color:var(--highlight)}
table.matrix tbody th{
  text-align:left;font-weight:600;color:var(--text);font-size:14.5px;
  /* Wide enough for the sentence under the label to be a paragraph rather
     than a column of single words. */
  width:46%;
}
table.matrix .why{display:block;font-weight:400;color:var(--dim);font-size:13px;margin-top:3px}
table.matrix td.mark{text-align:center;font-size:16px;font-weight:700;width:1%;white-space:nowrap}
table.matrix td.mark.yes{color:var(--good)}
table.matrix td.mark.no{color:var(--bad)}
table.matrix td.mark.wip{color:var(--warn)}
table.matrix tbody tr:hover{background:#12121b}

@media (max-width:720px){
  /* The label's sentence is what goes, not the comparison: a reader on a
     phone is here for the grid. The full sentence is on each game's own page. */
  table.matrix .why{display:none}
  table.matrix tbody th{width:auto;font-size:13.5px}
  table.matrix th,table.matrix td{padding:9px 6px}
  table.matrix thead th{font-size:10.5px;letter-spacing:.04em}
}

/* The key above such a table. */
.legend{
  display:flex;flex-wrap:wrap;gap:16px;margin:0 0 14px;
  font-size:13px;color:var(--dim);
}
.legend b{font-weight:700}
.legend .yes{color:var(--good)}
.legend .wip{color:var(--warn)}
.legend .no{color:var(--bad)}

@media (max-width:640px){
  /* Three columns of prose do not fit a phone, and stacking the mark onto a
     line of its own loses what it refers to. So the mark keeps the left edge
     and spans both lines, with the thing above and the note under it. */
  .stacked table.status,.stacked table.status tbody{display:block}
  .stacked table.status tr{
    display:grid;grid-template-columns:auto 1fr;gap:1px 10px;
    padding:10px 0;border-bottom:1px solid var(--border);
  }
  .stacked table.status td{display:block;padding:0;border:0}
  .stacked table.status td.mark{grid-row:span 2;align-self:start;width:auto;padding:0}
  .legend{gap:12px;font-size:12.5px}
}

figure{margin:0 0 24px}
figure img{
  width:100%;height:auto;display:block;
  border:1px solid var(--border);border-radius:6px;background:#000;
}
figcaption{color:var(--dim);font-size:13px;margin-top:9px}

pre{
  background:#07070c;border:1px solid var(--border);border-radius:6px;
  padding:14px 16px;overflow-x:auto;margin:0 0 16px;font-size:13.5px;
}
pre .c{color:var(--dim)}
pre .s{color:#9aa0b0}
code{color:var(--accent)}

table{width:100%;border-collapse:collapse;font-size:14px;margin:0 0 4px}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--border);vertical-align:top}
th{color:var(--dim);font-weight:600;font-size:12.5px;letter-spacing:.08em}
td:first-child{color:var(--text);white-space:normal}
td:last-child{color:var(--dim)}
td code{color:var(--highlight)}

/* Tables are how most of this site says things, and the widest of them are
   three columns of prose: the eye loses which row it is on between the label
   and the note. A row under the pointer costs nothing and fixes it. Not on
   phones, where there is no pointer and rows are already separated. */
@media (hover:hover){
  tbody tr{transition:background-color .12s ease}
  tbody tr:hover{background:#101019}
}


details.fold{
  border:1px solid var(--border);border-radius:6px;background:var(--bg-raised);
  margin:0 0 10px;overflow:hidden;
}
details.fold > summary{
  cursor:pointer;list-style:none;padding:14px 18px;
  display:flex;align-items:center;gap:10px;font-size:14.5px;
}
details.fold > summary::-webkit-details-marker{display:none}
details.fold > summary::before{content:"+";color:var(--highlight);font-weight:700;width:1ch;flex:none}
details.fold[open] > summary::before{content:"−"}
details.fold > summary:hover{background:#161622}
details.fold > summary .count{margin-left:auto;color:var(--dim);font-size:12px;flex:none}
details.fold > summary .blurb{color:var(--dim);font-size:13px}
.fold-body{padding:0 18px 8px}
details.fold[open] > summary{border-bottom:1px solid var(--border)}

.note{
  border-left:2px solid var(--warn);padding:2px 0 2px 16px;
  color:var(--dim);margin:14px 0 18px;font-size:14px;
}
.note b{color:var(--warn)}
td.na{color:var(--dim)}

footer{
  border-top:1px solid var(--border);margin-top:50px;padding:26px 0 40px;
  color:var(--dim);font-size:13.5px;
}
footer .row{display:flex;flex-wrap:wrap;gap:20px;justify-content:space-between}
footer .cols{display:flex;flex-wrap:wrap;gap:34px}
footer .cols div{min-width:120px}
footer .cols b{display:block;color:var(--dim);font-size:11.5px;letter-spacing:.08em;margin-bottom:6px}
footer .cols a{display:block;color:var(--text);font-size:13.5px}
footer .cols a:hover{color:var(--highlight)}

/* Wide things scroll inside themselves; the page never does. */
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch}
.scroll-x table{min-width:900px;margin-bottom:0}
.scroll-x pre{min-width:0}
.scroll-wrap{position:relative}
.scroll-wrap::after{
  content:"";position:absolute;top:0;right:0;bottom:0;width:34px;
  background:linear-gradient(90deg,transparent,var(--bg));pointer-events:none;
}
.swipe{display:none;color:var(--dim);font-size:12.5px;margin:6px 0 0}

/* ── phone ─────────────────────────────────────────────────────────── */

/* ── recent updates ───────────────────────────────────────────────────────
 *
 * A visitor arriving cold cannot tell a maintained project from an abandoned
 * one, and prose saying "actively developed" is what an abandoned one says
 * too. Dates and version numbers are not. This is generated from CHANGELOG.md
 * by build.py, so it cannot say something the releases do not.
 *
 * Beside the page rather than in it: the column is 1100px and a wide screen has
 * room to spare either side, so on a wide enough one this sits in that margin
 * and touches nothing. Under that width there is no margin to sit in, and it
 * becomes an ordinary card under the hero — the same content, in the flow,
 * rather than something covering the text on a laptop.
 *
 * The threshold is arithmetic, not a guess: 1100 for the column plus twice
 * (260 panel + 28 gutter) is 1676, so 1700 is the first width where both fit
 * with the column still centred. Pinning it to the *column* rather than to the
 * viewport edge is the other half — `right: 24px` put it 19px over the text on
 * a 1680px screen, because the panel walked inward as the window narrowed
 * while the column stayed centred.
 */
.releases{
  max-width:1100px;margin:0 auto 34px;padding:16px 18px;
  border:1px solid var(--border);border-radius:10px;background:var(--bg-raised);
}
.releases h2{
  margin:0;font-size:12px;letter-spacing:.14em;color:var(--dim);font-weight:600;
}
.releases .now{margin:2px 0 12px;font-size:13px;color:var(--dim)}
.releases .now b{color:var(--good)}
.releases ol{margin:0;padding:0;list-style:none;display:grid;gap:12px}
.releases li{border-left:2px solid var(--border);padding-left:12px}
.releases li:first-child{border-left-color:var(--highlight)}
.releases a{color:var(--highlight);text-decoration:none}
.releases a:hover{text-decoration:underline}
.releases time{color:var(--dim);font-size:12px;margin-left:8px}
.releases p{margin:3px 0 0;font-size:13px;line-height:1.6;color:var(--dim)}
.releases .all{display:inline-block;margin-top:12px;font-size:12.5px;color:var(--dim)}
.releases .all:hover{color:var(--highlight)}

@media (min-width:1700px){
  .releases{
    position:fixed;top:calc(var(--bar) + 24px);
    /* Half the viewport, plus half the column, plus the gutter — so it tracks
       the column's right edge instead of the window's. */
    left:calc(50% + 550px + 28px);
    width:260px;max-width:260px;margin:0;z-index:5;
    max-height:calc(100vh - var(--bar) - 48px);overflow:auto;
  }
  /* Four entries is a sign of life; a scrolling wall of them is a changelog,
     and there is a link to the real one at the bottom. */
  .releases li:nth-child(n+5){display:none}
}

@media (max-width:760px){
  :root{--pad:14px}
  .term-body{padding:20px 15px}
  .demo-body{font-size:12.5px;padding:12px 12px;line-height:1.7}
  section{padding:36px 0 4px}
  h2{font-size:13.5px;letter-spacing:.1em}
  .cta{gap:8px}
  .btn{flex:1 1 auto;text-align:center;padding:11px 12px}
  .stat b{font-size:20px}
  .stats{grid-template-columns:repeat(auto-fit,minmax(96px,1fr))}

  /* Two-column tables become stacked pairs: a label and what it means. A
     14-word description in a 40 %-wide cell on a 360 px screen is four words a
     line and eight lines tall. */
  .stacked table,.stacked tbody,.stacked tr,.stacked td{display:block;width:100%}
  .stacked tr{border-bottom:1px solid var(--border);padding:11px 0}
  .stacked td{border:0;padding:0}
  .stacked td:first-child{color:var(--text);font-weight:600;margin-bottom:2px}
  .stacked td:last-child{color:var(--dim);font-size:13.5px}
  .fold-body{padding:0 14px 6px}
  details.fold > summary{padding:13px 14px;flex-wrap:wrap}
  details.fold > summary .count{margin-left:0;width:100%;padding-left:2ch}
  footer .row{gap:14px}
  footer .cols{gap:20px}

  /* The wide table cannot stack — nine columns is a comparison, not a list —
     so it scrolls, and the hint that it does appears only where it is needed. */
  .swipe{display:block}
  .h1 span{font-size:13px}
  .tagline{font-size:17px}
  .grid{gap:12px}
  .card{padding:14px 15px}
  nav.tabs a{padding:13px 12px}   /* comfortably past the 44 px a thumb wants */
  .sweep{display:none}
  /* The rail lives in the wrapper's padding, and there is not enough of it
     here to put anything in. */
  section::before{display:none}
  .fineprint{font-size:12.5px}
  figcaption{font-size:12.5px}
  .note{padding-left:12px;font-size:13.5px}
  table{font-size:13.5px}
}

@media (max-width:400px){
  .demo-body{font-size:11.5px;padding:10px 10px}
  .stats{grid-template-columns:repeat(2,1fr)}
  .h1{font-size:20px}
  .btn{width:100%}
}

/* ── a state-of-the-thing checklist ──────────────────────────────────
   Three states and no fourth, because a fourth is where "in progress"
   lives and "in progress" is what a roadmap says instead of the truth.
   The mark carries the meaning, the colour only reinforces it: these
   have to be readable where colour does not survive — a screenshot in
   a forum thread, a colour-blind reader, a printed page. */
.check{list-style:none;margin:0 0 18px;padding:0}
.check li{
  display:grid;grid-template-columns:1.6em 1fr;gap:0 12px;
  padding:11px 0 11px 2px;border-bottom:1px solid var(--border);
  align-items:baseline;
}
.check li:last-child{border-bottom:0}
.check .mk{font-weight:700;text-align:center;line-height:1.3}
.check .what{color:var(--text);font-size:14.5px}
.check .why{grid-column:2;color:var(--dim);font-size:13.5px;margin-top:3px}
.check .why code{font-size:13px}

.check li.done .mk{color:var(--good)}
.check li.done .what{color:var(--text)}
.check li.part .mk{color:var(--warn)}
.check li.none .mk{color:var(--dim)}
.check li.none .what{color:var(--dim)}

/* The legend above a list, so the three marks are named once rather than
   guessed at. */
.legend{display:flex;flex-wrap:wrap;gap:18px;margin:0 0 16px;font-size:13px;color:var(--dim)}
.legend b{font-weight:700}
.legend .done b{color:var(--good)}
.legend .part b{color:var(--warn)}
.legend .none b{color:var(--dim)}

/* A one-line summary of where the whole thing stands, above the list. */
.tally{
  display:flex;flex-wrap:wrap;gap:1px;background:var(--border);
  border:1px solid var(--border);border-radius:6px;overflow:hidden;margin:0 0 18px;
}
.tally div{background:var(--bg-raised);padding:12px 16px;flex:1 1 8em;text-align:center}
.tally b{display:block;font-size:21px;line-height:1.2}
.tally span{color:var(--dim);font-size:11.5px;letter-spacing:.06em}
.tally .done b{color:var(--good)}
.tally .part b{color:var(--warn)}
.tally .none b{color:var(--dim)}

@media (max-width:760px){
  .check li{grid-template-columns:1.4em 1fr;gap:0 9px}
  .check .what{font-size:14px}
  .check .why{font-size:13px}
  .legend{gap:12px;font-size:12.5px}
  .tally div{padding:10px 8px}
  .tally b{font-size:18px}
}

/* ── the changelog ───────────────────────────────────────────────────
   /changelog/ is the application's own CHANGELOG.md rendered, so this
   styles Markdown's shapes rather than markup written for the site: a
   release is a section, its notes are headings, bullets and the odd
   block quote. Nothing else on the site has a plain <ul> in it, which is
   why these are scoped to .log — a rule for bare `ul` here would reach
   into the panels and tables on every other page. */
/* Two programs on one page, and the reader has to be told which is which.
   They ship together and carry the same version, so one list of numbers read
   as one product. The heading is deliberately louder than a release's. */
.stream{
  margin:64px 0 6px;font-size:1.45rem;letter-spacing:.01em;
  color:var(--text);scroll-margin-top:80px;
}
.stream:first-of-type{margin-top:38px}
.stream-note{color:var(--dim);margin:0 0 6px;max-width:78ch;line-height:1.7}
.log{padding:34px 0 6px;border-top:1px solid var(--border)}
.log:first-of-type{border-top:0}
.log > h2{margin-bottom:14px}
.log > h2 time{
  color:var(--dim);font-size:12.5px;letter-spacing:0;
  font-weight:400;flex:none;
}
/* The release the site is currently built from. A reader scrolling a
   year of history should not have to compare version numbers with the
   download button to find out where they are. */
.log > h2 .tag{flex:none}
.log h3{
  font-size:13px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent);margin:26px 0 8px;
}
.log p{color:var(--dim);margin:0 0 12px;max-width:78ch;line-height:1.75}
.log p b, .log li b{color:var(--text);font-weight:600}
.log-list{margin:0 0 16px;padding:0;list-style:none;max-width:78ch}
.log-list li{
  color:var(--dim);line-height:1.75;padding:0 0 10px 18px;
  position:relative;font-size:14.5px;
}
/* The mark is drawn rather than a bullet character: a list-style dot
   sits on the text's own line height and drifts as the size changes. */
.log-list li::before{
  content:"";position:absolute;left:2px;top:.72em;
  width:5px;height:5px;background:var(--border);border-radius:1px;
}
.log blockquote{
  border-left:2px solid var(--warn);margin:0 0 18px;padding:14px 0 2px 16px;
  background:linear-gradient(90deg,var(--bg-raised),transparent 60%);
}
.log blockquote h3{margin-top:0}
.log blockquote p:last-child{margin-bottom:12px}
.log .src{margin:6px 0 0;font-size:12.5px}
.log .src a{color:var(--dim);text-decoration:none}
.log .src a:hover{color:var(--highlight)}

/* The language reference, laid out like documentation rather than like a page.
   One column of prose is fine to read once and hopeless to use: a reference is
   for jumping to the one name you have forgotten, and scrolling to find it is
   what a sidebar exists to stop. */
.docs{display:grid;grid-template-columns:214px minmax(0,1fr);gap:36px;align-items:start}
.docs-nav{
  position:sticky;top:calc(var(--bar) + 18px);align-self:start;
  max-height:calc(100vh - var(--bar) - 40px);overflow:auto;
  border-left:1px solid var(--border);padding:2px 0 2px 14px;
  display:flex;flex-direction:column;gap:1px
}
/* **A wrapped entry has to still read as one entry.** "Tyre and brake
   channels" does not fit on one line at any sensible width, and left alone the
   second line started under the first and the list turned to mush. A hanging
   indent puts the continuation under the text rather than under the margin,
   and a little air between items does the rest. */
.docs-nav a{
  color:var(--dim);text-decoration:none;font-size:13px;line-height:1.5;
  padding:3px 0 3px 10px;text-indent:-10px
}
.docs-nav a:hover{color:var(--highlight)}
.docs-nav a code{font-size:12.5px;color:inherit}
.docs-nav .docs-head{
  color:var(--highlight);font-size:11px;letter-spacing:.08em;text-transform:uppercase;
  margin:16px 0 3px
}
.docs-nav .docs-head:first-child{margin-top:0}

/* The sections sit closer together than they do on a page of prose: beside a
   sticky sidebar, 46 px of air between them is 46 px of nothing to look at. */
.docs-body > section{padding:30px 0 4px}
.docs-body > section:first-child{padding-top:0}

/* **The name column is as wide as the names and no wider.** Left to itself the
   browser gave the first column whatever it liked, so the three tables did not
   line up with each other and the descriptions started in a different place in
   each — which is most of what made this hard to read down. `width:1%` with
   `nowrap` is the standing trick for "shrink to fit". */
.docs-body table td:first-child{width:1%;white-space:nowrap;padding-right:22px}
.docs-body table td:first-child code{color:var(--accent)}
.docs-body table td:last-child{color:var(--dim)}
.docs-body table tr{scroll-margin-top:calc(var(--bar) + 20px)}
/* And a row somebody has just jumped to says so, or a link into the middle of
   a table lands the reader somewhere with nothing to confirm they arrived. */
.docs-body table tr:target td{background:color-mix(in srgb,var(--highlight) 12%,transparent)}
.docs-body table tr:target td:first-child{box-shadow:inset 2px 0 0 var(--highlight)}

/* Below a laptop the sidebar is a strip across the top rather than a column
   squeezing the table it is meant to help with. */
@media (max-width:900px){
  .docs{grid-template-columns:1fr;gap:12px}
  .docs-nav{
    position:static;max-height:none;flex-direction:row;flex-wrap:wrap;gap:4px 14px;
    border-left:0;border-bottom:1px solid var(--border);padding:0 0 12px
  }
  .docs-nav .docs-head{margin:0}
  .docs-body table td:first-child{white-space:normal}
}

/* A heading row inside the reference tables: seventy channels under nine
   words rather than seventy in a row. */
.docs-body table tr.head td{
  color:var(--highlight);font-size:11px;letter-spacing:.08em;
  padding-top:20px;border-bottom:0
}
.docs-body table tr.head:first-child td{padding-top:2px}

/* ── the shared-laps library ──────────────────────────────────────────────
   A table, three pickers and one lap drawn from its own telemetry. Nothing
   here is a leaderboard: the time column is emphasised because it is what
   somebody is looking for, and no row is numbered or ranked. */
.filters { margin: 1.6rem 0 1rem; }
.filter-row {
  display: flex; flex-wrap: wrap; gap: 1rem 1.4rem; align-items: flex-end;
}
.filter-row label { display: flex; flex-direction: column; gap: .3rem; }
.filter-row label > span {
  font-size: .72rem; letter-spacing: .08em; opacity: .62; text-transform: uppercase;
}
.filter-row select {
  background: var(--panel, #11161d); color: inherit;
  border: 1px solid var(--line, #223); border-radius: 6px;
  padding: .45rem .6rem; min-width: 12rem; font: inherit;
}
.filter-row .tick { flex-direction: row; align-items: center; gap: .45rem; }
.filter-row .tick > span { font-size: .85rem; letter-spacing: 0; opacity: .8; text-transform: none; }
.count { opacity: .6; font-size: .85rem; margin: .8rem 0 0; }

.listing { overflow-x: auto; }
.listing table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.listing th {
  text-align: left; font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  opacity: .55; padding: .5rem .7rem; border-bottom: 1px solid var(--line, #223);
}
.listing td { padding: .55rem .7rem; border-bottom: 1px solid rgba(128,128,128,.14); }
.listing tbody tr { cursor: pointer; }
.listing tbody tr:hover, .listing tbody tr:focus {
  background: rgba(78,201,255,.08); outline: none;
}
.listing .lap-time { font-variant-numeric: tabular-nums; font-weight: 600; color: #4ec9ff; }
.empty { opacity: .6; padding: 1.2rem 0; }

.lap-view { margin-top: 2.4rem; }
.lap-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.6rem; }
@media (max-width: 760px) { .lap-grid { grid-template-columns: 1fr; } }
.lap-map svg, .lap-traces svg { width: 100%; height: auto; display: block; }
.lap-map figcaption, .lap-traces figcaption {
  font-size: .78rem; opacity: .6; margin-top: .4rem;
}
.key { padding: 0 .35rem; border-radius: 3px; color: #0b0f14; }
.key.slow { background: hsl(10, 85%, 58%); }
.key.fast { background: hsl(190, 85%, 58%); }
.lap-facts { display: grid; grid-template-columns: auto 1fr; gap: .3rem 1rem; margin: 1.2rem 0; }
.lap-facts dt { opacity: .6; font-size: .82rem; }
.lap-facts dd { margin: 0; font-variant-numeric: tabular-nums; }
.copy-row { display: flex; gap: .6rem; align-items: center; margin: .5rem 0; }
.copy-row code {
  background: var(--panel, #11161d); border: 1px solid var(--line, #223);
  border-radius: 5px; padding: .35rem .55rem; font-size: .85rem; word-break: break-all;
}
.copy-row button {
  background: transparent; color: inherit; border: 1px solid var(--line, #223);
  border-radius: 5px; padding: .35rem .7rem; cursor: pointer; font: inherit;
}
.copy-row button:hover { border-color: #4ec9ff; color: #4ec9ff; }

/* The four numbers at the top: what is actually in here. */
.tally { display: flex; flex-wrap: wrap; gap: 2.2rem; margin: 1.8rem 0 .4rem; }
.tally div { display: flex; flex-direction: column; gap: .15rem; }
.tally strong { font-size: 1.9rem; line-height: 1; color: #4ec9ff; font-variant-numeric: tabular-nums; }
.tally span { font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; opacity: .55; }

/* A tile per circuit — the way in for somebody who does not yet know what
   they are looking for. */
.tiles { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.2rem 0 1.8rem; }
.tile {
  display: flex; flex-direction: column; gap: .15rem; align-items: flex-start;
  background: var(--panel, #11161d); color: inherit; font: inherit; cursor: pointer;
  border: 1px solid var(--line, #223); border-radius: 8px; padding: .5rem .8rem;
}
.tile:hover { border-color: #4ec9ff; }
.tile.on { border-color: #4ec9ff; background: rgba(78,201,255,.1); }
.tile-name { font-size: .92rem; }
.tile small { font-size: .72rem; opacity: .55; }
.clear {
  background: transparent; color: inherit; font: inherit; cursor: pointer;
  border: 1px solid var(--line, #223); border-radius: 6px; padding: .45rem .8rem;
}
.clear:hover { border-color: #4ec9ff; color: #4ec9ff; }

/* One heading per circuit, and its own table under it. */
.track-head {
  display: flex; align-items: baseline; gap: .7rem;
  margin: 1.8rem 0 .4rem; font-size: 1.05rem; letter-spacing: .02em;
}
.track-head small { font-size: .74rem; opacity: .5; font-weight: 400; }
.listing table { margin-bottom: .4rem; }
.listing .driver { font-weight: 500; }
.listing .car { opacity: .78; font-size: .86rem; }
.listing .lap-time.best { color: #57d977; }
.listing .has-setup { color: #57d977; font-size: .84rem; }

/* The readout under the map, while the pointer is on the lap. */
.at-cursor {
  display: grid; grid-template-columns: auto 1fr; gap: .2rem .9rem;
  margin: .8rem 0 0; padding: .7rem .8rem;
  background: var(--panel, #11161d); border: 1px solid var(--line, #223);
  border-radius: 8px; font-size: .85rem;
}
.at-cursor dt { opacity: .55; }
.at-cursor dd { margin: 0; font-variant-numeric: tabular-nums; }
.lap-map svg, .lap-traces svg { cursor: crosshair; }

/* ── the type, and the product look ──────────────────────────────────
   **Sentences in a proportional face, names in a display face, numbers and
   labels still in monospace.** Every word on the site used to be monospace,
   which is the terminal's voice — right for a readout, tiring for a paragraph,
   and the reason the site read as a technical document rather than as three
   programs somebody might want. The two faces are the launcher's own (Inter and
   Rajdhani, both under the SIL Open Font Licence, the texts beside them), so
   the site and the newest program look like one family.

   Served from this domain, subset to Latin, `swap` so text is never invisible
   while they load. On the github.io mirror they do not exist and the system's
   own sans takes over, which is the fallback written here anyway. */
@font-face{
  font-family:"Inter";src:url("/assets/fonts/inter.woff2") format("woff2");
  font-weight:100 900;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Rajdhani";src:url("/assets/fonts/rajdhani-bold.woff2") format("woff2");
  font-weight:700;font-style:normal;font-display:swap;
}
:root{
  --sans:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --display:"Rajdhani","Inter",system-ui,-apple-system,"Segoe UI",sans-serif;
}
body{font-family:var(--sans);font-size:16px;line-height:1.65;letter-spacing:-.003em}
/* Monospace where it is a readout, a label or something to type. */
code,pre,kbd,samp,nav.tabs,.term-title,.crumbs,h2,.stat span,.demo,.tag,
.sim-badge,.releases time,.releases .now,.go,.fold .blurb,table th{font-family:var(--mono)}
.stat b{font-family:var(--display);font-size:30px;letter-spacing:.01em}
.h1,.tagline,.card h3,h3{font-family:var(--display);letter-spacing:.01em}
.h1{font-size:clamp(28px,4.2vw,44px);line-height:1.08}
.h1 span{font-family:var(--sans);letter-spacing:0}
/* A page's own name, the first half of its H1, in the display face: it was
   the same small grey line as the phrase after it, so a page opened on a
   breadcrumb and two quiet lines before anything said what it was. */
.h1 span:first-child{
  font-family:var(--display);color:var(--text);font-weight:700;
  font-size:clamp(24px,3vw,32px);line-height:1.15;letter-spacing:.01em;margin-top:0;
}
.term-body > .h1{margin-bottom:18px}
.tagline{font-size:clamp(20px,2.8vw,27px);line-height:1.2}
.card h3,h3{font-size:19px}
.btn{font-family:var(--display);font-size:16px;letter-spacing:.05em;text-transform:uppercase;padding:11px 18px}
.btn.solid{background:var(--highlight);color:#06060b}
.btn.solid:hover{background:#3cc8ff}
.sub,.lede{font-size:16.5px}
.card p{font-size:15px}
figcaption{font-size:14px}

/* ── the hero ────────────────────────────────────────────────────────
   Words on the left, the programs themselves on the right. The words are the
   page: they are real text in the document, in the order a reader and a
   search engine meet them, and the pictures beside them are decoration with
   alt text — never a headline baked into an image. */
.hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.08fr);gap:30px;align-items:center}
.hero .eyebrow{
  font-family:var(--mono);font-size:12px;letter-spacing:.16em;color:var(--highlight);
  margin:0 0 14px;display:flex;flex-wrap:wrap;gap:6px 14px;
}
.hero .eyebrow span::before{content:"●";color:var(--accent);margin-right:7px;font-size:9px;vertical-align:2px}
.hero .h1{font-size:clamp(34px,5.2vw,58px);margin-bottom:14px}
/* The product's name as a small line above the headline: it is in the H1, where
   a search engine wants it, without making the headline four lines long. */
.hero .h1 .brand{
  display:block;font-family:var(--mono);font-weight:600;font-size:13px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--accent);margin-bottom:12px;text-shadow:none;
}
.hero .h1 em{
  font-style:normal;
  /* Warm only. A run from the cyan to the orange passes through a grey-green
     in the middle, and that is the colour the middle word came out. */
  background:linear-gradient(92deg,#ffd27a 0%,var(--accent) 45%,#ff8a1f 70%,#ffd27a 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  text-shadow:none;
}
.hero .h1 span{font-size:clamp(15px,1.7vw,18px);color:var(--dim);margin-top:14px;font-weight:400;line-height:1.5;text-shadow:none}
.hero .h1{color:#f2f2f7}
.hero .sub{max-width:54ch}
.hero .cta{margin-top:20px}
@media (max-width:900px){.hero{grid-template-columns:1fr}}

/* Three screenshots, fanned and floating. Transform and opacity only, so it
   runs on the compositor and costs nothing per frame on the main thread. */
.stage{position:relative;aspect-ratio:16/12;perspective:1400px}
.stage img{
  position:absolute;width:78%;height:auto;border-radius:10px;
  border:1px solid color-mix(in srgb,var(--highlight) 30%,var(--border));
  box-shadow:0 30px 60px -20px rgba(0,0,0,.8),0 0 50px -10px rgba(0,180,255,.22);
  background:#000;
}
.stage .s1{left:0;top:6%;transform:rotateY(14deg) rotateZ(-2deg);z-index:1;opacity:.82}
.stage .s2{right:0;top:0;transform:rotateY(-10deg) rotateZ(1.5deg);z-index:2;opacity:.9}
.stage .s3{left:11%;bottom:0;width:80%;transform:rotateX(6deg);z-index:3;
  border-color:color-mix(in srgb,var(--accent) 45%,var(--border));
  box-shadow:0 30px 70px -18px rgba(0,0,0,.85),0 0 60px -12px rgba(255,165,0,.3)}
.stage::before{
  content:"";position:absolute;inset:8% 6%;z-index:0;filter:blur(60px);opacity:.55;
  background:radial-gradient(closest-side,rgba(0,180,255,.55),transparent 70%),
             radial-gradient(closest-side at 70% 80%,rgba(255,165,0,.4),transparent 70%);
}
@media (prefers-reduced-motion:no-preference){
  .stage .s1{animation:floatA 9s ease-in-out infinite}
  .stage .s2{animation:floatB 11s ease-in-out infinite}
  .stage .s3{animation:floatC 10s ease-in-out infinite}
  .stage::before{animation:breathe 8s ease-in-out infinite}
  .hero .h1 em{background-size:200% 100%;animation:sheen 7s ease-in-out infinite}
}
@keyframes floatA{50%{transform:rotateY(14deg) rotateZ(-2deg) translateY(-10px)}}
@keyframes floatB{50%{transform:rotateY(-10deg) rotateZ(1.5deg) translateY(9px)}}
@keyframes floatC{50%{transform:rotateX(6deg) translateY(-7px)}}
@keyframes breathe{50%{opacity:.85;transform:scale(1.06)}}
@keyframes sheen{50%{background-position:100% 0}}
@media (max-width:900px){.stage{max-width:620px;margin:10px auto 0;width:100%}}

/* ── three programs ─────────────────────────────────────────────────── */
.trio{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
@media (max-width:900px){.trio{grid-template-columns:1fr}}
.product{
  position:relative;display:flex;flex-direction:column;border-radius:12px;overflow:hidden;
  border:1px solid var(--border);background:linear-gradient(180deg,#12121c,#0d0d14);
  color:var(--text);text-decoration:none;
  transition:transform .25s ease,border-color .25s ease,box-shadow .25s ease;
}
.product:hover{text-decoration:none;transform:translateY(-4px);
  border-color:var(--highlight);box-shadow:0 18px 50px -18px rgba(0,180,255,.45)}
.product.orange:hover{border-color:var(--accent);box-shadow:0 18px 50px -18px rgba(255,165,0,.45)}
.product .shot{aspect-ratio:16/10;overflow:hidden;background:#05050a;border-bottom:1px solid var(--border)}
.product .shot img{width:100%;height:100%;object-fit:cover;object-position:top left;display:block;
  transition:transform .6s ease}
.product:hover .shot img{transform:scale(1.04)}
.product .body{padding:18px 20px 20px;display:flex;flex-direction:column;gap:8px;flex:1}
.product .kind{font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;color:var(--highlight)}
.product.orange .kind{color:var(--accent)}
.product h3{margin:0;font-size:26px;line-height:1.1}
.product p{margin:0;color:var(--dim);font-size:15px}
.product .meta{margin-top:auto;padding-top:10px;display:flex;flex-wrap:wrap;gap:6px}
.product .meta span{
  font-family:var(--mono);font-size:11.5px;color:var(--text);
  border:1px solid var(--border);border-radius:99px;padding:2px 10px;background:#0b0b12;
}
.product .new{
  position:absolute;top:12px;right:12px;z-index:2;font-family:var(--mono);font-size:11px;
  letter-spacing:.12em;color:#06060b;background:var(--accent);border-radius:99px;padding:3px 10px;
}

/* A moving picture of a program: an animated WebP, real frames of the real
   thing. Framed like a window so it reads as the program and not as an ad. */
.reel{
  border:1px solid var(--border);border-radius:12px;overflow:hidden;background:#05050a;
  box-shadow:0 30px 80px -30px rgba(0,180,255,.35);margin:0 0 12px;
}
.reel .bar{display:flex;align-items:center;gap:7px;padding:9px 12px;background:#15151f;
  font-family:var(--mono);font-size:12px;color:var(--dim)}
.reel .bar i{width:10px;height:10px;border-radius:50%;background:var(--border);display:block}
.reel .bar i:nth-child(1){background:var(--bad)}.reel .bar i:nth-child(2){background:var(--warn)}
.reel .bar i:nth-child(3){background:var(--good)}
.reel .bar b{margin-left:8px;font-weight:400}
.reel img{display:block;width:100%;height:auto}

/* Feature tiles with an icon-sized mark, for pages that sell rather than
   document. Plain cards underneath, so they degrade to what every other page
   already has. */
.perks{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:14px}
.perk{border:1px solid var(--border);border-radius:10px;padding:18px 20px;
  background:linear-gradient(180deg,#11111a,#0d0d14)}
.perk .ico{width:34px;height:34px;border-radius:9px;display:grid;place-items:center;margin-bottom:12px;
  background:rgba(0,180,255,.1);color:var(--highlight);font-family:var(--display);font-size:19px}
.perk.o .ico{background:rgba(255,165,0,.12);color:var(--accent)}
.perk h3{margin:0 0 6px;font-size:20px}
.perk p{margin:0;color:var(--dim);font-size:15px}

/* Two pictures side by side, stacking on a phone. */
.pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
@media (max-width:760px){.pair{grid-template-columns:1fr}}
.pair figure{margin:0}

/* Things appear as they scroll in, where the browser can tie it to the
   scroll; everywhere else they are simply there. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion:no-preference){
    .product,.perk,.reel,.pair figure{
      animation:risein linear both;animation-timeline:view();animation-range:entry 0% entry 30%;
    }
  }
}
/* `translate`, not `transform`: a filled animation on `transform` would pin
   it, and the lift a product card gives under the pointer would never happen. */
@keyframes risein{from{opacity:0;translate:0 18px}to{opacity:1;translate:0 0}}

/* ════════════════════════════════════════════════════════════════════
   THE PRODUCT LOOK, AND ITS MOVEMENT
   ════════════════════════════════════════════════════════════════════
   Everything above is the site as it was drawn: a terminal window with the
   pages as its tab strip. This is the layer that turns it into three
   products — a glass bar across the top, light drifting behind the page,
   cards that answer the pointer, and blocks that arrive as they are reached.
   It is written as a layer on purpose: every page's markup is unchanged,
   so each rule here restyles a class the fragments already use.

   **The movement is opt-in twice.** `motion.js` adds `.js` to the root and
   does nothing else for a reader whose system asks for reduced motion; and
   every animation here is either gated on that class or on
   `prefers-reduced-motion: no-preference`. Without the script, every block
   is simply where it belongs. */

:root{
  --bg:#07080d;
  --line:rgba(130,150,185,.16);
  --line-strong:rgba(130,150,185,.3);
  --glass:rgba(17,19,30,.62);
  --card-top:rgba(24,27,42,.78);
  --card-bottom:rgba(13,14,23,.78);
  --glow-blue:rgba(0,180,255,.35);
  --glow-orange:rgba(255,165,0,.28);
  --ease:cubic-bezier(.2,.75,.2,1);
  --bar:64px;
}
html{background:var(--bg)}
body{background:transparent;position:relative;overflow-x:clip}
/* The scanlines go: they were the terminal's, and on a phone they read as a
   dirty screen. A faint grid of points instead, fading out down the page. */
body::before{
  z-index:-1;
  background:radial-gradient(rgba(255,255,255,.05) 1px,transparent 1.2px) 0 0/28px 28px;
  -webkit-mask-image:linear-gradient(180deg,#000 0,rgba(0,0,0,.5) 40%,transparent 85%);
  mask-image:linear-gradient(180deg,#000 0,rgba(0,0,0,.5) 40%,transparent 85%);
}
.sweep{display:none}

/* ── light behind the page ─────────────────────────────────────────── */
.aurora{position:fixed;inset:0;z-index:-2;overflow:hidden;pointer-events:none;contain:strict}
.aurora i{
  position:absolute;width:62vmax;height:62vmax;border-radius:50%;opacity:.5;
  background:radial-gradient(closest-side,var(--glow-blue),transparent 72%);
  top:-28vmax;left:-14vmax;will-change:transform;
}
.aurora i:nth-child(2){
  background:radial-gradient(closest-side,var(--glow-orange),transparent 72%);
  top:auto;bottom:-34vmax;left:auto;right:-20vmax;opacity:.38;
}
.aurora i:nth-child(3){
  background:radial-gradient(closest-side,rgba(120,80,255,.22),transparent 72%);
  top:30vh;left:40vw;width:44vmax;height:44vmax;opacity:.35;
}
@media (prefers-reduced-motion:no-preference){
  .aurora i{animation:drift1 34s ease-in-out infinite alternate}
  .aurora i:nth-child(2){animation:drift2 41s ease-in-out infinite alternate}
  .aurora i:nth-child(3){animation:drift3 29s ease-in-out infinite alternate}
}
@keyframes drift1{to{transform:translate(18vw,14vh) scale(1.15)}}
@keyframes drift2{to{transform:translate(-16vw,-12vh) scale(1.1)}}
@keyframes drift3{to{transform:translate(-22vw,10vh) scale(.85)}}

/* ── the bar across the top ────────────────────────────────────────── */
.site{
  position:sticky;top:0;z-index:40;
  background:rgba(8,9,15,.55);
  -webkit-backdrop-filter:blur(16px) saturate(150%);backdrop-filter:blur(16px) saturate(150%);
  border-bottom:1px solid var(--line);
  transition:background-color .3s ease,box-shadow .3s ease;
}
.site.scrolled{background:rgba(8,9,15,.82);box-shadow:0 14px 40px -24px #000}
.site-in{
  max-width:1240px;margin:0 auto;padding:0 var(--pad);
  height:var(--bar);display:flex;align-items:center;gap:18px;
  transition:height .3s var(--ease);
}
.site.scrolled .site-in{height:56px}
.logo{display:flex;align-items:center;gap:10px;flex:none;color:var(--text)}
.logo:hover{text-decoration:none}
.logo .mark{width:30px;height:30px;margin:0;vertical-align:0;transition:transform .5s var(--ease)}
.logo:hover .mark{transform:rotate(-8deg) scale(1.06)}
.logo span{font-family:var(--display);font-weight:700;font-size:19px;letter-spacing:.1em;white-space:nowrap}
.logo b{color:var(--highlight)}
.read{
  position:absolute;left:0;right:0;bottom:-1px;height:2px;
  background:linear-gradient(90deg,var(--highlight),#7a7cff 50%,var(--accent));
  transform:scaleX(var(--read,0));transform-origin:0 50%;
}
.site nav.tabs::after{display:none}
.site nav.tabs{
  position:static;flex:1;justify-content:center;flex-wrap:nowrap;gap:2px;
  padding:0;background:none;border:0;box-shadow:none;
}
.site nav.tabs a,.site nav.tabs .games{
  padding:7px 11px;border:0;border-radius:999px;font-size:11.5px;letter-spacing:.08em;
  color:#a3a8b8;transition:color .2s ease,background-color .2s ease;
}
.site nav.tabs a:hover,.site nav.tabs .games:hover,
.site nav.tabs .has-menu:focus-within .games{color:#fff;background:rgba(255,255,255,.06)}
.site nav.tabs a.on,.site nav.tabs .has-menu.on .games{color:var(--highlight);background:rgba(0,180,255,.12)}
.site nav.tabs a.pick{color:var(--accent)}
.site nav.tabs a.pick.on{background:rgba(255,165,0,.12)}
.site nav.tabs .menu{
  top:calc(100% + 10px);left:50%;translate:-50% 0;min-width:240px;
  background:rgba(14,15,24,.94);
  -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
  border:1px solid var(--line-strong);border-radius:14px;padding:6px;
  box-shadow:0 30px 60px -20px #000,0 0 0 1px rgba(0,0,0,.4);
}
/* A bridge across the gap, so moving the pointer down to the menu does not
   pass over nothing and close it. */
.site nav.tabs .menu::before{content:"";position:absolute;left:0;right:0;top:-12px;height:12px}
.site nav.tabs .menu a{border-radius:9px;padding:9px 12px;font-size:12.5px;color:var(--text)}
.site nav.tabs .menu a:hover{background:rgba(0,180,255,.1);color:#fff}
.site nav.tabs .menu a.all{border-top:1px solid var(--line);border-radius:0 0 9px 9px}
.btn.get{flex:none;padding:8px 18px;font-size:14px;border-radius:10px}
.burger{display:none}

@media (max-width:1180px){
  .site nav.tabs{display:none}
  .site-in{justify-content:space-between}
  .burger{
    display:grid;gap:5px;place-content:center;width:42px;height:42px;flex:none;
    background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:10px;cursor:pointer;
  }
  .burger i{display:block;width:18px;height:2px;border-radius:2px;background:var(--text);
    transition:transform .3s var(--ease),opacity .2s ease}
  .site.open .burger i:nth-child(1){transform:translateY(7px) rotate(45deg)}
  .site.open .burger i:nth-child(2){opacity:0}
  .site.open .burger i:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
  .site .btn.get{margin-left:auto}
  .site.open nav.tabs{
    display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:2px;
    position:absolute;left:0;right:0;top:100%;height:calc(100vh - var(--bar));
    max-height:calc(100vh - var(--bar));overflow:auto;
    padding:12px var(--pad) 20px;background:rgba(8,9,15,.97);
    border-bottom:1px solid var(--line);
  }
  .site.open nav.tabs a,.site.open nav.tabs .games{font-size:13px;padding:12px 14px;border-radius:10px;text-align:left}
  .site.open nav.tabs .games{width:100%;color:var(--dim);background:none;cursor:default}
  .site.open nav.tabs .games::after{display:none}
  .site.open nav.tabs .menu{
    display:block;position:static;translate:none;min-width:0;padding:0 0 6px 10px;
    background:none;border:0;box-shadow:none;animation:none;
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
  .site.open nav.tabs .menu::before{display:none}
  /* Without the script there is no button to press, so the strip shows. */
  html:not(.js) .site nav.tabs{display:flex;flex-wrap:wrap;justify-content:flex-start}
  html:not(.js) .burger{display:none}
  html:not(.js) .site-in{height:auto;flex-wrap:wrap;padding-top:10px;padding-bottom:10px}
}
@media (max-width:520px){
  .logo span{font-size:16px}
  .logo .mark{width:26px;height:26px}
  .btn.get{padding:7px 12px;font-size:13px}
}

/* ── the page's opening ────────────────────────────────────────────── */
.wrap{max-width:1200px}
main.term-body{
  position:relative;border:0;border-radius:0;background:none;
  margin:0;padding:64px 0 34px;
}
.crumbs{font-size:12px;letter-spacing:.06em;color:var(--dim);margin-bottom:22px}
.crumbs a{color:var(--dim)}
.crumbs a:hover{color:var(--highlight)}
.hero{gap:48px}
.hero .h1{font-size:clamp(40px,5.6vw,74px);line-height:1.02;letter-spacing:-.005em;margin-bottom:20px}
.hero .h1 .brand{
  display:flex;width:max-content;align-items:center;gap:8px;margin-bottom:18px;
  font-family:var(--mono);font-size:12px;letter-spacing:.16em;font-weight:600;
  color:var(--accent);padding:6px 12px;border-radius:999px;
  background:rgba(255,165,0,.08);border:1px solid rgba(255,165,0,.28);
}
.hero .h1 em{
  font-style:normal;
  background:linear-gradient(100deg,var(--accent),#ffcf6b 30%,var(--highlight) 65%,#7a7cff);
  background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;
}
.hero .h1 span{color:#9ba0b2;font-size:clamp(16px,1.6vw,19px);line-height:1.55;margin-top:18px}
.hero .sub{color:#b6bac8;font-size:17px;max-width:56ch}
.term-body > .h1{font-size:clamp(34px,4.6vw,58px);line-height:1.06;margin-bottom:22px;color:#f3f4f8}
.term-body > .h1 span:first-child{font-size:clamp(15px,1.4vw,18px);color:var(--highlight);
  font-family:var(--mono);letter-spacing:.14em;text-transform:uppercase;margin:0 0 14px}
.term-body > p,.term-body > .sub{font-size:17px;color:#b6bac8;max-width:70ch}
.stage{
  transform:perspective(1400px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));
  transform-style:preserve-3d;transition:transform .7s var(--ease);
}
.stage img{border-radius:14px;border-color:var(--line-strong)}

/* ── sections and their headings ───────────────────────────────────── */
section{padding:88px 0 8px}
section::before{display:none}
h2{
  font-size:clamp(15px,1.5vw,18px);letter-spacing:.16em;color:#f3f4f8;
  text-shadow:none;margin:0 0 18px;gap:16px;
}
h2::before{
  content:"";width:26px;height:3px;border-radius:3px;flex:none;
  background:linear-gradient(90deg,var(--accent),var(--highlight));
}
h2::after{background:linear-gradient(90deg,var(--line-strong),transparent)}
.lede{font-size:18px;line-height:1.7;color:#a8adbd;max-width:68ch;margin-bottom:34px}
.lede b{color:#eef0f6}
p{color:inherit}

/* ── buttons ───────────────────────────────────────────────────────── */
.btn{
  position:relative;overflow:hidden;border-radius:12px;padding:13px 22px;
  transition:transform .25s var(--ease),box-shadow .25s ease,background-color .25s ease,border-color .25s ease,color .25s ease;
}
.btn.solid,.btn:not(.ghost){
  background:linear-gradient(135deg,#19c0ff,var(--highlight) 45%,#0090e0);color:#04060b;border-color:transparent;
  box-shadow:0 10px 30px -12px var(--glow-blue),inset 0 1px 0 rgba(255,255,255,.35);
}
.btn.solid:hover,.btn:not(.ghost):hover{color:#04060b;transform:translateY(-2px);
  box-shadow:0 16px 40px -12px rgba(0,180,255,.6),inset 0 1px 0 rgba(255,255,255,.4)}
.btn.ghost{background:rgba(255,255,255,.04);border:1px solid var(--line-strong);color:var(--text)}
.btn.ghost:hover{background:rgba(0,180,255,.08);border-color:var(--highlight);color:#fff;transform:translateY(-2px)}
.btn::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120px circle at var(--mx,50%) var(--my,50%),rgba(255,255,255,.28),transparent 70%);
  opacity:0;transition:opacity .25s ease;
}
.btn:hover::after{opacity:1}

/* ── cards, products, perks, stats ─────────────────────────────────── */
.card,.product,.perk,.stat,details.fold,.releases,.note,.demo{
  border:1px solid var(--line);border-radius:16px;
  background:linear-gradient(180deg,var(--card-top),var(--card-bottom));
}
.card,.product,.perk{position:relative;overflow:hidden;isolation:isolate}
.card{padding:22px 22px 20px}
.card::before,.product::before,.perk::before,.stat::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(300px circle at var(--mx,50%) var(--my,0%),rgba(0,180,255,.16),transparent 65%);
  opacity:0;transition:opacity .35s ease;
}
.card:hover::before,.product:hover::before,.perk:hover::before,.stat:hover::before{opacity:1}
.card,.product,.perk{transition:transform .35s var(--ease),border-color .35s ease,box-shadow .35s ease}
.card:hover,.perk:hover{transform:translateY(-4px);border-color:rgba(0,180,255,.4);
  box-shadow:0 24px 50px -28px rgba(0,180,255,.5)}
.card:not(a):hover{border-color:rgba(0,180,255,.3)}
.card.pick:hover,.product.orange:hover{border-color:rgba(255,165,0,.5);box-shadow:0 24px 50px -28px rgba(255,165,0,.5)}
.product{border-radius:18px}
.product:hover{transform:translateY(-6px)}
.product .shot{border-bottom:1px solid var(--line)}
.product .shot img{transition:transform .9s var(--ease)}
.product:hover .shot img{transform:scale(1.06)}
.grid,.sims,.trio{gap:18px}
.stats{gap:14px;background:none;border:0}
.stat{position:relative;overflow:hidden;isolation:isolate;padding:22px 14px}
.stat b{
  background:linear-gradient(180deg,#fff,var(--highlight));-webkit-background-clip:text;background-clip:text;color:transparent;
  font-size:clamp(28px,3vw,40px);text-shadow:none;
}
.stat:hover{background:linear-gradient(180deg,var(--card-top),var(--card-bottom))}

/* ── pictures ──────────────────────────────────────────────────────── */
figure img{
  border-radius:14px;border:1px solid var(--line-strong);
  box-shadow:0 30px 70px -36px #000,0 0 0 1px rgba(0,0,0,.35);
  transform:perspective(1100px) rotateX(var(--tx,0deg)) rotateY(var(--ty,0deg));
  transition:transform .6s var(--ease),box-shadow .4s ease,border-color .4s ease;
}
figure:hover img{border-color:rgba(0,180,255,.45);box-shadow:0 40px 80px -36px #000,0 0 50px -18px var(--glow-blue)}
figcaption{color:#9196a8;margin-top:12px}

/* ── the reels ─────────────────────────────────────────────────────── */
.reel{
  position:relative;border-radius:18px;border:1px solid var(--line-strong);overflow:hidden;
  background:#05060a;box-shadow:0 40px 90px -40px #000,0 0 70px -30px var(--glow-blue);
}
.reel .bar{background:rgba(20,22,34,.9);border-bottom:1px solid var(--line);color:var(--dim);font-family:var(--mono);font-size:12px}
.reel .frames{display:grid;overflow:hidden;
  transform:perspective(1300px) rotateX(var(--tx,0deg)) rotateY(var(--ty,0deg));transition:transform .6s var(--ease)}
.reel .frames > *{grid-area:1/1;display:block;opacity:0;transition:opacity 1.1s ease}
.reel .frames > :first-child{opacity:1}
.js .reel[data-reel] .frames > :first-child{opacity:0}
.js .reel[data-reel] .frames > .on{opacity:1}
.reel .frames img{display:block;width:100%;height:auto}
@media (prefers-reduced-motion:no-preference){
  .reel .frames > .on img{animation:kenburns 7s ease-out both}
}
@keyframes kenburns{from{transform:scale(1.07) translate(-1%,-1%)}to{transform:none}}
.reel .cap{
  position:absolute;left:16px;bottom:16px;max-width:calc(100% - 200px);
  font-size:13px;color:#e8eaf2;padding:7px 12px;border-radius:999px;
  background:rgba(8,9,15,.72);border:1px solid var(--line-strong);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.reel .cap:empty{display:none}
.reel .dots{position:absolute;right:16px;bottom:22px;display:flex;gap:6px}
.reel .dots button{
  width:22px;height:4px;border-radius:4px;border:0;padding:0;cursor:pointer;
  background:rgba(255,255,255,.22);position:relative;overflow:hidden;
}
.reel .dots button::after{content:"";position:absolute;inset:0;background:var(--highlight);transform:scaleX(0);transform-origin:0 50%}
.reel .dots button.on::after{transform:scaleX(1)}
@media (prefers-reduced-motion:no-preference){
  .reel .dots button.on::after{animation:dotfill var(--every,3600ms) linear both}
  .reel.held .dots button.on::after{animation-play-state:paused}
}
@keyframes dotfill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@media (max-width:620px){
  .reel .cap{display:none}
  .reel .dots{left:0;right:0;justify-content:center;bottom:12px}
  .reel .dots button{width:14px}
}

/* ── folds, tables, notes, footer ──────────────────────────────────── */
details.fold{overflow:hidden;margin-bottom:12px}
details.fold > summary{border-radius:16px}
details.fold > summary:hover{background:rgba(0,180,255,.06)}
details.fold[open] > summary{border-bottom-color:var(--line)}
table{border-radius:12px;overflow:hidden}
th,td{border-bottom-color:var(--line)}
pre{border-radius:12px;border-color:var(--line);background:rgba(6,7,12,.85)}
.note{border-left:3px solid var(--warn);padding:14px 18px}
footer{
  margin-top:110px;padding:40px 0 50px;border-top:0;position:relative;
}
footer::before{
  content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,transparent,var(--line-strong) 20%,rgba(0,180,255,.5) 50%,var(--line-strong) 80%,transparent);
}
footer .cols b{color:var(--highlight);letter-spacing:.14em}
footer .cols a{color:#b3b7c6;padding:3px 0;transition:color .2s ease,transform .2s ease}
footer .cols a:hover{color:#fff;text-decoration:none;transform:translateX(3px)}

/* ── arriving ──────────────────────────────────────────────────────────
   `backwards`, not `both`: once a block has arrived it goes back to its own
   rules, so a card can still lift under the pointer. Held invisible only
   until its turn — and only when the script is there to give it one. */
.js .rv:not(.in){opacity:0}
.js .rv.in{animation:rise .42s var(--ease) backwards;animation-delay:calc(var(--i,0) * 35ms)}
/* Opacity and transform only: the two a browser animates on the graphics
   card without laying the page out again. A blur here cost a repaint of
   every block on every frame of its arrival. */
@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}

@media (max-width:760px){
  :root{--bar:58px}
  main.term-body{padding:38px 0 20px}
  section{padding:60px 0 4px}
  h2{font-size:14px;letter-spacing:.12em}
  .lede{font-size:16.5px}
  .hero .h1{font-size:clamp(34px,10vw,46px)}
  footer{margin-top:70px}
}
@media (prefers-reduced-motion:reduce){
  .js .rv:not(.in){opacity:1}
  .stage,figure img,.reel .frames{transform:none}
}

/* ── an inner page's opening ───────────────────────────────────────────
   The page's name large, what it is underneath, the one-line claim under
   that — and the mark, drawn big and faint, drifting on the right. The
   landing and the launcher open on their pictures instead (`.hero`). */
.term-body > .h1{margin:0 0 22px;max-width:22ch}
.term-body > .h1 span:first-child{
  display:block;margin:0 0 16px;
  font-family:var(--display);font-weight:700;text-transform:none;letter-spacing:-.005em;
  font-size:clamp(46px,6.4vw,84px);line-height:.98;
  background:linear-gradient(180deg,#fff 35%,#a9bddc);-webkit-background-clip:text;background-clip:text;color:transparent;
}
.term-body > .h1 span:first-child + span{
  display:block;max-width:46ch;font-family:var(--sans);font-weight:400;letter-spacing:0;
  font-size:clamp(17px,1.7vw,21px);line-height:1.5;color:#a3a8b9;
}
.term-body > .tagline{
  font-size:clamp(22px,2.6vw,32px);line-height:1.25;color:#d5d8e3;max-width:30ch;margin:0 0 20px;
}
.term-body > .tagline b{
  background:linear-gradient(100deg,var(--accent),#ffd27a 45%,var(--highlight));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
main.term-body:not(:has(.hero))::after{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  right:0;top:30px;width:min(34vw,420px);aspect-ratio:1;opacity:.11;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%2300b4ff'/%3E%3Cstop offset='1' stop-color='%23ffa500'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M118 118 L262 256 L118 394' fill='none' stroke='url(%23g)' stroke-width='40' stroke-linecap='round' stroke-linejoin='round'/%3E%3Crect x='307' y='338' width='87' height='44' rx='14' fill='%23ffa500'/%3E%3C/svg%3E") center/contain no-repeat;
  filter:drop-shadow(0 0 40px rgba(0,180,255,.5));
}
@media (prefers-reduced-motion:no-preference){
  main.term-body:not(:has(.hero))::after{animation:hover 9s ease-in-out infinite}
}
@keyframes hover{50%{transform:translateY(-16px) rotate(-3deg)}}
@media (max-width:900px){main.term-body:not(:has(.hero))::after{display:none}}
/* The bar's own button keeps its size: the phone rules above stretch every
   `.btn` across its row, which in the bar pushed the menu button off it. */
.site .btn.get{flex:0 0 auto;width:auto}
/* Open on a phone, the bar is solid. A child reaching outside an element
   with `backdrop-filter` is drawn through it in Chrome, so the page showed
   through the open menu however opaque its own background was. */
@media (max-width:1180px){
  .site.open{-webkit-backdrop-filter:none;backdrop-filter:none;background:#08090f}
  .site.open nav.tabs{background:#08090f}
}

/* ── the light that follows the pointer ────────────────────────────────
   One fixed disc for the whole window, moved with `transform` alone, so it
   reaches every edge of the screen and costs nothing to move. It used to be
   painted inside the opening block, which clipped it to a rectangle. */
.glow{
  position:fixed;left:0;top:0;z-index:-1;pointer-events:none;
  width:900px;height:900px;margin:-450px 0 0 -450px;border-radius:50%;
  background:radial-gradient(closest-side,rgba(0,180,255,.13),rgba(0,180,255,.05) 45%,transparent 72%);
  opacity:0;transition:opacity .5s ease;will-change:transform;
}
.glow.on{opacity:1}

/* A program's card shows its screen whole across the top: the pictures are
   16:9-ish and 16:10, and a 16:10 box cropped the launcher's right side. */
.product .shot{aspect-ratio:16/9}
.product .shot img{object-position:top center}
/* A card that carries its own buttons — the download page's, one per
   program — with its "more" link under them. */
.card .cta{margin:16px 0 12px;gap:8px}
.card .cta .btn{flex:1 1 0;text-align:center;padding:10px 14px;font-size:15px}
.card > a.go{display:inline-block;font-family:var(--mono);font-size:12.5px;color:var(--dim)}
.card > a.go::after{content:" →";transition:transform .2s ease;display:inline-block}
.card > a.go:hover{color:var(--highlight);text-decoration:none}
.card > a.go:hover::after{transform:translateX(4px)}

/* ── the opening's three pictures ──────────────────────────────────────
   Solid, each with its name. They were translucent and overlapped, so the
   one in front showed the two behind through it and the three read as one
   muddle rather than as three programs. Now the two behind are dimmed, not
   see-through, and come up to full brightness under the pointer. */
.grid + .fineprint{margin-top:18px}
.stage{aspect-ratio:16/11.5}
.stage::before{inset:12% 8%}
.stage .shot{
  position:absolute;width:70%;opacity:1;z-index:1;
  transition:filter .5s ease,transform .6s var(--ease);
}
.stage .shot img{
  position:static;display:block;width:100%;height:auto;opacity:1;transform:none;animation:none;
  border-radius:12px;border:1px solid var(--line-strong);background:#05060a;
  box-shadow:0 34px 70px -24px rgba(0,0,0,.95),0 0 0 1px rgba(0,0,0,.5);
}
.stage .s1{left:0;top:0;filter:brightness(.62) saturate(.85);transform:rotate(-3deg)}
.stage .s2{right:0;top:9%;z-index:2;filter:brightness(.74) saturate(.9);transform:rotate(2.5deg)}
.stage .s3{left:13%;bottom:0;width:74%;z-index:3;transform:none}
.stage .s3 img{border-color:color-mix(in srgb,var(--accent) 50%,var(--border));
  box-shadow:0 40px 80px -24px rgba(0,0,0,.95),0 0 60px -16px rgba(255,165,0,.35)}
.stage:hover .s1,.stage:hover .s2{filter:none}
.stage .lbl{
  position:absolute;left:14px;top:-13px;z-index:1;white-space:nowrap;
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  padding:5px 11px;border-radius:999px;color:var(--highlight);
  background:rgba(8,9,15,.94);border:1px solid rgba(0,180,255,.35);
  box-shadow:0 8px 20px -8px #000;
}
.stage .lbl.o{color:var(--accent);border-color:rgba(255,165,0,.4)}
@media (prefers-reduced-motion:no-preference){
  .stage .s1{animation:floatA2 9s ease-in-out infinite}
  .stage .s2{animation:floatB2 11s ease-in-out infinite}
  .stage .s3{animation:floatC2 10s ease-in-out infinite}
}
@keyframes floatA2{50%{transform:rotate(-3deg) translateY(-10px)}}
@keyframes floatB2{50%{transform:rotate(2.5deg) translateY(9px)}}
@keyframes floatC2{50%{transform:translateY(-7px)}}
@media (max-width:620px){.stage .lbl{font-size:9.5px;padding:4px 8px;top:-11px}}
/* Spread so each of the three shows a real part of itself, and the middle
   one's name sits on its far side, clear of the first's. */
.stage{aspect-ratio:16/12.5}
.stage .s1{width:64%}
.stage .s2{width:64%;top:6%}
.stage .s2 .lbl{left:auto;right:14px}
.stage .s3{width:68%;left:16%}
/* A reel's caption wraps rather than trailing off in an ellipsis: every one
   of them is a sentence about the picture, and half a sentence says less. */
.reel .cap{white-space:normal;max-width:min(62%,560px);line-height:1.4;border-radius:12px;padding:8px 13px}

/* ── the download page: a column per program ───────────────────────────
   Each program's own buttons, the rest of its files, what it needs and where
   to read more — so nobody has to go to three pages to find three downloads. */
.dl-cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;align-items:stretch}
@media (max-width:980px){.dl-cols{grid-template-columns:1fr}}
.dl-col{
  position:relative;display:flex;flex-direction:column;gap:10px;padding:24px 22px 20px;
  border:1px solid var(--line);border-radius:18px;isolation:isolate;overflow:hidden;
  background:linear-gradient(180deg,var(--card-top),var(--card-bottom));
  transition:transform .35s var(--ease),border-color .35s ease,box-shadow .35s ease;
}
.dl-col::before{
  content:"";position:absolute;inset:0 0 auto;height:3px;z-index:1;
  background:linear-gradient(90deg,var(--highlight),transparent);
}
.dl-col.pick{border-color:rgba(255,165,0,.4)}
.dl-col.pick::before{background:linear-gradient(90deg,var(--accent),var(--highlight))}
.dl-col:hover{transform:translateY(-4px);border-color:rgba(0,180,255,.4);box-shadow:0 26px 60px -30px rgba(0,180,255,.5)}
.dl-col header{display:grid;grid-template-columns:1fr auto;align-items:end;gap:2px 10px}
.dl-kind{grid-column:1/-1;font-family:var(--mono);font-size:11.5px;letter-spacing:.16em;color:var(--highlight)}
.dl-kind.o{color:var(--accent)}
.dl-col h3{margin:0;font-size:28px;line-height:1.1}
.dl-ver{font-family:var(--mono);font-size:12.5px;color:var(--good);border:1px solid rgba(63,208,127,.35);
  border-radius:999px;padding:2px 9px;align-self:center}
.dl-col > p{margin:4px 0 6px;color:#a8adbd;font-size:15px}
.dl-main{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:2px 0 8px}
.dl-main .btn{display:flex;flex-direction:column;align-items:center;gap:1px;padding:11px 10px;text-align:center}
.dl-main .btn b{font-size:16px;letter-spacing:.06em}
.dl-main .btn small{font-family:var(--mono);font-size:11px;letter-spacing:.02em;text-transform:none;opacity:.75}
.dl-col h4{margin:8px 0 0;font-family:var(--mono);font-size:11px;letter-spacing:.16em;color:var(--dim);font-weight:600}
.dl-files{list-style:none;margin:0;padding:0;display:grid;gap:2px}
.dl-files li{display:flex;flex-wrap:wrap;justify-content:space-between;gap:2px 10px;padding:8px 0;
  border-bottom:1px solid var(--line);font-size:14px}
.dl-files li:last-child{border-bottom:0}
.dl-files span{color:var(--dim);font-size:12.5px}
.dl-files code{font-size:12px}
.dl-needs{margin:0;color:#a8adbd;font-size:13.5px}
.dl-col footer{margin-top:auto;padding-top:12px;display:flex;gap:16px;flex-wrap:wrap;border-top:1px solid var(--line)}
.dl-col footer a{font-family:var(--mono);font-size:12.5px;color:var(--dim)}
.dl-col footer a::after{content:" →";display:inline-block;transition:transform .2s ease}
.dl-col footer a:hover{color:var(--highlight);text-decoration:none}
.dl-col footer a:hover::after{transform:translateX(3px)}
.dl-col h4{text-transform:uppercase}

/* ── the download cards, clean ─────────────────────────────────────────
   A picture of the program, its name and version, one sentence, two
   buttons. Everything else a reader might want is under Extras, so a card
   says only what is being downloaded. */
.dl-cols{gap:22px}
.dl-col{padding:0;gap:0}
.dl-col::before{display:none}
.dl-shot{display:block;position:relative;aspect-ratio:16/9;overflow:hidden;border-bottom:1px solid var(--line)}
.dl-shot img{display:block;width:100%;height:100%;object-fit:cover;object-position:top left;
  transition:transform .9s var(--ease)}
.dl-shot::after{content:"";position:absolute;inset:auto 0 0;height:45%;
  background:linear-gradient(180deg,transparent,rgba(13,14,23,.92))}
.dl-col:hover .dl-shot img{transform:scale(1.05)}
.dl-body{display:flex;flex-direction:column;gap:10px;padding:18px 22px 22px;flex:1}
.dl-col h3{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:27px}
.dl-col h3 .dl-ver{font-size:12px}
.dl-col .dl-body > p{margin:0;color:#a8adbd;font-size:15px}
.dl-col .dl-body > .dl-needs{color:var(--dim);font-size:13px;margin-top:-2px}
.dl-main{margin:6px 0 2px}
.dl-more{margin-top:auto;padding-top:6px;font-family:var(--mono);font-size:12.5px;color:var(--dim)}
.dl-more::after{content:" →";display:inline-block;transition:transform .2s ease}
.dl-more:hover{color:var(--highlight);text-decoration:none}
.dl-more:hover::after{transform:translateX(3px)}
.dl-trust{margin-top:18px;max-width:none}

.extras{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px}
.extra{padding:18px 20px;border:1px solid var(--line);border-radius:14px;background:rgba(14,15,24,.6)}
.extra h3{margin:0 0 8px;font-size:18px}
.extra p{margin:0 0 8px;color:var(--dim);font-size:14px}
.extra ul{list-style:none;margin:0;padding:0}
.extra li{display:flex;flex-wrap:wrap;justify-content:space-between;gap:2px 12px;padding:7px 0;
  border-top:1px solid var(--line);font-size:14px}
.extra li:first-child{border-top:0}
.extra li a{font-family:var(--mono);font-size:13px;word-break:break-all}
.extra li span{color:var(--dim);font-size:12.5px}
