/*
 * The public pages: a spot, a challenge, a player.
 *
 * Built to read like the picture you share from the app rather than like a web page about it
 * (besluit 2026-09-18): the same ground lit from above, the same mark at the top, the same number
 * between two hairlines with what it is over it and where it was done under it. Somebody who taps
 * a link in a group chat and somebody who opens the app a minute later should not feel they have
 * been to two places.
 *
 * Hand-written and not built, because `apps/api` has no npm step and adding one for three pages
 * would cost more than it saves (see 07-deploy.md). The values under `:root` are copies of
 * `packages/shared/tokens.json`; change them there first and bring them here, the same deal the
 * admin stylesheet already lives by.
 */
:root {
  --bg: #0a0b0c;
  --bg-elevated: #111315;
  --panel: #15181b;
  --border: rgba(255, 255, 255, 0.08);
  --border-strong: rgba(255, 255, 255, 0.16);
  --text: #f4f6f7;
  --text-muted: #9aa3ab;
  --text-faint: #5f6870;
  --accent: #1ad4ff;
  --accent-dim: rgba(26, 212, 255, 0.16);
  --on-accent: #061012;
  --gold: #e0a93a;
  --gold-dim: rgba(224, 169, 58, 0.18);
  /* The hairlines the subject sits between. Cyan, faint: a frame and not a border. */
  --rule: rgba(26, 212, 255, 0.28);
}

/*
 * The brand's display face, the same one the app sets a record in. Self-hosted from /fonts, which
 * the ITF Free Font License allows in so many words. Capitals only, which is the only way it is
 * drawn anywhere.
 */
@font-face {
  font-family: 'Nippo';
  src: url('/fonts/Nippo-Bold.ttf') format('truetype');
  font-weight: 700;
  font-display: swap;
}

@font-face {
  font-family: 'Atlas fitness-gym';
  src: url('/fonts/atlas/fitness-gym.ttf') format('truetype');
  font-display: block;
}

@font-face {
  font-family: 'Atlas yoga';
  src: url('/fonts/atlas/yoga.ttf') format('truetype');
  font-display: block;
}

@font-face {
  font-family: 'Atlas basic-ui';
  src: url('/fonts/atlas/basic-ui.ttf') format('truetype');
  font-display: block;
}

@font-face {
  font-family: 'Atlas marketing';
  src: url('/fonts/atlas/marketing.ttf') format('truetype');
  font-display: block;
}

@font-face {
  font-family: 'Atlas achievement';
  src: url('/fonts/atlas/achievement.ttf') format('truetype');
  font-display: block;
}

@font-face {
  font-family: 'Atlas goals';
  src: url('/fonts/atlas/goals.ttf') format('truetype');
  font-display: block;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 16px/1.55 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/*
 * One light above the page, falling off before it reaches the middle, over a floor that fades back
 * to the background. The app icon's finish, and the share card's.
 */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(132% 52% at 50% -8%, rgba(26, 212, 255, 0.2) 0%, rgba(26, 212, 255, 0.07) 32%, rgba(26, 212, 255, 0) 62%);
}

a { color: var(--accent); }

.wrap { position: relative; max-width: 560px; margin: 0 auto; padding: 28px 20px 56px; }

/* The mark, centred and a little way back from full strength: the page is about a place and we are
   the paper it is printed on. */
.brand { display: block; width: 132px; margin: 0 auto 30px; opacity: 0.82; color: var(--text); }
.brand svg { width: 100%; height: auto; display: block; }

/* Capitals, tracked, in the brand face. The one thing on a page that is set the way the app sets it. */
.display { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; }

/*
 * The place, across the top of the page and melting into it.
 *
 * Behind the column rather than inside it, so it is the width of the window on a phone and on a
 * desktop alike. The fade goes opaque at the bottom so there is no seam, clears through the middle
 * where the picture actually is, and comes back a little at the top so the logo survives a bright
 * sky. The same three jobs the card's own fade does.
 */
