/* ---- src/styles/fonts.css ---- */
/* Self-hosted free fonts (SIL Open Font License), latin subset, from Google Fonts.
   Mona Sans is variable: weight 200-900, width 75%-125%. IBM Plex Mono: 400 and 600.
   Paths are relative to the built stylesheet, assets/site.css. */
@font-face {
  font-family: "Mona Sans";
  font-style: normal;
  font-weight: 200 900;
  font-stretch: 75% 125%;
  font-display: swap;
  src: url(fonts/MonaSans-normal-200-900.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/IBMPlexMono-normal-400.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/IBMPlexMono-normal-600.woff2) format("woff2");
}

/* ---- src/styles/tokens.css ---- */
/* Mickey 9s design tokens. The only file allowed to hold raw values (px, hex).
   Everything else in the site uses these names. tools/lint.js fails the build if a raw px or hex value
   appears anywhere else (the one breakpoint, 1024px, is the only exception, because CSS cannot put a
   custom property in a media query).

   Read bottom to top: palette -> colour roles -> type -> spacing -> layout roles -> controls -> states. */

:root {
  /* ---------------------------------------------------------------- palette (10 colours, nothing else) */
  --black: #000000;
  --white: #ffffff;
  --grey-70: #4d4d4d;   /* 8.45:1 on white, 7.56:1 on grey-6 */
  --grey-6: #f0f0f0;
  --yellow: #fabc1d;    /* sampled from the QMU 2026 poster; black on it is 12.3:1 */
  --blue: #6898ff;      /* pure blue lightened until black on it is 7.5:1 */
  --orange: #ff8000;    /* pure orange; black on it is 8.3:1 */
  --green: #28b83e;     /* pure green darkened so it shows on white; black on it is 8.0:1 */
  --red: #f04a3e;       /* a calmer true red (Antony, 19 Oct); black on it is 5.75:1, the one colour under 7:1 (a red that reaches 7:1 turns peach) */
  /* Instagram's own pink (#ff0069) lightened until black on it is 7.5:1, the same rule as the blue above, so it sits
     in this palette (Antony, 23 Oct 2026, topic 362 msgs 4291, 4295). Facebook's blue and YouTube's red, put through
     the same rule, come out as this palette's own blue and red, so those two links use them. */
  --pink: #ff61a2;      /* black on it is 7.48:1 */

  /* ---------------------------------------------------------------- colour roles */
  --c-bg: var(--white);          /* the page */
  --c-text: var(--black);        /* all text, unless a role below says otherwise */
  --c-text-2: var(--grey-70);    /* labels, secondary facts, placeholder text */
  --c-rule: var(--black);        /* every rule and border */
  --c-fill: var(--grey-6);       /* input wells, table heads, disabled controls, placeholder stripes */
  --c-bar: var(--black);         /* section bars and primary buttons */
  --c-on-bar: var(--white);      /* text on a bar */
  --c-here: var(--yellow);       /* "you are here": current page, pointer, focused field, chosen option; pages outside a section */
  /* each menu position keeps its colour, left to right yellow, blue, orange, green, red (Antony, 19 Oct 2026); base.css applies them */
  --c-gigs: var(--yellow);       /* Gigs, the 1st menu cell */
  --c-list: var(--green);        /* List, the 4th menu cell */
  --c-shop: var(--orange);       /* Shop, product pages, Bag, Checkout, Order, the 3rd menu cell */
  --c-music: var(--blue);        /* Tunes (Music) and release pages, the 2nd menu cell */
  --c-bio: var(--red);           /* Script (Bio), the 5th menu cell */
  --c-instagram: var(--pink);    /* the links to each platform, wherever they are (footer, Bio, the design system) */
  --c-facebook: var(--blue);
  --c-youtube: var(--red);
  --c-desk: var(--grey-70);      /* Setlist Democracy's page under its photo, while the photo loads (ballot.css); the share card's, under its shelf (reference.css) */

  /* ---------------------------------------------------------------- type */
  --f-display: "Mona Sans", "Arial Narrow", "Helvetica Neue", Arial, sans-serif; /* set condensed (75%) */
  --f-text: "Mona Sans", "Helvetica Neue", Arial, sans-serif;                   /* set at normal width */
  --f-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;

  /* the scale: 10 sizes, each with its own line height on the 4px grid */
  --fs-12: 12px;  --lh-12: 16px;
  --fs-14: 14px;  --lh-14: 20px;
  --fs-16: 16px;  --lh-16: 24px;
  --fs-20: 20px;  --lh-20: 24px;
  --fs-24: 24px;  --lh-24: 24px;
  --fs-32: 32px;  --lh-32: 32px;
  --fs-40: 40px;  --lh-40: 40px;
  --fs-56: 56px;  --lh-56: 48px;
  --fs-80: 80px;  --lh-80: 72px;
  --fs-120: 120px; --lh-120: 104px;

  --w-regular: 400;
  --w-semibold: 600;
  --w-heavy: 800;
  --w-black: 900;
  --stretch-display: 75%;
  --stretch-text: 100%;
  --ls-label: 0.04em;   /* mono labels */
  --ls-caps: 0.01em;    /* condensed capitals */

  /* ---------------------------------------------------------------- spacing scale (the only gaps allowed) */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;
  --s9: 96px;

  /* ---------------------------------------------------------------- rules */
  --rule: 2px;         /* every box edge and divider; also the gap between cells, so neighbours share one rule */
  --rule-thick: 4px;   /* focus ring, chosen option, current page marker, invalid field */

  /* ---------------------------------------------------------------- layout roles (phone first) */
  --max: 1376px;            /* content width at 1440 = 1440 - 2 x 32 */
  --margin: var(--s4);      /* page edge to content */
  --block: var(--s6);       /* between sections, header to first section, last section to footer */
  --pad: var(--s3);         /* inside every box */
  --gap-label: var(--s1);   /* a label to its value */
  --gap-stack: var(--s2);   /* stacked items inside a box: field label to input, input to message */
  --cols: 4;                /* grid columns */

  /* ---------------------------------------------------------------- controls */
  --ctl: 48px;              /* minimum tap target and control height */
  --ctl-lg: 64px;           /* big actions: Tickets, Add to bag, Checkout, Pay */
  --nav-h: 48px;
  --logo-h: 28px;           /* logo height in the header and footer */
  --credit-h: 19.52px;      /* Square9's stamp under the footer: "Built by", Tanker at 19px (14.25px capitals), the
                               Square9 site's button type (topic 149 msg 6271), its box the logo's proportion */
  --credit-logo: 26px;      /* Square9's logo in the stamp, tile and wordmark as drawn: its height. Bigger than the label
                               (msg 6517, 4 Nov: "the logo needs to be bigger whilst keeping the button the same"): at
                               26px its capitals are 18.98px against the label's 14.25px. Change this one number to
                               resize it; it stays centred on the label's capitals (--credit-logo-lift). */
  --credit-face-h: 51px;    /* the stamp's face: the Square9 site's button height (16 + 19 + 16) */
  --credit-lift: 1.07px;    /* nudges the stamp's label up, so its capitals sit where a Square9 button's do: 17.75px to
                               32px down its 51px face (measured on its test link, 3 Nov) */
  --credit-logo-lift: calc(var(--credit-lift) + (var(--credit-logo) - var(--credit-h)) * 0.023);
                            /* nudges the logo up so its capitals' middle is the label's, 24.88px down the face. Both
                               drawings have their capitals from 0.158 to 0.888 of their height, so their middle is
                               0.023 of the height below the drawing's own middle; flex centring puts the drawings'
                               middles together, and this takes off the difference (1.22px at 26px) */
  --credit-radius: 12px;    /* the stamp's corners: Square9's look, not this site's square one */
  --credit-offset: 6px;     /* the black block behind the stamp's face, and how far the face presses on hover */
  --credit-space: var(--s6); /* clear space above the stamp's face and below its block, the same on every Square9 site (Antony, topic 625 msg 6546) */
  --credit-tuck: 1px;       /* how far the block tucks in under the pressed face, so no dark edge shows round its corners */
  --t-credit: 120ms;        /* the stamp's press on hover */
  --count-w: 32px;          /* the bag count box */

  /* ---------------------------------------------------------------- media */
  --ratio-square: 1 / 1;
  --ratio-photo: 3 / 2;      /* every band photo (baked to 3:2 by tools/prep-images.py) */
  --ratio-hero: 3 / 2;       /* the lead photo on Home and Bio; 21:9 from 1024 */
  --ratio-video: 16 / 9;     /* video thumbnails (YouTube's own, cropped to 16:9 by tools/prep-images.py) */
  --thumb: 96px;            /* bag line image */

  /* ---------------------------------------------------------------- the ballot (Setlist Democracy, ballot.css) */
  /* the ballot paper lying on the polling-booth shelf on Setlist Democracy's share card (system/reference.css): the
     one shadow, on no page of the site; the page's sheets have the site's box edge */
  --shadow-paper: 0 1px 0 rgba(0, 0, 0, 0.15), 0 10px 24px rgba(0, 0, 0, 0.28);
  /* a song already in the setlist shows its sleeve in black and white, greyed with the rest of its row; every other
     sleeve on the ballot is in full colour (Antony, topic 362 msg 5513) */
  --emblem-filter: grayscale(1) contrast(1.15);

  /* ---------------------------------------------------------------- type roles (font shorthand + case) */
  --type-label: var(--w-regular) var(--fs-12) / var(--lh-12) var(--f-mono);
  --type-label-strong: var(--w-semibold) var(--fs-12) / var(--lh-12) var(--f-mono);
  --type-small: var(--w-regular) var(--fs-14) / var(--lh-14) var(--f-text);
  --type-mono-small: var(--w-semibold) var(--fs-14) / var(--lh-14) var(--f-mono);
  --type-body: var(--w-regular) var(--fs-16) / var(--lh-16) var(--f-text);
  --type-body-strong: var(--w-semibold) var(--fs-16) / var(--lh-16) var(--f-text);
  --type-input: var(--w-regular) var(--fs-16) / var(--lh-16) var(--f-mono);
  --type-input-lg: var(--w-regular) var(--fs-20) / var(--lh-20) var(--f-mono);
  --type-value: var(--w-heavy) condensed var(--fs-20) / var(--lh-20) var(--f-display);
  --type-title-s: var(--w-black) condensed var(--fs-24) / var(--lh-24) var(--f-display);
  --type-title-m: var(--w-black) condensed var(--fs-32) / var(--lh-32) var(--f-display);
  --type-title-l: var(--w-black) condensed var(--fs-40) / var(--lh-40) var(--f-display);
  --type-title-xl: var(--w-black) condensed var(--fs-56) / var(--lh-56) var(--f-display);
  --type-title-xxl: var(--w-black) condensed var(--fs-80) / var(--lh-80) var(--f-display);
  --type-title-xxxl: var(--w-black) condensed var(--fs-120) / var(--lh-120) var(--f-display);

  /* which size each role takes on a phone */
  --type-page: var(--type-title-xxl);
  --type-nav: var(--type-value);
  --type-bar: var(--type-title-s);
  --type-btn: var(--type-value);
  --type-btn-lg: var(--type-title-s);
  --type-date: var(--type-title-xl);
  --type-name: var(--type-title-l);
  --type-price-lg: var(--type-title-l);
  --type-ballot: var(--type-title-xl);   /* Setlist Democracy's title on its ballot paper */
  --type-voted: var(--type-title-l);     /* the song voted for; Voting closed */
  --type-rank: var(--type-title-m);      /* a place in the top 10 */
}

