/* ==========================================================
   RESET
   ========================================================== */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  width: 100%;
  overflow-x: hidden;
  background: #000000;
}

/* ==========================================================
   MOBILE PLACEHOLDERS — this design is fixed/desktop-only, not
   meant to reflow for tablet/phone sizes. Deliberately simple
   this time: plain max-width breakpoints only (no aspect-ratio/
   height conditions, no Level-4 boolean media query syntax) —
   the site keeps its normal look/feel at every desktop width,
   and only swaps to a static image once width actually drops to
   tablet size.
   - #mobileNotice (mobile.jpg): shown at tablet size and below
     (width <= 1024px, the standard iPad-width breakpoint).
   - #mobileNoticeSmaller (mobile-smaller.jpg): takes over at
     phone size (width <= 767px, the standard tablet/phone
     dividing line) — this rule comes AFTER the tablet rule in
     the stylesheet, so for widths <= 767px its `display: none`
     on #mobileNotice and `display: block` on itself simply win
     by source order. No AND/OR logic needed at all.
   ========================================================== */
#mobileNotice,
#mobileNoticeSmaller {
  display: none;
}

@media (max-width: 1024px) {
  .canvas,
  .invert-cursor,
  #contactWidget {
    display: none !important;
  }

  #mobileNotice {
    display: block;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    object-fit: cover;
    z-index: 10000;
  }
}

@media (max-width: 767px) {
  #mobileNotice {
    display: none;
  }

  #mobileNoticeSmaller {
    display: block;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    object-fit: cover;
    z-index: 10001;
  }
}

/* Every video on the site gets a slight, blurred 15%-opacity drop
   shadow — a global rule on the tag itself so it applies
   uniformly without needing a class on each <video>. */
video {
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.15));
}

/* ==========================================================
   CANVAS
   Each .section is full-viewport-width and scales purely with
   that width (never cropped) — see .section below. .canvas's
   own 1920px width is now just a reference size each section's
   breakout math uses; it no longer visually constrains anything
   itself.
   ========================================================== */
.canvas {
  position: relative;
}

/* Every section is full-bleed and NEVER crops: its height always
   derives purely from the viewport WIDTH times its own design
   frame's aspect ratio (frame-h/frame-w), so the entire design —
   background and every .frame-el element on it — is always shown
   in full and simply scales uniformly with screen width. Nothing
   is ever cut off top/bottom/sides, and nothing rearranges: an
   extra-wide screen just makes everything bigger (the section
   gets taller in px, requiring a bit more scroll), never cropped
   or reflowed. This applies identically to every section — there
   is no more "100vh + cover-crop" special case.
   No margin-left/breakout trick is needed: .canvas no longer has
   its own width, so .section (100vw) already starts flush at the
   viewport's left edge on its own — a "break out of a centered
   parent" trick was only ever needed while .canvas had a fixed
   1920px width, which is exactly what caused a rightward shift +
   black gap at any viewport narrower than 1920px (see the CSS
   spec's auto-margin-collapse-to-zero rule for over-constrained
   widths — .canvas doesn't overflow symmetrically like assumed). */
.section {
  width: 100vw;
  height: calc(100vw * var(--frame-h) / var(--frame-w));
  position: relative;
  /* Design-frame reference size for this section — defaults to
     1920x1080. Override --frame-h (and/or --frame-w) on a
     specific #section-NN if its own background artwork was
     designed at a different size (e.g. section-03 is 1920x1980).
     Since height is always derived from width * (frame-h/frame-w)
     — i.e. the section's box always exactly matches the design
     frame's own ratio, by construction — --scale is always purely
     width-based; there is no "cover the viewport" branch to
     compute anymore.
     Two flavors: --scale (a length, for top/left/translate calcs)
     and --scale-num (unitless, required by the scale() function —
     scale() rejects a length argument and drops the WHOLE
     transform if given one). */
  --frame-w: 1920;
  --frame-h: 1080;
  --scale: calc(100vw / var(--frame-w));
  --scale-num: calc(100vw / (var(--frame-w) * 1px));
}

