/*
 * Daily Scales — dailyscales.com
 *
 * The site is the app's About page grown to a full sheet. Every value here
 * is lifted from the app (Theme.swift, the icon): the ivory paper, the
 * icon's ink, the sun's brass, Literata. The page commits to the printed
 * page's world; the one dark passage is the app's own console ground.
 */

:root {
  --paper: #f6f1e5;
  --paper-deep: #efe8d8;
  --card: #fbf8f0;
  --ink: #241e16;
  --ink-soft: #6b6152;
  --stroke: rgba(36, 30, 22, 0.15);
  --stroke-strong: rgba(36, 30, 22, 0.32);
  /* The sun's dark stop, deepened until it actually clears AA on paper. At
     the icon's own #cb7d14 this is 2.6:1 and at #a86708 it is 4.04:1, both
     of which fail body text; #945a06 is 5.0:1 on the page and 5.3:1 on the
     card. It carries every link on the site, so it is not a place to be
     faithful to the icon. --brass-bright below is the icon's value and stays
     where it is only tinting shapes. */
  --brass: #945a06;
  --brass-bright: #cb7d14; /* the icon's sun top */
  --sun: #f7b93e;          /* the icon's sun bottom */
  --panel: #1c1712;
  --panel-raised: #262019;
  --cream: #efe7d6;
  --cream-faint: #a89b85;
  --cream-stroke: rgba(239, 231, 214, 0.16);
  /* ScoreAccidentals first: it carries three characters and nothing else, so
     every other character falls straight through to Literata. See the
     @font-face note below for why it is its own family and not a fifth cut
     of Literata's. */
  --serif: "ScoreAccidentals", "Literata", "Iowan Old Style", Georgia, serif;
}

