/* instrument — advisory, upskilling, custom
   The service-page cluster minus core. All three pages carry a byte-identical
   stylesheet (19,005 B) emitted by build/services/S7-build.mjs, so this file
   plus core is the whole of it — the three pages have no remainder.
   63 of these rules (11,199 B) are also carried verbatim by /about, which
   descends from the same stylesheet; they are repeated in pages/about.css
   because the four remaining rules here are on the service pages only and
   would render on /about if it linked this file.
*/

html{-webkit-text-size-adjust:100%}
b,strong,h1,h2,h3,h4{font-weight:400}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
.arrow{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:8px;background:var(--signal);color:var(--ink);flex:none}
.arrow svg{width:16px;height:16px}
.link{display:inline-flex;align-items:center;gap:12px;font-family:var(--mono);font-size:13px;letter-spacing:-0.02em;text-transform:uppercase;min-height:44px}
.link .ln{border-bottom:1px solid var(--hair-d);padding-bottom:2px}
.light .link .ln{border-bottom-color:var(--hair-l)}
.link .ar{width:14px;height:14px;flex:none}
.acts{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:14px;min-height:44px}
.brand .wm .name{font-size:17px;letter-spacing:-0.01em}
.brand .wm .line{font-family:var(--mono);font-size:11px;letter-spacing:0.02em;text-transform:uppercase;color:var(--mute-d);margin-top:3px;white-space:nowrap}
.nav ul a{display:inline-flex;align-items:center;min-height:44px;padding:0 14px;border-radius:12px;border:1px solid transparent;color:var(--mute-d)}
.nav ul a[aria-current="page"]{color:var(--paper);border-color:var(--hair-d)}
/* hero: the door. Three lines; subline and CTA inside the fold. */
.hero{padding:72px 0 0}
.hero h1{font-size:clamp(3.125rem,8vw,7.25rem);line-height:1;letter-spacing:-0.028em;max-width:66%;margin-top:28px}
.hero.up h1{font-size:clamp(2.5rem,6.7vw,6rem);letter-spacing:-0.024em;max-width:78%}
.hero .low{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:48px;margin-top:56px;align-items:start}
.hero .body{color:var(--mute-d);max-width:46ch;text-wrap:pretty}
.hero .acts{margin-top:32px}
.hero .acts.top{margin-top:6px}
.pubbox{display:none}
.two{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);gap:48px;align-items:stretch}
.two .head{display:flex;flex-direction:column;gap:24px}
.two h2{font-size:clamp(2.5rem,5.2vw,4.5rem);line-height:1.05;letter-spacing:-0.02em;max-width:12ch;margin-top:4px;text-wrap:balance}
.two .r .sub{font-size:24px;line-height:1.2;letter-spacing:-0.006em;max-width:24em;text-wrap:pretty;margin-bottom:28px}
.two .r .body{color:var(--mute-d);max-width:48ch;text-wrap:pretty}
.light .two .r .body{color:var(--mute-l)}
.two .head .body{color:var(--mute-d);max-width:40ch;text-wrap:pretty}
.light .two .head .body{color:var(--mute-l)}
.list li{padding:18px 0;font-size:22px;line-height:1.25;letter-spacing:-0.004em;border-top:1px solid var(--hair-d);text-wrap:pretty}
.list li:last-child{border-bottom:1px solid var(--hair-d)}
.light .list li{border-color:var(--hair-l)}
.list li b{display:block}
.list li span{display:block;color:var(--mute-d);font-size:18px;margin-top:6px;max-width:48ch}
.light .list li span{color:var(--mute-l)}
.steps li{display:grid;grid-template-columns:32px 160px minmax(0,1fr);gap:16px;align-items:baseline;padding:20px 0;border-top:1px solid var(--hair-d)}
.steps li:last-child{border-bottom:1px solid var(--hair-d)}
.steps.side li{grid-template-columns:32px 160px minmax(0,1fr)}
.steps.side .sd{grid-column:2;text-align:left;margin-top:6px}
.steps .n{font-family:var(--mono);font-size:13px;letter-spacing:-0.02em;color:var(--mute-d)}
.steps .s{font-family:var(--mono);font-size:15px;letter-spacing:-0.007em;text-transform:uppercase}
.steps .sd{font-family:var(--mono);font-size:13px;letter-spacing:-0.02em;text-transform:uppercase;color:var(--dim-d);text-align:right}
.steps .sd.y{color:var(--mute-d)}
.steps p{color:var(--mute-d);font-size:18px;text-wrap:pretty}
.steps li.on .s{color:var(--signal)}
.proof{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);gap:48px;padding-top:32px;margin-top:56px;border-top:1px solid var(--hair-d)}
.proof .cl{grid-column:2}
.light .proof{border-color:var(--hair-l)}
.proof .cl{color:var(--paper);display:inline-flex;min-height:44px;align-items:center}
.light .proof .cl{color:var(--ink)}
/* ledger rows: the homepage's row grammar */
.row{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1.3fr) minmax(0,1fr);gap:40px;padding:48px 0;position:relative}
.row + .row{border-top:1px solid var(--hair-d)}
.row .name{font-size:clamp(2.75rem,5vw,4rem);line-height:1;letter-spacing:-0.02em;margin-top:18px;text-wrap:balance}
.row .tf{display:block;color:var(--dim-d);margin-top:16px;max-width:30ch;text-wrap:pretty}
.row .lbl{display:block;color:var(--mute-d);margin-bottom:10px}
.row .learn{font-size:22px;line-height:1.3;letter-spacing:-0.004em;margin-bottom:16px;text-wrap:pretty}
.row .body{color:var(--mute-d);font-size:17px;text-wrap:pretty}
.row .out{font-size:18px;line-height:1.3;text-wrap:pretty}
.row .go{margin-top:20px}
.row .go + .go{margin-left:20px}
.row .wide{grid-column:2/-1}
.rowpub{grid-column:1/-1;height:200px;margin-top:16px}
.pubband{flex:1 1 auto;min-height:220px}
/* custom's beats are built pictures, not lane clouds: they need the room to resolve */
.cu .rowpub{height:280px}
.cu .pubband{min-height:360px}
.cal{margin-top:24px}
.cal li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;padding:12px 0;border-top:1px solid var(--hair-d);font-family:var(--mono);font-size:13px;letter-spacing:-0.02em;text-transform:uppercase}
.cal li:last-child{border-bottom:1px solid var(--hair-d)}
.cal .s{color:var(--paper)}
.cal .w{color:var(--dim-d);text-align:right}
.cal li.on .s{color:var(--signal)}
/* closing */
.close{padding:140px 0 130px}
.close .grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:48px;align-items:start}
.close h2{font-size:clamp(2.5rem,5.4vw,4.6875rem);line-height:1.05;letter-spacing:-0.02em;max-width:12ch;text-wrap:balance}
.close .sub{color:var(--mute-d);margin:32px 0 40px;max-width:44ch;font-size:22px;line-height:1.3;text-wrap:pretty}
/* responsive */
@media (max-width:1023px){
  .row{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .row .col3{grid-column:1/-1}
  .rowpub,.pubband{display:none}
  .fgrid{grid-template-columns:1fr 1fr 1fr}
  .fgrid .about{grid-column:1/-1}
  .hero h1{max-width:100%}
  .hero.up h1{max-width:100%}
  .pubbox{display:block;height:230px;margin-top:40px}
  .close .grid{grid-template-columns:1fr}
  .close .pubbox{margin:0 0 40px;height:230px}
  .cu .pubbox{height:300px}
  /* custom keeps every beat's band down to 390px: the offering rows and the section heads */
  .cu .rowpub,.cu .pubband{display:block}
  .cu .rowpub{height:270px;margin-top:24px}
  .cu .pubband{min-height:270px;margin-top:12px}
  /* upskilling keeps every beat's band down to 390px: the three track rows and the bone band */
  .up .rowpub,.up .pubband{display:block}
  .up .rowpub{height:230px;margin-top:24px}
  .up .pubband{min-height:220px;margin-top:16px}
  .steps.side li{grid-template-columns:32px 140px minmax(0,1fr)}
}
@media (max-width:1139px){
  .nav .wrap{gap:16px}
  .nav ul{gap:2px}
  .nav ul a{padding:0 9px}
  .nav .cta{margin-left:0}
}
@media (max-width:980px){
  .nav ul{display:none;position:absolute;left:0;right:0;top:100%;background:var(--ink);padding:8px var(--gut) 24px;flex-direction:column;gap:0;margin:0;z-index:5;border-bottom:1px solid var(--hair-d)}
  .nav[data-open="true"] ul{display:flex}
  .nav ul a{min-height:52px;padding:0;border-radius:0;color:var(--paper);font-size:15px;border:0}
  .nav ul a[aria-current="page"]{border:0}
  .nav ul li + li a{border-top:1px solid var(--hair-d)}
  .nav .cta{display:none}
  .m-cta{display:block;padding-top:20px}
  .nav ul li.m-cta a{border-top:0;min-height:44px;padding:10px 16px;border-radius:8px;color:var(--ink);font-size:13px}
  .menu{display:inline-flex;align-items:center;justify-content:center}
}
@media (max-width:760px){
  br.d{display:none}
  section{padding:80px 0}
  .brand .wm .name{font-size:15px}
  .hero{padding:48px 0 0}
  .hero h1{font-size:clamp(2.875rem,13.4vw,4.5rem)}
  .hero.up h1{font-size:clamp(2.25rem,10.4vw,3.75rem)}
  .hero .low{grid-template-columns:1fr;gap:24px;margin-top:36px}
  .hero .sub{max-width:100%}
  .hero .acts{margin-top:24px}
  .pubbox{height:210px;margin-top:32px}
  .close .pubbox{height:190px}
  .cu .pubbox{height:290px}
  .cu .close .pubbox{height:270px}
  .two{grid-template-columns:1fr;gap:32px}
  .two h2{max-width:100%}
  .two .r .sub{margin-bottom:20px}
  .list li{padding:12px 0;font-size:20px}
  .steps li{grid-template-columns:28px minmax(0,1fr);gap:8px 12px;padding:16px 0}
  .steps.side li{grid-template-columns:28px minmax(0,1fr)}
  .steps p{grid-column:2/-1}
  .steps .sd{grid-column:2/-1;text-align:left}
  .proof{grid-template-columns:1fr;gap:12px;margin-top:40px;padding-top:24px}
  .proof .cl{grid-column:1}
  .row{grid-template-columns:1fr;gap:20px;padding:34px 0}
  .row .wide{grid-column:auto}
  .row .name{margin-top:12px;font-size:40px}
  .row .go{margin-top:16px}
  .cal{margin-top:18px}
  .cal li{padding:10px 0}
  .close{padding:96px 0 88px}
  .close .sub{margin:24px 0 28px}
  .fgrid{grid-template-columns:1fr 1fr}
  footer{padding-top:72px}
}
/* ---------- the phone, composed ----------
   The ladder above ends at 760px, and a 390px phone was taking that rung. What a phone needs
   deciding that a tablet does not:

   The hero headline is the one thing on these pages that should stay the size it is — it is the
   door, and at 390px it already sets two or three full-bleed lines. Everything under it had
   collapsed towards it: section head 40px, row name 40px, closing head 40px, against a 52px
   h1. Four sizes within twelve pixels is not a hierarchy, it is noise. The heads drop to a step
   the h1 clears by half again, the sub-heads collapse onto one size, and the ranks that lost
   their size keep their place through the mono label already sitting above each of them.

   The rhythm follows: 80px of air above and below a section is a desktop measure, and the beat
   bands — which are the reason these pages have air at all — carry their own reserved height. */
@media (max-width:599px){
  /* the whole rag, not just the last line — see archive.css for why this is phone-only */
  .hero h1,.hero.up h1,.two h2,.close h2,.row .name,.row .learn{text-wrap:balance}
  /* the four steps */
  .two h2,.close h2{font-size:28px;line-height:1.08;letter-spacing:-0.016em}
  .row .name{font-size:30px;line-height:1.02;margin-top:10px}
  .two .r .sub,.row .learn,.list li,.close .sub{font-size:20px;line-height:1.25;letter-spacing:-0.004em}
  .list li span,.steps p,.row .body,.row .out,.hero .body,.two .r .body,.two .head .body{font-size:17px}
  .hero .sub{font-size:21px;line-height:1.22}

  /* the rhythm */
  section{padding:60px 0}
  .hero{padding:28px 0 0}
  .hero .low{margin-top:28px;gap:20px}
  .hero .acts{margin-top:20px}
  .close{padding:72px 0 64px}
  .close .sub{margin:20px 0 26px}
  footer{padding-top:60px}
  .fbot{margin-top:56px}

  /* density */
  .two{gap:24px}
  .list li{padding:10px 0}
  .steps li{padding:14px 0}
  .row{padding:26px 0;gap:16px}
  .proof{margin-top:28px;padding-top:20px}
  .cal li{padding:9px 0}
}

/* the public: one fixed canvas above the bands, below the masthead */
#pub{position:fixed;inset:0;width:100%;height:100%;z-index:2;pointer-events:none;display:block}
main,footer{position:relative;z-index:1}
/* motion, one easing, guarded */
@media (prefers-reduced-motion:no-preference){
  .rv{opacity:0;transform:translateY(12px);transition:opacity var(--dur-enter) var(--ease),transform var(--dur-enter) var(--ease)}
  .rv.in{opacity:1;transform:none}
  .nav .brand,.nav ul li,.nav .cta,.nav .menu,.hero .tag,.hero h1 .w,.hero .sub,.hero .body,.hero .acts .btn{opacity:0}
  /* The arrival. Identical to the one in pages/home.css, deliberately: the two
     share the `drop` / `word` / `light` / `rise` vocabulary and used to run it at
     two tempos, 1320ms here against 1840ms there. One step, one landing. */
  .go .nav .brand,.go .nav ul li,.go .nav .cta,.go .nav .menu{animation:drop var(--dur-signature) var(--ease) both}
  /* `.go ` on both, because the shorthand above is (0,3,0) and a bare `.nav .cta`
     is (0,2,0): the shorthand's implicit `animation-delay:0` outranked it and the
     masthead's last item dropped first, with the brand. That held on all four
     pages from the day the ladder was written. */
  .go .nav .brand{animation-delay:0ms}
  .go .nav ul li:nth-child(1){animation-delay:calc(var(--stagger)*2)}.go .nav ul li:nth-child(2){animation-delay:calc(var(--stagger)*3)}.go .nav ul li:nth-child(3){animation-delay:calc(var(--stagger)*4)}.go .nav ul li:nth-child(4){animation-delay:calc(var(--stagger)*5)}.go .nav ul li:nth-child(5){animation-delay:calc(var(--stagger)*6)}
  .go .nav .cta,.go .nav .menu{animation-delay:calc(var(--stagger)*7)}
  @keyframes drop{from{opacity:0;transform:translateY(-6px)}to{opacity:1}}
  .go .hero .tag{animation:rise var(--dur-signature) var(--ease) calc(var(--stagger)*2) both}
  .hero h1 .w{display:inline-block;color:var(--dim-d)}
  .go .hero h1 .w{animation:word 360ms var(--ease) both,light var(--dur-signature) var(--ease) both;animation-delay:calc(var(--stagger)*3 + var(--i,0)*var(--stagger)),calc(var(--stagger)*5 + var(--i,0)*var(--stagger))}
  @keyframes word{from{opacity:0;transform:translateY(.3em)}to{opacity:1}}
  @keyframes light{from{color:var(--dim-d)}to{color:var(--paper)}}
  .go .hero .sub{animation:rise var(--dur-signature) var(--ease) calc(var(--stagger)*10) both}
  .go .hero .body{animation:rise var(--dur-signature) var(--ease) calc(var(--stagger)*11) both}
  .go .hero .acts .btn{animation:rise var(--dur-signature) var(--ease) both}
  .go .hero .acts .btn:nth-child(1){animation-delay:calc(var(--stagger)*12)}.go .hero .acts .btn:nth-child(2){animation-delay:calc(var(--stagger)*13)}
  @keyframes rise{from{opacity:0;transform:translateY(12px)}to{opacity:1}}
  @keyframes typeset{from{clip-path:inset(-12% 100% -12% -8%);opacity:.7}to{clip-path:inset(-12% -80% -12% -8%);opacity:1}}
  @keyframes rule{0%{transform:scaleX(0);opacity:.9}70%{transform:scaleX(1);opacity:.9}100%{transform:scaleX(1);opacity:0}}

  .sg:not(.in) :is(
    .two .head > *, .two .r > :not(.list):not(.steps), .two .r .list li, .two .r .steps li,
    .row > div:first-child > :not(.cal), .row .cal li, .row > div:nth-child(2) > *, .row .col3 > *,
    .proof > *,
    .close .grid > div:first-child > *
  ){opacity:0}
  /* signature containers: one step, reading order, no ladder past five steps —
     so every entrance, delay included, completes within 600ms. The steps here
     used to run 50, 60, 80 and 100ms, and the `.list`/`.steps` ladder changed
     step at its sixth item (360 to 370). */
  .two.sg.in .head > .tag{animation:rise var(--dur-enter) var(--ease) both}
  .two.sg.in .head h2{animation:typeset 460ms var(--ease) var(--stagger) both}
  .two.sg.in .head .body{animation:rise var(--dur-enter) var(--ease) calc(var(--stagger)*2) both}
  .two.sg.in .r > :not(.list):not(.steps){animation:rise var(--dur-enter) var(--ease) calc(var(--stagger)*2) both}
  .two.sg.in .r .list li,.two.sg.in .r .steps li{animation:rise var(--dur-enter) var(--ease) both}
  .two.sg.in .r :is(.list,.steps) li:nth-child(1){animation-delay:calc(var(--stagger)*1)}
  .two.sg.in .r :is(.list,.steps) li:nth-child(2){animation-delay:calc(var(--stagger)*2)}
  .two.sg.in .r :is(.list,.steps) li:nth-child(3){animation-delay:calc(var(--stagger)*3)}
  .two.sg.in .r :is(.list,.steps) li:nth-child(4){animation-delay:calc(var(--stagger)*4)}
  .two.sg.in .r :is(.list,.steps) li:nth-child(5){animation-delay:calc(var(--stagger)*5)}
  .two.sg.in .r :is(.list,.steps) li:nth-child(6){animation-delay:calc(var(--stagger)*5)}
  .proof.sg.in > *{animation:rise var(--dur-enter) var(--ease) both}
  .proof.sg.in > :nth-child(2){animation-delay:calc(var(--stagger)*2)}

  .row.sg::before{content:"";position:absolute;left:0;right:0;top:-1px;height:1px;background:var(--paper);transform:scaleX(0);transform-origin:left center;opacity:0;pointer-events:none}
  .row + .row.sg.in::before{animation:rule var(--dur-signature) var(--ease) both}
  .row.sg.in > div:first-child > .mono{animation:rise var(--dur-enter) var(--ease) both}
  .row.sg.in > div:first-child > .name{animation:typeset 440ms var(--ease) var(--stagger) both}
  .row.sg.in > div:first-child > .tf{animation:rise var(--dur-enter) var(--ease) calc(var(--stagger)*3) both}
  .row.sg.in .cal li{animation:rise var(--dur-enter) var(--ease) both}
  .row.sg.in .cal li:nth-child(1){animation-delay:calc(var(--stagger)*3)}.row.sg.in .cal li:nth-child(2){animation-delay:calc(var(--stagger)*4)}.row.sg.in .cal li:nth-child(3){animation-delay:calc(var(--stagger)*5)}
  .row.sg.in > div:nth-child(2) > *{animation:rise var(--dur-enter) var(--ease) both}
  .row.sg.in > div:nth-child(2) > :nth-child(1){animation-delay:calc(var(--stagger)*1)}
  .row.sg.in > div:nth-child(2) > :nth-child(2){animation-delay:calc(var(--stagger)*2)}
  .row.sg.in > div:nth-child(2) > :nth-child(3){animation-delay:calc(var(--stagger)*3)}
  .row.sg.in .col3 > *{animation:rise var(--dur-enter) var(--ease) both}
  .row.sg.in .col3 > :nth-child(1){animation-delay:calc(var(--stagger)*2)}
  .row.sg.in .col3 > :nth-child(2){animation-delay:calc(var(--stagger)*3)}
  .row.sg.in .col3 > :nth-child(3){animation-delay:calc(var(--stagger)*4)}
  .row.sg.in .col3 > :nth-child(4){animation-delay:calc(var(--stagger)*5)}

  .close .grid.sg.in > div:first-child > h2{animation:typeset var(--dur-signature) var(--ease) both}
  .close .grid.sg.in > div:first-child > .sub{animation:rise var(--dur-enter) var(--ease) var(--stagger) both}
  .close .grid.sg.in > div:first-child > .acts{animation:rise var(--dur-enter) var(--ease) calc(var(--stagger)*2) both}

  /* state */
  .nav ul a{transition:color 200ms var(--ease),border-color 200ms var(--ease)}
  /* a pointer that cannot hover never sees the lift, so the press is the acknowledgement.
     The transition comes with it, because the block below hands the shared one to hover only. */
  @media (hover:none){
    .btn,.menu,.arrow{transition:transform 200ms var(--ease)}
    .btn:active,.menu:active,.arrow:active{transform:scale(.97)}
  }
  .link .ln{transition:border-color 200ms var(--ease)}
  .link .ar,.link .arrow{transition:transform 200ms var(--ease)}
  .steps .s,.cal .s{transition:color 400ms var(--ease)}
  /* Hover latches on touch — a finger has no way to leave the element — so the
     lift and the ring stayed on a button after a tap and the pressed state
     below never showed. Both belong to pointers that can hover. The
     `box-shadow` transition goes with them: it is the one non-compositor
     property in this block, and off a phone's hover path it costs nothing. */
  @media (hover:hover){
    .btn,.menu{transition:opacity 200ms var(--ease),transform 200ms var(--ease),box-shadow 200ms var(--ease)}
    .nav ul a:hover{color:var(--paper);border-color:var(--hair-d)}
    .btn:hover,.menu:hover{transform:translateY(-1px);box-shadow:0 0 0 3px rgba(24,181,174,.28)}
    .btn:active{transform:none}
    .link:hover .ln{border-bottom-color:currentColor}
    .link:hover .ar,.link:hover .arrow{transform:translateX(4px)}
  }
}
/* The product links carry an icon, which widens the nav list by ~36px. Without this the flex row
   shrinks the brand instead and the wordmark wraps to two lines around 1000-1024px. */
.brand{flex:0 0 auto}
.brand .wm .name{white-space:nowrap}
.nav ul a{white-space:nowrap}
/* links that leave for a product's own site */
.ext{width:11px;height:11px;margin-left:7px;flex:0 0 auto;vertical-align:baseline;opacity:.75}
.extlink:focus-visible .ext{opacity:1}
@media (hover:hover){ .extlink:hover .ext{opacity:1} }
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
/* .link already carries a 12px flex gap, so the icon does not need its own left margin there */
.link .ext{margin-left:0}
/* advisory: every beat carries its own formation, so its bands stay open all the way down to 390px */
body.ad .walk .pubband{min-height:300px}
@media (max-width:1023px){
  body.ad .pubband{display:block;min-height:210px;margin-top:4px}
  body.ad .how .pubband{min-height:250px}
  body.ad .walk .pubband{min-height:280px}
}
@media (max-width:760px){
  body.ad .pubband{min-height:200px;margin-top:8px}
  body.ad .how .pubband{min-height:240px}
  body.ad .walk .pubband{min-height:270px}
}
/* advisory, the opening argument. Its band is framed and holds the video's proportion at every
   width, because the founder's cut of this argument is what goes in it. The frame is the only
   ruled box on the page and it is there to say "a screen sits here", so it takes the button's
   radius and the same hairline as every rule on the page. The three min-heights above are
   overridden rather than removed: a 16:9 frame is taller than all of them at 390px, and a floor
   under an aspect-ratio box only breaks the ratio. */
body.ad .reel{display:flex;flex-direction:column;gap:16px}
body.ad .reel .pubband{flex:none;aspect-ratio:16/9;min-height:0;margin:0;border:1px solid var(--hair-d);border-radius:8px}
body.ad .reel figcaption .mono{display:block;margin-bottom:10px}
/* the caption's paragraph read as `.two .head .body` until the frame left `.head` */
body.ad .reel figcaption .body{color:var(--mute-d);max-width:40ch;text-wrap:pretty}
/* The frame is a grid child of `.two` rather than of `.head`, so that a phone reads the claims
   before the picture that illustrates them (the why is in S7-build.mjs). These four rules put it
   back under the head on the desktop grid: row two of the left column, right column spanning
   both rows. `row-gap` is restated at each step because the 24px here replaces what used to be
   `.head`'s own flex gap, and without the tier's 32 and 24 coming back under it the section
   grows by the difference and shunts the next one down the page. */
body.ad .stance .two{grid-template-rows:auto 1fr;row-gap:24px}
body.ad .stance .head{grid-row:1}
body.ad .stance .reel{grid-column:1;grid-row:2}
body.ad .stance .r{grid-column:2;grid-row:1/span 2}
@media (max-width:760px){
  body.ad .stance .two{grid-template-rows:none;row-gap:32px}
  body.ad .stance .head,body.ad .stance .r,body.ad .stance .reel{grid-column:auto;grid-row:auto}
}
@media (max-width:599px){
  body.ad .stance .two{row-gap:24px}
  body.ad .reel figcaption .body{font-size:17px}
}
@media (prefers-reduced-motion:no-preference){
  /* out of `.head`, the frame is no longer covered by the shared `.two .head > *` hide, so it
     carries its own. Same guard, so a reader who asked for no motion is never handed a
     hidden element with no animation to bring it back. */
  body.ad .stance .two.sg:not(.in) .reel{opacity:0}
  /* the section's one entrance, on the page's own vocabulary: the frame arrives after the
     block it belongs to, and lands inside the 650ms the block above holds itself to */
  .two.sg.in .reel{animation:rise var(--dur-enter) var(--ease) calc(var(--stagger)*3) both}
}

/* ====================== the beat rail ======================================
   Appended at build time to instrument.css (the three service pages) and to
   pages/home.css (the homepage). Never linked as a file of its own: the tiers
   do not share one — see docs/build.md § "CSS: three tiers", which records why.

   Two halves, and the split is the whole design.

   The rail chrome (#rail and its stations) renders whenever the markup is
   present and the viewport is wide enough. It is wayfinding, not motion, so it
   survives `prefers-reduced-motion: reduce` — with every transition switched off.

   The stage (#spacer, .beat, the fixed masthead) lives entirely under `html.rail`,
   a class JS sets only when `(min-width:1024px)` matches and reduced motion is not
   asked for. Without the class the page is exactly the flow document that shipped
   before this file existed. That is the no-JS fallback, the phone, the tablet, and
   the reduced-motion path, all for free.

   Spec: docs/specs/2026-08-31-beat-rail-marketing-pages.md
   ========================================================================== */

/* ---- the wrapper pays no rent -------------------------------------------
   A beat is a <section>, and `core.css` gives every section 120px of vertical
   padding (80 under 1023, 60 under 599). Wrapping the page's own sections would
   otherwise double the rhythm at every width, on the phone included. The beat
   contributes nothing to flow layout: the section inside it keeps its padding
   and the page is the page. Under `html.rail` the stage sets its own.
   The class outranks the bare `section` selector, so this needs no `!important`
   and no media query. */
.beat{padding:0}

/* ---- the stage ------------------------------------------------------------
   Everything below is under `html.rail`, which JS sets only at 1024 and above
   and only when reduced motion is not asked for. Without the class none of this
   exists and the page is the flow document it has always been — the no-JS
   fallback, the phone, the tablet and the reduced-motion path, all for free.

   The layers, bottom to top: #pub the canvas (fixed, z 2, already there), #stage
   the beats (fixed, z 10), #rail (fixed, z 30), the masthead (fixed, z 40).
   #spacer is the entire scroll length and holds nothing. */
html.rail #spacer{width:1px;pointer-events:none}
html.rail #stage{position:fixed;inset:0;z-index:10;pointer-events:none}

/* The masthead is `position:relative` in core.css — flow content. Under the
   stage the flow document is an empty spacer, so a static masthead scrolls off
   the top while the beats stay put, and every box the canvas engine measures
   against it goes with it. Measured before this rule existed: the hero's dot box
   was computed 276px above the viewport, and the field still drew, plausibly, in
   roughly the right column. Glasshouse's nav was fixed from the start, which is
   why porting from it would not have surfaced this. */
html.rail header.nav{position:fixed;inset:0 0 auto 0;z-index:40}

/* The beat is the whole viewport; the FRAME is its padding box. Top padding
   clears the masthead's band, bottom clears the rail's — including the 16px the
   `.rs` hit area hangs above the row — and a held beat centres its content in
   that box exactly as before. What changed on 2026-09-02: a run beat's content
   used to be severed at the frame edge, mid-glyph, because the beat's box WAS
   the frame and `overflow:clip` cut there, into bands that have no background
   to hide the cut. Now the content stays visible into the bands and the mask
   fades it out across them, so a headline leaving the top edge dims under the
   masthead instead of losing its ascenders. The fade is complete by the nav
   row (64px) and by the rail's labels (60px from the bottom), so nothing sits
   half-visible behind a link. The horizontal gutter stays with `.wrap` inside,
   so the page's own measure is untouched.

   And what changed on 2026-09-20: the frame is the mask's solid window, not a
   second guess at it. The two were independent numbers — padding 96/100 against
   bands 128/124 — and they disagreed by 32px at the top and 24px at the bottom,
   so content centred or aligned to the frame sat inside the fade AT REST.
   Measured at 1512x909 before the fix: `/upskilling/`'s "WHY EXECUTIVES FIRST"
   eyebrow rendered at 0.86 alpha, and the head of every run beat landed 32px
   into the top fade. On a dark beat that fades into a dark body and nobody sees
   it; on a light one it fades into that same dark body, which is why it was
   reported on the light surfaces and only there. The fade still does its job for
   content in TRANSIT, which is what it was added for. It should never have been
   deciding where content comes to rest.

   The four band edges are named once below. Derive the padding and the mask from
   the same four and they cannot drift apart again. `rail.js` reads them off
   `html.rail` rather than restating them, and `rail-probe`'s `stops` check
   asserts every station lands its block on solid ground. */
html.rail{
  --band-top-clear:64px;   /* mask fully gone by here: the masthead's own row */
  --band-top-solid:128px;  /* and fully back by here: the frame's top */
  --band-bot-solid:124px;  /* the frame's bottom, measured from the bottom edge */
  --band-bot-clear:60px;   /* mask fully gone by here: the rail's labels */
}
html.rail .beat{
  position:absolute;inset:0;
  padding:var(--band-top-solid) 0 var(--band-bot-solid);
  display:flex;align-items:center;
  opacity:0;
  /* `clip`, not `hidden`: hidden makes a scroll container, and a browser
     scrolling a focused child into view inside one would displace the frame
     under the run transform. `clip` cannot be scrolled at all. */
  overflow:clip;
  -webkit-mask-image:linear-gradient(to bottom,transparent var(--band-top-clear),#000 var(--band-top-solid),#000 calc(100% - var(--band-bot-solid)),transparent calc(100% - var(--band-bot-clear)));
  mask-image:linear-gradient(to bottom,transparent var(--band-top-clear),#000 var(--band-top-solid),#000 calc(100% - var(--band-bot-solid)),transparent calc(100% - var(--band-bot-clear)));
}
/* The canvas draws the dot formations under the beats and is not a child of any
   of them, so the frame's mask did not reach it: on a run beat a formation
   measured against a block that had travelled under the masthead drew its dots
   between the nav labels. Same mask, same bands. */
html.rail #pub{
  -webkit-mask-image:linear-gradient(to bottom,transparent var(--band-top-clear),#000 var(--band-top-solid),#000 calc(100% - var(--band-bot-solid)),transparent calc(100% - var(--band-bot-clear)));
  mask-image:linear-gradient(to bottom,transparent var(--band-top-clear),#000 var(--band-top-solid),#000 calc(100% - var(--band-bot-solid)),transparent calc(100% - var(--band-bot-clear)));
}
/* Compositor layers only for the beats that can paint. `will-change` on all six
   beats and all six inners reserved twelve viewport-sized layers whether or not
   a beat was `.off`; measured at 1512x861 the railed homepage carried 79 layers
   against 12 as a flow document. A beat leaves `.off` half a unit before its
   crossfade (rail.js, PREWARM), which is when its layer is created and
   rasterised — off the crossfade's own frames. */
html.rail .beat:not(.off){will-change:opacity}
html.rail .beat:not(.off) > .inner{will-change:transform}
/* A run beat's content is taller than the frame by construction, so it starts at
   the top and is translated up across its span. Centring it would push its head
   and its foot out of the frame at once.
   `beat-run`, not `run`: `.run` is the homepage's hero instrument figure, and
   home.css styles it in nine places. Named `run` this rule inherited all nine and
   pushed every run beat's box 128px down the screen — the beats still drew, and
   only their frame was wrong. */
html.rail .beat.beat-run{align-items:flex-start}
/* One property that removes a beat from paint, from hit-testing, from the
   accessibility tree and from the tab order. Not `display:none`, which would
   collapse the boxes the canvas engine measures, and not `aria-hidden`, which
   would only repeat what this already does. */
html.rail .beat.off{visibility:hidden}
html.rail .beat > .inner{width:100%;pointer-events:auto}
/* An entrance that has finished stays a compositor layer for as long as its
   keyframes fill: the `both` fill-mode the motion rules require keeps the
   animation "in effect" after its last frame, and Chrome keeps an element with
   an in-effect transform animation composited. Thirteen list items on one beat
   were layers at rest for that reason. rail.js marks each revealed block `.done`
   once its ladder has played out; the base style IS the end state of every
   keyframe this selector reaches (`rise`, `typeset`, `bubble`), so dropping the
   name changes nothing the reader sees and releases the layer. `rule` and
   `lift` run on `::before` pseudo-elements, which the selector does not reach
   and which end invisible either way. */
html.rail .sg.done,html.rail .sg.done *,html.rail .rv.done{animation-name:none}
/* the sections inside give up their own vertical rhythm; the beat supplies it */
html.rail .beat > .inner > section{padding:0}

/* The footer is reparented into the LAST BEAT'S `.inner` at init — see rail.js —
   in flow, so its height counts towards that beat's overflow and the run's own
   travel brings it up. It arrives as the travel ends, which is where the finale
   fades it in. */
html.rail .beat .inner > footer{opacity:0;transition:opacity 300ms var(--ease)}
html.rail .beat.foot-on .inner > footer{opacity:1}

/* ---- the rail: a centred row of station labels above the bottom edge ---- */
/* Below 1024 there is no rail at all. The marketing pages already have a full
   mobile navigation in the masthead sheet, and a second fixed bottom bar would
   sit on top of the page's existing bottom-anchored chrome. Glasshouse shows a
   band of tappable segments on phones; that is a separate decision with its own
   mobile round, and it is deliberately not smuggled in here. */
#rail{display:none}

@media (min-width:1024px){
  #rail{
    position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:30;
    display:flex;align-items:baseline;gap:26px;
    opacity:1;white-space:nowrap;
    transition:opacity 300ms var(--ease),bottom 300ms var(--ease);
  }
  /* before the first station's beat: present, dim, nothing lit */
  #rail.pre .rs{opacity:.6}
  /* the last station lit: the rail lifts clear of the closing block */
  #rail.end{bottom:48px}
  /* the finale: the guide has done its work. Hidden to focus as well as to the
     eye — the 0s delay on visibility is what lets the fade finish before the
     hide, and it is the reason this is not `display:none`. */
  #rail.rest{
    opacity:0;visibility:hidden;
    transition:opacity 300ms var(--ease),visibility 0s linear 300ms,bottom 300ms var(--ease);
  }

  .rs{
    display:inline-block;
    /* 44px of hit area under 11px of type. The site's floor is 44 and it holds
       at every width; phone-audit only measures 390, so nothing else would have
       caught this one. `align-items:baseline` on the row keeps the labels on a
       line despite the padding. */
    padding:16px 0;margin:-16px 0;
    font-family:var(--mono);font-size:11px;line-height:1.2;
    letter-spacing:-0.01em;text-transform:uppercase;
    color:var(--mute-d);opacity:.5;
    transition:color 200ms var(--ease),opacity 200ms var(--ease);
  }
  .rs:hover{opacity:.9;color:var(--paper)}
  .rs:focus-visible{opacity:1;color:var(--paper)}
  .rs.done{opacity:.85}
  .rs.act{opacity:1;color:var(--paper)}

  /* There is no light-ground variant of the rail. The beats' mask fades every
     beat, inverted ones included, out across the band the rail sits in, so the
     ground under it is the page's own; an `on-light` swap existed before the
     mask and painted the active station ink on ink once it landed. */

  /* the end cap: a drawn bar, lit when the reader reaches the last station */
  .rs-end{
    display:inline-block;width:2px;height:11px;margin-left:-14px;align-self:center;
    border-radius:1px;background:var(--mute-d);opacity:.35;
    transition:opacity 200ms var(--ease),background-color 200ms var(--ease);
  }
  #rail.end .rs-end{opacity:1;background:var(--signal)}

  /* the phone and tablet label. It never shows — the rail does not render below
     1024 — but applyRail writes to it, so it is declared rather than left to a
     missing-node branch in the script. */
  #raillab{display:none}
}

/* the row tightens before it wraps. Station counts here run to seven, well under
   the thirteen this tightening was measured to hold on one line. */
@media (min-width:1024px) and (max-width:1240px){#rail{gap:18px}}

/* Wayfinding is not motion. Under reduced motion the rail still renders and still
   lights, and every transition goes — the stateful selectors named one by one,
   because a bare `#rail{transition:none}` is outranked by the state rules that
   follow it. */
@media (prefers-reduced-motion:reduce){
  #rail,#rail.pre,#rail.end,#rail.rest,.rs,.rs:hover,.rs.act,.rs.done,.rs-end,#rail.end .rs-end{transition:none}
}
