/* The arcade: the stage it sits in, the cover you see before it loads, and the HUD over it.

   Two rules run through all of it. The cover has to look like something worth clicking before a
   single byte of Three.js is fetched, and the HUD has to stay out of the way — everything a
   player needs is either the body on screen or one bar. */

.stage {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--line);
  background: linear-gradient(180deg, #0d1122, #070910);
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.45);
}
@media (max-width: 640px) {
  .stage { aspect-ratio: 4 / 5; }
}

/* --- the cover ------------------------------------------------------------------------
   Vector, not a picture: sharp on every screen, a couple of kilobytes, and it can move.

   The middle of the frame is reserved for the title and the Play button, so the art is
   composed around a hole and every moving part lives outside it. Depth comes from four
   planes that drift by different amounts under the pointer (--px/--py, set in landing.js
   and only on a device that has a pointer to begin with). */
.cover-art {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  --px: 0; --py: 0;
}
.cover-art g { transform-box: view-box; }
.cv-p0 { transform: translate(calc(var(--px) * 16px), calc(var(--py) * 9px)); }
.cv-p1 { transform: translate(calc(var(--px) * 10px), calc(var(--py) * 6px)); }
.cv-p2 { transform: translate(calc(var(--px) * 6px), calc(var(--py) * 3.5px)); }
.cv-p3 { transform: translate(calc(var(--px) * 3px), calc(var(--py) * 1.6px)); }

.cover-art .tw { animation: cv-tw 3.6s ease-in-out infinite; }
@keyframes cv-tw { 0%, 100% { opacity: 1; } 50% { opacity: .18; } }

.cv-cloud-a { animation: cv-cloud 64s ease-in-out infinite alternate; }
.cv-cloud-b { animation: cv-cloud 82s ease-in-out infinite alternate-reverse; }
@keyframes cv-cloud { from { transform: translateX(-42px); } to { transform: translateX(42px); } }

.cv-blink { animation: cv-blink 6.5s steps(1, end) infinite; }
@keyframes cv-blink { 0%, 88% { opacity: 1; } 89%, 96% { opacity: .06; } 97%, 100% { opacity: 1; } }

.cv-beacon { animation: cv-beacon 2.6s ease-in-out infinite; }
@keyframes cv-beacon { 0%, 44% { opacity: .12; } 52%, 60% { opacity: 1; } 68%, 100% { opacity: .12; } }

/* The correction, in three parts that share one clock: the whole rig breathes, the body
   leans against it, and the pole leans back the other way. That counter-tilt is the only
   reason it reads as balancing rather than swinging. */
.cv-rig { animation: cv-rig 5.2s ease-in-out infinite alternate; }
@keyframes cv-rig { from { transform: translateY(-3px); } to { transform: translateY(3px); } }
.cv-walker { transform-origin: 545px 639px; animation: cv-lean 5.2s ease-in-out infinite alternate; }
@keyframes cv-lean { from { transform: rotate(-4.5deg); } to { transform: rotate(4.5deg); } }
.cv-pole { transform-origin: 545px 561px; animation: cv-pole 5.2s ease-in-out infinite alternate; }
@keyframes cv-pole { from { transform: rotate(9deg); } to { transform: rotate(-9deg); } }

.cv-crumb { transform-box: fill-box; transform-origin: center; opacity: 0;
  animation: cv-crumb 7.6s ease-in infinite; }
@keyframes cv-crumb {
  0% { opacity: 0; transform: translateY(0) rotate(0); }
  6% { opacity: .9; }
  46% { opacity: 0; transform: translateY(92px) rotate(190deg); }
  100% { opacity: 0; transform: translateY(92px) rotate(190deg); }
}

.cover-face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  text-align: center;
  padding: 24px;
  z-index: 2;
}
.cover-face h3 { font-size: clamp(26px, 4.6vw, 44px); font-weight: 900; letter-spacing: -1px; }
.cover-face .rules { color: var(--dim); font-size: 14px; }
.cover-face .rules b { color: var(--fg); }
.stage.playing .cover-face, .stage.playing .cover-art { opacity: 0; pointer-events: none; transition: opacity .45s ease; }

/* The button has to look like it is waiting for you: a slow breath, and a highlight that sweeps
   across the face every few seconds. */
