/* ============================================================
   FREDDIE THE BASSIST - professional redesign (amber / warm)
   Built from the Claude Design handoff. Tokens, keyframes and
   responsive rules live here; component styling is inline in the
   markup (as designed) and hover states are wired in main.js.
   ============================================================ */
:root{
  --ink:#100d0b; --ink-2:#0a0807; --panel:#1a1613; --panel-2:#231d17;
  --bone:#f3ede1; --body:#d7cdbd; --mute:#9c9184;
  /* The accent. Purple is the set the site opens on; the brand clef cycles it
     through the others (amber, rust, blue, sand) and rewrites these at runtime. */
  --amber:#a24be0; --amber-bright:color-mix(in oklab, var(--amber) 74%, #fff);
  --amber-fill:#a24be0;
  /* what sits ON a filled accent button. Amber and sand are light enough to
     take near black; purple and blue are not, so this flips with the accent
     (set alongside --amber-fill by the clef cycle) rather than being
     hardcoded per button. */
  --on-fill:#fff;
  --line:rgba(243,237,225,0.12); --line-2:rgba(243,237,225,0.22);
  /* section dividers keep their own token so the weight is still separable from
     the other lines, but the colour is back to what --line has always been. The
     2px they are drawn at is what carries them now, not the value. */
  --rule:rgba(243,237,225,0.12);
  --f-display:'Bricolage Grotesque',system-ui,sans-serif;
  --f-body:'Instrument Sans',system-ui,sans-serif;
  --f-clef:'Noto Music',serif;
  --f-poster:'Saira Condensed','Anton','Bricolage Grotesque',system-ui,sans-serif;
  --maxw:1240px; --gap:clamp(1.4rem,5vw,4rem);
  --edge:max(var(--gap), calc((100vw - var(--maxw)) / 2 + var(--gap)));
  --ctrl:44px;
  --ease:cubic-bezier(.22,1,.36,1);
  /* collaborator-poster category colours (dark theme: flat, vivid, screen-print) */
  --c-artists:#f7b733; --c-education:#1fcbbb; --c-studio:#ff4f97;
  --c-classical:#a97bff; --c-functions:#54d15c; --c-musicals:#ff5a35; --c-tribute:#3f9bff;
}
[data-theme="light"]{
  --ink:#efe7d8; --ink-2:#e4dac6; --panel:#fbf7ef; --panel-2:#f1e9da;
  --bone:#1c1610; --body:#3d342a; --mute:#5f5548;
  /* --amber intentionally NOT overridden here: buttons/icons/underlines keep
     the same accent as dark mode in both themes for brand consistency.
     --amber-bright IS overridden: its whole job is being accent-coloured *text*
     sitting directly on the page background, and the dark-mode tint (the accent
     lightened toward white) drops to ~1.5:1 against these light backgrounds -
     nowhere near readable. Darkening it here keeps it recognisably the accent
     while actually passing WCAG AA (4.5:1+) again. */
  --amber-bright:#5b21a8;
  --line:rgba(28,22,16,0.14); --line-2:rgba(28,22,16,0.26);
  --rule:rgba(28,22,16,0.14);
  /* festival screen-print palette: saturated, characterful, still AA-legible
     (all >=4:1) on the warm poster paper below */
  --c-artists:#a15f07; --c-education:#0d7269; --c-studio:#c02360;
  --c-classical:#6a34cf; --c-functions:#2b7d34; --c-musicals:#c93b1a; --c-tribute:#1f63c8;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
/* Base type was the browser default 16px, which put the small print (labels,
   credits, captions, the poster legend) down at 11-12px and made the page hard
   work to read. Lifting the root to 18px scales every rem-based size with it,
   type and the spacing around it together, so the proportions hold. */
html{font-size:112.5%;scroll-behavior:smooth;scroll-padding-top:4.75rem;-webkit-text-size-adjust:100%;color-scheme:dark;overscroll-behavior:none;}
[data-theme="light"]{color-scheme:light;}
/* the page never set a family on the root, so anything that inherited rather
   than declaring its own (the contact inputs, their labels, the submit
   button, the status line) fell through to the browser default serif */
/* Both axes, not just y. Containing the overscroll on the strips themselves
   was not enough: at scrollLeft 0 there is nothing left to scroll, so a
   backward swipe went straight to the document - where it is the browser's
   own back gesture. Swiping right on the first video was navigating off the
   page. */
body{background:var(--ink);overscroll-behavior:none;font-family:var(--f-body);color:var(--body);}

/* elegant cross-fade whenever the theme swaps the custom-property values;
   elements with their own inline transition (buttons, cards, links) keep
   using their own timing since inline style wins over this rule. */
*{transition:background-color .4s ease,color .4s ease,border-color .4s ease,fill .4s ease;}
img{max-width:100%;display:block;}
a{text-decoration:none;color:inherit;}
button{font-family:inherit;}
canvas{display:block;}
::selection{background:var(--amber);color:var(--on-fill);}
/* no native ring on mouse/touch click; keyboard focus still gets one.
   written as two separate rules (rather than :focus:not(:focus-visible))
   so it still works in engines with partial/no :focus-visible support. */
:focus{outline:none;}
:focus-visible{outline:2px solid var(--amber);outline-offset:3px;border-radius:3px;}
/* Set for one restore when a dialog hands focus back after a pointer-only
   exchange - see close() in makeLightbox. !important because several later
   rules name their own element at the same specificity and would otherwise
   win purely on source order. */
[data-noring]:focus-visible{outline:none !important;}

@keyframes ftb-pulse{0%,100%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.14)}}

/* links inside the marquee + event rows keep the design colour but hint interactivity */
.ftb-link{transition:color .25s var(--ease);}
.ftb-link:hover{color:var(--amber-bright);}
/* quiet inline links (e.g. gear endorsements): sit in the body copy, only a
   faint underline that warms up on hover - no shouty link colour */
.quiet-link{color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2px;text-decoration-color:var(--line-2);transition:color .25s var(--ease),text-decoration-color .25s var(--ease);}
.quiet-link:hover,.quiet-link:focus-visible{color:var(--bone);text-decoration-color:var(--amber);}