.photo { position: absolute; top: 0; left: 0; right: 0; height: 46vh; max-height: 420px; overflow: hidden; }
.photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo .fade {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    var(--bg) 0%,
    rgba(10, 11, 12, 0.9) 22%,
    rgba(10, 11, 12, 0.4) 52%,
    rgba(10, 11, 12, 0.15) 76%,
    rgba(10, 11, 12, 0.5) 100%
  );
}
/* The column starts under the picture, with a little of it behind the first line so the two are
   one thing rather than a photo and then a page. */
body.has-photo .wrap { padding-top: 28px; }
body.has-photo .below-photo { margin-top: calc(min(46vh, 420px) - 150px); }

/*
 * Shorter, for a page whose subject has to be on screen without scrolling. A challenge page is a
 * number: making somebody scroll past a picture of a park to reach it is the wrong way round
 * (besluit 2026-09-18). The place is still there, behind and above it.
 */
body.tight-photo .photo { height: 30vh; max-height: 250px; }
body.tight-photo .fade { background: linear-gradient(to top, var(--bg) 0%, rgba(10, 11, 12, 0.94) 34%, rgba(10, 11, 12, 0.55) 70%, rgba(10, 11, 12, 0.45) 100%); }

h1 { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; font-size: 30px; line-height: 1.08; margin: 0; letter-spacing: 0.02em; }
.kicker { color: var(--text-muted); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; margin: 0 0 8px; font-weight: 600; }
.lede { color: var(--text-muted); margin: 16px 0 0; }

/*
 * The subject: what it is, how many, where. The share card's centre block, on a page.
 */
.subject { text-align: center; margin: 8px 0 0; }
.subject .rule { height: 1px; width: 56%; margin: 0 auto; background: var(--rule); }
.subject .what { color: var(--accent); font-size: 17px; letter-spacing: 0.2em; margin: 24px 0 2px; }
.subject .figure { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 92px; line-height: 1.02; letter-spacing: 0.01em; margin: 0; }
.subject .unit { color: var(--text-muted); font-size: 12px; letter-spacing: 0.32em; margin: 2px 0 24px; }
.subject .where { color: var(--text-muted); font-size: 13px; letter-spacing: 0.12em; margin: 20px 0 0; }
.subject .unclaimed { color: var(--accent); text-transform: uppercase; letter-spacing: 0.04em; }

.rows { margin: 26px 0 0; border: 1px solid var(--border); border-radius: 16px; overflow: hidden; background: var(--panel); }
.row { display: flex; align-items: center; gap: 14px; padding: 15px 16px; border-top: 1px solid var(--border); }
.row:first-child { border-top: 0; }
.row .grow { flex: 1; min-width: 0; }
/* A row that is a link is still a row: it takes the page's ink, not the accent, and the whole
   thing is the target rather than the words in it. */
a.row { text-decoration: none; color: inherit; }
a.row:hover { background: var(--bg-elevated); }
.row .name { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; font-size: 15px; }
.row .sub { color: var(--text-muted); font-size: 13px; margin-top: 2px; }

/*
 * The exercise, in the circle the app draws it in: gold when somebody holds it, grey when nobody
 * does. The icon font is the app's own, copied to /fonts/atlas for the panel and used here for the
 * same reason (besluit 2026-09-18) — a row that names an exercise in words alone is a list, and
 * this is a scoreboard.
 */
.row .mark { width: 38px; height: 38px; border-radius: 999px; border: 1px solid var(--border-strong); background: var(--bg); display: flex; align-items: center; justify-content: center; flex: none; }
.row .mark i { font-style: normal; font-size: 17px; line-height: 1; color: var(--text-muted); }
.row.held .mark { border-color: rgba(224, 169, 58, 0.45); background: var(--gold-dim); }
.row.held .mark i { color: var(--gold); }

/* The record itself, which is what somebody came to this page to read. */
.row .record { text-align: right; flex: none; }
.row .record .n { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 28px; line-height: 1; letter-spacing: 0.01em; font-variant-numeric: tabular-nums; }
.row.held .record .n { color: var(--gold); }
.row .record .u { color: var(--text-faint); font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; margin-top: 4px; font-weight: 600; }
.row .record .u.open { color: var(--accent); }
.row .value { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 22px; letter-spacing: 0.02em; font-variant-numeric: tabular-nums; }
.row .value.held { color: var(--gold); }
.row .value.none { color: var(--text-faint); }