@font-face {
  font-family: "Literata";
  src: url("fonts/Literata-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("fonts/Literata-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("fonts/Literata-Italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("fonts/Literata-BoldItalic.woff2") format("woff2");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

/* Literata carries no ♭ ♮ ♯, so "Clarinet (B♭)" fell out of the page's face
   and out of its spacing. The app draws those three from a cut of Bravura
   (Fonts/ScoreAccidentals.otf) and folds the text codepoints onto SMuFL's;
   scripts/assets.py packs the same file as woff2 with the plain codepoints
   mapped, and the stack in --serif puts it in front of Literata.

   Its own family, and not a fifth @font-face on "Literata". It was one, at
   `font-weight: 400 700`, and that range tied with the real Bold cut for
   every bold upright run on the site — so the browser picked this file for
   the heading "Watch the days add up", found no glyph for any letter in it,
   and fell past Literata to the system serif. Bold upright text on the whole
   site was drawn in Iowan Old Style, and Literata-Bold.woff2 was never once
   requested. A separate family cannot collide with a weight, because the
   fallback runs per character: ♭ ♮ ♯ here, everything else in Literata.

   A SMuFL em is four staff spaces and a flat stands about two and a half of
   them, so the glyph arrives near cap height. The 62% it used to be shrank it
   to a speck under the baseline, and "Dorian ♭2" read as "Dorian ,2". */
/* Steinberg's companion face to the Petaluma music font, bundled with the app
   under the OFL and paired with it there (MusicFont.textFont): a hand-copied
   music font wants hand-copied text. One use on the site, the chord symbols
   over the jazz plate. Packed from the app's own PetalumaScript.otf by
   scripts/assets.py. */
@font-face {
  font-family: "Petaluma Script";
  src: url("fonts/PetalumaScript.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "ScoreAccidentals";
  src: url("fonts/ScoreAccidentals.woff2") format("woff2");
  unicode-range: U+266D-266F;
  size-adjust: 100%;
  ascent-override: 88%;
  descent-override: 24%;
}

* { box-sizing: border-box; }

html {
  background: var(--paper);
  scroll-behavior: smooth;
}

body {
  margin: 0;
  /* Short pages (support, privacy, 404) would otherwise leave the footer
     stranded halfway up the window. */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  /* No text-rendering: optimizeLegibility. It buys nothing Literata does not
     already do through normal shaping, and it blocks first paint on the whole
     text of the page in some engines. */
}
/* The skip link's target takes focus itself, or the browser moves the
   viewport and leaves the keyboard where it was. */
main:focus { outline: none; }

h1, h2, h3 { margin: 0; line-height: 1.18; text-wrap: balance; }
/* No paragraph on the page is long enough to afford a one-word last line. */
p { margin: 0 0 1rem; text-wrap: pretty; }
/* The prices and the instrument count sit in running text and never change,
   but the engraved fingerings under the demo are digits set at small sizes;
   lining them up costs nothing and stops the odd 1 from looking loose. */
.score-label, .pricenote, .buyonce .lines {
  font-variant-numeric: tabular-nums;
}
a { color: var(--brass); text-decoration-thickness: 1px; text-underline-offset: 2px; }
/* The ring follows whatever shape it is on: forcing a 2px radius put a
   near-rectangle around every pill. Links, which have no radius of their
   own, get a small one so the ring has corners rather than points. */
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--brass);
  outline-offset: 3px;
}
a:focus-visible { border-radius: 3px; }
img, svg, video { max-width: 100%; height: auto; }

/* For text only assistive tech should hear: the picker's status line, which
   says what the engraving changed to. Not `display: none` — that would take
   it out of the accessibility tree, which is the one place it exists. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The chooser is forty tab stops in from the top of the page. */
.skip {
  position: absolute;
  left: 1.25rem;
  top: -4rem;
  z-index: 10;
  background: var(--ink);
  color: var(--paper);
  border-radius: 0 0 8px 8px;
  padding: 0.7rem 1.1rem;
  text-decoration: none;
  transition: top 140ms ease;
}
.skip:focus { top: 0; }

/* Longhands only on .sheet and the section classes below: a `padding:`
   shorthand on either side silently zeroes the other's slots. */
.sheet { max-width: 62rem; margin: 0 auto; padding-left: 1.25rem; padding-right: 1.25rem; }
/* body is a flex column, and a flex item with auto cross-axis margins is
   sized to its content rather than stretched — so a section holding the
   chooser filled the sheet while a section holding only a 40rem measure came
   out narrower, and the page's left edge stepped in and out between them.
   This used to be patched with a hand-kept list of sections allowed to take
   the full width, which every new section had to be added to. One line
   instead, and the sections inside <main> are ordinary blocks again. */
body > * { width: 100%; }
.prose { max-width: 40rem; }
.prose.centered { margin-left: auto; margin-right: auto; text-align: center; }

/* The wide measure. Two sections take it: the picker, whose content is a
   staff and forty chips and wants every pixel, and the practice grid, which
   is the widest thing on paper. A page set at one measure from top to bottom
   is a page nobody finishes. */
.sheet.wide { max-width: 74rem; }

/* ------------------------------------------------------------ the spine */

/* The section label moves out of the text block and into a tracked column
   to its left — the same gutter the chooser sets its families in, the
   tallies set their numbers in and the colophon sets "MADE BY" in. It puts
   a spine down the page, and it uses the width that a 40rem column pinned
   inside a 62rem sheet was leaving empty on the right.
   Three sections carry it. The ones between them are set differently on
   purpose: a spine you can see is one that something else can break. */
.gutter {
  display: grid;
  /* wide enough for the longest label on the page, "FOR JAZZ PLAYERS", on
     one line at the eyebrow's tracking */
  grid-template-columns: 9.5rem minmax(0, 1fr);
  column-gap: 2.5rem;
  row-gap: 0; /* the children keep their own margins */
}
.gutter > * { grid-column: 2; }
.gutter > .eyebrow {
  grid-column: 1;
  grid-row: 1;
  margin-bottom: 0;
  padding-top: 0.5rem; /* onto the heading's first baseline, not its box */
  text-align: right;
}

/* Uppercase tracked label, mirroring the app's credits gutter. */
.eyebrow {
  display: block;
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1rem;
}

h2 {
  font-size: clamp(1.6rem, 3.5vw, 2.1rem);
  font-weight: 700;
  font-style: italic;
  margin-bottom: 1.1rem;
}

section { padding-top: 4.5rem; padding-bottom: 4.5rem; }

/* Density is a rhythm, not a setting. Every section at 4.5rem meant nothing
   on the page was ever quieter or louder than anything else, and nine
   identical intervals is the definition of a page that is boring to scroll.
   The two panels are the loudest and get the most air; the practice grid
   and the catalog are the workhorses and are set tight; the close is given
   the most room on the page, because it is the one section people screenshot
   and send to each other. */
#catalog { padding-top: 3.75rem; padding-bottom: 3.75rem; }
.band { padding-top: 7rem; padding-bottom: 7rem; }
#practice { padding-top: 4rem; padding-bottom: 4rem; }
.changes { padding-top: 0; padding-bottom: 6.5rem; }
#teachers { padding-top: 5rem; padding-bottom: 4rem; }

/* The plate ornament between sections: two hairlines and a lozenge, the
   separator an engraver would cut under a title. It is drawn at its own
   size and never stretched, so the hairline stays one pixel and the
   lozenge stays a lozenge. (The swelled rule this replaced was a curve
   squeezed into a 6px box, which read as a smudge.) */
.ornament { display: flex; justify-content: center; }
.ornament svg { display: block; }
.ornament rect { fill: var(--ink); opacity: 0.28; }
/* The lozenge carries the eye; the hairlines only lead to it, so it is set
   more than twice their weight. */
.ornament path { fill: var(--ink); opacity: 0.7; }
.panel .ornament rect, .panel .ornament path { fill: var(--cream); }

/* ----------------------------------------------------------------- bar */

/* Every page opens with it: the mark and the name at the left, where an
   off-centre logo is what a reader expects one to be, the two pages worth
   going to, and the ask. It replaced the title page in Sep 2026 (see the
   hero below); build.py's topbar() writes it, the same on every page. */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 1.1rem;
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  color: var(--ink);
  font-weight: 700;
  font-style: italic;
  font-size: 1.25rem;
  letter-spacing: 0.005em;
}
.topbar .links {
  display: flex;
  align-items: center;
  gap: 1.6rem;
  font-size: 0.95rem;
}
.topbar .links a:not(.appstore) {
  color: var(--ink-soft);
  text-decoration: none;
  transition: color 140ms ease;
}
.topbar .links a:not(.appstore):hover,
.topbar .links a[aria-current] { color: var(--ink); }
.appstore.mini { padding: 0.5rem 1rem; font-size: 0.85rem; border-radius: 9px; }
/* On a phone the bar keeps the brand and the ask; the footer has the pages. */
@media (max-width: 640px) {
  .topbar .links { gap: 1rem; }
  .topbar .links a:not(.appstore) { display: none; }
  .appstore.mini .long { display: none; }
}

/* The sunrise mark, choreographed like the app's own load (SunriseMark
   rise 0 -> 1 over 2.4s): the five lines start as a staff and draw in to
   their bars of water, then the sun comes up, ember on the horizon and
   warming into the golds as it clears. The markup's attribute values are
   the finished logo, so with animations off (reduced motion, no CSS
   animation support) only the finished mark is drawn. It used to be 92px
   on the title page and is 30px in the bar; the choreography is the same,
   clipped to its box so the staff lines it starts from do not cross the
   name beside it. Only the landing page's mark rises (build.py gives it
   .rise): a logo that performs on every page a reader lands on is a tic. */
.mark { display: flex; justify-content: center; }
.mark svg { display: block; overflow: hidden; }
/* In the bar. The close's standing mark (.mark.still, further down) keeps
   its own 64px. */
.brand .mark { width: 30px; height: 31px; }
.brand .mark svg { width: 100%; height: 100%; }
/* The icon's own water ink, not the site ink: #433625 sampled from the
   1024 AppIcon.png, like the sun stops below. */
.mark .bar { fill: #433625; }
/* The painted waterline (see src/icons/mark.svg): the page's own paper,
   hiding the half of the sun that is below the horizon. */
.mark .horizon { fill: var(--paper); }

/* Its own compositing layer, so the rise is not re-rasterized per frame. */
.mark.rise .sun {
  animation: mark-rise 1.4s cubic-bezier(0.4, 0, 0.2, 1) 1.35s both;
  will-change: transform;
}
@keyframes mark-rise {
  from { transform: translateY(280px); }
  to { transform: translateY(0); }
}
/* warmth = sun^2 in the app: the gold arrives late, ease-in quad. */
.mark.rise .sunstop-top {
  animation: mark-warm-top 1.4s cubic-bezier(0.55, 0.085, 0.68, 0.53) 1.35s both;
}
.mark.rise .sunstop-bottom {
  animation: mark-warm-bottom 1.4s cubic-bezier(0.55, 0.085, 0.68, 0.53) 1.35s both;
}
@keyframes mark-warm-top {
  from { stop-color: #943819; }
  to { stop-color: #d1a03a; }
}
@keyframes mark-warm-bottom {
  from { stop-color: #de6b29; }
  to { stop-color: #f4cc6e; }
}

/* Each line begins a beat after the one above it (the app's 0.07-per-bar
   ripple on a 2.4s master). All start as the same 880-wide staff line. */
.mark.rise .bar { animation: 0.95s cubic-bezier(0.4, 0, 0.2, 1) both; }
.mark.rise .bar-1 { animation-name: mark-settle-1; animation-delay: 0.40s; }
.mark.rise .bar-2 { animation-name: mark-settle-2; animation-delay: 0.57s; }
.mark.rise .bar-3 { animation-name: mark-settle-3; animation-delay: 0.74s; }
.mark.rise .bar-4 { animation-name: mark-settle-4; animation-delay: 0.91s; }
.mark.rise .bar-5 { animation-name: mark-settle-5; animation-delay: 1.08s; }
@keyframes mark-settle-1 { from { x: 72px; width: 880px; } }
@keyframes mark-settle-2 { from { x: 72px; width: 880px; } }
@keyframes mark-settle-3 { from { x: 72px; width: 880px; } }
@keyframes mark-settle-4 { from { x: 72px; width: 880px; } }
@keyframes mark-settle-5 { from { x: 72px; width: 880px; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .mark.rise .sun, .mark.rise .bar, .mark.rise .sunstop-top, .mark.rise .sunstop-bottom { animation: none; }
}

/* ---------------------------------------------------------------- hero */

/* The claim and the proof. The copy is a left-set block of at most 36rem;
   the device takes a fixed 28rem column, so its height sets the hero's and
   the copy centres against it. Measured at 1194x834, an iPad in landscape,
   which is the device this app is sold for: the badge sits near 590 and the
   picker's eyebrow near 820, so the first screen carries the ask, the whole
   iPad and the start of the engraving. Without the screenshot file
   (build.py's {{hero-screenshot}}) the copy stands alone. */
.hero { padding-top: 3rem; padding-bottom: 1rem; }
.hero:has(.hero-device) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 28rem;
  gap: 4rem;
  align-items: center;
}
.hero-copy { max-width: 36rem; }
.headline {
  font-size: clamp(2.2rem, 4.4vw, 3.5rem);
  font-weight: 700;
  font-style: italic;
  line-height: 1.08;
  letter-spacing: -0.005em;
  margin: 0 0 1.4rem;
}
.hero-support {
  color: var(--ink-soft);
  font-size: 1.12rem;
  max-width: 32em;
  margin: 0 0 1.2rem;
}
/* The credential, small and directly under the claim it backs. The full
   colophon is further down; this is only the reason to keep reading. */
.hero-credit {
  font-size: 0.92rem;
  font-style: italic;
  color: var(--ink-soft);
  margin: 0 0 1.6rem;
}
.hero .pricenote { margin-top: 0.9rem; }

.appstore {
  display: inline-block;
  font-size: 0.92rem;
  font-style: normal;
  background: var(--ink);
  color: var(--paper);
  border-radius: 10px;
  padding: 0.7rem 1.4rem;
  text-decoration: none;
  transition: background-color 140ms ease,
              scale 140ms cubic-bezier(0.2, 0, 0, 1);
}
.appstore:hover { background: #14110c; }
.appstore:active { scale: 0.96; }
a.appstore:focus-visible { outline-offset: 4px; }
.appstore .pre {
  display: block;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-faint);
  /* Tracking adds a space after the last letter too, which drags a centred
     line off centre by half of it. Pull it back. */
  margin-right: -0.14em;
}

.pricenote {
  font-size: 0.92rem;
  color: var(--ink-soft);
  margin: 1rem 0 0;
}

/* The spread. With the screenshot present (build.py's {{hero-screenshot}})
   the hero is two columns: the title page and, facing it, the app held
   portrait, the way it stands on a music stand. The card keeps its own
   measure and centers in its column; the device takes a fixed column, so
   its height rather than the copy's sets the spread's. 28rem is the widest
   the device can be and still leave the card its full 42.5rem at 1194
   (the iPad landscape fold, see .hero above). Without the figure the header
   is the centered title page it always was. */
.hero:has(.hero-device) {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 28rem;
  gap: 3rem;
  align-items: center;
}
.hero-device { margin: 0; width: 100%; justify-self: center; }
/* The bezel is CSS rather than baked pixels (unlike the band videos, whose
   art has an edge highlight that pokes past any radius): a flat still takes
   a radius cleanly. Near-black with the paper's warmth in it, an inner
   hairline where the glass meets the bezel, and no drop shadow — the site
   frames things with rules, not shadows. */
.hero-device .device {
  background: #15120e;
  border-radius: 26px;
  padding: 11px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
.hero-device img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 16px;
}
/* One column once the card would drop under 30rem beside the device. The
   device stacks below the badge, never between the headline and it: on a
   portrait iPad the price line is still the last thing before the picture. */
@media (max-width: 960px) {
  .hero:has(.hero-device) { grid-template-columns: 1fr; gap: 2.25rem; }
  .hero-device { max-width: 22rem; }
}


/* The device. The bezel is CSS rather than baked pixels (unlike the band
   videos, whose art has an edge highlight that pokes past any radius): a
   flat still takes a radius cleanly. Near-black with the paper's warmth in
   it, an inner hairline where the glass meets the bezel, and no drop shadow:
   the site frames things with rules, not shadows. */
.hero-device { margin: 0; width: 100%; justify-self: center; }
.hero-device .device {
  background: #15120e;
  border-radius: 26px;
  padding: 11px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.07);
}
.hero-device img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 16px;
}
/* One column once the copy would drop under 30rem beside the device. The
   device stacks below the price line, never between the headline and the
   badge; centred, since a left-set picture under a left-set block reads as
   a dropped column rather than a picture. */
@media (max-width: 960px) {
  .hero:has(.hero-device) { grid-template-columns: 1fr; gap: 2.5rem; }
  .hero-device { max-width: 24rem; }
}

/* ------------------------------------------------------------ the ask */

/* One call to action, repeated where a reader has just been given a reason:
   the hero, the end of the picker, the foot of the band panel, and the close.
   The band's went away while that panel was a heading and a paragraph on
   black, because an ask standing under nothing is the weakest of the four,
   and came back with the console screenshot. The picker's is still the badge
   alone: a line explaining what the reader has just watched happen would read
   as a lack of nerve. */
.cta {
  margin-top: 2.25rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--stroke);
  text-align: center;
}
/* The ask on the dark ground is the same button inverted: ink on paper is
   invisible against the console. Cream carries the panel's own colour back
   into it, and the tracked "DOWNLOAD ON" needs its own tone, because the one
   it wears on ink is a light grey that would sit at 1.3:1 on cream. */
.panel .cta { border-top-color: var(--cream-stroke); }
.panel .appstore { background: var(--cream); color: var(--panel); }
.panel .appstore:hover { background: #f7f2e6; }
.panel .appstore .pre { color: #5a5145; } /* 6.3:1 on the cream */

/* ------------------------------------------------------------- engraving */

/* The head is set two-up: the heading takes the left half at display size
   and the paragraph settles beside it, its last line on the heading's
   baseline. At the section's own 74rem a 40rem paragraph pinned left would
   have left a third of the page empty above the widest thing on it. */
.picker-section {
  padding-top: 3.5rem;
  padding-bottom: 5rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 3.5rem;
}
.picker-section > * { grid-column: 1 / -1; }
.picker-section > .eyebrow { grid-column: 1; grid-row: 1; }
.picker-section > h2 {
  grid-column: 1;
  grid-row: 2;
  font-size: clamp(1.8rem, 4vw, 2.7rem);
  margin-bottom: 1.6rem;
}
.picker-section > .prose {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: end;
  max-width: none;
  margin-bottom: 1.6rem;
}

.score-sheet {
  background: var(--card);
  border: 1px solid var(--stroke);
  border-radius: 12px;
  /* Verovio leaves a little air under the last stem; the sheet's own bottom
     padding is trimmed to match so the staff sits optically centred rather
     than measured centred. */
  padding: 1.6rem 2.25rem 1.1rem;
  color: var(--ink); /* the engraving inherits this as currentColor */
  /* Each engraving keeps its own height (the piano's grand staff is twice a
     single staff), so the sheet grows and shrinks between choices. site.js
     pins the height in pixels for the swap; this eases it. */
  transition: height 260ms cubic-bezier(0.4, 0, 0.2, 1), opacity 140ms ease;
}
/* The engraving, not every drawing in the sheet: unscoped, this took the
   dice in the head as well and set an 18px icon to 576. */
.score-sheet .engraving svg { width: 100%; height: auto; }
.score-sheet.loading { opacity: 0.55; }
/* It is a tab stop because it scrolls sideways on a phone; give the ring the
   sheet's own corners rather than the default square. */
.score-sheet:focus-visible { outline: 2px solid var(--brass); outline-offset: 3px; }
/* The new engraving fades up under the old one's departing height, so the
   swap reads as a page turning rather than a flicker. site.js adds
   `settled` a frame after it writes the markup. */
#score-slot {
  opacity: 0.001;
  transition: opacity 220ms cubic-bezier(0.2, 0, 0, 1);
}
#score-slot.settled { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  #score-slot { opacity: 1; transition: none; }
}

/* The heading the app engraves onto the page, set here in the page's own
   type instead: it reads better at web sizes, and the engraved one landed on
   top of the sax's first bis mark. */
/* The name of the page on the left, the dice on the right. Centred rather
   than baselined: a 40px pill hung off a 17px line of type sets the row by
   its own top edge, and the label then reads as sitting high in it. */
.score-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  margin-bottom: 0.4rem;
}
.score-label {
  margin: 0;
  font-weight: 700;
  font-size: 1.02rem;
}
.score-label .for {
  display: inline-block;
  margin-left: 0.7rem;
  font-weight: 400;
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  vertical-align: 0.08em;
}
/* The rhythm sits beside the name rather than inside it, because that is
   where the app keeps it: a page's heading says what is being played, not
   what it is written as. Set in the catalog's full words ("8th + Two 16ths"),
   so the chip's short name has somewhere to be spelled out. */
.score-label .beat {
  font-weight: 400;
  color: var(--ink-soft);
}
.score-label .beat::before {
  content: "·";
  margin: 0 0.45rem;
  color: var(--stroke-strong);
}
/* Verovio hands out font-family="Times, serif" and has no option for the
   drawn text face; the app overrides it in NotationShell.html and the site
   does the same here. Literata carries no circled digits, so the guitar's
   string numbers fall through to the next serif, exactly as in the app. */
.score-sheet svg text, .score-sheet svg tspan { font-family: var(--serif); }
/* The narrow layout lives at the foot of this file; see the note there. */

/* The pattern row: its own gutter label, matching the chooser's.

   The two rows are one control, so they sit closer to each other than the
   pair sits to the sheet above and the chooser below. They used to read the
   other way round: 22px of air above, 40px between them, 18px below, which
   set them as two strays rather than as two axes of the same choice.

   The 1px is the chooser's border. Its label column starts inside it, so a
   flat 1.25rem here put these labels and chips one pixel to the left of the
   identical ones in the card — the spine visibly kinks at the card's edge. */
/* Three columns: the gutter label, the chips, and what the row is not
   showing. The last one is beside the chips rather than under them because
   there is half a sheet of empty paper to the right of every one of these
   rows, and because "Major, Melodic Minor, Blues, Chromatic, and 31 others"
   is the sentence the row is already making. */
.picker-row {
  display: grid;
  grid-template-columns: 5.5rem auto 1fr;
  gap: 0.9rem;
  align-items: baseline;
  margin: 1.9rem 0 0;
  padding-left: calc(1.25rem + 1px);
}
.picker-row + .picker-row { margin-top: 1.25rem; }
/* What the row is not showing, under the chips and in the chips' own column.
   Quiet: it is a footnote to a control, and set at chip weight it would read
   as a thirteenth thing to press. The brass arrives on hover, where every
   other link on the page keeps it. */
.row-more {
  justify-self: start;
  font-size: 0.82rem;
  color: var(--ink-soft);
  text-decoration: underline;
  text-decoration-color: var(--stroke);
  text-underline-offset: 3px;
  transition: color 120ms ease, text-decoration-color 120ms ease;
}
.row-more:hover { color: var(--brass); text-decoration-color: var(--brass); }
.picker-label {
  font-size: 0.64rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: right;
}
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;   /* the size Apple's own guidance asks a finger for */
  font-family: var(--serif);
  font-size: 0.92rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--stroke-strong);
  border-radius: 999px;
  padding: 0.3rem 1.05rem;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease,
              scale 120ms cubic-bezier(0.2, 0, 0, 1);
}
.chip:hover { background: var(--paper-deep); }
.chip:active { scale: 0.96; }
.chip[aria-checked="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* The rhythm row. The app names the scale and the pattern in words and draws
   the rhythm, so this row is a cell rather than a pill: the app's own glyph
   over the app's own short name, at the app's own proportions (96x44). The
   glyph is drawn in currentColor, so it turns to paper when the chip is
   chosen along with the name under it. */
.chips.drawn .chip {
  flex-direction: column;
  justify-content: center;
  gap: 0.15rem;
  min-height: 3.9rem;
  padding: 0.45rem 1.1rem 0.4rem;
  border-radius: 14px;
}
.chips.drawn .glyph {
  display: block;
  width: 4.5rem;
  line-height: 0;
}
.chips.drawn .glyph svg { display: block; width: 100%; height: auto; }
.chips.drawn .chip .name {
  font-size: 0.78rem;
  letter-spacing: 0.02em;
}

/* The dice. A chip's geometry so it belongs to the picker, but none of a
   chip's weight: it is an action, and a control that looks selectable in a
   row of things that are selectable is a fourth axis nobody chose. */
.roll {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
  min-height: 40px;
  padding: 0 0.95rem;
  font-family: var(--serif);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--stroke);
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease,
              color 120ms ease, scale 120ms cubic-bezier(0.2, 0, 0, 1);
}
/* 40px of pill, 44px of finger: the four missing pixels are taken back
   outside the border rather than by growing the thing on the paper. */
