/* The Long Line — public content pages (composer, later work/home).
   Same palette as the timeline island; this is the server-rendered, crawlable
   surface, so styles are global here (no shadow root). */
:root{
  --paper:#EAE2CF; --paper-hi:#F3ECDA; --paper-lo:#DED4BC;
  --ink:#2A2621; --ink-soft:#5c5544; --rule:#C4B896;
  --brass:#C9A227; --brass-deep:#A2801A; --muted:#8A7F66;
  --serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
  --sans:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  /* UI control tokens — the toolbar search box, segmented toggle and icon buttons all read
     from these so the "chrome" stays one design language and doesn't drift per-edit. These
     mirror the timeline island header (--ll-ctrl-radius / --ll-search-w in
     frontend/src/styles.ts); change both together. Editorial type stays --serif; all chrome
     is --sans. */
  --ctrl-radius:2px;                   /* corner radius: search, segmented toggle, buttons */
  --ctrl-h:30px;                       /* height of the segmented toggle + icon buttons */
  --icon-btn:31px;                     /* square icon-button cell */
  --search-w:clamp(180px,24vw,300px);  /* centred search box — responsive, capped */
  --pad-x:10px;                        /* the one page gutter: bio, Works table, header, footer all read it.
                                          10px on phones; bumped to 20px on the wider iPad-portrait below. */
  --tri-size:.72rem;                   /* filled-triangle glyph size — sort arrows + the bio "Show more ▼" disclosure (DRY). Dropdown combos use a chevron, not this. */
}
/* iPad PORTRAIT is much wider than a phone, so 10px lets the prose run too wide and hugs the
   edge — give it a 20px gutter. Phones keep 10px; landscape/desktop use the centred 56rem column. */
@media (pointer:coarse) and (min-width:768px) and (max-width:1023.98px){
  :root{--pad-x:20px}
}
*{box-sizing:border-box}
html{background:var(--paper);scrollbar-gutter:stable}   /* always reserve the scrollbar gutter, so filtering the Works list (which changes the page height) never reflows the content width — the 1fr Title column was jumping when the scrollbar came and went */
/* Sticky footer: a full-height flex column so a short page (e.g. a sparse WDP) pushes the
   footer to the bottom of the viewport instead of floating it mid-screen. */
body{margin:0;color:var(--ink);font-family:var(--serif);line-height:1.6;
  min-height:100vh;min-height:100dvh;display:flex;flex-direction:column;
  background:
    radial-gradient(1200px 400px at 50% -10%, #f6f0e2 0%, transparent 70%),
    var(--paper);}
body > main{flex:1 0 auto}         /* the content grows to fill; the footer sits below it */
.site-footer{flex-shrink:0}

/* chrome */
.site-header,.site-footer{font-family:var(--sans);font-size:.8rem;letter-spacing:.02em}
.site-header{display:flex;align-items:center;height:46px;padding:0 var(--pad-x);
  position:sticky;top:0;z-index:4;border-bottom:1px solid var(--rule);
  background:linear-gradient(180deg,var(--paper-hi),var(--paper))}
.wordmark{display:inline-flex;align-items:center;gap:.5rem;color:var(--brass-deep);font-family:var(--serif);font-weight:600;
  font-size:1.05rem;text-decoration:none;letter-spacing:.01em}
.wordmark:hover{color:var(--brass)}
.site-logo{height:24px;width:auto}   /* the "f" mark, left of the wordmark */
/* tagline beside the wordmark — mirrors the timeline header's .ll-tag (desktop only) */
.site-tag{margin-left:.5rem;font-family:var(--sans);font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;margin-top:3px;color:var(--muted);white-space:nowrap}
/* iPad PORTRAIT (and phones): drop the tagline — the page header AND the docked Works bar — so
   the wordmark + Works controls have room. Landscape (≥1024) and desktop keep it. */
@media (pointer:coarse) and (max-width:1023.98px){
  .site-tag,.wh-tag{display:none}
}
/* the page's title (composer/work name) docks here, centred, once the reader scrolls past the
   on-page <h1> (works.js toggles .show). Same serif as the name, sized like the wordmark. It
   sits in the header (z-index 4) BELOW the works toolbar (z-index 5), so the toolbar replaces
   it when that docks. */
.site-dock{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);max-width:55%;
  font-family:var(--serif);font-weight:600;font-size:1.05rem;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  opacity:0;pointer-events:none;transition:opacity .2s ease}
.site-dock.show{opacity:1}
/* Matches the timeline island footer: small sans text, wordmark in brass serif. */
.site-footer{padding:.55rem var(--pad-x);margin-top:0;color:var(--muted);font-size:.72rem;
  border-top:1px solid var(--rule);text-align:center;
  position:sticky;bottom:0;z-index:5;background:var(--paper)}
.site-footer a{color:var(--brass-deep);text-decoration:none;font-family:var(--serif);font-weight:600;font-size:14px}
/* Global focus: a brass keyboard-focus ring, not the browser blue. */
:focus-visible{outline:2px solid var(--brass-deep);outline-offset:2px}
:focus:not(:focus-visible){outline:none}

/* layout */
.composer{max-width:56rem;margin:0 auto;padding:1rem var(--pad-x) .75rem;
  overflow-wrap:break-word}   /* matches the Works-table width; break long bio words/URLs so a narrow
                                 phone doesn't overflow sideways (they'd otherwise push past the gutter) */

/* Two-column header: photo + facts LEFT (¼), name + bio RIGHT (¾) — full 56rem, like Works. */
.c-head{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,3fr);column-gap:1.8rem;row-gap:0;align-items:start;margin-bottom:1.6rem;
  grid-template-areas:"side name" "side tag" "side body";
  grid-template-rows:auto auto 1fr}   /* name/tagline pack at the top; the flexible body row absorbs the
                                         photo column's extra height, so a short first paragraph sits UP
                                         under the tagline instead of floating to the middle */
.c-name{grid-area:name}
.c-head>.c-tagline{grid-area:tag}
.c-side{grid-area:side}
.c-body{grid-area:body}
.work-detail .c-head{display:block}   /* the work page has no portrait column — full-width title, not the CDP's 1fr photo / 3fr body split */
.c-name,.c-side,.c-body{min-width:0}
/* Guard: clip any stray horizontal overshoot in the name/portrait/bio area. `clip` NOT `hidden` —
   hidden forces overflow-y:auto, making .c-head a scroll container that JIGGLES under iOS momentum
   scroll. clip establishes no scroll container, so it can't jiggle; the real width fix is the
   max-width caps on .c-head + .c-body below. Scoped to .c-head (a SIBLING of .works-block). */
