/* ============================================================
   henry·designs kit · base.css
   The shared component registry. Twelve components, styled ONLY
   through the canonical tokens every system defines in
   kit/tokens/<system>.css. Per-system personality lives in
   kit/systems/<system>.css and may override anything here.
   Builder rule: use these classes. Do not invent new ones.
   ============================================================ */

/* ---- reset + document ---- */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:var(--font-body);font-weight:var(--body-weight);font-size:var(--body-size);line-height:1.6;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;min-height:100vh}
img,svg,video{max-width:100%;display:block}
a{color:inherit}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
:focus-visible{outline:2px solid var(--accent-2);outline-offset:3px;border-radius:4px}
::selection{background:var(--accent);color:var(--accent-ink)}
h1,h2,h3,h4{font-family:var(--font-display);font-weight:var(--display-weight);text-transform:var(--display-case);letter-spacing:var(--display-tracking);line-height:var(--display-lh);text-wrap:balance}
h1{font-size:clamp(44px,7.5vw,88px)}
h2{font-size:clamp(30px,4.6vw,54px)}
h3{font-size:clamp(20px,2.2vw,26px)}
h4{font-size:18px}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 28px}
section,header,footer{position:relative}
[data-count],.tabular{font-variant-numeric:tabular-nums}

/* ---- primitives ---- */
.eyebrow{font-family:var(--font-mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent-2);display:inline-flex;align-items:center;gap:10px}
.eyebrow::before{content:"";width:18px;height:1px;background:currentColor;opacity:.7}
.sec-head{max-width:62ch;margin-bottom:clamp(28px,4vw,48px)}
.sec-head .eyebrow{margin-bottom:16px}
.sec-head p{margin-top:14px;color:var(--muted);font-size:17px;max-width:56ch}
.sec-head.center{margin-inline:auto;text-align:center}
.sec-head.center p{margin-inline:auto}
.btn{font-family:var(--font-body);font-weight:500;font-size:14.5px;letter-spacing:.01em;text-decoration:none;padding:13px 24px;border-radius:var(--radius);display:inline-flex;align-items:center;justify-content:center;gap:8px;transition:.18s;cursor:pointer;border:1px solid transparent;white-space:nowrap}
.btn-primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.btn-primary:hover{background:var(--accent-2);border-color:var(--accent-2);transform:translateY(-1px)}
.btn-outline{background:transparent;color:var(--text);border-color:var(--text)}
.btn-outline:hover{background:var(--text);color:var(--bg)}
.btn-ghost{background:transparent;color:var(--text);border-color:var(--line-2)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent-2)}
.btn-lg{padding:16px 30px;font-size:15.5px}
.btn-sm{padding:9px 16px;font-size:13px}
.link-arrow{color:var(--text);text-decoration:none;border-bottom:1px solid var(--accent);padding:4px 0 3px;font-size:14.5px;transition:.15s;display:inline-block;min-height:24px}
.link-arrow:hover{color:var(--accent-2);border-color:var(--accent-2)}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:26px;box-shadow:var(--shadow)}
.chip{display:inline-flex;align-items:center;font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent-2);padding:6px 14px;border:1px solid var(--line);border-radius:var(--radius-pill);white-space:nowrap}
.chips{display:flex;flex-wrap:wrap;gap:8px}
/* .grad paints its gradient only inside its own box, so give descenders and italic
   overhangs room (padding) and take it back (negative margin) so layout is unchanged. */
.grad{color:var(--mesh-fallback);display:inline-block;padding:.06em .08em .18em 0;margin:-.06em -.08em -.18em 0}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .grad{background:var(--mesh);-webkit-background-clip:text;background-clip:text;color:transparent}
}
.hairline{height:1px;background:var(--line);border:0}
.skip{position:absolute;left:-999px;top:8px;z-index:100;background:var(--accent);color:var(--accent-ink);padding:10px 16px;border-radius:var(--radius);text-decoration:none;font-weight:600}
.skip:focus{left:8px}
main{display:block}

/* ---- effects · the 21st.dev shortlist, rebuilt vanilla + token-driven ----
   All four are opt-in classes, no dependencies. Only transform/opacity/
   background-position animate; the mesh layer quiets down on phones because
   a full-screen blur is the one thing that actually costs scroll frames.
   .fx-glow      animated halo behind a button, chip or card
   .fx-spotlight cursor-tracking light + inner glow ring on a card
   .fx-mesh      drifting mesh-gradient background layer (place behind content)
   .fx-shimmer   sweeping gradient through display text
   .fx-shine     sheen that crosses a button on hover
   ------------------------------------------------------------------------ */

/* 1 · fx-glow */
.fx-glow{position:relative;display:inline-flex;isolation:isolate}
.fx-glow>*{position:relative;z-index:1}
.fx-glow::before{
  content:"";position:absolute;z-index:0;inset:-7px;border-radius:999px;
  background:conic-gradient(from 0deg,var(--accent),var(--accent-2),var(--accent));
  filter:blur(10px);opacity:.5;
  animation:fx-spin 8s linear infinite;
}
.fx-glow.fx-soft::before{filter:blur(16px);opacity:.34}
.fx-glow.fx-strong::before{filter:blur(7px);opacity:.72}
@keyframes fx-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
.fx-glow-card{display:block}
/* Cards are not pills: rotating a conic gradient behind a rounded rect sweeps a
   circle the size of the card's diagonal, which read as a smear on light themes.
   The card variant gets an even linear halo that only breathes in opacity. */
.fx-glow-card::before{
  inset:-10px;border-radius:var(--radius-lg);--fx-o:.3;
  background:linear-gradient(120deg,var(--accent),var(--accent-2),var(--accent));
  filter:blur(20px);opacity:var(--fx-o);
  animation:fx-cardpulse 7s ease-in-out infinite alternate;
}
.fx-glow-card.fx-soft::before{--fx-o:.18}
.fx-glow-card.fx-strong::before{--fx-o:.44}
@keyframes fx-cardpulse{from{opacity:calc(var(--fx-o) * .55)}to{opacity:var(--fx-o)}}

/* 2 · fx-spotlight */
.fx-spotlight{position:relative;overflow:hidden;--mx:50%;--my:0px}
.fx-spotlight::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background:radial-gradient(320px circle at var(--mx) var(--my),var(--accent),transparent 68%);
  opacity:0;transition:opacity .4s;mix-blend-mode:screen;
}
.fx-spotlight>*{position:relative;z-index:1}
@media(hover:hover){
  .fx-spotlight:hover::after{opacity:.2}
  .fx-spotlight:hover{border-color:var(--line-2)}
}
@supports ((-webkit-mask-composite:exclude) or (mask-composite:exclude)){
  .fx-spotlight::before{
    content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:2;
    padding:1px;
    background:radial-gradient(260px circle at var(--mx) var(--my),var(--accent-2),transparent 70%);
    -webkit-mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
    -webkit-mask-composite:xor;mask:linear-gradient(#000,#000) content-box,linear-gradient(#000,#000);
    mask-composite:exclude;
    opacity:0;transition:opacity .4s;
  }
  @media(hover:hover){.fx-spotlight:hover::before{opacity:.75}}
}

/* 3 · fx-mesh — put inside a position:relative parent, or on the section itself */
.fx-mesh{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(58vmax 58vmax at 14% 34%,var(--accent) 0%,transparent 58%),
    radial-gradient(50vmax 50vmax at 86% 30%,var(--accent-2) 0%,transparent 56%),
    radial-gradient(62vmax 62vmax at 52% 108%,var(--accent-soft) 0%,transparent 60%);
  filter:blur(46px);opacity:.20;
  transform:translate3d(0,0,0);
  animation:fx-breathe 26s ease-in-out infinite alternate;
}
.fx-mesh.fx-mesh-soft{opacity:.12}
.fx-mesh.fx-mesh-hot{opacity:.3}
@keyframes fx-breathe{
  from{transform:scale(1) translate3d(0,0,0)}
  to{transform:scale(1.12) translate3d(1.5%,-2%,0)}
}
.fx-mesh-host{position:relative;overflow:hidden;isolation:isolate}
.fx-mesh-host>*:not(.fx-mesh){position:relative;z-index:1}

/* 4a · fx-shimmer */
.fx-shimmer{color:var(--text)}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .fx-shimmer{
    background:linear-gradient(100deg,var(--text) 0%,var(--text) 34%,var(--accent-2) 48%,var(--text) 62%,var(--text) 100%);
    background-size:240% 100%;
    -webkit-background-clip:text;background-clip:text;color:transparent;
    animation:fx-sweep 5s linear infinite;
  }
}
@keyframes fx-sweep{from{background-position:190% 0}to{background-position:-90% 0}}

/* 4b · fx-shine */
.fx-shine{position:relative;overflow:hidden}
.fx-shine::after{
  content:"";position:absolute;top:0;bottom:0;left:0;width:60%;
  background:linear-gradient(105deg,transparent 0%,rgba(255,255,255,.55) 50%,transparent 100%);
  transform:translateX(-180%) skewX(-12deg);pointer-events:none;
}
.fx-shine:hover::after{transition:transform .8s cubic-bezier(.22,1,.36,1);transform:translateX(280%) skewX(-12deg)}

/* effects · motion + phone guards */
@media(max-width:860px){
  .fx-mesh{filter:blur(16px);animation:none;opacity:.22}
  .fx-glow::before{filter:blur(16px)}
}
@media (prefers-reduced-motion:reduce){
  .fx-glow::before,.fx-glow-card::before,.fx-mesh,.fx-shimmer{animation:none!important}
  .fx-shimmer{background:none;color:var(--text)}
  .fx-shine::after{display:none}
}

/* ---- motion ---- */
@keyframes k-fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}
.js .reveal{opacity:0}
.reveal.in{animation:k-fadeUp .7s cubic-bezier(.22,1,.36,1) forwards}
.hero-stagger .wd{display:inline-block;opacity:0;transform:translateY(14px)}
@keyframes k-wdIn{to{opacity:1;transform:none}}
.js .hero-stagger .wd{animation:k-wdIn .7s cubic-bezier(.22,1,.36,1) forwards;animation-delay:calc(var(--i,0)*70ms)}
/* ---- SECTION TITLE TREATMENT · every system, every build ------------------
   Jamie, 20 Sep 2026: "add motion to all section titles in all themes, in the
   respective fonts". Not gated by --motion: the eyebrow scramble-resolves and its
   rule draws in, the title's words rise out of clipped masks 80 ms apart, the one
   accent phrase (.hx) carries a moving gradient in the system's palette, the sub
   fades after. kit.js marks the section .has-head and the head's siblings .sb so
   the head runs its own show while the body still fades in as one block (base) or
   cascades card by card (--motion full). Everything stops under reduced-motion.
   ------------------------------------------------------------------------- */
