:root{
  /* The page's own colour, taken off the reference the front page was drawn
     from: #0b0825, a violet navy rather than the near black this was. It is the
     single largest area of the screen, so it is the colour a person reads the
     whole page as being. */
  --void:#0b0825;
  --void-2:#100c30;
  --panel:rgba(18,11,38,.82);
  --panel-solid:#120b26;
  --line:rgba(150,116,255,.20);
  --line-hot:rgba(255,150,70,.45);
  --violet:#a78bfa;
  --violet-dim:#6d5aa8;
  --hot:#ff9a3d;
  --hot-2:#ffd166;
  --white-hot:#fff2d6;
  /* The front page's own colours, read off the reference with a pixel counter
     rather than matched by eye: they are warmer and flatter than the ones the
     rest of the app uses. Kept separate from --violet/--txt/--hot because those
     three carry the HUD, the upgrade panel and the board, and the reference is
     a picture of the front page and says nothing about those. */
  --menu-head:#ffffff;
  --menu-kicker:#ac8ffd;
  --menu-ink:#a39abd;
  --menu-dot:#ffca60;
  --menu-go-a:#ffcc63;
  --menu-go-b:#ff9f40;
  --menu-go-ink:#2a1400;
  /* The board's own measure: the reference draws it at 609 in a 2406 frame, a
     quarter of the width, and it is the whole panel -- track, type and all --
     that has to follow that rather than the panel alone. */
  --bw:clamp(280px,25.3vw,609px);
  --cyan:#5fd4e8;
  --txt:#e9e4ff;
  --txt-dim:#9b8fc4;
  --good:#6ee7a8;
  --bad:#ff5f7a;
  --r:14px;
}

*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}

html,body{height:100%;overflow:hidden}
body{
  background:var(--void);
  color:var(--txt);
  font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  font-size:15px;
  line-height:1.35;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overscroll-behavior:none;
}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}

/* ============================= layout ============================= */
#app{
  position:fixed;inset:0;
  display:grid;
  grid-template-columns:1fr;
  grid-template-rows:auto minmax(0,1fr) auto;
  /* minmax(0,1fr), not the default auto: an auto track is at least as wide as
     its widest item's min-content, so one long upgrade description was laying
     the whole app out wider than the phone and pushing the sound button off
     the right edge. The track is the screen; anything too wide for it scrolls
     or ellipsises inside its own box instead. */
  grid-template-columns:minmax(0,1fr);
  grid-template-areas:"hud" "stage" "panel";
  height:100dvh;
  max-height:100dvh;
}
#hud{grid-area:hud;position:relative;z-index:5;
  background:linear-gradient(180deg,rgba(17,11,48,.96),rgba(17,11,48,.72));
  border-bottom:1px solid var(--line);
  padding:calc(env(safe-area-inset-top,0px) + 8px) 12px 0;
  backdrop-filter:blur(8px);
}
#stage{grid-area:stage;position:relative;overflow:hidden;background:var(--void)}
#game{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:none;cursor:crosshair}
#panel{grid-area:panel;position:relative;z-index:5;
  display:flex;flex-direction:column;min-height:0;
  max-height:46vh;
  background:linear-gradient(180deg,rgba(16,10,34,.92),rgba(10,6,22,.98));
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom,0px);
  backdrop-filter:blur(10px);
}

