/* =========================================================
   LifeMathEqual — Galaxy Blue
   A welcoming deep-space theme with a living starfield,
   drifting nebula light, and shooting stars.
   ========================================================= */

:root {
  --void: #04060f;
  --deep: #081026;
  --galaxy: #0c1c44;
  --galaxy-2: #112a63;
  --azure: #5b93ff;
  --azure-soft: #8fb6ff;
  --cyan: #79e0ff;
  --star: #eef4ff;
  --ink: #dfe7fb;
  --muted: #93a4cf;
  --line: rgba(143, 182, 255, 0.16);
  --glass: rgba(13, 27, 62, 0.55);

  --display: "Cormorant Garamond", "Times New Roman", serif;
  --body: "Inter", system-ui, sans-serif;

  --radius: 20px;
  --maxw: 1180px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--body);
  color: var(--ink);
  background: var(--void);
  overflow-x: hidden;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* The brand rooted-tree mark replaces the default arrow site-wide.
     Falls back to the normal cursor if the image can't load. */
  cursor: url(assets/cursor-tree.png) 16 24, auto;
}

/* ---------------------------------------------------------
   The cosmos — fixed background layers

   Depth is built from five stacked layers, far to near:
     -5  base gradient + glowing galactic core
     -4  volumetric nebula clouds (fractal noise, tinted)
     -3  the Milky Way — a diagonal band of dust and dense stars
     -2  parallax starfields
     -1  shooting stars
   --------------------------------------------------------- */

/* Base deep-space gradient: a bright galactic core glows off to the
   upper right, wrapped in layered nebula clouds of blue, indigo, and
   violet, with a deep vignette that falls away to the void. */
body::before {
  content: "";
  position: fixed;
  inset: -20%;
  z-index: -5;
  background:
    /* bright galactic core, hot white-blue at its heart */
    radial-gradient(34% 24% at 70% 30%, rgba(214, 230, 255, 0.34), transparent 58%),
    radial-gradient(60% 48% at 70% 30%, rgba(120, 168, 255, 0.22), transparent 64%),
    /* surrounding indigo + violet gas clouds */
    radial-gradient(55% 46% at 15% 16%, rgba(99, 110, 240, 0.18), transparent 64%),
    radial-gradient(50% 42% at 88% 80%, rgba(58, 98, 212, 0.20), transparent 62%),
    radial-gradient(48% 40% at 26% 90%, rgba(40, 66, 152, 0.22), transparent 66%),
    /* cyan rim light feathering off the core */
    radial-gradient(40% 30% at 54% 58%, rgba(121, 224, 255, 0.10), transparent 62%),
    /* deep base, biased toward the core so it falls to void at the edges */
    radial-gradient(130% 120% at 66% 26%, var(--galaxy) 0%, var(--deep) 44%, var(--void) 100%);
  animation: nebula 32s ease-in-out infinite alternate;
}

@keyframes nebula {
  0%   { transform: scale(1) translate(0, 0); }
  100% { transform: scale(1.05) translate(-1.2%, 1%); }
}

/* Volumetric nebula: fractal-noise clouds tinted blue and screened over
   the base so the gas reads as real wisps rather than flat gradients.
   Masked toward the core and drifting slowly for a living feel. */
body::after {
  content: "";
  position: fixed;
  inset: -45%;
  z-index: -4;
  pointer-events: none;
  opacity: 0.55;
  mix-blend-mode: screen;
  background-image:
    radial-gradient(62% 52% at 66% 33%, rgba(110, 160, 255, 0.55), rgba(40, 70, 160, 0.18) 55%, transparent 72%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='800'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.011 0.015' numOctaves='4' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23c)'/%3E%3C/svg%3E");
  background-size: cover, 900px 900px;
  background-blend-mode: multiply;
  -webkit-mask-image: radial-gradient(72% 62% at 62% 36%, #000 8%, transparent 78%);
          mask-image: radial-gradient(72% 62% at 62% 36%, #000 8%, transparent 78%);
  animation: nebula-drift 70s ease-in-out infinite alternate;
}

@keyframes nebula-drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(-3%, 2%, 0) scale(1.12); }
}

/* The Milky Way — a diagonal band crossing the sky, with glowing dust
   along its spine and a dense scatter of stars concentrated in the band. */
.galaxy-band {
  position: fixed;
  inset: -45%;
  z-index: -3;
  pointer-events: none;
  transform: rotate(-24deg);
  opacity: 0.9;
  -webkit-mask-image: linear-gradient(180deg, transparent 38%, #000 50%, transparent 62%);
          mask-image: linear-gradient(180deg, transparent 38%, #000 50%, transparent 62%);
}
/* glowing dust clumps along the spine */
.galaxy-band::before {
  content: "";
  position: absolute;
  inset: 0;
  mix-blend-mode: screen;
  opacity: 0.6;
  background-image:
    linear-gradient(180deg, transparent 44%, rgba(150, 180, 255, 0.12) 50%, transparent 56%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.03 0.05' numOctaves='3' seed='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)'/%3E%3C/svg%3E");
  background-size: cover, 700px 700px;
  background-blend-mode: screen;
}
/* dense field of small stars riding the band */
.galaxy-band::after {
  content: "";
  position: absolute;
  inset: 0;
  background-repeat: repeat;
  background-size: 300px 300px;
  background-image:
    radial-gradient(1px 1px at 8% 47%, rgba(255, 255, 255, 0.95), transparent),
    radial-gradient(1px 1px at 17% 53%, rgba(208, 226, 255, 0.85), transparent),
    radial-gradient(1px 1px at 26% 49%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(1.4px 1.4px at 34% 51%, rgba(190, 214, 255, 0.9), transparent),
    radial-gradient(1px 1px at 43% 48%, rgba(255, 255, 255, 0.85), transparent),
    radial-gradient(1px 1px at 52% 52%, rgba(224, 236, 255, 0.8), transparent),
    radial-gradient(1.3px 1.3px at 61% 49%, rgba(255, 255, 255, 0.9), transparent),
    radial-gradient(1px 1px at 70% 51%, rgba(196, 218, 255, 0.85), transparent),
    radial-gradient(1px 1px at 79% 48%, rgba(255, 255, 255, 0.8), transparent),
    radial-gradient(1.4px 1.4px at 88% 52%, rgba(214, 230, 255, 0.9), transparent),
    radial-gradient(1px 1px at 95% 50%, rgba(255, 255, 255, 0.85), transparent);
  animation: drift-slow 240s linear infinite;
}

/* Three parallax star layers built from repeating radial dots */
.starfield {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  overflow: hidden;
}
.starfield::before,
.starfield::after {
  content: "";
  position: absolute;
  inset: -50%;
  background-repeat: repeat;
}
/* small distant stars, gentle twinkle, faint blue-white variety */
.starfield::before {
  background-image:
    radial-gradient(1px 1px at 20% 30%, rgba(238, 244, 255, 0.9), transparent),
    radial-gradient(1px 1px at 70% 60%, rgba(190, 214, 255, 0.7), transparent),
    radial-gradient(1px 1px at 40% 80%, rgba(200, 224, 255, 0.8), transparent),
    radial-gradient(1px 1px at 85% 15%, rgba(238, 244, 255, 0.7), transparent),
    radial-gradient(1px 1px at 10% 70%, rgba(180, 210, 255, 0.7), transparent),
    radial-gradient(1.5px 1.5px at 55% 25%, rgba(255, 255, 255, 0.9), transparent),
    radial-gradient(1px 1px at 33% 50%, rgba(238, 244, 255, 0.6), transparent),
    radial-gradient(1px 1px at 64% 38%, rgba(255, 246, 232, 0.6), transparent),
    radial-gradient(1px 1px at 90% 82%, rgba(214, 230, 255, 0.6), transparent);
  background-size: 360px 360px;
  animation: twinkle 6s steps(2, end) infinite, drift-slow 120s linear infinite;
}
/* larger near stars with soft halos, slower parallax */
.starfield::after {
  background-image:
    radial-gradient(2px 2px at 15% 45%, rgba(255, 255, 255, 0.95), transparent),
    radial-gradient(2px 2px at 80% 70%, rgba(143, 182, 255, 0.9), transparent),
    radial-gradient(2.5px 2.5px at 60% 20%, rgba(121, 224, 255, 0.85), transparent),
    radial-gradient(2px 2px at 35% 85%, rgba(255, 255, 255, 0.85), transparent),
    radial-gradient(2.5px 2.5px at 47% 58%, rgba(255, 252, 244, 0.8), transparent);
  background-size: 620px 620px;
  animation: twinkle 9s steps(2, end) infinite, drift-slow 200s linear infinite reverse;
}

@keyframes twinkle {
  0%, 100% { opacity: 0.9; }
  50%      { opacity: 0.55; }
}
@keyframes drift-slow {
  from { transform: translateY(0); }
  to   { transform: translateY(120px); }
}

/* Shooting stars layer */
.shooting {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.shooting span {
  position: absolute;
  top: -5%;
  left: 50%;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #fff;
  /* bright leading head */
  box-shadow:
    0 0 6px 2px rgba(255, 255, 255, 0.9),
    0 0 14px 4px rgba(143, 182, 255, 0.7);
  opacity: 0;
  /* the streak travels along its own +x axis; rotation aims it down-right */
  transform: rotate(32deg) translateX(0);
  animation: shoot 7s ease-in infinite;
}
/* the tail trails behind the head, brightest at the head and fading out */
.shooting span::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 3px;
  width: 220px;
  height: 2px;
  transform: translateY(-50%);
  border-radius: 2px;
  background: linear-gradient(
    90deg,
    rgba(121, 224, 255, 0) 0%,
    rgba(143, 182, 255, 0.45) 60%,
    rgba(238, 244, 255, 0.95) 100%
  );
}

/* Each star starts in a different spot and streaks at a different cadence.
   Long gaps between streaks keep the sky calm, then a star snaps across. */
.shooting span:nth-child(1) { top: 4%;  left: 8%;  animation-delay: 0s;   animation-duration: 7s;  }
.shooting span:nth-child(2) { top: -2%; left: 34%; animation-delay: 3.2s; animation-duration: 9s;  }
.shooting span:nth-child(3) { top: 10%; left: 60%; animation-delay: 5.5s; animation-duration: 8s;  }
.shooting span:nth-child(4) { top: 1%;  left: 78%; animation-delay: 1.6s; animation-duration: 11s; }
.shooting span:nth-child(5) { top: 16%; left: 20%; animation-delay: 7.8s; animation-duration: 10s; }

/* The star is invisible most of the cycle, then races fully across in a
   brief, fast burst — a real shooting-star streak rather than a slow drift. */
@keyframes shoot {
  0%   { transform: rotate(32deg) translateX(0);       opacity: 0; }
  1%   { opacity: 1; }
  9%   { opacity: 1; }
  12%  { transform: rotate(32deg) translateX(135vw);   opacity: 0; }
  100% { transform: rotate(32deg) translateX(135vw);   opacity: 0; }
}

/* Soft film grain to add texture over the void */
.grain {
  position: fixed;
  inset: 0;
  z-index: 999;
  pointer-events: none;
  opacity: 0.04;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------
   Checkout warp — a full-screen launch "into the galaxy"
   played the instant a shopper pays, just before the hand-off
   to the secure pay page. The sky punches into deep space as a
   bright shooting star streaks across, then the pay page loads
   as if we've arrived at the far end of the jump.
   --------------------------------------------------------- */
.warp {
  position: fixed;
  inset: 0;
  z-index: 10000;
  overflow: hidden;
  background:
    radial-gradient(120% 120% at 50% 58%, var(--galaxy) 0%, var(--deep) 46%, var(--void) 100%);
  opacity: 0;
  animation: warp-fade 0.3s ease forwards;
}
@keyframes warp-fade { to { opacity: 1; } }

/* Layered star dots that scale up from the centre and fade, so the whole
   sky appears to rush past — the feeling of punching forward into space. */
.warp-streaks {
  position: absolute;
  inset: -60%;
  background-image:
    radial-gradient(2px 2px   at 20% 30%, rgba(238, 244, 255, 0.95) 50%, transparent 51%),
    radial-gradient(2px 2px   at 70% 20%, rgba(143, 182, 255, 0.90) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 40% 72%, rgba(238, 244, 255, 0.85) 50%, transparent 51%),
    radial-gradient(2.5px 2.5px at 85% 64%, rgba(121, 224, 255, 0.90) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 55% 48%, rgba(238, 244, 255, 0.80) 50%, transparent 51%),
    radial-gradient(2px 2px   at 10% 82%, rgba(143, 182, 255, 0.85) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 32% 14%, rgba(238, 244, 255, 0.80) 50%, transparent 51%),
    radial-gradient(2px 2px   at 90% 38%, rgba(238, 244, 255, 0.90) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 62% 84%, rgba(121, 224, 255, 0.80) 50%, transparent 51%);
  transform: scale(0.35);
  opacity: 0;
  animation: warp-fly 1.45s cubic-bezier(0.5, 0, 0.9, 1) forwards;
}
.warp-streaks--2 {
  animation-delay: 0.16s;
  filter: blur(0.5px);
}
@keyframes warp-fly {
  0%   { transform: scale(0.35); opacity: 0; }
  18%  { opacity: 1; }
  100% { transform: scale(7);    opacity: 0; }
}

/* The hero shooting star: a bright head with a long trailing streak that
   races across the sky as we launch. */
.warp-star {
  position: absolute;
  top: 14%;
  left: -12%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow:
    0 0 10px 3px rgba(255, 255, 255, 0.95),
    0 0 26px 8px rgba(143, 182, 255, 0.8);
  transform: rotate(26deg) translateX(0);
  opacity: 0;
  animation: warp-shoot 1.1s cubic-bezier(0.45, 0, 0.55, 1) 0.12s forwards;
}
.warp-star::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 4px;
  width: 340px;
  height: 3px;
  transform: translateY(-50%);
  border-radius: 3px;
  background: linear-gradient(
    90deg,
    rgba(121, 224, 255, 0) 0%,
    rgba(143, 182, 255, 0.5) 55%,
    rgba(238, 244, 255, 0.98) 100%
  );
}
@keyframes warp-shoot {
  0%   { transform: rotate(26deg) translateX(0);      opacity: 0; }
  10%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: rotate(26deg) translateX(155vw);  opacity: 0; }
}

.warp-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 18%;
  margin: 0;
  text-align: center;
  font-family: var(--display);
  font-size: clamp(1.4rem, 4vw, 2.4rem);
  letter-spacing: 0.04em;
  color: var(--star);
  text-shadow: 0 0 18px rgba(143, 182, 255, 0.6);
  opacity: 0;
  animation: warp-label 1.45s ease forwards;
}
@keyframes warp-label {
  0%   { opacity: 0; transform: translateY(10px); }
  26%  { opacity: 1; transform: translateY(0); }
  84%  { opacity: 1; }
  100% { opacity: 0.85; }
}

@media (prefers-reduced-motion: reduce) {
  body::before,
  body::after,
  .galaxy-band::before,
  .galaxy-band::after,
  .starfield::before,
  .starfield::after,
  .shooting span,
  .warp-streaks,
  .warp-star,
  .warp-label,
  .garment { animation: none !important; }

  .shooting,
  .grain {
    display: none;
  }
}

@media (max-width: 760px), (pointer: coarse) {
  body::after,
  .galaxy-band::before,
  .grain {
    display: none;
  }

  .galaxy-band {
    opacity: 0.55;
  }

  .starfield::before,
  .starfield::after {
    animation-duration: 260s !important;
  }

  .shooting span:nth-child(n+3) {
    display: none;
  }

  .garment-stage::before {
    filter: blur(6px);
  }
}

/* ---------------------------------------------------------
   Layout primitives
   --------------------------------------------------------- */
main { width: 100%; }

.section {
  width: min(100% - 3rem, var(--maxw));
  margin: 0 auto;
  padding: 6rem 0;
}

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.32em;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--cyan);
  margin: 0 0 1rem;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.05;
  color: var(--star);
  margin: 0 0 1rem;
  letter-spacing: 0.4px;
}

