/* ============================================================
   OPTIC — MOTION + FIT LAYER
   Loaded after styles.css. Additive only: no brand changes.
   Typography (Syne / JetBrains Mono), palette (#0A192F / #ffffff /
   #4DA6FF) and layout language are preserved exactly.
   ============================================================ */

/* ------------------------------------------------------------
   1. FIT — make the hero fit the screen WITHOUT shrinking the
   wordmark.

   The export used h-screen + min-h-[700px] + pt-32: overflows any
   laptop under ~700px tall, and 100vh mis-measures on mobile where
   the browser chrome overlays the viewport.

   Strategy: the wordmark is the design, so it keeps its intended
   width-derived size (15.5vw) at every viewport. The vertical room
   is bought back from the CHROME — padding, eyebrow, marquee —
   which compresses as height gets tight. Type only gives way on
   genuinely extreme viewports (landscape phones), and even then by
   an explicit last-resort rule rather than a blanket vh cap that
   silently shrinks the wordmark on ordinary desktops.
   ------------------------------------------------------------ */
.optic-hero {
  height: 100svh;
  min-height: 0;
  max-height: 100svh;
  /* chrome scales with available height, type does not */
  padding-top: clamp(4.5rem, 9vh, 7rem);
  padding-bottom: clamp(0.75rem, 2.5vh, 2rem);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
}

@supports not (height: 100svh) {
  .optic-hero { height: 100vh; max-height: 100vh; }
}

/* The wordmark is sized to FILL the measure, never to clip.

   The export hardcoded `clamp(3rem, 15.5vw, 15.5vw)`. That number was
   tuned against the fallback sans-serif (where "HARDWARE." is 5.81x the
   font-size and happens to fill the container). Real Syne 800 is far
   wider — 10.18x — so 15.5vw demands 2270px inside a 1376px container
   and silently clips ~900px off the right. It only ever looked correct
   while the webfont failed to load.

   So: derive the size from the actual metric instead of guessing.
   "HARDWARE." is the longest line and governs both. The measured
   in-situ ratio is 10.381 (text width / font-size); 10.43 fills
   99.5% of the measure — flush, with a hair of optical breathing
   room so no edge ever kisses the container.

   Container = min(100vw, 1600px) - 2x page padding (px-8 = 32px, px-4
   = 16px below md). This is deterministic because the typeface is
   self-hosted — the metric cannot drift. No JS, no layout jump. */
.optic-hero-title {
  font-size: calc((min(100vw, 1600px) - 64px) / 10.43) !important;
  margin: 0;
}

@media (max-width: 767px) {
  .optic-hero-title {
    font-size: calc((100vw - 32px) / 10.43) !important;
  }
}

.optic-hero-inner {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 0;
  flex: 1;
}

/* Chrome compression ladder — each step reclaims height from
   spacing, never from the wordmark. */
@media (max-height: 860px) {
  .optic-hero-eyebrow { margin-bottom: 1.25rem !important; }
  .optic-hero-marquee { margin-top: 1.5rem !important; }
}
@media (max-height: 760px) {
  .optic-hero-eyebrow { margin-bottom: 0.75rem !important; }
  .optic-hero-marquee { margin-top: 1rem !important; }
  .optic-hero-marquee .optic-marquee-track > span {
    padding-top: 0.85rem !important;
    padding-bottom: 0.85rem !important;
  }
}
@media (max-height: 640px) {
  .optic-hero { padding-top: 4rem; }
  .optic-hero-eyebrow { margin-bottom: 0.5rem !important; }
  .optic-hero-marquee { margin-top: 0.75rem !important; }
  .optic-hero-marquee .optic-marquee-track > span {
    padding-top: 0.6rem !important;
    padding-bottom: 0.6rem !important;
  }
}

/* Last resort — landscape phones / very short windows. The wordmark
   now fills the width, so on a wide-but-tiny window it can still be
   too tall; cap it against height here. Explicit exception only. */
@media (max-height: 520px) and (min-aspect-ratio: 3/2) {
  .optic-hero-title { font-size: min(calc((min(100vw, 1600px) - 64px) / 10.43), 13vh) !important; }
  .optic-hero-marquee { display: none; }
}

/* Kill horizontal overflow globally (hero type + marquee are nowrap) */
html, body { overflow-x: hidden; max-width: 100%; }

/* ------------------------------------------------------------
   2. ANIMATION INITIAL STATES
   Content is VISIBLE by default. Initial states are only applied
   once JS confirms it can animate (html.optic-ready). If JS fails,
   nothing is ever hidden — the opposite of the broken export.
   ------------------------------------------------------------ */
