/* ============================================================
   HNB Studio 2.0: motion layer
   Everything added for the premium motion pass lives here so the
   original style.css stays readable. Loaded after style.css.
   ============================================================ */

/* ============================================================
   ENVIRONMENT: the page has weather.
   Five gradient plates stacked behind the 3D scene. Scroll scrubs
   their opacity, so the background changes STATE between sections
   instead of cutting. Nothing here ever triggers layout.
   ============================================================ */
/* z-index -1 keeps the weather behind everything the page paints,
   including the 3D canvas, without disturbing the existing stacking
   order (scene 0, wires 2, content 3). The body must stay transparent
   or it would paint straight over this. */
html { background: #06060a; }
body { background: transparent !important; }
#env {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: #06060a;
}
.env-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  will-change: opacity;
}
/* hero: cold vacuum, one cool bloom where the knot hangs */
.env-hero {
  opacity: 1;
  background:
    radial-gradient(80% 60% at 68% 42%, rgba(28, 44, 78, 0.55), transparent 70%),
    radial-gradient(100% 100% at 50% 100%, rgba(10, 10, 16, 1), #06060a 70%);
}
/* statement: the room lifts, indigo rising from the floor */
.env-statement {
  background:
    radial-gradient(120% 80% at 20% 90%, rgba(46, 40, 96, 0.55), transparent 65%),
    linear-gradient(to bottom, #06060a, #0b0b14 60%, #0d0c18);
}
/* services: graphite daylight, the flattest and most legible state */
.env-services {
  background:
    radial-gradient(90% 50% at 80% 15%, rgba(64, 72, 96, 0.45), transparent 70%),
    linear-gradient(to bottom, #0d0c18, #121218 45%, #101016);
}
/* work: dark again, so the screens are the only light source */
.env-work {
  background:
    radial-gradient(110% 70% at 50% 50%, rgba(20, 34, 62, 0.7), transparent 72%),
    linear-gradient(to bottom, #08080e, #05050a);
}
/* footer: last warmth before the sign-off */
.env-footer {
  background:
    radial-gradient(120% 70% at 50% 110%, rgba(72, 48, 96, 0.45), transparent 68%),
    linear-gradient(to bottom, #06060a, #08070c);
}
/* grain: kills gradient banding and gives flat dark UI a photographed
   texture instead of a filled-rectangle one */
#env::after {
  content: "";
  position: absolute;
  inset: -50%;
  opacity: 0.03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grainShift 6s steps(6) infinite;
  pointer-events: none;
}
@keyframes grainShift {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-3%, 2%); }
  40%  { transform: translate(2%, -3%); }
  60%  { transform: translate(-2%, -2%); }
  80%  { transform: translate(3%, 1%); }
  100% { transform: translate(0, 0); }
}

/* ============================================================
   Section handoff, outgoing sections leave in Z, incoming
   sections are already there, oversized and out of focus.
   ============================================================ */
[data-arrive] { will-change: transform, filter, opacity; }
.section-stage { perspective: 1600px; perspective-origin: 50% 40%; }

/* ============================================================
   Hero typography, the first screen earns a bigger voice.
   Scoped to the banner so the statement and footer keep the
   reading size the long copy needs.
   ============================================================ */
.banner-title-block h1 {
  font-size: clamp(3.25rem, 8.4vw, 9rem);
  line-height: 0.94;
  letter-spacing: -0.055em;
}

/* ============================================================
   Statement
   ============================================================ */
.statement-rule {
  display: block;
  height: 1px;
  margin-top: 3rem;
  max-width: 60rem;
  background: linear-gradient(to right, #6fb7ff, #9f7dff, transparent);
  transform: scaleX(0);
  transform-origin: left;
}

/* ============================================================
   Services, rows uncover instead of fading
   ============================================================ */
.service-row { overflow: hidden; }
.service-row .service-name { display: inline-block; }
/* tag and number move at different speeds on hover: two speeds in one row */
.service-tag,
.service-num {
  display: inline-block;
  transition: transform 0.5s var(--ease-expo), color 0.4s ease;
}
.service-row:hover .service-tag {
  transform: translateX(-0.4rem);
  color: var(--color-light-font);
}
.service-row:hover .service-num {
  transform: translateX(0.25rem);
  color: #6fb7ff;
}

/* ============================================================
   WORK: cards are website previews, not coloured rectangles.
   Each card is a mask; the "screenshot" inside is oversized and
   travels slower than the card. That speed difference is the whole
   trick behind images that feel like they have depth.
   ============================================================ */
.work-card { background: #0a0a10; isolation: isolate; }
/* the track opens with the first card already at centre stage, sharp and
   facing the room; everything after it waits in the wings to the right */
.work-track {
  padding-left: max(1.5rem, calc(50vw - min(36vw, 320px))) !important;
  padding-right: 12vw;
}
.work-media {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}
.work-shot {
  position: absolute;
  inset: -14%;
  display: flex;
  flex-direction: column;
  will-change: transform;
}
.work-shot .shot-chrome {
  height: 2.1rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-inline: 0.9rem;
  background: rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  flex: 0 0 auto;
}
.shot-chrome i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.22);
}
.shot-chrome .shot-url {
  margin-left: 0.6rem;
  height: 8px;
  flex: 1;
  max-width: 9rem;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.08);
}
.shot-body {
  flex: 1;
  padding: 1.6rem 1.4rem 2.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.shot-bar {
  height: 0.85rem;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.13);
  transform-origin: left;
}
.shot-bar.is-lead { height: 2.1rem; width: 78%; background: rgba(255, 255, 255, 0.32); }
.shot-bar.is-sub { width: 52%; }
.shot-bar.is-thin { height: 0.5rem; width: 40%; opacity: 0.7; }
.shot-hero {
  flex: 1;
  min-height: 4.5rem;
  margin-top: 0.4rem;
  border-radius: 3px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0.04));
}
.shot-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.55rem;
  flex: 0 0 auto;
}
.shot-grid span {
  height: 2.6rem;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.1);
  transform-origin: bottom;
}
/* scrim so the title stays readable over any preview */
.work-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(4, 4, 8, 0.94) 0%, rgba(4, 4, 8, 0.4) 40%, transparent 66%);
  pointer-events: none;
}
.work-card .work-card-body { position: relative; z-index: 2; }
.work-card .work-index { z-index: 2; }
.work-card .card-sheen { z-index: 3; }
.work-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  font-family: var(--font-martian);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.45s var(--ease-expo), transform 0.45s var(--ease-expo);
}
.work-card:hover .work-cta { opacity: 0.85; transform: translateY(0); }
/* no CSS transition on filter: the arc loop writes blur every frame and
   a transition would fight those writes */