h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); }
h2 { font-size: clamp(2rem, 4vw, 3rem); }
h3 { font-size: 1.4rem; }

p { color: var(--muted); }

/* ---------------------------------------------------------
   Navigation
   --------------------------------------------------------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.1rem clamp(1.5rem, 4vw, 3rem);
  background: linear-gradient(180deg, rgba(4, 6, 15, 0.85), rgba(4, 6, 15, 0.25));
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: var(--star);
  font-family: var(--display);
  font-size: 1.3rem;
  letter-spacing: 0.5px;
}
.brand img {
  width: auto;
  height: 46px;
  object-fit: contain;
  filter: brightness(0) invert(1);
}
/* Inline brand glyphs — the LifeMath logo symbol and the rooted-tree symbol
   stand in for the words "LifeMath"/"LifeMath=" and "Tree" in on-page labels. */
.lm-glyph,
.tree-glyph {
  display: inline-block;
  width: auto;
  margin: 0 0.12em;
}
.lm-glyph {
  height: 1.05em;
  vertical-align: -0.18em;
  filter: brightness(0) invert(1);
}
.tree-glyph {
  height: 1.35em;
  vertical-align: -0.32em;
}
/* Primary tab styling now lives in the "Tabbed navigation" block below
   (.nav-tabs). The legacy `.pill` button is kept for any other use. */
.pill {
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  border: 1px solid rgba(143, 182, 255, 0.4);
  background: rgba(91, 147, 255, 0.12);
  color: var(--star) !important;
  opacity: 1 !important;
}
.pill:hover {
  background: rgba(91, 147, 255, 0.25);
  box-shadow: 0 0 24px rgba(91, 147, 255, 0.35);
}

/* Cart tab — sits in the nav, links to the on-page cart and shows a live
   badge with however many items are currently in the cart. A shopping-bag
   glyph and a vibrant count badge make it read instantly as the cart. */
.cart-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1.05rem;
  border-radius: 999px;
  border: 1px solid rgba(143, 182, 255, 0.4);
  background: rgba(91, 147, 255, 0.12);
  color: var(--star) !important;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.2px;
  opacity: 1 !important;
  transition: transform 0.2s, box-shadow 0.25s, background 0.25s, border-color 0.2s;
}
.cart-tab:hover {
  background: rgba(91, 147, 255, 0.25);
  box-shadow: 0 0 24px rgba(91, 147, 255, 0.35);
  transform: translateY(-1px);
}
.cart-tab-label { line-height: 1; }

/* The shopping-bag glyph shared by the cart tab and the compact mobile cart. */
.cart-ico {
  flex: none;
  display: block;
}

/* The live count badge — a small azure pill that appears the moment the cart
   has something in it. It collapses out of view when the cart is empty, and
   gives a brief pop whenever the count changes. */
.cart-tab-count,
.cart-mini-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.3rem;
  height: 1.3rem;
  padding: 0 0.42rem;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--cyan), var(--azure-soft));
  color: var(--void);
  font-size: 0.78rem;
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 2px 10px rgba(91, 147, 255, 0.45);
  transition: transform 0.18s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.cart-tab-count.is-empty,
.cart-mini-count.is-empty { display: none; }
.cart-tab-count.is-bumped,
.cart-mini-count.is-bumped { animation: cartPop 0.42s cubic-bezier(0.2, 1.4, 0.4, 1); }
@keyframes cartPop {
  0% { transform: scale(1); }
  45% { transform: scale(1.4); }
  100% { transform: scale(1); }
}

/* Compact cart button pinned in the header bar on small screens, so the cart
   is always one tap away even while the menu is collapsed. Hidden on desktop,
   where the full cart button lives in the nav actions. */
.cart-mini {
  position: relative;
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.7rem;
  height: 2.7rem;
  flex: none;
  border-radius: 999px;
  border: 1px solid rgba(143, 182, 255, 0.4);
  background: rgba(91, 147, 255, 0.12);
  color: var(--star);
  text-decoration: none;
  transition: background 0.25s, box-shadow 0.25s;
}
.cart-mini:hover { background: rgba(91, 147, 255, 0.25); }
.cart-mini-count {
  position: absolute;
  top: -0.4rem;
  right: -0.4rem;
  min-width: 1.2rem;
  height: 1.2rem;
  padding: 0 0.34rem;
  font-size: 0.72rem;
  border: 1.5px solid var(--void);
}

/* ---------------------------------------------------------
   Tabbed navigation — sticky bar with primary tabs, two quick
   actions (Shop Now / Join the Community) and the live cart tab.
   The bar gains a touch more depth once the page is scrolled.
   --------------------------------------------------------- */
.nav.is-scrolled {
  background: linear-gradient(180deg, rgba(4, 6, 15, 0.96), rgba(4, 6, 15, 0.7));
  box-shadow: 0 10px 40px rgba(2, 4, 12, 0.55);
}

.nav-menu {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(1rem, 2.5vw, 2.2rem);
  flex: 1;
}

.nav-tabs {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 1.8vw, 1.7rem);
  flex-wrap: wrap;
}
.nav-tabs a {
  position: relative;
  text-decoration: none;
  color: var(--ink);
  font-size: 0.92rem;
  font-weight: 500;
  letter-spacing: 0.2px;
  opacity: 0.82;
  padding: 0.35rem 0.1rem;
  transition: opacity 0.2s, color 0.2s;
}
.nav-tabs a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--azure), var(--cyan));
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.nav-tabs a:hover { opacity: 1; color: var(--azure-soft); }
.nav-tabs a.is-active {
  opacity: 1;
  color: var(--star);
}
.nav-tabs a.is-active::after { transform: scaleX(1); }

.nav-actions {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

/* Quick-action buttons live in the bar alongside the tabs. */
.nav-cta {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  padding: 0.55rem 1.15rem;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  letter-spacing: 0.2px;
  transition: transform 0.2s, box-shadow 0.25s, background 0.25s, border-color 0.2s;
}
.nav-cta--shop {
  color: var(--void);
  background: linear-gradient(135deg, var(--cyan), var(--azure-soft));
  border: 1px solid rgba(121, 224, 255, 0.55);
  box-shadow: 0 6px 24px rgba(91, 147, 255, 0.3);
}
.nav-cta--shop:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 30px rgba(121, 224, 255, 0.45);
}
.nav-cta--join {
  color: var(--star);
  background: rgba(91, 147, 255, 0.12);
  border: 1px solid rgba(143, 182, 255, 0.4);
}
.nav-cta--join:hover {
  background: rgba(91, 147, 255, 0.25);
  box-shadow: 0 0 24px rgba(91, 147, 255, 0.35);
}

/* Hamburger — hidden on desktop, shown when the bar collapses. */
.nav-toggle {
  display: none;
  width: 44px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(13, 27, 62, 0.5);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  padding: 0 11px;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
}
.nav-toggle span {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: var(--star);
  transition: transform 0.25s, opacity 0.2s;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ---------------------------------------------------------
   Tab pages — only the active group is shown. Each switch replays
   a short, lightly staggered entrance so movement feels intentional.
   --------------------------------------------------------- */
.tab-hidden { display: none !important; }

@keyframes tabIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.tab-in {
  animation: tabIn 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: var(--tab-delay, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .tab-in { animation: none; }
}

/* ---------------------------------------------------------
   Buttons
   --------------------------------------------------------- */
.btn {
  display: inline-block;
  padding: 0.85rem 1.6rem;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  border: 1px solid transparent;
  transition: transform 0.2s, box-shadow 0.25s, background 0.25s;
}
.btn.primary {
  background: linear-gradient(135deg, var(--azure), var(--cyan));
  color: #05122e;
  box-shadow: 0 10px 30px rgba(91, 147, 255, 0.35);
}
.btn.primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 42px rgba(121, 224, 255, 0.45);
}
.btn.ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--ink);
}
.btn.ghost:hover {
  border-color: var(--azure-soft);
  color: var(--star);
  transform: translateY(-2px);
}

/* ---------------------------------------------------------
   Hero
   --------------------------------------------------------- */
.hero {
  width: min(100% - 3rem, var(--maxw));
  margin: 0 auto;
  padding: 5rem 0 4rem;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 3rem;
  align-items: center;
}
.hero-copy { animation: rise 0.9s ease both; }
.hero-copy .lead {
  font-size: 1.1rem;
  max-width: 46ch;
  color: var(--ink);
  opacity: 0.85;
}
.hero-actions {
  display: flex;
  gap: 1rem;
  margin-top: 2rem;
  flex-wrap: wrap;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Hero card holds the rotating garment inside an orbiting glow */
.hero-card {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 420px;
  animation: rise 1.1s ease 0.15s both;
}
.orbit {
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  /* a halo ring rather than a centre glow: the middle (behind the
     garment) stays dark so the piece floats, while the ring frames it */
  background:
    radial-gradient(circle at 50% 42%, transparent 40%, rgba(91, 147, 255, 0.22) 54%, transparent 74%);
  filter: blur(10px);
}
.orbit::before,
.orbit::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid rgba(143, 182, 255, 0.25);
  animation: spin 18s linear infinite;
}
.orbit::after {
  inset: 14%;
  border-style: dashed;
  border-color: rgba(121, 224, 255, 0.20);
  animation-duration: 26s;
  animation-direction: reverse;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------
   Rotating garment — full 360° "4D" spin, front to back

   The stage gives perspective; the garment is a 3D card with a
   front face and a back face. Each face holds one or more pieces
   (a top, and on sets a pair of joggers). The LifeMath= mark sits
   on the chest of the front, the tree on the spine of the back,
   and on sets the tree also prints on the top-left of the joggers.
   --------------------------------------------------------- */
.garment-stage {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 380px;
  perspective: 1300px;
  perspective-origin: 50% 42%;
}
/* a soft pool of deeper void seated directly behind the garment. The
   black pieces carry a near-black studio backdrop; this keeps the area
   right behind them dark so that backdrop blends into space instead of
   being lit up by the galaxy core — the piece reads as floating. */
.garment-stage::before {
  content: "";
  position: absolute;
  z-index: 0;
  width: 78%;
  height: 84%;
  top: 48%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    ellipse 50% 54% at 50% 50%,
    rgba(2, 3, 9, 0.96) 0%,
    rgba(2, 3, 9, 0.72) 48%,
    rgba(2, 3, 9, 0) 78%
  );
  filter: blur(12px);
  pointer-events: none;
}
.garment {
  position: relative;
  z-index: 2;
  width: 300px;
  height: 360px;
  /* the customizer drives these two: the fabric color and the print color.
     The default fabric is pure black, which leaves the real photo untouched
     (a screen blend with black is a no-op), and the default print is white. */
  --cloth-color: #000000;
  --logo-color: #ffffff;
  transform-style: preserve-3d;
  /* float uses the independent `translate` property so it composes
     with the JS-driven rotation set on `transform` */
  animation: float 7s ease-in-out infinite;
  cursor: grab;
  /* Let the browser keep handling vertical scrolling so a finger that lands on
     a garment while swiping down the page still scrolls the page. Only the
     horizontal axis is reserved for the drag-to-spin gesture, which is all the
     spinner needs. Using `none` here suppressed *all* touch gestures, which
     locked up scrolling on mobile whenever a swipe started on a garment. */
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}
.garment.grabbing { cursor: grabbing; }
.garment.is-set { width: 460px; }
/* A full-body set (the hoodie set and the sweatshirt set) is one tall combined
   cut-out — the top worn over the matching joggers — so the stage is sized to
   that portrait silhouette and the single image fills it edge to edge with no
   letterboxing. */