/* Sections use vw/vh calc()'d heights, which browsers can round
   to the nearest device pixel slightly differently per section
   at certain resolutions/zoom levels — that rounding mismatch
   is what shows up as an intermittent hairline gap between
   sections when resizing. Pulling every section (except the
   first) up by 1px to overlap the one above it makes that gap
   impossible regardless of resolution; the 1px overlap of two
   cover-fit background images is invisible. */
.section + .section {
  margin-top: -1px;
}

/* A section's own background photo (class="section-bg") always
   fills the section completely, cropped to fit, aspect ratio
   never distorted. This never touches .frame-el overlay images. */
.section-bg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* ==========================================================
   FRAME-EL — standing rule for placing anything on the hero.
   Give an element class="frame-el" plus inline custom
   properties --x and --y (its coordinates on the ORIGINAL
   1920x1080 design frame, top-left origin): it is anchored to
   that exact spot and scales with the photo at any screen size,
   keeping its own natural size/aspect ratio (never stretched).

   - Do not set width/height on a .frame-el — it renders at the
     image's natural pixel size, then scales uniformly.
   - Never give a .frame-el a responsive width/height (%, vw,
     media queries) — the transform already handles all of that.
   ========================================================== */
.frame-el {
  display: block;
  position: absolute;
  /* top/left are always 0 now: since the section's own box always
     exactly matches frame-w x frame-h * scale (no cropping, see
     .section above), there's never any leftover space to center
     within. */
  top: 0;
  left: 0;
  transform-origin: top left;
  transform: translate(calc(var(--x, 0) * var(--scale)), calc(var(--y, 0) * var(--scale))) scale(var(--scale-num));
}

/* ==========================================================
   CANVAS-EL — standing rule for placing anything at an exact
   spot within a normal (non-hero) fixed-1920px section. Give
   an element class="canvas-el" plus inline custom properties
   --x and --y (its coordinates on the 1920x1080 design frame):
   since these sections are never scaled to the viewport, the
   coordinates map straight to px — no scale math needed.
   ========================================================== */
.canvas-el {
  display: block;
  position: absolute;
  left: calc(var(--x, 0) * 1px);
  top: calc(var(--y, 0) * 1px);
}

/* ==========================================================
   ICON — hover state for the software-icon row. Each icon is
   the sole child of its own .frame-el wrapper (positioning
   only, top-left pivot) — .icon itself just scales 100% -> 110%
   from ITS OWN center on hover, independently per icon, with a
   soft white glow, both easing smoothly in and back out. Since
   this transform is separate from .frame-el's, it never
   disturbs the positioning/responsive-scale math.
   ========================================================== */
.icon {
  display: block;
  transform-origin: center;
  transition: transform 0.3s ease, filter 0.3s ease;
  cursor: pointer;
}

.icon:hover {
  transform: scale(1.1);
  filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.05));
}

/* ==========================================================
   HOVER-GROW — reusable scale-from-center hover (no glow).
   Same wrapper pattern as .icon: put this on the element
   itself (inside its own .frame-el positioning wrapper) so it
   grows from ITS OWN center without disturbing positioning.
   ========================================================== */
.hover-grow {
  display: block;
  transform-origin: center;
  transition: transform 0.3s ease;
  cursor: pointer;
}

.hover-grow:hover {
  transform: scale(1.1);
}

/* SECTION 3 — its background artwork (3bg.jpg) is 1920x1980,
   taller than the standard 1920x1080 frame. Overriding --frame-h
   here is all that's needed now — .section's height/--scale
   formulas already derive everything from --frame-h/--frame-w
   universally, so there's no separate override to keep in sync. */
#section-03 {
  --frame-h: 1980;
}

/* SECTION 4 — amazon-bg.jpg is 1920x2334. */
#section-04 {
  --frame-h: 2334;
}

/* SECTION 5 — 5.typek.jpg is 1920x2501. */
#section-05 {
  --frame-h: 2501;
}

/* SECTION 6 — 6.ul.jpg is 1920x1912. */
#section-06 {
  --frame-h: 1912;
}

/* SECTION 7 — 7.social.jpg is 1920x4086. */
#section-07 {
  --frame-h: 4086;
}

