/* ==========================================================================
   24K Studios, components v3. Flat pastel palette; hairlines are the only metal.
   ========================================================================== */

/* ---------- Kadence resets + breakout ---------- */
.site.wp-site-blocks{overflow-x:visible!important}
.entry-content-wrap,.entry-content.single-content{padding:0!important;max-width:none!important}
.entry.content-bg,.content-bg,.content-wrap,.site-container,.content-area,.site-main{background:transparent!important;box-shadow:none!important;border:0!important}
.site-content{padding-block:0!important}
.entry-content-wrap,.entry-content.single-content,.content-container.site-container{max-width:none!important;width:100%!important}
.k24-container{max-width:var(--k24-container);margin-inline:auto;padding-inline:var(--k24-s3)}
.k24-prose{max-width:var(--k24-prose)}
.k24-section,.k24-band,.k24-hero{margin-left:calc(50% - 50vw)!important;margin-right:calc(50% - 50vw)!important;max-width:100vw!important;width:auto!important}
/* The container moves from the children onto the section's own padding. The old model gave
 * every child `margin-inline:auto`, which on the hand-authored pages centred paragraphs
 * inside a 1400px box (services' prose rendered at x=444 with a 420px left margin) and on
 * the two block-editor pages fought WordPress's own is-layout-constrained !important. */
.k24-section{padding-block:var(--k24-section);
  padding-inline:max(var(--k24-s3),calc((100vw - var(--scrollbar-offset,0px) - var(--k24-container)) / 2))}
/* :not(.alignfull) on both, and it is load-bearing.
 *
 * These two rules pin every child of a section to the section's own content box, which is right
 * for text and cards and wrong for the one thing that is supposed to escape it. The video bands
 * on /gear/ sit inside a core group block and carry .alignfull, and they came out 24px in on the
 * left and 24px past the window on the right: computed margin-left was 0px, because the
 * !important below beat the band's own calc(50% - 50vw).
 *
 * Core's constrained-layout stylesheet already exempts .alignfull from its equivalent rule, for
 * exactly this reason. Ours now does the same, so a full-bleed child is full-bleed whether it is
 * inside a hand-authored section or a block-editor group. */
.k24-section>*:not(.alignfull){max-width:var(--k24-container);margin-inline:0}
.k24-section>.k24-prose{max-width:var(--k24-prose)}
.k24-section.is-layout-constrained>*:not(.alignfull){margin-left:0!important;margin-right:0!important}
.k24-section p,.k24-section li,.k24-band p,.k24-band li{max-width:var(--k24-measure)}
/* THE SECTION GROUNDS. Three light steps and one dark one.
 *
 * --tint and --white were both deleted in 8679b50 and the classes stayed in the page content, so
 * for weeks every page carried an authored alternation that painted nothing: /services/ has four
 * sections marked plain / tint / plain / tint and measured 97% light ground with 4726px, 5.25
 * screens, of unbroken paper.
 *
 * They are restored as TWO DIFFERENT VALUES, not one. On /corporate/ the --tint and --white
 * sections are adjacent (2331 + 443 = 2774): give them the same value and they merge into a
 * single 1599px slab with no seam, which is worse than leaving them unpainted. */
/* THE GROUNDS ARE RAMPS NOW, AND THE SHORTHAND HAD TO GO FIRST.
 *
 * `background:var(--k24-tint)` is the shorthand, and the shorthand resets background-image to
 * none. Every texture overlay below would have been silently wiped on any section carrying a
 * modifier, which is the majority of them. Long-hand throughout.
 *
 * Each ramp stays inside one value family (see tokens.css), so what a reader perceives is a
 * surface with depth rather than a gradient with a direction. */
.k24-section,.k24-hero{background-color:var(--k24-paper);background-image:var(--k24-ground-paper);
  background-repeat:no-repeat;background-size:100% 100%}
.k24-section--tint{background-color:var(--k24-tint);background-image:var(--k24-ground-tint)}
.k24-section--white{background-color:var(--k24-white);background-image:var(--k24-ground-white)}
.k24-section--ink,.k24-band--ink{background-color:var(--k24-ink);
  background-image:var(--k24-ground-ink);color:var(--k24-on-dark)}
/* An ink section that FOLLOWS an ink section, or that is the last one before the ink footer,
 * ramps the other way so the two never merge into one slab. On /music/ the CTA band and the
 * footer are both #1C1C21 today with nothing between them but a 12% white hairline. */
.k24-section--ink+.k24-section--ink,
.k24-section--ink:last-of-type{background-image:var(--k24-ground-ink-up)}
.k24-section--ink h1,.k24-section--ink h2,.k24-section--ink h3,.k24-band--ink h1,.k24-band--ink h2,.k24-band--ink h3{color:#fff}
.k24-section--ink p,.k24-section--ink li,.k24-band--ink p,.k24-band--ink li{color:var(--k24-on-dark-muted)}
.k24-section--ink a:not(.k24-btn),.k24-band--ink a:not(.k24-btn){color:#fff}
.k24-band{padding:var(--k24-section) var(--k24-s3)}
.k24-band>.k24-container{max-width:var(--k24-container);margin-inline:auto;padding-inline:0}
.k24-section .k24-band,.k24-band .k24-section{padding-block:0!important}

/* ---------- section typography ---------- */
.k24-band__eyebrow,.k24-hero__kicker,.k24-striplabel,.k24-cta-band__services,.k24-eyebrow{
  font-family:var(--k24-font-body);font-weight:600;letter-spacing:.12em;text-transform:uppercase;font-size:var(--k24-label);line-height:1}
.k24-band__eyebrow,.k24-eyebrow{color:var(--k24-muted);margin:0 0 var(--k24-s2)}
.k24-band--ink .k24-band__eyebrow,.k24-section--ink .k24-band__eyebrow,.k24-section--ink .k24-eyebrow{color:var(--k24-on-dark-muted)}
/* Descendant selector on purpose. `.k24-section>h2` reaches only direct children and would
 * skip music's .k24-split>div>h2, contact's "Send a message", portfolio's "Where it was
 * made" and every one of terms' eight headings: half the site ruled, half not, and the page
 * that gets none is the page with no cards and no bands left. 24ch because 20ch holds only
 * ~19 uppercase glyphs at this size. */
.k24-band h2,.k24-section h2{margin:0 0 var(--k24-s4);max-width:30ch;letter-spacing:0}
.k24-band h2::after,.k24-section h2::after{content:"";display:block;width:132px;max-width:100%;
  height:3px;margin:var(--k24-s2) 0 0;background-image:var(--k24-rule)}
/* h3 rank, scoped by CHILD combinator: unscoped `.k24-section h3` is an exact specificity
 * tie with .k24-card/.k24-room/.k24-step/.k24-icon-card h3 and would drop every card title
 * 64px below its own icon. */
.k24-section>h3,.k24-section>.k24-prose>h3{margin-top:var(--k24-s6)}
/* B7 newly applies a 24ch cap to split-column headings that previously had none. */
.k24-split h2{max-width:none}
/* WooCommerce emits its own h2s (product titles, Reviews, Related products). They render
 * unhidden on /shop/ and in the cart, where an uppercased product title under a metal bar
 * is plainly wrong. MUST stay after the rule above: same specificity, source order decides. */
.k24-book-embed h2,.k24-section .woocommerce h2{letter-spacing:0;max-width:none}
.k24-book-embed h2::after,.k24-section .woocommerce h2::after{content:none}
.k24-band__lede,.k24-lede{font-size:var(--k24-body);color:var(--k24-text-muted);max-width:56ch;margin:0 0 var(--k24-s4)}
.k24-section--ink .k24-lede{color:var(--k24-on-dark-muted)}
.k24-striplabel{color:var(--k24-muted);text-align:center;margin:0 auto var(--k24-s2)}

/* ---------- page header (interior pages) ---------- */
/* The fill goes (plan item 3) and so does the border-bottom, which was a 100vw line at
 * 1.21:1 using a token reserved for decoration, sitting 200px below the seam it was
 * imagined to cover. The real seam is the masthead ::after further down. Must stay ABOVE
 * .k24-hero--full, which re-declares padding:0;border:0 at the same specificity. */
.k24-hero{padding:clamp(56px,5vw,88px) var(--k24-s3) var(--k24-s6)}
/* Once every h2 below is uppercase and ruled, the largest heading would be the least
 * marked one. Same material, wider bar: width carries rank. */
.k24-hero:not(.k24-hero--full) h1::after{content:"";display:block;width:180px;max-width:100%;
  height:3px;margin:var(--k24-s2) 0 0;background-image:var(--k24-rule)}
.k24-hero__inner{max-width:var(--k24-container);margin-inline:auto}
.k24-hero__inner>*{margin-top:0;margin-bottom:0}
.k24-hero__inner>*+*{margin-top:var(--k24-s3)}
.k24-hero__kicker{color:var(--k24-primary-text)}
.k24-hero__kicker+h1{margin-top:var(--k24-s2)}
.k24-hero h1{max-width:18ch}
.k24-hero p:not(.k24-hero__kicker){max-width:56ch;color:var(--k24-text-muted);font-size:var(--k24-body)}
.k24-hero .wp-block-buttons,.k24-hero__cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:var(--k24-s4)}

/* ---------- image hero: full-bleed, copy bottom-left, minimal scrim (D30, D33) ---------- */
.k24-hero--full{position:relative;isolation:isolate;display:flex;align-items:flex-end;background:var(--k24-ink);
  min-height:min(72vh,680px);padding:0;border:0;border-radius:0}
.k24-hero--screen{min-height:100svh}
.k24-hero--full .k24-hero__figure{position:absolute;inset:0;z-index:0;margin:0}
/* No filter. A brightness lift was applied here earlier as a stopgap; it clipped a quarter of
 * the frame to a blown channel and tripled the sub-3:1 area behind the corporate headline.
 * The correct direction is to pull a bright source down, not push a dark one up, which is
 * what the imagery gate in ops/hero-candidates.py enforces on every candidate. */
.k24-hero--full .k24-hero__figure img{width:100%;height:100%;object-fit:cover;display:block}
/* Monotonic: darkens continuously from top to bottom. The previous ramp held alpha at ZERO
 * from 28% to 52% of the hero height, which is precisely where the copy block sits, so the
 * text was protected everywhere except where it actually is. Modelled on .k24-room::after,
 * which already measures a 4.33:1 floor on these same photographs. */
.k24-hero--full::after{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(28,28,33,.20) 0%,rgba(28,28,33,.48) 48%,rgba(28,28,33,.88) 100%)}
.k24-hero--full .k24-hero__inner{position:relative;z-index:2;width:100%;max-width:var(--k24-container);
  padding:var(--k24-s7) var(--k24-s3) clamp(48px,7vh,88px);display:flex;flex-direction:column;justify-content:flex-end}
.k24-hero--full .k24-hero__inner>*{max-width:640px}
.k24-hero--full h1,.k24-hero--full .k24-hero__inner p{color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.3)}
.k24-hero--full .k24-hero__inner p:not(.k24-hero__kicker){color:var(--k24-on-dark);font-size:var(--k24-body);max-width:52ch}
/* The hero kicker is removed from the image heroes entirely (the worst text on the site by
 * measurement). The h1 is the hero's only tier now, and it has the 180px mark on the
 * text heroes to carry rank. */
.k24-hero--full .k24-hero__kicker{display:none}
.k24-hero--full .k24-hero__cta{margin-top:var(--k24-s4)}
@media (max-width:860px){
  .k24-hero--full .k24-hero__inner{padding:var(--k24-s6) var(--k24-s3) var(--k24-s5)}
  .k24-hero--full::after{background:linear-gradient(180deg,rgba(28,28,33,.30) 0%,rgba(28,28,33,.58) 45%,rgba(28,28,33,.92) 100%)}
}

/* ---------- buttons: flat, pill, one weight ---------- */
/* BUTTONS ARE NOT PILLS.
 * They were capsules with a 600-weight sentence-case label that lifted 1px and dropped a
 * shadow on hover. Every one of those is a default: the capsule is the shape a UI kit gives
 * you, and the hover lift is the motion a UI kit gives you. In a layout built from full-bleed
 * bands, hard edges and hairline rules they were the one component that had not been designed,
 * which is exactly how they read.
 *
 * Now: a near-square edge, an uppercase label with real tracking (the same treatment the
 * eyebrows and the marquee credit already use, so the button belongs to the same family), and
 * no transform on hover. The state change is carried by fill and border, which is what the
 * rest of the site does. */
.k24-btn,.wp-block-button__link,.button,button:not(.k24-pill):not(.menu-toggle-open):not(.menu-toggle-close):not(.k24-slot),input[type=submit]{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--k24-s1);min-height:52px;padding:15px 32px;
  font-family:var(--k24-font-body);font-weight:500;font-size:var(--k24-label);border-radius:var(--k24-radius-btn);
  text-transform:uppercase;letter-spacing:.14em;
  border:1px solid transparent;cursor:pointer;text-decoration:none;line-height:1.2;
  transition:background-color var(--k24-dur-micro) var(--k24-ease-out),color var(--k24-dur-micro) var(--k24-ease-out),border-color var(--k24-dur-micro) var(--k24-ease-out)}