.c-head{overflow-x:clip}
.c-facts .c-line{font-size:.85rem;margin:.16rem 0}
.c-facts .timeline-link-p{margin:.9rem 0 0}
@media (max-width:560px){
  .site-tag{display:none}                    /* compact header: logo + wordmark only */
  .site-dock{display:none}                   /* no centred docked name on the narrow mobile header */
  /* Phones: name leads, then photo beside facts, then the bio below. minmax(0,1fr) — NOT 1fr — so the
     column can't be widened past the viewport by a grid item's min-content (the portrait's intrinsic
     image width bleeding through the nested .c-side flex is what pushed the bio wide on iOS; min-width:0
     on the items didn't cap the track). This is the real fix for the sideways-nudge; the clip was only
     ever a backstop. */
  .c-head{grid-template-columns:minmax(0,1fr);grid-template-areas:"name" "tag" "side" "body";grid-template-rows:none;row-gap:1rem}
  .c-side{display:flex;gap:1rem;align-items:flex-start}
  .portrait{flex:0 0 38%;width:38%;margin:0}
  .c-facts{flex:1;min-width:0}
  .c-name{font-size:30px}
  .timeline-link-p{margin:.6rem 0 0}
  /* Hard-cap the bio to the viewport (JH: it's really .c-body). Belt to the grid-track suspenders:
     even if some descendant re-widens the track, .c-body itself can never exceed the .composer content
     box, so the bio text always wraps within view AND stays aligned with the name/facts/Works above and
     below. Subtract the gutter on BOTH sides (2 * --pad-x = 20px on phones) — a smaller value would let
     the text run to the screen edge with no right gutter. */
  .c-body{max-width:calc(100vw - 2 * var(--pad-x))}
  .c-head{max-width:calc(100vw - 2 * var(--pad-x))}   /* same cap on the header — the width fix, no scroll container */
}

.portrait{margin:0 0 .8rem;width:100%}
.portrait img{width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;object-position:center top;
  display:block;background:var(--paper-lo);
  border:1px solid var(--rule);border-radius:2px;padding:6px;
  box-shadow:0 6px 22px #2a262114;
  /* engraving-on-a-mat feel: cream border via padding + inner rule */
  outline:1px solid var(--paper-hi);outline-offset:-7px}
.portrait figcaption{font-family:var(--sans);font-size:.66rem;color:var(--muted);
  margin-top:.4rem;line-height:1.35}

h1{font-size:36px;line-height:1.1;margin:0 0 .35rem;font-weight:600}
/* meta beside the portrait — same treatment as the timeline panel: name, then
   tagline / years / era / tier all in the sans font, muted colour, no chips */
.c-tagline{font-family:var(--sans);font-size:1rem;color:var(--muted);line-height:1.4;margin:.15rem 0 .7rem}
.c-line{font-family:var(--sans);font-size:.9rem;color:var(--muted);margin:.12rem 0}
.timeline-link{font-family:var(--sans);font-size:.82rem;color:var(--brass-deep);text-decoration:none}
.timeline-link:hover{color:var(--brass);background-size:100% 1px}

/* text */
.lead{font-size:1.2rem;line-height:1.55;color:var(--ink);margin:1.4rem 0 1rem}
/* append IPA-capable sans fonts so the Wikipedia pronunciation marks (ˈ ː ◌̩) don't box in the serif */
.bio{font-size:1.125rem;font-family:var(--serif),"Segoe UI","Helvetica Neue",Arial,sans-serif;
  /* "Show more" reveals later paragraphs that can hold long compound names, foreign words, or the
     odd wide element. Break tokens ANYWHERE (stronger than .composer's break-word — it also shrinks
     min-content so a grid track can't be forced wide) and cap wide children, so expanding never lets
     a phone scroll sideways. The .c-head overflow-x clip is only the backstop. (NTI-379) */
  overflow-wrap:anywhere}
.bio :is(img,svg,video,iframe,table,pre){max-width:100%}
.bio pre{white-space:pre-wrap}
.bio table{display:block;overflow-x:auto}   /* a wide table scrolls within itself, not the page */
.bio p{margin:0 0 1rem}
/* Editorial bios can carry headings (H1–H3 from the WYSIWYG). Size them explicitly — otherwise the
   browser default (h2 ≈ 1.5em bold) looks oversized against the 1.125rem serif body. */
.bio h1,.bio h2,.bio h3{line-height:1.25;font-weight:600;margin:1.3rem 0 .5rem}
.bio h1{font-size:1.5em}
.bio h2{font-size:1.25em}
.bio h3{font-size:1.1em}
/* Collapsed bio (NTI-379): paragraph 1 in full, then a fading PEEK of paragraph 2 — Amazon "See more"
   style, a stronger "there's more here" cue than a hard cut. The mask fades the text to transparent so
   it blends into the page on any background; paragraph 3+ stays hidden. Expanded state is untouched. */