html.optic-ready [data-anim="fade"]      { opacity: 0; }
html.optic-ready [data-anim="fade-up"]   { opacity: 0; transform: translateY(var(--anim-y, 40px)); }
html.optic-ready [data-anim="fade-left"] { opacity: 0; transform: translateX(var(--anim-x, -30px)); }
html.optic-ready [data-anim="mask-up"]   { transform: translateY(110%); }
html.optic-ready [data-anim="char"]      { transform: translateY(110%); }

[data-anim] { will-change: transform, opacity; }
[data-anim-done] { will-change: auto; }

/* Reveal-mask wrappers: clip the char/line as it travels up.
   NOTE: deliberately does NOT set `display`. This rule loads after
   Tailwind at equal specificity, so `display:block` here would beat
   the `.inline-block` on each character wrapper and stack every
   letter of the wordmark vertically down the page. Only clip. */
.optic-mask { overflow: hidden; }

/* ------------------------------------------------------------
   3. SCROLL PROGRESS BAR (was frozen at scaleX(0))
   ------------------------------------------------------------ */
.optic-progress {
  transform-origin: 0% 50%;
  transform: scaleX(0);
  will-change: transform;
}

/* ------------------------------------------------------------
   4. MARQUEE — driven by JS transform, not CSS keyframes, so it
   can share the scroll ticker and stay in sync at 60fps.
   ------------------------------------------------------------ */
.optic-marquee-track {
  animation: none !important; /* replaces .animate-marquee */
  will-change: transform;
}

/* ------------------------------------------------------------
   5. PRODUCT CARDS — cinematic reveal
   Images sit under a panel that wipes upward, and burn off
   grayscale as they settle. Emerging from darkness, not fading.
   ------------------------------------------------------------ */
.optic-media {
  position: relative;
  overflow: hidden;
  background: #0A192F;
}
.optic-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #0A192F;
  z-index: 2;
  /* Default OFF-screen. Cards injected after motion.js has already wired its
     ScrollTriggers (the live Shopify grid on the homepage) never receive the
     wipe tween, so a default of 0% left the panel permanently covering the
     photo. GSAP's fromTo sets 0% itself for the cards it does animate. */
  transform: translateY(var(--wipe, -101%));
  pointer-events: none;
  will-change: transform;
}
html:not(.optic-ready) .optic-media::after { display: none; }


.optic-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(1.05) brightness(0.95);
  transform: scale(1.06);
  transition: filter 0.9s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.9s cubic-bezier(0.16, 1, 0.3, 1);
}
.optic-card:hover .optic-media img,
.optic-card:focus-visible .optic-media img {
  filter: saturate(1.2) brightness(1.05);
  transform: scale(1);
}