/* the one breakpoint: 1024px and up */
@media (min-width: 1024px) {
  :root {
    --margin: var(--s6);
    --block: var(--s7);
    --pad: var(--s4);
    --cols: 12;
    --nav-h: 64px;
    --logo-h: 48px;
    --thumb: 128px;
    --ratio-hero: 21 / 9;
    --type-nav: var(--type-title-s);
    --type-bar: var(--type-title-m);
    --type-btn-lg: var(--type-title-m);
    --type-date: var(--type-title-xxl);
    --type-name: var(--type-title-xl);
    --type-page: var(--type-title-xxxl);
    --type-ballot: var(--type-title-xxl);
    --type-voted: var(--type-title-xl);
    --type-rank: var(--type-title-s);
  }
}

/* ---- src/styles/base.css ---- */
/* Base: reset and the defaults every page starts from. Values come from tokens.css. */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: var(--c-bg); }
body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font: var(--type-body);
  font-stretch: var(--stretch-text);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
}
/* Mona Sans draws a slashed zero by default; switch it off so prices and dates read normally. */
body, input, button, select { font-feature-settings: "zero" 0, "ss01" 0; }
img { display: block; max-width: 100%; height: auto; }
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; margin: 0; }
button { cursor: pointer; }

/* each section's "you are here" colour: set on the page (body), a Home glimpse, or a menu cell */
[data-section="gigs"] { --c-here: var(--c-gigs); }
[data-section="list"] { --c-here: var(--c-list); }
[data-section="shop"] { --c-here: var(--c-shop); }
[data-section="music"] { --c-here: var(--c-music); }
[data-section="bio"] { --c-here: var(--c-bio); }
/* a link to a platform lights in that platform's colour, inside any section */
[data-platform="instagram"] { --c-here: var(--c-instagram); }
[data-platform="facebook"] { --c-here: var(--c-facebook); }
[data-platform="youtube"] { --c-here: var(--c-youtube); }
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; }
a { color: inherit; }
[hidden] { display: none !important; }

/* Focus: one ring everywhere, thick and black, never removed. On black it turns yellow. */
:focus-visible, .is-focus { outline: var(--rule-thick) solid var(--c-text); outline-offset: var(--rule); }

/* main is focused by the skip link; no ring round the whole page */
main:focus { outline: none; }

/* Screen-reader-only text */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; /* lint-ok: the standard visually-hidden pattern */
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Skip link: first thing on every page, shown on focus */
.skip {
  position: absolute; left: var(--margin); top: calc(-1 * var(--ctl-lg)); z-index: 10;
  display: flex; align-items: center; min-height: var(--ctl); padding: 0 var(--s4);
  background: var(--c-bar); color: var(--c-on-bar);
  font: var(--type-value); text-transform: uppercase; text-decoration: none;
}
.skip:focus { top: var(--margin); outline-color: var(--c-here); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* ---- src/styles/layout.css ---- */
/* Layout: the page frame. One content column, the same left and right edges on every page.
   Phone: 16px margins. 1024 and up: 32px margins, content up to 1376px wide (exactly 1376 at 1440). */

.wrap {
  width: 100%;
  max-width: calc(var(--max) + 2 * var(--margin));
  margin-inline: auto;
  padding-inline: var(--margin);
}

/* Vertical rhythm. Every gap between the big blocks is --block: header -> first block, block -> block, last block ->
   footer. The page ends with Square9's stamp under the footer, which carries its own clear space, --credit-space above
   its face and below its block (frame.css .foot__credit).
   A page shorter than the screen fills it exactly (Antony, topic 149 msgs 6569-6571): main grows, so the footer and the
   stamp sit at the screen's bottom edge and the spare height goes between the last block and the footer. A page taller
   than the screen is laid out exactly as before.
   The screen's height is 100svh, the height with a phone browser's address bar and toolbar showing: the smallest the
   screen gets. A page that fits in that can't scroll, so Safari keeps its bars showing and the page is exactly the
   screen you see, with no scroll bar. Not 100vh: on an iPhone that is the height with the bars hidden, so a short page
   would be taller than the screen and scroll. Not 100dvh: it follows the bars as they slide, so a page a little taller
   than the screen would grow as the bars hide on scrolling, stop being scrollable, and jump. A browser without svh
   ignores the min-height and lays the page out as before. */
.page { display: flex; flex-direction: column; min-height: 100svh; }
.page__main { flex-grow: 1; display: flex; flex-direction: column; gap: var(--block); padding-block: var(--block); }
.page__foot { padding-bottom: 0; } /* the stamp closes the page: its own space sits below it */

/* Grid: 4 columns on a phone, 12 from 1024. Cells share rules: the grid's own background is the rule
   colour and the gap between cells is one rule wide. */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--rule);
  background: var(--c-rule);
  border: var(--rule) solid var(--c-rule);
}
:where(.grid > *) { background: var(--c-bg); min-width: 0; }

/* Stack: ruled blocks one under another sharing one rule (a bar and what hangs from it, a notice over a form). */
.stack > :not([hidden]) ~ :not([hidden]) { border-top-width: 0; }

/* Split: a main column and a side column sharing one rule; side by side (8/12 + 4/12) from 1024. */
.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); align-items: start; }
:where(.split > *) { background: var(--c-bg); min-width: 0; align-self: stretch; }
.split :is(.lines, .totals, .formcells) { border-width: 0; }
.split :is(.lines, .totals) + :is(.lines, .totals) { border-top-width: var(--rule); }
.split__side { display: flex; flex-direction: column; }
.split__side > :not(:last-child) { border-bottom: var(--rule) solid var(--c-rule); }
@media (min-width: 1024px) { .split { grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); } }

/* ---- src/components/frame.css ---- */
/* Frame components: header + nav, footer, section bar. The page title is the sr-only utility (base.css).
   States used here (see tokens.css): hover = --c-here; current page = --c-here + a 4px rule along the bottom
   (menu cells, Bag, footer links; never the logo);
   pressed = inverted; focus = the 4px ring, drawn inside cells so it never leaves the page edges. */

/* ------------------------------------------------------------------ header */
.head__bar { display: flex; border: var(--rule) solid var(--c-rule); }
.head__brand {
  flex: 1; display: flex; align-items: center;
  min-height: var(--ctl); padding: var(--pad);
  text-decoration: none;
}
.head__brand img, .foot__brand img { height: var(--logo-h); width: auto; }
.head__bag {
  display: flex; align-items: center; gap: var(--s2);
  min-width: var(--ctl); padding: 0 var(--pad);
  border-left: var(--rule) solid var(--c-rule);
  font: var(--type-nav); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}

/* the event header (a page about one gig: Setlist Democracy): the logo and Tickets, held at the top of the screen as the
   page scrolls; nothing else. Tickets is a black cell like a primary button. The bar is white and the band either side
   of it clear, so the photo behind Setlist Democracy runs past it to the screen's edges (ballot.css .desk__photo); what
   scrolls under the header scrolls under the bar, which is exactly as wide as the boxes below it. */