/* the last slot is an invitation, not a project */
.work-card.is-open {
  background: transparent;
  border-style: dashed;
  border-color: #33363f;
  justify-content: center;
  box-shadow: none;
}
.work-card.is-open .work-media { display: none; }
.work-card.is-open::before { opacity: 0.25; }

/* ============================================================
   Footer, the wordmark is architecture, not decoration
   ============================================================ */
#footer { overflow: hidden; }
/* tail room so the landed wordmark is fully on screen at the end of
   the scroll, not half cut off by the viewport edge */
#footer { padding-bottom: clamp(3rem, 6vh, 5rem); }
.footer-mark {
  position: absolute;
  left: 50%;
  bottom: clamp(8rem, 24vh, 13rem);
  transform: translateX(-50%);
  font-family: var(--font-familjen);
  font-size: clamp(7rem, 27vw, 24rem);
  line-height: 0.8;
  letter-spacing: -0.05em;
  white-space: nowrap;
  color: transparent;
  -webkit-text-stroke: 1px rgba(216, 216, 216, calc(0.13 * (1 - var(--mark-lit, 0))));
  text-shadow: none;
  pointer-events: none;
  user-select: none;
  z-index: 2;
  will-change: transform;
}
.footer-inner { position: relative; z-index: 3; pointer-events: none; }
.footer-inner > * { pointer-events: auto; }

/* ============================================================
   Micro-interactions
   ============================================================ */
[data-magnetic] { will-change: transform; }

#scroll-rail {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 100%;
  z-index: 120;
  pointer-events: none;
  transform-origin: left;
  transform: scaleX(0);
  background: linear-gradient(to right, #6fb7ff, #9f7dff);
  opacity: 0.75;
}

/* ============================================================
   Preloader, the lockup's own frame becomes the doorway
   ============================================================ */