.js body:not([data-motion="full"]) .reveal.has-head{opacity:1;animation:none}
.js body:not([data-motion="full"]) .reveal.has-head .sb{opacity:0}
.js body:not([data-motion="full"]) .reveal.has-head.in .sb{animation:k-fadeUp .7s cubic-bezier(.22,1,.36,1) .3s forwards}
.js body:not([data-motion="full"]) .k-cta.reveal{opacity:1;animation:none}
.js body:not([data-motion="full"]) .k-cta .band{opacity:0}
.js body:not([data-motion="full"]) .k-cta .band.in{animation:k-fadeUp .8s cubic-bezier(.22,1,.36,1) forwards}
/* masked word reveal */
.js .mt .ml{display:inline-block;overflow:hidden;vertical-align:top;padding:0 .12em .14em 0;margin:0 -.12em -.14em 0}
.js .mt .mw{display:inline-block;transform:translate3d(0,112%,0);opacity:0;transition:transform .95s cubic-bezier(.22,1,.36,1),opacity .6s ease;transition-delay:calc(var(--w,0)*80ms)}
.js .sec-head.in .mt .mw,.js .band.in .mt .mw,.js .mt.in .mw{transform:none;opacity:1}
/* the head: eyebrow scrambles (kit.js) and its rule draws, words rise, then the sub fades */
.js .sec-head p{opacity:0;transform:translateY(14px)}
.js .sec-head.in p{animation:k-fadeUp .8s cubic-bezier(.22,1,.36,1) .45s forwards}
.js .sec-head .eyebrow::before{transform-origin:left;transform:scaleX(0);transition:transform .8s cubic-bezier(.22,1,.36,1) .3s}
.js .sec-head.in .eyebrow::before{transform:scaleX(1)}
.scr{font-variant-ligatures:none}
/* .hx — the accent phrase of a section title: *phrase* / <em>phrase</em> in content, the
   system's .grad look (fonts + palette come from kit/systems/<slug>.css) with a gradient that
   keeps moving. Default sweep: text → accent-2 → text; systems override --hx. */
.hx{color:var(--mesh-fallback);display:inline-block;padding:.06em .08em .18em 0;margin:-.06em -.08em -.18em 0}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .hx{background-image:var(--hx,linear-gradient(100deg,var(--text) 0%,var(--text) 30%,var(--accent-2) 50%,var(--text) 70%,var(--text) 100%));background-size:240% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:fx-sweep 5s linear infinite}
}
/* on the CTA band the title sits on the accent (or ink) ground, so the phrase sweeps in the band's own
   ink instead of the accent colour — every system sets --hx-band on .k-cta .band (base: bg → accent-2 → bg) */
.k-cta .band .hx{--hx:var(--hx-band,linear-gradient(100deg,var(--bg) 0%,var(--bg) 30%,var(--accent-2) 50%,var(--bg) 70%,var(--bg) 100%))}
/* .hl — the marker bar behind a word; on the dark systems it is a translucent highlighter so light type stays readable */
.hl{position:relative;display:inline-block;z-index:0;white-space:nowrap;color:inherit;background:none}
.hl::before{content:"";position:absolute;z-index:-1;left:-.06em;right:-.06em;bottom:.06em;height:.46em;background:var(--accent);opacity:.9;transform:scaleX(1);transform-origin:left center}
.js .hl::before{transform:scaleX(0);transition:transform .8s cubic-bezier(.22,1,.36,1) .55s}
.js .sec-head.in .hl::before,.js .band.in .hl::before,.js .mt.in .hl::before{transform:scaleX(1)}
[data-system="forge"] .hl::before,[data-system="pulse"] .hl::before,[data-system="vault"] .hl::before{opacity:.34;height:.6em;bottom:.02em}
@media (prefers-reduced-motion:reduce){
  .js .reveal.has-head .sb,.js .k-cta .band,.js .mt .mw,.js .sec-head p{opacity:1!important;transform:none!important;animation:none!important;transition:none!important}
  .js .sec-head .eyebrow::before,.js .hl::before{transform:none!important;transition:none!important}
  .hx{animation:none!important}
}

/* ---- MOTION PACKAGE · body[data-motion="full"] ---------------------------
   The sellable "motion package": word-stagger hero with blur, cascading reveals
   (every card, step, row and cell is its own .mv element), masked marquee, hover
   polish. Opt-in via data-motion="full" on <body>; the section title treatment
   above is NOT part of it — that is on everywhere.
   ------------------------------------------------------------------------- */
[data-motion="full"] .hero-stagger .wd{transform:translateY(.55em);filter:blur(6px)}
@keyframes k-wdInFull{to{opacity:1;transform:none;filter:blur(0)}}
.js [data-motion="full"] .hero-stagger .wd{animation:k-wdInFull .9s cubic-bezier(.16,1,.3,1) forwards;animation-delay:calc(120ms + var(--i,0)*80ms)}
.js [data-motion="full"] .k-hero .eyebrow,.js [data-motion="full"] .k-hero .sub,.js [data-motion="full"] .k-hero .cta,.js [data-motion="full"] .k-hero .chips,.js [data-motion="full"] .k-hero .visual{opacity:0;transform:translateY(16px);animation:k-fadeUp .8s cubic-bezier(.22,1,.36,1) forwards}
.js [data-motion="full"] .k-hero .eyebrow{animation-delay:0ms}
.js [data-motion="full"] .k-hero .sub{animation-delay:.55s}
.js [data-motion="full"] .k-hero .cta{animation-delay:.7s}
.js [data-motion="full"] .k-hero .chips{animation-delay:.85s}
.js [data-motion="full"] .k-hero .visual{animation-delay:.4s;animation-duration:1.1s}
/* hero art fades up after the words */
@keyframes k-artIn{to{opacity:1}}
.js [data-motion="full"] .k-hero .art{opacity:0;animation:k-artIn 1.6s ease-out .3s forwards}
/* sections: the block no longer fades as one — every head, card, step, row and cell is its own
   .mv element, observed individually (kit.js) and animated the moment it enters the viewport */
.js [data-motion="full"] .reveal{opacity:1;animation:none}
@keyframes k-mvIn{from{opacity:0;transform:translateY(34px) scale(.985)}to{opacity:1;transform:none}}
.js [data-motion="full"] .mv{opacity:0}
.js [data-motion="full"] .mv.in{animation:k-mvIn .9s cubic-bezier(.22,1,.36,1) forwards;animation-delay:calc((var(--i,1) - 1)*110ms)}
.js [data-motion="full"] .sec-head.mv.in{animation-delay:0ms}
/* a head with a masked title does not also rise as a block — its eyebrow scrambles, its words rise, its sub fades */
.js [data-motion="full"] .sec-head.mv{opacity:1;animation:none}
/* stats: figures rise, count-up already runs */
.js [data-motion="full"] .k-stats .cell .num{display:inline-block}
/* pricing: featured tier lands last and lifts */
.js [data-motion="full"] .k-pricing .tier.featured{--i:4}
/* marquee: fades in from the right edge and keeps a soft edge mask */
[data-motion="full"] .k-marquee{-webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.js [data-motion="full"] .k-marquee .track{opacity:0;animation:k-artIn 1.2s ease-out .5s forwards}
/* hover polish: cards lift, buttons press */
[data-motion="full"] .k-services .item,[data-motion="full"] .k-pricing .tier,[data-motion="full"] .k-testimonials .quote{transition:transform .35s cubic-bezier(.22,1,.36,1),border-color .35s,box-shadow .35s}
[data-motion="full"] .k-pricing .tier:hover{transform:translateY(-4px)}
[data-motion="full"] .btn{transition:transform .18s cubic-bezier(.22,1,.36,1),background .18s,border-color .18s,color .18s}
[data-motion="full"] .btn:active{transform:translateY(1px) scale(.985)}
/* FAQ answers slide open */
[data-motion="full"] .k-faq details .a{animation:k-fadeUp .45s cubic-bezier(.22,1,.36,1)}
/* nav: solidifies after the hero */
[data-motion="full"] .k-nav{transition:background .4s,box-shadow .4s,border-color .4s}
[data-motion="full"] .k-nav.scrolled{box-shadow:0 8px 30px -20px rgba(0,0,0,.6)}
@media (prefers-reduced-motion:reduce){
  .js [data-motion="full"] .hero-stagger .wd,.js [data-motion="full"] .k-hero .eyebrow,.js [data-motion="full"] .k-hero .sub,.js [data-motion="full"] .k-hero .cta,.js [data-motion="full"] .k-hero .chips,.js [data-motion="full"] .k-hero .visual,.js [data-motion="full"] .k-hero .art,
  .js [data-motion="full"] .mv,.js [data-motion="full"] .mv.in,.js [data-motion="full"] .k-marquee .track{opacity:1!important;transform:none!important;filter:none!important;animation:none!important;transition:none!important}
  [data-motion="full"] .k-marquee{-webkit-mask-image:none;mask-image:none}
}

html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .reveal,.reveal.in,.js .hero-stagger .wd{opacity:1!important;transform:none!important;animation:none!important}
  .k-marquee .track{animation:none!important;opacity:1!important}
}

/* ============================================================
   01 · NAV  .k-nav
   ============================================================ */