/* Three numbers side by side, the way the app's profile puts them. */
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 24px; }
.tile { border: 1px solid var(--border); background: var(--panel); border-radius: 14px; padding: 14px 10px; text-align: center; }
.tile .n { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 26px; line-height: 1.1; }
.tile .n.gold { color: var(--gold); }
.tile .l { color: var(--text-faint); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; margin-top: 4px; font-weight: 600; }

.face { display: block; width: 36px; height: 36px; border-radius: 999px; object-fit: cover; background: var(--bg-elevated); }
.face.lg { width: 88px; height: 88px; border: 2px solid var(--accent); }

.tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; justify-content: center; }
.tag { border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 6px 12px; font-size: 11px; color: var(--text-muted); letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; }
.tag.gold { border-color: rgba(224, 169, 58, 0.32); background: var(--gold-dim); color: var(--gold); }

.shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 12px; }
.shots img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 12px; border: 1px solid var(--border); display: block; }

/*
 * The one thing this page wants you to do, and the only place it is asked. Lit like the badge tiles
 * in the app, so the ask reads as part of the same object rather than as an advertisement bolted on.
 */
.cta { margin-top: 34px; border: 1px solid rgba(26, 212, 255, 0.18); border-radius: 18px; padding: 24px 20px; text-align: center; background: var(--panel) radial-gradient(120% 90% at 50% -20%, rgba(26, 212, 255, 0.16) 0%, rgba(26, 212, 255, 0) 70%); }
.cta p { margin: 0 0 18px; color: var(--text-muted); }

/* Apple's badge and Google's, as they supply them. Sized by height, which is how both ask for it. */
.stores { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; align-items: center; }
.stores a, .stores .soon { display: block; line-height: 0; }
.stores img { height: 48px; width: auto; display: block; }
/* Not a link until there is something to link to. Dimmed, and it says why underneath. */
.stores .soon { opacity: 0.4; filter: grayscale(1); }
.stores .soon-label { display: block; margin-top: 6px; color: var(--text-faint); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; text-align: center; font-weight: 600; line-height: 1.4; }

footer { margin-top: 44px; color: var(--text-faint); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; text-align: center; font-weight: 600; }

/*
 * The one form on these pages: deleting an account (besluit 2026-09-19). Every other public page
 * is read-only, so this is the only place a field exists, and it is styled to look like the panels
 * around it rather than like a browser default in the middle of them.
 */
.form { margin-top: 26px; }
.field { display: block; }
.field .l { display: block; color: var(--text-faint); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600; margin-bottom: 8px; }
.field input {
  width: 100%;
  border: 1px solid var(--border-strong);
  background: var(--panel);
  color: var(--text);
  border-radius: 12px;
  padding: 14px 16px;
  font: inherit;
  font-size: 16px; /* Anything smaller and iOS zooms the page when the field is tapped. */
}
.field input:focus { outline: none; border-color: var(--accent); }
/* The code is six characters that have to be read back one at a time. */
.field input.code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.32em; text-transform: uppercase; }