/* ==========================================================
   INVERT-CURSOR — shared 100px circle that follows the mouse and
   inverts whatever it's over (mix-blend-mode: difference against
   a white fill inverts every color beneath it). A smaller 20px
   Gaussian blur (20% of the 100px size) only feathers the outer
   edge — the 50px blur previously used was strong enough relative
   to the circle's size that it diluted the ENTIRE circle, so even
   dead-center wasn't a full/solid revert. At 20px blur, the
   center (well beyond the ~20px-wide blurred edge band) stays a
   full-strength invert. Fixed to the viewport (real screen px,
   not scaled by any section's --scale) so it's always a true
   100px circle regardless of screen size. Shown/positioned by
   js/script.js only while hovering the specific target element
   (e.g. #amazonText) — purely hover-driven, unaffected by clicks.
   ========================================================== */
.invert-cursor {
  position: fixed;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background: #ffffff;
  filter: blur(20px);
  mix-blend-mode: difference;
  pointer-events: none;
  display: none;
  z-index: 9999;
}

/* ==========================================================
   POP-IN — scroll-triggered reveal used by section-04's amazon
   group. Scales 0% -> 110% over the first 1s, then settles
   110% -> 100% over the next 3s (4s total), from its own center.
   Stays at scale(0) (invisible) until js/script.js adds .play
   (via IntersectionObserver, once section-04 scrolls into view)
   — plays once, never repeats. Each element's own inline
   animation-delay staggers the group into the requested order.
   ========================================================== */
.pop-in {
  display: block;
  transform-origin: center;
  transform: scale(0);
  animation-duration: 4s;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
}

.pop-in.play {
  animation-name: pop-in-reveal;
}

@keyframes pop-in-reveal {
  0% {
    transform: scale(0);
  }
  25% {
    transform: scale(1.1);
  }
  100% {
    transform: scale(1);
  }
}

/* ==========================================================
   CAROUSEL — infinite-scrolling image strips (section-05's
   typek1-min..typek20-min showcase). Two rows total: carousel 1
   is one row ascending/left-to-right, carousel 2 is one row
   descending/right-to-left. Rows are populated AND driven by JS
   (js/script.js) — a manual requestAnimationFrame loop, not CSS
   @keyframes, so scrolling can stop instantly on hover and ease
   back up from 0 to full speed on mouse-out. Each row's 20 images
   are duplicated to 40 so the loop is seamless (wraps exactly at
   the halfway point, where the content repeats). No explicit
   viewport width/overflow:hidden needed — html/body's
   overflow-x:hidden already clips anything that scrolls past the
   edge of the actual browser window.
   ========================================================== */
.carousel-tilt {
  transform: rotate(-3.9deg);
  transform-origin: top left;
}

.carousel-row {
  display: flex;
  gap: 30px;
  width: max-content;
}

/* Native size 1080x1350, resized down to 252.37x316.35 (same
   ratio). A slight 25%-opacity blurred drop shadow on every
   image; hovering one scales it to 120% from its own center
   (independent per image — only the one under the cursor grows)
   and lifts it above its neighbors so the scale isn't clipped by
   them. The pause/ramp-up-speed behavior on hover is handled in
   JS, not here — see initCarousel in js/script.js. */
.carousel-row img {
  display: block;
  width: 252.37px;
  height: 316.35px;
  position: relative;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.25));
  transform-origin: center;
  transition: transform 0.3s ease;
}

.carousel-row img:hover {
  transform: scale(1.2);
  z-index: 1;
}

/* ==========================================================
   SLIDE-IN-RIGHT — scroll-triggered reveal (section-05's
   type-2). Starts off-frame to the right, slowly slides in to
   its resting position (translateX(0)) once .play is added by
   js/script.js the first time section-05 scrolls into view —
   plays once, never repeats. 150% is relative to the element's
   own width, so it starts fully clear of itself regardless of
   its actual size.
   ========================================================== */
.slide-in-right {
  display: block;
  transform: translateX(150%);
  transition: transform 1.8s ease-out;
}

.slide-in-right.play {
  transform: translateX(0);
}