.k-nav{position:sticky;top:0;z-index:50;background:var(--nav-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.k-nav .in{display:flex;align-items:center;justify-content:space-between;height:66px;gap:20px}
.k-nav .brand{font-family:var(--font-display);font-weight:var(--display-weight);text-transform:var(--display-case);letter-spacing:var(--display-tracking);font-size:19px;text-decoration:none;color:var(--text);display:inline-flex;align-items:center;gap:10px}
.k-nav .brand .mark{width:10px;height:10px;border-radius:50%;background:var(--accent)}
.k-nav .links{display:flex;gap:4px;align-items:center}
.k-nav .links a{color:var(--muted);text-decoration:none;font-size:14px;font-weight:500;padding:8px 12px;border-radius:var(--radius);transition:.15s}
.k-nav .links a:hover{color:var(--text);background:var(--panel-2)}
.k-nav .links a[aria-current]{color:var(--accent-2)}
.k-nav .links a:focus:not(:focus-visible){background:transparent;color:var(--muted)}
.k-nav .actions{display:flex;align-items:center;gap:10px}
.k-nav .burger{display:none;width:42px;height:42px;border-radius:var(--radius);border:1px solid var(--line-2);place-items:center}
.k-nav .burger span{display:block;width:18px;height:2px;background:var(--text);position:relative;transition:.2s}
.k-nav .burger span::before,.k-nav .burger span::after{content:"";position:absolute;left:0;width:18px;height:2px;background:var(--text);transition:.2s}
.k-nav .burger span::before{top:-6px}.k-nav .burger span::after{top:6px}
.k-nav.open .burger span{background:transparent}
.k-nav.open .burger span::before{top:0;transform:rotate(45deg)}
.k-nav.open .burger span::after{top:0;transform:rotate(-45deg)}
.k-nav .drawer{display:none}
@media(max-width:860px){
  .k-nav .links,.k-nav .actions .btn{display:none}
  .k-nav .burger{display:grid}
  .k-nav.open .drawer{display:block;border-top:1px solid var(--line);padding:10px 0 18px}
  .k-nav .drawer a{display:block;padding:13px 0;text-decoration:none;color:var(--text);font-size:17px;border-bottom:1px solid var(--line)}
  .k-nav .drawer .btn{margin-top:16px;width:100%}
}

/* ============================================================
   02 · HERO  .k-hero
   ============================================================ */
.k-hero{padding:clamp(72px,10vw,128px) 0 clamp(56px,8vw,96px);overflow:hidden;border-bottom:1px solid var(--line)}
.k-hero .wrap{position:relative;z-index:2}
.k-hero .eyebrow{margin-bottom:22px}
.k-hero h1{max-width:14ch;text-wrap:pretty}
.k-hero .sub{margin-top:24px;max-width:54ch;color:var(--muted);font-size:clamp(16px,1.4vw,19px)}
.k-hero .cta{display:flex;gap:12px;margin-top:36px;flex-wrap:wrap}
.k-hero .chips{margin-top:28px}
.k-hero.center{text-align:center}
.k-hero.center h1,.k-hero.center .sub{margin-inline:auto}
.k-hero.center .cta,.k-hero.center .chips{justify-content:center}
.k-hero .art{position:absolute;inset:0;pointer-events:none;z-index:1;overflow:hidden}
.k-hero .blob{position:absolute;border-radius:50%;filter:blur(60px);opacity:.35;pointer-events:none;background:var(--accent)}
.k-hero .orbit,.k-hero .hart{width:100%;height:100%;display:block}
.k-hero.split .wrap{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center}
.k-hero.split .visual{position:relative;min-height:320px;border-radius:var(--radius-lg);background:linear-gradient(160deg,var(--panel-2),var(--bg-2));border:1px solid var(--line);overflow:hidden}

/* ============================================================
   03 · BAND / AMBIENT GLOW  .k-marquee
   ============================================================ */
/* Jamie, 22 Sep 2026: the scrolling line is retired. The band is an AMBIENT GLOW —
   two blurred radial gradients that fade out to transparent and drift on long, slow,
   independent paths — with the words sitting still on top of it. Same class and the
   same content key, so every existing build and every system override still applies;
   `marquee.variant: "glow"` drops the words and leaves the glow alone. Tune per system
   or per build with --orb-opacity / --orb-blur / --orb-a / --orb-b. */
.k-marquee{--band-gap:20px;--orb-1:min(40vw,580px);--orb-2:min(33vw,470px);
  position:relative;isolation:isolate;border-block:1px solid var(--line);background:var(--bg-2);overflow:hidden;padding:clamp(30px,3.4vw,46px) 0}
.k-marquee::before,.k-marquee::after{content:"";position:absolute;z-index:0;pointer-events:none;top:50%;
  border-radius:50%;filter:blur(var(--orb-blur,58px));opacity:var(--orb-opacity,.42);will-change:transform}
.k-marquee::before{width:var(--orb-1);height:var(--orb-1);margin-top:calc(var(--orb-1) / -2);left:5vw;
  background:radial-gradient(closest-side,var(--orb-a,var(--accent)) 0%,rgba(var(--accent-rgb),.42) 48%,transparent 100%);
  animation:k-orb-a 34s ease-in-out infinite alternate}
.k-marquee::after{width:var(--orb-2);height:var(--orb-2);margin-top:calc(var(--orb-2) / -2);right:4vw;
  background:radial-gradient(closest-side,var(--orb-b,var(--accent-2)) 0%,rgba(var(--accent-2-rgb),.38) 50%,transparent 100%);
  animation:k-orb-b 47s ease-in-out infinite alternate}
/* the words sit still on top. --band-gap is the one lever: it spaces the row AND the
   separator inside each item, so a system tunes spacing in one place. */
.k-marquee .track{position:relative;z-index:1;display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:var(--band-gap);width:auto;margin:0 auto;padding:0 clamp(18px,4vw,48px);animation:none}
.k-marquee .item{font-family:var(--font-mono);font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);white-space:nowrap;display:inline-flex;align-items:center;gap:var(--band-gap)}
.k-marquee .item::after{content:"·";color:var(--accent)}
.k-marquee .item:last-child::after{content:none!important}
.k-marquee.logos .item{font-family:var(--font-display);font-size:18px;letter-spacing:var(--display-tracking);text-transform:var(--display-case);color:var(--dim)}
.k-marquee.glow .track{display:none}
.k-marquee.glow{padding:clamp(56px,7vw,92px) 0}
/* phones: the row always wraps, so a separator would land at a line end (HOUSE-RULES 56). Under
   700px every system drops its separators and spaces the words instead, 16px across and 14px
   between rows (Henry's Vault rule of 5 Oct 2026, now kit-wide). Systems set --band-gap on
   .k-marquee, so re-declaring it on the track wins for the words, and naming the track in the
   separator rule gives it one more class than any system's `.k-marquee .item::after`: no
   !important needed. Above 700px every system keeps its own gap and glyph. */
@media(max-width:700px){.k-marquee .track{--band-gap:16px;row-gap:14px}.k-marquee .item{font-size:11px}
  .k-marquee .track .item::after{content:none}}
@keyframes k-orb-a{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(14%,6%,0) scale(1.18)}}
@keyframes k-orb-b{from{transform:translate3d(0,0,0) scale(1.08)}to{transform:translate3d(-12%,-7%,0) scale(.94)}}

/* .fx-orb — the same drifting gradient orb as a primitive, droppable into any
   position:relative section (hero, CTA band, a services panel). Position it yourself. */
.fx-orb{position:absolute;z-index:0;pointer-events:none;border-radius:50%;
  width:var(--orb-size,46vmax);height:var(--orb-size,46vmax);
  background:radial-gradient(closest-side,var(--orb-a,var(--accent)) 0%,rgba(var(--accent-rgb),.34) 50%,transparent 100%);
  filter:blur(var(--orb-blur,70px));opacity:var(--orb-opacity,.18);will-change:transform;
  animation:k-orb-a 38s ease-in-out infinite alternate}
.fx-orb.fx-orb-2{background:radial-gradient(closest-side,var(--orb-b,var(--accent-2)) 0%,rgba(var(--accent-2-rgb),.3) 52%,transparent 100%);animation:k-orb-b 52s ease-in-out infinite alternate}
@media(max-width:860px){.k-marquee::before,.k-marquee::after,.fx-orb{filter:blur(34px);animation:none}}
@media (prefers-reduced-motion:reduce){.k-marquee::before,.k-marquee::after,.fx-orb{animation:none}}

/* ============================================================
   04 · SERVICES / OFFERS GRID  .k-services
   ============================================================ */
.k-services{padding:clamp(64px,8vw,112px) 0}
/* Jamie, 22 Sep 2026: the what-we-do cards were crowding each other. */
.k-services .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:var(--card-gap,clamp(20px,2.1vw,30px))}
.k-services .item{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:28px;box-shadow:var(--shadow);transition:.2s;display:flex;flex-direction:column;gap:12px}
.k-services .item:hover{border-color:var(--line-2);transform:translateY(-3px)}
.k-services .item .num{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;color:var(--accent-2)}
.k-services .item h3{font-size:21px}
.k-services .item p{color:var(--muted);font-size:14.5px;flex:1}
.k-services .item .link-arrow{align-self:flex-start;margin-top:6px}

.k-services .item{position:relative;overflow:hidden}
.k-services .item.has-img::before{content:"";position:absolute;inset:0;z-index:0;opacity:var(--card-img-opacity,.55);
  background-image:var(--card-img);background-size:cover;background-position:center;background-repeat:no-repeat}
.k-services .item>.num,.k-services .item>h3,.k-services .item>p,.k-services .item>.link-arrow{position:relative;z-index:1}
/* card image: slow zoom on hover (gated on the pointer having hover) */
@media(hover:hover) and (pointer:fine){
  .k-services .item.has-img::before{transition:transform .6s cubic-bezier(.2,.6,.2,1),opacity .6s ease}
  .k-services .item.has-img:hover::before{transform:scale(1.2);opacity:calc(var(--card-img-opacity,.55) * 2)}
}
@media(prefers-reduced-motion:reduce){.k-services .item.has-img::before{transition:none}}

/* the hovered card carries the glow; the resting grid stays flat (Jamie, 20 Sep 2026: the glow is for
   the box you are hovering on, not for every card that scrolls through the middle of the viewport).
   Lives in the system's own hover rule (forge.css) — box-shadow, so the card's overflow:hidden is fine. */

/* ============================================================
   05 · PROCESS / STEPS  .k-process
   ============================================================ */
.k-process{padding:clamp(64px,8vw,112px) 0;background:var(--bg-2);border-block:1px solid var(--line)}
.k-process .steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:0;counter-reset:step}
.k-process .step{padding:28px 26px 28px 0;border-top:1px solid var(--line-2);position:relative}
.k-process .step .num{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;color:var(--accent-2);margin-bottom:18px;display:block}
.k-process .step h3{font-size:20px;margin-bottom:10px}
.k-process .step p{color:var(--muted);font-size:14.5px}
.k-process .step::before{content:"";position:absolute;top:-1px;left:0;width:34px;height:1px;background:var(--accent)}

/* ============================================================
   06 · GALLERY  .k-gallery
   ============================================================ */
.k-gallery{padding:clamp(64px,8vw,112px) 0}
.k-gallery .grid{display:grid;grid-template-columns:repeat(4,1fr);grid-auto-rows:200px;gap:12px}
.k-gallery .tile{position:relative;border-radius:var(--radius-lg);overflow:hidden;background:var(--panel-2);border:1px solid var(--line);display:flex;align-items:flex-end;padding:14px}
.k-gallery .tile img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.k-gallery .tile .cap{position:relative;z-index:1;font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--text);background:var(--nav-bg);padding:5px 9px;border-radius:var(--radius)}
.k-gallery .tile.wide{grid-column:span 2}
.k-gallery .tile.tall{grid-row:span 2}
.k-gallery .tile[data-tone="1"],.tone[data-tone="1"]{background:linear-gradient(135deg,var(--accent-soft),var(--panel-2))}
.k-gallery .tile[data-tone="2"],.tone[data-tone="2"]{background:linear-gradient(160deg,var(--panel-2),var(--bg-2))}
.k-gallery .tile[data-tone="3"],.tone[data-tone="3"]{background:linear-gradient(135deg,var(--accent),var(--accent-2));}
.k-gallery .tile[data-tone="4"],.tone[data-tone="4"]{background:radial-gradient(at 30% 30%,var(--accent-soft),var(--panel-2) 70%)}
.k-gallery .tile[data-tone="5"],.tone[data-tone="5"]{background:linear-gradient(180deg,var(--panel),var(--accent-soft))}
.k-gallery .tile[data-tone="6"],.tone[data-tone="6"]{background:linear-gradient(135deg,var(--text),var(--accent-2))}
@media(max-width:860px){.k-gallery .grid{grid-template-columns:repeat(2,1fr);grid-auto-rows:160px}}

/* ============================================================
   07 · TESTIMONIALS / PROOF  .k-testimonials
   ============================================================ */