/* ---------------- HORIZONTAL TIMELINE (present on the right; drag to go back) ---------------- */
.tl-h{display:flex;align-items:stretch;overflow-x:auto;cursor:grab;
  padding:.4rem 0 1.2rem;scrollbar-width:none;-ms-overflow-style:none;touch-action:pan-x;
  user-select:none;-webkit-user-select:none;overscroll-behavior-x:contain;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 20%);mask-image:linear-gradient(90deg,transparent,#000 20%);}
.tl-h::-webkit-scrollbar{display:none;}
.tl-h.grabbing{cursor:grabbing;}
.tlh-item{flex:0 0 clamp(228px,64vw,290px);position:relative;padding:2.4rem 1.7rem 0 0;box-sizing:border-box;}
.tlh-item.tlh-end{flex:0 0 clamp(84px,16vw,130px);}
.tlh-ell{font-family:var(--f-display);font-weight:700;font-size:1.5rem;color:var(--mute);letter-spacing:.12em;line-height:1;}
.tlh-item::before{content:'';position:absolute;left:0;right:0;top:8px;height:2px;background:var(--line-2);}
.tl-h .tlh-item:last-child::before{background:linear-gradient(90deg,var(--line-2),var(--amber) 85%);}
.tlh-dot{position:absolute;left:0;top:8px;transform:translateY(-50%);width:14px;height:14px;border-radius:50%;background:var(--ink);border:2px solid var(--amber);transition:transform .3s var(--ease),background .3s;}
.tlh-item:hover .tlh-dot{background:var(--amber);transform:translateY(-50%) scale(1.18);}
.tlh-year{font-family:var(--f-display);font-weight:700;font-size:.92rem;color:var(--amber-bright);letter-spacing:.02em;}
.tlh-title{font-family:var(--f-display);font-weight:700;font-size:clamp(1.05rem,1.7vw,1.3rem);color:var(--bone);line-height:1.18;margin:.2rem 0 0;letter-spacing:-.01em;display:inline-flex;align-items:baseline;gap:.4rem;}
a.tlh-title:hover{color:var(--amber-bright);}
a.tlh-title .tlh-arrow{font-size:.82em;color:var(--amber);opacity:0;transform:translateX(-3px);transition:opacity .25s,transform .25s var(--ease);}
a.tlh-title:hover .tlh-arrow{opacity:1;transform:none;}
.tlh-meta{color:var(--body);font-size:.88rem;line-height:1.5;margin:.45rem 0 0;}
.tlh-src{color:var(--mute);font-size:.75rem;margin-top:.5rem;}
.tlh-src a{color:var(--mute);text-decoration:underline;text-underline-offset:2px;}
.tlh-src a:hover{color:var(--amber-bright);}

/* ---------------- SERVICES FLIP CARDS (photo front, tap to flip to text) ---------------- */
[data-svc].flip{container-type:inline-size;background:transparent;border:0;padding:0;min-height:0;aspect-ratio:2/3;position:relative;cursor:pointer;-webkit-tap-highlight-color:transparent;-webkit-perspective:1200px;perspective:1200px;}
/* fallback for browsers without aspect-ratio (older iOS Safari): 2/3 = 150% */
@supports not (aspect-ratio:1/1){[data-svc].flip{height:0;padding-bottom:150%;}}
[data-svc].flip .flip-inner{position:absolute;inset:0;transition:transform .6s var(--ease);-webkit-transform-style:preserve-3d;transform-style:preserve-3d;}
[data-svc].flip.flipped .flip-inner{transform:rotateY(180deg);}
[data-svc].flip:focus-visible{outline:2px solid var(--amber);outline-offset:3px;border-radius:5px;}
.flip-front,.flip-back{position:absolute;inset:0;-webkit-backface-visibility:hidden;backface-visibility:hidden;border:1px solid var(--line-2);border-radius:3px;overflow:hidden;box-sizing:border-box;transition:border-color .3s var(--ease);}
[data-svc].flip:hover .flip-front,[data-svc].flip:hover .flip-back{border-color:var(--amber);}
.flip-front{background-repeat:no-repeat;background-origin:padding-box;background-clip:padding-box;background-size:100% 100%,cover;background-position:center,center 30%;display:flex;flex-direction:column;justify-content:space-between;padding:1.3rem;}
.flip-front .flip-icon{color:#f3ede1;filter:drop-shadow(0 2px 8px rgba(0,0,0,.65));}
.flip-front .flip-title{color:#f3ede1;text-shadow:0 2px 14px rgba(0,0,0,.7);}
.flip-cue{display:inline-block;margin-top:.4rem;font-family:var(--f-body);font-size:clamp(.64rem,2.6cqw,.82rem);letter-spacing:.09em;text-transform:uppercase;color:#f3ede1;opacity:.9;text-shadow:0 1px 10px rgba(0,0,0,.8);}
.flip-back{transform:rotateY(180deg);background:var(--panel);display:flex;flex-direction:column;gap:.45rem;padding:1.45rem;overflow-y:auto;}
.flip-back .flip-icon{color:var(--amber);margin-bottom:.1rem;}
.flip-back .flip-title{color:var(--bone);font-size:clamp(1.02rem,8.6cqw,2.05rem);}
.flip-title{font-family:var(--f-display);font-weight:700;text-transform:uppercase;font-size:clamp(1.02rem,7.4cqw,1.85rem);letter-spacing:-.01em;margin:0;line-height:1.08;}
.flip-lead{margin:0;font-weight:600;font-size:clamp(.86rem,6.3cqw,1.42rem);color:var(--amber-bright);line-height:1.35;}
/* Titles and leads wrap to different line counts across the cards, which used to
   push the three text levels out of step from card to card. Reserving the tallest
   case for each keeps the title, lead and description starting on the same line
   on every card. Values are in em, so they track each element's own font size.
   flex:none stops the flex column squashing a long title below its own text,
   which used to make a third line spill over the lead. */
.flip-back .flip-icon{flex:none;}
.flip-back .flip-title{min-height:2.16em;flex:none;}   /* up to 2 lines at line-height 1.08 */
.flip-back .flip-lead{min-height:4.05em;flex:none;}    /* up to 3 lines at line-height 1.35 */
.flip-desc{margin:0;color:var(--body);font-size:clamp(.82rem,5.9cqw,1.3rem);line-height:1.45;}
.flip-icon{display:block;line-height:0;}
/* services grid: fluid, wide cards on desktop; on phones tessellate two-up (a
   deck of options) instead of a tall wall of full-width cards. At half width the
   flip-back is text-heavy, so trim its type/padding a touch and give the mobile
   card a little more height so the detail still reads without scrolling. */
/* Eight services, so the desktop grid is an explicit 4 x 2 rather than whatever
   auto-fill lands on: at the usual widths a 300px floor only fitted three across,
   which left a ragged 3 + 3 + 2. Below 1120px there is not room for four without
   the backs overflowing, so it steps to two, then one on a phone. */
[data-svcgrid]{grid-template-columns:repeat(2,minmax(0,1fr));}
/* three across in the middle band: at two columns a 1024px viewport gave a
   453px card with the same type as a 266px one, and the text filled under half
   of it */
@media(min-width:900px) and (max-width:1199px){[data-svcgrid]{grid-template-columns:repeat(3,minmax(0,1fr));}}
@media(min-width:1200px){
  [data-svcgrid]{grid-template-columns:repeat(4,minmax(0,1fr));}
  /* Taller than the 2:3 the other breakpoints use - about 15% - because the
     body text below is now set to match the Study Bass lead and needs the
     room. Four columns is what makes them readable at all: at three they are
     wide enough to work but you lose the row, so the height buys what the
     width cannot. */
  [data-svc].flip{aspect-ratio:40/69;}
  /* Freddie asked for the card text to be as readable as the Study Bass
     section. That lead lands at 18.4px here and the card detail was at 15.4px,
     so these are the container-query widths that close the gap. Scoped to this
     breakpoint: at three columns the cards are already wider and the same
     numbers would overshoot. */
  .flip-lead{font-size:clamp(.94rem,7.5cqw,1.5rem);}
  .flip-desc{font-size:clamp(.9rem,7.05cqw,1.38rem);}
  /* narrower cards wrap the longest title onto a third line, so the title
     reserves three lines here and the lead and detail below it stay level
     across all eight backs */
  .flip-back .flip-title{min-height:3.24em;}
}
@media(max-width:520px){
  [data-svcgrid]{grid-template-columns:1fr;gap:.7rem;}
  [data-svc].flip{aspect-ratio:5/4;}
  /* the phone card is wide but short, so width-based scaling overshoots and the
     text runs past the bottom: cap it lower than the container query wants */
  .flip-back .flip-title{font-size:clamp(1rem,5.6cqw,1.36rem);}
  .flip-lead{font-size:clamp(.86rem,4.2cqw,1.04rem);}
  .flip-desc{font-size:clamp(.8rem,3.9cqw,.98rem);}
  .flip-front .flip-title{font-size:clamp(1rem,4.6cqw,1.2rem);}
  .flip-front{padding:1rem;}
  .flip-back{padding:.82rem;gap:.26rem;}
  .flip-back .flip-icon svg{width:22px;height:22px;}
  .flip-back .flip-title{line-height:1.08;}
  .flip-lead{line-height:1.3;}
  .flip-desc{line-height:1.42;}
  /* the phone cards are much shorter, so reserve less for the lead there or the
     longest card (Tuition) runs past the bottom */
  .flip-back .flip-lead{min-height:2.2em;}
  .flip-desc{line-height:1.38;}
  .flip-back .flip-title{min-height:0;}
}

/* ---------------- "WHO HE'S PLAYED WITH" client list ---------------- */
/* ---------- collaborators festival poster ---------- */
/* framed like a printed bill: a solid outer rule plus a soft amber inner rule
   set in from the edge - the brand colour giving a hint of Glasto playfulness,
   theme-aware so it works on both the dark island and (were it ever) light. */
.poster{position:relative;overflow:hidden;background:var(--panel);border:2.5px solid var(--line-2);
  box-shadow:inset 0 0 0 6px var(--panel),
             inset 0 0 0 8px color-mix(in oklab,var(--amber) 68%,transparent),
             inset 0 0 0 9px color-mix(in oklab,var(--amber) 22%,transparent);
  border-radius:3px;padding:clamp(2.4rem,5vw,4.4rem) clamp(.9rem,2vw,1.7rem);}
/* In light mode the poster stays a dark Glastonbury-style island: deep navy
   stock with the vivid dark-theme colours (real Glasto bills are navy, not
   cream - a cream ground forced the "artists" gold down to a lacklustre
   brown). Re-declaring the theme variables ON the poster makes every
   descendant - names, measured clefs, the hint, chips, borders - inherit the
   dark values, so it all reads correctly light-on-dark. */
[data-theme="light"] .poster{
  --panel:#171a3d;
  --bone:#f3ede1; --body:#d7cdbd; --mute:#9a93b0;
  --line:rgba(243,237,225,0.14); --line-2:rgba(243,237,225,0.24);
  --c-artists:#f7b733; --c-education:#1fcbbb; --c-studio:#ff4f97;
  --c-classical:#a97bff; --c-functions:#54d15c; --c-musicals:#ff5a35; --c-tribute:#3f9bff;
  --c-uncat:#f3ede1;
  background:#171a3d;
}
.poster::before,.poster::after{content:"\1D122";font-family:var(--f-clef);position:absolute;line-height:1;color:var(--bone);opacity:.05;font-size:clamp(14rem,40vw,32rem);pointer-events:none;z-index:0;}
.poster::before{top:-3.2rem;left:-1.4rem;}
.poster::after{bottom:-6.5rem;right:-1.4rem;transform:scaleX(-1);}
/* the roll-call: dense, centred, letterpress rows; one type size per row so
   no line mixes sizes, with big gaps + centred bass-clef dividers between acts */
.poster-names{position:relative;z-index:1;}
/* position:relative = origin for the measured clefs; padding-inline reserves
   room for the per-line bookend clefs that hug the widest lines' outer edges;
   the larger inter-name margin leaves a gap for the between-name clefs */
.p-row{position:relative;text-align:center;line-height:1.16;margin:0;text-wrap:balance;padding-inline:1.35em;}
/* the row carries its tier's type size: names inherit it and every measured
   divider derives its .72em size from it, so dividers scale per row */
.p-row-t1{font-size:clamp(2rem,4.4vw,3.3rem);}
.p-row-t2{font-size:clamp(1.65rem,3.5vw,2.5rem);}
.p-row-t3{font-size:clamp(1.24rem,2.3vw,1.7rem);}
.p-row-t4{font-size:clamp(1.02rem,1.7vw,1.28rem);}
.p-name{display:inline-block;max-width:100%;overflow-wrap:break-word;vertical-align:middle;font-family:var(--f-poster);font-weight:800;text-transform:uppercase;letter-spacing:.012em;color:var(--c);cursor:default;text-decoration:none;margin:.05em .62em;transition:opacity .3s var(--ease),transform .18s var(--ease),filter .25s;}
a.p-name{cursor:pointer;}
/* keeps a group's last two names on the same line (see wireClients) */
.p-pair{white-space:nowrap;}
/* a pair that must never be split across lines, tied at build time rather than
   by the orphan stitcher - which unwraps .p-pair every time it re-measures */
.p-keep{white-space:nowrap;}
.p-name:hover,.p-name:focus-visible{transform:translateY(-1px) scale(1.03);filter:brightness(1.15);outline:none;}
.p-t1{font-size:clamp(2rem,4.4vw,3.3rem);}
.p-t2{font-size:clamp(1.65rem,3.5vw,2.5rem);}
.p-t3{font-size:clamp(1.24rem,2.3vw,1.7rem);}
.p-t4{font-size:clamp(1.02rem,1.7vw,1.28rem);}
.p-sep{font-family:var(--f-clef);color:var(--amber);opacity:.72;font-size:.72em;line-height:1;pointer-events:none;user-select:none;}
/* every divider (bookends + between-name) is measured and absolutely placed by
   layoutClefs() at (left,top) = the gap/edge and the line's vertical centre;
   translate(-50%,-50%) then centres the glyph on that anchor point */
.p-clef{position:absolute;transform:translate(-50%,-50%);white-space:nowrap;transition:opacity .3s var(--ease);}
.poster[data-active] .p-name{opacity:.12;}
.poster[data-active] .p-clef{opacity:.06;}
.poster[data-active="artists"] .p-name[data-cat="artists"],
.poster[data-active="education"] .p-name[data-cat="education"],
.poster[data-active="studio"] .p-name[data-cat="studio"],
.poster[data-active="classical"] .p-name[data-cat="classical"],
.poster[data-active="functions"] .p-name[data-cat="functions"],
.poster[data-active="musicals"] .p-name[data-cat="musicals"],
.poster[data-active="tribute"] .p-name[data-cat="tribute"]{opacity:1;}
/* integral printed footer: category roll acting as the highlight control */
.poster-legend-wrap{position:relative;z-index:1;margin-top:clamp(1.9rem,4vw,2.9rem);padding-top:clamp(1.3rem,3vw,1.9rem);border-top:1px solid var(--line);text-align:center;}
.poster-legend-hint{display:block;font-family:var(--f-body);font-size:.72rem;letter-spacing:.2em;text-transform:uppercase;color:var(--mute);margin-bottom:.75rem;}
.poster-legend{display:block;line-height:1.65;}
.legend-chip{display:inline-block;font-family:var(--f-poster);font-weight:400;text-transform:uppercase;letter-spacing:.02em;font-size:clamp(1rem,1.7vw,1.22rem);color:var(--c);background:none;border:0;padding:0 .05em;margin:0;cursor:pointer;line-height:1.35;transition:filter .2s;}
.legend-chip .legend-clef{display:none;}
.legend-dot{color:var(--mute);margin:0 .4em;font-family:var(--f-body);font-size:.72em;vertical-align:.12em;}
.legend-chip:hover,.legend-chip:focus-visible,.legend-chip.is-active{text-decoration:underline;text-underline-offset:.22em;text-decoration-thickness:.09em;outline:none;filter:brightness(1.12);}
/* antique pointing-hand ornaments (Glastonbury-style) */
.manicule{font-family:Georgia,'Times New Roman',serif;color:var(--amber);font-style:normal;font-size:1.25em;line-height:0;vertical-align:-.06em;}
.poster-legend-hint .manicule{margin:0 .55em;opacity:.9;}
.poster-more{position:relative;z-index:1;text-align:center;margin:clamp(1.1rem,2.6vw,1.7rem) 0 0;font-family:var(--f-body);font-size:.92rem;letter-spacing:.03em;color:var(--mute);line-height:1.2;}
.poster-more .manicule{margin:0 .5em;opacity:.9;}
@media(prefers-reduced-motion:reduce){.p-name{transition:opacity .3s ease;} .p-name:hover,.p-name:focus-visible{transform:none;}}
/* Phones: the base clamp() floors (2rem/1.65rem/1.24rem/1.02rem) are too big for
   a ~360px column, so the dense bill unravels into a near-one-name-per-row list.
   Below 700px keep scaling the type down (steeper vw, lower floors) so the poster
   holds its packed, desktop-like composition - just smaller. Each cap equals the
   base floor, so the sizes meet with no jump at the breakpoint; layoutClefs()
   re-measures on load and resize, so the dividers follow. */
@media(max-width:700px){
  .p-row-t1,.p-t1{font-size:clamp(1.3rem,6vw,2rem);}
  .p-row-t2,.p-t2{font-size:clamp(1.1rem,5vw,1.65rem);}
  .p-row-t3,.p-t3{font-size:clamp(.92rem,4vw,1.24rem);}
  .p-row-t4,.p-t4{font-size:clamp(.78rem,3.2vw,1.02rem);}
  /* let the category roll and "& many more" shrink on the same footing as the
     names, so the printed footer stays in proportion with the smaller bill */
  .legend-chip{font-size:clamp(.88rem,3vw,1.22rem);}
  .poster-more{font-size:clamp(.78rem,2.6vw,.92rem);}
}

/* ---------------- REVEAL fallback (JS sets inline) ---------------- */
[data-reveal]{will-change:opacity,transform;}

/* grid children must be allowed to shrink below their content's intrinsic width,
   otherwise a wide child (e.g. the topics marquee track) blows the column out */
[data-two]>*{min-width:0;}

/* ---------------- RESPONSIVE (from design) ---------------- */
@media(max-width:920px){
  [data-hero]{grid-template-columns:1fr !important;min-height:auto !important;}
  [data-heroart]{order:-1 !important;min-height:46vh !important;max-height:480px;}
  [data-herocopy]{padding-inline:var(--gap) !important;}
  [data-two]{grid-template-columns:1fr !important;}
  [data-navlinks]{position:fixed !important;top:0;right:0;height:100vh;width:min(72vw,300px);flex-direction:column !important;align-items:flex-start !important;justify-content:flex-start;gap:1.3rem !important;background:var(--panel);padding:6rem 1.8rem 2rem;transform:translateX(105%);transition:transform .45s var(--ease);z-index:120;box-shadow:-30px 0 80px rgba(0,0,0,.4);overflow-y:auto;}
  [data-navlinks][data-open="true"]{transform:translateX(0);}
  [data-navlinks] a{font-size:1.05rem !important;}
  [data-drawer-brand]{display:flex !important;width:100%;padding-bottom:1.1rem;margin-bottom:.3rem;border-bottom:1px solid var(--line-2);}
  [data-navlinks-extra]{display:flex !important;flex-direction:column;width:100%;margin-top:.4rem;}
  [data-desktop-only]{display:none !important;}
  [data-navtoggle],[data-navclose]{display:inline-flex !important;}
  [data-bookbar]{display:block !important;}
}
@media(min-width:921px){ [data-navtoggle],[data-navclose]{display:none !important;} [data-navbackdrop]{display:none !important;} [data-navlinks-extra]{display:none !important;} [data-drawer-brand]{display:none !important;} }

/* ---------- scrollspy: a single underline slides between the active nav links ---------- */
[data-navlinks] a[data-active]{color:var(--amber-bright);}
[data-navunderline]{position:absolute;left:0;bottom:-7px;width:0;height:2px;border-radius:2px;background:var(--amber);opacity:0;pointer-events:none;transform:translateX(0);transition:transform .38s var(--ease),width .38s var(--ease),opacity .3s var(--ease);}
@media(min-width:921px){ [data-navlinks]{position:relative;} }
@media(max-width:920px){ [data-navunderline]{display:none;} }
@media(prefers-reduced-motion:reduce){ [data-navunderline]{transition:opacity .2s var(--ease);} }
/* When the booking section is in view, the booking CTAs (nav + the in-page email
   button) take over from the underline with a gentle sheen sweep to draw the eye. */
[data-book-active]{position:relative;overflow:hidden;}
[data-book-active]::after{content:"";position:absolute;top:0;bottom:0;left:0;width:45%;pointer-events:none;background:linear-gradient(100deg,transparent 0%,rgba(255,255,255,.5) 50%,transparent 100%);transform:translateX(-180%);animation:book-sheen 1.5s var(--ease) 1;}
@keyframes book-sheen{from{transform:translateX(-180%);}to{transform:translateX(320%);}}
@media(prefers-reduced-motion:reduce){[data-book-active]::after{display:none;}}

/* ---------- contact form (Web3Forms) ---------- */
.cform{display:flex;flex-direction:column;gap:1.1rem;max-width:40rem;margin-top:2rem;}
/* Anchor style: heading sits at the top of the column, the form is pushed to the
   bottom so it lines up with the base of the full-height photo beside it. */
.contact-left .cform{margin-top:auto;padding-top:2.2rem;}
.contact-socials{margin-top:1.9rem;padding-top:1.6rem;border-top:1px solid var(--line);display:flex;flex-wrap:wrap;align-items:center;gap:1rem;}
/* Label hugs the left edge, buttons hug the right. margin-left rather than
   space-between so the buttons stay right-aligned on the line of their own
   they take when the row wraps. */
.csocial-links{display:flex;gap:.6rem;margin-left:auto;}
.contact-photo{position:relative;overflow:hidden;border-radius:3px;background:var(--panel);min-height:clamp(440px,50vw,600px);}
.contact-photo::after{content:"";position:absolute;inset:0;border-radius:3px;pointer-events:none;background:linear-gradient(180deg,transparent 60%,rgba(18,14,11,.4));box-shadow:inset 0 0 0 1px color-mix(in oklab,var(--amber) 14%,transparent);}
@media(max-width:920px){
  /* photo drops below the form as a modest landscape banner (3/2 ~= its native
     crop) rather than a tall portrait shoved above the form */
  .contact-photo{order:1;min-height:0;aspect-ratio:3/2;margin-top:.5rem;}
  .contact-left .cform{padding-top:1.6rem;}
}
.cform[hidden]{display:none !important;}
.cform-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem;}
@media(max-width:560px){ .cform-row{grid-template-columns:1fr;} }
.cfield{display:flex;flex-direction:column;gap:.4rem;min-width:0;}
.cfield label{font-size:.82rem;color:var(--mute);font-weight:500;letter-spacing:.02em;}
.cinput{width:100%;background:color-mix(in oklab,var(--panel) 88%,var(--bone) 12%);border:1px solid var(--line-2);border-radius:3px;color:var(--bone);font-family:inherit;font-size:.98rem;padding:.75rem .85rem;transition:border-color .2s var(--ease),background .2s var(--ease);}
.cinput::placeholder{color:var(--mute);}
.cinput:focus{outline:none;border-color:var(--amber);box-shadow:0 0 0 3px color-mix(in oklab,var(--amber) 22%,transparent);}
textarea.cinput{resize:vertical;min-height:120px;}
.cform-foot{display:flex;flex-wrap:wrap;align-items:center;gap:1rem;}
.cbtn{display:inline-flex;align-items:center;gap:.55rem;background:var(--amber-fill);color:var(--on-fill);font-family:inherit;font-weight:600;font-size:.98rem;padding:.95rem 1.6rem;border:0;border-radius:3px;cursor:pointer;transition:transform .25s var(--ease),box-shadow .25s;}
.cbtn:hover:not(:disabled),.cbtn:focus-visible:not(:disabled){transform:translateY(-2px);box-shadow:0 14px 34px -12px var(--amber-fill);}
.cbtn:disabled{opacity:.6;cursor:default;}
.cstatus{font-size:.9rem;color:var(--mute);}
.cstatus[data-state="ok"]{color:var(--amber-bright);}
.cstatus[data-state="err"]{color:#e5644a;}
.choney{position:absolute !important;left:-9999px !important;width:1px;height:1px;overflow:hidden;}

@media (prefers-reduced-motion: reduce){
  *{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;}
  html{scroll-behavior:auto;}
}

/* native :hover for dynamically-created elements (video thumbs, gig rows) -
   see wireHovers() for why this replaced JS mouseenter/mouseleave pairing */
/* A tile sits at full brightness with nothing over it. The scrim and the
   caption belong to hover: they are what the pointer buys you, rather than
   furniture the picture has to carry all the time. */
.lyt-scrim{position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(0deg,rgba(0,0,0,.55),rgba(0,0,0,.15));transition:opacity .3s var(--ease);}
.lyt-btn:hover .lyt-scrim,.lyt-btn:focus-visible .lyt-scrim{opacity:1;}
/* the thumbnail itself: fades up as it arrives rather than snapping in, and
   sits on a panel tone so a tile that has not landed yet reads as a frame
   waiting rather than a hole in the row */
.lyt-thumb{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center;
  opacity:0;transition:opacity .45s var(--ease),transform .6s var(--ease),filter .4s;}
.lyt-thumb.is-ready{opacity:1;}
.lyt-btn:hover .lyt-thumb,.lyt-btn:focus-visible .lyt-thumb{transform:scale(1.05) !important;filter:brightness(.72) saturate(.95) !important;}
.lyt-btn:hover .lyt-play,.lyt-btn:focus-visible .lyt-play{transform:translate(-50%,-50%) scale(1.08) !important;}
/* the clip's name over the top left of the still. The colour is the literal
   bone rather than var(--bone) on purpose: it sits on a photograph, which does
   not follow the theme, so flipping it dark in light mode would put dark text
   on a dark stage. Same reasoning as --amber-fill. */
.lyt-title{position:absolute;left:0;right:0;top:0;pointer-events:none;text-align:left;
  padding:.66rem .8rem 1.5rem;
  font-family:var(--f-display);font-weight:700;font-size:clamp(.78rem,2.2vw,.92rem);
  line-height:1.24;letter-spacing:-.01em;color:#f3ede1;text-shadow:0 1px 3px rgba(0,0,0,.8);
  background:linear-gradient(180deg,rgba(0,0,0,.74),rgba(0,0,0,.36) 50%,transparent);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  opacity:0;transition:opacity .3s var(--ease);}
.lyt-btn:hover .lyt-title,.lyt-btn:focus-visible .lyt-title{opacity:1;}
/* hero art: two stacked layers of the same frame under different stage lights,
   so changing the accent crossfades the colour rather than cutting to it. Both
   layers carry the same object-position and parallax, so nothing shifts.

   Only the incoming layer animates. Fading both at once is what made the
   picture dip dark halfway: at the midpoint the pair composites to about three
   quarters cover and the near-black behind them shows through. The outgoing
   layer instead stays fully opaque underneath while the new one fades in over
   it, so the stack is always a whole picture and the light goes colour to
   colour rather than colour to black to colour. z-index decides which is seen;
   both keep the -on class. Slow, because it reads as a stage light changing. */
.hero-art-img{position:absolute;inset:0;width:100%;height:118%;object-fit:cover;object-position:30% 40%;
  transform:translateY(var(--par,0));will-change:transform,opacity;
  opacity:0;transition:opacity 1.15s ease-in-out;}
.hero-art-img.hero-art-on{opacity:1;}
@media(prefers-reduced-motion:reduce){.hero-art-img{transition:none;}}
/* on-stage gallery: one big picture that crossfades (two stacked layers,
   JS toggles .on). A fixed-height band rather than a fixed aspect ratio,
   because Freddie's set runs two thirds portrait and square: a 16:9 box on
   "cover" would have thrown away more than half of most of them. Anything
   narrower than 3:2 is shown whole and the gap either side is filled with a
   blurred, darkened copy of the same frame, so the letterbox reads as a
   deliberate surround instead of bars. */
/* Runs the full width of the column, flush with the carousel above it, and a
   fixed 2:1 rather than a viewport-height band so the frame keeps its shape on
   a short laptop as well as a tall monitor. Wide enough that most of the set
   sits in the middle of it — which is the point: the picture is surrounded by
   its own colour rather than trimmed to fit. */
.stage-gallery{position:relative;overflow:hidden;border-radius:3px;background:#0b0a0c;
  width:100%;aspect-ratio:2/1;max-height:78vh;}
.gal-layer{position:absolute;inset:0;opacity:0;transition:opacity .55s var(--ease);}
.gal-layer.on{opacity:1;}
.gal-layer img{position:absolute;inset:0;width:100%;height:100%;}
/* the wash behind: same picture, blown past the edges and blurred out */
.gal-bg{object-fit:cover;filter:blur(46px) saturate(1.35) brightness(.42);transform:scale(1.22);z-index:0;}
/* pulls the wash towards the site's own colour at the edges and darkens the
   corners, so the surround reads as a deliberate ground and the photograph
   sitting on it lifts off the page */
.gal-layer::before{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    radial-gradient(115% 88% at 50% 50%, transparent 34%, color-mix(in srgb, var(--amber) 34%, transparent) 100%),
    radial-gradient(120% 96% at 50% 48%, transparent 46%, rgba(6,5,8,.72) 100%);}
.gal-img{object-fit:contain;z-index:2;}
/* wide frames fill the band outright — there is nothing to letterbox */
.gal-layer.is-wide .gal-img{object-fit:cover;}
.gal-layer.is-wide .gal-bg,.gal-layer.is-wide::before{display:none;}
.stage-gallery:focus-visible{outline:2px solid var(--amber);outline-offset:3px;}
/* on a phone the frame turns upright — most of Freddie's set is portrait */
@media(max-width:700px){.stage-gallery{aspect-ratio:auto;max-height:none;height:clamp(300px,48vh,400px);}}
@media(prefers-reduced-motion:reduce){.gal-layer{transition:none;}}
/* Watch & Listen arrows. They sit ON the strip rather than in a row beneath
   it, because the job here is to say "this thing scrolls" - a control parked
   somewhere else does not. Hover-capable pointers only: a finger already knows
   what to do with a strip like this, and on a phone they would cover a third
   of the tile they are meant to be helping you reach. Each one hides itself at
   its own end, so the strip's limits show rather than needing explaining. */
.vid-wrap{position:relative;}
.vid-arrow{display:none;position:absolute;top:50%;transform:translateY(-50%);z-index:3;
  width:46px;height:46px;align-items:center;justify-content:center;
  border:1px solid var(--line-2);border-radius:50%;cursor:pointer;
  background:color-mix(in srgb, var(--ink) 78%, transparent);color:var(--bone);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  transition:opacity .25s var(--ease),border-color .25s,color .25s,background .25s,transform .2s var(--ease);}
.vid-prev{left:.5rem;}
.vid-next{right:.5rem;}
.vid-arrow:hover{border-color:var(--amber);color:var(--amber-bright);
  background:color-mix(in srgb, var(--ink) 92%, transparent);}
.vid-arrow:active{transform:translateY(-50%) scale(.94);}
/* at an end there is nothing that way to go, so the control goes too */
.vid-arrow[hidden]{opacity:0;pointer-events:none;}
@media (hover:hover) and (pointer:fine){
  .vid-arrow{display:flex;}
}
/* Watch & Listen videos: a horizontal scroll-snap carousel of 16:9 tiles
   (each clip plays inline / opens fullscreen on tap). Scrollbar hidden; on
   phones a smaller play badge suits the tighter tiles. */
[data-vidtrack]::-webkit-scrollbar{display:none;}
@media(max-width:700px){
  [data-vidtrack] .lyt-play{width:44px !important;height:44px !important;}
}
/* video lightbox: tap a thumbnail to watch it big and centred */
.vlb{position:fixed;inset:0;z-index:300;display:none;align-items:center;justify-content:center;padding:clamp(1rem,4vw,3rem);opacity:0;transition:opacity .3s var(--ease);}
.vlb.on{display:flex;opacity:1;}
.vlb-backdrop{position:absolute;inset:0;background:rgba(8,6,5,.92);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);cursor:pointer;}
.vlb-dialog{position:relative;width:min(1100px,100%);display:flex;flex-direction:column;align-items:center;gap:.9rem;}
.vlb-stage{position:relative;max-width:100%;background:#000;border-radius:4px;overflow:hidden;box-shadow:0 30px 90px -20px rgba(0,0,0,.85);}
.vlb-frame img,.vlb-frame video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000;}
.vlb-frame{position:absolute;inset:0;}
.vlb-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0;}
/* the lightbox is always a dark overlay, so its controls and caption keep
   light-on-dark colours in both themes (the theme vars would flip to dark) */
.vlb-nav,.vlb-close{position:absolute;z-index:3;display:flex;align-items:center;justify-content:center;border:1px solid rgba(243,237,225,.3);background:rgba(16,13,11,.55);color:#f3ede1;cursor:pointer;border-radius:50%;transition:background .25s,border-color .25s,color .25s,transform .2s var(--ease);}
.vlb-nav{top:50%;transform:translateY(-50%);width:46px;height:46px;}
.vlb-prev{left:.7rem;} .vlb-next{right:.7rem;}
.vlb-close{top:.7rem;right:.7rem;width:40px;height:40px;}
.vlb-nav:hover{background:rgba(16,13,11,.85);border-color:var(--amber);color:#f2b276;transform:translateY(-50%) scale(1.06);}
.vlb-close:hover{background:rgba(16,13,11,.85);border-color:var(--amber);color:#f2b276;}
.vlb-cap{margin:0;font-family:var(--f-display);font-weight:700;font-size:clamp(1rem,2.2vw,1.22rem);letter-spacing:-.01em;line-height:1.25;color:#f3ede1;text-align:center;max-width:52ch;}
@media(max-width:700px){ .vlb-nav{width:40px;height:40px;} .vlb-prev{left:.4rem;} .vlb-next{right:.4rem;} }
@media(prefers-reduced-motion:reduce){ .vlb{transition:none;} }
[data-gigrow]:hover{background:var(--panel);padding-left:1rem;}
/* the marquee's own drift/drag/momentum is fully rAF-driven in wireMarquee() now
   (not a CSS animation), so its hover-pause is a plain :hover check in that loop */

/* ---------- Study bass: intro + photo/quote card, then the lesson rail ---------- */
/* One glanceable block: heading, tight intro and actions on the left with a
   single topics row; the photo on the right carries a rotating student quote.
   The lessons follow in .teach-panel, always shown - they used to sit behind a
   "Watch him teach" button, which meant the thing the section points at only
   appeared if you pressed something. */
.study-intro{display:flex;flex-direction:column;gap:1.15rem;min-width:0;}
.study-h2{margin:0;font-family:var(--f-display);font-weight:800;text-transform:uppercase;letter-spacing:-.02em;line-height:.92;color:var(--bone);font-size:clamp(2.2rem,5.2vw,3.8rem);}
.study-h2 span{color:var(--amber);}
.study-lead{margin:0;color:var(--body);max-width:34rem;line-height:1.55;font-size:clamp(1.02rem,1.15vw,1.14rem);}
.study-inlink{color:var(--amber-bright);text-decoration:underline;text-underline-offset:.14em;text-decoration-thickness:.06em;transition:color .2s var(--ease);}
.study-inlink:hover,.study-inlink:focus-visible{color:var(--amber);}
.study-actions{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:.1rem;}
.study-btn{font-weight:600;font-size:.95rem;padding:.9rem 1.5rem;border-radius:3px;border:1px solid var(--line-2);display:inline-flex;align-items:center;gap:.5rem;cursor:pointer;text-decoration:none;transition:transform .25s var(--ease),border-color .25s,color .25s,background .25s,box-shadow .25s;}
.study-btn-fill{background:var(--amber-fill);color:var(--on-fill);border-color:var(--amber-fill);}
.study-btn-fill:hover,.study-btn-fill:focus-visible{transform:translateY(-2px);box-shadow:0 14px 34px -12px var(--amber-fill);}
.study-btn-ghost{background:transparent;color:var(--bone);}
.study-btn-ghost:hover,.study-btn-ghost:focus-visible{border-color:var(--amber);color:var(--amber-bright);transform:translateY(-2px);}

/* A pull quote, not a paragraph. The rules editorial typography uses for these:
   40 to 60 characters a line rather than the 66 body copy wants, four or five
   lines at most, set noticeably larger than the surrounding text, and a clear
   device marking it as speech. It was running about 85 characters at body size
   with the name stranded at the far end of the block, which is why it read as
   stray text rather than a testimonial.
   The amber bar is the device, and it is the same accent the timeline and the
   eyebrows use, so it belongs to this page rather than to a template. */
.study-quote{margin-top:clamp(1.6rem,3vw,2.4rem);border-top:1px solid var(--line-2);padding-top:1.2rem;}
/* --qpw tracks four fifths of the height this row settles at, so the stretched
   frame lands on the photographs' own 4:5 and crops next to nothing. The values
   are solved against the longest quote at each width, not guessed. */
.study-qrow{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:clamp(1rem,2.4vw,2.2rem);
  align-items:stretch;--qpw:clamp(196px,calc(104px + 9.5vw),268px);
  /* the frame used to take whatever height the quote happened to settle at,
     which on the shorter quotes left it small. Now it sets a floor of its own
     4:5 instead, so the picture is the thing driving the row and the students
     are big enough to see playing. The text column stretches to match, so the
     amber rule still runs the full depth. */
  }
/* past this width the 1240px container has stopped growing while the quote type
   has not, so the longest quote takes one more line and the row steps taller.
   The frame steps with it rather than being left behind at the narrower value. */
@media(min-width:1352px){
  .study-qrow{--qpw:clamp(268px,21vw,340px);}
}
/* The frame still takes the height of the text so both edges of the row match,
   but its width is now set to about four fifths of that height. At 4:5 the box
   is the native shape of the photographs, so cover crops essentially nothing and
   the players keep their basses. A wider box was slicing the top and bottom off. */
/* The frame keeps 4:5 - the photographs' own shape - at every width, so cover
   never has anything to crop. It used to stretch to the height of the quote
   beside it, which was fine while the frame was the taller of the two but
   between about 660 and 740px the words outgrew it: the box went taller than
   4:5 and cover took 9-22% off the SIDES, which is exactly where a bass neck
   sits. Wide layouts are unchanged - the old min-height was qpw x 1.25, which
   is 4:5 already. */
.study-qtext-col{grid-column:1;grid-row:1;}
.study-qphoto{grid-column:2;grid-row:1;}
.study-qphoto{position:relative;overflow:hidden;border-radius:3px;background:var(--panel);
  width:var(--qpw);aspect-ratio:4/5;align-self:start;
  padding:0;border:0;cursor:pointer;display:block;
  box-shadow:0 0 0 1px var(--line-2);transition:box-shadow .25s;-webkit-tap-highlight-color:transparent;}
.study-qphoto:hover,.study-qphoto:focus-visible{box-shadow:0 0 0 1px var(--amber);}
.study-qphoto img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* The tall portraits fill the frame on their own. The square snapshots are
   shown whole instead of cropped (see the fit() in main.js), which leaves bands
   above and below; this is the same picture blown out and blurred behind them,
   so the frame still reads as full rather than as a letterboxed accident. */
.study-qphoto img[data-quotebackdrop]{object-fit:cover;transform:scale(1.15);
  filter:blur(22px) saturate(1.15) brightness(.5);opacity:0;transition:opacity .45s var(--ease);}
.study-qphoto.has-backdrop img[data-quotebackdrop]{opacity:1;}
.study-qtext-col{display:flex;flex-direction:column;justify-content:flex-start;width:100%;
  text-align:left;background:none;border:0;padding:0;cursor:pointer;color:inherit;font:inherit;
  min-width:0;-webkit-tap-highlight-color:transparent;}
/* grows to fill whatever height the frame beside it sets, so the accent rule
   runs the full depth of the row. The words inside it sit centred on that
   depth: now the picture sets the height rather than the text, a short quote
   left flush to the top opened a hole between it and the credit. */
.study-qbar{border-left:3px solid var(--amber);padding-left:clamp(1rem,1.6vw,1.5rem);
  flex:1;display:flex;flex-direction:column;justify-content:center;}
.study-quote-fig{margin:0;display:grid;}
.study-quote .study-q{display:block;}
.study-quote .study-q-body{display:block;}
/* each blockquote keeps its own photo, stars and name for assistive tech and the
   no-script case; only the lifted-out picture and credit are shown */
.study-quote .study-stars,.study-quote-fig .study-av,.study-quote-fig .study-qwho{
  position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
.study-quote .study-qtext{margin:0;font-family:var(--f-display);font-weight:500;line-height:1.32;
  color:var(--bone);font-size:clamp(1.18rem,1.95vw,1.82rem);}
/* indented under the quote, and following it directly rather than being pushed
   to the foot: the block as a whole is centred on the frame's height now, so a
   credit dropped to the bottom would read as detached from the words it names */
.study-qbar > .study-qwho{display:flex;align-items:baseline;gap:.55rem;flex-wrap:wrap;
  padding-top:1.3rem;padding-left:clamp(1.1rem,2vw,2rem);font-size:.92rem;line-height:1.35;}
.study-qdash{color:var(--amber);font-weight:700;margin-right:.1rem;letter-spacing:-.04em;}
.study-qname{font-family:var(--f-display);font-weight:700;color:var(--bone);font-size:1rem;}
.study-qrole{color:var(--mute);}
.study-q{margin:0;grid-area:1/1;display:flex;gap:.85rem;align-items:center;transition:opacity .5s var(--ease);}
.study-av{width:58px;height:58px;border-radius:50%;flex:none;object-fit:cover;border:1px solid var(--line-2);}
.study-av-mono{display:inline-flex;align-items:center;justify-content:center;background:color-mix(in oklab,var(--amber) 20%,var(--panel));color:var(--amber-bright);font-family:var(--f-display);font-weight:700;font-size:1.4rem;}
.study-q-body{display:flex;flex-direction:column;gap:.3rem;min-width:0;}
.study-stars{color:var(--amber);letter-spacing:.16em;font-size:.72rem;}
.study-qtext{margin:0;font-family:var(--f-body);font-weight:500;font-size:.95rem;line-height:1.42;color:var(--bone);}
.study-qwho{font-size:.78rem;font-weight:500;color:var(--body);}

/* Heading, lead and button on the left; the lesson reel alongside them on the
   right, rather than the reel running the full width underneath. Both columns
   are inside the page container now, so neither needs its own gutters. */
.study-top{display:grid;grid-template-columns:minmax(0,0.8fr) minmax(0,1.2fr);
  gap:clamp(1.6rem,4vw,3.4rem);align-items:start;}
@media(max-width:920px){.study-top{grid-template-columns:1fr;gap:2rem;align-items:start;}}
.teach-panel{min-width:0;display:flex;flex-direction:column;}
.teach-railwrap{display:flex;}
.teach-railfoot{display:flex;justify-content:flex-end;gap:.6rem;margin-top:1rem;}
.teach-navbtn{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line-2);background:transparent;color:var(--bone);border-radius:3px;
  cursor:pointer;transition:border-color .25s,color .25s,transform .25s var(--ease);}
.teach-navbtn:hover,.teach-navbtn:focus-visible{border-color:var(--amber);color:var(--amber-bright);transform:translateY(-2px);}
/* The lessons used to be a grid of 16:9 tiles with a second grid of portrait
   shorts stacked above it, which ran to most of a screen for nine clips. They
   are one horizontal rail now, built like Watch & Listen: every tile is the
   same height and its aspect ratio decides its width, so the shorts come out
   narrow beside the lessons and the strip still reads as one level row. */
.teach-railwrap{-webkit-mask-image:linear-gradient(90deg,#000 78%,transparent);
  mask-image:linear-gradient(90deg,#000 78%,transparent);}
/* One row, as tall as it can be while a whole 16:9 lesson still fits the
   column's width - which is what stops it becoming a strip of part-tiles. Every
   tile fills that height and takes its width from its own aspect, so a lesson
   is wide, a short is narrow, and the row stays level. --railh is written by
   wireTeachRail from the measured column: the tiles need a real length to
   derive a width from, and a percentage cannot serve in a horizontal scroller. */
.teach-rail{--railh:190px;
  display:flex;align-items:stretch;gap:.9rem;overflow-x:auto;overscroll-behavior-x:contain;
  scrollbar-width:none;-ms-overflow-style:none;padding-bottom:.5rem;}
.teach-rail::-webkit-scrollbar{display:none;}
@media(max-width:700px){
  .teach-rail .lyt-play{width:44px !important;height:44px !important;}
}
/* a short is about a third the width of a lesson at the same height, so the
   caption that fits a lesson in two lines runs out of room here: smaller, and
   allowed a third line before it clips */
.lyt-btn.is-portrait .lyt-title{font-size:.76rem;line-height:1.28;
  padding:.5rem .58rem 1.1rem;-webkit-line-clamp:3;}
/* corner marker for a tile that is more than a clip (the paid course) */
.lyt-badge{position:absolute;right:.6rem;bottom:.6rem;pointer-events:none;
  font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;color:#f3ede1;
  border:1px solid rgba(243,237,225,.55);padding:.24rem .55rem;border-radius:2px;
  white-space:nowrap;background:rgba(0,0,0,.42);text-shadow:0 1px 3px rgba(0,0,0,.8);}

}

/* ---------- type roles ----------
   The section headings are the design language and stay as they are. Below
   them the page had four different sizes doing the same job: the About prose,
   the Services intro, the Study lead and the Contact lead all landed within
   3px of each other, which reads as drift rather than hierarchy. These give
   the repeating roles one size each, and hold the ratio between them at every
   width rather than only at the top end. */
.t-lead,#about > div > div > div[data-reveal] > p,#services > div > p,.study-lead,#contact p.t-lead{
  font-size:clamp(1.02rem,1.15vw,1.14rem);line-height:1.55;}
/* the nav should never outrank the copy it sits above: on a phone it was 16.8px
   against 16.3px body */
@media(max-width:920px){[data-navlinks] a{font-size:1rem !important;}}

/* the picture keeps its share of a phone screen without squeezing the quote to
   a ribbon: both columns stay readable and their edges still line up */
/* The picture stays beside the quote on a phone too, so it takes a smaller
   share of the width there and the quote steps down with it. */
/* one column below 921, so the row is wider and the quote shorter; the frame
   grows as the screen narrows to keep pace with the height the words take */
@media(max-width:920px){
  .study-qrow{--qpw:clamp(200px,26vw,260px);}
}
@media(max-width:740px){
  .study-qrow{--qpw:clamp(190px,30vw,240px);}
}
/* A phone cannot have all three of picture-right, whole frame and matched
   bottoms: below about 660px there is no frame width where a 4:5 box is as tall
   as the words beside it, because narrowing the column adds lines faster than
   the frame gains height. The whole frame wins, so the picture sits top right at
   its native shape and the quote runs on past it. */
@media(max-width:659px){
  .study-qrow{--qpw:clamp(150px,38vw,210px);gap:.75rem;}
  .study-qbar{padding-left:.75rem;}
  /* The two-column grid left a wedge of nothing under the picture, beside the
     credit, on every quote shorter than the frame is tall. Floating it instead
     lets the words run round it and then out to the full width underneath.
     A float can only be intruded upon by inline content in the same formatting
     context, so the stack of seven has to come apart here: the one in view sits
     in normal flow and the rest are taken out of it. They still cross-fade -
     opacity does not care whether a box is positioned. */
  .study-qrow{display:block;}
  .study-qphoto{float:right;width:var(--qpw);margin:0 0 .7rem .85rem;}
  /* every box between the float and the words has to stay a plain block: a
     flex container establishes its own formatting context and steps around a
     float instead of letting the lines run past it */
  .study-qtext-col,.study-qbar{display:block;}
  .study-quote-fig{display:block;position:relative;}
  .study-q{position:absolute;top:0;left:0;right:0;}
  .study-q.is-on{position:static;}
  .study-quote .study-qtext{font-size:clamp(1rem,3.5vw,1.2rem);line-height:1.36;}
  .study-qbar > .study-qwho{padding-top:.9rem;padding-left:.8rem;font-size:.84rem;}
}
/* Under about 430px the column beside the picture is narrow enough that the
   quote needs a smaller size to keep a readable number of words a line. */
@media(max-width:430px){
  .study-qrow{--qpw:clamp(132px,36vw,168px);gap:.65rem;}
  .study-qbar{padding-left:.65rem;}
  .study-quote .study-qtext{font-size:.95rem;line-height:1.38;}
  .study-qbar > .study-qwho{padding-top:.8rem;padding-left:.7rem;font-size:.8rem;gap:.4rem;}
  .study-qname{font-size:.88rem;}
}