.play-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 18px 40px;
  font-size: 19px;
  font-weight: 800;
  border: none;
  border-radius: 999px;
  cursor: pointer;
  color: #07120c;
  background: linear-gradient(135deg, var(--lime), var(--blue));
  box-shadow: 0 0 0 0 rgba(125, 255, 155, 0.45);
  animation: play-breathe 3.4s ease-in-out infinite;
  overflow: hidden;
}
.play-btn::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  transform: translateX(-160%);
  animation: play-sheen 5.5s ease-in-out infinite;
}
.play-btn:hover { transform: translateY(-2px) scale(1.02); }
.play-btn:active { transform: translateY(0) scale(0.99); }
@keyframes play-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(125, 255, 155, 0.4); }
  50% { box-shadow: 0 0 34px 6px rgba(125, 255, 155, 0.22); }
}
@keyframes play-sheen {
  0%, 62% { transform: translateX(-160%); }
  84%, 100% { transform: translateX(300%); }
}

/* Bricks that fall away from under the cursor — the same idea as the page building itself. */
.play-wrap { position: relative; }
.play-wrap i {
  position: absolute;
  width: 10px;
  height: 6px;
  border-radius: 1px;
  background: var(--lime);
  opacity: 0;
  pointer-events: none;
}
.play-wrap:hover i { animation: brick-drop 1.1s ease-in forwards; }
.play-wrap i:nth-child(2) { animation-delay: .12s; }
.play-wrap i:nth-child(3) { animation-delay: .26s; }
.play-wrap i:nth-child(4) { animation-delay: .4s; }
@keyframes brick-drop {
  0% { opacity: .9; transform: translateY(0) rotate(0); }
  100% { opacity: 0; transform: translateY(46px) rotate(140deg); }
}

@media (prefers-reduced-motion: reduce) {
  .play-btn, .play-btn::after, .play-wrap i,
  .cover-art .tw, .cv-cloud-a, .cv-cloud-b, .cv-blink, .cv-beacon,
  .cv-rig, .cv-walker, .cv-pole, .cv-crumb { animation: none; }
  .cv-crumb { opacity: 0; }
  .cover-art { --px: 0 !important; --py: 0 !important; }
}

/* --- the game -------------------------------------------------------------------------- */
.arcade { position: absolute; inset: 0; }
.ar-canvas { width: 100%; height: 100%; display: block; touch-action: none; cursor: crosshair; }

.ar-hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
  font-variant-numeric: tabular-nums;
  /* The sky behind the numbers changes brightness as you climb; a shadow is cheaper than a
     panel and keeps the HUD off the picture. */
  text-shadow: 0 2px 14px rgba(0, 0, 0, .85), 0 0 3px rgba(0, 0, 0, .7);
  opacity: 0;
  transition: opacity .4s ease;
}
.arcade .ar-hud { opacity: 1; }
.ar-ended .ar-hud { opacity: .25; }

.ar-top {
  position: absolute; top: 16px; left: 18px; right: 18px;
  display: flex; align-items: baseline; justify-content: space-between;
}
.ar-metres b { font-size: 40px; font-weight: 900; letter-spacing: -1.5px; line-height: 1; }
.ar-metres small { display: block; font-size: 10px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--dim); }
.ar-mult { font-size: 15px; color: var(--dim); font-weight: 700; }
.ar-mult b { color: var(--lime); font-size: 22px; }

/* The lean, as one line. Centre is upright, the ends are the fall, and the shaded bands are the
   part of the range you cannot come back from — so the moment it is over is on screen instead of
   being something you work out afterwards. */
.ar-balance {
  position: absolute; left: 50%; bottom: 54px; transform: translateX(-50%);
  width: min(300px, 58%); height: 5px; border-radius: 4px;
  background: rgba(255, 255, 255, .12);
  opacity: 0; transition: opacity .3s;
}
.ar-wire .ar-balance { opacity: 1; }
/* The two no-return bands. 28% at each end, which is where the maths gives up. */
.ar-balance::before, .ar-balance::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 28%;
  background: linear-gradient(90deg, rgba(255, 122, 198, .55), rgba(255, 122, 198, .1));
}
.ar-balance::before { left: 0; }
.ar-balance::after { right: 0; transform: scaleX(-1); }

.ar-balance i {
  position: absolute; left: 50%; top: -5px; width: 14px; height: 15px; margin-left: -7px;
  border-radius: 3px; background: var(--lime);
  box-shadow: 0 0 14px rgba(125, 255, 155, .85);
  transition: background-color .12s linear, box-shadow .12s linear;
}
.ar-tilting .ar-balance i { background: var(--amber); box-shadow: 0 0 16px rgba(255, 206, 92, .9); }
.ar-doomed .ar-balance i { background: var(--pink); box-shadow: 0 0 22px rgba(255, 122, 198, 1); }