.roll::after {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
}
/* A quarter turn per press, and the pips are set on the diagonal so the turn
   is actually visible. A transition rather than a keyframe: press it four
   times quickly and it keeps turning from wherever it has got to.
   The span is not decoration: Chromium does not apply the individual
   transform properties to an <svg> element, so the turn has to be given to
   an HTML box around it. */
.roll .face {
  display: inline-flex;
  color: var(--brass);
  transition: rotate 420ms cubic-bezier(0.2, 0, 0, 1);
}
.roll:hover { background: var(--paper-deep); border-color: var(--stroke-strong); color: var(--ink); }
.roll:active { scale: 0.96; }
@media (prefers-reduced-motion: reduce) {
  .roll .face { transition: none; }
}

/* ------------------------------------------------------ the chooser */

/* The app's instrument sheet, on paper: the family in a tracked gutter, its
   instruments flowing off to the right, in the catalog's own order. It is the
   picker and the list of everything the app knows at once, which is why the
   whole app is in it.

   Set as type, with no icons. The app has thirteen instrument pictures for
   these forty names, so the Violin chip wore a cello, the Oboe chip wore a
   clarinet and Euphonium wore a tuba. Inside the app, where you have already
   chosen, that is a shrug; on the page that argues it knows exactly what you
   play, it is the argument losing. See SKIP in scripts/build.py. */
