/* ==========================================================================
   Knepper Contracting — rooms.css
   Ported from the agency's shared "rooms" effects engine, restyled to
   Knepper's own palette (navy/ink, lime, cyan, violet, gold, coral —
   never another client's green/gold).
   Loaded after site.css on inner pages (locations hub + city pages first;
   safe to reuse anywhere else in the site). Depends on the tokens defined
   in css/site.css (--ink, --lime, --cyan, --gold, --violet, --coral, --on,
   --hair, --line, --display, --body, --mono, --wrap, --gut, --sec, --r,
   --ease) and on site.js already having wired the header/burger/form.

   Each class is one trick, same contract as the shared engine:
   - .cta-row              three-button close for every section
   - .lsec / .lsec--bright a plain dark section vs. a full-bleed accent band
   - .spot                 a dark room lit only by the pointer
   - .drift                a canvas of drifting packet / RJ45 / fibre marks
   - .glass + [data-tilt]  glass glare + pointer tilt over a photograph
   - .trail                the pointer drags a trail of job photos
   - .atlas / .map / .town the service-area map and its town list
   - .icard / .stamp       the credentials card with inspection stamps
   - .band / .band__cap    a full-bleed photo band with a caption
   - .ba                   a before/after photo pair
   All decoration: every element here degrades to fully readable, static
   content with JS off, images unloaded, or prefers-reduced-motion set.
   ========================================================================== */