.k-testimonials{padding:clamp(64px,8vw,112px) 0;background:var(--bg-2);border-block:1px solid var(--line)}
.k-testimonials .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px}
.k-testimonials .quote{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:30px;box-shadow:var(--shadow);display:flex;flex-direction:column;gap:20px}
.k-testimonials .quote .mark{font-family:var(--font-display);font-size:44px;line-height:.6;color:var(--accent);height:22px}
.k-testimonials .quote p{font-size:17px;line-height:1.55;flex:1;--lines:6;display:-webkit-box;-webkit-line-clamp:var(--lines);-webkit-box-orient:vertical;overflow:hidden;min-height:calc(1.55em*var(--lines));min-height:calc(var(--lines)*1lh)}
.k-testimonials .quote.open p{-webkit-line-clamp:unset;display:block}
.k-testimonials .quote .more{align-self:flex-start;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-2);background:none;border:0;padding:0;cursor:pointer;visibility:hidden;margin-top:-6px}
.k-testimonials .quote.clamped .more{visibility:visible}
.k-testimonials .quote .more:hover{color:var(--text)}
.k-testimonials .who{display:flex;align-items:center;gap:12px}
.k-testimonials .who .avatar{width:38px;height:38px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent-2));flex:none;object-fit:cover}
.k-testimonials .who b{display:block;font-weight:600;font-size:14px}
.k-testimonials .who span{font-size:12.5px;color:var(--muted)}

/* ============================================================
   08 · PRICING  .k-pricing
   ============================================================ */
.k-pricing{padding:clamp(64px,8vw,112px) 0}
.k-pricing .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:18px;align-items:stretch}
.k-pricing .tier{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:30px;display:flex;flex-direction:column;gap:18px;box-shadow:var(--shadow)}
.k-pricing .tier.featured{border-color:var(--accent);position:relative}
.k-pricing .tier.featured::before{content:attr(data-badge);position:absolute;top:-12px;left:26px;background:var(--accent);color:var(--accent-ink);font-family:var(--font-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;padding:4px 10px;border-radius:var(--radius-pill)}
.k-pricing .tier .name{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-2)}
.k-pricing .price{font-family:var(--font-display);font-weight:var(--display-weight);letter-spacing:var(--display-tracking);font-size:48px;line-height:1}
.k-pricing .price small{font-family:var(--font-body);font-size:14px;color:var(--muted);letter-spacing:0;font-weight:400;text-transform:none}
.k-pricing .tier .desc{color:var(--muted);font-size:14.5px}
.k-pricing .tier ul{list-style:none;display:grid;gap:10px;flex:1;font-size:14.5px}
.k-pricing .tier li{display:flex;gap:10px;align-items:flex-start}
.k-pricing .tier li::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent);flex:none;margin-top:8px}
.k-pricing .tier .btn{width:100%}

/* consecutive pricing groups alternate ground so three in a row read as three sections */
.k-pricing+.k-pricing{background:var(--bg-2);border-block:1px solid var(--line)}
.k-pricing+.k-pricing+.k-pricing{background:var(--bg);border-block:0}
.k-pricing+.k-pricing+.k-pricing+.k-pricing{background:var(--bg-2);border-block:1px solid var(--line)}

/* ============================================================
   09 · FAQ / ACCORDION  .k-faq
   ============================================================ */
.k-faq{padding:clamp(64px,8vw,112px) 0;background:var(--bg-2);border-block:1px solid var(--line)}
.k-faq .wrap{display:grid;grid-template-columns:1fr 1.4fr;gap:48px}
.k-faq .list{border-top:1px solid var(--line-2)}
.k-faq details{border-bottom:1px solid var(--line-2)}
.k-faq summary{list-style:none;cursor:pointer;display:flex;justify-content:space-between;align-items:center;gap:20px;padding:20px 0;font-family:var(--font-display);font-weight:var(--display-weight);text-transform:var(--display-case);letter-spacing:var(--display-tracking);font-size:19px;line-height:1.25}
.k-faq summary::-webkit-details-marker{display:none}
.k-faq summary .ic{width:26px;height:26px;border-radius:50%;border:1px solid var(--line-2);display:grid;place-items:center;flex:none;position:relative;transition:.25s}
.k-faq summary .ic::before,.k-faq summary .ic::after{content:"";position:absolute;width:10px;height:1.5px;background:var(--text)}
.k-faq summary .ic::after{transform:rotate(90deg)}
.k-faq details[open] summary .ic{background:var(--accent);border-color:var(--accent)}
.k-faq details[open] summary .ic::before,.k-faq details[open] summary .ic::after{background:var(--accent-ink)}
.k-faq details[open] summary .ic::after{transform:rotate(0)}
.k-faq .a{padding:0 40px 22px 0;color:var(--muted);font-size:15px;max-width:60ch}
@media(max-width:860px){.k-faq .wrap{grid-template-columns:1fr;gap:20px}}

/* ============================================================
   10 · STATS / DATA  .k-stats
   ============================================================ */
/* Jamie, 22 Sep 2026: the band stays edge to edge, but its content lines up with the rest of
   the page. The first and last cells pad out to the page gutter, so the leading figure starts
   on the same line as every headline above it instead of against the viewport edge. Below
   --maxw the max() collapses to the ordinary cell padding, which is also the only width at
   which the cells wrap, so :first-child / :last-child are always the ends of a single row. */
.k-stats{border-block:1px solid var(--line);background:var(--bg)}
.k-stats>.wrap{padding-inline:28px}
.k-stats .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1px;background:var(--line)}
.k-stats .cell{padding:38px 22px;text-align:center;background:var(--bg)}
.k-stats .num{font-family:var(--font-display);font-weight:var(--display-weight);text-transform:var(--display-case);letter-spacing:var(--display-tracking);font-size:clamp(36px,5vw,56px);line-height:1}
.k-stats .lbl{margin-top:10px;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}

/* ============================================================
   11 · CTA BAND  .k-cta
   ============================================================ */
.k-cta{padding:clamp(64px,8vw,112px) 0}
.k-cta .band{background:var(--text);color:var(--bg);border-radius:var(--radius-lg);padding:clamp(40px,6vw,72px);display:grid;grid-template-columns:1.4fr 1fr;gap:32px;align-items:center;position:relative;overflow:hidden}
.k-cta .band h2{max-width:16ch}
.k-cta .band p{margin-top:14px;opacity:.72;max-width:46ch;font-size:16px}
.k-cta .band .cta{display:flex;gap:12px;flex-wrap:wrap;justify-content:flex-end}
.k-cta .band .btn-outline{color:var(--bg);border-color:var(--bg)}
.k-cta .band .btn-outline:hover{background:var(--bg);color:var(--text)}
.k-cta .band .glow{position:absolute;right:-120px;top:-120px;width:380px;height:380px;border-radius:50%;background:var(--accent);filter:blur(80px);opacity:.35;pointer-events:none}
@media(max-width:860px){.k-cta .band{grid-template-columns:1fr}.k-cta .band .cta{justify-content:flex-start}}

/* ============================================================
   12 · FOOTER  .k-footer
   ============================================================ */