.chooser {
  border: 1px solid var(--stroke);
  border-radius: 12px;
  background: var(--card);
  padding: 0.35rem 1.25rem;
  margin-top: 1.9rem;
  margin-bottom: 1.6rem;
}
.family {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 0.9rem;
  align-items: baseline;
  padding: 0.85rem 0;
  border-top: 1px solid var(--stroke);
}
.family:first-child { border-top: 0; }
.family-name {
  font-size: 0.64rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: right;
  padding-top: 0.55rem;
}
.family-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.ichip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--serif);
  font-size: 0.9rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--stroke);
  border-radius: 999px;
  padding: 0.2rem 0.95rem;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease,
              scale 120ms cubic-bezier(0.2, 0, 0, 1);
}
.ichip:active { scale: 0.96; }
.ichip:hover { background: var(--paper-deep); }
.ichip[aria-checked="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* ----------------------------------------------------------------- band */

/* The app's console ground. Two passages of the page are set on it now, not
   one: the band and the changes. Both are the console side of the app, and
   two of them turn a single dark stripe into an alternation — paper, panel,
   paper, panel, paper — which is what the app itself does. */
.panel {
  background: var(--panel);
  color: var(--cream);
}
.panel .eyebrow { color: var(--cream-faint); }
.panel .lede { color: var(--cream-faint); max-width: 40em; }
.panel h2 { color: var(--cream); }
.panel .prose { color: var(--cream-faint); }
.panel a { color: var(--sun); }
.panel a:focus-visible, .panel button:focus-visible { outline-color: var(--sun); }

/* The band is a heading and a paragraph on black until the console
   screenshot lands, so it is set as a spread: the heading takes the left
   half at display size, the paragraph sits beside it. Big type is the only
   thing a panel with no picture in it has to carry it. */
.band-spread {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  column-gap: 4rem;
  align-items: start;
}
.band-spread > .eyebrow { grid-column: 1; grid-row: 1; }
.band-spread > h2 {
  grid-column: 1;
  grid-row: 2;
  font-size: clamp(2rem, 5vw, 3.3rem);
  margin-bottom: 0;
}
/* Set on the heading's last line, not its first. Three lines of display type
   beside four of body left 65px of nothing under the paragraph and a column
   that stopped halfway; sitting the two blocks on one baseline puts that air
   at the top of the column, where the eyebrow already holds the other side.
   The margin is the difference between the two boxes' last baselines:
   the heading's leading is 62px around a 53px face, the paragraph's 28
   around 17. */
.band-spread > .lede {
  grid-column: 2;
  grid-row: 2;
  margin: 0 0 0.45rem;
  align-self: end;
}
/* These are emitted only when their file exists; when they do, they run
   under the spread rather than beside it. */
.band-spread > .band-points,
.band-spread > .players,
.band-spread > .band-screenshot,
.band-spread > .cta { grid-column: 1 / -1; }

/* The drone and the drums, under the spread and across it. Two columns, both
   filled: a row of two with a caption under one of them is the "grid somebody
   stopped filling in" the practice section was rebuilt to avoid. */
/* The spread's own tracks, not auto-fit ones: at 1fr each the second point
   began 21px to the left of the paragraph standing directly above it, and a
   panel whose two columns do not agree where the second column starts reads
   as ragged however well set each half is. Stacked below 860px. */
.band-points {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 2rem 4rem;
  margin-top: 2.6rem;
}
.band-point h3 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 0 0 0.45rem;
  color: var(--cream);
}
.band-point p {
  margin: 0;
  color: var(--cream-faint);
}

