/* One face for everything a visitor reads: Fira Code (SIL OFL 1.1). Its
   Medium carries every weight from 500 up, so nothing is ever a bold the
   browser makes up. */
@font-face { font-family: "Fira Code"; src: url("/fonts/FiraCode-Light-latin.woff2") format("woff2"); font-weight: 100 300; font-display: swap; }
@font-face { font-family: "Fira Code"; src: url("/fonts/FiraCode-Regular-latin.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Fira Code"; src: url("/fonts/FiraCode-Medium-latin.woff2") format("woff2"); font-weight: 500 900; font-display: swap; }
/* The mark's > where WebGL is missing: Geist Bold, the letter the mark is
   cut from, so it is the logo and not text. Held back until it has arrived,
   so the > is never seen in another face first. */
@font-face { font-family: "Geist Mark"; src: url("/fonts/Geist-Variable.woff2") format("woff2"); font-weight: 100 900; font-display: block; }
/* The menu's signs on a phone: five circled operators cut from STIX Two Math
   (SIL OFL 1.1, fonts/STIXTwoMath-LICENSE.txt), and nothing else of it:
   symbols, not words. Held back until it has arrived. */
@font-face { font-family: "Signs"; src: url("/fonts/STIXTwoMath-signs.otf") format("opentype"); font-display: block; }

:root {
  --width: 1200px;
  --bar: 4rem;
  --gutter: 1.5rem;
  --tabs: 3.5rem;  /* the menu's row at the foot of a phone */
  --line: rgba(255, 255, 255, 0.14);
  --dim: #8e9099;   /* the second ink: everything that is not a value */
  --ink: #fff;      /* the first ink: values, names, actions */
}
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: #000;
  color: #fff;
  font: 400 0.8125rem/1.4 "Fira Code", ui-monospace, monospace;
  font-feature-settings: "tnum" 1, "cv11" 1;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

/* The loader: over the whole window from the first paint, black, with the
   mark at its middle as the page draws it (a ring round a Geist Bold >) at
   twice the bar's size. The ring is steel at rest and one highlight travels
   round it; it never glows. It comes up a moment late, so a page that is
   ready at once shows no more of it than black. When the page is fit to be
   seen (html.loaded, loader.js) it lifts; should that script never arrive it
   lifts on its own, later. */
.loader {
  position: fixed; inset: 0; z-index: 30; display: grid; place-items: center; background: #000;
  transition: opacity 0.7s cubic-bezier(0.2, 0.7, 0.1, 1), visibility 0s linear 0.7s;
  animation: loader-out 0.7s linear 15s forwards;
}
.loaded .loader { opacity: 0; visibility: hidden; pointer-events: none; }
.loader-mark {
  position: relative; display: grid; place-items: center; width: 3.5rem; height: 3.5rem; border-radius: 50%;
  box-shadow: inset 0 0 0 3px #3a3b41;
  animation: loader-in 0.6s cubic-bezier(0.2, 0.7, 0.1, 1) 0.25s both;
}
.loader-mark span { font: 700 3.1rem/1 "Geist Mark", sans-serif; transform: translate(0.05em, -0.04em); }
/* The highlight: a sweep of light that is the ring's width and no more, cut
   to the ring, turning. */
.loader-mark::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(rgba(255, 255, 255, 0) 0 50%, rgba(255, 255, 255, 0.35) 78%, #fff 97%, rgba(255, 255, 255, 0) 100%);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 3.5px), #000 calc(100% - 3px));
  mask: radial-gradient(closest-side, transparent calc(100% - 3.5px), #000 calc(100% - 3px));
  animation: loader-turn 1.6s linear infinite;
}
@keyframes loader-in { from { opacity: 0; } }
@keyframes loader-turn { to { transform: rotate(1turn); } }
@keyframes loader-out { to { opacity: 0; visibility: hidden; } }
@media (prefers-reduced-motion: reduce) {
  .loader { transition: none; }
  .loader-mark, .loader-mark::before { animation: none; }
}

/* One fixed column: the bar and the stage share its width. It is pinned to
   the window, so nothing inside it that runs past the foot can make the
   document itself scroll (a focus or a click would scroll it, even with
   overflow hidden, and take the menu up under the browser's own bar). */
.site {
  position: fixed;
  inset: 0;
  max-width: var(--width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}
.bar {
  /* The menu floats over the piano: no panel, no ring. The name on the left,
     the menu in the centre, the wallet on the right, as Solana apps place it. */
  position: absolute; z-index: 4; top: 0; left: 0; right: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 0.5rem 1.5rem;
  padding: 1.1rem max(var(--gutter), env(safe-area-inset-right)) 0 max(var(--gutter), env(safe-area-inset-left));
  pointer-events: none;
}
.bar > * { pointer-events: auto; }
/* The mark, and beside it the path of the page, in chrome type. */
.bar-start { justify-self: start; display: flex; align-items: center; gap: 0.875rem; min-width: 0; }
.bar-path { margin: 0; font: 500 1.0625rem/1.3 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; white-space: nowrap; }
.bar-path[hidden] { display: none; }
/* A soft fall of black from the top edge, so the floating menu reads over
   any part of the piano. */
/* Behind the menu it is black, and the piano fades up into it. No blur. */
.bar::before {
  content: ""; position: absolute; z-index: -1; inset: 0 0 -6rem; pointer-events: none;
  background: #000;
  -webkit-mask: linear-gradient(#000 38%, rgba(0, 0, 0, 0.82) 52%, rgba(0, 0, 0, 0.45) 70%, rgba(0, 0, 0, 0.15) 86%, transparent);
  mask: linear-gradient(#000 38%, rgba(0, 0, 0, 0.82) 52%, rgba(0, 0, 0, 0.45) 70%, rgba(0, 0, 0, 0.15) 86%, transparent);
}
.bar .mark { justify-self: start; }
.bar > nav { justify-self: center; }
.bar-end { position: relative; justify-self: end; display: flex; align-items: center; }
/* The wallet: the same Solana chrome as BUY A KEY, at the menu's size; its
   address, with a green dot, once connected. */
/* It is as tall as the mark's ring beside it: the ring is 32px of the mark's
   36px, and a capsule's chrome stands 1.5px inside its box. */
.bar .bar-end .wallet { height: 2.1875rem; padding: 0 1.15rem; gap: 0; white-space: pre; font-size: 0.6875rem; }
.ring-ready .bar .bar-end .wallet::before, .ring-ready .bar .bar-end .wallet::after { border-width: 1.09375rem; }
.bar .bar-end .wallet.on { cursor: default; }
/* MEMBER ACCESS, and on a phone ACCESS. */
.bar .wallet .short { display: none; }
.bar .bar-end .wallet.on > i { width: 6px; height: 6px; margin-right: 0.5rem; border-radius: 50%; background: #14f195; }
/* Where to get a wallet, when the browser has none. */
.wallet-help {
  position: absolute; z-index: 5; top: calc(100% + 0.6rem); right: 0; width: 17rem;
  padding: 1rem 1.1rem; border-radius: 14px; border: 1px solid rgba(255, 255, 255, 0.12);
  background: linear-gradient(180deg, #121318, #08080a); box-shadow: 0 1.5rem 3rem -1rem #000;
  font: 400 0.75rem/1.45 "Fira Code", ui-monospace, monospace; letter-spacing: 0; color: var(--dim);
}
.wallet-help[hidden] { display: none; }
.wallet-help p { margin: 0 0 0.75rem; }
.wallet-help nav { display: grid; gap: 0.25rem; }
.wallet-help a { display: flex; justify-content: space-between; padding: 0.55rem 0.7rem; border-radius: 8px; background: rgba(255, 255, 255, 0.05); color: #fff; font-weight: 500; text-decoration: none; }
.wallet-help a small { color: var(--dim); }
.wallet-help a:hover, .wallet-help a:focus-visible { background: rgba(255, 255, 255, 0.1); outline: none; }
.bar a { color: inherit; text-decoration: none; }
/* The mark: a big Geist Bold > filling a ring the page draws, so it is the
   same everywhere and every part of it is ours or OFL. */
.bar .mark { display: grid; place-items: center; width: 1.75rem; height: 1.75rem; box-sizing: border-box; border: 1.5px solid currentColor; border-radius: 50%; overflow: hidden; }
.bar .mark span { font: 700 1.55rem/1 "Geist Mark", sans-serif; transform: translate(0.05em, -0.04em); transition: transform 0.9s cubic-bezier(0.65, 0, 0.35, 1); }
html[data-back] .bar .mark span { transform: translate(-0.05em, -0.04em) rotateY(180deg); }
/* Live, the mark is the turning chrome of mark.js, a little larger than the drawn ring. */
.bar .mark.live { width: 2.25rem; height: 2.25rem; border: 0; overflow: visible; }
.bar .mark.live span { display: none; }
.bar .mark canvas { width: 100%; height: 100%; display: block; }
.bar > nav { display: flex; align-items: center; gap: 2.25rem; font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.12em; }
.bar > nav a { position: relative; padding: 0.5rem 0; color: var(--dim); transition: color 0.2s; }
/* The names' signs are for the phone's menu alone, and so are MEMBERS (the
   members' room) and MEMBERSHIP's short name, KEY. */
.bar > nav .sign, .bar > nav a[href="/members"], .bar > nav .short { display: none; }
.bar > nav a:hover { color: #fff; }
/* The page you are on: white, with a fine line under it. */
.bar > nav a[aria-current] { color: #fff; }
.bar > nav a[aria-current]::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0.1rem; height: 1px; background: #fff; }
.bar a:focus-visible { outline: 1px solid #fff; outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .bar > nav a { transition: none; } }

.stage { flex: 1; min-height: 0; position: relative; }
#piano {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

/* The campaign panel sits under the stage, in the same column. */
.panel {
  flex: none;
  display: grid;
  gap: 0.75rem;
  padding: 1rem max(var(--gutter), env(safe-area-inset-right)) 1.25rem max(var(--gutter), env(safe-area-inset-left));
  border-top: 1px solid var(--line);
  letter-spacing: 0.06em;
}
.panel[hidden] { display: none; }
/* Liquid chrome, as on scent.tech and rainyseason.cr: bright and dark bands
   swept round an edge like a polished tube catching studio light, with a
   fainter inner line for the tube's far wall. The light drifts slowly; a hover
   sends one glint round. */
@property --turn { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@property --sweep { syntax: "<angle>"; inherits: true; initial-value: 0deg; }
@property --glint { syntax: "<number>"; inherits: true; initial-value: 0; }
@keyframes drift { to { --turn: 360deg; } }
@keyframes glint { 0% { --sweep: 0deg; --glint: 0; } 15% { --glint: 1; } 80% { --glint: 1; } 100% { --sweep: 360deg; --glint: 0; } }
:root {
  /* Dark metal that catches Solana's colours: gunmetal bands, a violet and a
     green highlight where polished chrome would flash white, one steel glint. */
  --chrome-stops: #2b2c31, #9945ff 7%, #4a4b53 15%, #0c0c0e 25%, #5d5f68 35%, #14f195 41%, #3a3b42 50%, #0e0e10 60%, #7d6bff 70%, #33343a 80%, #d5d8de 90%, #2b2c31;
  --chrome-inner-stops: #3a3b42, #14f195 28%, #16161a 55%, #9945ff 82%, #2b2c31;
  /* Inside a chrome ring, a capsule or a panel: nothing. The ring is the
     object, and what is behind it shows through. */
  --metal: transparent;
  --quiet: #8e9099;
  --ease: cubic-bezier(0.2, 0.7, 0.1, 1);
}
.chrome { position: relative; isolation: isolate; animation: drift 12s linear infinite; }
.chrome::before, .chrome::after {
  content: ""; position: absolute; border-radius: inherit; pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  transition: filter 0.4s var(--ease), opacity 0.3s;
}
.chrome::before {
  inset: 0; padding: 2px;
  background:
    conic-gradient(from calc(var(--sweep, 0deg) - 90deg), rgb(255 255 255 / var(--glint, 0)), transparent 5% 95%, rgb(255 255 255 / var(--glint, 0))),
    conic-gradient(from calc(200deg + var(--turn, 0deg)), var(--chrome-stops));
}
.chrome::after { inset: 3.5px; padding: 1px; opacity: 0.45; background: linear-gradient(calc(180deg - var(--turn, 0deg)), var(--chrome-inner-stops)); }
button.chrome:hover:not(:disabled), button.chrome:focus-visible { animation: drift 12s linear infinite, glint 0.7s var(--ease); outline: none; }
button.chrome:hover:not(:disabled)::before, button.chrome:focus-visible::before { filter: brightness(1.3); }
button.chrome:disabled { color: var(--quiet); cursor: default; animation-play-state: paused; }
button.chrome:disabled::before, button.chrome:disabled::after { opacity: 0.35; }
/* Once the rings are baked (chrome-rings.js), the drawn chrome gives way to
   pictures of real polished metal, stretched round each bezel. */
.ring-ready .chrome { animation: none; }
.ring-ready .chrome::before {
  background: none; -webkit-mask: none; mask: none; padding: 0; opacity: 1;
  border-style: solid; border-width: 18px; border-image: var(--ring-frame) var(--ring-frame-slice) stretch;
}
.ring-ready .chrome::after { display: none; }
.ring-ready .gauge-tube::before, .ring-ready button.chrome::before { border-width: 1.375rem; border-image: var(--ring-pill) 50% 22.32% stretch; }
/* The primary buttons (BUY A KEY, MEMBER SIGN-IN, the booking button when it
   can be pressed) wear the mark's chrome, lit. The rest are secondary:
   quieter silver, dimmer type. */
.ring-ready button.primary { --ring-primary: var(--ring-pill); }
.ring-ready button.primary::before { border-image-source: var(--ring-primary); filter: saturate(1.35) brightness(1.15); }
.ring-ready button.primary:hover:not(:disabled)::before, .ring-ready button.primary:focus-visible::before { filter: saturate(1.35) brightness(1.45); }
/* The light moves round it: a brighter copy of the same ring, shown only
   through two soft bands that turn with --turn, so the highlight travels over
   the metal. */
.ring-ready button.primary { animation: drift 6s linear infinite; }
.ring-ready button.primary::after {
  display: block; inset: 0; padding: 0; opacity: 1; background: none;
  border-style: solid; border-width: 1.375rem; border-image: var(--ring-primary) 50% 22.32% stretch;
  filter: saturate(1.2) brightness(2.2);
  -webkit-mask: conic-gradient(from var(--turn), #000, transparent 12% 50%, #000 62%, transparent 74% 100%);
  mask: conic-gradient(from var(--turn), #000, transparent 12% 50%, #000 62%, transparent 74% 100%);
}
.ring-ready button.primary:disabled { animation: none; }
.ring-ready button.primary:disabled::after { display: none; }
.ring-ready button.chrome:hover:not(:disabled)::before, .ring-ready button.chrome:focus-visible::before { filter: brightness(1.3); }
.ring-ready button.chrome:disabled::before { opacity: 0.4; }

/* The raise, read from the chain: a gauge, BUY A KEY, and four
   figures, kept low so the piano has the height. */
.panel {
  position: relative; background: #000; gap: 0.6rem; padding-top: 1rem; padding-bottom: calc(1rem + var(--foot, 2.3rem));
  clip-path: inset(-100vh 0 var(--foot, 2.3rem) 0);
  font-weight: 300; letter-spacing: 0.02em;
}
.deck { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.6rem; }

/* The gauge: one fine line in a chrome pill. White as far as the raise has
   come, a tick where the goal sits. */
.gauge-tube { height: 2.75rem; padding: 0 1.35rem; display: flex; align-items: center; gap: 1.25rem; border-radius: 999px; background: var(--metal); }
.gauge-tube::before { opacity: 0.9; }
.gauge-tube::after { opacity: 0.3; }
.gauge-line { position: relative; flex: 1; height: 1px; background: #333; }
.gauge-line div { position: absolute; left: 0; top: -1px; height: 3px; width: 0; border-radius: 2px; background: linear-gradient(90deg, #9945ff, #5497d5, #14f195); transition: width 0.9s var(--ease); }
.gauge-line .mark { position: absolute; left: calc(var(--goal, 0.7) * 100%); top: -0.4rem; width: 1px; height: calc(0.8rem + 1px); background: var(--quiet); }
.gauge-tube output { flex: none; display: flex; align-items: baseline; gap: 0.35em; font: 600 0.9375rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; color: #fff; white-space: nowrap; }
.gauge-tube output small { font-weight: 400; font-size: 0.8125rem; letter-spacing: 0; color: var(--dim); }
@media (max-width: 600px) { .gauge-tube output .wide { display: none; } .gauge-tube output small { font-size: 0.6875rem; letter-spacing: 0.04em; } }

/* The buttons: chrome pills, as on the sign-up screens. */
.pack-button, .wallet {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.75rem;
  height: 2.75rem; padding: 0 1.45rem; border: 0; border-radius: 999px; background: var(--metal); color: #fff;
  font: 600 0.8125rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.06em; cursor: pointer; white-space: nowrap;
}
/* BUY A KEY, the one thing to do here: larger than the rest. */
.deck .pack-button { height: 2.75rem; padding: 0 2.6rem; font-size: 0.875rem; letter-spacing: 0.08em; }
/* On a desktop it is wide, a clear target beside the gauge. */
@media (min-width: 901px) { .deck .pack-button { min-width: 19rem; } }
.wallet { padding: 0 1.4rem; }
/* The view toggle: a piece of hardware, after cleavs.com's night switch.
   It floats on the piano, bottom right of the stage, in line with the menu
   bar's right edge.
   - The track is gunmetal cut into the page (debossed): near-black steel with
     a fine grain, darker under its top lip, a catch-light on its bottom lip.
   - Round it, the baked chrome capsule the secondary buttons wear, dimmed.
   - The knob is spun dark steel (the conic sweep is the turning marks of the
     lathe), domed by a soft top light, set in a baked chrome rim that sits
     wholly inside the bezel: the knob is never bigger than the track.
   - Its number is engraved into the steel and lit from inside, pale steel at
     rest and white under the pointer, sharp edged with no glow on the face.
   - It slides between three stops, 1 the room, 2 the player's angle, 3 the
     keys, rolling one full turn as it crosses; the numbers crossfade.
   - The stops it is not on are small pits drilled in the track. */
.view-toggle {
  --knob: 16px; --pad: 4px; --ease-knob: cubic-bezier(0.65, 0, 0.35, 1);
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/></filter><rect width='80' height='80' filter='url(%23g)'/></svg>");
  position: absolute; z-index: 2; right: max(var(--gutter), env(safe-area-inset-right)); bottom: 1.1rem;
  width: calc(var(--knob) * 3 + var(--pad) * 2); height: calc(var(--knob) + var(--pad) * 2);
  padding: 0; border: 0; border-radius: 999px; cursor: pointer;
  background: var(--grain), linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%);
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.85), inset 0 1px 1px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07);
  -webkit-tap-highlight-color: transparent;
}
/* The bezel. */
.view-toggle::before { content: ""; position: absolute; z-index: 2; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: inset 0 0 0 1px #3a3b41; transition: filter 0.3s; }
.ring-ready .view-toggle::before { box-shadow: none; border: calc((var(--knob) + var(--pad) * 2) / 2) solid transparent; border-image: var(--ring-pill) 50% 22.32% / calc((var(--knob) + var(--pad) * 2) / 2) stretch; filter: brightness(0.7); }
.ring-ready .view-toggle:hover::before, .ring-ready .view-toggle:focus-visible::before { filter: brightness(0.95); }
/* The pits: a dark hole with the light catching its lower edge. */
.view-toggle::after {
  content: ""; position: absolute; inset: var(--pad); pointer-events: none;
  background:
    radial-gradient(circle at 50% 50%, #000 0.9px, transparent 1.4px) 0 0 / var(--knob) var(--knob) repeat-x,
    radial-gradient(circle at 50% calc(50% + 0.6px), rgba(255, 255, 255, 0.16) 1px, transparent 1.5px) 0 0 / var(--knob) var(--knob) repeat-x;
}
.view-toggle .knob { position: absolute; z-index: 1; top: var(--pad); left: var(--pad); width: var(--knob); height: var(--knob); border-radius: 50%; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.8); transition: transform 0.7s var(--ease-knob); }
.view-toggle[data-state="stage"] .knob { transform: translateX(var(--knob)); }
.view-toggle[data-state="keys"] .knob { transform: translateX(calc(var(--knob) * 2)); }
.view-toggle .face {
  position: absolute; inset: 1px; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 28%, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0) 62%),
    conic-gradient(from 20deg, #5d5f63, #2c2d30, #4e5054, #232426, #5d5f63, #35363a, #56585c, #28292c, #5d5f63);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.25), inset 0 -0.5px 0 rgba(0, 0, 0, 0.6);
  transition: transform 0.7s var(--ease-knob);
}
.view-toggle[data-state="stage"] .face { transform: rotate(360deg); }
.view-toggle[data-state="keys"] .face { transform: rotate(720deg); }
.view-toggle .knob::after { content: ""; position: absolute; inset: 0; border-radius: 50%; box-shadow: inset 0 0 0 1px #8e9099; pointer-events: none; }
.ring-ready .view-toggle .knob::after { box-shadow: none; background: var(--ring-dot) center / 100% 100% no-repeat; }
/* The engraving: the groove's upper wall in shadow, its lower lip catching
   the light, the cut filled with light from behind. */
.view-toggle .face b {
  position: absolute; inset: 0; display: grid; place-items: center; padding-top: 0.5px;
  font: 700 8.5px/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums;
  color: #c9ccd3; opacity: 0;
  text-shadow: 0 -0.5px 0 rgba(0, 0, 0, 0.9), 0 0.5px 0 rgba(255, 255, 255, 0.22);
  transition: opacity 0.18s ease-out, color 0.25s;
}
.view-toggle:hover .face b, .view-toggle:focus-visible .face b { color: #fff; }
/* The old number goes as the knob starts to roll, and the new one appears as
   it settles, so two are never seen crossed over each other. */
.view-toggle[data-state="room"] .i-room, .view-toggle[data-state="stage"] .i-stage, .view-toggle[data-state="keys"] .i-keys { opacity: 1; transition: opacity 0.3s ease-in 0.42s, color 0.25s; }
.view-toggle:focus-visible { outline: 1px solid #fff; outline-offset: 5px; }
/* An unseen 44px-tall hit area, so the small toggle is an easy tap. */
.view-toggle .hit { position: absolute; inset: -10px -6px; }
@media (hover: none) { .view-toggle:hover .face b { color: #c9ccd3; } .ring-ready .view-toggle:hover::before { filter: brightness(0.7); } }
@media (prefers-reduced-motion: reduce) { .view-toggle .knob, .view-toggle .face, .view-toggle .face b { transition: none; } }
.wallet.on { cursor: default; color: var(--quiet); }
.wallet[hidden] { display: none; }

/* The cards: each edged in the same chrome, quieter, its light at a different point. */
/* The figures: one slim chrome strip, five readouts split by hairlines. */
/* The figures: one display face behind a chrome bezel, like a hardware
   readout, clear inside the bezel and split by hairlines. */
.chain { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 0.8fr) minmax(0, 1.2fr); border-radius: 18px; background: var(--metal); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1rem 2rem -1rem #000; }
.chain::before { opacity: 0.9; }
.chain::after { opacity: 0.3; }
.stat { display: grid; align-content: start; justify-items: start; gap: 0.3rem; min-width: 0; padding: 0.7rem 1.25rem 0.75rem; text-align: left; }
.stat + .stat { border-left: 1px solid rgba(255, 255, 255, 0.06); }
.stat > span { justify-self: stretch; display: flex; align-items: center; gap: 0.6em; margin-bottom: 0.05rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; white-space: nowrap; }
button.holders > span { justify-content: space-between; }
/* Figures as on Solana's finance apps: a clean sans with even digits, the
   value bright, its unit muted beside it, a muted line of context below. */
.stat b { display: inline-flex; align-items: baseline; max-width: 100%; color: #fff; font: 550 1.25rem/1.1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; white-space: nowrap; overflow: hidden; }
.stat b small { margin: 0 0.32em 0 0.06em; color: var(--dim); font-size: 0.6em; font-weight: 500; letter-spacing: 0; }
.stat b small[data-wide] { margin-left: 0.25em; }
.stat em { max-width: 100%; overflow: hidden; color: var(--dim); font: 400 0.75rem/1.2 "Fira Code", ui-monospace, monospace; font-style: normal; font-variant-numeric: tabular-nums; letter-spacing: 0; text-overflow: ellipsis; white-space: nowrap; }
.stat em:empty { display: none; }.stat b small:last-child { margin-right: 0; }
.stat b small + small, .stat b small + small[data-wide] { margin-left: 0; }
@media (max-width: 600px) {
  /* The goal is left out on a phone, where the cell is too narrow for it
     (the gauge shows the raise): the first muted part, whichever the units. */
  .stat.raised b small:first-of-type { display: none; }
  .stat.raised b small + small[data-wide] { margin-left: 0.32em; }
}
button.holders { margin: 0; border-top: 0; border-right: 0; border-bottom: 0; border-radius: 0 18px 18px 0; background: none; color: inherit; font: inherit; cursor: pointer; transition: background 0.25s; }
button.holders:focus-visible { background: rgba(255, 255, 255, 0.035); outline: none; }
/* KEY HOLDERS and RAISED open their sheets: a round arrow sits on the right,
   ringed, and fills white as the cell is hovered or focused. */
button.holders, button.stat.raised { position: relative; padding-right: 4rem; }
.go-arrow {
  position: absolute; right: 1.1rem; top: 50%; translate: 0 -50%;
  display: grid; place-items: center; width: 2rem; height: 2rem; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.28); background: rgba(255, 255, 255, 0.05); color: #fff;
  transition: background 0.25s, color 0.25s, border-color 0.25s, transform 0.35s var(--ease);
}
.go-arrow svg { display: block; }
button.holders:focus-visible .go-arrow, button.stat.raised:focus-visible .go-arrow { background: #fff; border-color: #fff; color: #000; transform: translateX(3px); }
button.holders:focus-visible em, button.stat.raised:focus-visible em { color: #fff; }
/* No hover on touch: a tap would leave the arrow lit. */
@media (hover: hover) {
  button.holders:hover .go-arrow, button.stat.raised:hover .go-arrow { background: #fff; border-color: #fff; color: #000; transform: translateX(3px); }
  button.holders:hover em, button.stat.raised:hover em { color: #fff; }
  button.holders:hover, button.stat.raised:hover { background: rgba(255, 255, 255, 0.035); }
}
.ledger button {
  font: inherit; letter-spacing: 0.1em; font-size: 0.625rem; color: var(--quiet); cursor: pointer; padding: 0;
  border: 0; border-bottom: 1px solid #333; background: none;
}
.ledger button:hover, .ledger button:focus-visible { color: #fff; border-color: #fff; outline: none; }

/* The ledger: every key and its holder. It opens by itself when no keys are left. */
.ledger { position: absolute; left: 0; right: 0; bottom: 100%; max-height: 46vh; overflow: auto; background: rgba(0, 0, 0, 0.92); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border-top: 1px solid var(--line); padding: 0 var(--gutter) 0.75rem; }
.ledger[hidden] { display: none; }
.ledger-head { position: sticky; top: 0; display: flex; justify-content: space-between; gap: 1rem; padding: 0.75rem 0; background: #000; color: #fff; }
.ledger ol { list-style: none; margin: 0; padding: 0; font-size: 0.75rem; letter-spacing: 0.04em; }
.ledger li { display: grid; grid-template-columns: 11rem 3.5rem minmax(0, 1fr); gap: 1rem; padding: 0.3rem 0; border-top: 1px solid var(--line); }
.ledger li span { color: var(--dim); }
.ledger li a { color: #fff; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ledger li a:hover { text-decoration: underline; }
.ledger li.mine a { text-decoration: underline; text-underline-offset: 4px; }
@media (max-width: 720px) {

  .ledger li { grid-template-columns: 6.5rem 2.5rem minmax(0, 1fr); }
}

/* The key card: a graded slab, revealed like a card pulled from a pack. */
.rip { position: fixed; inset: 0; z-index: 10; display: grid; place-items: center; }
.rip[hidden] { display: none; }
.rip-shade { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.82); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); animation: rip-fade 0.3s both; }
.rip-stage { position: relative; display: grid; justify-items: center; gap: 1rem; padding: 3rem 1rem 1rem; perspective: 1400px; }
/* Close: a round button with an X, made like the arrows on KEY HOLDERS and
   RAISED, white under the pointer. Its word, CLOSE, stays its name for
   screen readers; the X is drawn from a mask in the button's own colour. A
   36px ring with a 44px hit area round it. */
.rip-close {
  position: absolute; top: 0.5rem; right: 1rem; z-index: 1;
  width: 2.25rem; height: 2.25rem; padding: 0; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.28); background: rgba(255, 255, 255, 0.05); color: #fff;
  font-size: 0; cursor: pointer;
  transition: background 0.25s, border-color 0.25s, color 0.25s;
}
.rip-close::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; translate: -50% -50%;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M1.5 1.5l9 9M10.5 1.5l-9 9' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>") center / 100% 100% no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M1.5 1.5l9 9M10.5 1.5l-9 9' stroke='black' stroke-width='1.6' stroke-linecap='round'/></svg>") center / 100% 100% no-repeat;
}
.rip-close::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
.rip-close:focus-visible { background: #fff; border-color: #fff; color: #000; outline: none; }
@media (hover: hover) { .rip-close:hover { background: #fff; border-color: #fff; color: #000; } }
/* Once baked (mark.js), the close is the mark itself with an X for its >:
   the chrome ring and the X, a little dimmed at rest, lit full under the
   pointer, the way the mark is in the bar. */
.close-ready .rip-close { border: 0; background: var(--mark-close) center / 100% 100% no-repeat; filter: brightness(0.8); transition: filter 0.25s var(--ease); }
.close-ready .rip-close::before { display: none; }
.close-ready .rip-close:focus-visible { background: var(--mark-close) center / 100% 100% no-repeat; filter: brightness(1.15); outline: 1px solid #fff; outline-offset: 3px; }
@media (hover: hover) { .close-ready .rip-close:hover { background: var(--mark-close) center / 100% 100% no-repeat; filter: brightness(1.15); } }
@media (prefers-reduced-motion: reduce) { .close-ready .rip-close { transition: none; } }
.rip-tilt {
  --rx: 0deg; --ry: 0deg; --mx: 50%; --my: 50%;
  height: min(72vh, 42rem);
  aspect-ratio: 80.25 / 135.75;
  transform: rotateX(var(--rx)) rotateY(var(--ry));
  transform-style: preserve-3d;
  transition: transform 0.12s ease-out;
}
.slab { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; }
.is-open .slab { animation: rip-turn 0.95s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.slab-front, .slab-back {
  position: absolute; inset: 0;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  border-radius: 0.6rem;
  border: 1px solid rgba(255, 255, 255, 0.38);
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.04) 40%, rgba(255, 255, 255, 0.1));
  box-shadow: 0 2rem 4rem rgba(0, 0, 0, 0.7), inset 0 0 0 0.3rem rgba(255, 255, 255, 0.05), inset 0 0 1.2rem rgba(255, 255, 255, 0.12);
}
.slab-back { transform: rotateY(180deg); display: grid; place-items: center; background-color: #050505; letter-spacing: 0.1em; color: var(--dim); }
.slab-front { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 0.55rem; padding: 0.6rem; overflow: hidden; }

/* The grade label: thick red frame, white face, barcode, uniform type. */
.grade { padding: 4px; background: #8b0012; }
.grade-face {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 2px 0.75rem;
  padding: 6px 8px;
  color: #000;
  border: 1px solid #efefef;
  background: repeating-linear-gradient(180deg, transparent 0 2px, rgba(4, 106, 239, 0.12) 2px 3px), #efefef;
}
.grade-face p { margin: 0; font-weight: 500; font-size: 0.75rem; line-height: 1.2; letter-spacing: 0; white-space: nowrap; font-variant-numeric: tabular-nums; }
.grade-face .r { text-align: right; }
.barcode { display: flex; align-items: stretch; gap: 1px; height: 1rem; }
.barcode span { display: block; background: #000; }

.well { min-height: 0; display: grid; place-items: center; padding: 0.45rem; background: #000; border-radius: 0.3rem; box-shadow: inset 0 0 0.6rem rgba(255, 255, 255, 0.08); }
.keycard {
  position: relative;
  height: 100%;
  max-width: 100%;
  aspect-ratio: 63.5 / 88.9;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 0.7rem 0.75rem 0.75rem;
  border-radius: 0.5rem;
  border: 1px solid rgba(212, 173, 85, 0.55);
  background: radial-gradient(120% 80% at 50% 38%, #26221a 0, #0d0c0a 62%, #050505 100%);
  overflow: hidden;
}
.keycard-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.keycard-head span:first-child { font-size: 2rem; font-weight: 500; line-height: 1; }
.keycard-head span:last-child { color: var(--dim); }
.keycard canvas { width: 100%; height: 100%; min-height: 0; display: block; }
.keycard dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 1rem; font-size: 0.6875rem; letter-spacing: 0.06em; }
.keycard dt { color: var(--dim); }
.keycard dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.foil {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 24%, rgba(255, 110, 190, 0.5) 38%, rgba(110, 220, 255, 0.5) 48%, rgba(255, 236, 140, 0.5) 58%, transparent 72%);
  background-size: 260% 260%;
  background-position: var(--mx) var(--my);
  mix-blend-mode: color-dodge;
  opacity: 0.22;
}
.glare {
  position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: radial-gradient(60% 45% at var(--mx) var(--my), rgba(255, 255, 255, 0.2), transparent 70%);
}
.rip-act { display: grid; justify-items: center; gap: 0.5rem; min-height: 4.5rem; }
.rip-act[hidden] { display: none; }
.rip-act p { margin: 0; min-height: 1.2em; color: var(--dim); letter-spacing: 0.02em; }
.rip-act button {
  font: inherit; letter-spacing: 0.06em; color: #000; background: #fff;
  border: 1px solid #fff; border-radius: 999px; padding: 0.7rem 1.6rem; cursor: pointer;
}
.rip-act button:disabled { color: var(--dim); background: transparent; border-color: var(--line); cursor: default; }
.rip-act button:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.rip-flash { position: absolute; inset: 0; pointer-events: none; opacity: 0; background: radial-gradient(circle at 50% 45%, #fff 0, rgba(255, 236, 170, 0.7) 22%, transparent 60%); }
.is-open .rip-flash { animation: rip-flash 0.95s ease-out both; }
.is-open .rip-act, .is-open .rip-close { animation: rip-fade 0.4s 0.8s both; }

@keyframes rip-fade { from { opacity: 0; } }
@keyframes rip-turn {
  0% { transform: translateY(60vh) rotateY(180deg) rotateZ(-8deg) scale(0.6); }
  45% { transform: translateY(-2vh) rotateY(180deg) rotateZ(2deg) scale(1.04); }
  78% { transform: translateY(0) rotateY(-10deg) scale(1.03); }
  100% { transform: none; }
}
@keyframes rip-flash { 0%, 48% { opacity: 0; } 62% { opacity: 0.85; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .is-open .slab, .is-open .rip-flash { animation: none; }
  .rip-tilt { transition: none; transform: none; }
}

/* The pack. */
.pack { position: fixed; inset: 0; z-index: 10; display: grid; place-items: center; }
.pack[hidden] { display: none; }
.pack-shade { position: absolute; inset: 0; background: radial-gradient(60% 55% at 50% 45%, rgba(60, 30, 120, 0.5), rgba(0, 0, 0, 0.9) 70%); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); animation: rip-fade 0.3s both; }
.pack-stage { position: relative; display: grid; justify-items: center; gap: 0.75rem; width: min(100vw, 40rem); padding: 2.5rem 1rem 1rem; }
.pack-stage canvas { width: 100%; height: min(62vh, 36rem); display: block; cursor: grab; animation: pack-in 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.pack-deck { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 1.5rem; width: 100%; }
.pack-read { display: grid; gap: 0.15rem; letter-spacing: 0.1em; }
.pack-read:last-child { text-align: right; }
.pack-read span { color: var(--dim); font-size: 0.625rem; }
.pack-read b { font-weight: 500; font-size: 1.2rem; color: #eafff6; text-shadow: 0 0 0.7rem rgba(20, 241, 149, 0.5); font-variant-numeric: tabular-nums; }
.pack-go {
  font: inherit; font-weight: 500; letter-spacing: 0.1em; color: #000; cursor: pointer;
  padding: 0.85rem 1.8rem; border: 0; border-radius: 0.6rem;
  background: linear-gradient(90deg, #b98bff, #7fe9c4 60%, #14f195);
  box-shadow: 0 0 2rem rgba(153, 69, 255, 0.5), 0 0 2rem rgba(20, 241, 149, 0.35);
  transition: transform 0.15s, box-shadow 0.15s;
}
.pack-go:hover:not(:disabled) { transform: translateY(-1px) scale(1.02); }
.pack-go:disabled { color: var(--dim); background: rgba(255, 255, 255, 0.06); box-shadow: none; cursor: default; }
.pack-go:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.pack-note { margin: 0; min-height: 1.2em; color: var(--dim); letter-spacing: 0.02em; }
.pack.is-ripping .pack-deck, .pack.is-ripping .pack-note, .pack.is-ripping .rip-close { opacity: 0; transition: opacity 0.3s; }
.pack.is-ripping .rip-flash { animation: pack-flash 1.6s ease-out both; }
@keyframes pack-in { from { opacity: 0; transform: translateY(8vh) scale(0.85); } }
@keyframes pack-flash { 0%, 40% { opacity: 0; } 85% { opacity: 0.95; } 100% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .pack-stage canvas { animation: none; } }

/* Sheets: the story and the booking, over the stage. */
.sheet { position: fixed; inset: 0; z-index: 9; display: grid; place-items: center; padding: 1rem; }
.sheet[hidden] { display: none; }
.sheet-shade { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.84); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
.sheet-body {
  position: relative; width: min(100%, 46rem); max-height: calc(100vh - 2rem); overflow: auto;
  padding: 2.75rem 1.75rem 1.75rem; border-radius: 18px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: linear-gradient(180deg, #0c0d12, #050507);
  box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* In a sheet the X sits on the title's line, its right edge on the text's. */
.sheet-body .rip-close { top: calc(2.2rem + 2px); right: 1.75rem; }
/* Baked, each sheet sits in the same polished frame as the figures strip:
   the frame is drawn 18px deep over the sheet's own 1px edge, so it stays put
   while the sheet scrolls beneath it. */
.ring-ready .sheet-body { border-color: transparent; border-image: var(--ring-frame) var(--ring-frame-slice) / 18px stretch; box-shadow: 0 2rem 5rem rgba(0, 0, 0, 0.8); }
.sheet h2 { margin: 0 0 1rem; font-size: 1rem; font-weight: 500; letter-spacing: 0.1em; }
.sheet h3 { margin: 1.25rem 0 0.5rem; font-size: 0.625rem; font-weight: 400; letter-spacing: 0.1em; color: var(--dim); }
.sheet .dim { color: var(--dim); margin: 0.25rem 0; letter-spacing: 0.02em; }

.story { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }
.story li { display: grid; grid-template-columns: 3rem minmax(0, 1fr); gap: 0.25rem 1rem; }
.story span { grid-row: 1 / 3; color: #14f195; font-size: 1.25rem; font-weight: 500; text-shadow: 0 0 0.7rem rgba(20, 241, 149, 0.5); }
.story h2 { margin: 0; }
.story p { margin: 0; font-size: 1rem; line-height: 1.5; letter-spacing: 0; color: rgba(255, 255, 255, 0.82); }

.book-row { display: grid; grid-template-columns: 5rem minmax(0, 1fr) auto auto; align-items: center; gap: 1rem; padding: 0.5rem 0; border-top: 1px solid var(--line); letter-spacing: 0.06em; }
.book-row b { font-weight: 500; }
.book-days, .book-grid { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.sheet button:not(.rip-close):not(.pack-go):not(.k):not(.kn):not([role="tab"]), .sheet select, .sheet input {
  font: inherit; letter-spacing: 0.08em; color: #fff; padding: 0.5rem 0.75rem; border-radius: 0.4rem;
  border: 1px solid rgba(255, 255, 255, 0.16); background: rgba(255, 255, 255, 0.04);
}
.sheet button:not(.rip-close):not(.pack-go), .board .k, .keylist .kn { cursor: pointer; }
.sheet button.on { border-color: #14f195; box-shadow: 0 0 0.8rem rgba(20, 241, 149, 0.45); color: #eafff6; }
.sheet button:disabled:not(.pack-go) { cursor: default; color: var(--dim); }
.book-grid button { min-width: 5.5rem; display: grid; gap: 0.1rem; justify-items: center; }
.book-grid button i { font-style: normal; font-size: 0.5625rem; letter-spacing: 0.06em; min-height: 0.8em; }
.book-grid .taken, .book-grid .blocked { opacity: 0.45; }
.book-grid .yours { border-color: #9945ff; color: #d9c2ff; }
.book-act { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 0.6rem 1rem; margin-top: 1.5rem; }
.book-act label { font-size: 0.625rem; letter-spacing: 0.1em; color: var(--dim); }
.book-act .pack-go, .book-act > p { grid-column: 1 / -1; justify-self: start; }
.sheet select option { background: #0c0d12; }
.sheet input::placeholder { color: rgba(255, 255, 255, 0.3); }
.sheet :focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.book-note { min-height: 1.3em; }

/* The other buttons wear the same chrome. */
.pack-go, .rip-act button {
  position: relative; isolation: isolate; animation: drift 12s linear infinite;
  height: 3.25rem; padding: 0 2.2rem; border: 0; border-radius: 999px; background: #000; color: #fff; box-shadow: none;
  font: 600 0.875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.06em; cursor: pointer;
}
.pack-go::before, .pack-go::after, .rip-act button::before, .rip-act button::after {
  content: ""; position: absolute; border-radius: inherit; pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.pack-go::before, .rip-act button::before {
  inset: 0; padding: 1.5px;
  background:
    conic-gradient(from calc(var(--sweep, 0deg) - 90deg), rgb(255 255 255 / var(--glint, 0)), transparent 5% 95%, rgb(255 255 255 / var(--glint, 0))),
    conic-gradient(from calc(200deg + var(--turn, 0deg)), var(--chrome-stops));
}
.pack-go::after, .rip-act button::after { inset: 2.5px; padding: 1px; opacity: 0.6; background: linear-gradient(calc(180deg - var(--turn, 0deg)), var(--chrome-inner-stops)); }
.pack-go:hover:not(:disabled), .rip-act button:hover:not(:disabled) { animation: drift 12s linear infinite, glint 0.7s var(--ease); transform: none; }
.pack-go:disabled, .rip-act button:disabled { color: var(--quiet); background: #000; cursor: default; animation-play-state: paused; }
.pack-go:disabled::before, .pack-go:disabled::after, .rip-act button:disabled::before, .rip-act button:disabled::after { opacity: 0.35; }
.pack-read b { text-shadow: none; font-weight: 300; color: #fff; }
@media (max-width: 900px) {
  .chain { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat:nth-child(n + 3) { border-top: 1px solid rgba(255, 255, 255, 0.08); }
  .stat:nth-child(odd) { border-left: 0; }
  button.holders { border-radius: 0 0 18px 0; }
}
@media (prefers-reduced-motion: reduce) { .chrome, .pack-go, .rip-act button { animation: none; } }

/* The foot: the year, the name, and what it is built on. It floats at the
   bottom of the column with nothing of its own behind it, no strip and no
   line: on a page it lies on the velvet. The panel keeps room for it at its
   foot (--foot, its height, measured by membership.js) and is clipped at its
   top edge, so the figures sinking as they fade pass under it, never across it. */
.foot {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.65rem max(var(--gutter), env(safe-area-inset-right)) max(0.65rem, env(safe-area-inset-bottom)) max(var(--gutter), env(safe-area-inset-left));
  /* The footer is set in Fira Code, as everything is. */
  font: 300 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; color: var(--quiet);
}
.foot .built { font-family: "Fira Code", ui-monospace, monospace; display: inline-flex; align-items: center; gap: 0.5rem; color: var(--ink); font-weight: 600; letter-spacing: 0.1em; font-size: 0.5625rem; }
/* BUILT ON SOLANA is a button: a 44px tall target round the words and the
   mark, the card opening above it. */
.onchain { position: relative; display: flex; }
.foot button.built { position: relative; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; transition: opacity 0.2s; }
.foot button.built::before { content: ""; position: absolute; inset: -0.9rem -0.6rem; }
.foot button.built:disabled { cursor: default; }
.foot button.built:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; border-radius: 2px; }
@media (hover: hover) { .foot button.built:not(:disabled):hover { opacity: 0.75; } }
.foot svg { display: block; }
.foot .built svg { width: 13px; height: 11.6px; }


/* The figures fold away under the gauge, leaving only the status bar. */
.drawer { display: grid; grid-template-rows: 1fr; transition: grid-template-rows 0.5s var(--ease), opacity 0.4s var(--ease); }
.drawer > .chain { min-height: 0; overflow: hidden; }
.folded .drawer { grid-template-rows: 0fr; opacity: 0; }
.panel.folded { gap: 0; }
.grip { position: absolute; left: 50%; top: 0; transform: translate(-50%, -50%); display: grid; place-items: center; width: 4.5rem; height: 1.5rem; padding: 0; border: 0; background: none; cursor: pointer; touch-action: none; }
.grip span { width: 2.25rem; height: 3px; border-radius: 2px; background: #6a6c75; transition: background 0.2s, width 0.3s var(--ease); }
.grip:hover span, .grip:focus-visible span { width: 3rem; background: linear-gradient(90deg, #9945ff, #14f195); }
.grip:focus-visible { outline: none; }
@media (prefers-reduced-motion: reduce) { .drawer { transition: none; } }

/* Bare: the menu, panel and footer glide away and the piano grows into the
   whole page. The menu drifts up as it fades; the figures and the footer sink
   as they fade, while the panel and footer close (their heights are set by
   membership.js, which knows how tall they are). Only the grip stays, faint
   at the foot, to bring everything back. */
.bar, .stage .view-toggle { transition: opacity 0.6s var(--ease), transform 0.8s var(--ease); }
.bare .bar { opacity: 0; transform: translateY(-1.25rem); pointer-events: none; }
.bare .stage .view-toggle { opacity: 0; transform: translateY(1rem); pointer-events: none; }
.panel, .foot { transition: height 0.8s var(--ease), padding 0.8s var(--ease), gap 0.5s var(--ease), background-color 0.6s var(--ease), border-color 0.6s var(--ease), opacity 0.5s var(--ease); }
.deck, .drawer > .chain, .foot > * { transition: opacity 0.45s var(--ease), transform 0.8s var(--ease); }
.bare .panel { padding-top: 0; padding-bottom: 0; background-color: transparent; border-top-color: transparent; }
/* Bare, the footer is gone too, and the grip floats where it stood: unclipped. */
.bare .panel { clip-path: none; }
.bare .deck, .bare .drawer > .chain, .bare .foot > * { opacity: 0; transform: translateY(1.5rem); pointer-events: none; }
.bare .foot { overflow: hidden; padding-top: 0; padding-bottom: 0; border-top-color: transparent; }
.grip { transition: top 0.8s var(--ease); }
/* The panel closes to nothing, so the grip floats just above the page's foot. */
.bare .grip { top: -1.25rem; }
.bare .grip span { background: #3a3b41; }
@media (prefers-reduced-motion: reduce) {
  .bar, .stage .view-toggle, .panel, .foot, .deck, .drawer > .chain, .foot > *, .grip { transition: none; }
}

/* The pages (pages.js): away from the piano the panel closes as it does going
   bare, its figures sinking as they fade, and the camera toggle goes with the
   piano. The menu and the footer stay. The panel is shut by html.away, which
   membership.js holds until the piano is back, so nothing of it returns
   before the piano has. */
.away .panel { padding-top: 0; padding-bottom: 0; background-color: transparent; border-top-color: transparent; }
.away .deck, .away .drawer > .chain, .away .grip { opacity: 0; transform: translateY(1.5rem); pointer-events: none; }
.away .grip { transform: translate(-50%, 1rem); }
.paged .stage .view-toggle, .away .stage .view-toggle { opacity: 0; transform: translateY(1rem); pointer-events: none; }
.grip { transition: top 0.8s var(--ease), opacity 0.45s var(--ease), transform 0.8s var(--ease); }

/* A page lies over the piano's room, under the menu, in the one column. It
   rises in at once when it is chosen, and sinks away at once when it is left:
   no waiting on the velvet between. */
.page {
  position: absolute; z-index: 2; inset: 0; overflow: auto; overscroll-behavior: contain;
  padding: calc(var(--bar) + 2.5rem) max(var(--gutter), env(safe-area-inset-right)) calc(var(--foot, 2.3rem) + 4rem) max(var(--gutter), env(safe-area-inset-left));
  /* A long page scrolls; it fades out just above the footer, which floats
     with nothing behind it, so nothing is ever read through it. */
  -webkit-mask-image: linear-gradient(#000 calc(100% - var(--foot, 2.3rem) - 2.75rem), transparent calc(100% - var(--foot, 2.3rem) - 0.25rem));
  mask-image: linear-gradient(#000 calc(100% - var(--foot, 2.3rem) - 2.75rem), transparent calc(100% - var(--foot, 2.3rem) - 0.25rem));
  opacity: 0; visibility: hidden; transform: translateY(1.5rem);
  transition: opacity 0.4s var(--ease), transform 0.6s var(--ease), visibility 0s linear 0.6s;
}
html[data-page="about"] .page[data-for="about"],
html[data-page="membership"] .page[data-for="membership"],
html[data-page="live"] .page[data-for="live"],
html[data-page="members"] .page[data-for="members"],
html[data-page="steinway"] .page[data-for="steinway"] {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity 0.7s var(--ease) var(--page-wait, 0s), transform 1s var(--ease) var(--page-wait, 0s), visibility 0s linear var(--page-wait, 0s);
}
.page-body { width: min(100%, 40rem); margin: 0 auto; }
/* The velvet is the piano's alone. Every other page (but STEINWAY's white
   sheet) is the blackest black: the page and the window either side of it
   are #000, and once the page has risen (html.void, pages.js) the menu's fall
   and the footer go to the same black, so nothing of the velvet shows.
   RECORDS: the working diary, set as a terminal. The records sit in one terminal
   framed in the mark's chrome (.chrome), in Fira Code; every title is cast in
   chrome (data-chrome, chrome-type.js). */
.page[data-for="about"], .page[data-for="membership"], .page[data-for="live"], .page[data-for="members"] { -webkit-mask-image: none; mask-image: none; background: #000; }
body { transition: background-color 0.7s var(--ease); }
html.void body { background-color: #000; }
@media (prefers-reduced-motion: reduce) { body { transition: none; } }
.void .bar::before { background: #000; }
.void .foot::before { opacity: 1; background: #000; -webkit-mask-image: linear-gradient(transparent, #000 2.75rem); mask-image: linear-gradient(transparent, #000 2.75rem); }
.page-body.about { width: min(100%, 52rem); }
.term { --dim: #b3b5bc; border-radius: 18px; padding: 0.5rem 2.25rem 2.25rem; background: none; font-family: "Fira Code", ui-monospace, monospace; }
/* The terminal's head: where it is on the left, what it holds on the right.
   The way back from an opened record is the mark in the bar (mark.js). */
.term-head {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem; min-height: calc(4.25rem + 1px); padding: 0.75rem 0;
  border-bottom: 1px solid transparent;
  color: var(--dim); font-size: 0.75rem; font-weight: 400; letter-spacing: 0.12em;
}
.term-head > :last-child { margin-left: auto; text-align: right; }
/* The hole punched in the window's head, as in an event badge: a round
   hole the size of the mark in the bar, cut through and ringed in the
   buttons' chrome, the black of the room behind it, shadowed under its top
   lip. Out of the flow, in the middle of the head, as on the passes of the
   list, so a record opened wears its hole where its pass did. */
.term .term-hole {
  position: absolute; top: 1.5rem; left: 50%; margin-left: -1.125rem; width: 2.25rem; height: 2.25rem; border-radius: 50%;
  background: #000; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.9), inset 0 -1px 0 rgba(255, 255, 255, 0.05);
  animation: none;
}
/* An opened record's head is its pass's: the hole in the middle, its number
   at the right, on one line; and at its foot, at the left, the mark. */
.term-head .record-no { position: absolute; top: 1.5rem; right: 2.25rem; height: 2.25rem; display: flex; align-items: center; font-size: 0.8125rem; }
.record > .record-foot { margin-top: 3rem; }
.record > .record-foot img { width: 2.25rem; height: 2.25rem; filter: none; }
.ring-ready .term-hole::before { border-width: 1.125rem; border-image: var(--ring-pill) 50% 22.32% stretch; filter: brightness(0.85); }
.term-right { display: flex; align-items: baseline; gap: 1.5rem; white-space: nowrap; }
/* Where it is, the terminal's title: in chrome, as the record titles are. */
/* (The path is shown beside the mark in the bar; here it is the heading, unseen.) */
.term .term-title { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* The list: a pass to a record, newest first, as one is worn at a door:
   taller than it is wide, in its own chrome frame. At its head a hole in the
   middle for the lanyard and the record's number at the right. Then the
   title in chrome and the first lines of what it says; and at its foot the
   mark at the left, the way in, which lights and steps forward under the
   pointer, with when it was sealed and, quietest, the address
   beside it. The passes start at the left and run on,
   a row of them as records are added. The terminal round them is not drawn:
   they stand on the black themselves. */
.page-body.about:has(.record-list) { width: min(100%, var(--width, 75rem)); }
.term:has(> .record-list) { padding: 0; background: none; }
.term:has(> .record-list)::before, .term:has(> .record-list)::after { display: none; }
.record-fails { margin: 0 0 1.5rem; color: var(--ink); font-size: 0.75rem; letter-spacing: 0.12em; }
.record-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 2rem; }
/* (A pass is never taller than the window has room for: on a short screen
   it is drawn smaller, whole, and everything on it with it, each size being
   a part of the pass's own width. The head's three stand on one line, each
   --head tall: a whole, even number of pixels, so the hole's ring, two
   halves of a capsule, meets itself exactly at its sides.) */
.record-pass { --head: 2.25rem; width: min(100%, 19rem, (100dvh - 17rem) * 0.625); aspect-ratio: 5 / 8; border-radius: 18px; background: none; container-type: inline-size; }
@supports (width: round(1cqi, 2px)) { .record-pass { --head: round(11.84cqi, 2px); } }
.record-list a, .record-pass > .term-hole { --head: inherit; }
.term .record-pass .term-hole { top: 7.9cqi; left: 50%; width: var(--head); height: var(--head); margin-left: calc(var(--head) / -2); }
.ring-ready .record-pass .term-hole::before { border-width: calc(var(--head) / 2); }
.record-list a {
  position: relative; z-index: 1; box-sizing: border-box; height: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 27.6cqi 9.2cqi 9.2cqi; border-radius: inherit; color: inherit; text-decoration: none;
}
.record-list a:focus-visible { outline: 1px solid var(--dim); outline-offset: 0.375rem; }
.record-no { position: absolute; top: 7.9cqi; height: var(--head); display: flex; align-items: center; }
.record-go { flex: none; display: flex; align-items: center; color: var(--dim); font-size: 5.26cqi; transition: color 0.3s var(--ease), transform 0.4s var(--ease); }
/* The foot: the mark at the left, and beside it, standing on the same line, when, who and where. */
.record-foot { align-self: end; display: flex; align-items: flex-end; gap: 4.6cqi; }
.record-no { right: 9.2cqi; color: var(--dim); font-size: 4.28cqi; letter-spacing: 0.12em; font-variant-numeric: tabular-nums; transition: color 0.3s var(--ease); }
.record-title { font-size: 7.9cqi; font-weight: 500; line-height: 1.2; letter-spacing: -0.01em; }
.record-lead {
  align-self: start; margin-top: 6.58cqi; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; line-clamp: 4;
  color: var(--dim); font-size: 4.28cqi; line-height: 1.6;
}
.record-meta {
  min-width: 0; display: grid; gap: 1.84cqi;
  color: var(--dim); font-size: 3.62cqi; letter-spacing: 0.06em; font-variant-numeric: tabular-nums;
  transition: color 0.3s var(--ease);
}
/* The address, last at the foot: in the quietest ink, set close, as an address is. */
.record-site { color: #5d5f66; letter-spacing: 0; }
.record-list a:hover .record-go, .record-list a:focus-visible .record-go { color: var(--ink); transform: translateX(0.25rem); }
/* The baked mark: the hole's own size, a little dimmed at rest, lit full as
   the pass is pointed at. */
.record-go img { display: block; width: var(--head); height: var(--head); filter: brightness(0.75); transition: filter 0.3s var(--ease); }
.record-list a:hover .record-go img, .record-list a:focus-visible .record-go img { filter: none; }
.record-list a:hover .record-meta, .record-list a:focus-visible .record-meta, .record-list a:hover .record-no, .record-list a:focus-visible .record-no { color: var(--ink); }
@media (hover: none) { .record-list a:hover .record-go { color: var(--dim); transform: none; } .record-list a:hover .record-go img { filter: brightness(0.75); } .record-list a:hover .record-meta, .record-list a:hover .record-no { color: var(--dim); } }
/* An opened record: the title in chrome, large; its address, day and author
   as a terminal lists them; what was said; and its hash, last. The cursor
   waits at the end of the words. */
.term .record-name { margin: 2.75rem 0 0; font-size: clamp(1.875rem, 4.4vw, 3rem); font-weight: 500; line-height: 1.15; letter-spacing: -0.01em; text-wrap: balance; }
.record-facts {
  display: grid; grid-template-columns: auto 1fr; gap: 0.45rem 2rem; margin: 2.25rem 0 0;
  font-size: 0.8125rem; line-height: 1.5; letter-spacing: 0.02em;
}
.record-facts dt { color: var(--dim); letter-spacing: 0.12em; }
.record-facts dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.record-say { display: grid; gap: 1.35rem; margin-top: 2.5rem; padding-top: 2.25rem; border-top: 1px solid transparent; }
.record-say p { margin: 0; max-width: 64ch; color: var(--ink); font-size: 1rem; line-height: 1.75; }
.record-say p:last-child::after {
  content: ""; display: inline-block; width: 0.6em; height: 1.15em; margin-left: 0.35em; vertical-align: -0.2em;
  background: var(--ink); animation: cursor 1.1s steps(1) infinite;
}
@keyframes cursor { 50% { opacity: 0; } }
/* A hash, in groups of eight as a terminal prints a digest. */
.record-hash { word-spacing: 0.4em; }
.term .record-bad { color: var(--ink); }
.record-facts .record-quiet { color: var(--dim); letter-spacing: 0.12em; }
.record-none { margin: 2.25rem 0 0.5rem; color: var(--dim); font-size: 0.875rem; line-height: 1.6; }
.record-facts a { color: inherit; text-decoration: none; background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat; transition: background-size 0.4s var(--ease); }
.record-facts a:hover, .record-facts a:focus-visible { background-size: 100% 1px; outline: none; }
@media (hover: none) { .record-facts a:hover { background-size: 0 1px; } }
/* Chrome type. Until it is cast, or without WebGL, a plain chrome sheen (no
   filter on it: phones draw a filtered, text-clipped line in a scrolling
   page as a black box). */
[data-chrome] {
  --chrome-pad: 12px;
  position: relative; display: block; width: fit-content; max-width: 100%;
  color: transparent;
  background: linear-gradient(180deg, #f4f5f7 0%, #c4c7cd 46%, #6b6e76 54%, #d9dbe0 100%);
  -webkit-background-clip: text; background-clip: text;
}
[data-chrome] canvas {
  position: absolute; left: calc(-1 * var(--chrome-pad)); top: calc(-1 * var(--chrome-pad));
  width: calc(100% + 2 * var(--chrome-pad)); height: calc(100% + 2 * var(--chrome-pad));
  pointer-events: none; opacity: 0; transition: opacity 0.6s var(--ease);
}
[data-chrome].cast { background: none; }
[data-chrome].cast canvas { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  [data-chrome] canvas, .record-go, .record-go img, .record-meta, .record-no, .record-facts a, .bar .mark span { transition: none; }
  .record-say p:last-child::after { animation: none; }
}
@media (max-width: 600px) {
  .term { padding: 0.25rem 1.25rem 1.5rem; }
  .term .term-hole, .term-head .record-no { top: 1.25rem; }
  .term-head .record-no { right: 1.25rem; }
  .term-right { gap: 1rem; }
  .record-facts { grid-template-columns: 1fr; gap: 0.15rem; }
  .record-facts dd + dt { margin-top: 0.6rem; }
}
/* STEINWAY: the making, a step at a time, beside the piano in pieces
   (steinway.js). The piano stays where it is while the steps pass it: it is
   the picture, and each step is what is said of it: a part, how many of
   it, and one line, as a kit's instructions are set out. */
/* This one page is a white sheet, as a kit's instructions are printed: the
   black piano and its brass read plainly on it. Its inks are turned to suit. */
.page[data-for="steinway"] { background: #fff; color: #0b0b0c; --ink: #0b0b0c; --dim: #5d5f66; --line: rgba(0, 0, 0, 0.14); }
/* The page starts exactly where its title and piano are held (top: 0 and
   below), so neither moves from the first pixel of scroll to the last. */
@media (min-width: 901px) { .page[data-for="steinway"] { padding-top: calc(var(--bar) + 1rem); } }
@media (max-width: 900px) { .page[data-for="steinway"] { padding-top: calc(var(--bar) + 2rem); } }
/* The maker's name in solid black on the sheet. */
.page[data-for="steinway"] .pitch-maker { filter: brightness(0); opacity: 1; }
/* The page's two heads (the title, and the history's): the maker's name or a
   small label, the line, and a few words, stacked. Set here for this page
   alone. */
.page[data-for="steinway"] .pitch-head { display: grid; gap: 1.1rem; }
.page[data-for="steinway"] .pitch-head > span { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.12em; }
.page[data-for="steinway"] .pitch-head h2 { margin: 0; }
.page[data-for="steinway"] .pitch-head p { margin: 0.3rem 0 0; max-width: 34rem; color: var(--dim); line-height: 1.5; }
/* The menu and the footer go with the sheet (html.paper, set by steinway.js
   once the page has risen and taken off the moment it is left): white behind
   them in place of black, and their inks turned dark. The sheet runs on to
   the foot of the window, and the footer stands on a fall of white, so
   nothing is read through it. The sign-in keeps its chrome ring. */
.page[data-for="steinway"] { -webkit-mask-image: none; mask-image: none; }
.bar::before { transition: background-color 0.6s var(--ease), bottom 0.6s var(--ease); }
/* On the sheet the fade stops just under the menu, so the maker's name and
   the title, held beneath it, are never behind its white or its blur. */
.paper .bar::before { background-color: #fff; bottom: -1rem; }
.paper .bar { color: #0b0b0c; }
.paper .bar > nav a { color: #5d5f66; }
.paper .bar > nav a:hover, .paper .bar > nav a[aria-current] { color: #0b0b0c; }
.paper .bar > nav a[aria-current]::after { background: #0b0b0c; }
.paper .bar a:focus-visible { outline-color: #0b0b0c; }
/* The sign-in is clear inside its ring, so on the white sheet its words are ink. */
.paper .bar .wallet { color: #0b0b0c; }
.paper .bar .wallet.on { color: #5d5f66; }
.foot::before { content: ""; position: absolute; z-index: -1; inset: -2.75rem 0 0; pointer-events: none; opacity: 0; background: linear-gradient(rgba(255, 255, 255, 0), #fff 2.75rem); transition: opacity 0.6s var(--ease); }
.paper .foot::before { opacity: 1; }
.paper .foot { color: #5d5f66; }
.paper .foot .built { color: #0b0b0c; }
@media (prefers-reduced-motion: reduce) { .bar::before, .foot::before { transition: none; } }
.page[data-for="steinway"] .pitch-link::before { background: var(--ink); }
.page[data-for="steinway"] .pitch-link:focus-visible { outline-color: var(--ink); }
/* The piano is held only as long as the build it stands in, so the room the
   page keeps above the footer is given inside the build, under the history,
   and not below it: the build then runs to the very end of the scroll and
   the piano never moves. */
.made { display: grid; gap: clamp(3rem, 6vw, 5rem); }
.page[data-for="steinway"] { padding-bottom: 0; }
.made-history { padding-bottom: calc(var(--foot, 2.3rem) + 6rem); }
/* The piano has all the room there is: the words keep to a narrow column
   and the picture takes the rest, as tall as the window allows. */
.page-body.made { width: min(100%, 78rem); }
.made-build { display: grid; grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); gap: 0 clamp(1.5rem, 3vw, 3rem); align-items: start; }
/* (A sticky piece is held clear of the page's own padding, which already
   keeps it below the menu: so its place is given from there.) */
.made-show { position: sticky; top: -1.5rem; order: 2; margin: 0; display: grid; grid-template-rows: minmax(0, 1fr); height: calc(100dvh - var(--bar) - var(--foot, 2.3rem) - 3.5rem); min-height: 20rem; }
.made-show canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: pan-y; opacity: 0; transition: opacity 0.8s var(--ease) 0.2s; }
.made-build.on canvas { opacity: 1; }
.made-show canvas.held { cursor: grabbing; }
.made-say { display: grid; gap: 2rem; }
/* The maker's name and the title stay where they are while the parts pass
   beneath them, on the sheet's own white, which falls away softly below. */
.made-say > header { position: sticky; z-index: 1; top: -2.75rem; padding-bottom: 1rem; background: #fff; box-shadow: 0 -4rem 0 #fff, 0 1.25rem 1.25rem #fff; }
.made-intro { margin: -1rem 0 0; max-width: 24rem; color: var(--dim); font-size: 0.9375rem; line-height: 1.5; }
.made-steps { list-style: none; margin: 0; padding: 0; }
.made-steps li { display: grid; align-content: center; gap: 0.6rem; min-height: 38dvh; transition: opacity 0.5s var(--ease); }
.made-build.on .made-steps li:not(.on) { opacity: 0.32; }
.made-steps span i { margin-left: 0.6rem; font-style: normal; color: var(--ink); }
.made-steps span, .made-years dt { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.12em; }
.made-steps h3 { margin: 0; font-family: "Fira Code", ui-monospace, monospace; font-weight: 400; font-size: clamp(1.625rem, 2.6vw, 2.125rem); line-height: 1.12; letter-spacing: -0.015em; }
.made-steps p { margin: 0; max-width: 24rem; color: var(--dim); font-size: 0.9375rem; line-height: 1.5; }
/* The history follows the last part in the same column, beside the finished
   piano, which stays where it is to the foot of the page. The room after the
   last part lets it reach the line it is read at before the history begins. */
.made-steps { padding-bottom: 12dvh; }
/* The history: a year, and what happened in it. */
.made-history { display: grid; gap: 2.5rem; max-width: 40rem; }
.made-years { display: grid; margin: 0; border-top: 1px solid var(--line); }
.made-years div { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); gap: 1rem; align-items: baseline; padding: 0.95rem 0; border-bottom: 1px solid var(--line); }
.made-years dt { color: var(--ink); font-family: "Fira Code", ui-monospace, monospace; font-size: 1.125rem; font-weight: 400; letter-spacing: 0; }
.made-years dd { margin: 0; color: var(--dim); font-size: 0.9375rem; line-height: 1.5; }
@media (max-width: 900px) {
  /* One column: the piano stays at the head of the window, on the sheet, and
     the steps pass beneath it. */
  .made-build { grid-template-columns: minmax(0, 1fr); gap: 1rem 0; }
  .made-say { display: contents; }
  .made-say > header { order: -1; top: 0; height: 4rem; align-content: end; padding: 0; box-shadow: 0 -3rem 0 #fff; }
  .made-intro, .made-steps, .made-history { order: 1; }
  .made-intro { margin: 0; }
  .made-show { order: 0; z-index: 1; top: 5rem; height: 45dvh; min-height: 15rem; margin: 0 calc(-1 * var(--gutter)); padding: 0 0 0.9rem; background: #fff; box-shadow: 0 -1rem 0 #fff, 0 1.5rem 1.5rem #fff; }
  .made-steps li { min-height: 26dvh; align-content: start; padding-top: 1.5rem; }
}
/* On a wide window the maker's name and the title stand over the piano, set
   to the right, and are held there with it under the menu: the parts' words
   then pass up the left column with nothing laid over them. The title is one
   line here, so its height is known (--made-head), and the piano is held that
   far below it. */
@media (min-width: 901px) {
  .made-build { --made-head: 4.5rem; grid-template-rows: auto auto auto auto; row-gap: 1rem; }
  .made-say { display: contents; }
  .made-say > header { grid-column: 2; grid-row: 1; justify-items: end; align-content: end; height: var(--made-head); top: 0; padding: 0; text-align: right; background: none; box-shadow: none; }
  .made-intro { grid-column: 1; grid-row: 2; margin: 0; }
  .made-steps { grid-column: 1; grid-row: 3; }
  .made-history { grid-column: 1; grid-row: 4; }
  .page[data-for="steinway"] .made-show { grid-column: 2; grid-row: 2 / 5; top: calc(var(--made-head) + 1rem); height: calc(100dvh - var(--bar) - var(--foot, 2.3rem) - 4.5rem - var(--made-head)); }
  .made-say > header h2 { white-space: nowrap; }
}
@media (prefers-reduced-motion: reduce) { .made-show canvas, .made-steps li { transition: none; } }
/* (The menu is one row at every width, folded behind its button on a phone,
   so every page starts the same distance below it.) */
.page h1 { margin: 0 0 2rem; font-size: 0.6875rem; font-weight: 500; letter-spacing: 0.12em; color: var(--dim); }
.page h2 { margin: 0 0 1rem; font-size: 1rem; font-weight: 500; letter-spacing: 0.1em; }
@media (prefers-reduced-motion: reduce) { .page, html[data-page] .page[data-for] { transition: none; } }
/* The proof: a card over the foot, made as the wallet's help is, a row for
   each thing to check. It rises a little as it appears. */
.proof {
  position: absolute; z-index: 5; right: 0; bottom: calc(100% + 0.9rem); width: 16rem;
  padding: 1rem 0.6rem 0.6rem; border-radius: 14px; border: 1px solid rgba(255, 255, 255, 0.12);
  background: linear-gradient(180deg, #121318, #08080a); box-shadow: 0 1.5rem 3rem -1rem #000;
  font: 400 0.75rem/1.45 "Fira Code", ui-monospace, monospace; letter-spacing: 0; color: var(--dim);
  opacity: 0; visibility: hidden; transform: translateY(0.4rem);
  transition: opacity 0.3s var(--ease), transform 0.4s var(--ease), visibility 0s linear 0.4s;
}
.proof.open { opacity: 1; visibility: visible; transform: none; transition: opacity 0.3s var(--ease), transform 0.4s var(--ease), visibility 0s; }
.proof p { margin: 0 0.5rem 0.75rem; }
.proof nav { display: grid; gap: 0.25rem; }
.proof a, .proof button { display: flex; align-items: center; gap: 0.6em; width: 100%; min-height: 2.75rem; padding: 0 0.7rem; border: 0; border-radius: 8px; background: rgba(255, 255, 255, 0.05); color: #fff; font: 500 0.6875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; text-align: left; text-decoration: none; cursor: pointer; transition: background 0.2s; }
.proof a small, .proof button small { margin-left: auto; color: var(--dim); font-size: 1em; letter-spacing: 0; }
.proof a:hover, .proof button:hover, .proof a:focus-visible, .proof button:focus-visible { background: rgba(255, 255, 255, 0.1); outline: none; }
.proof em { font-style: normal; color: #14f195; letter-spacing: 0.1em; font-size: 0.875em; }
.proof [hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .proof, .proof.open { transition: none; } }

/* The key holders sheet: the board of 88 keys, then the lists. */
.holders-body { width: min(100%, 52rem); height: min(46rem, calc(100vh - 2rem)); }
.board-wrap { margin: 1.25rem 0 0; overflow-x: auto; scrollbar-width: none; }
.board-wrap::-webkit-scrollbar { display: none; }
.board { position: relative; min-width: 34rem; height: 6.5rem; display: block; }
.board .k {
  --w: calc(100% / var(--whites));
  position: absolute; top: 0; left: calc(var(--at) * var(--w)); width: var(--w); height: 100%;
  padding: 0; border: 0; border-radius: 0 0 3px 3px; cursor: pointer;
  background: linear-gradient(#f3f1ec, #dcd8cf);
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.35);
  overflow: hidden; transition: filter 0.2s;
}
/* The held share of a key, filled up from its front in the Solana colour at
   its place on the keyboard. */
.board .k::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: calc(var(--fill) * 100%);
  background: color-mix(in oklab, #9945ff calc((1 - var(--t)) * 100%), #14f195);
  opacity: 0.85; transition: height 0.6s var(--ease);
}
.board .k.black {
  z-index: 1; width: calc(var(--w) * 0.62); height: 62%; translate: -50% 0;
  border-radius: 0 0 2px 2px; box-shadow: none;
  background: linear-gradient(color-mix(in oklab, #000 45%, color-mix(in oklab, #9945ff calc((1 - var(--t)) * 100%), #14f195)), color-mix(in oklab, #000 75%, color-mix(in oklab, #9945ff calc((1 - var(--t)) * 100%), #14f195)));
}
.board .k:hover { filter: brightness(1.15); }
.board .k[aria-pressed="true"] { outline: 2px solid #fff; outline-offset: -2px; z-index: 2; }
.board .k:focus-visible { outline: 2px solid #fff; outline-offset: -2px; z-index: 2; }
.tabs { display: flex; gap: 0.25rem; margin: 1.25rem 0 0; }
.sheet .tabs button { padding: 0.55rem 0.9rem; border: 0; border-radius: 10px; background: none; color: var(--dim); font: 500 0.6875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.08em; box-shadow: none; }
.sheet .tabs button[aria-selected="true"] { color: #fff; background: rgba(255, 255, 255, 0.07); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.keylist { list-style: none; margin: 0.75rem 0 0; padding: 0; }
.keylist[hidden] { display: none; }
.keylist li { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) 7.5rem; align-items: center; gap: 1rem; padding: 0.6rem 0.5rem; border-top: 1px solid var(--line); border-radius: 8px; transition: background 0.3s; }
.keylist li.picked { background: rgba(255, 255, 255, 0.06); }
.sheet .keylist .kn { display: grid; justify-items: start; gap: 0.15rem; padding: 0; border: 0; background: none; color: #fff; box-shadow: none; text-align: left; }
.keylist .kn b { font: 600 0.9375rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0; }
.keylist .kn small { color: var(--dim); font-size: 0.5625rem; letter-spacing: 0.08em; }
.keylist .owners { display: flex; flex-wrap: wrap; gap: 0.35rem; min-width: 0; }
.keylist .chip { display: inline-flex; align-items: baseline; gap: 0.3em; max-width: 100%; padding: 0.3rem 0.6rem; border-radius: 999px; background: rgba(255, 255, 255, 0.06); font-size: 0.75rem; }
.keylist .chip a { overflow: hidden; color: #fff; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; }
.keylist .chip a.anon { color: var(--quiet); }
.keylist .chip a.you::after { content: " · YOU"; color: #14f195; }
.keylist .chip a:hover { text-decoration: underline; text-underline-offset: 3px; }
.keylist .chip small { color: var(--dim); }
.keylist .none { color: var(--dim); font-size: 0.75rem; opacity: 0.6; }
.keylist .left { display: grid; grid-template-columns: auto 1fr; align-items: baseline; justify-items: end; column-gap: 0.1rem; row-gap: 0.4rem; text-align: right; }
.keylist .left b { grid-column: 1; font: 600 0.9375rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.keylist .left small { grid-column: 2; justify-self: start; color: var(--dim); font-size: 0.6875rem; white-space: nowrap; }
.keylist .meter { grid-column: 1 / -1; justify-self: stretch; height: 2px; border-radius: 2px; background: #2a2a2e; overflow: hidden; }
.keylist .meter::before { content: ""; display: block; height: 100%; width: calc(var(--fill) * 100%); background: linear-gradient(90deg, #9945ff, #14f195); }
.wall[hidden] { display: none; }
.wall a.you::after { content: " · YOU"; color: #14f195; }
@media (max-width: 600px) {
  .keylist li { grid-template-columns: 4rem minmax(0, 1fr) 5.5rem; gap: 0.6rem; }
  /* A half cell is too narrow for a figure and the arrow side by side, so the
     arrow, a little smaller, rises into the label's row, on both cells alike. */
  button.holders, button.stat.raised { padding-right: 1.4rem; }
  .stat .go-arrow { top: 0.5rem; right: 0.75rem; translate: none; width: 1.75rem; height: 1.75rem; }
  .stat .go-arrow svg { width: 12px; height: 12px; }
}
.wall { list-style: none; margin: 1.25rem 0 0; padding: 0; }
.wall li { display: grid; grid-template-columns: 2.25rem minmax(0, 13rem) minmax(0, 1fr) 2.5rem; align-items: baseline; gap: 1rem; padding: 0.75rem 0; border-top: 1px solid var(--line); }
.wall .n { color: var(--dim); font: 500 0.8125rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.wall b.n { justify-self: end; color: #fff; font-weight: 600; font-size: 0.9375rem; }
.wall a { overflow: hidden; color: #fff; font-size: 0.875rem; letter-spacing: 0.04em; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; }
.wall a.anon { color: var(--quiet); }
.wall a:hover, .wall a:focus-visible { text-decoration: underline; text-underline-offset: 4px; outline: none; }
.wall .keys { overflow: hidden; color: var(--dim); font-size: 0.6875rem; letter-spacing: 0.06em; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 600px) { .wall li { grid-template-columns: 2rem minmax(0, 1fr) 2rem; } .wall .keys { grid-column: 2 / 4; grid-row: 2; } }

/* The name a buyer can leave with a purchase. */
.pack-name { display: grid; gap: 0.6rem; width: min(100%, 24rem); text-align: center; }
.pack-name[hidden] { display: none; }
.pack-name span { display: grid; gap: 0.35rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.pack-name i { font-style: normal; font-weight: 400; color: var(--quiet); font-size: 0.5625rem; letter-spacing: 0.1em; }
.pack-name input { padding: 0.75rem 1.25rem; border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 999px; background: rgba(255, 255, 255, 0.04); color: #fff; font: 500 0.875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; text-align: center; }
.pack-name input::placeholder { color: #55575e; }
.pack-name input:focus { border-color: rgba(255, 255, 255, 0.5); outline: none; }
.pack.is-ripping .pack-name { opacity: 0; transition: opacity 0.3s; }

/* A status LED: lit while the chain feed is live, off for a beat whenever
   the chain reports a change, like a hardware activity light. */
.led { flex: none; width: 5px; height: 5px; border-radius: 50%; background: #1b1c20; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.6); }
.led.on { background: #14f195; }
.led.on.blink { background: #0b3d28; }

.pack-read em { font-style: normal; color: var(--quiet); font-size: 0.6875rem; letter-spacing: 0.08em; }

/* Location. */
.place { display: grid; gap: 0.9rem; }
.place > span { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.place h2 { margin: 0; font-size: 2rem; font-weight: 600; letter-spacing: -0.03em; }
.place p { margin: 0; max-width: 34rem; color: var(--dim); font-size: 0.9375rem; line-height: 1.55; }
.place dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; margin: 0.5rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
.place dt { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.place dd { margin: 0.35rem 0 0; font-size: 0.875rem; font-weight: 500; }
@media (max-width: 600px) { .place dl { grid-template-columns: 1fr; } }

/* LOCATION: where the piano will live is put to the key holders. The title
   runs across the top. Beneath, two columns
   whose tops are level: on the left the leaderboard and the one action, on
   the right the map. Chrome is kept to what matters: the leaderboard's
   frame, the map's and the button; a capsule is only ever a button. On a phone they stack. Nothing is said twice: the
   turnout is the only count, and each card carries its own share. */
.page-body.wide { width: min(100%, 68rem); }
.vote-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.5rem 3rem; flex-wrap: wrap; margin-bottom: 2rem; }
/* The plate's grain, for the map wherever it stands. */
.vote, .pitch-home { --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/></filter><rect width='80' height='80' filter='url(%23g)'/></svg>"); }
.vote { display: grid; grid-template-columns: minmax(0, 27rem) minmax(0, 1fr); align-items: stretch; gap: 2.5rem 3rem; }
/* The left column is as tall as the map: the leaderboard takes all the
   height the button leaves, so the button's base is level with the map's,
   and the line under it sits level with the map's caption. */
.vote-side { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 0.75rem; }
/* The piano's home, with no vote on it: what is said of it stands in the
   middle of its side, beside the map, with the address under it as a label. */
.vote.home .vote-side { grid-template-rows: none; align-content: center; }
.vote.home .place { display: grid; gap: 1rem; max-width: 26rem; }
.vote.home .place p { margin: 0; }
.vote.home address { margin-top: 0.75rem; padding-top: 1.25rem; border-top: 1px solid var(--line); color: var(--dim); font-style: normal; font-size: 0.625rem; font-weight: 500; letter-spacing: 0.12em; line-height: 1.9; }
/* The leaderboard: one chrome frame, the page's only one beside the map's,
   holding a row for each place in the order of their votes. A row is its
   place (1, 2, 3), its photograph in a small frame cut into the board, its
   name, and its tally (its address is under the map, when it is the one
   shown there). The whole row is a button
   (.candidate-show, laid under the rest): choosing it shows the place on the
   map and makes it the one the action below votes for. The chosen row is
   lit; the others stand back in the second ink. When the order changes the
   rows glide to their new places (location.js). */
.leaderboard { display: flex; flex-direction: column; overflow: hidden; border-radius: 18px; background: var(--metal); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1rem 2rem -1rem #000; }
.leaderboard::before { z-index: 2; opacity: 0.9; }
.leaderboard::after { opacity: 0.3; }
.leaderboard header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.9rem 1.25rem 0.8rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
/* The turnout is the board's own head: the votes cast, of those there are,
   at its right, and beneath it the line the rows hang from, lit in Solana's
   colours as far as the votes have been cast. One count, in one place. */
.leaderboard header output { display: flex; align-items: baseline; gap: 0.5em; white-space: nowrap; text-transform: uppercase; }
.leaderboard header output b { color: var(--ink); font: 600 0.8125rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.leaderboard header output small { font-size: inherit; }
.gauge-line.turnout { flex: none; background: rgba(255, 255, 255, 0.08); }
.gauge-line.turnout div { top: -0.5px; height: 2px; border-radius: 0 2px 2px 0; }
.ballot .candidate:first-child { border-top: 0; }
.ballot { flex: 1; display: grid; grid-auto-rows: 1fr; list-style: none; margin: 0; padding: 0; }
.candidate { position: relative; display: grid; grid-template-columns: 1rem 7.5rem minmax(0, 1fr); align-items: center; gap: 0 1.1rem; padding: 0.8rem 1.25rem; border-top: 1px solid rgba(255, 255, 255, 0.06); transition: background-color 0.4s var(--ease); }
.candidate.on { background: rgba(255, 255, 255, 0.05); }
.candidate-show { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; }
.candidate.on .candidate-show { cursor: default; }
.candidate-show:focus-visible { outline: 1px solid #fff; outline-offset: -4px; }
.rank, .candidate-photo, .candidate-body { position: relative; pointer-events: none; }
@media (hover: hover) { .candidate:not(.on):hover { background: rgba(255, 255, 255, 0.025); } }
/* The place: the leader's in the first ink. */
.rank { color: #5d5f66; font: 400 0.6875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; text-align: center; transition: color 0.4s var(--ease); }
.candidate.leads .rank { color: var(--ink); }
.candidate-photo {
  /* The photograph's frame takes the whole height of its row. */
  align-self: stretch; display: grid; place-items: center; min-height: 4.5rem; overflow: hidden; border-radius: 10px;
  background: var(--grain), linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%);
  box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.85), inset 0 1px 1px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07);
}
.candidate-photo span { max-width: 4rem; color: #4a4c53; font-size: 0.5rem; font-weight: 500; letter-spacing: 0.12em; line-height: 1.6; text-align: center; }
.candidate-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.candidate-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.85rem; min-width: 0; transition: opacity 0.4s var(--ease); }
/* The name has the whole line, but for the corner the tick takes when it is there. */
.candidate:has(.yours:not([hidden])) h3 { padding-right: 1.75rem; }
.candidate h3 { margin: 0; font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.03em; line-height: 1.15; transition: color 0.4s var(--ease); }
.candidate p { margin: 0.2rem 0 0; overflow: hidden; color: var(--dim); font-size: 0.75rem; line-height: 1.4; text-overflow: ellipsis; white-space: nowrap; }
.candidate .tally output { transition: color 0.4s var(--ease); }
/* The rows not chosen: the second ink, at a little over half their light. */
.candidate:not(.on) h3, .candidate:not(.on) .tally output { color: var(--dim); }
.candidate:not(.on) .candidate-body, .candidate:not(.on) .candidate-photo { opacity: 0.6; }
@media (hover: hover) { .candidate:not(.on):hover .candidate-body { opacity: 0.85; } }
/* Which place leads is said by its standing first, and by its tally alone
   being in Solana's colours (the others' lines are pale steel). The row this
   visitor has voted for carries a tick beside its name, drawn in those same
   colours: the gradient shown through a tick-shaped opening. Its words (YOUR
   VOTE) stay in the page for a screen reader, at no size. */
.candidate-tags { position: absolute; top: 0; right: 0; }
.yours {
  display: block; width: 1.125rem; height: 1.125rem; overflow: hidden; font-size: 0; color: transparent;
  background: linear-gradient(90deg, #9945ff, #5497d5, #14f195);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'><path d='M3.5 9.6l3.6 3.6 7.4-8.2' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'><path d='M3.5 9.6l3.6 3.6 7.4-8.2' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / contain no-repeat;
}
.yours[hidden] { display: none; }
/* The tally: the gauge's fine line with no capsule round it, lit as far as
   this place's share of the votes cast, and the share at its end. */
.tally { display: flex; align-items: center; gap: 0.8rem; }
.tally output { flex: none; display: flex; align-items: baseline; gap: 0.35em; font: 600 0.8125rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; white-space: nowrap; }
.tally output small { color: var(--dim); font-size: 0.6875rem; font-weight: 400; letter-spacing: 0; }
.tally output b { min-width: 1.4em; text-align: right; }
.tally .gauge-line div { top: -0.5px; height: 2px; background: #8e9099; }
.candidate.leads .tally .gauge-line div { background: linear-gradient(90deg, #9945ff, #5497d5, #14f195); }
/* The one action: a single button that always says what it will do, for the
   place chosen above, and under it one line saying where the visitor stands.
   It wears Solana's chrome whenever pressing it does something (connect,
   buy a key, vote, move a vote), and plain chrome when it only states a fact. */
.ballot-act { position: relative; }
.ballot-act button { width: 100%; padding: 0 1rem; overflow: hidden; text-overflow: ellipsis; }
.ballot-act p { position: absolute; left: 0; right: 0; top: calc(100% + 0.6rem); margin: 0; color: var(--dim); font-size: 0.75rem; line-height: 1.5; text-align: center; }
.pin span em { font-style: normal; font-weight: 500; color: var(--dim); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; }
.pin span em:empty { display: none; }
/* The preview's own switch: not part of the site, shown only on a local page
   that asks for it, so it is set plainly in the footer's type. */
.preview { display: grid; justify-items: center; gap: 0.5rem; margin-top: 4.5rem; color: var(--quiet); font: 400 0.625rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; }
.preview[hidden] { display: none; }
.preview div { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.4rem; }
.preview button { min-height: 1.75rem; padding: 0 0.7rem; border: 1px dashed #3a3b41; border-radius: 6px; background: none; color: var(--quiet); font: inherit; cursor: pointer; }
.preview button[aria-pressed="true"] { border-style: solid; border-color: #8e9099; color: #fff; }
.preview button:focus-visible { outline: 1px solid #fff; outline-offset: 2px; }

/* The map: the streets round a place, cut into a plate of gunmetal in a
   chrome frame. The plate is recessed: darker under its top lip, a
   catch-light along its bottom one. The streets are engraved: fine lines of
   pale steel, the great streets brightest, the side streets all but dark; a
   railway is a dashed line; the parks and squares are the plate left a shade
   lighter. Nothing on it is coloured but the line of light that draws it. */
.map { align-self: start; margin: 0; border-radius: 18px; aspect-ratio: 1120 / 794; background: #050506; }
.map::before { z-index: 2; opacity: 0.9; }
.map-plate {
  position: absolute; inset: 3px; overflow: hidden; border-radius: 15px;
  background: var(--grain), linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%);
}
.map-plate::after { content: ""; position: absolute; z-index: 2; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: inset 0 6px 10px rgba(0, 0, 0, 0.85), inset 0 1px 2px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07); }
/* The land fills the plate. Inside it the map itself (.map-pan: the streets
   and the pins together, so each pin keeps its place on the streets) is all
   of central and west London, and can be moved about and drawn back or
   brought closer (location.js sets --x, --y and --s on the plate, and never
   lets the map leave an edge of the plate bare). Drawn right back it fills
   the plate exactly, with every place in view. */
.map-plate { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; }
.map-plate.held { cursor: grabbing; }
.map-plate:focus-visible { outline: 1px solid #fff; outline-offset: 5px; }
.map-land { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.map-pan { position: absolute; left: 0; top: 0; width: 100%; aspect-ratio: 4480 / 3176; transform-origin: 0 0; transform: translate(var(--x, 0px), var(--y, 0px)) scale(var(--s, 1)); }
.map-streets svg { display: block; width: 100%; height: 100%; }
.map .road, .map .rail { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* A line keeps its weight on the plate however near the map is brought: its
   width is divided by the zoom, which would otherwise thicken it. */
.map .road.c { stroke: #25262b; stroke-width: calc(1px / var(--s, 1)); }
.map .road.b { stroke: #45474e; stroke-width: calc(1.25px / var(--s, 1)); }
.map .road.a { stroke: #8e9099; stroke-width: calc(1.5px / var(--s, 1)); }
.map .rail { stroke: #3a3b41; stroke-width: calc(1px / var(--s, 1)); stroke-dasharray: calc(5px / var(--s, 1)) calc(4px / var(--s, 1)); stroke-linecap: butt; }
/* Water is its banks: the river's and the park lakes', a fine pale line. */
.map .shore { fill: none; stroke: #33343a; stroke-width: calc(1px / var(--s, 1)); stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.map .green { fill: rgba(255, 255, 255, 0.04); stroke: rgba(255, 255, 255, 0.09); stroke-width: calc(1px / var(--s, 1)); vector-effect: non-scaling-stroke; }
/* The names keep one size on the plate however near the map is brought:
   --name is that size in the map's own units, set with the zoom. */
.map text { font: 500 calc(var(--name, 17) * 1px) "Fira Code", ui-monospace, monospace; letter-spacing: 0.12em; fill: #6a6c75; paint-order: stroke; stroke: #08090a; stroke-width: calc(var(--name, 17) * 0.3px); stroke-linejoin: round; }
.map text.green-name { fill: #4a4c53; }
/* From far off only the great roads, the parks and the river are drawn, and
   only the great names, so all of London reads at a glance. The lesser roads
   come up part way in (.mid), the side streets and the smaller names close in
   (.near): location.js sets both with the zoom. */
.map .road.b, .map .road.c, .map text.near { opacity: 0; transition: opacity 0.35s var(--ease); }
.map-plate.mid .road.b, .map-plate.near .road.c, .map-plate.near text.near { opacity: 1; }
/* Right back, the streets go unnamed: only the parks and the places are, or the names crowd each other. */
.map text.street.far { transition: opacity 0.35s var(--ease); }
.map-plate:not(.mid) text.street.far { opacity: 0; }
/* A land is drawn in from the foot up, as the piano is, and goes out the
   same way in reverse: only as much of it shows as the line of light
   (.map-beam, moved by location.js to these same times) has passed, up the
   plate, wherever the map has been moved to. It is
   drawn once the page has risen; moving from one place to another (.moved),
   the old land goes out over 0.7s and the new is drawn over 1.1s after it. */
.map-land {
  -webkit-mask: linear-gradient(#000, #000) no-repeat 0 100% / 100% 0%; mask: linear-gradient(#000, #000) no-repeat 0 100% / 100% 0%;
  transition: -webkit-mask-size 0.7s cubic-bezier(0.65, 0, 0.35, 1), mask-size 0.7s cubic-bezier(0.65, 0, 0.35, 1);
}
html[data-page="membership"] .map-land.on {
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  transition: -webkit-mask-size 1.6s cubic-bezier(0.65, 0, 0.35, 1) calc(var(--page-wait, 0s) + 0.4s), mask-size 1.6s cubic-bezier(0.65, 0, 0.35, 1) calc(var(--page-wait, 0s) + 0.4s);
}
html[data-page="membership"] .map.moved .map-land.on { transition: -webkit-mask-size 1.1s cubic-bezier(0.65, 0, 0.35, 1) 0.75s, mask-size 1.1s cubic-bezier(0.65, 0, 0.35, 1) 0.75s; }
/* The line of light: Solana's colours, a hair thick, across the plate. */
.map-beam { position: absolute; z-index: 1; left: 0; right: 0; top: 0; height: 1px; opacity: 0; pointer-events: none; background: linear-gradient(90deg, transparent, #9945ff 12%, #5497d5 50%, #14f195 88%, transparent); }
/* A pin: a point of light in a chrome rim, with a ring of light going out
   from it, and its name on a small plate above. */
/* A pin for every place, set on the map by location.js. The chosen place
   carries the full label; the others a point and their name, which choose
   them when pressed. */
.pin { position: absolute; z-index: 1; left: 50%; top: 50%; width: 0; height: 0; opacity: 0; transform: scale(calc(1 / var(--s, 1))); transition: opacity 0.3s var(--ease); }
.pin.on { z-index: 2; }
html[data-page="membership"] .map-land.on .pin { opacity: 1; transition: opacity 0.6s var(--ease) calc(var(--page-wait, 0s) + 1.5s); }
html[data-page="membership"] .map.moved .map-land.on .pin { transition: opacity 0.6s var(--ease) 1.45s; }
.pin i { position: absolute; left: -8px; top: -8px; width: 16px; height: 16px; border-radius: 50%; background: radial-gradient(circle, #fff 0 2.5px, #000 3px 5px, #6a6c75 5.5px 6.5px, transparent 7px); }
.ring-ready .pin i { background: radial-gradient(circle, #fff 0 2.5px, #000 3px 100%), none; }
.ring-ready .pin i::after { content: ""; position: absolute; inset: 0; background: var(--ring-dot) center / 100% 100% no-repeat; }
.pin i::before { content: ""; position: absolute; inset: -1px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.7); opacity: 0; animation: ping 3.2s cubic-bezier(0.2, 0.7, 0.1, 1) infinite; }
@keyframes ping { 0% { transform: scale(1); opacity: 0.7; } 70%, 100% { transform: scale(3.6); opacity: 0; } }
.pin span {
  position: absolute; left: 0; bottom: 18px; transform: translateX(-50%); display: flex; align-items: baseline; gap: 0.6em;
  padding: 0.45rem 0.7rem 0.45rem; border-radius: 8px; white-space: nowrap;
  background: linear-gradient(#17181b, #0c0d0f); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 6px 14px rgba(0, 0, 0, 0.7);
  color: var(--ink); font-size: 0.625rem; font-weight: 600; letter-spacing: 0.1em; line-height: 1;
}
/* The label carries the piano itself: the 3D piano, drawn once as a small
   picture (--piano-pin, piano/main.js), whole and shut, set straight on the
   label's own metal with nothing behind it, with the place's name and its
   votes beneath. With the picture the label is
   nearly square; without it (no WebGL) it is the name and votes alone. */
.pin span b { display: none; width: 104px; height: 76px; background: var(--piano-pin) center / contain no-repeat; }
.pin-ready .pin span { display: grid; justify-items: center; gap: 0.4rem; padding: 0.5rem 0.75rem 0.6rem; border-radius: 12px; }
.pin-ready .pin span b { display: block; margin-bottom: 0.1rem; }
.pin span strong { font-weight: 600; }
/* Near the top of the plate there is no room above the point, and the label
   hangs beneath it instead (.below, location.js). */
.pin.below span { bottom: auto; top: 18px; }
.pin.below span::after { top: auto; bottom: 100%; }
/* Only the chosen place carries the label; the others carry their name on a
   small tag beside the point (to one side or the other, so that two places
   close together do not cover each other), and can be pressed. */
.pin:not(.on) span { display: none; }
.pin small { position: absolute; left: 14px; top: -0.6rem; padding: 0.3rem 0.5rem; border-radius: 6px; white-space: nowrap; background: rgba(8, 9, 10, 0.82); color: var(--dim); font-size: 0.5625rem; font-weight: 600; letter-spacing: 0.1em; line-height: 1; transition: color 0.2s; }
.pin[data-place="the-maine-mayfair"] small { left: auto; right: 14px; }
.pin.on small { display: none; }
.pin:not(.on) i, .pin:not(.on) small { pointer-events: auto; cursor: pointer; }
.pin:not(.on) i::before { animation: none; }
@media (hover: hover) { .pin:not(.on):hover small { color: var(--ink); } }
/* The plate's stem: a hairline from the name down to the point. */
.pin span::after { content: ""; position: absolute; left: 50%; top: 100%; width: 1px; height: 9px; background: linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.15)); }
.map figcaption { position: absolute; left: 0; right: 0; top: calc(100% + 0.7rem); display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.35rem 1rem; white-space: nowrap; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.map figcaption span { transition: opacity 0.35s var(--ease); }
.map figcaption span.out { opacity: 0; }
.map figcaption a { color: var(--quiet); font: 300 0.625rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; text-decoration: none; transition: color 0.2s; }
.map figcaption a:hover, .map figcaption a:focus-visible { color: #fff; outline: none; }
@media (max-width: 900px) {
  .vote { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .leaderboard header, .candidate { padding-left: 0.9rem; padding-right: 0.9rem; }
  .candidate { grid-template-columns: 0.9rem 5rem minmax(0, 1fr); gap: 0 0.8rem; }
  /* Stacked, the column is as tall as it needs, and the line under the button takes its own room. */
  .ballot-act { display: grid; gap: 0.7rem; }
  .ballot-act p { position: static; }
  .preview { margin-top: 2.5rem; }
  /* On a phone the plate is square; the map starts nearer, with the pin near the middle (location.js). */
  /* Room beneath it for its caption, which takes two lines here: the address, then the map's credit. */
  .map { aspect-ratio: 1; margin-bottom: 2.75rem; }
}
@media (prefers-reduced-motion: reduce) {
  .map-land, html[data-page="membership"] .map-land.on, html[data-page="membership"] .map.moved .map-land.on,
  .map .road.b, .map .road.c, .map text.near, .pin small, .pin, html[data-page="membership"] .map-land.on .pin, html[data-page="membership"] .map.moved .map-land.on .pin, .map figcaption span, .candidate-photo, .rank, .candidate h3, .candidate .tally output, .candidate, .candidate-body { transition: none; }
  .pin i::before { animation: none; }
}

/* Phones: the menu fits inside its bar, the two buttons share one row. */
@media (max-width: 600px) {
  /* The name and the wallet share the bar. The menu is a row at the foot of
     the window, where a thumb reaches it, made as an app's is: five equal
     places, each a sign over its name, the whole place the target, the piano
     at the middle. The page you are on is in the first ink, the rest in the
     second; no line under it. MEMBERSHIP is named KEY, for what is bought
     on it. MEMBERS, the members' room, is in this menu alone.
     An app has no footer, so on a phone the footer's words are gone and its
     box is kept, empty, as the menu's ground: its black or white runs up
     behind the menu, and everything that keeps clear of the footer (--foot,
     now the menu's height) keeps clear of the menu. */
  .bar { grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem 0.25rem; padding-top: 0.9rem; }
  .bar > nav {
    position: fixed; left: 0; right: 0; bottom: 0; justify-self: stretch; align-self: stretch;
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0;
    height: calc(var(--tabs) + env(safe-area-inset-bottom));
    padding: 0 max(0.25rem, env(safe-area-inset-right)) env(safe-area-inset-bottom) max(0.25rem, env(safe-area-inset-left));
    font-size: 0.5625rem; letter-spacing: 0.08em;
  }
  /* The tracking after the last letter is taken back, so the name sits
     centred under its sign. */
  .bar > nav a, .bar > nav a[href="/members"] { display: grid; justify-items: center; align-content: center; gap: 0.3rem; height: 100%; padding: 0 0 0 0.08em; line-height: 1.2; white-space: nowrap; -webkit-tap-highlight-color: transparent; }
  /* Records, the key, the piano, its members, live. */
  .bar > nav .full { display: none; }
  .bar > nav .short { display: block; }
  .bar > nav a[href="/records"] { order: 1; }
  .bar > nav a[href="/membership"] { order: 2; }
  .bar > nav a[href="/"] { order: 3; }
  .bar > nav a[href="/members"] { order: 4; }
  .bar > nav a[href="/live"] { order: 5; }
  /* The sign: its ring is 0.76 of the face's em, so the face is set at the
     size that makes the ring 24px; the ring is centred in its line. The
     name's tracking does not reach it. */
  .bar > nav .sign { display: block; height: 1.5rem; margin-left: -0.045rem; font: 400 calc(1.5rem / 0.76)/1.5rem "Signs", "STIX Two Math", "Apple Symbols", sans-serif; letter-spacing: 0; }
  .bar > nav a[aria-current]::after { display: none; }
  .bar > nav a:focus-visible { outline: 1px solid currentColor; outline-offset: -6px; border-radius: 10px; }
  .foot { padding-top: var(--tabs); padding-bottom: env(safe-area-inset-bottom); }
  .foot > * { display: none; }
  /* The bar only fades when everything is hidden: moved, it would carry the
     menu at the foot of the window up with it. */
  .bare .bar { transform: none; }
  .bar .bar-end .wallet { padding: 0 1rem; }
  .bar .wallet .long { display: none; }
  .bar .wallet .short { display: inline; }
  .deck { gap: 0.5rem; }
  .deck .pack-button { padding: 0 1.5rem; font-size: 0.8125rem; letter-spacing: 0.06em; }
}

/* RAISED opens the allocation: the cell is a button made like KEY HOLDERS,
   with the same round arrow at its right. */
button.stat.raised { margin: 0; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; transition: background 0.25s; }
button.stat.raised:focus-visible { background: rgba(255, 255, 255, 0.035); outline: none; }

/* The allocation sheet: a dial and its legend. The dial is gunmetal cut into
   the sheet, in a baked chrome rim; membership.js draws the shares and a
   scale inside it, dark until paid, lit from the top round to what has been
   raised, in the order it is spent. */
/* The whole sheet is seen at once: it never scrolls. Its air is measured
   from the window's height (--g, nothing at 720px tall, all of it by
   1165px), so a tall window gets wide margins, a title clear of what it
   heads and deep rows, and a short one the same sheet drawn closer. */
.alloc-body { --g: clamp(0px, (100dvh - 720px) * 0.036, 16px); width: min(100%, 65rem); max-height: calc(100dvh - 2rem); }
/* The head: the mark at the top left, the title beside it. The mark is the
   bar's own, live chrome (mark.js), drawn here as a ring round a Geist > until
   it is; it takes no more height than the title's line. With a share opened
   (#allocation.one) it has turned to face left and is the way back. */
.alloc-top { display: flex; align-items: center; gap: 0.875rem; margin-bottom: calc(0.5rem + var(--g) * 0.6); }
.alloc-body .alloc-top h2 { margin: 0; }
#allocation button.alloc-go {
  position: relative; flex: none; display: grid; place-items: center; width: 1.75rem; height: 1.75rem; margin: -0.25rem 0; padding: 0; box-sizing: border-box;
  border: 1.5px solid currentColor; border-radius: 50%; background: none; color: #fff; letter-spacing: 0; cursor: default; overflow: hidden;
}
#allocation.one button.alloc-go { cursor: pointer; }
.alloc-go::after { content: ""; position: absolute; inset: -0.375rem; border-radius: 50%; }
.alloc-go span { font: 700 1.55rem/1 "Geist Mark", sans-serif; transform: translate(0.05em, -0.04em); transition: transform 0.9s cubic-bezier(0.65, 0, 0.35, 1); }
#allocation.one .alloc-go span { transform: translate(-0.05em, -0.04em) rotateY(180deg); }
#allocation button.alloc-go.live { width: 2.25rem; height: 2.25rem; margin: -0.5rem 0 -0.5rem -0.125rem; border: 0; overflow: visible; }
.alloc-go.live span { display: none; }
.alloc-go canvas { width: 100%; height: 100%; display: block; }
@media (prefers-reduced-motion: reduce) { .alloc-go span { transition: none; } }
.alloc-body > .dim { margin: 0; }
@media (min-width: 721px) {
  .alloc-body { padding: calc(1.75rem + var(--g) * 2) 3.25rem calc(1.25rem + var(--g) * 1.5); }
  .alloc-body .rip-close { top: calc(1.2rem + var(--g) * 2 + 2px); right: 3.25rem; }
}
/* The dial and the figures it sums to stand together at the left, level with
   the first share; the
   breakdown is the right. On a phone the dial comes first, then its figures,
   then the shares. */
.alloc { display: grid; grid-template-columns: 17rem minmax(0, 1fr); align-items: start; gap: 4.5rem; margin: calc(1.25rem + var(--g) * 2.5) 0 calc(0.5rem + var(--g) * 2.3); }
.alloc-dial {
  --d: 14rem;
  position: relative; width: var(--d); height: var(--d); border-radius: 50%; margin: 6px;
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='80' height='80'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='1.4' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .07 0'/></filter><rect width='80' height='80' filter='url(%23g)'/></svg>"),
    linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%);
  box-shadow: inset 0 6px 12px rgba(0, 0, 0, 0.85), inset 0 1px 2px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07);
}
.alloc-dial::after { content: ""; position: absolute; inset: -6px; border-radius: 50%; pointer-events: none; box-shadow: 0 0 0 1px #3a3b41; }
.ring-ready .alloc-dial::after { box-shadow: none; background: var(--ring-chart) center / 100% 100% no-repeat; }
/* The rim and the scale in the mark's own chrome (bakeDial, mark.js); over
   them the scale again in coloured chrome, uncovered round to what is paid,
   and the needle, turned to it. */
.alloc-dial.chromed::after { box-shadow: none; background: var(--dial-chrome) center / 100% 100% no-repeat; }
.alloc-lit, .alloc-needle { position: absolute; inset: -6px; z-index: 1; pointer-events: none; background: center / 100% 100% no-repeat; }
.alloc-lit { -webkit-mask: linear-gradient(transparent, transparent); mask: linear-gradient(transparent, transparent); }
.alloc-needle { z-index: 2; }
/* The needle's reading, riding just outside the rim at its tip. */
.alloc-pct {
  position: absolute; z-index: 2; translate: -50% -50%; opacity: 0; pointer-events: none;
  color: #fff; font: 550 0.6875rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.02em;
  transition: opacity 0.35s var(--ease);
}
.alloc-pct.shown { opacity: 1; }
.alloc-needle[hidden] { display: none; }
.alloc-ring { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.alloc-ring line { stroke-width: 1; stroke-linecap: butt; }
.alloc-ring .needle { stroke: #fff; }
/* The bezel round the figures, inside the scale. */
/* Inside the scale the gunmetal gives way to plain black, and the mark turns
   there, live chrome (mark.js), its ring the edge of the black; without WebGL
   a drawn ring round a Geist Bold > stands in. */
.alloc-core::before { content: ""; position: absolute; left: 50%; top: 50%; width: 152px; height: 152px; translate: -50% -50%; border-radius: 50%; background: #000; pointer-events: none; }
.alloc-core { position: absolute; inset: 0; display: grid; place-items: center; }
.alloc-mark { position: relative; display: grid; place-items: center; width: 156px; height: 156px; }
.alloc-mark::before { content: ""; position: absolute; inset: 2px; border: 3px solid #c9cbd1; border-radius: 50%; }
.alloc-mark.live::before { display: none; }
.alloc-mark span { font: 700 5rem/1 "Geist Mark", sans-serif; color: #c9cbd1; transform: translate(0.05em, -0.04em); }
.alloc-mark.live span { display: none; }
.alloc-mark canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.alloc-side .am b small.sol { margin-left: 0.3em; color: var(--dim); font-size: 0.6em; font-weight: 500; letter-spacing: 0.02em; }

/* The legend: one row a share, its tone at the left, a hairline beneath that
   fills in Solana's colours as the share is paid for. */
/* The dial, and under it the status: what has been raised so far. */
.alloc-left { display: grid; justify-items: center; gap: calc(0.875rem + var(--g) * 1.6); padding-top: calc(0.75rem + var(--g) * 0.7); }
.alloc-left .alloc-members { width: 100%; margin: 0; padding-top: calc(0.75rem + var(--g) * 1.5); border-top: 1px solid var(--line); gap: calc(0.3rem + var(--g) * 0.6) 0.75rem; line-height: 1.5; }
.alloc-left .alloc-members span { flex-basis: 100%; }
.alloc-status { display: grid; justify-items: center; gap: 0.5rem; margin: 0; text-align: center; }
/* The reading is live: its lamp beats once a second, the slot and the age of
   the reading are set under it in the footer's face, and the figure is lit
   for a moment when it moves. */
.alloc-status span { display: inline-flex; align-items: center; gap: 0.5rem; }
.alloc-status .led.beat { animation: beat 1s var(--ease) infinite; }
@keyframes beat { 0% { box-shadow: 0 0 0 0 rgba(20, 241, 149, 0.55); } 70%, 100% { box-shadow: 0 0 0 5px rgba(20, 241, 149, 0); } }
.alloc-status small { margin-top: 0.35rem; color: #5d5f66; font: 400 0.625rem/1.3 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
.alloc-status small:empty { display: none; }
.alloc-status b.moved { animation: moved 1.2s var(--ease); }
@keyframes moved { 0% { color: #14f195; } 100% { color: #fff; } }
@media (prefers-reduced-motion: reduce) { .alloc-status .led.beat, .alloc-status b.moved { animation: none; } }
/* The piano in the dial's middle, inside the black of its core. */
.alloc-piano { position: relative; display: block; width: 172px; height: auto; }
/* Behind it the core is lit a little from above, so the black piano stands off it. */
#allocation .alloc-core::before { background: radial-gradient(circle at 50% 38%, #2a2c33 0%, #131418 55%, #060607 100%); }
.alloc-status span { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.alloc-status b { display: inline-flex; align-items: baseline; color: #fff; font: 550 1.125rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; }
.alloc-status b small.sol { margin-left: 0.3em; color: var(--dim); font-size: 0.6em; font-weight: 500; letter-spacing: 0.02em; }
.alloc-status em { color: var(--dim); font: 400 0.75rem/1.3 "Fira Code", ui-monospace, monospace; font-style: normal; font-variant-numeric: tabular-nums; }
.alloc-side { min-width: 0; }
.alloc-list { list-style: none; margin: 0; padding: 0; display: grid; }
.alloc-list li {
  display: grid; grid-template-columns: 8px minmax(0, 1fr) auto 7.5rem; grid-template-areas: "chip nm pc am" "chip nm gt am" ". ln ln ln";
  column-gap: 1rem; row-gap: 0.3rem; align-items: baseline; padding: calc(0.45rem + var(--g)) 0 calc(0.35rem + var(--g)); border-top: 1px solid rgba(255, 255, 255, 0.06);
  outline: none; transition: opacity 0.25s;
}
.alloc-list li:first-child { border-top: 0; padding-top: calc(0.1rem + var(--g) * 0.4); }
/* A row opens: pressed, its share takes the list's place (membership.js).
   A small chevron after its name points the way in. */
.alloc-list li { cursor: pointer; }
.alloc-list li > .nm b::after { content: ""; display: inline-block; width: 5px; height: 5px; margin-left: 0.7em; border-right: 1.5px solid var(--dim); border-bottom: 1.5px solid var(--dim); transform: translateY(-1px) rotate(-45deg); }
/* The share opened. It lies over the list, in the same place and no bigger,
   so the sheet keeps its size: the way back, the share's row as it stood in
   the list, and its parts, each with its cost. The list fades out under it. */
.alloc { position: relative; }
/* What comes in can be reached at once (visibility is not delayed), so the
   keyboard's focus can go to it; what goes out is out of reach once faded. */
.alloc-list, .alloc-total, .alloc-rate, .alloc-left, .alloc-side.one .alloc-one { transition: opacity 0.3s var(--ease), visibility 0s; }
.alloc-one, .alloc-side.one .alloc-list, .alloc-side.one .alloc-total, .alloc-side.one .alloc-rate, .alloc.one .alloc-left { transition: opacity 0.3s var(--ease), visibility 0s 0.3s; }
.alloc-side.one .alloc-list, .alloc-side.one .alloc-total, .alloc-side.one .alloc-rate { opacity: 0; visibility: hidden; }
.alloc-one { position: absolute; top: 0; right: 0; bottom: 0; left: calc(17rem + 4.5rem); display: grid; align-content: start; overflow: auto; opacity: 0; visibility: hidden; scrollbar-width: none; }
.alloc-side.one .alloc-one { opacity: 1; visibility: visible; }
.alloc-head {
  display: grid; grid-template-columns: 8px minmax(0, 1fr) auto 7.5rem; grid-template-areas: "chip nm pc am" "chip nm gt am";
  column-gap: 1rem; row-gap: 0.3rem; align-items: baseline; padding: calc(0.1rem + var(--g) * 0.4) 0 calc(0.6rem + var(--g));
}
.alloc-head .chip { --fill: 1; }
.alloc-items p { display: flex; justify-content: space-between; align-items: baseline; gap: 1.5rem; margin: 0; padding: calc(0.3rem + var(--g) * 0.25) 0; border-top: 1px solid rgba(255, 255, 255, 0.06); color: var(--dim); font: 400 0.75rem/1.35 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.alloc-items p b { color: var(--ink); font-weight: 500; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { #allocation .alloc-list, #allocation .alloc-total, #allocation .alloc-rate, #allocation .alloc-left, #allocation .alloc-one { transition: none; } }
/* The total: a row made like the shares, but larger, under a bright rule, as
   a table's sum is. */
.alloc-total {
  display: grid; grid-template-columns: 8px minmax(0, 1fr) auto 7.5rem; grid-template-areas: "chip nm pc am" "chip nm gt am";
  column-gap: 1rem; row-gap: 0.35rem; align-items: baseline; margin-top: calc(0.25rem + var(--g) * 0.5); padding: calc(0.9rem + var(--g)) 0 calc(0.5rem + var(--g));
  border-top: 1px solid rgba(255, 255, 255, 0.28);
}
.alloc-side .alloc-total .nm b { font-size: 0.875rem; }
.alloc-side .alloc-total .pc { font-size: 1.25rem; }
.alloc-side .alloc-total .am b { font-size: 1.125rem; font-weight: 550; }
.alloc-total:empty { display: none; }
/* The total's LED: Solana's whole gradient up its full height, purple at the
   foot, lit in full. */
.alloc-side .alloc-total .chip::before {
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 40%, rgba(0, 0, 0, 0.2)),
    linear-gradient(to bottom, color-mix(in srgb, #5497d5 22%, #000) calc((1 - var(--fill, var(--got))) * 100%), transparent 0),
    linear-gradient(to top, #9945ff, #5497d5, #14f195);
}
/* The inks, the same as the dial's (OFF in membership.js): unpaid
   is very dark in its colour; a row pointed at lights whole, its LED and its
   line full, as its share does on the dial (the total lights every row);
   while one row is, the others dim and their progress goes dark. */
.alloc-side { --off: 8%; }
/* A share not yet funded stands back until it is pointed at. */
.alloc-list li.queued { opacity: 0.5; }
.alloc-list li.queued:focus-visible, .alloc-side:has(.alloc-total:focus-visible) li.queued { opacity: 1; }
@media (hover: hover) { .alloc-list li.queued:hover, .alloc-side:has(.alloc-total:hover) li.queued { opacity: 1; } }
.alloc-total { outline: none; }
@media (hover: hover) {
  .alloc-list li:hover, .alloc-side:has(.alloc-total:hover) li { --fill: 1; }
  .alloc-list:has(li:hover) li:not(:hover), .alloc-side:has(li:hover) .alloc-total { opacity: 0.3; --fill: 0; }
}
.alloc-list li:focus-visible, .alloc-side:has(.alloc-total:focus-visible) li { --fill: 1; }
.alloc-list:has(li:focus-visible) li:not(:focus-visible), .alloc-side:has(li:focus-visible) .alloc-total { opacity: 0.3; --fill: 0; }
/* Each share's LED: a capsule of chrome round a gunmetal well, the lens in
   the share's colour, dark where unpaid and lit from the bottom up to how much
   of the share is paid: full strength, with a thin hot core down its middle. */
.alloc-side .chip {
  grid-area: chip; align-self: stretch; position: relative; margin: 0.1rem 0; border-radius: 4px;
  background: linear-gradient(#050506, #141517);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.8), 0 0 0 1px #3a3b41;
}
.alloc-side .chip::before {
  content: ""; position: absolute; inset: 2px; border-radius: 2px;
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 40%, rgba(0, 0, 0, 0.2)),
    linear-gradient(90deg, var(--led) 30%, color-mix(in srgb, var(--led) 72%, #fff) 50%, var(--led) 70%) bottom / 100% calc(var(--fill, var(--got)) * 100%) no-repeat,
    color-mix(in srgb, var(--tone) var(--off), #000);
}
.alloc-side .chip::after { content: ""; position: absolute; inset: 0; border-radius: 4px; pointer-events: none; }
.ring-ready .alloc-side .chip { box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.8); }
.ring-ready .alloc-side .chip::after { border: 4px solid transparent; border-image: var(--ring-led) var(--ring-led-slice) / 4px stretch; }
.alloc-side .nm { grid-area: nm; display: grid; gap: 0.2rem; min-width: 0; }
.alloc-side .nm b { color: #fff; font-weight: 500; font-size: 0.75rem; letter-spacing: 0.1em; }
.alloc-side .nm em, .alloc-side .am em, .alloc-side .got { color: var(--dim); font: 400 0.75rem/1.3 "Fira Code", ui-monospace, monospace; font-style: normal; font-variant-numeric: tabular-nums; }
.alloc-side .pc { grid-area: pc; justify-self: end; color: #fff; font: 550 1rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.alloc-side .got { grid-area: gt; justify-self: end; white-space: nowrap; }
.alloc-side .pc small { color: var(--dim); font: 400 0.6875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0; }
/* Every membership, above the dial and the table. */
.alloc-members { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.75rem; margin: 0.75rem 0 0; font: 400 0.75rem/1.3 "Fira Code", ui-monospace, monospace; }
.alloc-members b { color: #fff; font-weight: 500; font-size: 0.6875rem; letter-spacing: 0.1em; }
.alloc-members span { color: var(--dim); }
/* The budget and the price it asks, each on a line of its own under the count. */
.alloc-members span:nth-of-type(n+2) { flex-basis: 100%; }
.alloc-members span:nth-of-type(2) { color: var(--ink); }
/* The rate the pounds are reckoned at, small, under the total. */
.alloc-rate { margin: calc(0.5rem + var(--g) * 0.5) 0 0; color: var(--dim); font: 400 0.6875rem/1.3 "Fira Code", ui-monospace, monospace; text-align: right; }
.alloc-rate:empty { display: none; }
.alloc-side .am { grid-area: am; display: grid; justify-items: end; gap: 0.2rem; }
.alloc-side .am b { display: inline-flex; align-items: baseline; color: #fff; font: 500 0.875rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
/* The sign-off: the line, "music offline, infrastructure onchain", with the
   name beside it, small and quiet. */
.alloc-sign { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; padding-top: calc(0.75rem + var(--g)); border-top: 1px solid var(--line); }
.alloc-sign p { margin: 0; color: #fff; font: 400 1.125rem/1.2 "Fira Code", ui-monospace, monospace; letter-spacing: -0.01em; }
/* Beside it, as at the foot of the page: POSSIBLE WITH SOLANA and its mark. */
.alloc-sign small { display: inline-flex; align-items: center; gap: 0.5rem; color: var(--ink); font: 600 0.5625rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; }
.alloc-sign small svg { width: 13px; height: 11.6px; }
/* On a phone the sheet is still one screen. The dial is drawn at half size
   with the reading beside it, the note on the memberships under both, and
   each share is one line of a table: its name, its part of the budget, its
   cost. The rows take what height the screen has left (--r). Pressing a row
   opens it over the whole of this, the dial too: what it is, how far it is
   funded, the SOL, and its parts. */
@media (max-width: 720px) {
  #allocation { padding: 0.5rem; }
  .alloc-body { --r: clamp(1.5rem, 1.5rem + (100dvh - 660px) * 0.125, 2.875rem); max-height: calc(100dvh - 1rem); padding: 1.5rem 1rem 1.125rem; }
  .alloc-body .rip-close { top: calc(0.95rem + 2px); right: 1rem; }
  .alloc-top { gap: 0.625rem; margin-bottom: 0.5rem; }
  .alloc { grid-template-columns: minmax(0, 1fr); gap: 0.625rem; margin: 0.875rem 0 0.625rem; }
  .alloc-left { grid-template-columns: auto minmax(0, 1fr); align-items: center; justify-items: start; gap: 0.75rem 1.25rem; width: 100%; padding-top: 0; }
  .alloc-left .alloc-dial { zoom: 0.5; }
  .alloc-left .alloc-pct { display: none; }
  .alloc-status { justify-items: start; gap: 0.35rem; text-align: left; }
  .alloc-status small { margin-top: 0.15rem; }
  .alloc-left .alloc-members { grid-column: 1 / -1; padding-top: 0.625rem; gap: 0.2rem 0.75rem; font-size: 0.6875rem; line-height: 1.35; }
  .alloc-side { justify-self: stretch; }
  .alloc-list li, .alloc-total { grid-template-columns: 8px minmax(0, 1fr) auto 4.75rem; column-gap: 0.75rem; row-gap: 0; align-items: center; }
  .alloc-list li, .alloc-list li:first-child { padding: calc((var(--r) - 1rem) / 2) 0; }
  .alloc-total { margin-top: 0; padding: calc((var(--r) - 0.75rem) / 2) 0; }
  .alloc-side .chip { margin: 0; min-height: 0.75rem; }
  .alloc-list .pc small, .alloc-total .pc small, .alloc-total .nm em, .alloc-total .am em, .alloc-total .got,
  .alloc-list .nm em, .alloc-list .am em, .alloc-list .got { display: none; }
  .alloc-one { left: 0; }
  .alloc.one .alloc-left { opacity: 0; visibility: hidden; }
  .alloc-head { grid-template-columns: 8px minmax(0, 1fr) auto; grid-template-areas: "chip nm pc" "chip nm gt" "chip am am"; column-gap: 0.75rem; padding: 0 0 0.75rem; }
  .alloc-head .chip { margin: 0.1rem 0; }
  .alloc-head .am { display: flex; justify-content: flex-start; align-items: baseline; gap: 0.6rem; margin-top: 0.3rem; }
  .alloc-items p { gap: 1rem; padding: 0.25rem 0; font-size: 0.6875rem; line-height: 1.3; }
  /* Closed, a row is one line: the share stands level with its name and cost. */
  .alloc-list .pc, .alloc-total .pc { grid-area: 1 / 3 / 3 / 4; }
  .alloc-list .nm b { font-size: 0.6875rem; line-height: 1.2; }
  .alloc-list .pc, .alloc-list .am b { font-size: 0.8125rem; }
  .alloc-side .alloc-total .pc { font-size: 0.9375rem; }
  .alloc-side .alloc-total .am b { font-size: 1rem; }
  .alloc-rate { margin-top: 0.375rem; }
  .alloc-sign { flex-wrap: nowrap; padding-top: 0.625rem; }
  .alloc-sign p { font-size: 0.8125rem; white-space: nowrap; }
}
@media (prefers-reduced-motion: reduce) { .alloc-list li, .alloc-total, .alloc-pct { transition: none; } }

/* MEMBERSHIP: a members' club, one page in two states, the door and the
   room (sessions.js sets data-member from the wallet). At the door: a stage
   where the card stands, the terms beside it, the raise and the way in;
   choosing a term brings its object up on the stage in the card's place.
   Under it, who is in. In the room: the card, the member's wallet, their
   booking and sessions. The page scrolls as any page does. */
.page-body.mem { width: min(100%, 72rem); }
.page[data-for="membership"]:not([data-member="holder"]) .mem-room,
.page[data-for="membership"][data-member="holder"] .mem-door { display: none; }
/* The preview's stand-in wallets (a local page only) float at the foot. */
.page[data-for="membership"] .preview { position: fixed; left: 50%; bottom: calc(var(--foot, 2.3rem) + 0.25rem); z-index: 3; margin: 0; transform: translateX(-50%); }

/* The head: the stage on the left, and what is said beside it. Every
   object lies in the same place on the stage, one over another, so it never
   changes size; the one chosen is seen, and comes up as the last one goes.
   The card is turned a little, as a card lies on a table. Under the stage,
   in one place too, the chosen object's way through to its own page. */
/* The top of the page is one screen, as a trading platform's is: the stage
   and the terms with BUY A KEY fill the room between the bar and the footer,
   and Who's in waits below it, never seen with it. */
.mem-hero {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: center; align-content: center; gap: clamp(1rem, 3vh, 2.5rem) 5rem;
  box-sizing: border-box; min-height: calc(100vh - var(--bar) - 2.5rem - var(--foot, 2.3rem));
  min-height: calc(100dvh - var(--bar) - 2.5rem - var(--foot, 2.3rem));
}
.mem-stage { display: grid; justify-items: center; gap: 0.5rem; min-width: 0; scroll-margin-top: calc(var(--bar) + 1rem); }
/* (On a short window the stage is drawn smaller, so the title, the stage and the way in stay on one screen.) */
.mem-objs { --shape: 6 / 5; display: grid; place-items: center; width: min(100%, 69.6vh); min-height: 0; aspect-ratio: var(--shape); }
.mem-obj { grid-area: 1 / 1; display: grid; place-items: center; width: 100%; min-width: 0; opacity: 0; visibility: hidden; transform: translateY(0.75rem); transition: opacity 0.35s var(--ease), transform 0.5s var(--ease), visibility 0s 0.35s; }
.mem-obj.on { opacity: 1; visibility: visible; transform: none; transition: opacity 0.5s var(--ease) 0.15s, transform 0.6s var(--ease) 0.15s, visibility 0s; }
/* BUY A KEY, the full width of the terms, and under it, as wide, the way
   through for the object on the stage. The ways lie one over another; the
   one for the chosen object is seen, so the button changes in place. */
.mem-pair { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.75rem; width: 100%; max-width: 33rem; }
.mem-pair .offer { display: block; }
.mem-pair .offer[hidden] { display: none; }
.mem-pair :is(.offer, .mem-ways) .pack-button { width: 100%; height: 2.75rem; padding: 0 0.75rem; font-size: 0.75rem; letter-spacing: 0.08em; text-decoration: none; }
.mem-ways { display: grid; grid-template-columns: minmax(0, 1fr); }
.mem-ways .pack-button { grid-area: 1 / 1; color: var(--dim); opacity: 0; visibility: hidden; transition: opacity 0.35s var(--ease), visibility 0s 0.35s, color 0.2s; }
.mem-ways .pack-button.on { opacity: 1; visibility: visible; transition: opacity 0.5s var(--ease) 0.15s, visibility 0s, color 0.2s; }
.mem-ways .pack-button:focus-visible { color: var(--ink); outline: none; }
@media (hover: hover) { .mem-ways .pack-button:hover { color: var(--ink); } }
/* The two that are links wear the same capsule as the buttons. */
.ring-ready .mem-ways a.chrome::before { border-width: 1.375rem; border-image: var(--ring-pill) 50% 22.32% stretch; }
@media (hover: hover) { .ring-ready .mem-ways a.chrome:hover::before { filter: brightness(1.3); } }
.ring-ready .mem-ways a.chrome:focus-visible::before { filter: brightness(1.3); }
@media (prefers-reduced-motion: reduce) { .mem-obj, .mem-obj.on, .mem-ways .pack-button, .mem-ways .pack-button.on { transition: none; transform: none; } }
.mem-say { min-width: 0; display: grid; justify-items: start; gap: clamp(1rem, 2.6vh, 2rem); }
.mem-say > * { margin: 0; }
.page .mem-say h1, .mem h2 { color: var(--ink); font-family: "Fira Code", ui-monospace, monospace; font-weight: 400; letter-spacing: -0.02em; text-transform: none; }
.page .mem-say h1 { margin: 0; font-size: clamp(2.5rem, 4.4vw, 3.75rem); line-height: 1; }
/* The head of the panel, over the names and the terms: what is bought, in
   capitals, cast in chrome (chrome-type.js) and set as the RECORDS titles
   are; under it the line, "music offline, infrastructure onchain". */
.mem-head { display: grid; justify-items: start; gap: 0.75rem; width: 100%; max-width: 33rem; }
.mem-head > * { margin: 0; }
/* (Its chrome is lifted a little, to stand off the black.) */
.mem-title canvas { filter: brightness(1.3); }
.page .mem-say h1.mem-title { color: transparent; font: 500 clamp(1.125rem, 1.6vw, 1.375rem)/1.2 "Fira Code", ui-monospace, monospace; letter-spacing: -0.01em; text-transform: none; white-space: nowrap; }
.mem-say .mem-line { max-width: 26rem; color: var(--ink); font: 400 0.8125rem/1.5 "Fira Code", ui-monospace, monospace; letter-spacing: -0.01em; text-wrap: balance; }
@media (max-width: 900px) {
  .page .mem-say h1.mem-title { font-size: min(1.125rem, 4.9vw); }
  .mem-head { gap: 0.4rem; }
  .mem-say .mem-line { font-size: 0.8125rem; }
}
}
.mem-say p { max-width: 28rem; color: var(--dim); font-size: 1rem; line-height: 1.6; }
.mem-say .pass-note { font-size: 0.75rem; margin-top: -1.25rem; }
.mem-acts { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.mem-acts .pack-button { height: 2.75rem; padding: 0 2rem; font-size: 0.75rem; letter-spacing: 0.08em; display: inline-grid; place-items: center; text-decoration: none; }
/* The terms: a label and its value to a line, the value at the right, the
   lines cut between them as a groove is: dark above, a catch of light below. */
/* The terms are the chosen object's: a list for each lies in one place, so
   the column keeps the height of the longest, and the chosen one is seen. */
/* The venue's map on the stage: the plate at its own shape, whatever the
   width, with room under it for its credit. */
.mem-obj .map { width: 100%; aspect-ratio: 1120 / 794; margin-bottom: 1.6rem; }
.mem-terms { display: grid; align-items: start; width: 100%; max-width: 33rem; }
.terms { grid-area: 1 / 1; display: grid; width: 100%; margin: 0; opacity: 0; visibility: hidden; transition: opacity 0.35s var(--ease), visibility 0s 0.35s; }
.terms.on { opacity: 1; visibility: visible; transition: opacity 0.5s var(--ease) 0.15s, visibility 0s; }
@media (prefers-reduced-motion: reduce) { .terms, .terms.on { transition: none; } }
.terms div { display: flex; align-items: baseline; justify-content: space-between; gap: 2rem; padding: 0.85rem 0; box-shadow: inset 0 1px 0 #000, inset 0 2px 0 rgba(255, 255, 255, 0.06); }
/* On a short window the rows draw closer, so the terms and BUY A KEY stay on one screen. */
@media (min-width: 901px) { .terms div { padding: clamp(0.4rem, 3.2vh - 0.95rem, 0.85rem) 0; } }
.terms div:first-child { box-shadow: none; padding-top: 0; }
.terms div[hidden] { display: none; }
/* A data sheet (the instrument's): the same lines set close, in the footer's
   face, in two columns, read down the first and then the second. */
.terms.spec { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(8, auto); grid-auto-flow: column; column-gap: 2rem; }
.terms.spec div, .terms.spec div:first-child { gap: 0.75rem; padding: 0.4rem 0; box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.06); }
.terms.spec dt, .terms.spec dd { font: 400 0.625rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.04em; }
@media (max-width: 900px) { .terms.spec { column-gap: 1rem; } .terms.spec dt, .terms.spec dd { font-size: 0.5625rem; letter-spacing: 0.02em; } }
/* A term's name and its value are set as the names over them are: small
   capitals with tracking, the name in the second ink and the value in the
   first. (The site's own name keeps its lowercase.) */
.terms dt, .terms dd { font: 500 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; text-transform: uppercase; }
.terms dt { flex: none; color: var(--dim); }
.terms dd { margin: 0; color: var(--ink); text-align: right; }
.terms .as-is { text-transform: none; letter-spacing: 0.04em; }
/* A value that leads out: the same ink, an arrow after it, a hairline under it when pointed at. */
.terms dd a { color: inherit; text-decoration: none; background: linear-gradient(currentColor, currentColor) 0 100% / 0 1px no-repeat; transition: background-size 0.4s var(--ease); }
.terms dd a i { margin-left: 0.5em; font-style: normal; color: var(--dim); }
.terms dd a:focus-visible { outline: 1px solid #fff; outline-offset: 3px; }
.terms dd a[href]:focus-visible { background-size: 100% 1px; }
@media (hover: hover) { .terms dd a[href]:hover { background-size: 100% 1px; } }
@media (prefers-reduced-motion: reduce) { .terms dd a { transition: none; } }
/* Which object stands on the stage: a row of names, the chosen one in the
   first ink with a line beneath it, as the menu marks the page in view. */
/* The names stand over the terms they choose between as a bar of tabs: set
   across the whole width of the terms, on a rule, the chosen one in the
   first ink with a bright line under it, on the rule. */
.mem-pick { display: flex; justify-content: space-between; gap: 0 0.75rem; width: 100%; max-width: 33rem; box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.16); }
.mem-pick button { position: relative; height: 2.75rem; padding: 0; border: 0; background: none; color: var(--dim); font: 600 0.6875rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.12em; white-space: nowrap; cursor: pointer; transition: color 0.2s; }
.mem-pick button[aria-pressed="true"] { color: var(--ink); }
.mem-pick button[aria-pressed="true"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: #fff; }
.mem-pick button:focus-visible { outline: 1px solid #fff; outline-offset: 2px; }
@media (hover: hover) { .mem-pick button:hover { color: var(--ink); } }
/* The room's panel: the member's next session, in a gunmetal well. */
.session { display: grid; gap: 0.8rem; width: 100%; max-width: 30rem; padding: 1.5rem 1.75rem; border-radius: 18px; background: var(--grain), linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%); box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.85), inset 0 1px 1px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07); }
.session span { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.12em; }
.session b { font: 400 1.875rem/1.1 "Fira Code", ui-monospace, monospace; letter-spacing: -0.02em; }
.session p { margin: 0; max-width: none; color: var(--dim); font-size: 0.9375rem; line-height: 1.5; }
/* The member's sessions, across the page: when, what for, and the way to give one up. */
.mine.mem-room { margin-top: 3rem; }
.mine.mem-room header { display: grid; grid-template-columns: 11rem minmax(0, 1fr) auto; gap: 1rem; justify-content: stretch; }
.mine.mem-room li { grid-template-columns: 11rem minmax(0, 1fr) auto; min-height: 2.75rem; }
.mine.mem-room li b { grid-column: 1; grid-row: 1; font: 400 0.75rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; }
.mine.mem-room li span { grid-column: 2; grid-row: 1; }
.mine.mem-room li button { grid-column: 3; grid-row: 1; color: var(--ink); }
/* The raise: a gunmetal well filled in Solana's colours as far as the keys
   sold, a tick of light where the goal is, and the count and time under it. */
.raise { width: 100%; max-width: 33rem; display: grid; gap: 0.7rem; }
.raise[hidden] { display: none; }
.raise-bar { position: relative; height: 8px; border-radius: 4px; background: linear-gradient(#050506, #141517); box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.85), inset 0 -1px 0 rgba(255, 255, 255, 0.07); }
/* The fill is the whole run of colour, cut to the share sold, so its colour
   says how far along it is; it lies 1px inside the well, its corners the
   well's less that 1px. */
.raise-bar i { position: absolute; inset: 1px; border-radius: 3px; background: linear-gradient(90deg, #9945ff, #5497d5, #14f195); clip-path: inset(0 calc((1 - var(--of, 0)) * 100%) 0 0 round 3px); }
.raise-bar b { position: absolute; top: -4px; bottom: -4px; left: calc(var(--of, 0) * 100%); width: 1px; margin-left: -0.5px; background: #fff; }
.raise-line { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.3rem 1rem; color: var(--dim); font: 400 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

/* The room's own: booking, the calendar beside what it is for and the
   member's sessions. */
.mem-book { display: grid; grid-template-columns: minmax(0, 34rem) minmax(0, 1fr); align-items: start; gap: 2.5rem 5rem; margin-top: 4rem; scroll-margin-top: calc(var(--bar) + 2rem); }
.mem-book-side { display: grid; gap: 1.25rem; max-width: 28rem; }
.mem-book-side > h2, .mem-book-side > p { margin: 0; }
.mem-book-side > p { color: var(--dim); line-height: 1.6; }
.mem-book .booker .steps { min-height: 19rem; }

/* Each object on the stage, held to its size. */
.page .mem h2 { margin: 0; font-size: clamp(1.75rem, 2.8vw, 2.375rem); line-height: 1.1; }
/* Each way through is a 44px target: more padding round the words, the
   line under them kept as near as before. */
.mem .pitch-link { padding-top: 0.9rem; padding-bottom: 0.9rem; line-height: 1.4; }
.mem .pitch-link::before { bottom: 0.55rem; }
.mem-in .pitch-link { margin-top: -0.65rem; }
/* On the stage the box's picture is the stage's own shape, so the open box has its whole height. */
.mem-obj .pitch-stage { width: 100%; aspect-ratio: var(--shape); }
/* An object's photograph fills the stage, cut about its subject. */
.mem-shot { display: block; width: 100%; }
.mem-shot img { display: block; width: 100%; height: auto; aspect-ratio: var(--shape); object-fit: cover; object-position: 50% 50%; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
/* Under the photographs, a dot for each name, as on the phone's head: the one
   shown is white. A press on one shows it; so does a swipe across the stage,
   or the arrow keys, and the terms beside it go with it. Wider windows only. */
.mem-dots { display: flex; justify-content: center; margin-top: -0.25rem; }
.mem-dots button { display: grid; place-items: center; width: 24px; height: 44px; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.mem-dots button::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: #3a3b41; transition: background-color 0.3s var(--ease); }
.mem-dots button[aria-current]::before { background: #fff; }
@media (hover: hover) { .mem-dots button:not([aria-current]):hover::before { background: var(--dim); } }
.mem-dots button:focus-visible { outline: 1px solid var(--dim); outline-offset: -4px; border-radius: 50%; }
.mem-objs.swipe { cursor: grab; touch-action: pan-y; }
.mem-objs.swipe.held { cursor: grabbing; }
@media (max-width: 600px) { .mem-dots { display: none; } }
@media (prefers-reduced-motion: reduce) { .mem-dots button::before { transition: none; } }
.mem-obj .pitch-key { width: min(100%, 21rem); }
.mem-obj .map { width: 100%; }
/* The tech rider: the equipment in 3D, turning (rig.js). It comes up once it is there. */
.mem-rig { width: 100%; margin: 0; opacity: 0; transition: opacity 0.9s var(--ease); }
.mem-rig.on { opacity: 1; }
.mem-rig canvas { display: block; width: 100%; aspect-ratio: 6 / 5; cursor: grab; touch-action: pan-y; }
.mem-rig canvas.held { cursor: grabbing; }
@media (prefers-reduced-motion: reduce) { .mem-rig { transition: none; } }
/* Our own events: the full logo in chrome, larger. */
@media (min-width: 901px) { .mem-obj .lockup .alloc-mark { width: 7.5rem; height: 7.5rem; } .mem-obj .lockup .alloc-mark span { font-size: 3.75rem; } .mem-obj .lockup b { font-size: 3.41rem; } .mem-obj .lockup { gap: 1.5rem; } }

/* Who's in: the 88 keys as a keyboard, ivory and black. A key with any of
   its places taken wears a mark in Solana's colours near its foot; a
   member's own is ringed in white; the one under the pointer is lit. */
.mem-in { display: grid; justify-items: start; gap: 1.25rem; margin-top: 5rem; }
.mem-in-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 2rem; width: 100%; }
.mem-in-read { color: var(--dim); font: 400 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; }
.mem-board { position: relative; width: 100%; height: 8rem; }
.mem-board i { position: absolute; top: 0; bottom: 0; left: calc(var(--at) * 100% / var(--whites)); width: calc(100% / var(--whites)); border-radius: 0 0 3px 3px; background: linear-gradient(#cfd0cc, #f4f3ee 70%, #e3e2dc); box-shadow: inset 0 -4px 0 #b9b8b2, 0 0 0 1px #000; }
.mem-board i.b { z-index: 1; bottom: 37%; width: calc(100% / var(--whites) * 0.6); transform: translateX(-50%); background: linear-gradient(#1b1c1f, #050506); box-shadow: inset 0 -3px 0 #2b2c31, 0 0 0 1px #000; }
.mem-board i.t::after { content: ""; position: absolute; left: 18%; right: 18%; bottom: 9px; height: 4px; border-radius: 2px; background: linear-gradient(90deg, #9945ff, #5497d5, #14f195); }
.mem-board i.b.t::after { bottom: 7px; }
.mem-board i.yours { outline: 2px solid #fff; outline-offset: -2px; z-index: 2; }
@media (hover: hover) { .mem-board i.w:hover { background: #fff; } .mem-board i.b:hover { background: linear-gradient(#3a3b41, #1b1c1f); } }
/* The holders: place, wallet, the name they gave, and their keys. */
.mem-club { list-style: none; width: 100%; margin: 0; padding: 0; color: var(--dim); font: 400 0.75rem/1.4 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.mem-club[hidden] { display: none; }
.mem-club li { display: grid; grid-template-columns: 2.5rem 8rem minmax(0, 1fr) auto; align-items: center; gap: 1rem; min-height: 2.75rem; border-top: 1px solid var(--line); }
.mem-club li:first-child { border-top: 0; }
.mem-club .who { display: flex; align-items: center; min-height: 2.75rem; color: var(--ink); text-decoration: none; }
.mem-club .who:focus-visible { outline: 1px solid #fff; outline-offset: 3px; }
.mem-club .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mem-club b { color: var(--ink); font-weight: 400; }
@media (hover: hover) { .mem-club .who:hover { text-decoration: underline; text-underline-offset: 3px; } }

/* A phone: one column, the stage over the terms, each term's label over
   its value. */
@media (max-width: 900px) {
  .mem-hero, .mem-book { grid-template-columns: minmax(0, 1fr); }
  .mem-hero { gap: 0.75rem; }
  /* A phone's stage is wider than it is tall, so the head, the names, the raise and BUY A KEY follow it on the first screen. */
  .mem-objs { --shape: 3 / 2; width: 100%; }
  .mem-obj .map { aspect-ratio: 16 / 9; }
  /* The offer comes first: under the title the raise and BUY A KEY, on the
     first screen with the object, and the terms after them. */
  .mem-say.mem-door { gap: 1rem; }
  .mem-door .raise { order: 1; }
  .mem-door .mem-pair { order: 2; }
  .mem-door .mem-terms { order: 3; margin-top: 0.75rem; }
  .mem-obj[data-obj="card"] { padding: 0 0.75rem; }
  .mem-obj .pitch-key { width: min(100%, 15rem); }
  .mem-in { margin-top: 4rem; }
  .mem-board { height: 5rem; }
  .terms div { gap: 1rem; }
  /* Six names on one line: closer set, none broken across two. */
  .mem-pick { gap: 0 0.25rem; }
  /* (Shorter here, to keep BUY A KEY on the first screen; each still takes a press 44px tall.) */
  .mem-pick button { height: 2.25rem; font-size: 0.5625rem; letter-spacing: 0.03em; }
  .mem-pick button::before { content: ""; position: absolute; inset: -0.25rem 0; }
  .mine.mem-room header, .mine.mem-room li { grid-template-columns: 8.5rem minmax(0, 1fr) auto; }
  .mem-acts .pack-button { flex: 1 1 12rem; }
  .mem-club li { grid-template-columns: 1.75rem 6.5rem minmax(0, 1fr) auto; gap: 0.6rem; }
}
/* The card stands larger here, and a little turned, as a card lies on a table. */
.mem-obj .pass { width: min(100%, 27rem); margin: 0 auto; transform: rotate(-3deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), inset 0 -1px 0 rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(0, 0, 0, 0.18), 0 2.5rem 4rem -1.5rem #000; }
/* The piano, held in light: the same picture the LIVE page's screen stands by with. */
.pitch-piano { position: relative; display: block; width: min(100%, 30rem); aspect-ratio: 1.3; margin: 0 auto; }
/* The piano itself, not its projection: the picture taken of it, whole. */
.pitch-piano.real { width: min(100%, 34rem); background: var(--piano-real) center / contain no-repeat; }
/* The maker's own wordmark, where a row's small label would be. */
.pitch-maker { display: block; width: auto; height: 0.6875rem; opacity: 0.75; }
.pitch-link { justify-self: start; position: relative; padding: 0.5rem 1.4rem 0.5rem 0; color: var(--ink); font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em; text-decoration: none; }
.pitch-link::before { content: ""; position: absolute; left: 0; right: 1.4rem; bottom: 0.15rem; height: 1px; background: #fff; }
.pitch-link::after { content: ""; position: absolute; right: 0.3rem; top: 50%; width: 6px; height: 6px; margin-top: -4px; border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor; transform: rotate(45deg); transition: transform 0.3s var(--ease); }
.pitch-link:focus-visible { outline: 1px solid #fff; outline-offset: 4px; }
@media (hover: hover) { .pitch-link:hover::after { transform: translateX(3px) rotate(45deg); } }
button.pitch-link { border: 0; background: none; font-family: inherit; cursor: pointer; }
/* The box, live, at the head of the page: its canvas fills this stage, on
   nothing, so the velvet is its table. It comes up once it is there. A drag
   turns it; on a phone a drag up or down is still the page's. */
.pitch-stage { width: min(100%, 56rem); aspect-ratio: 3 / 2; margin: 0 auto; opacity: 0; transition: opacity 0.9s var(--ease); }
.pitch-stage.on { opacity: 1; }
.pitch-stage canvas { display: block; width: 100%; height: 100%; cursor: grab; touch-action: pan-y; }
.pitch-stage canvas.held { cursor: grabbing; }
@media (max-width: 600px) { .pitch-stage { aspect-ratio: 4 / 3; } }
@media (prefers-reduced-motion: reduce) { .pitch-stage { transition: none; } }
/* The full logo, at the head of the MEMBERSHIP page: the chrome mark, and
   beside it the name in two lines of capitals, as tall together
   as the mark; under them the motto. */
.lockup { display: flex; align-items: center; gap: 1.1rem; }
.lockup .alloc-mark { width: 5.5rem; height: 5.5rem; flex: none; }
.lockup .alloc-mark span { font-size: 2.75rem; }
.lockup b { font: 700 2.5rem/0.92 "Fira Code", ui-monospace, monospace; letter-spacing: 0.01em; text-align: left; color: #fff; }
/* Live, the name is chrome too: mark.js sets it beside the mark in one canvas
   laid over the whole lockup, wider and taller than it so the piece can turn
   without being cut off. The letters stay in the page, unseen, to hold the
   lockup's size. */
.lockup { position: relative; }
.lockup > canvas { position: absolute; left: -8%; top: -40%; width: 116%; height: 180%; display: block; pointer-events: none; }
.lockup.live b { visibility: hidden; }
/* The motto, close under the logo as part of it, quiet, so the logo stays
   the loudest thing. */
@media (max-width: 600px) { .lockup .alloc-mark { width: 4.25rem; height: 4.25rem; } .lockup b { font-size: 1.9375rem; } .lockup { gap: 0.85rem; } }
/* The way in: the one primary button, and under it how many are left and
   how long there is, under the logo. */
.offer { display: grid; justify-items: start; gap: 0.9rem; }
.offer[hidden] { display: none; }
.offer .pack-button { height: 2.75rem; padding: 0 2.4rem; font-size: 0.8125rem; letter-spacing: 0.08em; }
.offer p { display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; margin: 0; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.offer p b { color: var(--ink); font-weight: 500; }
/* The dial on the page: the allocation dial at rest, the figure in its
   middle where the sheet's has the mark. It is a button: it opens the sheet. */
.pitch-dial { display: block; margin: 0 auto; padding: 0; border: 0; border-radius: 50%; background: none; color: inherit; cursor: pointer; }
.pitch-dial:focus-visible { outline: 1px solid #fff; outline-offset: 10px; }
.pitch-dial .alloc-dial { display: block; }
.page-core { align-content: center; gap: 0.5rem; }
.page-core b { position: relative; font-family: "Fira Code", ui-monospace, monospace; font-weight: 400; font-size: 2.5rem; letter-spacing: -0.02em; line-height: 1; }
.page-core small { position: relative; color: var(--dim); font-size: 0.5625rem; font-weight: 500; letter-spacing: 0.14em; }
/* The key: one of the piano's own, turning on nothing, and under it its
   note in the headline face with what it is in small capitals. */
.pitch-key { display: grid; justify-items: center; gap: 0.4rem; margin: 0 auto; width: min(100%, 26rem); opacity: 0; transition: opacity 0.9s var(--ease); }
.pitch-key.on { opacity: 1; }
.pitch-key canvas { display: block; width: 100%; aspect-ratio: 1; cursor: grab; touch-action: pan-y; }
.pitch-key canvas.held { cursor: grabbing; }
/* One key gives way to the next: it and its note fade, and the next come up. */
.pitch-key canvas, .pitch-key figcaption { transition: opacity 0.45s var(--ease); }
.pitch-key.swap canvas, .pitch-key.swap figcaption { opacity: 0; }
.pitch-key figcaption { display: grid; justify-items: center; gap: 0.5rem; }
.pitch-key b { font-family: "Fira Code", ui-monospace, monospace; font-size: 1.75rem; font-weight: 400; letter-spacing: -0.02em; line-height: 1; }
.pitch-key span { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.12em; }
@media (max-width: 900px) { .pitch-key canvas { aspect-ratio: 4 / 3; } }
@media (prefers-reduced-motion: reduce) { .pitch-key { transition: none; } }
@media (max-width: 900px) { .mem-obj .pass { transform: rotate(-2deg); } }
@media (prefers-reduced-motion: reduce) { .pitch-link::after { transition: none; } }
.booker { --grain: none; overflow: hidden; border-radius: 18px; background: var(--metal); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1rem 2rem -1rem #000; }
.booker::before { z-index: 2; opacity: 0.9; }
.booker::after { opacity: 0.3; }
.booker > header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.9rem 1.25rem 0.8rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.booker > header { border-bottom: 1px solid rgba(255, 255, 255, 0.08); }
/* The month, with a step either way: each arrow is cut as the engravings
   are, pale steel at rest and white under the pointer. */
.month { display: flex; align-items: center; gap: 0.5rem; }
.month b { min-width: 9.5rem; color: var(--ink); font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.12em; text-align: center; }
.month button { width: 1.75rem; height: 1.75rem; padding: 0; border: 0; background: none; color: #8e9099; cursor: pointer; }
.month button::before { content: ""; display: block; width: 7px; height: 7px; margin: auto; border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateX(2px) rotate(45deg); }
.month button[data-month="1"]::before { transform: translateX(-2px) rotate(-135deg); }
.month button:disabled { color: #3a3b41; cursor: default; }
.month button:focus-visible { outline: 1px solid #fff; outline-offset: -2px; border-radius: 6px; }
@media (hover: hover) { .month button:not(:disabled):hover { color: #fff; } }
/* The days: seven to a row, Monday first. A day that can be booked is in the
   second ink, the chosen day lit as the chosen row of a board is, a day gone
   or closed all but dark; today wears a point beneath its number, and a day
   with a session of the member's own a point in Solana's colours. */
.cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; padding: 0.75rem 1rem 0.9rem; }
.cal span { padding: 0.2rem 0 0.5rem; color: #5d5f66; font-size: 0.5625rem; font-weight: 500; letter-spacing: 0.1em; text-align: center; }
.cal button { position: relative; height: 2.5rem; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--dim); font: 500 0.8125rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; cursor: pointer; transition: background-color 0.3s var(--ease), color 0.3s var(--ease); }
.cal button:disabled { color: #33343a; cursor: default; }
.cal button.on { background: rgba(255, 255, 255, 0.09); color: var(--ink); }
.cal button.today::after, .cal button.mine::after { content: ""; position: absolute; left: 50%; bottom: 0.35rem; width: 4px; height: 4px; margin-left: -2px; border-radius: 50%; background: #5d5f66; }
.cal button.mine::after { width: 10px; margin-left: -5px; border-radius: 2px; height: 2px; bottom: 0.4rem; background: linear-gradient(90deg, #9945ff, #5497d5, #14f195); }
.cal button:focus-visible { outline: 1px solid #fff; outline-offset: -2px; }
@media (hover: hover) { .cal button:not(:disabled):not(.on):hover { background: rgba(255, 255, 255, 0.04); color: var(--ink); } }
/* The day's times, in the same manner: free in the second ink, the chosen
   one lit, one that is taken struck through. */
.times { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: minmax(2.75rem, 1fr); gap: 2px; height: 100%; padding: 0.75rem 1rem 0.9rem; }
/* Booking goes a step at a time in one frame: the day, its times, then what
   the session is for. The steps lie over one another, so the frame is the
   size of the largest and never changes; the one in hand is seen, and the
   others wait a little to its side. */
.booker > header { min-height: 3.5rem; }
.booker-at { display: flex; align-items: center; gap: 0.35rem; min-width: 0; }
.booker-at span { color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.booker:not([data-step="day"]) .booker-at span { color: var(--ink); }
.booker-back { flex: none; width: 2.75rem; height: 2.75rem; margin: -0.5rem -0.35rem -0.5rem -1.05rem; padding: 0; border: 0; background: none; color: #8e9099; cursor: pointer; }
.booker-back::before { content: ""; display: block; width: 7px; height: 7px; margin: auto; border-left: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateX(2px) rotate(45deg); }
.booker-back:focus-visible { outline: 1px solid #fff; outline-offset: -2px; border-radius: 6px; }
@media (hover: hover) { .booker-back:hover { color: #fff; } }
.booker-back[hidden], .month[hidden], .booker-aside[hidden] { display: none; }
.booker-aside { flex: none; }
.steps { display: grid; }
.step { grid-area: 1 / 1; min-width: 0; opacity: 0; visibility: hidden; transform: translateX(0.75rem); transition: opacity 0.35s var(--ease), transform 0.45s var(--ease), visibility 0s 0.35s; }
.booker[data-step="day"] .step[data-panel="day"], .booker[data-step="time"] .step[data-panel="time"], .booker[data-step="for"] .step[data-panel="for"] { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }
.booker[data-step="time"] .step[data-panel="day"], .booker[data-step="for"] .step[data-panel="day"], .booker[data-step="for"] .step[data-panel="time"] { transform: translateX(-0.75rem); }
/* The last step: two plain questions, and the wallet a gift is for. */
.step-for { display: grid; align-content: center; }
.ask { display: grid; grid-template-columns: 6rem minmax(0, 1fr); align-items: center; gap: 1rem; padding: 0.95rem 1.25rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.ask + .ask { border-top: 1px solid rgba(255, 255, 255, 0.08); }
.ask .choice { padding: 0; gap: 1.75rem; }
.ask .choice button { padding: 1.05rem 0; font-size: 0.6875rem; }
.ask .choice button[aria-pressed="true"]::after { bottom: 0.6rem; }
@media (max-width: 600px) { .ask { grid-template-columns: minmax(0, 1fr); gap: 0; padding: 0.8rem 0.9rem; } .times { padding-left: 0.6rem; padding-right: 0.6rem; } }
@media (prefers-reduced-motion: reduce) { .step { transition: none; transform: none; } }
.times button { height: auto; min-height: 2.75rem; padding: 0; border: 0; border-radius: 8px; background: none; color: var(--dim); font: 500 0.75rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; cursor: pointer; transition: background-color 0.3s var(--ease), color 0.3s var(--ease); }
.times button:disabled { color: #33343a; text-decoration: line-through; cursor: default; }
.times button.on { background: rgba(255, 255, 255, 0.09); color: var(--ink); }
.times button.mine { color: var(--ink); text-decoration: none; box-shadow: inset 0 -2px 0 -0.5px #14f195; }
.times button:focus-visible { outline: 1px solid #fff; outline-offset: -2px; }
@media (hover: hover) { .times button:not(:disabled):not(.on):hover { background: rgba(255, 255, 255, 0.04); color: var(--ink); } }
/* The two choices: what the session is for, and who it is for. Each is a
   pair of words, the chosen one in the first ink with a line beneath it, as
   the menu marks the page in view. */
.choice { display: flex; gap: 1.4rem; padding: 0.55rem 1.25rem 0.65rem; }
.choice button { position: relative; padding: 0.55rem 0; border: 0; background: none; color: var(--dim); font: 500 0.625rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; cursor: pointer; transition: color 0.2s; }
.choice button[aria-pressed="true"] { color: var(--ink); }
.choice button[aria-pressed="true"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0.1rem; height: 1px; background: #fff; }
.choice button:focus-visible { outline: 1px solid #fff; outline-offset: 3px; }
@media (hover: hover) { .choice button:hover { color: var(--ink); } }
.guest { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: 1rem; padding: 0.75rem 1.25rem 0.85rem; border-top: 1px solid rgba(255, 255, 255, 0.08); color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.guest[hidden] { display: none; }
.guest input { min-width: 0; height: 2.25rem; padding: 0 0.75rem; border: 0; border-radius: 8px; background: #050506; box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.85), inset 0 -1px 0 rgba(255, 255, 255, 0.07); color: var(--ink); font: 400 0.75rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; }
.guest input::placeholder { color: #4a4c53; }
.guest input:focus-visible { outline: 1px solid #8e9099; outline-offset: 0; }
.booking-act { display: grid; gap: 0.7rem; }
.booking-act button { width: 100%; padding: 0 1rem; overflow: hidden; text-overflow: ellipsis; }
.booking-act p { margin: 0; color: var(--dim); font-size: 0.75rem; line-height: 1.5; text-align: center; }

/* The membership card, as it will sit in a phone's wallet: the size and
   corners of a wallet pass, black lacquer under a soft band of studio light,
   the mark turning in chrome, the name in the logotype's face, the key and
   the member beneath, and along its foot a run of piano keys in Solana's
   colours, as the piano's own black keys are painted. */
/* Brushed steel: fine lines along its length under a soft band of light,
   a bright top edge and a dark foot. What is on it is engraved: dark in the
   cut, a pale lip under each stroke. Corners eased, as the card's are. */
.pass {
  position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto minmax(0, 1fr) auto; align-items: start; gap: 0 1rem; aspect-ratio: 1.586; padding: 6.5% 7%; overflow: hidden; border-radius: 3.5% / 5.55%;
  color: #1d1e21; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
  background:
    linear-gradient(112deg, rgba(255, 255, 255, 0) 26%, rgba(255, 255, 255, 0.5) 44%, rgba(255, 255, 255, 0) 62%),
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.05) 0 1px, rgba(255, 255, 255, 0.05) 1px 2px, rgba(0, 0, 0, 0) 2px 3px),
    linear-gradient(170deg, #b9bbc0 0%, #8f9197 48%, #a6a8ae 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7), inset 0 -1px 0 rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(0, 0, 0, 0.18), 0 1.5rem 2.5rem -1rem #000;
}
/* The mark's ring is 0.76 of its em. Solana's symbol is 0.84 of that (solid
   bars read larger than a fine ring), on the same centre line, and half its
   own height clear of the ring (the character has a little space of its own each side). */
.pass-signs { grid-area: 1 / 1; display: flex; align-items: center; gap: calc(2.75rem * 0.32 - 2.75rem * 0.051); }
.pass-mark { font: 400 2.75rem/1 "Signs", "STIX Two Math", serif; }
.pass-sol { display: block; height: calc(2.75rem * 0.76 * 0.84); width: auto; fill: currentColor; filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.45)); }
.pass-tap { grid-area: 1 / 2; align-self: center; width: 1.5rem; height: 1.5rem; margin-right: -0.15rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; filter: drop-shadow(0 1px 0 rgba(255, 255, 255, 0.45)); }
.pass-key { grid-area: 3 / 1; align-self: end; font: 500 1.15rem/1 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; white-space: nowrap; }
.pass-whose { grid-area: 3 / 2; align-self: end; font: 400 0.75rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; white-space: nowrap; }
.pass-note { margin: 0; color: var(--dim); font-size: 0.75rem; line-height: 1.5; }
/* The member's sessions: when, what for, who for, and a way to give one up. */
.mine { margin-top: 0.75rem; }
.mine header { display: flex; justify-content: space-between; gap: 1rem; padding-bottom: 0.7rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.mine ol { list-style: none; margin: 0; padding: 0; }
.mine li { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.2rem 1rem; padding: 0.7rem 0; border-top: 1px solid var(--line); }
.mine li b { font-size: 0.8125rem; font-weight: 600; letter-spacing: -0.01em; }
.mine li span { grid-column: 1; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.mine li button { grid-column: 2; grid-row: 1 / 3; padding: 0.5rem 0; border: 0; background: none; color: var(--quiet); font: 500 0.625rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; cursor: pointer; transition: color 0.2s; }
.mine li button:focus-visible { outline: 1px solid #fff; outline-offset: 3px; }
@media (hover: hover) { .mine li button:hover { color: #fff; } }
.mine p { margin: 0; padding-top: 0.8rem; border-top: 1px solid var(--line); color: var(--dim); font-size: 0.75rem; }
.mine [hidden] { display: none; }

/* LIVE: the player. Our own, in the chrome frame. With nothing on air its
   screen is the piano alone, held in light (--piano-holo, the 3D piano drawn
   once as a projection by piano/main.js) and never still, as a feed is: scan
   lines drift up it, a band of light rolls down it, and now and again a
   slice of it slips sideways (.holo). The controls are cut into gunmetal
   along the foot of the screen and come up under the pointer; on a touch
   screen, with no pointer to come up under, they stay. */
.player { --grain: none; width: min(100%, 60rem); margin: 0 auto; overflow: hidden; border-radius: 18px; background: #050506; }
.player::before { z-index: 3; opacity: 0.9; }
.player-screen { position: relative; aspect-ratio: 16 / 9; overflow: hidden; background: radial-gradient(ellipse 70% 60% at 50% 48%, #0b0c10, #030304 75%); }
.player-holo { position: absolute; left: 50%; top: 49%; width: min(62%, 36rem); aspect-ratio: 1.3; translate: -50% -50%; }
/* The projection, alive. The picture breathes; over it lie two more of
   itself, each shown only through a thin slice that jumps about and slips
   sideways, a few frames at a time. */
.holo { background: var(--piano-holo) center / contain no-repeat; animation: holo 5s ease-in-out infinite, holo-shake 7s steps(1) infinite; }
.holo::before, .holo::after { content: ""; position: absolute; inset: 0; background: inherit; clip-path: inset(0 0 100% 0); mix-blend-mode: screen; }
.holo::before { animation: holo-slip-a 3.1s steps(1) infinite; }
.holo::after { animation: holo-slip-b 4.3s steps(1) infinite; }
@keyframes holo { 0%, 100% { opacity: 0.95; } 50% { opacity: 0.74; } }
@keyframes holo-shake { 0%, 100% { transform: none; } 31% { transform: translateX(2px); } 32% { transform: translateX(-3px) skewX(-1.5deg); } 33% { transform: none; } 71% { transform: translateY(-2px); } 72% { transform: none; } }
@keyframes holo-slip-a {
  0%, 100% { clip-path: inset(0 0 100% 0); transform: none; }
  7% { clip-path: inset(24% 0 66% 0); transform: translateX(-14px); }
  9% { clip-path: inset(24% 0 66% 0); transform: translateX(9px); }
  11% { clip-path: inset(0 0 100% 0); }
  43% { clip-path: inset(62% 0 27% 0); transform: translateX(18px); }
  46% { clip-path: inset(58% 0 36% 0); transform: translateX(-6px); }
  48% { clip-path: inset(0 0 100% 0); }
  77% { clip-path: inset(8% 0 86% 0); transform: translateX(-22px); }
  79% { clip-path: inset(0 0 100% 0); }
}
@keyframes holo-slip-b {
  0%, 100% { clip-path: inset(0 0 100% 0); transform: none; }
  19% { clip-path: inset(44% 0 49% 0); transform: translateX(24px); }
  21% { clip-path: inset(41% 0 44% 0); transform: translateX(-10px); }
  23% { clip-path: inset(0 0 100% 0); }
  58% { clip-path: inset(78% 0 12% 0); transform: translateX(-16px); }
  60% { clip-path: inset(0 0 100% 0); }
  88% { clip-path: inset(30% 0 62% 0); transform: translateX(12px); }
  90% { clip-path: inset(33% 0 55% 0); transform: translateX(-20px); }
  92% { clip-path: inset(0 0 100% 0); }
}
.player-scan { position: absolute; inset: -6px 0; pointer-events: none; background: repeating-linear-gradient(rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 3px); animation: scan 1.6s linear infinite; }
.player-scan::after { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 22%; background: linear-gradient(transparent, rgba(120, 160, 255, 0.07) 60%, rgba(180, 255, 230, 0.1) 96%, transparent); animation: band 5.5s linear infinite; }
@keyframes scan { to { transform: translateY(-6px); } }
@keyframes band { from { transform: translateY(-110%); } to { transform: translateY(560%); } }
/* Paused, everything on the screen holds where it is. */
.player.paused .holo, .player.paused .holo::before, .player.paused .holo::after, .player.paused .player-scan, .player.paused .player-scan::after { animation-play-state: paused; }
.player-bar {
  position: absolute; z-index: 2; left: 0; right: 0; bottom: 0; display: flex; align-items: center; gap: 1rem; height: 3.25rem; padding: 0 1.1rem;
  background: linear-gradient(rgba(5, 5, 6, 0), rgba(5, 5, 6, 0.9) 45%); opacity: 0; transform: translateY(0.4rem); transition: opacity 0.35s var(--ease), transform 0.5s var(--ease);
}
.player:hover .player-bar, .player:focus-within .player-bar, .player.paused .player-bar { opacity: 1; transform: none; }
@media (hover: none) { .player-bar { opacity: 1; transform: none; } }
.player-bar .gauge-line { background: #2a2b30; }
.player-air { display: flex; align-items: center; gap: 0.6em; color: var(--dim); font-size: 0.5625rem; font-weight: 600; letter-spacing: 0.12em; white-space: nowrap; }
/* The controls are engravings: pale steel at rest, white under the pointer. */
.player-bar button { flex: none; width: 2.75rem; height: 2.75rem; margin: 0 -0.5rem; padding: 0; border: 0; background: none; color: #8e9099; cursor: pointer; transition: color 0.2s; }
.player-bar button:focus-visible { outline: 1px solid #fff; outline-offset: -8px; border-radius: 10px; }
@media (hover: hover) { .player-bar button:hover { color: #fff; } }
/* Playing, the button offers a pause (two bars); paused, to play (a triangle). */
.player-play::before { content: ""; display: block; width: 10px; height: 12px; margin: auto; border-left: 3px solid currentColor; border-right: 3px solid currentColor; }
.player.paused .player-play::before { width: 0; height: 0; border: 0; border-left: 10px solid currentColor; border-top: 6.5px solid transparent; border-bottom: 6.5px solid transparent; transform: translateX(1px); }
.player-full::before { content: ""; display: block; width: 12px; height: 12px; margin: auto; background: linear-gradient(currentColor, currentColor) 0 0 / 4.5px 1.5px no-repeat, linear-gradient(currentColor, currentColor) 0 0 / 1.5px 4.5px no-repeat, linear-gradient(currentColor, currentColor) 100% 0 / 4.5px 1.5px no-repeat, linear-gradient(currentColor, currentColor) 100% 0 / 1.5px 4.5px no-repeat, linear-gradient(currentColor, currentColor) 0 100% / 4.5px 1.5px no-repeat, linear-gradient(currentColor, currentColor) 0 100% / 1.5px 4.5px no-repeat, linear-gradient(currentColor, currentColor) 100% 100% / 4.5px 1.5px no-repeat, linear-gradient(currentColor, currentColor) 100% 100% / 1.5px 4.5px no-repeat; }
/* Filling the screen: no frame, no corners, the piano as large as it will go. */
.player:fullscreen { width: 100vw; height: 100vh; max-width: none; border-radius: 0; }
.player:fullscreen::before, .player:fullscreen::after { display: none; }
.player:fullscreen .player-screen { aspect-ratio: auto; height: 100%; }
.player:fullscreen .player-holo { width: min(70vw, 105vh); }
/* What is on, said under the player and not on its screen. */
.player-cap { display: flex; align-items: baseline; justify-content: space-between; gap: 0.4rem 2rem; flex-wrap: wrap; width: min(100%, 60rem); margin: 0.9rem auto 0; color: var(--dim); font-size: 0.8125rem; line-height: 1.5; }
.player-cap b { color: var(--ink); font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }
/* What is coming: each broadcast booked, soonest first; when, who, and how
   long until. The soonest, the one the player waits for, is in the first
   ink with a lit point hung in the margin, so the dates stay in one line;
   the rest in the second. */
.upcoming { width: min(100%, 60rem); margin: 3.5rem auto 0; }
.upcoming header { display: flex; justify-content: space-between; gap: 1rem; padding-bottom: 0.9rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.upcoming ol { list-style: none; margin: 0; padding: 0; }
.upcoming li { display: grid; grid-template-columns: minmax(0, 14rem) minmax(0, 1fr) auto; align-items: baseline; gap: 0.3rem 1.5rem; padding: 0.95rem 0; border-top: 1px solid var(--line); color: var(--dim); }
.upcoming li b { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }
.upcoming li span, .upcoming li em { font-size: 0.625rem; font-weight: 500; font-style: normal; letter-spacing: 0.1em; }
.upcoming li.next b, .upcoming li.next em { color: var(--ink); }
.upcoming li { position: relative; }
.upcoming li.next::before { content: ""; position: absolute; left: -14px; top: 50%; width: 5px; height: 5px; margin-top: -3px; border-radius: 50%; background: #14f195; }
.upcoming p { margin: 0; padding-top: 0.9rem; border-top: 1px solid var(--line); color: var(--dim); font-size: 0.875rem; line-height: 1.5; }
.upcoming [hidden] { display: none; }
@media (max-width: 600px) { .upcoming li { grid-template-columns: minmax(0, 1fr) auto; } .upcoming li span { grid-column: 1; grid-row: 2; } .upcoming li em { grid-column: 2; grid-row: 1; } }
/* The archive: every broadcast kept, newest first, three to a row. Each is a
   small still screen and, under it, when it was and who played. */
.archive { width: min(100%, 60rem); margin: 4.5rem auto 0; }
.archive header { display: flex; justify-content: space-between; gap: 1rem; padding-bottom: 0.9rem; border-bottom: 1px solid var(--line); color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.archive ol { list-style: none; margin: 1.5rem 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2rem 1.5rem; }
.archive ol:empty { display: none; }
.archive li button { display: grid; gap: 0.7rem; width: 100%; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.archive li i { position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 10px; background: radial-gradient(ellipse 70% 60% at 50% 50%, #0b0c10, #030304 75%); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08); transition: box-shadow 0.3s var(--ease); }
.archive li i::before { content: ""; position: absolute; inset: 14% 22%; background: var(--piano-holo) center / contain no-repeat; opacity: 0.55; transition: opacity 0.3s var(--ease); }
.archive li i::after { content: attr(data-length); position: absolute; right: 0.5rem; bottom: 0.45rem; padding: 0.2rem 0.4rem; border-radius: 4px; background: rgba(0, 0, 0, 0.7); color: var(--ink); font: 500 0.625rem/1 "Fira Code", ui-monospace, monospace; font-style: normal; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }
.archive li b { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.01em; }
.archive li span { margin-top: -0.4rem; color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.archive li button[aria-pressed="true"] i { box-shadow: inset 0 0 0 1px #8e9099; }
.archive li button[aria-pressed="true"] i::before { opacity: 1; }
.archive li button:focus-visible { outline: 1px solid #fff; outline-offset: 6px; border-radius: 10px; }
@media (hover: hover) { .archive li button:hover i::before { opacity: 0.9; } }
.archive p { margin: 1.2rem 0 0; color: var(--dim); font-size: 0.875rem; line-height: 1.5; }
.archive [hidden] { display: none; }
@media (max-width: 900px) {
  .cal { padding-left: 0.6rem; padding-right: 0.6rem; }
  .booker > header, .guest { padding-left: 0.9rem; padding-right: 0.9rem; }
  .month b { min-width: 7.5rem; }
  .player-holo { width: 66%; }
  .archive ol { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem 1rem; }
}
@media (prefers-reduced-motion: reduce) {
  .cal button, .times button, .choice button { transition: none; }
  .holo, .holo::before, .holo::after, .player-scan, .player-scan::after { animation: none; }
  .player-bar { transition: none; }
}

/* The box (pack.js): a key is bought in a box. It stands on plain black with
   a little light behind it, none of the old pack's colour or flash. */
.pack-shade { background: radial-gradient(60% 55% at 50% 42%, rgba(46, 48, 56, 0.5), rgba(0, 0, 0, 0.94) 72%); }
.pack-stage canvas { cursor: grab; touch-action: none; }
.pack-stage canvas.held { cursor: grabbing; }
/* The box has a wide, tall window, so that open, with its lid standing, all
   of it is in view at a good size; the price and the button keep their width. */
.pack-stage { width: min(100vw, 58rem); }
.pack-stage canvas { height: min(66vh, 42rem); }
.pack-deck, .pack-name { width: min(100%, 38rem); }
/* Choosing the key: the keyboard of the 88, the width of the deck under it.
   The chosen key is lit, a sold one is dark and cannot be chosen. */
.pack-keys.board { min-width: 0; width: min(100% - 2 * var(--gutter), 34rem); height: 5.5rem; margin: 0 0 1.25rem; touch-action: none; }
.pack-keys .k[aria-pressed="true"] { outline: 0; }
.pack-keys .k[aria-pressed="true"]::after { opacity: 1; }
.pack-keys .k:disabled { cursor: default; filter: none; }
.pack-keys .k.sold:not(.black) { background: linear-gradient(#5d5c59, #4a4946); }
.pack-keys .k.black.sold { background: #0b0b0c; }
@media (hover: none) { .pack-keys .k:hover { filter: none; } }
.pack-read b, .pack-go { white-space: nowrap; }
/* PRICE and KEY on one line, whatever stands under either. */
.pack-deck .pack-read { align-self: start; }
/* On a phone the box gives up height so the keys, the deck and the note fit
   the window with it, and the deck closes up. */
@media (max-width: 600px) {
  .pack-stage canvas { height: min(46svh, 24rem); }
  .pack-stage { grid-template-columns: minmax(0, 1fr); }
  .pack-deck { gap: 0.75rem; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
  .pack-read b { font-size: 1.05rem; }
  .pack-go { padding: 0 1.25rem; font-size: 0.8125rem; }
}

/* The headlines are set small: a quiet line at the head of each page and of
   each row, not a banner. The big figures that stand with them are a size
   up, and no more. */
.place h2, .pitch-head h2 { font-family: "Fira Code", ui-monospace, monospace; font-weight: 400; letter-spacing: -0.015em; }
.place h2 { font-size: 1.5rem; line-height: 1.15; }
.pitch-head h2 { font-size: clamp(1.625rem, 2.6vw, 2.125rem); line-height: 1.12; }
.pitch-head p { font-size: 0.9375rem; }

/* The chosen day wears chrome: the baked ring the buttons
   wear, drawn as a small round bezel about the number. (Without the baked
   rings it keeps its plain lit square.) */
.ring-ready .cal button.on { background: none; }
.ring-ready .cal button.on::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 2.25rem; height: 2.25rem; translate: -50% -50%; box-sizing: border-box; pointer-events: none;
  border-style: solid; border-width: 1.125rem; border-image: var(--ring-pill) 50% 22.32% stretch; filter: brightness(1.2);
}

/* The master button of a phone's screen: not on wider windows. */
.master { display: none; }
@media (max-width: 600px) {
  .master {
    display: inline-flex; position: absolute; z-index: 4; bottom: calc(var(--foot, 3.5rem) + 1rem);
    left: max(var(--gutter), env(safe-area-inset-left)); right: max(var(--gutter), env(safe-area-inset-right));
    height: 2.75rem; padding: 0 0.75rem; font-size: 0.75rem; letter-spacing: 0.08em;
    opacity: 0; visibility: hidden; transition: opacity 0.3s var(--ease), visibility 0s linear 0.3s;
  }
}
@media (prefers-reduced-motion: reduce) { .master { transition: none !important; } }

/* MEMBERSHIP on a phone, at the door, is laid out as an asset's page is in a
   trading app (keyscreen.js): three screens, one under the next, and the
   page catches on each as it is scrolled (a snap, but only when it is let go
   near one). One button for all of them, BUY A KEY, pegged over the menu
   (the master button, pages.js).
   1. The asset: photographs of it to swipe through (the box, what is in
      it, the card, the key, the piano), the full width of the window and
      from its very top, then what is bought and the line. Exactly the room
      down to the button, on any height of phone.
   2. The numbers: the raise's bar, and under it four large figures.
   3. What you get: six rows, each a sign, a name, a line and an arrow.
   A row opens its own screen (.page[data-see]): the object, large, its name,
   and its terms as wide rows; the mark in the bar is the way back.
   The row of six small names and each object's own button are for wider
   windows.
   How: the door's parts are laid straight onto the hero's grid (its wrapper
   is display: contents). An empty piece spans the rows of the first screen
   and is as tall as the screen (--screen), so they add up to it; the stage's
   row is the one that gives, and the stage, which has no size of its own
   here (contain: size), draws its object as large as that row and the width
   allow. */
.key-shots, .key-figs, .key-rows, .key-seen { display: none; }
@media (max-width: 600px) {
  .page[data-for="membership"] { scroll-snap-type: y proximity; }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-hero {
    /* (5.5rem over the menu: the button, 1rem under it, 1.75rem over it.) */
    --screen: calc(100dvh - var(--bar) - 2.5rem - var(--foot, 3.5rem) - 5.5rem);
    grid-template-rows: minmax(0, 1fr) auto; grid-auto-rows: auto; gap: 0; min-height: 0; align-items: stretch; align-content: start;
    scroll-snap-align: start; scroll-margin-top: calc(var(--bar) + 2.5rem);
  }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-hero::before { content: ""; grid-area: 1 / 1 / 3 / 2; height: max(var(--screen), 22rem); visibility: hidden; pointer-events: none; }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-say.mem-door { display: contents; }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-stage { grid-area: 1 / 1; contain: size; grid-template: minmax(0, 1fr) / minmax(0, 1fr); align-items: center; }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-objs { width: auto; height: 100%; max-width: 100%; }
  /* The photographs stand where the stage does when a row is opened: in the
     row that gives. They run out to the window's edges and up under the bar
     to its top; each is cut to fill its frame, about its subject. Swiped
     sideways, one at a time; the dots say which. */
  .page[data-for="membership"]:not([data-member="holder"]):not([data-see]) .mem-stage { display: none; }
  /* (They are the whole first screen: from the window's top edge to its
     foot, under the bar and under the menu. The title lies over their foot,
     where they are darkened for it, and the master button floats over them.) */
  .page[data-for="membership"]:not([data-member="holder"]):not([data-see]) .key-shots {
    grid-area: 1 / 1 / 3 / 2; display: block; position: relative; contain: size;
    margin: calc(-1 * (var(--bar) + 2.5rem)) calc(-1 * max(var(--gutter), env(safe-area-inset-right))) calc(-1 * (var(--foot, 3.5rem) + 5.5rem)) calc(-1 * max(var(--gutter), env(safe-area-inset-left)));
  }
  .key-shots::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 46%; pointer-events: none; background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.72) 52%, rgba(0, 0, 0, 0.9)); }
  .page[data-for="membership"]:not([data-member="holder"]):not([data-see]) .mem-head { position: relative; z-index: 1; pointer-events: none; }
  /* What follows starts under the window's foot, clear of them. */
  .page[data-for="membership"]:not([data-member="holder"]):not([data-see]) .raise { margin-top: calc(var(--foot, 3.5rem) + 5.5rem + 2.5rem); }
  .key-shots-run { display: flex; height: 100%; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none; }
  .key-shots-run::-webkit-scrollbar { display: none; }
  .key-shots-run:focus-visible { outline: 1px solid #fff; outline-offset: -1px; }
  .key-shots figure { flex: 0 0 100%; min-width: 0; margin: 0; scroll-snap-align: center; scroll-snap-stop: always; }
  .key-shots picture, .key-shots img { display: block; width: 100%; height: 100%; }
  .key-shots img { object-fit: cover; object-position: 50% 46%; -webkit-user-select: none; user-select: none; }
  .key-dots { position: absolute; z-index: 1; left: 0; right: 0; bottom: calc(var(--foot, 3.5rem) + 11.75rem); display: flex; justify-content: center; gap: 0.4rem; pointer-events: none; }
  .key-dots i { width: 6px; height: 6px; border-radius: 50%; background: #3a3b41; transition: background-color 0.3s var(--ease); }
  .key-dots i.on { background: #fff; }
  .page[data-for="membership"]:not([data-member="holder"]) .mem-head { grid-area: 2 / 1; margin-top: 1rem; }
  .page[data-for="membership"]:not([data-member="holder"]) :is(.mem-pick, .mem-pair, .mem-terms, .raise-line) { display: none; }
  /* 2. The numbers. */
  .page[data-for="membership"]:not([data-member="holder"]) .raise { grid-area: 3 / 1; margin-top: 3rem; scroll-snap-align: start; scroll-margin-top: calc(var(--bar) + 1.5rem); }
  .page[data-for="membership"]:not([data-member="holder"]) .key-figs { grid-area: 4 / 1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; margin-top: 1.25rem; }
  .key-fig { display: grid; align-content: start; gap: 0.5rem; min-width: 0; padding: 1rem 1.1rem 1.05rem; border-radius: 18px; background: linear-gradient(#050506 0%, #0a0b0c 30%, #0f1011 70%, #141517 100%); box-shadow: inset 0 3px 6px rgba(0, 0, 0, 0.85), inset 0 1px 1px rgba(0, 0, 0, 0.7), inset 0 -1px 0 rgba(255, 255, 255, 0.07); }
  .key-fig span { color: var(--dim); font: 500 0.625rem/1.2 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; }
  .key-fig b { color: var(--ink); font: 600 1.75rem/1.05 "Fira Code", ui-monospace, monospace; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; white-space: nowrap; }
  .key-fig b small { margin-left: 0.2em; color: var(--dim); font-size: 0.5em; font-weight: 500; letter-spacing: 0; }
  .key-fig em { min-height: 1.4em; color: var(--dim); font: 400 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; font-style: normal; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* 3. What you get. */
  .page[data-for="membership"]:not([data-member="holder"]) .key-rows { grid-area: 5 / 1; display: grid; margin-top: 3rem; scroll-snap-align: start; scroll-margin-top: calc(var(--bar) + 1.5rem); }
  .page .key-rows h2, .page .key-seen h2 { margin: 0; color: var(--ink); font: 400 1.75rem/1.1 "Fira Code", ui-monospace, monospace; letter-spacing: -0.015em; }
  .key-rows h2 { padding-bottom: 0.75rem; }
  .key-rows a { display: grid; grid-template-columns: 2.75rem minmax(0, 1fr) 1.5rem; grid-template-rows: auto auto; align-items: center; align-content: center; gap: 0.2rem 1rem; min-height: 4.75rem; color: var(--ink); text-decoration: none; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08); -webkit-tap-highlight-color: transparent; }
  .key-rows svg { display: block; width: 1.5rem; height: 1.5rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
  /* The sign sits in a round well of gunmetal. */
  .key-rows i { grid-area: 1 / 1 / 3 / 2; display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: 50%; background: linear-gradient(#050506, #141517); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.85), inset 0 -1px 0 rgba(255, 255, 255, 0.08); }
  .key-rows b { grid-area: 1 / 2; align-self: end; font: 600 0.8125rem/1.2 "Fira Code", ui-monospace, monospace; letter-spacing: 0.1em; }
  .key-rows span { grid-area: 2 / 2; align-self: start; min-width: 0; overflow: hidden; color: var(--dim); font-size: 0.8125rem; line-height: 1.3; letter-spacing: 0; text-overflow: ellipsis; white-space: nowrap; }
  .key-rows .go { grid-area: 1 / 3 / 3 / 4; color: var(--dim); }
  .key-rows a:focus-visible { outline: 1px solid #fff; outline-offset: -1px; border-radius: 8px; }
  /* A row opened: the object over its name and its terms, nothing else. */
  .page[data-for="membership"]:not([data-member="holder"])[data-see] :is(.mem-head, .raise, .key-figs, .key-rows) { display: none; }
  .page[data-for="membership"]:not([data-member="holder"])[data-see] .mem-hero { grid-template-rows: auto; }
  .page[data-for="membership"]:not([data-member="holder"])[data-see] .mem-hero::before { grid-area: 1 / 1 / 2 / 2; height: 36dvh; }
  .page[data-for="membership"]:not([data-member="holder"])[data-see] .key-seen { grid-area: 2 / 1; display: block; margin-top: 1rem; }
  .page[data-for="membership"]:not([data-member="holder"])[data-see] .mem-terms { grid-area: 3 / 1; display: grid; margin-top: 1rem; max-width: none; }
  .page[data-for="membership"]:not([data-member="holder"])[data-see] .mem-in { display: none; }
  .page[data-for="membership"]:not([data-member="holder"]) .terms div, .page[data-for="membership"]:not([data-member="holder"]) .terms.spec div, .page[data-for="membership"]:not([data-member="holder"]) .terms.spec div:first-child { gap: 1.25rem; padding: 1rem 0; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08); }
  .page[data-for="membership"]:not([data-member="holder"]) .terms div:first-child { box-shadow: none; }
  .page[data-for="membership"]:not([data-member="holder"]) .terms.spec { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; grid-auto-flow: row; }
  .page[data-for="membership"]:not([data-member="holder"]) .terms dt, .page[data-for="membership"]:not([data-member="holder"]) .terms dd, .page[data-for="membership"]:not([data-member="holder"]) .terms.spec dt, .page[data-for="membership"]:not([data-member="holder"]) .terms.spec dd { font: 500 0.75rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.08em; }
  .page[data-for="membership"] .mem-in { scroll-snap-align: start; scroll-margin-top: calc(var(--bar) + 1rem); }
  /* LIVE and KEY, and MEMBERS until a key is held (RECORDS has none): the master
     button, in one place, so it does not move from one screen to the next.
     It floats over the page, which is scrolled beneath it, so the menu's
     black rises behind it and fades out above it. */
  html[data-page="live"] .master, html[data-page="membership"]:not([data-key-room]) .master, html[data-page="members"]:not([data-club="holder"]) .master { opacity: 1; visibility: visible; transition: opacity 0.5s var(--ease) 0.2s, visibility 0s; }
  .page[data-for="live"], .page[data-for="members"], .page[data-for="membership"]:not([data-member="holder"]) { padding-bottom: calc(var(--foot, 3.5rem) + 5.75rem); }
  html[data-page="live"].void .foot::before, html[data-page="membership"]:not([data-key-room]):not([data-key-top]).void .foot::before, html[data-page="members"]:not([data-club="holder"]).void .foot::before { inset: -5.25rem 0 0; -webkit-mask-image: linear-gradient(transparent, #000 1.5rem); mask-image: linear-gradient(transparent, #000 1.5rem); }
  /* The black that rises behind the menu stops short of the buttons. */
  .void .foot::before { inset: -1rem 0 0; -webkit-mask-image: linear-gradient(transparent, #000 1rem); mask-image: linear-gradient(transparent, #000 1rem); }
}

/* MEMBERS: the members' room (club.js). Four cards, each a plate of dark
   metal in the chrome frame the terminal and the booking wear: its sign, its
   name, and under the name how it stands. At the top right, what a press
   will do: an arrow where it opens, a padlock where a key is wanted. A
   locked card is the same plate with the light turned down. Two to a row;
   four in one row on a wide window. */
.page-body.club { width: min(100%, 60rem); }
.club-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
@media (min-width: 901px) { .club-cards { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; } }
.club-card {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto minmax(1.5rem, 1fr) auto auto; gap: 0.4rem 0.5rem;
  min-height: 9.5rem; padding: 1.25rem 1.25rem 1.15rem; border-radius: 18px; background: var(--metal); color: var(--ink); text-decoration: none;
  cursor: pointer; -webkit-tap-highlight-color: transparent; transition: color 0.3s var(--ease);
}
.club-card svg { display: block; width: 1.5rem; height: 1.5rem; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.club-sign { grid-area: 1 / 1; }
.club-state { grid-area: 1 / 2; display: grid; color: var(--dim); transition: color 0.2s; }
.club-state svg { grid-area: 1 / 1; transition: opacity 0.3s var(--ease); }
.club-state .lock, .club-card.locked .club-state .go { opacity: 0; }
.club-card.locked .club-state .lock { opacity: 1; }
.club-card b { grid-area: 3 / 1 / 4 / 3; font: 600 0.75rem/1.2 "Fira Code", ui-monospace, monospace; letter-spacing: 0.12em; }
/* How it stands: two lines at most, and room kept for two, so the names sit level across a row. */
.club-card span { grid-area: 4 / 1 / 5 / 3; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; min-width: 0; min-height: 2.8em; overflow: hidden; color: var(--dim); font: 400 0.625rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; overflow-wrap: anywhere; }
.club-card.locked { color: #5d5f66; cursor: default; }
.club-card.locked span { color: #5d5f66; }
.ring-ready .club-card.locked::before { filter: brightness(0.55); }
@media (hover: hover) {
  .club-card:not(.locked):hover .club-state { color: var(--ink); }
  .ring-ready .club-card:not(.locked):hover::before { filter: brightness(1.3); }
}
.club-card:focus-visible { outline: none; }
.club-card:focus-visible .club-state { color: var(--ink); }
.ring-ready .club-card:not(.locked):focus-visible::before { filter: brightness(1.3); }
.club-card.locked:focus-visible { outline: 1px solid #5d5f66; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .club-card, .club-state, .club-state svg { transition: none; } }

/* The chatroom and the profile, in sheets. The room is the sheet's height:
   its messages scroll between the title and the line they are typed on.
   A message is its sender (a name if they gave one), when, and the words;
   the reader's own are marked by a fine line of Solana's green. */
.chat-body { display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto auto; width: min(100%, 36rem); height: min(44rem, calc(100vh - 2rem)); height: min(44rem, calc(100dvh - 2rem)); }
.chat-log { min-height: 0; margin: 1rem 0 0; padding: 0; overflow: auto; overscroll-behavior: contain; list-style: none; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.chat-log li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.2rem 1rem; padding: 0.8rem 0 0.8rem 0.75rem; border-bottom: 1px solid rgba(255, 255, 255, 0.08); box-shadow: inset 1px 0 0 transparent; }
.chat-log li.own { box-shadow: inset 1px 0 0 #14f195; }
.chat-log li.none { display: block; padding-left: 0; color: var(--dim); }
.chat-log b { min-width: 0; overflow: hidden; font: 600 0.6875rem/1.4 "Fira Code", ui-monospace, monospace; letter-spacing: 0.08em; text-overflow: ellipsis; white-space: nowrap; }
.chat-log time { color: var(--dim); font: 400 0.625rem/1.6 "Fira Code", ui-monospace, monospace; letter-spacing: 0.02em; white-space: nowrap; }
.chat-log p { grid-column: 1 / 3; margin: 0; color: var(--ink); font-size: 0.875rem; line-height: 1.5; letter-spacing: 0; overflow-wrap: anywhere; }
.chat-form { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem; margin-top: 1rem; }
/* (16px, so a phone does not zoom the page in when the line is tapped.) */
.sheet .chat-form input { height: 2.75rem; padding: 0 1rem; border-radius: 999px; font-size: 1rem; letter-spacing: 0; }
/* (By the sheets' names, to stand over the sheets' own plain buttons.) */
:is(#chat, #profile) .chat-form .pack-button { height: 2.75rem; padding: 0 1.4rem; border: 0; border-radius: 999px; background: var(--metal); font: 600 0.75rem/1 "Fira Code", ui-monospace, monospace; letter-spacing: 0.08em; }
/* Before the holder is in, the way in is the whole line. */
.chat-form.way-in { grid-template-columns: minmax(0, 1fr); }
.chat-form input[hidden] { display: none; }
.sheet .chat-note { min-height: 1.4em; margin: 0.6rem 0 0; font-size: 0.6875rem; }
.profile-body { width: min(100%, 30rem); }
.profile-facts { display: grid; margin: 0.5rem 0 0; border-top: 1px solid rgba(255, 255, 255, 0.08); }
.profile-facts div { display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem; padding: 0.9rem 0; border-bottom: 1px solid rgba(255, 255, 255, 0.08); }
.profile-facts dt { color: var(--dim); font-size: 0.625rem; font-weight: 500; letter-spacing: 0.1em; }
.profile-facts dd { margin: 0; min-width: 0; text-align: right; font-size: 0.875rem; font-weight: 500; letter-spacing: 0.02em; overflow-wrap: anywhere; }
.profile-facts a { color: inherit; text-decoration: underline; text-decoration-color: rgba(255, 255, 255, 0.3); text-underline-offset: 3px; }