.head--event { position: sticky; top: 0; z-index: 5; }
.head--event .head__bar { background: var(--c-bg); }
html:has(.head--event) { scroll-padding-top: var(--s9); }   /* a field brought into view is not left under it */
.head__tickets {
  display: flex; align-items: center; justify-content: center;
  min-width: var(--ctl); padding: 0 var(--s5);
  border-left: var(--rule) solid var(--c-rule);
  background: var(--c-bar); color: var(--c-on-bar);
  font: var(--type-nav); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
.head__tickets:hover, .head__tickets.is-hover { background: var(--c-here); color: var(--c-text); }
.head__tickets:active, .head__tickets.is-active { background: var(--c-bg); color: var(--c-text); }
.head__tickets:focus-visible, .head__tickets.is-focus { outline-offset: calc(-2 * var(--rule-thick)); box-shadow: inset 0 0 0 calc(2 * var(--rule-thick)) var(--c-here); }
@media (min-width: 1024px) { .head__tickets { padding: 0 var(--s7); } }

/* the count box: bag count, and counts in section links */
.count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--count-w); height: var(--count-w); padding: 0 var(--s1);
  border: var(--rule) solid currentColor;
  font: var(--type-mono-small); letter-spacing: 0;
}

/* one equal cell per section, however many the build has (four when the shop is off) */
.nav__list {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  border: var(--rule) solid var(--c-rule); border-top: 0;
}
.nav__list > li { min-width: 0; }
.nav__list > li + li { border-left: var(--rule) solid var(--c-rule); }
.nav__link {
  display: flex; align-items: center;
  min-height: var(--nav-h); padding: 0 var(--s2);
  font: var(--type-nav); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
@media (min-width: 1024px) { .nav__link { padding: 0 var(--pad); } }

/* states shared by every cell-shaped link in the frame */
:is(.head__bag, .nav__link, .foot__link):hover,
:is(.head__bag, .nav__link, .foot__link).is-hover { background: var(--c-here); }
/* the logo is never filled as the current page: on Home it keeps aria-current for screen readers, but the
   menu shows nothing lit */
:is(.head__bag, .nav__link, .foot__link)[aria-current="page"],
:is(.head__bag, .nav__link, .foot__link).is-current {
  background: var(--c-here);
  box-shadow: inset 0 calc(-1 * var(--rule-thick)) 0 var(--c-rule);
}
:is(.head__bag, .nav__link, .foot__link):active,
:is(.head__bag, .nav__link, .foot__link).is-active { background: var(--c-bar); color: var(--c-on-bar); }
:is(.head__brand, .head__bag, .nav__link, .foot__link):focus-visible,
:is(.head__brand, .head__bag, .nav__link, .foot__link).is-focus { outline-offset: calc(-2 * var(--rule-thick)); }
/* the logo stays white whatever happens to it (Antony, 19 Oct): no hover, pressed or current fill; only the focus ring */

/* ------------------------------------------------------------------ footer */
.foot__grid {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule);
}
:where(.foot__grid > *) { background: var(--c-bg); min-width: 0; }
.foot__brand { display: flex; align-items: center; min-height: var(--ctl); padding: var(--pad); }
.foot__list { display: grid; gap: var(--rule); background: var(--c-rule); }
:where(.foot__list > li) { display: flex; background: var(--c-bg); min-width: 0; }
.foot__list--social { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.foot__contact { display: flex; flex-direction: column; justify-content: center; gap: var(--gap-label); padding: var(--pad); }
.foot__contact > .link { align-self: flex-start; }
/* each address with its name beside it, the names in one column so the addresses line up (Antony, topic 362 msg 4291) */
.foot__addresses { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--s4); align-items: baseline; }
.foot__addresses > .link { justify-self: start; min-height: var(--s6); }   /* lines close together (msg 4295); a 32px target, agreed as the one exception to 48px (msg 4339) */

/* the credit under the footer: Square9's own stamp, the same on every site Square9 makes, in the Square9 site's look
   rather than this site's (Antony, topic 149 msg 6147, 3 Nov 2026). It is the Square9 site's button: a face, a black
   block 6px behind it with no blur, 12px corners. The face is white with a thin black line inside its edge on every
   page (msg 6488: "White with a thin black line on everything"); the block and the drawings are black. On hover or
   press the face moves onto the block; the block stays put, so the pointer never loses the link. Centred under the
   footer (msg 4303), with 32px of clear space (--credit-space) above its face and below its block, at every width
   and on every Square9 site (topic 625 msg 6546): the top padding starts at the footer's bottom line, and the bottom
   padding adds the block's 6px, as the block hangs out of the link.
   Its size is the Square9 site's button's, measured on its test link (msg 6271, 3 Nov): 51px high, 24px each side.
   One line of two drawings, 8px apart (a Square9 button's gap between its label and an icon): "Built by", Tanker at
   19px with a Square9 button's 0.08em tracking, its capitals on a Square9 button's lines (--credit-lift); then
   Square9's real logo, tile and wordmark as drawn (msgs 6510 and 6513, 4 Nov), bigger than the label (msg 6517: "the
   logo needs to be bigger whilst keeping the button the same"), its capitals' middle on the label's (--credit-logo-lift).
   The logo's height is one token (--credit-logo). */