/* The players, the screenshot and the portrait are emitted by build.py only
   when their files exist, so there is nothing here to hide: the old
   [hidden] guards, and the HEAD requests that lifted them, are gone. */
.players {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 12px;
  margin-top: 2.25rem;
}
/* Concentric: the 44px button inside 0.9rem of padding wants a 36px shell
   radius, and the pill it sits in reads as one piece rather than a circle
   dropped in a box. */
.player {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  background: var(--panel-raised);
  border: 1px solid var(--cream-stroke);
  border-radius: 36px;
  padding: 0.9rem 1.4rem 0.9rem 0.9rem;
}
.player button {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--cream-stroke);
  background: linear-gradient(180deg, var(--brass-bright), var(--sun));
  color: var(--panel);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: scale 140ms cubic-bezier(0.2, 0, 0, 1);
}
.player button:active { scale: 0.96; }
/* A play triangle's mass sits left of its bounding box; nudged right so it
   reads as centred in the circle. The pause bars are symmetric, so the
   nudge is on the glyph, not the button. */
.player button svg { width: 16px; height: 16px; display: block; }
.player button svg path { transform: translateX(0.6px); }
.player .which { min-width: 0; }
.player .name { display: block; font-weight: 700; font-size: 0.98rem; color: var(--cream); }
.player .detail { display: block; font-size: 0.82rem; color: var(--cream-faint); }

/* The recordings: two iPads side by side, each small enough that the pair
   sits inside the spread with dark margins either side. Portrait like the
   device they were taken on. The bezel and its rounded corners are in the
   pixels, so no outline — an edge on top of an edge reads as a double
   frame. One column when the pair would squeeze below legibility. */
.band-videos {
  grid-column: 1 / -1;
  margin: 2.5rem auto 0;
  max-width: 50rem;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
  justify-items: center;
}
.band-video { max-width: 24rem; }
.band-video video {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 18px;
}
@media (max-width: 720px) {
  .band-videos { grid-template-columns: 1fr; }
}
.band-screenshot { margin-top: 2.5rem; }
/* An inset outline rather than a border: it does not add to the box, and
   pure white at a tenth reads as an edge on any screenshot instead of
   picking up the panel's brown and reading as dirt. */
.band-screenshot img {
  border-radius: 12px;
  outline: 1px solid rgba(255, 255, 255, 0.1);
  outline-offset: -1px;
}

/* -------------------------------------------------------------- changes */

/* The plate: one engraving run the full width of the window, no card, no
   border, no sheet around it. Everything else on the page is set inside a
   62 or 74rem measure, so the one thing that ignores the measure entirely
   is the thing the page most wants looked at. It is the app's own output on
   the app's own ground, in cream, which is what the currentColor fix in
   scripts/engrave.py bought. */
.plate {
  padding: 3.5rem 0 1.1rem;
  color: var(--cream);
}
.plate svg { display: block; width: 100%; height: auto; }
.plate:focus-visible { outline: 2px solid var(--sun); outline-offset: -4px; }
/* Verovio hands out font-family="Times, serif" for the text it draws and has
   no option for the face, so the page names it, exactly as the app does in
   NotationShell.html. The plate is engraved in Petaluma (see OVERRIDES in
   scripts/engrave.py), and Petaluma's companion text face is the hand the
   chord symbols over it are set in — the same pairing as
   MusicFont.petaluma.textFont, fallbacks and all. */
.plate svg text, .plate svg tspan {
  font-family: "Petaluma Script", "Noteworthy", "Bradley Hand", cursive;
}

/* Names the picture, in the caption's own voice rather than the body's, and
   set back inside the measure the plate has just run past. */
.plate-caption {
  /* the sheet's own geometry, written out rather than borrowed: a .sheet
     that is a direct child of a panel is treated as the pass-through
     wrapper by the reveal, and this is content */
  max-width: 62rem;
  margin: 0 auto 3.25rem;
  /* the sheet's padding plus the spine, so the caption starts where the
     heading under it starts rather than hanging off to its left */
  padding-left: 13.25rem;
  padding-right: 1.25rem;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--cream-faint);
}

/* The four projections, out of the paragraph and into a row: four labels a
   reader can take in at once, where the sentence made them a list to parse.
   The rules are the sun, which is the one place on the page the icon's own
   brass can be used at full strength — on the dark ground it is 8:1. */
