/* ═══════════════════════════════════════════════════════════════
   HOLOCENE v10 — "DITHER" design system
   A plotted computer-art print · live 1-bit dithering · generative
   glyph field · registration marks & stamps · Print/Negative/Riso
   Shared by every page; page-specific rules live in each page.
   ═══════════════════════════════════════════════════════════════ */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* ── palette ──
   SPECTRA is the DEFAULT plate — the view a cold visitor gets, and therefore
   the one WCAG 2.1 AA is scoped to. It conforms: body ink 19.60:1, muted
   5.33:1, focus 6.32:1, every accent pairing over 4.5:1 (3:1 for large text
   and non-text). PRINT, NEGATIVE, FLUORO, MERIDIAN, BLUEPRINT, OXBLOOD and FOREST also pass — each carries
   its measured ratios beside its tokens below. RISO is a deliberately
   non-conforming opt-in plate, held outside the conformance scope on purpose.

   (This paragraph used to name PRINT as the default, which stopped being true
   when the default moved to spectra. It is worth keeping accurate: it is what
   tells the next person which single plate an accessibility audit must clear.)

   --focus is separate from --acc precisely so a skin may take its accent
   somewhere illegible without taking the focus ring with it. Every skin must
   define a --focus that clears 3:1 against its own --paper. Non-negotiable:
   the focus ring is how a keyboard user finds the way back out. */
:root {
  --paper: #f2efe7;
  --ink:   #16140f;
  --mute:  #716d5a;     /* 4.53:1 on --paper. Was #77735f = 4.15:1 (failed) */
  --line:  #16140f;
  --acc:   #5b00ff;
  --acc-ink: #f2efe7;   /* text over accent fills */
  --acc-on-ink: #b48cff;/* accent ON an --ink surface (ribbon). 7.13:1 */
  --acc2:  var(--acc);  /* second highlight. Follows --acc, so a plate that does
                           not name its own gets its OWN highlight here — it was
                           written as the literal #5b00ff, which meant negative
                           and riso showed print's purple instead of theirs.
                           fluoro and spectra still take it somewhere else. */
  --focus: #5b00ff;     /* 6.33:1 on --paper */
  /* EARLY / LATE on a night that carries two shows. Two pens of their own on
     every plate: EARLY is always the WARMER of the two (a red, a maroon, an
     amber) and LATE the cooler (a blue, a teal, a lavender), so the pair reads
     by temperature before it reads by word. Neither is the pen the dates
     already use, and neither is --acc, so the marker reads as a different
     KIND of fact and not as more date. Every skin's pair clears AA as type on
     its own --paper — that is the only way the marks are used (see
     .spec-when, .crow-mark, .spec-mark). */
  --mark-early: #b8410a;/* vermilion. 4.81:1 on --paper */
  --mark-late:  #1a5f7a;/* petrol blue. 6.17:1 — not the cobalt or green the dates take */
  /* How far the cursor reaches: the hero's x-ray disc (--xr). */
  --prox: 234px;
  /* How close the cursor has to come to a plate before its colour comes up
     through the dither, measured to the plate's nearest edge. Two thirds of
     the disc: at the disc's own reach the posters were lighting up before
     the cursor had really arrived. */
  --prox-plate: 156px;

  --ev-a0: #5b00ff;
  --ev-a1: #16140f;
  --ev-a2: #5b00ff;
  --ev-a3: #16140f;

  --disp: 'Bricolage Grotesque', 'Arial Black', sans-serif;
  --mono: 'Martian Mono', ui-monospace, monospace;

  --pad: clamp(1.1rem, 3.6vw, 3.6rem);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* PRINT — the bare plate: off-white paper, near-black ink, and a plotter's
   pen rack. It stayed a one-pen print for a long time (violet, and nothing
   else) while SPECTRA and MERIDIAN each carried five, which made the plate
   with the least colour also the one that had earned the most. Five pens now:
   the same electric violet leads, and beside it magenta, vermilion, cobalt
   and a pen green — all saturated, all DARK, because on this paper a pen is
   ink and never a fill. Every one clears AA as type on the paper (violet
   6.33:1, magenta 4.89:1, vermilion 4.81:1, cobalt 6.36:1, green 4.74:1),
   and paper type clears the same ratios on any of them as a solid.

   Where SPECTRA blocks and MERIDIAN draws, PRINT writes: the colour is in the
   letters and the frames, one pen per element, and the page never fills a
   shape with anything but the ink or a pen. Scoped with :not([data-skin])
   because PRINT has no attribute — it IS the absence of one. Two tints for
   the ribbon bar, which is ink and needs light colour on it (11.33:1 and
   7.13:1). */
:root {
  --p-violet: #5b00ff; --p-mag: #c8007a; --p-verm: #b8410a;
  --p-cob: #0047d6; --p-green: #0a7a3a;
  --p-mint-on-ink: #5ee39a;
}
html:not([data-skin]) {
  --acc2: var(--p-green);   /* WHAT'S NEXT, the stamp, the sparks: green, not violet again */
  --ev-a0: #5b00ff; --ev-a1: #c8007a; --ev-a2: #0047d6; --ev-a3: #0a7a3a;
}

/* Wordmark: hover turns over to magenta, which is the pen violet sits beside. */
html:not([data-skin]) .bar-logo:hover { background-color: var(--p-mag); }

/* Nav. The base hover is an ink frame; here each item draws its frame AND its
   label in its own pen, so the bar is a rack of five and shows one at a time.
   The page you are on keeps a violet frame with an ink label, which is the one
   combination hovering never produces. */
html:not([data-skin]) .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { color: var(--p-violet); border-color: var(--p-violet); }
html:not([data-skin]) .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { color: var(--p-mag);    border-color: var(--p-mag); }
html:not([data-skin]) .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { color: var(--p-verm);   border-color: var(--p-verm); }
html:not([data-skin]) .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { color: var(--p-cob);    border-color: var(--p-cob); }
html:not([data-skin]) .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { color: var(--p-green);  border-color: var(--p-green); }
html:not([data-skin]) .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--p-violet); }
/* BOOK THE ROOM flips to vermilion, so the bar's hovers are not all violet. */
html:not([data-skin]) .nav-links a.nav-cta:hover { background: var(--p-verm); color: var(--paper); }

/* Fullscreen menu — one pen per line. */
html:not([data-skin]) .menu a.menu-link:nth-child(5n+1):hover { color: var(--p-violet); }
html:not([data-skin]) .menu a.menu-link:nth-child(5n+2):hover { color: var(--p-mag); }
html:not([data-skin]) .menu a.menu-link:nth-child(5n+3):hover { color: var(--p-verm); }
html:not([data-skin]) .menu a.menu-link:nth-child(5n+4):hover { color: var(--p-cob); }
html:not([data-skin]) .menu a.menu-link:nth-child(5n+5):hover { color: var(--p-green); }

/* Highlighted words. The hero and the plate captions keep the violet; every
   other heading family takes a different pen, so the rack is visible down a
   page and no two neighbouring sections are underlined in the same colour. */
html:not([data-skin]) .mdiv h2 i,
html:not([data-skin]) .prose h2 i        { color: var(--p-cob); }
html:not([data-skin]) .cta-band h2 i     { color: var(--p-mag); }
html:not([data-skin]) .tech-cap i,
html:not([data-skin]) .stat-num i        { color: var(--p-verm); }
html:not([data-skin]) .hh h2 i,
html:not([data-skin]) .hh h2 .t-serif    { color: var(--p-mag); }
html:not([data-skin]) .cta-band::before  { color: var(--p-green); }
html:not([data-skin]) .cta-band::after   { color: var(--p-cob); }
html:not([data-skin]) .hero-side u,
html:not([data-skin]) .hero-annot .desc u { text-decoration-color: var(--p-mag); text-decoration-thickness: 3px; }

/* Dates: pen type, rotating down the grid. The list view takes cobalt so the
   two views of a month are not the same page. */
html:not([data-skin]) .spec:nth-child(4n+2) .spec-date { color: var(--p-mag); }
html:not([data-skin]) .spec:nth-child(4n+3) .spec-date { color: var(--p-green); }
html:not([data-skin]) .spec:nth-child(4n+4) .spec-date { color: var(--p-cob); }
html:not([data-skin]) .crow-date { color: var(--p-cob); }

/* Solids: buttons stay violet, the newsletter goes green, the venue strip
   marks "here" in cobalt. Paper type on all three. */
@media (hover: hover) { html:not([data-skin]) .newsletter-form button:hover { background: var(--p-green); color: var(--paper); } }
html:not([data-skin]) ::selection { background: var(--p-mag); color: var(--paper); }

/* Ribbon: the bar is ink, so the shows run in a mint tint beside the lavender
   lead — two tints, not one, so the belt is not a single colour of type. */
html:not([data-skin]) .ribbon-track span.hot { color: var(--p-mint-on-ink); }

/* Plot layer — decoration, so the pens can go on at low strength. */
html:not([data-skin]) .plot .ring { border-color: color-mix(in srgb, var(--p-mag) 45%, transparent); }
html:not([data-skin]) .plot .ring::after { border-color: color-mix(in srgb, var(--p-cob) 35%, transparent); }
html:not([data-skin]) .plot .cross-h { background: color-mix(in srgb, var(--p-green) 35%, transparent); }
html:not([data-skin]) .plot .cross-v { background: color-mix(in srgb, var(--p-violet) 45%, transparent); }
html:not([data-skin]) .plot .reg { border-color: var(--p-violet); opacity: 1; }
html:not([data-skin]) .quote { border-left-color: var(--p-verm); }
html:not([data-skin]) .hh { border-color: var(--p-green); }
html:not([data-skin]) .dmap .pin .ring { border-color: var(--p-mag); }

/* Footer heads, one pen each. */
html:not([data-skin]) .foot-col:nth-child(1) .foot-col-title { color: var(--p-violet); }
html:not([data-skin]) .foot-col:nth-child(2) .foot-col-title { color: var(--p-mag); }
html:not([data-skin]) .foot-col:nth-child(3) .foot-col-title { color: var(--p-green); }
html:not([data-skin]) .newsletter-label { color: var(--p-cob); }

/* ── skins ── */
[data-skin="negative"] {
  --paper: #0e0d0a; --ink: #f2efe7; --mute: #8b8776; --line: #f2efe7;
  --acc: #9a5cff; --acc-ink: #0e0d0a;
  --acc-on-ink: #5b00ff;/* --ink is the LIGHT colour here, so this goes dark. 6.33:1 */
  --focus: #9a5cff;     /* 4.95:1 on --paper */
  --mark-early: #ffb245;/* warm amber. 10.83:1 on the near-black stock */
  --mark-late:  #7ec4ff;/* sky blue. 10.39:1 */
  --ev-a0: #9a5cff; --ev-a1: #f2efe7; --ev-a2: #9a5cff; --ev-a3: #f2efe7;
}
/* RISO — opt-in decorative plate. Its fluoro accent is 2.92:1 on paper and its
   --mute is 3.08:1; both are deliberate and out of conformance scope. The two
   things that are NOT optional are wired below: the focus ring falls back to
   the blue ink (8.31:1), and the plate picker — the way back to PRINT — drops
   its muted treatment so it stays legible from inside this skin. */
[data-skin="riso"] {
  --paper: #efe9db; --ink: #1c33a8; --mute: #7181c4; --line: #1c33a8;
  --acc: #ff3d1f; --acc-ink: #efe9db;
  --acc-on-ink: #ff8f6b;/* 4.50:1 on the blue ink bar */
  --focus: #1c33a8;     /* 8.31:1 on --paper — NOT --acc, which is 2.92:1 */
  --mark-early: #9e1b1b;/* maroon. 6.61:1 — a dark red, not the fluoro red --acc */
  --mark-late:  #0f6b60;/* deep teal. 5.27:1 — neither the blue ink nor the red */
  --ev-a0: #ff3d1f; --ev-a1: #1c33a8; --ev-a2: #ff3d1f; --ev-a3: #1c33a8;
}
[data-skin="riso"] .skin-btn,
[data-skin="riso"] .skin-picker .lbl { color: var(--ink); }

/* FLUORO — near-black stock, one very bright ink. The whole plate is built so
   the brightness costs nothing in legibility: on this ground the lime is
   16.52:1 and the body text 17.17:1, both far past AA. Second highlight is a
   cold cyan (11.74:1) so the two brights read as separate passes of the press
   rather than one glow. */
[data-skin="fluoro"] {
  /* Neutral since 20 Oct 2026: white, black and grey with no tint, so only the
     neons carry colour. Was lime-tinted: --ink/--line #e9f4de, --paper and
     --acc-ink #0b0d08, --mute #93a389. The greys keep the old brightness, so
     every contrast ratio below is unchanged (grey on paper 7.30:1). */
  --paper: #0c0c0c; --ink: #ffffff; --mute: #9e9e9e; --line: #ffffff;
  --acc: #c8ff00; --acc-ink: #0c0c0c;
  --acc-on-ink: #3d5c00;/* --ink is the LIGHT colour here, so this goes dark. 7.69:1 */
  --acc2: #00e0d5;
  --focus: #c8ff00;     /* 16.52:1 on --paper */
  --mark-early: #ff8a00;/* the orange tube. 8.26:1 */
  --mark-late:  #4db8ff;/* a sky-blue tube. 8.94:1 — not the cyan or lavender the dates take */
  --ev-a0: #c8ff00; --ev-a1: #00e0d5; --ev-a2: #c8ff00; --ev-a3: #00e0d5;
}

/* ── FLUORO: five tubes, not one ──
   A near-black ground is the one stock where every bright is also legible
   type, so this plate does not need SPECTRA's two registers: the same five
   neons are fills with dark type through them AND type on the dark, and both
   directions clear AA by a wide margin — lime 16.52:1, cyan 11.74:1, orange
   8.26:1, lavender 7.58:1, pink 6.03:1. The lime still leads. The other four
   are the colours a sign shop keeps beside it.

   The one surface that is NOT dark is the ribbon bar, which is filled with
   the light --ink; the two colours for that are pushed down until they hold
   on it (9.56:1 and 7.69:1). */
[data-skin="fluoro"] {
  --f-lime: #c8ff00; --f-cyan: #00e0d5; --f-pink: #ff3ea5;
  --f-orange: #ff8a00; --f-lav: #b78bff;
  --f-pink-on-ink: #8a0050;
  --ev-a0: #c8ff00; --ev-a1: #00e0d5; --ev-a2: #ff3ea5; --ev-a3: #ff8a00;
}

/* Wordmark lit at rest — lime, and it turns pink under the cursor. */
[data-skin="fluoro"] .bar-logo { background-color: var(--f-lime); }
[data-skin="fluoro"] .bar-logo:hover { background-color: var(--f-pink); }
[data-skin="fluoro"] .bar-logo:hover::before { background-color: var(--paper); }

/* Nav: each item fills in its own tube with the dark ground as its type. */
[data-skin="fluoro"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s, background-color 0.15s; }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):hover { color: var(--paper); }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { background: var(--f-lime);   border-color: var(--f-lime); }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { background: var(--f-cyan);   border-color: var(--f-cyan); }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { background: var(--f-pink);   border-color: var(--f-pink); }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { background: var(--f-orange); border-color: var(--f-orange); }
[data-skin="fluoro"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { background: var(--f-lav);    border-color: var(--f-lav); }
/* The page you are on: lime frame, no fill, light label. */
[data-skin="fluoro"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--f-lime); }
[data-skin="fluoro"] .nav-links a.nav-cta:hover { background: var(--f-pink); color: var(--paper); }

/* Fullscreen menu — poster type, one tube per line. */
[data-skin="fluoro"] .menu a.menu-link:nth-child(5n+1):hover { color: var(--f-lime); }
[data-skin="fluoro"] .menu a.menu-link:nth-child(5n+2):hover { color: var(--f-cyan); }
[data-skin="fluoro"] .menu a.menu-link:nth-child(5n+3):hover { color: var(--f-pink); }
[data-skin="fluoro"] .menu a.menu-link:nth-child(5n+4):hover { color: var(--f-orange); }
[data-skin="fluoro"] .menu a.menu-link:nth-child(5n+5):hover { color: var(--f-lav); }

/* Highlighted words: lit type, a different tube per heading family. The hero
   and the plate captions keep the lime. */
[data-skin="fluoro"] .mdiv h2 i,
[data-skin="fluoro"] .prose h2 i        { color: var(--f-cyan); }
[data-skin="fluoro"] .idx-cap h2 i      { color: var(--f-pink); }
[data-skin="fluoro"] .cta-band h2 i     { color: var(--f-orange); }
[data-skin="fluoro"] .tech-cap i,
[data-skin="fluoro"] .stat-num i        { color: var(--f-lav); }
[data-skin="fluoro"] .hh h2 i,
[data-skin="fluoro"] .hh h2 .t-serif    { color: var(--f-pink); }
[data-skin="fluoro"] .cta-band::before  { color: var(--f-pink); }
[data-skin="fluoro"] .cta-band::after   { color: var(--f-orange); }
[data-skin="fluoro"] .hero-side u,
[data-skin="fluoro"] .hero-annot .desc u { text-decoration-color: var(--f-orange); text-decoration-thickness: 4px; }

/* Dates rotate through the tubes; the list view takes lavender. */
[data-skin="fluoro"] .spec:nth-child(4n+2) .spec-date { color: var(--f-pink); }
[data-skin="fluoro"] .spec:nth-child(4n+3) .spec-date { color: var(--f-cyan); }
[data-skin="fluoro"] .spec:nth-child(4n+4) .spec-date { color: var(--f-orange); }
[data-skin="fluoro"] .crow-date { color: var(--f-lav); }

/* Solids: lime stays on the buttons; the newsletter goes cyan, "here" on the
   venue strip goes pink, the CMS banner orange. Dark type on all of them. */
@media (hover: hover) { [data-skin="fluoro"] .newsletter-form button:hover { background: var(--f-cyan); color: var(--paper); } }
[data-skin="fluoro"] .cms-banner { background: var(--f-orange); color: var(--paper); }
[data-skin="fluoro"] ::selection { background: var(--f-lime); color: var(--paper); }

/* Ribbon: the bar is the LIGHT ink, so the shows go to a deep magenta
   against the deep-green lead — the plate's two tubes, switched off. */
[data-skin="fluoro"] .ribbon-track span.hot { color: var(--f-pink-on-ink); }

/* Plot layer — the tubes at low strength under everything. */
[data-skin="fluoro"] .plot .ring { border-color: color-mix(in srgb, var(--f-pink) 55%, transparent); }
[data-skin="fluoro"] .plot .ring::after { border-color: color-mix(in srgb, var(--f-lav) 45%, transparent); }
[data-skin="fluoro"] .plot .cross-h { background: color-mix(in srgb, var(--f-cyan) 35%, transparent); }
[data-skin="fluoro"] .plot .cross-v { background: color-mix(in srgb, var(--f-orange) 55%, transparent); }
[data-skin="fluoro"] .plot .reg { border-color: var(--f-lime); opacity: 1; }
[data-skin="fluoro"] .quote { border-left-color: var(--f-pink); }
[data-skin="fluoro"] .hh { border-color: var(--f-cyan); }
[data-skin="fluoro"] .dmap .pin .ring { border-color: var(--f-orange); }

/* Footer heads, one tube each. */
[data-skin="fluoro"] .foot-col:nth-child(1) .foot-col-title { color: var(--f-lime); }
[data-skin="fluoro"] .foot-col:nth-child(2) .foot-col-title { color: var(--f-cyan); }
[data-skin="fluoro"] .foot-col:nth-child(3) .foot-col-title { color: var(--f-pink); }
[data-skin="fluoro"] .newsletter-label { color: var(--f-lav); }

/* The outlined hero word takes the cyan, so the two brightest tubes share
   the top of the page. */
[data-skin="fluoro"] .hw-xray { -webkit-text-stroke-color: var(--f-cyan); }

/* SPECTRA — white stock, black ink, and the whole house palette let loose
   on top of it. The base is achromatic on purpose: nothing in the paper or the
   body text carries a hue, so every colour on the page is an accent doing a
   job. Two registers, because white paper needs both —

     BRIGHTS  chartreuse, lavender, mint, sky, azure, exactly as the house
              palette defines them. They are FILLS. Black type sits on them (16.25:1, 10.48:1,
              12.58:1, 10.10:1, 5.40:1) — nav hovers, date chips, highlighted
              words, buttons, the plot layer.
     DEEPS    the same five pushed down until they hold their own as ink on
              white: azure 6.32:1, violet 6.46:1, green 5.31:1, olive 5.90:1.
              These are the TYPE colours — anything small and coloured uses one.

   A bright is never asked to be small text and a deep is never asked to be a
   fill, so the plate can be as loud as it likes without a single pairing going
   under AA. Body is 19.60:1, muted 5.33:1, focus 6.32:1. */
[data-skin="spectra"] {
  --paper: #ffffff; --ink: #111111; --mute: #6b6b6b; --line: #111111;
  --acc: #2f5fa8;       /* deep azure. 6.32:1 on --paper */
  --acc-ink: #ffffff;   /* type over an --acc fill. 6.32:1 */
  --acc-on-ink: #d4ff00;/* the ribbon bar is --ink, so this goes BRIGHT. 16.25:1 */
  --acc2: #0b7a5c;      /* deep mint. 5.31:1 */
  --focus: #2f5fa8;     /* 6.32:1 on --paper */
  --mark-early: #b8410a;/* vermilion. 5.53:1 — the one warm ink on this plate, on purpose */
  --mark-late:  #0b7a5c;/* deep mint. 5.31:1 — the dates take violet */
  --ev-a0: #2f5fa8; --ev-a1: #5b4bc4; --ev-a2: #0b7a5c; --ev-a3: #5c6b00;

  /* the brights, named once so the rules below read as colour and not as hex */
  --s-chart: #d4ff00; --s-lav: #c4b8ff; --s-mint: #00f0b5;
  --s-sky:   #7ec4ff; --s-az:  #4a8ae0;
  --s-violet: #5b4bc4; --s-olive: #5c6b00;
}

/* ── SPECTRA: where the colour actually lands ──
   Scoped to the plate so no other skin inherits any of it. */

/* Wordmark. It is a mask, so it takes a colour the way type does — violet at
   rest, and it turns over to the deep green on hover rather than to --acc,
   which is the one colour already carrying the rest of the bar. */
[data-skin="spectra"] .bar-logo { background-color: var(--s-violet); }
[data-skin="spectra"] .bar-logo:hover { background-color: var(--acc2); }

/* Nav. The hover frame was an empty box changing colour; here it FILLS, and
   each item owns a different bright — so the bar reads as a row of colour
   chips that only ever shows one at a time. Type goes to --ink on every one of
   them, which is what keeps a 1.1:1 chartreuse legible: it is never the ink. */
[data-skin="spectra"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s, background-color 0.15s; }
[data-skin="spectra"] .nav-links a:not(.nav-cta):hover,
[data-skin="spectra"] .nav-links a:not(.nav-cta).active { color: var(--ink); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+1):hover { background: var(--s-chart); border-color: var(--s-chart); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+2):hover { background: var(--s-lav);   border-color: var(--s-lav); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+3):hover { background: var(--s-mint);  border-color: var(--s-mint); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+4):hover { background: var(--s-sky);   border-color: var(--s-sky); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+5):hover { background: var(--s-az);    border-color: var(--s-az); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+6):hover { background: var(--s-chart); border-color: var(--s-chart); }
[data-skin="spectra"] .nav-links a:not(.nav-cta):nth-of-type(7n+7):hover { background: var(--s-lav);   border-color: var(--s-lav); }
/* The page you are on keeps its frame drawn, in its own colour, with no fill —
   so "here" and "hovering" never look the same. */
[data-skin="spectra"] .nav-links a:not(.nav-cta).active { border-color: var(--s-violet); }
/* BOOK THE ROOM: black block that flips to chartreuse. */
[data-skin="spectra"] .nav-links a.nav-cta:hover { background: var(--s-chart); color: var(--ink); }

/* Fullscreen menu — same rotation, one colour per line, so the mobile menu is
   the bar's colour set at poster size. Large display type, so these can take a
   bright as the ink itself: the deeps would waste the scale. */
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+1):hover { color: var(--s-violet); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+2):hover { color: var(--acc2); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+3):hover { color: var(--acc); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+4):hover { color: var(--s-olive); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+5):hover { color: var(--s-violet); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+6):hover { color: var(--acc2); }
[data-skin="spectra"] .menu a.menu-link:nth-child(7n+7):hover { color: var(--acc); }

/* Highlighted words in headings. Print sets these in the accent; here they get
   the marker-pen treatment instead — a bright block with black type through
   it. The em-scale padding keeps the block on the word at every clamp size. */