.go {
  display: block; width: 100%; margin-top: 16px;
  border: 0; border-radius: 12px; padding: 15px 20px;
  background: var(--accent); color: var(--on-accent);
  font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; font-size: 15px; cursor: pointer;
}
/* The last button is not cyan. Cyan is the colour of everything you are invited to do here. */
.go.danger { background: transparent; border: 1px solid rgba(255, 92, 92, 0.5); color: #ff5c5c; }
.fine { color: var(--text-faint); font-size: 12px; line-height: 1.5; margin: 14px 0 0; }
.bad { color: #ff5c5c; font-size: 13px; margin: 10px 0 0; }

/*
 * The pages that are mostly words: what RYVLS is, support, privacy, terms.
 *
 * Everything above this line is a scoreboard in a 560 column, because a shared link is about one
 * place and one number. These four are read instead of scanned, so they get a wider column and a
 * left edge to run along. Nothing else changes: the same ground, the same mark, the same hairlines.
 */
body.read .wrap { max-width: 680px; }

.page-head { margin-top: 6px; }
body.read .page-head h1 { font-size: 40px; }
.page-head .stamp { color: var(--text-muted); font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 600; margin: 16px 0 0; }
.page-head .lede { font-size: 18px; line-height: 1.75; }

/* A short cyan line of capitals above a block, which is how a section announces itself here. */
.kick { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--accent); font-size: 11px; letter-spacing: 0.3em; text-transform: uppercase; margin: 0 0 18px; }

/*
 * Long text. Generous leading, a heading that is plainly a heading, and links that are visible
 * without shouting: underlined in a dim rule rather than set in the accent, which stays reserved
 * for the things you are invited to do.
 */
.prose { margin-top: 10px; }
.prose h2 { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; font-size: 24px; line-height: 1.16; letter-spacing: 0.03em; margin: 44px 0 14px; }
.prose p { color: var(--text-muted); font-size: 17px; line-height: 1.75; margin: 0 0 14px; }
.prose p:last-child { margin-bottom: 0; }
.prose ul { margin: 0; padding-left: 20px; }
.prose li { color: var(--text-muted); font-size: 17px; line-height: 1.7; margin: 0 0 10px; }
.prose a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(26, 212, 255, 0.4); }
/* Where reviewed wording still has to go. Visible on purpose: a gap you can see is a gap that gets
   filled, and one you cannot see is a gap that ships. */
.prose .todo { color: var(--text-faint); }

/* The jump list at the top of a long page, so nobody has to scroll to find out what is in it. */
.toc { display: flex; flex-wrap: wrap; gap: 14px 18px; margin-top: 30px; border: 1px solid var(--border); background: var(--panel); border-radius: 16px; padding: 20px 22px; }
.toc a { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; text-decoration: none; }

/* One paragraph lit like the ask, for the single thing on a page that is worth stopping at. */
.callout { border: 1px solid rgba(26, 212, 255, 0.22); border-radius: 16px; padding: 22px 20px; background: var(--panel) radial-gradient(130% 120% at 30% -24%, rgba(26, 212, 255, 0.18) 0%, rgba(26, 212, 255, 0) 70%); margin: 0 0 14px; }
.callout p { color: var(--text); margin: 0; }

/* What is kept and why, side by side. It stacks on a phone, where two columns of this leave no
   room for either. */
.facts { border-bottom: 1px solid var(--border); }
.facts .f { display: flex; flex-wrap: wrap; gap: 8px 24px; padding: 16px 0; border-top: 1px solid var(--border); }
.facts .f .k { flex: none; width: 190px; font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; font-size: 14px; }
.facts .f .v { flex: 1; min-width: 0; color: var(--text-muted); font-size: 16px; line-height: 1.65; }

/*
 * What it does, and what it does not. The second half is the useful one: a product that will not
 * say what it skips gets sorted by whoever is guessing, and is then judged on somebody else's
 * terms.
 */
.does { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 34px; }
.does .col { border: 1px solid var(--border); border-radius: 16px; padding: 22px 20px; }
.does .col.yes { border-color: rgba(26, 212, 255, 0.22); background: var(--panel); }
.does .kick { color: var(--text-muted); }
.does .col.yes .kick { color: var(--accent); }
.does ul { margin: 0; padding: 0; list-style: none; }
.does li { display: flex; gap: 12px; color: var(--text-muted); font-size: 15px; line-height: 1.6; margin: 0 0 14px; }
.does li:last-child { margin-bottom: 0; }
.does .col.yes li { color: var(--text); }
.does li::before { content: ''; flex: none; width: 6px; height: 6px; margin-top: 8px; background: rgba(255, 255, 255, 0.28); }
.does .col.yes li::before { background: var(--accent); }

/* A question and its answer, open. No accordion: an answer that is folded away is invisible to a
   search engine and to an assistant, and these pages exist to be landed on from a search. */