/* ==========================================================
   ROCK — a slight, continuous +-2deg rocking motion, looping
   seamlessly every 10s (section-06's hands + ul-video1). The
   pivot (bottom-left vs bottom-right) matches which corner of
   the element should stay planted while the rest sways. Any
   element sharing the same class/animation stays in sync with
   every other element using it, since they all start the
   identical animation at the same time (page load) — this is
   how hand-right1 and ul-video1 rock together without JS.
   ========================================================== */
.rock-bottom-left {
  display: block;
  transform-origin: bottom left;
  animation: rock 10s ease-in-out infinite;
}

.rock-bottom-right {
  display: block;
  transform-origin: bottom right;
  animation: rock 10s ease-in-out infinite;
}

@keyframes rock {
  0% {
    transform: rotate(0deg);
  }
  25% {
    transform: rotate(2deg);
  }
  50% {
    transform: rotate(0deg);
  }
  75% {
    transform: rotate(-2deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

/* ==========================================================
   FADE-IN-FOCUS — scroll-triggered reveal (section-06's
   social-text). Starts invisible and blurred, fades to 100%
   opacity and sharp focus over 5s once .play is added by
   js/script.js the first time section-06 scrolls into view —
   plays once, never repeats. Same wrapper pattern as the other
   reveals: positioned via an outer .frame-el, animated here on
   the inner element.
   ========================================================== */
.fade-in-focus {
  display: block;
  opacity: 0;
  filter: blur(20px);
  transition: opacity 5s ease-out, filter 5s ease-out;
}

.fade-in-focus.play {
  opacity: 1;
  filter: blur(0);
}

/* ==========================================================
   CLICK-RIPPLE — a hollow ring that starts at the click point
   and slowly expands in place, losing opacity as it grows.
   Spawned/removed by js/script.js on every left click; this
   just defines the look/animation of each one.
   ========================================================== */
.click-ripple {
  position: fixed;
  top: 0;
  left: 0;
  width: 20px;
  height: 20px;
  border: 2px solid rgba(255, 255, 255, 0.8);
  border-radius: 50%;
  background: transparent;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(1);
  z-index: 9999;
  animation: click-ripple-expand 0.9s ease-out forwards;
}

@keyframes click-ripple-expand {
  from {
    transform: translate(-50%, -50%) scale(1);
    opacity: 0.8;
  }
  to {
    transform: translate(-50%, -50%) scale(6);
    opacity: 0;
  }
}

/* ==========================================================
   FLOAT — a slight, continuous drift of just a few pixels,
   looping seamlessly (section-01's hp-gradient). Starts and ends
   at the same transform so the loop is invisible.
   ========================================================== */
.float {
  display: block;
  animation: float 4s ease-in-out infinite;
}

/* Mouse-parallax layer for hp-gradient: js/script.js sets this
   element's transform directly on mousemove; the transition here
   is what gives it a soft, trailing lag rather than snapping
   straight to the cursor. Kept on its own element (not the
   .float img) so the JS-driven transform and the CSS keyframe
   bob never overwrite each other. */
#hpGradientMouse {
  display: block;
  transition: transform 0.6s ease-out;
}

/* Slight, slow, ambient liquid wobble on hp-gradient's shape —
   the actual distortion (SVG feTurbulence/feDisplacementMap) is
   defined in index.html; js/script.js slowly drifts the
   turbulence over time for a gentle, continuous "liquid" feel
   (not hover-triggered — always on, subtle). */
.liquid-wobble {
  filter: url(#liquid-gradient-filter);
}

/* ==========================================================
   HOVER-GROW-SLOW — section-05's images. Scale 100%->110% on
   hover over 1s ease-in; reverts over 2s ease-out. Asymmetric
   timing works because the :hover rule's transition governs
   entering hover, while the base rule's transition governs
   leaving it — standard technique for different enter/exit
   speeds. Deliberately does NOT set `transform` outside :hover,
   so it composes safely with any other class that already
   controls this element's resting transform (e.g. type-2's
   .slide-in-right, which settles at translateX(0) once played —
   this rule only ever touches transform while actively hovered).
   ========================================================== */
.hover-grow-slow {
  display: block;
  transform-origin: center;
  transition: transform 2s ease-out;
}

.hover-grow-slow:hover {
  transform: scale(1.1);
  transition: transform 1s ease-in;
}

@keyframes float {
  0%, 100% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(4px, -6px);
  }
}

/* ==========================================================
   CONTACT WIDGET — fixed to the bottom-right of the browser
   window (real px, never scaled by --scale — stays a constant
   size/position on any screen, unlike everything else on the
   site which uses .frame-el). 260x89 container, right:0;
   bottom:276px, matching long-retangle.png's actual size and
   the "260 = 1920-1660" / "89 = shared height" math worked out
   from the given design coordinates.

   Two states, toggled via #contactWidget.contact-open (added/
   removed by js/script.js):
   - Collapsed (default): #contactSquare + #contactPlus visible.
   - Open: they fade out while #contactRect fades in; once that
     cross-fade finishes (JS listens for transitionend on
     #contactRect), the 3 .contact-icon-link icons fade in on
     top (each also needs .contact-icons-visible, added by JS,
     so they can't be clicked/seen until the reveal completes).
   HOVER-driven (not click): mouseenter on #contactWidget opens
   it, mouseleave closes it — hovering anywhere in the widget's
   box (including once expanded) keeps it open.
   #contactSquare/#contactPlus use mix-blend-mode: difference so
   they stay visible against light AND dark sections alike.
   ========================================================== */
#contactWidget {
  position: fixed;
  right: 0;
  bottom: 276px;
  width: 260px;
  height: 89px;
  z-index: 9990;
}

#contactSquare,
#contactPlus {
  position: absolute;
  display: block;
  opacity: 1;
  transition: opacity 0.4s ease;
  pointer-events: auto;
}