.projections {
  display: grid;
  /* Four across, always: three and a stray is a list that has come apart,
     and the whole point of lifting them out of the sentence was that they
     are four of one thing. They stack in pairs on a narrow page instead. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.4rem 1.5rem;
  margin: 1.9rem 0 2.1rem;
}
.projection { padding-top: 0.7rem; border-top: 1px solid var(--brass-bright); }
.projection .what {
  display: block;
  font-weight: 700;
  font-size: 0.98rem;
  color: var(--cream);
}
.projection .how {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--cream-faint);
}

/* ------------------------------------------------------------- teachers */

/* The other wall of prose. Its own middle sentence names a bassoonist, a
   guitarist and a singer, so the three pages sit beside it: same scale,
   three clefs, frets under one of them. */
/* The shot in this column is the one portrait picture on the site (a shelf is
   a list, so it is taller than it is wide), which is why the text takes the
   larger share here and the picture the smaller: at an even split the column
   ran 700px against two paragraphs. */
.studio {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: 2rem 3.5rem;
  align-items: start;
}
/* The heading rides in the left column rather than over both, so the text
   side has something at the top to stand against the pages beside it. */
.studio-text h2 { margin-bottom: 1.3rem; }
.studio-text p:last-child { margin-bottom: 0; }

/* When the iPad shot lands it replaces the three engravings; the column is
   the same either way. */
.studio-shot img {
  border-radius: 12px;
  outline: 1px solid rgba(0, 0, 0, 0.1);
  outline-offset: -1px;
}
.studio-plates { display: grid; gap: 0.75rem; }
.plate-small {
  margin: 0;
  background: var(--card);
  border: 1px solid var(--stroke);
  border-radius: 10px;
  padding: 0.55rem 1rem 0.35rem;
  color: var(--ink);
}
.plate-small figcaption {
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.2rem;
}
.plate-small svg { display: block; width: 100%; height: auto; }
.plate-small svg text, .plate-small svg tspan { font-family: var(--serif); }

/* ----------------------------------------------------------------- grid */

/* The console's pattern axes, drawn with the app's own glyph SVGs. The row
   is the whole of this block now — the caption beside it went, and with it
   the two-column split that used to hold the glyphs to the left half. They
   space themselves across the measure instead, which is what makes a dozen
   of them read as a rule of notation rather than as a heap of icons. */
.axes {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1.4rem 1.8rem;
  margin-top: 1.8rem;
  padding: 1.4rem 0;
  border-top: 1px solid var(--stroke);
  border-bottom: 1px solid var(--stroke);
  color: var(--ink);
}
.axes svg { height: 30px; width: auto; opacity: 0.78; }
.axes > span { display: flex; }
/* One height for glyphs of a dozen aspect ratios put a 182px hairpin next to
   a 14px time signature, and the row read as debris. Size them by what they
   are instead: the clef, the time signature and the dynamic letters are
   notation, drawn at a staff's proportions and set to a staff's height; the
   console rhythm icons sit in a wide box; the hairpins are three to six times
   as wide as they are tall and have to come down to sit among them; the
   direction arrow is interface, not notation, and sits smallest. */
.axes .clef svg, .axes .meter svg { height: 44px; }
.axes .dyn svg { height: 26px; }
.axes .hairpin svg { height: 18px; }
.axes .arrow svg { height: 20px; }

/* The section's one picture, above the grid rather than inside it. Set
   narrower than the band's, which runs the full width of its column: two 4:3
   iPad screens at the same size in consecutive sections would rhyme, and the
   point of this stretch of the page is that no two blocks are the same
   shape. */
.practice-shot { max-width: 46rem; margin-top: 2.5rem; }
.practice-shot img {
  border-radius: 12px;
  outline: 1px solid rgba(0, 0, 0, 0.1);
  outline-offset: -1px;
  display: block;
  width: 100%;
  height: auto;
}

/* 19rem, not 16. At 16 an iPad in portrait fits three columns of 264px, and
   these cells are three-sentence paragraphs: five or six words to a line, six
   lines deep, in a grid that reads as a spreadsheet. 19rem is the floor at
   which the tablet takes two columns and the desktop still takes three. */
.grind-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 1px;
  background: var(--stroke);
  border: 1px solid var(--stroke);
  border-radius: 12px;
  overflow: hidden;
  margin-top: 2rem;
}
.grind-cell { background: var(--card); padding: 1.5rem 1.6rem; }
.grind-cell h3 { font-size: 1.02rem; font-weight: 700; margin-bottom: 0.4rem; }
.grind-cell p { margin: 0; font-size: 0.95rem; color: var(--ink-soft); }

/* ---------------------------------------------------------- the catalog */

/* The multiplication, set as a pull quote rather than as body text: it is the
   one arithmetic claim on the page and it should be read, not skimmed. */
.arithmetic {
  max-width: 44rem;
  margin: 1.9rem 0;
  padding-left: 1.15rem;
  border-left: 2px solid var(--brass);
  font-size: 1.08rem;
}
.arithmetic strong { font-weight: 700; font-style: italic; }

/* One row per shelf of the catalog: the tally in a fixed gutter so the
   numbers stack into a column the eye can add up, then what it counts. */
/* Eight rows stacked was eight hundred pixels of the same shape, and each
   row held a number and two short lines with half its width left over. Two
   columns of four: the same evidence, half the scroll, and the numbers still
   stack into columns the eye can add up.
   The rules are drawn per cell rather than by the grid's own background (as
   .shelves does, and for the same reason): the ones that land on the outside
   edge are clipped away by the container's radius. */
.tallies {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
  max-width: 60rem;
  border: 1px solid var(--stroke);
  border-radius: 12px;
  background: var(--card);
  overflow: hidden;
  margin: 1.9rem 0 1.5rem;
}
/* align-content, because the cells of a row are as tall as the tallest of
   them and the shelf lists are one to three lines. Left at the default the
   slack all fell under the shorter cell — "Arpeggios and chords" sat with
   13px above it and 60px below — and the paired rows read as unequal
   weights rather than as two entries of the same table. */
a.tally-row {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 0.9rem;
  align-items: baseline;
  align-content: center;
  padding: 0.85rem 1.3rem;
  box-shadow: -1px 0 0 var(--stroke), 0 -1px 0 var(--stroke);
  color: var(--ink);
  text-decoration: none;
  transition: background-color 120ms ease;
}
a.tally-row:hover { background: var(--paper-deep); }
a.tally-row:focus-visible { border-radius: 0; outline-offset: -2px; }
.tally {
  font-size: 1.35rem;
  font-weight: 700;
  font-style: italic;
  font-variant-numeric: tabular-nums;
  color: var(--brass);
  text-align: right;
}
.tally-row .what { min-width: 0; }
.tally-row .name { display: block; font-weight: 700; }
/* The shelf titles are the app's own picker sections. They are evidence, not
   a sentence, so they are set as a quiet run-on line and allowed to wrap. */
.tally-row .shelf-list {
  display: block;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

/* ------------------------------------------------- the catalog page */

.catalog-block { padding-top: 2.75rem; padding-bottom: 1.5rem; }
.catalog-block h2 { display: flex; align-items: baseline; gap: 0.85rem; }
.catalog-block h2 .tally { font-size: 1.1rem; text-align: left; }
.catalog-block .prose { margin-bottom: 1.6rem; }
/* The grid's own background cannot draw the hairlines here the way it does
   in .grind-grid: the shelves are a ragged count, so the empty cells of the
   last row would show the stroke colour as a grey slab. Each shelf draws its
   own top and left rule instead, and the container's border and clip absorb
   the ones that land on the outside edge. */
.shelves {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  background: var(--card);
  border: 1px solid var(--stroke);
  border-radius: 12px;
  overflow: hidden;
}
.shelf {
  padding: 1.25rem 1.4rem;
  box-shadow: -1px 0 0 var(--stroke), 0 -1px 0 var(--stroke);
}
.shelf h3 {
  font-size: 0.64rem;
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.7rem;
}
.shelf ul { margin: 0; padding: 0; list-style: none; }
.shelf li { font-size: 0.95rem; line-height: 1.62; }

/* Every name is a link to its own engraved example, which is 184 links on one
   page: in the site's brass, underlined, the list would read as a menu rather
   than as a catalog. So they are set as the type they were, with a hairline
   under them that says they can be pressed, and the brass is kept for the one
   being read. */
.shelf a.entry {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--stroke);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  cursor: pointer;
  transition: color 120ms ease, text-decoration-color 120ms ease;
}
.shelf a.entry:hover {
  color: var(--brass);
  text-decoration-color: var(--brass);
}
.shelf a.entry[aria-current="true"] {
  color: var(--brass);
  font-weight: 700;
  text-decoration-color: var(--brass);
}

/* The paper the chosen example lands on: one sheet for the whole page,
   docked to the foot of the window. It used to sit inline under each
   section's shelves, which on a phone meant three screens between the name
   pressed and the music, and a scroll to meet it that threw the reader's
   place away. Docked, the page holds still: the name stays put (aria-current
   marks it in the list) and the music surfaces where the thumb already is.
   Hidden until something is pressed: the page already opens on an engraving. */