/* Once it is decided, the whole frame says so. */
.ar-balance u {
  position: absolute; inset: -16px -12px; border-radius: 9px;
  border: 1px solid rgba(255, 122, 198, .9); opacity: 0;
}
.ar-doomed .ar-balance u { opacity: 1; animation: gone .5s linear infinite; }
@keyframes gone { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }

/* Leaning hard darkens the corners: peripheral, so it is felt rather than read. */
.ar-strainmark {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: radial-gradient(120% 90% at 50% 50%, transparent 42%, rgba(255, 122, 198, .5) 100%);
  transition: opacity .15s linear;
}

/* Running effects, stacked under the distance. Each chip drains as its seconds do, so the
   question "how long have I got" is answered without a number to read. */
.ar-effects {
  position: absolute; top: 88px; left: 18px;
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
}
.ar-chip {
  position: relative; overflow: hidden;
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--c); padding: 5px 11px 7px; border-radius: 7px;
  border: 1px solid color-mix(in srgb, var(--c) 55%, transparent);
  background: color-mix(in srgb, var(--c) 12%, rgba(7, 9, 16, .72));
}
.ar-chip i { position: absolute; left: 0; bottom: 0; height: 2px; background: var(--c); }

/* What you just picked up: one word, in the middle, gone before it can get in the way. */
.ar-grab {
  position: absolute; left: 0; right: 0; top: 40%; text-align: center;
  font-size: 30px; font-weight: 900; letter-spacing: -.5px; opacity: 0;
}
.ar-grab.show { animation: grabbed 1.15s ease-out forwards; }
@keyframes grabbed {
  0% { opacity: 0; transform: translateY(14px) scale(.86); }
  18% { opacity: 1; transform: translateY(0) scale(1.04); }
  70% { opacity: 1; transform: translateY(-6px) scale(1); }
  100% { opacity: 0; transform: translateY(-26px) scale(.98); }
}

.ar-hint {
  position: absolute; left: 0; right: 0; bottom: 18px; text-align: center;
  font-size: 12.5px; color: var(--dim);
}
.ar-hint b { color: var(--fg); background: rgba(255,255,255,.07); border-radius: 4px; padding: 1px 6px; }
.ar-wire .ar-hint { opacity: .45; }

.ar-over {
  position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  background: rgba(7, 9, 16, .82);
}
/* An author rule for `display` beats the browser's rule for [hidden], so without this the
   result screen stays on top of the next run — which is exactly what it did. */
.ar-over[hidden] { display: none; }
.ar-card { text-align: center; padding: 26px; max-width: 380px; }
.ar-score { font-size: 68px; font-weight: 900; letter-spacing: -3px; line-height: .95; }
.ar-score small { display: block; font-size: 11px; letter-spacing: 2px; text-transform: uppercase; color: var(--dim); }
.ar-line { color: var(--dim); font-size: 14px; margin: 12px 0 16px; }
.ar-xp { height: 4px; border-radius: 4px; background: rgba(255,255,255,.12); overflow: hidden; margin-bottom: 20px; }
.ar-xp i { display: block; height: 100%; background: linear-gradient(90deg, var(--lime), var(--blue)); }
.ar-again {
  font: inherit; font-weight: 800; font-size: 16px; padding: 13px 30px; border: none; border-radius: 999px;
  cursor: pointer; color: #07120c; background: linear-gradient(135deg, var(--lime), var(--blue));
  display: inline-flex; align-items: center; gap: 10px;
}
.ar-again em {
  font-style: normal; font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  padding: 3px 8px; border-radius: 5px; background: rgba(7, 18, 12, .22);
}
.ar-soon { margin-top: 14px; font-size: 12px; color: var(--dim); }

/* The trade, in the order it belongs: a free name first, an address offered second. One field
   at a time — a form with two boxes on a result screen reads as a sign-up wall. */