.garment.is-fullbody { width: 200px; height: 460px; }

@keyframes float {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 -10px; }
}

/* a quiet prompt that the piece is interactive; it fades once touched */
.spin-hint {
  position: absolute;
  bottom: 2%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.7rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--azure-soft);
  background: rgba(13, 27, 62, 0.55);
  border: 1px solid var(--line);
  padding: 0.4rem 0.85rem;
  border-radius: 999px;
  backdrop-filter: blur(6px);
  pointer-events: none;
  z-index: 3;
  animation: hint-pulse 2.6s ease-in-out infinite;
}
.garment-stage.touched .spin-hint {
  opacity: 0;
  animation: none;
  transition: opacity 0.5s ease;
}
@keyframes hint-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 0.95; }
}

.g-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  backface-visibility: hidden;
  filter: drop-shadow(0 26px 34px rgba(0, 0, 0, 0.55));
}
.g-face.back { transform: rotateY(180deg); }

/* `isolation` keeps the fabric-tint screen blend contained to the garment
   photo within this piece, so it never lightens the starfield behind it. */
.piece { position: relative; height: 100%; width: 100%; isolation: isolate; }
.is-set .piece.top { width: 60%; }
.piece.pants { display: none; width: 38%; }
.is-set .piece.pants { display: block; }
/* the full-body set is a single image, so its one top piece fills the face */
.is-fullbody .piece.top { width: 100%; }
.is-fullbody .piece.pants { display: none; }

/* the real product photo fills its piece, shown full and solid so
   the whole garment reads as it spins. Its rectangular frame is
   feathered to nothing with a soft elliptical mask, so the piece has
   no hard photo edge — only the garment remains, floating free. The
   near-black studio backdrop that the black pieces carry is the same
   value as the deep-space void, so once the frame is gone and the
   stage directly behind stays dark (see .garment-stage::before), the
   backdrop is invisible and the clothing appears to hover in space. */