.group { margin-top: 44px; }
.qa { border-top: 1px solid var(--border); padding: 20px 0; }
.qa:last-child { border-bottom: 1px solid var(--border); }
.qa h3 { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; font-size: 17px; line-height: 1.2; margin: 0 0 10px; }
.qa p { color: var(--text-muted); font-size: 17px; line-height: 1.7; margin: 0; }
.qa a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(26, 212, 255, 0.4); }

/* A slot with nothing in it yet, drawn so that it reads as deliberate rather than as forgotten. */
.slot { border: 1px dashed rgba(255, 255, 255, 0.18); border-radius: 16px; padding: 22px 20px; margin-top: 40px; }
.slot p { color: var(--text-faint); font-size: 17px; line-height: 1.75; margin: 0; }

/* Where to write when the page did not answer it. An address and not a form: a form is a wall
   between somebody with a problem and somebody who can fix it. */
.reach { border: 1px solid rgba(26, 212, 255, 0.22); border-radius: 18px; padding: 26px 22px; margin-top: 52px; background: var(--panel) radial-gradient(130% 110% at 20% -20%, rgba(26, 212, 255, 0.16) 0%, rgba(26, 212, 255, 0) 70%); }
.reach h2 { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; font-size: 24px; letter-spacing: 0.03em; margin: 0 0 12px; }
.reach p { color: var(--text-muted); font-size: 17px; line-height: 1.7; margin: 0 0 16px; }
.reach a { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 15px; letter-spacing: 0.1em; text-decoration: none; }

/* The footer carries the four pages a store listing has to be able to reach, on every page. */
footer .links { display: flex; flex-wrap: wrap; gap: 10px 20px; justify-content: center; margin-top: 18px; }
footer .links a { color: var(--text-muted); text-decoration: underline; text-underline-offset: 4px; text-decoration-color: rgba(255, 255, 255, 0.2); }

@media (max-width: 620px) {
  body.read .page-head h1 { font-size: 32px; }
  .does { grid-template-columns: minmax(0, 1fr); }
  .facts .f .k { width: 100%; }
}

/*
 * While the beta is on, every public page says so before it says anything else.
 *
 * Above the picture rather than beside it: `.photo` starts at the top of the document, so the bar
 * is lifted over it and given its own solid ground. That way it needs no agreement with the photo
 * about how tall it is, which matters on a phone where the sentence takes three lines.
 *
 * Switched in the panel, on Settings. On by default, because announcing a beta that has ended is
 * embarrassing and failing to announce one that has not is worse.
 */
.beta {
  position: relative;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 12px;
  padding: 11px 18px;
  background: #0d1a20;
  border-bottom: 1px solid rgba(26, 212, 255, 0.3);
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
}
.beta .chip {
  font-family: 'Nippo', system-ui, sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: 10px;
  color: var(--accent);
  border: 1px solid rgba(26, 212, 255, 0.4);
  border-radius: 999px;
  padding: 4px 10px;
  flex: none;
}
.beta a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(26, 212, 255, 0.4); }

/*
 * The homepage.
 *
 * The only page here that is a pitch rather than a record, so it gets the wide column and a rhythm
 * of its own. Everything it is built from is already above: the same tokens, the same scoreboard
 * rows, the same ask at the bottom. What is new is the light, which walks down the page instead of
 * sitting still at the top, and the screens it walks past.
 */
body.home .wrap { max-width: 1040px; padding: 40px 20px 110px; }

/* The page light is fixed everywhere else, which is right for a page you barely scroll. Here it
   would follow you down five screens, so it is pinned to the top of the document and made big
   enough to sit behind the mark rather than behind the middle of the block. */
body.home::before {
  position: absolute;
  height: 1560px;
  background: radial-gradient(1100px 700px at 50% 90px, rgba(26, 212, 255, 0.34) 0%, rgba(26, 212, 255, 0.12) 38%, rgba(26, 212, 255, 0) 72%);
}

/* Concrete over that light, so the glow has a surface instead of being a wash. Value noise on a
   wrapping grid, generated rather than photographed: it tiles with no seam and carries no licence.
   Masked away downwards so it never reaches the bands. */