[data-skin="spectra"] .cap h2 i,
[data-skin="spectra"] .ph-title i,
[data-skin="spectra"] .mdiv h2 i,
[data-skin="spectra"] .prose h2 i,
[data-skin="spectra"] .tech-cap i,
[data-skin="spectra"] .stat-num i,
[data-skin="spectra"] .hh h2 i, [data-skin="spectra"] .hh h2 .t-serif {
  color: var(--ink); background: var(--s-chart);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
[data-skin="spectra"] .idx-cap h2 i { color: var(--ink); background: var(--s-mint);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
[data-skin="spectra"] .cta-band h2 i { color: var(--ink); background: var(--s-lav);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
/* The two ✳ marks in the corners of the band, and the underlines in the hero
   copy — decoration, so they take brights straight. */
[data-skin="spectra"] .cta-band::before { color: var(--s-mint); }
[data-skin="spectra"] .cta-band::after { color: var(--s-sky); }
[data-skin="spectra"] .hero-side u,
[data-skin="spectra"] .hero-annot .desc u { text-decoration-color: var(--s-chart); text-decoration-thickness: 6px; }

/* Dates. Every listing gets a colour chip where it had coloured text, and the
   card grid and the list view take different ones so the two views of the same
   month don't read as the same page. */
[data-skin="spectra"] .spec-date { display: inline-block; color: var(--ink);
  background: var(--s-mint); padding: 2px 7px; margin-bottom: 7px; }
[data-skin="spectra"] .spec-date i { color: color-mix(in srgb, var(--ink) 62%, transparent); }
[data-skin="spectra"] .crow-date { color: var(--s-violet); }
[data-skin="spectra"] .spec:nth-child(4n+2) .spec-date { background: var(--s-lav); }
[data-skin="spectra"] .spec:nth-child(4n+3) .spec-date { background: var(--s-sky); }
[data-skin="spectra"] .spec:nth-child(4n+4) .spec-date { background: var(--s-chart); }

/* Buttons and the stamp. Solid fills go bright with black type; the outlined
   ones keep a deep, because there the colour IS the type. */
[data-skin="spectra"] .btn-solid,
[data-skin="spectra"] .modal-close:hover,
[data-skin="spectra"] .modal-meta span.hl {
  background: var(--s-chart); border-color: var(--s-chart); color: var(--ink);
}
@media (hover: hover) {
  [data-skin="spectra"] .spec-buy:hover,
  [data-skin="spectra"] .newsletter-form button:hover { background: var(--s-chart); border-color: var(--s-chart); color: var(--ink); }
}
[data-skin="spectra"] .cms-banner { background: var(--s-chart); color: var(--ink); }
[data-skin="spectra"] ::selection { background: var(--s-chart); color: var(--ink); }

/* Ribbon. The bar is black, so both labels can run bright — chartreuse for the
   lead, mint for the shows, which is the only place on the plate where two
   brights sit side by side as type. */
[data-skin="spectra"] .ribbon-track span.hot { color: var(--s-mint); }

/* The plot layer — rings, crosshairs, registration marks. Pure decoration
   under everything else, so this is where the brights can be brights with no
   contrast question attached at all. */
[data-skin="spectra"] .plot .ring { border-color: color-mix(in srgb, var(--s-lav) 85%, transparent); }
[data-skin="spectra"] .plot .ring::after { border-color: color-mix(in srgb, var(--s-mint) 70%, transparent); }
/* Sky at 32%, not 80%. Full-bleed horizontal rules read far heavier than the
   same colour does in a ring or a tick — this one line was the loudest thing
   on the plate at 80%, and it is meant to be plot chrome. 60% off. */
[data-skin="spectra"] .plot .cross-h { background: color-mix(in srgb, var(--s-sky) 32%, transparent); }
[data-skin="spectra"] .plot .cross-v { background: color-mix(in srgb, var(--s-chart) 90%, transparent); }
[data-skin="spectra"] .plot .reg { border-color: var(--s-violet); opacity: 1; }
[data-skin="spectra"] .quote { border-left-width: 6px; border-left-color: var(--s-chart); }
[data-skin="spectra"] .hh { border-color: var(--s-lav); }
[data-skin="spectra"] .dmap .pin .ring { border-color: var(--s-mint); }

/* Footer column heads, each its own deep — small caps, so deeps not brights. */
[data-skin="spectra"] .foot-col:nth-child(1) .foot-col-title { color: var(--s-violet); }
[data-skin="spectra"] .foot-col:nth-child(2) .foot-col-title { color: var(--acc2); }
[data-skin="spectra"] .foot-col:nth-child(3) .foot-col-title { color: var(--s-olive); }
[data-skin="spectra"] .newsletter-label { color: var(--s-violet); }

/* The giant outlined hero word, and the big spec numerals: display scale, but
   drawn as a 1–3px stroke rather than filled, so a bright would vanish. Deep
   violet holds the line. */
[data-skin="spectra"] .hw-xray { -webkit-text-stroke-color: var(--s-violet); }

/* MERIDIAN — the only plate printed on COLOURED stock. Pale pink paper, deep
   teal ink, and five pens carried across it: teal, raspberry, lemon, coral,
   cobalt. Warm and saturated where SPECTRA is cool and neon, and nothing in it
   is achromatic — there is no black and no white anywhere in the base, which
   is what keeps it from reading as another version of that plate.

   The stock was a peach-pink (#f7ddd6) and the set ran teal / crimson / gold /
   tangerine, with the small-type register forced down into mustard, burnt
   orange and brown — the whole plate read as harvest. The paper is now a true
   pale pink with no orange in it, the ink is a cleaner, darker teal, the muted
   text is a dusty plum instead of taupe, gold gave way to a clean lemon and
   tangerine to a coral, and NO yellow or orange is ever asked to be small
   type — the brown that produces is the one colour this plate must not have.

   It also spends its colour differently. SPECTRA blocks: filled chips, filled
   nav, marker-pen words. MERIDIAN draws: coloured RULES under the nav, coloured
   INK in the headings, outlined chips instead of filled ones. Same breadth of
   palette, opposite instinct.

   Three registers, because a light stock leaves no choice and a teal ink
   takes one away —
     BRIGHTS  teal, lemon, coral as fills (ink type on them: 5.64:1, 11.15:1,
              5.54:1). Raspberry and cobalt sit in the middle where neither the
              ink nor the paper clears 4.5:1 on them, so those two are RULES
              and decoration only, never a filled label.
     DEEPS    four pushed down to hold as SMALL type on the pink: raspberry
              5.94:1, cobalt 5.29:1, teal 5.08:1, coral (a brick) 4.60:1.
              The catch: the ink is itself a deep teal, so the deep teal
              (2.28:1 from it) reads as the ink and is never used AS colour.
              The deep cobalt is a saturated blue this time (2.19:1 from the
              ink, but a different hue), so it can carry small type where the
              old dull one could not. Lemon has no deep: see above.
     MIDS     four lifted to where they clear 3:1 on the pink AND 3:1 from the
              ink (teal 3.39/3.41, raspberry 4.15/2.78, cobalt 4.39/2.63,
              coral 3.29/3.52). Large type only — the heading highlights and
              the fullscreen menu, both 26px+ bold, which is where AA allows
              3:1. This is the register that lets teal appear as words at all.

   Colour at rest, not only under the cursor: the rentals button is a coral
   block, the lead weekday numeral is mid raspberry, and every highlighted
   word carries a bright rule beneath a mid — so the page is drawn in the
   whole set before anything is hovered.

   Body ink is 11.57:1, muted 5.08:1, focus 5.29:1. */
[data-skin="meridian"] {
  --paper: #f9dfe7; --ink: #032f2c; --mute: #7a5468; --line: #032f2c;
  --acc: #a80f44;       /* deep raspberry. 5.94:1 on --paper */
  --acc-ink: #ffffff;   /* type over an --acc fill. 7.46:1 */
  --acc-on-ink: #ffe14d;/* the ribbon bar is the teal --ink, so this goes BRIGHT. 11.15:1 */
  --acc2: #1d4fd8;      /* deep cobalt. 5.29:1. NOT the deep teal — that is
                           2.28:1 from the teal ink and reads as ink on the stamp */
  --focus: #1d4fd8;     /* deep cobalt. 5.29:1 on --paper */
  --mark-early: #b8391c;/* brick, the coral's deep. 4.60:1 — not the raspberry --acc */
  --mark-late:  #1d4fd8;/* deep cobalt. 5.29:1 */
  --ev-a0: #a80f44; --ev-a1: #1d4fd8; --ev-a2: #0b6b62; --ev-a3: #b8391c;

  /* the Meridian five, plus their deeps and mids */
  --m-teal: #12b5a3; --m-rasp: #e8175a; --m-lem: #ffe14d;
  --m-cor:  #ff7660; --m-cob:  #2f6ff5;
  --m-teal-d: #0b6b62; --m-rasp-d: #a80f44; --m-cob-d: #1d4fd8; --m-cor-d: #b8391c;
  --m-teal-m: #0c8a7a; --m-rasp-m: #d5124f; --m-cob-m: #2a5fe0; --m-cor-m: #dd4a2a;
}

/* ── MERIDIAN: rules and ink, not blocks ── */

/* Wordmark: deep raspberry at rest, and under the cursor the bright teal with
   the ink mark through it — the one place the two ends of the set touch. */
[data-skin="meridian"] .bar-logo { background-color: var(--m-rasp-d); }
[data-skin="meridian"] .bar-logo:hover { background-color: var(--m-teal); }
[data-skin="meridian"] .bar-logo:hover::before { background-color: var(--ink); }

/* Nav. The frame that SPECTRA fills, MERIDIAN drops to a single thick rule
   under the label — a bright, because a 3px underline is decoration and not
   type, which is the one place raspberry and cobalt can appear at full
   strength. The label itself goes to --ink, so the reading contrast never
   moves. */
[data-skin="meridian"] .nav-links a:not(.nav-cta) {
  border-color: transparent; border-bottom-width: 3px;
  transition: color 0.15s, border-color 0.15s;
}
/* border-color here too: the base hover draws an ink frame at the same
   specificity as the rest rule above and later in the file, so without this
   the rule sat inside a box. */
[data-skin="meridian"] .nav-links a:not(.nav-cta):hover,
[data-skin="meridian"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: transparent; }
[data-skin="meridian"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { border-bottom-color: var(--m-teal); }
[data-skin="meridian"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { border-bottom-color: var(--m-rasp); }
[data-skin="meridian"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { border-bottom-color: var(--m-lem); }
[data-skin="meridian"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { border-bottom-color: var(--m-cor); }
[data-skin="meridian"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { border-bottom-color: var(--m-cob); }
/* The page you are on keeps its rule drawn at all times. */
[data-skin="meridian"] .nav-links a:not(.nav-cta).active { border-bottom-color: var(--m-rasp); }
/* PRIVATE RENTALS is a coral block at rest (ink type, 5.54:1) and goes to
   lemon under the cursor — the bar's one piece of colour before a hover. */
[data-skin="meridian"] .nav-links a.nav-cta { background: var(--m-cor); color: var(--ink); }
[data-skin="meridian"] .nav-links a.nav-cta:hover { background: var(--m-lem); color: var(--ink); }

/* Fullscreen menu — one line, one mid. Poster type, so the 3:1 register.
   Lemon has no mid (a yellow at 3:1 on pink is an ochre), so its line keeps
   the ink and takes the lemon as a rule under it instead. */
[data-skin="meridian"] .menu a.menu-link:nth-child(5n+1):hover { color: var(--m-teal-m); }
[data-skin="meridian"] .menu a.menu-link:nth-child(5n+2):hover { color: var(--m-rasp-m); }
[data-skin="meridian"] .menu a.menu-link:nth-child(5n+3):hover { color: var(--ink);
  text-decoration: underline; text-decoration-color: var(--m-lem);
  text-decoration-thickness: 0.12em; text-underline-offset: 0.08em; text-decoration-skip-ink: none; }
[data-skin="meridian"] .menu a.menu-link:nth-child(5n+4):hover { color: var(--m-cor-m); }
[data-skin="meridian"] .menu a.menu-link:nth-child(5n+5):hover { color: var(--m-cob-m); }

/* Highlighted words are INK here, not a marker block — the exact inverse of
   SPECTRA, and the reason the two plates never look like siblings. Each
   heading family takes a different MID for the word and a different BRIGHT
   for a thick rule beneath it, so the set is visible down a page at rest.
   Display scale throughout, which is what admits the mids. */
[data-skin="meridian"] .cap h2 i,
[data-skin="meridian"] .ph-title i,
[data-skin="meridian"] .mdiv h2 i,
[data-skin="meridian"] .prose h2 i,
[data-skin="meridian"] .idx-cap h2 i,
[data-skin="meridian"] .cta-band h2 i,
[data-skin="meridian"] .tech-cap i,
[data-skin="meridian"] .stat-num i,
[data-skin="meridian"] .hh h2 i,
[data-skin="meridian"] .hh h2 .t-serif {
  text-decoration: underline; text-decoration-thickness: 0.12em;
  text-underline-offset: 0.06em; text-decoration-skip-ink: none;
}
[data-skin="meridian"] .cap h2 i,
[data-skin="meridian"] .ph-title i        { color: var(--m-rasp-m); text-decoration-color: var(--m-lem); }
[data-skin="meridian"] .mdiv h2 i,
[data-skin="meridian"] .prose h2 i        { color: var(--m-cob-m);  text-decoration-color: var(--m-cor); }
[data-skin="meridian"] .idx-cap h2 i      { color: var(--m-teal-m); text-decoration-color: var(--m-lem); }
[data-skin="meridian"] .cta-band h2 i     { color: var(--m-cor-m);  text-decoration-color: var(--m-teal); }
[data-skin="meridian"] .tech-cap i,
[data-skin="meridian"] .stat-num i        { color: var(--m-teal-m); text-decoration-color: var(--m-rasp); }
[data-skin="meridian"] .hh h2 i,
[data-skin="meridian"] .hh h2 .t-serif    { color: var(--m-rasp-m); text-decoration-color: var(--m-teal); }
/* The lead weekday numeral is display scale too: mid raspberry, not the deep. */
[data-skin="meridian"] .spec:first-child .spec-num { color: var(--m-rasp-m); }
[data-skin="meridian"] .cta-band::before  { color: var(--m-lem); }
[data-skin="meridian"] .cta-band::after   { color: var(--m-teal); }
/* Underlines in the hero copy stay bright and get thick — decoration. */
[data-skin="meridian"] .hero-side u,
[data-skin="meridian"] .hero-annot .desc u { text-decoration-color: var(--m-lem); text-decoration-thickness: 6px; }

/* Dates: outlined, not filled. Deep ink inside a bright rule. */
/* Small type, so the word is always a deep that reads as colour from the
   ink — raspberry, brick, cobalt — never the deep teal, and never a yellow. */
[data-skin="meridian"] .spec-date { display: inline-block; color: var(--m-rasp-d);
  border: 2px solid var(--m-rasp); padding: 1px 7px; margin-bottom: 7px; }
[data-skin="meridian"] .spec:nth-child(4n+2) .spec-date { color: var(--m-cor-d); border-color: var(--m-cob); }
[data-skin="meridian"] .spec:nth-child(4n+3) .spec-date { color: var(--m-cob-d); border-color: var(--m-teal); }
[data-skin="meridian"] .spec:nth-child(4n+4) .spec-date { color: var(--ink);     border-color: var(--m-cor); }
[data-skin="meridian"] .crow-date { color: var(--m-cob-d); }

/* The few things that ARE solid: buttons, and only in the three brights that
   can hold ink type. */
[data-skin="meridian"] .btn-solid,
[data-skin="meridian"] .modal-close:hover,
[data-skin="meridian"] .modal-meta span.hl {
  background: var(--m-lem); border-color: var(--m-lem); color: var(--ink);
}
@media (hover: hover) {
  [data-skin="meridian"] .spec-buy:hover,
  [data-skin="meridian"] .newsletter-form button:hover { background: var(--m-lem); border-color: var(--m-lem); color: var(--ink); }
}
[data-skin="meridian"] .cms-banner { background: var(--m-cor); color: var(--ink); }
[data-skin="meridian"] ::selection { background: var(--m-lem); color: var(--ink); }

/* Ribbon: the bar is the deep teal ink, so the shows can run coral against
   the lemon lead — the one warm-on-warm pairing the plate allows itself
   (5.54:1 and 11.15:1 on that bar). */
[data-skin="meridian"] .ribbon-track span.hot { color: var(--m-cor); }

/* Plot layer. Pure decoration, so the two colours with nowhere else to go —
   raspberry and cobalt — get the run of it. */
[data-skin="meridian"] .plot .ring { border-color: color-mix(in srgb, var(--m-rasp) 55%, transparent); }
[data-skin="meridian"] .plot .ring::after { border-color: color-mix(in srgb, var(--m-cob) 45%, transparent); }
[data-skin="meridian"] .plot .cross-h { background: color-mix(in srgb, var(--m-cor) 55%, transparent); }
[data-skin="meridian"] .plot .cross-v { background: color-mix(in srgb, var(--m-teal) 65%, transparent); }
[data-skin="meridian"] .plot .reg { border-color: var(--m-rasp-d); opacity: 1; }
[data-skin="meridian"] .quote { border-left-width: 6px; border-left-color: var(--m-cob); }
[data-skin="meridian"] .hh { border-color: var(--m-teal); }
[data-skin="meridian"] .dmap .pin .ring { border-color: var(--m-cor); }

/* Footer heads: small caps, so the three deeps that read as colour. */
[data-skin="meridian"] .foot-col:nth-child(1) .foot-col-title { color: var(--m-rasp-d); }
[data-skin="meridian"] .foot-col:nth-child(2) .foot-col-title { color: var(--m-cob-d); }
[data-skin="meridian"] .foot-col:nth-child(3) .foot-col-title { color: var(--m-cor-d); }
[data-skin="meridian"] .newsletter-label { color: var(--m-rasp-d); }

/* Outlined display type — a 1-3px stroke, so it takes a deep, not a bright. */
[data-skin="meridian"] .hw-xray { -webkit-text-stroke-color: var(--m-rasp-d); }

/* BLUEPRINT — the first plate whose ground is a COLOUR. Every other stock is
   a light paper or a near-black; this one is a saturated ultramarine, the blue
   of a cyanotype, with chalk for ink. The whole design system was built as a
   plotted print — registration marks, crosshairs, a live coordinate readout,
   posters reduced to one ink — and on this ground all of that chrome becomes
   what it always was underneath: a drawing on a blueprint. The posters need
   no work at all; they are already ink through a mask, so here they come out
   as chalk on blue.

   Five pens, all light, because on a deep ground a light colour is legible
   BOTH ways — as type on the blue and as a fill with the blue as its type —
   so this plate needs neither SPECTRA's two registers nor MERIDIAN's three.
   Sulphur leads: the yellow of a highlighter drawn across a drawing, and the
   focus ring. Coral is the redline — the colour an architect's corrections
   come back in — and takes the second-highlight jobs. Rose, ice and mint are
   the chalk pastels beside them. Type on the blue: sulphur 7.94:1, mint
   7.42:1, ice 7.25:1, rose 6.07:1, coral 4.90:1; blue type on any of them is
   the same ratio read the other way.

   Where the plate DOES borrow: the two light-ground plates fill nothing and
   draw rules; the dark-ground FLUORO fills its nav. BLUEPRINT fills too, but
   with the ground as the type, so a hovered item reads as a hole cut in the
   sheet rather than a lit sign — the same move, the opposite reading.

   The one surface that is NOT blue is the ribbon bar, which is filled with
   the chalk --ink; the lead there is the ground itself and the shows are a
   deep coral (8.65:1 and 4.91:1 on that bar).

   Body ink is 8.65:1, muted 5.43:1, focus 7.94:1. */
[data-skin="blueprint"] {
  --paper: #1a2fb0; --ink: #f1ede3; --mute: #b4bce6; --line: #f1ede3;
  --acc: #ffe45c;       /* sulphur. 7.94:1 on --paper */
  --acc-ink: #1a2fb0;   /* type over an --acc fill: the ground. 7.94:1 */
  --acc-on-ink: #1a2fb0;/* the ribbon bar is the chalk --ink, so this goes DARK: the ground. 8.65:1 */
  --acc2: #ff9a80;      /* coral, the redline. 4.90:1 */
  --focus: #ffe45c;     /* 7.94:1 on --paper */
  --mark-early: #ff9a80;/* coral. 4.90:1 — the warm one */
  --mark-late:  #a6e3ff;/* ice. 7.25:1 — not the mint or sulphur the dates take */
  --ev-a0: #ffe45c; --ev-a1: #ff9a80; --ev-a2: #8cf0c9; --ev-a3: #ffb4c8;

  --b-sulph: #ffe45c; --b-coral: #ff9a80; --b-rose: #ffb4c8;
  --b-ice: #a6e3ff; --b-mint: #8cf0c9;
  --b-coral-on-ink: #b83a1e;/* the redline, deep enough for the chalk bar. 4.91:1 */
}

/* ── BLUEPRINT: chalk on blue, and five pastels ── */

/* Wordmark lit at rest — a sulphur block with the blue mark through it, the
   one highlighter stroke in the bar — and it turns coral under the cursor. */
[data-skin="blueprint"] .bar-logo { background-color: var(--b-sulph); }
[data-skin="blueprint"] .bar-logo:hover { background-color: var(--b-coral); }
[data-skin="blueprint"] .bar-logo:hover::before { background-color: var(--paper); }

/* Nav: each item fills in its own pastel with the blue ground as its type. */
[data-skin="blueprint"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s, background-color 0.15s; }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):hover { color: var(--paper); }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { background: var(--b-sulph); border-color: var(--b-sulph); }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { background: var(--b-coral); border-color: var(--b-coral); }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { background: var(--b-mint);  border-color: var(--b-mint); }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { background: var(--b-rose);  border-color: var(--b-rose); }
[data-skin="blueprint"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { background: var(--b-ice);   border-color: var(--b-ice); }
/* The page you are on: sulphur frame, no fill, chalk label. */
[data-skin="blueprint"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--b-sulph); }
/* PRIVATE RENTALS: the chalk block goes coral under the cursor. */
[data-skin="blueprint"] .nav-links a.nav-cta:hover { background: var(--b-coral); color: var(--paper); }

/* Fullscreen menu — poster type, one pastel per line. */
[data-skin="blueprint"] .menu a.menu-link:nth-child(5n+1):hover { color: var(--b-sulph); }
[data-skin="blueprint"] .menu a.menu-link:nth-child(5n+2):hover { color: var(--b-coral); }
[data-skin="blueprint"] .menu a.menu-link:nth-child(5n+3):hover { color: var(--b-mint); }
[data-skin="blueprint"] .menu a.menu-link:nth-child(5n+4):hover { color: var(--b-rose); }
[data-skin="blueprint"] .menu a.menu-link:nth-child(5n+5):hover { color: var(--b-ice); }

/* Highlighted words. The hero and the plate captions take the highlighter
   itself — a sulphur block with the blue through it, the one place the plate
   blocks. Every other heading family is lit type in a different pastel. */
[data-skin="blueprint"] .cap h2 i,
[data-skin="blueprint"] .ph-title i {
  color: var(--paper); background: var(--b-sulph);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
[data-skin="blueprint"] .mdiv h2 i,
[data-skin="blueprint"] .prose h2 i        { color: var(--b-coral); }
[data-skin="blueprint"] .idx-cap h2 i      { color: var(--b-mint); }
[data-skin="blueprint"] .cta-band h2 i     { color: var(--b-rose); }
[data-skin="blueprint"] .tech-cap i,
[data-skin="blueprint"] .stat-num i        { color: var(--b-ice); }
[data-skin="blueprint"] .hh h2 i,
[data-skin="blueprint"] .hh h2 .t-serif    { color: var(--b-coral); }
[data-skin="blueprint"] .cta-band::before  { color: var(--b-coral); }
[data-skin="blueprint"] .cta-band::after   { color: var(--b-mint); }
/* The redline under the hero copy. */
[data-skin="blueprint"] .hero-side u,
[data-skin="blueprint"] .hero-annot .desc u { text-decoration-color: var(--b-coral); text-decoration-thickness: 4px; }

/* Dates rotate through the pastels; the list view takes ice. */
[data-skin="blueprint"] .spec-date { color: var(--b-sulph); }
[data-skin="blueprint"] .spec:nth-child(4n+2) .spec-date { color: var(--b-coral); }
[data-skin="blueprint"] .spec:nth-child(4n+3) .spec-date { color: var(--b-mint); }
[data-skin="blueprint"] .spec:nth-child(4n+4) .spec-date { color: var(--b-rose); }
[data-skin="blueprint"] .crow-date { color: var(--b-ice); }

/* Solids: sulphur stays on the buttons (that is --acc); the newsletter goes
   mint, "here" on the venue strip goes rose, the CMS banner coral. Blue type
   on all of them. */
@media (hover: hover) { [data-skin="blueprint"] .newsletter-form button:hover { background: var(--b-mint); color: var(--paper); } }
[data-skin="blueprint"] .cms-banner { background: var(--b-coral); color: var(--paper); }
[data-skin="blueprint"] ::selection { background: var(--b-sulph); color: var(--paper); }

/* Ribbon: the bar is the chalk ink, so the shows go to the deep coral beside
   the blue lead — redline on a title block. */
[data-skin="blueprint"] .ribbon-track span.hot { color: var(--b-coral-on-ink); }

/* Plot layer — on this plate it is not decoration so much as the point. The
   crosshairs go chalk at higher strength than any other plate allows, the
   rings take rose and ice, the registration marks the highlighter. */
[data-skin="blueprint"] .plot .ring { border-color: color-mix(in srgb, var(--b-rose) 60%, transparent); }
[data-skin="blueprint"] .plot .ring::after { border-color: color-mix(in srgb, var(--b-ice) 50%, transparent); }
[data-skin="blueprint"] .plot .cross-h { background: color-mix(in srgb, var(--ink) 40%, transparent); }
[data-skin="blueprint"] .plot .cross-v { background: color-mix(in srgb, var(--ink) 40%, transparent); }
[data-skin="blueprint"] .plot .reg { border-color: var(--b-sulph); opacity: 1; }
[data-skin="blueprint"] .quote { border-left-color: var(--b-coral); }
[data-skin="blueprint"] .hh { border-color: var(--b-mint); }
[data-skin="blueprint"] .dmap .pin .ring { border-color: var(--b-coral); }

/* Footer heads, one pastel each. */
[data-skin="blueprint"] .foot-col:nth-child(1) .foot-col-title { color: var(--b-sulph); }
[data-skin="blueprint"] .foot-col:nth-child(2) .foot-col-title { color: var(--b-coral); }
[data-skin="blueprint"] .foot-col:nth-child(3) .foot-col-title { color: var(--b-mint); }
[data-skin="blueprint"] .newsletter-label { color: var(--b-rose); }

/* The outlined hero word takes the sulphur, so the highlighter and the chalk
   share the top of the page. */
[data-skin="blueprint"] .hw-xray { -webkit-text-stroke-color: var(--b-sulph); }

/* OXBLOOD — a warm dark ground. Wine-red stock, cream ink, and five light
   pens that read as gilt and velvet: brass, rose, celadon, apricot, powder
   blue. Where BLUEPRINT is a drawing office, this is the front of house — a
   playbill, a lobby, a bar at one in the morning. It is the plate for anyone
   who wants the site dark without the dark being black or cold.

   A deep ground makes every light colour legible both ways, so the five are
   type on the wine AND fills with the wine through them: brass 8.17:1, rose
   7.93:1, celadon 9.86:1, apricot 8.47:1, powder 8.48:1. Brass leads and is
   the focus ring; rose is the second highlight.

   OXBLOOD frames. Nothing in the nav fills: a hovered item draws its frame
   AND its label in one pen, the way PRINT does on white, and the dates are
   outlined chips. Solid brass is kept for the few things that are buttons.

   The ribbon bar is the cream --ink, so the lead there is the wine itself
   and the shows are a deep brass (12.59:1 and 6.21:1 on that bar).

   Body ink is 12.59:1, muted 6.06:1, focus 8.17:1. */
[data-skin="oxblood"] {
  --paper: #4a1020; --ink: #f3e9d8; --mute: #c29a9c; --line: #f3e9d8;
  --acc: #e6b84a;       /* brass. 8.17:1 on --paper */
  --acc-ink: #4a1020;   /* type over an --acc fill: the ground. 8.17:1 */
  --acc-on-ink: #4a1020;/* the ribbon bar is the cream --ink, so this goes DARK: the ground. 12.59:1 */
  --acc2: #f2a7b8;      /* rose. 7.93:1 */
  --focus: #e6b84a;     /* 8.17:1 on --paper */
  --mark-early: #ffb07a;/* apricot. 8.47:1 — the warm one */
  --mark-late:  #a9c4e8;/* powder blue. 8.48:1 */
  --ev-a0: #e6b84a; --ev-a1: #f2a7b8; --ev-a2: #a8dcc0; --ev-a3: #ffb07a;

  --o-brass: #e6b84a; --o-rose: #f2a7b8; --o-cel: #a8dcc0;
  --o-apri: #ffb07a; --o-pow: #a9c4e8;
  --o-brass-on-ink: #6e5000;/* deep brass for the cream bar. 6.21:1 */
}

/* ── OXBLOOD: gilt frames on wine ── */

/* Wordmark: a brass block with the wine mark through it; rose under the cursor. */
[data-skin="oxblood"] .bar-logo { background-color: var(--o-brass); }
[data-skin="oxblood"] .bar-logo:hover { background-color: var(--o-rose); }
[data-skin="oxblood"] .bar-logo:hover::before { background-color: var(--paper); }

/* Nav: frame and label in one pen, no fill. */
[data-skin="oxblood"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s; }
[data-skin="oxblood"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { color: var(--o-brass); border-color: var(--o-brass); }
[data-skin="oxblood"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { color: var(--o-rose);  border-color: var(--o-rose); }
[data-skin="oxblood"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { color: var(--o-cel);   border-color: var(--o-cel); }
[data-skin="oxblood"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { color: var(--o-apri);  border-color: var(--o-apri); }
[data-skin="oxblood"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { color: var(--o-pow);   border-color: var(--o-pow); }
/* The page you are on: brass frame, cream label. */
[data-skin="oxblood"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--o-brass); }
/* PRIVATE RENTALS: the cream block goes brass under the cursor. */
[data-skin="oxblood"] .nav-links a.nav-cta:hover { background: var(--o-brass); color: var(--paper); }

/* Fullscreen menu — one pen per line. */
[data-skin="oxblood"] .menu a.menu-link:nth-child(5n+1):hover { color: var(--o-brass); }
[data-skin="oxblood"] .menu a.menu-link:nth-child(5n+2):hover { color: var(--o-rose); }
[data-skin="oxblood"] .menu a.menu-link:nth-child(5n+3):hover { color: var(--o-cel); }
[data-skin="oxblood"] .menu a.menu-link:nth-child(5n+4):hover { color: var(--o-apri); }
[data-skin="oxblood"] .menu a.menu-link:nth-child(5n+5):hover { color: var(--o-pow); }

/* Highlighted words: lit type, brass at the top of the page and a different
   pen for each family below it. */
[data-skin="oxblood"] .cap h2 i,
[data-skin="oxblood"] .ph-title i        { color: var(--o-brass); }
[data-skin="oxblood"] .mdiv h2 i,
[data-skin="oxblood"] .prose h2 i        { color: var(--o-rose); }
[data-skin="oxblood"] .idx-cap h2 i      { color: var(--o-cel); }
[data-skin="oxblood"] .cta-band h2 i     { color: var(--o-apri); }
[data-skin="oxblood"] .tech-cap i,
[data-skin="oxblood"] .stat-num i        { color: var(--o-pow); }
[data-skin="oxblood"] .hh h2 i,
[data-skin="oxblood"] .hh h2 .t-serif    { color: var(--o-rose); }
[data-skin="oxblood"] .cta-band::before  { color: var(--o-rose); }
[data-skin="oxblood"] .cta-band::after   { color: var(--o-cel); }
[data-skin="oxblood"] .hero-side u,
[data-skin="oxblood"] .hero-annot .desc u { text-decoration-color: var(--o-brass); text-decoration-thickness: 4px; }

/* Dates: outlined chips, pen inside its own frame. */
[data-skin="oxblood"] .spec-date { display: inline-block; color: var(--o-brass);
  border: 1px solid var(--o-brass); padding: 2px 7px; margin-bottom: 7px; }
[data-skin="oxblood"] .spec:nth-child(4n+2) .spec-date { color: var(--o-rose); border-color: var(--o-rose); }
[data-skin="oxblood"] .spec:nth-child(4n+3) .spec-date { color: var(--o-cel);  border-color: var(--o-cel); }
[data-skin="oxblood"] .spec:nth-child(4n+4) .spec-date { color: var(--o-apri); border-color: var(--o-apri); }
[data-skin="oxblood"] .crow-date { color: var(--o-pow); }

/* Solids: brass stays on the buttons (that is --acc); the newsletter goes
   celadon, "here" rose, the CMS banner apricot. Wine type on all of them. */
@media (hover: hover) { [data-skin="oxblood"] .newsletter-form button:hover { background: var(--o-cel); color: var(--paper); } }
[data-skin="oxblood"] .cms-banner { background: var(--o-apri); color: var(--paper); }
[data-skin="oxblood"] ::selection { background: var(--o-brass); color: var(--paper); }

/* Ribbon: the bar is cream, so the shows go to a deep brass beside the wine lead. */
[data-skin="oxblood"] .ribbon-track span.hot { color: var(--o-brass-on-ink); }

/* Plot layer. */
[data-skin="oxblood"] .plot .ring { border-color: color-mix(in srgb, var(--o-rose) 55%, transparent); }
[data-skin="oxblood"] .plot .ring::after { border-color: color-mix(in srgb, var(--o-pow) 45%, transparent); }
[data-skin="oxblood"] .plot .cross-h { background: color-mix(in srgb, var(--o-brass) 40%, transparent); }
[data-skin="oxblood"] .plot .cross-v { background: color-mix(in srgb, var(--o-cel) 40%, transparent); }
[data-skin="oxblood"] .plot .reg { border-color: var(--o-brass); opacity: 1; }
[data-skin="oxblood"] .quote { border-left-color: var(--o-brass); }
[data-skin="oxblood"] .hh { border-color: var(--o-rose); }
[data-skin="oxblood"] .dmap .pin .ring { border-color: var(--o-apri); }

/* Footer heads. */
[data-skin="oxblood"] .foot-col:nth-child(1) .foot-col-title { color: var(--o-brass); }
[data-skin="oxblood"] .foot-col:nth-child(2) .foot-col-title { color: var(--o-rose); }
[data-skin="oxblood"] .foot-col:nth-child(3) .foot-col-title { color: var(--o-cel); }
[data-skin="oxblood"] .newsletter-label { color: var(--o-apri); }
[data-skin="oxblood"] .hw-xray { -webkit-text-stroke-color: var(--o-brass); }

/* FOREST — the v4 study's green and orange, carried onto this design system.
   Forest-green stock — lifted a step from the study's #0f2418 so it reads as
   green and not as black, with the orange and the sage lifted with it to hold
   AA —, a warm white ink, ONE orange as the loud pen, and the study's two warm
   greys, sage and silver, as the quiet ones.

   One orange, on purpose. The study also carried an apricot and a honey, and
   on the page the three read as the same orange mismatched rather than as
   three colours — most plainly on the booking page, where orange heading
   blocks sat over a honey closing line. Apricot survives only as what orange
   turns under the cursor; honey is gone. Variety comes from the greys.

   Everything light is legible both ways on this ground: orange 4.96:1,
   apricot (hover only) 6.70:1, sage 5.19:1, silver 6.94:1.

   FOREST blocks, the way the v4 site did: filled orange buttons, a hovered
   nav item filled in its pen with the green through it, the hero's
   highlighted word an orange block, and the dates as filled chips. The
   greys keep it from being an orange site — they take every rule, ring and
   small label that is not a call to action.

   The ribbon bar is the white --ink; the lead there is the green itself and
   the shows are a deep orange (10.77:1 and 5.30:1 on that bar) — the one
   other orange, because the lead orange cannot be read on white.

   Body ink is 10.77:1, muted 5.61:1, focus 4.96:1. */
[data-skin="forest"] {
  --paper: #133f2b; --ink: #f7f4ec; --mute: #a8b6ac; --line: #f7f4ec;
  --acc: #f28f45;       /* orange. 4.96:1 on --paper */
  --acc-ink: #133f2b;   /* type over an --acc fill: the ground. 4.96:1 */
  --acc-on-ink: #133f2b;/* the ribbon bar is the white --ink, so this goes DARK: the ground. 10.77:1 */
  --acc2: #b6bcb4;      /* silver: the second highlight is a grey, not a second orange. 6.94:1 */
  --focus: #f28f45;     /* 4.96:1 on --paper */
  --mark-early: #f28f45;/* orange. 4.96:1 — the warm one */
  --mark-late:  #b6bcb4;/* silver. 6.94:1 */
  --ev-a0: #f28f45; --ev-a1: #b6bcb4; --ev-a2: #f28f45; --ev-a3: #9fb0a3;

  --f4-orange: #f28f45; --f4-sage: #9fb0a3; --f4-silver: #b6bcb4;
  --f4-orange-hover: #f0a860;/* apricot: orange under the cursor, and nowhere else */
  --f4-orange-on-ink: #a8481a;/* deep orange for the white bar. 5.30:1 */
}

/* ── FOREST: orange blocks, grey rules ── */

/* Wordmark: an orange block with the green mark through it; lighter under the cursor. */
[data-skin="forest"] .bar-logo { background-color: var(--f4-orange); }
[data-skin="forest"] .bar-logo:hover { background-color: var(--f4-orange-hover); }
[data-skin="forest"] .bar-logo:hover::before { background-color: var(--paper); }

/* Nav: each item fills in its own pen with the green as its type. */
[data-skin="forest"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s, background-color 0.15s; }
[data-skin="forest"] .nav-links a:not(.nav-cta):hover { color: var(--paper); }
[data-skin="forest"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { background: var(--f4-orange); border-color: var(--f4-orange); }
[data-skin="forest"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { background: var(--f4-silver); border-color: var(--f4-silver); }
[data-skin="forest"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { background: var(--f4-orange); border-color: var(--f4-orange); }
[data-skin="forest"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { background: var(--f4-sage);   border-color: var(--f4-sage); }
[data-skin="forest"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { background: var(--f4-silver); border-color: var(--f4-silver); }
/* The page you are on: orange frame, no fill, white label. */
[data-skin="forest"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--f4-orange); }
/* PRIVATE RENTALS is an orange block at rest and goes lighter under the cursor. */
[data-skin="forest"] .nav-links a.nav-cta { background: var(--f4-orange); border-color: var(--f4-orange); color: var(--paper); }
[data-skin="forest"] .nav-links a.nav-cta:hover { background: var(--f4-orange-hover); border-color: var(--f4-orange-hover); color: var(--paper); }

/* Fullscreen menu — one pen per line. */
[data-skin="forest"] .menu a.menu-link:nth-child(5n+1):hover { color: var(--f4-orange); }
[data-skin="forest"] .menu a.menu-link:nth-child(5n+2):hover { color: var(--f4-silver); }
[data-skin="forest"] .menu a.menu-link:nth-child(5n+3):hover { color: var(--f4-orange); }
[data-skin="forest"] .menu a.menu-link:nth-child(5n+4):hover { color: var(--f4-sage); }
[data-skin="forest"] .menu a.menu-link:nth-child(5n+5):hover { color: var(--f4-silver); }

/* Highlighted words. The hero and the plate captions take an orange block
   with the green through it; the families below are lit type. */
[data-skin="forest"] .cap h2 i,
[data-skin="forest"] .ph-title i {
  color: var(--paper); background: var(--f4-orange);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
[data-skin="forest"] .mdiv h2 i,
[data-skin="forest"] .prose h2 i        { color: var(--f4-orange); }
[data-skin="forest"] .idx-cap h2 i      { color: var(--f4-orange); }
[data-skin="forest"] .cta-band h2 i     { color: var(--f4-orange); }
[data-skin="forest"] .tech-cap i,
[data-skin="forest"] .stat-num i        { color: var(--f4-orange); }
[data-skin="forest"] .hh h2 i,
[data-skin="forest"] .hh h2 .t-serif    { color: var(--f4-orange); }
[data-skin="forest"] .cta-band::before  { color: var(--f4-sage); }
[data-skin="forest"] .cta-band::after   { color: var(--f4-silver); }
[data-skin="forest"] .hero-side u,
[data-skin="forest"] .hero-annot .desc u { text-decoration-color: var(--f4-orange); text-decoration-thickness: 4px; }

/* Dates: filled chips, green type through them; the list view takes orange type. */
[data-skin="forest"] .spec-date { display: inline-block; color: var(--paper);
  background: var(--f4-orange); padding: 2px 7px; margin-bottom: 7px; }
[data-skin="forest"] .spec-date i { color: color-mix(in srgb, var(--paper) 70%, transparent); }
[data-skin="forest"] .spec:nth-child(4n+2) .spec-date { background: var(--f4-silver); }
[data-skin="forest"] .spec:nth-child(4n+3) .spec-date { background: var(--f4-orange); }
[data-skin="forest"] .spec:nth-child(4n+4) .spec-date { background: var(--f4-sage); }
[data-skin="forest"] .crow-date { color: var(--f4-orange); }

/* Solids: orange on the buttons (that is --acc) and the CMS banner, lighter
   under the cursor; "here" is sage. Green type on all of them. */
@media (hover: hover) { [data-skin="forest"] .newsletter-form button:hover { background: var(--f4-orange-hover); color: var(--paper); } }
[data-skin="forest"] .cms-banner { background: var(--f4-orange); color: var(--paper); }
[data-skin="forest"] ::selection { background: var(--f4-orange); color: var(--paper); }

/* Ribbon: the bar is white, so the shows go to a deep orange beside the green lead. */
[data-skin="forest"] .ribbon-track span.hot { color: var(--f4-orange-on-ink); }

/* Plot layer — the greys, so the chrome stays chrome. */
[data-skin="forest"] .plot .ring { border-color: color-mix(in srgb, var(--f4-sage) 60%, transparent); }
[data-skin="forest"] .plot .ring::after { border-color: color-mix(in srgb, var(--f4-silver) 45%, transparent); }
[data-skin="forest"] .plot .cross-h { background: color-mix(in srgb, var(--f4-orange) 45%, transparent); }
[data-skin="forest"] .plot .cross-v { background: color-mix(in srgb, var(--f4-sage) 50%, transparent); }
[data-skin="forest"] .plot .reg { border-color: var(--f4-orange); opacity: 1; }
[data-skin="forest"] .quote { border-left-color: var(--f4-orange); }
[data-skin="forest"] .hh { border-color: var(--f4-sage); }
[data-skin="forest"] .dmap .pin .ring { border-color: var(--f4-orange); }

/* Footer heads. */
/* nth-child counts the address block, which comes first and has no heading —
   so Navigate is the 2nd child and Connect the 3rd. */
[data-skin="forest"] .foot-col:nth-child(2) .foot-col-title { color: var(--f4-orange); }
[data-skin="forest"] .foot-col:nth-child(3) .foot-col-title { color: var(--f4-sage); }
[data-skin="forest"] .newsletter-label { color: var(--f4-silver); }
[data-skin="forest"] .hw-xray { -webkit-text-stroke-color: var(--f4-orange); }

/* VELVET — plum stock, blush ink, and five juicy brights run warm and cool:
   mango, lime, cyan, rose, lilac. Spectra's instinct (the colour is in
   fills, a different fill per element) on a coloured ground; one of the
   Oct 27 2026 candidates, kept. Its tokens:
     --k1..--k5  fills; type on them is --on-k (the stock), 4.5:1 or more
     --t1..--t4  colours that hold as SMALL type on the stock, 4.5:1 or more
     --hot       the ribbon's show names, on the blush --ink bar */
/* VELVET: plum stock, blush ink, juicy brights (mango, lime, cyan, rose, lilac). Ink 13.79:1, muted 7.15:1. */
[data-skin="velvet"] {
  --paper: #2d1b3f; --ink: #fbeee6; --mute: #bba9c4; --line: #fbeee6;
  --acc: #ffb347; --acc-ink: #2d1b3f; --acc-on-ink: #6a2c8a; --acc2: #c6f06a; --focus: #ffb347;
  --mark-early: #ffb347; --mark-late: #7fdcf2;
  --ev-a0: #ffb347; --ev-a1: #c6f06a; --ev-a2: #5fd8f0; --ev-a3: #ff9fc0;
  --k1: #ffb347; --k2: #c6f06a; --k3: #5fd8f0; --k4: #ff7aa8; --k5: #b9a3ff; --on-k: var(--paper);
  --t1: #ffb347; --t2: #c6f06a; --t3: #5fd8f0; --t4: #ff9fc0; --hot: #6a2c8a;
}

/* ── VELVET: where the colour lands ── */
[data-skin="velvet"] .bar-logo { background-color: var(--t1); }
[data-skin="velvet"] .bar-logo:hover { background-color: var(--k1); }
[data-skin="velvet"] .bar-logo:hover::before { background-color: var(--on-k); }
[data-skin="velvet"] .nav-links a:not(.nav-cta) { transition: color 0.15s, border-color 0.15s, background-color 0.15s; }
[data-skin="velvet"] .nav-links a:not(.nav-cta):hover { color: var(--on-k); }
[data-skin="velvet"] .nav-links a:not(.nav-cta):nth-of-type(5n+1):hover { background: var(--k1); border-color: var(--k1); }
[data-skin="velvet"] .nav-links a:not(.nav-cta):nth-of-type(5n+2):hover { background: var(--k2); border-color: var(--k2); }
[data-skin="velvet"] .nav-links a:not(.nav-cta):nth-of-type(5n+3):hover { background: var(--k3); border-color: var(--k3); }
[data-skin="velvet"] .nav-links a:not(.nav-cta):nth-of-type(5n+4):hover { background: var(--k4); border-color: var(--k4); }
[data-skin="velvet"] .nav-links a:not(.nav-cta):nth-of-type(5n+5):hover { background: var(--k5); border-color: var(--k5); }
[data-skin="velvet"] .nav-links a:not(.nav-cta).active { border-color: var(--t1); }
[data-skin="velvet"] .nav-links a.nav-cta:hover { background: var(--k1); color: var(--on-k); }
[data-skin="velvet"] .menu a.menu-link:nth-child(4n+1):hover { color: var(--t1); }
[data-skin="velvet"] .menu a.menu-link:nth-child(4n+2):hover { color: var(--t2); }
[data-skin="velvet"] .menu a.menu-link:nth-child(4n+3):hover { color: var(--t3); }
[data-skin="velvet"] .menu a.menu-link:nth-child(4n+4):hover { color: var(--t4); }
/* Highlighted words: a fill behind the word, a different one per family. */
[data-skin="velvet"] .cap h2 i, [data-skin="velvet"] .ph-title i, [data-skin="velvet"] .mdiv h2 i, [data-skin="velvet"] .prose h2 i, [data-skin="velvet"] .tech-cap i, [data-skin="velvet"] .stat-num i,
[data-skin="velvet"] .hh h2 i, [data-skin="velvet"] .hh h2 .t-serif, [data-skin="velvet"] .idx-cap h2 i, [data-skin="velvet"] .cta-band h2 i {
  color: var(--on-k); background: var(--k1);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
[data-skin="velvet"] .idx-cap h2 i { background: var(--k3); }
/* Section heads take the fills in turn, so a long page shows the whole set. */
[data-skin="velvet"] .cap:nth-of-type(4n+2) h2 i { background: var(--k3); }
[data-skin="velvet"] .cap:nth-of-type(4n+3) h2 i { background: var(--k5); }
[data-skin="velvet"] .cap:nth-of-type(4n+4) h2 i { background: var(--k2); }
[data-skin="velvet"] .cta-band h2 i { background: var(--k5); }
[data-skin="velvet"] .mdiv h2 i, [data-skin="velvet"] .prose h2 i { background: var(--k2); }
[data-skin="velvet"] .tech-cap i, [data-skin="velvet"] .stat-num i { background: var(--k4); }
[data-skin="velvet"] .cta-band::before { color: var(--k2); }
[data-skin="velvet"] .cta-band::after { color: var(--k4); }
[data-skin="velvet"] .hero-side u, [data-skin="velvet"] .hero-annot .desc u { text-decoration-color: var(--k1); text-decoration-thickness: 6px; }
/* Dates: chips, four fills in turn; the list view takes coloured type. */
[data-skin="velvet"] .spec-date { display: inline-block; color: var(--on-k); background: var(--k3); padding: 2px 7px; margin-bottom: 7px; }
[data-skin="velvet"] .spec-date i { color: inherit; opacity: 0.75; }
[data-skin="velvet"] .spec:nth-child(4n+2) .spec-date { background: var(--k5); }
[data-skin="velvet"] .spec:nth-child(4n+3) .spec-date { background: var(--k2); }
[data-skin="velvet"] .spec:nth-child(4n+4) .spec-date { background: var(--k4); }
[data-skin="velvet"] .crow-date { color: var(--t1); }
[data-skin="velvet"] .btn-solid, [data-skin="velvet"] .modal-close:hover, [data-skin="velvet"] .modal-meta span.hl { background: var(--k1); border-color: var(--k1); color: var(--on-k); }
@media (hover: hover) {
  [data-skin="velvet"] .spec-buy:hover, [data-skin="velvet"] .newsletter-form button:hover { background: var(--k1); border-color: var(--k1); color: var(--on-k); }
}
[data-skin="velvet"] .cms-banner { background: var(--k1); color: var(--on-k); }
[data-skin="velvet"] ::selection { background: var(--k1); color: var(--on-k); }
[data-skin="velvet"] .ribbon-track span.hot { color: var(--hot); }
/* Plot layer: decoration, so the fills go on as they are. */
[data-skin="velvet"] .plot .ring { border-color: color-mix(in srgb, var(--k5) 85%, transparent); }
[data-skin="velvet"] .plot .ring::after { border-color: color-mix(in srgb, var(--k2) 75%, transparent); }
[data-skin="velvet"] .plot .cross-h { background: color-mix(in srgb, var(--k3) 45%, transparent); }
[data-skin="velvet"] .plot .cross-v { background: color-mix(in srgb, var(--k1) 85%, transparent); }
[data-skin="velvet"] .plot .reg { border-color: var(--t1); opacity: 1; }
[data-skin="velvet"] .quote { border-left-width: 6px; border-left-color: var(--k1); }
[data-skin="velvet"] .hh { border-color: var(--k4); }
[data-skin="velvet"] .dmap .pin .ring { border-color: var(--k3); }
[data-skin="velvet"] .foot-col:nth-child(2) .foot-col-title { color: var(--t2); }
[data-skin="velvet"] .foot-col:nth-child(3) .foot-col-title { color: var(--t3); }
[data-skin="velvet"] .newsletter-label { color: var(--t4); }
[data-skin="velvet"] .hw-xray { -webkit-text-stroke-color: var(--t1); }


/* GRAPHITE — secret colour. A grey page: light grey stock, black ink, grey
   fills where the other plates put colour. The colour is there, but only
   where you are: the background is drawn in brights and greyed everywhere
   but a disc around the cursor (--field-reveal, dither.js), the posters
   bloom as they always do, and buttons, nav, chips and highlighted words
   turn a bright under the cursor and a DIFFERENT bright while pressed.
     BRIGHTS  chartreuse, pink, cyan, orange, violet, mint: hover and press
              fills only, black type on them (15.97, 7.08, 11.00, 9.34, 7.98,
              12.72:1), and the background's pens.
     DEEPS    raspberry 5.76, blue 5.60, violet 6.56, green 5.48:1 on the
              stock: coloured type on hover (links, menu lines).
   Body 15.84:1, muted 5.61:1, focus (deep violet) 6.56:1. */
[data-skin="graphite"] {
  --paper: #ebebeb; --ink: #111111; --mute: #5c5c5c; --line: #111111;
  --acc: #111111; --acc-ink: #ebebeb; --acc-on-ink: #9a9a9a; --acc2: #5c5c5c;
  --focus: #5a2fc9;
  --mark-early: #a33f0b; --mark-late: #1d5a85;
  --ev-a0: #111111; --ev-a1: #5c5c5c; --ev-a2: #111111; --ev-a3: #5c5c5c;
  --g-fill: #cfcfcf; --g-chip: #d9d9d9;
  --g-chart: #c8ff00; --g-pink: #ff6aa8; --g-cyan: #38d6ff; --g-orange: #ffa12e; --g-violet: #b996ff; --g-mint: #2ef0a8;
  --g-rasp: #b0144f; --g-blue: #0a5f9e; --g-deepv: #5a2fc9; --g-green: #0b6b4a;
  /* the background: brights, revealed within this many px of the cursor, at
     full strength only within the core, fading out to the rim */
  --field-acc: #ff3d8b; --field-acc2: #12b8f0; --field-ev-a0: #ff8a00; --field-ev-a1: #8a5cff;
  --field-ev-a2: #00c98a; --field-ev-a3: #b8e600;
  --field-reveal: 380; --field-reveal-core: 130;
}
/* The background's strokes are hairlines at low alpha, so their colour reads
   faint even where it is revealed; lift it. Grey stays grey under saturate(). */
[data-skin="graphite"] #field { filter: saturate(2.2) contrast(1.15); }

/* Everything that turns colour eases into it. */
[data-skin="graphite"] :is(.bar-logo, .nav-links a, .btn, .spec-buy, .chip, .skin-btn, .spec-date, .cap h2 i, .ph-title i, .idx-cap h2 i, .cta-band h2 i, .faq-q span) {
  transition: background-color 0.18s, border-color 0.18s, color 0.18s;
}

/* Wordmark: a black block; a bright under the cursor, another while pressed. */
[data-skin="graphite"] .bar-logo:hover { background-color: var(--g-chart); }
[data-skin="graphite"] .bar-logo:hover::before { background-color: var(--ink); }
[data-skin="graphite"] .bar-logo:active { background-color: var(--g-pink); }

/* Nav: each item its own bright under the cursor, the next one along while pressed. */
[data-skin="graphite"] .nav-links a:not(.nav-cta):hover { color: var(--ink); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+1):hover { background: var(--g-chart);  border-color: var(--g-chart); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+2):hover { background: var(--g-pink);   border-color: var(--g-pink); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+3):hover { background: var(--g-cyan);   border-color: var(--g-cyan); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+4):hover { background: var(--g-orange); border-color: var(--g-orange); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+5):hover { background: var(--g-violet); border-color: var(--g-violet); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+6):hover { background: var(--g-mint);   border-color: var(--g-mint); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+1):active { background: var(--g-pink);   border-color: var(--g-pink); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+2):active { background: var(--g-cyan);   border-color: var(--g-cyan); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+3):active { background: var(--g-orange); border-color: var(--g-orange); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+4):active { background: var(--g-violet); border-color: var(--g-violet); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+5):active { background: var(--g-mint);   border-color: var(--g-mint); }
[data-skin="graphite"] .nav-links a:not(.nav-cta):nth-of-type(6n+6):active { background: var(--g-chart);  border-color: var(--g-chart); }
[data-skin="graphite"] .nav-links a:not(.nav-cta).active { color: var(--ink); border-color: var(--ink); }
[data-skin="graphite"] .nav-links a.nav-cta:hover { background: var(--g-orange); color: var(--ink); }
[data-skin="graphite"] .nav-links a.nav-cta:active { background: var(--g-cyan); color: var(--ink); }

/* Phone menu: lines turn a deep under the finger. */
[data-skin="graphite"] .menu a.menu-link:nth-child(4n+1):hover { color: var(--g-deepv); }
[data-skin="graphite"] .menu a.menu-link:nth-child(4n+2):hover { color: var(--g-rasp); }
[data-skin="graphite"] .menu a.menu-link:nth-child(4n+3):hover { color: var(--g-blue); }
[data-skin="graphite"] .menu a.menu-link:nth-child(4n+4):hover { color: var(--g-green); }

/* Highlighted words: a grey block at rest; the heading under the cursor takes a bright. */
[data-skin="graphite"] :is(.cap h2 i, .ph-title i, .mdiv h2 i, .prose h2 i, .tech-cap i, .stat-num i, .hh h2 i, .hh h2 .t-serif, .idx-cap h2 i, .cta-band h2 i) {
  color: var(--ink); background: var(--g-fill);
  padding: 0 0.14em; box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
[data-skin="graphite"] .cap:nth-of-type(4n+1):hover h2 i, [data-skin="graphite"] .page-head:hover .ph-title i { background: var(--g-chart); }
[data-skin="graphite"] .cap:nth-of-type(4n+2):hover h2 i, [data-skin="graphite"] .idx-cap:hover h2 i { background: var(--g-cyan); }
[data-skin="graphite"] .cap:nth-of-type(4n+3):hover h2 i, [data-skin="graphite"] .cta-band:hover h2 i { background: var(--g-pink); }
[data-skin="graphite"] .cap:nth-of-type(4n+4):hover h2 i { background: var(--g-orange); }
[data-skin="graphite"] .hero-side u, [data-skin="graphite"] .hero-annot .desc u { text-decoration-color: var(--g-fill); text-decoration-thickness: 6px; }

/* Show cards: a grey date chip; the card under the cursor lights its chip. */
[data-skin="graphite"] .spec-date { display: inline-block; color: var(--ink); background: var(--g-chip); padding: 2px 7px; margin-bottom: 7px; }
[data-skin="graphite"] .spec-date i { color: inherit; opacity: 0.7; }
[data-skin="graphite"] :is(.spec:hover, .spec.hot):nth-child(4n+1) .spec-date { background: var(--g-mint); }
[data-skin="graphite"] :is(.spec:hover, .spec.hot):nth-child(4n+2) .spec-date { background: var(--g-pink); }
[data-skin="graphite"] :is(.spec:hover, .spec.hot):nth-child(4n+3) .spec-date { background: var(--g-cyan); }
[data-skin="graphite"] :is(.spec:hover, .spec.hot):nth-child(4n+4) .spec-date { background: var(--g-chart); }
[data-skin="graphite"] .crow:hover .crow-date { color: var(--g-deepv); }

/* A touch screen has no cursor to find the colour by, so the highlighted
   headings and the date chips flash a bright as they scroll into view
   (.g-pop, set once each by dither.js) and settle back to grey. No fill
   mode, so a chip the card-in-the-middle rule has lit stays lit after. */
[data-skin="graphite"] :is(.cap h2 i, .ph-title i, .mdiv h2 i, .prose h2 i, .idx-cap h2 i, .cta-band h2 i) { --rest: var(--g-fill); --pop: var(--g-chart); }
[data-skin="graphite"] .cap:nth-of-type(4n+2) h2 i, [data-skin="graphite"] .idx-cap h2 i { --pop: var(--g-cyan); }
[data-skin="graphite"] .cap:nth-of-type(4n+3) h2 i, [data-skin="graphite"] .cta-band h2 i { --pop: var(--g-pink); }
[data-skin="graphite"] .cap:nth-of-type(4n+4) h2 i, [data-skin="graphite"] .mdiv h2 i { --pop: var(--g-orange); }
[data-skin="graphite"] .spec-date { --rest: var(--g-chip); --pop: var(--g-mint); }
[data-skin="graphite"] .spec:nth-child(4n+2) .spec-date { --pop: var(--g-pink); }
[data-skin="graphite"] .spec:nth-child(4n+3) .spec-date { --pop: var(--g-cyan); }
[data-skin="graphite"] .spec:nth-child(4n+4) .spec-date { --pop: var(--g-violet); }
@keyframes g-pop { 0%, 40% { background-color: var(--pop); } 100% { background-color: var(--rest); } }
@media (hover: none) and (prefers-reduced-motion: no-preference) {
  [data-skin="graphite"] .g-pop { animation: g-pop 2.6s ease-out; }
}

/* Buttons: black or outlined at rest; a bright under the cursor, another pressed. */
@media (hover: hover) {
  [data-skin="graphite"] .btn:hover, [data-skin="graphite"] .btn-solid:hover { background: var(--g-chart); border-color: var(--g-chart); color: var(--ink); }
  [data-skin="graphite"] .spec-buy:hover { background: var(--g-cyan); border-color: var(--g-cyan); color: var(--ink); }
  [data-skin="graphite"] .newsletter-form button:hover { background: var(--g-pink); border-color: var(--g-pink); color: var(--ink); }
  [data-skin="graphite"] .chip:not(.active):hover { background: var(--g-violet); border-color: var(--g-violet); color: var(--ink); }
  [data-skin="graphite"] .skin-btn:not(.active):hover { background: var(--g-mint); border-color: var(--g-mint); color: var(--ink); }
  [data-skin="graphite"] .faq-q:hover span { color: var(--g-rasp); }
}
[data-skin="graphite"] :is(.btn, .btn-solid, .newsletter-form button):active { background: var(--g-pink); border-color: var(--g-pink); color: var(--ink); }
[data-skin="graphite"] :is(.spec-buy, .chip, .skin-btn):active { background: var(--g-orange); border-color: var(--g-orange); color: var(--ink); }

/* Links in running text: a bright behind them under the cursor. */
[data-skin="graphite"] main :is(p, li, dd) a:hover { background: var(--g-chart); color: var(--ink); text-decoration-color: var(--ink); }
[data-skin="graphite"] main :is(p, li, dd) a:active { background: var(--g-cyan); }
[data-skin="graphite"] .foot-col a:hover { color: var(--ink); }
[data-skin="graphite"] .foot-col a:nth-child(3n+2):hover { color: var(--g-deepv); }
[data-skin="graphite"] .foot-col a:nth-child(3n+3):hover { color: var(--g-rasp); }
[data-skin="graphite"] .foot-col a:nth-child(3n+4):hover { color: var(--g-blue); }

[data-skin="graphite"] .cms-banner { background: var(--g-fill); color: var(--ink); }
[data-skin="graphite"] ::selection { background: var(--g-chart); color: var(--ink); }

/* Ribbon: grey on black at rest; the show names light up while it is hovered. */
[data-skin="graphite"] .ribbon-track span.hot { color: #9a9a9a; transition: color 0.2s; }
[data-skin="graphite"] .ribbon:hover .ribbon-track span.hot { color: var(--g-chart); }

/* Plot layer: grey chrome. */
[data-skin="graphite"] .plot .ring { border-color: color-mix(in srgb, var(--ink) 30%, transparent); }
[data-skin="graphite"] .plot .reg { border-color: var(--ink); opacity: 0.7; }
[data-skin="graphite"] .quote { border-left-width: 6px; border-left-color: var(--g-fill); }
[data-skin="graphite"] .hh { border-color: var(--g-fill); }
[data-skin="graphite"] .foot-col-title, [data-skin="graphite"] .newsletter-label { color: var(--mute); }
[data-skin="graphite"] .hw-xray { -webkit-text-stroke-color: var(--ink); }

html { scroll-behavior: smooth; }
body {
  background: var(--paper); color: var(--ink);
  font-family: var(--mono); font-size: 13px; line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  transition: background-color 0.35s ease, color 0.35s ease;
  overflow-x: hidden;
}
::selection { background: var(--acc); color: var(--acc-ink); }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
img { display: block; max-width: 100%; }

/* ═════════ GENERATIVE FIELD + PLOT LAYERS ═════════ */
/* width/height are explicit because `inset: 0` does NOT stretch a replaced
   element — a canvas takes its intrinsic size from its width/height attributes,
   and this box only ever matched the viewport because dither.js sets the
   backing store to the viewport. A no-op while that stays true, and the thing
   that stops a smaller backing store from silently shrinking the canvas to a
   corner of the screen, which is exactly what happened once. */
#field { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* overflow: the ring runs past the screen's edge on a narrow phone, and a
   fixed box's overflow still counts as page width there */
.plot { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.plot .ring {
  position: absolute; width: 130vmin; height: 130vmin;
  left: 60%; top: 45%; translate: -50% -50%;
  border: 1px dashed color-mix(in srgb, var(--ink) 26%, transparent); border-radius: 50%;
  animation: ringspin 90s linear infinite;
}
.plot .ring::after { content: ''; position: absolute; inset: 12%; border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent); border-radius: 50%; }
@keyframes ringspin { to { rotate: 360deg; } }
.plot .cross-h, .plot .cross-v { position: absolute; background: color-mix(in srgb, var(--ink) 14%, transparent); }
.plot .cross-h { left: 0; right: 0; top: 45%; height: 1px; }
.plot .cross-v { top: 0; bottom: 0; left: 60%; width: 1px; }
.plot .reg { position: absolute; width: 18px; height: 18px; border-left: 1px solid var(--ink); border-top: 1px solid var(--ink); opacity: 0.6; }
.plot .reg.tl { top: 10px; left: 10px; } .plot .reg.tr { top: 10px; right: 10px; rotate: 90deg; }
.plot .reg.bl { bottom: 10px; left: 10px; rotate: 270deg; } .plot .reg.br { bottom: 10px; right: 10px; rotate: 180deg; }
/* Before the translate/rotate properties (Chrome < 104) the centring and the
   corner turns go through transform instead, or the ring sits half a ring off
   to the lower right and every corner mark faces the same way. The spin and
   the twinkle stay still there; nothing else on these sets a transform. */
@supports not (translate: 0) {
  .plot .ring, .sparks i, .sparks .sp-ring, .sparks .sp-reg { transform: translate(-50%, -50%); }
  .plot .reg.tr { transform: rotate(90deg); }
  .plot .reg.bl { transform: rotate(270deg); }
  .plot .reg.br { transform: rotate(180deg); }
}

@media (max-width: 760px) {
  /* On a phone the fixed crosshair ran straight through paragraphs. */
  .plot .cross-h, .plot .cross-v { display: none; }
}
/* Dark plates: native date pickers, selects and scrollbars follow them
   instead of staying light. */
[data-skin="negative"], [data-skin="fluoro"], [data-skin="blueprint"], [data-skin="oxblood"], [data-skin="forest"], [data-skin="velvet"] { color-scheme: dark; }

main, .bar, footer, .ribbon-wrap, .cms-banner { position: relative; z-index: 1; }

/* ═════════ TOP BAR ═════════ */
.bar {
  position: sticky; top: 0; z-index: 1100;
  display: flex; align-items: center; gap: 1.5rem;
  padding: 0 var(--pad); height: 54px;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
/* The Hc monogram, reversed out of a solid box built to the CTA's: the same
   40px height and 12px of horizontal air as PRIVATE PARTIES at the other end of
   the bar, so the two ends of the nav are the same object. The whole "Holocene"
   ran 118.7px here and was the widest thing in a 54px bar; the H and the c
   joined the way the wordmark's own "oc" is says the same name in 31px. See
   holocene-hc.svg for where every number in it comes from.

   The box takes --ink (what the mark itself used to be painted in) and the mark
   takes --paper, so the pair inverts skin by skin without either being spelled
   white anywhere: on NEGATIVE and FLUORO, where --ink is the light one, the box
   goes light and the mark dark, which is the same reversal read the other way.

   Two elements rather than one because a mask and a background cannot both live
   on the same box — the mask would eat the box. The ::before carries the mark,
   the <a> carries the field.

   The mark is still 20.8px — cap height, as the wordmark it replaced was cap
   height plus the comma's tail, so the H lands exactly where it always did and
   the bar's optical baseline never moves. Width follows the artwork's own
   71.83/48.37. The link keeps its text in a .vh span for screen readers and
   search, which is the only place the full word is now. */
.bar-logo {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  min-height: 40px; padding: 0 12px;
  background-color: var(--ink);
  transition: background-color 0.15s;
}
.bar-logo::before {
  content: ''; display: block;
  height: 20.8px; width: calc(20.8px * 71.83 / 48.37);
  background-color: var(--paper);
  -webkit-mask: url("holocene-hc.svg") no-repeat center / contain;
  mask: url("holocene-hc.svg") no-repeat center / contain;
  transition: background-color 0.15s;
}
.bar-logo:hover { background-color: var(--acc); }
.bar-logo:hover::before { background-color: var(--acc-ink); }
/* `is-type` is gone with the wordmark. Home used to swap the bar to the word set
   in Bricolage whenever the hero wore the logo, so the page never showed the
   same Holocene in the same face twice. The monogram is not the word, so it
   collides with neither hero — and swapping a 31px mark for a 120px word would
   have shoved the whole nav sideways on every toggle, where the wordmark it
   replaced moved it by 2px. The bar-logo click still toggles the hero. */
/* The gap is now the air BETWEEN THE HOVER FRAMES, not between the labels —
   see the frame rule below for why the frames stopped being free. Small
   numbers, because each frame already carries 12px of its own inset either
   side and the eye reads that as the spacing. */
/* No gap: each link already carries a 1px transparent border, so the hover
   rectangles are the border boxes and these sit exactly edge to edge —
   touching, never overlapping. Spacing between the words is the 11px
   padding on each side, and that is where to change it. */
.nav-links { display: flex; align-items: center; gap: 0; margin-left: auto; }
/* 12.5px cap — halfway between the original 10px and the 15px that proved too
   big. Still scales with the viewport, and never wraps inside a 54px bar. */
.nav-links a { display: inline-flex; align-items: center; min-height: 40px; white-space: nowrap;
  font-size: clamp(10px, 0.87vw, 12.5px); letter-spacing: 0.14em; text-transform: uppercase; color: var(--mute); transition: color 0.15s; }
.nav-links a:hover, .nav-links a.active { color: var(--ink); }
/* Hover frame, pulled in a pixel on each of its four sides from where it was:
   11px of horizontal padding where it had 12, and 38px tall where it had 40,
   with box-sizing: border-box putting the border inside that height. Measured
   to its outer edge that leaves it exactly level with the CTA's box either side
   (11 + 1 of border against the CTA's 12 of padding) and a pixel inside it top
   and bottom — where it used to stand a pixel proud of it horizontally.

   It used to cost no width at all — `margin: 0 -13px` cancelled the padding and
   the border exactly, so the frame was drawn INTO the gap either side. That is
   what put the frames on top of each other: reaching 13px out of a gap that is
   only 19.2px wide at 1280 left adjacent frames overlapping by 6.8px, and at
   the narrow end the fill ran under the neighbouring label. Only one frame is
   ever coloured, so nothing looked wrong until a skin filled it.

   So the frame now occupies its own space and the row's gap measures the air
   between frames instead. Labels sit further apart than they did — that spread
   IS the frame, made visible.

   The border is present at all times and only changes colour, so nothing moves
   on hover — padding applied on hover alone would shove the whole row sideways. */
.nav-links a:not(.nav-cta) { min-height: 38px; padding: 0 11px; border: 1px solid transparent; transition: color 0.15s, border-color 0.15s; }
.nav-links a:not(.nav-cta):hover { border-color: var(--ink); }
/* 38px to match the link boxes it now abuts: at 40 it stood 1px proud top and
   bottom, which read as emphasis with a gap there and as a misalignment without. */
.nav-links a.nav-cta { color: var(--paper); background: var(--ink); min-height: 38px; padding: 7px 12px; }
.nav-links a.nav-cta:hover { background: var(--acc); color: var(--acc-ink); }
.burger { display: none; align-items: center; min-height: 44px; min-width: 44px; justify-content: flex-end; margin-left: auto; font-size: 12px; font-weight: 700; letter-spacing: 0.1em; color: var(--ink); padding: 8px; }
@media (max-width: 1000px) { .nav-links { display: none; } .burger { display: flex; } }

/* ═════════ FULLSCREEN MENU ═════════ */
.menu {
  position: fixed; inset: 0; z-index: 1090; background: var(--paper);
  display: flex; flex-direction: column; justify-content: center;
  padding: 54px var(--pad) 0;
  opacity: 0; visibility: hidden; transition: opacity 0.25s, visibility 0.25s;
}
.menu.open { opacity: 1; visibility: visible; }
.menu a.menu-link {
  font-family: var(--disp); font-weight: 800; font-size: clamp(32px, 7vw, 68px);
  line-height: 1.22; text-transform: uppercase; letter-spacing: -0.01em;
  display: flex; align-items: baseline; gap: 16px;
  opacity: 0; transform: translateY(14px) rotate(-0.5deg);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease), color 0.15s;
}
.menu.open a.menu-link { opacity: 1; transform: none; }
/* The stagger delays the entrance (opacity, transform) only. The third value
   is the hover colour, which must answer at once, not 0.24s late. */
.menu.open a.menu-link:nth-child(2) { transition-delay: 0.04s, 0.04s, 0s; }
.menu.open a.menu-link:nth-child(3) { transition-delay: 0.08s, 0.08s, 0s; }
.menu.open a.menu-link:nth-child(4) { transition-delay: 0.12s, 0.12s, 0s; }
.menu.open a.menu-link:nth-child(5) { transition-delay: 0.16s, 0.16s, 0s; }
.menu.open a.menu-link:nth-child(6) { transition-delay: 0.2s, 0.2s, 0s; }
.menu.open a.menu-link:nth-child(7) { transition-delay: 0.24s, 0.24s, 0s; }
.menu a.menu-link:hover { color: var(--acc); }
.menu a.menu-link em { font-family: var(--mono); font-style: normal; font-weight: 400; font-size: 9px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--mute); }
.menu-foot { position: absolute; bottom: max(1.4rem, env(safe-area-inset-bottom)); left: var(--pad); right: var(--pad);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  font-size: 9.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--mute); }

/* ── CMS banner ── */
.cms-banner { display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 10px 18px; text-align: center; background: var(--acc); color: var(--acc-ink);
  z-index: 1080; }
.cms-banner span { font-size: 10.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; }
.cms-banner i { font-style: normal; transition: transform 0.2s; }
a.cms-banner:hover i { transform: translateX(4px); }

/* ═════════ HOME HERO / TITLE BLOCK ═════════ */
/* Top pad is the old clamp less 0.25in, floored so it can't collapse on short
   viewports. Bottom pad is deliberately much tighter than the top: the ribbon
   is the next thing down and the gap to it was reading as a hole. */
.hero { padding: max(0.5rem, calc(clamp(2.2rem, 7vh, 5rem) - 24px)) var(--pad) clamp(0.4rem, 0.93vh, 0.73rem); }
.hero-word {
  font-family: var(--disp); font-weight: 420; text-transform: uppercase;
  /* 10% up on every stop of the old clamp(50px, 14.8vw, 270px). */
  font-size: clamp(55px, 16.28vw, 297px); line-height: 1.02; letter-spacing: -0.02em;
  /* No top margin: the coords strip that used to sit above it is gone, and its
     cushion went with it. */
  margin: 0; white-space: nowrap;
  font-variation-settings: 'opsz' 96;
}
.hero-word .kl { display: inline-block; font-weight: inherit; transition: font-weight 0.12s linear; }
/* Until index.html has drawn the real mark, the h1 is only the word HOLOCENE
   set in Bricolage, and on a reload that flashed up before the scripts at the
   foot of the page ran. Opacity, not visibility, so the heading stays in the
   accessibility tree meanwhile. If the script never gets there, the word shows
   after 3s anyway rather than leaving a hole; with no script at all the
   <noscript> rule in index.html shows it at once. */
.hero-word:not(.is-drawn) { opacity: 0; animation: hero-word-fallback 0s 3s forwards; }
@keyframes hero-word-fallback { to { opacity: 1; } }

/* Smoke mode — the hero swapped for the real wordmark, and the wordmark drawn
   as nothing but smoke. The canvas fills the same box the type version
   occupied; its intrinsic size carries the aspect ratio, so `height: auto` is
   the whole layout. The colour here is what the canvas reads back for its ink,
   which is how the skins reach the smoke.

   There is no outline copy in this mode, and no CSS mask either. The cursor's
   cloud is still here — it is what the smoke lives inside — but it is drawn into
   the canvas's own alpha instead, because a mask cuts a HOLE in the mark and
   what this needs inside the disc is a change of substance, not an absence. The
   disc's geometry stays in CSS (--xr / --xf below) and the canvas reads it. */
.hero-word.is-smoke { line-height: 0; }
.hero-word .hw-smoke { display: block; width: 100%; height: auto; }
/* The solid-ink layer over the smoke, faded in when the window loses focus
   (index.html draws it on blur). A transition, so it runs on the compositor
   even when the page is no longer getting animation frames. */
.hero-word.is-smoke .hw-solid { position: relative; }
.hero-word .hw-ink {
  position: absolute; left: 0; top: 0; height: auto; pointer-events: none;
  opacity: 0; transition: opacity 0.6s ease;
}
.hero-word.is-away .hw-ink { opacity: 1; }
/* The type version's equivalent: a third copy of the letters in solid ink,
   over the masked pair, and the outline copy fades out under it. */
.hero-word .hw-ink-type { display: block; white-space: nowrap; }
.hero-word .hw-xray { transition: opacity 0.6s ease; }
.hero-word.is-away .hw-xray { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .hero-word .hw-ink, .hero-word .hw-xray { transition: none; } }
.hero-word.is-smoke .hw-solid {
  color: var(--ink);
  -webkit-mask-image: none; mask-image: none;
}
.hero-word.is-smoke .hw-xray { display: none; }

/* Cursor x-ray. Two stacked copies of the wordmark share one radial mask: the
   solid copy is cut away inside the disc, the accent outline is revealed there.
   --xm/--xy (the disc centre, in the h1's own coordinates) are written per
   frame from index.html; everything else is tunable here.
     --xr  disc radius, matched to the swell reach it replaces
     --xf  where the falloff starts — inside this the swap is complete, and
           from here to the rim the two copies cross-fade. */
.hero-word { position: relative; --xr: var(--prox); --xf: 32%; --xm: -9999px; --xy: -9999px; }
.hw-solid, .hw-xray { display: block; white-space: nowrap; }
.hw-xray {
  position: absolute; left: 0; top: 0; pointer-events: none;
  color: transparent; -webkit-text-stroke: clamp(1.2px, 0.18vw, 3px) var(--acc);
}
@media (hover: hover) {
  .hw-solid {
    -webkit-mask-image: radial-gradient(circle var(--xr) at var(--xm) var(--xy), transparent 0, transparent var(--xf), #000 100%);
    mask-image: radial-gradient(circle var(--xr) at var(--xm) var(--xy), transparent 0, transparent var(--xf), #000 100%);
  }
  .hw-xray {
    -webkit-mask-image: radial-gradient(circle var(--xr) at var(--xm) var(--xy), #000 0, #000 var(--xf), transparent 100%);
    mask-image: radial-gradient(circle var(--xr) at var(--xm) var(--xy), #000 0, #000 var(--xf), transparent 100%);
  }
}
/* No pointer means no mask to hide it behind — drop the outline copy entirely. */
@media (hover: none) { .hw-xray { display: none; } }
/* The description, stood on its end down the right of the mark. Not a
   transform: `vertical-rl` is a real writing mode, so the paragraph keeps an
   ordinary box that ordinary positioning can place — and it gives exactly the
   orientation asked for, first line at the RIGHT with the glyph tops facing the
   page edge, subsequent lines stacking leftward.

   Right edge on the x the rule below terminates at. The foot is set from
   index.html, not from here: --side-bottom carries the distance from the mark's
   box to the lowest INK in it, which is not the box's own bottom — the type
   version's box runs on below the baseline of its caps, and the smoke version's
   canvas has padding under the comma. CSS cannot see either. `text-align: end`
   is the paragraph's "right" in a vertical writing mode, so every line finishes
   flush on that edge rather than only the longest one.

   It keeps the size and the accent underline the copy had when it was
   horizontal, so the lines are short: the column's height is the mark's height,
   and 22px runs out of it fast. */
.hero-mark { position: relative; }
.hero-side {
  position: absolute; right: 0; bottom: var(--side-bottom, 0); margin: 0;
  writing-mode: vertical-rl; text-align: end;
  font-family: var(--disp); font-weight: 500;
  font-size: clamp(15px, 1.9vw, 22px); line-height: 1.45; color: var(--ink);
}
.hero-side u { text-decoration-color: var(--acc); text-decoration-thickness: 2px; text-underline-offset: 4px; }
/* No column to stand it in once the mark fills the width. */
@media (max-width: 860px) { .hero-side { display: none; } }

.hero-annot {
  border-top: 1px solid var(--line); margin-top: 0.5rem; padding-top: 0.9rem;
}
/* The 48px drop this used to carry went with the fine print it was dodging —
   it was a translate rather than a margin so it wouldn't fight the old two-up
   row's end-alignment, and with nothing beside it, it was only holding the
   ribbon away. The indent stays: that reads as deliberate. */
.hero-annot .desc { font-family: var(--disp); font-weight: 500; font-size: clamp(15px, 1.9vw, 22px); line-height: 1.45; max-width: 44ch;
  /* Down 0.3in off the rule, as a translate: the drop must not be real. A
     margin here grows the hero and carries the ribbon down with it, and the
     ribbon is meant to stay where it is — so this moves through the cushion
     under the text rather than adding to it. */
  margin-left: 80px; translate: 0 0.3in;
  /* Lifted over .ribbon-wrap, which is z-index 1 and next in flow. The drop
     above is a translate, so the address moves into the ribbon's box without
     moving the box — and the wrap, being the tall axis-aligned catcher the
     ribbon's hover once needed, was swallowing every click on the Contact link.
     It has to go HERE rather than on the <a>: `translate` makes this paragraph
     a stacking context, so a z-index on the link inside it never escapes.
     The strip this shadows is the wrap's empty top, well clear of the band. */
  position: relative; z-index: 2; }
.hero-annot .desc u { text-decoration-color: var(--acc); text-decoration-thickness: 2px; text-underline-offset: 4px; }
/* The address reads as the line it replaced, not as a link, until you point at
   it. Safe here only because it is a standalone line rather than a link buried
   in a paragraph — colour is the only thing distinguishing it. */
/* Caps by transform, not in the markup: the address stays sentence case in the
   source, so a screen reader says the street name rather than spelling it. */
.hero-annot .desc a { color: inherit; text-decoration: none; text-transform: uppercase; transition: color 0.15s; }
.hero-annot .desc a:hover { color: var(--acc); }
/* Single column has no room for the desktop indent, and none for the drop
   either: the ribbon rides up as the viewport narrows, and 0.3in of translate
   puts the second line straight through the black band. Reset both. */
@media (max-width: 860px) { .hero-annot .desc { margin-left: 0; translate: none; } }
/* Desktop sets the address 20% larger (25 Oct 2026) without moving anything:
   the line-height comes down by the same 1.2, so the line box, and the hero
   and ribbon under it, keep their height and the text stays centred where it was. */
@media (min-width: 861px) { .hero-annot .desc { font-size: clamp(18px, 2.28vw, 26.4px); line-height: calc(1.45 / 1.2); } }

/* ═════════ PAGE HEAD (inner pages) ═════════ */
.page-head { padding: clamp(2rem, 6vh, 4rem) var(--pad) clamp(1.4rem, 3.5vh, 2.4rem); }
.ph-coords { display: flex; justify-content: space-between; gap: 0.6rem 1.6rem; flex-wrap: wrap;
  font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute);
  padding-bottom: 0.9rem; border-bottom: 1px solid var(--line); }
.ph-coords b { color: var(--acc); font-weight: 400; }
.ph-title {
  font-family: var(--disp); font-weight: 800; text-transform: uppercase;
  font-size: clamp(42px, 9.5vw, 150px); line-height: 1; letter-spacing: -0.015em;
  margin: clamp(0.6rem, 2vh, 1.2rem) 0 0;
}
.ph-title i { font-style: normal; color: var(--acc); }
.ph-sub { display: flex; justify-content: space-between; align-items: baseline; gap: 0.8rem 2rem; flex-wrap: wrap;
  border-top: 1px solid var(--line); margin-top: 0.6rem; padding-top: 0.9rem; }
.ph-sub p { font-family: var(--disp); font-weight: 500; font-size: clamp(14px, 1.7vw, 19px); line-height: 1.5; max-width: 52ch; }
.ph-sub .idx { font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); text-align: right; line-height: 2; }

/* section caption */
.cap { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  padding: clamp(1.6rem, 4vh, 3rem) var(--pad) 1.2rem; }
.cap h2 { font-family: var(--disp); font-weight: 800; font-size: clamp(24px, 3.6vw, 46px); text-transform: uppercase; letter-spacing: -0.01em; line-height: 1; }
.cap h2 i { font-style: normal; color: var(--acc); }
.cap .src { font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); }

/* ═════════ DIAGONAL RIBBON ═════════ */
/* The wrap is only the axis-aligned box the slanted band sits in — the ribbon
   itself takes the pointer, so the pause follows the slant and the corners
   either side of it neither hover nor click. */
/* overflow-x: the tilted ribbon is wider than the screen by design; clipped
   here (the wrap is the screen's width) so it is not page width iOS Safari
   can pan into. Only x: its tilt still rises and falls past the wrap. */
.ribbon-wrap { position: relative; height: clamp(70px, 11vw, 130px); overflow-x: clip; }
/* Chrome before 90 has no `clip`, drops the line, and the ribbon's 4vw either
   side becomes page width. On Android that widens the layout viewport, and the
   fixed modal sizes to it — its edges (and the logo) run off the screen. Plain
   hidden is the fallback: it clips y as well, but only the ribbon's clear pad
   reaches past the wrap at that tilt, never the ink. */
@supports not (overflow-x: clip) { .ribbon-wrap { overflow: hidden; } }
/* Hovering stops the belt so you can read it, and the thing being hovered is
   the band you see, give or take --rib-pad.

   The ribbon is rotated -2.2deg and its hit box rotates with it, so tracking
   the text sideways at a level height slides you off the band partway along:
   the belt resumes, and re-pauses when you drift back in. The wrap was the
   trigger for a while to avoid exactly that, but its hit box is a broad level
   strip that stops the belt from well above and below the band, which reads
   as the page pausing for no reason. So the band is the trigger, and the
   slide-off is met two ways instead: a --rib-pad of clear space either side
   of the band belongs to the hit box, and dither.js holds the pause for a
   moment after the pointer leaves, so a brush across the edge is not a
   restart.

   The pad is a transparent border. Borders are part of the hit box;
   background-clip keeps the ink inside the padding box; overflow clips the
   belt at the padding edge, so the pad stays clear. The 1px rules that used
   to be the borders are inset shadows now, and the padding grew by that 1px
   so the band is the same height it was.

   :focus-within pauses it too, so tabbing to the link holds it still.

   Scoped to :not(.rib-js) because animation-play-state pauses the belt at the
   MAIN THREAD's idea of where it is, and the belt is a composited transform —
   the compositor has carried it further. dither.js stamps .rib-js and pauses
   through the Web Animations API with the time read off the wall clock, which
   is the same pause without the rewind. This rule is the no-JS fallback. */
.ribbon {
  --rib-pad: 10px;
  position: absolute; left: -4vw; right: -4vw; top: 50%;
  /* One transform, not the `translate`/`rotate` properties: Chrome before 104
     ignores those, which left the band flat and half its height too low. */
  transform: translateY(-50%) rotate(-2.2deg);
  background: var(--ink); background-clip: padding-box; color: var(--paper);
  overflow: hidden; padding: 12px 0; pointer-events: auto;
  border: solid transparent; border-width: var(--rib-pad) 0;
  box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line);
}
html:not(.rib-js) .ribbon:hover .ribbon-track,
html:not(.rib-js) .ribbon:focus-within .ribbon-track { animation-play-state: paused; }
/* The 32s here is a placeholder: the duration is rewritten from the belt's own
   width by dither.js, because the keyframe travels -50% and a fixed duration
   therefore makes SPEED a function of how much text the belt is carrying. No
   will-change — it was tried to hold the layer across the pause and made the
   belt visibly jerkier, which is what a 6780px-wide layer under a rotated,
   overflow-hidden parent will do. */
.ribbon-track { display: flex; width: max-content; animation: ribX 32s linear infinite; }
.ribbon-track span { font-size: 11.5px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; white-space: nowrap; padding: 0 1.5rem; }
/* The ribbon bar is filled with --ink, so its accent has to be measured against
   --ink, not --paper. Plain --acc was 2.53:1 here, and the old negative-skin
   override was written for a dark bar it does not have (1.74:1). */
.ribbon-track span.hot { color: var(--acc-on-ink); }
/* The "just announced" label, repeated along the belt. Same accent-on-ink as
   the separators — plain --acc is only 2.53:1 against the ink bar. */
.ribbon-track span.lead { color: var(--acc-on-ink); }
@keyframes ribX { to { transform: translateX(-50%); } }

/* ═════════ WHAT'S NEXT (home) ═════════ */
/* The heading sits over the right-hand plate rather than the left: it ends on
   the same edge the grid does, so it reads as the label on that column and
   fills the offset the second card is already carrying. Padding is a third of
   an inch tighter than it was — 24px off the top, 8 off the bottom — which
   closes the hole that opened between the ribbon and the first plates. */
/* Oct 2026: the gap under the ribbon opened by about 30% — measured at ~20px
   on a phone and ~60px on a laptop, so the extra is 6–18px on the same curve. */
.idx-cap { display: flex; justify-content: flex-end; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  padding: calc(clamp(0.5rem, 1.3vh, 1.2rem) + clamp(6px, 1.4vw, 18px)) var(--pad) 0.9rem; }
/* Off the right edge by 1.2in, and 0.125in up. The horizontal step is a margin
   because nothing else shares the row, so it may as well be real; the vertical
   one is a translate on purpose — it lifts the heading toward the ribbon
   WITHOUT taking the plates below up with it, which is what shortening the
   caption's padding would have done. */
.idx-cap h2 { text-align: right; margin-right: 1.2in; translate: 0 -0.125in; }
/* One column below this, and nothing to sit over — put it back on the left. */
@media (max-width: 860px) {
  .idx-cap { justify-content: flex-start; }
  .idx-cap h2 { text-align: left; margin-right: 0; translate: none; }
}
.idx-cap h2 { font-family: var(--disp); font-weight: 800; font-size: clamp(26px, 4vw, 52px); text-transform: uppercase; letter-spacing: -0.01em; line-height: 1; }
/* Second highlight lives here and on the availability stamp — enough to read as
   a two-colour press on the plates that set --acc2, invisible on the three that
   inherit it from --acc. */
.idx-cap h2 i { font-style: normal; color: var(--acc2); }
.idx-cap .src { font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); }

/* denser two-up scatter: strict halves, small offsets, kept tilt */
.specs { display: grid; grid-template-columns: repeat(12, 1fr); gap: clamp(1.4rem, 3.5vh, 2.6rem) clamp(0.9rem, 1.6vw, 1.6rem); padding: 0 var(--pad); }
.spec { position: relative; cursor: pointer; }
/* Trailing cards that would leave a part-filled row — see trimPartialRow(). */
.spec--overflow { display: none; }
.spec:nth-child(odd)  { grid-column: 1 / span 6; rotate: -0.9deg; }
.spec:nth-child(even) { grid-column: 7 / span 6; rotate: 0.7deg; margin-top: clamp(0.8rem, 3vh, 2rem); }
.spec:nth-child(4n+3) { rotate: 0.8deg; }
.spec:nth-child(4n)   { rotate: -0.6deg; }
.spec:first-child { grid-column: 1 / span 7; rotate: -0.7deg; }
.spec:nth-child(2) { grid-column: 8 / span 5; }
.spec { transition: rotate 0.4s var(--ease), translate 0.4s var(--ease); }
@media (hover: hover) { .spec:hover { rotate: 0deg; translate: 0 -6px; } }
.spec.hot { rotate: 0deg; translate: 0 -6px; }
@media (max-width: 860px) {
  .specs { grid-template-columns: 1fr; gap: 1.6rem; }
  .spec, .spec:nth-child(n) { grid-column: 1 / -1; margin-top: 0; }
}

/* 13" and wider: two plates up top, threes underneath. Items 3+ each span four
   of the twelve columns and auto-place, so the triple rows need no explicit
   column starts — only the per-position tilt/offset that keeps the scatter. */
@media (min-width: 1200px) {
  .spec:nth-child(n+3) { grid-column: span 4; margin-top: 0; }
  .spec:nth-child(n+3):nth-child(3n)   { rotate: -0.7deg; }
  .spec:nth-child(n+3):nth-child(3n+1) { rotate: 0.6deg; margin-top: clamp(0.6rem, 2vh, 1.4rem); }
  .spec:nth-child(n+3):nth-child(3n+2) { rotate: -0.4deg; margin-top: clamp(0.3rem, 1vh, 0.7rem); }
  .spec:nth-child(n+3) .spec-title { font-size: clamp(18px, 1.7vw, 24px); }
  .spec:nth-child(n+3) .spec-num { font-size: clamp(24px, 2.4vw, 38px); }
  /* Straighten-on-hover, restated for the three-up rows. The tilt selectors
     above are (0,3,0) and outrank the generic `.spec:hover` at (0,2,0), so
     without this the cards from row two down stayed crooked under the cursor.
     Matched specificity, declared after — same lift, same easing. */
  @media (hover: hover) {
    .spec:nth-child(n+3):hover { rotate: 0deg; translate: 0 -6px; }
  }
  .spec:nth-child(n+3).hot,
  .spec:nth-child(n+3):focus-within { rotate: 0deg; translate: 0 -6px; }
}

/* Wide monitors: three plates up top, fours underneath. The switch is where a
   four-wide row fits at the SAME plate width the three-wide rows had at 1200px
   (~358px): 4 × 358 + three gutters + both pads = ~1626px. The first row keeps
   its lead — spans of four, so each hero plate is a third of the width, a step
   up from the quarter-width plates below. Titles are re-scaled by vw so the
   type sits in a same-sized plate at the same density it had at 1200px, rather
   than jumping to its cap. trimPartialRow() in index.html mirrors the counts. */
@media (min-width: 1630px) {
  .spec:first-child   { grid-column: 1 / span 4; }
  .spec:nth-child(2)  { grid-column: 5 / span 4; }
  .spec:nth-child(3)  { grid-column: 9 / span 4; rotate: -0.5deg; margin-top: clamp(0.4rem, 1.5vh, 1rem); }
  /* .specs prefix: the base first-child title/num rules sit later in the file
     at equal specificity and would otherwise win. */
  .specs .spec:nth-child(-n+3) .spec-title { font-size: clamp(24px, 2vw, 40px); }
  .specs .spec:nth-child(3) .spec-num   { font-size: clamp(26px, 2.2vw, 40px); }
  .specs .spec:first-child .spec-num    { font-size: clamp(34px, 3.3vw, 60px); }
  .spec:nth-child(n+4) { grid-column: span 3; margin-top: 0; }
  .spec:nth-child(n+4):nth-child(4n)   { rotate: -0.7deg; }
  .spec:nth-child(n+4):nth-child(4n+1) { rotate: 0.6deg; margin-top: clamp(0.6rem, 2vh, 1.4rem); }
  .spec:nth-child(n+4):nth-child(4n+2) { rotate: -0.4deg; margin-top: clamp(0.3rem, 1vh, 0.7rem); }
  .spec:nth-child(n+4):nth-child(4n+3) { rotate: 0.5deg; }
  .spec:nth-child(n+4) .spec-title { font-size: clamp(18px, 1.25vw, 24px); }
  .spec:nth-child(n+4) .spec-num { font-size: clamp(24px, 1.75vw, 38px); }
  /* Same straighten-on-hover restatement as the 1200px block, same reason. */
  @media (hover: hover) {
    .spec:nth-child(n+4):hover { rotate: 0deg; translate: 0 -6px; }
  }
  .spec:nth-child(n+4).hot,
  .spec:nth-child(n+4):focus-within { rotate: 0deg; translate: 0 -6px; }
}

/* Wider still: four up top, fives underneath, by the same rule — five plates
   at the ~360px the fours had at 1630px, plus gutters and pads, is ~2015px.
   Twelve columns do not split into fives, so the grid goes to twenty here:
   the lead row spans five each, the rows below span four. */
@media (min-width: 2016px) {
  .specs { grid-template-columns: repeat(20, 1fr); }
  .spec:first-child   { grid-column: 1 / span 5; }
  .spec:nth-child(2)  { grid-column: 6 / span 5; }
  .spec:nth-child(3)  { grid-column: 11 / span 5; }
  /* .specs prefix: the 1630px row-of-four tilt rules match item 4 at (0,3,0). */
  .specs .spec:nth-child(4) { grid-column: 16 / span 5; rotate: 0.6deg; margin-top: clamp(0.6rem, 2vh, 1.4rem); }
  .specs .spec:nth-child(-n+4) .spec-title { font-size: clamp(24px, 1.5vw, 40px); }
  .specs .spec:nth-child(3) .spec-num,
  .specs .spec:nth-child(4) .spec-num { font-size: clamp(26px, 1.7vw, 40px); }
  .specs .spec:first-child .spec-num  { font-size: clamp(34px, 2.45vw, 60px); }
  .spec:nth-child(n+5) { grid-column: span 4; }
  .spec:nth-child(n+5):nth-child(5n)   { rotate: -0.7deg; margin-top: 0; }
  .spec:nth-child(n+5):nth-child(5n+1) { rotate: 0.6deg; margin-top: clamp(0.6rem, 2vh, 1.4rem); }
  .spec:nth-child(n+5):nth-child(5n+2) { rotate: -0.4deg; margin-top: clamp(0.3rem, 1vh, 0.7rem); }
  .spec:nth-child(n+5):nth-child(5n+3) { rotate: 0.5deg; margin-top: 0; }
  .spec:nth-child(n+5):nth-child(5n+4) { rotate: -0.6deg; margin-top: clamp(0.4rem, 1.5vh, 1rem); }
  .spec:nth-child(n+5) .spec-title { font-size: clamp(18px, 1vw, 24px); }
  .spec:nth-child(n+5) .spec-num { font-size: clamp(24px, 1.43vw, 38px); }
  @media (hover: hover) {
    .spec:nth-child(n+5):hover { rotate: 0deg; translate: 0 -6px; }
  }
  .spec:nth-child(n+5).hot,
  .spec:nth-child(n+5):focus-within { rotate: 0deg; translate: 0 -6px; }
}

.spec-num {
  position: absolute; top: -0.55em; right: -0.1em; z-index: 3;
  font-family: var(--disp); font-weight: 800; font-size: clamp(26px, 3vw, 46px); line-height: 1;
  letter-spacing: 0.01em;
  /* Paper fill, not transparent: the label hangs over the card image, and a
     see-through counter let the photo read straight through the letterforms. */
  color: var(--paper); -webkit-text-stroke: 1px var(--ink);
  pointer-events: none; user-select: none;
}
.spec:first-child .spec-num { font-size: clamp(34px, 4.4vw, 66px); color: var(--acc); -webkit-text-stroke: 0; }

/* ── EARLY / LATE ──
   Two shows on one date (core/night.js decides which nights qualify). Without
   a marker the first plate reads as the whole evening, and someone books the
   6 PM set not knowing the room turns over at 9.

   On the plate it hangs off the weekday's left shoulder. .spec-num is itself
   absolutely positioned, so it is this marker's containing block: right:100%
   parks the marker flush against the weekday's left edge whatever the day is
   called, and no offset has to be guessed per weekday. Sized in em, so it
   stays the same fraction of the badge from the hero plate down to the
   five-up rows. The top alignment puts it in the clear band ABOVE the card
   rather than over the poster, which is the only place small type is reliably
   legible on a plate. */
.spec-when {
  /* Lifted 3px clear of the card's top edge, which puts it slightly ABOVE the
     weekday's cap rather than flush with it. A fixed px and not an em: the gap
     being asked for is a constant distance from the frame, so it must not grow
     with the hero plate's much larger badge. */
  position: absolute; right: 100%; top: -3px; margin-right: 0.3em;
  font-size: 0.45em; font-style: normal; font-weight: 800; letter-spacing: 0.06em;
  text-transform: uppercase; white-space: nowrap;
  /* Without this the inline box is font-size x 1.2 and its descender dips over
     the card's top edge — the one place small type stops being legible. */
  line-height: 1;
  /* The badge is paper-filled with an ink outline; the marker is neither.
     Warm for EARLY; the --late class (set from the slot name) turns it cool. */
  color: var(--mark-early); -webkit-text-stroke: 0;
  text-decoration: underline; text-decoration-thickness: 0.08em; text-underline-offset: 0.18em;
}

.spec-frame { border: 1px solid var(--line); background: var(--paper); }
/* Full frame, always. .spec-img is the photo's own box and .spec-color covers
   it, so nothing is trimmed — the ink mask, the hover swap, the stamps and the
   dither fallback all work against that box unchanged. --ev-ar is written per
   card by dither.js from the image's natural size; the fallback ratios are what
   a photo that never reports a size falls back to. */
.spec-img { position: relative; aspect-ratio: var(--ev-ar, 16/9); overflow: hidden; border-bottom: 1px solid var(--line); background: var(--paper); }
.spec:first-child .spec-img { aspect-ratio: var(--ev-ar, 16/7); }
.spec-color { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 0.35s; }
.spec-inkimg { position: absolute; inset: 0; background: var(--ink);
  -webkit-mask-size: cover; mask-size: cover; -webkit-mask-position: center; mask-position: center;
  opacity: 0; transition: opacity 0.3s; }
.spec-inkimg.on { opacity: 1; }
/* `.ready` (set from dither.js once the photo has pixels) gates the reveal.
   Without it, hover/.hot hid the ink for a photo that had not loaded yet and
   the plate went blank inside its own frame until the file landed. */
@media (hover: hover) {
  .spec:hover .ready .spec-color, .hotable:hover .ready .spec-color { opacity: 1; }
  .spec:hover .ready .spec-inkimg, .hotable:hover .ready .spec-inkimg { opacity: 0; }
}
.spec.hot .ready .spec-color, .hotable.hot .ready .spec-color { opacity: 1; }
.spec.hot .ready .spec-inkimg, .hotable.hot .ready .spec-inkimg { opacity: 0; }
/* fallback when canvas is CORS-tainted: grayscale + ink duotone + dot screen */
.spec-img.dither-fallback .spec-color { opacity: 1; filter: grayscale(1) contrast(1.25); transition: filter 0.35s; }
.spec-img.dither-fallback::before { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: var(--ink); mix-blend-mode: lighten; transition: opacity 0.35s; }
[data-skin="negative"] .spec-img.dither-fallback::before,
[data-skin="fluoro"] .spec-img.dither-fallback::before,
[data-skin="blueprint"] .spec-img.dither-fallback::before,
[data-skin="oxblood"] .spec-img.dither-fallback::before,
[data-skin="forest"] .spec-img.dither-fallback::before,
[data-skin="velvet"] .spec-img.dither-fallback::before { mix-blend-mode: darken; }
.spec-img.dither-fallback::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(color-mix(in srgb, var(--paper) 80%, transparent) 1px, transparent 1.4px);
  background-size: 4px 4px; mix-blend-mode: lighten; transition: opacity 0.35s; }
[data-skin="negative"] .spec-img.dither-fallback::after,
[data-skin="fluoro"] .spec-img.dither-fallback::after,
[data-skin="blueprint"] .spec-img.dither-fallback::after,
[data-skin="oxblood"] .spec-img.dither-fallback::after,
[data-skin="forest"] .spec-img.dither-fallback::after,
[data-skin="velvet"] .spec-img.dither-fallback::after { mix-blend-mode: darken; }
@media (hover: hover) {
  .spec:hover .spec-img.dither-fallback .spec-color, .hotable:hover .spec-img.dither-fallback .spec-color { filter: none; }
  .spec:hover .spec-img.dither-fallback::before, .spec:hover .spec-img.dither-fallback::after,
  .hotable:hover .spec-img.dither-fallback::before, .hotable:hover .spec-img.dither-fallback::after { opacity: 0; }
}
.spec.hot .spec-img.dither-fallback .spec-color, .hotable.hot .spec-img.dither-fallback .spec-color { filter: none; }
.spec.hot .spec-img.dither-fallback::before, .spec.hot .spec-img.dither-fallback::after,
.hotable.hot .spec-img.dither-fallback::before, .hotable.hot .spec-img.dither-fallback::after { opacity: 0; }
/* natural-flow variant (masonry walls): color img keeps layout height */
.spec-img--flow { aspect-ratio: auto; border-bottom: none; }
.spec-img--flow .spec-color { position: static; height: auto; }
/* Cards in a row differ in height, which is the point of showing whole
   posters — `align-items: start` lets each be its own length instead of
   stretching to the tallest. */
.specs { align-items: start; position: relative; }

/* ── gap constellations ──
   Ragged rows leave real holes under the shorter plates. These fill them in
   the language the page already speaks — the field's mono marks, the plot's
   ring — but louder: accent-coloured rather than ink, and up around 0.8 alpha
   where the field tops out at 0.45. Written into .specs by dither.js, which is
   the only thing that can know where a hole is or how big.

   Painted BEFORE the plates (they are the grid's first children), so a
   cluster that overruns its hole passes behind the card rather than over it. */
/* The clusters are appended after the plates, so the plates need lifting
   rather than the clusters sinking — see the note in dither.js. */
.spec { z-index: 1; }
.sparks { position: absolute; pointer-events: none; z-index: 0; }
.sparks i {
  position: absolute; font-family: var(--mono); font-weight: 700; font-style: normal; line-height: 1;
  translate: -50% -50%; will-change: opacity, scale, rotate;
  color: var(--acc); opacity: 0;
  animation: sparkTwinkle var(--sp-dur, 3.4s) var(--ease) infinite;
  animation-delay: var(--sp-delay, 0s);
}
.sparks i.alt { color: var(--acc2); }
.sparks .sp-ring {
  position: absolute; translate: -50% -50%; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--acc) 62%, transparent);
  animation: sparkRing 7s var(--ease) infinite;
}
.sparks .sp-ring::after {
  content: ''; position: absolute; inset: 26%; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--acc2) 40%, transparent);
}
/* The donut is the TARGET figure's whole point — everywhere else the ring is
   a single line, so the two do not read as the same object. */
.sparks .sp-ring.plain::after { display: none; }
/* a registration tick, the same mark the page corners carry */
.sparks .sp-reg {
  position: absolute; width: 13px; height: 13px; translate: -50% -50%;
  border-left: 1.5px solid var(--acc2); border-top: 1.5px solid var(--acc2);
  opacity: 0; animation: sparkTwinkle 5.2s var(--ease) infinite;
  animation-delay: var(--sp-delay, 0s);
}
/* The field tops out at 0.45 alpha in ink. These sit well above it, and never
   drop to nothing — a mark that vanishes reads as a rendering fault rather
   than a twinkle. */
@keyframes sparkTwinkle {
  0%, 100% { opacity: 0.22; scale: 0.66; rotate: -8deg; }
  45%      { opacity: var(--sp-max, 0.92); scale: 1.18; rotate: 6deg; }
  62%      { opacity: calc(var(--sp-max, 0.92) * 0.6); scale: 0.96; rotate: 2deg; }
}
@keyframes sparkRing {
  0%, 100% { scale: 0.82; opacity: 0.3; }
  50%      { scale: 1.08; opacity: 0.78; }
}
/* An orbit is a zero-size pivot that spins; the mark hangs off it at --sp-r.
   Put the pivot away from the middle of the hole and the mark swings around a
   point that is not its own centre, which is the whole trick. */
.sp-orbit {
  position: absolute; width: 0; height: 0;
  animation: sparkSpin var(--sp-spin, 22s) linear infinite;
}
.sp-orbit.rev { animation-name: sparkSpinRev; }
.sp-orbit > i { left: var(--sp-r, 40px); top: 0; }
@keyframes sparkSpin    { to { rotate: 360deg; } }
@keyframes sparkSpinRev { to { rotate: -360deg; } }
@media (prefers-reduced-motion: reduce) {
  /* Still there, just not blinking: hold each mark at its own mid brightness. */
  .sparks i, .sparks .sp-reg { animation: none; opacity: calc(var(--sp-max, 0.92) * 0.7); scale: 1; }
  .sparks .sp-ring { animation: none; opacity: 0.5; }
  .sp-orbit { animation: none; }
}

.spec-img--empty { background: repeating-linear-gradient(45deg, transparent 0 10px, color-mix(in srgb, var(--ink) 10%, transparent) 10px 11px); }

.spec-stamp {
  position: absolute; top: 10px; left: 10px; z-index: 2; rotate: -4deg;
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  border: 2px solid var(--acc2); color: var(--acc2); background: var(--paper);
  padding: 5px 10px;
}
.spec-stamp.soldout { border-color: var(--ink); color: var(--ink); text-decoration: line-through; }

.spec-body { padding: clamp(0.8rem, 1.4vw, 1.1rem); }
.spec-date { font-size: 10px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--acc); margin-bottom: 5px; }
.spec-date i { font-style: normal; color: var(--mute); font-weight: 400; }
.spec-title { font-family: var(--disp); font-weight: 800; font-size: clamp(18px, 2.1vw, 28px); line-height: 1.08; text-transform: uppercase; letter-spacing: -0.01em; transition: color 0.2s; }
.spec:first-child .spec-title { font-size: clamp(24px, 3.4vw, 48px); }
@media (hover: hover) { .spec:hover .spec-title { color: var(--acc); } }
.spec.hot .spec-title { color: var(--acc); }
/* The card is no longer focusable itself (its title link is) — mirror the hover
   treatment when focus lands inside so keyboard users get the same feedback. */
.spec:focus-within .spec-title { color: var(--acc); }
.spec:focus-within .ready .spec-color { opacity: 1; }
.spec:focus-within .ready .spec-inkimg { opacity: 0; }
.crow:focus-within { background: color-mix(in srgb, var(--ink) 5%, transparent); }
.crow:focus-within .crow-title { color: var(--acc); }
.spec-support { font-family: var(--disp); font-weight: 500; font-size: clamp(12.5px, 1.4vw, 15px); color: var(--mute); margin-top: 4px; }
.spec-meta { display: flex; flex-wrap: wrap; gap: 2px 14px; margin-top: 8px;
  font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--mute); }
.spec-meta b { color: var(--ink); font-weight: 700; }
.spec-buy { display: inline-flex; align-items: center; min-height: 40px; margin-top: 10px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  border: 1px solid var(--line); padding: 9px 16px;
  transition: background 0.15s, color 0.15s; }
@media (hover: hover) { .spec-buy:hover { background: var(--acc); border-color: var(--acc); color: var(--acc-ink); } }
/* Sold out is information, not chrome — an opacity wash dropped it to ~1.8:1.
   Keep it visibly inactive with line-through + muted ink, not transparency. */
.spec-buy.disabled { color: var(--mute); border-color: var(--mute); text-decoration: line-through; pointer-events: none; }
/* Online sales are over and the door is selling: a label, not a link. Dashed so
   it doesn't read as a button you can press. */
.spec-buy.at-door { border-style: dashed; pointer-events: none; cursor: default; }
/* "More info →" — touch screens only. The whole plate (and calendar row)
   opens the event pop-up, but on a phone the Tickets button was the only
   thing that LOOKED pressable, so people went straight to Etix. A pointer
   gets the lift and the accent title on hover and needs no label. A word,
   not a second button: it sits beside Tickets as plain underlined type.
   aria-hidden, because the title link already says the same thing to a
   screen reader; the card's own click handler catches a tap on it. */
.spec-more, .crow-more { display: none; }
@media (hover: none) {
  .spec-more, .crow-more {
    font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink);
    text-decoration: underline; text-decoration-color: var(--acc); text-decoration-thickness: 2px; text-underline-offset: 5px;
  }
  .spec-more { display: inline-flex; align-items: center; min-height: 40px; margin: 10px 0 0 18px; vertical-align: top; }
  .spec-meta + .spec-more { margin-left: 0; }
  .crow-more { display: inline-block; margin-top: 8px; }
  /* and a press you can feel, so the first tap teaches the rest */
  .spec:active, .crow[data-id]:active { scale: 0.985; }
}
:root[data-type] :is(.spec-more, .crow-more) { font-family: var(--text); text-transform: none; letter-spacing: 0.005em; font-weight: var(--ts-ui-w); font-size: 15px; }

.spec-skel { grid-column: span 6; aspect-ratio: 16/11; border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  background: linear-gradient(100deg, transparent 40%, color-mix(in srgb, var(--ink) 7%, transparent) 50%, transparent 60%);
  background-size: 200% 100%; animation: shimmer 1.4s infinite; }
.spec-skel:nth-child(odd) { grid-column: 1 / span 6; } .spec-skel:nth-child(even) { grid-column: 7 / span 6; }
@media (max-width: 860px) { .spec-skel, .spec-skel:nth-child(n) { grid-column: 1 / -1; } }
@keyframes shimmer { to { background-position: -200% 0; } }
.specs-empty { grid-column: 1 / -1; font-family: var(--disp); font-weight: 500; font-size: clamp(17px, 2.4vw, 26px); color: var(--mute); }
.specs-empty a { color: var(--acc); text-decoration: underline; }
.idx-foot { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 12px 16px; padding: clamp(2rem, 5vh, 3.2rem) var(--pad); }
.idx-foot a { font-size: 11px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  border: 1px solid var(--line); padding: 14px 30px; transition: background 0.15s, color 0.15s; }
.idx-foot a:hover { background: var(--ink); color: var(--paper); }
/* The month poster link (home page foot, calendar filter bar): the plate's
   own accent pair, so it reads as the one coloured control on the line and
   keeps the contrast every plate already checks for --acc-ink on --acc.
   Written as a.poster-link to match .idx-foot a and come after it. */
a.poster-link { display: inline-flex; align-items: center; gap: 9px; min-height: 40px;
  font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; text-align: left;
  background: var(--acc); color: var(--acc-ink); border: 1px solid var(--acc); padding: 9px 16px;
  transition: background 0.15s, color 0.15s, border-color 0.15s; }
a.poster-link svg { flex: none; }
a.poster-link:hover, a.poster-link:focus-visible { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.idx-foot a.poster-link { font-size: 11px; letter-spacing: 0.18em; padding: 13px 24px; }
/* Not on a phone, here or on the calendar: a large-format print is a desk job. */
@media (max-width: 700px) { .idx-foot a.poster-link { display: none; } }

/* ═════════ TECH TABLE ═════════ */
.tech { margin: 0 var(--pad); border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 65%, transparent); }
.tech-cap { padding: 0.8rem 1.2rem; border-bottom: 1px solid var(--line);
  font-size: 10px; font-weight: 700; letter-spacing: 0.22em; text-transform: uppercase; }
.tech-cap i { font-style: normal; color: var(--acc); }
.tech-row { display: grid; grid-template-columns: minmax(120px, 2fr) 5fr; border-bottom: 1px solid var(--line); }
.tech-row:last-child { border-bottom: none; }
a.tech-row { transition: background 0.15s, color 0.15s; }
a.tech-row:hover { background: var(--ink); color: var(--paper); }
a.tech-row:hover .tk, a.tech-row:hover .tv { color: inherit; }
.tk { padding: 12px 1.2rem; font-family: var(--disp); font-weight: 700; font-size: 14px; text-transform: uppercase; border-right: 1px solid var(--line); }
.tv { padding: 12px 1.2rem; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); }
@media (max-width: 560px) { .tech-row { grid-template-columns: 1fr; } .tk { border-right: none; padding-bottom: 2px; } .tv { padding-top: 0; } }

/* ═════════ STAT BAND ═════════ */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); margin: 0 var(--pad); border: 1px solid var(--line); }
.stat { padding: clamp(1.2rem, 3vh, 1.9rem) 1.2rem; border-right: 1px solid var(--line); }
.stat:last-child { border-right: none; }
.stat-num { font-family: var(--disp); font-weight: 800; font-size: clamp(28px, 4.5vw, 58px); line-height: 1; letter-spacing: -0.02em; }
.stat-num i { font-style: normal; color: var(--acc); }
.stat-num .unit { font-size: 0.45em; color: var(--mute); font-weight: 600; }
.stat-label { font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); margin-top: 6px; }
@media (max-width: 700px) { .stats { grid-template-columns: 1fr 1fr; } .stat:nth-child(2) { border-right: none; } .stat:nth-child(-n+2) { border-bottom: 1px solid var(--line); } }

/* ═════════ CARDS ═════════ */
.dgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: clamp(0.9rem, 1.6vw, 1.4rem); padding: 0 var(--pad); }
.dcard { border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 65%, transparent); padding: clamp(1.1rem, 2vw, 1.5rem); position: relative; transition: translate 0.3s var(--ease), box-shadow 0.3s var(--ease); }
/* Only a card that IS a link lifts (the contact cards). The venue and private
   events cards are plain text, and a lift on them promised a click that
   went nowhere. .hot is the touch stand-in, set by touchHot on contact. */
@media (hover: hover) { a.dcard:hover { translate: 0 -4px; box-shadow: 5px 5px 0 color-mix(in srgb, var(--ink) 85%, transparent); } }
a.dcard.hot { translate: 0 -4px; box-shadow: 5px 5px 0 color-mix(in srgb, var(--ink) 85%, transparent); }
.dcard .tag { font-size: 9px; font-weight: 700; letter-spacing: 0.24em; text-transform: uppercase; color: var(--acc); margin-bottom: 0.7rem; }
.dcard h3 { font-family: var(--disp); font-weight: 800; font-size: clamp(17px, 2vw, 24px); text-transform: uppercase; letter-spacing: -0.01em; line-height: 1.1; }
.dcard .addr { display: block; font-size: clamp(12px, 1.5vw, 15px); font-weight: 700; color: var(--acc); margin: 0.5rem 0 0.7rem; word-break: break-all; }
.dcard p { font-size: 12px; line-height: 1.8; color: color-mix(in srgb, var(--ink) 78%, var(--mute)); margin-top: 0.5rem; }
.dcard ul { list-style: none; margin-top: 0.9rem; }
.dcard ul li { font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--mute); padding: 5px 0 5px 18px; position: relative; border-top: 1px dashed color-mix(in srgb, var(--ink) 25%, transparent); }
.dcard ul li::before { content: '+'; position: absolute; left: 0; color: var(--acc); font-weight: 700; }

/* ═════════ CHIPS / FILTER BAR ═════════ */
.chip { display: inline-flex; align-items: center; min-height: 40px; font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  border: 1px solid var(--line); color: var(--mute); padding: 8px 14px; margin: 0 -1px -1px 0;
  transition: background 0.15s, color 0.15s; cursor: pointer; }
@media (hover: hover) { .chip:hover { color: var(--ink); } }
.chip.active { background: var(--ink); color: var(--paper); }
/* Either "All" lights up when it is NOT picked: a month, "Last week" or one
   kind of night is narrowing the list, and this is the way back to everything.
   An accent line inside its frame, like the player's, not a fill — a fill
   read as a second selection. */
.chip[data-month="all"]:not(.active), .chip[data-kind="all"]:not(.active) { color: var(--acc); box-shadow: inset 0 0 0 2px var(--paper), inset 0 0 0 3.5px var(--acc); }
/* Two rows inside one column (.filter-main), with the view toggle at the far
   right of the first. Top row: search, then All / Live Shows / DJ Nights. Second
   row: the months, then "Last week". The month row sets the column's width
   and the top row is barred from setting it (width 0, min-width 100%), so the
   search field takes whatever the months leave over and [DJ Nights] always
   ends where "Last week" does. A short season with few months would leave
   too little search, so the column has a floor — and "Last week" rides the
   right edge (margin-left: auto) so the two still line up when it applies. */
.filter-bar { display: flex; align-items: flex-start; gap: 0.8rem 1.4rem; flex-wrap: wrap;
  padding: 0 var(--pad) 1.2rem; }
.filter-main { display: flex; flex-direction: column; gap: 0.8rem; flex: 0 1 auto; min-width: min(100%, 460px); }
.filter-top { display: flex; align-items: stretch; gap: 0.8rem; width: 0; min-width: 100%; }
/* The search field is the one thing here that can give. */
.search-wrap { display: flex; align-items: center; gap: 8px; flex: 1 1 140px; min-width: 0;
  border: 1px solid var(--line); padding: 0 12px; background: color-mix(in srgb, var(--paper) 65%, transparent); }
.search-wrap svg { color: var(--mute); flex-shrink: 0; }
.search-wrap input { background: none; border: none; outline: none; color: var(--ink); font-family: var(--mono); font-size: 11.5px; padding: 10px 2px; width: 100%; min-width: 0; }
.search-wrap input::placeholder { color: var(--mute); }
.month-chips, .view-toggle, .kind-toggle { display: flex; flex-wrap: wrap; }
.kind-toggle { flex: 0 0 auto; }
.kind-toggle .chip { white-space: nowrap; }
/* The view toggle, and under it the month poster link, right-aligned. */
.view-side { flex: 0 1 auto; margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: 0.8rem; }
.view-toggle { flex: 0 0 auto; }
.month-row { display: flex; align-items: center; }
.month-chips { flex: 0 0 auto; align-items: center; }
/* "Last week" sits after the months but is not one of them: a pill, dashed,
   smaller and set off by a gap (.chip-break, at least 12px), so the eye files
   it as a different kind of button rather than the month before the first.
   Still 32px tall for a thumb. */
.chip--past { min-height: 32px; padding: 5px 12px; margin: 0 0 -1px auto; font-size: 9px; letter-spacing: 0.1em;
  border-style: dashed; border-radius: 999px; white-space: nowrap; }
.chip--past.active { border-style: solid; }
.chip-break { display: block; flex: 0 0 12px; height: 0; }
/* Narrow: the column comes apart and its pieces join the bar directly
   (display: contents), so they can be re-ordered across the old rows —
   search and the view toggle first, as before, then the months, then "Last
   week" with DJ Nights / Live Shows pushed to the far end of its line. That
   keeps the phone at three rows of chrome, not four; if the two do not fit
   beside "Last week" they wrap under it, still on the right. The ::before is
   the line break after the view toggle. */
@media (max-width: 700px) {
  .filter-bar { align-items: center; row-gap: 0; column-gap: 0.8rem; padding-bottom: 0.5rem; }
  .filter-bar::before { content: ''; order: -1; flex: 0 0 100%; height: 1.1rem; }
  .filter-main, .filter-top, .month-row { display: contents; }
  .search-wrap { order: -3; min-height: 40px; }     /* as tall as [List] [Large] beside it */
  .view-side { display: contents; }
  .view-toggle { order: -2; margin-left: auto; }
  /* No poster download on a phone: a large-format print is a desk job. */
  .filter-bar .poster-link { display: none; }
  .month-chips { flex: 0 0 100%; }
  .chip-break { display: none; }
  .chip--past { order: 1; margin: 15px 0 0; }
  .kind-toggle { order: 2; margin: 15px 0 0 auto; }
  /* Three chips beside "Last week" down to a 360px phone: less padding and
     tracking than the other chips, or the row wraps (and costs ~47px). */
  .kind-toggle .chip { padding-left: 6px; padding-right: 6px; letter-spacing: 0.05em; }
}
.ev-row-badge.past, .spec-stamp.past { background: none; border: 1px dashed var(--mute); color: var(--mute); }
.btn.is-disabled.is-past { text-decoration: none; }
.arc-link--week { padding-top: 1.6rem; }
.cal-status { display: flex; align-items: center; gap: 10px; padding: 0 var(--pad) 1rem;
  font-size: 9.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--mute); }
.cal-status i { width: 8px; height: 8px; border-radius: 50%; background: var(--acc); flex-shrink: 0; animation: blip 1.6s infinite; }
.cal-status.offline i { background: var(--mute); animation: none; }
@keyframes blip { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ═════════ CAL ROWS (list view) ═════════ */
.mdiv { display: flex; align-items: baseline; gap: 1.2rem; padding: clamp(1.6rem, 4vh, 2.6rem) var(--pad) 0.8rem; }
.mdiv h2 { font-family: var(--disp); font-weight: 800; font-size: clamp(20px, 3vw, 34px); text-transform: uppercase; letter-spacing: -0.01em; white-space: nowrap; }
.mdiv h2 i { font-style: normal; color: var(--acc); }
.mdiv::after { content: ''; flex: 1; border-bottom: 1px dashed color-mix(in srgb, var(--ink) 35%, transparent); }
.mdiv span { font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--mute); white-space: nowrap; }
.crows { margin: 0 var(--pad); border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 65%, transparent); }
/* Date column is 84px, not 130: the date text only inks ~61px, so the old
   column carried 69px of dead slack which — plus the 22px gap — read as ~91px
   of emptiness before the thumbnail. 84px halves that to ~45px, and every pixel
   comes out of slack rather than out of breathing room. */