.ar-claim { margin: 4px 0 18px; }
.ar-field { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; }
.ar-field > span {
  flex: 1 0 100%; font-size: 12px; letter-spacing: .6px; text-transform: uppercase;
  color: var(--dim); margin-bottom: 2px;
}
.ar-field input {
  flex: 1 1 170px; min-width: 0; font: inherit; font-size: 15px; padding: 10px 13px;
  border-radius: 9px; border: 1px solid var(--line); background: rgba(255,255,255,.05); color: var(--fg);
}
.ar-field input:focus { outline: none; border-color: var(--lime); background: rgba(125,255,155,.07); }
.ar-field .ar-do {
  font: inherit; font-weight: 800; font-size: 14px; padding: 10px 18px; border: none; border-radius: 9px;
  cursor: pointer; color: #07120c; background: var(--lime);
}
.ar-field .ar-do:disabled { opacity: .55; cursor: default; }
.ar-note { margin-top: 9px; font-size: 12px; color: var(--dim); line-height: 1.45; }
.ar-note.bad { color: var(--pink); }
.ar-won { font-size: 15px; margin-bottom: 10px; }
.ar-won b { color: var(--lime); }
.ar-won a { color: var(--blue); text-decoration: underline; }

/* --- the board on the page -------------------------------------------------------------
   Folded until someone wants it — a table of strangers isn't what the page is selling — and
   it opens itself the moment a run lands, because then it is about the reader. */
.board {
  margin-top: 26px; border-radius: var(--radius); overflow: hidden;
  /* A lit edge rather than a hairline: this panel is where the game asks for something, and
     it was disappearing into the page. The border is the gradient showing through a padding
     box, so it glows without a wrapper element. */
  border: 1px solid transparent;
  background:
    linear-gradient(var(--card), var(--card)) padding-box,
    linear-gradient(120deg, rgba(125,255,155,.75), rgba(106,139,255,.6) 55%, rgba(255,122,198,.5)) border-box;
  box-shadow: 0 0 0 1px rgba(125,255,155,.06), 0 18px 44px rgba(0,0,0,.35);
}
.board-head {
  display: flex; align-items: center; gap: 12px; width: 100%; padding: 17px 20px;
  font: inherit; text-align: left; color: var(--fg); background: transparent; border: none; cursor: pointer;
}
.board-head:hover { background: rgba(125,255,155,.05); }
.board-title { font-size: 17px; font-weight: 900; letter-spacing: -.3px; }
.board-title::before {
  content: ""; display: inline-block; width: 9px; height: 9px; margin-right: 10px; border-radius: 2px;
  background: var(--lime); box-shadow: 0 0 10px rgba(125,255,155,.9); vertical-align: middle;
}
.board-peek { margin-left: auto; font-size: 13px; font-weight: 700; color: var(--lime); }

/* Which day the list is showing, said by the chips rather than the title. */
.board .ranges button[aria-pressed="true"] { box-shadow: 0 0 16px rgba(125,255,155,.35); }

.yours .asyou { font-size: 14px; color: var(--dim); margin-bottom: 12px; }
.yours .asyou b { color: var(--lime); font-weight: 800; }
.yours .rename {
  font: inherit; font-size: 12px; font-weight: 700; margin-left: 6px; padding: 3px 9px;
  border-radius: 999px; cursor: pointer; color: var(--dim);
  border: 1px solid var(--line); background: transparent;
}
.yours .rename:hover { color: var(--fg); }
.board-chevron {
  width: 9px; height: 9px; border-right: 2px solid var(--dim); border-bottom: 2px solid var(--dim);
  transform: rotate(45deg); transition: transform .18s ease; flex: none; margin-left: 4px;
}
.board-head[aria-expanded="true"] .board-chevron { transform: rotate(-135deg); }
.board-body { border-top: 1px solid var(--line); }

/* Your own run, at the top of the panel it just landed in: the number, and the one field
   that turns it into a name. */