body.home::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  height: 1560px;
  pointer-events: none;
  opacity: 0.11;
  background-image: url('/images/concrete.webp');
  background-size: 384px 384px;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.85) 46%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0.85) 46%, transparent 100%);
}

body.home .brand { width: 180px; opacity: 1; margin-bottom: 0; }

.hero { position: relative; text-align: center; margin-top: 54px; }
.hero h1 { font-size: 82px; line-height: 1.04; }
.hero .lede { max-width: 640px; margin: 26px auto 0; font-size: 19px; line-height: 1.6; }
.hero .stores { margin-top: 50px; }
.hero .micro { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--text-muted); font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase; margin: 28px 0 0; }

/*
 * Three screens as one object. The counting shot in front because it is the picture that explains
 * the app, the map and the record behind it. The big one overlaps the inner edge of both, and the
 * bottoms nearly line up: exactly level is stiff, far apart is a hand of cards.
 */
.trio { position: relative; height: 685px; margin: 50px auto 0; max-width: 1040px; }
.trio img { position: absolute; display: block; height: auto; }
.trio .big { width: 340px; left: 50%; margin-left: -170px; top: 0; z-index: 2; }
.trio .side { width: 262px; top: 139px; z-index: 1; }
.trio .side.l { left: 50%; margin-left: -346px; }
.trio .side.r { left: 50%; margin-left: 84px; }

/*
 * Real takeovers, edge to edge under the hero, faded off at both ends. It is the one thing on the
 * page that proves the city is alive before anybody has scrolled. It does not move: a strip that
 * scrolls by itself is a thing to wait for rather than to read, and the names on it are the point.
 */
.ticker { position: relative; margin: 96px -20px 0; height: 68px; overflow: hidden; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.ticker .run { position: absolute; left: 0; top: 0; bottom: 0; display: flex; align-items: center; gap: 18px; padding-left: 40px; }
.ticker .who { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 14px; letter-spacing: 0.18em; text-transform: uppercase; white-space: nowrap; }
.ticker .who em { font-style: normal; font-weight: 500; color: var(--text-muted); }
.ticker .when { color: var(--text-muted); font-size: 11px; letter-spacing: 0.18em; font-weight: 600; white-space: nowrap; }
.ticker .dot { color: var(--accent); font-size: 9px; line-height: 1; }
.ticker .edge { position: absolute; top: 0; bottom: 0; width: 170px; pointer-events: none; }
.ticker .edge.l { left: 0; background: linear-gradient(to right, var(--bg) 0%, rgba(10, 11, 12, 0) 100%); }
.ticker .edge.r { right: 0; background: linear-gradient(to left, var(--bg) 0%, rgba(10, 11, 12, 0) 100%); }
.ticker .glow { position: absolute; left: 50%; top: 50%; width: 1400px; height: 300px; margin: -150px 0 0 -700px; pointer-events: none; background: radial-gradient(closest-side, rgba(26, 212, 255, 0.26) 0%, rgba(26, 212, 255, 0.08) 42%, rgba(26, 212, 255, 0) 76%); }

/*
 * A band: words on one side, a screen on the other, and its own light on the side the screen is
 * on. The light is wider than the column and gets cut off by the edge of the page, which is what
 * makes it read as light rather than as a shape, and it hangs low so the hot middle lands on the
 * screen and not on a paragraph.
 */
.band { position: relative; display: flex; gap: 72px; align-items: center; margin-top: 150px; }
.band::before {
  content: '';
  position: absolute;
  top: 62%;
  width: 1320px;
  height: 1080px;
  margin: -540px 0 0 -660px;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(26, 212, 255, 0.26) 0%, rgba(26, 212, 255, 0.09) 40%, rgba(26, 212, 255, 0) 74%);
}
.band.lit-right::before { left: 86%; }
.band.lit-left::before { left: 14%; }
.band.lit-mid::before { left: 50%; top: 64%; width: 1620px; height: 1300px; margin: -650px 0 0 -810px; }
.band .col { position: relative; flex-grow: 1; }
.band h2 { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; font-size: 52px; line-height: 1.06; letter-spacing: 0.02em; margin: 0 0 22px; }
.band p { max-width: 480px; color: var(--text-muted); font-size: 18px; line-height: 1.7; margin: 0; }
.band .shot { position: relative; flex: none; width: 340px; }
.band .shot img { display: block; width: 100%; height: auto; }
.band .shot::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 52%;
  width: 483px;
  height: 483px;
  margin: -241px 0 0 -241px;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(26, 212, 255, 0.34) 0%, rgba(26, 212, 255, 0.1) 40%, rgba(26, 212, 255, 0) 74%);
}