.k-footer{border-top:1px solid var(--line);padding:clamp(40px,6vw,64px) 0 28px;background:var(--bg)}
.k-footer .grid{display:grid;grid-template-columns:1.6fr repeat(3,1fr);gap:32px}
.k-footer .brand{font-family:var(--font-display);font-weight:var(--display-weight);text-transform:var(--display-case);letter-spacing:var(--display-tracking);font-size:22px}
.k-footer .blurb{margin-top:12px;color:var(--muted);font-size:14px;max-width:34ch}
.k-footer h3,.k-footer h4{font-family:var(--font-mono);font-weight:400;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-2);margin-bottom:14px;letter-spacing:.14em}
.k-footer ul{list-style:none;display:grid;gap:9px}
.k-footer li a{text-decoration:none;color:var(--muted);font-size:14.5px;transition:.15s;display:inline-block;padding:3px 0}
.k-footer li a:hover{color:var(--text)}
.k-footer .legal a{color:var(--dim);text-decoration:none;border-bottom:1px solid var(--line-2)}
.k-footer .legal a:hover{color:var(--text)}
.k-footer .legal{margin-top:44px;padding-top:20px;border-top:1px solid var(--line);display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--dim)}
@media(max-width:860px){.k-footer .grid{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.k-footer .grid{grid-template-columns:1fr}}


/* ============================================================
   13 · CONTACT  .k-contact  (Henry, henry/about-contact-components; Netlify Forms wired)
   ============================================================ */
.k-contact{padding:clamp(64px,8vw,112px) 0;background:var(--bg-2);border-top:1px solid var(--line)}
.k-contact .cols{display:grid;grid-template-columns:.85fr 1.15fr;gap:48px;margin-top:28px;align-items:start}
.k-contact .info .row{display:flex;justify-content:space-between;gap:18px;padding:15px 0;border-bottom:1px solid var(--line)}
.k-contact .info .lbl{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.k-contact .info .val{font-size:15px;text-align:right}
.k-contact .info .note{margin-top:20px;color:var(--dim);font-size:13.5px;line-height:1.6;max-width:40ch}
.k-contact .fields{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.k-contact .field{display:flex;flex-direction:column;gap:8px}
.k-contact .field.wide{grid-column:1/-1}
.k-contact .field span{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.k-contact input,.k-contact select,.k-contact textarea{font-family:var(--font-body);font-size:15px;color:var(--text);background:var(--panel);border:1px solid var(--line-2);border-radius:var(--radius);padding:13px 14px;width:100%}
.k-contact textarea{resize:vertical;min-height:120px}
.k-contact input::placeholder,.k-contact textarea::placeholder{color:var(--dim)}
.k-contact input:focus,.k-contact select:focus,.k-contact textarea:focus{outline:none;border-color:var(--accent)}
.k-contact .form .btn{margin-top:20px}
.k-contact .form-note{margin-top:12px;font-size:13px;color:var(--dim)}
.k-contact .form-note a{color:var(--muted);border-bottom:1px solid var(--line-2);text-decoration:none}
@media(max-width:860px){.k-contact .cols{grid-template-columns:1fr;gap:30px}.k-contact .fields{grid-template-columns:1fr}}


.k-contact .hp{position:absolute;left:-9999px;opacity:0;height:0;overflow:hidden}
.k-contact .thanks{padding:22px 24px;border:1px solid var(--accent);border-radius:var(--radius-lg);background:var(--accent-soft);color:var(--text);font-size:15px}

/* Onboarding extras (additive — only render when a build supplies contact.groups /
   contact.steps / contact.wide_form; every existing build is untouched). */
.k-contact .cols.wide-form{grid-template-columns:.62fr 1.38fr}
.k-contact .info .step{display:flex;gap:14px;padding:14px 0;border-bottom:1px solid var(--line)}
.k-contact .info .step .n{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;color:var(--accent);padding-top:3px}
.k-contact .info .step h4{font-size:15px;font-weight:600}
.k-contact .info .step p{margin-top:4px;color:var(--dim);font-size:13.5px;line-height:1.55}
.k-contact fieldset.group{border:0;padding:0;margin:34px 0 0}
.k-contact fieldset.group+fieldset.group{margin-top:38px;padding-top:30px;border-top:1px solid var(--line)}
.k-contact fieldset.group legend{display:flex;align-items:baseline;gap:10px;padding:0;margin-bottom:6px;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--text)}
.k-contact fieldset.group legend .step-n{color:var(--accent)}
.k-contact .group-note{margin:0 0 18px;color:var(--dim);font-size:13.5px;line-height:1.6;max-width:56ch}
.k-contact .field .hint{display:block;margin-top:-2px;font-style:normal;font-size:12.5px;color:var(--dim);line-height:1.5}
.k-contact .checks{display:flex;flex-direction:column;gap:11px}
.k-contact .check{display:flex;align-items:flex-start;gap:10px;cursor:pointer}
.k-contact .check input{width:16px;height:16px;flex:none;margin-top:2px;padding:0;accent-color:var(--accent)}
.k-contact .check span{font-family:var(--font-body);font-size:14.5px;letter-spacing:0;text-transform:none;color:var(--muted);line-height:1.5}
.k-contact .form-status{margin-top:14px;font-size:14px;color:var(--accent-2)}
.k-contact .form-status[data-state=error]{color:#ff6b6b}
.k-contact .form.sending button[type=submit]{opacity:.6;pointer-events:none}
@media(max-width:860px){.k-contact .cols.wide-form{grid-template-columns:1fr}}

/* ============================================================
   14 · ARTICLE  .k-article  +  15 · ARTICLES (index)  .k-articles
   ============================================================ */
.k-article{padding:clamp(56px,7vw,96px) 0}
.k-article .head{max-width:60ch}
.k-article .head h1{font-size:clamp(34px,4.6vw,58px)}
.k-article .meta{margin-top:18px;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);display:flex;gap:18px;flex-wrap:wrap}
.k-article .lede{margin-top:22px;font-size:19px;line-height:1.55;color:var(--muted);max-width:56ch}
.k-article .body{margin-top:44px;max-width:64ch;font-size:17px;line-height:1.72}
.k-article .body h2{font-size:clamp(24px,2.6vw,32px);margin:44px 0 14px}
.k-article .body h3{font-size:21px;margin:32px 0 10px}
.k-article .body p{margin:0 0 18px}
.k-article .body ul,.k-article .body ol{margin:0 0 18px 22px}
.k-article .body li{margin-bottom:8px}
.k-article .body a{color:var(--accent-2);text-decoration:none;border-bottom:1px solid var(--accent)}
.k-article .body blockquote{margin:28px 0;padding:6px 0 6px 22px;border-left:2px solid var(--accent);color:var(--muted);font-size:18px}
.k-article .body code{font-family:var(--font-mono);font-size:.9em;background:var(--panel-2);padding:2px 6px;border-radius:4px}
.k-article .body pre{background:var(--panel-2);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px;overflow-x:auto;margin:0 0 20px}
.k-article .body pre code{background:none;padding:0}
.k-article .body hr{border:0;height:1px;background:var(--line);margin:36px 0}
.k-article .body strong{font-weight:600;color:var(--text)}
.k-article .back{margin-top:48px;display:inline-block}
.k-articles{padding:clamp(64px,8vw,112px) 0}
.k-articles .list{display:grid;gap:0;border-top:1px solid var(--line-2);margin-top:18px}
.k-articles{padding-top:clamp(36px,4vw,56px)}
.k-articles .item{display:grid;grid-template-columns:1fr auto;gap:28px;align-items:baseline;padding:26px 0;border-bottom:1px solid var(--line);text-decoration:none;color:inherit;transition:.2s}
.k-articles .item:hover h3{color:var(--accent-2)}
.k-articles .item h3{font-size:22px;transition:.2s}
.k-articles .item p{margin-top:8px;color:var(--muted);font-size:15px;max-width:62ch}
.k-articles .item .when{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);white-space:nowrap}
@media(max-width:860px){.k-articles .item{grid-template-columns:1fr;gap:6px}}
/* ============================================================
   16 · REVIEW  .k-review  — page component: the review-collection page a
   client sends its customers (text the link, they fill it on their phone).
   Netlify Forms + honeypot like contact. One template, two states: the form,
   or with review.thanks the thank-you page with the optional "copy it, post it
   on Google" step. Carries the page's h1, so no hero on this page.
   ============================================================ */
.k-review{padding:clamp(56px,7vw,96px) 0 clamp(72px,9vw,128px);background:var(--bg-2);border-top:1px solid var(--line)}
.k-review .wrap{max-width:720px}
.k-review .head{max-width:56ch;margin-bottom:clamp(28px,4vw,44px)}
.k-review .head h1{font-size:clamp(34px,4.8vw,56px);margin-top:14px}
.k-review .head .sub{margin-top:16px;color:var(--muted);font-size:17px;line-height:1.6}
.k-review .form{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:clamp(22px,3vw,34px)}
.k-review .rating{border:0;padding:0;margin:0 0 22px;min-width:0}
.k-review .rating legend{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);margin-bottom:10px;padding:0}
.k-review .stars{display:flex;gap:6px}
.k-review .stars label{position:relative;cursor:pointer;padding:2px;border-radius:6px;line-height:0}
.k-review .stars input{position:absolute;opacity:0;width:1px;height:1px;margin:0}
.k-review .stars .star{width:34px;height:34px;fill:var(--line-2);transition:fill .12s,transform .12s}
.k-review .stars label:hover .star{transform:scale(1.08)}
.k-review .stars input:checked+.star{fill:var(--accent)}
.k-review .stars:has(input[value="2"]:checked) label:nth-child(-n+2) .star,
.k-review .stars:has(input[value="3"]:checked) label:nth-child(-n+3) .star,
.k-review .stars:has(input[value="4"]:checked) label:nth-child(-n+4) .star,
.k-review .stars:has(input[value="5"]:checked) label:nth-child(-n+5) .star{fill:var(--accent)}
.k-review .stars:has(label:nth-child(2):hover) label:nth-child(-n+2) .star,
.k-review .stars:has(label:nth-child(3):hover) label:nth-child(-n+3) .star,
.k-review .stars:has(label:nth-child(4):hover) label:nth-child(-n+4) .star,
.k-review .stars:has(label:nth-child(5):hover) label:nth-child(-n+5) .star,
.k-review .stars label:hover .star{fill:var(--accent-2)}
.k-review .stars label:has(input:focus-visible){outline:2px solid var(--accent-2);outline-offset:2px}
.k-review .sr{position:absolute;left:-9999px}
.k-review .fields{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.k-review .field{display:flex;flex-direction:column;gap:8px}
.k-review .field.wide{grid-column:1/-1}
.k-review .field span{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.k-review .field .hint{font-style:normal;font-size:12.5px;color:var(--dim)}
.k-review input,.k-review select,.k-review textarea{font-family:var(--font-body);font-size:16px;color:var(--text);background:var(--bg);border:1px solid var(--line-2);border-radius:var(--radius);padding:13px 14px;width:100%}
.k-review textarea{resize:vertical;min-height:150px}
.k-review input::placeholder,.k-review textarea::placeholder{color:var(--dim)}
.k-review input:focus,.k-review select:focus,.k-review textarea:focus{outline:none;border-color:var(--accent)}
.k-review .consent{display:flex;gap:12px;align-items:flex-start;margin-top:20px;font-size:14.5px;color:var(--muted);line-height:1.5;cursor:pointer}
.k-review .consent input{width:18px;height:18px;margin-top:2px;flex:none;accent-color:var(--accent)}
.k-review .form .btn{margin-top:22px}
.k-review .form-note{margin-top:12px;font-size:13px;color:var(--dim)}
.k-review .hp{position:absolute;left:-9999px;opacity:0;height:0;overflow:hidden}
.k-review .google{padding:clamp(20px,3vw,28px);border:1px solid var(--line);border-radius:var(--radius-lg);background:var(--panel)}
.k-review .google-lead{font-size:16px;line-height:1.6;color:var(--muted);max-width:52ch}
.k-review .echo{margin:18px 0 0;padding:16px 18px;border-left:2px solid var(--accent);background:var(--bg);border-radius:0 var(--radius) var(--radius) 0;font-size:16px;line-height:1.6;color:var(--text);white-space:pre-wrap}
.k-review .google .actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:20px}
.k-review .back{display:inline-block;margin-top:28px;color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line-2)}
.k-review .back:hover{color:var(--text);border-color:var(--accent)}
@media(max-width:640px){.k-review .fields{grid-template-columns:1fr}.k-review .form .btn{width:100%}.k-review .stars .star{width:38px;height:38px}}

/* ---- section background image, any component: data-bg="url" data-bg-opacity=".1" on the section (kit.js paints it) ---- */
.k-bg{position:relative;isolation:isolate}
.k-bg>.bgimg{position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:var(--bg-fit,cover);object-position:var(--bg-pos,center);opacity:var(--bg-opacity,.1);pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent);mask-image:linear-gradient(180deg,transparent,#000 18%,#000 82%,transparent)}
.k-bg>*:not(.bgimg){position:relative;z-index:1}
.k-cta .band .bandimg{position:absolute;right:-6%;top:50%;transform:translateY(-50%);height:150%;width:auto;opacity:var(--band-img-opacity,.08);pointer-events:none;z-index:0}
.k-cta .band>*:not(.bandimg):not(.glow){position:relative;z-index:1}
.k-article .body figure{margin:36px 0}
.k-article .body figure img{width:100%;border-radius:var(--radius-lg);border:1px solid var(--line)}
.k-article .body figcaption{margin-top:10px;font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--dim)}

/* ---- services item figure strip (image below the text, never behind it) ---- */
.k-services .item .figure{display:block;margin:22px -28px -28px;border-top:1px solid var(--line);border-radius:0 0 var(--radius-lg) var(--radius-lg);overflow:hidden;aspect-ratio:3/2;background:var(--panel-2)}
.k-services .item .figure img{width:100%;height:100%;object-fit:cover;object-position:top left;opacity:var(--figure-opacity,.55);transition:opacity .5s,transform .8s cubic-bezier(.2,.6,.2,1)}
@media(hover:hover){.k-services .item:hover .figure img{opacity:1;transform:scale(1.04)}}

/* ============================================================
   16 · WORK  .k-work  — desktop shot with the phone overlaid, rows of 3 (2 on mobile)
   ============================================================ */
.k-work{padding:clamp(64px,8vw,112px) 0}
.k-work .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}
.k-work .item{display:block;text-decoration:none;color:inherit;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);overflow:hidden;transition:transform .35s cubic-bezier(.22,1,.36,1),border-color .35s}
.k-work .item:hover{transform:translateY(-4px);border-color:var(--line-2)}
.k-work .shots{display:block;position:relative;aspect-ratio:4/3;background:var(--panel-2);overflow:hidden;padding:26px 30% 0 8%}
.k-work .shots .desk{position:absolute;left:8%;top:9%;width:78%;height:auto;border-radius:6px;border:1px solid var(--line-2);box-shadow:0 20px 40px -20px rgba(0,0,0,.7)}
.k-work .shots .mob{position:absolute;right:7%;bottom:-3%;width:26%;height:auto;border-radius:14px;border:2px solid var(--line-2);box-shadow:0 24px 50px -18px rgba(0,0,0,.85);z-index:2}
.k-work .meta{display:flex;flex-direction:column;gap:4px;padding:18px 20px 20px}
.k-work .name{font-family:var(--font-display);font-weight:var(--display-weight);font-size:19px;letter-spacing:var(--display-tracking)}
.k-work .what{color:var(--muted);font-size:14px}
.k-work .sys{margin-top:6px;font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent-2)}
@media(max-width:1024px){.k-work .grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.k-work .grid{grid-template-columns:repeat(2,1fr);gap:12px}.k-work .meta{padding:12px 12px 14px}.k-work .name{font-size:15px}.k-work .what{font-size:12px}}