.bio.collapsed .bio-extra{display:none}
.bio.collapsed .bio-teaser{
  display:block;margin-bottom:0;
  max-height:3.1em;overflow:hidden;                 /* ~2 lines — a first-sentence-ish peek */
  -webkit-mask-image:linear-gradient(to bottom,#000 55%,transparent);
          mask-image:linear-gradient(to bottom,#000 55%,transparent);
}
/* same button treatment as the timeline panel's Open/Close Bio, but not full-width */
.bio-toggle{display:inline-flex;align-items:center;justify-content:center;width:120px;
  margin:1rem 0;padding:.4rem .5rem;font-family:var(--sans);font-size:.72rem;
  letter-spacing:.06em;text-transform:uppercase;color:var(--brass-deep);
  background:none;border:1px solid var(--rule);border-radius:2px;cursor:pointer}
.bio-toggle[hidden]{display:none}
.bio-toggle:hover{border-color:var(--brass);background:color-mix(in srgb, var(--brass) 8%, transparent)}
ul.movements{font-size:1.125rem}
.bt-tri{font-size:1em;line-height:0;margin-left:.4em;vertical-align:-.075em}

/* Primary CTA — same shape as .bio-toggle (uppercase sans, radius 2), but INVERTED into
   a filled button: brass-deep background, ink text. The one strong action on a page or
   popup. Reusable — apply `class="cta"` to that action's link/button. */
.cta{
  align-items: center;
  background: var(--brass);
  border-radius: 2px;
  border: 1px solid var(--brass);
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  font-family: var(--sans);
  font-size: .72rem;
  font-weight: 400;
  justify-content: center;
  letter-spacing: .06em;
  line-height: normal;
  margin: 1rem 0;
  padding: .4rem .5rem;
  text-decoration: none;
  text-transform: uppercase;
}
.cta:hover{background:var(--brass);border-color:var(--brass);color:var(--ink)}
.attribution{font-family:var(--sans);font-size:.76rem;color:var(--muted);margin:.2rem 0 0}
.attribution a{color:var(--brass-deep);text-decoration:none}
.attribution a:hover{background-size:100% 1px}

/* blocks */
.block{margin-top:2.2rem;border-top:1px solid var(--rule);padding-top:1.2rem}
.block h2{font-family:var(--sans);font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);margin:0 0 .8rem;font-weight:600}

/* works */
.works{list-style:none;margin:0;padding:0}
/* Column grid shared by the sortable header and every row, so Title | Year | Genre align. */
.works li,.w-thead{display:grid;grid-template-columns:minmax(0,1fr) 3.5rem 4.5rem;gap:.9rem;align-items:baseline}   /* Title gets the room; Year needs only 4 digits, Genre only a funnel + short label */
.works li{padding:.45rem var(--pad-x);margin-inline:calc(-1 * var(--pad-x));border-bottom:1px dotted var(--rule)}   /* full-bleed rows: content stays bio-aligned, but the divider runs to the gutter like the HP list (padding:0 10px) */
.works li:last-child{border-bottom:0}
.works .w-title{font-style:italic;min-width:0}
.works .w-sub{font-style:italic;color:var(--ink-soft)}
.works .w-cat{font-family:ui-monospace,Menlo,monospace}
.works .w-flag{color:var(--brass-deep);border:1px solid var(--brass);border-radius:10px;padding:0 .4rem}
.works .gateway .w-title{font-weight:600}
.works .gateway{background:linear-gradient(90deg,#c9a2270f,transparent)}
.works .w-year{font-family:ui-monospace,Menlo,monospace;font-style:normal;font-size:.82rem;color:var(--muted);white-space:nowrap}
.works .w-genre{font-family:var(--sans);font-size:.76rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Sortable column header (Title / Year), matching the composers-page header. */
/* Docks just below the works toolbar as you scroll (all devices), so the column labels stay
   with the rows. Opaque bg + z-index so rows pass under it; top matches the toolbar height. */
.w-thead{padding:.5rem var(--pad-x);margin-inline:calc(-1 * var(--pad-x));border-bottom:1px solid var(--rule);align-items:center;
  position:sticky;top:var(--nav-h,56px);z-index:4;background:var(--paper)}
.w-thead[hidden]{display:none}
.w-th{font-family:var(--sans);font-size:.78rem;letter-spacing:.08em;text-transform:uppercase;font-weight:600;
  color:var(--muted);background:none;border:0;padding:0;text-align:left;display:flex;align-items:center;gap:.3rem;min-width:0}
button.w-th{cursor:pointer}
button.w-th:hover,.w-th.on{color:var(--brass-deep)}
.w-arw{font-size:var(--tri-size);line-height:1;color:var(--brass-deep)}
/* Genre filter — a multi-select combo box that IS the Genre column header. It shows only a
   funnel icon (no "All genres"/selection label), so the column stays narrow and the Title column
   gets the room. The funnel goes brass + filled when a filter is active. Panel holds the checkboxes. */
.w-multi{position:relative;display:inline-flex;align-items:center;justify-self:start}
.w-multi[open]{z-index:31}
.w-multi-btn{list-style:none;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:.28rem;
  height:1.7rem;padding:0 .25rem;cursor:pointer;color:var(--brass-deep);font-size:.78rem;
  background:none;border:0;border-radius:var(--ctrl-radius)}   /* brass like the header accents; it's a dropdown, so it carries a chevron */
.w-multi-btn::-webkit-details-marker{display:none}
.w-multi-btn:hover{background:var(--paper-hi)}
.w-multi-btn:focus-visible{outline:2px solid var(--brass-deep);outline-offset:1px}
.w-multi-funnel{display:block;width:15px;height:15px}
/* the dropdown chevron (a bordered square rotated 45°, brass); flips up when open */
.w-multi-car{flex:0 0 auto;box-sizing:border-box;width:.5em;height:.5em;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;transform:rotate(45deg)}
.w-multi[open] .w-multi-car{transform:rotate(225deg)}
.w-multi.is-filtering .w-multi-funnel{fill:currentColor}   /* active filter → filled funnel */
.w-multi-panel{position:absolute;z-index:31;top:calc(100% + 4px);right:0;min-width:max-content;
  background:var(--paper-hi);border:1px solid var(--rule);border-radius:var(--ctrl-radius);
  box-shadow:0 4px 14px #2a262122;padding:.25rem;white-space:nowrap}
.w-multi-opt{display:flex;align-items:center;gap:.45rem;padding:.3rem .45rem;border-radius:2px;
  font-family:var(--sans);font-size:.72rem;font-weight:400;letter-spacing:0;text-transform:none;
  color:var(--ink);cursor:pointer}
.w-multi-opt:hover{background:var(--paper-lo)}
.w-multi-opt input{accent-color:var(--brass-deep);cursor:pointer;margin:0}
/* "Clear all" footer — shown only while something is selected (JS toggles [hidden]). */
.w-multi-clear{display:block;width:100%;text-align:left;margin-top:.15rem;padding:.34rem .45rem;
  border:0;border-top:1px solid var(--rule);background:none;cursor:pointer;
  font-family:var(--sans);font-size:.7rem;letter-spacing:.02em;color:var(--muted)}
.w-multi-clear[hidden]{display:none}
.w-multi-clear:hover{color:var(--brass-deep)}
/* Shared styling for every native <select> on the server pages (works genre, /composers/
   filters). Add class="dropdown" to any new select. Brass control look, brass focus — never
   the browser's blue: a mouse click just tints the border, keyboard focus keeps a brass ring.
   (The Shadow-DOM timeline dropdown, .ll-genre, is a separate stylesheet and mirrors this.) */
.dropdown{font-family:var(--sans);font-size:.8rem;color:var(--brass-deep);background:var(--paper-hi);
  border:1px solid var(--rule);border-radius:var(--ctrl-radius);padding:.2rem .4rem;cursor:pointer}
.dropdown:hover{border-color:var(--brass)}
.dropdown:disabled{opacity:.5;cursor:default}
.dropdown:focus{outline:none;border-color:var(--brass-deep)}
.dropdown:focus-visible{outline:2px solid var(--brass-deep);outline-offset:1px}
.works .w-link{color:var(--brass-deep);text-decoration:none}   /* clickable → link colour (has a detail page) */
.works .w-link:hover{background-size:100% 1px}
.works .w-plain{color:inherit;cursor:default}                  /* no extra info → plain, non-clickable text */

/* work detail page */
.recordings{list-style:none;margin:0;padding:0}
.recordings li{padding:.6rem 0;border-bottom:1px dotted var(--rule)}
.recordings li:last-child{border-bottom:0}
/* custom audio player — styled to match the toolbar buttons */
.player{display:flex;align-items:center;gap:.6rem;max-width:34rem}
.player-btn{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;border:1px solid var(--rule);border-radius:4px;
  background:var(--paper-hi);color:var(--brass-deep);cursor:pointer;line-height:0}
.player-btn:hover{border-color:var(--brass);background:color-mix(in srgb, var(--brass) 12%, transparent)}
.player-btn svg{width:14px;height:14px;fill:currentColor}
.player-bar{flex:1;height:7px;background:var(--paper-lo);border:1px solid var(--rule);
  border-radius:4px;overflow:hidden;cursor:pointer}
.player-fill{height:100%;width:0;background:var(--brass)}
.player-time{flex:0 0 auto;font-family:ui-monospace,Menlo,monospace;font-size:.72rem;
  color:var(--muted);min-width:6.5em;text-align:right}
.rec-cred{font-family:var(--sans);font-size:.72rem;color:var(--muted);margin:.4rem 0 0}
.rec-cred a{color:var(--brass-deep);text-decoration:none}
.rec-cred a:hover{background-size:100% 1px}
.w-crumb{font-family:var(--sans);font-size:.8rem;margin:0 0 .35rem}
.w-crumb a{color:var(--brass-deep);text-decoration:none}
.w-crumb a:hover{color:var(--brass);background-size:100% 1px}
.w-back{margin-top:2.2rem}

/* --- works toolbar (list / timeline / sort / fullscreen) ------------------ */
.works-block{position:relative;padding-top:0;scroll-margin-top:60px}   /* #works anchor clears the sticky bar */
/* (The works section is now the full 56rem of .composer — no break-out needed.) */
/* toolbar pins to the top of the window while the works section is in view. A fixed
   min-height (matching the site header) means docking doesn't change the bar's height. */
.works-head{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;column-gap:.8rem;
  position:sticky;top:0;z-index:5;background:var(--paper);   /* above the column header (.w-thead, z-index:4) so the
                                                                search autocomplete, trapped in this stacking context, can overlay it */
  min-height:var(--nav-h,56px);padding:.35rem 0;margin-bottom:.4rem;border-bottom:1px solid var(--rule)}   /* in-flow: no own gutter (composer's --pad-x), so "Works" + buttons line up with the table + bio */
/* Desktop: a homepage wordmark on the left (a quick way back once you've scrolled and the
   bar has docked over the site header), the search centred, controls on the right — the
   same three-zone shape as the timeline island header. */
/* Left slot: the "Works" heading in normal flow; when the bar DOCKS it swaps to the "f"
   logo, so the docked bar merges into the page header (which also leads with the logo).
   Only one shows at a time — both live in grid column 1. */
.wh-title{grid-column:1;justify-self:start;display:block}
.works-head h2.wh-title{margin:0}
.wh-home{grid-column:1;justify-self:start;display:none;align-items:center;gap:.5rem;text-decoration:none}
.wh-logo{height:24px;width:auto}   /* same as .site-logo so the docked bar lines up with the page header */
/* name + tagline beside the logo — they live inside .wh-home, which only shows when docked or
   full-window, so the docked bar reads exactly like the page header. Hidden on mobile (the
   fixed narrow left column only has room for the mark). */
.wh-brand{font-family:var(--serif);font-weight:600;font-size:1.05rem;color:var(--brass-deep);white-space:nowrap}
.wh-tag{font-family:var(--sans);font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap}
.works-head.stuck .wh-title{display:none}
.works-head.stuck .wh-home{display:flex}
.works-sentinel{display:block;height:1px;margin-bottom:-1px}
/* Docked bar goes FULL-WIDTH (like the page header it merges into, and like the full-window
   bar): logo + wordmark hug the left, controls hug the right, the search stays centred in the
   viewport (unchanged from the in-flow bar, since it's the centre grid column either way). The
   in-flow bar, by contrast, is the narrow 44rem column with "Works" on its left. 10px sides
   line the logo up with the page header. `.stuck` is toggled by works.js. */
.works-head.stuck{width:100vw;margin-left:calc(50% - 50vw);z-index:5;padding:.35rem var(--pad-x)}   /* docked: breaks full-width, re-pads the same gutter → no shift on docking */
.works-block.fs-on .works-head .wh-title{display:none}   /* full-window reads as docked: logo, not "Works" */
.works-block.fs-on .wh-home{display:flex}
.works-tools{grid-column:3;justify-self:end;display:flex;align-items:center;gap:.5rem;font-family:var(--sans)}
.works-tools[hidden]{display:none}

/* Works search — centred; sizing + placeholder styling standardized to the timeline island
   header search (.ll-search / .ll-q): same padding, radius, font and muted placeholder, just
   a touch wider. Fills the bar on mobile (see the media query). The autocomplete mirrors its
   width so long titles are readable; selecting a result scrolls to + highlights the work. */
.wt-search{grid-column:2;justify-self:center;width:var(--search-w);min-width:0;position:relative;
  display:flex;align-items:center;gap:.4rem;font-family:var(--sans);
  background:var(--paper-hi);border:1px solid var(--rule);border-radius:var(--ctrl-radius);padding:.26rem .5rem}
.wt-search[hidden]{display:none}
.wt-search:focus-within{border-color:var(--brass);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--brass) 26%,transparent)}
.wt-q-ic{flex:0 0 auto;color:var(--muted)}
.wt-q{flex:1;min-width:0;border:0;background:none;font:inherit;font-size:.8rem;line-height:normal;color:var(--ink)}
.wt-q::placeholder{color:var(--muted)}
.wt-q:focus{outline:none}
.wt-q::-webkit-search-cancel-button,.wt-q::-webkit-search-decoration{-webkit-appearance:none}
/* "/" shortcut hint — same treatment as the island header's .ll-slash; JS focuses the box
   on "/" and hides this while there's text (see works.js) */
.wt-slash{flex:0 0 auto;font:inherit;font-size:.7rem;line-height:1;color:var(--muted);
  background:var(--paper);border:1px solid var(--rule);border-radius:3px;padding:.12rem .34rem}
.wt-slash[hidden]{display:none}
.wt-qx{flex:0 0 auto;border:0;background:none;color:var(--muted);cursor:pointer;
  font-size:1.05rem;line-height:1;padding:0 .1rem}
.wt-qx:hover{color:var(--ink)}
.wt-qx[hidden]{display:none}
.wt-ac{position:absolute;left:0;right:0;top:calc(100% + 5px);z-index:30;
  background:var(--paper-hi);border:1px solid var(--rule);border-radius:5px;
  box-shadow:0 10px 28px #2a262122;max-height:min(60vh,340px);overflow-y:auto;padding:.25rem}
.wt-ac[hidden]{display:none}
.wt-ac-opt{display:block;width:100%;text-align:left;font:inherit;font-size:.82rem;
  border:0;background:none;color:var(--ink);padding:.4rem .55rem;border-radius:3px;cursor:pointer}
.wt-ac-opt:hover,.wt-ac-opt.active{background:color-mix(in srgb,var(--brass) 14%,transparent)}
.wt-ac-yr{color:var(--muted);font-family:ui-monospace,Menlo,monospace;font-size:.9em;margin-left:.4em}
.wt-ac-empty{padding:.5rem .6rem;font-size:.78rem;color:var(--muted)}
/* the work a search result jumps to — highlighted in high brass, and it stays put so the
   title link is easy to find and click. Clears on the next jump or when search is cleared. */
.works .w-hit,.tl-row.w-hit{background:color-mix(in srgb,var(--brass) 42%,transparent);
  border-radius:3px;transition:background .3s ease;cursor:pointer}   /* whole highlighted row is clickable */
.works .w-hit{box-shadow:0 0 0 3px color-mix(in srgb,var(--brass) 42%,transparent)}

/* view toggle — a segmented control. Desktop matches the HP orientation toggle (squared,
   --ctrl-radius); mobile rounds it into an iOS pill (see the media query). overflow:hidden
   clips the active brass segment to the shape. The Settings button stays separate, outside. */
.wt-seg{flex:0 0 auto;display:inline-flex;height:var(--ctrl-h);border:1px solid var(--rule);border-radius:var(--ctrl-radius);overflow:hidden}
.wt-chip{display:inline-flex;align-items:center;justify-content:center;font:inherit;
  border:0;border-right:1px solid var(--rule);background:var(--paper-hi);
  color:var(--brass);padding:0 .5rem;cursor:pointer;line-height:0}   /* mobile widens these (see media query) */
.wt-chip:last-child{border-right:0}
.wt-chip svg{display:block}
.wt-chip:hover{color:var(--brass-deep)}
.wt-chip.on{background:var(--brass);color:#fff}

/* single-icon buttons, all squared to a 31px cell so the toolbar reads as an even row */
.wt-btn{display:inline-flex;align-items:center;justify-content:center;width:var(--icon-btn);height:var(--ctrl-h);
  font:inherit;border:1px solid var(--rule);border-radius:var(--ctrl-radius);background:var(--paper-hi);
  color:var(--brass);cursor:pointer;line-height:0}
.wt-btn:hover:not(:disabled){border-color:var(--brass);color:var(--brass-deep)}
.wt-btn:disabled{opacity:.38;cursor:not-allowed}
.wt-btn.active,.wt-btn.active:hover:not(:disabled){border-color:var(--brass-deep);color:#fff;
  background:var(--brass)}

.wt-sort,.wt-filter{position:relative}
.wt-menu{position:absolute;right:0;top:calc(100% + 5px);z-index:20;min-width:196px;
  max-height:min(60vh,340px);overflow-y:auto;
  background:var(--paper-hi);border:1px solid var(--rule);border-radius:5px;
  box-shadow:0 10px 28px #2a262122;padding:.25rem}
.wt-menu[hidden]{display:none}
.wt-opt{display:block;width:100%;text-align:left;font:inherit;font-size:.78rem;
  border:0;background:none;color:var(--ink);padding:.4rem .55rem;border-radius:3px;cursor:pointer}
.wt-opt:hover{background:color-mix(in srgb, var(--brass) 14%, transparent)}
.wt-opt.on{color:var(--brass-deep);font-weight:600}
.wt-opt.on::after{content:"\2713";float:right}

/* settings menu (mobile) — View / Sort / Filter grouped behind one icon */
.wt-settings{position:relative;display:none}
/* Settings sits outside the toggle capsule as a bare black glyph — no border, no fill */
#wt-setbtn{border:0;background:none;color:var(--ink)}
#wt-setbtn:hover:not(:disabled){color:var(--ink)}
/* Titles sit in a tinted band, full-bleed across the menu, so each section reads
   as a distinct block above its (larger) options. */
.wt-grouplabel{font-family:var(--sans);font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-soft);font-weight:700;background:var(--paper-lo);
  margin:.3rem -.25rem .14rem;padding:.3rem .55rem}
.wt-group:first-child .wt-grouplabel{margin-top:0}
/* The settings menu holds all three groups, so give it room — it shouldn't scroll
   until it genuinely runs out of viewport. */
.wt-setmenu{max-height:min(82vh,640px)}
/* Below 560px the three controls collapse into the settings menu; only the
   settings icon and the full-viewport button remain on the toolbar (sized to match). */
@media (max-width:560px){
  .wt-sort,.wt-filter{display:none}        /* sort + filter fold into the settings menu */
  .wt-settings{display:inline-block}        /* …the view toggle now stays out on the bar */
  #wt-fs{display:none}                       /* no full-viewport on mobile — just scroll */
  .wt-q{font-size:16px}                      /* iOS: an input ≥16px doesn't trigger focus-zoom */
  /* phone: search fills; the left slot (Works heading ↔ logo) is governed by the dock state.
     Fixed left column so the search box doesn't resize/shift when "Works" swaps to the logo
     on docking (auto would size to each, jarring the search). */
  .works-head{grid-template-columns:3.5rem 1fr auto}
  .wt-search{grid-column:2;justify-self:stretch;width:auto;border-radius:999px}   /* pill, matching the toggle */
  .wt-seg{border-radius:999px}                /* mobile: iOS pill segmented control */
  .wt-chip{padding:0 .8rem}                   /* …and wider chips for a longer capsule */
  .wh-brand,.wh-tag{display:none}             /* docked mobile bar shows just the mark */
  .wt-slash{display:none}                     /* no "/" shortcut on touch */
  /* year + age drop to their own second line (the ", " stays up on line 1 with the
     title), kept together so "age" and the number never split across a wrap */
  .tl-ya{display:block;white-space:nowrap}
}

/* --- timeline (dot-plot) view --------------------------------------------- */
/* Each work is a dot at its year on a full-width track; its label sits beside
   the dot — to the right when the dot is in the left half, to the left when it's
   in the right half — so labels stay inside the panel. Labels may wrap to 2 lines. */
.tl{position:relative;overflow-x:clip}   /* clip the edge dot/label overhang so the page never scrolls sideways */
/* Vertical gridlines at the labeled years, in BOTH views, so a work reads against a
   year the same way whether the panel is inline or full-viewport. Sits behind the rows. */
.tl-grid{display:block;position:absolute;inset:0;z-index:0;pointer-events:none}
.tl-grid-line{position:absolute;top:0;bottom:0;width:1px;background:var(--rule);
  opacity:.55;transform:translateX(-.5px)}
/* Vertical gridlines carry the sense of the timeline; horizontal per-row rules would
   just add noise, so the rows have no borders — only the year lines run through. */
.tl-row{position:relative;min-height:2.7em}
.tl-row.gateway{background:linear-gradient(90deg,#c9a2270f,transparent)}
.tl-dot{position:absolute;top:50%;width:9px;height:9px;border-radius:50%;
  background:var(--brass);border:1px solid var(--brass-deep);transform:translate(-50%,-50%)}
.tl-label{position:absolute;top:50%;transform:translateY(-50%);font-style:italic;
  font-size:.92rem;line-height:1.2;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tl-label.tl-right{text-align:left;padding-left:14px}
.tl-label.tl-left{text-align:right;padding-right:14px}
a.tl-label{color:var(--brass-deep);text-decoration:none}   /* clickable → link colour */
a.tl-label:hover{background-size:100% 1px}
.tl-label.tl-plain{color:inherit;cursor:default}           /* no extra info → plain, non-clickable */
.tl-row.gateway .tl-label{font-weight:600}
.tl-y{font-style:normal;font-family:ui-monospace,Menlo,monospace;font-size:.82em;color:var(--muted)}
.tl-nd{font-style:normal;font-family:var(--sans);font-size:.7rem;color:var(--muted)}

/* year ruler — mirrors the canvas timeline's bottom tick style (monospace).
   Pins to the bottom of the window, sitting just above the (also-pinned) footer
   so scrolling text never shows below it. --footer-h is set by works.js. */
.tl-axis{position:sticky;bottom:var(--footer-h,0);z-index:2;height:46px;margin-top:.4rem;
  background:var(--paper);border-top:1px solid var(--rule);padding:.35rem 0 10px}
.tl-tick{position:absolute;top:0;transform:translateX(-50%);text-align:center}
.tl-tick::before{content:"";display:block;width:1px;height:12px;margin:0 auto .18rem;
  background:var(--ink);opacity:.6}
.tl-tick.minor::before{height:6px;opacity:.4}
.tl-ticklabel{font-family:ui-monospace,Menlo,monospace;font-size:.78rem;color:var(--ink);opacity:.85}

/* --- full-window view (an in-page overlay; the browser window is unchanged) - */
.fs-ic{display:inline-block}
.fs-collapse{display:none}
.works-block.fs-on{position:fixed;inset:0;z-index:1000;margin:0;border-top:0;
  background:var(--paper);padding:0 var(--pad-x);overflow:auto;scrollbar-gutter:stable both-edges}   /* full-window escapes .composer, so the block supplies the gutter */
.works-block.fs-on .works-head{padding-top:.5rem;margin-bottom:0}
/* Timeline fills the window below the header, and the year axis is pushed to the bottom
   (margin-top:auto) — so with few works the ruler sits at the browser bottom, never mid-screen.
   For many works the timeline grows past the viewport and the sticky axis rides the bottom. */
.works-block.fs-on .tl{display:flex;flex-direction:column;min-height:calc(100vh - var(--nav-h, 46px))}
.works-block.fs-on .tl-axis{margin-top:auto}
/* Full-window mode is already full-width, so the docked re-inset (which targets the 44rem
   in-page container) would wrongly pull the logo + controls toward the centre on scroll.
   Keep the docked bar full-width here so nothing moves; the 1rem side padding + symmetric
   scrollbar-gutter above give the logo and the controls equal space from each edge. */
.works-block.fs-on .works-head.stuck{width:auto;margin-left:0;padding-left:0;padding-right:0}
.works-block.fs-on .tl-axis{bottom:0}
.works-block.fs-on .fs-expand{display:none}
.works-block.fs-on .fs-collapse{display:inline-block}

/* links */
.links{list-style:none;margin:0;padding:0}
.links li{padding:.35rem 0;font-family:var(--sans);font-size:.76rem}
.links a{color:var(--brass-deep);text-decoration:none}
.links a:hover{background-size:100% 1px;color:var(--brass)}
.links .l-src{font-family:var(--sans);font-size:.72rem;color:var(--muted);margin-left:.5rem}

/* --- Composer index (list view, NTI-278) ------------------------------------ */
.composer-index{max-width:56rem;margin:0 auto;padding:1rem 12px 1.5rem}
.cl-top{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:.6rem;margin:.2rem 0 .8rem}
.composer-index h1{margin:0;font-size:1.9rem}
.cl-count{font-family:var(--sans);font-size:.9rem;font-weight:400;color:var(--muted);margin-left:.4rem}
.cl-filters{display:flex;gap:.9rem;font-family:var(--sans);font-size:.8rem;color:var(--ink-soft)}
.cl-filters label{display:inline-flex;align-items:center;gap:.35rem}
/* .cl-filters selects use the shared .dropdown class (see above) */
.cl-table{width:100%;border-collapse:collapse;font-size:.95rem}
.cl-table thead th{text-align:left;font-family:var(--sans);font-size:.78rem;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--muted);
  border-bottom:1px solid var(--rule);padding:.35rem .5rem}
.cl-table thead a{color:inherit;text-decoration:none}
.cl-table thead a:hover{color:var(--brass-deep)}
.cl-arrow{color:var(--brass-deep)}
.cl-table td{padding:.4rem .5rem;border-bottom:1px dotted var(--rule);vertical-align:baseline}
.cl-table tbody tr:hover .cl-name a{background-size:100% 1px}   /* hover the row → underline the name; no full-row wash (matches the timeline list) */
.cl-name a{color:var(--ink);text-decoration:none;font-weight:600}
.cl-name a:hover{color:var(--brass-deep);background-size:100% 1px}
.cl-years{font-family:var(--sans);font-size:.82rem;color:var(--ink-soft);white-space:nowrap}
.cl-living{color:inherit}   /* "present" is plain text, not the brass link colour (the row/name is the link) */
.cl-era{font-family:var(--sans);font-size:.82rem;color:var(--muted)}
.cl-tier{font-family:var(--sans);font-size:.82rem;color:var(--muted);text-align:right;width:3rem}
.cl-empty{color:var(--muted);text-align:center;padding:1.2rem 0}
.cl-pager{display:flex;align-items:center;justify-content:center;gap:1rem;margin:1rem 0 .4rem;
  font-family:var(--sans);font-size:.85rem}
.cl-pager a{color:var(--brass-deep);text-decoration:none}
.cl-pager a:hover{background-size:100% 1px;color:var(--brass)}
.cl-off{color:var(--rule)}
.cl-page{color:var(--muted)}
.cl-back{text-align:center;margin-top:1rem}
@media (max-width:560px){
  .cl-era{display:none}
  .cl-table thead th.cl-era{display:none}
  .composer-index h1{font-size:1.5rem}
}

/* --- Program page — custom-focus split view (Phase 7.5) -----------------------
   Only custom-focus pages use this (timeline/program.html); /timeline/ + the homepage
   are unchanged. Bounded to one screenful on desktop: the full-width header + footer
   cross the page, a two-pane split sits between. The LEFT pane scrolls INTERNALLY —
   never the host page (the embed rule, NTI-335) — and the RIGHT pane is the vertical
   miniature timeline filling its box. Phones stack it (playlist, then timeline). */
body.program-page{height:100svh;overflow:hidden}          /* desktop: no page scroll */
.program-page .site-header{flex:0 0 auto}
/* EMBED (NTI-336): the bounded iframe card swaps the site header/footer for one slim brand bar;
   .program-split below fills the rest of the iframe. Deliberately minimal so it doesn't fight a
   host site's own sticky chrome (a white-label / no-bar option can drop it later). */
.embed-bar{flex:0 0 auto;display:flex;align-items:center;gap:.5rem;height:38px;padding:0 12px;
  border-bottom:1px solid var(--rule);background:linear-gradient(180deg,var(--paper-hi),var(--paper));
  font-family:var(--sans)}
.embed-brand{color:var(--brass-deep);font-family:var(--serif);font-weight:600;font-size:1rem;
  text-decoration:none;white-space:nowrap}
.embed-brand:hover,.embed-explore:hover{color:var(--brass)}
.embed-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--muted);font-size:.75rem}
.embed-explore{color:var(--brass-deep);font-family:var(--serif);font-weight:600;font-size:.85rem;
  text-decoration:none;white-space:nowrap;flex:0 0 auto}
.program-split{flex:1;min-height:0;display:flex;align-items:stretch;
  overflow:hidden;max-width:none;margin:0;padding:0}
.program-text{flex:1 1 0;min-width:0;overflow-y:auto;
  padding:0.75rem var(--pad-x) 2rem;border-right:1px solid var(--rule)}   /* even 50-50 split */
.program-stage{flex:1 1 0;min-width:0;position:relative}
.program-stage long-line{display:block;width:100%;height:100%;border:0;border-radius:0}
.program-handle{display:none}   /* desktop: no bubble — the timeline fills the right pane directly */

/* Playlist typography — editorial serif, same language as the bio/composer pages. */
.program-title{font-family:var(--serif);font-size:1.8rem;line-height:1.15;font-weight:600;
  color:var(--brass-deep);margin:0 0 1rem}
.program-text h1,.program-text h2,.program-text h3{font-family:var(--serif);color:var(--ink);
  margin:.2rem 0 .4rem;font-weight:600}
.program-text h1{font-size:1.5rem}
.program-text h2{font-size:1.25rem; margin-top:2.4rem}
/* Roomy top margin so each entry (an <h3> "Composer: Work" per piece on an ingested play history) has
   air — a less-dense left column lets the right-side scroll-sync reveal read one piece at a time. */
.program-text h3{font-size:1.05rem; margin-top:2.4rem}
.program-text h3:first-child{margin-top:0}
.program-text p{margin:0;line-height:1.6}
.program-text strong{font-weight:600}
.program-text em{font-style:italic}

/* ── Global link hover: a 1px underline that wipes in from the left, the same effect as the program
   .pl-ref / .ll-ref refs. One rest-state definition for every text link on the page; the individual
   :hover rules elsewhere just set `background-size:100% 1px`. Buttons (.cta) and the docked logo
   (.wh-home) carry their own background, so they're not in this list and stay unaffected. */
.timeline-link,.attribution a,.works .w-link,.rec-cred a,.w-crumb a,a.tl-label,.links a,.cl-name a,
.cl-pager a,.site-footer a,.ref-lb-cta{
  text-decoration:none;background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:left bottom;background-size:0 1px;
  transition:background-size .8s ease}
/* Auto-linked composer names: click → light up the matching name on the timeline (no popup). */
.program-text .pl-ref{color:var(--brass-deep);text-decoration:none;cursor:pointer;padding-bottom:1px;
  /* underline is a 1px bar drawn in the text colour; it wipes in from the LEFT on hover (200ms) */
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;background-position:left bottom;background-size:0% 1px;
  transition:background-size 800ms ease}
.program-text .pl-ref:hover{background-size:100% 1px}
.program-text .pl-ref.active{background-color:color-mix(in srgb,var(--brass) 20%,transparent);
  border-radius:2px;box-shadow:0 0 0 2px color-mix(in srgb,var(--brass) 20%,transparent)}

/* Auto-linked work titles (.ll-ref): click → the reference lightbox (like the composer bio popup).
   Uses the brass link colour like composer names, so it reads as clickable before you hover. */
.program-text .ll-ref{color:var(--brass-deep);text-decoration:none;cursor:pointer;
  background-image:linear-gradient(var(--brass-deep),var(--brass-deep));
  background-repeat:no-repeat;background-position:left bottom;background-size:0% 1px;
  transition:background-size 800ms ease}
.program-text .ll-ref:hover{background-size:100% 1px;color:var(--brass-deep)}

/* Reference lightbox — ONE card for any .ll-ref kind (works today; composers/… later). Cream paper, serif. */
.ref-lb{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;padding:1.2rem}
.ref-lb[hidden]{display:none}
.ref-lb-backdrop{position:absolute;inset:0;background:rgba(20,17,12,.5)}
.ref-lb-card{position:relative;max-width:34rem;width:100%;max-height:82vh;
  display:flex;flex-direction:column;overflow:hidden;   /* the card is fixed — only the description scrolls */
  background:var(--paper-hi);color:var(--ink);border:1px solid var(--rule);border-radius:6px;
  box-shadow:0 18px 60px rgba(20,17,12,.4)}
.ref-lb-card:focus{outline:none}   /* focused programmatically for a11y (tabindex=-1) — not a visible focus target */
.ref-lb-body{display:flex;flex-direction:column;min-height:0;flex:1}
/* Sticky header (like the bio panel's .ll-pt): sub + title + meta stay put; the blurb scrolls below. */
.ref-lb-head{flex:0 0 auto;padding:1.5rem 1.7rem .9rem;border-bottom:1px solid var(--rule)}
/* Close X — matches the composer bio lightbox's .ll-x (a 23px SVG in a 34×34 button). */
.ref-lb-x{position:absolute;top:.35rem;right:.4rem;z-index:2;display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;margin:0;padding:0;border:0;background:none;color:var(--muted);cursor:pointer}
.ref-lb-x svg{width:23px;height:23px}
.ref-lb-x:hover{color:var(--ink)}
.ref-lb-sub{font-family:var(--sans);font-size:.82rem;letter-spacing:.04em;color:var(--muted);margin:0 0 .15rem}
.ref-lb-title{font-family:var(--serif);font-size:1.5rem;font-weight:600;line-height:1.15;margin:0 0 .7rem;color:var(--ink)}
.ref-lb-meta{display:flex;flex-wrap:wrap;gap:.4rem;margin:0}   /* last row in the fixed header — its padding gives the gap */
.ref-lb-tag{font-family:var(--sans);font-size:.72rem;color:var(--ink-soft);
  background:var(--paper-lo);border:1px solid var(--rule);border-radius:999px;padding:.15rem .6rem}
/* The scrolling region — the description only. */
.ref-lb-blurb{flex:1 1 auto;min-height:0;overflow-y:auto;padding:1.1rem 1.7rem;
  font-family:var(--serif);font-size:1.02rem;line-height:1.55;color:var(--ink)}
.ref-lb-blurb p{margin:0 0 .8rem}.ref-lb-blurb p:last-child{margin-bottom:0}
/* CTA — pinned footer under the scroll, so "Open the work page" stays reachable. */
.ref-lb-cta{flex:0 0 auto;display:block;padding:.85rem 1.7rem 1.1rem;border-top:1px solid var(--rule);
  font-family:var(--sans);font-size:.82rem;font-weight:600;color:var(--brass-deep);text-decoration:none}
.ref-lb-cta:hover{background-size:100% 1px}
.ref-lb-loading{padding:1.4rem 1.7rem;font-family:var(--sans);color:var(--muted)}
body.ref-lb-open{overflow:hidden}

/* Preview banner (draft shown to staff / ?preview token) — pink on brass, like the timeline page. */
.program-preview{position:fixed;z-index:10;top:0;left:50%;transform:translateX(-50%);
  background:#A2801A;color:#F7D2D8;font:700 12px/1 var(--sans);letter-spacing:.09em;
  text-transform:uppercase;padding:7px 14px;border-radius:0 0 7px 7px;box-shadow:0 2px 8px #0003}

/* Phone: the playlist is the page (scrolls normally); the timeline is a bottom SHEET. Collapsed it's
   an "Explore the composers" bubble that rides above the Safari bar and hides on scroll-down (like the
   URL bar); tapping it flies up to a full-screen card, tapping the top handle sends it back down. */
@media (max-width:760px){
  body.program-page{height:auto;overflow:visible}
  body.program-page.sheet-open{overflow:hidden}          /* full-screen sheet up → lock the page behind it */
  .program-split{flex-direction:column;overflow:visible}
  .program-text{flex:0 0 auto;overflow:visible;border-right:0;padding-bottom:5rem}   /* clear the bubble */
  .program-page .site-footer{margin-bottom:4.5rem}       /* footer clears the fixed bubble */

  /* Anchored at the bottom; grows its HEIGHT to fly up (no `top` jump). Column: handle then canvas. */
  .program-stage{
    position:fixed;left:12px;right:12px;bottom:calc(env(safe-area-inset-bottom,0px) + 10px);
    height:52px;z-index:40;flex:none;display:flex;flex-direction:column;
    background:#fff5df;border:1px solid var(--rule);border-radius:26px;   /* lighter than the page → reads as a popup layer */
    box-shadow:0 6px 22px #00000030;overflow:hidden;
    transition:left .3s ease, right .3s ease, bottom .3s ease, height .3s ease,
               border-radius .3s ease, transform .28s ease}
  .program-stage long-line{display:none}                 /* collapsed: just the bubble */
  .program-handle{display:flex;align-items:center;justify-content:center;position:relative;
    flex:1 1 auto;width:100%;background:none;border:0;cursor:pointer;padding:0;touch-action:none}
  .program-grip{position:absolute;top:7px;left:50%;transform:translateX(-50%);
    width:36px;height:4px;border-radius:2px;background:var(--muted);opacity:.55}
  .program-cta{font-family:var(--sans);font-weight:600;font-size:1.25rem;color:var(--brass-deep)}

  /* Hidden on scroll-down (mimics the Safari toolbar collapsing). */
  .program-stage.bubble-hidden{transform:translateY(calc(100% + env(safe-area-inset-bottom,0px) + 18px))}

  /* Expanded → full-screen card. 16px top corners + the handle read as "slide me back down". The
     handle is a 28px strip ABOVE the canvas (never overlapping the timeline's own header). */
  .program-stage.expanded{left:0;right:0;bottom:0;height:100svh;
    border-radius:16px 16px 0 0;transform:none;box-shadow:0 -6px 30px #0000003a}
  .program-stage.expanded .program-handle{flex:0 0 28px}   /* top strip = tap to fly back down */
  .program-stage.expanded .program-cta{display:none}
  .program-stage.expanded .program-grip{top:9px;width:52px;opacity:.6}
  .program-stage.expanded long-line{display:block;flex:1 1 auto;width:100%;min-height:0;
    /* the whole popup reads on the lighter paper — the component re-resolves these on expand */
    --ll-paper:#fff5df;--ll-paper-hi:#fffbf0;--ll-paper-lo:#f1e6c8}
}