/* One record, the way the app sets it: the number between two hairlines with what it is over it. */
.record-card { position: relative; border: 1px solid var(--border); background: var(--panel); border-radius: 20px; padding: 28px 24px 26px; text-align: center; max-width: 420px; margin-top: 30px; overflow: hidden; }
.record-card .lit { position: absolute; inset: 0; pointer-events: none; background: radial-gradient(130% 100% at 50% -12%, rgba(26, 212, 255, 0.34) 0%, rgba(26, 212, 255, 0.1) 34%, rgba(26, 212, 255, 0) 68%); }
.record-card .in { position: relative; }
.record-card .rule { height: 1px; width: 56%; margin: 0 auto; background: var(--rule); }
.record-card .what { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--accent); font-size: 14px; letter-spacing: 0.2em; text-transform: uppercase; margin: 20px 0 0; }
.record-card .n { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 108px; line-height: 1.02; margin: 2px 0 0; }
.record-card .u { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--text-muted); font-size: 11px; letter-spacing: 0.32em; text-transform: uppercase; margin: 2px 0 20px; }
.record-card .where { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--text-muted); font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; margin: 16px 0 14px; }

/*
 * The three exercises, as a strip you push rather than tabs you click.
 *
 * `apps/api` has no npm step and this page is not worth starting one for, so the choice was between
 * markup pretending to be a control and a control the browser already has. Scroll snapping is the
 * second: it swipes on a phone, drags on a trackpad, and the screen after this one is visible at
 * the edge, which is the part that makes anybody push it at all. The push-up shot is on its side
 * because the app turns the phone for an exercise seen from the side.
 */
.strip { display: flex; align-items: center; gap: 32px; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 8px; scrollbar-width: none; }
.strip::-webkit-scrollbar { display: none; }
.strip .slide { scroll-snap-align: start; flex: none; }
.strip .slide img { display: block; height: auto; }
.strip .slide img.up { width: 340px; }
.strip .slide img.flat { width: 430px; }
.strip .slide .name { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; color: var(--text-muted); font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase; text-align: center; margin: 18px 0 0; }

@media (max-width: 900px) {
  body.home .wrap { padding-top: 30px; }
  body.home .brand { width: 150px; }
  body.home::before, body.home::after { height: 1240px; }
  .hero { margin-top: 44px; }
  .hero h1 { font-size: 38px; }
  .hero .lede { font-size: 16px; }
  .trio { height: 419px; max-width: 350px; }
  .trio .big { width: 208px; margin-left: -104px; }
  .trio .side { width: 152px; top: 101px; }
  .trio .side.l { margin-left: -207px; }
  .trio .side.r { margin-left: 55px; }
  .ticker { margin-top: 72px; height: 56px; }
  .ticker .who { font-size: 12px; }
  .ticker .edge { width: 70px; }
  .band { display: block; margin-top: 90px; }
  .band::before { left: 50% !important; width: 880px; height: 1140px; margin: -570px 0 0 -440px; }
  .band h2 { font-size: 36px; }
  .band p { font-size: 16px; max-width: none; }
  .band .shot { width: 300px; margin: 32px auto 0; }
  .band .shot.first { margin-top: 0; margin-bottom: 32px; }
  .record-card { max-width: none; }
  .record-card .n { font-size: 92px; }
  .strip .slide img.up { width: 300px; }
  .strip .slide img.flat { width: 340px; }
}