.yours { padding: 16px 18px; border-bottom: 1px solid var(--line); background: rgba(125,255,155,.04); }
.yours-run { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.yours-run b { font-size: 30px; font-weight: 900; letter-spacing: -1px; }
.yours-run b small { font-size: 14px; font-weight: 700; color: var(--dim); margin-left: 2px; }
.yours-run .pts { font-weight: 700; color: var(--lime); }
.yours-run .rank { color: var(--dim); font-size: 13.5px; }
.yours-run .named { margin-left: auto; font-weight: 800; color: var(--lime); }
.yours .field { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.yours .field > span { flex: 1 0 100%; font-size: 12px; letter-spacing: .6px; text-transform: uppercase; color: var(--dim); }
.yours .field input {
  flex: 1 1 200px; min-width: 0; font: inherit; font-size: 15px; padding: 10px 13px;
  border-radius: 9px; border: 1px solid var(--line); background: rgba(255,255,255,.05); color: var(--fg);
}
.yours .field input:focus { outline: none; border-color: var(--lime); background: rgba(125,255,155,.07); }
.yours .field .do {
  font: inherit; font-weight: 800; font-size: 14px; padding: 10px 20px; border: none; border-radius: 9px;
  cursor: pointer; color: #07120c; background: var(--lime);
}
.yours .field .do:disabled { opacity: .55; cursor: default; }
.yours .note { margin-top: 9px; font-size: 12.5px; color: var(--dim); line-height: 1.5; }
.yours .note.bad { color: var(--pink); }
.yours .note.done { color: var(--fg); font-size: 14px; }
.yours .note a { color: var(--blue); text-decoration: underline; }

.board .ranges { display: flex; gap: 6px; padding: 12px 18px 4px; }
.board .ranges button {
  font: inherit; font-size: 12px; font-weight: 700; padding: 6px 12px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: transparent; color: var(--dim);
}
.board .ranges button[aria-pressed="true"] { color: #07120c; background: var(--lime); border-color: transparent; }
.board ol { list-style: none; margin: 0; padding: 6px 0; }
.board li { display: grid; grid-template-columns: 34px 1fr auto auto; gap: 12px; align-items: baseline;
  padding: 9px 18px; font-size: 14.5px; }
.board li + li { border-top: 1px solid rgba(255,255,255,.045); }
.board li .place { color: var(--dim); font-variant-numeric: tabular-nums; }
.board li .who { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board li .who.anon { color: var(--dim); font-weight: 500; font-style: italic; }
.board li .m { color: var(--dim); font-size: 13px; font-variant-numeric: tabular-nums; }
.board li .sc { font-weight: 800; color: var(--lime); font-variant-numeric: tabular-nums; }
.board li:nth-child(1) .place, .board li:nth-child(2) .place, .board li:nth-child(3) .place { color: var(--amber); }
.board .empty { padding: 22px 18px; color: var(--dim); font-size: 14px; }

.ar-nogl .ar-hud { opacity: 1; }

/* The way out, shown when Escape releases the mouse. Secondary to the button next to it: the
   likeliest thing the player wants is to go back to the wire. */
.ar-leave {
  display: block; margin: 10px auto 0; font: inherit; font-size: 13px; font-weight: 700;
  padding: 8px 18px; border-radius: 999px; cursor: pointer;
  color: var(--dim); background: transparent; border: 1px solid var(--line);
}
.ar-leave:hover { color: var(--fg); }

/* Google first: one tap, and the account already exists at the end of it. The address stays
   available underneath for anyone who doesn't have one, but it is not the headline any more. */
.yours .keep { font-size: 12px; letter-spacing: .6px; text-transform: uppercase; color: var(--dim); margin-bottom: 8px; }
.yours .google {
  display: inline-flex; align-items: center; gap: 10px; width: 100%; justify-content: center;
  font: inherit; font-weight: 800; font-size: 15px; padding: 12px 20px; border-radius: 9px;
  cursor: pointer; color: #1f1f1f; background: #fff; border: none;
}
.yours .google:hover { filter: brightness(.95); }
/* The mark, drawn rather than fetched: four arcs in Google's colours, no external request. */
.yours .google i {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  background:
    conic-gradient(from -45deg, #ea4335 0deg 90deg, #fbbc05 90deg 180deg, #34a853 180deg 270deg, #4285f4 270deg 360deg);
  -webkit-mask: radial-gradient(circle, transparent 40%, #000 41%);
  mask: radial-gradient(circle, transparent 40%, #000 41%);
}
.yours .field { margin-top: 12px; }

/* Offered, not forced. Somebody already signed in has nothing to be asked for here, so the
   panel says who they are and shows the door to the dashboard rather than pushing them
   through it. */
.yours .dash {
  display: inline-flex; align-items: center; gap: 8px;
  font-weight: 800; font-size: 15px; padding: 11px 20px 12px; border-radius: 12px;
  color: #07120c; background: linear-gradient(180deg, #8dffab, #43cf6b);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5), 0 4px 0 #1c8b40;
  transition: transform .14s cubic-bezier(.2, .9, .3, 1.2), box-shadow .14s ease;
}
.yours .dash:hover { transform: translateY(-2px); box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 6px 0 #1c8b40; }
.yours .dash:active { transform: translateY(3px); box-shadow: inset 0 1px 0 rgba(255,255,255,.45), 0 1px 0 #1c8b40; }
@media (prefers-reduced-motion: reduce) { .yours .dash { transition: none; } }