.crow { display: grid; grid-template-columns: 84px 64px 1fr auto; gap: 0.3rem 1.4rem; align-items: center;
  padding: clamp(0.9rem, 2vh, 1.2rem) 1.2rem; border-bottom: 1px solid var(--line); cursor: pointer;
  transition: background 0.15s; }
.crow:last-child { border-bottom: none; }
@media (hover: hover) { .crow:hover { background: color-mix(in srgb, var(--ink) 5%, transparent); } .crow:hover .crow-title { color: var(--acc); } }
.crow.hot { background: color-mix(in srgb, var(--ink) 5%, transparent); }
.crow.hot .crow-title { color: var(--acc); }
.crow-date { font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--acc); line-height: 1.8; }
.crow-date b { display: block; color: var(--ink); font-size: 12px; }
/* In front of the day. The column is a fixed 84px and "EARLY FRI" inks 76px,
   so it shares the line; only "EARLY TONIGHT" (109px) is too long, and that
   one wraps onto its own line rather than overflowing. */
.crow-mark { font-style: normal; margin-right: 5px; color: var(--mark-early);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
/* Poster thumbnail — same slot as the v4 list, plain colour (no dither).
   The column's width, and the poster's own height at that width: the frame is
   the poster's edge, never cropped and never padded with paper, at every
   width. It used to stretch to the row's height and `cover`, and rows run
   82–113px, so a poster was cut top and bottom in a short row and left and
   right in a tall one (Oct 2026). A tall poster may make its row a little
   taller, as a long title does; the -15px block margins eat most of the row's
   padding first. Until the image loads it holds a 4:5 space (`auto 4 / 5`),
   the usual shape, so the rows do not jump much as posters arrive. */
.crow-thumb { align-self: center; margin-block: -15px;
  border: 1px solid var(--line); background: var(--paper); overflow: hidden; }
.crow-thumb img { width: 100%; height: auto; aspect-ratio: auto 4 / 5; }
.crow-thumb--empty { aspect-ratio: 4 / 5;
  background: repeating-linear-gradient(45deg, transparent 0 6px, color-mix(in srgb, var(--ink) 12%, transparent) 6px 7px); }
.crow-main { min-width: 0; }
.crow-presenter { font-size: 8.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--mute); margin-bottom: 2px; }
.crow-title { font-family: var(--disp); font-weight: 800; font-size: clamp(16px, 2.2vw, 26px); line-height: 1.1; text-transform: uppercase; letter-spacing: -0.01em; transition: color 0.15s; }
.crow-support { font-family: var(--disp); font-weight: 500; font-size: 12.5px; color: var(--mute); margin-top: 2px; }
.crow-side { display: flex; align-items: center; gap: 1rem; }
.crow-meta { font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); text-align: right; line-height: 1.9; }
.crow-meta > * { display: block; }
.crow-meta b { color: var(--ink); }
/* The buy button closes the row, so its width sets the list's right edge.
   "Sold out" is a shorter word than "Tickets ↗", which left sold-out rows
   sitting a few pixels short of their neighbours. Floor the width and centre
   the label so the two boxes read as the same control. */