.catalog-dock {
  position: fixed;
  inset: auto 0 0;
  z-index: 20;
  display: flex;
  justify-content: center;
  padding: 0 0.9rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  /* The gutters beside the paper stay part of the page. */
  pointer-events: none;
}
.catalog-dock[hidden] { display: none; }
.catalog-dock .score-sheet {
  pointer-events: auto;
  width: 100%;
  max-width: 50rem;
  overflow-x: auto;
  /* Lifted off the page, so it reads as a sheet standing over the list
     rather than a band cut out of it. */
  box-shadow: 0 22px 60px rgba(36, 30, 22, 0.30), 0 4px 16px rgba(36, 30, 22, 0.16);
  opacity: 0;
  transform: translateY(0.9rem);
  transition: transform 240ms cubic-bezier(0.2, 0, 0, 1), opacity 180ms ease;
}
.catalog-dock.up .score-sheet { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .catalog-dock .score-sheet { transition: none; transform: none; opacity: 1; }
}
/* The tallest examples (the piano's two staves, the turn's high line) would
   otherwise stand the dock half a window tall. The capped box letterboxes
   the drawing, centred by the SVG's own preserveAspectRatio. */
.catalog-dock .engraving svg { max-height: 40vh; }
/* The label and its close ride the visible edge of the sheet, not the
   engraving's own width: at phone widths the staff scrolls sideways under
   them, and a close button set at the staff's far end is a screen away. */
.catalog-dock .score-label {
  position: sticky;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.4rem;
}
/* Quieter than a chip and quieter than the dice: it dismisses something the
   reader asked for, so it is chrome, not a choice. */
.catalog-dock .close {
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  font-size: 1.15rem;
  line-height: 1;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--stroke);
  border-radius: 999px;
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.catalog-dock .close:hover { color: var(--ink); border-color: var(--stroke-strong); }

/* ------------------------------------------------------------ buy once */

/* The most room on the page. It is the last thing read and the section
   people screenshot, and after two dark panels and a photograph the page
   has earned a screen of nothing but paper. */
.buyonce { text-align: center; padding-top: 6.5rem; padding-bottom: 7rem; }
.buyonce h2 { font-size: clamp(1.9rem, 4.4vw, 2.9rem); margin-bottom: 1.6rem; }
/* The page opens on the mark rising out of the water and closes on it
   standing over it. Static: the rise belongs to the load. */
.mark.still { margin-bottom: 1.1rem; }
.mark.still svg { width: 64px; height: 66.8px; }
.mark.still .sun, .mark.still .bar { animation: none; }
.mark.still .horizon { fill: var(--paper); }
.buyonce .lines {
  font-size: clamp(1.6rem, 4.2vw, 2.4rem);
  font-style: italic;
  font-weight: 700;
  line-height: 1.45;
  margin-bottom: 2rem;
}
.buyonce .lines span { display: block; }
.buyonce .lines .quiet { font-weight: 400; color: var(--ink-soft); }

/* -------------------------------------------------------------- maker */

.maker { padding-bottom: 5rem; }
.maker-row {
  display: grid;
  /* The portrait is close to square (it has to be wide enough to hold the
     whole trombone), so it needs more column than a head-and-shoulders
     would to read at the same size. */
  grid-template-columns: minmax(13rem, 19rem) 1fr;
  gap: 2.5rem;
  align-items: start;
}
.maker-row.no-photo { grid-template-columns: 1fr; }
.portrait img {
  border-radius: 12px;
  outline: 1px solid rgba(0, 0, 0, 0.1);
  outline-offset: -1px;
}

.colophon-credit {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: 1rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--stroke);
  border-bottom: 1px solid var(--stroke);
  margin-bottom: 1.4rem;
}
.colophon-credit .role {
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-top: 0.35rem;
}
.colophon-credit .name { font-weight: 700; }
.colophon-credit .lines { font-size: 0.92rem; color: var(--ink-soft); }
.colophon-credit .lines span { display: block; }

@media (max-width: 700px) {
  .maker-row { grid-template-columns: 1fr; }
  .portrait { max-width: 14rem; }
}

/* -------------------------------------------------------------- footer */