/* ============================= hud ============================= */
.hud-row{display:flex;align-items:center;gap:10px}
.hud-top{justify-content:space-between;padding-bottom:6px}
.brand{display:flex;align-items:center;gap:7px;min-width:0}
.brand-dot{
  width:11px;height:11px;border-radius:50%;flex:none;
  background:radial-gradient(circle at 50% 50%,#000 0 34%,var(--hot) 46%,#b06cff 62%,transparent 74%);
  box-shadow:0 0 12px rgba(255,140,60,.85),0 0 22px rgba(150,90,255,.5);
  animation:dotPulse 3.2s ease-in-out infinite;
}
@keyframes dotPulse{0%,100%{transform:scale(1)}50%{transform:scale(1.16)}}
.brand-name{font-size:11px;font-weight:800;letter-spacing:.19em;color:var(--txt-dim)}
.stage-chip{
  font-size:9.5px;font-weight:800;letter-spacing:.13em;
  padding:2.5px 7px;border-radius:999px;
  color:var(--hot-2);background:rgba(255,154,61,.11);
  border:1px solid rgba(255,154,61,.32);
  white-space:nowrap;
}
.hud-actions{display:flex;gap:6px;flex:none}
.icon-btn{
  width:32px;height:32px;border-radius:9px;display:grid;place-items:center;
  border:1px solid var(--line);background:rgba(255,255,255,.03);
  transition:background .15s,border-color .15s,transform .1s;
}
.icon-btn:active{transform:scale(.92)}
.icon-btn svg{width:17px;height:17px;fill:none;stroke:var(--txt-dim);stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.icon-btn:hover{border-color:var(--violet-dim);background:rgba(167,139,250,.10)}
.icon-btn:hover svg{stroke:var(--txt)}
#btnSound .spk-body{fill:var(--txt-dim);stroke:none}
#btnSound.is-off .spk-wave{display:none}
#btnSound.is-off .spk-x{display:block!important}
#btnSound .spk-x{display:none}
#btnSound.is-off svg{stroke:var(--bad)}
#btnSound.is-off .spk-body{fill:var(--bad)}

.hud-stats{gap:8px;padding-bottom:7px;align-items:flex-end;flex-wrap:nowrap}
.stat{display:flex;flex-direction:column;gap:0;min-width:0}
.stat-label{font-size:8.5px;font-weight:800;letter-spacing:.16em;color:var(--txt-dim);opacity:.8}
.stat-value{
  font-variant-numeric:tabular-nums;
  font-weight:800;font-size:19px;line-height:1.15;letter-spacing:-.01em;
  white-space:nowrap;
}
.stat-mass{flex:1 1 auto}
.stat-mass .stat-value{color:var(--white-hot);text-shadow:0 0 16px rgba(255,180,90,.55)}
.stat-flux{flex:0 1 auto;align-items:flex-end}
.stat-flux .stat-value{color:var(--hot-2);text-shadow:0 0 14px rgba(255,209,102,.35)}
.stat-flux .stat-value::before{content:"\26A1";font-size:12px;margin-right:3px;opacity:.85}
.stat-small{flex:0 0 auto;align-items:flex-end;opacity:.92}
.stat-small .stat-value{font-size:12px;color:var(--violet);font-weight:700}
.combo{transition:opacity .2s;opacity:.28}
.combo.is-on{opacity:1}
.combo.is-on .stat-value{color:var(--hot-2);text-shadow:0 0 14px rgba(255,154,61,.6)}
/* The mass column is stretched across the whole free width of the row, so a
   bump scaled about its centre would throw the number off the left edge of the
   screen. Each value scales about the edge its text is actually pinned to. */
.stat-mass>.stat-value{transform-origin:left center}
.stat-flux>.stat-value,.stat-small>.stat-value{transform-origin:right center}
.stat-value.bump{animation:bump .22s ease-out}
@keyframes bump{0%{transform:scale(1)}35%{transform:scale(1.16)}100%{transform:scale(1)}}

.stage-bar{height:3px;background:rgba(255,255,255,.05);overflow:hidden;margin:0 -12px}
.stage-bar i{display:block;height:100%;width:0%;
  background:linear-gradient(90deg,#7c4dff,var(--hot),var(--white-hot));
  box-shadow:0 0 10px rgba(255,154,61,.8);transition:width .25s ease-out}

/* ============================= stage ============================= */
.hint{
  position:absolute;left:50%;top:10px;transform:translateX(-50%);
  z-index:3;pointer-events:none;
  font-size:11px;color:var(--txt-dim);
  background:rgba(17,11,48,.66);border:1px solid var(--line);
  padding:5px 12px;border-radius:999px;
  backdrop-filter:blur(6px);
  transition:opacity .6s;
  max-width:calc(100% - 20px);text-align:center;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.hint strong{color:var(--hot-2);font-weight:700}
.hint.gone{opacity:0}
.toast{
  position:absolute;left:50%;bottom:16px;transform:translate(-50%,14px);
  z-index:4;pointer-events:none;opacity:0;
  font-size:12.5px;font-weight:700;letter-spacing:.02em;
  color:var(--white-hot);
  background:rgba(30,14,54,.92);
  border:1px solid var(--line-hot);
  padding:8px 16px;border-radius:999px;
  box-shadow:0 6px 30px rgba(255,120,40,.3);
  transition:opacity .3s,transform .3s;
  max-width:calc(100% - 24px);text-align:center;
}
.toast.show{opacity:1;transform:translate(-50%,0)}

/* ============================= panel ============================= */
/* No tabs: one list, all twelve nodes, scrolled. The three branches are told
   apart by their heading and by the accent every node under it is drawn in, so
   the colour that used to live in the tab strip now lives on the section. */
.nodes{
  flex:1 1 auto;min-height:0;
  overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;overscroll-behavior:contain;
  padding:4px 10px 12px;
  display:flex;flex-direction:column;gap:7px;
  scrollbar-width:thin;
  scroll-padding-top:4px;
}
/* The list is a flex column, and a flex item shrinks by default: without this
   the twelve nodes squash into the panel instead of running past it, and there
   is nothing to scroll. */
.nodes > *{flex:0 0 auto}
.nodes::-webkit-scrollbar{width:6px}
.nodes::-webkit-scrollbar-thumb{background:rgba(167,139,250,.3);border-radius:3px}

.branch-head{
  position:sticky;top:0;z-index:2;
  display:flex;align-items:baseline;gap:7px;
  margin:7px -10px 0;padding:7px 11px 6px;
  background:linear-gradient(180deg,rgba(9,7,20,.97) 62%,rgba(9,7,20,0));
  /* Plain colour first, color-mix after: an old engine drops the second line
     and still gets a rule under the heading instead of no rule at all. */
  border-bottom:1px solid var(--accent);
  border-bottom:1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  box-shadow:inset 0 -1px 14px -8px var(--accent);
}
.nodes > .branch-head:first-child{margin-top:0}
.bh-name{
  font-size:12px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent);
  text-shadow:0 0 14px var(--accent);
}
.bh-sub{
  font-size:8.5px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--txt-dim);opacity:.62;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.bh-tick{
  margin-left:auto;flex:none;
  font-size:9.5px;font-weight:800;font-variant-numeric:tabular-nums;
  letter-spacing:.04em;color:var(--accent);opacity:.72;
  background:rgba(255,255,255,.05);
  background:color-mix(in srgb, var(--accent) 13%, transparent);
  border:1px solid var(--line);
  border-color:color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius:999px;padding:1px 7px;
}
.branch-head.is-done .bh-tick{color:var(--good);background:rgba(110,231,168,.12);border-color:rgba(110,231,168,.34);opacity:1}
.branch-head.is-done .bh-tick::after{content:" \2713"}

.node{
  position:relative;display:flex;gap:9px;align-items:stretch;
  padding:7px 9px;border-radius:12px;
  border:1px solid var(--line);
  background:linear-gradient(135deg,rgba(255,255,255,.035),rgba(255,255,255,.012));
  /* No width:100% here. The list is a flex column, so a node stretches to fill
     it anyway; the explicit percentage makes the node's intrinsic width depend
     on the list's, which makes the list's depend on the node's, and the whole
     app ends up laid out wider than the phone that is holding it. */
  overflow:hidden;text-align:left;
  transition:transform .1s,border-color .16s,background .16s,box-shadow .16s;
}
.node::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--accent,var(--violet));opacity:.55;
  transition:opacity .16s;
}
.node.no{opacity:.46;cursor:default}
.node.locked{opacity:.34;cursor:default}
.node.can{cursor:pointer;border-color:rgba(255,154,61,.42);
  background:linear-gradient(135deg,rgba(255,154,61,.10),rgba(255,255,255,.015));
  box-shadow:0 0 0 1px rgba(255,154,61,.10),0 4px 18px -8px rgba(255,154,61,.6)}
.node.can::before{opacity:1;box-shadow:0 0 12px var(--accent)}
.node.can:hover{transform:translateY(-1px);border-color:var(--hot)}
.node.can:active{transform:scale(.985)}
.node.bought{animation:bought .42s ease-out}
@keyframes bought{0%{box-shadow:0 0 0 0 rgba(255,209,102,.85)}100%{box-shadow:0 0 0 16px rgba(255,209,102,0)}}
.node.maxed{border-color:rgba(110,231,168,.35);background:linear-gradient(135deg,rgba(110,231,168,.08),rgba(255,255,255,.012))}

.n-icon{
  flex:none;width:30px;height:30px;border-radius:9px;align-self:center;
  display:grid;place-items:center;
  background:rgba(0,0,0,.35);border:1px solid var(--line);
}
.n-icon svg{width:19px;height:19px;fill:none;stroke:var(--accent,var(--violet));stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
/* Every part of a node is allowed to shrink. Without this the intrinsic width
   of the list is set by whichever upgrade has the longest description, and on a
   narrow phone that is wider than the screen. */
.n-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px;justify-content:center}
/* The value chip drops to its own line rather than squeezing the name into an
   ellipsis. "Accretion Efficiency" is the longest name here and the chip beside
   it is a number, so on a phone the chip is what moves. */
.n-head{display:flex;align-items:baseline;gap:6px;min-width:0;flex-wrap:wrap;row-gap:2px}
.n-head > *{min-width:0}
.n-name{font-size:12.5px;font-weight:800;letter-spacing:.01em;color:var(--txt);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.n-lvl{font-size:9.5px;font-weight:800;color:var(--accent,var(--violet));
  background:rgba(255,255,255,.06);padding:1px 4px;border-radius:5px;
  font-variant-numeric:tabular-nums;white-space:nowrap;flex:none}
.n-now{margin-left:auto;font-size:10px;font-weight:800;color:var(--accent,var(--violet));
  font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;opacity:.95}
.n-desc{font-size:10.5px;color:var(--txt-dim);line-height:1.28;min-width:0;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.n-desc b{color:var(--white-hot);font-weight:700}
.n-desc .up{color:var(--good)}
.n-buy{
  flex:none;align-self:center;min-width:66px;text-align:center;
  padding:7px 9px;border-radius:9px;
  font-size:11.5px;font-weight:800;font-variant-numeric:tabular-nums;
  border:1px solid var(--line);background:rgba(255,255,255,.03);color:var(--txt-dim);
  white-space:nowrap;
}
.node.can .n-buy{
  color:#2a1400;
  background:linear-gradient(135deg,var(--hot-2),var(--hot));
  border-color:rgba(255,209,102,.7);
  box-shadow:0 0 18px -4px rgba(255,154,61,.75);
}
.node.maxed .n-buy{color:var(--good);border-color:rgba(110,231,168,.4);background:rgba(110,231,168,.09)}
.n-buy small{display:block;font-size:8.5px;font-weight:700;opacity:.75;letter-spacing:.06em}

.panel-foot{
  flex:none;padding:6px 12px 9px;
  font-size:10px;color:var(--txt-dim);opacity:.62;text-align:center;
  border-top:1px solid var(--line);
}

/* ============================= responsive ============================= */
/* Wide / landscape: playfield left, tree docked right. */
@media (min-aspect-ratio: 1/1){
  #app{
    grid-template-columns:minmax(0,1fr) clamp(330px,38vw,460px);
    grid-template-rows:auto minmax(0,1fr);
    grid-template-areas:"hud hud" "stage panel";
  }
  #panel{border-top:none;border-left:1px solid var(--line);max-height:none}
  .nodes{padding:4px 12px 14px}
  .branch-head{margin-left:-12px;margin-right:-12px;padding-left:13px;padding-right:13px}
  .stat-value{font-size:22px}
  .stat-mass .stat-value{font-size:26px}
  /* The docked column is narrower than the portrait panel is tall, so the
     value chip drops to its own line rather than squeezing the node's name
     into an ellipsis. */
  .n-head{flex-wrap:wrap;row-gap:3px}
  .n-now{margin-left:auto}
}
@media (min-aspect-ratio: 1/1) and (max-width:900px){
  #app{grid-template-columns:minmax(0,1fr) clamp(288px,37vw,340px)}
  .bh-sub{display:none}
  .n-name{font-size:12.5px}
  .n-buy{min-width:52px;font-size:10.5px;padding:6px 6px}
  /* A docked column this narrow leaves about 150px beside the icon, which is
     two short words of description per line and an ellipsis on the second. The
     icon is the one thing here that carries no information the name does not,
     so it is the thing that goes. */
  .n-icon{display:none}
  .n-desc{font-size:10.5px}
}
/* Short landscape phones: keep the tree usable without eating the field. */
@media (max-height:460px){
  .hud-top{padding-bottom:2px}
  .hud-stats{padding-bottom:3px}
  .stat-value{font-size:16px}
  .stat-mass .stat-value{font-size:18px}
  .nodes{padding:4px 10px 10px;gap:6px}
  .node{padding:7px 9px}
  .n-desc{font-size:10.5px}
  /* In a short landscape the panel is a column as tall as the screen, and this
     line is decoration. It costs 43px of a 309px list, which is a third of an
     upgrade, so it goes. */
  .panel-foot{display:none}
}
@media (max-width:380px){
  .brand-name{display:none}
  .stat-value{font-size:17px}
  .stat-mass .stat-value{font-size:19px}
  .n-buy{min-width:56px;font-size:11px;padding:6px 7px}
  .bh-sub{display:none}
  .n-icon{width:26px;height:26px}
  .n-icon svg{width:17px;height:17px}
  /* A 320px screen runs out of room for a name, a level chip and a value chip
     on one line. Rather than cut the name off at an ellipsis, let it take a
     second line: every upgrade has to be identifiable on the smallest phone,
     and "Extracti..." is not a name. */
  .n-name{
    white-space:normal;text-overflow:clip;line-height:1.15;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  }
  .n-head{align-items:center}
}
/* ============================== run clock ============================== */
/* Where the hint pill sits, because the hint is the thing a run replaces. */
.runchip{
  position:absolute;left:50%;top:10px;transform:translateX(-50%);
  z-index:3;pointer-events:none;
  display:flex;flex-direction:column;align-items:center;gap:4px;
  padding:4px 12px 7px;border-radius:999px;
  background:rgba(17,11,48,.7);border:1px solid var(--line-hot);
  backdrop-filter:blur(6px);
  box-shadow:0 0 22px -8px rgba(255,154,61,.7);
}
.runchip b{
  font-size:12px;font-weight:800;font-variant-numeric:tabular-nums;
  letter-spacing:.06em;color:var(--hot-2);
}
.runchip-bar{display:block;width:74px;height:3px;border-radius:2px;background:rgba(255,255,255,.12);overflow:hidden}
.runchip-bar b{display:block;height:100%;width:100%;
  background:linear-gradient(90deg,var(--hot),var(--hot-2));
  box-shadow:0 0 8px rgba(255,154,61,.8);transition:width .25s linear}
.runchip.is-low b{color:var(--bad)}
.runchip.is-low .runchip-bar b{background:linear-gradient(90deg,#ff7a4d,var(--bad))}
/* ENDING A RUN IS NOT SOMETHING A STRAY TAP SHOULD BE ABLE TO FIND. The button
   lives in the HUD beside the sound switch, never over the field, because
   debris drifts under everything in the field and a rock is exactly what a
   thumb goes for. It holds the slot the reset button gives up for the length
   of a run. The clock above the field takes no taps at all, so the field under
   it is still the field. */
.hud-end{
  display:none;height:32px;padding:0 11px;border-radius:9px;
  border:1px solid var(--line);
  background-color:rgba(255,255,255,.03);
  background-image:linear-gradient(90deg,rgba(255,95,122,.6),rgba(255,95,122,.34));
  background-repeat:no-repeat;background-size:0% 100%;
  color:var(--txt-dim);font-size:10px;font-weight:800;letter-spacing:.14em;
  transition:background-size .18s linear,color .14s,border-color .14s;
}
#app.is-run .hud-end{display:block}
/* HOLD, DO NOT TAP. The fill is the gesture: it starts the instant a finger
   lands and it reaches the end of the button at the moment the run ends, so
   the control explains itself while it is being used. A finger that lets go
   early has done nothing at all, which is the point of it. */
.hud-end.is-hold{
  color:var(--bad);border-color:var(--bad);background-size:100% 100%;
  transition:background-size .8s linear,color .1s,border-color .1s;
}

/* ============================== shell ============================== */
/* The front door and the two screens behind it. While one is up the HUD and
   the upgrade panel come off the page, so the field is the whole screen and
   the hole is the middle of the layout rather than something behind it. */
#app.is-shell{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);grid-template-areas:"stage"}
#app.is-shell #hud,#app.is-shell #panel{display:none}
/* The hint is a line about the field, and there is no field to point at while
   a screen is up. It would otherwise sit on top of the menu's own title. */
#app.is-shell .hint{display:none}

.shell{position:fixed;inset:0;z-index:20}
/* A hidden element with a display of its own is not hidden by the browser's
   own [hidden] rule, which these all have. */
.shell [hidden],.runchip[hidden]{display:none!important}
.scr{
  position:absolute;inset:0;
  display:none;
  grid-template-rows:auto minmax(0,1fr) auto;
  /* If the screen is shorter than the screen needs, this scrolls rather than
     clipping. A button cut off by the bottom edge is a button that cannot be
     pressed, and a shallow laptop window is not an exotic case. The document
     itself still never scrolls: this is a fixed panel with its own range. */
  overflow-y:auto;overflow-x:hidden;
  overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  /* Clear through the middle: the hole is being drawn behind this and the
     menu is built around it, so the band the hole is in stays open. */
  background:linear-gradient(180deg,
    rgba(11,8,37,.95) 0%,
    rgba(11,8,37,.74) 15%,
    rgba(11,8,37,.16) 30%,
    rgba(11,8,37,0)   42%,
    rgba(11,8,37,0)   60%,
    rgba(11,8,37,.66) 74%,
    rgba(11,8,37,.95) 100%);
}
.scr.is-on{display:grid}

/* A column, not the whole width. On a laptop an edge to edge button under an
   edge to edge title is a banner; held to a column it is a title screen with
   the hole behind it, which is what this is. */
.scr-pad{
  width:100%;max-width:520px;margin:0 auto;
  min-width:0;padding:0 18px;display:flex;flex-direction:column;
}
.scr-top{padding-top:calc(env(safe-area-inset-top,0px) + 22px);gap:5px}
.scr-bot{padding-bottom:calc(env(safe-area-inset-bottom,0px) + 20px);gap:9px}
.scr-mid{min-height:0}

/* Off the reference: a light periwinkle at full strength, and only lightly
   spaced. It was set at .34em, which is a different thing entirely, and the
   reference's own line measures 0.354 of the column, which at this size is a
   tracking of about a tenth of an em and no more. */
.scr-kicker{
  font-size:clamp(10.5px,2.565vw,12.05px);font-weight:600;letter-spacing:.06em;
  color:var(--menu-kicker);
}
/* Held against the height as well as the width: on a phone lying on its side
   there is 390px of screen and a two line title at 52px eats a third of it,
   which is the room the black hole behind the menu is meant to have. */
.scr-title{
  font-size:clamp(26px,min(10.5vw,9.2vh),52px);line-height:.92;font-weight:900;
  letter-spacing:-.02em;color:var(--white-hot);
  text-shadow:0 0 26px rgba(255,154,61,.42),0 0 60px rgba(150,90,255,.35);
}
.scr-h2{
  font-size:clamp(16px,min(5.4vw,5.6vh),26px);line-height:1.05;font-weight:900;
  letter-spacing:.02em;color:var(--white-hot);
  text-shadow:0 0 20px rgba(255,154,61,.35);
}
.scr-sub{font-size:11.5px;color:var(--txt-dim);line-height:1.35}

/* The bullets, off the reference. Two numbers here are not what they were: the
   text is smaller than the paragraph above it rather than the same size, and
   the dot stands a long way off the margin. Both were measured rather than
   guessed: the reference's bullet cap is 22px against the paragraph's 29, and
   its dot sits 28px clear of the text's margin on a 417px column. */
.how{list-style:none;display:flex;flex-direction:column;gap:1.06em;margin-bottom:0;
  max-width:100%;font-size:clamp(11.5px,2.648vw,12.45px)}
.how li{
  position:relative;padding-left:6.3%;
  color:var(--menu-ink);line-height:1.3;
}
.how li::before{
  content:"";position:absolute;left:0;top:.46em;
  width:.49em;height:.49em;border-radius:50%;
  background:var(--menu-dot);
}

.btn{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;width:100%;min-height:46px;padding:8px 14px;
  border-radius:var(--r);border:1px solid var(--line);
  background:rgba(255,255,255,.03);color:var(--txt);
  transition:transform .1s,border-color .16s,background .16s;
}
.btn:active{transform:scale(.985)}
.btn b{font-size:14px;font-weight:900;letter-spacing:.1em}
.btn small{font-size:9px;font-weight:700;letter-spacing:.13em;opacity:.7}
/* Off the reference, and it is not the button this was. The reference's is one
   line of sentence case at 23px on a 440px pill, its amber runs left to right
   rather than corner to corner, it has no glow and its corners are 8px rather
   than 14. Its label is 5.2% of its own width, which is what makes it read as
   something to press rather than as a banner with a word on it. */
.btn-go{
  color:var(--menu-go-ink);
  background:linear-gradient(90deg,var(--menu-go-a),var(--menu-go-b));
  border-color:transparent;
  min-height:clamp(44px,11.4vw,55.1px);
}
.btn-go b{font-size:clamp(15px,3.23vw,21.85px);font-weight:800;letter-spacing:.005em}
.btn-go small{opacity:.72}
.btn-ghost{min-height:40px;color:var(--violet);border-color:var(--line)}
.btn-ghost b,.btn-ghost{font-size:12px;font-weight:800;letter-spacing:.13em}
.btn-ghost:hover{border-color:var(--violet-dim);background:rgba(167,139,250,.10)}
.btn-quiet{
  min-height:40px;border-color:transparent;background:none;
  font-size:11px;font-weight:700;letter-spacing:.13em;color:var(--txt-dim);
}

.scr-note{
  font-size:10.5px;font-weight:700;letter-spacing:.08em;
  color:var(--txt-dim);text-align:center;min-height:14px;
}

/* ------------------------- the front page -------------------------
   Two halves. What the game is on the left, left aligned inside itself,
   because the words are a briefing and a briefing is read down a margin. The
   board is the right half: a column of names and numbers somebody can take in
   at a glance without reading the paragraph beside it.
   The sky and the hole are behind both and belong to neither. Neither half has
   a panel behind it for that reason; the only thing with an edge is the board,
   because a board is a table and a table needs one. */
/* THE BAND IS CENTRED IN THE WINDOW, THE TEXT INSIDE IT IS NOT. Hung from the
   top it was a heading with a screenful of empty sky under it, which reads as
   a page that failed to finish loading rather than as a title screen. Centred
   it is a band with sky above and below it and the hole behind it, which is
   what the game is about.
   FLEX WITH AUTO MARGINS RATHER THAN justify-content. A centred flex item that
   is taller than its container overflows past the TOP, where the scroll range
   cannot reach it, and the title becomes the first thing a short window loses.
   Auto margins resolve to zero when there is no room to give, so the block
   starts at the top and scrolls down the way it always did. */
.scr-menu.is-on{display:flex;flex-direction:column}
.menu-grid{
  width:100%;min-width:0;
  display:grid;gap:18px;align-content:start;
  /* The reading area stops widening somewhere. Past that the margins open up
     and the whole band is centred in the window, which is the difference
     between a briefing against the left edge of a 2400px monitor and one being
     read. Below this width nothing moves: the padding was already the margin. */
  max-width:1724px;margin-block:auto;margin-inline:auto;
  padding:calc(env(safe-area-inset-top,0px) + 22px) clamp(18px,4vw,80px)
          calc(env(safe-area-inset-bottom,0px) + 20px);
}
/* Left with the column, and held off the button above it. Flush, the line reads
   as a caption printed on the button rather than as a note under it. */
.scr-menu .scr-note{text-align:left;margin-top:11px}
/* NO gap, and a margin on each piece instead. The reference's rhythm is not
   evenly spaced: the kicker sits close under nothing in particular, the name
   follows it tightly, the briefing follows that, and then there is a long
   silence of about seventy pixels before the button. One column gap can only
   say all of those are the same distance, which is the one thing they are
   not. */
.menu-col{min-width:0;display:flex;flex-direction:column;gap:0}
/* A ground under the briefing, with no edge to it. The field is going on
   behind this and the debris in it drifts: without something the words and a
   tumbling asteroid end up in the same place, and body text that something
   crosses while you read it is text you stop reading. It is a falloff and not
   a panel, so the sky still shows through everywhere the words are not. */
.scr-menu .menu-col:first-child{position:relative}
/* AN ELLIPSE INSCRIBED IN ITS OWN BOX, so it has no edge anywhere. This was a
   radial gradient near the top left corner with its transparent stop at 92% of
   a radius that reached well past the box, which left the left and bottom
   edges at about half strength: on a near black field that is invisible, and
   against the navy this is now it drew a hard rectangle around the briefing
   that a person can see. closest-side puts the transparent stop on the box's
   own boundary, and past it the corners clamp to transparent, so there is
   nothing anywhere for an edge to be. */
.scr-menu .menu-col:first-child::before{
  content:"";position:absolute;inset:-60px -90px;
  background:radial-gradient(closest-side at 50% 50%,
    rgba(11,8,37,.88) 0%,rgba(11,8,37,.80) 35%,rgba(11,8,37,.60) 60%,
    rgba(11,8,37,.32) 80%,rgba(11,8,37,.12) 92%,rgba(11,8,37,0) 100%);
  pointer-events:none;
}
.scr-menu .menu-col:first-child > *{position:relative}
/* The briefing column stops widening at the reference's own measure, which is
   what every number above is a fraction of. Its button takes 440 of it. */
@media (min-width:720px){
  .scr-menu .menu-col:first-child{max-width:441px}
}
/* The rhythm, as margins rather than as one column gap. Each is in em of the
   element it hangs off, so the whole spacing scales with the type instead of
   being a set of desktop pixels that collapse into each other on a phone. The
   numbers are the reference's own ink gaps minus what the line boxes already
   contribute above and below each band. */
.scr-menu .scr-kicker{margin-bottom:.85em}
.scr-menu .menu-name{margin-bottom:.355em}
.scr-menu .menu-pitch{margin-bottom:1.153em}
.scr-menu .how{margin-bottom:5.416em}

/* The name is the biggest thing on the screen and it is not centred. It is
   held against the height as well as the width: the hole is drawn behind this
   and a name that eats a third of a phone lying on its side leaves the thing
   the name is about nowhere to be. */
/* Off the reference. Three things changed: the colour is flat white rather
   than the cream the rest of the app uses, it has no glow, and its lines are
   set tighter. The reference's two lines stand 52px apart with a 45px cap
   between them, which is a line-height of 0.84, and a name with a halo on it
   cannot be measured and does not match the picture either. */
.menu-name{
  font-size:clamp(30px,min(10.925vw,9.5vh),62.7px);line-height:.84;font-weight:900;
  letter-spacing:-.025em;color:var(--menu-head);
  /* The reference's name is set in a condensed heavy grotesque: at the same cap
     height its "BLACK HOLE" is 8.3 times as wide as it is tall, where this
     stack's is 9.2. Measured on this machine, asking for a condensed width
     picks DejaVu Sans Condensed out of the family and lands on 8.3, which is
     the reference exactly. Where the platform's own UI font has no condensed
     width the ask is ignored and the name is simply the name it always was. */
  font-stretch:condensed;
}
/* The briefing. Set a little wider than the name, because it is read rather
   than looked at, and capped in measure so a laptop does not get one long
   line with a fixed number of words on it. Off the reference it is larger
   than the bullets under it, which is the way round the picture has it, and
   it is the muted lavender rather than the bright one. */
.menu-pitch{
  font-size:clamp(13px,3.534vw,16.63px);line-height:1.48;color:var(--menu-ink);
  max-width:100%;
}
/* Held to a measure of its own rather than the width of the column. The column
   is as wide as the window, and a button that stretches across all of a
   laptop is a banner, not a button: nobody reads a control that wide as
   something to press. */
.menu-acts{display:flex;flex-direction:column;gap:9px;margin-top:2px;
  max-width:418px}

/* The expanded board. Same rows as the board screen, same renderer, same
   metal on the top three; only the wrapper is new, because here it is a panel
   on the front page rather than a screen of its own. */
.board-panel{
  display:flex;flex-direction:column;gap:5px;
  /* Everything inside the panel is set in em against this, so the board can be
     given the panel's own measure and grow with it rather than staying the size
     it was when the panel was half this wide. */
  font-size:10px;
  padding:11px 12px 12px;border-radius:var(--r);
  border:1px solid var(--line);
  background:linear-gradient(180deg,rgba(18,11,38,.80),rgba(10,7,22,.66));
  box-shadow:0 0 40px -22px rgba(150,90,255,.9);
}
.bp-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:.8em;
}
.bp-title{
  font-size:1em;font-weight:900;letter-spacing:.24em;color:var(--white-hot);
}
/* The rows in here are invented for now, and a made up name read as a real
   one is the page telling a lie about who played. The tag goes when the rows
   in shell.js do. */
.bp-tag{
  font-size:.8em;font-weight:800;letter-spacing:.18em;
  color:var(--hot-2);opacity:.75;
  border:1px solid rgba(255,209,102,.34);border-radius:999px;padding:.2em .7em;
}
.bp-cols{
  display:flex;gap:.9em;padding:0 .9em .4em;
  font-size:.8em;font-weight:800;letter-spacing:.18em;color:var(--txt-dim);
  border-bottom:1px solid var(--line);
}
.bp-cols span:first-child{width:2.2em;flex:0 0 2.2em}
.bp-cols span:nth-child(2){flex:1 1 auto}
.bp-cols span:last-child{flex:0 0 auto;text-align:right}
.bp-rows{display:flex;flex-direction:column;gap:.2em}
.bp-rows .brow{padding:4px 9px;border-radius:.8em;font-size:1em}
.bp-rows .bname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bp-rows .bval{font-variant-numeric:tabular-nums}

/* The panel is two lists now, and it scrolls rather than growing without end.
   The height is held in em against the panel's own base, so it is the same
   panel at every width: about ten rows at the reference's frame, which is what
   the panel was measured at, and a little more on a phone where the rows are
   small. The page behind it never changes size because a board got long. */
.bp-scroll{
  display:flex;flex-direction:column;gap:.2em;
  overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  max-height:min(58vh,32em);
}
.bp-scroll::-webkit-scrollbar{width:5px}
.bp-scroll::-webkit-scrollbar-thumb{background:rgba(167,139,250,.3);border-radius:3px}
/* Out of reach while a run is on. The reset wipes everything the player has
   built, and the field is under the thumb for five minutes: one stray tap puts
   the wipe confirmation over the game, which is the page looking broken at the
   moment somebody is trying to play it. It comes back when the run ends. */
#app.is-run #btnReset{display:none}

/* ---------------------------- end of run ---------------------------- */
.score{
  display:flex;flex-direction:column;align-items:center;gap:1px;
  padding:10px 12px;border-radius:var(--r);
  border:1px solid var(--line-hot);
  background:linear-gradient(135deg,rgba(255,154,61,.10),rgba(150,90,255,.07));
  box-shadow:0 0 34px -14px rgba(255,154,61,.75);
}
.score-label{font-size:9px;font-weight:800;letter-spacing:.24em;color:var(--txt-dim)}
.score-value{
  font-size:clamp(28px,min(11vw,9vh),46px);font-weight:900;line-height:1.05;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
  color:var(--white-hot);text-shadow:0 0 24px rgba(255,180,90,.6);
}
.score-rank{
  font-size:10.5px;font-weight:800;letter-spacing:.1em;
  color:var(--hot-2);text-align:center;
}
.score-rank.is-flat{color:var(--txt-dim);font-weight:700}

.facts{list-style:none;display:flex;gap:7px}
.facts li{
  flex:1 1 0;min-width:0;text-align:center;
  padding:6px 4px;border-radius:10px;border:1px solid var(--line);
  background:rgba(255,255,255,.03);
}
.facts b{display:block;font-size:13px;font-weight:800;font-variant-numeric:tabular-nums;color:var(--txt)}
.facts span{display:block;font-size:8px;font-weight:800;letter-spacing:.14em;color:var(--txt-dim)}

/* ---------------------------- leaderboard ---------------------------- */
/* The one screen with something to read, so the middle is a panel rather
   than a window: the hole stays behind it, dimmed, instead of being cut. */
#scrBoard{background:linear-gradient(180deg,rgba(11,8,37,.93),rgba(11,8,37,.86))}
.scr-list{
  min-height:0;width:100%;max-width:520px;margin:12px auto;padding:0 14px;
  overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;
  display:flex;flex-direction:column;gap:5px;
  background:linear-gradient(180deg,rgba(18,11,38,.72),rgba(10,7,22,.86));
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding-top:12px;padding-bottom:12px;
  scrollbar-width:thin;
}
.scr-list::-webkit-scrollbar{width:6px}
.scr-list::-webkit-scrollbar-thumb{background:rgba(167,139,250,.3);border-radius:3px}

.brow{
  flex:0 0 auto;display:flex;align-items:baseline;gap:9px;
  padding:7px 10px;border-radius:10px;border:1px solid transparent;
  font-size:13px;min-width:0;
}
.brow .brank{
  flex:none;width:26px;font-size:11px;font-weight:800;font-variant-numeric:tabular-nums;
  color:var(--txt-dim);letter-spacing:.02em;
}
.brow .bname{
  flex:1 1 auto;min-width:0;font-weight:700;color:var(--txt);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.brow .bname em{font-style:normal;color:var(--txt-dim)}
/* A RUN OF YOURS CARRIES ITS OWN LINE. What tells one of your runs from the
   next is the length and the stage it reached, and a handle long enough to
   fill the row would cut exactly that off if it shared the line with the name.
   So the name keeps the line it needs and the run's own detail sits under it,
   where no handle can reach it. */
.brow.is-own{align-items:center}
/* WRAPS, AND STOPS AT THE EDGE OF ITS OWN COLUMN. A handle is one unbreakable
   token, so `normal` alone does not wrap it: it runs straight out of the row
   and draws over the badge and the mass, which is the collision this row was
   already fixed once to avoid. Breaking inside the token keeps the whole name
   on screen and keeps every other thing on the row where it belongs. */
.brow.is-own .bname{
  white-space:normal;overflow:hidden;text-overflow:clip;
  overflow-wrap:anywhere;word-break:break-word;
}
.brow.is-own .bname em{
  display:block;margin-top:1px;
  font-size:11px;font-weight:600;letter-spacing:.04em;
}
.brow .bval{
  flex:none;font-weight:900;font-variant-numeric:tabular-nums;color:var(--hot-2);
}
/* Somebody's own row has to be findable at a glance on a screen of other
   people's numbers, and a colour alone is not findable for everybody: the
   edge, the glow and the word are three separate answers to the same
   question, and any one of them is enough. */
.brow.is-mine{
  border-color:var(--line-hot);
  background:linear-gradient(135deg,rgba(255,154,61,.20),rgba(255,154,61,.05));
  box-shadow:inset 3px 0 0 var(--hot),0 0 20px -8px rgba(255,154,61,.85);
  padding-left:13px;
}
.brow.is-mine .brank,.brow.is-mine .bname{color:var(--white-hot)}
/* Its own element rather than a tag inside the name: a long handle is
   ellipsised, and the one thing that must never be cut off is the word that
   says this row is yours. */
.brow .byou{
  flex:none;padding:1px 5px;border-radius:999px;
  font-size:8.5px;font-weight:900;letter-spacing:.14em;
  color:#2a1400;background:var(--hot);
}
/* ----------------------------- the podium -----------------------------
   The first three are what anybody opening a board is looking for, so they
   are not three more rows. Each carries the metal of its place in a disc
   where the rows under it carry a bare number, and the type steps up with
   it: the first place is the largest number on the screen. Size and colour
   say it together, because for some people a colour on its own says nothing.
   This block sits after the "yours" rules deliberately. The disc is painted
   behind the number and the halo is an outline, so a row that is both on the
   podium and yours keeps its bar, its edge and the word YOU, and still takes
   the metal it earned. */
.brow.is-r1,.brow.is-r2,.brow.is-r3{align-items:center}
.brow.is-r1 .brank,.brow.is-r2 .brank,.brow.is-r3 .brank{
  display:flex;align-items:center;justify-content:center;
  height:auto;border-radius:999px;letter-spacing:0;
}
.brow.is-r1{
  padding-top:10px;padding-bottom:10px;gap:11px;
  outline:1px solid rgba(255,209,102,.42);
}
.brow.is-r1 .brank{
  width:32px;height:29px;font-size:15px;font-weight:900;color:#3a2200;
  background:linear-gradient(135deg,var(--hot-2),var(--hot));
  box-shadow:0 0 20px -4px rgba(255,209,102,.95);
}
.brow.is-r1 .bname{
  font-size:15.5px;font-weight:900;
  text-shadow:0 0 16px rgba(255,209,102,.45);
}
.brow.is-r1 .bval{
  font-size:18px;text-shadow:0 0 16px rgba(255,209,102,.7);
}
.brow.is-r2{padding-top:8px;padding-bottom:8px;gap:10px}
.brow.is-r2 .brank{
  width:29px;height:26px;font-size:13px;font-weight:900;color:#20263a;
  background:linear-gradient(135deg,#eef2ff,#aab6cc);
}
.brow.is-r2 .bname{font-size:14px;font-weight:800;color:#f0f3ff}
.brow.is-r2 .bval{font-size:15.5px;color:#dde4f2}
.brow.is-r3{padding-top:7px;padding-bottom:7px;gap:9px}
.brow.is-r3 .brank{
  width:27px;height:24px;font-size:12px;font-weight:900;color:#33201a;
  background:linear-gradient(135deg,#f0b183,#c1783f);
}
.brow.is-r3 .bname{font-size:13.5px;font-weight:800;color:#f6e3d0}
.brow.is-r3 .bval{font-size:14.5px;color:#e6b98e}

/* The front page's copy of the same rows, in a panel rather than a screen.
   Three classes deep, so it beats the podium's own sizes without an
   !important anywhere. The metal and the size that goes with it survive the
   step down: the first place is still the biggest number in the panel. */
.bp-rows .brow.is-r1{padding-top:6px;padding-bottom:6px;gap:.8em;
  outline-color:rgba(255,209,102,.34)}
.bp-rows .brow.is-r1 .brank{width:2.6em;height:2.3em;font-size:1.2em}
.bp-rows .brow.is-r1 .bname{font-size:1.25em;text-shadow:none}
.bp-rows .brow.is-r1 .bval{font-size:1.4em}
.bp-rows .brow.is-r2{padding-top:5px;padding-bottom:5px;gap:.8em}
.bp-rows .brow.is-r2 .brank{width:2.4em;height:2.1em;font-size:1.1em}
.bp-rows .brow.is-r2 .bname{font-size:1.2em}
.bp-rows .brow.is-r2 .bval{font-size:1.25em}
.bp-rows .brow.is-r3{padding-top:5px;padding-bottom:5px;gap:.8em}
.bp-rows .brow.is-r3 .brank{width:2.2em;height:1.9em;font-size:1.05em}
.bp-rows .brow.is-r3 .bname{font-size:1.15em}
.bp-rows .brow.is-r3 .bval{font-size:1.2em}
.bp-rows .brow .brank{width:2.2em;font-size:1.05em}
.bp-rows .brow .bname{font-size:1.2em}
.bp-rows .brow .bval{font-size:1.25em}
.bempty{
  flex:1 1 auto;display:grid;place-items:center;text-align:center;
  font-size:12px;color:var(--txt-dim);line-height:1.5;padding:20px 10px;
}
/* A line that explains the list under it. Not a filler, so it does not take
   the leftover height the way an empty state does: it sits under the last row
   and the panel ends there. */
.bnote{
  flex:0 0 auto;margin:0;padding:7px 2px 2px;
  font-size:11.5px;color:var(--txt-dim);line-height:1.5;
}

@media (max-height:560px){
  /* A short landscape phone. The hole is drawn at its own size behind all of
     this and on 390px of height it reaches up into the how-to lines, so the
     bottom block gets a ground of its own to sit on rather than the lines
     being dropped to make room. Nothing here is decoration except the title,
     and that is what gives. */
  .scr-top{padding-top:calc(env(safe-area-inset-top,0px) + 10px)}
  /* A panel with an edge, the same way the HUD is one. On 390px of height the
     disk of the hole is right behind these lines and a fading gradient leaves
     the first of them on the brightest part of it. */
  .scr-bot{
    padding-top:8px;padding-bottom:calc(env(safe-area-inset-bottom,0px) + 10px);gap:6px;
    background:linear-gradient(180deg,rgba(17,11,48,.88),rgba(11,8,37,.96));
    border-top:1px solid var(--line);
  }
  /* Side by side and wrapping, so three short lines are two rows at most. */
  .how{flex-direction:row;flex-wrap:wrap;gap:2px 14px;margin-bottom:2px}
  .how li{font-size:11px}
  /* The type gets smaller on a short screen. The buttons do not: a thumb is
     the same size whatever the window is, and 34px of LEADERBOARD is a
     button somebody misses. What gives instead is the title, and if the
     screen still cannot fit, .scr scrolls. */
  .btn{min-height:44px}
  .btn-ghost{min-height:40px}
  .btn-quiet{min-height:40px}
  .score{padding:6px 10px}
  .facts li{padding:4px 4px}
  /* The front page on a short screen. The name is the first thing to give and
     the play button is the last, which is the order the two matter in. The
     briefing keeps its measure and loses its size, because on 390px of height
     a paragraph at body size is most of the screen. */
  .menu-grid{gap:13px;padding-top:calc(env(safe-area-inset-top,0px) + 9px);
    padding-bottom:calc(env(safe-area-inset-bottom,0px) + 9px)}
  .menu-col{gap:6px}
  .menu-name{font-size:clamp(21px,min(5.4vw,8.4vh),36px)}
  .menu-pitch{font-size:11px;line-height:1.34}
  .board-panel{padding:8px 9px 9px;gap:4px}
  .bp-head{font-size:9px}
  .bp-cols{padding-bottom:3px}
}
/* Side by side, because a short screen has width to spare and no height at
   all. PLAY keeps the wider half: it is the one a person came to press.
   Held back until the row is wide enough to carry both labels on one line:
   at 320px across, two buttons abreast is two buttons with their words
   wrapped, and stacked costs less than that does. */
@media (max-height:560px) and (min-width:420px){
  .menu-acts{flex-direction:row;gap:8px;margin-top:0}
  .menu-acts .btn-go{flex:1 1 56%}
  .menu-acts .btn-ghost{flex:1 1 30%}
}
/* The two halves, once there is width for two. Width and not aspect: a phone
   on its side has the aspect but not the room, and two columns in 568px is
   two columns of nothing. Every phone on its side that is wide enough gets
   them, which is where the board beside the name is worth the most. */
@media (min-width:720px){
  .menu-grid{grid-template-columns:minmax(0,1fr) minmax(252px,352px);gap:26px;
    align-items:start}
  /* The name is sized against its column here, not against the window. The
     board has taken a fixed slice off the right, so at 768px across the column
     is nearer 350 than 768, and a name sized off the window wraps to three
     lines on the one screen where there is height to spare for two. */
  .menu-name{font-size:clamp(30px,min(5.32vw,7.22vh),62.7px)}
}
/* THE BOARD IS THE OTHER HALF OF THE BAND, NOT A SIDEBAR BESIDE IT. At the
   reference's own frame the panel is 609 wide against a 418 column and the
   silence between them is wider than the column itself; a panel narrower than
   the briefing beside it reads as a widget that happens to be there. Below
   this width there is no room for that, and the panel stays the narrow column
   it was, which is the version that fits a phone on its side.
   Its inside is measured off the reference as well: ten rows 43px apart, 16px
   of padding, and the header held off the first row. That spacing is what
   makes ten names read as a table rather than as a list with rules in it. */
@media (min-width:1180px){
  .menu-grid{grid-template-columns:minmax(0,441px) minmax(280px,var(--bw));
    justify-content:space-between;gap:26px}
  /* Every measurement here is the reference's own, divided by its panel width:
     the gap, the padding, the room each row is given. Set in em against a base
     that follows the panel, they all hold together as it narrows. */
  .board-panel{font-size:clamp(11px,.6vw,14.4px);
    gap:.694em;padding:3.889em 1.111em 1.875em}
  .bp-cols{padding-bottom:0}
  .bp-rows{gap:0}
  .bp-rows .brow{padding:.625em .833em}
  .bp-rows .brow.is-r1,
  .bp-rows .brow.is-r2{padding:.556em .833em}
  .bp-rows .brow.is-r3{padding:.625em .833em}
}
@media (max-height:430px){
  /* One line, not two. Two lines of title on a 390px screen is a third of the
     height spent on the name of the game, and the hole behind it needs that. */
  .scr-title{font-size:clamp(20px,min(6.6vw,7.6vh),30px)}
  .scr-title br{display:none}
  .scr-kicker{font-size:8px}
  .scr-note{font-size:9.5px}
  /* And the front page's own name, which has a second line to spend. It keeps
     both, because "FEED THE / BLACK HOLE" broken after two words is the shape
     the whole screen is built on, but the lines themselves get smaller. */
  .menu-name{font-size:clamp(18px,min(4.4vw,7.4vh),28px)}
  .menu-pitch{font-size:10.5px}
}

@media (prefers-reduced-motion: reduce){
  *{animation-duration:.01ms!important;animation-iteration-count:1!important}
}