.g-svg {
  position: absolute;
  inset: 0;
}
.g-svg img.garment-photo,
.garment-photo {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  -webkit-user-drag: none;
  user-select: none;
  -webkit-mask-image: radial-gradient(ellipse at 50% 50%, #000 82%, transparent 100%);
          mask-image: radial-gradient(ellipse at 50% 50%, #000 82%, transparent 100%);
}

/* The tee, sweatshirt, and hoodie photos are cut out with a genuine
   transparent background, so the garment already ends exactly at its own
   silhouette — no studio backdrop to feather away. Skipping the elliptical
   mask lets the full garment (shoulders, sleeves, hood, hem) show edge to edge
   while it floats and spins, with no photo frame and no soft-clipped corners. */
.g-svg img.garment-photo.alpha-cut,
.garment-photo.alpha-cut {
  -webkit-mask-image: none;
          mask-image: none;
}

/* ---------------------------------------------------------
   Fabric color (the customizer)

   Each garment photo is a black studio shot. To recolor it without
   losing the real folds, seams, and lighting, a flat sheet of the
   chosen color is laid over the photo, clipped to the garment's own
   silhouette (the photo doubles as the mask), and blended with
   `screen`. Screen leaves the deep shadows dark and lifts the chosen
   hue into the lit areas, so the cloth reads as that color while
   keeping every crease. With the default black, screen changes
   nothing and the original photo shows through untouched.
   --------------------------------------------------------- */
.garment-tint {
  position: absolute;
  inset: 0;
  background: var(--cloth-color, #000000);
  -webkit-mask-image: var(--tint-src);
          mask-image: var(--tint-src);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  mix-blend-mode: screen;
  pointer-events: none;
  transition: background 0.25s ease;
}

/* Prints overlaid on each piece. Each print is an empty box filled with
   the chosen logo color and clipped to the artwork's shape with a CSS
   mask, so the customizer can recolor the mark and the tree to any color
   just by changing --logo-color (default white). Sizes and positions
   mirror the real embroidered placement on the garments. */
.print {
  position: absolute;
  pointer-events: none;
  background: var(--logo-color, #ffffff);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
  /* The wrap-* variables are driven each frame as the garment spins (see
     script.js), so the artwork rides around the body's curve. --center-x keeps
     whatever centering the print needs (chest/spine/hip are anchored at left:50%
     and pulled back by half their width). */
  --wrap-x: 0px;
  --wrap-y: 0px;
  --wrap-skew: 0deg;
  --center-x: 0px;
  transform: translate(var(--center-x), 0) translate(var(--wrap-x), var(--wrap-y)) skewX(var(--wrap-skew));
  transform-origin: center center;
  transition: background 0.25s ease;
}
/* the LifeMath= mark prints on the chest (and the front-corner variant) */
.print.chest,
.print.corner {
  -webkit-mask-image: url(assets/lifemath-mark.png);
          mask-image: url(assets/lifemath-mark.png);
  aspect-ratio: 790 / 725;
}
/* the rooted tree prints on the spine and on the jogger hip. The tree carries
   its own color (--tree-color) so it can be recolored independently of the
   LifeMath= mark; it falls back to --logo-color when no separate tree color is
   set, preserving the old single-color behavior. */
.print.spine,
.print.hip {
  -webkit-mask-image: url(assets/tree-mark.png);
          mask-image: url(assets/tree-mark.png);
  aspect-ratio: 1150 / 1716;
  background: var(--tree-color, var(--logo-color, #ffffff));
}
.print.chest  { top: 23%; left: 50%; width: 25%; --center-x: -50%; }
/* The rooted tree rides high on the spine, its top leaves reaching just below the
   back collar (the blue placement mark), matching the approved sample. */
.print.spine  { top: 10%; left: 50%; width: 32%; --center-x: -50%; }
.print.corner { top: 17%; left: 18%; width: 30%; }
/* the jogger-hip tree only belongs on the full-body set */
.print.hip    { display: none; }

/* On the full-body hoodie set the same prints land on the taller outfit: the
   LifeMath= mark on the hoodie chest, the tree on the upper back, and a smaller
   tree on the top-left of the joggers. */
.is-fullbody .print.chest { top: 19%; width: 33%; }
.is-fullbody .print.spine { top: 14%; width: 36%; }
.is-fullbody .print.hip {
  display: block;
  top: 52%; left: 33%; width: 13%;
  --center-x: -50%;
}

/* On the two-piece sets the top is worn over joggers, so it fills only the
   upper portion of the tall combined photo. The shared full-body placement
   above drops the prints too far down the body, leaving them looking low on the
   outfit. Raise the LifeMath= chest mark and the rooted-tree back print on each
   set so they sit on the upper chest and upper back of the top — matching where
   they print on the single hoodie and sweatshirt. The crewneck of the
   sweatshirt set rides higher and more compact than the hoodie, so it is lifted
   the most. */
.garment[data-style="hoodieset"] .print.chest { top: 12%; }
.garment[data-style="hoodieset"] .print.spine { top: 7%; }
.garment[data-style="sweatset"]  .print.chest { top: 8%; }
.garment[data-style="sweatset"]  .print.spine { top: 3%; }

/* On the single hoodie in the "Design your piece" builder the back tree sat at
   the shared spine height (10% above), where it tucked up under the hood. Drop
   it lower on the back so it clears the hood and sits well down the upper back,
   reading cleanly on the garment. This carries the same style tag through to the
   cart and order thumbnails, so the piece a subscriber designs and the piece they
   buy match (see previewGeometry's matching hoodie spine in script.js).
   The tee, sweatshirt, and the sets are untouched (each has its own data-style). */
.garment[data-style="hoodie"] .print.spine { top: 24%; }

/* The sweatshirt set's chest mark above is tuned for the "Design your piece"
   builder, where it was nudged up a little on the chest. The showcase card photo
   sits a touch differently, so pin its sweatshirt set mark to its prior spot so
   only the builder preview moved. */
.showcase-card .garment[data-style="sweatset"] .print.chest { top: 10%; }

/* In the "Shop the collection" showcase cards the hoodie set's LifeMath= mark
   had been raised so far it read too high on the chest, so it is lowered to sit
   on the centre of the chest matching where it lands on the hoodie. */
.showcase-card .garment[data-style="hoodieset"] .print.chest { top: 12%; }

/* In the "Shop the collection" showcase the single hoodie's back tree sat high
   on the spine (the shared 10% above), where the top leaves tucked up under the
   hood. The single hoodie card has no data-style, so target it by its back
   mount — this keeps the tee, sweatshirt, and the sets untouched. Drop the tree
   a little so it clears the hood and reads cleanly on the upper back. */
.showcase-card .g-svg[data-mount^="hoodie:back"] ~ .print.spine { top: 17%; }

/* The joggers carry their own rooted-tree print, so it has to sit the same way
   on every piece of every set too — not just the top. The shared full-body hip
   position above is tuned for the hoodie set, where the joggers start lower in
   the photo. The sweatshirt set's crewneck is shorter, so its joggers ride
   higher and the same value drops the tree onto the mid-thigh. Lift the
   sweatshirt set's jogger tree so it lands just below the waistband on the upper
   hip, matching where it sits on the hoodie set. */
.garment[data-style="hoodieset"] .print.hip { top: 52%; }
.garment[data-style="sweatset"]  .print.hip { top: 49%; }

/* The joggers can carry the LifeMath= mark instead of the rooted tree (chosen
   on its own via the "Pants logo" selector). The mark is wider and shorter than
   the tree, so it swaps in its own mask and aspect ratio and is widened a touch
   to read at the same scale on the hip. */
.garment.pants-lifemath .print.hip {
  -webkit-mask-image: url(assets/lifemath-mark.png);
          mask-image: url(assets/lifemath-mark.png);
  aspect-ratio: 790 / 725;
  width: 17%;
  /* It's the LifeMath= mark on the joggers now, not the tree, so it follows the
     logo color rather than the separate tree color. */
  background: var(--logo-color, #ffffff);
}

/* ---------------------------------------------------------
   Marquee
   --------------------------------------------------------- */
.marquee {
  display: flex;
  gap: 3rem;
  padding: 1.1rem 0;
  margin: 1rem 0 3rem;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: rgba(13, 27, 62, 0.35);
  white-space: nowrap;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee span {
  font-family: var(--display);
  font-size: 1.3rem;
  letter-spacing: 0.5px;
  color: var(--azure-soft);
  animation: slide 22s linear infinite;
}
.marquee span:nth-child(even) { color: var(--muted); }
@keyframes slide {
  from { transform: translateX(0); }
  to   { transform: translateX(-120%); }
}

/* ---------------------------------------------------------
   Split / collection
   --------------------------------------------------------- */
.split { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 3rem; align-items: start; }
.products {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.4rem;
}
.products article {
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
  backdrop-filter: blur(8px);
}
.products article:hover {
  transform: translateY(-6px);
  border-color: rgba(143, 182, 255, 0.4);
  box-shadow: 0 20px 50px rgba(7, 16, 38, 0.6);
}
.products img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: contain;
  border-radius: 14px;
  background: linear-gradient(180deg, #0e1d44, #060c1d);
  margin-bottom: 0.9rem;
}
.products h3 { margin-bottom: 0.3rem; }
.products p { font-size: 0.9rem; margin: 0; }

/* ---------------------------------------------------------
   Shop showcase — every piece we sell, each spinning in 3D
   like the hero, on its own deep-space stage.
   --------------------------------------------------------- */
.showcase-head { max-width: 70ch; margin-bottom: 2.5rem; }
.showcase-note { font-size: 1rem; color: var(--ink); opacity: 0.85; }
.showcase-note strong { color: var(--azure-soft); font-weight: 600; }

.showcase-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem;
}
.showcase-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.8rem 1rem 1.4rem;
  transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
  backdrop-filter: blur(8px);
}
.showcase-card:hover {
  transform: translateY(-6px);
  border-color: rgba(143, 182, 255, 0.4);
  box-shadow: 0 20px 50px rgba(7, 16, 38, 0.6);
}
/* each card gets its own compact spin stage */
.showcase-card .garment-stage { min-height: 300px; }
.showcase-card .garment { width: 200px; height: 240px; }
.showcase-card .garment.is-set { width: 270px; }
.showcase-card .garment.is-fullbody { width: 130px; height: 300px; }
.showcase-card h3 { margin-bottom: 0.3rem; }
.showcase-card p { font-size: 0.9rem; margin: 0; opacity: 0.85; }
.showcase-card .from-price {
  margin-top: 0.6rem;
  font-family: var(--display);
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--cyan);
  opacity: 1;
}

/* ---------------------------------------------------------
   Specials — owner-curated items for sale, populated from the
   admin. A photo-led card grid; each card carries a logo
   chooser, a live price, and an add-to-cart button.
   --------------------------------------------------------- */
.specials-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.5rem;
}
/* Shown in place of the cards when no special is live, so the always-present
   Specials tab never opens onto a blank grid. */
.specials-empty {
  grid-column: 1 / -1;
  margin: 0;
  padding: 2.4rem 1.6rem;
  text-align: center;
  color: var(--muted, rgba(231, 238, 255, 0.7));
  font-size: 1.02rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
}
.special-card {
  display: flex;
  flex-direction: column;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  backdrop-filter: blur(8px);
  transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
}
.special-card:hover {
  transform: translateY(-6px);
  border-color: rgba(143, 182, 255, 0.4);
  box-shadow: 0 20px 50px rgba(7, 16, 38, 0.6);
}
.special-card-photo {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  background: #070d1f;
  overflow: hidden;
}
/* When the special carries photos it renders as a spinning 3D garment (front
   shot on the front face, back shot on the back), so the frame drops the fixed
   4:3 crop and gives the piece a tall stage to turn in — matching the showcase
   cards. The deep-space background lets the feathered garment float free. */
.special-card-photo--stage {
  aspect-ratio: auto;
  background:
    radial-gradient(ellipse 70% 60% at 50% 42%, rgba(20, 32, 68, 0.55), transparent 72%),
    #070d1f;
}
.special-card-photo .special-stage {
  width: 100%;
  min-height: 320px;
}
.special-card-photo .special-garment {
  width: 200px;
  height: 250px;
}
/* A designed two-piece set is drawn full-body, so it needs the same tall,
   narrow proportions the showcase set cards use. */
.special-card-photo .special-garment.is-fullbody {
  width: 130px;
  height: 300px;
}
/* The full product photo, shown complete (never cropped) and centered. */
.special-card-photo__img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 14px 30px rgba(0, 0, 0, 0.45));
}
/* A blurred, dimmed copy of the same photo fills the frame behind it, so the
   image never floats in empty space — every card gets a clean, finished look. */
.special-card-photo__backdrop {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.18);
  filter: blur(26px) brightness(0.5) saturate(1.15);
  display: block;
}
.special-card-photo.is-empty {
  background:
    repeating-linear-gradient(45deg, rgba(143,182,255,0.06) 0 12px, transparent 12px 24px),
    var(--deep);
}
/* When a special is added to the cart, the spinning garment gives a quick
   "your order's on its way" nod — it lifts a touch toward the deep-space
   backdrop, eases back, and settles right where the shopper left it, ready to
   grab another. The piece stays fully visible the whole time: the motion is a
   gentle rise-and-return rather than a vanish, so it reads as deliberate polish
   instead of the product glitching out. The launch rides the stage wrapper, so
   the garment keeps spinning throughout. */
.special-card-photo.is-launching .special-stage {
  animation: special-launch 0.85s cubic-bezier(0.34, 1.2, 0.4, 1) both;
  will-change: transform;
}
@keyframes special-launch {
  0%   { transform: translateY(0) scale(1); }
  38%  { transform: translateY(-12%) scale(0.9); }
  70%  { transform: translateY(3%) scale(1.02); }
  100% { transform: translateY(0) scale(1); }
}
/* Shoppers who prefer reduced motion still get the instant add, no movement. */
@media (prefers-reduced-motion: reduce) {
  .special-card-photo.is-launching .special-stage { animation: none; }
}
/* Thumbnail strip for flipping between a special's photos (front, back, …). */
.special-thumbs {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  gap: 0.4rem;
  padding: 0.5rem;
  background: linear-gradient(to top, rgba(7, 13, 31, 0.85), transparent);
}
.special-thumb-btn {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  background: #070d1f;
  opacity: 0.7;
  transition: opacity 0.2s, border-color 0.2s;
}
.special-thumb-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }
.special-thumb-btn:hover { opacity: 1; }
.special-thumb-btn.is-active { opacity: 1; border-color: var(--cyan); }
.special-card-body {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1.1rem 1.2rem 1.3rem;
  flex: 1;
}
.special-card-body h3 { margin: 0; font-size: 1.2rem; }
.special-desc { margin: 0; font-size: 0.9rem; color: var(--ink); opacity: 0.85; }
.special-options {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.2rem;
}
.special-option {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.9rem;
  cursor: pointer;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  transition: border-color 0.2s, background 0.2s;
}
.special-option:hover { border-color: rgba(143, 182, 255, 0.4); }
.special-option input { accent-color: var(--azure); }
.special-single-logo {
  margin: 0;
  font-size: 0.82rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.special-price {
  margin: 0.2rem 0 0;
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--cyan);
}
/* Color choosers on each special card (logo color / shirt color). */
.special-colors {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-top: 0.2rem;
}
.special-colors-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.special-color-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.special-color-swatch {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  background: var(--sw);
  border: 2px solid var(--line);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
  transition: border-color 0.2s, transform 0.1s;
}
.special-color-swatch:hover { transform: scale(1.08); }
.special-color-swatch.is-active {
  border-color: var(--cyan);
  box-shadow: 0 0 0 2px rgba(121, 224, 255, 0.4), inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
/* Size chooser on each special card. */
.special-sizes {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-top: 0.2rem;
}
.special-sizes-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.special-size-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.special-size-pill {
  min-width: 2.4rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s, color 0.2s;
}
.special-size-pill:hover { border-color: rgba(143, 182, 255, 0.4); }
.special-size-pill.is-active {
  border-color: var(--cyan);
  background: rgba(121, 224, 255, 0.14);
  color: var(--cyan);
}
/* A sold-out size on a stocked drop: grayed, struck through, and unclickable. */
.special-size-pill.is-soldout {
  opacity: 0.4;
  cursor: not-allowed;
  text-decoration: line-through;
  border-style: dashed;
}
.special-size-pill.is-soldout:hover { border-color: var(--line); }
.special-add { margin-top: auto; }
.special-add.is-added { background: var(--cyan); border-color: var(--cyan); }
.special-add:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ---- Promo drops ---------------------------------------------------------
   A special the owner runs as a limited release: a badge, a "how many left"
   line, and (for members-only drops) an email unlock gate over the buy
   controls. The controls themselves keep their own flow inside .special-controls. */
.special-controls {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  flex: 1;
}
.special-promo-badge {
  align-self: flex-start;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.28rem 0.7rem;
  border-radius: 999px;
  color: #0b1020;
  background: linear-gradient(135deg, var(--cyan, #79e0ff), #b69cff);
}
.special-promo-left {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--cyan, #79e0ff);
}
.special-promo-gate {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.9rem 1rem;
  border: 1px dashed rgba(255, 255, 255, 0.25);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.04);
}
.special-promo-note { margin: 0; font-size: 0.85rem; color: var(--ink); opacity: 0.9; }
.special-promo-form { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.special-promo-email {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.7rem;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(0, 0, 0, 0.25);
  color: var(--ink);
  font: inherit;
}
.special-promo-unlock { flex: 0 0 auto; }
.special-promo-msg { margin: 0; font-size: 0.82rem; color: var(--ink); opacity: 0.95; }
.special-promo-msg a { color: var(--cyan, #79e0ff); font-weight: 700; }
.cart-thumb--photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 10px;
  display: block;
}

/* ---------------------------------------------------------
   Customizer — fabric + print color swatches

   A compact two-row palette under each garment. Each swatch is a small
   color dot; the active one is ringed. Built by script.js, one panel per
   garment, so every piece can be specced independently.
   --------------------------------------------------------- */
.customizer {
  width: 100%;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.swatch-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}
.swatch-label {
  flex: 0 0 auto;
  width: 3.2rem;
  text-align: left;
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.swatch {
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  border-radius: 50%;
  background: var(--sw);
  border: 1px solid rgba(143, 182, 255, 0.45);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  position: relative;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
}
.swatch:hover { transform: scale(1.18); }
.swatch:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}
.swatch.is-selected {
  transform: scale(1.12);
  box-shadow: 0 0 0 2px var(--void), 0 0 0 4px var(--cyan);
}

/* Premium "More colors" group — each adds $10. A small label introduces it
   and the dots wear a faint gold ring so they read as the upgrade option. */
.swatch-sublabel {
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-top: 0.15rem;
}
.swatches--premium { margin-top: 0.1rem; }
.swatch--premium {
  border-color: rgba(212, 175, 55, 0.7);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25), 0 0 0 1px rgba(212, 175, 55, 0.5);
}
.swatch--premium.is-selected {
  box-shadow: 0 0 0 2px var(--void), 0 0 0 4px var(--cyan);
}

/* The "Customize & Buy" link on each shop card */
.card-customize {
  margin-top: 0.95rem;
  padding: 0.55rem 1.2rem;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
}

/* ---------------------------------------------------------
   Builder — the single "Design your piece" configurator

   Left: a large preview garment that spins like the rest and recolors
   live. Right: the controls — style, print, fabric color, logo color —
   a running price, and the order action. Below, the order form.
   --------------------------------------------------------- */
.builder-head { max-width: 64ch; margin-bottom: 2.5rem; }
.builder-note { font-size: 1rem; color: var(--ink); opacity: 0.85; }
.builder-note a { color: var(--cyan); text-decoration: none; border-bottom: 1px solid rgba(121, 224, 255, 0.4); }
.builder-note a:hover { color: var(--star); }

.builder-grid {
  display: grid;
  grid-template-columns: 0.95fr 1.05fr;
  gap: 3rem;
  align-items: center;
}
.builder-preview {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  background:
    radial-gradient(70% 90% at 50% 40%, rgba(91, 147, 255, 0.10), transparent 70%),
    var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  backdrop-filter: blur(8px);
  padding: 1rem;
}

.builder-panel {
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}
.builder-field { display: flex; flex-direction: column; gap: 0.7rem; }
/* The joggers-size field is toggled via the `hidden` attribute; without this the
   class's display:flex would override it and the field would always show. So it
   only appears once a two-piece set is selected. */
.builder-field[hidden] { display: none; }
.field-label {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azure-soft);
}

.option-row { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.option-btn {
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(4, 6, 15, 0.4);
  color: var(--ink);
  font: 600 0.88rem var(--body);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: background 0.2s, border-color 0.2s, color 0.2s, box-shadow 0.2s, transform 0.15s;
}
.option-btn:hover { transform: translateY(-1px); border-color: var(--azure-soft); color: var(--star); }
.option-btn.is-active {
  background: linear-gradient(135deg, rgba(91, 147, 255, 0.28), rgba(121, 224, 255, 0.22));
  border-color: var(--cyan);
  color: var(--star);
  box-shadow: 0 0 18px rgba(91, 147, 255, 0.3);
}

/* Design-your-piece Print buttons: blow the brand glyphs up well past their
   inline-label size so a shopper can clearly see which mark they're picking and
   has a roomy target to tap. Centered with flex so the bigger art stays aligned. */
#printOptions .option-btn {
  display: inline-flex;
  align-items: center;
  padding: 0.7rem 1.3rem;
}
#printOptions .lm-glyph { height: 34px; vertical-align: middle; }
#printOptions .tree-glyph { height: 42px; vertical-align: middle; }

.builder-summary {
  border-top: 1px solid var(--line);
  padding-top: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.summary-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
.summary-title { font-size: 1rem; color: var(--ink); }
.builder-price {
  font-family: var(--display);
  font-size: 2rem;
  font-weight: 600;
  color: var(--cyan);
}
.builder-summary .btn { align-self: flex-start; }
.builder-cart-hint {
  font-size: 0.8rem;
  color: var(--ink);
  opacity: 0.7;
  margin: 0;
}
#addToCartBtn.is-added {
  background: linear-gradient(135deg, #2faa55, #79e0ff);
}

/* ---------------------------------------------------------
   Cart — the configured pieces collected before checkout
   --------------------------------------------------------- */
.cart {
  margin-top: 2.4rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem 1.8rem;
  backdrop-filter: blur(8px);
}
.cart-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}
.cart-title { font-family: var(--display); font-size: 1.5rem; color: var(--star); margin: 0; }
.cart-count {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.cart-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.cart-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem 1.2rem;
  padding: 1rem 0;
  border-top: 1px solid var(--line);
}
.cart-item:first-child { border-top: none; }
.cart-item-info { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; flex: 1 1 16rem; }
.cart-item-title { font-weight: 600; color: var(--star); }
.cart-item-detail { font-size: 0.82rem; color: var(--azure-soft); }
.cart-item-controls { display: flex; align-items: center; gap: 1rem; }
.qty-stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.qty-btn {
  width: 2rem;
  height: 2rem;
  border: none;
  background: rgba(4, 6, 15, 0.4);
  color: var(--ink);
  font: 700 1rem var(--body);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: background 0.2s, color 0.2s;
}
.qty-btn:hover { background: rgba(91, 147, 255, 0.28); color: var(--star); }
.qty-val { min-width: 2rem; text-align: center; font-weight: 600; color: var(--ink); }
.cart-item-price {
  min-width: 4rem;
  text-align: right;
  font-weight: 600;
  color: var(--cyan);
}
.cart-remove {
  border: none;
  background: none;
  color: var(--azure-soft);
  font: 500 0.8rem var(--body);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  text-decoration: underline;
  opacity: 0.8;
  transition: color 0.2s, opacity 0.2s;
}
.cart-remove:hover { color: #ff6f7a; opacity: 1; }
.cart-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--line);
  padding-top: 1.2rem;
  margin-top: 0.4rem;
}
.cart-total-label {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.cart-total { font-family: var(--display); font-size: 1.8rem; font-weight: 600; color: var(--cyan); }
.cart #checkoutBtn { margin-top: 1.4rem; }

/* Promo code — applied to the whole order before checkout */
.cart-promo {
  margin-top: 1.4rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.cart-promo-label {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azure-soft);
  margin-bottom: 0.6rem;
}
.cart-promo-row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.cart-promo-input {
  flex: 1 1 10rem;
  min-width: 0;
  padding: 0.7rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(4, 6, 15, 0.4);
  color: var(--ink);
  font: 500 0.92rem var(--body);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.cart-promo-input::placeholder { color: var(--muted); letter-spacing: normal; text-transform: none; }
.cart-promo-input:focus { outline: none; border-color: var(--azure-soft); }
.cart-promo-input:disabled { opacity: 0.7; }
.cart-promo-btn { flex: 0 0 auto; }
.cart-promo-btn.is-applied { color: #ff6f7a; }
.cart-promo-hint { margin: 0.55rem 0 0; font-size: 0.74rem; color: var(--muted); }
.cart-promo-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.85rem;
}
.cart-promo-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.4rem 0.4rem 0.85rem;
  border: 1px solid var(--azure-soft);
  border-radius: 999px;
  background: rgba(4, 6, 15, 0.4);
  font-size: 0.82rem;
}
.cart-promo-chip-code {
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cyan);
}
.cart-promo-chip-label { color: var(--azure-soft); }
.cart-promo-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(255, 111, 122, 0.12);
  color: #ff6f7a;
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease;
}
.cart-promo-chip-remove:hover { background: rgba(255, 111, 122, 0.25); }
.cart-promo-msg { margin: 0.7rem 0 0; font-size: 0.82rem; color: var(--azure-soft); }
.cart-promo-msg.is-error { color: #ff6f7a; }
.cart-promo-msg.is-ok { color: var(--cyan); }

.cart-discount-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.2rem;
}
.cart-discount-label {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.cart-discount-amount { font-family: var(--display); font-size: 1.2rem; font-weight: 600; color: var(--cyan); }

/* ---------------------------------------------------------
   Cart line preview — a small, static, front-facing render of the exact
   configured piece, recolored to the chosen fabric/logo, plus swatch chips
   that name every color picked. Reuses the garment photo + tint + prints from
   the builder (see garments.js), but scaled down and frozen (no spin/float).
   --------------------------------------------------------- */
.cart-thumb {
  flex: none;
  width: 62px;
  height: 80px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: radial-gradient(
    ellipse 62% 60% at 50% 46%,
    rgba(2, 3, 9, 0.92) 0%,
    rgba(2, 3, 9, 0.5) 60%,
    rgba(2, 3, 9, 0) 100%
  );
  overflow: hidden;
}
.cart-thumb .garment,
.cart-thumb .garment.is-fullbody {
  position: relative;
  width: 100%;
  height: 100%;
  animation: none;
  cursor: default;
  transform: none;
  translate: none;
}
.cart-thumb .g-face {
  filter: drop-shadow(0 5px 9px rgba(0, 0, 0, 0.55));
}
.cart-thumb .spin-hint { display: none; }
/* the chest mark's soft shadow is overkill at thumbnail scale */
.cart-thumb .print { filter: none; }

/* Color chips: a swatch dot + the color's name, so the shopper can confirm
   every color choice for the piece at a glance. */
.cart-item-colors {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.8rem;
  margin: 0.1rem 0 0.15rem;
}
.color-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.74rem;
  color: var(--azure-soft);
}
.color-dot {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  flex: none;
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.45);
}
.color-chip--letters { gap: 0.3rem; }
.color-chip-label { color: var(--azure-soft); }
.letter-chip {
  font: 800 0.78rem/1 var(--body);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
}