.foot__credit { display: flex; justify-content: center; padding: var(--credit-space) 0 calc(var(--credit-space) + var(--credit-offset)); }
.foot__credit-link { position: relative; display: inline-flex; border-radius: var(--credit-radius); text-decoration: none; }
.foot__credit-link::before { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--black); transform: translate(var(--credit-offset), var(--credit-offset)); }
.foot__credit-card {
  position: relative; display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--credit-face-h); padding: 0 var(--s5); border-radius: inherit; background: var(--white);
  box-shadow: inset 0 0 0 var(--rule) var(--black);
  transition: transform var(--t-credit);
}
.foot__credit-label, .foot__credit-logo { position: relative; top: calc(-1 * var(--credit-lift)); display: block; height: var(--credit-h); width: auto; }
.foot__credit-logo { top: calc(-1 * var(--credit-logo-lift)); height: var(--credit-logo); }
.foot__credit-link:hover .foot__credit-card, .foot__credit-link:active .foot__credit-card,
.foot__credit-link.is-hover .foot__credit-card { transform: translate(var(--credit-offset), var(--credit-offset)); }
/* pressed, the face lies exactly on the block; the block tucks in a little so its edge can't show round the corners */
.foot__credit-link:hover::before, .foot__credit-link:active::before, .foot__credit-link.is-hover::before { inset: var(--credit-tuck); }
/* the ring clears the block by one rule, as on the Square9 site's buttons */
.foot__credit-link:focus-visible, .foot__credit-link.is-focus { outline-offset: calc(var(--credit-offset) + var(--rule)); }
.foot__link {
  flex: 1; display: flex; align-items: center;
  min-height: var(--ctl); padding: 0 var(--pad);
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
@media (min-width: 1024px) {
  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot__list--legal { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
}

/* prototype strip: only in the prototype build, never on the live site */
.proto {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--s4);
  padding: 0 var(--pad);
  border: var(--rule) solid var(--c-rule); border-top: 0;
  background: var(--c-fill);
}

/* ------------------------------------------------------------------ section bar */
.bar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 var(--s4);
  min-height: var(--ctl); padding: 0 var(--pad);
  background: var(--c-bar); color: var(--c-on-bar);
}
.bar__title { font: var(--type-bar); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.bar__link {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--ctl); min-width: var(--ctl);
  padding: 0 var(--s2); margin-inline: calc(-1 * var(--s2));   /* hover box inset all round, like .link */
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
/* the underline is on the words only: on the link itself it would carry into the count box's number (topic 362 msg 5446) */
.bar__label { text-decoration: underline; text-decoration-thickness: var(--rule); text-underline-offset: var(--s1); }
.bar__link:hover, .bar__link.is-hover { background: linear-gradient(var(--c-here), var(--c-here)) center / 100% calc(var(--lh-20) + 2 * var(--s2)) no-repeat; color: var(--c-text); }
.bar__link:active, .bar__link.is-active { background: linear-gradient(var(--c-bg), var(--c-bg)) center / 100% calc(var(--lh-20) + 2 * var(--s2)) no-repeat; color: var(--c-text); }
.bar__link:focus-visible, .bar__link.is-focus { outline-color: var(--c-here); outline-offset: 0; }
/* a note on the right of a bar (the number of votes): a label, white on the black */
.bar .bar__note { color: var(--c-on-bar); }

/* ---- src/components/feedback.css ---- */
/* Feedback and marks: label, tag, placeholder, notice, error summary, text links. */

/* a small label: mono capitals. Used above every value, and anywhere a thing needs naming quietly */
.label { font: var(--type-label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--c-text-2); }

/* tag: a short black stamp ("Sold out", "Prototype") */
.tag {
  display: inline-flex; align-items: center;
  padding: var(--s1) var(--s2);
  background: var(--c-bar); color: var(--c-on-bar);
  font: var(--type-label-strong); letter-spacing: var(--ls-label); text-transform: uppercase;
}

/* placeholder: anything not known yet. Hatched, dashed, [bracketed]. It must never read as a fact. */
.ph {
  display: flex; align-items: center; justify-content: center;
  padding: var(--pad);
  background: repeating-linear-gradient(-45deg, var(--c-fill) 0 var(--s2), var(--c-bg) var(--s2) var(--s4));
  border: var(--rule) dashed var(--c-text-2);
  color: var(--c-text-2);
  font: var(--type-mono-small); text-align: center; text-transform: none; letter-spacing: 0;
}
.ph--block { justify-content: flex-start; align-items: flex-start; min-height: var(--s9); text-align: left; }
.ph--media { aspect-ratio: var(--ratio-square); width: 100%; border: 0; }
.ph--line { justify-content: flex-start; min-height: var(--ctl); text-align: left; }
.ph--inline { display: inline-flex; align-self: flex-start; padding: 0 var(--s1); }

/* notice: a message in a ruled box. Success is plain, error is inverted, prototype carries a tag. */
.notice {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-stack);
  padding: var(--pad);
  border: var(--rule) solid var(--c-rule);
  font: var(--type-body-strong);
}
.notice__title { font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.notice--success { box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }
.notice--error { background: var(--c-bar); color: var(--c-on-bar); }
.notice--proto { background: var(--c-fill); }
.notice .btn, .notice .link { margin-top: 0; }

/* error summary (checkout): every problem at the top, each one a link to its field */
.errsum { padding: var(--pad); border: var(--rule-thick) solid var(--c-rule); display: flex; flex-direction: column; gap: var(--gap-stack); }
.errsum:focus-visible { outline-offset: var(--rule); }
.errsum__title { font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.errsum__list { display: flex; flex-direction: column; }
.errsum__list a {
  display: flex; align-items: center; min-height: var(--ctl);
  font: var(--type-body-strong);
  text-decoration: underline; text-decoration-thickness: var(--rule); text-underline-offset: var(--s1);
}
.errsum__list a:hover { background: var(--c-here); }

/* text link: a standalone link or link-styled button, never inside running text, always a full tap target.
   Hover and pressed draw a box with the same space all round the text, inset from the 48px tap target
   (Antony, 19 Oct, option B); the side padding is taken back by the margin, so the text keeps its edge. */
.link {
  display: inline-flex; align-items: center; min-height: var(--ctl);
  padding: 0 var(--s2); margin-inline: calc(-1 * var(--s2)); border: 0; background: none;
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase;
  text-decoration: underline; text-decoration-thickness: var(--rule); text-underline-offset: var(--s1);
  cursor: pointer;
}
.link:hover, .link.is-hover { background: linear-gradient(var(--c-here), var(--c-here)) center / 100% calc(var(--lh-20) + 2 * var(--s2)) no-repeat; }
.link:active, .link.is-active { background: linear-gradient(var(--c-bar), var(--c-bar)) center / 100% calc(var(--lh-20) + 2 * var(--s2)) no-repeat; color: var(--c-on-bar); }
/* an email address keeps its own case, in body type; the hover box follows its line height */
.link--address { font: var(--type-body-strong); text-transform: none; letter-spacing: 0; }
.link--address:is(:hover, .is-hover, :active, .is-active) { background-size: 100% calc(var(--lh-16) + 2 * var(--s2)); }

/* ---- src/components/actions.css ---- */
/* Actions: buttons, crumbs (the way back up), row links. */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--ctl); padding: 0 var(--s5);
  border: var(--rule) solid var(--c-rule); border-radius: 0;
  background: var(--c-bg); color: var(--c-text);
  font: var(--type-btn); letter-spacing: var(--ls-caps); text-transform: uppercase; text-align: center; text-decoration: none;
}
.btn--primary { background: var(--c-bar); color: var(--c-on-bar); }
.btn--lg { min-height: var(--ctl-lg); font: var(--type-btn-lg); letter-spacing: var(--ls-caps); }
.btn--block { display: flex; width: 100%; }
.btn--cell { border: 0; }   /* a button that fills a ruled cell, the cell draws the rules */
.btn:hover, .btn.is-hover { background: var(--c-here); color: var(--c-text); }
.btn:active, .btn.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.btn--primary:active, .btn--primary.is-active { background: var(--c-bg); color: var(--c-text); }
.btn[disabled], .btn[aria-disabled="true"], .btn.is-disabled {
  background: var(--c-fill); color: var(--c-text-2); border-style: dashed; border-color: var(--c-text-2); cursor: not-allowed;
}
.btn--cell[disabled], .btn--cell.is-disabled { border: 0; }
.btn[aria-busy="true"], .btn.is-loading { background: var(--c-fill); color: var(--c-text); cursor: progress; }
/* focus on a black button: the black ring outside, and a yellow ring inside that shows on the black */
.btn--primary:focus-visible, .btn--primary.is-focus { box-shadow: inset 0 0 0 var(--rule-thick) var(--c-here); }
.btn--cell:focus-visible, .btn--cell.is-focus { outline-offset: calc(-2 * var(--rule-thick)); }  /* a cell button keeps its ring inside the cell */