.crow-side .spec-buy { min-width: 120px; justify-content: center; }
/* Wide: the badge, then time / age / price, then the button, all on one
   centre line. The button's 10px top margin is the card's (.spec-buy, under
   a caption) and here only pushed it 5px below the words beside it. The
   badge goes first so the time and price sit against the button in every
   row, badge or not — after them, it shoved them left in the rows that had
   one and the column went ragged. */
@media (min-width: 701px) {
  .crow-side .spec-buy { margin-top: 0; }
  .crow-side .ev-row-badge { order: -1; }
}
/* A row with no ticket link keeps an invisible button in its place (wide),
   and none at all on a phone, where nothing lines up against it. */
.crow-buy-none { visibility: hidden; pointer-events: none; }
@media (max-width: 700px) { .crow-side .crow-buy-none { display: none; } }
@media (max-width: 700px) {
  /* Narrow: the poster leads. A 92px-wide poster on the left, the text
     stacked beside it — date, presenter, title, support, then price and the
     button. Rows land at ~160px, four to five to a phone screen, where the
     plates ran 620–805px each.

     The poster keeps its own shape, framed at its edge (see .crow-thumb
     above). The thumb spans all three text rows and sets a floor on the row
     height; the text rarely exceeds it. */
  .crow { grid-template-columns: 92px 1fr; grid-template-rows: auto auto auto; gap: 0 14px; align-items: start; padding: 12px; }
  .crow-thumb { grid-row: 1 / 4; margin: 0; width: 92px; align-self: start; }
  .crow-date { grid-column: 2; display: flex; gap: 6px; align-items: baseline; line-height: 1.3; margin-bottom: 3px; }
  .crow-date b { display: inline; }
  .crow-main { grid-column: 2; }
  .crow-presenter { font-size: 8px; margin: 2px 0 1px; }
  /* Two lines, then an ellipsis. The link goes inline for the clamp to see
     its line boxes; the whole row is the tap target on a phone anyway. The
     extra .crow outranks the inline-block rule at the foot of this file. */
  .crow-title { font-size: 17px; line-height: 1.15; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .crow .crow-title a { display: inline; padding: 0; }
  .crow-support { font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .crow-side { grid-column: 2; justify-content: space-between; margin-top: 8px; gap: 8px; }
  /* Price first, then time and age, on one line. The date line stays date-only:
     "TONIGHT SEP 03 · SHOW 9 PM · 21+" wrapped beside a 92px poster. */
  .crow-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; min-width: 0; text-align: left; line-height: 1.4; }
  .crow-meta > * { display: inline; }
  .crow-meta b { order: -1; font-size: 11px; }
  /* The button never breaks its label; the meta beside it wraps instead. */
  .crow-side .spec-buy { min-width: 0; margin-top: 0; padding: 7px 11px; font-size: 9.5px; white-space: nowrap; }
  /* Two columns: price and time on the left, and on the right the badge
     stacked ABOVE the button. In one row at 390px the "Low tix" badge ran
     into the Tickets button and the price wrapped over both. */
  .crow-side { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 10px; row-gap: 6px; align-items: center; }
  .crow-side .crow-meta { grid-column: 1; grid-row: 1 / span 2; }
  .crow-side .ev-row-badge { grid-column: 2; justify-self: end; }
  .crow-side .spec-buy { grid-column: 2; justify-self: end; }
}
/* Tablets, and any touch screen wider than a phone: the poster leads here
   too. The wide row's 64px thumb stretches to the row's height, which is fine
   at ~84px on a laptop — but between 701 and 1100 the titles wrap, and on
   touch "More info" adds a line, so rows ran 130–140px and the poster was
   cropped to a sliver half its width (an iPad Air, Oct 2026). Here the date
   moves over the title to give its column to the poster, which keeps its own
   shape, uncropped, and the price and button stack at the right. The 1fr
   rows above and below the text centre it on the poster without the poster
   pulling the date and title apart. */
@media (min-width: 701px) and (max-width: 1100px), (min-width: 701px) and (hover: none) {
  .crow { grid-template-columns: clamp(100px, 9vw, 112px) minmax(0, 1fr) auto;
    grid-template-rows: 1fr auto auto 1fr; gap: 0 1.4rem; padding: 12px 1.2rem 12px 12px; }
  .crow-thumb { grid-column: 1; grid-row: 1 / 5; align-self: center; margin: 0; }
  .crow-date { grid-column: 2; grid-row: 2; display: flex; gap: 8px; align-items: baseline; line-height: 1.3; margin-bottom: 4px; }
  .crow-date b { display: inline; }
  .crow-main { grid-column: 2; grid-row: 3; }
  .crow-side { grid-column: 3; grid-row: 1 / 5; flex-direction: column; align-items: flex-end; gap: 8px; }
}
.cal-empty { padding: 3rem var(--pad); font-family: var(--disp); font-weight: 500; font-size: clamp(17px, 2.4vw, 26px); color: var(--mute); }

/* flat uniform spec grid (calendar grid view, no scatter) */
.specs--flat { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(0.9rem, 1.6vw, 1.4rem); padding: 0 var(--pad); }
.specs--flat .spec { grid-column: auto !important; margin-top: 0 !important; rotate: 0deg !important; }
.specs--flat .spec-num { display: none; }
/* The Full view has no hanging weekday for the marker to sit in front of, so
   it goes beside the date chip — on the card's own paper, never on the chip.
   SPECTRA and MERIDIAN fill that chip with a colour of their own, and a mark
   laid over one of those fills is legible in neither. */
.spec-dateline { display: flex; align-items: baseline; flex-wrap: wrap;
  gap: 0 7px; margin-bottom: 5px; }
.spec-dateline .spec-date { margin-bottom: 0; }
.spec-mark { font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; white-space: nowrap; color: var(--mark-early);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
/* LATE is the cool pen, in all three places the mark appears. */
.spec-when--late, .crow-mark--late, .spec-mark--late { color: var(--mark-late); }
/* Fours once a four-wide row fits at the plate width the threes had at 1000px
   (~299px): 4 × 299 + three gutters + both pads = ~1358px. */
@media (min-width: 1358px) { .specs--flat { grid-template-columns: repeat(4, 1fr); } }
/* Fives at 1700px, where five of those ~299px plates fit. */
@media (min-width: 1700px) { .specs--flat { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 1000px) { .specs--flat { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .specs--flat { grid-template-columns: 1fr; } }

/* ═════════ FAQ ═════════ */
.faq { margin: 0 var(--pad); border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 65%, transparent); }
/* The divider is the top border of the item below, not the bottom border of
   the one above. Closing an answer shrinks its item, and Safari repaints only
   part of a box that shrinks: the bottom border went missing until something
   else (the field's next frame, another click) repainted it. The item below
   only moves, and a moved box is repainted whole. */
.faq-item + .faq-item { border-top: 1px solid var(--line); }
/* The question is a heading AND a control, so the heading wraps the button
   (<h3><button>), never the reverse — a heading nested inside a button is
   flattened into the button's accessible name and disappears from the
   screen-reader heading list. */
.faq-h { margin: 0; }
.faq-q { display: flex; justify-content: space-between; align-items: center; gap: 1rem; width: 100%; text-align: left;
  padding: 1.1rem 1.3rem; cursor: pointer;
  font-family: var(--disp); font-weight: 700; font-size: clamp(14px, 1.7vw, 18px); text-transform: uppercase; letter-spacing: 0; }
.faq-q > span { font-family: var(--mono); font-weight: 700; font-size: 16px; color: var(--acc); transition: rotate 0.25s var(--ease); flex-shrink: 0; }
.faq-item.open .faq-q span { rotate: 45deg; }
.faq-a { max-height: 0; overflow: hidden; transition: max-height 0.35s var(--ease); }
.faq-a p { padding: 0 1.3rem 1.2rem; font-size: 12px; line-height: 1.9; color: color-mix(in srgb, var(--ink) 78%, var(--mute)); max-width: 72ch; }
/* Answers may run to several paragraphs and carry a booking address. */
.faq-a p + p { padding-top: 0; }
.faq-a a { color: var(--acc); text-decoration: underline; text-underline-offset: 2px; }

/* ═════════ SPLIT / PROSE ═════════ */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.6rem, 4vw, 3.5rem); padding: 0 var(--pad); align-items: center; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }
.prose .knum { display: block; font-size: 10px; font-weight: 700; letter-spacing: 0.22em; text-transform: uppercase; color: var(--acc); margin-bottom: 1rem; }
.prose h2 { font-family: var(--disp); font-weight: 800; font-size: clamp(22px, 3.2vw, 40px); text-transform: uppercase; letter-spacing: -0.01em; line-height: 1.05; margin-bottom: 1.1rem; }
.prose h2 i { font-style: normal; color: var(--acc); }
.prose p { font-size: 12.5px; line-height: 1.95; color: color-mix(in srgb, var(--ink) 80%, var(--mute)); margin-bottom: 0.9rem; max-width: 62ch; }

/* ═════════ STEPS ═════════ */
.steps { display: flex; flex-direction: column; }
.step { display: grid; grid-template-columns: auto 1fr; gap: 1.1rem; padding: 1rem 0; border-top: 1px dashed color-mix(in srgb, var(--ink) 30%, transparent); }
.step-num { font-family: var(--disp); font-weight: 800; font-size: clamp(22px, 2.6vw, 32px); line-height: 1; color: transparent; -webkit-text-stroke: 1px var(--acc); }
.step h3 { font-family: var(--disp); font-weight: 700; font-size: 15px; text-transform: uppercase; margin-bottom: 3px; }
.step p { font-size: 11.5px; line-height: 1.8; color: var(--mute); }

/* ═════════ QUOTE ═════════ */
.quote { margin: 0 var(--pad); border-left: 3px solid var(--acc); padding: 0.4rem 0 0.4rem clamp(1.2rem, 3vw, 2.2rem); }
.quote p { font-family: var(--disp); font-weight: 500; font-size: clamp(19px, 3vw, 32px); line-height: 1.4; max-width: 30ch; }
.quote cite { display: block; font-family: var(--mono); font-style: normal; font-size: 9.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--mute); margin-top: 0.9rem; }