#preloader { overflow: hidden; }
.preloader-frame {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 17rem;
  height: 10rem;
  margin: -5rem 0 0 -8.5rem;
  border: 1px solid #2f323b;
  border-radius: 0.125rem;
  pointer-events: none;
  will-change: transform, opacity;
}
/* corner crop marks: the print-shop detail that reads as art direction */
.preloader-frame span {
  position: absolute;
  width: 0.75rem;
  height: 0.75rem;
  border: 1px solid #6fb7ff;
  opacity: 0.55;
}
.preloader-frame span:nth-child(1) { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.preloader-frame span:nth-child(2) { top: -1px; right: -1px; border-left: 0; border-bottom: 0; }
.preloader-frame span:nth-child(3) { bottom: -1px; left: -1px; border-right: 0; border-top: 0; }
.preloader-frame span:nth-child(4) { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

/* ============================================================
   Responsive: the desktop choreography adapts, it does not shrink
   ============================================================ */
@media (max-width: 1024px) {
  .work-card { width: min(84vw, 460px); height: min(58vh, 420px); }
}
@media (max-width: 640px) {
  .service-row { grid-template-columns: 2.5rem 1fr; gap: 0.5rem 0.75rem; padding: 1.5rem 0.25rem; }
  .service-tag { grid-column: 2; text-align: left; font-size: 0.8125rem; }
  .ghost-num { font-size: clamp(6rem, 30vw, 10rem); }
  .work-card { width: 82vw; height: 56vh; }
  .shot-body { padding: 1.1rem 1rem 1.8rem; }
  .shot-grid span { height: 1.8rem; }
}

/* ============================================================
   Reduced motion
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  #env::after { animation: none; }
  .work-shot { inset: 0; transform: none !important; }
  .work-card { filter: none !important; }
  #scroll-rail { display: none; }
  .statement-rule { transform: scaleX(1); }
  .service-row { clip-path: none !important; }
}


/* ============================================================
   Footer atmosphere, fog drifting across the wordmark.
   Three slow layers at different speeds and scales, so the motion
   never reads as one looping texture. Sits above the mark and below
   the content, which is what makes the letters feel far away.
   ============================================================ */
.footer-fog {
  position: absolute;
  inset: auto 0 0;
  height: min(62vh, 34rem);
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
  mask-image: linear-gradient(to top, #000 55%, transparent);
  -webkit-mask-image: linear-gradient(to top, #000 55%, transparent);
}
.footer-fog i {
  position: absolute;
  display: block;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(150, 165, 200, 0.3), transparent 72%);
  filter: blur(34px);
  will-change: transform;
}
.footer-fog i:nth-child(1) {
  width: 78%; height: 150%; left: -18%; bottom: -46%;
  animation: fog-a 46s linear infinite;
}
.footer-fog i:nth-child(2) {
  width: 62%; height: 130%; left: 34%; bottom: -40%;
  background: radial-gradient(closest-side, rgba(120, 140, 190, 0.26), transparent 72%);
  animation: fog-b 63s linear infinite;
}
.footer-fog i:nth-child(3) {
  width: 46%; height: 108%; left: 8%; bottom: -34%;
  background: radial-gradient(closest-side, rgba(190, 200, 225, 0.2), transparent 70%);
  animation: fog-c 38s linear infinite;
}
@keyframes fog-a {
  0%   { transform: translate3d(-8%, 0, 0) scale(1); }
  50%  { transform: translate3d(42%, -6%, 0) scale(1.18); }
  100% { transform: translate3d(-8%, 0, 0) scale(1); }
}
@keyframes fog-b {
  0%   { transform: translate3d(18%, 3%, 0) scale(1.1); }
  50%  { transform: translate3d(-34%, -4%, 0) scale(0.94); }
  100% { transform: translate3d(18%, 3%, 0) scale(1.1); }
}
@keyframes fog-c {
  0%   { transform: translate3d(0, 0, 0) scale(0.95); }
  50%  { transform: translate3d(26%, -9%, 0) scale(1.24); }
  100% { transform: translate3d(0, 0, 0) scale(0.95); }
}
@media (prefers-reduced-motion: reduce) {
  .footer-fog i { animation: none; }
}

/* the wordmark becomes playable: each letter is its own note */
.footer-mark { display: flex; gap: 0.02em; }
.footer-mark span {
  pointer-events: auto;
  cursor: none;
  /* keeps each letter's own box, so neighbouring UI stays reachable */
  isolation: isolate;
  transition: -webkit-text-stroke-color 0.45s var(--ease-soft), text-shadow 0.5s var(--ease-soft), transform 0.55s var(--ease-soft);
}
.footer-mark span:hover { -webkit-text-stroke-color: rgba(216, 216, 216, 0.28); }
.footer-mark span.is-struck { -webkit-text-stroke-color: rgba(240, 244, 255, 0.45); }

/* Tells you the mark is an instrument. Without this the interaction is
   invisible: sound is opt-in, so hovering a silent wordmark teaches the
   visitor nothing. */
.mark-hint {
  /* In normal flow, directly above the meta row. Positioning it against
     the footer's bottom edge looked right until [data-arrive] revealed
     and moved the row ~30px, at which point they collided. Flow order
     cannot drift. */
  position: relative;
  z-index: 4;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-martian);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-light-font) 34%, transparent);
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0.4rem 0;
  transition: color 0.4s var(--ease-soft);
  white-space: nowrap;
}
.mark-hint:hover { color: color-mix(in srgb, var(--color-light-font) 78%, transparent); }
.mark-hint i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #2f323b;
  transition: background 0.4s var(--ease-soft), box-shadow 0.4s var(--ease-soft);
}
.mark-hint.is-live i {
  background: #6fb7ff;
  box-shadow: 0 0 0 3px rgba(111, 183, 255, 0.16), 0 0 12px 0 rgba(111, 183, 255, 0.8);
}