/* Hide the back tree when the shopper picks the front-mark-only option. The
   jogger print is chosen independently (see the "Pants logo" selector), so it
   stays put regardless of the top's print. */
.garment.print-front-only .print.spine { display: none; }
/* The mirror case, used by a special's "Tree logo" option: show the rooted tree
   on the back only and hide the LifeMath= chest mark, so the live preview shows
   exactly the marks the shopper picked. */
.garment.print-back-only .print.chest { display: none; }

/* Per-letter logo coloring — an opt-in $10 upgrade. The toggle sits left,
   followed by a short note and, when active, a per-letter dropdown that only
   lists the colors we stock, so every letter is a color that's in stock. */
#letterField #letterToggle { align-self: flex-start; }
.letter-note {
  font-size: 0.8rem;
  color: var(--ink);
  opacity: 0.7;
  margin: 0;
}
.letter-note strong { color: var(--star); opacity: 0.95; }
.letter-pickers {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 0.2rem;
}
.letter-pick {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
}
.letter-pick-glyph {
  font: 700 0.9rem var(--body);
  color: var(--azure-soft);
  line-height: 1;
}
/* The dropdown wears a dot of the chosen in-stock color (--lc) on its left,
   so the row of letters reads at a glance while still naming each color. */
.letter-pick-select {
  --lc: #ffffff;
  appearance: none;
  -webkit-appearance: none;
  max-width: 7.5rem;
  padding: 0.4rem 0.55rem 0.4rem 1.6rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background-color: rgba(4, 6, 15, 0.55);
  background-image: radial-gradient(circle at 0.7rem center, var(--lc) 0 0.32rem, transparent 0.34rem);
  background-repeat: no-repeat;
  color: var(--ink);
  font: 600 0.8rem var(--body);
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
}
.letter-pick-select:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
}
.letter-pick-select option { color: #0b0e1a; background: #f4f6ff; }

/* When per-letter coloring is on, the chest mark KEEPS the real hand-lettered
   artwork (the lifemath-mark.png mask inherited from .print.chest) and simply
   fills it with a left-to-right per-letter color gradient (--logo-gradient, set
   in script.js). This recolors the genuine logo letter by letter while it still
   reads as the printed mark — never as typed text. */
.print.chest.is-multicolor {
  background: var(--logo-gradient, var(--logo-color, #ffffff));
}

/* Per-section tree coloring — an opt-in $5 upgrade. The toggle, note and (when
   active) dropdowns reuse the per-letter field's layout. Each dropdown only
   lists colors the shop stocks, so every section is a color that's in stock. */
#treeField #treeToggle { align-self: flex-start; }
.tree-note {
  font-size: 0.8rem;
  color: var(--ink);
  opacity: 0.7;
  margin: 0;
}
.tree-pickers {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
  margin-top: 0.2rem;
}

/* When tree-section coloring is on, the rooted-tree print drops its single
   --logo-color fill and instead stacks one masked layer per section. The body
   bands (branches/trunk/roots) wear the same tree mask clipped to their band;
   the stars and the centre circle override mask-image (set inline) with their
   own mask so they can be colored on their own and are stacked last so they
   paint on top. Together they rebuild the whole tree in the chosen colors. The
   shared drop-shadow sits on the holder so the silhouette reads as one piece
   rather than separate banded slabs. */
.print.spine.is-sectioned,
.print.hip.is-sectioned {
  background: transparent;
  -webkit-mask-image: none;
          mask-image: none;
  filter: none;
}
.tree-sections {
  position: absolute;
  inset: 0;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4));
}
.tree-section {
  position: absolute;
  inset: 0;
  -webkit-mask-image: url(assets/tree-mark.png);
          mask-image: url(assets/tree-mark.png);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  transition: background 0.25s ease;
}

/* ---------------------------------------------------------
   Order form — captures the cart contents (Netlify Forms)
   --------------------------------------------------------- */
.order-form-wrap {
  margin-top: 3rem;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2rem;
  backdrop-filter: blur(8px);
}
.order-form-title { margin-bottom: 0.4rem; }
.order-spec {
  font-size: 0.92rem;
  color: var(--azure-soft);
  margin: 0 0 1.4rem;
}
.order-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.1rem 1.4rem;
}
.order-grid + .order-grid { margin-top: 1.1rem; }
.order-section-label {
  margin: 1.8rem 0 0.9rem;
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cyan);
  font-weight: 600;
}
.order-spec + .order-section-label { margin-top: 0.4rem; }
.order-label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.order-label--wide { grid-column: 1 / -1; }
.size-note {
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: none;
  color: var(--azure-soft);
  opacity: 0.75;
}
.order-label input,
.order-label select,
.order-label textarea {
  background: rgba(4, 6, 15, 0.6);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  padding: 0.6rem 0.75rem;
  font: 400 0.95rem var(--body);
  text-transform: none;
  letter-spacing: normal;
}
.order-label input:focus,
.order-label select:focus,
.order-label textarea:focus {
  outline: none;
  border-color: var(--cyan);
  box-shadow: 0 0 0 2px rgba(121, 224, 255, 0.2);
}
.order-actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.6rem; }

.subscribe-check {
  display: flex;
  align-items: flex-start;
  gap: 0.8rem;
  margin-top: 1.3rem;
  padding: 1rem 1.1rem;
  border: 1px solid rgba(121, 224, 255, 0.24);
  border-radius: 12px;
  background: rgba(121, 224, 255, 0.07);
  color: var(--ink);
  font-size: 0.9rem;
  line-height: 1.45;
}
.subscribe-check input {
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.12rem;
  accent-color: var(--cyan);
  flex: none;
}
.subscribe-check strong {
  display: block;
  color: var(--star);
  margin-bottom: 0.1rem;
}

/* Payment — choose how to pay, with the amount due shown up front. */
.pay-box {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.1rem 1.2rem;
  background: rgba(4, 6, 15, 0.4);
}
.pay-total-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--azure-soft);
}
.pay-total { font: 700 1.5rem var(--display, var(--body)); color: var(--star); letter-spacing: normal; }
/* The live delivery-fee line sits above "Amount due"; it shares the row layout
   but reads as a normal value rather than the big star total. */
.pay-delivery-row { margin-bottom: 0.55rem; }
.pay-delivery { font-weight: 600; font-size: 0.95rem; color: var(--ink); letter-spacing: normal; text-transform: none; }
.pay-delivery-msg.is-error { color: #ffd2d2; opacity: 1; }
.pay-hint {
  margin: 0.6rem 0 0;
  font-size: 0.82rem;
  color: var(--azure-soft);
  opacity: 0.85;
}
.pay-methods {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.7rem;
  margin-top: 1rem;
}
.pay-method {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
}
.pay-method:hover { border-color: var(--cyan); }
.pay-method:has(input:checked) {
  border-color: var(--cyan);
  background: rgba(121, 224, 255, 0.08);
  box-shadow: 0 0 0 1px rgba(121, 224, 255, 0.25);
}
.pay-method input { accent-color: var(--cyan); width: 1.05rem; height: 1.05rem; flex-shrink: 0; }
.pay-method-body { display: flex; flex-direction: column; gap: 0.15rem; }
.pay-method-name { font-weight: 600; font-size: 0.95rem; color: var(--ink); }
.pay-method-sub { font-size: 0.76rem; color: var(--azure-soft); opacity: 0.8; }
/* A payment method the shop hasn't enabled yet (e.g. card before the processor
   is connected): visibly muted and not selectable, so no one lands on a dead
   option. It lights back up automatically once the server reports it live. */
.pay-method.is-unavailable {
  opacity: 0.5;
  cursor: not-allowed;
  border-style: dashed;
}
.pay-method.is-unavailable:hover { border-color: var(--line); }
.pay-hint--setup {
  margin-top: 0.9rem;
  padding: 0.7rem 0.85rem;
  border: 1px dashed rgba(121, 224, 255, 0.45);
  border-radius: 10px;
  background: rgba(121, 224, 255, 0.06);
  opacity: 1;
}
.order-error {
  margin: 0.9rem 0 0;
  padding: 0.7rem 0.85rem;
  border: 1px solid rgba(255, 138, 138, 0.5);
  border-radius: 10px;
  background: rgba(255, 80, 80, 0.08);
  color: #ffd2d2;
  font-size: 0.85rem;
}

.order-thanks {
  margin: 0;
  font-size: 1.05rem;
  color: var(--star);
  text-align: center;
  padding: 1rem 0;
}

/* ---------------------------------------------------------
   Membership — the "why join" comparison shown just above the
   subscribe form. Two cards, side by side: what a member unlocks
   (checked with the rooted-tree mark) vs. what a guest misses.
   --------------------------------------------------------- */
.membership {
  display: grid;
  gap: 2.2rem;
}
.membership-head {
  text-align: center;
  max-width: 60ch;
  margin: 0 auto;
}
.membership-head h2 { margin: 0.5rem 0 0.9rem; }
.membership-intro {
  color: var(--ink);
  opacity: 0.85;
  margin: 0 auto;
  max-width: 56ch;
}
.membership-compare {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.4rem;
  align-items: stretch;
}
.member-card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem 1.6rem 1.8rem;
  background: rgba(4, 6, 15, 0.4);
  overflow: hidden;
}
/* The member card is the hero: brighter glow, accent border, a "best value"
   feel so the eye lands there first. */