/* ═════════ FORM ═════════ */
.dform { display: flex; flex-direction: column; gap: 0.9rem; }
.dform .frow { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem; }
@media (max-width: 620px) { .dform .frow { grid-template-columns: 1fr; } }
.dform .field { display: flex; flex-direction: column; gap: 5px; }
.dform label { font-size: 9px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; color: var(--mute); }
.dform input, .dform select, .dform textarea {
  background: color-mix(in srgb, var(--paper) 65%, transparent); border: 1px solid var(--line); color: var(--ink);
  font-family: var(--mono); font-size: 12px; padding: 11px 12px; outline: none; border-radius: 0; }
.dform input:focus, .dform select:focus, .dform textarea:focus { border-color: var(--acc); }
.dform textarea { min-height: 120px; resize: vertical; }

/* ═════════ HOURS ═════════ */
.hours { border: 1px solid var(--line); background: color-mix(in srgb, var(--paper) 65%, transparent); }
.hours-row { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding: 11px 1.2rem; border-bottom: 1px dashed color-mix(in srgb, var(--ink) 25%, transparent); }
.hours-row:last-child { border-bottom: none; }
.hours-row .day { font-family: var(--disp); font-weight: 700; font-size: 14px; text-transform: uppercase; }
.hours-row .time { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--mute); }
.hours-row .time.open { color: var(--acc); font-weight: 700; }