/* ============================================================
   17 · CHANGES  .k-changes — the client's change-request page (update.<domain>/ or /changes/)
   Gated by an access code baked into the build; posts to Netlify Forms (form "change").
   ============================================================ */
.k-changes{padding:clamp(56px,7vw,96px) 0;min-height:70vh}
.k-changes .unlock{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;max-width:520px}
.k-changes .unlock .field{flex:1;min-width:220px}
.k-changes .field{display:flex;flex-direction:column;gap:8px}
.k-changes .field>span:first-child{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
.k-changes input:not([type=radio]):not([type=file]),.k-changes select,.k-changes textarea{font-family:var(--font-body);font-size:15px;color:var(--text);background:var(--panel);border:1px solid var(--line-2);border-radius:var(--radius);padding:13px 14px;width:100%}
.k-changes textarea{resize:vertical;min-height:120px}
.k-changes input:focus,.k-changes select:focus,.k-changes textarea:focus{outline:none;border-color:var(--accent)}
.k-changes .fields{display:grid;grid-template-columns:1fr 1fr;gap:16px;max-width:820px}
.k-changes .field.wide{grid-column:1/-1}
.k-changes .kinds{display:flex;flex-wrap:wrap;gap:8px}
.k-changes .kind{position:relative}
.k-changes .kind input{position:absolute;opacity:0;inset:0}
.k-changes .kind span{display:inline-block;padding:9px 14px;border:1px solid var(--line-2);border-radius:var(--radius-pill);font-size:14px;cursor:pointer;transition:.15s}
.k-changes .kind input:checked+span{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.k-changes .kind input:focus-visible+span{outline:2px solid var(--accent-2);outline-offset:2px}
.k-changes .drop{position:relative}
.k-changes .drop .picker{position:absolute;inset:0;opacity:0;cursor:pointer;z-index:2}
.k-changes .drop input.slot{display:none}
.k-changes .dropzone{position:relative;display:block;padding:28px 18px;border:1px dashed var(--line-2);border-radius:var(--radius-lg);text-align:center;color:var(--muted);font-size:14px;transition:.15s;cursor:pointer}
.k-changes .filelist{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:6px}
.k-changes .filelist li{display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:10px;padding:9px 12px;background:var(--panel);border:1px solid var(--line-2);border-radius:var(--radius);font-size:14px}
.k-changes .filelist .fn{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.k-changes .filelist .fz{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--dim)}
.k-changes .drop-file{background:none;border:0;color:var(--dim);font-size:19px;line-height:1;cursor:pointer;padding:0 2px}
.k-changes .drop-file:hover{color:var(--text)}
.k-changes .drop.over .dropzone,.k-changes .drop:hover .dropzone{border-color:var(--accent);color:var(--text)}
.k-changes .preview{display:block;margin-top:10px;font-family:var(--font-mono);font-size:11px;letter-spacing:.08em;color:var(--accent-2)}
.k-changes .preview img{display:block;max-height:140px;border-radius:var(--radius);border:1px solid var(--line);margin-top:8px}
.k-changes .form .btn{margin-top:22px}
.k-changes .form-note{margin-top:12px;font-size:13px;color:var(--dim);max-width:60ch}
.k-changes .form-note a{color:var(--muted);border-bottom:1px solid var(--line-2);text-decoration:none}
.k-changes .hp{position:absolute;left:-9999px;opacity:0;height:0;overflow:hidden}
@media(max-width:860px){.k-changes .fields{grid-template-columns:1fr}}

.k-changes .who[hidden]{display:none}
.k-changes .who{display:inline-block;margin-bottom:18px;padding:7px 14px;border:1px solid var(--accent);border-radius:999px;background:var(--accent-soft);font-family:var(--font-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--text)}
/* Repeatable change rows (changes.list_mode) — the pre-launch punch list. */
.k-changes .items{gap:12px}
.k-changes .item-rows{display:flex;flex-direction:column;gap:12px}
.k-changes .item{display:grid;grid-template-columns:auto 1fr auto;gap:12px;align-items:start;padding:14px;border:1px solid var(--line-2);border-radius:var(--radius);background:var(--panel)}
.k-changes .item-n{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;color:var(--accent);padding-top:14px}
.k-changes .item-body{display:flex;flex-direction:column;gap:8px;min-width:0}
.k-changes .drop-item{background:none;border:0;color:var(--dim);font-size:20px;line-height:1;cursor:pointer;padding:10px 4px}
.k-changes .drop-item:hover{color:var(--text)}
.k-changes .add-item{margin-top:4px;align-self:flex-start}
.k-changes .hint{display:block;margin-top:6px;font-style:normal;font-family:var(--font-body);font-size:12.5px;letter-spacing:0;text-transform:none;color:var(--dim);line-height:1.5}
.k-changes .form-status{margin-top:14px;font-size:14px;color:var(--accent-2)}
.k-changes .form-status[data-state=error]{color:#ff6b6b}
@media(max-width:600px){.k-changes .item{grid-template-columns:auto 1fr;gap:10px}.k-changes .drop-item{grid-column:2;justify-self:end;padding:0}}
/* ============================================================
   LAB CHROME — only on henry-designs system pages (body.lab)
   ============================================================ */
body.lab .k-tag{position:absolute;top:10px;left:10px;z-index:5;font-family:'JetBrains Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:#fff;background:rgba(94,92,230,.92);padding:4px 8px;border-radius:4px;pointer-events:none}
.k-tag{display:none}
body.lab .k-tag{display:inline-block}
.labbar{background:#0C0C0E;color:#F4F4F2;font-family:'Inter',system-ui,sans-serif;font-size:13px;border-bottom:1px solid #26262E;position:relative;z-index:60}
.labbar .in{max-width:1180px;margin:0 auto;padding:0 20px;height:44px;display:flex;align-items:center;justify-content:space-between;gap:16px}
.labbar a{color:#9A9AA3;text-decoration:none;padding:5px 9px;border-radius:6px;font-size:12.5px;white-space:nowrap}
.labbar a:hover{color:#F4F4F2;background:#17171C}
.labbar a.on{color:#F4F4F2;background:#1D1D24}
.labbar .sys{display:flex;gap:2px;overflow-x:auto;scrollbar-width:none}
.labbar .sys::-webkit-scrollbar{display:none}
.labbar .home{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.08em;color:#7B79F5}
.lab-foundation{padding:clamp(56px,7vw,96px) 0;border-top:1px solid var(--line);background:var(--bg)}
.lab-foundation .grid{display:grid;grid-template-columns:1fr 1fr;gap:40px}
.lab-foundation h4{margin-bottom:14px;font-size:19px}
.swatches{display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:10px}
.swatch{height:74px;border-radius:var(--radius);border:1px solid var(--line);display:flex;align-items:flex-end;padding:8px;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.04em}
.swatch span{background:rgba(255,255,255,.82);color:#000;padding:2px 5px;border-radius:3px}
.type-row{display:flex;align-items:baseline;gap:18px;padding:16px 0;border-bottom:1px solid var(--line)}
.type-row:last-child{border-bottom:0}
.type-row .sample{flex:1;min-width:0}
.type-row .meta{font-family:var(--font-mono);font-size:10.5px;color:var(--accent-2);white-space:nowrap}
.lab-note{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.06em;color:var(--dim);padding:22px 0 40px;border-top:1px solid var(--line)}
@media(max-width:860px){.lab-foundation .grid{grid-template-columns:1fr}}

/* ---- mobile: orbit art and lab tags get out of the way ---- */
@media(max-width:860px){
  .k-hero .orbit,.k-hero .hart{display:none}
  body.lab .k-tag{display:none}
}

/* ============================================================
   OVERRIDE-SAFE MOBILE RULES — last in the cascade on purpose.
   System layers load after base and may restyle any grid; these
   !important rules guarantee the mobile stack survives regardless.
   ============================================================ */
@media(max-width:860px){
  .k-hero.split .wrap{grid-template-columns:1fr!important}
  .k-hero.split .visual{min-height:240px}
  .k-faq .wrap{grid-template-columns:1fr!important;gap:20px!important}
  .k-cta .band{grid-template-columns:1fr!important}
  .k-gallery .grid{grid-template-columns:repeat(2,1fr)!important;grid-auto-rows:160px!important}
  .k-gallery .tile.tall{grid-row:span 1!important}
  .k-footer .grid{grid-template-columns:1fr 1fr!important}
  .k-services .grid,.k-process .steps,.k-testimonials .grid,.k-pricing .grid{grid-template-columns:1fr!important}
  .k-work .grid{grid-template-columns:repeat(2,1fr)!important}
  .k-services .item,.k-gallery .tile,.k-pricing .tier{grid-column:auto!important}
  .k-nav .links{display:none!important}
}
@media(max-width:860px){
  .type-row{flex-wrap:wrap;gap:4px 14px}
  .type-row .sample{flex:1 1 100%}
  .type-row .meta{white-space:normal}
}
@media(max-width:520px){
  .k-footer .grid{grid-template-columns:1fr!important}
  .k-gallery .tile.wide{grid-column:span 2}
}

/* ============================================================================
   Ported from the client-build line (18 Sep 2026): brand logo, hero background
   video, dot field, gallery zoom + lightbox, parallax frames, about component.
   ============================================================================ */

/* nav · brand logo: `nav.logo` = the client's wordmark/lockup instead of the text (height nav.logo_height, default
   26–34px); `nav.logo_mark` = the client's mark/badge/mascot BESIDE the text wordmark, replacing the system's dot
   (default 34px) — the right slot for square logos that would be a smudge at wordmark height. The footer takes
   `footer.logo` (+ footer.logo_height, default 64px) where the full lockup has room to read. HOUSE-RULES 4d. */
.k-nav .brand .brand-logo{display:block;height:var(--logo-h,clamp(26px,2.4vw,34px));width:auto}
.k-nav .brand .brand-mark{display:block;height:var(--logo-h,34px);width:auto;margin-right:2px}
.k-nav .brand{white-space:nowrap}
@media(max-width:480px){.k-nav .brand .brand-mark{height:min(var(--logo-h,34px),32px)}.k-nav .brand{font-size:17px}}
.k-footer .brand-logo{display:block;height:var(--footer-logo-h,64px);width:auto;margin-bottom:16px}

/* hero · background video + scrim */
.k-hero.has-video{--text:#fff;--muted:rgba(255,255,255,.8);--dim:rgba(255,255,255,.62);--line:rgba(255,255,255,.18);--line-2:rgba(255,255,255,.3)}
.k-hero .bgvideo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* hero.image — a photo at low opacity under the copy (Jamie, 20 Sep 2026: contractor / service samples carry one).
   A direct child of the hero, UNDER the art layer, so --hero-img-opacity is exactly what the visitor sees on every
   system (the art layer's own opacity no longer multiplies it). Soft edge mask so it fades out under the copy.
   Ken Burns by default: a slow drift-and-zoom over 36 s, back and forth, paused while the hero is off-screen
   (kit.js) and off under reduced-motion; hero.image_motion "none" switches it off. */
.k-hero{position:relative}
.k-hero .bgimg{position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:cover;object-position:center;opacity:var(--hero-img-opacity,.28);pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 55%,transparent 100%);mask-image:linear-gradient(180deg,#000 0%,#000 55%,transparent 100%);
  transform-origin:50% 50%;will-change:transform;animation:k-burns 36s ease-in-out infinite alternate}
@keyframes k-burns{from{transform:scale(1.02) translate3d(0,0,0)}to{transform:scale(1.14) translate3d(-1.6%,-1.2%,0)}}
.k-hero[data-image-motion="none"] .bgimg,.k-hero.offscreen .bgimg{animation-play-state:paused}
.k-hero[data-image-motion="none"] .bgimg{animation:none;transform:scale(1.02)}
@media (prefers-reduced-motion:reduce){.k-hero .bgimg{animation:none;transform:none}}
/* Jamie, 22 Sep 2026: EVERY hero background image drifts, not just .bgimg — a photo used
   as the split-layout visual and a background video get the same slow Ken Burns, a little
   longer so a moving picture does not fight itself. Parallax images are left alone: they
   already own their scale, and two transforms on one element compound. */
.k-hero.split .visual>img:not([data-parallax]),.k-hero .visual>picture>img:not([data-parallax]),.k-hero .bgvideo:not([data-parallax]){
  transform-origin:50% 50%;will-change:transform;animation:k-burns-slow 52s ease-in-out infinite alternate}
@keyframes k-burns-slow{from{transform:scale(1.03) translate3d(.8%,.6%,0)}to{transform:scale(1.13) translate3d(-1.1%,-.9%,0)}}
.k-hero[data-image-motion="none"] .visual>img,.k-hero[data-image-motion="none"] .bgvideo{animation:none}
.k-hero.offscreen .visual>img,.k-hero.offscreen .bgvideo{animation-play-state:paused}
@media (prefers-reduced-motion:reduce){.k-hero .visual>img,.k-hero .bgvideo{animation:none;transform:none}}
.k-hero .bgscrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(6,7,8,.74),rgba(6,7,8,.52) 45%,rgba(6,7,8,.84))}
@media(prefers-reduced-motion:reduce){.k-hero .bgvideo{display:none}}

/* photo heroes: a scrim in the hero's OWN ground colour, drawn from the copy side, so body
   text stays legible over a busy photograph without dimming the picture everywhere. Fades to
   fully transparent (premultiplied), so it tints nothing on a plain photo, and it is skipped
   on centred heroes where the copy sits over the middle. --hero-scrim tunes the reach. */
.k-hero.has-image>.scrim{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(90deg,var(--bg) 0%,var(--bg) 8%,transparent var(--hero-scrim,64%))}
.k-hero.has-image.center>.scrim{background:linear-gradient(180deg,transparent,var(--bg) 96%)}
@media(max-width:860px){.k-hero.has-image>.scrim{--hero-scrim:88%}}

/* hero · ambient glow (Jamie, 22 Sep 2026) — the hero's standard motion layer, in place of
   the survey grid and its laser sweep. Two blurred orbs drifting behind the copy on long,
   independent paths; sized to the hero rather than the viewport so they read as blobs and
   not as a wash, and pinned to the two sides so the copy column stays clean. Paused with
   the rest of the hero when it scrolls out, off under reduced-motion. */
.k-hero .horb{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none;display:block}
.k-hero .horb .fx-orb{--orb-size:min(58vw,760px);--orb-blur:78px;--orb-opacity:.3;
  left:-12vw;top:-16%}
.k-hero .horb .fx-orb-2{--orb-size:min(46vw,600px);--orb-opacity:.24;left:auto;right:-9vw;top:auto;bottom:-22%}
.k-hero.has-image .horb .fx-orb{--orb-opacity:.22}
.k-hero.offscreen .horb .fx-orb{animation-play-state:paused}
@media(max-width:860px){.k-hero .horb .fx-orb{--orb-blur:44px;--orb-opacity:.22}}

/* hero · dot field */
.k-hero .dots{position:absolute;inset:0;width:100%;height:100%;display:block}

/* hero.grid — the SURVEY GRID (Jamie, 21 Sep 2026: replaced Vault's orbit ring; the photo is the visual now, this is
   the motion over it). Fine graph paper inked from the system's text colour (14% minor, 26% every 4th line — light on a
   dark ground, dark on a light one, so it reads over the photo on every system; the hairline tokens are too close to the
   ground to show) drifting slowly down-right while the photo's Ken Burns drifts up-left, plus a laser-level line in the
   client's accent that sweeps top to bottom every 11 s. Masked to the top-right so it fades under the copy. --grid-opacity
   is the visibility dial (hero.grid.opacity, 1 = as designed), --grid-size the cell (hero.grid.size, px), --grid-drift
   the loop (hero.grid.drift), hero.grid.no_sweep drops the laser line. Transforms only, so the compositor carries it;
   paused off-screen (kit.js), static under reduced-motion. A system whose art layer already draws a static grid
   (Forge, Level) yields to it. */
.k-hero .hgrid{position:absolute;inset:0;overflow:hidden;opacity:var(--grid-opacity,1);
  --grid-ink:color-mix(in srgb,var(--text) 14%,transparent);--grid-ink-2:color-mix(in srgb,var(--text) 26%,transparent);
  -webkit-mask-image:radial-gradient(125% 125% at 68% 6%,#000 0%,rgba(0,0,0,.65) 45%,transparent 85%);
  mask-image:radial-gradient(125% 125% at 68% 6%,#000 0%,rgba(0,0,0,.65) 45%,transparent 85%)}
.k-hero .hgrid i{position:absolute;top:calc(-4 * var(--grid-size,62px));left:calc(-4 * var(--grid-size,62px));right:0;bottom:0;
  background-image:
    linear-gradient(var(--grid-ink-2) 1px,transparent 1px),linear-gradient(90deg,var(--grid-ink-2) 1px,transparent 1px),
    linear-gradient(var(--grid-ink) 1px,transparent 1px),linear-gradient(90deg,var(--grid-ink) 1px,transparent 1px);
  background-size:calc(4 * var(--grid-size,62px)) calc(4 * var(--grid-size,62px)),calc(4 * var(--grid-size,62px)) calc(4 * var(--grid-size,62px)),
    var(--grid-size,62px) var(--grid-size,62px),var(--grid-size,62px) var(--grid-size,62px);
  will-change:transform;animation:k-gridDrift var(--grid-drift,72s) linear infinite}
@keyframes k-gridDrift{from{transform:translate3d(0,0,0)}to{transform:translate3d(calc(4 * var(--grid-size,62px)),calc(4 * var(--grid-size,62px)),0)}}
.k-hero .hgrid b{position:absolute;inset:0;opacity:0;will-change:transform;
  background:linear-gradient(180deg,rgba(var(--accent-rgb),.9) 0,rgba(var(--accent-rgb),.9) 2px,rgba(var(--accent-rgb),.14) 3px,transparent 140px);
  animation:k-gridSweep 11s cubic-bezier(.45,0,.55,1) infinite}
@keyframes k-gridSweep{0%{transform:translateY(-3px);opacity:0}5%{opacity:1}62%{transform:translateY(100%);opacity:1}64%,100%{transform:translateY(100%);opacity:0}}
.k-hero.offscreen .hgrid i,.k-hero.offscreen .hgrid b{animation-play-state:paused}
.k-hero.has-grid .art{background-image:none;opacity:1;-webkit-mask-image:none;mask-image:none}
@media (prefers-reduced-motion:reduce){.k-hero .hgrid i{animation:none}.k-hero .hgrid b{display:none}}

/* gallery · zoom target, grid CTA, lightbox */
.k-gallery .tile-zoom{position:absolute;inset:0;z-index:2;cursor:zoom-in}
.k-gallery .tile-zoom:focus-visible{outline:2px solid var(--accent);outline-offset:-3px}
.k-gallery .grid-cta{margin-top:30px;display:flex;justify-content:center}
.k-gallery .tile img[data-parallax]{scale:1.3}
.k-gallery .tile img[data-parallax]{transition:scale .7s cubic-bezier(.2,.6,.2,1),filter .7s ease}
.k-gallery .tile:hover img[data-parallax]{scale:1.42;filter:brightness(1.12)}
.k-hero.split .visual img[data-parallax],.k-hero .bgvideo[data-parallax]{scale:1.18}
@media(prefers-reduced-motion:reduce){.k-gallery .tile img[data-parallax],.k-hero .visual img[data-parallax],.k-hero .bgvideo[data-parallax]{scale:1}}

.k-lightbox{border:0;padding:0;background:transparent;max-width:min(1500px,94vw);margin:auto}
.k-lightbox::backdrop{background:rgba(4,4,5,.92)}
.k-lightbox img{display:block;margin:0 auto;max-width:94vw;max-height:82vh;width:auto;height:auto;border-radius:var(--radius-lg)}
.k-lightbox .lb-cap{margin-top:14px;font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.72);text-align:center}
.k-lightbox .lb-close{position:absolute;top:-52px;right:0;width:42px;height:42px;border-radius:50%;border:1px solid rgba(255,255,255,.35);background:rgba(0,0,0,.5);color:#fff;font:600 14px/1 var(--font-body);cursor:pointer}
.k-lightbox .lb-close:hover{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}

/* footer · client badges */
.k-footer .logos{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin-top:18px}
.k-footer .logos img{height:34px;width:auto;display:block}

/* about */
.k-about{padding:clamp(64px,8vw,112px) 0;border-top:1px solid var(--line)}
.k-about .cols{display:grid;grid-template-columns:1.15fr .85fr;gap:48px;align-items:stretch}
.k-about .text .sec-head{margin-bottom:clamp(22px,3vw,36px)}
.k-about .copy p{color:var(--muted);font-size:16.5px;line-height:1.7;max-width:62ch}
.k-about .copy p+p{margin-top:16px}
.k-about .shot{position:relative;min-height:320px;border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--line);background:var(--panel-2);margin:0}
/* the photo fills the text block's height and never sets the row height itself (a portrait
   shot, or a lazy image arriving late, would otherwise push the column past the copy) */
.k-about .shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.k-about .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:18px;margin-top:40px}
.k-about .card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);padding:26px 24px}
.k-about .card h3{font-size:19px}
.k-about .card p{margin-top:10px;color:var(--muted);font-size:15px;line-height:1.6}
@media(max-width:860px){.k-about .cols{grid-template-columns:1fr;gap:26px}.k-about .shot{min-height:0;aspect-ratio:4/3}}

/* ============================================================
   07 · BEFORE & AFTER  .k-compare      (Jamie, 22 Sep 2026)
   A drag-to-reveal slider over two photos of the same view. Built on a native
   range input, so it works with a mouse, a finger, a keyboard and a screen
   reader without a line of custom drag handling — JS only mirrors the value
   into --pos. An item without photos falls back to two token gradients, which
   is what the lab demos use where a system has no photography.
   ============================================================ */
.k-compare{padding:clamp(64px,8vw,112px) 0;border-top:1px solid var(--line)}
.k-compare .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:var(--card-gap,clamp(20px,2.1vw,30px));margin-top:clamp(26px,3.4vw,44px)}
/* one pair on its own should not run the full page width, and reads better wider than tall */
.k-compare .grid:has(>.pair:only-child){max-width:1000px;margin-inline:auto}
.k-compare .pair:only-child{aspect-ratio:3/2}
.k-compare .pair{--pos:50%;position:relative;margin:0;border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--line);background:var(--panel-2);aspect-ratio:4/3;box-shadow:var(--shadow);touch-action:pan-y}
.k-compare .pair>img,.k-compare .pair .tone{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.k-compare .pair .before{position:absolute;inset:0;clip-path:inset(0 calc(100% - var(--pos)) 0 0);will-change:clip-path}
.k-compare .pair .before img,.k-compare .pair .before .tone{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:var(--before-filter,none)}
.k-compare .pair .tag{position:absolute;top:14px;z-index:2;font-family:var(--font-mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
  padding:6px 11px;border-radius:var(--radius-pill);background:rgba(8,9,10,.62);color:#fff;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);pointer-events:none;transition:opacity .3s}
.k-compare .pair .tag.b{left:14px}
.k-compare .pair .tag.a{right:14px;background:var(--accent);color:var(--accent-ink)}
.k-compare .pair .handle{position:absolute;top:0;bottom:0;left:var(--pos);width:2px;background:#fff;z-index:2;pointer-events:none;box-shadow:0 0 0 1px rgba(0,0,0,.22)}
.k-compare .pair .handle i{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:44px;height:44px;border-radius:50%;
  background:#fff;box-shadow:0 6px 22px -8px rgba(0,0,0,.6);display:grid;place-items:center}
.k-compare .pair .handle i::before{content:"";width:16px;height:9px;
  background:var(--accent);
  clip-path:polygon(0 50%,38% 0,38% 100%,0 50%,100% 50%,62% 100%,62% 0,100% 50%)}
.k-compare .pair .range{position:absolute;inset:0;z-index:3;width:100%;height:100%;margin:0;padding:0;opacity:0;cursor:ew-resize;appearance:none;-webkit-appearance:none;background:transparent}
.k-compare .pair .range::-webkit-slider-thumb{-webkit-appearance:none;width:56px;height:100vh;cursor:ew-resize}
.k-compare .pair .range::-moz-range-thumb{width:56px;height:100vh;border:0;border-radius:0;cursor:ew-resize}
.k-compare .pair .range:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:3px}
.k-compare .pair:hover .tag{opacity:.35}
.k-compare figcaption{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:34px 16px 14px;font-size:13.5px;color:#fff;
  background:linear-gradient(180deg,transparent,rgba(6,7,8,.78));pointer-events:none}
@media(max-width:560px){.k-compare .pair .handle i{width:38px;height:38px}}

/* ============================================================
   05 · SHOWCASE  .k-showcase           (Jamie, 22 Sep 2026)
   Scroll-linked: the picture is pinned while the steps scroll past it and
   swap it. Falls back to a plain stack of step + picture under 860px, where
   sticky columns are more trouble than they are worth.
   ============================================================ */
.k-showcase{padding:clamp(64px,8vw,112px) 0;border-top:1px solid var(--line)}
.k-showcase .cols{display:grid;grid-template-columns:.92fr 1.08fr;gap:clamp(30px,4.4vw,72px);align-items:start;margin-top:clamp(26px,3.4vw,44px)}
.k-showcase .media{position:sticky;top:104px;aspect-ratio:4/5;border-radius:var(--radius-lg);overflow:hidden;border:1px solid var(--line);background:var(--panel-2);box-shadow:var(--shadow)}
.k-showcase .media .shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transform:scale(1.07);
  transition:opacity .55s cubic-bezier(.22,1,.36,1),transform 1.3s cubic-bezier(.22,1,.36,1)}
.k-showcase .media .shot.on{opacity:1;transform:scale(1)}
.k-showcase .media .count{position:absolute;right:14px;bottom:14px;z-index:2;font-family:var(--font-mono);font-size:10.5px;letter-spacing:.16em;
  padding:6px 11px;border-radius:var(--radius-pill);background:rgba(8,9,10,.6);color:#fff;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.k-showcase .steps{display:flex;flex-direction:column;gap:clamp(24px,3.2vw,44px)}
.k-showcase .step{position:relative;padding:4px 0 4px 26px;border-left:2px solid var(--line);opacity:.4;transition:opacity .45s,border-color .45s}
.k-showcase .step.on{opacity:1;border-left-color:var(--accent)}
.k-showcase .step .num{font-family:var(--font-mono);font-size:11px;letter-spacing:.14em;color:var(--accent-2)}
.k-showcase .step h3{font-size:clamp(20px,2.2vw,26px);margin-top:8px}
.k-showcase .step p{color:var(--muted);font-size:15.5px;line-height:1.7;margin-top:10px;max-width:52ch}
.k-showcase .step .shot{display:none;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius);border:1px solid var(--line);margin-top:16px}
@media(max-width:860px){
  .k-showcase .cols{grid-template-columns:1fr;gap:26px}
  .k-showcase .media{display:none}
  .k-showcase .step{opacity:1;border-left-color:var(--line-2)}
  .k-showcase .step .shot{display:block}
}
@media (prefers-reduced-motion:reduce){.k-showcase .media .shot{transition:none}}
/* A showcase panel and a compare half are both the size of a poster, so the gallery's
   quieter tones wash out on a light ground and the picture stops reading. Inside these two
   the demo tones are re-cut from the accent family, so every system shows distinct,
   saturated slots. A real build passes photos and never sees them. */
.k-showcase .media .tone[data-tone="1"],.k-compare .pair .tone[data-tone="1"]{background:linear-gradient(200deg,var(--accent-2),var(--accent))}
.k-showcase .media .tone[data-tone="2"],.k-compare .pair .tone[data-tone="2"]{background:linear-gradient(160deg,var(--text),var(--accent-2))}
.k-showcase .media .tone[data-tone="4"],.k-compare .pair .tone[data-tone="4"]{background:radial-gradient(at 30% 25%,var(--accent),var(--accent-2) 72%)}
.k-showcase .media .tone[data-tone="5"],.k-compare .pair .tone[data-tone="5"]{background:linear-gradient(180deg,var(--accent-soft),var(--accent-2))}

/* .tone[data-tone] is the gallery ramp, shared — see 06 · gallery */

/* ============================================================
   LIGHT SURFACE  html[data-surface=light]  (Jamie, 23 Sep 2026: "kinda tough to
   read the small print and very dark ... make it lighter for the input pages")
   Pages people TYPE on — /onboarding/, /updates/, a client's /changes/ — flip the
   token set rather than restyling components, so every component follows and the
   marketing pages keep the system's own dark. The small print also grows: mono
   labels and hints were sized for decoration, not for filling in on a phone.
   ============================================================ */
html[data-surface=light]{
  --bg:#f7f8f7;--bg-2:#ffffff;--panel:#ffffff;--panel-2:#eef0ee;
  --line:#e3e6e3;--line-2:#cdd2ce;
  --text:#16191b;--muted:#3f484c;--dim:#68737a;
  --nav-bg:rgba(255,255,255,.88);
  --shadow:0 1px 2px rgba(16,25,20,.06);
  --hx:linear-gradient(100deg,var(--text) 0%,var(--text) 28%,var(--accent-deep,var(--accent-2)) 50%,var(--text) 72%,var(--text) 100%);
}
html[data-surface=light] body{background:var(--bg);color:var(--text)}
/* accent-coloured TEXT needs the deep tone on a light ground; accent BACKGROUNDS are fine */
html[data-surface=light] .eyebrow,
html[data-surface=light] .k-changes .item-n,
html[data-surface=light] .k-contact fieldset.group legend .step-n,
html[data-surface=light] .k-contact .info .step .n,
html[data-surface=light] .form-status,
html[data-surface=light] .k-contact .form-status,
html[data-surface=light] .k-changes .form-status{color:var(--accent-deep,var(--accent-2))}
html[data-surface=light] .k-changes .who{border-color:var(--accent-deep,var(--accent-2));color:var(--text)}
/* Nav and footer stay dark: both carry a white logo image, which disappears on a light
   ground. Re-scoping the tokens inside them is enough — every child follows. */
html[data-surface=light] .k-nav,html[data-surface=light] .k-footer{
  --text:#f4f6f5;--muted:#9aa6a0;--dim:#6b7772;--panel-2:#1a1f22;--line:#20252a;--line-2:#2a2e31;
  background:#0b0d0e;border-color:#20252a}
html[data-surface=light] .k-nav{border-bottom:1px solid #20252a}
html[data-surface=light] .k-footer{border-top:0}
html[data-surface=light] .k-nav .drawer{background:#0b0d0e}
html[data-surface=light] .k-hero .glow,html[data-surface=light] .k-hero .blob{opacity:.22}
/* The hero accent word is painted in --accent-soft, which is tuned to glow on the system's
   dark ground and washes out on a light one. Use the deep tone here instead. */
html[data-surface=light] .k-hero h1 .grad,html[data-surface=light] .k-hero h1 .grad .wd{color:var(--accent-deep,var(--accent-2))}
html[data-surface=light] input,html[data-surface=light] select,html[data-surface=light] textarea{box-shadow:var(--shadow)}
html[data-surface=light] .k-changes .item{background:var(--bg-2)}
/* small print, sized to be filled in rather than admired */
html[data-surface=light] .k-contact .field span,
html[data-surface=light] .k-changes .field span,
html[data-surface=light] .k-contact fieldset.group legend,
html[data-surface=light] .k-changes fieldset.group legend{font-size:12px;letter-spacing:.1em;color:var(--muted)}
html[data-surface=light] .k-contact input,html[data-surface=light] .k-contact select,html[data-surface=light] .k-contact textarea,
html[data-surface=light] .k-changes input,html[data-surface=light] .k-changes select,html[data-surface=light] .k-changes textarea{font-size:16px;padding:14px 15px}
html[data-surface=light] .k-contact input::placeholder,html[data-surface=light] .k-contact textarea::placeholder,
html[data-surface=light] .k-changes input::placeholder,html[data-surface=light] .k-changes textarea::placeholder{color:#8a9299}
html[data-surface=light] .hint,html[data-surface=light] .k-changes .hint,html[data-surface=light] .k-contact .field .hint{font-size:13.5px;color:var(--muted)}
html[data-surface=light] .form-note,html[data-surface=light] .group-note,
html[data-surface=light] .k-contact .info .note{font-size:14px;color:var(--muted)}
html[data-surface=light] .k-contact .info .step p{font-size:14.5px;color:var(--muted)}