.member-card--in {
  border-color: rgba(121, 224, 255, 0.45);
  background:
    linear-gradient(155deg, rgba(121, 224, 255, 0.12), rgba(143, 182, 255, 0.05)),
    rgba(4, 6, 15, 0.5);
  box-shadow: 0 18px 50px rgba(8, 30, 50, 0.45), inset 0 0 0 1px rgba(121, 224, 255, 0.08);
}
.member-card--out {
  background: rgba(4, 6, 15, 0.32);
  opacity: 0.96;
}
.member-card-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding-bottom: 1.1rem;
  margin-bottom: 1.1rem;
  border-bottom: 1px solid var(--line);
}
.member-card-head h3 {
  margin: 0;
  font-size: 1.18rem;
  color: var(--star);
}
.member-emblem {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 14px;
  flex: none;
  background: linear-gradient(150deg, rgba(121, 224, 255, 0.22), rgba(143, 182, 255, 0.1));
  border: 1px solid rgba(121, 224, 255, 0.35);
  color: var(--cyan);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1;
}
.member-emblem img { width: 28px; height: 28px; object-fit: contain; }
.member-emblem--muted {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--azure-soft);
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--line);
  opacity: 0.7;
}
.member-tag {
  display: inline-block;
  margin-top: 0.3rem;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 800;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
}
.member-tag--free { color: #062018; background: var(--cyan); }
.member-tag--miss { color: var(--azure-soft); background: rgba(255, 255, 255, 0.06); }
.perk-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}
.perk-list li {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--ink);
}
.perk-list--out li { opacity: 0.74; }
.perk-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  margin-top: 0.05rem;
}
.perk-mark--yes {
  background: rgba(121, 224, 255, 0.14);
  border: 1px solid rgba(121, 224, 255, 0.4);
  color: var(--cyan);
  font-size: 0.92rem;
  font-weight: 800;
  line-height: 1;
}
.perk-mark--no {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  color: #ff9aa6;
  font-size: 0.8rem;
  font-weight: 700;
}

/* ---------------------------------------------------------
   Subscriber access — a clear opt-in band in the shopping flow.
   --------------------------------------------------------- */
.subscribe-band {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
  gap: 3rem;
  align-items: center;
  border: 1px solid rgba(121, 224, 255, 0.18);
  border-radius: var(--radius);
  background:
    radial-gradient(900px 320px at 12% -40%, rgba(121, 224, 255, 0.12), transparent 70%),
    linear-gradient(120deg, rgba(121, 224, 255, 0.08), rgba(143, 182, 255, 0.04)),
    rgba(4, 6, 15, 0.26);
  box-shadow: 0 24px 70px rgba(6, 16, 32, 0.4);
  /* A contained, centered panel — overrides .section's `6rem 0`. The previous
     full-bleed padding hack was meant for a viewport-wide element, but this band
     is width-capped by .section, so it just shoved content into the middle with
     lopsided gaps on desktop. */
  padding: clamp(2rem, 4vw, 3.4rem);
}
.subscribe-copy h2 { margin-bottom: 0.8rem; }
.subscribe-copy > p {
  max-width: 54ch;
  color: var(--ink);
  opacity: 0.84;
}
.subscribe-points {
  list-style: none;
  margin: 1.3rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.7rem;
}
.subscribe-points li {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: 0.92rem;
  color: var(--ink);
}
.subscribe-point-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(121, 224, 255, 0.14);
  border: 1px solid rgba(121, 224, 255, 0.4);
  color: rgba(121, 224, 255, 0.95);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1;
}
.subscribe-form {
  display: grid;
  gap: 0.75rem;
  padding: 1.7rem 1.6rem;
  border: 1px solid rgba(121, 224, 255, 0.28);
  border-radius: var(--radius);
  background: var(--glass);
  backdrop-filter: blur(8px);
  box-shadow: 0 20px 55px rgba(6, 16, 32, 0.5);
}
.subscribe-form-title {
  margin: 0;
  font: 700 1.35rem var(--display, var(--body));
  color: var(--star);
}
.subscribe-form-sub {
  margin: -0.2rem 0 0.4rem;
  font-size: 0.86rem;
  color: var(--azure-soft);
  opacity: 0.85;
}
.subscribe-label {
  display: grid;
  gap: 0.45rem;
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--azure-soft);
  font-weight: 700;
}
.subscribe-label input {
  min-width: 0;
  padding: 0.82rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(4, 6, 15, 0.58);
  color: var(--ink);
  font: 500 0.95rem var(--body);
  letter-spacing: normal;
  text-transform: none;
}
.subscribe-label input:focus {
  outline: none;
  border-color: var(--cyan);
  box-shadow: 0 0 0 2px rgba(121, 224, 255, 0.18);
}
.subscribe-optional {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 500;
  font-size: 0.78rem;
  opacity: 0.72;
}
.subscribe-submit { margin-top: 0.3rem; width: 100%; }
.subscribe-fineprint {
  margin: 0;
  text-align: center;
  font-size: 0.74rem;
  color: var(--azure-soft);
  opacity: 0.7;
}
.subscribe-msg {
  margin: 0;
  color: var(--cyan);
  font-size: 0.85rem;
}
.subscribe-msg.is-error { color: #ff9aa6; }

@media (max-width: 900px) {
  .builder-grid { grid-template-columns: 1fr; }
  .subscribe-band { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .membership-compare { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .order-grid { grid-template-columns: 1fr; }
  .pay-methods { grid-template-columns: 1fr; }
}

/* On a PC the two-column builder put the spinning preview in a narrower column,
   so the configured piece read small next to the controls — much smaller than
   it feels in the single-column mobile layout where it fills the width. Scale
   the customizer's preview up on desktop so the piece reads large and
   presentable like it does on a phone. Scoped to .builder-preview so the
   "Shop the collection" showcase cards and cart thumbnails keep their sizes.
   Sizes step up in two tiers so the piece never overflows its glass box on the
   narrower end of the desktop range. Print placements are percentage-based, so
   they stay perfectly aligned as the piece grows. */
@media (min-width: 901px) {
  .builder-preview .garment-stage { min-height: 500px; }
  .builder-preview .garment { width: 390px; height: 468px; }
  .builder-preview .garment.is-set { width: 470px; }
  .builder-preview .garment.is-fullbody { width: 260px; height: 600px; }
}
@media (min-width: 1200px) {
  .builder-preview .garment-stage { min-height: 560px; }
  .builder-preview .garment { width: 430px; height: 516px; }
  .builder-preview .garment.is-set { width: 500px; }
  .builder-preview .garment.is-fullbody { width: 290px; height: 660px; }
}

/* ---------------------------------------------------------
   Pricing
   --------------------------------------------------------- */
.pricing-head { max-width: 60ch; margin-bottom: 2.5rem; }
.pricing-note { font-size: 1rem; color: var(--ink); opacity: 0.8; }
.pricing-note strong { color: var(--azure-soft); font-weight: 600; }

.price-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.4rem;
}
.price-card {
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem 1.8rem;
  backdrop-filter: blur(8px);
  transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
}
.price-card:hover {
  transform: translateY(-6px);
  border-color: rgba(143, 182, 255, 0.4);
  box-shadow: 0 20px 50px rgba(7, 16, 38, 0.6);
}
.price-card h3 {
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-family: var(--body);
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--star);
  padding-bottom: 0.9rem;
  margin-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}
.price-card ul { list-style: none; margin: 0; padding: 0; }
.price-card li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  padding: 0.55rem 0;
  font-size: 0.95rem;
  color: var(--ink);
}
.price-card li span:first-child {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.82rem;
  color: var(--muted);
  flex: 1;
}
.price-card .price {
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--cyan);
}
/* Render the brand glyphs at a legible size inside the price-list labels. */
.price-card li span:first-child .lm-glyph { height: 22px; vertical-align: middle; }
.price-card li span:first-child .tree-glyph { height: 30px; vertical-align: middle; }

/* ---------------------------------------------------------
   Customizer
   --------------------------------------------------------- */
.customizer { display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; align-items: center; }
.panel {
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2rem;
  backdrop-filter: blur(10px);
}
.panel label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0.9rem 0;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
}
.panel select,
.panel input[type="color"] {
  background: rgba(4, 6, 15, 0.6);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  padding: 0.5rem 0.7rem;
  font: inherit;
}
.panel input[type="color"] {
  width: 56px;
  height: 38px;
  padding: 3px;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
}
.panel .btn { margin-top: 1rem; }

.mockup-stage { display: grid; place-items: center; gap: 1rem; position: relative; }
/* a soft studio halo that frames the floating piece from the outside,
   leaving the centre behind it dark so the garment reads as hovering */
.mockup-stage::before {
  content: "";
  position: absolute;
  top: 40%;
  left: 50%;
  width: 86%;
  height: 80%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle at 50% 45%, transparent 42%, rgba(91, 147, 255, 0.16) 56%, transparent 76%);
  filter: blur(12px);
  pointer-events: none;
  z-index: 0;
}
.mockup-stage .garment-stage { position: relative; z-index: 1; }
.mockup-stage .hint, .hint { font-size: 0.85rem; color: var(--muted); }

/* ---------------------------------------------------------
   Story
   --------------------------------------------------------- */
.story {
  width: min(100% - 3rem, var(--maxw));
  margin: 0 auto;
  padding: 6rem 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
  align-items: center;
}
.story img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.45);
  background: radial-gradient(circle at 50% 40%, var(--galaxy-2), var(--void));
}

/* ---------------------------------------------------------
   CTA
   --------------------------------------------------------- */
.cta {
  text-align: center;
  display: grid;
  place-items: center;
  gap: 0.6rem;
  padding: 6rem 2rem;
  border-radius: 28px;
  background:
    radial-gradient(70% 120% at 50% 0%, rgba(91, 147, 255, 0.18), transparent 70%),
    var(--glass);
  border: 1px solid var(--line);
  margin-bottom: 5rem;
}
.cta p { max-width: 52ch; }
.cta .btn { margin-top: 1.2rem; }

/* ---------------------------------------------------------
   Footer
   --------------------------------------------------------- */
footer {
  text-align: center;
  padding: 2.5rem 1.5rem;
  color: var(--muted);
  font-size: 0.85rem;
  border-top: 1px solid var(--line);
}

/* ---------------------------------------------------------
   Buy / bag

   "Add" pills on every price line, a bag trigger in the nav, and a
   slide-in drawer that collects the order and submits it through
   Netlify Forms.
   --------------------------------------------------------- */
.add-btn {
  flex: none;
  border: 1px solid rgba(143, 182, 255, 0.4);
  background: rgba(91, 147, 255, 0.12);
  color: var(--star);
  font: 600 0.74rem var(--body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: background 0.2s, box-shadow 0.2s, transform 0.15s;
}
.add-btn:hover {
  background: rgba(91, 147, 255, 0.28);
  box-shadow: 0 0 18px rgba(91, 147, 255, 0.3);
}
.add-btn.added {
  background: linear-gradient(135deg, var(--azure), var(--cyan));
  color: #05122e;
  border-color: transparent;
}

.panel-actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 1.2rem; }
.panel-actions .btn { margin-top: 0; }

/* Nav bag trigger */
.cart-link {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  background: transparent;
  border: 0;
  color: var(--ink);
  font-family: var(--body);
  font-size: 0.92rem;
  font-weight: 500;
  opacity: 0.85;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: opacity 0.2s, color 0.2s;
}
.cart-link:hover { opacity: 1; color: var(--azure-soft); }
.cart-count {
  min-width: 1.35rem;
  height: 1.35rem;
  padding: 0 0.4rem;
  display: inline-grid;
  place-items: center;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--azure), var(--cyan));
  color: #05122e;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1;
}
.cart-count[data-empty="true"] {
  background: rgba(143, 182, 255, 0.18);
  color: var(--azure-soft);
}

/* Drawer + backdrop */
.cart-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: rgba(2, 4, 12, 0.6);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.cart-backdrop.show { opacity: 1; }

.cart-drawer {
  position: fixed;
  top: 0;
  right: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
  width: min(420px, 92vw);
  height: 100%;
  background: linear-gradient(180deg, rgba(10, 20, 48, 0.96), rgba(5, 9, 24, 0.98));
  border-left: 1px solid var(--line);
  box-shadow: -30px 0 80px rgba(2, 4, 12, 0.7);
  transform: translateX(100%);
  transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.cart-drawer.open { transform: translateX(0); }

.cart-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.4rem 1.6rem;
  border-bottom: 1px solid var(--line);
}
.cart-head h3 { margin: 0; font-size: 1.4rem; }
.cart-close {
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 1.8rem;
  line-height: 1;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: color 0.2s;
}
.cart-close:hover { color: var(--star); }

.cart-body { flex: 1; overflow-y: auto; padding: 1rem 1.6rem; }
.cart-empty { color: var(--muted); font-size: 0.92rem; padding: 1.5rem 0; }