/* ═════════ PLOTTED MAP ═════════ */
.dmap { position: relative; display: block; aspect-ratio: 4/3; border: 1px solid var(--line);
  background:
    radial-gradient(color-mix(in srgb, var(--ink) 12%, transparent) 1px, transparent 1.4px) 0 0 / 22px 22px,
    color-mix(in srgb, var(--paper) 65%, transparent);
  overflow: hidden; }
.dmap .street { position: absolute; background: color-mix(in srgb, var(--ink) 45%, transparent); }
.dmap .street.h { left: 0; right: 0; height: 1px; }
.dmap .street.v { top: 0; bottom: 0; width: 1px; }
.dmap .street-label { position: absolute; font-size: 8.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--mute); }
/* On Morrison (the 32% line), hard up against SE 10th (the 28% line) —
   the corner the building is actually on, not mid-block. */
.dmap .pin { position: absolute; left: 30%; top: 32%; transform: translate(-50%, calc(-50% - 10px)); }
.dmap .pin .ring { position: absolute; inset: -22px; border: 1px solid var(--acc); border-radius: 50%; animation: pinPulse 2.4s infinite; }
@keyframes pinPulse { 0% { transform: scale(0.4); opacity: 1; } 100% { transform: scale(1.5); opacity: 0; } }
.dmap .pin .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--acc); }
/* Shifted off the pin so the plate clears the vertical SE 10TH AVE legend,
   which the corner position now sits right on top of. */
.dmap .pin .lbl { position: absolute; top: -36px; left: 50%; transform: translateX(calc(-50% + 72px)) rotate(-3deg);
  font-size: 9px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
  border: 2px solid var(--acc); color: var(--acc); background: var(--paper); padding: 4px 9px; white-space: nowrap; }

/* ═════════ DOWNLOADS (cms.js fills #downloads with .dl) ═════════ */
.dl-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; padding: 0 var(--pad); max-width: 900px; }
@media (max-width: 640px) { .dl-row { grid-template-columns: 1fr; } }
.dl { display: flex; align-items: center; gap: 1.1rem; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper) 65%, transparent); padding: 1.1rem 1.3rem;
  transition: translate 0.25s var(--ease), box-shadow 0.25s var(--ease); }
@media (hover: hover) { a.dl:hover { translate: 0 -3px; box-shadow: 5px 5px 0 color-mix(in srgb, var(--ink) 85%, transparent); } }
.dl-badge { font-size: 9.5px; font-weight: 700; letter-spacing: 0.14em; border: 2px solid var(--acc); color: var(--acc); padding: 9px 11px; }
.dl h3 { font-family: var(--disp); font-weight: 800; text-transform: uppercase; font-size: 15px; }
.dl p { font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--mute); margin-top: 3px; }

/* ═════════ MENU TABS (food & drink) ═════════ */
.menu-tabs { position: sticky; top: 54px; z-index: 900; display: flex; flex-wrap: wrap;
  padding: 0.8rem var(--pad); background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); border-bottom: 1px solid var(--line); }
.menu-tabs:empty { display: none; }
/* `?blur=off` — see the measurement switches in dither.js. Both of these sit
   over backgrounds that are already 90–92% opaque, so what the blur is actually
   contributing is small; what it COSTS is a full-width backdrop recomputed
   every time the fixed field canvas behind it redraws. */
[data-noblur] .bar, [data-noblur] .menu-tabs {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* menu items (dotted leaders) */
/* --mi-lead is the name's own line box; the leader and the price centre on it */
.menu-list { --mi-lead: 24px; }
.mi-cat { font-size: 16px; font-weight: 700; letter-spacing: 0.24em; text-transform: uppercase; color: var(--acc); margin: 2rem 0 0.8rem;
  padding-bottom: 0.35rem; border-bottom: 1px solid color-mix(in srgb, var(--acc) 55%, transparent); }
.mi { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; }
.mi-main { flex: 1 1 auto; min-width: 0; }
.mi-head { display: flex; align-items: flex-start; }
.mi-name { flex: 0 1 auto; min-width: 0; font-family: var(--disp); font-weight: 700; font-size: 17px; line-height: var(--mi-lead); text-transform: uppercase; }
.mi-desc { font-size: 12.5px; color: var(--mute); margin-top: 2px; line-height: 1.7; }
/* the leader starts a space off the name and fades in, rather than butting against it */
.mi-dots { flex: 1 1 auto; min-width: 24px; margin-left: 1ch; align-self: flex-start; height: var(--mi-lead); position: relative; }
.mi-dots::before { content: ''; position: absolute; left: 0; right: 0; top: calc(var(--mi-lead) / 2);
  border-bottom: 1px dotted color-mix(in srgb, var(--ink) 45%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 min(72px, 45%));
  mask-image: linear-gradient(90deg, transparent 0, #000 min(72px, 45%)); }
.mi-price { font-size: 12px; line-height: var(--mi-lead); font-weight: 700; color: var(--acc); white-space: nowrap; }
/* the house list gets its own pane so it reads as the bar's own work */
.menu-pane { background: color-mix(in srgb, var(--acc) 8%, var(--paper) 92%);
  border: 1px solid color-mix(in srgb, var(--acc) 30%, transparent);
  padding: 1.1rem clamp(1rem, 3vw, 1.8rem) 1.3rem; }
.menu-pane > .mi-cat:first-child { margin-top: 0; }
/* Two sticky bars sit above the menu — the nav at 54px and the section tabs
   under it — so a jump that lands the section at y=0 lands its heading
   BEHIND them, and the reader arrives at a sub-head with no idea which
   list they are in. --menu-stick is the measured height of both, written
   by the page once the tabs exist and again on resize, because the tabs
   wrap to a second line on a narrow screen. */
.menu-section { padding-bottom: clamp(1rem, 3vh, 2rem); scroll-margin-top: var(--menu-stick, 118px); }
.menu-section > *:not(.cap) { margin-left: var(--pad); margin-right: var(--pad); }
.hh { margin: clamp(2rem, 5vh, 3.5rem) var(--pad); border: 2px dashed var(--acc); text-align: center;
  padding: clamp(1.8rem, 4vh, 3rem) 1.5rem; }
.hh h2 { font-family: var(--disp); font-weight: 800; text-transform: uppercase; font-size: clamp(26px, 5vw, 54px); letter-spacing: -0.015em; line-height: 0.98; }
.hh h2 i, .hh h2 .t-serif { font-style: normal; color: var(--acc); }
.hh p { font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--mute); margin-top: 0.9rem; }
.menu-note { text-align: center; font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); padding: 0 var(--pad) 2.5rem; line-height: 2.2; }

/* ═════════ FLYER WALL (masonry) ═════════ */
.wall { columns: 4 240px; column-gap: 12px; padding: 0 var(--pad); }
.wall-item { display: block; position: relative; break-inside: avoid; margin-bottom: 12px; border: 1px solid var(--line); cursor: pointer;
  transition: translate 0.3s var(--ease), box-shadow 0.3s var(--ease); }
.wall-item:nth-child(3n+1) { rotate: -0.6deg; } .wall-item:nth-child(3n+2) { rotate: 0.5deg; }
@media (hover: hover) { .wall-item:hover { translate: 0 -4px; rotate: 0deg; box-shadow: 6px 6px 0 color-mix(in srgb, var(--ink) 85%, transparent); z-index: 2; } }
.wall-item.hot { translate: 0 -4px; rotate: 0deg; box-shadow: 6px 6px 0 color-mix(in srgb, var(--ink) 85%, transparent); z-index: 2; }
/* Text over poster art — the gradient has to reach full paper behind the words,
   not fade out under them, or the caption contrast depends on the flyer. */
.wall-overlay { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 0.9rem;
  background: linear-gradient(to top, var(--paper) 0%, var(--paper) 55%, color-mix(in srgb, var(--paper) 80%, transparent) 80%, transparent 100%);
  opacity: 0; translate: 0 6px; transition: opacity 0.25s, translate 0.3s var(--ease); }
@media (hover: hover) { .wall-item:hover .wall-overlay { opacity: 1; translate: 0 0; } }
.wall-item.hot .wall-overlay { opacity: 1; translate: 0 0; }
@media (hover: none) { .wall-overlay { opacity: 1; translate: 0 0; } }
.wall-overlay .t { display: block; font-family: var(--disp); font-weight: 800; text-transform: uppercase; font-size: 13.5px; line-height: 1.15; }
.wall-overlay .d { display: block; font-size: 8.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--acc); margin-top: 4px; }

/* archive tiles (hatch plates) */
/* ═════════ GALLERY — photos ═════════
   Nothing in here dithers, duotones or hover-reveals. These are photographs and
   poster art; the plate's character is carried by the frame, the labels and the
   paper around them, not by recolouring the picture. Tiles deliberately sit
   OUTSIDE .hotable / .spec-color so no treatment can pick them up by accident. */

/* One fixed height, variable width from each image's own ratio. Cropping every
   tile to a single shape would slice through a portrait poster's type — and a
   rail wants variable widths anyway. */
.ph-tile { position: relative; margin: 0; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--ink) 6%, transparent); overflow: hidden; }
.ph-tile img { display: block; width: 100%; height: 100%; object-fit: cover; }

.hero-strip, .ph-rail {
  display: flex; gap: 12px; padding: 0 var(--pad) 4px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity; scrollbar-width: thin;
  /* Without this the snapport starts at the border box, so on load the browser
     snaps tile 1 flush to the window edge — scrolling the rail right by exactly
     the padding and leaving every rail misaligned with the heading above it. */
  scroll-padding-inline: var(--pad);
}
.hero-strip .ph-tile, .ph-rail .ph-tile { flex: 0 0 auto; scroll-snap-align: start; }
/* A highlight is a tile plus the folder it came from, so the rail's flex child
   is the pair, not the picture. The width calc and the snap move up with it;
   --ar is set on the wrapper and inherits down to the tile. */
.hero-strip .ph-item { flex: 0 0 auto; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 7px;
  width: calc(var(--ar) * clamp(200px, 34vh, 340px)); max-width: 86vw; }
.hero-strip .ph-tile { height: clamp(200px, 34vh, 340px); width: 100%; max-width: none; }
/* The source folder, named under its photo. One line: a long party name
   truncates rather than pushing the strip's tiles out of alignment. */
.ph-src { font-family: var(--mono); font-size: 9px; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ph-rail .ph-tile   { height: clamp(150px, 24vh, 240px); width: calc(var(--ar) * clamp(150px, 24vh, 240px)); max-width: 78vw; }
.hero-strip .ph-tile img, .ph-rail .ph-tile img { object-fit: cover; }

/* The full archive for one category: a plain grid, intrinsic ratios kept. */
.ph-grid { display: grid; gap: 12px; padding: 0 var(--pad);
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); }
.ph-grid .ph-tile { aspect-ratio: var(--ar); height: auto; width: auto; }

/* Caption sits on the image only on hover/focus, so a rail reads as pictures
   first. It is always in the DOM, so it is always available to a screen reader. */
.ph-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 0.7rem 0.8rem;
  display: flex; flex-direction: column; gap: 2px;
  background: linear-gradient(to top, color-mix(in srgb, var(--ink) 82%, transparent), transparent);
  color: var(--paper); opacity: 0; translate: 0 6px;
  transition: opacity 0.25s var(--ease), translate 0.25s var(--ease); }
.ph-tile:hover .ph-cap, .ph-tile:focus-within .ph-cap { opacity: 1; translate: 0 0; }
.ph-cap .t { font-family: var(--disp); font-weight: 800; text-transform: uppercase;
  font-size: 12px; line-height: 1.15; }
.ph-cap .by { font-size: 8.5px; letter-spacing: 0.18em; text-transform: uppercase; opacity: 0.78; }

/* "See all" closes a rail rather than letting it run past its cap. */
.ph-more { display: flex; align-items: center; justify-content: center; text-align: center;
  height: clamp(150px, 24vh, 240px); width: clamp(120px, 16vh, 170px); flex: 0 0 auto;
  font-family: var(--disp); font-weight: 800; text-transform: uppercase; font-size: 13px;
  border: 1px dashed var(--line); background: none; }
@media (hover: hover) { .ph-more:hover { border-style: solid; color: var(--acc); } }

/* Instagram (core/instagram.js). Posts come as 3:4, 4:5 and square, and none
   is cropped. On the homepage the four share one row at one height, each as
   wide as its own shape needs: a flex-grow equal to the ratio makes the widths
   proportional to it, so the heights come out equal and the row fills the
   width exactly. Three on a tablet; a scrolling strip on a phone. On the
   gallery it is an ordinary .ph-rail, like every other category there. */
.ig { padding-bottom: clamp(1rem, 3vh, 2rem); }
.ig-row { display: flex; gap: 12px; padding: 0 var(--pad); }
.ig-row .ig-tile { display: block; flex: var(--ar) 1 0; min-width: 0; aspect-ratio: var(--ar); }
/* The category chips follow straight after it; give it the air a .cap gives the rails below. */
.ig-rail { margin-bottom: clamp(1.6rem, 4vh, 3rem); }
.ig-follow { text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; }
@media (hover: hover) { .ig-follow:hover { color: var(--acc); text-decoration-color: currentColor; } }
@media (max-width: 1100px) { .ig-row > :nth-child(n+4) { display: none; } }
@media (max-width: 700px) {
  .ig-row { gap: 8px; overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scroll-padding-inline: var(--pad); scrollbar-width: thin; }
  .ig-row > :nth-child(n) { display: block; }
  .ig-row .ig-tile { flex: 0 0 auto; height: 300px; width: calc(var(--ar) * 300px); max-width: 82vw;
    aspect-ratio: auto; scroll-snap-align: start; }
}

.ph-chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 var(--pad) 1.6rem; }
.ph-chip { font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  border: 1px solid var(--line); padding: 0.45rem 0.7rem; }
@media (hover: hover) { .ph-chip:hover { border-color: var(--acc); color: var(--acc); } }
.ph-chip .n { color: var(--mute); margin-left: 4px; }

.ph-pager { display: flex; align-items: center; justify-content: center; gap: 1rem;
  flex-wrap: wrap; padding: 2rem var(--pad) 0; }
.ph-pagenum { font-size: 9.5px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); }
/* the categories "More categories" leads to, named, each a link to its rail */
.ph-nextup { padding: 1.2rem var(--pad) 0; text-align: center; }
.ph-nextup .lbl { display: block; margin-bottom: 0.6rem; font-size: 9.5px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--mute); }
.ph-nextup ul { list-style: none; margin: 0 auto; padding: 0; max-width: 70ch;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 0.35rem 0; }
.ph-nextup li + li::before { content: '·'; margin: 0 0.6rem; color: var(--mute); }
.ph-nextup a { font-size: 13px; text-decoration: underline; text-decoration-color: var(--line);
  text-underline-offset: 3px; }
.ph-nextup a:hover { color: var(--acc); text-decoration-color: currentColor; }
.cap[id] { scroll-margin-top: 5rem; }
.ph-back { padding: 1.6rem var(--pad) 0; font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; }
.ph-note { padding: 1.4rem var(--pad) 0; font-size: 9.5px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--mute); }
/* Sentence case, not the uppercase the other notes take: this one is a request
   to a reader, and tracked-out caps make a plea read like a stamp. */
.ph-rights { padding: 2.4rem var(--pad) 0; font-size: 11px; line-height: 1.8; color: var(--mute); max-width: 62ch; }
.ph-rights a { color: var(--acc); text-decoration: underline; text-underline-offset: 2px; }

/* ── lightbox (core behaviour, theme skin) ──
   Core builds the DOM and owns the keyboard, focus trap and history; this is
   only what it looks like. The class names themselves are fixed by core. */