/* Accent hairline that draws across the card on hover */
.optic-card .optic-rule {
  position: relative;
  overflow: hidden;
}
.optic-card .optic-rule::after {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 100%; height: 1px;
  background: #4DA6FF;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.optic-card:hover .optic-rule::after,
.optic-card:focus-visible .optic-rule::after { transform: scaleX(1); }

/* ------------------------------------------------------------
   6. MAGNETIC BUTTONS / LINKS
   ------------------------------------------------------------ */
.optic-magnetic { display: inline-block; will-change: transform; }

/* ------------------------------------------------------------
   7. CUSTOM CURSOR — desktop + fine pointer only
   ------------------------------------------------------------ */
#optic-cursor { display: none !important; }

/* ------------------------------------------------------------
   8. AWKWARDNESS FIXES — found by auditing at 10 viewports
   ------------------------------------------------------------ */

/* 8a. Mobile nav: the logo sits in a 4-col cage (~109px at 390px
   wide), so "Est. 2026 — Bengaluru, India" wrapped to THREE lines
   (four at 320px) and collided with the links, while "Cart (0)"
   broke across two. Below md the tagline is redundant — the
   wordmark carries the brand — so it steps aside and the row
   becomes a simple flex. */
@media (max-width: 767px) {
  header .grid {
    display: flex !important;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-left: 0.875rem !important;
    padding-right: 0.875rem !important;
  }
  header .grid > a { flex: 0 0 auto; }
  header .grid > a > span:first-child {
    font-size: 1rem !important;
    letter-spacing: -0.01em;
  }
  header .grid > nav {
    flex: 1 1 auto;
    justify-content: flex-end;
    gap: 0.75rem !important;
    letter-spacing: 0.06em !important;
    font-size: 10px !important;
    white-space: nowrap;
  }
  header .grid > nav > a:first-child { display: none; } /* INDEX redundant with logo */
  header .grid > nav > div:nth-of-type(3) { display: none; } /* hide ABOUT on mobile */
  .optic-nav-tag { display: none; }
}

@media (max-width: 380px) {
  header .grid > nav { gap: 0.5rem !important; font-size: 9px !important; }
}

/* 8b. Tap targets */
@media (max-width: 900px), (hover: none), (pointer: coarse) {
  header nav a,
  header nav button,
  header > div > a,
  footer a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  header > div > a { flex-direction: column; justify-content: center; align-items: flex-start; }
}

/* Marquee: mask edges so text never appears cut off */
.optic-hero-marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #0A192F 6%, #0A192F 94%, transparent);
          mask-image: linear-gradient(to right, transparent, #0A192F 6%, #0A192F 94%, transparent);
}

/* 8c. Images: no width/height in the export means the browser can't
   reserve space, so cards jump as each photo decodes. The grid is
   square, so declare the ratio and let the box be reserved. */
.optic-media {
  aspect-ratio: 1 / 1;
}
.optic-media img {
  display: block;
  width: 100%;
  height: 100%;
}

/* 8d. Display headings outside the hero. "WE DON'T DROPSHIP." is a
   single unbreakable word wider than its column once real Syne
   loads — at 1440 it wanted 1143px inside a 720px column and spilled
   423px across the paragraph beside it.

   Its column is not a clean vw fraction (it swings from .35 to .44 of
   the viewport across breakpoints), so a calc() would be guesswork.
   motion.js measures and fits it exactly instead. These two rules are
   only the no-JS safety net: contain the word rather than let it
   overlap neighbouring text. */
.optic-fit-line {
  max-width: 100%;
  overflow-wrap: break-word;
}
/* 8e. Unified page gutter. Mirrors index.html's container
   (max-w-[1600px] mx-auto px-4 md:px-8) so the nav logo, page
   headings and footer all share one left edge on every page. */
.optic-gutter {
  max-width: 1600px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 1rem;
  padding-right: 1rem;
}
@media (min-width: 768px) {
  .optic-gutter {
    padding-left: 2rem;
    padding-right: 2rem;
  }
}

/* ------------------------------------------------------------
   10. SIGNATURE — the hero fill
   A solid twin sits exactly over the hollow wordmark and is
   revealed by clip-path as you scroll. Both copies share the same
   box, so they cannot drift apart at any viewport.
   ------------------------------------------------------------ */
.optic-fill-twin {
  position: absolute;
  inset: 0;
  color: #ffffff;
  -webkit-text-stroke: 0;
  pointer-events: none;
  will-change: clip-path;
}

/* the accent seam riding the leading edge of the fill */
.optic-fill-seam {
  position: absolute;
  top: -2%;
  bottom: -2%;
  width: 2px;
  margin-left: -1px;
  background: linear-gradient(
    to bottom,
    transparent,
    #4DA6FF 18%,
    #4DA6FF 82%,
    transparent
  );
  box-shadow: 0 0 18px 2px rgba(77,166,255, 0.55);
  pointer-events: none;
  will-change: left;
  opacity: 0;
}

/* scroll-velocity shear target */
.optic-shear { will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  /* no drive, no fill: show the word solid and be done */
  .optic-fill-twin { clip-path: none !important; }
  .optic-fill-seam { display: none !important; }
  .optic-shear { transform: none !important; }
}

/* ------------------------------------------------------------
   11. ACCESSIBILITY — reduced motion wins over everything
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  html.optic-ready [data-anim] {
    opacity: 1 !important;
    transform: none !important;
  }
  html.optic-ready .optic-media::after { display: none; }
  .optic-media img { filter: none; transform: none; }
  .optic-progress { transform: scaleX(0); }
  #optic-cursor { display: none; }
  html.optic-ready, html.optic-ready a, html.optic-ready button { cursor: auto; }
  .optic-marquee-track { transform: none !important; }
}

/* Keyboard focus stays visible even with the custom cursor on */
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 2px solid #4DA6FF;
  outline-offset: 3px;
}

/* Safeguard: nav must always be readable, never faded */
header.nav-blend { mix-blend-mode: normal !important; }
header [data-anim] { opacity: 1 !important; transform: none !important; }

/* Live (Shopify-injected) product cards mount after the scroll timeline is
   built, so they animate in with plain CSS instead of GSAP. */
@keyframes optic-live-card-in {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: translateY(0); }
}
.optic-card--live {
  opacity: 0;
  animation: optic-live-card-in 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@media (prefers-reduced-motion: reduce) {
  .optic-card--live { opacity: 1; animation: none; }
}