.cart-items { list-style: none; margin: 0; padding: 0; }
.cart-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--line);
}
.ci-info { display: flex; flex-direction: column; gap: 0.2rem; }
.ci-name { color: var(--ink); font-size: 0.9rem; font-weight: 600; }
.ci-price { color: var(--muted); font-size: 0.78rem; }
.ci-qty { display: flex; align-items: center; gap: 0.6rem; flex: none; }
.ci-qty button {
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(91, 147, 255, 0.1);
  color: var(--star);
  font-size: 1rem;
  line-height: 1;
  cursor: url(assets/cursor-tree.png) 16 24, pointer;
  transition: background 0.2s;
}
.ci-qty button:hover { background: rgba(91, 147, 255, 0.28); }
.ci-count { min-width: 1.2rem; text-align: center; color: var(--star); font-weight: 600; }

.cart-foot { padding: 1.2rem 1.6rem 1.6rem; border-top: 1px solid var(--line); }
.cart-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 1rem;
  font-family: var(--display);
}
.cart-total span:first-child { color: var(--muted); font-size: 1rem; }
.cart-total span:last-child { color: var(--cyan); font-size: 1.8rem; font-weight: 600; }

#orderForm { display: flex; flex-direction: column; gap: 0.7rem; }
#orderForm label {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
#orderForm input,
#orderForm textarea {
  background: rgba(4, 6, 15, 0.6);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  padding: 0.6rem 0.7rem;
  font: 400 0.95rem var(--body);
  resize: vertical;
}
#orderForm input:focus,
#orderForm textarea:focus { outline: none; border-color: var(--azure-soft); }
.opt-tag { text-transform: none; letter-spacing: 0; color: rgba(147, 164, 207, 0.6); }
.cart-submit { width: 100%; margin-top: 0.4rem; }
.cart-fineprint { font-size: 0.74rem; color: var(--muted); margin: 0.6rem 0 0; line-height: 1.5; }
.cart-success {
  color: var(--star);
  font-size: 0.95rem;
  line-height: 1.6;
  padding: 0.5rem 0;
}
.hp { position: absolute; left: -5000px; }

/* ---------------------------------------------------------
   Responsive
   --------------------------------------------------------- */
@media (max-width: 900px) {
  .hero,
  .split,
  .customizer,
  .story { grid-template-columns: 1fr; }
  .products { grid-template-columns: 1fr 1fr; }

  /* Collapse the bar into a hamburger-driven drop panel. The menu slides
     down beneath the sticky bar so tabs and quick actions stay reachable
     without crowding the brand. */
  .nav { flex-wrap: nowrap; }
  .nav-toggle { display: flex; }
  /* Surface the compact cart in the bar, just left of the hamburger, so the
     cart count stays visible at all times while browsing on a phone. */
  .cart-mini { display: inline-flex; margin-left: auto; }
  .nav-menu {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 1.1rem;
    padding: 1.4rem clamp(1.5rem, 4vw, 3rem) 1.8rem;
    background: linear-gradient(180deg, rgba(4, 6, 15, 0.98), rgba(6, 10, 26, 0.98));
    backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 24px 50px rgba(2, 4, 12, 0.6);
    transform: translateY(-12px);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1), opacity 0.24s;
  }
  .nav-menu.is-open {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }
  .nav-tabs {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
    width: 100%;
  }
  .nav-tabs a {
    width: 100%;
    padding: 0.7rem 0.2rem;
    font-size: 1rem;
    border-bottom: 1px solid var(--line);
  }
  .nav-tabs a::after { display: none; }
  .nav-tabs a.is-active { color: var(--azure-soft); }
  .nav-actions {
    flex-wrap: wrap;
    gap: 0.6rem;
    width: 100%;
    padding-top: 0.4rem;
  }
  .nav-cta { flex: 1 1 auto; justify-content: center; }
}
@media (max-width: 560px) {
  .products { grid-template-columns: 1fr; }
  .price-grid { grid-template-columns: 1fr; }
  .section { padding: 4rem 0; }
  .brand img { height: 38px; }
}

/* =========================================================
   Content page — hero quote, message sections, the equation,
   taglines, and the footer message. The page leads with the
   spinning hero piece, then reads top to bottom through the
   brand's own words.
   ========================================================= */

/* The hero's italic promise, set off with a luminous rule */
.hero-quote {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.3rem, 2.7vw, 1.95rem);
  line-height: 1.4;
  color: var(--ink);
  max-width: 30ch;
  margin: 1.6rem 0 0;
  padding-left: 1.2rem;
  border-left: 2px solid var(--azure);
}

/* A narrow, readable column for the Welcome and Brand Story copy,
   each opened by a hairline rule like the dividers in the brief. */
.message {
  max-width: 64ch;
  border-top: 1px solid var(--line);
}
.message h2 { margin-bottom: 1.3rem; }
.message p {
  font-size: 1.15rem;
  line-height: 1.7;
  color: var(--ink);
  opacity: 0.9;
  margin: 0 0 1.1rem;
}
.message p:last-child { margin-bottom: 0; }

/* The signature formula, raised out of the brand story */
.equation {
  font-family: var(--display);
  font-size: clamp(1.4rem, 3.4vw, 2.2rem) !important;
  font-weight: 600;
  letter-spacing: 0.5px;
  color: var(--cyan) !important;
  opacity: 1 !important;
  margin-top: 2rem !important;
  padding-top: 1.8rem;
  border-top: 1px solid var(--line);
}

/* Taglines — the brand's short lines, two columns settling to one */
.taglines { border-top: 1px solid var(--line); }
.taglines ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.2rem 3rem;
}
.taglines li {
  position: relative;
  padding-left: 1.5rem;
  font-family: var(--display);
  font-size: clamp(1.2rem, 2.4vw, 1.6rem);
  font-weight: 600;
  color: var(--star);
  line-height: 1.25;
}
.taglines li::before {
  content: "•";
  position: absolute;
  left: 0;
  color: var(--cyan);
}

/* Footer message — the closing quote over the void */
.footer-message {
  text-align: center;
  padding: 5rem 1.5rem 3rem;
  border-top: 1px solid var(--line);
  color: var(--muted);
}
.footer-quote {
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.35rem, 3vw, 2.1rem);
  line-height: 1.45;
  color: var(--star);
  max-width: 30ch;
  margin: 0 auto 2rem;
}
.footer-tree {
  display: block;
  width: auto;
  height: clamp(72px, 12vw, 120px);
  margin: 0 auto 0;
  opacity: 0.92;
  position: relative;
  z-index: 1;
}
.footer-fine {
  margin: 0;
  font-size: 0.85rem;
  color: var(--muted);
}
.footer-admin-link {
  color: inherit;
  text-decoration: none;
  opacity: 0.55;
  transition: opacity 0.2s ease;
}
.footer-admin-link:hover { opacity: 1; }

@media (max-width: 560px) {
  .taglines ul { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------
   Galaxy portal — the brand-on-the-board photo at the bottom
   of the page, which pulls you into space and becomes the
   rooted-tree chess piece when clicked.
   --------------------------------------------------------- */
.portal {
  display: flex;
  justify-content: center;
  padding-top: 2rem;
}
.portal-card {
  position: relative;
  display: block;
  width: min(100%, 820px);
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  cursor: pointer;
  background: var(--deep);
  box-shadow: 0 24px 70px rgba(2, 6, 20, 0.6);
  transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1),
              box-shadow 0.5s ease, border-color 0.5s ease;
}
.portal-card:hover,
.portal-card:focus-visible {
  transform: translateY(-6px) scale(1.012);
  border-color: rgba(143, 182, 255, 0.5);
  box-shadow: 0 32px 90px rgba(8, 24, 70, 0.75),
              0 0 0 1px rgba(121, 224, 255, 0.25);
  outline: none;
}
.portal-photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1), filter 0.7s ease;
}
.portal-card:hover .portal-photo,
.portal-card:focus-visible .portal-photo {
  transform: scale(1.05);
  filter: saturate(1.1) brightness(1.04);
}
/* nebula veil so the photo sits inside the galaxy theme */
.portal-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 120%, rgba(8, 16, 38, 0.92), transparent 60%),
    radial-gradient(80% 70% at 75% 15%, rgba(91, 147, 255, 0.18), transparent 60%),
    linear-gradient(180deg, rgba(4, 6, 15, 0.1), rgba(4, 6, 15, 0.45));
  mix-blend-mode: normal;
}
.portal-cta {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 1.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  text-align: center;
  color: var(--star);
  text-shadow: 0 2px 14px rgba(2, 6, 20, 0.85);
}
.portal-cta-kicker {
  font-size: 0.72rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--cyan);
}
.portal-cta-line {
  font-family: var(--display);
  font-size: clamp(1.15rem, 3.4vw, 1.7rem);
  font-weight: 600;
}

/* Full-screen immersion overlay */
.galaxy-portal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: radial-gradient(120% 120% at 60% 35%, rgba(12, 28, 68, 0.7), rgba(4, 6, 15, 0.96) 70%);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 0.6s ease;
}
.galaxy-portal[hidden] { display: none; }
.galaxy-portal.is-open { opacity: 1; }

.galaxy-portal-close {
  position: absolute;
  top: 1.2rem;
  right: 1.4rem;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--glass);
  color: var(--star);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.3s ease, transform 0.3s ease;
}
.galaxy-portal-close:hover { background: rgba(91, 147, 255, 0.25); transform: rotate(90deg); }

/* warp streaks rushing past as you fall into the galaxy */
.portal-warp {
  position: absolute;
  inset: -20%;
  pointer-events: none;
  background:
    radial-gradient(2px 2px at 20% 30%, rgba(255,255,255,0.9), transparent),
    radial-gradient(2px 2px at 70% 60%, rgba(143,182,255,0.9), transparent),
    radial-gradient(1.5px 1.5px at 40% 80%, rgba(121,224,255,0.85), transparent),
    radial-gradient(2px 2px at 85% 25%, rgba(255,255,255,0.85), transparent),
    radial-gradient(1.5px 1.5px at 12% 65%, rgba(190,214,255,0.85), transparent),
    radial-gradient(2px 2px at 55% 18%, rgba(255,255,255,0.9), transparent),
    radial-gradient(1.5px 1.5px at 33% 48%, rgba(143,182,255,0.8), transparent);
  background-size: 480px 480px;
  opacity: 0.7;
  animation: portal-warp 2.6s ease-out both;
}
@keyframes portal-warp {
  0%   { transform: scale(0.2); opacity: 0; }
  30%  { opacity: 0.9; }
  100% { transform: scale(2.6); opacity: 0.25; }
}

/* the scene where the photo dissolves into the tree piece */
.portal-scene {
  position: relative;
  width: min(86vw, 420px);
  aspect-ratio: 3 / 4;
  display: grid;
  place-items: center;
}
.portal-scene-photo {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 18px;
  box-shadow: 0 30px 90px rgba(2, 6, 20, 0.7);
  animation: portal-photo-dissolve 2.4s ease forwards;
}
@keyframes portal-photo-dissolve {
  0%   { opacity: 1; transform: scale(1); filter: saturate(1) blur(0); }
  55%  { opacity: 0.55; transform: scale(1.06); filter: saturate(1.3) blur(2px); }
  100% { opacity: 0; transform: scale(1.18); filter: saturate(1.6) blur(8px); }
}

