@font-face { font-family: 'Goldman'; src: url(fonts/goldman-700.woff2) format('woff2'); font-weight: 700; font-display: swap; }
@font-face { font-family: 'Electrolize'; src: url(fonts/electrolize-400.woff2) format('woff2'); font-weight: 400; font-display: swap; }

/* The world's colours, eased as the world changes: --neon and --g2 are the logo's two glows, --line the world's neon. */
@property --neon { syntax: '<color>'; inherits: true; initial-value: #5ef2ff; }
@property --g2 { syntax: '<color>'; inherits: true; initial-value: #ff5c8a; }
@property --line { syntax: '<color>'; inherits: true; initial-value: #3f6dff; }

:root { --ink: #f6fbff; --mist: #9aabd6; --gold: #ffd166; --night: #03071a; color-scheme: dark; }
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: var(--night); color: var(--ink); font-family: 'Electrolize', system-ui, sans-serif; }

.hero {
  position: relative; height: 100vh; height: 100svh; overflow: hidden;
  user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; touch-action: none; -webkit-tap-highlight-color: transparent;
  transition: opacity .45s ease-out, --neon 1.2s, --g2 1.2s, --line 1.2s;
}
/* Held back till it's ready (main.js takes loading off: the fonts in, the phone laid out to them, the world's first
   frame drawn), then shown whole, fading in, rather than in pieces; if the script never runs, shown after 3s anyway. */
html.loading .hero { opacity: 0; animation: unhold 0s 3s forwards; }
@keyframes unhold { to { opacity: 1; } }
#world { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* The sky: the logo and its words */
.sky {
  position: absolute; top: 0; left: 0; right: 0; pointer-events: none;
  padding: max(28px, env(safe-area-inset-top)) 20px 0;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.logo {
  --logo: clamp(38px, min(5.1vw, 8.2vh), 84px); /* (on a laptop, a headline that leaves the phone the room: it shows the game) */
  margin: 0; display: flex; flex-direction: column; align-items: center;
  font-family: 'Goldman', sans-serif; font-weight: 700; line-height: .9; color: var(--ink);
  text-shadow: 0 0 .08em var(--neon), 0 0 .35em color-mix(in srgb, var(--g2) 60%, transparent);
}
.logo-top { font-size: var(--logo); letter-spacing: .08em; padding-left: .08em; }
.logo-bot { font-size: calc(var(--logo) * 1.34); letter-spacing: .02em; display: flex; align-items: center; margin-top: .16em; } /* (room for the O's antenna) */
/* The O in BOT is Orb Bot in outline: a neon ring, his antenna and its bulb. The antenna springs as he lands in the
   phone and each time he launches (main.js). Round where the font's O is square, but its letters' own height and
   weight: Goldman's capitals are .59em tall with strokes .135 to .2em, so the ring is .62em (round shapes need a hair
   more to look as tall) with a .15em stroke, leaving the font's own size of hole. */
/* (its glow breathes with the demo's beat, main.js: a little brighter on each, more on the downbeat) */
.logo.throb { animation: throb .34s ease-out; } /* (not .beat: that's the challenge line's) */
.logo.throb.down { animation-name: throb-down; }
@keyframes throb { 0% { filter: brightness(1.14); } }
@keyframes throb-down { 0% { filter: brightness(1.32); } }
.o {
  position: relative; display: inline-block; width: .62em; height: .62em; margin: 0 .07em; border-radius: 50%;
  border: .15em solid var(--ink);
  box-shadow: 0 0 .1em var(--neon), inset 0 0 .1em var(--neon), 0 0 .38em color-mix(in srgb, var(--g2) 55%, transparent);
}
.ant {
  position: absolute; left: .226em; bottom: calc(100% + .09em); width: .07em; height: .25em; border-radius: .035em; background: var(--ink);
  box-shadow: 0 0 .06em var(--neon); transform-origin: 50% 100%; transform: rotate(24deg);
}
.ant::after {
  content: ''; position: absolute; left: 50%; top: 0; width: .16em; height: .16em; border-radius: 50%; transform: translate(-50%, -60%);
  background: var(--neon); box-shadow: 0 0 .08em var(--neon), 0 0 .22em var(--neon);
}
.ant.spring { animation: spring .9s ease-out; }
@keyframes spring { 15% { transform: rotate(42deg); } 35% { transform: rotate(10deg); } 55% { transform: rotate(32deg); } 75% { transform: rotate(20deg); } }
.tagline {
  margin: .75em 0 0; font-size: clamp(16px, 1.8vw, 24px); letter-spacing: .34em; padding-left: .34em; text-transform: uppercase;
  text-shadow: 0 0 12px var(--neon);
}
.sub { margin: .5em 0 0; font-size: clamp(13px, 1.1vw, 15px); line-height: 1.5; color: var(--mist); max-width: 44em; text-wrap: balance; }
.sub span { display: inline-block; } /* (where it's two lines, it breaks between its sentences, not in one) */

/* The floor: under the phone */
.floor {
  position: absolute; left: 0; right: 0; bottom: 0; pointer-events: none;
  padding: 18px 24px max(20px, env(safe-area-inset-bottom));
  display: grid; grid-template-columns: 1fr; justify-items: center; align-items: center; gap: 14px;
}
.floor > * { pointer-events: auto; }

/* The store badges, by Apple's and Google's rules: their own artwork, untouched (no glow, tilt or motion on it), the
   two the same height (--bh: Apple's least is 40px), App Store first, a quarter of the height clear all round (the
   padding: nothing else in it) on the floor's plain dark. Plain: the page's glow is the logo's, the world's and the
   #1 card's. */
.stores { --bh: 46px; display: flex; gap: 14px; justify-content: center; }
/* On a computer, getting it is the floor's middle: a big QR code for your phone (in the world's neon), and beside it
   "scan to play on your phone, or" over the badges (their images lined up with the words, their clear space kept
   from the QR's). A phone or tablet, or an upright or short window, has the badges alone (and no QR: it is the
   phone, or there's no room). */
.get { display: flex; align-items: center; gap: 22px; }
.get-qr {
  padding: 7px; border-radius: 16px; background: var(--night);
  border: 1.5px solid color-mix(in srgb, var(--line) 75%, white); box-shadow: 0 0 24px -4px var(--line);
}
.get-qr img { display: block; width: 92px; height: 92px; }
.get-words { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.get-head { margin: 0; display: flex; flex-direction: column; line-height: 1.2; }
.get-head b { font-family: 'Goldman', sans-serif; font-weight: 700; font-size: 19px; letter-spacing: .08em; text-transform: uppercase; text-shadow: 0 0 12px var(--neon); }
.get-head span { font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: var(--mist); }
html.watch .get, html.watch .get-words { display: contents; } /* (the badges then the floor's own, as before) */
html.watch .get-qr, html.watch .get-head { display: none; }
@media (max-aspect-ratio: 9/10), (max-height: 520px) {
  .get, .get-words { display: contents; }
  .get-qr, .get-head { display: none; }
}
@media (min-aspect-ratio: 9/10) and (min-height: 521px) {
  html:not(.watch) .get-words .stores { --bh: 40px; margin-left: calc(-1 * (var(--bh) / 4 + 1px)); }
}
.store { display: block; padding: calc(var(--bh) / 4 + 1px); border-radius: calc(var(--bh) / 4 + 10px); }
.store img { display: block; height: var(--bh); width: auto; }

.champ {
  position: fixed; top: max(20px, env(safe-area-inset-top)); right: 20px;
  display: flex; align-items: center; gap: 10px; padding: 6px 16px 6px 6px;
  border-radius: 16px; border: 1px solid rgba(255, 209, 102, .4); background: rgba(3, 7, 26, .7);
  box-shadow: 0 0 22px -8px rgba(255, 209, 102, .6);
}
.champ[hidden] { display: none; }
/* (today's #1 not in yet: its room kept, so nothing moves as it comes in, fading in) */
.champ, .rival, .beat { transition: opacity .4s ease-out; }
.champ.pending, .rival.pending, .champ.pending + .beat { visibility: hidden; opacity: 0; }
.champ-bot { width: 46px; height: 46px; display: block; }
.champ-who { display: flex; flex-direction: column; line-height: 1.25; }
.champ-label { font-size: 10px; letter-spacing: .2em; text-transform: uppercase; color: var(--gold); }
.champ-name { font-family: 'Goldman', sans-serif; font-weight: 700; font-size: 16px; letter-spacing: .04em; max-width: 12em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.champ-score { font-family: 'Goldman', sans-serif; font-weight: 700; font-size: 24px; color: var(--gold); text-shadow: 0 0 12px rgba(255, 209, 102, .55); margin-left: 6px; }
.challenge { display: contents; }
.beat { display: none; }
/* On a computer, today's #1 is a gold line under the scan block, a challenge (its card hidden); a phone has the card
   under the game, and other screens the card top right. */
.rival { display: none; margin: 0; font-size: 11px; letter-spacing: .2em; text-transform: uppercase; color: var(--gold); text-shadow: 0 0 10px rgba(255, 209, 102, .45); }
.rival b { font-family: 'Goldman', sans-serif; font-weight: 700; font-size: 12px; letter-spacing: .08em; }
@media (min-aspect-ratio: 9/10) and (min-height: 521px) {
  html:not(.watch) .rival:not([hidden]) { display: block; }
  html:not(.watch) .champ { display: none; }
}

/* (on a narrow phone, the © line goes under the links, each kept whole) */
.links { grid-column: 1 / -1; justify-self: center; display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 18px; font-size: 12px; letter-spacing: .12em; color: var(--mist); white-space: nowrap; }
.links a { color: inherit; text-decoration: none; }
.links a:hover { color: var(--ink); }
/* the credit lines the badges ask for */
.legal { grid-column: 1 / -1; justify-self: center; margin: -6px 0 0; max-width: 62em; text-align: center; text-wrap: balance; font-size: 10px; line-height: 1.5; letter-spacing: .04em; color: color-mix(in srgb, var(--mist) 65%, transparent); }
.links a:focus-visible, a.store:focus-visible { outline: 2px solid var(--neon); outline-offset: 3px; }

/* Upright phones: the logo up top, the phone in the middle, everything else stacked under it. Laid out for an iPhone
   SE (375 x 667); a taller phone scales it by --k (main.js: its height over the SE's, 1 to 1.4) where there's room
   to grow: the words, the #1 card, the gaps, and the badges as far as the width lets them (the phone itself is as
   wide as it can be already). What's left over goes to the margins top and bottom (--extra, main.js), so the middle
   never stretches. */
@media (max-aspect-ratio: 9/10) {
  .sky { padding-top: calc(max(34px * var(--k, 1), env(safe-area-inset-top) + 14px) + var(--extra, 0px)); }
  .logo { --logo: max(38px, min(43px * var(--k, 1), 16vw, 72px)); }
  .tagline { font-size: calc(14px * min(var(--k, 1), 1.2)); letter-spacing: .28em; padding-left: .28em; }
  .sub { font-size: calc(13px * min(var(--k, 1), 1.15)); max-width: 24em; }
  .floor {
    grid-template-columns: 1fr; justify-items: center; gap: calc(14px * var(--k, 1));
    padding-top: calc(22px * var(--k, 1)); padding-bottom: calc(max(20px, env(safe-area-inset-bottom)) + var(--extra, 0px));
  }
  .stores { order: 0; } .links { order: 1; } .legal { order: 2; }
  /* (the badges, both, wherever it's seen (so it's plain it's on both, to tell a friend): Apple's 40px at least, and no
     bigger than lets the row of both fit the width) */
  .stores { --bh: max(40px, min(40px * var(--k, 1), (100vw - 68px) / 7.37)); gap: 10px; }
  /* (and on a phone the demo above them a way to its own store) */
  html.watch.ios .device, html.watch.android .device { pointer-events: auto; cursor: pointer; }
  .champ { position: static; }
  /* today's #1 under the phone (main.js sets where, --under), a challenge on the way down to the store buttons */
  .challenge { display: flex; flex-direction: column; align-items: center; gap: calc(8px * var(--k, 1)); position: absolute; left: 0; right: 0; top: var(--under, 60%); pointer-events: none; }
  .challenge > * { zoom: min(var(--k, 1), 1.3); }
  .beat { display: block; margin: 0; font-size: 12px; letter-spacing: .22em; padding-left: .22em; text-transform: uppercase; color: var(--gold); text-shadow: 0 0 10px rgba(255, 209, 102, .45); }
  .champ[hidden] + .beat { display: none; }
  .links { gap: 5px 14px; font-size: calc(11px * min(var(--k, 1), 1.2)); }
  .legal { font-size: calc(10px * min(var(--k, 1), 1.2)); }
}
/* (where the line it'd take would come off the demo, main.js's tight: just the ©, the links in one row) */
html.tight .links .rights { display: none; }

/* Short landscape windows (a phone on its side): smaller words, no subline, one row on the floor */
@media (max-height: 520px) and (min-aspect-ratio: 9/10) {
  .sky { padding-top: max(14px, env(safe-area-inset-top)); }
  .logo { --logo: clamp(22px, 6.4vh, 36px); }
  .tagline { margin-top: .5em; font-size: 12px; }
  .sub { display: none; }
  .floor { padding: 12px 18px max(10px, env(safe-area-inset-bottom)); }
  .stores { --bh: 40px; }
  .champ { top: max(10px, env(safe-area-inset-top)); right: max(12px, env(safe-area-inset-right)); padding: 3px 10px 3px 3px; }
  .champ-bot { width: 34px; height: 34px; }
  .champ-score { font-size: 18px; }
}

/* The phone: the game's screen, sideways as it's played (main.js places it and sizes the bezel, --b, and the screen's
   corners, --r). The screen is a hole: the world under it is the one on the page, dimmed outside it. */
.device {
  position: absolute; pointer-events: none; container-type: size; border-radius: var(--r, 30px);
  box-shadow: 0 0 0 var(--b, 12px) #080b18, 0 0 0 calc(var(--b, 12px) + 1.5px) rgba(154, 171, 214, .45), 0 0 70px calc(var(--b, 12px) + 6px) rgba(0, 0, 0, .55);
}
/* the island, at the left as it's held; the side button on its top edge */
.device::before { content: ''; position: absolute; left: 3cqh; top: 50%; width: 8.5cqh; height: 30cqh; transform: translateY(-50%); border-radius: 99px; background: #000; transition: opacity .4s; }
html.bleed .device::before { opacity: 0; } /* (filling a real phone: it has its own) */
.device::after { content: ''; position: absolute; top: calc(-1 * var(--b, 12px) - 3px); left: 22%; width: 8%; height: 3px; border-radius: 2px 2px 0 0; background: rgba(154, 171, 214, .45); }
.hud { position: absolute; top: 5cqh; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; line-height: 1; }
.hud b { font-family: 'Goldman', sans-serif; font-weight: 700; font-size: 14cqh; color: var(--ink); text-shadow: 0 0 .12em var(--neon), 0 0 .4em color-mix(in srgb, var(--neon) 50%, transparent); }
/* as each point lands (main.js): it pops, tilting one way then the other (main.gd's keeper: up in 0.08s, back in 0.14s) */
.hud b.pop { animation: pop .22s linear; }
.hud b.pop.left { animation-name: pop-left; }
@keyframes pop { 36% { transform: scale(1.22) rotate(8deg); animation-timing-function: cubic-bezier(.33, 1, .68, 1); } }
@keyframes pop-left { 36% { transform: scale(1.22) rotate(-8deg); animation-timing-function: cubic-bezier(.33, 1, .68, 1); } }
/* each digit; one that's changed rolls over, the old sliding up and out as the new springs up in */
.hud b i { font-style: normal; display: inline-block; position: relative; }
.hud b i.roll .was { position: absolute; left: 0; right: 0; text-align: center; animation: roll-out .2s cubic-bezier(.33, 1, .68, 1) forwards; }
.hud b i.roll .now { display: inline-block; animation: roll-in .2s cubic-bezier(.34, 1.8, .64, 1) both; }
@keyframes roll-out { to { transform: translateY(-80%); opacity: 0; } }
@keyframes roll-in { from { transform: translateY(80%); opacity: 0; } 66% { opacity: 1; } }
/* every 4 pillars, with the fireworks: bigger, and gold a moment */
.hud b.burst { animation: burst .6s ease-out; }
@keyframes burst {
  30% { transform: scale(1.45); color: #fff3d1; text-shadow: 0 0 .12em var(--gold), 0 0 .5em color-mix(in srgb, var(--gold) 70%, transparent); }
}
.hud > span { margin-top: 1.8cqh; font-size: max(10px, 4cqh); letter-spacing: .24em; padding-left: .24em; text-transform: uppercase; color: var(--mist); }

/* Where you are: the game's plate (title.gd), top left of its screen past the island, the world's name glowing in its
   colour (--where, main.js); it turns over as the world changes, and keeps out of the way of someone's run. It's in
   proportion with the screen, as the score is (on a phone upright the screen's small: a plate any bigger reached the
   score with the longer names), its words no smaller than 7px. */
.where {
  position: absolute; left: 13cqh; top: 5cqh; display: flex; align-items: center; gap: 1.8cqh;
  padding: 1.5cqh 3.2cqh 1.5cqh 1.8cqh; border-radius: 3cqh;
  background: rgba(4, 6, 18, .74); border: 1px solid color-mix(in srgb, var(--where, #5ef2ff) 45%, transparent);
  transition: opacity .4s;
}
.where i { width: max(1.5px, .9cqh); height: 4.6cqh; border-radius: 9px; background: var(--where, #5ef2ff); }
.where span {
  font-family: 'Goldman', sans-serif; font-weight: 700; font-size: max(7px, 4.2cqh); letter-spacing: .08em; line-height: 1;
  text-transform: uppercase; white-space: nowrap; color: var(--where, #5ef2ff); text-shadow: 0 0 .5em color-mix(in srgb, var(--where, #5ef2ff) 60%, transparent);
}
.where.flip { animation: flip .34s ease-in-out; }
@keyframes flip { 50% { transform: scaleY(.05); } }
html.bleed .where { display: none; } /* (a phone on its side: the name top left is the page's) */
/* a phone held sideways: the screen grows to fill it (main.js zooms it), the page's words make way for a name and
   the store badges in the corners */
.sky, .floor { transition: opacity .3s; }
html.leaving .sky, html.leaving .floor { opacity: 0; }
html.bleed .sky { align-items: flex-start; padding: max(12px, env(safe-area-inset-top)) 0 0 max(18px, env(safe-area-inset-left)); }
html.bleed .logo { --logo: 16px; flex-direction: row; gap: .32em; text-shadow: 0 0 .12em var(--neon); }
html.bleed .logo-bot { font-size: var(--logo); margin: 0; }
html.bleed .tagline, html.bleed .sub, html.bleed .links, html.bleed .champ, html.bleed .legal { display: none; }
html.bleed .floor { top: 0; bottom: auto; grid-template-columns: 1fr auto; padding: max(10px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) 0 0; }
html.bleed .stores { grid-column: 2; --bh: 40px; gap: 10px; }
html.bleed .store { background: var(--night); } /* (over the moving world: the badges' solid ground) */