/* ── CTA row + extra button finishes (site.css only ships lime/wire) ────── */
.cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
.btn--amber{background:var(--gold);color:#3A2A08}
.btn--amber:hover{transform:translateY(-2px);box-shadow:0 12px 34px -10px rgba(255,197,62,.7)}
.btn--cyan{background:var(--cyan);color:#04222A}
.btn--cyan:hover{transform:translateY(-2px);box-shadow:0 12px 34px -10px rgba(0,229,255,.65)}
.btn--violet{background:var(--violet);color:#1B0F33}
.btn--violet:hover{transform:translateY(-2px);box-shadow:0 12px 34px -10px rgba(179,107,255,.65)}

/* ── section wrappers: plain dark vs. full-bleed accent band ────────────── */
.lsec{padding:var(--sec) 0;position:relative}
.lsec--bright{padding:var(--sec) 0;background:var(--c,var(--gold));color:#0A0E24;position:relative;overflow:hidden}
.lsec--bright .kick{color:rgba(10,14,36,.62)}
.lsec--bright .sub{color:rgba(10,14,36,.72)}
.lsec--bright .btn--wire{border-color:rgba(10,14,36,.4);color:#0A0E24}
.lsec--bright .btn--wire:hover{border-color:#0A0E24}
/* Catch-all: .kick/.sub above are the only children this block recolours, so any other
   text dropped into a bright band (any --c: cyan, gold, violet, lime) still inherits the
   dark-theme on/on-soft tokens meant for a navy background — e.g. .check__list span,
   which is how "what happens when we come to <City>" failed contrast on the cyan band. */
.lsec--bright p,.lsec--bright dd,.lsec--bright .check__list span{color:rgba(10,14,36,.85)}
.lsec--bright h1,.lsec--bright h2,.lsec--bright h3{color:#0A0E24}

/* ── R1 · THE DARK ROOM — lit only by the pointer ────────────────────────
   Same mask trick as the shared engine: a dim static layer sits under a masked "lit"
   copy of the same content that only shows inside a radial spotlight that
   follows the cursor. Reduced motion / no-JS: the lit layer is shown flat. */
.spot{position:relative;background:#05081C;color:var(--on);overflow:hidden;--mx:50%;--my:42%}
.spot__dim,.spot__lit{padding:var(--sec) 0}
.spot__dim{opacity:.16}
.spot__lit{position:absolute;inset:0;opacity:0;transition:opacity .6s;
  -webkit-mask-image:radial-gradient(circle clamp(190px,26vw,380px) at var(--mx) var(--my),#000 0%,rgba(0,0,0,.85) 45%,transparent 100%);
  mask-image:radial-gradient(circle clamp(190px,26vw,380px) at var(--mx) var(--my),#000 0%,rgba(0,0,0,.85) 45%,transparent 100%)}
.spot.lit .spot__lit{opacity:1}
.spot__glow{position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle clamp(230px,30vw,440px) at var(--mx) var(--my),rgba(168,255,62,.16),transparent 70%);opacity:0;transition:opacity .6s}
.spot.lit .spot__glow{opacity:1}
.spot .h2,.spot h2,.spot h3{color:var(--on)}
.spot .sub{color:var(--on-soft)}
.spot .cta-row{position:relative;z-index:3}
/* .spot__grid (the closet/approach/answers 3-col room) puts its real copy+cta-row
   in the grid itself, after the empty .spot__dim/.spot__lit spacer layers — so
   the fix for hint/button collision has to add clearance below the GRID, not
   below those spacers (padding on .spot__dim only pushes the grid down, it never
   opens space under it). This guarantees room for .spot__hint even when the
   narrow 1fr copy column wraps its three-button .cta-row onto two lines. */
.spot .spot__grid{padding-bottom:56px}
.spot__hint{position:absolute;left:var(--gut);bottom:16px;z-index:3;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--on-faint);display:flex;gap:9px;align-items:center}
.spot__hint i{width:8px;height:8px;border-radius:50%;background:var(--lime);box-shadow:0 0 12px var(--lime);animation:spotblip 1.8s infinite}
@keyframes spotblip{50%{opacity:.3}}
@media (prefers-reduced-motion:reduce){.spot__lit{opacity:1!important;position:static;mask-image:none;-webkit-mask-image:none}.spot__dim{display:none}.spot__glow{opacity:.5!important}.spot__hint{display:none}}
@media (max-width:900px){.spot__hint{display:none}}

/* ── R2 · drifting packet / RJ45 / fibre-spark marks ─────────────────────── */
.drift{position:relative;overflow:hidden}
.drift__cv{position:absolute;inset:0;pointer-events:none;z-index:0}
.drift .wrap{position:relative;z-index:1}

/* ── R3 · glass glare + pointer tilt over a photograph ───────────────────── */
.glass{position:relative;--gx:50%;--gy:50%;display:block}
.glass::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .4s;mix-blend-mode:screen;
  background:radial-gradient(circle 230px at var(--gx) var(--gy),rgba(238,242,255,.55),rgba(238,242,255,.10) 42%,transparent 72%)}
.glass.shine::after{opacity:1}
[data-tilt]{transition:transform .5s var(--ease)}
[data-tilt].tilt{transform:perspective(900px) rotateX(var(--tx,0deg)) rotateY(var(--ty,0deg));transition:transform .08s linear}

/* ── R4 · the photo trail ────────────────────────────────────────────────── */
.trail{position:relative;overflow:hidden;cursor:crosshair}
.trail__img{position:absolute;width:104px;height:132px;object-fit:cover;border:4px solid var(--ink);border-radius:4px;box-shadow:0 20px 40px -18px rgba(0,0,0,.7),0 0 0 1px var(--line);pointer-events:none;z-index:1;
  transform:translate(-50%,-50%) rotate(var(--r,0deg)) scale(.4);animation:trailin 1.1s cubic-bezier(.2,.9,.3,1) forwards}
@keyframes trailin{20%{transform:translate(-50%,-50%) rotate(var(--r,0deg)) scale(1);opacity:1}100%{transform:translate(-50%,-60%) rotate(var(--r,0deg)) scale(.9);opacity:0}}
.trail .wrap{position:relative;z-index:2}
.trail__hint{font-family:var(--mono);font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--on-faint);margin-top:20px}
@media (pointer:coarse){.trail__hint{display:none}}

/* ── R5 · the service-area map ───────────────────────────────────────────── */
.atlas{display:grid;grid-template-columns:7fr 5fr;gap:clamp(24px,4vw,60px);align-items:start}
.map{position:relative;background:linear-gradient(180deg,rgba(24,31,74,.85),rgba(14,19,56,.92));border:1px solid var(--hair);border-radius:10px;padding:14px;box-shadow:inset 0 1px 0 rgba(255,255,255,.06)}
.map svg{display:block;width:100%;height:auto}
.map__route{fill:none;stroke:rgba(238,242,255,.28);stroke-width:1.6;stroke-dasharray:7 7}
.map__pin circle{fill:var(--gold);stroke:#0A0E24;stroke-width:2.5}
.map__pin text{font:700 11px 'JetBrains Mono',monospace;letter-spacing:.08em;fill:var(--on-soft)}
.map__pin.hot circle{fill:var(--cyan);r:11}
.map__pin.hot text{fill:var(--cyan)}
.map__pin.here circle{fill:var(--lime);stroke:#0A0E24}
.map__pin.here text{fill:var(--lime);font-weight:900}
.map__pin.on circle,.map__route.on{}
.map__home circle{fill:var(--coral);stroke:#0A0E24;stroke-width:3}
.map__home text{font:900 13px 'JetBrains Mono',monospace;letter-spacing:.08em;fill:var(--on)}
.map__ring{fill:none;stroke:var(--coral);stroke-width:2;opacity:.6;animation:mapring 2.6s ease-out infinite;transform-origin:center;transform-box:fill-box}
@keyframes mapring{0%{transform:scale(.4);opacity:.7}100%{transform:scale(1.7);opacity:0}}
.map__legend{position:absolute;left:24px;bottom:16px;font-family:var(--mono);font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--on-faint)}
.map__title{position:absolute;right:22px;top:16px;font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--on-faint);text-align:right}
.towns{border-top:1px solid var(--hair);margin-top:4px}
.town{display:grid;grid-template-columns:1fr auto auto;gap:14px;align-items:baseline;padding:12px 0;border-bottom:1px solid var(--hair);text-decoration:none;color:inherit;transition:padding-left .25s var(--ease),background .25s}
.town:hover{padding-left:10px;background:rgba(168,255,62,.06)}
.town b{font-family:var(--display);font-weight:700;font-size:1.02rem;text-transform:none}
.town span{font-family:var(--mono);font-size:.74rem;letter-spacing:.04em;color:var(--lime)}
.town i{font-family:var(--mono);font-style:normal;font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:var(--on-faint)}
.town--here{background:rgba(168,255,62,.08)}.town--here b{color:var(--lime)}
@media (max-width:900px){.atlas{grid-template-columns:1fr}}

/* ── R6 · credentials card with inspection stamps ────────────────────────── */
.inspect{display:grid;grid-template-columns:6fr 6fr;gap:clamp(28px,5vw,64px);align-items:center}
.icard{position:relative;background:linear-gradient(180deg,#151B45,#0E1338);border:1px solid var(--hair);border-radius:10px;padding:clamp(20px,3vw,32px);box-shadow:0 30px 70px -30px rgba(0,0,0,.85),inset 0 1px 0 rgba(255,255,255,.06)}
.icard h3{font-family:var(--mono);font-weight:700;font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--on-faint);margin:0 0 14px;border-bottom:1px solid var(--hair);padding-bottom:12px}
.icard .row{position:relative;display:grid;grid-template-columns:1fr auto auto;gap:10px 14px;align-items:center;padding:15px 0;border-bottom:1px solid var(--hair)}
.icard .row:last-of-type{border-bottom:0}
.icard .row b{display:block;font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--on-faint)}
.icard .row em{display:block;font-style:normal;font-family:var(--mono);font-size:1.1rem;font-weight:700;color:var(--on);margin-top:3px}
.stamp{justify-self:end;font-family:var(--mono);font-weight:900;font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:var(--lime);border:2px double var(--lime);border-radius:5px;padding:7px 10px;transform:rotate(-10deg) scale(2.4);opacity:0;transition:transform .35s cubic-bezier(.2,1.6,.4,1),opacity .2s;white-space:nowrap}
.stamp.down{transform:rotate(-7deg) scale(1);opacity:.95}
.stamp--wait{color:var(--coral);border-color:var(--coral)}
.icard .foot{display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px;font-family:var(--mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--on-faint);margin-top:16px;padding-top:14px;border-top:1px solid var(--hair)}
@media (prefers-reduced-motion:reduce){.stamp{opacity:.95;transform:rotate(-7deg) scale(1)}}
@media (max-width:900px){.inspect{grid-template-columns:1fr}.icard .row{grid-template-columns:1fr}.stamp{justify-self:start;margin-top:4px;margin-left:10px}.stamp.down{transform:rotate(-3deg) scale(1)}}

/* ── R7 · full-bleed photo band with caption ─────────────────────────────── */
.band{position:relative;overflow:hidden}
.band img{width:100%;height:clamp(280px,42vw,520px);object-fit:cover;display:block;filter:saturate(1.05) contrast(1.03)}
.band__cap{position:absolute;left:0;right:0;bottom:0;padding:34px var(--gut) 30px;background:linear-gradient(0deg,rgba(5,8,28,.92),rgba(5,8,28,.35) 70%,transparent)}
.band__cap p{margin:0 0 6px;font-family:var(--mono);font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--lime)}
.band__cap h3{font-size:clamp(1.4rem,3vw,2.1rem);margin:0;color:var(--on)}

/* ── R8 · before / after ──────────────────────────────────────────────────
   No forced crop: each figure sizes to its OWN photo's real proportions
   (from the width/height attributes already on the <img>, which the
   generator sets per-pair — a wide pano next to a portrait "after" is an
   honest pairing, not a bug) and is capped so neither exceeds ~60vh. ──── */
.ba{display:grid;grid-template-columns:1fr 1fr;gap:clamp(14px,2.4vw,26px);align-items:end}
.ba figure{margin:0;position:relative;border-radius:8px;overflow:hidden;border:1px solid var(--hair)}
.ba img{display:block;width:100%;height:auto;max-height:60vh;object-fit:contain}
.ba figcaption{position:absolute;left:10px;top:10px;font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;background:rgba(5,8,28,.78);color:var(--on);padding:6px 10px;border-radius:4px;border:1px solid var(--hair)}
.ba figure:last-child figcaption{color:var(--lime);border-color:rgba(168,255,62,.4)}
@media (max-width:760px){.ba{grid-template-columns:1fr}}

/* ── R9 · the fan — spec-sheet index cards fan out on hover / tap ────────── */
.fan{position:relative;height:420px;perspective:1200px}
.idx{position:absolute;left:50%;top:50%;width:min(360px,80vw);margin-left:calc(min(360px,80vw) * -.5);
  background:linear-gradient(180deg,#151B45,#0E1338);color:var(--on);border:1px solid var(--hair);border-radius:8px;padding:20px 22px 18px;
  box-shadow:0 30px 70px -30px rgba(0,0,0,.85),inset 0 1px 0 rgba(255,255,255,.06);text-decoration:none;
  transform:translate(0,-50%) rotate(var(--r,0deg)) translateY(var(--y,0px));transition:transform .7s var(--ease)}
.idx i{display:block;font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--lime);font-style:normal;margin-bottom:16px}
.idx b{display:block;font-family:var(--display);font-weight:800;font-size:1.3rem;text-transform:uppercase;line-height:1.05;margin-bottom:8px}
.idx p{margin:0;font-family:var(--mono);font-size:.8rem;color:var(--on-soft)}
.fan .idx:nth-child(1){--r:-3deg;--y:-84px}.fan .idx:nth-child(2){--r:-1.8deg;--y:-68px}.fan .idx:nth-child(3){--r:-.6deg;--y:-52px}
.fan .idx:nth-child(4){--r:.6deg;--y:-36px}.fan .idx:nth-child(5){--r:1.8deg;--y:-18px}.fan .idx:nth-child(6){--r:3deg;--y:0px}
.fan.open .idx:nth-child(1){transform:translate(-56%,-50%) rotate(-17deg) translateY(-44px)}
.fan.open .idx:nth-child(2){transform:translate(-34%,-50%) rotate(-10deg) translateY(-20px)}
.fan.open .idx:nth-child(3){transform:translate(-11%,-50%) rotate(-3deg)}
.fan.open .idx:nth-child(4){transform:translate(11%,-50%) rotate(3deg)}
.fan.open .idx:nth-child(5){transform:translate(34%,-50%) rotate(10deg) translateY(-20px)}
.fan.open .idx:nth-child(6){transform:translate(56%,-50%) rotate(17deg) translateY(-44px)}
.idx:hover{z-index:5;border-color:var(--lime)}
@media (max-width:900px){.fan{height:380px}}

/* ── the floating 3D gallery (ground3d.js) — layered over the existing
   cable ground; shows only while a [data-fx="frames"] section is in view
   (effects.js toggles body.frames-on) — otherwise the plain cable ground
   underneath is what's visible ────────────────────────────────────────── */
.ground3d{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.ground3d__gl{position:absolute;inset:0;width:100%;height:100%;opacity:0;transition:opacity 1.2s var(--ease)}
body.frames-on .ground3d__gl{opacity:1}

/* ── fixed dock: Book / Call / Email, the desktop companion to .mbar ─────── */
.dock{position:fixed;right:22px;bottom:22px;z-index:70;display:none;flex-direction:column;gap:10px}
@media (min-width:800px){.dock{display:flex}}
.dock__b{display:flex;align-items:center;gap:10px;padding:11px 16px 11px 11px;border-radius:999px;background:rgba(16,22,58,.94);backdrop-filter:blur(10px);
  border:1px solid var(--hair);color:var(--on);text-decoration:none;font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  box-shadow:0 14px 30px rgba(0,0,0,.5);transition:transform .3s var(--ease),border-color .3s,color .3s}
.dock__b i{font-style:normal;width:26px;height:26px;display:grid;place-items:center;border-radius:50%;flex-shrink:0}
.dock__b:hover{transform:translateX(-4px);border-color:var(--lime);color:var(--lime)}
.dock__b--book i{background:var(--lime);color:#08210A}
.dock__b--call i{background:var(--cyan);color:#04222A}
.dock__b--mail i{background:var(--gold);color:#3A2A08}

/* ── R9 · drive-time table (a 4-col variant of site.css's .spec table) ───── */
.spec__row--geo{grid-template-columns:1.7fr .55fr .8fr .95fr}
.spec__row--geo a{color:var(--lime);text-decoration:none;border-bottom:1px solid rgba(168,255,62,.4)}
.spec__row--geo a:hover{color:var(--on)}
.spec__row--geo em{font-style:normal;color:var(--on-faint);font-size:.82em}

/* ── R10 · page head (crumb / h1 / lede), used by services + other inner
   pages ahead of a .cta-row — same contract as the shared engine's phead ─ */
.phead{padding:148px 0 54px;position:relative}
.crumb{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--on-faint);margin:0 0 22px}
.crumb a{color:var(--lime);text-decoration:none;border-bottom:1px solid rgba(168,255,62,.35)}
.crumb a:hover{color:var(--cyan);border-color:rgba(0,229,255,.4)}

/* ── R11 · plain section wrapper with alternating tone (never true white) ──
   Sits alongside .lsec/.lsec--bright above: .sec.light/.sec.dark give a
   two-tone dark/darker rhythm for pages that don't need a full accent band. */
.sec{padding:var(--sec) 0;position:relative}
.sec.dark{background:var(--ink)}
.sec.light{background:linear-gradient(180deg,var(--ink-3),var(--ink-2))}
.sec-head{max-width:70ch;margin-bottom:44px}

/* ── R12 · two-column photo + copy split (photos take .glass + data-tilt) ── */
.split{display:grid;gap:40px;align-items:center}
@media (min-width:900px){
  .split{grid-template-columns:1fr 1fr;gap:60px}
  .split--reverse .split__media{order:2}
}
.split__media{position:relative;border-radius:10px;overflow:hidden;border:1px solid var(--hair);aspect-ratio:4/3;background:var(--ink-2)}
.split__media--tall{aspect-ratio:3/4}
.split__media img{width:100%;height:100%;object-fit:cover;display:block}

/* ── R13 · three-column icon feature row ─────────────────────────────────── */
.icons{display:grid;gap:32px;margin-top:44px}
@media (min-width:860px){.icons{grid-template-columns:repeat(3,1fr)}}
.icons>div{padding-top:20px;border-top:2px solid var(--lime)}
.icons svg{width:30px;height:30px;color:var(--lime);margin-bottom:14px}
.icons h3{font-size:1.22rem;margin:0 0 8px}
.icons p{margin:0;color:var(--on-soft);font-size:.94rem}

/* ── R14 · closing band: dark, vignetted, hosts the .trail ───────────────── */
.ctaband{position:relative;padding:var(--sec) 0;background:radial-gradient(120% 100% at 50% 0%,var(--ink-3),var(--ink) 70%);overflow:hidden}
.ctaband .wrap{position:relative;z-index:2}
.ctaband h2{max-width:16ch}

/* ── links that wrap a whole card (e.g. the services-hub rack units) should
   never show the browser's default underline on their heading/body text ── */
.ru{text-decoration:none}

/* rotated/scaled bands must not add horizontal scroll */
main{overflow-x:hidden;overflow-x:clip}

/* ══════════════════════════════════════════════════════════════════════
   BUILD 06 ADDITIONS — carrying "The Run"'s neon vocabulary onto every
   inner page: the cable + packet down the gutter, gradient headlines, a
   card-hover language (lift + accent hairline + photo scale), an
   underline-grow link, portrait photo frames sized to what the source
   actually is, a click-to-expand lightbox, and a before/after slider.
   Hooks: .irun (js/effects.js draws + animates it), .gr / .gr2 (gradient
   text), .ulink, .liftcard, .portrait, [data-lightbox], .tvcompare.
   ══════════════════════════════════════════════════════════════════════ */

/* ── B1 · THE RUN, ported — a fixed cable down the left gutter on every
   inner page. Hidden on the homepage, which draws its own richer version
   in run.css/run.js. Pure decoration: degrades to a plain dim line with
   no JS or under reduced motion. ────────────────────────────────────── */
.irun{position:fixed;left:0;top:0;bottom:0;width:6px;z-index:3;pointer-events:none;display:none;--irc:var(--lime)}
@media (min-width:640px){.irun{display:block}}
@media (min-width:1100px){.irun{width:28px}}
.runpage .irun{display:none!important}
.irun__svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.irun__base{fill:none;stroke:rgba(238,242,255,.16);stroke-width:2;vector-effect:non-scaling-stroke}
.irun__live{fill:none;stroke:var(--irc);stroke-width:2.5;vector-effect:non-scaling-stroke;
  filter:drop-shadow(0 0 6px var(--irc));transition:stroke .6s}
.irun__pk{position:absolute;left:0;top:0;width:10px;height:10px;margin:-5px 0 0 -5px;border-radius:50%;
  background:#fff;box-shadow:0 0 0 3px color-mix(in srgb,var(--irc) 45%,transparent),0 0 16px 5px var(--irc);
  opacity:0;transition:background .5s,box-shadow .5s}
.js .irun__pk{opacity:1}
@media (prefers-reduced-motion:reduce){.irun__pk{top:6%!important}}

/* B2–B7 (gradient text, underline-grow links, card-hover language, the
   portrait frame, the lightbox and the before/after slider) moved to
   css/site.css — the homepage loads run.css instead of this file, and all
   of those are used there too (the monitor-wall lightbox, the TV-mount
   slider). Kept here only: B1, the inner-page run cable, which is
   deliberately never shown on the homepage. */