html.holo-lb-open { overflow: hidden; }
.holo-lb-miss { margin: 1rem var(--pad) 0; padding: 10px 14px; border: 1px solid var(--line); font-size: 14px; }
.holo-lb { position: fixed; inset: 0; z-index: 9000; display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 92%, transparent); padding: clamp(0.8rem, 3vw, 2.4rem); }
.holo-lb[hidden] { display: none; }
.holo-lb-inner { position: relative; display: grid; place-items: center;
  width: 100%; height: 100%; grid-template-columns: auto 1fr auto; gap: clamp(4px, 1.5vw, 18px); }
.holo-lb-figure { grid-column: 2; margin: 0; display: flex; flex-direction: column;
  align-items: center; gap: 0.7rem; max-height: 100%; min-width: 0; }
.holo-lb-img { max-width: 100%; max-height: calc(100vh - 8rem); object-fit: contain;
  border: 1px solid color-mix(in srgb, var(--paper) 30%, transparent); background: var(--ink); }
.holo-lb-cap { display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center; }
.holo-lb-text { font-family: var(--disp); font-weight: 800; text-transform: uppercase;
  font-size: 14px; color: var(--paper); }
.holo-lb-credit { font-size: 9px; letter-spacing: 0.2em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper) 62%, transparent); }
.holo-lb-link { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--paper);
  text-decoration: underline; text-underline-offset: 3px; margin-top: 4px; }
.holo-lb-link[hidden] { display: none; }
.holo-lb-count { position: absolute; top: 0; left: 0; font-size: 9px; letter-spacing: 0.2em;
  text-transform: uppercase; color: color-mix(in srgb, var(--paper) 62%, transparent); margin: 0; }
.holo-lb-close { position: absolute; top: -4px; right: 0; z-index: 2; background: none; border: 0;
  color: var(--paper); font-size: 30px; line-height: 1; cursor: pointer; padding: 4px 10px; }
.holo-lb-nav { background: none; border: 1px solid color-mix(in srgb, var(--paper) 28%, transparent);
  color: var(--paper); font-size: 26px; line-height: 1; cursor: pointer;
  padding: 0.5rem 0.7rem; align-self: center; }
/* The backdrop is the plate's ink, so the accent is the one each plate tunes
   for an ink surface. Plain --acc came out at 1.14:1 on Fluoro, 1.05:1 on Blueprint. */
@media (hover: hover) { .holo-lb-nav:hover:not(:disabled) { border-color: var(--acc-on-ink); color: var(--acc-on-ink); } }
/* Disabled rather than hidden at the ends: a control that vanishes moves the
   image sideways under the reader mid-browse. */
.holo-lb-nav:disabled { opacity: 0.22; cursor: default; }
.holo-lb-close:focus-visible, .holo-lb-nav:focus-visible { outline: 2px solid var(--acc-on-ink); outline-offset: 2px; }
@media (max-width: 640px) {
  .holo-lb-nav { position: absolute; bottom: 6px; }
  .holo-lb-prev { left: 4px; } .holo-lb-next { right: 4px; }
  .holo-lb-inner { grid-template-columns: 1fr; }
  .holo-lb-figure { grid-column: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .ph-cap { transition: none; }
}

/* ═════════ CTA BAND ═════════ */
.cta-band { margin: clamp(2.4rem, 6vh, 4rem) var(--pad) 0; border: 1px solid var(--line);
  padding: clamp(2rem, 5vh, 3.4rem) clamp(1.2rem, 3vw, 2.4rem); text-align: center;
  background: color-mix(in srgb, var(--paper) 65%, transparent); position: relative; }
.cta-band::before { content: '✳'; position: absolute; top: 10px; left: 14px; color: var(--acc); }
.cta-band::after { content: '✳'; position: absolute; bottom: 10px; right: 14px; color: var(--acc); }
.cta-band h2 { font-family: var(--disp); font-weight: 800; font-size: clamp(26px, 5.5vw, 60px); text-transform: uppercase; letter-spacing: -0.015em; line-height: 1; }
.cta-band h2 i { font-style: normal; color: var(--acc); }
.btn-row { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; margin-top: 1.4rem; }

/* ═════════ STAIRS (home explore) ═════════ */
.stairs { padding: clamp(2.4rem, 6vh, 4rem) var(--pad); }
.stairs a { display: block; padding: 0.35rem 0; width: fit-content; max-width: 100%; }
/* The staircase. These used to start at :nth-child(3) because a caption and a
   letter/description ran with each link; the links are the only children now,
   so the first one sits flush and the rest step in from it. */
.stairs a:nth-child(2) { margin-left: clamp(1.5rem, 8vw, 8rem); }
.stairs a:nth-child(3) { margin-left: clamp(3rem, 16vw, 16rem); }
.stairs a:nth-child(4) { margin-left: clamp(4.5rem, 24vw, 24rem); }
.stairs .st-label {
  font-family: var(--disp); font-weight: 800; font-size: clamp(30px, 6.5vw, 84px);
  line-height: 1.06; text-transform: uppercase; letter-spacing: -0.015em;
  color: transparent; -webkit-text-stroke: 1.5px var(--ink);
  transition: color 0.25s, -webkit-text-stroke-color 0.25s, translate 0.3s var(--ease);
}
.stairs a:hover .st-label, .stairs a.hot .st-label { color: var(--acc); -webkit-text-stroke-color: var(--acc); translate: 8px 0; }
@media (max-width: 760px) { .stairs a:nth-child(n) { margin-left: 0; } }

/* ═════════ FOOTER ═════════ */
footer { border-top: 1px solid var(--line); padding: clamp(2rem, 5vh, 3.4rem) var(--pad) 1.3rem; margin-top: clamp(2rem, 5vh, 3.5rem); }
.foot-grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1.5fr; gap: clamp(1.5rem, 4vw, 3.2rem); margin-bottom: 2.2rem; }
/* A column may shrink below its content: the newsletter's first/last name
   inputs are 20 characters wide each by default, and on a phone under ~380px
   that held the one-column grid wider than the screen. */
.foot-grid > * { min-width: 0; }
.foot-brand { font-family: var(--disp); font-weight: 800; font-size: clamp(19px, 2vw, 26px); text-transform: uppercase; margin-bottom: 0.8rem; }
.foot-addr { font-size: 10px; line-height: 2.1; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); }
.foot-col-title { font-size: 9.5px; font-weight: 700; letter-spacing: 0.24em; text-transform: uppercase; color: var(--acc); margin-bottom: 0.9rem; }
.foot-col a { display: flex; align-items: center; min-height: 40px; font-family: var(--disp); font-weight: 600; font-size: 13.5px; text-transform: uppercase; color: var(--mute); transition: color 0.15s, translate 0.2s var(--ease); }
.foot-col a:hover { color: var(--ink); translate: 4px 0; }
.newsletter-label { font-size: 9.5px; font-weight: 700; letter-spacing: 0.24em; text-transform: uppercase; color: var(--acc); margin-bottom: 0.9rem; }
.newsletter-form { display: flex; flex-wrap: wrap; border: 1px solid var(--line); }
.newsletter-form input { flex: 1; min-width: 0; background: none; border: none; outline: none; padding: 14px; color: var(--ink); font-family: var(--mono); font-size: 11.5px; }
.newsletter-form input::placeholder { color: var(--mute); }
.newsletter-form button { font-family: var(--mono); font-weight: 700; font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; background: var(--ink); color: var(--paper); padding: 0 18px; transition: background 0.15s, color 0.15s; }
@media (hover: hover) { .newsletter-form button:hover { background: var(--acc); color: var(--acc-ink); } }
/* First and last name, on their own line under the address. Core builds this
   row (applyNewsletter in /_core/cms.js); the plate only decides that it reads
   as the quieter half — a size down, a shorter measure of padding, hairline
   ruled off the email row it sits beneath. */
.newsletter-names { display: flex; width: 100%; border-top: 1px solid var(--line); }
.newsletter-names input { padding: 9px 14px; font-size: 10.5px; }
.newsletter-names input + input { border-left: 1px solid var(--line); }
/* Core swaps the form out for this on a successful signup (applyNewsletter in
   /_core/cms.js). It is a direct child of the form and the SDK reveals it with
   jQuery .show(), which clears the inline display — so it must NOT rely on the
   form's own flex layout for its box. */
.newsletter-form .newsletter-done { padding: 14px; font-family: var(--mono); font-size: 11.5px; line-height: 1.55; color: var(--ink); }
.newsletter-form .newsletter-done b { display: block; color: var(--acc); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 4px; }
.newsletter-form .newsletter-done:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.newsletter-form .newsletter-err { flex-basis: 100%; margin-top: 8px; font-size: 12px; line-height: 1.4; color: var(--ink); }
.newsletter-form .newsletter-err[hidden] { display: none; }
.newsletter-note { font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); margin-top: 0.7rem; }
.foot-giant {
  font-family: var(--disp); font-weight: 800; font-size: clamp(52px, 14vw, 240px);
  line-height: 0.95; text-transform: uppercase; text-align: center; letter-spacing: -0.02em;
  color: var(--ink); user-select: none; margin: 1.2rem 0 1.4rem;
  -webkit-mask-image: radial-gradient(circle at 50% 40%, black 30%, rgba(0,0,0,0.35) 100%),
    radial-gradient(black 1.1px, transparent 1.5px);
  -webkit-mask-size: auto, 5px 5px; -webkit-mask-composite: source-in;
  mask-image: radial-gradient(circle at 50% 40%, black 30%, rgba(0,0,0,0.35) 100%),
    radial-gradient(black 1.1px, transparent 1.5px);
  mask-size: auto, 5px 5px; mask-composite: intersect;
}
.skin-picker { display: flex; justify-content: center; align-items: center; gap: 8px; margin-bottom: 1.4rem; flex-wrap: wrap; }
.skin-picker .lbl { font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--mute); margin-right: 8px; }
.skin-btn { display: inline-flex; align-items: center; min-height: 40px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--mute); padding: 7px 14px; border: 1px solid var(--line); transition: color 0.15s, background 0.15s; }
.skin-btn:hover { color: var(--ink); }
.skin-btn.active { background: var(--ink); color: var(--paper); }
.foot-bottom { border-top: 1px solid var(--line); padding-top: 1.1rem;
  display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap;
  font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--mute); }
@media (max-width: 900px) { .foot-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .foot-grid { grid-template-columns: 1fr; } .foot-bottom { flex-direction: column; text-align: center; } }

/* ═════════ BUTTONS / BADGES ═════════ */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--mono); font-weight: 700; font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 13px 24px; border: 1px solid var(--line); color: var(--ink);
  transition: background 0.15s, color 0.15s; cursor: pointer; }
@media (hover: hover) { .btn:hover { background: var(--ink); color: var(--paper); } }
.btn-solid { background: var(--acc); border-color: var(--acc); color: var(--acc-ink); }
.btn-solid:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn-ghost { border-color: color-mix(in srgb, var(--ink) 35%, transparent); color: var(--mute); }
.btn.is-disabled { color: var(--mute); border-color: var(--mute); text-decoration: line-through; pointer-events: none; cursor: default; }
.btn.is-door { border-style: dashed; pointer-events: none; cursor: default; }
.ev-row-badge { font-size: 9.5px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 8px 12px; background: var(--acc); color: var(--acc-ink); white-space: nowrap; align-self: center; }
.ev-row-badge.low { background: var(--ink); color: var(--paper); }
.ev-row-badge.soldout { background: none; border: 2px solid var(--ink); color: var(--ink); text-decoration: line-through; }

/* ═════════ EVENT MODAL (events.js contract) ═════════ */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 2000;
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  display: flex; align-items: flex-end; justify-content: center;
  opacity: 0; visibility: hidden; transition: opacity 0.25s, visibility 0.25s;
}
@media (min-width: 769px) { .modal-backdrop { align-items: center; padding: 2rem; } }
.modal-backdrop.open { opacity: 1; visibility: visible; }
.modal {
  position: relative; background: var(--paper); color: var(--ink);
  border: 2px solid var(--line);
  box-shadow: 8px 8px 0 color-mix(in srgb, var(--ink) 80%, transparent);
  width: 100%; max-width: 760px; max-height: 92vh; max-height: 92dvh; overflow-y: auto;
  transform: translateY(30px) rotate(0.4deg); transition: transform 0.35s var(--ease); overscroll-behavior: contain;
}
.modal-backdrop.open .modal { transform: translateY(0) rotate(0deg); }
.modal-top { padding: 1.4rem 3.6rem 1.2rem 1.6rem; border-bottom: 2px solid var(--line); position: relative; }
.modal-split { display: flex; flex-direction: column; }
.modal-img { position: relative; aspect-ratio: 3/4; background: color-mix(in srgb, var(--ink) 6%, var(--paper)); flex-shrink: 0; }
.modal-img img { width: 100%; height: 100%; object-fit: cover; }
.modal-close {
  position: absolute; top: 0; right: 0; z-index: 3; width: 46px; height: 46px;
  background: var(--ink); color: var(--paper);
  display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1;
  transition: background 0.15s, color 0.15s;
}
.modal-close:hover { background: var(--acc); color: var(--acc-ink); }
.modal-body { padding: clamp(1.3rem, 3vw, 2rem); }
.modal-presenter { font-size: 10px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase; color: var(--acc); margin-bottom: 0.5rem; }
.modal-title { font-family: var(--disp); font-weight: 800; font-size: clamp(24px, 3.6vw, 38px); line-height: 1.05; text-transform: uppercase; letter-spacing: -0.01em; }
.modal-subtitle { font-family: var(--disp); font-weight: 600; font-size: clamp(13px, 1.8vw, 16px); text-transform: uppercase; color: var(--mute); margin-top: 0.35rem; }
.modal-support { font-family: var(--disp); font-weight: 500; font-size: clamp(13.5px, 1.8vw, 17px); color: var(--mute); margin-top: 0.4rem; }
.modal-meta { display: flex; flex-wrap: wrap; gap: 0; margin: 1.2rem 0; }
.modal-meta span { font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase;
  padding: 8px 12px; border: 1px solid var(--line); margin: 0 -1px -1px 0; color: var(--mute); }
.modal-meta span.hl { background: var(--acc); border-color: var(--acc); color: var(--acc-ink); font-weight: 700; }
.modal-desc { font-size: 12px; line-height: 1.9; margin: 1.3rem 0 1.5rem; white-space: pre-line; }
.modal-actions { display: flex; gap: 10px; flex-wrap: wrap; }
/* The show sheet's facts, in two rows. Row 1: the date, unboxed, at the left;
   time and age boxed together at the right, each just wide enough. Row 2: the
   price boxed at the left, Buy at the right. The markup is one flat list; the
   ::after is a full-width break between the rows. The date is short
   ("Thu, Oct 29") so a phone fits it. Back to Calendar sits alone at the
   bottom, centred. */
.modal-badge { display: inline-block; margin-top: 0.7rem; }
.modal-meta.modal-ticket { display: flex; flex-wrap: wrap; align-items: stretch; gap: 0; margin: 1.2rem 0; max-width: 460px; }
.modal-meta.modal-ticket::after { content: ''; order: 2; flex-basis: 100%; height: 0; }
.modal-meta.modal-ticket > * {
  order: 1; display: flex; align-items: center; margin: 0; padding: 7px 10px; border: 1px solid var(--line);
  font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  letter-spacing: 0.12em; line-height: 1.2; text-transform: uppercase; white-space: nowrap;
}
/* Buy keeps the plate's own .btn-solid colours, like every other Buy button
   on the site. */
.modal-meta.modal-ticket > :not(.btn-solid) { background: var(--paper); color: var(--ink); }
/* (0,4,1): above the plates' own .modal-meta span.hl colours, which were for
   the old lime date chip. */
.modal-meta.modal-ticket > span.mt-date.hl { margin-right: auto; padding-left: 0; border-color: transparent;
  background: none; color: var(--ink); font-size: 12px; font-weight: 700; }
.modal-meta.modal-ticket > .mt-age { margin-left: -1px; }
.modal-meta.modal-ticket > .mt-price,
.modal-meta.modal-ticket > .mt-buy { order: 3; margin-top: 10px; min-height: 44px; }
.modal-meta.modal-ticket > .mt-price { margin-right: auto; padding-inline: 12px; }
.modal-meta.modal-ticket > .mt-buy { margin-left: auto; padding-inline: 18px; justify-content: center; gap: 8px; font-weight: 700; }
.modal-meta.modal-ticket > .is-disabled, .modal-meta.modal-ticket > .is-door { color: var(--mute); }
.modal-meta.modal-ticket > .is-door { border-style: dashed; }
.modal-actions { justify-content: center; margin-top: 1.6rem; }
/* Phones: the block goes above the poster, straight under the title, so Buy
   is on the first screen whatever shape the poster is (a tall one alone used
   to fill it). The body's own box steps aside so its parts can be reordered
   around the poster; each takes the body's side padding. */
@media (max-width: 768px) {
  .modal-body { display: contents; }
  .modal-facts { order: -1; padding: 1rem clamp(1.3rem, 3vw, 2rem); border-bottom: 2px solid var(--line); }
  .modal-facts .modal-ticket { margin: 0; max-width: none; }
  .modal-body > :not(.modal-facts) { margin-inline: clamp(1.3rem, 3vw, 2rem); }
  .modal-body > .modal-listen { margin-top: 1.3rem; }
  .modal-actions { margin-bottom: calc(clamp(1.3rem, 3vw, 2rem) + env(safe-area-inset-bottom, 0px)); }
}
/* An SE-sized phone with the longest likely rows ("$19.17–$35.00",
   "7:30PM", "All ages") needs a little less padding and tracking. */
@media (max-width: 380px) {
  .modal-facts { padding-inline: 16px; }
  .modal-meta.modal-ticket > * { padding-inline: 8px; letter-spacing: 0.06em; }
  .modal-meta.modal-ticket > .mt-buy { padding-inline: 12px; }
}

/* The grab bar: on a touch screen the sheet can be swiped down to close
   (core/client/events.js), and this is the usual sign of it. */
.modal-grab { display: none; }
@media (hover: none) {
  .modal-grab { display: block; position: absolute; top: 7px; left: 50%; transform: translateX(-50%); z-index: 2;
    width: 40px; height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--ink) 28%, transparent); pointer-events: none; }
}
@media (max-width: 768px) {
  .modal-backdrop { align-items: flex-start; }
  /* The height cap is what makes the sheet scroll. It is written twice because
     a declaration with env() in it is only checked at computed time: Chrome
     before 108 (no dvh) does not skip a bad one for the line before, it resets
     max-height to none, and the sheet runs off the bottom of a locked page
     with no way to scroll to the rest. 100% is the backdrop, which is the
     screen; dvh is kept where it is understood. */
  .modal { margin-top: calc(env(safe-area-inset-top, 0px) + 54px); max-height: calc(100% - env(safe-area-inset-top, 0px) - 54px); box-shadow: none; }
  @supports (height: 100dvh) {
    .modal { max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 54px); }
  }
  /* Open at the bottom. The sheet ends at the bottom of the screen, so a
     closed frame put its two lower corners exactly where a phone's rounded
     corners cut them off — a bright line visibly sliced. With no bottom edge
     the side lines run off the screen like a sheet that continues below.
     min-height keeps it reaching the bottom when the show has little to say,
     or the side lines would stop in mid-air; written twice for the same
     Chrome-before-108 reason as max-height above. */
  .modal { border-bottom: 0; min-height: calc(100% - env(safe-area-inset-top, 0px) - 54px); }
  @supports (height: 100dvh) {
    .modal { min-height: calc(100dvh - env(safe-area-inset-top, 0px) - 54px); }
  }
  /* The last button cleared the edge by the ordinary padding alone, which on
     an iPhone puts it inside the corner curve and under the home bar. */
  .modal-body { padding-bottom: calc(clamp(1.3rem, 3vw, 2rem) + env(safe-area-inset-bottom, 0px)); }
  .modal-img { aspect-ratio: unset; }
  .modal-img img { height: auto; object-fit: unset; }
}
@media (min-width: 769px) {
  /* Wider than it used to be (920px) to buy the poster column its own width
     without squeezing the text: the frame now takes the artwork's shape rather
     than cropping it into a fixed box. --poster-ar is set by core once the
     image reports its natural size; 3/4 is the standing assumption until then. */
  /* A definite height (not just max-height) is what lets the poster's height:100%
     resolve — without it the split's height is flex-derived, the percentage
     falls back to auto, and the frame sizes itself from max-width instead. */
  .modal { max-width: 1200px; height: min(88vh, 880px); overflow: hidden; display: flex; flex-direction: column; }
  .modal-top { flex-shrink: 0; }
  /* Flex, not grid: the poster stretches to the full height of the row and its
     width is then transferred from --poster-ar, so a vertical poster runs flush
     top to bottom with no bands, and the text column takes whatever is left.
     A grid track can't do this — it would have to be sized before the height
     is known, which is what left blank space above and below. */
  /* flex-direction must be restated — the base (mobile) rule stacks this column. */
  .modal-split { display: flex; flex-direction: row; flex: 1; min-height: 0; overflow: hidden; }
  .modal-img {
    /* height:100% (not align-self:stretch) is what makes the cross size
       definite, so the aspect ratio can transfer it into a width. */
    flex: 0 0 auto; height: 100%; width: auto;
    /* 58% is what a square needs at this modal height; below that the cap, not
       the artwork, sets the width and the bands come back. */
    aspect-ratio: var(--poster-ar, 3/4); max-width: 58%;
    overflow: hidden; border-right: 2px solid var(--line);
    /* content-box, so the ratio sizes the artwork and the border sits outside
       it. Under the global border-box the 2px border came out of the poster's
       own width, the contained image fell 2px short, and a 4:5 poster showed a
       1.25px band of frame above and below. */
    box-sizing: content-box;
  }
  /* contain, not cover: with the frame already on the artwork's aspect there is
     nothing to crop, and it stays a letterbox rather than a crop in the corner
     case where max-width clamps a very wide image. */
  .modal-img img { object-fit: contain; }
  /* Landscape can't run full-height without eating the modal — keep it centred
     at a fixed share of the width instead. */
  .modal-img--landscape { align-self: center; flex: 0 0 min(520px, 45%); max-width: none; border-right: none; }
  .modal-body { flex: 1 1 0; min-width: 0; }
  .modal-body { overflow-y: auto; overscroll-behavior: contain; padding: 1.7rem 1.9rem; }
}

/* ═════════ MOTION / A11Y ═════════ */
.reveal { opacity: 0; translate: 0 22px; transition: opacity 0.6s var(--ease), translate 0.6s var(--ease); }
.reveal.in { opacity: 1; translate: 0 0; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .reveal { opacity: 1; translate: 0 0; }
  html { scroll-behavior: auto; }
}
/* Focus ring: two-tone on purpose. The outline carries the 3:1 contrast; the
   box-shadow fills the offset gap with paper so the ring survives landing on a
   dark poster or a filled button, where a single-colour ring can vanish. */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--paper);
  border-radius: 1px;
}

/* Visually hidden but read aloud — for section headings the design carries
   typographically rather than as text, so the heading outline stays unbroken. */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Skip link — first thing in the tab order, invisible until focused. */
.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 3000;
  background: var(--paper); color: var(--ink); border: 2px solid var(--focus);
  font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 12px 18px; transition: top 0.15s;
}
.skip-link:focus { top: 8px; }

/* Event titles are real links to /events/<slug> — crawlable and shareable —
   but visually still headings; the modal intercepts the click. */
.spec-title a, .crow-title a { color: inherit; text-decoration: none; }
.spec-title a:hover, .crow-title a:hover { color: inherit; }
/* The title link is now the keyboard/AT route into an event, so it has to be a
   real target: a tight display line-height left it 23px tall. */
.spec-title a, .crow-title a { display: inline-block; padding: 4px 0; }

/* ═════════ TYPE SETS ═════════
   Three type sets, picked in the footer beside the plate. GROTESK is the
   original and the default: it is the absence of data-type, and nothing in
   this section touches it. The other two are alternates a reader opts into:

   SLAB   display  Montagu Slab — a bookish slab with an optical-size axis, so
                   the big headings get the sharp, high-contrast cut and the
                   leads the sturdy one. It has no italic: accent words are the
                   same face at its hairline weight, in the plate's accent.
          text     Schibsted Grotesk — a newspaper grotesk, sturdy and plain
          data     IBM Plex Mono — its slab serifs answer the display face

   READING  is Grotesk itself, the capitals, brackets, nav and accents all
            kept, with only the mechanics fixed: long text (event bios, the
            venue history, menu descriptions, FAQs) gets a face and size made
            for reading, and no label is smaller than 11px. It is outside the
            shared rules below, which belong to Slab. The long-text face is
            Zilla Slab (picked 28 Oct 2026 from a trial of 31 faces; before
            it Fraunces, soft and wonky, from 9 Oct, which read
            old-fashioned beside the rest of the site), set through --long-*.
            (WIDE held this slot until 8 Oct 2026, then briefly PLAIN, a
            neutral sans that made the site look ordinary. A stored "wide" or
            "plain" reads as Reading.)

   Slab is set larger than Grotesk. The shared rules below say WHAT each part
   of the page is (heading, lead, label, control, data); the --ts-* values a
   set defines say how big and how heavy. Another set of that kind is a new
   block of --ts-* values, not another copy of the rules.

   Those rules also read the page's labels plainly: mixed-case headings,
   one label style instead of a dozen tracked sizes, no brackets on buttons
   and no "01 / Layout" numbers. Event titles stay in capitals because Etix
   sends them in mixed shouting, and the one-page novelty faces fall back to
   the set. Reading opts out of all of that, which is the point of it.

   Every shared selector starts :root[data-type]:not([data-type="reading"])
   (0,3,0), which outranks the plate rules it has to override ([data-skin] .x
   and :not([data-skin]) .x). */
.type-picker, .field-picker { margin-top: -0.7rem; }

:root[data-type="slab"] {
  --disp: 'Montagu Slab', 'Rockwell', 'Georgia', serif;
  --text: 'Schibsted Grotesk', 'Helvetica Neue', Arial, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
  --ts-body: 17px;
  --ts-head-w: 500;  --ts-head-ls: -0.02em;  --ts-head-x: 100%;
  --ts-acc-style: normal; --ts-acc-w: 200;
  --ts-title-w: 600; --ts-title-ls: 0.004em; --ts-title-x: 100%;
  --ts-lead-w: 400;
  --ts-ui-w: 600;
  --ts-label-w: 700; --ts-label-ls: 0.06em;
  --ts-data-w: 600;
}
/* Reading: Grotesk with its long text and smallest labels fixed. The shared
   alternate rules skip this set; everything else on the page is Grotesk.
   The default (no data-type) borrows its long-text face for the three places
   people read at length; see the rule after this set. */
:root:not([data-type]), :root[data-type="reading"] {
  /* Zilla Slab: a sturdy, slightly condensed slab that reads as of a piece
     with the grotesque headings and the mono labels. Static cuts, so no
     variation settings. */
  --long: 'Zilla Slab', 'Rockwell', Georgia, serif;
  --long-x: 100%;            /* font-stretch; Zilla Slab has no width axis */
  --long-var: normal;
  --long-w: 400;
  --long-size: 17.5px;       /* paragraphs */
  --long-size-s: 15.5px;     /* card, menu and spec-sheet text */
  --long-lh: 1.55;
  --long-ls: 0;
  --long-measure: 64ch;
}
:root[data-type="reading"] :is(.ev-body, .prose p, .page-block p, .faq-a p, .faq-a ul, .faq-a ol, .step p,
  .modal-desc, .cf-done p, .menu-intro, .arc-lede, .vkey-note,
  .dcard p, .mi-desc, .tv, .vs-sum) {
  font-family: var(--long); font-stretch: var(--long-x); font-variation-settings: var(--long-var);
  font-weight: var(--long-w); font-size: var(--long-size); line-height: var(--long-lh);
  letter-spacing: var(--long-ls); text-transform: none;
}
:root[data-type="reading"] :is(.dcard p, .mi-desc, .tv, .vs-sum, .vkey-note) { font-size: var(--long-size-s); line-height: 1.65; }
:root[data-type="reading"] :is(.ev-body, .prose p, .page-block p, .faq-a p, .arc-lede) { max-width: var(--long-measure); }

/* Grotesk's long text was 12.5–15px mono: fine for a line, hard work for a
   privacy policy, the 20-year history or an artist bio. Those three (special
   page sections, the event page and the calendar's show sheet) take
   Reading's face and size; every short label stays Grotesk. */
:root:not([data-type]) :is(p.ev-body, .modal-desc, .page-block p) {
  font-family: var(--long); font-variation-settings: var(--long-var);
  font-weight: var(--long-w); font-size: var(--long-size); line-height: var(--long-lh);
  letter-spacing: var(--long-ls); text-transform: none; max-width: var(--long-measure);
}

/* No label under 11px (some were 8px on a phone). They stay tracked mono
   capitals; Martian's narrowest cut and a slightly tighter track keep each
   one about the width it had, so rows do not start wrapping. The contact
   map's street labels are part of the drawing and keep their size. */
:root[data-type="reading"] :is(.bk-mail-label, .board-cap span, .btn, .cal-status span, .chip, .crow-date,
  .crow-mark, .crow-meta, .crow-meta b, .crow-presenter, .crow-when, .ct-where-lbl, .dl-badge, .ev-crumbs,
  .ev-presenter, .ev-row-badge, .field label, .foot-addr, .foot-bottom span, .foot-col-title,
  .fresh-cap span, .frow-dash, .frow-dot, .frow-price, .frow-time, .hl-count, .hl-kick, .hl-more, .hl-song, .idx, .kv strong,
  .skin-picker .lbl, .mdiv span, .menu-note, .mk, .newsletter-done b, .newsletter-form button, .newsletter-label,
  .newsletter-note, .ph-chip, .ph-src, .poster-link, .quote cite, .rsvp-stamp, .skin-btn, .spec-buy, .spec-date,
  .spec-meta, .spec-meta b, .spec-meta span, .spec-stamp, .src, .stat-label, .dcard .tag, .dcard ul li, .tech-cap,
  .vjump a, .vs-o, .dl p, .wall-overlay .d, .menu-foot, .menu a.menu-link em) {
  font-size: 11px; font-stretch: 75%; letter-spacing: 0.12em;
}

