/* 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}
@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; }
