/* Homepage (catalog) only — loaded from list.html's head_extra, not base.html, so
   none of this sits in the player's render path.

   The page is designed at 390px first and widens; every horizontally-long thing
   (the shelf, the screenshot rails, the catalog) is a contained scroll rail, and
   nothing is positioned with a viewport-width box. No absolute full-bleed element
   either: width:100% plus negative left/right offsets overflows the document on a
   phone.

   The hero is a warm near-black room with one lit object in it — the seam figure,
   half printed plate and half screen — and the sections after it alternate
   between that same case and the ivory page: Study and What it never does on the
   black key, the shelf, Perform and the catalog on ivory. Two grounds, nothing
   else, and the change of ground is the only divider any join gets. */

/* The score tile colour, which the navbar's split circle cycles. Declared here
   rather than left to the JS default so the pitch keeps the warm sheet it was
   designed around when nobody has chosen anything: the switcher writes an inline
   style on <html> and wins over this the moment someone does. It also gives the
   control's left half something true to show, since that swatch reads the same
   variable. */
html { --tile-bg: #fdf5e6; }

/* ── Shared ───────────────────────────────────────────────── */
.home-wrap {
  /* Bleed: sections pull out through the page's .px-4 gutter and put it back
     inside themselves, so a band's colour reaches the window edge while its copy
     stays on the text column. */
  --bleed: 1.5rem;
  --case: #141210;
  --case-band: #100d0a;         /* the black key: every dark band after the hero
                                   is this one value, so the page reads as two
                                   grounds alternating rather than three */
  --case-lit: #241d16;
  --case-ink: #c6b8a4;          /* secondary copy on the case — tinted from the
                                   ground's own hue, never grey */
  /* The one lamp every edition is lit by: the tablet's screen half, the shelf's
     editions and the screenshots all take it, so cycling the tile colour repapers
     the whole page and not just the one surface the visitor is pointing at.

     This sheet is the catalog's too, so a choice made here follows onto that
     shelf, which carries no switcher of its own. Deliberate: it is the same
     edition card lit by the same lamp, and a preference that stopped at the page
     it was set on would read as the setting failing to take. */
  --paper: var(--tile-bg, #fdf5e6);
  --measure: 34rem;

  /* The music desk's own woods, under the tablet. New material for this surface:
     DESIGN.md has the case as one flat black, which is enough for a navbar and not
     enough for an object with a lit edge. */
  --lid: #241a12;
  --lid-shadow: #120d09;
  --desk-lit: #3a2c21;

  /* Type steps. Everything but --t-display is the ramp DESIGN.md already
     documents; --t-display is one new step, for the single h1 on the only
     Persuade surface in the product. */
  --t-display: clamp(1.65rem, 7vw, 4.1rem);
  --t-section: clamp(1.65rem, 3.4vw, 2.5rem);
  --t-title: 1.15rem;
  --t-body: 1rem;
  --t-label: .82rem;
}
.home-main { min-width: 0; }

/* The engraving's own face, already in the render path for fingering numerals.
   It is the page's display voice: this surface sells reproductions of printed
   editions, so the type should come off one. */
.home-display {
  font-family: var(--font-prose);
  font-weight: 700;
  letter-spacing: -.015em;
  line-height: 1.06;
  text-wrap: balance;
}

.home-band {
  margin-inline: calc(-1 * var(--bleed));
  padding-inline: var(--bleed);
}
.home-band-head {
  max-width: var(--measure);
  margin: 0 auto;
  text-align: center;
}
.home-band-title {
  font-size: var(--t-section);
  margin: 0 0 .5rem;
  color: var(--gp-black);
}
.home-band-lede {
  font-family: var(--font-prose);
  margin: 0;
  font-size: var(--t-body);
  line-height: 1.6;
  color: #5c5c5c;
  text-wrap: pretty;            /* no one-word last line on a wide screen */
}

/* ── 1. The seam ──────────────────────────────────────────────
   The hero is a real room: a photograph of a score propped on an upright's music
   desk, keys below it, warm and dim, shallow enough depth of field that the room
   stays background. The product stands in that room as one lit object — a page,
   half printed plate and half the same plate as the app lays it out, with a brass
   rule between them the visitor drags.

   The room is drawn, not photographed, and it is meant to look drawn: an ogee
   wallpaper (room-wallpaper.svg, which tiles by construction) and a lamp pool over
   the sheet. Nothing else, and no instrument: a keyboard at the foot of the hero
   competes with the score for the brightest thing in the frame, and wins.

   Both halves occupy the same box and are clipped against --seam from opposite
   sides, so nothing moves as the seam travels — the reveal is a wipe, not a
   resize, and the two renderings stay in register the way a comparison has to. */
.seam-hero {
  position: relative;
  margin: calc(-1 * var(--bleed)) calc(-1 * var(--bleed)) 3.5rem;
  padding: 1.75rem var(--bleed) 2.75rem;
  /* The room's four variables. A paper is never just a pattern: it is a printed
     ink, a wall colour behind it, and the light in the room that falls on both.
     Change one without the others and you get a swatch stuck on a brown wall.
     The defaults here are the room's own paper; [data-paper] below repapers it.
     Named --wall-paper, not --paper: .home-wrap already owns --paper for the
     ivory every edition is lit by, and a hero-scoped --paper silently handed
     .seam-screen a wallpaper URL where it wanted that ivory. */
  --wall-paper: url("../room-wallpaper.svg");
  --wall: linear-gradient(180deg, #2a221a 0%, #1d1811 58%, #14100c 100%);
  --lamp: rgba(255, 226, 176, .17);
  --wall-shade: 14, 11, 8;         /* the corner fall-off and the copy's scrim */
  /* Top to bottom: the lamp pool over the sheet, a fall-off that darkens the wall
     towards its corners, the papered wall itself, and the wall's own colour under
     the paper's translucent lines. */
  background:
    radial-gradient(52% 40% at 50% 46%, var(--lamp) 0%, transparent 70%),
    radial-gradient(120% 100% at 50% 24%, transparent 34%, rgba(var(--wall-shade), .62) 100%),
    var(--wall-paper) 50% 0 / 150px 216px repeat,
    var(--wall);
  color: var(--gp-ivory);
  display: grid;
  gap: 1.25rem;
  justify-items: center;
  /* The desk plane is deliberately longer than it needs to be, so that it always
     reaches the foot of the room whatever the hero's height. This is what stops it
     there instead of letting it run down over the shelf below. */
  overflow: hidden;
}
/* The wall's paper is low-contrast by design, but copy still gets a guaranteed
   ground rather than relying on where the pattern happens to fall. */
.seam-hero::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(var(--wall-shade), .55) 0%, rgba(var(--wall-shade), .18) 52%, transparent 100%);
}

/* ── 1b. The year of papers ───────────────────────────────────
   Twelve papers for the same room, one per month. Only the wall, the ink and the
   lamp move: the desk stays walnut, the tablet stays the brightest object, and
   the pattern's construction (150x216, lattice loud, motif quiet) is identical in
   all twelve — a room that is repapered, not twelve different rooms.

   Each block sets the four variables together on purpose. A pattern swapped
   without its wall and its light is the failure mode this replaces: a January
   frost printed in silver reads as scratches on brown, and July's peacock reads
   as a mistake, until the wall behind them changes too.

   The month is chosen server-side (repertoire/views.py sets data-paper); the
   default above is the fallback if the two ever drift apart, and ?paper=00 puts
   it back deliberately for comparison.

   Editing a tile in place is the one thing to avoid: prod serves /static with a
   one-year immutable cache and these URLs carry no {% static_v %} query, so a
   changed paper reaches returning visitors under a new filename or not at all. */
.seam-hero[data-paper="01"] {   /* January — givre, silver on cold slate */
  --wall-paper: url("../wallpapers/01-frost.svg");
  --wall: linear-gradient(180deg, #1d2836 0%, #16202c 58%, #0e1620 100%);
  --lamp: rgba(198, 224, 255, .14);
  --wall-shade: 8, 12, 18;
}
.seam-hero[data-paper="02"] {   /* February — chandelle, candle gold on plum */
  --wall-paper: url("../wallpapers/02-candle.svg");
  --wall: linear-gradient(180deg, #271b28 0%, #1b1220 58%, #110a15 100%);
  --lamp: rgba(255, 203, 132, .22);
  --wall-shade: 14, 8, 16;
}
.seam-hero[data-paper="03"] {   /* March — saule, young green on moss */
  --wall-paper: url("../wallpapers/03-willow.svg");
  --wall: linear-gradient(180deg, #1e2718 0%, #161e11 58%, #0d130a 100%);
  --lamp: rgba(226, 255, 196, .13);
  --wall-shade: 8, 13, 7;
}
.seam-hero[data-paper="04"] {   /* April — ondée, wet silver on rain grey */
  --wall-paper: url("../wallpapers/04-shower.svg");
  --wall: linear-gradient(180deg, #1f2c30 0%, #162126 58%, #0d1519 100%);
  --lamp: rgba(206, 238, 244, .14);
  --wall-shade: 7, 13, 16;
}
.seam-hero[data-paper="05"] {   /* May — muguet, white on garden green */
  --wall-paper: url("../wallpapers/05-muguet.svg");
  --wall: linear-gradient(180deg, #182a1d 0%, #112016 58%, #0a150e 100%);
  --lamp: rgba(236, 255, 232, .15);
  --wall-shade: 6, 14, 9;
}
.seam-hero[data-paper="06"] {   /* June — treillis, painted ivory and rose */
  --wall-paper: url("../wallpapers/06-rose-trellis.svg");
  --wall: linear-gradient(180deg, #1a2519 0%, #121c12 58%, #0b140c 100%);
  --lamp: rgba(255, 236, 206, .17);
  --wall-shade: 7, 13, 8;
}
.seam-hero[data-paper="07"] {   /* July — paon, the loud one */
  --wall-paper: url("../wallpapers/07-fan.svg");
  --wall: linear-gradient(180deg, #0d3239 0%, #082a30 58%, #05171c 100%);
  --lamp: rgba(150, 255, 238, .14);
  --wall-shade: 3, 14, 18;
}
.seam-hero[data-paper="08"] {   /* August — blé, ripe gold on tobacco */
  --wall-paper: url("../wallpapers/08-wheat.svg");
  --wall: linear-gradient(180deg, #3a2b10 0%, #261c09 58%, #150f05 100%);
  --lamp: rgba(255, 226, 158, .21);
  --wall-shade: 16, 11, 5;
}
.seam-hero[data-paper="09"] {   /* September — chêne, copper on forest brown */
  --wall-paper: url("../wallpapers/09-oak.svg");
  --wall: linear-gradient(180deg, #212216 0%, #18190e 58%, #0e0f07 100%);
  --lamp: rgba(255, 214, 163, .16);
  --wall-shade: 13, 12, 6;
}
.seam-hero[data-paper="10"] {   /* October — vigne, the incumbent brass on oxblood */
  --wall-paper: url("../wallpapers/10-vine.svg");
  --wall: linear-gradient(180deg, #2a141a 0%, #1c0c11 58%, #100609 100%);
  --lamp: rgba(255, 199, 174, .16);
  --wall-shade: 16, 6, 9;
}
.seam-hero[data-paper="11"] {   /* November — fougère, pewter on green-black */
  --wall-paper: url("../wallpapers/11-fern.svg");
  --wall: linear-gradient(180deg, #1b201d 0%, #141917 58%, #0c100e 100%);
  --lamp: rgba(222, 238, 228, .14);
  --wall-shade: 8, 11, 10;
}
.seam-hero[data-paper="12"] {   /* December — étoile, silver and gold on midnight */
  --wall-paper: url("../wallpapers/12-star.svg");
  --wall: linear-gradient(180deg, #161f31 0%, #101724 58%, #080d16 100%);
  --lamp: rgba(214, 226, 255, .16);
  --wall-shade: 6, 10, 20;
}

.seam-hero > * { position: relative; z-index: 1; }

.seam-copy {
  max-width: 34rem;
  text-align: center;
}
.seam-title {
  font-size: clamp(1.65rem, 4.4vw, 3rem);
  margin: 0 0 .85rem;
  color: var(--gp-ivory);
}
.seam-lede {
  font-family: var(--font-prose);
  margin: 0;
  font-size: clamp(1rem, 3.6vw, var(--t-title));
  line-height: 1.6;
  color: var(--case-ink);
  text-wrap: pretty;
}
.seam-cta {
  margin-top: 0;
  text-align: center;
}
/* The homepage's calls to action are set in the prose face, not the interface
   one. Inter is right for a toolbar, where a control should be a control and
   nothing else; but these three sit inside an argument, and in Inter they read
   as chrome that wandered onto the page. Newsreader at 500 with a little
   tracking gives them the headline's voice at a button's size. Scoped to the
   homepage — the app's own buttons stay in Inter. */
.home-wrap .btn,
.seam-alt {
  font-family: var(--font-prose);
  font-weight: 500;
  letter-spacing: .006em;
}
/* The primary button carries the page's own mark rather than a chip of its
   colour. DESIGN.md is explicit that brass is "a stroke over something, not a
   fill of something", and the CTA was the one element inverting that: a flat
   fill with geometric edges, sitting under a headline whose emphasis is a
   hand-drawn swash. Same device here, at button scale — two passes, wobble on
   every edge, ends curved so it reads as a stroke that started and stopped.

   The label keeps the display cut of the variable face: opsz is not a size but
   how much contrast the drawing carries, and at 60 the hairlines thin and the
   serifs sharpen. Weight rises with it, or the thinner strokes read as a lighter
   button rather than a sharper one.

   No radius: the mark's own edge is the shape. Padding keeps the 44px target the
   product requires of anything pressed at arm's length. */
.home-wrap .btn-primary {
  padding: .62rem 2.5rem;
  border: 0;
  border-radius: 0;
  background-color: transparent;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 260 56' preserveAspectRatio='none'%3E%3Cpath d='M7 12 C46 4 96 9 136 7 C178 5 222 10 253 6 C257 18 256 40 252 50 C214 55 168 49 126 51 C86 53 42 48 8 52 C4 40 4 22 7 12 Z' fill='%23C8A96B' fill-opacity='.94'/%3E%3Cpath d='M12 30 C52 25 104 31 150 28 C192 25 224 30 249 27 C250 37 249 45 247 49 C210 54 166 48 124 50 C84 52 44 47 11 51 C9 44 10 35 12 30 Z' fill='%23B8965A' fill-opacity='.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-position: center;
  color: var(--gp-black);
  font-size: 1.12rem;
  font-weight: 600;
  font-variation-settings: "opsz" 60;
  letter-spacing: .012em;
}
.home-wrap .btn-primary:hover,
.home-wrap .btn-primary:focus-visible {
  background-color: transparent;
  color: var(--gp-black);
  filter: brightness(1.06);
}
.home-wrap .btn-primary:active {
  filter: brightness(.95);
}
/* Brass on brass would be invisible; the ring is the page's other light. */
.home-wrap .btn-primary:focus-visible {
  outline: 2px solid var(--gp-ivory);
  outline-offset: 3px;
}
.seam-cta .btn { padding-inline: 2.25rem; }
.seam-note {
  display: block;
  margin-top: .7rem;
  font-size: var(--t-label);
  line-height: 1.45;
  color: var(--case-ink);
}
.seam-alt {
  display: inline-block;
  margin-top: .55rem;
  font-size: var(--t-body);
  color: var(--gp-gold);
  text-decoration: none;
}
.seam-alt:hover { color: #d8bd86; text-decoration: underline; }

/* The tablet holds the comparison, and it is load-bearing rather than decorative:
   both halves of the seam are the same scan on the same screen, so the device being
   visible is what stops the left half reading as paper. It reuses the .tablet shell
   from the desk section below — same bezel, same object shadow — with the seam as its
   screen instead of a capture. */
/* Device and desk share a column so the desk can measure itself against the tablet
   and still overrun it. Both stay relative with stated z-indexes: the desk tucks under
   the tablet's foot, and source order alone would have painted it over the bezel. */
.seam-stage {
  position: relative;            /* the desk measures itself against this */
  display: flex;
  flex-direction: column;
  align-items: center;
  width: min(100%, 16.5rem);
}
.seam-device {
  position: relative;
  z-index: 1;
  width: 100%;
  /* A deeper bezel than the desk section's tablet wears. That one is a small object
     in a scene; this one has to be recognisable as a device at a glance, because the
     whole comparison depends on the visitor seeing that both halves are one screen. */
  padding: 4.6% 3.8% 7%;
  border-radius: 22px;
  box-shadow:
    0 26px 44px rgba(0, 0, 0, .58),
    0 6px 14px rgba(0, 0, 0, .42),
    inset 0 0 0 1px rgba(255, 255, 255, .2);
}
.seam-device .tablet-cam  { top: 1.9%; width: 6px; height: 6px; }
.seam-device .tablet-home { bottom: 1.9%; width: 15px; height: 15px; }

/* Walnut, running well past the device on both sides and down to the foot of the
   hero, which the section clips. A band that stops short reads as a lip the tablet
   is balanced on; a plane that reaches the bottom edge reads as the surface the
   whole scene is standing on, with the wall above it.

   Lit at the front arris and falling away below: the first two stops are the lip
   catching the lamp, the rest is the plane receding into the room. */
.seam-desk {
  position: absolute;
  z-index: 0;
  /* It has to run off both edges of the window at every width. An overhang stated
     as a multiple of the tablet clears the window only at the two ends of the
     range: on a phone the plane is wider than the screen, and past 64em the stage
     sits in the right-hand track and the plane spills off that side. Between them
     it lands entirely inside the frame, and a plane with both its edges showing
     stops being a surface and becomes an object with proportions of its own — at
     those widths a tall narrow block, which is the one shape a piano top cannot
     be. Off the edge there is nothing to measure, and the room is free to
     continue. 100vw each side of a stage that is itself always on screen reaches
     both edges from either layout, centred or offset. */
  left: -100vw;
  right: -100vw;
  top: calc(100% - 11px);
  height: 100vh;                 /* any surplus is clipped by .seam-hero */
  background: linear-gradient(180deg,
    #6b4f3a 0, #543d2c 9px, #3a2a1e 22px, #281d14 42px, #1d150e 40%, #150f0a 100%);
  box-shadow:
    0 -1px 0 rgba(200, 169, 107, .42),
    0 14px 26px rgba(0, 0, 0, .45);
}
/* The figure in the wood. A desk seen head-on shows its grain running left to
   right, so these are horizontal lines — three periods that share no common
   factor (5 / 11 / 29px), because evenly spaced lines read as corduroy and a
   shared factor beats into moiré. The widest, warmest band is the ribbon that
   makes a bubinga top look like bubinga rather than stain.

   Masked to the lit band at the front, because grain is something light does to
   a surface: this plane is already falling into the dark by 190px, and figure
   still legible down there reads as wallpaper on the floor rather than a
   polished top receding. The alphas are in the low hundredths for the same
   reason — at a tenth it stops being furniture and becomes a texture swatch.
   The sheen is one soft radial off the front-left, where the room's lamp is.

   The top is a burr, not a plank: blotch, swirl and knotted eyes, with no
   direction in it. Parallel lines — however fine — read as sawn boards, so the
   figure is shapes (wood-burl.svg) and the only gradient left here is the lamp's
   sheen. Tiled at roughly life size, and the sheen sized in vw for the same
   reason: the plane is several windows wide, so anything stated as a percentage
   of it is blown up past recognition — the burr into smoke, the lamp pool into a
   flat wash three screens across. */
.seam-desk::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(62vw 62% at calc(50% - 7vw) 0%, rgba(255, 236, 208, .055), transparent 62%),
    url("../wood-burl.svg");
  background-repeat: no-repeat, repeat;
  background-size: 100% 100%, 620px 400px;
  -webkit-mask-image: linear-gradient(180deg, #000 0, rgba(0, 0, 0, .85) 26px, transparent 190px);
  mask-image: linear-gradient(180deg, #000 0, rgba(0, 0, 0, .85) 26px, transparent 190px);
  /* The figure modulates the plane rather than covering it: soft-light lets the
     burr's contours lighten and darken the gradient underneath, which is what a
     figured veneer does to the colour of the wood it is cut from. Painted on
     opaque it reads as a sticker of wood laid over a brown desk. */
  mix-blend-mode: soft-light;
  opacity: .5;
}

/* A tablet's own 3:4 portrait screen, which an A4 page very nearly fills — so the
   PDF half needs no letterbox and reads as what it is: the whole page, fit to the
   glass. */
.seam {
  /* A native range thumb's centre travels [thumbW/2, W - thumbW/2], not [0, W]:
     it cannot reach the edges, because it would hang off them. So the rule and
     the two clip-paths derive from the same expression the thumb follows: at a
     flat percentage of the figure they meet the handle only at 50% and drift
     from it by up to half a thumb toward either end, which puts the wipe a few
     pixels off the thing being dragged.

     Deriving the wipe from where the thumb *can* go aligns the two but strands
     the seam 22px short of each edge, so neither alternative is ever seen whole.
     The input is widened past the figure instead (see .seam-range), which lets
     the thumb's centre span 0 to 100 and makes a flat percentage true again. */
  --thumb: 44px;
  --seam-n: 52;
  --seam: calc(var(--seam-n) * 1%);
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 4;
  margin: 0;
  border-radius: 3px;             /* the screen's radius; the bezel carries the rest */
  background: var(--paper);
  /* Not hidden: the handle hangs half past this box at either end, and clipping
     it there would hide the control at exactly the position the viewer is
     reaching for. The halves clip themselves — .seam-side carries the same
     radius and its own overflow — so nothing leaks. */
  overflow: visible;
}
.seam-side {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 3px;
}
/* Clipped from opposite edges against the same value: the printed side keeps what
   is left of the seam, the screen side keeps what is right of it. */
.seam-print { clip-path: inset(0 calc(100% - var(--seam)) 0 0); }

/* The screen half: the same bars at the size they are read at rather than at page
   scale, centred vertically so the comparison lands on the music. */
.seam-screen {
  clip-path: inset(0 0 0 var(--seam));
  display: flex;
  align-items: center;
  background: var(--paper);
}

.seam-print img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  mix-blend-mode: multiply;      /* the scan's paper becomes the app's paper */
}

/* The bars wrapped into systems and centred, which is what sheet mode does with
   them. Anchored right so dragging the seam leftward brings more of the screen's
   layout in from the left. Rows run past the visible edge on purpose: reading size
   means fewer bars fit per line, and that trade is the whole comparison. */
.seam-strip {
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: flex-end;
  column-gap: .3rem;
  row-gap: 4%;
  width: 100%;
  height: 100%;
  padding: 7% .6rem;
}
.seam-strip img {
  height: 16%;
  width: auto;
  flex: 0 0 auto;
  mix-blend-mode: multiply;
}
.seam-tag {
  position: absolute;
  bottom: .6rem;
  left: .7rem;
  padding: .15rem .55rem;
  border: 1px solid rgba(132, 98, 74, .45);
  border-radius: 999px;
  background: rgba(253, 245, 230, .88);
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--gp-walnut);
}
.seam-tag-right { left: auto; right: .7rem; }

/* The seam itself: a brass rule, which is what brass is for. */
.seam-rule {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--seam);
  z-index: 3;
  width: 2px;
  margin-left: -1px;
  background: var(--gp-gold);
  pointer-events: none;
}

/* A real range input, spanning the figure: drag anywhere, tap to jump, arrow keys
   to nudge, and an accessible name — none of which pointer handlers would give for
   free. Transparent track; the thumb is the only visible part. */
.seam-range {
  position: absolute;
  /* Wider than the figure by one thumb, offset by half: the thumb's centre then
     travels the figure's full width rather than being inset by its own radius,
     so the seam can reach 0 and 100 and each alternative can be seen whole. */
  top: 0;
  bottom: 0;
  left: calc(var(--thumb) / -2);
  z-index: 4;
  width: calc(100% + var(--thumb));
  height: 100%;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: ew-resize;
}
.seam-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: var(--thumb);
  height: var(--thumb);
  border: 2px solid rgba(246, 242, 232, .9);
  border-radius: 50%;
  background: var(--gp-black);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F6F2E8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 8 6 12l4 4M14 8l4 4-4 4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 22px 22px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .45);
  cursor: ew-resize;
}
.seam-range::-moz-range-thumb {
  width: var(--thumb);
  height: var(--thumb);
  border: 2px solid rgba(246, 242, 232, .9);
  border-radius: 50%;
  background: var(--gp-black);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F6F2E8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 8 6 12l4 4M14 8l4 4-4 4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 22px 22px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .45);
  cursor: ew-resize;
}
.seam-range:focus-visible { outline: 2px solid var(--gp-gold); outline-offset: 4px; }

/* ── 2. The shelf ────────────────────────────────────────────────
   The editions themselves, edge to edge, one card per piece. The face of a card
   is that piece's own first bars — the real tiles the player lays out — so the
   shelf shows nine different printed sources under one lamp rather than nine
   product thumbnails. */
.shelf {
  margin-inline: calc(-1 * var(--bleed));
}
.shelf-rail {
  padding-inline: var(--bleed);
  padding-block: .5rem .9rem;
}
.shelf-rail > li {
  flex: 0 0 auto;
  width: min(19rem, 78vw);
  scroll-snap-align: center;
}
.edition {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: .55rem .55rem .3rem;
  border-radius: 10px;
  background: var(--paper);
  border: 1px solid rgba(132, 98, 74, .18);
  color: var(--gp-black);
  text-decoration: none;
  transition: transform .18s ease-out, box-shadow .18s ease-out;
}
.edition:hover,
.edition:focus-visible {
  transform: translateY(-3px);
  border-color: var(--gp-gold);
  box-shadow: 0 10px 26px rgba(16, 16, 16, .14);
}
.edition:focus-visible { outline: 2px solid var(--gp-gold); outline-offset: 3px; }

/* The card's face: one system of the piece's own tiles, multiplied onto the
   shelf's single paper tint. This is the mechanism the copy claims — different
   books, different scanners, one lamp — so it has to be the real tiles. */
.edition-face {
  display: flex;
  align-items: center;
  height: 6.5rem;
  overflow: hidden;            /* a cover crop, not the player's strip */
  border-radius: 6px;
  background: var(--paper);
}
.edition-face img {
  height: 100%;
  width: auto;
  flex: 0 0 auto;
  mix-blend-mode: multiply;
}
.edition-meta {
  display: block;
  padding: .6rem .35rem .45rem;
  overflow-wrap: break-word;
}
.edition-composer {
  display: block;
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--gp-walnut);
}
.edition-title {
  display: block;
  margin-top: .12rem;
  font-family: var(--font-prose);
  font-size: var(--t-title);
  line-height: 1.25;
  color: var(--gp-black);
}
/* The one piece anybody can open without an account. Brass marks it; it is a
   stroke on the card, not a filled badge. */
.edition-open {
  display: inline-block;
  margin-top: .4rem;
  padding: .1rem .5rem;
  border: 1px solid var(--gp-gold);
  border-radius: 999px;
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--gp-walnut);
}

/* ── 3. The desk ──────────────────────────────────────────────
   The pull-back's second step: the same score, now a screen standing on the
   music desk. The tablet is the page's one authored motion moment — clicking it
   dives through the screen into the piece (js/tablet-zoom.js). */
.desk {
  margin-bottom: 3.5rem;
  text-align: center;
}
.desk-stage {
  position: relative;
  margin: 1.5rem auto 0;
  width: min(21rem, 72vw);
}
.tablet {
  position: relative;
  z-index: 2;
  display: block;
  padding: 2.2% 1.8% 3.4%;
  border-radius: 18px;
  background: linear-gradient(160deg, #43434a, #17171b 55%);
  box-shadow: 0 18px 34px rgba(0, 0, 0, .34), inset 0 0 0 1px rgba(255, 255, 255, .1);
  text-decoration: none;
}
.tablet-screen {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 3px;
  background: #fff;
}
.tablet-cam,
.tablet-home {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: #2c2c31;
}
.tablet-cam  { top: .85%; width: 5px; height: 5px; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18); }
.tablet-home { bottom: .7%; width: 13px; height: 13px; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22); }

/* Sticky note over the screen — the whole tablet is the link, this labels it. */
.tablet-note {
  position: absolute;
  left: 50%;
  bottom: 22%;
  z-index: 3;
  transform: translateX(-50%) rotate(-4deg);
  padding: .6rem 1.35rem;
  background: var(--gp-note);
  color: #4a3d16;
  font-family: var(--font-prose);
  font-size: var(--t-title);
  font-weight: 700;
  white-space: nowrap;
  box-shadow: 0 5px 12px rgba(0, 0, 0, .3);
  transition: transform .18s ease-out;
}
a.tablet:hover .tablet-note,
a.tablet:focus-visible .tablet-note { transform: translateX(-50%) rotate(-4deg) scale(1.06); }
a.tablet:focus-visible { outline: 2px solid var(--gp-gold); outline-offset: 4px; }

/* The music desk the screen stands on: the piano's own top, drawn as three
   tonal bands so it needs no photograph and no fixed width. */
.desk-top {
  position: relative;
  z-index: 1;
  height: 26px;
  margin-top: -8px;
  border-radius: 0 0 6px 6px;
  background: linear-gradient(180deg, var(--desk-lit) 0 42%, var(--lid) 42% 70%, var(--lid-shadow));
  box-shadow: 0 -1px 0 rgba(200, 169, 107, .22), 0 10px 20px rgba(0, 0, 0, .28);
}

/* Dive-into-the-tablet overlay (js/tablet-zoom.js). Fixed over everything; the
   script scales the group inside it until the screen covers the viewport. */
.tablet-zoom {
  position: fixed;
  inset: 0;
  z-index: 2000;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.tablet-zoom g { will-change: transform; }
.tablet-zoom-backdrop { fill: var(--gp-black); opacity: 0; }

/* ── 4 & 5. The two stages, a section each ────────────────────
   The two bands alternate value, the way two neighbouring keys do: Study on black,
   Perform on the ivory page. That alternation is also why they need no divider — the
   ground changes at the join — and it doubles the mirroring, since the copy and the
   media swap sides between them as well.

   What a piece is for, once it is open. Each stage takes a full band with its own
   heading: as two columns they had to share one heading and half a screen, which
   read as a feature pair rather than the two halves of a piece's life. Copy and
   media swap sides between the two, which is also the page's one change of rhythm
   after the fold.

   Study leads because that is where a recording earns its place; Perform's copy
   then has to stand on its own, because every self-imported piece has none. */
.stage-band {
  display: grid;
  gap: 1.25rem;
  margin-inline: calc(-1 * var(--bleed));
  padding: 2.5rem var(--bleed);
  margin-bottom: 0;
}
/* The black key. */
.stage-band-dark {
  background: var(--case-band);
  color: var(--gp-ivory);
}
/* Perform is followed by another black band (What it never does), so the join is
   a value change like the one above it and needs no gap. */
.stage-band-flip { margin-bottom: 0; }
.stage-band-copy,
.stage-band-media { min-width: 0; }
.stage-band-title {
  font-size: var(--t-section);
  margin: 0 0 .6rem;
  color: var(--gp-black);
}
.stage-band-dark .stage-band-title { color: var(--gp-ivory); }
.stage-band-lede {
  margin: 0 0 .7rem;
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--gp-charcoal);
  max-width: var(--measure);
}
.stage-band-dark .stage-band-lede { color: var(--case-ink); }
.stage-band-lede:last-child { margin-bottom: 0; }
.stage-band-lede b { font-weight: 700; color: var(--gp-black); }
.stage-band-dark .stage-band-lede b { color: var(--gp-ivory); }

/* ── 6. What it never does ────────────────────────────────────
   The reassurance, stated as fact. A visitor arriving from a practice app is
   looking for exactly this list, and it is cheaper to read than to discover.

   On the black key, like Study: this is the page's other flat statement of what
   the product is, and stating it on the case rather than on a tinted panel is
   what stops it reading as a footnote. It also keeps the alternation going —
   black, ivory, black, ivory — so no rule has to be drawn at any join. */
.never {
  margin-bottom: 0;
  padding: 2.5rem var(--bleed);
  margin-inline: calc(-1 * var(--bleed));
  background: var(--case-band);
  color: var(--gp-ivory);
}
.never > * {
  max-width: 52rem;
  margin-inline: auto;
}
.never-title {
  font-size: var(--t-section);
  margin: 0 auto 1rem;
  color: var(--gp-ivory);
  text-align: center;
}
.never-list {
  display: grid;
  gap: .7rem;
  margin-block: 0;
  padding: 0;
  list-style: none;
}
.never-list li {
  display: flex;
  gap: .6rem;
  font-size: var(--t-body);
  line-height: 1.5;
  color: var(--gp-ivory);
}
/* Drawn from the icon set already in the page, at one weight — not a unicode
   tick standing in for an icon system. Brass rather than walnut: walnut is a
   mark on paper and disappears against the case. */
.never-list i {
  flex: 0 0 auto;
  margin-top: .1rem;
  font-size: var(--t-title);
  line-height: 1.35;
  color: var(--gp-gold);
}
.never-note {
  margin: 1.25rem auto 0;
  font-size: var(--t-label);
  line-height: 1.55;
  color: var(--case-ink);
  text-align: center;
}
.never-note a { color: var(--gp-gold); }

/* ── Rails ────────────────────────────────────────────────────
   One horizontal-scroll component for the shelf, the score crops and the stage
   screenshots. Native scrolling with snap points, so a phone swipes it with no
   JS at all; the arrows are added only where a pointer exists (js/rail.js) and
   scroll the same box. */
.rail {
  display: flex;
  gap: .75rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { min-width: 0; }

.rail-shell { position: relative; }
/* Arrow caps, injected by rail.js only when the device has a fine pointer.
   Circular because they are pressed. */
.rail-arrow {
  position: absolute;
  top: 50%;
  z-index: 4;
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  transform: translateY(-50%);
  border: 1px solid rgba(132, 98, 74, .4);
  border-radius: 50%;
  background: rgba(255, 255, 255, .94);
  color: var(--gp-walnut);
  font-size: var(--t-title);
  cursor: pointer;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .18);
  transition: background .15s, border-color .15s, opacity .15s;
}
/* Inside the shell: a bled-out rail reaches the window edge, so an arrow
   hung outside it would sit off-screen. */
.rail-arrow[data-dir="-1"] { left: .25rem; }
.rail-arrow[data-dir="1"]  { right: .25rem; }
.rail-arrow:hover { background: #fff; border-color: var(--gp-gold); }
.rail-arrow:disabled { opacity: 0; pointer-events: none; }
.rail-arrow:focus-visible { outline: 2px solid var(--gp-gold); outline-offset: 2px; }
/* A screenshot rail: one shot per slide, its legend under it. */
.shot {
  flex: 0 0 100%;
  margin: 0;
  scroll-snap-align: center;
}
.shot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: 10px;
  border: 1px solid rgba(132, 98, 74, .2);
  background: var(--paper);
}
.stage-band-dark .shot img { border-color: rgba(200, 169, 107, .28); }
.shot figcaption {
  margin-top: .7rem;
  font-size: var(--t-label);
  line-height: 1.4;
  color: #5c5c5c;
}
.stage-band-dark .shot figcaption { color: var(--case-ink); }
.stage-band-dark .rail-arrow {
  background: rgba(16, 13, 10, .92);
  border-color: rgba(200, 169, 107, .45);
  color: var(--gp-gold);
}
.stage-band-dark .rail-arrow:hover { background: var(--case-lit); border-color: var(--gp-gold); }

/* ── Catalog heading row ──────────────────────────────────────
   On the homepage it is the label on its rule, separating the pitch from the
   teaser. On /catalog/ there is no pitch above it, so the row also carries the
   account's two destinations. */
.home-catalog-bar {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-bottom: 1rem;
}
.home-catalog-bar .home-catalog-head { flex: 1 1 auto; margin-bottom: 0; }
.home-catalog-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  font-size: .82rem;
  white-space: nowrap;
}
.home-catalog-actions a { color: var(--gp-walnut); text-decoration: none; }
.home-catalog-actions a:hover { text-decoration: underline; }
.home-catalog-head {
  display: flex;
  align-items: center;
  gap: .9rem;
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--gp-walnut);
  margin: 0 0 1rem;
}
.home-catalog-head::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: rgba(0, 0, 0, .12);
}
.home-catalog-blurb {
  margin: -.5rem 0 1.1rem;
  font-size: var(--t-label);
  color: #5c5c5c;
}
.home-catalog-blurb a { color: var(--gp-walnut); }

/* ── 7. The catalog ───────────────────────────────────────────
   The homepage's last section, built like the four above it: a centred claim,
   then a rail of the catalog's own cards, then the way out to the whole thing.
   Deliberately not a second catalog — no search, no count of what's on screen. */
.catalog-band {
  /* The black band above it ends at its own edge, so this section carries the
     whole gap between them. */
  padding-block: 3.5rem 0;
  margin-bottom: 1rem;
}
.catalog-band .home-band-head { margin-bottom: 1.5rem; }
.catalog-band .home-band-lede a { color: var(--gp-walnut); }

/* The rail bleeds to the window edge like the shelf's, so the last card is cut
   rather than fenced — that cut is what says the rail keeps going. */
.catalog-rail-wrap { margin-inline: calc(-1 * var(--bleed)); }
.catalog-rail {
  padding-inline: var(--bleed);
  padding-block: .5rem 1rem;
  margin: 0;
  list-style: none;
}
.catalog-rail > li {
  display: flex;                /* stretches the card partial's .col wrapper to
                                   the rail's height, so the cards line up */
  flex: 0 0 auto;
  width: min(17rem, 74vw);
  scroll-snap-align: center;
}
.catalog-rail > li > * { width: 100%; }

.catalog-band-more {
  margin: 1.5rem 0 0;
  text-align: center;
}
/* The out-link is the section's whole purpose, so it is a button, not a "more"
   link — but outline rather than filled: the hero's CTA above it is the page's
   one primary action and this must not compete with it. */
.catalog-band-more .btn {
  --bs-btn-color: var(--gp-walnut);
  --bs-btn-border-color: rgba(132, 98, 74, .5);
  --bs-btn-hover-color: var(--gp-ivory);
  --bs-btn-hover-bg: var(--gp-walnut);
  --bs-btn-hover-border-color: var(--gp-walnut);
}

/* ── Library page ─────────────────────────────────────────────
   Empty slots up to the plan's capacity, and the import card in the first one. */
.library-paper {
  background-image: url("../ivory-pattern.jpg");
  background-size: cover;
  background-position: center;
  background-attachment: fixed;
}
.library-slot {
  min-height: 240px;
  border: 2px dashed rgba(0, 0, 0, .22);
  border-radius: 6px;
  color: rgba(0, 0, 0, .4);
  background: rgba(255, 255, 255, .25);
  transition: border-color .15s, color .15s, background .15s;
}
.library-slot:hover,
.library-import:hover {
  border-color: var(--gp-gold);
  color: var(--gp-walnut);
  background: rgba(255, 255, 255, .5);
}
.library-import { cursor: pointer; }
.import-cloud { font-size: 3.5rem; line-height: 1; color: rgba(0, 0, 0, .16); }
.library-import.drag-over .import-cloud { color: var(--gp-gold); }
.library-import.drag-over {
  border-color: var(--gp-gold);
  border-style: solid;
  color: var(--gp-walnut);
  background: rgba(200, 169, 107, .18);
}
.library-import.importing { opacity: .6; pointer-events: none; }

/* ── Wider than a phone ───────────────────────────────────────
   The phone layout is the design; these are the cases where there is more room,
   not a different page. */
@media (min-width: 48em) {
  .shelf-rail > li { width: 21rem; }
  .edition-face { height: 7.5rem; }
  .stage-band {
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: 3.5rem;
    align-items: center;
    padding-block: 4rem;
  }
  /* Perform reads media-first, so the pair alternates instead of marching. The
     tracks are mirrored and each child placed explicitly: reordering alone would
     have left the media in the narrow column and the copy in the wide one. */
  .stage-band-flip {
    grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
    margin-bottom: 4.5rem;
  }
  .stage-band-flip .stage-band-media { grid-area: 1 / 1; }
  .stage-band-flip .stage-band-copy  { grid-area: 1 / 2; }
  .never { padding: 2rem var(--bleed); }
  .never-list { grid-template-columns: 1fr 1fr; column-gap: 2rem; }
}
@media (min-width: 64em) {
  /* Copy beside the sheet instead of above it: the figure is portrait, so a wide
     window has room next to it and none below. */
  /* Copy and CTA stack in the left column, the sheet spans both rows beside them.
     Explicit placement, because the phone order (copy, figure, CTA) would otherwise
     put the figure between them here too. */
  .seam-hero {
    grid-template-columns: minmax(0, 30rem) auto;
    grid-template-rows: auto auto;
    align-items: center;
    justify-content: center;      /* centre the pair of tracks, not just their contents */
    justify-items: start;
    column-gap: 4rem;
    row-gap: 1.75rem;
    padding-block: 3.5rem 4rem;
  }
  .seam-hero::before {
    background: linear-gradient(90deg,
      rgba(10, 9, 8, .82) 0%, rgba(10, 9, 8, .5) 40%, transparent 66%);
  }
  /* With no bars to compare there is no stage, so the copy takes the whole
     width rather than sitting in a two-column grid with an empty second column. */
  .seam-hero-solo { grid-template-columns: 1fr; }
  .seam-hero-solo .seam-copy,
  .seam-hero-solo .seam-cta { grid-column: 1; text-align: center; }
  .seam-hero-solo .seam-copy { max-width: 40rem; margin-inline: auto; }
  .seam-copy { grid-area: 1 / 1; align-self: end; text-align: left; }
  .seam-cta  { grid-area: 2 / 1; align-self: start; margin-top: 1.75rem; text-align: left; }
  .seam-stage {
    grid-area: 1 / 2 / 3 / 3;
    align-self: center;
    width: min(21rem, 27vw);
  }
}
@media (min-width: 75em) {
  .desk-stage { width: 26rem; }
}
/* ── A desktop that is genuinely wide ─────────────────────────
   Past ~1440px a page whose tracks are pinned in rem sits in a column down the
   middle — at the base sizes (30rem of hero copy, 34rem of band lede) two thirds
   of an 1850px monitor is wall.

   Lines must not run longer to fill it: measure is the one thing that may not
   change, and 34rem of 1.15rem text is already ~68 characters. Type and its
   container grow by the same factor instead, so each block keeps the same line
   length and simply occupies the room it has. */
@media (min-width: 90em) {
  .home-wrap {
    --measure: 40rem;
    --t-section: clamp(1.65rem, 3.4vw, 2.9rem);
    --t-body: 1.1rem;
    /* Two steps that only exist past this breakpoint. --t-hero stops short of
       --t-display on purpose: at 4.1rem the headline takes a third line in a
       38rem column, which costs more than the extra size buys. */
    --t-hero: 3.5rem;
    --t-lede: 1.28rem;
  }
  .seam-copy { max-width: var(--measure); }
  .seam-title { font-size: var(--t-hero); }
  .seam-lede { font-size: var(--t-lede); }
  .seam-hero {
    grid-template-columns: minmax(0, var(--measure)) auto;
    column-gap: 5rem;
  }
  /* The tablet grows with the copy beside it: a device that stays 21rem while the
     headline gains 8px reads as a phone propped next to a poster. */
  .seam-stage { width: min(26rem, 27vw); }
  /* The fine print does not follow the column: at .82rem, 38rem is ninety-odd
     characters, which is a paragraph pretending to be a footnote. */
  .seam-note { max-width: 30rem; }
}

@media (prefers-reduced-motion: reduce) {
  .edition,
  .tablet-note,
  .rail { transition: none; }
  .rail { scroll-behavior: auto; }
}