#contactSquare {
  left: 150px;
  top: 0;
  width: 110px;
  height: 89px;
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1.5px solid #ffffff;
  box-shadow: 0 0 0 1.5px #000000;
  box-sizing: border-box;
  border-top-left-radius: 10px;
  border-bottom-right-radius: 10px;
  mix-blend-mode: difference;
}

#contactPlus {
  left: 175px;
  top: 17px;
  width: 63px;
  height: 63px;
  pointer-events: none; /* hover on it still reaches #contactWidget beneath it */
  transform-origin: center;
  animation: plus-pulsate 4s infinite;
  mix-blend-mode: difference;
}

/* 100% -> 105% over 2s ease-in, then back to 100% over 2s
   ease-out, looping forever. Per-keyframe animation-timing-
   function governs the SEGMENT LEADING UP TO that keyframe —
   ease-in on the 0% stop times the 0%->50% (grow) segment,
   ease-out on the 50% stop times the 50%->100% (shrink) segment. */
@keyframes plus-pulsate {
  0% {
    transform: scale(1);
    animation-timing-function: ease-in;
  }
  50% {
    transform: scale(1.05);
    animation-timing-function: ease-out;
  }
  100% {
    transform: scale(1);
  }
}

#contactWidget.contact-open #contactSquare,
#contactWidget.contact-open #contactPlus {
  opacity: 0;
  pointer-events: none;
}

#contactRect {
  position: absolute;
  left: 0;
  top: 0;
  width: 260px;
  height: 89px;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  background: rgba(255, 255, 255, 0.25);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1.5px solid #ffffff;
  box-shadow: 0 0 0 1.5px #000000;
  box-sizing: border-box;
  border-top-left-radius: 10px;
  border-bottom-right-radius: 10px;
}

#contactWidget.contact-open #contactRect {
  opacity: 1;
  pointer-events: auto;
}

.contact-icon-link {
  position: absolute;
  display: block;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

.contact-icon-link.contact-icons-visible {
  opacity: 1;
  pointer-events: auto;
}

.contact-icon-link img {
  display: block;
  width: 100%;
  height: 100%;
}

#contactPhone {
  left: 20px;
  top: 20px;
  width: 58px;
  height: 59px;
}

#contactEmail {
  left: 99.57px;
  top: 26.07px;
  width: 60px;
  height: 47px;
}

#contactWebsite {
  left: 180.57px;
  top: 20.55px;
  width: 58px;
  height: 58px;
}

/* Add section-specific styles below as sections are filled in,
   e.g.:

   #section-02 { background: #000; }
   #section-02 h1 { font-family: ...; font-size: ...px; }
*/