/* crumbs: where this page sits, and a one-tap way back up */
.crumbs { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.crumbs__up {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  min-height: var(--ctl); min-width: var(--ctl); padding: 0 var(--s4);
  border: var(--rule) solid var(--c-rule);
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
.crumbs__up:hover, .crumbs__up.is-hover { background: var(--c-here); }
.crumbs__up:active, .crumbs__up.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.crumbs__here { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* row links: a list of big links, one per ruled row */
.rows { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.rows > li) { display: flex; background: var(--c-bg); min-width: 0; }
.row {
  flex: 1; display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  min-height: var(--ctl-lg); padding: 0 var(--pad);
  font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
.row:hover, .row.is-hover { background: var(--c-here); }
.row:active, .row.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.row:active .label, .row.is-active .label { color: var(--c-on-bar); }
.row:focus-visible, .row.is-focus { outline-offset: calc(-2 * var(--rule-thick)); }
@media (min-width: 1024px) { .rows--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ---- src/components/facts.css ---- */
/* Facts: the form look. Each fact is a ruled cell holding a small label and its value.
   A facts grid is 2 columns on a phone and 4 from 1024; cells share rules. */

.facts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule);
}
:where(.facts > *) { background: var(--c-bg); min-width: 0; }
.facts--1 { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 1024px) {
  .facts { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .facts--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .facts--1 { grid-template-columns: minmax(0, 1fr); }
}

.fact { display: flex; flex-direction: column; gap: var(--gap-label); padding: var(--pad); }
.fact--full { grid-column: 1 / -1; }
.fact--wide { grid-column: 1 / -1; }
@media (min-width: 1024px) { .fact--wide { grid-column: span 2; } }
.fact__value { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; overflow-wrap: anywhere; }
.fact__value--big { font: var(--type-date); letter-spacing: var(--ls-caps); }
.fact__value--text { font: var(--type-body-strong); text-transform: none; letter-spacing: 0; }
.fact__note { font: var(--type-small); color: var(--c-text-2); }
.fact__line { display: block; }
.fact__pairs { display: grid; grid-template-columns: max-content max-content; gap: var(--gap-label) var(--s4); }
.fact__pairs dt { font: var(--type-body); text-transform: none; letter-spacing: 0; }
.fact__pairs dd { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.fact--action { padding: 0; display: flex; }
.fact--action > .btn { flex: 1; }

/* a facts list in rows: label beside value (bio, order details) */
.facts--rows .fact { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); gap: var(--s3); align-items: baseline; }
.facts--rows .fact__value { font: var(--type-body-strong); text-transform: none; letter-spacing: 0; }
.facts--rows .fact__value > * + * { margin-top: var(--gap-label); }
.facts--rows .fact > :not(.label) { grid-column: 2; justify-self: start; }

/* ticks: a ruled list of short facts (a tee's details) */
.ticks > li { padding-block: var(--s2); border-bottom: var(--rule) solid var(--c-rule); font: var(--type-body); }
.ticks > li:first-child { border-top: var(--rule) solid var(--c-rule); }

/* ---- src/components/forms.css ---- */
/* Forms: field (label, control, message), input, select, choices (size and colour), quantity, sign-up.
   Label above control, message below it, every gap --gap-stack. The field you are in turns --c-here.
   An invalid control gets a 4px edge and a black message that starts "Error:" for screen readers. */

.field { display: flex; flex-direction: column; gap: var(--gap-stack); min-width: 0; }
.field__label { font: var(--type-label-strong); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--c-text); }
.field__hint { font: var(--type-small); color: var(--c-text-2); }
.field__error {
  align-self: flex-start; width: fit-content; padding: var(--s1) var(--s2);
  background: var(--c-bar); color: var(--c-on-bar);
  font: var(--type-mono-small);
}

.input, .select {
  width: 100%; min-width: 0; height: var(--ctl); padding: 0 var(--s3);
  border: var(--rule) solid var(--c-rule); border-radius: 0;
  background: var(--c-fill); color: var(--c-text);
  font: var(--type-input);
  -webkit-appearance: none; appearance: none;
}
.input:hover, .input.is-hover, .select:hover, .select.is-hover { background: var(--c-bg); }
.input:focus, .input.is-focus, .select:focus, .select.is-focus { background: var(--c-here); outline: var(--rule-thick) solid var(--c-text); outline-offset: var(--rule); }
.input:disabled, .select:disabled { border-style: dashed; border-color: var(--c-text-2); color: var(--c-text-2); cursor: not-allowed; }
.input[aria-invalid="true"], .select[aria-invalid="true"], .is-invalid .input, .is-invalid .select { box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }

/* large: 64px tall, 20px type (the List page's sign-up uses .signup--lg for the same) */
.input--lg { height: var(--ctl-lg); font: var(--type-input-lg); }

/* select: native control, drawn flat, with a solid down-pointer */
.select-wrap { position: relative; display: flex; }
.select { padding-right: var(--s7); cursor: pointer; }
.select-wrap::after {
  content: ""; position: absolute; right: var(--s4); top: 50%; pointer-events: none;
  border-left: var(--s2) solid transparent; border-right: var(--s2) solid transparent; border-top: var(--s2) solid var(--c-text);
  transform: translateY(-50%);
}

/* choices: radio buttons drawn as boxes (size, colour). Chosen = --c-here and a 4px edge.
   Not available = struck through, dashed and greyed, and cannot be chosen. */
.choices { display: block; min-width: 0; }
.choices > legend { float: left; width: 100%; margin-bottom: var(--gap-stack); }
.choices > legend + * { clear: both; }
.choices > .field__error { margin-top: var(--gap-stack); }
.choices__list { display: flex; flex-wrap: wrap; gap: var(--gap-stack); }
.choice { position: relative; display: flex; }
.choice__input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.choice__box {
  display: flex; align-items: center; justify-content: center;
  min-width: var(--ctl); min-height: var(--ctl); padding: 0 var(--s3);
  border: var(--rule) solid var(--c-rule); background: var(--c-bg);
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase;
}
.choice__input:hover + .choice__box, .choice.is-hover .choice__box { background: var(--c-here); }
.choice__input:checked + .choice__box, .choice.is-checked .choice__box { background: var(--c-here); box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }
.choice__input:focus-visible + .choice__box, .choice.is-focus .choice__box { outline: var(--rule-thick) solid var(--c-text); outline-offset: var(--rule); }
.choice__input:active + .choice__box, .choice.is-active .choice__box { background: var(--c-bar); color: var(--c-on-bar); }
.choice__input:disabled { cursor: not-allowed; }
.choice__input:disabled + .choice__box, .choice.is-disabled .choice__box {
  background: var(--c-fill); color: var(--c-text-2); border-style: dashed; border-color: var(--c-text-2);
  text-decoration: line-through; text-decoration-thickness: var(--rule);
}
.is-invalid .choice__box { box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }

/* tick box: a drawn box and its words on one line. The real checkbox lies over the whole line, unseen, so the whole
   line is the tap target. Hover and ticked are the chosen colour; ticked adds a 4px edge and the tick. */
.tick { position: relative; display: flex; align-items: center; gap: var(--s3); min-height: var(--ctl); cursor: pointer; }
.tick__input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.tick__box {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: var(--s6); height: var(--s6);
  border: var(--rule) solid var(--c-rule); background: var(--c-bg);
}
.tick__mark { width: var(--s5); height: var(--s5); visibility: hidden; }
.tick__text { font: var(--type-body); }
.tick__input:hover + .tick__box, .tick.is-hover .tick__box { background: var(--c-here); }
.tick__input:checked + .tick__box, .tick.is-checked .tick__box { background: var(--c-here); box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }
.tick__input:checked + .tick__box .tick__mark, .tick.is-checked .tick__mark { visibility: visible; }
.tick__input:focus-visible + .tick__box, .tick.is-focus .tick__box { outline: var(--rule-thick) solid var(--c-text); outline-offset: var(--rule); }

/* quantity: minus, the number, plus; three cells sharing rules */
.qty { display: inline-flex; align-self: flex-start; }
.qty__btn, .qty__value {
  display: flex; align-items: center; justify-content: center;
  width: var(--ctl); height: var(--ctl);
  border: var(--rule) solid var(--c-rule);
  font: var(--type-value);
}
.qty > * + * { border-left-width: 0; }
.qty__btn { background: var(--c-bg); padding: 0; }
.qty__value { font: var(--type-mono-small); }
.qty__btn:hover, .qty__btn.is-hover { background: var(--c-here); }
.qty__btn:active, .qty__btn.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.qty__btn:focus-visible, .qty__btn.is-focus { outline-offset: calc(-1 * var(--rule-thick)); }
.qty__btn:disabled { background: var(--c-fill); color: var(--c-text-2); cursor: not-allowed; }

/* sign-up: email and button on one line; the message underneath; success replaces the field */
.signup { display: flex; flex-direction: column; gap: var(--gap-stack); padding: var(--pad); border: var(--rule) solid var(--c-rule); }
.signup__row { display: flex; }
.signup__row .input { flex: 1; }
.signup__row .btn { flex: none; border-left: 0; }
.signup--lg .input, .signup--lg .btn { height: var(--ctl-lg); }
.signup--lg .input { font: var(--type-input-lg); }
.signup__done { font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.signup__foot { display: flex; }
@media (min-width: 1024px) {
  .signup { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); column-gap: var(--s4); align-items: end; }
  .signup__foot { justify-content: flex-end; }
}

/* a form laid out in ruled cells (product buy box, checkout) */
.formcells { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.formcells > *) { background: var(--c-bg); min-width: 0; padding: var(--pad); }
.formcells > .formcells__flush { padding: 0; display: flex; }
.formcells > :empty { display: none; }
.formcells__msg { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 var(--s4); box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }
.formcells__flush > .btn { flex: 1; }
.formcells__group { display: flex; flex-direction: column; gap: var(--s4); }
.formcells__legend { font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.formcells__pair { display: flex; flex-direction: column; gap: var(--s4); }
@media (min-width: 1024px) { .formcells__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- src/components/gigs.css ---- */
/* Gigs: the gig detail block (next gig) and gig rows (upcoming, past, sold out). */

/* gig detail: on a phone the poster first, then the facts, in the page's order (topic 362 msg 5442); from 1024 the full
   variant has the facts on the left and the poster on the right, as before. All cells share rules. */
.gig { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.gig > *) { background: var(--c-bg); min-width: 0; }
.gig .facts { border: 0; }
.gig__poster { padding: var(--pad); }
.gig__poster img { width: 100%; }
@media (min-width: 1024px) {
  .gig--full { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
  .gig--full > .gig__poster { grid-column: 2; grid-row: 1; }
}
.fact--half { grid-column: span 1; }
@media (min-width: 1024px) { .fact--half { grid-column: span 2; } }
/* side: a narrow column (Setlist Democracy): the poster, then the date a size smaller, then the city, one under the other
   (the facts are one column: facts--1) */
.gig--side { --type-date: var(--type-title-l); }
.gig__status { display: flex; flex-direction: column; align-items: flex-start; gap: var(--gap-stack); }
.gig__status > .btn { align-self: stretch; }

/* gig rows: one ruled row per gig. Desktop shows column labels above. */
.gigrows { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.gigrows > li) { background: var(--c-bg); min-width: 0; }
.gigrow {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "date status" "venue venue" "city city";
  column-gap: var(--s4); row-gap: var(--gap-label);
  padding: var(--pad);
}
.gigrow__date { grid-area: date; font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; align-self: center; }
.gigrow__venue { grid-area: venue; font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.gigrow__city { grid-area: city; font: var(--type-body); }
.gigrow__status { grid-area: status; display: flex; align-items: center; justify-content: flex-end; }
.gigrows__head { display: none; }
.gigrow--empty, .gigrow--ph { display: block; padding: var(--pad); }
/* past rows add With: on a phone a fourth line, labelled; on a desktop its own column */
.gigrow--past { grid-template-columns: minmax(0, 1fr); grid-template-areas: "date" "venue" "city" "with"; }
.gigrow__with { grid-area: with; font: var(--type-body); }
.gigrow__with:empty { display: none; }
.gigrow__label { margin-right: var(--s1); }
/* Show all: the button hangs under the first rows and shares their rules; once open it goes */
.more > .gigrows { border-top-width: 0; }
.more__btn { border-top-width: 0; list-style: none; cursor: pointer; }
.more__btn::-webkit-details-marker { display: none; }
.more[open] > .more__btn { display: none; }
@media (min-width: 1024px) {
  .gigrow, .gigrows__head {
    grid-template-columns: minmax(0, 3fr) minmax(0, 5fr) minmax(0, 2fr) minmax(0, 2fr);
    grid-template-areas: "date venue city status";
    align-items: center;
  }
  .gigrow--past {
    grid-template-columns: minmax(0, 3fr) minmax(0, 4fr) minmax(0, 2fr) minmax(0, 5fr);
    grid-template-areas: "date venue city with";
  }
  .gigrow__label { display: none; }
  .gigrow__with:empty { display: block; }
  .gigrow { min-height: var(--ctl-lg); padding-block: var(--s2); }
  .gigrow__status { justify-content: flex-start; }
  .gigrows__head { display: grid; column-gap: var(--s4); padding: var(--s2) var(--pad); background: var(--c-fill); }
  .gigrow--empty, .gigrow--ph { display: block; }
}

/* ---- src/components/shop.css ---- */
/* Shop: product card and grid, and the product page parts (photos, buy box, details). */

/* cards: a ruled grid of tees, 2 across on a phone; from 1024, 4 or 5 across, or wide: two across with the photo
   beside the text (one or two tees) */
.cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.cards > li) { background: var(--c-bg); min-width: 0; display: flex; }
/* a short last row: its last card fills it, with its photo kept one column wide beside the text
   (so one tee fills its row with the photo the size of the others) */
.cards > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
.cards > li:last-child:nth-child(odd) .card { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cards > li:last-child:nth-child(odd) .card__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
.cards > li:last-child:nth-child(odd) .card__body { align-content: start; }
@media (min-width: 1024px) {
  .cards--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cards--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .cards--wide > li:last-child:nth-child(odd) { grid-column: auto; }
  .cards--wide .card { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cards--wide .card__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
  .cards--wide .card__body { align-content: start; }
  :is(.cards--4, .cards--5) > li:last-child:nth-child(odd) { grid-column: auto; }
  :is(.cards--4, .cards--5) > li:last-child:nth-child(odd) .card { display: flex; }
  :is(.cards--4, .cards--5) > li:last-child:nth-child(odd) .card__media { border-bottom: var(--rule) solid var(--c-rule); border-right: 0; }
  .cards--4 > li:last-child:nth-child(4n+1) { grid-column: span 4; }
  .cards--4 > li:last-child:nth-child(4n+2) { grid-column: span 3; }
  .cards--4 > li:last-child:nth-child(4n+3) { grid-column: span 2; }
  .cards--5 > li:last-child:nth-child(5n+1) { grid-column: span 5; }
  .cards--5 > li:last-child:nth-child(5n+2) { grid-column: span 4; }
  .cards--5 > li:last-child:nth-child(5n+3) { grid-column: span 3; }
  .cards--5 > li:last-child:nth-child(5n+4) { grid-column: span 2; }
  :is(.cards--4 > li:last-child:not(:nth-child(4n)), .cards--5 > li:last-child:not(:nth-child(5n))) .card { display: grid; }
  :is(.cards--4 > li:last-child:not(:nth-child(4n)), .cards--5 > li:last-child:not(:nth-child(5n))) .card__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
  :is(.cards--4 > li:last-child:not(:nth-child(4n)), .cards--5 > li:last-child:not(:nth-child(5n))) .card__body { grid-column: 2 / -1; align-content: start; }
  .cards--4 > li:last-child:nth-child(4n+1) .card { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cards--4 > li:last-child:nth-child(4n+2) .card { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cards--4 > li:last-child:nth-child(4n+3) .card { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cards--5 > li:last-child:nth-child(5n+1) .card { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .cards--5 > li:last-child:nth-child(5n+2) .card { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cards--5 > li:last-child:nth-child(5n+3) .card { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cards--5 > li:last-child:nth-child(5n+4) .card { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.card { flex: 1; display: flex; flex-direction: column; text-decoration: none; }
.card__media { display: block; border-bottom: var(--rule) solid var(--c-rule); }
.card__media img { width: 100%; aspect-ratio: var(--ratio-square); object-fit: cover; }
.card__body {
  flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "name name" "meta price";
  align-items: end; gap: var(--gap-stack);
  padding: var(--pad);
}
.card__name { grid-area: name; font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.card__meta { grid-area: meta; }
.card__price { grid-area: price; font: var(--type-value); letter-spacing: var(--ls-caps); }
.card:hover, .card.is-hover { background: var(--c-here); }
.card:hover .label, .card.is-hover .label { color: var(--c-text); }
.card:active, .card.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.card:active .label, .card.is-active .label { color: var(--c-on-bar); }
.card:focus-visible, .card.is-focus { outline-offset: calc(-1 * var(--rule-thick)); }

/* product page: photos 7/12, buy box 5/12 from 1024; stacked on a phone */
.product { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.product > *) { background: var(--c-bg); min-width: 0; }
/* inside each column, parts are divided by a top rule (not the gap trick), so a shorter column stays white */
.product__media { display: flex; flex-direction: column; }
.product__media img { width: 100%; }
.product__media img + img { border-top: var(--rule) solid var(--c-rule); }
.product__info { display: flex; flex-direction: column; }
.product__info > * { min-width: 0; }
.product__info > * + * { border-top: var(--rule) solid var(--c-rule); }
.product__name { padding: var(--pad); font: var(--type-name); letter-spacing: var(--ls-caps); text-transform: uppercase; overflow-wrap: anywhere; }
.product__price { padding: var(--pad); font: var(--type-price-lg); letter-spacing: var(--ls-caps); }
.product__info .formcells { border-width: var(--rule) 0 0; }
.product__details { display: flex; flex-direction: column; gap: var(--gap-stack); padding: var(--pad); }
@media (min-width: 1024px) {
  .product { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
}

/* measuring diagram: a flat tee in grey with the measurements in black, at its drawn size (320 wide), shrinking
   with a narrower cell. Strokes stay one rule wide at any size. */
.teefig { display: block; min-width: 0; }
.teefig__svg { display: block; max-width: 100%; height: auto; }
.teefig__tee, .teefig__seam, .teefig__line { stroke-width: var(--rule); vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.teefig__tee { fill: var(--c-bg); stroke: var(--c-text-2); }
.teefig__seam { fill: none; stroke: var(--c-text-2); }
.teefig__line { fill: none; stroke: var(--c-text); }
.teefig__label { font: var(--type-mono-small); letter-spacing: var(--ls-label); text-transform: uppercase; fill: var(--c-text); }

/* size guide: how it is measured (words and diagram), then the table; side by side from 1024, the table under
   the product photos and the diagram under the buy box */
.sizeguide { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: "how" "chart"; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.sizeguide > *) { background: var(--c-bg); min-width: 0; }
.sizeguide__how { grid-area: how; display: flex; flex-direction: column; gap: var(--s4); padding: var(--pad); }
.sizeguide__note { max-width: 40em; font: var(--type-body); }
.sizeguide__chart { grid-area: chart; }
@media (min-width: 1024px) {
  .sizeguide { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); grid-template-areas: "chart how"; }
}

/* size table: ruled cells in equal columns, the head row on the fill; flush with its cell, so each cell draws
   only its bottom and left rules and the outer edge is the cell's own */
.sizes { width: 100%; border-collapse: separate; border-spacing: 0; table-layout: fixed; }
.sizes th, .sizes td { padding: var(--s3) var(--pad); border: 0 solid var(--c-rule); border-width: 0 0 var(--rule) var(--rule); text-align: left; }
.sizes tr > :first-child { border-left-width: 0; }
.sizes tbody tr:last-child > * { border-bottom-width: 0; }
.sizes thead th { font: var(--type-label-strong); letter-spacing: var(--ls-label); text-transform: uppercase; background: var(--c-fill); }
.sizes tbody th, .sizes td { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }

/* ---- src/components/bag.css ---- */
/* Bag and checkout: bag line, totals, order summary lines. */

.lines { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.lines > li) { background: var(--c-bg); min-width: 0; }

/* bag line: photo and name, then size, quantity, price and Remove */
.line {
  display: grid; grid-template-columns: var(--thumb) minmax(0, 1fr);
  grid-template-areas: "media main" "controls controls";
  gap: var(--s4) var(--pad); padding: var(--pad);
}
.line__media { grid-area: media; display: block; border: var(--rule) solid var(--c-rule); align-self: start; }
.line__media img { width: 100%; aspect-ratio: var(--ratio-square); object-fit: cover; }
.line__main { grid-area: main; display: flex; flex-direction: column; gap: var(--gap-stack); min-width: 0; }
.line__name {
  align-self: flex-start; display: inline-flex; align-items: center; min-height: var(--ctl);
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase;
  text-decoration: underline; text-decoration-thickness: var(--rule); text-underline-offset: var(--s1);
}
.line__name:hover { background: var(--c-here); }
.line__facts { display: flex; flex-wrap: wrap; gap: var(--gap-label) var(--s4); }
.line__facts > div { display: flex; gap: var(--s2); align-items: baseline; }
.line__facts dd { font: var(--type-body-strong); }
.line__price { font: var(--type-value); letter-spacing: var(--ls-caps); }
.line__controls { grid-area: controls; display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s4); }
.line__controls .field { flex: 1 1 calc(var(--ctl) * 2); }
.line__controls .select-wrap { min-width: calc(var(--ctl) * 2); }
.line__controls .btn { margin-left: auto; }
@media (min-width: 1024px) {
  .line { grid-template-columns: var(--thumb) minmax(0, 1fr) auto; grid-template-areas: "media main controls"; align-items: center; }
  .line__controls { flex-wrap: nowrap; }
  .line__controls .field { flex: none; width: calc(var(--ctl) * 3); }
}

/* totals: label left, amount right, total last and heavier */
.totals { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
.totals__row { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); min-height: var(--ctl); padding: 0 var(--pad); background: var(--c-bg); }
.totals__row dt { font: var(--type-label-strong); letter-spacing: var(--ls-label); text-transform: uppercase; }
.totals__row dd { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: 0 var(--s2); font: var(--type-value); letter-spacing: var(--ls-caps); text-align: right; }
.totals__row--total { min-height: var(--ctl-lg); }
.totals__row--total dd { font: var(--type-title-m); letter-spacing: var(--ls-caps); }
.totals__extra { font: var(--type-small); letter-spacing: 0; }

/* order summary line: compact, no controls */
.sumline { display: grid; grid-template-columns: var(--ctl-lg) minmax(0, 1fr) auto; gap: var(--pad); align-items: start; padding: var(--pad); }
.sumline__media { border: var(--rule) solid var(--c-rule); }
.sumline__media img { width: 100%; aspect-ratio: var(--ratio-square); object-fit: cover; }
.sumline__main { display: flex; flex-direction: column; gap: var(--gap-label); min-width: 0; }
.sumline__name { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.sumline__price { font: var(--type-value); letter-spacing: var(--ls-caps); }

/* ---- src/components/music.css ---- */
/* Music: release card and grid, the release page block, tracklist, video card and grid. */

.rels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.rels > li) { background: var(--c-bg); min-width: 0; display: flex; }
/* row (Music): two across on a phone, where the last of an odd number fills its row with the sleeve beside the
   text (the same sleeve size as the rest); every release in one row from 1024 */
.rels--row > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
.rels--row > li:last-child:nth-child(odd) .rel { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rels--row > li:last-child:nth-child(odd) .rel__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
@media (min-width: 1024px) {
  .rels--row { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
  .rels--row > li:last-child:nth-child(odd) { grid-column: auto; }
  .rels--row > li:last-child:nth-child(odd) .rel { display: flex; }
  .rels--row > li:last-child:nth-child(odd) .rel__media { border-bottom: var(--rule) solid var(--c-rule); border-right: 0; }
}
.rel { flex: 1; display: flex; flex-direction: column; text-decoration: none; }
.rel__media { display: block; border-bottom: var(--rule) solid var(--c-rule); }
.rel__media img { width: 100%; aspect-ratio: var(--ratio-square); object-fit: cover; }
.rel__body { flex: 1; display: flex; flex-direction: column; gap: var(--gap-stack); padding: var(--pad); }
.rel__title { font: var(--type-title-s); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.rel__meta { display: flex; flex-wrap: wrap; gap: var(--gap-label) var(--s2); align-items: center; }
.rel:hover, .rel.is-hover { background: var(--c-here); }
.rel:hover .label, .rel.is-hover .label { color: var(--c-text); }
.rel:active, .rel.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.rel:active .label, .rel.is-active .label { color: var(--c-on-bar); }
.rel:focus-visible, .rel.is-focus { outline-offset: calc(-1 * var(--rule-thick)); }
/* wide: sleeve beside the text, for two releases across a desktop page */
@media (min-width: 1024px) {
  .rels--wide .rel { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rels--wide .rel__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
}

/* release page: sleeve 7/12, facts and links 5/12 from 1024 */
.release { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.release > *) { background: var(--c-bg); min-width: 0; }
.release__media img { width: 100%; }
.release__info { display: flex; flex-direction: column; }
.release__info > * { min-width: 0; }
.release__info > * + * { border-top: var(--rule) solid var(--c-rule); }
.release__info .facts { border-width: var(--rule) 0 0; }
.release__title { padding: var(--pad); font: var(--type-name); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.release__block { display: flex; flex-direction: column; gap: var(--gap-stack); padding: var(--pad); }
.release__block > .link { align-self: flex-start; }
/* Buy: one square box per download format (MP3, WAV), side by side */
.buy { display: flex; gap: var(--s2); }
.buy__box {
  display: flex; align-items: center; justify-content: center;
  width: var(--s9); aspect-ratio: var(--ratio-square);
  border: var(--rule) solid var(--c-rule);
  font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; text-decoration: none;
}
.buy__box:hover, .buy__box.is-hover { background: var(--c-here); }
.buy__box:active, .buy__box.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.buy__box:focus-visible, .buy__box.is-focus { outline-offset: calc(-2 * var(--rule-thick)); }
@media (min-width: 1024px) { .release { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); } }

/* tracklist: numbered rows; the numbers are the real track numbers */
.tracks { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.tracks > li) { display: grid; grid-template-columns: var(--ctl) minmax(0, 1fr); align-items: center; gap: var(--s3); min-height: var(--ctl); padding: 0 var(--pad); background: var(--c-bg); }
.tracks__no { font: var(--type-mono-small); color: var(--c-text-2); }
.tracks__title { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; padding-block: var(--s3); }

/* video card: YouTube's thumbnail at 16:9, the song, the year; the whole card links out to the video */
.vids { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.vids > li) { background: var(--c-bg); min-width: 0; display: flex; }
/* a short last row: its last card fills it, with its thumbnail kept one column wide beside the text */
.vids > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
.vids > li:last-child:nth-child(odd) .vid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.vids > li:last-child:nth-child(odd) .vid__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
@media (min-width: 1024px) {
  .vids { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .vids > li:last-child:nth-child(odd) { grid-column: auto; }
  .vids > li:last-child:nth-child(odd) .vid { display: flex; }
  .vids > li:last-child:nth-child(odd) .vid__media { border-bottom: var(--rule) solid var(--c-rule); border-right: 0; }
  .vids > li:last-child:nth-child(4n+1) { grid-column: span 4; }
  .vids > li:last-child:nth-child(4n+2) { grid-column: span 3; }
  .vids > li:last-child:nth-child(4n+3) { grid-column: span 2; }
  .vids > li:last-child:is(:nth-child(4n+1), :nth-child(4n+2), :nth-child(4n+3)) .vid { display: grid; }
  .vids > li:last-child:is(:nth-child(4n+1), :nth-child(4n+2), :nth-child(4n+3)) .vid__media { border-bottom: 0; border-right: var(--rule) solid var(--c-rule); }
  .vids > li:last-child:is(:nth-child(4n+1), :nth-child(4n+2), :nth-child(4n+3)) .vid__body { grid-column: 2 / -1; }
  .vids > li:last-child:nth-child(4n+1) .vid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .vids > li:last-child:nth-child(4n+2) .vid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .vids > li:last-child:nth-child(4n+3) .vid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.vid { flex: 1; display: flex; flex-direction: column; text-decoration: none; }
.vid__media { display: block; border-bottom: var(--rule) solid var(--c-rule); }
.vid__media img { width: 100%; aspect-ratio: var(--ratio-video); object-fit: cover; }
.vid__body { flex: 1; display: flex; flex-direction: column; gap: var(--gap-label); padding: var(--pad); }
.vid__title { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.vid:hover, .vid.is-hover { background: var(--c-here); }
.vid:hover .label, .vid.is-hover .label { color: var(--c-text); }
.vid:active, .vid.is-active { background: var(--c-bar); color: var(--c-on-bar); }
.vid:active .label, .vid.is-active .label { color: var(--c-on-bar); }
.vid:focus-visible, .vid.is-focus { outline-offset: calc(-1 * var(--rule-thick)); }

/* ---- src/components/content.css ---- */
/* Content blocks: photo, prose. */

/* photo: every band photo is shown at 3:2 (the hero at 21:9 from 1024), filling its cell edge to edge */
.photo { display: block; border: var(--rule) solid var(--c-rule); }
.photo img { width: 100%; aspect-ratio: var(--ratio-photo); object-fit: cover; }
.photo--hero img { aspect-ratio: var(--ratio-hero); object-position: 50% 35%; }
:is(.split, .photos, .stack) .photo { border-width: 0; }
.stack > .photo:not(:first-child) { border-width: 0 var(--rule) var(--rule); }

.photos { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.photos > li) { background: var(--c-bg); min-width: 0; }
@media (min-width: 1024px) { .photos { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* prose: running text (legal pages, bio). Lines stop at a readable length. */
.prose { display: flex; flex-direction: column; gap: var(--s4); padding: var(--pad); border: var(--rule) solid var(--c-rule); }
.prose > p { max-width: 40em; font: var(--type-body); }
.prose__head { font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.prose > .ph { min-height: var(--s9); }
.split > .prose { border: 0; }

/* ---- src/components/ballot.css ---- */
/* The ballot: Setlist Democracy (concept A, "Ballot paper"). The desk is the page's layout, the ballot beside the top 10
   and the gig, over a polling-station photo that covers the whole page; a sheet is a white box with the site's box
   edge, as on every other page; the ballot is the vote; a candidate (cand) is a song as a ballot row; the declaration
   (decl) is the top 10. Every size, gap and colour is a token, as on every page. */

/* ------------------------------------------------------------------ desk */
/* The photo behind the whole page, edge to edge and top to bottom, behind the header and the footer as well (Antony,
   topic 362 msg 5398; the shelf of 27 Oct sat behind the main part only and left white round it on a phone, msgs
   5321-5322). It is fixed to the screen, so it stays still as the page scrolls over it, and as tall as the screen with
   a phone's bars hidden (lvh; vh where lvh is unknown), so it never runs short as the bars come and go. The page under
   it is the desk colour, not white: that shows while the photo loads and if a phone pulls the page past either end.
   Every word on the page sits on the site's own white boxes and black bars (the event header's bar is white: frame.css),
   never on the photo, so the photo can be any of the eight, the four photos or the four acid patterns (msg 5436). */
/* It reaches 96 px past the screen's top and bottom (topic 362 msg 5477, 28 Oct: on an iPhone the page showed the grey
   desk colour in a band at the top while scrolling). Safari now draws the page on under its address bar as that bar
   shrinks, but a fixed box with top 0 starts below the bar, so the band under the bar had no photo. */
.desk__photo { position: fixed; top: calc(-1 * var(--s9)); left: 0; z-index: -1; width: 100%; height: calc(100vh + 2 * var(--s9)); height: calc(100lvh + 2 * var(--s9)); }
.desk__photo img { width: 100%; height: 100%; object-fit: cover; }
html:has(body > .desk__photo) { background: var(--c-desk); }
/* Under an acid pattern (5 to 8), on an upright screen, the page itself carries the same pattern, a screen wide and
   repeated down the page, on black, so wherever a phone shows past the photo it shows the pattern, not grey (topic 362
   msg 5494, 29 Oct: the 96 px reach fixed the top but not the bottom). It scrolls with the page, so it can't line up
   with the photo, but on an all-over collage the join doesn't read. The photos (1 to 4) don't repeat, so they keep the
   desk colour. It is the same file the photo shows on an upright screen, so it costs no second download. */
@media (orientation: portrait) {
  html:has(body > .desk__photo[data-n="5"]) { background: var(--black) url(img/setlist/backdrop-5-tall-1024.jpg) top center / 100% auto repeat-y; }
  html:has(body > .desk__photo[data-n="6"]) { background: var(--black) url(img/setlist/backdrop-6-tall-1024.jpg) top center / 100% auto repeat-y; }
  html:has(body > .desk__photo[data-n="7"]) { background: var(--black) url(img/setlist/backdrop-7-tall-1024.jpg) top center / 100% auto repeat-y; }
  html:has(body > .desk__photo[data-n="8"]) { background: var(--black) url(img/setlist/backdrop-8-tall-1024.jpg) top center / 100% auto repeat-y; }
}
html:has(body > .desk__photo) body { background: none; }   /* the photo lies between the page's colour and the boxes */
/* A black focus ring drawn outside a box would vanish on a dark photo. Two were: the footer's logo cell and Square9's
   stamp. Over the photo they draw it inside, 4 px in from the edge, as the header's and footer's other cells always do.
   The stamp's face is drawn over its link, so a ring inside the link would be hidden: the stamp's ring goes on the face. */
html:has(body > .desk__photo) .foot__brand:is(:focus-visible, .is-focus) { outline-offset: calc(-2 * var(--rule-thick)); }
html:has(body > .desk__photo) .foot__credit-link:is(:focus-visible, .is-focus) { outline: none; }
html:has(body > .desk__photo) .foot__credit-link:is(:focus-visible, .is-focus) .foot__credit-card { outline: var(--rule-thick) solid var(--c-text); outline-offset: calc(-2 * var(--rule-thick)); }
.desk__layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--block); align-items: start; }
.desk__main, .desk__side { display: grid; gap: var(--block); min-width: 0; }
@media (min-width: 1024px) {
  /* the ballot eight columns wide and the side four, the site's main and side split (layout.css .split), edge to edge
     with the header and footer (topic 362, after msg 5325: the one-column inset looked like a mistake on plain white) */
  .desk__layout { grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); }
  .desk__main { grid-column: 1 / span 8; }
  .desk__side { grid-column: 9 / span 4; }
}

/* ------------------------------------------------------------------ sheet */
.sheet { min-width: 0; background: var(--c-bg); border: var(--rule) solid var(--c-rule); }
.sheet .gig { border-width: 0; }   /* the sheet's edge is its edge */

/* ------------------------------------------------------------------ ballot */
.ballot__head { display: flex; flex-direction: column; gap: var(--s3); padding: var(--s4) var(--pad) var(--s3); border-bottom: var(--rule) solid var(--c-rule); }
.ballot__title { font: var(--type-ballot); letter-spacing: var(--ls-caps); text-transform: uppercase; }
.ballot__rules { display: grid; grid-template-columns: minmax(0, 1fr) var(--ctl-lg); gap: var(--s3); align-items: center; }
.ballot__lines { display: flex; flex-direction: column; }
.ballot__line { font: var(--type-body); }
.ballot__line--strong { font: var(--type-body-strong); }
.ballot__mark { width: var(--ctl-lg); height: var(--ctl-lg); padding: var(--s2); border: var(--rule) solid var(--c-rule); }

/* the part under the head that changes with each step (search, picked, voted, closed) */
.ballot__body { display: flex; flex-direction: column; min-width: 0; }
.ballot__part { display: flex; flex-direction: column; gap: var(--s4); padding: var(--pad); }
.ballot__said { display: flex; flex-direction: column; gap: var(--s2); }
.ballot__form { display: flex; flex-direction: column; gap: var(--s3); }
.ballot__check:empty { display: none; }
/* Here the field you are in and the news box stay white instead of turning --c-here; the focus ring, the ticked box's
   4px edge and its tick still show (Antony, topic 362 msg 5494, 29 Oct: "I don't think we need the live text box or
   tick to go yellow on this page"). On the ballot's steps, so the reference's specimens show it too. */
.ballot__part .input:is(:focus, .is-focus) { background: var(--c-bg); }
.ballot__part .tick__input:is(:hover, :checked) + .tick__box, .ballot__part .tick:is(.is-hover, .is-checked) .tick__box { background: var(--c-bg); }
.ballot__voted { font: var(--type-voted); letter-spacing: var(--ls-caps); text-transform: uppercase; overflow-wrap: anywhere; }
.ballot__voted:focus { outline: none; }
.ballot__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
.ballot__foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 var(--s4); }
.ballot__note, .ballot__status { font: var(--type-body-strong); }
.ballot__status:empty { display: none; }

/* closed: the final top 5 under Voting closed, ruled off from it */
.ballot__body > .decl { border-top: var(--rule) solid var(--c-rule); }

/* the search: the list drops down under the field, in the paper, as ballot rows */
.combo { display: flex; flex-direction: column; min-width: 0; }

/* ------------------------------------------------------------------ candidates (songs as ballot rows) */
.cands { display: grid; gap: var(--rule); background: var(--c-rule); border: var(--rule) solid var(--c-rule); }
:where(.cands > li) { background: var(--c-bg); min-width: 0; }
.cands--drop { border-top-width: 0; }
.cands__none { padding: var(--pad); font: var(--type-body-strong); }
.cand {
  display: grid; grid-template-columns: minmax(0, 1fr) var(--ctl) var(--ctl); gap: var(--s3); align-items: center;
  min-height: var(--ctl-lg); padding: var(--s2) var(--s3);
}
.cand[role="option"]:not([aria-disabled="true"]) { cursor: pointer; }   /* a song in the setlist gets not-allowed (.cand--set) */
/* the row under the pointer, and the one the arrow keys are on (with a 4px edge, for those who can't tell the colour) */
.cand[role="option"]:not([aria-disabled="true"]):hover, .cand.is-hover { background: var(--c-here); }
.cand[aria-selected="true"] { background: var(--c-here); box-shadow: inset 0 0 0 var(--rule) var(--c-rule); }
.cand--picked { border: var(--rule) solid var(--c-rule); }
.cand__text { min-width: 0; }
.cand__name { display: block; font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; overflow-wrap: anywhere; }
.cand__name mark { background: none; color: inherit; text-decoration: underline; text-decoration-thickness: var(--rule-thick); text-underline-offset: var(--s1); }
.cand__with { display: block; font: var(--type-small); }
.cand__release { display: block; font: var(--type-small); color: var(--c-text-2); }
.cand__year::before { content: ", "; }
/* already in the setlist (topic 362 msg 5494): greyed words, a dashed empty box, and the line that says why; never lit,
   never picked */
.cand--set { cursor: not-allowed; background: var(--c-fill); }   /* a slight grey (topic 362 msg 5513) */
.cand--set :is(.cand__name, .cand__with, .cand__release) { color: var(--c-text-2); }
.cand--set .cand__box { border-style: dashed; border-color: var(--c-text-2); background: var(--c-fill); }
.cand--set .cand__emblem { filter: var(--emblem-filter); }   /* its sleeve in black and white; the others are in colour */
.cand__set { display: block; margin-top: var(--s1); font: var(--type-label-strong); letter-spacing: var(--ls-label); text-transform: uppercase; }
/* the release's sleeve in full colour (Antony, topic 362 msg 5513: "make the album covers in the available tunes full
   colour"); a song in the setlist keeps it in black and white (.cand--set above) */
.cand__emblem { width: var(--ctl); height: var(--ctl); object-fit: cover; }
/* the box, and the pencil cross over it: a little bigger than the box, as a cross drawn by hand is; multiply lets the
   box's rule show through the cross's white */
.cand__box { position: relative; width: var(--ctl); height: var(--ctl); border: var(--rule) solid var(--c-rule); background: var(--c-bg); }
.cand__cross {
  position: absolute; top: calc(-1 * var(--s2)); left: calc(-1 * var(--s2));
  width: calc(100% + 2 * var(--s2)); height: calc(100% + 2 * var(--s2)); max-width: none;
  mix-blend-mode: multiply;
}

/* ------------------------------------------------------------------ declaration (the top 10) */
.decl { display: grid; gap: var(--rule); background: var(--c-rule); }
:where(.decl > li) { background: var(--c-bg); min-width: 0; }
.decl__row {
  display: grid; grid-template-columns: var(--ctl) minmax(0, 1fr); gap: var(--s2); align-items: center;
  min-height: var(--ctl-lg); padding: var(--s2) var(--pad);
}
.decl__n { font: var(--type-rank); letter-spacing: var(--ls-caps); }
.decl__song { min-width: 0; }
.decl__title { display: block; font: var(--type-value); letter-spacing: var(--ls-caps); text-transform: uppercase; overflow-wrap: anywhere; }
.decl__with { display: block; font: var(--type-small); }
.decl__release { display: block; font: var(--type-small); color: var(--c-text-2); }
/* the yellow line under the songs that get played, with a pointer up at them */
.decl__cut { display: flex; align-items: center; gap: var(--s2); padding: var(--s1) var(--pad); background: var(--c-here); }
:where(.decl > .decl__cut) { background: var(--c-here); }
.decl__cut::before { content: ""; border-left: var(--s1) solid transparent; border-right: var(--s1) solid transparent; border-bottom: var(--s2) solid var(--c-text); }
.decl__cut .label { color: var(--c-text); }
.decl__empty { padding: var(--pad); font: var(--type-body-strong); }