/* the rooted tree standing as a glowing chess piece among the stars */
.portal-piece {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  animation: portal-piece-rise 2.6s ease 1.4s forwards;
}
@keyframes portal-piece-rise {
  0%   { opacity: 0; transform: translateY(26px) scale(0.86); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
.portal-piece-tree {
  position: relative;
  z-index: 2;
  width: min(58vw, 230px);
  filter: drop-shadow(0 0 18px rgba(143, 182, 255, 0.6))
          drop-shadow(0 0 42px rgba(121, 224, 255, 0.35));
  animation: portal-piece-float 6s ease-in-out 1.4s infinite alternate;
}
@keyframes portal-piece-float {
  from { transform: translateY(0); }
  to   { transform: translateY(-12px); }
}
/* the chess-piece pedestal the tree stands on */
.portal-piece-base {
  position: relative;
  z-index: 1;
  margin-top: -8px;
  width: min(42vw, 168px);
  height: 30px;
  border-radius: 50%;
  background:
    radial-gradient(60% 120% at 50% 0%, rgba(238, 244, 255, 0.95), rgba(143, 182, 255, 0.4) 60%, transparent 72%),
    radial-gradient(closest-side, rgba(91, 147, 255, 0.5), transparent);
  box-shadow: 0 14px 40px rgba(8, 24, 70, 0.8);
}
.portal-piece-base::before {
  content: "";
  position: absolute;
  left: 18%;
  right: 18%;
  top: -10px;
  height: 22px;
  border-radius: 50%;
  background: linear-gradient(180deg, rgba(238, 244, 255, 0.85), rgba(120, 160, 240, 0.3));
}
.portal-piece-glow {
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 50%;
  width: 120%;
  height: 120%;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, rgba(121, 224, 255, 0.22), transparent 70%);
  pointer-events: none;
}
.portal-caption {
  margin: 2.4rem auto 0;
  max-width: 34ch;
  text-align: center;
  font-family: var(--display);
  font-style: italic;
  font-size: clamp(1.1rem, 2.6vw, 1.5rem);
  color: var(--star);
  opacity: 0;
  animation: portal-caption-in 1.4s ease 2.6s forwards;
}
@keyframes portal-caption-in {
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .portal-warp,
  .portal-scene-photo,
  .portal-piece,
  .portal-piece-tree,
  .portal-caption { animation: none !important; }
  .portal-scene-photo { opacity: 0; }
  .portal-piece,
  .portal-caption { opacity: 1; }
}

/* =========================================================
   Journeys — share your story; it streaks into the galaxy,
   then joins the shared constellation wall below.
   ========================================================= */
.journeys { border-top: 1px solid var(--line); }
.journeys-head { max-width: 60ch; }
.journeys-note {
  font-size: 1.08rem;
  line-height: 1.7;
  color: var(--ink);
  opacity: 0.9;
}

/* The share form sits in a glassy panel that floats over the starfield. */
.journey-form {
  margin: 2.2rem 0 3rem;
  max-width: 720px;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.8rem;
  backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.journey-name-label,
.journey-story-label {
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cyan);
  font-weight: 600;
}
.journey-story-label { margin-top: 0.9rem; }
#journeyName {
  background: rgba(4, 6, 15, 0.6);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  padding: 0.6rem 0.75rem;
  font: 400 0.95rem var(--body);
  max-width: 320px;
}
.journey-input-wrap { position: relative; }
#journeyStory {
  width: 100%;
  background: rgba(4, 6, 15, 0.6);
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--star);
  padding: 0.85rem 0.9rem;
  font: 400 1.02rem/1.6 var(--body);
  resize: vertical;
  min-height: 96px;
  transition: border-color 0.25s, box-shadow 0.25s;
}
#journeyName:focus,
#journeyStory:focus {
  outline: none;
  border-color: var(--azure-soft);
  box-shadow: 0 0 0 3px rgba(91, 147, 255, 0.16), 0 0 26px rgba(121, 224, 255, 0.18);
}
.journey-hint {
  display: block;
  margin-top: 0.5rem;
  font-size: 0.76rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.journey-actions {
  margin-top: 1.1rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}
#journeySend { cursor: url(assets/cursor-tree.png) 16 24, pointer; }
#journeySend:disabled { opacity: 0.55; cursor: default; transform: none; }
.journey-error {
  margin: 0.4rem 0 0;
  font-size: 0.9rem;
  color: #ffb3c6;
}
/* Same slot, but a warm confirmation once a journey is sent for review. */
.journey-error.is-thanks {
  color: #9ff3c8;
}

/* The shared wall — every journey a point of light in the same sky. It stays
   hidden until a visitor opens it via the toggle below the form. */
.journey-wall-toggle {
  margin-top: 0.5rem;
}
.journey-wall {
  margin-top: 2.2rem;
}
.journey-wall-empty {
  font-family: var(--display);
  font-style: italic;
  font-size: 1.2rem;
  color: var(--azure-soft);
  margin: 0;
}
.journey-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.2rem;
}
.journey-card {
  position: relative;
  background: linear-gradient(180deg, rgba(17, 42, 99, 0.5), rgba(8, 16, 38, 0.5));
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 1.3rem 1.3rem 1.1rem;
  overflow: hidden;
  transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
}
.journey-card:hover {
  transform: translateY(-3px);
  border-color: rgba(143, 182, 255, 0.4);
  box-shadow: 0 16px 40px rgba(4, 6, 15, 0.5), 0 0 26px rgba(91, 147, 255, 0.16);
}
/* A small star burning in the corner of each journey. */
.journey-card-star {
  position: absolute;
  top: 1rem;
  right: 1.1rem;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 8px 2px rgba(255, 255, 255, 0.8), 0 0 16px 5px rgba(121, 224, 255, 0.5);
}
.journey-card-story {
  margin: 0 0 1rem;
  color: var(--star);
  font-size: 1rem;
  line-height: 1.65;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.journey-card-meta {
  margin: 0;
  font-size: 0.8rem;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.journey-card-name { color: var(--azure-soft); font-weight: 600; }

/* A new journey settling onto the wall, as if drifting in from the stars. */
.journey-card.is-arriving { animation: journey-arrive 0.9s ease both; }
@keyframes journey-arrive {
  from { opacity: 0; transform: translateY(-22px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* The flying capsule: the words lift, gather into a bright head, and streak
   off into the galaxy trailing a shooting-star tail. */
.journey-comet {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.85rem 0.9rem;
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(91, 147, 255, 0.2), rgba(8, 16, 38, 0.55));
  border: 1px solid rgba(143, 182, 255, 0.45);
  box-shadow: 0 0 30px rgba(91, 147, 255, 0.45), 0 0 60px rgba(121, 224, 255, 0.25);
  transform-origin: center;
  will-change: transform, opacity;
}
.journey-comet-text {
  color: var(--star);
  font: 500 1rem/1.6 var(--body);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 8em;
  overflow: hidden;
}
/* The bright head that remains once the words have gathered. */
.journey-comet-core {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: #fff;
  opacity: 0;
  box-shadow: 0 0 12px 4px rgba(255, 255, 255, 0.95), 0 0 28px 10px rgba(143, 182, 255, 0.7);
}
/* The streak trailing behind the head as it races away. */
.journey-comet-tail {
  position: absolute;
  top: 50%;
  right: 50%;
  height: 3px;
  width: 0;
  border-radius: 3px;
  transform-origin: right center;
  background: linear-gradient(
    90deg,
    rgba(121, 224, 255, 0) 0%,
    rgba(143, 182, 255, 0.55) 55%,
    rgba(238, 244, 255, 0.95) 100%
  );
}

@media (prefers-reduced-motion: reduce) {
  .journey-card.is-arriving { animation: none; }
}

@media (max-width: 560px) {
  .journey-form { padding: 1.3rem; }
  .journey-list { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------
   Stitch zoom — a close-up of the configured design, styled
   as raised satin-stitch embroidery on a woven-fabric swatch,
   with a magnifier loupe that follows the pointer.
   --------------------------------------------------------- */
.zoom-trigger {
  margin: 0.9rem auto 0;
  display: inline-flex;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
}

.stitch-zoom {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: 2rem 1.4rem;
  overflow-y: auto;
  background: radial-gradient(120% 120% at 50% 30%, rgba(12, 28, 68, 0.72), rgba(3, 5, 12, 0.97) 72%);
  backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.stitch-zoom[hidden] { display: none; }
.stitch-zoom.is-open { opacity: 1; }

.stitch-zoom-close {
  position: fixed;
  top: 1.1rem;
  right: 1.2rem;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(8, 13, 30, 0.7);
  color: var(--ink);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  backdrop-filter: blur(6px);
  transition: background 0.2s ease, transform 0.2s ease;
}
.stitch-zoom-close:hover { background: rgba(20, 34, 74, 0.9); transform: scale(1.06); }

.stitch-zoom-inner {
  width: 100%;
  max-width: 1000px;
  text-align: center;
}
.stitch-zoom-kicker {
  font-family: var(--display);
  font-size: 1.15rem;
  color: var(--cyan);
  margin: 0 0 1.4rem;
}
.stitch-zoom-note {
  font-size: 0.82rem;
  color: var(--azure-soft);
  opacity: 0.85;
  margin: 1.5rem auto 0;
  max-width: 540px;
}

.stitch-plates {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  justify-content: center;
  align-items: flex-start;
}

/* A single fabric swatch. The shopper's shirt color is the cloth, overlaid with
   a fine cross-hatch weave so it reads as a real woven textile at any color. */
.stitch-plate {
  --cloth-color: #000000;
  --logo-color: #ffffff;
  position: relative;
  width: min(420px, 82vw);
  aspect-ratio: 4 / 5;
  margin: 0;
  border-radius: 16px;
  overflow: hidden;
  cursor: crosshair;
  /* keep vertical scrolling of the zoom modal working on touch devices while a
     sideways drag still drives the magnifier loupe */
  touch-action: pan-y;
  background-color: var(--cloth-color);
  background-image:
    repeating-linear-gradient(45deg,  rgba(255, 255, 255, 0.07) 0 1.5px, transparent 1.5px 4px),
    repeating-linear-gradient(-45deg, rgba(0, 0, 0, 0.22) 0 1.5px, transparent 1.5px 4px);
  box-shadow:
    inset 0 0 60px rgba(0, 0, 0, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.06),
    0 24px 60px rgba(2, 5, 14, 0.6);
}
.stitch-plate-cap {
  position: absolute;
  left: 50%;
  bottom: 0.7rem;
  transform: translateX(-50%);
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
  background: rgba(4, 6, 15, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.12);
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  pointer-events: none;
  z-index: 4;
}

/* The design itself fills the swatch; the print is masked artwork filled with
   the chosen logo color (inherited via --logo-color), enlarged and centered. */
.stitch-design { position: absolute; inset: 0; }
.stitch-design .print {
  position: absolute !important;
  inset: 9% !important;
  top: 9% !important;
  left: 9% !important;
  width: auto !important;
  height: auto !important;
  aspect-ratio: auto !important;
  transform: none !important;
  /* a raised satin bead all the way round the artwork plus a cast shadow, so the
     embroidery sits proud of the cloth like a real stitched patch */
  filter:
    drop-shadow(0 0.5px 0 rgba(255, 255, 255, 0.55))
    drop-shadow(0.6px 0 0 rgba(255, 255, 255, 0.3))
    drop-shadow(0 -0.5px 0 rgba(0, 0, 0, 0.55))
    drop-shadow(-0.6px 0 0 rgba(0, 0, 0, 0.35))
    drop-shadow(0 0 1px rgba(255, 255, 255, 0.2))
    drop-shadow(0 4px 6px rgba(0, 0, 0, 0.55));
}
.stitch-design .print.spine,
.stitch-design .print.hip { display: flex; align-items: center; justify-content: center; }

/* The thread relief: rows of raised satin stitches clipped to the artwork and
   blended over the fill so they read as real embroidered floss — rounded threads
   that catch the light, a fine ladder of individual stitch ends across the run,
   and a faint floss grain on top. It is color-independent, so it works over any
   single, lettered, or sectioned fill. --stitch-angle sets the stitch direction
   per face so the front mark and the back tree are stitched their own way. */
.stitch-threads {
  position: absolute;
  inset: 9%;
  pointer-events: none;
  --stitch-angle: 62deg;
  background-image:
    /* floss grain — the slight fuzz of real thread */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='0.3' intercept='0.35'/%3E%3CfeFuncG type='linear' slope='0.3' intercept='0.35'/%3E%3CfeFuncB type='linear' slope='0.3' intercept='0.35'/%3E%3CfeFuncA type='linear' slope='0' intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)'/%3E%3C/svg%3E"),
    /* the ladder of individual stitch ends, across the run of the floss */
    repeating-linear-gradient(
      calc(var(--stitch-angle) + 90deg),
      rgba(0, 0, 0, 0.22) 0 0.5px,
      transparent 0.5px 6px
    ),
    /* the satin ridges: rounded parallel threads catching a rim of light */
    repeating-linear-gradient(
      var(--stitch-angle),
      rgba(255, 255, 255, 0.5) 0,
      rgba(255, 255, 255, 0.12) 0.7px,
      rgba(0, 0, 0, 0.14) 1.7px,
      rgba(0, 0, 0, 0.5) 2.3px,
      rgba(0, 0, 0, 0.14) 2.9px,
      rgba(255, 255, 255, 0.5) 3.4px
    );
  background-size: 90px 90px, auto, auto;
  mix-blend-mode: overlay;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}
.stitch-threads.is-mark {
  --stitch-angle: 62deg;
  -webkit-mask-image: url(assets/lifemath-mark.png);
          mask-image: url(assets/lifemath-mark.png);
}
.stitch-threads.is-tree {
  --stitch-angle: 90deg;
  -webkit-mask-image: url(assets/tree-mark.png);
          mask-image: url(assets/tree-mark.png);
}

/* The magnifier loupe — a circular lens that tracks the pointer and shows a
   scaled copy of the design so the stitch detail comes right up. */
.stitch-loupe {
  position: absolute;
  width: 160px;
  height: 160px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 3;
  background-color: var(--cloth-color);
  background-image:
    repeating-linear-gradient(45deg,  rgba(255, 255, 255, 0.07) 0 2px, transparent 2px 6px),
    repeating-linear-gradient(-45deg, rgba(0, 0, 0, 0.22) 0 2px, transparent 2px 6px);
  border: 3px solid rgba(255, 255, 255, 0.8);
  box-shadow:
    0 10px 28px rgba(0, 0, 0, 0.55),
    inset 0 0 0 5px rgba(0, 0, 0, 0.28),
    inset 0 0 30px rgba(0, 0, 0, 0.4);
}
.stitch-loupe.is-on { opacity: 1; }
.stitch-loupe-content {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}
/* inside the loupe the cloned design's drop-shadows are scaled with it */
.stitch-loupe-content .stitch-design { position: absolute; inset: 0; }

@media (prefers-reduced-motion: reduce) {
  .stitch-zoom { transition: none; }
}
@media (max-width: 560px) {
  .stitch-zoom-kicker { font-size: 1rem; }
  .stitch-plate { width: min(420px, 88vw); }
}