/*
 * The pages about a place: a spot, and the city it sits in.
 *
 * Wider than a share card and narrower than the homepage, because these are lists you scan rather
 * than a number you read or a pitch you scroll. Everything on them is built from the rows and tiles
 * already above; what is new is the block that says where a thing is, and the small head that lets
 * a section carry a link on its right.
 */
body.wide .wrap { max-width: 880px; }
body.wide h1 { font-size: 56px; }
body.wide .page-head .lede { font-size: 17px; max-width: 640px; }

/* A section title with room for one link opposite it. */
.head { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; margin: 0 0 18px; }
.head .kick { margin: 0; }
.head a { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; text-decoration: none; white-space: nowrap; }
.sect { margin-top: 56px; }

/* Cities / Meppel / Calisthenics parks. The last one is where you are and is not a link. */
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin: 0 0 26px; }
.crumbs a, .crumbs .here { color: var(--text-muted); font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; font-weight: 600; text-decoration: none; }
.crumbs .here { color: var(--text); }
.crumbs span:not(.here) { color: rgba(255, 255, 255, 0.22); font-size: 11px; }

/*
 * Where it is, for the half of the traffic that came to find that out.
 *
 * The aerial if there is one, because a roof seen from above tells you more about whether you can
 * train there than a name does. Otherwise a lit pin on a ruled field, which says "a point on a map"
 * without pretending to be one. The district, the town and the coordinates underneath are text on
 * purpose: that is the part a crawler can read and an assistant can answer with.
 */
.place { border: 1px solid var(--border); border-radius: 18px; overflow: hidden; background: var(--panel); }
.place .view { position: relative; height: 220px; background: #0d1114; overflow: hidden; }
.place .view img { width: 100%; height: 100%; object-fit: cover; display: block; }
.place .grid { position: absolute; inset: 0; background-image: linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px); background-size: 46px 46px; }
.place .halo { position: absolute; left: 50%; top: 50%; width: 260px; height: 260px; margin: -130px 0 0 -130px; background: radial-gradient(closest-side, rgba(26, 212, 255, 0.22) 0%, rgba(26, 212, 255, 0) 72%); }
.place .pin { position: absolute; left: 50%; top: 50%; width: 20px; height: 20px; margin: -10px 0 0 -10px; border-radius: 999px; background: var(--accent); box-shadow: 0 0 0 8px rgba(26, 212, 255, 0.18); }
.place .at { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; padding: 16px 18px; border-top: 1px solid var(--border); }
.place .at .grow { flex: 1; min-width: 0; }
.place .at .n { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; font-size: 14px; }
.place .at .c { color: var(--text-muted); font-size: 13px; margin-top: 3px; font-variant-numeric: tabular-nums; }
.place .at a { font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; text-decoration: none; white-space: nowrap; }
.place .at i { font-style: normal; font-size: 17px; color: var(--text-muted); flex: none; }

/* The city page leads with counts rather than a picture, so its tiles take four across. */
.tiles.four { grid-template-columns: repeat(4, 1fr); }

/* Every category is its own page, so the row that looks like a filter is a row of links. */
.pills { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 30px; }
.pills a { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 16px; border-radius: 999px; text-decoration: none; border: 1px solid var(--border); font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-muted); }
.pills a.on { border-color: rgba(26, 212, 255, 0.45); background: var(--accent-dim); color: var(--accent); }
.pills a span { font-weight: 500; color: var(--text-muted); }

/* Districts, as cards you can open. */
.districts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.districts a { display: block; text-decoration: none; color: inherit; border: 1px solid var(--border); background: var(--panel); border-radius: 14px; padding: 16px; }
.districts .n { display: flex; align-items: center; gap: 10px; font-family: 'Nippo', system-ui, sans-serif; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; font-size: 14px; }
.districts .n i { font-style: normal; font-size: 15px; color: var(--text-muted); }
.districts .s { color: var(--text-muted); font-size: 12px; margin-top: 8px; }
.districts .s .gold { color: var(--gold); }
.districts .s .open { color: var(--accent); }

@media (max-width: 720px) {
  body.wide h1 { font-size: 36px; }
  .tiles.four { grid-template-columns: repeat(2, 1fr); }
  .districts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .place .view { height: 170px; }
}