/* Montagu Slab has no italic; a browser left to itself slants the roman,
   which is the one thing a type set must never be seen doing. */
:root[data-type="slab"] { font-synthesis: none; }

:root[data-type]:not([data-type="reading"]) body { font-family: var(--text); font-size: var(--ts-body); line-height: 1.6; }
:root[data-type]:not([data-type="reading"]) .ev-sheet { --body: var(--text); }

/* editorial headings: mixed case, tight */
:root[data-type]:not([data-type="reading"]) :is(.ph-title, .cap h2, .idx-cap h2, .mdiv h2, .prose h2, .hh h2, .cta-band h2,
  .stairs .st-label, .menu a.menu-link, .ct-open-title, .ct-ask h2, .cf-head h2, .cf-done h3,
  .page-block h2, .vsub h3, .dcard h3, .foot-brand) {
  font-family: var(--disp); text-transform: none; font-weight: var(--ts-head-w);
  letter-spacing: var(--ts-head-ls); font-stretch: var(--ts-head-x);
}
:root[data-type]:not([data-type="reading"]) .ph-title { letter-spacing: calc(var(--ts-head-ls) - 0.015em); line-height: 0.95; }
/* The page title's highlight stays a block, set in the heading's own style.
   As an inline-block the fill is the box itself, so a tall line box cannot
   push it down onto the lede below. */
:root[data-type]:not([data-type="reading"]) .ph-title i { font-family: var(--disp); font-style: normal; font-weight: var(--ts-head-w);
  letter-spacing: inherit; font-size: 1em; font-stretch: var(--ts-head-x);
  display: inline-block; line-height: 0.9; padding-block: 0.04em 0.1em; }
/* The giant footer word is sized to fit the page at normal width; Wide's
   expanded cut would run it off the right edge. */
:root[data-type]:not([data-type="reading"]) .foot-giant { font-family: var(--disp); font-weight: var(--ts-head-w); letter-spacing: -0.01em;
  /* Slab and Wide set the word wider than Grotesk: at the 52px floor it ran
     past a 320px phone. 14vw all the way down fits both. */
  font-size: clamp(40px, 14vw, 240px); }

/* small headings and item names */
:root[data-type]:not([data-type="reading"]) :is(.faq-q, .step h3, .dl h3, .tk, .vs-title, .mi-name, .vkey b, .hours-row .day) {
  font-family: var(--disp); text-transform: none; font-weight: var(--ts-head-w); letter-spacing: -0.005em;
  font-stretch: var(--ts-head-x);
}
:root[data-type]:not([data-type="reading"]) .mi-name { font-size: 20px; }
:root[data-type]:not([data-type="reading"]) .faq-q { font-size: clamp(17px, 1.9vw, 22px); }
:root[data-type]:not([data-type="reading"]) :is(.step h3, .dl h3, .tk, .hours-row .day) { font-size: 16px; }

/* accent words in section headings: the plate's accent, no block. Plain is
   left out: it keeps each plate's own Grotesk accent (Spectra's highlight
   blocks), because those are part of how the site looks, not a type problem. */
:root[data-type]:not([data-type="reading"]) :is(.cap h2, .idx-cap h2, .mdiv h2, .prose h2, .hh h2, .cta-band h2, .ct-ask h2) i,
:root[data-type]:not([data-type="reading"]) :is(.tech-cap, .stat-num) i,
:root[data-type]:not([data-type="reading"]) .hh h2 .t-serif {
  font-family: var(--disp); font-style: var(--ts-acc-style); font-weight: var(--ts-acc-w);
  letter-spacing: var(--ts-head-ls); font-stretch: var(--ts-head-x);
  color: var(--acc); background: none; padding: 0; font-size: 1em; text-transform: none;
}

/* event titles are data: they keep their capitals, like a poster headline */
:root[data-type]:not([data-type="reading"]) :is(.spec-title, .crow-title, .modal-title, .ev-title, .frow-title,
  .ev-more-title, .wall-overlay .t, .ph-cap .t, .holo-lb-text) {
  font-family: var(--disp); text-transform: uppercase; font-weight: var(--ts-title-w);
  letter-spacing: var(--ts-title-ls); line-height: 1.04; font-stretch: var(--ts-title-x);
}
:root[data-type]:not([data-type="reading"]) .ev-title { letter-spacing: -0.005em; }
:root[data-type]:not([data-type="reading"]) :is(.spec-support, .crow-support, .modal-support, .modal-subtitle, .ev-sub, .ev-with) {
  font-family: var(--text); text-transform: none; font-weight: 400; letter-spacing: 0; font-stretch: 100%;
}
:root[data-type]:not([data-type="reading"]) :is(.spec-support, .crow-support) { font-size: 15px; }

/* leads: the display face, light */
:root[data-type]:not([data-type="reading"]) :is(.ph-sub p, .page-intro, .bk-lede p, .vlede, .hero-side, .hero-annot .desc) {
  font-family: var(--disp); font-weight: var(--ts-lead-w); letter-spacing: -0.008em;
}
:root[data-type]:not([data-type="reading"]) .quote p { font-family: var(--disp); font-style: var(--ts-acc-style); font-weight: var(--ts-lead-w);
  letter-spacing: -0.01em; }
:root[data-type="slab"] .quote p { font-weight: 300; }

/* running text. Grotesk sets its paragraphs at 11.5–12.5px in a wide mono;
   in a proportional face that size is too small to read, so every paragraph
   the page carries comes up to a reading size here, page-level ones too. */
:root[data-type]:not([data-type="reading"]) :is(.bk-sheet > p, .bk-sheet li, .ct-ask p, .ct-open-note, .specs-empty, .cal-empty,
  .cm-addr, .mi-desc, .tv, .dcard ul li) {
  font-family: var(--text); text-transform: none; letter-spacing: 0;
}
:root[data-type]:not([data-type="reading"]) :is(.tv, .dcard ul li, .mi-desc) { font-size: 15.5px; }
:root[data-type]:not([data-type="reading"]) :is(.prose p, .page-block p, .faq-a p, .faq-a ul, .faq-a ol, .step p, .dcard p, .modal-desc,
  .vs-sum, .cf-done p, .nf-why div) {
  font-family: var(--text); font-size: 16.5px; line-height: 1.65; letter-spacing: 0; text-transform: none;
}
:root[data-type]:not([data-type="reading"]) :is(.step p, .dcard p, .vs-sum) { font-size: 15.5px; }
/* The event description is sized in event.html for Martian Mono: 15px, and a
   62ch measure that in a proportional face runs to ~90 characters a line. */
:root[data-type]:not([data-type="reading"]) .ev-body { font-size: 17px; line-height: 1.65; max-width: 36em; }
:root[data-type]:not([data-type="reading"]) :is(.ph-rights, .bk-aside, .vkey-note, .vs-foot, .vmanifest, .cf-small, .cf-hint, .cm-fallback,
  .vkey small) {
  font-family: var(--text); font-size: 14px; line-height: 1.6; letter-spacing: 0; text-transform: none;
}

/* controls: nav, buttons, chips, links — the text face, mixed case */
:root[data-type]:not([data-type="reading"]) :is(.nav-links a, .burger, .spec-buy, .idx-foot a, .poster-link, .btn, .newsletter-form button, .skin-btn,
  .foot-col a, .ph-back, .ph-more, .ph-chip, .chip, .vjump a, .cmf-close, .cm-full, .skip-link,
  .arc-nav a, .arc-link, .hero-annot .desc a) {
  font-family: var(--text); text-transform: none; letter-spacing: 0.005em; font-weight: var(--ts-ui-w);
}
:root[data-type]:not([data-type="reading"]) .nav-links a { font-size: clamp(14px, 1.1vw, 16px); }
:root[data-type]:not([data-type="reading"]) :is(.spec-buy, .btn, .newsletter-form button, .skin-btn, .chip, .ph-chip, .vjump a, .idx-foot a, a.poster-link) { font-size: 15px; }
:root[data-type]:not([data-type="reading"]) .burger { font-size: 15.5px; }
:root[data-type]:not([data-type="reading"]) .foot-col a { font-size: 16.5px; font-weight: 500; }

/* one label style for every eyebrow, caption and field label */
:root[data-type]:not([data-type="reading"]) :is(.cap .src, .idx-cap .src, .ph-sub .idx, .tech-cap, .stat-label, .dcard .tag, .mdiv span,
  .crow-presenter, .spec-stamp, .spec-mark, .quote cite, .foot-col-title, .newsletter-label, .skin-picker .lbl,
  .menu a.menu-link em, .cms-banner span, .ph-src, .ph-cap .by, .wall-overlay .d, .holo-lb-credit,
  .ev-presenter, .modal-presenter, .ev-crumbs, .ev-more-h, .ev-flag, .board-cap, .fresh-cap span, .fresh-cap b,
  .ct-where-lbl, .bk-mail-label, .draft-flag, .vs-more, .rsvp-stamp, .nf-why b, .dform label, .cf label,
  .menu-foot, .knum, .ev-row-badge, .dl-badge, .bk-sheet li.kv > strong, .mi-cat) {
  font-family: var(--text); font-size: 12.5px; font-weight: var(--ts-label-w);
  letter-spacing: var(--ts-label-ls); text-transform: uppercase;
}
:root[data-type]:not([data-type="reading"]) .mi-cat { font-size: 14px; }

/* small print: sentences, so sentence case */
:root[data-type]:not([data-type="reading"]) :is(.newsletter-note, .foot-bottom, .ph-note, .menu-note, .hh p, .dl p, .cal-status, .foot-addr) {
  font-family: var(--text); font-size: 14px; letter-spacing: 0.005em; text-transform: none;
}

/* data: dates, times, prices, counts */
:root[data-type]:not([data-type="reading"]) :is(.spec-date, .crow-date, .crow-date b, .spec-meta, .crow-meta, .modal-meta span,
  .hours-row .time, .frow-date, .frow-meta, .board-row > span, .ev-date, .ev-spec div, .ev-more-when,
  .arc-list time, .arc-group > h2 .n, .spec-when, .ribbon-track span, .mi-price, .ph-pagenum, .holo-lb-count,
  .newsletter-form .newsletter-done b) {
  font-family: var(--mono); letter-spacing: 0.02em;
}
:root[data-type]:not([data-type="reading"]) :is(.spec-date, .crow-date, .spec-meta, .crow-meta, .modal-meta span, .frow-meta, .frow-date) { font-size: 13px; }
:root[data-type]:not([data-type="reading"]) :is(.spec-date, .crow-date, .crow-date b, .frow-date, .ev-date) { font-weight: var(--ts-data-w); }
:root[data-type]:not([data-type="reading"]) .mi-price { font-size: 16px; font-weight: var(--ts-data-w); }
:root[data-type]:not([data-type="reading"]) .ev-spec div { font-size: 13.5px; }
:root[data-type]:not([data-type="reading"]) .ev-spec dd { font-weight: var(--ts-data-w); }
:root[data-type]:not([data-type="reading"]) :is(.stat-num, .stat-num .cnt, .spec-num) { font-family: var(--disp); font-weight: var(--ts-head-w);
  letter-spacing: -0.02em; font-stretch: var(--ts-head-x); }

/* form fields: the text face, 16px so iOS does not zoom on focus */
:root[data-type]:not([data-type="reading"]) :is(.search-wrap input, .newsletter-form input, .dform input, .dform select, .dform textarea,
  .cf input, .cf textarea, .cf select, .cf-type select) {
  font-family: var(--text); font-size: 16px; letter-spacing: 0;
}

/* the happy-hour receipt drops its typewriter */
:root[data-type]:not([data-type="reading"]) .hh { font-family: var(--text); }

/* affectations the alternate sets read without: "##", "01 / Layout", "01" jump
   numbers and the cursor readout. dither.js marks the numbered tags. */
:root[data-type]:not([data-type="reading"]) .tech-cap i,
:root[data-type]:not([data-type="reading"]) .is-numbered,
:root[data-type]:not([data-type="reading"]) .vjump a span { display: none; }

/* ═════════ BACKGROUND SETS ═════════
   Picked in the footer under the type set. GLYPHS is the original and the
   default: it is the absence of data-field, and nothing in this section
   touches it. LEVELS, HATCH, RIPPLE, TOPO and STRATA (on trial from 14 Oct
   2026) redraw the same #field canvas from fields.js, loaded on demand; each
   has a figure of its own, so the plot ring and the crosshair go: they
   belong to Glyphs. Any data-field at all is a set other than Glyphs. NONE is nothing at all
   behind the page: no field, no plot chrome, no mark clusters under the
   cards. (Smoke went 3 Oct 2026, Halftone 14 Oct 2026.) The gap
   constellations under short event cards take each set's marks
   (SPARK_MARKS in dither.js). */

:root[data-field] .plot .ring,
:root[data-field] .plot .cross-h,
:root[data-field] .plot .cross-v { display: none; }
/* ORRERY and TRACE (on trial from 25 Oct 2026) draw their own
   orbits, which slide with the page. Their moving bodies are elements
   fields.js puts beside the canvas and the compositor turns (see
   placeBodies() there), shown only under their own set. */
.orrery { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; display: none; }
:root[data-field="orrery"] .orrery[data-for="orrery"],
:root[data-field="trace"] .orrery[data-for="trace"],
:root[data-field="sample"] .orrery[data-for="sample"] { display: block; }
.orb-sys { position: absolute; left: 0; top: 0; width: 1px; height: 1px; }
/* the box that spins about its orbit's centre; a body fades out over the wordmark */
.orb-body { position: absolute; transition: opacity 1.2s ease; }
.orb { position: absolute; inset: 0; display: block; border-radius: 50%; }
.orb-ring { box-sizing: border-box; border: 1px solid currentColor; }
.orb-dot { background: currentColor; }
/* SAMPLE's swimmers: each moved along its loop by its own animation, and
   pulsing as it goes, squeezing and stretching a little. */
.swim { position: absolute; left: 0; top: 0; }
.swim-pulse { position: relative; width: 100%; height: 100%; animation: swimPulse 1.8s ease-in-out infinite alternate; }
.swim svg { display: block; overflow: visible; }
@keyframes swimPulse { from { transform: scale(1, 1); } to { transform: scale(1.07, 0.92); } }
/* contorting: a squeeze one way, then the other, then a lean */
.swim-pulse.swim-squish { animation-name: swimSquish; animation-direction: normal; }
@keyframes swimSquish {
  0%, 100% { transform: scale(1, 1) skewX(0deg); }
  25%      { transform: scale(1.13, 0.88) skewX(7deg); }
  50%      { transform: scale(0.92, 1.1) skewX(-4deg); }
  75%      { transform: scale(1.05, 0.95) skewY(6deg); }
}
/* a thread at the back that flicks side to side about where it joins */
.swim-tail { position: absolute; inset: 0; animation: tailFlick 0.32s ease-in-out infinite alternate; }
@keyframes tailFlick { from { transform: rotate(-18deg); } to { transform: rotate(18deg); } }
@media (prefers-reduced-motion: reduce) { .swim-pulse, .swim-tail { animation: none; } }
:root[data-field="none"] #field,
:root[data-field="none"] .plot,
:root[data-field="none"] .sparks { display: none; }


/* ═════════ SHOW PREVIEWS ═════════
   The player from /_core/listen.js: 30-second clips by the acts on upcoming
   shows. One ruled row, built like the calendar's "Just announced" strip, so
   it can sit in a caption row or between two blocks without needing a space
   of its own. "Up next" opens the queue under it; nothing else grows.

   The bar lays itself out by the width it is GIVEN, not the screen's (a
   container query), because the same player sits in a full-width row on the
   calendar and in a narrow column in the event modal. */
.holo-listen { position: relative; container-type: inline-size; border: 1px solid var(--line);
  background: color-mix(in srgb, var(--paper) 72%, transparent); }
.holo-listen [hidden] { display: none !important; }
.holo-listen button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
.hl-bar { position: relative; display: grid; align-items: center; gap: 0 12px; padding: 7px 10px 8px 7px;
  grid-template-columns: 42px 42px minmax(0, 1fr) auto auto auto;
  grid-template-areas: "play art now skip tix more"; }
.hl-play { grid-area: play; width: 42px; height: 42px; display: grid; place-items: center;
  background: var(--ink) !important; color: var(--paper) !important; transition: background 0.15s; }
.hl-play svg { width: 15px; height: 15px; }
.hl-play:hover { background: var(--acc) !important; color: var(--acc-ink) !important; }
/* The cover is printed in ink until its clip plays — the plates do the same. */
.hl-art { grid-area: art; width: 42px; height: 42px; border: 1px solid var(--line); overflow: hidden;
  background: color-mix(in srgb, var(--ink) 8%, transparent); }
.hl-art img { display: block; width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) contrast(1.15); transition: filter 0.3s; }
.holo-listen.is-playing .hl-art img { filter: none; }
.hl-now { grid-area: now; min-width: 0; }
.hl-kick { font-family: var(--disp); font-weight: 800; font-size: 10px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--acc); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hl-line { display: flex; align-items: baseline; gap: 0.6em; min-width: 0; margin-top: 2px; }
.hl-artist { font-family: var(--disp); font-weight: 800; font-size: 15px; line-height: 1.3;
  text-transform: uppercase; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; flex: 0 1 auto; min-width: 0; color: inherit; }
a.hl-artist[href]:hover { color: var(--acc); }
.hl-song { font-size: 10.5px; letter-spacing: 0.04em; color: var(--mute); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; flex: 1 1 0; min-width: 4em; }
.hl-skip { grid-area: skip; display: flex; }
.hl-skip button { width: 36px; height: 40px; display: grid; place-items: center; transition: color 0.15s; }
.hl-skip svg { width: 13px; height: 13px; }
.hl-skip button:hover { color: var(--acc); }
.hl-tix { grid-area: tix; display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap;
  border: 1px solid var(--line); transition: background 0.15s, color 0.15s; }
.hl-tix:hover { background: var(--acc); border-color: var(--acc); color: var(--acc-ink); }
.hl-more { grid-area: more; min-height: 36px; padding: 0 4px; font-size: 10px !important;
  letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; transition: color 0.15s; }
.hl-more::after { content: ' \25BE'; }
.hl-more[aria-expanded="true"]::after { content: ' \25B4'; }
.hl-more:hover, .hl-more[aria-expanded="true"] { color: var(--acc); }
.holo-listen button:focus-visible, .holo-listen a:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
/* 30 seconds along the bottom rule, and a scrubber: click or drag along it.
   The line is 2px on the player's bottom border; the hit strip around it is
   18px (22px for a finger), 8px up into the bar's bottom padding — clear of
   the buttons — and the rest below the player, in the gap before whatever
   follows. --hl-y is the line's centre, up from the strip's bottom edge. */
.hl-prog { --hl-y: 10px; --hl-h: 2px; position: absolute; left: 0; right: 0; bottom: -10px; height: 18px;
  cursor: pointer; touch-action: pan-y; -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent; }
@media (pointer: coarse) { .hl-prog { --hl-y: 14px; bottom: -14px; height: 22px; } }
.hl-prog::before, .hl-prog i { content: ''; position: absolute; left: 0; right: 0;
  bottom: calc(var(--hl-y) - var(--hl-h) / 2); height: var(--hl-h); transition: height 0.12s, bottom 0.12s; }
.hl-prog i { display: block; background: var(--acc); transform-origin: left; transform: scaleX(0);
  transition: transform 0.26s linear, height 0.12s, bottom 0.12s; }
/* Under the pointer, focused or held: the whole length shows, and it thickens. */
@media (hover: hover) { .hl-prog:hover { --hl-h: 4px; } }
.hl-prog:focus-visible, .is-scrubbing .hl-prog { --hl-h: 4px; }
@media (pointer: coarse) { .is-scrubbing .hl-prog { --hl-h: 6px; } }
@media (hover: hover) { .hl-prog:hover::before { background: color-mix(in srgb, var(--ink) 18%, transparent); } }
.hl-prog:focus-visible::before, .is-scrubbing .hl-prog::before { background: color-mix(in srgb, var(--ink) 18%, transparent); }
.is-scrubbing .hl-prog i { transition: height 0.12s, bottom 0.12s; }   /* follows the pointer, no lag */
.hl-prog:focus { outline: none; }
.hl-prog:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

/* The queue. */
.hl-list { position: relative; z-index: 1; border-top: 1px solid var(--line); }   /* over the scrub strip's lower half */
/* Site-wide, the queue drops down over what is below rather than pushing the
   listings down the page; it closes like a menu. */
.holo-listen--site .hl-list { position: absolute; z-index: 30; top: 100%; left: -1px; right: -1px; max-height: min(60vh, 460px);
  overflow-y: auto; border: 1px solid var(--line); background: var(--paper);
  box-shadow: 0 10px 24px color-mix(in srgb, var(--ink) 16%, transparent); }
.hl-rows { list-style: none; margin: 0; padding: 0; }
.hl-row { width: 100%; display: grid; grid-template-columns: 32px minmax(0, auto) minmax(0, 1fr) auto;
  align-items: center; gap: 0 12px; padding: 5px 10px 5px 12px; text-align: left;
  border-bottom: 1px dashed color-mix(in srgb, var(--ink) 18%, transparent) !important; transition: background 0.15s; }
.hl-rows li:last-child .hl-row { border-bottom: 0 !important; }
.hl-row:hover { background: color-mix(in srgb, var(--ink) 6%, transparent) !important; }
.hl-row:hover .hl-row-who { color: var(--acc); }
/* The clip that is playing (or cued): a bar down its left edge and the
   accent on its name, so it stays marked once the pointer moves off. */
.hl-row.is-now { background: color-mix(in srgb, var(--acc) 12%, transparent) !important; box-shadow: inset 4px 0 0 var(--acc); }
.hl-row.is-now .hl-row-who { color: var(--acc); }
.hl-row-art { width: 32px; height: 32px; overflow: hidden; background: color-mix(in srgb, var(--ink) 8%, transparent); }
.hl-row-art img { display: block; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.15); }
.hl-row-who { font-family: var(--disp); font-weight: 800; font-size: 13px; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 0.15s; }
.hl-row-song { font-size: 10px; color: var(--mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hl-row-when { font-family: 'Doto', var(--mono); font-weight: 900; font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--mute); white-space: nowrap; }
.hl-all { display: block; width: 100%; padding: 8px 12px; text-align: left; font-size: 10px !important;
  letter-spacing: 0.14em; text-transform: uppercase; border-top: 1px solid var(--line) !important; }
.hl-all:hover { color: var(--acc); }
.hl-links { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; padding: 8px 12px 9px;
  border-top: 1px solid var(--line); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; }
.hl-links span { color: var(--mute); }
.hl-links a { text-decoration: underline; text-underline-offset: 3px; }
.hl-links a:hover { color: var(--acc); }
.hl-links .hl-pl { flex-basis: 100%; }
.hl-links:empty { display: none; }

/* Site-wide (home, calendar): a capsule with a second line, in the accent,
   just inside its edge — a control, not one more ruled box among the strips
   around it. The frame moves onto the bar, which is all of this player that
   takes up room (its queue drops over the page), so the bar's own container
   query can round it less when it runs to two lines: a full capsule's ends
   would cut into the buttons. The progress line keeps to the flat run of the
   bottom edge. */
.holo-listen--site { border: 0; background: none; }
.holo-listen--site .hl-bar { --hl-r: 999px; border: 1px solid var(--line); border-radius: var(--hl-r);
  background: color-mix(in srgb, var(--paper) 72%, transparent); padding: 7px 14px 8px 8px; }
.holo-listen--site .hl-bar::after { content: ''; position: absolute; inset: 3px; pointer-events: none;
  border: 1.5px solid var(--acc); border-radius: calc(var(--hl-r) - 3px); }
.holo-listen--site .hl-play { border-radius: 50%; }
.holo-listen--site .hl-art { border-radius: 6px; }
.holo-listen--site .hl-prog { left: 24px; right: 24px; }
.holo-listen--site .hl-list { top: calc(100% + 6px); border-radius: 18px; }

/* Not started: what the player holds — a fan of covers, a line saying how
   many acts — and a bigger play button in the accent. No single act, no
   tickets, nothing to scrub; the queue button reads "See all". */
.holo-listen.is-idle .hl-bar { grid-template-columns: 48px auto minmax(0, 1fr) auto;
  grid-template-areas: "play stack now more"; }
.is-idle .hl-play { width: 48px; height: 48px; background: var(--acc) !important; color: var(--acc-ink) !important; }
.is-idle .hl-play svg { width: 17px; height: 17px; margin-left: 2px; }
.is-idle .hl-play:hover { background: var(--ink) !important; color: var(--paper) !important; }
.is-idle .hl-art, .is-idle .hl-skip, .is-idle .hl-kick, .is-idle .hl-line, .is-idle .hl-prog { display: none; }
.hl-pitch { display: none; }
.is-idle .hl-pitch { display: block; min-width: 0; }
.hl-head { display: block; font-family: var(--disp); font-weight: 800; font-size: 20px; line-height: 1.15;
  text-transform: uppercase; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hl-count { display: block; margin-top: 2px; font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hl-stack { grid-area: stack; display: none; padding-left: 12px; }
.is-idle .hl-stack { display: flex; }
.hl-stack img { display: block; width: 34px; height: 34px; margin-left: -12px; border-radius: 50%; object-fit: cover;
  border: 2px solid var(--paper); background: var(--paper); }

/* Narrow (a phone, a narrow modal): two lines. The clip on the first, with
   the whole width for the names; the controls on the second — skip on the
   left under the play button, tickets and the queue on the right. */
@container (max-width: 560px) {
  .hl-bar { grid-template-columns: 42px 42px minmax(0, 1fr) auto auto;
    grid-template-areas: "play art now now now" "skip skip . tix more"; row-gap: 4px; }
  /* Not started, it stays one short row — on the calendar it sits between
     the title and the listings, and every line of it is calendar not seen. */
  .holo-listen.is-idle .hl-bar { grid-template-columns: 42px minmax(0, 1fr) auto;
    grid-template-areas: "play now more"; padding: 5px 10px 5px 6px; column-gap: 8px; }
  .is-idle .hl-play { width: 42px; height: 42px; }
  .is-idle .hl-stack, .hl-long { display: none; }
  .hl-head { font-size: 14px; }
  /* Tighter tracking so "[See tracks ▾]" leaves the two lines whole on a
     375px phone. */
  .is-idle .hl-more { letter-spacing: 0.06em; }
  .hl-count { letter-spacing: 0.03em; }
  .holo-listen--site:not(.is-idle) .hl-bar { --hl-r: 22px; }
  .hl-skip { margin-left: -6px; }
  .hl-tix, .hl-more { min-height: 32px; }
  .hl-row { grid-template-columns: 32px minmax(0, 1fr) auto; }
  .hl-row-song { grid-column: 2; grid-row: 2; margin-top: -2px; }
  .hl-row-art { grid-row: span 2; }
  .hl-row-when { grid-row: span 2; }
}
/* The same two lines for a browser with no container queries (Chrome before
   105), going by the screen instead — and always in the modal, whose column is
   narrow at every width. Without it the one-row bar ran ~80px past a phone's
   edge, and on Android that widened the page, so the event modal opened wider
   than the screen. Keep in step with the block above. */
@supports not (container-type: inline-size) {
  @media (max-width: 860px) {
    .hl-bar { grid-template-columns: 42px 42px minmax(0, 1fr) auto auto;
      grid-template-areas: "play art now now now" "skip skip . tix more"; row-gap: 4px; }
    .holo-listen.is-idle .hl-bar { grid-template-columns: 42px minmax(0, 1fr) auto;
      grid-template-areas: "play now more"; padding: 5px 10px 5px 6px; column-gap: 8px; }
    .is-idle .hl-play { width: 42px; height: 42px; }
    .is-idle .hl-stack, .hl-long { display: none; }
    .hl-head { font-size: 14px; }
    .is-idle .hl-more { letter-spacing: 0.06em; }
    .hl-count { letter-spacing: 0.03em; }
    .holo-listen--site:not(.is-idle) .hl-bar { --hl-r: 22px; }
    .hl-skip { margin-left: -6px; }
    .hl-tix, .hl-more { min-height: 32px; }
    .hl-row { grid-template-columns: 32px minmax(0, 1fr) auto; }
    .hl-row-song { grid-column: 2; grid-row: 2; margin-top: -2px; }
    .hl-row-art { grid-row: span 2; }
    .hl-row-when { grid-row: span 2; }
  }
  .modal-listen .hl-bar { grid-template-columns: 42px 42px minmax(0, 1fr) auto auto;
    grid-template-areas: "play art now now now" "skip skip . tix more"; row-gap: 4px; }
  .modal-listen .hl-skip { margin-left: -6px; }
  .modal-listen .hl-tix, .modal-listen .hl-more { min-height: 32px; }
  .modal-listen .hl-row { grid-template-columns: 32px minmax(0, 1fr) auto; }
  .modal-listen .hl-row-song { grid-column: 2; grid-row: 2; margin-top: -2px; }
  .modal-listen .hl-row-art { grid-row: span 2; }
  .modal-listen .hl-row-when { grid-row: span 2; }
}

/* Where it sits. Home: under "Full calendar" and the poster, whose row's
   padding is the gap above it. Below it, extra room over .stairs' own
   padding, so the big menu words keep a cushion. */
.idx-listen:not([hidden]) { display: block; margin: 0 var(--pad) clamp(1.4rem, 4vh, 2.4rem); }
.cal-listen:not([hidden]) { display: block; margin: 0 var(--pad) 1.1rem; }
@media (max-width: 700px) { .cal-listen:not([hidden]) { margin-bottom: 1.2rem; } }
.ev-listen:not([hidden]) { display: block; margin: 1.4rem 0 1.2rem; max-width: 640px; }
.modal-listen:not([hidden]) { display: block; margin: 0 0 1rem; }
/* Above the plates and their sparks, so the dropped-down queue is on top. */
.idx-listen:not([hidden]), .cal-listen:not([hidden]) { position: relative; z-index: 40; }