.k24-btn--primary,.wp-block-button__link,.button.alt,input[type=submit],.wc-block-components-button{
  background:var(--k24-primary)!important;color:#fff!important;border-color:transparent}
.k24-btn--primary:hover,.wp-block-button__link:hover,.button.alt:hover,input[type=submit]:hover,.wc-block-components-button:hover{
  background:var(--k24-primary-hover)!important;color:#fff!important}
.k24-btn--ghost,.is-style-outline .wp-block-button__link,.button:not(.alt){
  background:transparent!important;color:var(--k24-text)!important;border:1px solid var(--k24-silver)!important}
.k24-btn--ghost:hover,.is-style-outline .wp-block-button__link:hover,.button:not(.alt):hover{border-color:var(--k24-primary)!important;color:var(--k24-primary-text)!important}
/* The primary button on an ink band is #1C1C21 on #1C1C21 with a transparent border:
 * 1.00:1, reading as bare white text beside the ghost button's visible pill. Only the
 * ghost variant was ever covered here. Same 55% white this file already uses. */
/* On an ink band the primary button was #1C1C21 on #1C1C21 held together by a 55% white
 * outline: legible, but visually the WEAKER of the two buttons, which is backwards. Inverted
 * to a solid white block it is unambiguously the primary action, and ink on white is the same
 * 15.86:1 the light-ground version has. */
.k24-hero--full .k24-btn--primary,
.k24-section--ink .k24-btn--primary,.k24-band--ink .k24-btn--primary,
.k24-section--ink .wp-block-button__link,.k24-band--ink .wp-block-button__link{
  background:var(--k24-white)!important;color:var(--k24-ink)!important;border-color:var(--k24-white)!important}
.k24-hero--full .k24-btn--primary:hover,
.k24-section--ink .k24-btn--primary:hover,.k24-band--ink .k24-btn--primary:hover,
.k24-section--ink .wp-block-button__link:hover,.k24-band--ink .wp-block-button__link:hover{
  background:var(--k24-on-dark-muted)!important;color:var(--k24-ink)!important;border-color:var(--k24-on-dark-muted)!important}
.k24-hero--full .k24-btn--ghost,.k24-section--ink .k24-btn--ghost,.k24-band--ink .k24-btn--ghost{color:#fff!important;border-color:rgba(255,255,255,.55)!important}
.k24-hero--full .k24-btn--ghost:hover,.k24-section--ink .k24-btn--ghost:hover{border-color:#fff!important;background:rgba(255,255,255,.08)!important}
.k24-btn--sm{min-height:40px;padding:9px 18px;font-size:var(--k24-label)}

/* ---------- cards (E40: 8px) ---------- */
/* The fill and the 32px inset go with the border. --k24-surface-1 on paper is 1.03:1: 20
 * rectangles that exist in the DOM and not on screen, indenting their text 32px away from
 * the h2 and mark that both sit at the section edge. 88px at 2px sits under the h2's 132px
 * at 3px; a full-bleed mark would be 3.4x the ink of the heading it is subordinate to. */
.k24-card,.wp-block-column.k24-card{position:relative;background:none;border:0;border-radius:0;
  box-shadow:none;padding:var(--k24-s3) 0 0}
.k24-card::before{content:"";position:absolute;inset:0 auto auto 0;width:88px;height:2px;
  background-image:var(--k24-rule)}
.k24-card h3{margin-top:0}
.k24-card--soft{background:none}
.k24-card p:last-child{margin-bottom:0}
.k24-card .k24-btn{margin-top:var(--k24-s2)}
.wp-block-columns{gap:var(--k24-s5)}
.k24-section .wp-block-columns{margin-bottom:var(--k24-s5)}
@media (max-width:781px){.wp-block-columns{gap:var(--k24-s4)}}
.k24-card ul{margin:0;padding-left:1.2em}
/* Kept, and promoted from the 1.21:1 decorative hairline to the 3.38:1 control border. The
 * consistent rule for this commit: text loses its box, an image keeps its frame at a
 * contrast that actually draws, unless the image supplies its own edge (.k24-room's scrim). */
.k24-figure{margin:0;border-radius:var(--k24-radius);overflow:hidden;border:1px solid var(--k24-border)}
.k24-figure img{display:block;width:100%;height:auto}
.k24-grid-3{display:grid;gap:var(--k24-s5);grid-template-columns:1fr}
@media (min-width:760px){.k24-grid-3{grid-template-columns:repeat(3,1fr)}}
.k24-grid-2{display:grid;gap:var(--k24-s5);grid-template-columns:1fr}
@media (min-width:760px){.k24-grid-2{grid-template-columns:1fr 1fr}}

/* ---------- split: copy + figure ---------- */
.k24-split{display:grid;gap:var(--k24-s5);grid-template-columns:1fr;align-items:center}
@media (min-width:900px){.k24-split{grid-template-columns:1.05fr .95fr}}
.k24-split .k24-figure{aspect-ratio:4/3}.k24-split .k24-figure img{height:100%;object-fit:cover}

/* ---------- rooms ---------- */
.k24-rooms{display:grid;gap:var(--k24-s3);grid-template-columns:1fr}
@media (min-width:820px){.k24-rooms{grid-template-columns:1fr 1fr}}
.k24-room{position:relative;overflow:hidden;border-radius:var(--k24-radius);min-height:340px;display:flex;flex-direction:column;
  justify-content:flex-end;padding:var(--k24-s4);text-decoration:none;color:#fff;isolation:isolate}
.k24-room img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;transition:transform .5s ease}
.k24-room::after{content:"";position:absolute;inset:0;z-index:1;background:linear-gradient(180deg,rgba(28,28,33,.05) 0%,rgba(28,28,33,.82) 100%)}
.k24-room>*{position:relative;z-index:2}
.k24-room h3{font-size:var(--k24-h3);margin:0 0 6px;color:#fff}
.k24-room p{color:var(--k24-on-dark);font-size:var(--k24-body);margin:0}
.k24-room:hover img{transform:scale(1.03)}

/* ---------- steps (a real sequence) ---------- */
.k24-steps{display:grid;gap:var(--k24-s5);grid-template-columns:1fr;counter-reset:step}
@media (min-width:760px){.k24-steps{grid-template-columns:repeat(3,1fr)}}
.k24-step{background:none;border:0;border-radius:0;box-shadow:none;padding:0}
/* The numeral disc was --k24-lavender at 1.06:1 on paper; it read only because it sat on a
 * white card inside a tint band, and this commit deletes both. */
.k24-step::before{counter-increment:step;content:counter(step);display:grid;place-items:center;
  width:36px;height:36px;border-radius:50%;background:var(--k24-ink);color:var(--k24-white);
  font-family:var(--k24-font-display);font-weight:700;margin-bottom:var(--k24-s3)}
.k24-step h3{font-size:var(--k24-h3);margin:0 0 6px}
.k24-step p{margin:0;color:var(--k24-text-muted);font-size:var(--k24-body)}

/* ---------- icon cards (G57) ---------- */
.k24-icons{display:grid;gap:var(--k24-s5) var(--k24-s4);grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.k24-icon-card{position:relative;background:none;border:0;border-radius:0;box-shadow:none;
  padding:var(--k24-s3) 0 0}
.k24-icon-card::before{content:"";position:absolute;inset:0 auto auto 0;width:56px;height:2px;
  background-image:var(--k24-rule)}
.k24-icon-card svg{width:28px;height:28px;stroke:var(--k24-primary);fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;display:block;margin-bottom:14px}
.k24-icon-card h3{font-size:var(--k24-h3);margin:0 0 6px}
.k24-icon-card p{margin:0;font-size:var(--k24-body);color:var(--k24-text-muted)}
.k24-icon-card--lead{grid-column:span 1}
.k24-icons--detail{grid-template-columns:1fr}
@media (min-width:760px){.k24-icons--detail{grid-template-columns:1fr 1fr}}
.k24-icons--detail .k24-icon-card{padding:var(--k24-s3) 0 0}
.k24-icons--detail .k24-icon-card p{font-size:var(--k24-body)}

/* ---------- quotes / releases (render only with data) ---------- */
.k24-quotes{display:grid;gap:var(--k24-s5);grid-template-columns:1fr}
@media (min-width:820px){.k24-quotes{grid-template-columns:repeat(3,1fr)}}
.k24-quote{position:relative;background:none;border:0;border-radius:0;box-shadow:none;
  padding:var(--k24-s3) 0 0}
.k24-quote::before{content:"";position:absolute;inset:0 auto auto 0;width:88px;height:2px;
  background-image:var(--k24-rule)}
.k24-quote blockquote{margin:0 0 var(--k24-s3);font-size:var(--k24-body);line-height:1.55}
.k24-quote cite{font-style:normal;display:block;font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-body)}
.k24-quote small{display:block;color:var(--k24-text-muted);font-size:var(--k24-label);margin-top:2px}
.k24-releases{display:grid;gap:var(--k24-s2);grid-template-columns:repeat(2,1fr)}
@media (min-width:760px){.k24-releases{grid-template-columns:repeat(4,1fr)}}
.k24-release{position:relative;display:block;border-radius:var(--k24-radius);overflow:hidden;aspect-ratio:1;background:var(--k24-lavender);isolation:isolate;text-decoration:none;color:#fff;border:1px solid var(--k24-hairline)}
.k24-release img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .5s ease}
.k24-release__meta{position:absolute;inset:auto 0 0 0;z-index:2;padding:16px;background:linear-gradient(180deg,rgba(28,28,33,0),rgba(28,28,33,.85));opacity:0;transition:opacity .25s}
.k24-release:hover img,.k24-release:focus-visible img{transform:scale(1.04)}
.k24-release:hover .k24-release__meta,.k24-release:focus-visible .k24-release__meta{opacity:1}
.k24-release__meta b{display:block;font-family:var(--k24-font-display);font-size:var(--k24-body)}
.k24-release__meta span{display:block;font-size:var(--k24-label);color:var(--k24-on-dark-muted)}
@media (hover:none){.k24-release__meta{opacity:1}}

/* ---------- cta band ---------- */
.k24-cta-band{text-align:center}
.k24-cta-band h2{margin-inline:auto;max-width:20ch}
.k24-cta-band.k24-cta-band>*{margin-inline:auto}
.k24-cta-band.k24-cta-band h2::after{margin-inline:auto}
.k24-cta-band__services{color:var(--k24-on-dark-muted);margin:0 0 var(--k24-s4)}
.k24-cta-band .k24-hero__cta{justify-content:center;margin-top:var(--k24-s3)}

/* ---------- marquee ---------- */
.k24-marquee{max-width:var(--k24-container);margin:0 auto;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.k24-marquee__track{display:flex;gap:var(--k24-s2);width:max-content;animation:k24-scroll 46s linear infinite}
.k24-marquee:hover .k24-marquee__track{animation-play-state:paused}
@keyframes k24-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.k24-marquee__item img{max-width:62%;max-height:44%;object-fit:contain}
.k24-marquee__item{position:relative;height:92px;width:164px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  background:none;border:0;border-radius:0}
.k24-marquee--lg .k24-marquee__item{height:108px;width:196px}

/* ---------- forms ---------- */
input[type=text],input[type=email],input[type=tel],input[type=number],input[type=date],input[type=password],input[type=search],input[type=url],select,textarea{
  background:var(--k24-surface-1);color:var(--k24-text);border:1px solid var(--k24-silver);border-radius:var(--k24-radius);
  padding:12px 14px;min-height:48px;font-family:var(--k24-font-body);font-size:var(--k24-body);width:100%}
::placeholder{color:var(--k24-text-muted);opacity:1}
label{display:block;margin-bottom:6px;font-weight:600;font-size:var(--k24-body)}
.k24-form{display:grid;gap:var(--k24-s2) var(--k24-s3);grid-template-columns:1fr}
@media (min-width:640px){.k24-form{grid-template-columns:1fr 1fr}}
.k24-field.is-full{grid-column:1/-1}
.k24-field .k24-opt{font-weight:400;color:var(--k24-text-muted);font-size:var(--k24-label)}
.k24-field.has-error input,.k24-field.has-error select,.k24-field.has-error textarea{border-color:var(--k24-danger)}
.k24-error{margin:6px 0 0;color:var(--k24-danger);font-size:var(--k24-body)}
.k24-hint{margin:6px 0 0;color:var(--k24-text-muted);font-size:var(--k24-label)}
.k24-check{display:flex;gap:10px;align-items:flex-start;font-weight:400;padding-top:30px}
.k24-check input{width:20px;height:20px;margin-top:2px;accent-color:var(--k24-primary)}
.k24-form__hp{position:absolute!important;left:-9999px!important;width:1px;height:1px;overflow:hidden}
.k24-notice{padding:var(--k24-s2) var(--k24-s3);border-radius:var(--k24-radius);margin:0 0 var(--k24-s3);font-weight:400}
.k24-notice--ok{background:var(--k24-ok-bg);color:var(--k24-ok)}
.k24-notice--bad{background:var(--k24-danger-bg);color:var(--k24-danger)}
.k24-form-wrap{max-width:820px}
/* Same treatment as .k24-figure: framed media keeps its frame, at a contrast that draws.
 * The plan left this alone on the grounds that live colour supplies its own edge, but that
 * argument would leave a 1.21:1 line that is present and unseeable, which is the defect the
 * commit removes everywhere else. --k24-surface-2 stays: it is the placeholder behind the iframe. */
.k24-map{margin:0;border-radius:var(--k24-radius);overflow:hidden;border:1px solid var(--k24-border);aspect-ratio:16/10;background:var(--k24-surface-2)}
.k24-map iframe{width:100%;height:100%;border:0;display:block}

/* ---------- header: not sticky (C26); transparent over the home heroes (C27); smaller logo (C25) ---------- */
.site-header,#masthead,.site-header-wrap,.site-header-inner-wrap,.site-top-header-wrap,.site-main-header-wrap,.site-bottom-header-wrap,
.site-header-row,.site-header-row-container-inner{background:var(--k24-base)!important;border:0!important;box-shadow:none!important}
#masthead{position:relative;z-index:60}
/* Lines above paint the masthead the same #F7F7F9 as the page with border:0!important, so
 * once the hero fill goes there is no edge at all between the nav and the h1 on the seven
 * pages without a photo hero. Solid --k24-metal-dark, NOT the rule gradient: a gradient
 * designed for a <=180px bar stretched to 1440px puts its 1.84:1 stop mid-viewport. */
/* The header is ink now, so on a page that opens with an ink hero this seam would be a metal
 * line between two charcoals. It still earns its place on /gear/, where the header meets a tint
 * section directly, so it stays: 3.36:1 on ink, and the step below it does the rest. */
body:not(.k24-has-hero) #masthead::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:1px;background:var(--k24-metal-dark)}
.site-header .site-header-row-container-inner>.site-container{max-width:var(--k24-container)}
.main-navigation a{font-family:var(--k24-font-body);font-weight:600;letter-spacing:.12em;font-size:var(--k24-label);line-height:1;text-transform:uppercase;color:var(--k24-text-muted)}
.main-navigation a:hover,.main-navigation .current-menu-item>a,.main-navigation .current-menu-ancestor>a{color:var(--k24-text)}
.main-navigation .primary-menu-container>ul{gap:4px;align-items:center}
/* Excludes the pill items. This rule exists to beat Kadence's nav padding, but at
 * (0,2,3) it also outranked `.k24-book` at (0,1,0), so the button that takes the
 * deposit rendered with 11px of side padding against the 22px it asks for. */
.main-navigation .primary-menu-container>ul>li:not(.k24-book-item):not(.k24-pill-item)>a{padding-inline:14px!important}
/* The dropdown panel is deliberately NOT forced. Kadence paints it from its own
 * darkest palette slot, which tokens.css now declares as the ink value, so the panel
 * is charcoal by agreement instead of by override, and the ink below is light to match.
 * This replaced a forced-white panel plus two colour overrides: three !important
 * declarations removed, and the rule that produced the 1.00:1 failure on nine pages
 * (D27) is gone rather than patched. Continuation lines start with * so the
 * pre-commit colour check can tell comment prose from code. */
.main-navigation ul ul.sub-menu{border:1px solid rgba(255,255,255,.10);border-radius:var(--k24-radius);box-shadow:0 18px 40px -22px rgba(0,0,0,.55);padding:6px}
/* The link colour must be forced at the SAME scope as the panel background above, i.e.
   unscoped. It was previously only forced under `body.k24-has-hero`, a class carried by
   just /music/ and /corporate/. The panel was forced white on all eleven pages, so on the
   other nine Kadence's higher-specificity rule kept painting the links #FFFFFF on that white
   panel: 1.00:1, invisible. Verified on one page and generalised, do not repeat that. */
.main-navigation ul ul.sub-menu a{text-transform:none;letter-spacing:0;font-size:var(--k24-body);font-weight:400;color:var(--k24-on-dark);border-radius:6px}
.main-navigation ul ul.sub-menu a:hover{background:rgba(255,255,255,.10);color:#fff}
.custom-logo{max-width:104px!important;width:auto;height:auto;display:block}
.site-branding .site-title,.site-branding .site-description{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}
.k24-topbar,.k24-switch-item{display:none!important}
/* home pages: header floats over the full-screen hero */
body.k24-has-hero #masthead{position:absolute;top:0;left:0;right:0}
body.k24-has-hero .site-header,body.k24-has-hero #masthead,body.k24-has-hero .site-header-wrap,body.k24-has-hero .site-header-inner-wrap,
body.k24-has-hero .site-main-header-wrap,body.k24-has-hero .site-header-row,body.k24-has-hero .site-header-row-container-inner{background:transparent!important}
/* Kadence emits an INLINE rule at specificity (0,3,3):
     .main-navigation .primary-menu-container > ul > li.menu-item > a { color: var(--global-palette5) }
   which outranks a plain `body.k24-has-hero .main-navigation a` at (0,2,2). The result was
   nav links at 2.26:1 on the charcoal hero and the current item at 1.04:1, invisible.
   Because the rule is inline and generated, it cannot be found by grepping the theme.
   !important is the idiom this file already uses against Kadence (see the background reset
   three lines up); the sub-menu override below must therefore be !important too, or the
   dropdown panel goes light-on-light. */
/* UNSCOPED from body.k24-has-hero, because the header now paints its own ink ground on every
 * page rather than only floating over the two photo heroes. These values were always the right
 * ones for a dark header; they were simply only reachable on two of eleven pages, which is what
 * produced the ghost-button inconsistency between the sides. `body ` is kept in front of each so
 * the specificity stays at (0,1,x) or better and the !important flags still beat Kadence.
 * The LAYOUT rules that genuinely depend on the header floating are deliberately NOT unscoped:
 * #masthead{position:absolute}, the .content-area margins and the seam under a static header. */
body .main-navigation a:not(.k24-book){color:var(--k24-on-dark)!important}
body .main-navigation a:not(.k24-book):hover,
body .main-navigation a:not(.k24-book):focus-visible,
body .main-navigation .current-menu-item>a:not(.k24-book),
body .main-navigation .current-menu-ancestor>a:not(.k24-book){color:#fff!important}
/* The Book Now pill keeps a pure white label. With the accent retired (D28) its fill is
 * --k24-primary, now charcoal, and white on it measures 16.97:1. It is excluded from the
 * over-hero nav colour above rather than restated here, so the pill has one source of truth. */
/* the dropdown has its own light panel, its links stay dark */
/* #masthead, not `body`, and that is the whole fix. Kadence paints this with
 * `.mobile-toggle-open-container .menu-toggle-open{color:var(--global-palette5)}` at (0,2,0),
 * which beat `body .menu-toggle-open` at (0,1,1). Measured, the burger was rendering
 * #5B5B62 on the ink header: 2.52:1, under the 3:1 floor for a control, and it had been
 * losing the same way over the photo heroes at 3.12:1 before this header change existed.
 * An id selector is (1,1,0) and settles it without another !important. */
#masthead .menu-toggle-open,#masthead .menu-toggle-open:focus{color:#fff}
body .k24-pill{background:rgba(255,255,255,.92)}

/* the side switch: a small pill, silver hairline border, left of the nav (C20) */
.k24-pill-item{display:flex;align-items:center;margin-right:var(--k24-s2)}
.k24-pill{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 14px 0 12px;border-radius:var(--k24-radius-pill);
  font-family:var(--k24-font-body);font-weight:600;font-size:var(--k24-label);line-height:1;letter-spacing:.12em;text-transform:uppercase;color:var(--k24-text)!important;
  text-decoration:none;border:1px solid var(--k24-border);background:var(--k24-white);
  transition:box-shadow .18s ease,transform .18s ease}
.k24-pill:hover{box-shadow:var(--k24-shadow-lift);transform:translateY(-1px)}
.k24-pill__dot{width:8px;height:8px;border-radius:50%;background:var(--k24-metal-dark);flex-shrink:0}
.k24-pill__arrow{opacity:.6}
/* Book Now: isolated from the menu, with emphasis (C22) */
/* Book Now lives ONLY in the drawer now, and the divider that used to separate it from a
 * horizontal desktop nav is scoped to that nav. In the drawer, a border-LEFT plus a 24px
 * margin and 24px padding pushed the item 49px past every other item and drew a stray vertical
 * hairline down the side of it: measured, items 1-7 all started at x=24.0 and this one's link
 * started at x=73.0. That is the "out of place" the owner saw. */
.main-navigation .k24-book-item{display:flex;align-items:center;margin-left:var(--k24-s3);padding-left:var(--k24-s3);border-left:1px solid var(--k24-hairline)}
body .main-navigation .k24-book-item{border-left-color:rgba(255,255,255,.3)}
.k24-book{min-height:42px!important;padding:10px 22px!important;font-size:var(--k24-label)!important;line-height:1;letter-spacing:.12em;text-transform:uppercase;
  background:var(--k24-primary)!important;color:#fff!important;border-radius:var(--k24-radius-btn)!important}
.k24-book:hover{background:var(--k24-primary-hover)!important}
@media (max-width:1024px){
  .k24-pill-item{margin:var(--k24-s2) 0}.k24-pill{width:100%;justify-content:center;min-height:44px}
  .k24-book-item{margin:var(--k24-s2) 0 0;padding:0;border:0}.k24-book{width:100%;justify-content:center;min-height:48px!important}
}
/* mobile menu: full-screen overlay (C28) */
#mobile-drawer .drawer-inner,#mobile-drawer.popup-drawer .drawer-inner{width:100%!important;max-width:100%!important;background:var(--k24-offwhite)!important;color:var(--k24-text)}
#mobile-drawer .drawer-header{background:var(--k24-offwhite)!important}
#mobile-drawer .mobile-navigation a:not(.k24-book){font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h2);letter-spacing:-.02em;
  text-transform:none;color:var(--k24-text)!important;padding:12px 0!important;border:0!important}
#mobile-drawer .mobile-navigation ul ul a{font-family:var(--k24-font-body);font-weight:400;font-size:var(--k24-body);padding:8px 0 8px 12px!important;color:var(--k24-text-muted)!important}
#mobile-drawer .drawer-nav-drop-wrap .drawer-sub-toggle{color:var(--k24-text)!important;border:0!important}
#mobile-drawer .mobile-navigation .k24-book{font-family:var(--k24-font-body)!important;font-size:var(--k24-body)!important;color:#fff!important;padding:14px 24px!important}
#mobile-drawer .mobile-navigation .k24-pill{font-family:var(--k24-font-body)!important;font-size:var(--k24-label)!important;padding:0 16px!important}
#mobile-drawer .drawer-content{padding:var(--k24-s3)}

/* The drawer's Book button was measured BELOW THE FOLD at both 1440x900 (y=900.6 in a 900px
 * viewport) and 1280x800: the one action the drawer exists to offer could not be seen without
 * scrolling it. Ordering it first costs nothing and removes the whole class of problem, since
 * the menu grew to 9 items on the music side. */
#mobile-drawer .mobile-navigation>ul{display:flex;flex-direction:column}
#mobile-drawer .k24-book-item{order:-1;margin:0 0 var(--k24-s3);padding:0;border:0}
#mobile-drawer .k24-book{width:100%;justify-content:center;min-height:52px!important}

/* ---------- the side switch ----------
 * Two segments, not a link. A link ("go to the corporate side") tells you where you could go;
 * this shows that there are exactly two sides and which one you are on, at rest, without
 * opening anything. Absolutely positioned inside the header rather than inserted into
 * Kadence's flex row, so a theme update that reshuffles that row cannot move it. */
#masthead{position:relative}
.k24-sideswitch{position:absolute;top:50%;transform:translateY(-50%);
  right:calc(var(--k24-hdr-inset) + 64px);z-index:6;
  display:inline-flex;align-items:stretch;border:1px solid var(--k24-border);
  border-radius:var(--k24-radius-btn);overflow:hidden;background:var(--k24-white)}
.k24-sideswitch__opt{display:inline-flex;align-items:center;justify-content:center;
  min-height:36px;padding:0 14px;text-decoration:none;
  font-family:var(--k24-font-body);font-weight:500;font-size:var(--k24-label);line-height:1;
  letter-spacing:.14em;text-transform:uppercase;color:var(--k24-text);
  transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-sideswitch__opt+.k24-sideswitch__opt{border-left:1px solid var(--k24-border)}
.k24-sideswitch__opt.is-active{background:var(--k24-ink);color:var(--k24-white)}
.k24-sideswitch__opt:not(.is-active):hover{background:var(--k24-silver-light);color:var(--k24-text)}

/* Over a photograph the switch needs its own ground or it competes with the image. Same
 * charcoal the marquee band uses, so the two dark objects on the page agree. */
body .k24-sideswitch{background:rgba(28,28,33,.72);border-color:rgba(255,255,255,.34);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
body .k24-sideswitch__opt{color:var(--k24-white)}
body .k24-sideswitch__opt+.k24-sideswitch__opt{border-left-color:rgba(255,255,255,.34)}
body .k24-sideswitch__opt.is-active{background:var(--k24-white);color:var(--k24-ink)}
body .k24-sideswitch__opt:not(.is-active):hover{background:rgba(255,255,255,.14);color:var(--k24-white)}

@media (max-width:620px){
  /* Below this the logo and the switch genuinely compete for the row: at 390 there are 110px
   * between the logo's right edge and the switch's left. Both give a little rather than one of
   * them being dropped, because the switch is the thing that tells a phone visitor there are
   * two sides at all, and the drawer no longer carries that. */
  /* #masthead prefix is load-bearing: `.site-mobile-header-wrap .custom-logo{max-width:104px
   * !important}` is declared LATER in this file, so an equal-specificity !important rule here
   * loses on source order and the logo silently stayed 104px wide. (1,1,1) beats (0,2,1). */
  /* 40px, not 32. Shrinking the switch to fit beside the logo took its tap target under the
   * 44px guideline and made it the smallest control on the site, on the device where it is
   * hardest to hit. The width is what was short of room, not the height. */
  .k24-sideswitch__opt{padding:0 9px;min-height:40px;font-size:10px;letter-spacing:.08em}
}
@media (max-width:430px){
  /* 360 is the narrowest phone worth holding; below the logo and the switch cannot both keep
   * their desktop proportions and the mark is the one that survives being smaller.
   *
   * Retuned when the second label changed from "Voice" to "Corporate". Four extra characters
   * at this size is about 30px, and it put the switch through the logo at 390 and 360. The
   * label is the owner's word and is not being abbreviated back, so the space comes from the
   * mark and from the switch's own padding instead. */
  /* The 58px logo tier that used to be here is gone. It existed only to avoid colliding with
   * the side switch, and that collision was caused by the stale 48px inset: with the inset now
   * the page's own 24px gutter there is 57.03px clear between the logo's right edge and the
   * switch's left at 360px, against 19.03px before. The switch keeps its tighter padding. */
  .k24-sideswitch__opt{padding:0 6px;font-size:9.5px;letter-spacing:.03em}
}

/* switching overlay (C21) */
.k24-switching{position:fixed;inset:0;z-index:99999;display:flex;align-items:center;justify-content:center;background:var(--k24-ink);color:#fff;
  opacity:0;pointer-events:none;transition:opacity .28s ease}
.k24-switching.is-on{opacity:1;pointer-events:auto}
.k24-switching__inner{text-align:center}
.k24-switching__label{font-family:var(--k24-font-body);font-weight:600;letter-spacing:.12em;text-transform:uppercase;font-size:var(--k24-label);line-height:1;color:var(--k24-on-dark-muted);margin:0 0 10px}
.k24-switching__name{font-family:var(--k24-font-display);font-weight:900;font-size:var(--k24-h2);letter-spacing:-.03em;margin:0;color:#fff}

/* ---------- footer: charcoal, centred ---------- */
/* A 12% white hairline: on music and corporate the ink CTA band and this footer are the
 * same #1C1C21 with border-top:0, and raising section air lengthens that unbroken dark run. */
/* THE FOOTER CONTINUES THE CTA BAND'S RAMP INSTEAD OF STARTING A NEW SURFACE.
 *
 * Owner: "I really like the gradient that you have going on there. I want to continue that
 * gradient into the footer... just make it all one gradient... it's all in one kind of like
 * container."
 *
 * Two things were separating them. The footer was FLAT --k24-ink while the band above it ended
 * on #2E2E36, a visible step; and a 12% white hairline was drawn across the join. So the page
 * finished with a seam and then a slab.
 *
 * The ramp is now continuous and monotonic across both elements: the band runs #131317 to
 * #26262D (see .k24-cta-band below) and the footer picks up at #26262D and carries on to
 * #33333C. One direction, no repeat, no edge. The hairline goes with it - a border is the thing
 * that was being asked to disappear.
 *
 * Contrast at the lightest stop #33333C: #FFFFFF is 12.42:1 and --k24-on-dark-muted is 7.42:1,
 * both well clear. */
.site-footer,#colophon{background-color:var(--k24-ink);
  background-image:var(--k24-ground-footer);
  color:var(--k24-on-dark-muted);border-top:0}
.site-footer p,.site-footer li,#colophon p,#colophon li,.site-footer .footer-html-inner,#colophon .footer-html-inner{color:var(--k24-on-dark-muted)}
.site-footer a,#colophon a{color:#fff}
.site-footer a:hover,#colophon a:hover{color:var(--k24-on-dark-muted)}
.site-footer .footer-html-inner,#colophon .footer-html-inner,.site-footer .site-footer-row,#colophon .site-footer-row,.site-footer .site-footer-section,#colophon .site-footer-section{text-align:center!important;justify-content:center!important}
.site-footer .custom-logo,#colophon .custom-logo{max-width:120px;opacity:.95}
.k24-footer-links{display:flex;flex-wrap:wrap;gap:6px 22px;justify-content:center;margin:12px 0 4px;padding:0;list-style:none}
.k24-footer-links a{font-size:var(--k24-label);line-height:1;letter-spacing:.12em;text-transform:uppercase;font-weight:600;text-decoration:none}
.k24-footer-other{font-size:var(--k24-body);margin-top:var(--k24-s2)}
/* Centre the BOXES, not just the text inside them. tokens.css caps p and li at the 66ch
 * measure (528px), and with no auto margin those boxes sat flush left at x=24 while the
 * links list ran the full 680px and centred correctly. text-align:center then centred each
 * line within its own left-aligned box, so the footer's lines disagreed with each other. */
.site-footer p,#colophon p,.site-footer li,#colophon li{margin-inline:auto}

/* ---------- footer: one row ----------
 * Every selector here carries #colophon deliberately. components.css:409 above is
 * `#colophon p,#colophon li` at (1,0,1), so a class-only override loses silently: injecting
 * `.site-footer .k24-footer-links li{margin-inline:0}` live left the computed margin at
 * 0px 163.391px, unchanged. Measured, not assumed.
 *
 * The stacking was structural, not spacing: Kadence's .footer-html-inner is display:block, so
 * the shortcode's pieces were block boxes in normal flow. Making that container a flex row is
 * the fix; the rest of this block removes the three things that fought it.
 *   - the 66ch measure from tokens.css:165, which capped the address at 551px and broke the
 *     email onto its own line even though the row had space
 *   - margin-inline:auto on li, correct for centring block boxes and wrong for flex items,
 *     where it absorbed the free space and flung the three links 163px apart
 *   - the empty <p> wpautop used to leave, now prevented by the wrapper in inc/menus.php but
 *     still guarded here in case the shortcode's markup changes again
 */
#colophon .footer-html{margin:0}
#colophon .k24-footer-bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  column-gap:clamp(18px,2.4vw,36px);row-gap:10px}
#colophon .footer-html-inner>p:empty,#colophon .k24-footer-bar>p:empty{display:none}
#colophon .k24-footer-bar>p,#colophon .k24-footer-bar>ul{margin:0;max-width:none}
#colophon .k24-footer-links{margin:0;gap:clamp(18px,2.4vw,36px)}
#colophon .k24-footer-links li{margin:0;max-width:none}
#colophon .k24-footer-links a{white-space:nowrap}
#colophon .k24-footer-addr{white-space:nowrap}

/* The fixed "Book the room" CTA is position:fixed at bottom-right with z-index 80, so a
 * one-line footer runs straight underneath it. Reserve its band rather than moving it. */
#colophon .site-bottom-footer-inner-wrap{padding-bottom:90px}

@media (max-width:1023px){
  /* One line at 390 is not physically possible: the shortened copy needs a measured 905px in a
   * 342px content box, which would mean 4.7px type. A three-block column is the honest answer
   * and is still far better than the seven lines this used to produce. */
  #colophon .k24-footer-bar{flex-direction:column;row-gap:12px}
  #colophon .k24-footer-addr{white-space:normal}
  #colophon .site-bottom-footer-inner-wrap{padding-bottom:80px}
}
html body .site-footer :focus-visible,html body #colophon :focus-visible,html body .popup-drawer :focus-visible,html body .mobile-navigation :focus-visible{outline-color:#fff!important}

/* ---------- tables ---------- */
.k24-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.k24-section figure.wp-block-table{overflow-x:auto;max-width:100%}
.k24-section figure.wp-block-table table{min-width:0;border-collapse:collapse}
.k24-section figure.wp-block-table td{border:0;border-bottom:1px solid var(--k24-hairline);padding:10px 12px 10px 0}
.k24-hours td:first-child{color:var(--k24-text-muted)}

/* ---------- WooCommerce ---------- */
.woocommerce .price,.woocommerce-Price-amount{color:var(--k24-text);font-family:var(--k24-font-display);font-weight:700}
.woocommerce ul.products li.product{background:var(--k24-surface-1);border-radius:var(--k24-radius);padding:var(--k24-s2);border:1px solid var(--k24-hairline)}
.woocommerce-message,.woocommerce-info{background:var(--k24-surface-1);border-top-color:var(--k24-primary);color:var(--k24-text)}
.woocommerce-error{background:var(--k24-surface-1);border-top-color:var(--k24-danger);color:var(--k24-text)}
.woocommerce table.shop_table{background:var(--k24-surface-1);border-color:var(--k24-hairline)}
.k24-deposit-note{background:var(--k24-lavender);border-radius:var(--k24-radius);padding:12px 16px;font-size:var(--k24-body);margin:var(--k24-s2) 0}
/* [hidden] has to be restated because this rule breaks it. `display:flex` on a plain class is
 * (0,1,0) and the user-agent's `[hidden]{display:none}` is (0,1,0) in the UA origin, which the
 * author origin outranks. So inc/deposit.php sets the attribute, the attribute does nothing, and
 * the box renders before a duration is chosen: measured live at 27px tall reading "Today / At
 * the studio" with both amounts blank, on the page that takes the deposit. Any component that
 * sets display AND expects to be toggled with [hidden] needs this line. */
.k24-deposit-split{display:flex;gap:var(--k24-s3);flex-wrap:wrap;margin:var(--k24-s2) 0;font-size:var(--k24-body)}
.k24-deposit-split[hidden]{display:none}
.k24-deposit-split b{font-family:var(--k24-font-display);font-size:var(--k24-h3);display:block}
.k24-book-embed .product{padding:0}
.k24-book-embed .woocommerce-product-gallery,.k24-book-embed .product_meta,.k24-book-embed .related,.k24-book-embed .woocommerce-tabs,.k24-book-embed .product_title{display:none!important}
.k24-book-embed .summary{width:100%!important;float:none!important;max-width:760px;margin:0!important}
/* --k24-border, not --k24-hairline. This is the outer edge of the booking form: it is the
 * boundary of the one control on the site that takes money, so it is an interface boundary and
 * owes 3:1. The hairline measured 1.11:1 against the ramped ground's darkest stop (#EDEDF1)
 * and had been failing at 1.21:1 on flat paper before that; the gate only caught it once the
 * gradient work made it look at the worst stop instead of the flat colour. Same mistake, same
 * fix, as .k24-credits and .k24-brands__item. #86868C measures 3.10:1 on #EDEDF1, which is the
 * exact reason #EDEDF1 is the darkest light value the palette allows under a control. */
.wc-bookings-booking-form{border:1px solid var(--k24-border)!important;border-radius:var(--k24-radius);background:var(--k24-surface-1);padding:var(--k24-s3)!important}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td a.ui-state-active,.wc-bookings-booking-form .ui-datepicker td.ui-datepicker-current-day a{background:var(--k24-primary)!important;color:#fff!important}
.wc-bookings-booking-form .ui-datepicker th{color:var(--k24-text-muted)}
.wc-bookings-booking-cost{background:var(--k24-lavender)!important;color:var(--k24-text)!important;border-radius:var(--k24-radius);font-weight:600}
body.k24-cart-corporate .wc-block-components-text-input:has(#contact-24k-artist-name),body.k24-cart-corporate .wc-block-components-text-input:has(#contact-24k-instagram),
body.k24-cart-corporate .wc-block-components-text-input:has(#contact-24k-socials){display:none}
body.k24-cart-music .wc-block-components-text-input:has(#contact-24k-company){display:none}

/* ---------- gate: two halves, an image on each, the logo over the seam (B12, B13, B16, B17) ---------- */
.k24-gate{position:relative;min-height:100svh;display:grid;grid-template-columns:1fr 1fr;background:var(--k24-ink);overflow:hidden;margin:0;width:100%;max-width:none}
.k24-gate__half{position:relative;display:flex;align-items:flex-end;min-height:50svh;padding:clamp(24px,4vw,56px);text-decoration:none;color:#fff;overflow:hidden;isolation:isolate}
.k24-gate__half+.k24-gate__half{border-left:1px solid rgba(196,196,204,.35)}
/* THE GAP. gap was never the problem: it is 8px and always was ~10px. The grid box is
 * position:absolute inset:-8%, so it has a FIXED height (1044px at 1440x900), and with
 * grid-auto-rows:auto the three row tracks stretched to 341.3px each to fill it while a tile
 * is only 201.3px tall. The leftover 140px per row is the "so much of a gap" the owner sees:
 * measured 153.0px vertical against 10.2px horizontal, 15x, on the same grid with one gap value.
 * align-content:start ALONE makes it worse, not better: Chrome cannot size an auto row from an
 * aspect-ratio item inside 1fr columns, so the tracks collapse to 167px and the tiles OVERLAP by
 * 28px. It has to be paired with grid-auto-rows:min-content. Measured after: rows 202.8px, and
 * the vertical gap equals the horizontal at 8.2px, holding at 1440, 1024 and 390. */
.k24-gate__grid{position:absolute;inset:-8%;z-index:0;display:grid;grid-template-columns:repeat(3,1fr);
  gap:8px;align-content:start;grid-auto-rows:min-content;opacity:1;
  transform:rotate(-4deg) scale(1.18);animation:k24-gate-drift 48s ease-in-out infinite alternate;will-change:transform}
@media (min-width:1100px){.k24-gate__grid{grid-template-columns:repeat(4,1fr)}}
.k24-gate__half--corp .k24-gate__grid{animation-direction:alternate-reverse}
@keyframes k24-gate-drift{from{transform:rotate(-4deg) scale(1.18) translate3d(-2.5%,-2%,0)}to{transform:rotate(-4deg) scale(1.18) translate3d(2.5%,2%,0)}}
.k24-gate__tile{aspect-ratio:1/1;border-radius:var(--k24-radius);overflow:hidden;background:var(--k24-ink-2)}
.k24-gate__tile img{width:100%;height:100%;object-fit:cover;display:block}
/* NO WHITE PLATES. Each mark used to sit on its own #fff card, which is why the corporate half
 * measured 2.6x brighter than the music half and why the metallic logo over the seam was
 * invisible on that side (1.65:1 median, 82.8% of its pixels under 3:1): a mid-grey mark over a
 * mid-grey ground made of white cards. The marquee already solved this. Same solution here.
 *
 * The treatment MUST stay per-mark. Blanket brightness(0) invert(1) turns any mark whose detail
 * is colour rather than transparency into a white slab, which is how Discovery, DC, Penguin and
 * Warner Bros shipped as unidentifiable blobs in the marquee. k24_client_logos() already stores
 * the right treatment per mark and inc/gate.php now passes it through, so both surfaces read
 * from one list and cannot drift apart. */
/* The corporate half needs MORE, SMALLER tiles than the music half, not the same grid. A sleeve
 * fills its tile edge to edge; a mark is a small object floating in the middle of one. Run the
 * same 4-column grid on both and the music side reads dense while the corporate side reads like
 * five logos lost on a black field. Five columns and a bigger mark inside each tile is what
 * makes the marks sit together as a wall. */
.k24-gate__tile--brand{background:none;padding:0;display:flex;align-items:center;justify-content:center}
.k24-gate__tile--brand img{width:auto;height:48%;max-width:78%;max-height:none;object-fit:contain}
@media (min-width:1100px){.k24-gate__half--corp .k24-gate__grid{grid-template-columns:repeat(5,1fr)}}
.k24-gate__tile--knock img{filter:brightness(0) invert(1)}
.k24-gate__tile--plain img{filter:none}
.k24-gate__tile--lift img{filter:brightness(1.85) saturate(.9)}
/* The "weird dimming" was FOUR stacked layers, not one: the grid at opacity .6, this gradient,
 * a white hover wash, and a 44px black drop-shadow on the logo. Together they removed 77.6% of
 * the music half's luminance and 87.8% of the corporate half's (measured mean luminance 0.0315
 * as-is vs 0.1407 with the first two off). The gradient stays, because the labels sit on it,
 * but it only has to carry the labels now that the grid is at full strength. Measured at the
 * lighter stops below, both labels still read at 17.2:1 and 16.6:1.
 * The transition:opacity here was dead code: nothing in the file ever changed ::after opacity. */
.k24-gate__half::after{content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,rgba(28,28,33,.28) 0%,rgba(28,28,33,.18) 55%,rgba(28,28,33,.86) 100%)}
.k24-gate__half::before{content:"";position:absolute;inset:0;z-index:1;background:rgba(255,255,255,0);transition:background .35s ease}
.k24-gate__half:hover::before,.k24-gate__half:focus-visible::before{background:rgba(255,255,255,.04)}
.k24-gate__label{position:relative;z-index:2;display:flex;align-items:baseline;gap:14px}
.k24-gate__label b{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h1);letter-spacing:-.03em;line-height:1}
.k24-gate__label span{font-size:var(--k24-h3);color:var(--k24-on-dark-muted);transition:transform .25s ease}
.k24-gate__half:hover .k24-gate__label span{transform:translateX(6px)}
.k24-gate__logo{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:3;width:min(300px,44vw);height:auto;pointer-events:none;filter:drop-shadow(0 8px 24px rgba(0,0,0,.45))}
.k24-gate__h{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}
@media (max-width:760px){
  .k24-gate{grid-template-columns:1fr;grid-template-rows:1fr 1fr}
  .k24-gate__half+.k24-gate__half{border-left:0;border-top:1px solid rgba(196,196,204,.35)}
  .k24-gate__grid{grid-template-columns:repeat(4,1fr)}
  .k24-gate__logo{width:min(200px,52vw)}
}
.k24-gate-page .site-header,.k24-gate-page #masthead,.k24-gate-page .site-footer,.k24-gate-page #colophon{display:none!important}
.k24-gate-page .site-content,.k24-gate-page .content-area,.k24-gate-page .site-main,.k24-gate-page .content-container{padding:0!important;margin:0!important}
@media (prefers-reduced-motion:reduce){.k24-gate__grid{animation:none;transform:rotate(-4deg) scale(1.18)}}

/* ---------- 404 ---------- */
.k24-404{min-height:60vh;display:flex;align-items:center;padding:var(--k24-s7) var(--k24-s3)}
.k24-404__inner{max-width:640px;margin-inline:auto;text-align:center}
.k24-404 .k24-hero__cta{justify-content:center}

/* ---------- utilities ---------- */
.k24-doors{display:grid;gap:var(--k24-s3);grid-template-columns:1fr}
@media (min-width:860px){.k24-doors{grid-template-columns:1fr 1fr}}
.k24-muted{color:var(--k24-text-muted)}
.k24-accent,.k24-gold{color:var(--k24-primary-text)}
.k24-stack>*+*{margin-top:var(--k24-s5)}
.k24-inline-link{font-weight:600}
.screen-reader-text{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}
.skip-link:focus{position:fixed;top:8px;left:8px;z-index:99999;width:auto;height:auto;clip:auto;padding:12px 20px;background:var(--k24-primary);color:#fff;font-weight:600;border-radius:var(--k24-radius-pill)}
.k24-terms h2{margin-top:clamp(48px,4.5vw,72px)}
.k24-terms h2:first-of-type{margin-top:0}
.k24-terms ul{padding-left:1.2em}
.k24-terms li+li{margin-top:8px}

/* Kadence puts an 80px margin on .content-area, which pushed the hero below the absolute header. */
body.k24-has-hero .content-area{margin-top:0!important}
/* Kadence puts 5rem (83.6px at 1440) above AND below .content-area; only the top was zeroed,
 * and only for hero pages. Eight pages carry 83.6px of dead paper opening the page and
 * another above the charcoal footer. */
body:not(.k24-has-hero) .content-area{margin-block:0!important}
body.k24-has-hero .site-header-upper-wrap,body.k24-has-hero .site-header-upper-inner-wrap{background:transparent!important}
/* the logo strip is a caption band, not a section: keep it tight */

/* ---------- one booking page, two rates (D31) ----------
 * The two products stay separate because their tiers are not a constant multiple and
 * WooCommerce Bookings adds resource costs after the block rules, so no single resource
 * cost can express the corporate uplift. See inc/booking-page.php for the arithmetic.
 * This is the chooser that sits above whichever form is active. */
.k24-booking-switch{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:var(--k24-s3);margin:0 0 var(--k24-s5)}
.k24-booking-switch__opt{display:flex;flex-direction:column;gap:6px;padding:var(--k24-s3);
  text-decoration:none;border:1px solid var(--k24-border);border-radius:var(--k24-radius);
  background:var(--k24-surface-1);color:var(--k24-text);transition:border-color .18s,background .18s}
.k24-booking-switch__opt:hover{border-color:var(--k24-text)}
.k24-booking-switch__opt.is-active{background:var(--k24-text);color:var(--k24-surface-1);
  border-color:var(--k24-text)}
.k24-booking-switch__label{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  letter-spacing:-.01em}
.k24-booking-switch__note{font-size:var(--k24-body);line-height:1.45;color:var(--k24-text-muted)}
.k24-booking-switch__opt.is-active .k24-booking-switch__note{color:var(--k24-on-dark)}

/* The footer fine print. Was an inline style:.8rem in inc/menus.php, which bypassed the
 * token system entirely and was the last rendered size outside the six-step scale. */
.k24-footer-fine{font-size:var(--k24-label);letter-spacing:.04em;text-transform:none}

/* Every separation device in this commit is a background-image, and browsers default
 * "Background graphics" to OFF when printing. /terms/ is a page people print. border-color
 * survives both print and forced-colors; currentColor resolves to ink on paper and #fff on
 * the ink band. */
@media print,(forced-colors:active){
  .k24-band h2::after,.k24-section h2::after,.k24-hero:not(.k24-hero--full) h1::after{
    background-image:none;height:0;border-top:3px solid currentColor}
  .k24-card::before,.k24-quote::before,.k24-icon-card::before{
    background-image:none;height:0;border-top:2px solid currentColor}
  body:not(.k24-has-hero) #masthead::after{background:none;height:0;border-top:1px solid currentColor}
}

/* ==========================================================================
   THE RESTRUCTURE (owner, 2026-09-08). Steps 1-7 rebuilt the system underneath
   these pages and left their shape alone, so the site still read as the original.
   These rules change the shape.
   ========================================================================== */

/* 1. One navigation, at every width.
 * Kadence ships two complete headers and swaps them at 1025px. Rather than picking the
 * desktop nav apart, the swap is simply removed: the hamburger header runs everywhere.
 * This is the single most visible thing about the reference site, it is what makes a
 * header read as calm rather than busy, and it permanently retires the Services dropdown
 * that produced the same invisible-text bug twice (D26, D27). */
#masthead .site-header-wrap{display:none!important}
#masthead .site-mobile-header-wrap{display:block!important}
/* The header's own content was inset 45px further than the page content below it, because this
 * padding is applied INSIDE a wrap that Kadence has already padded: measured, the logo started
 * at x=69.1 while the page h1 started at x=24.0, so the mark visibly failed to line up with
 * everything under it. That misalignment is a large part of why the header "looks weird" on the
 * plain pages, where there is no photograph to distract from it. max-width and the auto margin
 * stay; only the double padding goes. */
/* THE INSET IS THE PAGE'S OWN GUTTER, TRANSCRIBED. It is not a calibration any more.
 *
 * It used to be max(48px,(100vw - container)/2 + 53px), fitted to where the hamburger happened
 * to land. Measured against the page content it was 3.92px stale at 1440 and drifting: the logo
 * sat +45.08px right of the page's own content edge at 1440, +50.39 at 1600 and +52.50 at 1920.
 * The cause is Kadence's .site-container{padding:0 var(--global-content-edge-padding)} at 1.5rem,
 * on top of this wrap's own padding, and tokens.css makes 1rem fluid, so the double padding grew
 * with the viewport. On the reference site the logo's x and the body copy's x are identical.
 *
 * One expression now governs both the left inset and the header's height, which is the
 * relationship that makes a header look drawn rather than assembled. Measured after the change:
 * logo x minus page content x is 0.00 at 1920, 1600, 1440, 1300, 1100, 900, 768, 620, 500, 430,
 * 390 and 360, on both a hero page and a plain one. */
:root{--k24-hdr-inset:max(var(--k24-s3),(100vw - var(--k24-container)) / 2)}
#masthead .site-mobile-header-wrap .site-header-inner-wrap{
  max-width:none;margin-inline:0;padding-inline:var(--k24-hdr-inset)}
/* kills Kadence's own edge padding, which is the entire 24-to-52.5px drift */
#masthead .site-container{max-width:none;margin-inline:0;padding-inline:0}

/* HEIGHT AND SCALE. "its too big and too close to the top of the page" is two complaints and
 * both measure true, but only one is what it looks like.
 *
 * Too close: viewport top to logo top was 7.86px at every width above 620, in an 80px bar the
 * mark filled to 80.4%. The reference gives its logo 27px of air and fills 47.1% of a 114.69px
 * header. Ours is now 96px = two 48px hamburgers, which gives 23.89px top and bottom: the same
 * 24px as the page's left gutter, from the same expression.
 *
 * Too big: yes, but it was NOT sitting high. Logo, side switch and hamburger centre-lines all
 * measured exactly 40.0 in the 80px bar. The defect was scale alone: 63px of logo ink beside a
 * 12px hamburger glyph.
 *
 * 78px and not smaller, because the mark is a stacked lockup. Alpha analysis of logo-ink.png
 * (516x319) puts "24K" in rows 3-247 and a letter-spaced "STUDIOS" in rows 256-311, so that
 * sub-line is 55/319 of the height: 11.09px inside a 104px mark, 8.31px at 78px, 6.82px at 64px.
 * The smallest type on the site is --k24-label at 12.54px with a cap height near 8.8px, so 78px
 * keeps the sub-line within half a pixel of the smallest type already on the page. The 16px
 * comes out of the bar, not out of the mark. */
#masthead .site-main-header-inner-wrap{min-height:96px}
#masthead .site-mobile-header-wrap .custom-logo{max-width:78px!important}
.menu-toggle-open{min-height:48px;min-width:48px}
@media (max-width:620px){
  #masthead .site-main-header-inner-wrap{min-height:80px}
  /* 68/1.6176 = 42.03px, which is the measured height of the side switch at this breakpoint
   * (its 40px min-height plus two 1px borders). The mark and the control it sits beside are
   * then the same object height, which is the thing that was wrong at 390 where the logo had
   * become the SMALLEST element in the row at 35.84px against a 42px switch and a 48px burger. */
  #masthead .site-mobile-header-wrap .custom-logo{max-width:68px!important}
}
/* the drawer is the whole navigation now, so it gets the room to be one */
#mobile-drawer .drawer-inner{padding-block:var(--k24-s6)!important}
#mobile-drawer .mobile-navigation ul{gap:var(--k24-s2);display:flex;flex-direction:column}
#mobile-drawer .mobile-navigation>div>ul>li>a{font-size:var(--k24-h1);line-height:1.06}

/* 3. The display size, finally used.
 * --k24-display was defined at 104px in step 3 and wired to nothing, so every hero
 * headline rendered at the h1's 56px. The reference site's hero words are 84px at 1440.
 * This is the change that most makes a hero feel like theirs, and it cost one rule. */
.k24-hero--full h1{font-size:var(--k24-display);line-height:1;letter-spacing:0;max-width:16ch}
.k24-hero--full .k24-hero__inner>*{max-width:min(760px,90vw)}

/* ==========================================================================
   MOTION. The values are the reference site's own, read off its computed styles:
   reveal opacity/transform .9s cubic-bezier(.22,1,.36,1), slide 1.2s
   cubic-bezier(.77,0,.175,1), micro-interactions .25s, colour .3s.

   Two rules govern everything below.
   1. NOTHING is hidden by CSS alone. Every animated element is fully visible by
      default and only starts hidden once JS has added .k24-anim to <html>. If the
      script fails, is blocked, or the page is printed, the content is simply there.
      A reveal system that can hide content permanently is a bug, not an effect.
   2. prefers-reduced-motion removes the movement, not the content. tokens.css
      already neutralises durations globally; these rules add the opacity reset so a
      reduced-motion visitor never sees a half-faded element.
   ========================================================================== */

/* -- entrances: sections, headings, cards, figures rise and fade in once -- */
/* TWO EASINGS ON ONE ELEMENT, which is the detail that makes the reference site's text read
 * the way it does: the transform runs on easeOutQuint (front-loaded, most of the travel in the
 * first third) while the opacity runs on plain `ease` (symmetric). The fade therefore trails
 * the movement slightly instead of tracking it, and the line appears to settle into place
 * rather than fading in at a position. Sampled off their page mid-flight: at 206ms the element
 * had covered 64% of its travel but only 26% of its fade. */
.k24-anim .k24-reveal{opacity:0;transform:translateY(var(--k24-reveal-rise));
  transition:opacity var(--k24-dur-reveal) ease,
             transform var(--k24-dur-reveal) var(--k24-ease-out)}
.k24-anim .k24-reveal.is-in{opacity:1;transform:none}
/* a short stagger inside a group, so a row of cards arrives as a row and not a block */
.k24-anim .k24-reveal[data-k24-delay="1"]{transition-delay:.08s}
.k24-anim .k24-reveal[data-k24-delay="2"]{transition-delay:.16s}
.k24-anim .k24-reveal[data-k24-delay="3"]{transition-delay:.24s}

/* -- the metal rule draws itself in from the left, which is the one place the
      site's own signature device gets to be the thing that moves -- */
.k24-anim .k24-reveal h2::after,
.k24-anim .k24-reveal.k24-hero h1::after{transform:scaleX(0);transform-origin:left center;
  transition:transform var(--k24-dur-slide) var(--k24-ease-inout) .1s}
.k24-anim .k24-reveal.is-in h2::after,
.k24-anim .k24-reveal.is-in.k24-hero h1::after{transform:scaleX(1)}

/* -- the hero arrives on load rather than on scroll: it is already in view -- */
.k24-anim .k24-hero__inner>*{opacity:0;transform:translateY(1.25rem);
  transition:opacity var(--k24-dur-reveal) var(--k24-ease-out),
             transform var(--k24-dur-reveal) var(--k24-ease-out)}
.k24-anim .k24-hero__inner.is-in>*{opacity:1;transform:none}
.k24-anim .k24-hero__inner.is-in>*:nth-child(2){transition-delay:.10s}
.k24-anim .k24-hero__inner.is-in>*:nth-child(3){transition-delay:.20s}
.k24-anim .k24-hero__inner.is-in>*:nth-child(4){transition-delay:.30s}
/* the hero photograph settles: a slow scale-down, their 1.2s slide easing */
.k24-anim .k24-hero--full .k24-hero__figure img{transform:scale(1.06);
  transition:transform 1.8s var(--k24-ease-inout)}
.k24-anim .k24-hero--full.is-in .k24-hero__figure img{transform:none}

/* -- micro-interactions, .25s, the half of the system that fires on hover -- */
.k24-btn,.k24-book,.k24-pill,.k24-booking-switch__opt,.k24-room,.k24-figure{
  transition:background-color var(--k24-dur-micro),border-color var(--k24-dur-micro),
             color var(--k24-dur-micro),transform var(--k24-dur-micro)}
.k24-btn:hover,.k24-book:hover{transform:translateY(-2px)}
.k24-room:hover{transform:translateY(-3px)}
.main-navigation a,#mobile-drawer .mobile-navigation a,.site-footer a,#colophon a{
  transition:color .3s}
/* the drawer's links arrive in sequence when the menu opens */
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li{
  animation:k24-drawer-in var(--k24-dur-reveal) var(--k24-ease-out) both}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(2){animation-delay:.05s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(3){animation-delay:.10s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(4){animation-delay:.15s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(5){animation-delay:.20s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(6){animation-delay:.25s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(7){animation-delay:.30s}
.k24-anim #mobile-drawer.show-drawer .mobile-navigation li:nth-child(8){animation-delay:.35s}
@keyframes k24-drawer-in{from{opacity:0;transform:translateY(1rem)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  .k24-anim .k24-reveal,.k24-anim .k24-hero__inner>*,
  .k24-anim #mobile-drawer.show-drawer .mobile-navigation li{opacity:1!important;transform:none!important;animation:none!important}
  .k24-anim .k24-reveal h2::after,.k24-anim .k24-hero--full .k24-hero__figure img{transform:none!important}
}

/* ---------- the persistent CTA ----------
 * The reference keeps one small "Reach out" pill anchored bottom-right on every screen,
 * which is the only element that never moves. It is why their pages can afford to carry no
 * other chrome: the way out is always in the same place. 24K's equivalent is the booking
 * link, and this is additional to the Book Now in the menu, not a replacement: the menu is
 * behind a hamburger now, so without this there is no visible route to booking at rest. */
.k24-cta-fixed{position:fixed;right:clamp(16px,2.4vw,32px);bottom:clamp(16px,2.4vw,32px);z-index:80;
  display:inline-flex;align-items:center;gap:10px;
  padding:15px 24px;border-radius:var(--k24-radius-btn);
  background:var(--k24-ink);color:var(--k24-white);text-decoration:none;
  font-family:var(--k24-font-body);font-weight:600;font-size:var(--k24-label);
  line-height:1;letter-spacing:.12em;text-transform:uppercase;
  box-shadow:0 10px 30px -12px rgba(0,0,0,.55);
  transition:transform var(--k24-dur-micro),background-color var(--k24-dur-micro)}
.k24-cta-fixed:hover{background:var(--k24-ink-2);color:var(--k24-white)}
.k24-cta-fixed__arrow{display:inline-block;transition:transform var(--k24-dur-micro)}
.k24-cta-fixed:hover .k24-cta-fixed__arrow{transform:translateX(4px)}
/* it arrives after the page has settled rather than competing with the hero. .35s of delay,
 * was .6s: with the reveal duration itself down to .55s, the old delay meant the only visible
 * booking link on the page took 1.5s to appear. */
.k24-anim .k24-cta-fixed{opacity:0;transform:translateY(.75rem);
  transition:opacity var(--k24-dur-reveal) var(--k24-ease-out) .35s,
             transform var(--k24-dur-reveal) var(--k24-ease-out) .35s}
.k24-anim .k24-cta-fixed.is-in{opacity:1;transform:none}
@media (max-width:600px){.k24-cta-fixed{padding:12px 18px}}
@media print{.k24-cta-fixed{display:none}}

/* ---------- the outlined display word ----------
 * The reference's most recognisable device: transparent type with a 1px white stroke,
 * measured off their hero at `-webkit-text-stroke:1px rgba(255,255,255,.92)`. It was refused
 * earlier for the page heroes and that still holds: those are sentences, and outlining a
 * sentence is just copying. The gate is different. Its two labels are single words on a
 * photograph, which is exactly the shape the device is for, and the gate is 24K's own
 * structure rather than a borrowed one. The ACTIVE (hovered) half fills in, so the outline
 * carries the choice rather than being decoration. */
/* SOLID, ALWAYS. This was transparent text painted only by a 1px stroke, so under a third of
 * each letterform (31.9%) carried any ink at all: an effective 3.10:1 over the glyph footprint,
 * against 13.38:1 once it filled on hover. The outline was meant to make the hovered half read
 * as the chosen one; what it actually did was make the resting state look unfinished, and the
 * choice is already carried by the arrow nudge and the hover wash. Solid measures 17.2:1 and
 * 16.6:1 on the two halves. The @supports fallback and the colour transition existed only to
 * serve the stroke and go with it. */
.k24-gate__label b{color:var(--k24-white);-webkit-text-stroke:0}

/* ---------- the credit marquee, given its own moment ----------
 * This is 24K's single strongest asset and the one thing the reference site cannot copy:
 * it renders zero client logos across all eight of its pages. It was being shown as 92x35px
 * thumbnails behind a fade under a small caption, which is the opposite of emphasis.
 *
 * Now: a full-bleed charcoal band directly under the hero, logos knocked to white so
 * thirteen marks in thirteen different brand colours read as one set, and no heading above
 * them. The logos are the statement. The qualifying line sits underneath, quiet, because
 * these are engineer credits and the wording is what keeps the claim honest. */
.k24-marquee-block{position:relative;padding-block:clamp(56px,6.4vw,104px);
  background:var(--k24-ink);color:var(--k24-on-dark-muted);
  margin-inline:calc(50% - 50vw);width:100vw;max-width:100vw}
.k24-band--caption{padding:0!important;background:none!important}

.k24-marquee{max-width:none;margin:0;overflow:hidden;
  /* the track fades into the band's own ground at both edges instead of hard-cutting */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.k24-marquee__track{display:flex;gap:clamp(44px,5.5vw,96px);width:max-content;
  align-items:center;animation:k24-scroll 64s linear infinite}
.k24-marquee:hover .k24-marquee__track,
.k24-marquee:focus-within .k24-marquee__track{animation-play-state:paused}

/* No plate, no border, no box: just the mark. Knocking every logo to white is what makes a
 * row of thirteen different brand palettes read as one roster rather than a clipart shelf. */
.k24-marquee__item{position:relative;height:auto;width:auto;flex-shrink:0;
  display:flex;align-items:center;justify-content:center;background:none;border:0}

/* Height ALONE is the wrong normaliser for this set, and measuring proved it. Eight of the
 * thirteen source files are square-cropped (aspect 1.00) with the mark padded inside them,
 * while Tim Hortons (4.61) and Walmart (4.21) are tight-cropped wordmarks. At a uniform 37px
 * height Apple rendered 37px wide and Tim Hortons 173px: one instruction, a 4.7x spread in
 * how large each brand appeared. max-width is the second constraint that closes it, and
 * object-fit:contain letterboxes the over-wide wordmarks inside their box rather than
 * stretching them, so they lose height instead of the set losing its baseline. */
.k24-marquee__item img{max-height:none;width:auto;
  height:clamp(30px,3.3vw,48px);max-width:clamp(88px,8.2vw,120px);
  object-fit:contain;opacity:.88;transition:opacity var(--k24-dur-micro)}
.k24-marquee:hover .k24-marquee__item img{opacity:1}

/* The knockout is per-mark, not blanket, and inc/marquee.php carries the reason next to each
 * file. Short version: brightness(0) invert(1) repaints opaque pixels white and preserves
 * alpha, so it only works where the mark's detail is transparency. Applied to a mark whose
 * detail is colour it produces a white slab, which is how Discovery Channel, DC, Penguin and
 * Warner Bros were shipping as unidentifiable blobs. Marks in the second group render
 * unaltered. */
.k24-marquee__item--knock img{filter:brightness(0) invert(1)}
.k24-marquee__item--plain img{filter:none}
/* brightness only, no hue rotation and no forced saturation shift beyond a slight pull-back:
 * this has to make a trademark easier to see on a dark ground, not repaint it. */
.k24-marquee__item--lift img{filter:brightness(1.85) saturate(.9)}

/* Album sleeves. A different object from a logo and styled as one: artwork is reproduced, not
 * treated, so no knockout, no brightness lift, no opacity. Square, because sleeves are square,
 * and cropping one to fit a strip would be defacing the thing being credited. Bigger than the
 * marks too: a logo is a symbol that reads at any size, a sleeve is a picture and stops being
 * legible when it is small. */
.k24-marquee-block--covers{padding-block:clamp(44px,5vw,80px)}
.k24-marquee__item--cover img{width:clamp(84px,8.4vw,124px);height:clamp(84px,8.4vw,124px);
  max-width:none;max-height:none;object-fit:cover;filter:none;opacity:1;
  border-radius:var(--k24-radius);display:block}
.k24-marquee-block--covers .k24-marquee__track{gap:clamp(14px,1.5vw,22px)}
.k24-marquee:hover .k24-marquee__item--cover img{opacity:1}

/* The gate's music half carries sleeves, which are 1:1. The default tile is 3/4 for the room
 * photographs it used to hold, and forcing a square image into it cropped a fifth off every
 * sleeve. */
.k24-gate__grid--covers .k24-gate__tile{aspect-ratio:1/1}
.k24-marquee--lg .k24-marquee__item{height:auto;width:auto}

/* The qualifier: present, legible, and clearly subordinate to the marks above it.
 *
 * --k24-on-dark-muted (10.2:1), NOT --k24-metal-dark (3.36:1, a boundary value). This is the
 * sentence that keeps thirteen blue-chip marks an honest claim, so it has to be readable; it
 * is made subordinate by size, weight and tracking. Fourth time in this project a non-text
 * token was reached for as text, and the render gate caught all four.
 *
 * No `opacity` here, deliberately. It used to carry opacity:.75, which paints #9E9EA3 and
 * measures 6.36:1, while the gate reads the computed `color` and reported 10.29:1. That
 * passed, but only by luck: the number the gate prints has to be the number on the glass.
 *
 * Specificity is the other half. Kadence content.min.css sets `.single-content p` at (0,2,1)
 * to margin-top:0, which beat the old (0,1,0) class selector and collapsed the gap to zero,
 * putting this line flat against the logos; `.k24-band p` (0,1,1) capped it to --k24-measure
 * and wrapped it to two lines. (0,2,2) here clears both outright rather than by source order,
 * so a change in enqueue order cannot silently reintroduce the collision. */
.k24-marquee__credit{font-family:var(--k24-font-body);font-weight:400;
  font-size:var(--k24-label);letter-spacing:.16em;text-transform:uppercase;
  color:var(--k24-on-dark-muted)}
section.k24-marquee-block p.k24-marquee__credit{
  margin:clamp(34px,3.8vw,60px) auto 0;max-width:none;text-align:center;line-height:1.45}

@media (prefers-reduced-motion:reduce){
  .k24-marquee__track{animation:none;flex-wrap:wrap;justify-content:center;width:100%}
  .k24-marquee{-webkit-mask-image:none;mask-image:none}
}


/* The white strip above the footer.
 * Kadence puts margin-bottom on .content-area (83.6px at 1440). On a page whose last section
 * is the ink CTA band, that margin painted a band of paper between two identical dark blocks,
 * which read as a seam in the page rather than as spacing. Sections here carry their own
 * bottom padding (the CTA band has 108px) and the footer carries its own top padding, so the
 * margin was contributing nothing but the seam. */
.content-area{margin-bottom:0!important}


/* ---------- more motion ----------
 * Everything here stays scoped under .k24-anim, which JS only adds once it has confirmed it
 * can observe. That is the same safety model as the reveals: if the script never runs, none of
 * these rules apply and the page is simply static and complete. A motion system whose failure
 * mode is invisible or frozen content is worse than no motion system.
 */

/* 1. Photographs settle rather than appear. The image starts fractionally over-scaled and
 * eases back to true while the block fades in, so the picture reads as arriving with the
 * layout rather than being pasted into it. The parent already clips, so nothing spills. */
.k24-figure,.k24-room,.k24-release,.k24-gate__tile{overflow:hidden}
.k24-anim .k24-reveal.k24-figure img,
.k24-anim .k24-reveal.k24-room img,
.k24-anim .k24-reveal .k24-figure img,
.k24-anim .k24-reveal .k24-room img{
  transform:scale(1.07);transition:transform 1.4s var(--k24-ease-out)}
.k24-anim .k24-reveal.is-in.k24-figure img,
.k24-anim .k24-reveal.is-in.k24-room img,
.k24-anim .k24-reveal.is-in .k24-figure img,
.k24-anim .k24-reveal.is-in .k24-room img{transform:scale(1)}

/* 2. The hero image drifts. Slow enough (28s, 3% of scale) that it is not a visible movement
 * so much as the sense that the frame is alive, which is what a looping video gives the
 * reference site without any of the weight of one. Alternates so there is no seam on repeat. */
@keyframes k24-hero-drift{from{transform:scale(1.02)}to{transform:scale(1.06)}}
.k24-anim .k24-hero--full .k24-hero__figure img{
  animation:k24-hero-drift 28s var(--k24-ease-inout) infinite alternate;will-change:transform}

/* 3. The metallic rule under a heading already draws itself left to right on reveal. It now
 * carries a slower travelling highlight afterwards, so the thing the owner singled out as
 * the site's signature is present while a section is being read, not only in the moment it
 * arrives. The gradient is 3x the element width and slides one width per cycle. */
@keyframes k24-sheen{from{background-position:0 0}to{background-position:200% 0}}
.k24-anim .k24-reveal.is-in h2::after{
  background-size:200% 100%;animation:k24-sheen 7s linear 1.1s infinite}

/* 4. Buttons: the fill wipes in from the left rather than swapping colour. Uses a pseudo
 * element so the label never repaints. */
.k24-btn{position:relative;isolation:isolate}
.k24-btn--ghost::before{content:"";position:absolute;inset:0;z-index:-1;background:currentColor;
  opacity:0;transform:scaleX(0);transform-origin:left center;
  transition:transform var(--k24-dur-micro) var(--k24-ease-out),opacity var(--k24-dur-micro)}
.k24-btn--ghost:hover::before{opacity:.10;transform:scaleX(1)}

/* prefers-reduced-motion removes the movement, not the content: every element above is
 * already at its final state, so cancelling the animation leaves the page correct. */
@media (prefers-reduced-motion:reduce){
  .k24-anim .k24-hero--full .k24-hero__figure img{animation:none;transform:none}
  .k24-anim .k24-reveal.is-in h2::after{animation:none}
  .k24-anim .k24-reveal.k24-figure img,
  .k24-anim .k24-reveal.k24-room img,
  .k24-anim .k24-reveal .k24-figure img,
  .k24-anim .k24-reveal .k24-room img{transform:none;transition:none}
  .k24-btn--ghost::before{transition:none}
}


/* ---------- portfolio: the discography and the roster ----------
 * The sleeve is the object; everything under it is a caption and is sized like one. A record
 * grid where the type competes with the artwork reads as a shop, not a credit list.
 * auto-fill with a minmax track means the column count follows the viewport instead of being
 * declared per breakpoint, so this holds from 360 to 1920 without a media query. */
.k24-disc{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(140px,15vw,200px),1fr));
  gap:clamp(20px,2.4vw,36px);margin-top:var(--k24-s5)}
.k24-disc__item{margin:0;min-width:0}
.k24-disc__art{aspect-ratio:1;overflow:hidden;background:var(--k24-silver-light);
  border-radius:var(--k24-radius)}
.k24-disc__art img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform var(--k24-dur-slide) var(--k24-ease-out)}
.k24-disc__item:hover .k24-disc__art img{transform:scale(1.04)}
.k24-disc__title{font-family:var(--k24-font-body);font-weight:600;font-size:var(--k24-body);
  line-height:1.3;margin:var(--k24-s2) 0 2px;letter-spacing:0;
  overflow-wrap:anywhere}
.k24-disc__artist{font-size:var(--k24-body);color:var(--k24-text-muted);margin:0;line-height:1.3}
.k24-disc__role{font-size:var(--k24-label);color:var(--k24-muted);margin:6px 0 0;line-height:1.3;
  letter-spacing:.08em;text-transform:uppercase;display:flex;justify-content:space-between;gap:8px}
.k24-disc__year{flex-shrink:0}
/* the caption must never be capped at the prose measure: these are 150px columns */
.k24-section .k24-disc p,.k24-section .k24-disc h3{max-width:none}

/* The roster is the corporate equivalent: the mark, then the name, and nothing invented in
 * between. Each item reuses the marquee's own per-mark treatment class so a mark that cannot be
 * knocked to white is not knocked to white here either. */
.k24-roster{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(150px,16vw,210px),1fr));
  gap:clamp(20px,2.4vw,36px);margin-top:var(--k24-s5)}
.k24-roster__item{margin:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;
  gap:var(--k24-s2);padding:var(--k24-s4) var(--k24-s2);background:var(--k24-ink);
  border-radius:var(--k24-radius);min-height:150px}
.k24-roster__item img{width:auto;height:clamp(26px,2.8vw,40px);max-width:74%;object-fit:contain}
.k24-roster__name{font-size:var(--k24-label);letter-spacing:.1em;text-transform:uppercase;
  color:var(--k24-on-dark-muted);margin:0;text-align:center}
.k24-section .k24-roster p{max-width:none}
.k24-roster__item.k24-marquee__item--knock img{filter:brightness(0) invert(1)}
.k24-roster__item.k24-marquee__item--plain img{filter:none}
.k24-roster__item.k24-marquee__item--lift img{filter:brightness(1.85) saturate(.9)}


/* ---------- the gear roster ----------
 * The 47 plugin vendors were one 623-character paragraph of names separated by middots, set in
 * --k24-text-muted and measuring 551x241px. Forty-seven suppliers rendered as a single grey
 * block, which reads as one object and invites the eye to skip it whole.
 *
 * A grid gives each name its own cell and the same weight, which is what the list is actually
 * claiming. auto-fill with a minmax track means the column count follows the viewport rather
 * than being declared per breakpoint. The cell size does not change when a name is replaced by
 * a logo, so a roster that is only half illustrated still lines up. */
.k24-brands{display:grid;grid-template-columns:repeat(auto-fill,minmax(clamp(126px,12vw,168px),1fr));
  gap:0;margin:var(--k24-s4) 0 0;padding:0;list-style:none}
/* Rules come from COLLAPSING CELL BORDERS, not from a 1px gap over a coloured container. The
 * container trick paints its own background wherever a cell is missing, so the final row of a
 * 47-into-8 grid showed a grey filler block and the left edge showed a grey strip. Borders on
 * the cells with a -1px overlap have no gaps to fill and end exactly where the cells do. */
.k24-brands__item{display:flex;align-items:center;justify-content:center;text-align:center;
  min-height:clamp(56px,5.4vw,74px);padding:10px 12px;max-width:none;
  /* --k24-metal-dark, not --k24-border. The render gate blocked the deploy on this: #86868C
   * measures 2.60:1 against the tint band these cells sit on, under the 3:1 an interface
   * boundary needs, and the rule IS the component here, not decoration. #6E6E76 clears every
   * surface it touches: 5.05:1 on white, 3.63:1 on tint, 4.72:1 on paper. */
  border:1px solid var(--k24-metal-dark);margin:-1px 0 0 -1px;
  background:var(--k24-tint);color:var(--k24-text);
  font-size:var(--k24-label);letter-spacing:.04em;line-height:1.25;
  transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-brands__item:hover{background:var(--k24-ink);color:var(--k24-white)}
.k24-brands__item--logo img{width:auto;height:clamp(22px,2.4vw,32px);max-width:82%;
  max-height:56%;object-fit:contain}
/* A white-on-transparent mark is invisible on a white cell. brightness(0) is the mark's own
 * monochrome form, not a recolour; which marks need it is measured, not guessed
 * (ops/plugin-logo-treatment.py, 16 of 35 on the current set). */
.k24-brands__item--logo.is-invert img{filter:brightness(0)}
.k24-brands__item--logo:hover img{filter:brightness(0) invert(1)}
.k24-brands__item--logo.is-invert:hover img{filter:brightness(0) invert(1)}
/* the tint band this sits on is already --k24-tint, so cells lift to white to stay legible */
.k24-section--tint .k24-brands__item{background:var(--k24-white)}
.k24-section--tint .k24-brands__item:hover{background:var(--k24-ink);color:var(--k24-white)}
.k24-section .k24-brands li{max-width:none}

/* Gear photographs render only when the file exists (see inc/gear.php), so these rules are
 * inert until the shoot lands rather than holding open an empty box. */
.k24-gearshot{margin:var(--k24-s4) 0 0}
.k24-gearshot img{width:100%;height:auto;display:block}
/* The caption sits INSIDE .k24-figure's border, so it needs the inset the image does not.
 * Without it the first letter butts against the 1px rule on the left and the last line sits on
 * the one at the bottom, which reads as a caption that has escaped its box. Only visible once
 * real photographs landed in these slots; the rules above were written before there were any. */
.k24-gearshot figcaption{font-size:var(--k24-label);color:var(--k24-muted);margin-top:0;
  padding:var(--k24-s2) var(--k24-s3);letter-spacing:.06em;text-transform:uppercase}
.k24-gearshot--wide{margin-inline:calc(50% - 50vw);width:100vw;max-width:100vw}


/* ---------- services: five cards ----------
 * The page was four long essays explaining what recording, mixing and mastering are, which the
 * music landing page has already said. Measured, it was the worst page on the site for unbroken
 * ground: 4954px tall and 97% light. Five cards say the same things in the order someone
 * choosing a service actually needs them: what it is, what you get, what it costs, how to start.
 *
 * The cards are not boxes. A border and a fill on every one of five would make the page a shelf;
 * a single rule between them reads as a list, which is what it is. */
/* Three across at desktop, not four. There are five cards: auto-fit at a 320px minimum gives
 * four columns at 1440 and leaves the fifth alone in a row of its own with three empty cells
 * beside it. A 380px minimum gives three, so the five break 3 + 2 and the second row fills. */
.k24-svcgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(380px,100%),1fr));
  gap:0;margin-top:var(--k24-s5)}
.k24-svc{display:flex;flex-direction:column;align-items:flex-start;margin:0;
  padding:clamp(28px,3vw,44px) clamp(22px,2.4vw,38px);
  border-top:1px solid var(--k24-metal-dark);margin:-1px 0 0 -1px;
  border-left:1px solid var(--k24-metal-dark)}
.k24-svc__title{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  letter-spacing:-.01em;margin:0 0 var(--k24-s2);max-width:none}
/* The metallic rule under each card title is KEPT. It was going to be suppressed here on the
 * grounds that it belongs to prose headings, but `.k24-section h2::after` is (0,2,1) and beat
 * the (0,1,1) that tried to remove it, so it drew anyway, and drawing it looks right: it marks
 * each card the way it marks each section. Left in deliberately rather than left in by accident.
 * It is shortened, because a 132px rule under a 3-word title overshoots the title. */
.k24-section .k24-svc .k24-svc__title::after{width:64px;margin-top:10px}
.k24-svc__lede{font-size:var(--k24-body);color:var(--k24-text);margin:0 0 var(--k24-s2);
  max-width:34ch;font-weight:500}
.k24-svc__body{font-size:var(--k24-body);color:var(--k24-text-muted);margin:0 0 var(--k24-s3);
  max-width:40ch;flex:1}
.k24-svc__meta{font-size:var(--k24-label);letter-spacing:.1em;text-transform:uppercase;
  color:var(--k24-muted);margin:0 0 var(--k24-s3);max-width:none}
.k24-svc__cta{font-size:var(--k24-label);letter-spacing:.12em;text-transform:uppercase;
  font-weight:500;text-decoration:none;color:var(--k24-text);
  border-bottom:1px solid var(--k24-metal-dark);padding-bottom:4px;
  transition:border-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-svc__cta:hover{color:var(--k24-ink);border-bottom-color:var(--k24-ink)}
.k24-svc__cta span{display:inline-block;transition:transform var(--k24-dur-micro)}
.k24-svc:hover .k24-svc__cta span{transform:translateX(4px)}
.k24-section .k24-svc p{max-width:none}


/* ---------- motion borrowed from the reference site ----------
 * encounterstudios.ca runs no animation library at all: no GSAP, no ScrollTrigger, no Lenis.
 * Every bit of its motion is CSS transitions and @keyframes, which is why it can be taken
 * rather than approximated. Read off its computed styles, its vocabulary is four things:
 *
 *   1. a slow vertical float on feature imagery      (whatImageFloat, 10px, 5s ease-in-out)
 *   2. six drift paths so nothing floating syncs up  (logosFloat1..6, up to 16px, translate3d)
 *   3. a scaleX(0)->scaleX(1) underline on links     (.faq-tabs-nav-button::after)
 *   4. an accordion on grid-template-rows 0fr->1fr   (.35s, cubic-bezier(.22,1,.36,1))
 *
 * Taken here: 1, 2 and 3. The accordion is not, because this site has nothing that opens and
 * closes, and copying a component to own its animation is how a borrowed site becomes a clone.
 * Their -webkit-text-stroke hero titles are also not taken: the same device measured 3.10:1 on
 * our own gate and was removed for it.
 */

/* 1 and 2. The float, in three phases so that two figures on the same screen are never in step.
 * 10px and 5s are their numbers. Applied ONLY to decorative photography: never to text, never
 * to anything clickable, and never to the 47-cell plugin roster, where forty-seven things
 * drifting independently would be unusable rather than alive. */
@keyframes k24-float-a{0%,100%{transform:translate3d(0,0,0)}50%{transform:translate3d(0,-10px,0)}}
@keyframes k24-float-b{0%,100%{transform:translate3d(0,0,0)}
  30%{transform:translate3d(4px,-8px,0)}70%{transform:translate3d(-3px,5px,0)}}
@keyframes k24-float-c{0%,100%{transform:translate3d(0,0,0)}
  40%{transform:translate3d(-5px,-6px,0)}75%{transform:translate3d(3px,7px,0)}}
.k24-anim .k24-gearshot img,
.k24-anim .k24-figure img{animation:k24-float-a 5s var(--k24-ease-inout) infinite;will-change:transform}
.k24-anim .k24-gearshot:nth-of-type(2n) img{animation-name:k24-float-b;animation-duration:6.5s}
.k24-anim .k24-gearshot:nth-of-type(3n) img{animation-name:k24-float-c;animation-duration:7.5s}
/* the reveal's own settle must finish before the float takes over, or they fight for transform */
.k24-anim .k24-reveal:not(.is-in) .k24-gearshot img,
.k24-anim .k24-reveal:not(.is-in) .k24-figure img{animation:none}

/* the mark over the gate seam gets the gentlest of the three, at half amplitude */
.k24-anim .k24-gate__logo{animation:k24-float-a 7s var(--k24-ease-inout) infinite;
  will-change:transform}

/* 3. The underline sweep. Their nav uses a ::after that scales from the centre; ours grows from
 * the left, matching the direction the section rules already draw in. Footer and drawer links
 * only: body-copy links keep their static underline, because a link inside a sentence that
 * animates on hover pulls the eye off the sentence. */
#colophon .k24-footer-links a,
#mobile-drawer .mobile-navigation a:not(.k24-book){position:relative;text-decoration:none}
#colophon .k24-footer-links a::after,
#mobile-drawer .mobile-navigation a:not(.k24-book)::after{content:"";position:absolute;
  left:0;bottom:-3px;width:100%;height:1px;background:currentColor;
  transform:scaleX(0);transform-origin:left center;
  transition:transform var(--k24-dur-micro) var(--k24-ease-out)}
#colophon .k24-footer-links a:hover::after,
#colophon .k24-footer-links a:focus-visible::after,
#mobile-drawer .mobile-navigation a:not(.k24-book):hover::after,
#mobile-drawer .mobile-navigation a:not(.k24-book):focus-visible::after{transform:scaleX(1)}

/* the side switch is a state change the visitor triggers, so it gets the harder ease */
.k24-sideswitch__opt{transition:background-color var(--k24-dur-micro) var(--k24-ease-expo),
  color var(--k24-dur-micro) var(--k24-ease-expo)}

@media (prefers-reduced-motion:reduce){
  .k24-anim .k24-gearshot img,.k24-anim .k24-figure img,.k24-anim .k24-gate__logo{animation:none}
  #colophon .k24-footer-links a::after,
  #mobile-drawer .mobile-navigation a:not(.k24-book)::after{transition:none;transform:scaleX(1)}
}


/* ---------- the curtain wipe, read off the reference site ----------
 * Their .anim-image is the effect their whole site is built on and the one thing we had not
 * copied. It is two moves at once, and the pairing is the point:
 *
 *   an ::after panel covers the image and slides off in a named direction
 *       transform .9s cubic-bezier(.77,0,.175,1)
 *   the image underneath eases from scale(1.06) to scale(1)
 *       transform 1.2s cubic-bezier(.77,0,.175,1)
 *
 * The panel takes .9s and the settle takes 1.2s, so the picture is still coming to rest after
 * the curtain has gone. That overlap is why it reads as a camera finding focus rather than as
 * a box sliding away, and it is worth keeping exactly.
 *
 * Their panel is white with a --black variant, because a white curtain on a dark ground is a
 * flash. Ours takes the ground it is actually on: paper by default, ink inside a dark band.
 */
.k24-wipe{position:relative;overflow:hidden;isolation:isolate}
.k24-wipe img{display:block;width:100%}
.k24-anim .k24-wipe::after{content:"";position:absolute;inset:0;z-index:2;
  background:var(--k24-paper);
  transition:transform .9s var(--k24-ease-inout);will-change:transform}
.k24-anim .k24-wipe img{transform:scale(1.06);
  transition:transform 1.2s var(--k24-ease-inout);will-change:transform}
.k24-anim .k24-wipe.is-in img{transform:scale(1)}
/* default direction, and the three named ones */
.k24-anim .k24-wipe.is-in::after{transform:translateX(100%)}
.k24-anim .k24-wipe--left.is-in::after{transform:translateX(-100%)}
.k24-anim .k24-wipe--up.is-in::after{transform:translateY(-100%)}
.k24-anim .k24-wipe--down.is-in::after{transform:translateY(100%)}
.k24-section--ink .k24-wipe::after,.k24-band--ink .k24-wipe::after,
.k24-marquee-block .k24-wipe::after{background:var(--k24-ink)}
.k24-section--tint .k24-wipe::after{background:var(--k24-tint)}

/* the float and the wipe both drive transform on the same <img>, so the float waits its turn */
.k24-anim .k24-wipe img{animation:none!important}
.k24-anim .k24-wipe.is-in img{animation:k24-float-a 5s var(--k24-ease-inout) 1.2s infinite!important}

/* ---------- the page curtain ----------
 * Theirs: three fixed panels, each a third of the viewport wide and full height, that drop away
 * on load and close on navigation. transform .85s cubic-bezier(.83,0,.17,1), which is the ease
 * this file already carries as --k24-ease-expo.
 *
 * Used here ONLY for crossing between the music and corporate sides, which is the one navigation
 * on this site that is meant to feel like arriving somewhere else. Using it on every link would
 * put .85s in front of every page and make the site feel slower, not richer.
 *
 * It replaces the .k24-switching overlay, which did the same job by fading a flat panel.
 */
.k24-curtain{position:fixed;inset:0;z-index:9999;pointer-events:none;display:flex}
.k24-curtain__panel{flex:1 1 0;height:100dvh;background:var(--k24-ink);
  transform:translateY(100%);
  transition:transform .85s var(--k24-ease-expo);will-change:transform}
.k24-curtain.is-closing{pointer-events:auto}
.k24-curtain.is-closing .k24-curtain__panel{transform:translateY(0)}
/* a stagger across the three so it reads as a wipe rather than a shutter */
.k24-curtain__panel:nth-child(2){transition-delay:.06s}
.k24-curtain__panel:nth-child(3){transition-delay:.12s}

@media (prefers-reduced-motion:reduce){
  .k24-anim .k24-wipe::after{display:none}
  .k24-anim .k24-wipe img,.k24-anim .k24-wipe.is-in img{transform:none;animation:none!important}
  .k24-curtain__panel{transition:none}
}


/* The .k24-textline rules that used to sit here are gone. They gave four text children of a
 * section fixed 70ms delays, which is not what the reference site does: it has no
 * transition-delay on text at all, and its cascade is emergent from the fact that stacked
 * elements cross the fold at different moments. Each text leaf is its own observer target now,
 * so that cascade happens on its own and is correct at any layout. */

/* ---------- client credits: a logo (or wordmark) and one line of work each ---------- */
/* The OUTER edge is --k24-border, the INNER rules stay --k24-hairline, and the difference is
 * the difference between the two tokens' jobs.
 *
 * Both were hairline, and the gate blocked the deploy on it: 1.29:1 against the white ground
 * where an interface boundary needs 3. The token's own comment says decorative only, which the
 * 1px gaps between cells are, they separate rows within one object. The outer border is not
 * decorative, it is what says where this component ends, so it takes the boundary token and
 * measures 3.38:1. The grid keeps its softer internal lines. */
.k24-credits{display:grid;gap:1px;background:var(--k24-hairline);border:1px solid var(--k24-border);
  border-radius:var(--k24-radius);overflow:hidden;grid-template-columns:1fr}
@media (min-width:900px){.k24-credits{grid-template-columns:1fr 1fr}}
.k24-credit{display:grid;grid-template-columns:112px minmax(0,1fr);gap:var(--k24-s3);align-items:center;
  background:var(--k24-surface-1);padding:var(--k24-s3) var(--k24-s4)}
.k24-credit__mark{display:flex;align-items:center;justify-content:center;height:44px}
.k24-credit__mark img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.k24-credit__body h3{font-size:.95rem;margin:0 0 4px;letter-spacing:-.01em}
.k24-credit__body p{margin:0;font-size:.9rem;line-height:1.5;color:var(--k24-text-muted);max-width:46ch}
.k24-credit__word{font-family:var(--k24-font-display,inherit);font-weight:700;font-size:.95rem;
  letter-spacing:.02em;text-transform:uppercase;color:var(--k24-text-muted);text-align:center;line-height:1.2}
@media (max-width:560px){
  .k24-credit{grid-template-columns:1fr;gap:var(--k24-s2);padding:var(--k24-s3)}
  .k24-credit__mark{justify-content:flex-start;height:36px}
  .k24-credit__mark img{max-width:118px}
  .k24-credit__word{text-align:left}
}


/* ---------- video backgrounds ----------
 * The layer sits exactly where .k24-hero__figure sits and under the same ::after scrim, so the
 * headline's contrast is governed by the scrim it always was and the render gate keeps measuring
 * the thing that is actually behind the type.
 *
 * object-fit:cover on the <video> is what stops a 16:9 clip letterboxing inside a hero that is
 * taller than it, and the poster is given the same fit so the still and the moving frame are
 * cropped identically. Without that the picture jumps at the moment playback starts.
 */
.k24-vbg{position:absolute;inset:0;z-index:0;overflow:hidden;background:var(--k24-ink)}
.k24-vbg__v{width:100%;height:100%;object-fit:cover;display:block;
  object-position:center center}
.k24-hero--full .k24-vbg{z-index:0}
.k24-hero--full .k24-vbg+.k24-hero__inner{position:relative;z-index:2}

/* A section-level band, for the gear and booth clips. Full-bleed like the marquee, with its own
 * scrim because there is no hero scrim out here to borrow. */
/* max-width is 100vw AND overrides core's .alignfull rule, which sets max-width:none. Without
 * the second one a band inside a group block is unbounded rather than viewport-wide. */
.k24-vband.alignfull{max-width:100vw}
/* THE BAND IS TALLER THAN IT NEEDS TO BE FOR THE TEXT, on purpose, twice over.
 *
 * At clamp(320px,46vw,560px) a 1440px-wide viewport got a 560px band. The clip inside is 16:9,
 * so object-fit:cover scales it to 1440x810 and then throws away 250px of it: 31% of every
 * frame was cropped off. On the booth clip that 31% was the wall of comic art and the top of
 * the mic stands, which is most of what makes it read as a booth rather than a window.
 *
 * 56vw capped at 760 shows 94% of the frame at 1440 and still leaves the band shorter than the
 * viewport, so it reads as a band and not a second hero. It also answers the other complaint:
 * a taller band is that much less white page, which is the cheapest way to spend the footage
 * the owner already shot. */
.k24-vband{position:relative;margin-inline:calc(50% - 50vw);width:100vw;max-width:100vw;
  min-height:clamp(420px,56vw,760px);display:flex;align-items:flex-end;overflow:hidden;
  background:var(--k24-ink)}
.k24-vband .k24-vbg{position:absolute;inset:0}
/* TWO gradients, because the copy is in one corner and the picture is the rest of the band.
 *
 * A single top-to-bottom ramp has to darken the WHOLE bottom edge to protect type that only
 * occupies the bottom LEFT, which costs the footage more than it needs to. Measured against the
 * frame with the copy hidden, at four playback positions each, the single ramp put the eyebrow at
 * 3.06:1 on /music/ and 3.58:1 on /corporate/ where it needs 4.5, and the second gear paragraph
 * at 4.42:1. Marginal, but the failures were on the small text, which is exactly where a floor
 * matters, and a pan means the background under a fixed piece of type keeps changing.
 *
 * So: a vertical ramp that holds the picture open through the top two thirds, and a horizontal
 * one that darkens the left edge where the words are and reaches zero before the middle. The
 * right-hand side of every clip, which is where the rack detail and the booth window are, keeps
 * more of its own contrast than it had before. */
.k24-vband::after{content:"";position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(90deg,rgba(24,24,28,.60) 0%,rgba(24,24,28,.42) 30%,rgba(24,24,28,.12) 52%,rgba(24,24,28,0) 66%),
    linear-gradient(180deg,rgba(24,24,28,.34) 0%,rgba(24,24,28,.20) 34%,rgba(24,24,28,.56) 68%,rgba(24,24,28,.88) 100%)}
.k24-vband__inner{position:relative;z-index:2;width:100%;max-width:var(--k24-container);
  margin-inline:auto;padding:clamp(28px,4vw,56px) clamp(24px,4.2vw,72px)}
.k24-vband__inner h2,.k24-vband__inner h3{color:#fff;margin:0 0 var(--k24-s2)}
.k24-vband__inner p{color:var(--k24-on-dark-muted);margin:0;max-width:52ch}
.k24-vband__inner .k24-eyebrow{color:var(--k24-on-dark-muted)}

/* REDUCED MOTION GETS THE POSTER, NOT A FROZEN VIDEO.
 * Hiding the <video> and painting its poster as the layer's background is the only way to honour
 * the preference and still show the picture: pausing it would leave whatever frame it stopped
 * on, which is arbitrary, and hiding it without a replacement leaves a black rectangle. */
@media (prefers-reduced-motion:reduce){
  .k24-vbg__v{display:none}
  .k24-vbg{background-size:cover;background-position:center center}
}

/* ONE CLIP, TWO MOODS.
 *
 * Both heroes now run the same footage of the same room, because there is only one room and
 * pretending otherwise was never the goal: the owner's brief was that switching sides should
 * change the MOOD, not move the desk. The sides are already separated by everything that
 * carries meaning (the marquee's sleeves against brand marks, the portfolio, the services, the
 * copy), so the picture does not have to lie to do its share.
 *
 * What separates them here is the scrim's hue, applied on top of the same neutral gradient
 * rather than replacing it, so the contrast floor the hero was measured at does not move. Music
 * cools and deepens toward the blue the room's own LEDs throw; corporate stays close to neutral
 * and lifts slightly, which is what a daylight room looks like. Both are a few percent of alpha:
 * enough to feel on arrival, not enough to read as a colour cast.
 */
body.k24-side-music .k24-hero--full::after{
  background:linear-gradient(180deg,rgba(22,26,44,.34) 0%,rgba(24,28,46,.54) 48%,rgba(18,20,32,.90) 100%)}
body.k24-side-corporate .k24-hero--full::after{
  background:linear-gradient(180deg,rgba(30,30,34,.20) 0%,rgba(30,30,34,.46) 48%,rgba(26,26,30,.86) 100%)}
@media (max-width:860px){
  body.k24-side-music .k24-hero--full::after{
    background:linear-gradient(180deg,rgba(22,26,44,.42) 0%,rgba(24,28,46,.62) 45%,rgba(18,20,32,.93) 100%)}
  body.k24-side-corporate .k24-hero--full::after{
    background:linear-gradient(180deg,rgba(30,30,34,.30) 0%,rgba(30,30,34,.58) 45%,rgba(26,26,30,.92) 100%)}
}


/* ================= THE SEAM, THE FOLD AND THE TEXTURE FIELDS =========================
 *
 * Measured across 13 pages: 48 section boundaries, of which 19 step by 1.000:1 (the same
 * ground twice), 4 by 1.070:1 and 12 by 1.301:1. Thirty-five of forty-eight cannot be seen
 * at all, which is why the site reads as one continuous sheet of paper rather than as a
 * sequence of sections.
 *
 * The reference site solves this with no rules whatsoever, because its light-to-dark step is
 * 21.00:1 and a hard edge is all it needs. Ours are 1.070 and 1.301, so a hard edge shows
 * nothing. This is a deliberate divergence from the reference, not an oversight: we draw the
 * boundary it does not have to.
 *
 * The seam is 1px of --k24-metal-dark. The FOLD is the part that makes it depth instead of a
 * ruled line: 96px of shadow decaying downward from the seam, which reads as one page laid
 * over another. Nothing here moves, so none of it needs a reduced-motion guard. */
.k24-section,.k24-hero{position:relative}
.k24-section::before,.k24-hero::before{content:"";position:absolute;left:0;right:0;top:0;
  height:1px;background:var(--k24-seam);z-index:2;pointer-events:none}
.k24-section::after{content:"";position:absolute;left:0;right:0;top:1px;height:96px;
  background-image:var(--k24-fold);z-index:1;pointer-events:none}
.k24-section--ink::after,.k24-band--ink::after{background-image:var(--k24-fold-dark)}

/* NO seam where the ground step already does the work, and none at the top of a page. A seam
 * drawn across a 15:1 boundary is a scratch on the join, not a join. */
.k24-section--ink+.k24-section:not(.k24-section--ink)::before,
.k24-section:not(.k24-section--ink)+.k24-section--ink::before,
.k24-vband+.k24-section::before,
.k24-marquee-block+.k24-section::before,
.k24-hero--full+.k24-section::before,
.k24-hero--full::before,
.k24-hero:first-child::before{content:none}
.k24-section--ink+.k24-section:not(.k24-section--ink)::after,
.k24-section:not(.k24-section--ink)+.k24-section--ink::after{content:none}

/* Content clears the fold.
 *
 * .k24-section ONLY, and never .k24-hero. That distinction was learned the hard way: this rule
 * originally read `.k24-section>*,.k24-hero>*` and it broke both photo heroes on sight. The
 * hero's media layers are its DIRECT CHILDREN, and .k24-hero--full .k24-hero__figure is
 * position:absolute;inset:0;z-index:0 at (0,2,0) specificity, exactly the same as this
 * selector. Appended later in the file, this one won on source order, so the figure and the
 * .k24-vbg video layer both flipped to position:relative and dropped into normal flow: the
 * media stopped filling the hero and shoved the headline down the page. The owner's report was
 * "you removed the original hero header video" and "all the text is shifted".
 *
 * The hero does not need this rule anyway. It has no fold (.k24-section::after is not on
 * .k24-hero) and it already manages its own stack: figure 0, scrim 1, inner 2.
 *
 * :not(.k24-tex) is still needed here, because the texture overlay is a direct child of the
 * section too and has to stay behind the content rather than be lifted with it. */
.k24-section>*:not(.k24-tex){position:relative;z-index:3}

/* THE TEXTURE FIELDS.
 *
 * On a ::before-style overlay element rather than on the section's own background-image,
 * because that slot now holds the ground ramp. The mask is what stops it reading as wallpaper:
 * the field is dense in the middle and gone at the edges, so it behaves like a lit surface
 * rather than a repeating tile. The reference site's only pattern is 60 dots on one section of
 * one page, which is the restraint being copied here even though the mechanism is ours. */
.k24-section--tex,.k24-hero--tex{isolation:isolate}
.k24-tex{position:absolute;inset:0;z-index:0;pointer-events:none;background-repeat:repeat;
  -webkit-mask-image:radial-gradient(120% 90% at 50% 30%,#000 0%,#000 42%,transparent 88%);
          mask-image:radial-gradient(120% 90% at 50% 30%,#000 0%,#000 42%,transparent 88%)}
.k24-tex--perf{background-image:var(--k24-tex-perf);background-size:var(--k24-tex-perf-size)}
.k24-tex--grid{background-image:var(--k24-tex-grid);background-size:var(--k24-tex-grid-size)}
.k24-tex--hatch{background-image:var(--k24-tex-hatch);background-size:var(--k24-tex-hatch-size)}
.k24-section--ink .k24-tex--perf,.k24-band--ink .k24-tex--perf{background-image:var(--k24-tex-perf-dark)}
.k24-section--ink .k24-tex--grid,.k24-band--ink .k24-tex--grid{background-image:var(--k24-tex-grid-dark)}
.k24-section--ink .k24-tex--hatch,.k24-band--ink .k24-tex--hatch{background-image:var(--k24-tex-hatch-dark)}


/* ================= EVERY PAGE OPENS ON DARK ==========================================
 *
 * "the white everywhere is hard on the eyes". Measured, the site was 70.4% pale across
 * 116,940px, and on 8 of 13 pages the longest unbroken pale run STARTED AT y=0: the first
 * thing a visitor met was the beginning of the biggest slab of paper on the page. The
 * reference site opens on 1.00 viewport heights of near-black on 6 of its 8 pages, and gives
 * the seventh a black header fill so the rule still holds.
 *
 * Two ways to satisfy that, and the site needs both because it has two kinds of page.
 *
 * THE HEADER IS DARK, ALWAYS. It was --k24-base, the same #F7F7F9 as the page, which is why
 * it had to be told to disappear over the photo heroes and reappear everywhere else, and why
 * the logo had to be swapped between two files to stay legible. One ground means one logo and
 * one nav colour: the rules already scoped to body.k24-has-hero now describe every page.
 *
 * NOT by putting a hero on /gear/. The owner removed that hero deliberately ("go with option
 * 2"), and floating the header over the first section there would put a white mark on tint.
 * A header that paints its own ground needs no hero underneath it to be legible. */
.site-header,#masthead,.site-header-wrap,.site-header-inner-wrap,.site-top-header-wrap,
.site-main-header-wrap,.site-bottom-header-wrap,.site-header-row,.site-header-row-container-inner{
  background:var(--k24-ink)!important}
/* the two photo heroes keep the floating transparent header they already had */
body.k24-has-hero .site-header,body.k24-has-hero #masthead,body.k24-has-hero .site-header-wrap,
body.k24-has-hero .site-header-inner-wrap,body.k24-has-hero .site-top-header-wrap,
body.k24-has-hero .site-main-header-wrap,body.k24-has-hero .site-bottom-header-wrap,
body.k24-has-hero .site-header-row,body.k24-has-hero .site-header-row-container-inner{
  background:transparent!important}

/* THE TEXT HERO IS INK. This is the other half: on the nine pages that open with a
 * .k24-hero rather than a photograph, that hero becomes the dark opening. It is also what
 * makes the ink header sit on something instead of floating above paper. */
.k24-hero:not(.k24-hero--full){background-color:var(--k24-ink);
  background-image:var(--k24-ground-ink);color:var(--k24-on-dark)}
.k24-hero:not(.k24-hero--full) h1,
.k24-hero:not(.k24-hero--full) h2{color:#fff}
.k24-hero:not(.k24-hero--full) p{color:var(--k24-on-dark-muted)}
.k24-hero:not(.k24-hero--full) .k24-hero__kicker{color:var(--k24-on-dark-muted)}
.k24-hero:not(.k24-hero--full) a:not(.k24-btn){color:#fff}
/* the metal rule under the h1 already reads on ink: #B8B8C0 at its lightest stop is 9.6:1 */

/* THE NAV, now that there is only one header ground. These values were already correct and
 * already written; they were just scoped to a class two pages carried. Unscoping them is what
 * removes the ghost-button inconsistency between the two sides (#1C1C21/#86868C on music's
 * paper against #FFFFFF/rgba(255,255,255,.55) on corporate's ink). */
#masthead .k24-sideswitch{background:rgba(28,28,33,.72);border-color:rgba(255,255,255,.34)}


/* ================= THE TWO SIDES ====================================================
 *
 * "music and corporate still need to feel different".
 *
 * The measurement behind this: the entire codebase contained SIX side-scoped hits, four of
 * them CSS declarations, and all four were the background of .k24-hero--full::after (two being
 * the mobile duplicate). data-side has been stamped on <html> since sides.php was written and
 * was used by zero selectors. A computed-style diff across 25 component classes and 9 tags
 * returned 0 property differences on /services/, /gear/ and /contact/, and /contact/ against
 * /corporate/contact/ was 99.4% identical pixels.
 *
 * TWO PROPOSALS WERE REJECTED and it is worth recording why, because both are the obvious ones.
 *
 * A second palette per side: --k24-ink does three jobs (text on light, the ink ground, the
 * footer), so flipping music's paper to near-black moves --k24-ink too, which inverts
 * .k24-section--ink h1{color:#fff} and invalidates every AA annotation in tokens.css. It is the
 * only option that can move a measured contrast floor.
 *
 * A gold accent for music: D28 retired five brand hues after the owner reviewed three complete
 * palettes on rendered pages and chose the neutral system. #B98A2E measures ~3.6:1 on paper,
 * fine as a graphic and a failure the moment anyone widens it to a colour. The 24K signal is
 * the metal, which is where tokens.css deliberately put it.
 *
 * What is left is mood: ground budget, texture, shape and pace. Everything below is ADDITIVE.
 * html[data-side] outranks bare :root and body.k24-side-* outranks a bare class, so nothing
 * existing is edited and no !important is introduced. And none of it duplicates content or
 * implies two rooms: the same section at the same URL simply sits on a different ground
 * depending which door the visitor came through. */

/* ---- S-1. THE GROUND BUDGET. One class in the content, two meanings.
 * Authored, not nth-of-type: section counts differ per page (2 on /portfolio/, 4 on /gear/,
 * 6 on /corporate/), so a positional rule produces a different rhythm on every page and on
 * some of them lands an ink band directly above the ink CTA and the ink footer.
 * Precedent, measured: the reference runs the SAME components at 18% dark on / and 46% dark
 * on /work/. Our own homepages already do a weaker version of this by accident, music 67.1%
 * dark against corporate 47.5%. This makes it deliberate and extends it to the eight shared
 * pages that had no side styling at all. */
body.k24-side-music .k24-section--alt{background-color:var(--k24-ink);
  background-image:var(--k24-ground-ink);color:var(--k24-on-dark)}
body.k24-side-music .k24-section--alt h1,body.k24-side-music .k24-section--alt h2,
body.k24-side-music .k24-section--alt h3{color:#fff}
body.k24-side-music .k24-section--alt p,body.k24-side-music .k24-section--alt li,
body.k24-side-music .k24-section--alt .k24-lede{color:var(--k24-on-dark-muted)}
body.k24-side-music .k24-section--alt a:not(.k24-btn){color:#fff}
body.k24-side-music .k24-section--alt::after{background-image:var(--k24-fold-dark)}
body.k24-side-corporate .k24-section--alt{background-color:var(--k24-tint);
  background-image:var(--k24-ground-tint)}

/* ---- S-2. THE SURFACE. Music gets a perforation field (a grille, a panel, a screen).
 * Corporate gets a drafting grid (a plan, a spec, a schedule). Neither says anything about
 * the room, which is the constraint: there is one room and the imagery must not imply two. */
body.k24-side-music     .k24-tex{background-image:var(--k24-tex-perf);
  background-size:var(--k24-tex-perf-size)}
body.k24-side-corporate .k24-tex{background-image:var(--k24-tex-grid);
  background-size:var(--k24-tex-grid-size)}
body.k24-side-music     .k24-section--ink .k24-tex,
body.k24-side-music     .k24-section--alt .k24-tex{background-image:var(--k24-tex-perf-dark)}
body.k24-side-corporate .k24-section--ink .k24-tex{background-image:var(--k24-tex-grid-dark)}
/* an explicit .k24-tex--* modifier still wins where a page wants a specific field */
body.k24-side-music .k24-tex--hatch,body.k24-side-corporate .k24-tex--hatch{
  background-image:var(--k24-tex-hatch);background-size:var(--k24-tex-hatch-size)}
body.k24-side-music .k24-section--ink .k24-tex--hatch,
body.k24-side-music .k24-section--alt .k24-tex--hatch,
body.k24-side-corporate .k24-section--ink .k24-tex--hatch{
  background-image:var(--k24-tex-hatch-dark)}

/* ---- S-3. SHAPE. Square and hard-edged against round and lifted.
 * --k24-metal-dark on the music card outline and NOT --k24-border, because a card on tint
 * needs 3:1 and #86868C measures 2.60:1 there against #6E6E76's 3.63:1. That is the exact
 * failure the render gate already caught once on .k24-brands__item.
 * The focus ring is unaffected: tokens.css hard-codes border-radius:4px on :focus-visible,
 * so setting --k24-radius:0 does not flatten the focus indicator. */
/* SHAPE IS RADIUS ONLY. The first version of this also outlined every card, one side with an
 * inset hairline and the other with the plate shadow, and the owner's answer was immediate:
 * "why are you putting a box around everything i dont like these boxes youre putting around
 * the text it looked better before". They were right, and the CSS said so already: .k24-icon-card
 * is declared `background:none;border:0;border-radius:0;box-shadow:none`, which is a deliberate
 * no-box design, and .k24-svc carries a single border-top rule rather than a frame. Both were
 * being given an outline they had been explicitly designed without.
 *
 * The radius tokens stay, because they change the shape of the things that ALREADY have edges,
 * the media frames, the buttons, the plate and the slab, without drawing any new ones. */
html[data-side="music"]{--k24-radius:0px;--k24-radius-btn:0px}
html[data-side="corporate"]{--k24-radius:12px;--k24-radius-btn:4px}

/* ---- S-4. VOICE AND PACE. Music is shouted and arrives quickly; corporate is spoken and
 * arrives slowly, on a shorter measure so its prose reads as a document.
 * Tracking on headings stays at 0 on both: tokens.css deliberately neutralised it ("tight
 * tracking is a fashion-brand signal, theirs is a studio one") and that is not being reversed.
 * Case and the eyebrow are what change.
 * No new font file: assets/fonts/ ships exactly three faces and there is no heavier display
 * weight on disk, so reaching for one would be a licence purchase, not a CSS change.
 * Reduced motion needs no guard here: tokens.css's global reduce block already forces every
 * transition-duration and animation-duration to .01ms, and these two tokens feed those. */
body.k24-side-music .k24-section h2,body.k24-side-music .k24-band h2{text-transform:uppercase}
body.k24-side-music .k24-eyebrow{letter-spacing:.24em}
html[data-side="music"]{--k24-dur-reveal:.45s;--k24-reveal-rise:5rem;--k24-dur-slide:.8s}
body.k24-side-corporate .k24-section h2,body.k24-side-corporate .k24-band h2{text-transform:none}
body.k24-side-corporate .k24-eyebrow{letter-spacing:.08em}
html[data-side="corporate"]{--k24-dur-reveal:1.0s;--k24-reveal-rise:2rem;--k24-dur-slide:1.5s;
  --k24-measure:58ch}

/* NOT DOING: a per-side CSS filter on .k24-vbg video. The frame cost of filtering a full-bleed
 * 1920 video was never measured, and the alternative (two graded masters) doubles a payload
 * that is already 19.4 MB. The per-side hero scrim hue above is enough. If the poster's grade
 * should carry into playback later, encode two masters rather than filter at runtime. */


/* ================= DEPTH OBJECTS ====================================================
 *
 * The owner asked for "3d elements". State plainly what the reference site actually does,
 * because it is not what the phrase suggests: across 6574 lines of CSS it has perspective 0,
 * transform-style:preserve-3d 0, rotate3d 0, rotateX/rotateY 0, matrix3d 0, translateZ 0. All
 * 58 of its translate3d calls have a Z of exactly 0 and are GPU-compositing hints. It has zero
 * scroll listeners and a measured element-movement-to-scroll ratio of 0.00.
 *
 * Its entire dimensional vocabulary is three box-shadows and one backdrop-filter. So these
 * three are its devices done properly, rather than an invented 3D that would make this site
 * look LESS like the one the owner pointed at, not more. Perspective, parallax and canvas
 * particles are all deliberately not here. */

/* ---- D3-1. THE LIFTED PLATE. The reference's .why-block-item verbatim: white cards on a
 * white ground separated by nothing but a tight 4/8 contact shadow and a soft 14/14 ambient
 * one. That is its actual answer to "the white everywhere is hard on the eyes", and it is not
 * a gradient. The shadow does not animate on hover; the reference never animates a shadow. */
.k24-plate{position:relative;background:var(--k24-white);border-radius:var(--k24-radius);
  padding:var(--k24-s4);box-shadow:var(--k24-shadow-plate)}
/* on an ink ground a shadow is ink on ink, so the plate lifts by VALUE instead */
.k24-section--ink .k24-plate,body.k24-side-music .k24-section--alt .k24-plate{
  background:var(--k24-ink-2);box-shadow:none;border:1px solid var(--k24-metal-dark)}

/* ---- D3-2. THE INSET INK SLAB. The reference's .black_section-block: a near-black bar INSIDE
 * the container, so the page's own margins frame it left and right. Depth by layering, an
 * object sitting on the page rather than a band cutting through it, and the cheapest way to
 * break a pale run without spending a full-bleed band on it. */
.k24-slab{position:relative;background-color:var(--k24-ink);background-image:var(--k24-ground-ink);
  color:var(--k24-on-dark);border-radius:var(--k24-radius);
  padding:var(--k24-s4) clamp(24px,4.4vw,70px);
  display:flex;align-items:center;justify-content:space-between;gap:var(--k24-s4);flex-wrap:wrap;
  box-shadow:0 18px 40px -22px rgba(28,28,33,.55)}
.k24-slab h2,.k24-slab h3{color:#fff;margin:0;max-width:34ch}
/* no metal rule here: it would be a third mark inside one object */
.k24-slab h2::after,.k24-slab h3::after{content:none}
.k24-slab p{color:var(--k24-on-dark-muted);margin:0;max-width:52ch}
.k24-slab__actions{display:flex;gap:12px;flex-wrap:wrap;flex:0 0 auto}
@media (max-width:760px){.k24-slab{flex-direction:column;align-items:flex-start}}

/* ---- D3-3. GLASS TILES ON THE FOOTAGE. The reference's one backdrop-filter, over its video
 * hero. THEIR 10% WHITE FILL IS NOT SAFE HERE AND IS NOT COPIED: theirs sits on pure black
 * video, ours on footage whose bright frames are unknown at build time. rgba(19,19,23,.72)
 * composited over a hypothetical pure-white frame gives #555558, on which #FFFFFF measures
 * 7.43:1. Every real frame is darker than white, so 7.43:1 is a floor, not an estimate.
 * Do NOT put --k24-on-dark-muted inside a tile: at .72 over white it lands on 4.50:1 exactly,
 * with no margin. White at opacity .86 stays above 6:1. */
.k24-glasstiles{position:relative;z-index:2;display:grid;gap:1px;
  grid-template-columns:repeat(auto-fit,minmax(200px,1fr));margin-top:var(--k24-s4);
  max-width:760px}
.k24-glasstile{padding:var(--k24-s3);color:#FFFFFF;background:rgba(19,19,23,.72);
  -webkit-backdrop-filter:blur(10px) saturate(1.08);backdrop-filter:blur(10px) saturate(1.08);
  border:1px solid rgba(233,231,239,.62);margin:-1px 0 0 -1px;
  transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-glasstile__k{display:block;font-size:var(--k24-label);letter-spacing:.14em;
  text-transform:uppercase;color:#FFFFFF;opacity:.86;margin:0 0 6px}
.k24-glasstile__v{font-family:var(--k24-font-display);font-size:var(--k24-h3);margin:0;color:#FFFFFF}
.k24-glasstile:hover,.k24-glasstile:focus-within{background:var(--k24-white);color:var(--k24-ink)}
.k24-glasstile:hover .k24-glasstile__k,.k24-glasstile:hover .k24-glasstile__v{color:var(--k24-ink)}
/* no blur available: buy the same floor back with opacity rather than dropping the tile */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .k24-glasstile{background:rgba(19,19,23,.88)}
}


/* ================= THE GEAR PAGE ====================================================
 *
 * "fix the layout of the gear page", "the gear list needs work".
 *
 * THE SINGLE CAUSE, measured. Six cards holding 2/3/2/7/6/1 list items were forced into two
 * fixed 3-across wp-block-columns rows whose align-items computes to stretch. Three of the six
 * carry a 960x720 photograph stacked IN SERIES above the list, adding 377-397px to a column
 * whose text-only neighbour holds 54px of list.
 *
 * The damage at 1440: 437/0/437px of dead column height in row 0 and 0/27/591px in row 1.
 * 1,492px, 37.2% of the two rows' combined area. At 390 it is 0px on every column, which is
 * the proof that the cause is the equal-height 3-across row and not the content.
 *
 * The worse symptom is the divergence: list tops 410px apart in row 0 and 430px apart in row 1,
 * so "DAWs / Pro Tools 2024.10" floated alone 430px above its neighbours with 591px of nothing
 * beneath it. And .k24-card was inert here anyway: computed on all six columns at three widths
 * it was background:transparent, border:0, radius:0, shadow:none. The holes had no edges, so
 * they read as holes in the page rather than as empty cards.
 *
 * THE FIX IS ARCHETYPE, NOT SPAN. A photo card puts its media BESIDE its list, so its height is
 * max(media, list) instead of media + list. That is what removes the 437 and 591px holes: the
 * tall thing and the short thing stop being stacked in series. */
/* THE GEAR ROSTER IS A TILE GRID, and that is what fixes the alignment.
 *
 * Owner: "if you look at signal chain and hardware preamps interface is microphones, those
 * sections are all scattered, it's like the text is all scattered randomly... everything's out
 * of place because of how the photos are shifting things over." And, separately: "I really like
 * what you did with the videos and how it says front end colour conversion and how it's nicely
 * putting like a box... I would like to take the same approach for the other sections."
 *
 * Both notes have one answer. Measured at 1440, the six headings started at FOUR different x
 * positions: 24 for the plain cards, 401 for the three photo cards (pushed right by a 340px
 * media column that was a sibling of the heading), and 742 for the second card in a 2-up row.
 * Nothing was aligned to anything.
 *
 * Now every category is full width, its heading sits at the section's own content edge, and its
 * roster is one uniform grid. The photograph is not a sibling of the text any more, it is a
 * TILE in that same grid spanning two columns and two rows, so it cannot push anything. Empty
 * cells are simply not drawn, which is what lets a two-item category and a seven-item category
 * use the same grid without either looking short.
 *
 * The tiles collapse their borders the way .k24-brands does: 1px each with a -1px margin, so
 * shared edges are one line and not two. */
.k24-gear{display:flex;flex-direction:column;gap:clamp(34px,4vw,60px);margin-top:var(--k24-s5)}
.k24-gearcard{min-width:0;background:none;border:0;padding:0}
.k24-gearcard h3{margin:0 0 var(--k24-s3)}
.k24-gearcard h3::after{content:"";display:block;width:88px;height:2px;margin-top:10px;
  background-image:var(--k24-rule)}

.k24-gearset{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  margin:0;padding:0;list-style:none}
/* A gear tile can carry a short note under its name. Used on FIVE of the twenty-one items and
 * no more: a note on every tile is a wall of captions, and the ones that earn it are the pieces
 * a visiting engineer actually asks about. The note never makes a claim the room cannot back , 
 * it says what the piece DOES here, not how good it is. */
.k24-gearitem__n{display:block}
.k24-gearitem__w{display:block;margin-top:6px;font-family:var(--body,inherit);
  font-size:.8125rem;line-height:1.4;letter-spacing:0;color:var(--k24-muted);
  font-weight:400;text-transform:none}
.k24-section--ink .k24-gearitem__w{color:var(--k24-on-dark-muted)}
.k24-gearitem:hover .k24-gearitem__w{color:inherit;opacity:.72}
/* the one-line framing under a category heading */
.k24-gearcard__lede{margin:calc(var(--k24-s3) * -1) 0 var(--k24-s4);max-width:62ch;
  color:var(--k24-muted);font-size:.9375rem;line-height:1.5}
.k24-section--ink .k24-gearcard__lede{color:var(--k24-on-dark-muted)}

.k24-gearitem{display:flex;flex-direction:column;justify-content:flex-end;min-height:118px;padding:var(--k24-s3);
  border:1px solid var(--k24-metal-dark);margin:-1px 0 0 -1px;max-width:none;
  background:var(--k24-white);color:var(--k24-ink);
  font-family:var(--k24-font-display);font-size:1.0625rem;line-height:1.25;
  letter-spacing:-.01em;transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-gearitem:hover{background:var(--k24-ink);color:#fff}
/* the photograph, as a tile in the same grid */
.k24-gearitem--photo{grid-column:span 2;grid-row:span 2;padding:0;position:relative;
  overflow:hidden;background:var(--k24-ink)}
.k24-gearitem--photo:hover{background:var(--k24-ink)}
/* contain, not cover. The owner's requirement on these is that no piece of gear is cut off,
 * and `cover` cannot honour that: it fills the tile by cropping whichever axis is proportionally
 * longer, so the moment a photograph's ratio differs from the tile's it takes a slice off a
 * faceplate. `contain` fits the whole frame and letterboxes on the tile's own ink, which on a
 * dark ground reads as the photograph having an edge rather than as empty space. */
.k24-gearitem--photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  display:block}
.k24-gearcard__cap{font-size:var(--k24-label);color:var(--k24-muted);letter-spacing:.06em;
  text-transform:uppercase;margin:var(--k24-s3) 0 0}
/* on the ink section the tiles invert, same as the plugin cells keep their own ground */
.k24-section--ink .k24-gearitem{background:var(--k24-ink-2);color:var(--k24-on-dark);
  border-color:var(--k24-border)}
.k24-section--ink .k24-gearitem:hover{background:var(--k24-white);color:var(--k24-ink)}

@media (max-width:560px){
  .k24-gearset{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
  .k24-gearitem{min-height:96px;font-size:1rem}
  .k24-gearitem--photo{grid-column:span 2;grid-row:span 1;aspect-ratio:16/9}
}

/* Rhythm. The bands are section SIBLINGS now rather than interruptions inside one section, so
 * each section's own padding sets both gaps symmetrically. Before: 48px above a band and 0px
 * below it with only 24px of card padding before the next heading, so every band read as
 * belonging to the row underneath it. And those 48px were measured from the TALLEST column;
 * from the visible bottom of the short ones it was 485px and 639px. */
.k24-vband{margin-block:0}
.k24-section+.k24-vband,.k24-vband+.k24-section{margin-top:0}

/* Two live defects on the same page.
 * The plugin grid was inset 33.44px on its LEFT ONLY, so its edge sat at x=56 while the h2,
 * the intro, every card rule and the section edge sat at x=24. The winner was Kadence's
 * `.single-content ul,.single-content ol{padding-left:2em}` at (0,1,1); this is (0,2,0). */
.k24-section .k24-brands{padding-left:0}
/* THE PLUGIN CELLS STAY WHITE ON THE INK GROUND, and the version of this that did not was a
 * repeat of a bug this project has already paid for twice.
 *
 * Moving the roster onto ink, I first repainted the cells ink-2 and knocked every mark to white
 * with brightness(0) invert(1). That filter repaints every opaque pixel white and keeps only
 * the alpha channel, so it works when a mark's internal detail is TRANSPARENCY and destroys it
 * when the detail is COLOUR. It is exactly what turned six of thirteen marquee logos into white
 * slabs, and here it blanked a dozen plugin marks into plain white squares and circles.
 *
 * It also threw away the per-mark treatment in assets/logos/plugins/treatment.json, which was
 * measured by ops/plugin-logo-treatment.py precisely so each mark gets the one thing it can
 * survive: 16 of the 47 are flipped, the other 31 are left alone.
 *
 * White cells on a dark ground keep every one of those decisions valid and read as a contact
 * sheet of marks on a mat, which is what the reference site does with its own gallery. The
 * section still counts as ink for the light-share budget: the cells are 47 small tiles with a
 * 1px gutter, not a field. */
.k24-section--ink .k24-brands__item{background:var(--k24-white);color:var(--k24-ink);
  border-color:var(--k24-border)}
/* --k24-border here and --k24-metal-dark on the light grounds, which is the reverse of the usual
 * pairing and is right for the reverse reason. This grid draws its rules by COLLAPSING CELL
 * BORDERS, so on ink each line has white on both sides of it in the middle of the grid and the
 * ink ground on the outside of it at the edge. #6E6E76 measured 2.66:1 against the ink ramp's
 * lightest stop #2E2E36 and the gate blocked on it. #86868C is 3.62:1 on the white cell it
 * divides and 3.64:1 on that same ink stop, so it clears against both of its neighbours, which
 * is what a rule between two different grounds actually has to do. */


/* THE BOOKING SWITCH IS NOT RESTYLED FOR INK, and the version that was is deleted.
 *
 * I inverted it on the assumption that it sat on the section's ground. It does not: measured,
 * .k24-booking-switch is INSIDE .k24-book-embed, which is the white plate. So the inverted rules
 * put a #2E2E36 chip carrying #1C1C21 text on a white field (1.26:1) and gave the active option
 * a white border on a white plate (1:1). Both were worse than the failure they were written to
 * fix, and that original failure is gone anyway now that the plate paints itself white: the
 * switch is on white, which is the ground it was designed for.
 *
 * The lesson is the one this file keeps recording: check where an element actually sits before
 * styling it for where its section sits. */

/* ---- THE BOOKING EMBED IS ALWAYS A WHITE PLATE WITH DARK TEXT, whatever ground it sits on.
 *
 * Putting the embed section on the alternate ground made it ink on the music side, and
 * .k24-section--ink sets color:var(--k24-on-dark) on everything inside it. The WooCommerce
 * Bookings form keeps its own white background, so eight strings inherited a light colour onto
 * a white field: "America/Toronto" at 1.65:1, "50% today" at 1.41:1, the visually-hidden
 * "Choose a time" at 1.23:1. All eight were invisible on the page that takes money, and the
 * gate only reported them once it stopped treating the textured section as a photograph.
 *
 * The alternative was to restyle the whole Bookings form dark. That is a large third-party
 * surface with a date picker, a time grid and a price block, on the one page where a rendering
 * mistake costs a booking. A white plate on a dark ground is the safer answer and it is also
 * the better one: it makes the form the lit object on the page. */
.k24-section--ink .k24-book-embed.k24-plate,
body.k24-side-music .k24-section--alt .k24-book-embed.k24-plate{
  background:var(--k24-white);color:var(--k24-text);box-shadow:var(--k24-shadow-plate);border:0}
/* The overrides are TARGETED and their specificity is deliberate.
 *
 * The first attempt reset every span, div, strong, td and th inside the plate to --k24-text.
 * That is a blanket, and a blanket overrides components that legitimately set their own colour:
 * it repainted the ACTIVE switch option's label dark on its dark fill, 1:1, which is a worse
 * failure than the one it fixed. What has to be beaten is only the handful of descendant rules
 * the ink ground sets, so only those are named.
 *
 * The `.k24-section` prefix is load-bearing. `body.k24-side-music .k24-section--alt p` is
 * (0,2,2); `.k24-book-embed.k24-plate p` is (0,2,1) and loses. With the prefix it is (0,3,1)
 * and wins outright rather than by source order. */
.k24-section .k24-book-embed.k24-plate h1,.k24-section .k24-book-embed.k24-plate h2,
.k24-section .k24-book-embed.k24-plate h3{color:var(--k24-ink)}
.k24-section .k24-book-embed.k24-plate p,.k24-section .k24-book-embed.k24-plate li,
.k24-section .k24-book-embed.k24-plate .k24-lede{color:var(--k24-text)}
/* :not(.k24-booking-switch__opt) because that option IS an <a>. Without the exclusion this
 * (0,3,1) rule beat .k24-booking-switch__opt.is-active at (0,2,0) and painted the selected
 * chip's label #1C1C21 on its own #1C1C21 fill: 1:1, on the control that chooses which service
 * you are about to pay for. This is the third time in this file that a rule written for prose
 * has repainted a component that happens to use the same element. */
.k24-section .k24-book-embed.k24-plate a:not(.k24-btn):not(.k24-booking-switch__opt){
  color:var(--k24-ink)}

/* The WooCommerce breadcrumb inside the embed reads "Home / Shop / Uncategorized / Studio Time:
 * Music Recording". Uncategorized is the default term both booking products still sit in, so
 * the trail advertises a taxonomy accident on the checkout path; the old Wix flow showed
 * "Home > Services". Hidden rather than recategorised because the trail adds nothing here at
 * all: this is a landing page, not a shop crumb trail, and the nav above it already says where
 * the visitor is. Recategorising the products is still worth doing for the shop archive. */
.k24-book-embed .woocommerce-breadcrumb,.k24-book-embed .kadence-breadcrumbs{display:none}


/* THE BOOKING SWITCH OWNS ITS OWN COLOURS, stated once at a specificity nothing around it beats.
 *
 * Both options are <a> elements, and every ground on this site sets a colour on descendant
 * links: .k24-section--ink a:not(.k24-btn){color:#fff}, the same for --alt on the music side,
 * and the plate's own reset. So the chip kept being repainted by whichever ground it happened
 * to sit inside, once to dark-on-dark and once to white-on-white, and each fix moved the failure
 * rather than ending it.
 *
 * Two rules at (0,3,0) and (0,4,0) settle it: the component states what it is, and no ground
 * rule reaches it. Contrast: --k24-text on --k24-surface-1 is 16.97:1, and inverted on the
 * active chip it is the same pair the other way up. */
/* FOUR classes, not three, and the reason is a specificity miscount worth writing down:
 * `:not(.k24-btn)` contributes its ARGUMENT's specificity, so
 * `body.k24-side-music .k24-section--alt a:not(.k24-btn)` is (0,3,2), not (0,2,2). A three-class
 * selector at (0,3,0) loses to it, which is why the first attempt matched, sat last in the
 * cascade, and still lost. Including the wrapper puts these at (0,4,0) and (0,5,0). */
.k24-section .k24-book-embed .k24-booking-switch .k24-booking-switch__opt{color:var(--k24-text)}
.k24-section .k24-book-embed .k24-booking-switch .k24-booking-switch__opt.is-active{
  color:var(--k24-surface-1)}
.k24-section .k24-book-embed .k24-booking-switch .k24-booking-switch__opt .k24-booking-switch__note{
  color:var(--k24-text-muted)}
.k24-section .k24-book-embed .k24-booking-switch .k24-booking-switch__opt.is-active .k24-booking-switch__note{
  color:var(--k24-on-dark)}


/* The CTA band is the top half of the ramp that finishes the page. It ends where the footer
 * begins (#26262D) so the two read as one object rather than as a band and then a footer.
 * It overrides .k24-section--ink:last-of-type, which would otherwise give it the generic
 * ink-up ramp ending on #2E2E36 and reintroduce the step. */
.k24-section--ink.k24-cta-band,.k24-cta-band{
  background-image:var(--k24-ground-cta)}
/* and no seam or fold across that join either */
.k24-cta-band::before,.k24-cta-band::after{content:none}

/* ---- THE CARD RULE ABOVE Recording / Mixing / Mastering IS GONE.
 *
 * Owner: "let's remove the Little metallic lines that are over recording mixing and mastering...
 * But you can keep the one that's under from the first take to the master."
 *
 * That is a distinction worth keeping straight, because they are two different marks doing two
 * different jobs. .k24-section h2::after is the SECTION's rule, one per section, and it marks
 * rank: it stays. .k24-card::before was the same material repeated once per card, so a section
 * with three cards drew the mark four times and it stopped meaning anything.
 *
 * .k24-icon-card and .k24-quote keep theirs: they are not stacked three-across under a ruled
 * heading, so the repetition that made this read as noise does not arise. */
.k24-card::before{content:none}


/* ---- THE CLIENT WALL'S ODD LAST ROW.
 *
 * Owner: "the left side has six companies you work for and the right side has five, it's
 * missing one, there's like an empty blank. That doesn't look good."
 *
 * Correct: eleven credits in a two-column grid leaves one cell empty. The right answer is a
 * twelfth real credit and I cannot write one, because a credit is a claim about work actually
 * done for a named client and inventing that is the one thing this file must never do.
 *
 * So the blank is removed structurally instead: an odd last child spans both columns. The row
 * then reads as the end of a list rather than as a missing tile, and the moment a real twelfth
 * credit is added this rule stops applying on its own. */
@media (min-width:900px){
  .k24-credits .k24-credit:last-child:nth-child(odd){grid-column:1 / -1}
}

/* ---- THE CLIENT ROSTER: bigger marks, on charcoal, with some life in them.
 *
 * Owner: "we can make the company logos a bit bigger. Audible, we can definitely make a bit
 * bigger. Rogers, we can definitely make a bit bigger. And I think we can make the pattern
 * background the same charcoal colour black as the hero header, and then you can find something
 * creative to do with the company logos, maybe give them like some slight animation."
 *
 * Measured at 1440 before this: every mark was capped at 40px tall, so the square ones (Audible
 * and Apple at 40x40, Arsenal 34x40) were the smallest objects in a 150px tile while the wide
 * wordmarks ran to 162px. Raising the height cap is what actually helps the square marks;
 * raising max-width is what helps Rogers, which was width-limited at 80px.
 *
 * The section becomes ink so it matches the hero, and the tiles lift to --k24-ink-2 so they do
 * not vanish into it: white on #2E2E36 is 13.46:1 and --k24-on-dark-muted is 8.16:1.
 *
 * The movement is deliberately small. Fifteen tiles drifting hard would be a novelty; 3px over
 * eight to eleven seconds, staggered so no two are in phase, reads as a wall that is alive
 * rather than as an animation. Hover is the interactive part: the tile lifts, its edge lights
 * up and the mark grows a little. The global prefers-reduced-motion block already collapses
 * every duration in this file to .01ms, so the drift stops for anyone who asked it to. */
.k24-section--ink .k24-roster__item,
body.k24-side-corporate .k24-section--alt .k24-roster__item{
  background:var(--k24-ink-2);border:1px solid var(--k24-metal-dark)}
.k24-roster__item img{height:clamp(34px,3.6vw,52px);max-width:82%}
.k24-roster__item{transition:transform var(--k24-dur-micro),border-color var(--k24-dur-micro),
  background-color var(--k24-dur-micro);will-change:transform}
.k24-roster__item img{transition:transform var(--k24-dur-micro)}
.k24-roster__item:hover{transform:translateY(-4px);border-color:var(--k24-on-dark-muted);
  background:var(--k24-ink)}
.k24-roster__item:hover img{transform:scale(1.06)}

@keyframes k24-roster-drift{0%,100%{transform:translate3d(0,0,0)}50%{transform:translate3d(0,-3px,0)}}
.k24-anim .k24-roster__item{animation:k24-roster-drift 9s ease-in-out infinite}
.k24-anim .k24-roster__item:nth-child(3n+1){animation-duration:11s;animation-delay:-2.5s}
.k24-anim .k24-roster__item:nth-child(3n+2){animation-duration:8s;animation-delay:-5s}
.k24-anim .k24-roster__item:nth-child(5n){animation-duration:10.5s;animation-delay:-1.2s}
/* hover must win over the drift, or the lift is fought by the keyframe every cycle */
.k24-anim .k24-roster__item:hover{animation-play-state:paused;transform:translateY(-4px)}

/* ================= THE GEAR STAGES ==================================================
 *
 * Owner, on /gear/: "I really like the video that's on the preamp section... and the video
 * that's on the outward rack section. What I was hoping we can do is we can just have those
 * two videos fill up the complete entirety of the background of the page, because I don't
 * like how the images for monitoring, for analog processing, and for preamps is coming out.
 * Looks really bad... the videos, only, as the only media assets. And then we have all of the
 * cards under that... the videos would replace that background gray dotted texture, or you can
 * find the cool way to incorporate both. But I want this page to be more charcoal instead of
 * the off white gray."
 *
 * WHY A LOCKED BACKDROP AND NOT A TALL ONE. The obvious build is one <video> stretched over
 * the whole section at object-fit:cover, and it destroys the footage. A 16:9 clip covering a
 * 1440-wide section that is 2100px tall is scaled to 3733x2100 and then cropped to 1440: 61%
 * of every frame is thrown away, horizontally, which on a rack clip is most of the rack. The
 * taller the section the worse it gets, and these sections are tall because they hold the
 * whole roster.
 *
 * So the clip is pinned to the viewport instead. .k24-stage__lock is position:sticky at
 * top:0, height 100vh, inside an absolutely positioned layer that spans the section. The
 * frame therefore always renders at viewport proportions, cropping about as much as the hero
 * does, and the cards scroll over it. Crossing from one stage into the next swaps the rack
 * behind the page in one movement, which is the whole reason to do it this way.
 *
 * Three things make the sticky actually stick, and each of them is a way it silently fails:
 *   - .k24-stage__bg must NOT have overflow:hidden. An overflow container becomes the sticky
 *     element's scroll context, and a container that does not scroll pins it in place at the
 *     top and it never moves.
 *   - .k24-stage must NOT carry .k24-section. That class's own rule,
 *     `.k24-section>*:not(.k24-tex){position:relative;z-index:3}`, would flip the backdrop
 *     layer to position:relative and drop it into normal flow. That is the same rule that
 *     broke both photo heroes once already.
 *   - the section needs real height for the sticky to travel through, which the roster gives
 *     it. A short stage would simply behave like a fixed background, which is also fine.
 *
 * CONTRAST OVER FOOTAGE IS BOUGHT WITH A FLOOR, NOT A POSITION. On the vband the copy lives
 * in one corner, so the scrim can be a ramp that protects that corner and leaves the rest of
 * the picture alone. Here the copy moves THROUGH the scrim as the page scrolls, so a ramp
 * protects nothing: a tile that is comfortable at the bottom of the screen is on open footage
 * a second later. Every stop therefore holds a minimum alpha of .68, and the gradient only
 * deepens the top and bottom edges from there.
 *
 * .68 of #131317 over a hypothetical pure-white frame composites to #5E5E62, on which #FFFFFF
 * measures 6.43:1 and the tiles' own rgba(19,19,23,.72) composites again to #282830, where
 * white is 14.7:1 and --k24-on-dark-muted is 8.9:1. Every real frame in these two clips is
 * darker than white, so those are floors rather than estimates, and ops/gear-stage-contrast.py
 * measures them for real by forcing the backdrop to solid white and sampling the composited
 * pixel under every piece of type.
 *
 * The render gate cannot see any of this: text over a <video> rect is filed as unmeasurable,
 * correctly, which is why the floor is proved separately rather than asserted here.
 */
.k24-stage{position:relative;margin-inline:calc(50% - 50vw);width:100vw;max-width:100vw;
  background:var(--k24-ink-deep);color:var(--k24-on-dark);isolation:isolate}
.k24-stage.alignfull{max-width:100vw}
.k24-stage__bg{position:absolute;inset:0;z-index:0}
.k24-stage__lock{position:sticky;top:0;height:100vh;overflow:hidden}
@supports (height:100lvh){.k24-stage__lock{height:100lvh}}
.k24-stage__lock .k24-vbg{position:absolute;inset:0}
.k24-stage__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;display:block;
  background:linear-gradient(180deg,
    rgba(19,19,23,.90) 0%,
    rgba(19,19,23,.70) 20%,
    rgba(19,19,23,.68) 56%,
    rgba(19,19,23,.93) 100%)}
.k24-stage__inner{position:relative;z-index:2;
  padding-block:clamp(72px,9vw,140px);
  padding-inline:max(var(--k24-s3),calc((100vw - var(--scrollbar-offset,0px) - var(--k24-container)) / 2))}
.k24-stage__inner>*{max-width:var(--k24-container)}
.k24-stage__inner h2,.k24-stage__inner h3{color:#FFFFFF}
.k24-stage .k24-eyebrow{color:rgba(255,255,255,.85)}
/* the one paragraph under a stage heading. White at .88 rather than --k24-on-dark-muted,
 * which at this scrim's floor lands on 3.2:1 and fails. */
.k24-stage__inner>p{color:rgba(255,255,255,.88);font-size:var(--k24-body);
  max-width:62ch;margin:0 0 var(--k24-s2)}

/* Cards breathe further apart here than on a flat ground, because the gap is where the
 * footage is actually visible. On paper the gap is empty; here it is the picture. */
.k24-stage .k24-gear{gap:clamp(44px,5.5vw,84px)}
.k24-stage .k24-gearcard__lede{color:rgba(255,255,255,.88)}

/* THE ROSTER TILES BECOME GLASS, which is what lets the footage read through the cards
 * instead of being buried under them. Same construction as .k24-glasstile on the old bands,
 * so the page has one treatment for "a fact stated over moving picture" and not two. */
.k24-stage .k24-gearitem{background:rgba(19,19,23,.72);color:var(--k24-on-dark);
  border-color:rgba(233,231,239,.28);
  -webkit-backdrop-filter:blur(12px) saturate(1.06);backdrop-filter:blur(12px) saturate(1.06)}
.k24-stage .k24-gearitem__w{color:var(--k24-on-dark-muted)}
.k24-stage .k24-gearitem:hover{background:var(--k24-white);color:var(--k24-ink);
  border-color:var(--k24-white)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .k24-stage .k24-gearitem{background:rgba(19,19,23,.86)}
}

/* One hairline between the two stages and at the join into the plugins section. The seam and
 * fold that .k24-section draws are not available out here and would be wrong anyway: there is
 * no ground step to articulate, the picture behind simply changes. */
.k24-stage+.k24-stage{border-top:1px solid rgba(233,231,239,.18)}
.k24-stage+.k24-section::before,.k24-stage+.k24-section::after{content:none}
.k24-section+.k24-stage,.k24-stage+.k24-section{margin-top:0}

/* A viewport-locked backdrop with content travelling over it IS a parallax effect, so the
 * reduced-motion path unpins it: the layer becomes an ordinary full-height background and the
 * poster (already substituted for the video by the rule further up) simply covers it. */
@media (prefers-reduced-motion:reduce){
  .k24-stage__lock{position:absolute;inset:0;height:100%}
}

/* ---- THE PLUGIN ROSTER GOES CHARCOAL ----------------------------------------------
 *
 * Owner: "can you make the cards that the plugins are on charcoal black instead of white".
 *
 * The fill is the easy half. The half that would have broken it silently is the MARKS: the
 * cells were light, so ops/plugin-logo-treatment.py measured every logo and forced the 16 that
 * are drawn light on transparent to their monochrome black form, because a white-on-transparent
 * mark is invisible on a white cell. Flip the cell to charcoal and that measurement inverts
 * exactly: the 16 it rescued become the only ones still legible, and the 31 it left alone go
 * dark on dark. A background-only change would have hidden two thirds of the roster.
 *
 * The first attempt at that half was to knock every mark to white, which is what this site does
 * for a logo on a dark ground everywhere else: the marquee, the client roster, and these cells'
 * own hover state. Rendered, it destroyed THIRTEEN of the forty-seven. They came out as solid
 * white squares and circles.
 *
 * brightness(0) maps every opaque pixel to black whatever its colour, so once it is inverted the
 * only surviving information is the alpha channel. That is exactly right for a wordmark, where
 * the alpha channel IS the mark, and fatal for a file whose artwork is a filled plate with the
 * brand knocked out of it, because such a file's alpha channel is a rectangle.
 *
 * ops/plugin-logo-dark.py rasterises all forty-seven in a real browser (22 are SVG) and measures
 * COVERAGE, the share of its own bounding box each mark fills. The set separates with nothing
 * between 0.73 and 0.83:
 *
 *   coverage >= 0.78   13 files, a plate with its own ground   no filter, it supplies its own
 *   already light      18 files, drawn light on transparent    no filter, it already reads
 *   the rest           16 files, dark line art                 knocked to white
 *
 * Every assignment is then composited onto this cell colour and checked for two things, because
 * the failure this exists to catch scores perfectly on contrast alone: at least 3:1 against the
 * cell, and a structure figure, the share of the mark that departs from its own dominant tone,
 * of at least 0.06. A blob scores near zero there. All 47 pass, worst structure 0.087 and worst
 * ratio 8.60:1.
 *
 * is-invert is left in place and simply not consulted on this ground, so the light-cell path
 * still works if the grid is ever used on a light section.
 *
 * Hover reverses into the old resting state: white cell, and every mark back to monochrome black,
 * where a blanket filter IS correct because the destination is a plain white ground.
 *
 * --k24-ink-deep, not --k24-ink-lift. The section's ground is a ramp from #24242B down to
 * #131317, so a cell filled with the lift colour would match the ground at the top of the
 * section and disappear. The deep end is the darkest value in the system, which is what
 * "charcoal black" asks for, and the 1px collapsed border is the component either way:
 * --k24-border measures 3.10:1 on it, over the 3:1 an interface boundary needs, and white
 * measures 18.53:1 for the marks and the text fallback.
 */
.k24-section--ink .k24-brands__item{background:var(--k24-ink-deep);color:var(--k24-on-dark);
  border-color:var(--k24-border)}
.k24-section--ink .k24-brands__item:hover{background:var(--k24-white);color:var(--k24-ink)}
.k24-section--ink .k24-brands__item--logo.is-dknock img{filter:brightness(0) invert(1)}
.k24-section--ink .k24-brands__item--logo.is-dplain img,
.k24-section--ink .k24-brands__item--logo.is-dplain.is-invert img{filter:none}
.k24-section--ink .k24-brands__item--logo.is-dknock:hover img,
.k24-section--ink .k24-brands__item--logo.is-dplain:hover img,
.k24-section--ink .k24-brands__item--logo.is-dplain.is-invert:hover img{filter:brightness(0)}

/* ================= THE RATE TABLE ===================================================
 *
 * Owner: "yes show the prices on the site".
 *
 * This component had never rendered. .k24-rates carried ZERO rules in all thirteen
 * stylesheets and six inline blocks the site loads, so the shortcode's price branch would have
 * fallen through to Kadence's table{width:100%} and the browser's own defaults: 1px cell
 * padding, border-spacing 2px, no rules, and th centred while td stayed left. Measured by
 * injecting the real markup into /services/ at 1440, the table stretched to 1344px against a
 * max-content width of 386, so the "Pay now" heading began at x=1106 above a value at x=946,
 * with roughly 400px of dead paper after the last figure. It would have shipped as raw HTML.
 *
 * THE VOICE IS BORROWED, NOT INVENTED, because this site already has one for every part of it:
 *   column headings   .k24-svc__meta and .k24-glasstile__k, the label voice: 12.5px, .1em
 *                     tracking, uppercase, --k24-muted
 *   row names         .k24-gearitem, the tile voice: display face at 1.0625rem, -.01em
 *   the price         .k24-deposit-split b and .k24-glasstile__v: display face at --k24-h3,
 *                     which is how this site has always presented a number as an object
 *
 * Capped at --k24-prose. A three-column table on a 1400px container is not a table, it is three
 * columns of isolated text, and every other reading surface here is capped: .k24-band__lede at
 * 56ch, .k24-svc__body at 40ch, .k24-section p at --k24-measure.
 *
 * --k24-hairline for the row rules, which is what tokens.css reserves it for in as many words
 * ("decorative ONLY: table rows, the drawer divider"). The rules separate rows, they are not an
 * interface boundary, so the 3:1 floor does not apply and --k24-metal-dark would draw a ledger
 * grid over what should read as a list.
 *
 * NO ACCENT COLOUR ON THE DEPOSIT COLUMN. The markup used to carry .k24-gold there, which
 * resolves to --k24-primary-text, which the v4 neutral palette collapsed onto --k24-ink: the
 * same colour as the cell beside it on paper, and #1C1C21 on #1C1C21 at 1.00:1 on any ink
 * section, where the entire column would have vanished. This palette has no accent hue, so
 * emphasis is carried by the face and the weight instead.
 */
.k24-rates{border-collapse:collapse;width:100%;max-width:var(--k24-prose);
  margin:var(--k24-s4) 0 0;table-layout:auto}
.k24-rates th,.k24-rates td{padding:var(--k24-s2) var(--k24-s3) var(--k24-s2) 0;
  border-bottom:1px solid var(--k24-hairline);vertical-align:baseline}
.k24-rates thead th{font-family:var(--k24-font-body);font-size:var(--k24-label);font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--k24-muted);
  padding-bottom:var(--k24-s1);border-bottom-color:var(--k24-metal-dark)}
.k24-rates tbody th{font-family:var(--k24-font-display);font-weight:700;font-size:1.0625rem;
  line-height:1.25;letter-spacing:-.01em;color:var(--k24-text);text-align:left}
.k24-rates th{text-align:left}
/* The money is a column of figures, so it is right-aligned and tabular: without
 * font-variant-numeric the digits are proportional and C$1,500 does not sit under C$800. */
.k24-rates td{text-align:right;padding-right:0;font-variant-numeric:tabular-nums;
  color:var(--k24-text-muted);font-size:var(--k24-body)}
.k24-rates thead th:not(:first-child){text-align:right}
.k24-rates__price{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  color:var(--k24-text)}
.k24-rates__note{max-width:var(--k24-prose);margin:var(--k24-s3) 0 0;
  font-size:.9375rem;line-height:1.5;color:var(--k24-muted)}
/* on the inverted band, the same table with the ground's own text colours */
.k24-section--ink .k24-rates tbody th,.k24-section--ink .k24-rates__price{color:var(--k24-on-dark)}
.k24-section--ink .k24-rates td,.k24-section--ink .k24-rates thead th,
.k24-section--ink .k24-rates__note{color:var(--k24-on-dark-muted)}
.k24-section--ink .k24-rates th,.k24-section--ink .k24-rates td{border-bottom-color:var(--k24-border)}

/* A PHONE GETS A STACK, NOT A SWIPE.
 *
 * The wrap this used to sit in is overflow-x:auto and nothing else: no tabindex, no role, no
 * label and no scroll affordance, which is an inaccessible scroll region rather than a
 * responsive table. And a price a visitor has to swipe sideways to read is a price they do not
 * read. Below the same 560px breakpoint .k24-gearset and .k24-credit already use, each row
 * becomes its own block and the column heading moves inline from the data-label the shortcode
 * emits, so the figures keep their labels without the header row. */
@media (max-width:560px){
  .k24-rates,.k24-rates tbody,.k24-rates tr,.k24-rates th,.k24-rates td{display:block;width:auto}
  .k24-rates thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(1px,1px,1px,1px);white-space:nowrap}
  .k24-rates tr{padding:var(--k24-s3) 0;border-bottom:1px solid var(--k24-hairline)}
  .k24-rates th,.k24-rates td{border:0;padding:0}
  .k24-rates tbody th{font-size:var(--k24-h3);margin-bottom:var(--k24-s1)}
  .k24-rates td{display:flex;justify-content:space-between;align-items:baseline;
    text-align:left;padding-top:4px}
  .k24-rates td::before{content:attr(data-label);font-family:var(--k24-font-body);
    font-size:var(--k24-label);font-weight:600;letter-spacing:.1em;text-transform:uppercase;
    color:var(--k24-muted)}
  .k24-rates__price{font-size:1.0625rem}
  .k24-section--ink .k24-rates tr{border-bottom-color:var(--k24-border)}
  .k24-section--ink .k24-rates td::before{color:var(--k24-on-dark-muted)}
}

/* WooCommerce's own price string, wherever one lands on a dark ground. components.css pins
 * .woocommerce-Price-amount to --k24-text with a bare class selector and no dark counterpart,
 * so a Woo price inside an ink section computes 1.00:1. Scoped to --ink ONLY: the --alt ground
 * is also #1C1C21 but the booking embed sits on a white plate inside it, where the existing
 * rule is already correct and this would repaint the checkout price at 1.2:1. */
.k24-section--ink .woocommerce-Price-amount,
.k24-band--ink .woocommerce-Price-amount{color:var(--k24-on-dark)}

/* The rate on a service card. Display face at --k24-h3, the same device .k24-rates__price and
 * .k24-deposit-split b use, so a number reads as a number wherever it appears on this site. The
 * unit rides beside it in the label voice rather than in the same size, because "an hour" is a
 * qualifier and not part of the figure. Only three of the fifteen cards carry one; see
 * k24_svc_rate() in inc/services.php for why the other twelve deliberately do not. */
.k24-svc__rate{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  line-height:1.1;color:var(--k24-text);margin:0 0 var(--k24-s2)}
.k24-svc__unit{font-family:var(--k24-font-body);font-size:var(--k24-label);font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--k24-muted)}
.k24-section--ink .k24-svc__rate{color:var(--k24-on-dark)}
.k24-section--ink .k24-svc__unit{color:var(--k24-on-dark-muted)}

/* ================= THE BOOKING FORM ==================================================
 *
 * Owner: "the booking system right now feels more like a checkout for like retail when it needs
 * to feel more like a booking process and make the booking system reflect the ui design of the
 * site it looks very plain and dull compared to the rest of the site."
 *
 * They are describing a form that was never designed. Everything below the switch was whatever
 * WooCommerce Bookings and jQuery UI happened to ship, and this is what that meant, all measured
 * on the live page rather than eyeballed:
 *
 *   available days painted #1F874B, a green that appears NOWHERE else on this site
 *   the month bar a jQuery UI gradient from #999 to #AAA, at 2.85:1 against the plate, which is
 *     already below the 3:1 an interface boundary needs
 *   the whole calendar set in VERDANA, because .ui-widget declares it and nothing overrode it
 *   calendar grid lines at 1.24:1, under the threshold at which a reader can see a surface
 *     change at all, so the grid is effectively invisible
 *   the cost box and the deposit note sharing one fill (--k24-lavender #EDEDF0) with nothing
 *     between them, which is why the money area reads as a single grey slab, and the box itself
 *     at 1.17:1 against the plate, so it has no perceivable edge
 *   42.8% of the plate empty at 1440, because .summary was capped at 760px inside a 1328px box
 *
 * WHAT IT BECOMES is the site's own strongest pattern: the collapsed-hairline tile grid that
 * .k24-gearset uses on /gear/, where structure is carried by 1px rules and alignment rather than
 * by fills. A calendar IS a tile grid. So is a set of start times. The numerals take the display
 * face, the labels take the label voice, and the only fill in the whole component is the ink one
 * that marks what you have chosen.
 *
 * THREE THINGS THE PLUGIN DOES THAT DICTATE HOW THESE SELECTORS ARE WRITTEN:
 *
 * 1. td.bookable IS ON EVERY DAY IN THE MONTH, including days that cannot be booked. date-picker.js
 *    pushes it unconditionally, and adds not_bookable separately. The only reason past days are
 *    not green today is that jQuery UI renders unselectable days as <span> and the plugin's rule
 *    is td.bookable a. So EVERY day-state rule here is written against the ANCHOR. A rule on
 *    td.bookable would paint dead days as available.
 * 2. The green is !important at (0,3,2), so these carry !important at (0,4,2). The header bar and
 *    the arrows are not !important and are simply outweighed.
 * 3. Almost the whole form is replaced by AJAX on every date click: of 163 elements, 26 survive.
 *    Nothing here targets an id the plugin generates (div.picker's id is random per page load) or
 *    uses :nth-child on cells. Every selector is a class the plugin re-emits.
 */

/* ---- the form as a whole, and the empty 42.8% --------------------------------------------- */
.k24-book-embed .summary{max-width:none}
.wc-bookings-booking-form{border:1px solid var(--k24-metal-dark)!important;
  border-radius:var(--k24-radius);background:var(--k24-white);padding:0!important;overflow:hidden}
/* Calendar and choices side by side once there is room for both. Below that they stack, which is
 * also the order you do them in. .wc-bookings-date-picker and .form-field are siblings that both
 * survive the AJAX replacement, so this needs no markup of ours inside the form. */
@media (min-width:900px){
  .wc-bookings-booking-form{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .wc-bookings-booking-form>.wc-bookings-date-picker{border-right:1px solid var(--k24-hairline)}
  .wc-bookings-booking-form>.wc-bookings-booking-cost{grid-column:1 / -1}
}
.wc-bookings-booking-form>.wc-bookings-date-picker{padding:var(--k24-s4)}
.wc-bookings-booking-form>.form-field{padding:var(--k24-s4)}

/* ---- typography: nothing on this site renders in a font nobody chose ---------------------- */
.wc-bookings-booking-form .ui-widget,
.wc-bookings-booking-form .ui-widget input,
.wc-bookings-booking-form .ui-widget select,
.wc-bookings-booking-form .ui-widget textarea,
.wc-bookings-booking-form .ui-widget button{font-family:var(--k24-font-body)}

/* ---- the month bar ------------------------------------------------------------------------ */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-header{
  background:none;background-image:none;border:0;border-radius:0;color:var(--k24-ink);
  padding:0 0 var(--k24-s2);text-transform:none;position:relative}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-title{
  font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);line-height:1.1;
  color:var(--k24-ink);text-align:left;margin:0;padding:0 64px 0 0;white-space:nowrap}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-title select{display:none}
/* the rule under the month, the same specular line the section headings use */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-header::after{
  content:"";display:block;width:88px;height:2px;margin-top:10px;background-image:var(--k24-rule)}

/* Prev and next: the plugin's plates are #999 blocks and the glyphs are background-image sprites,
 * so the sprite is switched off and the chevron is drawn. */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-prev,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-next{
  background:var(--k24-white);border:1px solid var(--k24-border);border-radius:0;
  width:32px;height:32px;top:0;cursor:pointer;transition:background-color var(--k24-dur-micro)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-prev{right:36px;left:auto}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-next{right:0}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-prev:hover,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-datepicker-next:hover{
  background:var(--k24-ink)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-icon{
  background-image:none!important;text-indent:0;overflow:visible;width:auto;height:auto;
  margin:0;position:absolute;inset:0;font-size:0}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker .ui-icon::before{
  content:"";position:absolute;top:50%;left:50%;width:7px;height:7px;
  border-left:1.5px solid var(--k24-ink);border-bottom:1.5px solid var(--k24-ink)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-prev .ui-icon::before{
  transform:translate(-40%,-50%) rotate(45deg)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-next .ui-icon::before{
  transform:translate(-60%,-50%) rotate(-135deg)}
.wc-bookings-booking-form .ui-datepicker-prev:hover .ui-icon::before,
.wc-bookings-booking-form .ui-datepicker-next:hover .ui-icon::before{border-color:var(--k24-white)}
.wc-bookings-booking-form .ui-datepicker .ui-state-disabled .ui-icon::before{border-color:var(--k24-muted)}

/* ---- the calendar as a tile grid ---------------------------------------------------------- */
.wc-bookings-booking-form .ui-datepicker-calendar{border-collapse:collapse;width:100%;margin:0}
.wc-bookings-booking-form .ui-datepicker-calendar th{
  font-family:var(--k24-font-body);font-size:var(--k24-label);font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;color:var(--k24-muted);padding:0 0 var(--k24-s1);border:0;background:none}
.wc-bookings-booking-form .ui-datepicker-calendar td{padding:0;border:0;background:none;opacity:1}
.wc-bookings-booking-form .ui-datepicker-calendar td a,
.wc-bookings-booking-form .ui-datepicker-calendar td span{
  display:flex;align-items:center;justify-content:center;min-height:44px;padding:0;
  border:1px solid var(--k24-metal-dark);margin:-1px 0 0 -1px;border-radius:0;
  font-family:var(--k24-font-display);font-weight:500;font-size:1rem;line-height:1;
  text-align:center;text-decoration:none;transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}

/* THE FIVE DAY STATES, all against the anchor, all beating a (0,3,2) !important rule. */
/* available */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.bookable a{
  background-color:var(--k24-white)!important;background-image:none!important;
  color:var(--k24-ink)!important;border-color:var(--k24-metal-dark)!important;text-shadow:none!important}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.bookable a:hover{
  background-color:var(--k24-ink)!important;color:var(--k24-white)!important}
/* partially booked: available, but said out loud. The plugin painted it the SAME green as a wide
 * open day, so "some time left" and "nothing booked" were pixel-identical and the only difference
 * was a title attribute nobody sees without a mouse. */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.partial_booked a{
  background-color:var(--k24-white)!important;color:var(--k24-ink)!important;
  border-color:var(--k24-metal-dark)!important;position:relative}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.partial_booked a::after{
  content:"";position:absolute;top:5px;right:5px;width:5px;height:5px;border-radius:50%;
  background:var(--k24-metal-dark)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.partial_booked a:hover{
  background-color:var(--k24-ink)!important;color:var(--k24-white)!important}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.partial_booked a:hover::after{
  background:var(--k24-white)}
/* fully booked, and the rules variant the plugin excludes from its own selector */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.fully_booked a,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.fully_booked span,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.fully_booked.not_bookable_by_rules a{
  background-color:var(--k24-paper-deep)!important;color:var(--k24-muted)!important;
  border-color:var(--k24-hairline)!important;text-decoration:line-through;cursor:not-allowed}
/* closed, past, or outside the window: no anchor at all, so this is the span */
.wc-bookings-booking-form .ui-datepicker-calendar td.not_bookable span,
.wc-bookings-booking-form .ui-datepicker-calendar td.ui-state-disabled span{
  background:var(--k24-paper-deep);color:var(--k24-muted);border-color:var(--k24-hairline)}
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.ui-state-disabled{opacity:1}
/* chosen */
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td a.ui-state-active,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.ui-datepicker-current-day a,
.wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker td.bookable-range .ui-state-default{
  background-color:var(--k24-ink)!important;background-image:none!important;
  color:var(--k24-white)!important;border-color:var(--k24-ink)!important;font-weight:700}
.wc-bookings-booking-form .wc-bookings-date-picker-timezone-block{
  font-size:var(--k24-label);letter-spacing:.06em;text-transform:uppercase;color:var(--k24-muted);
  text-align:left!important;margin:0 0 var(--k24-s3)}

/* ---- how long, and when: the hours-first controls ------------------------------------------
 *
 * inc/booking-hours.php replaces the plugin's Starts/Ends pair with a length and a set of start
 * times that can hold it. The slots are the gear roster's tiles again: collapsed hairlines, the
 * display face on the time, the label voice on the range, and one ink fill for the chosen one.
 */
.k24-hours{margin:0}
.k24-hours__row{display:flex;flex-direction:column;gap:var(--k24-s1);margin:0 0 var(--k24-s3)}
.k24-hours__label{font-family:var(--k24-font-body);font-size:var(--k24-label);font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--k24-muted)}
.k24-hours__select{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  line-height:1.2;color:var(--k24-ink);background:var(--k24-white);
  border:1px solid var(--k24-border);border-radius:var(--k24-radius);
  padding:10px var(--k24-s3) 10px var(--k24-s2);min-height:48px;width:100%;max-width:none}
.k24-hours__legend{font-size:var(--k24-label);letter-spacing:.06em;text-transform:uppercase;
  color:var(--k24-muted);margin:0 0 var(--k24-s2)}

.k24-slots{display:grid;grid-template-columns:repeat(auto-fill,minmax(128px,1fr));margin:0;padding:0}
/* A slot is a .k24-gearitem that happens to be a button: same collapsed border, same tile logic,
 * same hover inversion. min-height 56 keeps the tap target over the 44px floor at 390. */
/* :not(.k24-slot) IS ON THE BLANKET BUTTON RULE ABOVE, AND IT HAS TO BE.
 *
 * A slot is a <button>, and `button:not(.k24-pill):not(.menu-toggle-open):not(.menu-toggle-close)`
 * is (0,3,1), NOT (0,1,0): :not() contributes the specificity of its ARGUMENT, three classes plus
 * the element. This file has documented that arithmetic since the booking switch was fixed, and
 * these tiles were shipped at (0,2,0) anyway and lost every property that matters. Measured live
 * before the fix: min-height 52px not 56, padding 15px 32px not var(--k24-s2), uppercase,
 * letter-spacing 1.76px, and border 1px solid rgba(0,0,0,0), so the collapsed grid the tiles are
 * built on drew nothing at all.
 *
 * Raising these rules to (0,3,0) would have fixed the tie and left the transparent border winning
 * on the properties this block does not name. Excluding the class is the honest fix and it is the
 * pattern the rule already uses for .k24-pill. */
.k24-slots .k24-slot{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;
  gap:2px;min-height:56px;padding:var(--k24-s2);text-align:left;cursor:pointer;
  border:1px solid var(--k24-metal-dark);margin:-1px 0 0 -1px;border-radius:0;
  background:var(--k24-white);color:var(--k24-ink);width:100%;
  transition:background-color var(--k24-dur-micro),color var(--k24-dur-micro)}
.k24-slots .k24-slot__t{font-family:var(--k24-font-display);font-weight:700;font-size:1.0625rem;
  line-height:1.15;letter-spacing:-.01em;text-transform:none}
.k24-slots .k24-slot__e{font-family:var(--k24-font-body);font-size:var(--k24-label);font-weight:400;
  letter-spacing:.04em;text-transform:none;color:var(--k24-muted)}
.k24-slots .k24-slot:hover{background:var(--k24-ink);color:var(--k24-white)}
.k24-slots .k24-slot:hover .k24-slot__e{color:var(--k24-on-dark-muted)}
.k24-slots .k24-slot.is-on{background:var(--k24-ink);color:var(--k24-white);
  border-color:var(--k24-ink);position:relative;z-index:1}
.k24-slots .k24-slot.is-on .k24-slot__e{color:var(--k24-on-dark-muted)}

/* ---- the money -----------------------------------------------------------------------------
 *
 * The cost box and the deposit note were the SAME fill, #EDEDF0, stacked with nothing between
 * them, which is the real reason this area read as one undifferentiated grey slab. And the box
 * measured 1.17:1 against the white plate with a #ddd top border at 1.16:1, so it had no
 * perceivable edge at all: it was grey text furniture, not a total.
 *
 * The total is the one thing a person is looking for, so it is inverted: ink ground, white
 * figure, the display face. The deposit note stays paper. They can no longer be confused for
 * each other because they are no longer the same object.
 */
.wc-bookings-booking-form .wc-bookings-booking-cost{
  background:var(--k24-ink)!important;background-image:var(--k24-ground-ink)!important;
  color:var(--k24-white)!important;border:0!important;border-top:1px solid var(--k24-ink)!important;
  border-radius:0!important;margin:0!important;padding:var(--k24-s3) var(--k24-s4)!important;
  font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);letter-spacing:-.01em}
.k24-deposit-note{background:var(--k24-paper-deep);border:0;border-left:2px solid var(--k24-metal-dark);
  border-radius:0;padding:var(--k24-s3);margin:var(--k24-s4) 0 var(--k24-s3);
  font-size:var(--k24-body);color:var(--k24-text)}

@media (max-width:560px){
  .wc-bookings-booking-form>.wc-bookings-date-picker,
  .wc-bookings-booking-form>.form-field{padding:var(--k24-s3)}
  /* The month title wrapped mid-year at 390, rendering "September 202 / 6". Measured: the title
   * box is 228px with 76px reserved for the two arrows, leaving 152px, and "September 2026" at
   * 20px needs more than that. Smaller type and nowrap together, because either alone still
   * breaks on the longest month at the narrowest width. */
  .wc-bookings-booking-form .wc-bookings-date-picker .ui-datepicker-title{
    font-size:1.125rem;padding-right:72px}
  .k24-slots{grid-template-columns:repeat(auto-fill,minmax(118px,1fr))}
  .wc-bookings-booking-form .wc-bookings-booking-cost{padding:var(--k24-s3)!important}
}

/* The total's TEXT, which the plate's own rule was repainting ink-on-ink at 1.00:1.
 *
 * The cost markup is <div class="wc-bookings-booking-cost"><p class="booking-cost-label">, and
 * `.k24-section .k24-book-embed.k24-plate p` at (0,3,1) sets every paragraph inside the plate to
 * --k24-text so the white plate reads correctly on the dark section. Inverting the box to ink
 * put that rule on the wrong side of its own assumption: measured, the paragraph computed
 * rgb(28,28,33) on rgb(28,28,33). The price span is pinned separately by the bare
 * .woocommerce-Price-amount rule, so it needs naming too. (0,4,1) and (0,4,2) clear both. */
.k24-section .k24-book-embed.k24-plate .wc-bookings-booking-cost p,
.k24-section .k24-book-embed.k24-plate .wc-bookings-booking-cost .booking-cost-label{
  color:var(--k24-white);margin:0;font-family:var(--k24-font-display);font-weight:700;
  font-size:var(--k24-h3);line-height:1.2;letter-spacing:-.01em}
.k24-section .k24-book-embed.k24-plate .wc-bookings-booking-cost .woocommerce-Price-amount,
.wc-bookings-booking-form .wc-bookings-booking-cost .woocommerce-Price-amount{color:var(--k24-white)}

/* The day turning over, inside the slot grid. The room is open until 2am, so "12:00 am" and
 * "1:00 am" in a list that begins at 9:00 am are the NEXT day, and a customer scanning the strip
 * reads one evening unless something says otherwise. */
.k24-slots__break{grid-column:1 / -1;margin:var(--k24-s3) 0 0;padding:0 0 var(--k24-s1);
  font-size:var(--k24-label);font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  color:var(--k24-muted);border-bottom:1px solid var(--k24-metal-dark)}
.k24-slots__break+.k24-slot{margin-top:0}
/* the length question now sits above the form rather than inside it */
.k24-hours{margin:0 0 var(--k24-s4);max-width:420px}

/* The session said back as a fact, above the money. Display face, because it is the sentence the
 * customer is checking, not a form label. */
.k24-readback{font-family:var(--k24-font-display);font-weight:700;font-size:var(--k24-h3);
  line-height:1.25;letter-spacing:-.01em;color:var(--k24-ink);
  margin:var(--k24-s4) 0 var(--k24-s2);padding:0 0 var(--k24-s2);
  border-bottom:1px solid var(--k24-metal-dark)}