footer {
  margin-top: auto;
  border-top: 1px solid var(--stroke);
  padding: 2.5rem 0 3rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
/* Footer links are the smallest targets on the page; the row gives them the
   height a thumb needs without moving them apart. */
footer nav { display: flex; flex-wrap: wrap; gap: 0 1.5rem; margin-bottom: 0.6rem; }
footer nav a { display: inline-flex; align-items: center; min-height: 40px; }
footer .credits { max-width: 46em; margin-bottom: 1.6rem; }
/* The other two apps. On every page rather than the landing page alone, so a
   reader who arrived on support or privacy can still find them. */
footer .also { max-width: 46em; margin-bottom: 1.6rem; }
.imprint {
  font-size: 0.64rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ------------------------------------------------------- inner pages */

.pagehead { padding-top: 2.75rem; padding-bottom: 1rem; }
.pagehead h1 { font-size: clamp(1.8rem, 5vw, 2.4rem); font-weight: 700; font-style: italic; }
/* The catalog is a search landing page, so it asks near the top. Inline, not
   centred on its own rule: it sits under a paragraph, not under a section. */
.pagehead-cta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.9rem 1.25rem;
  margin: 1.6rem 0 0.5rem;
}
.pagehead-cta .pricenote { margin: 0; }

/* The one engraving on the catalog page. It sits between the head and the
   list, carries no eyebrow and no heading of its own, and is sized to be
   read at a glance rather than studied: this page's job is the list, and the
   music is here to say what kind of list it is. */
.catalog-sample { padding-top: 1.5rem; padding-bottom: 0.5rem; }
.sample-caption {
  margin: 0.85rem 0 0;
  font-size: 0.92rem;
  color: var(--ink-soft);
}
/* The measure is set on the content, not on the sheet: putting .prose on
   the sheet itself narrowed the sheet and pushed the text out of line
   with the heading above it. */
.page-body { padding-bottom: 4rem; }
.page-body h2 { font-size: 1.15rem; font-style: normal; margin-top: 2.2rem; }

/* ------------------------------------------------------ scroll reveal */

/* A section does not arrive as one slab. Its parts are staggered a beat
   apart, so the eye is led down the page in the order it should read it:
   the eyebrow, the heading, then the body. The section itself carries no
   motion of its own; only its children move.

   Everything here hangs off `.js`, which index.html sets in <head>. Only
   JavaScript ever adds `.in`, so without the gate a script error — or a
   reader with JavaScript off — got the hero and then eight screens of blank
   paper. The `.js` prefix has to be repeated on the reduced-motion reset
   below, or that reset loses to this rule on specificity and the page stays
   hidden for exactly the readers least able to work out why. */
.revealing .reveal > *,
.revealing .panel.reveal > .sheet > * {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.5s cubic-bezier(0.2, 0, 0, 1),
              transform 0.5s cubic-bezier(0.2, 0, 0, 1);
}
/* A panel's parts hang one level deeper (the section itself carries the
   dark ground), so its .sheet is a pass-through rather than a moving part. */
.panel.reveal > .sheet { opacity: 1; transform: none; transition: none; }

.reveal.in > *,
.panel.reveal.in > .sheet > * { opacity: 1; transform: none; }
.reveal.in > :nth-child(2),
.panel.reveal.in > .sheet > :nth-child(2) { transition-delay: 70ms; }
.reveal.in > :nth-child(3),
.panel.reveal.in > .sheet > :nth-child(3) { transition-delay: 140ms; }
.reveal.in > :nth-child(n + 4),
.panel.reveal.in > .sheet > :nth-child(n + 4) { transition-delay: 200ms; }

/* On the dark ground the arrival is different: no rise, a longer fade, and
   the parts come up together rather than a beat apart. Paper turns; a panel
   is lit. It is the same trick either way, but the page stops doing exactly
   one thing nine times, and the change lands where the ground changes. */
.revealing .panel.reveal > .sheet > *,
.revealing .panel.reveal > .plate-caption {
  transform: none;
  transition: opacity 0.85s ease;
}
.panel.reveal.in > .sheet > :nth-child(2) { transition-delay: 120ms; }
.panel.reveal.in > .sheet > :nth-child(3),
.panel.reveal.in > .sheet > :nth-child(n + 4) { transition-delay: 240ms; }

/* The one motion moment that is not a fade. The engraved line is uncovered
   left to right, at about the speed it would be read, because that is the
   only direction music happens in. It is a wipe on content that is already
   there: like every other reveal it hangs off `revealing`, so if site.js
   never runs the plate is simply visible.
   clip-path rather than a width or a mask, so nothing reflows and the SVG
   is never re-rasterized. */
.revealing .panel.reveal > .plate {
  opacity: 1;
  transform: none;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1.35s cubic-bezier(0.33, 0, 0.1, 1) 120ms;
}
.panel.reveal.in > .plate { clip-path: inset(0 0 0 0); }

@media (prefers-reduced-motion: reduce) {
  .revealing .reveal > *,
  .revealing .panel.reveal > .sheet > * {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0ms;
  }
  .revealing .panel.reveal > .plate {
    clip-path: none;
    transition: none;
  }
}

/* ------------------------------------------------------ the narrow page */

/* Last in the file on purpose. A media query carries no extra specificity,
   so an override written beside the rule it overrides loses to every base
   rule declared after it. This block sat next to the score sheet, which put
   it above .family, .picker-row and the chooser, and none of the stacking
   below actually took effect on a phone. Everything narrow goes here now. */
/* The spine, the spread and the studio are all two columns of real content.
   They come apart before the measure does, at the width where the narrower
   of the two columns stops holding a line of text. */
@media (max-width: 860px) {
  .gutter { display: block; }
  .gutter > .eyebrow {
    margin-bottom: 1rem;
    padding-top: 0;
    text-align: left;
  }
  .plate-caption { padding-left: 1.25rem; }
  .picker-section { display: block; }
  .picker-section > h2 { margin-bottom: 1.1rem; }
  .picker-section > .prose { max-width: 40rem; }
  .projections { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .band-spread { display: block; }
  .band-spread > h2 { margin-bottom: 1.1rem; }
  .band-spread > .lede { margin-bottom: 1rem; }
  .band-points { grid-template-columns: 1fr; }
  /* Once the glyphs wrap, space-between stretches every row to the measure
     including the short last one, which sets three symbols a hand's width
     apart. Centred, a wrapped row reads as a wrapped row. */
  .axes { justify-content: center; gap: 1.4rem; }
  .studio { grid-template-columns: 1fr; }
  .studio-plates { max-width: 34rem; }
  /* Stacked, the portrait shot would be a full-width slab a thousand pixels
     tall on a tablet. It is a sidebar picture; keep it sidebar-sized. */
  .studio-shot { max-width: 24rem; }
}

@media (max-width: 640px) {
  /* Same bargain the score sheet strikes: the staff stays readable and the
     plate scrolls under it, rather than shrinking eight bars into 375
     pixels. Full bleed already, so there is nothing to inset. */
  .plate { overflow-x: auto; }
  /* eight bars at ten and a half to one: below about this the staff is a
     line of specks, and above it the plate is three screens of dragging */
  .plate svg { min-width: 58rem; }

  /* The full canvas would shrink the staff to a squint. Keep the music
     readable and let the sheet scroll sideways instead — but say so. A
     staff that runs off the edge with no shadow at the edge reads as an
     engraving that has been cut off, not as one that continues. The two
     `local` layers travel with the content and cover the shadow under them
     when that end is reached, so the hint is there exactly when there is
     something still to scroll to. */
  .score-sheet {
    overflow-x: auto;
    padding: 1.25rem 1.25rem 1rem;
    background:
      linear-gradient(to right, var(--card) 40%, rgba(251, 248, 240, 0))
        left / 34px 100% no-repeat local,
      linear-gradient(to left, var(--card) 40%, rgba(251, 248, 240, 0))
        right / 34px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%,
        rgba(36, 30, 22, 0.24), rgba(36, 30, 22, 0)) left / 26px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%,
        rgba(36, 30, 22, 0.24), rgba(36, 30, 22, 0)) right / 26px 100% no-repeat scroll,
      var(--card);
  }
  /* 14px at 0.16 was invisible against ivory paper, which meant the only
     thing saying "this continues" was the staff being cut off — and a cut-off
     staff reads as a bug, not as an invitation. 26px at 0.24 is still quiet
     enough not to look like a card edge and is actually seen. */
  .score-sheet .engraving svg { min-width: 36rem; }
  /* The staff holds the sheet open at 36rem, so the head is 36rem wide too
     and anything pushed to its right edge is a screen and a half away. Both
     to the left, where the reader already is at scroll 0. */
  .score-head { justify-content: flex-start; gap: 0.9rem; }

  /* The gutter costs a third of a phone's width. Stack instead, and let the
     labels sit over their own rows. */
  .family, .picker-row { grid-template-columns: 1fr; gap: 0.5rem; }
  .family-name, .picker-label { text-align: left; padding-top: 0; }
  .picker-row { padding-left: 0; }
  /* One column, so the row's remainder goes under its chips rather than
     beside them — and the chips are a sideways run at this width, so there is
     no room beside them anyway. */
  .row-more { margin-top: 0.1rem; }
  .chooser { padding-left: 1rem; padding-right: 1rem; }

  /* The tally keeps its gutter: two digits are narrow, and the column of
     numbers is what the row is for. */
  a.tally-row { grid-template-columns: 2.75rem 1fr; gap: 0.85rem; padding: 0.9rem 1rem; }
  .arithmetic { padding-left: 0.9rem; }
}

/* --------------------------------------------------- the chooser, on a phone
   Wrapped, the forty names are a 1,295px wall: 28 rows of unbroken nouns
   sitting between the engraving and every other argument on the page. The
   roll call is an argument — forty names is the claim, made by being long —
   but at 375px it was costing more than it earned, and a reader who has just
   met the aha then wades through a screen and a half of it.

   Each family becomes its own sideways run instead. Nothing is hidden and no
   name is dropped behind a "show all": the six family rows are the same six
   the wide layout sets, one line each, and the wall is 570px instead of 1,295.
   A row past its edge carries the score sheet's own scroll shadow, which is
   the affordance a cut-off chip is not.

   No keyboard trap to fix: the chips inside are the focusable things, and
   arrow-keying through the group focuses each in turn, which scrolls its own
   row. That is WCAG 2.1.1 satisfied by the content rather than by a tabindex
   on the box, which is why this one does not carry one and .score-sheet
   (whose content is an inert SVG) does. */
@media (max-width: 500px) {
  .family-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    /* overflow-x: auto makes the other axis auto too, which would clip the
       2px focus ring and its 3px offset off the top and bottom of a chip.
       Room for it, taken straight back out of the layout. */
    padding-block: 6px;
    margin-block: -6px;
    background:
      linear-gradient(to right, var(--card) 40%, rgba(251, 248, 240, 0))
        left / 30px 100% no-repeat local,
      linear-gradient(to left, var(--card) 40%, rgba(251, 248, 240, 0))
        right / 30px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%,
        rgba(36, 30, 22, 0.22), rgba(36, 30, 22, 0)) left / 22px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%,
        rgba(36, 30, 22, 0.22), rgba(36, 30, 22, 0)) right / 22px 100% no-repeat scroll,
      var(--card);
  }
  /* A flex item in a nowrap row will still be squeezed by the container.
     These must keep their own width, and "Clarinet (B♭)" must not break. */
  .ichip { flex: 0 0 auto; white-space: nowrap; }
  /* The rows carry the rhythm now, so the rules between them can go quiet. */
  .family { padding: 0.7rem 0; }
}
