/* ============================================================
   Components
   Reusable building blocks: layout wrap, buttons, cards,
   chips, form fields, toasts, marquee and decorative marks.
   ============================================================ */

/* ---------- Primitives ---------- */
.wrap { max-width: 1240px; margin: 0 auto; padding-left: 1.5rem; padding-right: 1.5rem; }
@media (min-width: 768px) { .wrap { padding-left: 3rem; padding-right: 3rem; } }

.section-line { border-top: 1px solid var(--line-0); }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-body); font-weight: 500; font-size: 14px;
  padding: 0.8rem 1.4rem; border-radius: var(--r-pill);
  cursor: pointer; border: 1px solid transparent; text-decoration: none;
  transition: all 0.35s var(--ease); white-space: nowrap; line-height: 1;
}
.btn-primary { background: var(--fg-0); color: var(--bg-0); }
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 14px 30px -12px var(--accent-line); }
.btn-accent { background: var(--accent); color: var(--accent-fg); }
.btn-accent:hover { transform: translateY(-1px); background: var(--accent-deep); box-shadow: 0 16px 36px -10px var(--accent-deep-line); }
.btn-ghost { background: var(--surface-veil); color: var(--fg-0); border-color: var(--line-1); }
.btn-ghost:hover { background: var(--surface-veil-2); border-color: var(--fg-3); }

/* secondary CTA that still invites the click: accent-tinted edge, lifts and glows on hover.
   Louder than .btn-ghost, deliberately quieter than the always-animating .btn-cta so the
   primary action keeps the hierarchy. */
.btn-demo { background: var(--surface-veil); color: var(--fg-0); border-color: var(--accent-line); }
.btn-demo:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 14px 30px -14px var(--accent-deep-line);
}
.btn svg { transition: transform 0.35s var(--ease); }
.btn:hover svg.arr { transform: translate(2px, -2px); }

/* deploy mocks: lift a touch on hover, open full-size on click.
   scale() is a paint-time transform, so the mock's ResizeObserver (which reads
   clientWidth) keeps its internal scale — the UI inside doesn't re-layout. */
.deploy-card {
  position: relative;
  /* zoom-in belongs with the click that zooms. Without a pointer there is no lightbox and
     no cursor either, but the declaration would still be there for anything that inspects
     it, and the class would still be claiming an affordance the card no longer offers. */
  cursor: default;
  transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
  /* Promoted for a :hover transform, which a touch device cannot fire — so on a phone this
     was two permanently promoted layers, 1.3 Mpx of GPU memory, for an effect that never
     runs there. Behind (hover: hover) it still helps the desktop it was written for. */
}
@media (hover: hover) and (pointer: fine) { .deploy-card { will-change: transform; cursor: zoom-in; } }
.deploy-card:hover {
  transform: scale(1.02);
  box-shadow: var(--shadow-lg), 0 32px 70px -24px var(--accent-line);
}
/* The mocks are light UIs and glare against the dark site. Tint them down in the
   section only — the lightbox (.zoom-shell) has no .deploy-card, so it stays bright. */
.deploy-card::after {
  content: "";
  position: absolute; inset: 0; z-index: 2;
  background: rgba(0, 0, 0, 0.08);
  pointer-events: none;
  transition: background 0.45s var(--ease);
}
.deploy-card:hover::after { background: rgba(0, 0, 0, 0.03); }

/* magnifier that fades in over the mock */
.zoom-hint {
  position: absolute; inset: 0; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  /* the card is already tinted by .deploy-card::after, so this only needs to add
     enough contrast for the magnifier to read */
  background: color-mix(in oklab, var(--bg-0) 30%, transparent);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
  pointer-events: none;
}
.deploy-card:hover .zoom-hint,
.deploy-card:focus-visible .zoom-hint { opacity: 1; }
/* just a magnifier, dead centre on the mock — see-through, no fill.
   NOTE: no backdrop-filter here. This sits inside .zoom-hint, which animates
   opacity; a backdrop-filter in an opacity-animated layer gets composited
   against its own layer and flashes white mid-transition. */
.zoom-badge {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 68px; height: 68px; border-radius: 999px;
  background: color-mix(in oklab, var(--fg-0) 12%, transparent);
  border: 1.5px solid color-mix(in oklab, var(--fg-0) 60%, transparent);
  color: var(--fg-0);
  transform: scale(0.86);
  transition: transform 0.35s var(--ease);
}
.deploy-card:hover .zoom-badge,
.deploy-card:focus-visible .zoom-badge { transform: scale(1); }

/* sonar ring rippling out of the lens */
.zoom-badge::after {
  content: "";
  position: absolute;
  inset: -1.5px;
  border-radius: 999px;
  border: 1.5px solid color-mix(in oklab, var(--fg-0) 50%, transparent);
  animation: zoomRing 2.4s var(--ease) infinite;
  pointer-events: none;
}
@keyframes zoomRing {
  0%        { transform: scale(1);    opacity: 0.6; }
  70%, 100% { transform: scale(1.5);  opacity: 0; }
}

/* the glass drifts as if scanning the mock, and breathes a little */
.zoom-badge svg { animation: zoomScan 3.2s var(--ease) infinite; }
@keyframes zoomScan {
  0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); }
  25%      { transform: translate(-2.5px, -2px) rotate(-7deg) scale(1.06); }
  50%      { transform: translate(2.5px, -1px) rotate(6deg) scale(1); }
  75%      { transform: translate(1px, 2.5px) rotate(-3deg) scale(1.06); }
}

/* lightbox */
.zoom-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(1rem, 3vw, 2.5rem);
  background: color-mix(in oklab, var(--bg-0) 84%, transparent);
  backdrop-filter: blur(16px) saturate(120%);
  cursor: zoom-out;
  animation: zoomFade 0.26s var(--ease) both;
}
.zoom-shell {
  position: relative;               /* anchors the close button to the preview, not the screen */
  /* Cap by BOTH axes. A 16:9 mock at 95vw was taller than a short/laptop viewport, so the preview
     overflowed (read as "off-centre") and the top-right close button scrolled off the top edge.
     calc(92vh * 16/9) caps the width so the derived height never exceeds 92vh. */
  width: min(1560px, 95vw, calc(92vh * 16 / 9));
  border-radius: var(--r-xl);
  overflow: hidden;
  border: 1px solid var(--line-1);
  box-shadow: 0 60px 140px -40px rgba(0, 0, 0, 0.75);
  cursor: default;
  animation: zoomPop 0.34s var(--ease) both;
}
/* sits in the preview's own top-right corner. Fully opaque — the mocks behind it
   are light UIs, so any transparency makes it disappear into the screenshot.
   No backdrop-filter: its ancestors animate opacity and a filtered layer flashes. */
.zoom-close {
  position: absolute; top: 14px; right: 14px; z-index: 3;
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 999px;
  background: #171513; color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.28);
  box-shadow: 0 8px 22px -6px rgba(0, 0, 0, 0.55);
  cursor: pointer;
  transition: background 0.25s var(--ease), transform 0.25s var(--ease);
}
.zoom-close:hover { background: #000; transform: scale(1.08); }
@keyframes zoomFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes zoomPop  { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .deploy-card, .zoom-hint, .zoom-badge { transition: none; }
  .deploy-card:hover { transform: none; }
  .zoom-overlay, .zoom-shell, .zoom-badge svg { animation: none; }
  .zoom-badge::after { display: none; }
}

/* oversized CTA (primary action of a section) */
.btn-xl { font-size: 16px; font-weight: 600; padding: 1.05rem 2rem; gap: 0.65rem; }

/* attention-grabbing demo CTA: breathing glow + a white shine sweeping L→R */
.btn-cta {
  position: relative;
  overflow: hidden;
  animation: ctaPulse 2.6s var(--ease) infinite;
}
.btn-cta:hover { animation-play-state: paused; }
/* keep the label/icon above the sweeping highlight */
.btn-cta > * { position: relative; z-index: 1; }
.btn-cta::before {
  content: "";
  position: absolute;
  top: -25%; left: -80%;
  width: 40%; height: 150%;
  background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, 0.6) 50%, transparent 100%);
  transform: skewX(-18deg);
  animation: ctaShine 3.6s var(--ease) infinite;
  pointer-events: none;
  z-index: 0;
}
/* glow only — the button must not change size */
@keyframes ctaPulse {
  0%, 100% { box-shadow: 0 6px 14px -13px var(--accent-deep-line); }
  50%      { box-shadow: 0 9px 22px -13px var(--accent); }
}
/* sweep across, then wait offscreen so it reads as a periodic glint */
@keyframes ctaShine {
  0%   { left: -80%; }
  45%  { left: 130%; }
  100% { left: 130%; }
}
@media (prefers-reduced-motion: reduce) {
  .btn-cta, .btn-cta::before { animation: none; }
  .btn-cta::before { display: none; }
}

/* Cards */
.card {
  position: relative; overflow: hidden;
  border: 1px solid var(--line-0);
  background: var(--surface-veil);
  border-radius: var(--r-lg);
  transition: border-color 0.45s var(--ease), background 0.45s var(--ease), transform 0.45s var(--ease);
}
.card-hover:hover { border-color: var(--line-1); background: var(--surface-veil-2); }

/* Capabilities — glassmorphism card with intense accent glow on hover */
.cap-card {
  transition: transform .45s var(--ease), border-color .45s var(--ease), box-shadow .45s var(--ease);
}
.cap-card:hover {
  transform: translateY(-5px);
  border-color: var(--accent);
  box-shadow:
    0 0 0 1px var(--accent-line),
    0 22px 60px -16px color-mix(in oklab, var(--accent) 50%, transparent),
    0 0 70px color-mix(in oklab, var(--accent) 22%, transparent),
    inset 0 1px 0 color-mix(in oklab, var(--fg-0) 8%, transparent);
}
.cap-card:hover .cap-ic {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent);
}

/* Chip / tag */
.chip {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--font-mono); font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.18em; color: var(--fg-2);
  padding: 0.4rem 0.75rem; border-radius: var(--r-pill);
  border: 1px solid var(--line-0); background: var(--surface-veil);
}
/* On small phones keep a chip (e.g. the hero eyebrow "Real-time 3D · Built for manufacturers") on
   one line — it must shrink AND nowrap together, never nowrap alone (that would overflow). */
@media (max-width: 480px) {
  .chip { font-size: 10px; letter-spacing: 0.08em; padding: 0.4rem 0.6rem; white-space: nowrap; }
}

/* Fields */
.field-label { display: block; font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.18em; color: var(--fg-2); margin-bottom: 0.55rem; }
.field-input {
  width: 100%; background: var(--bg-inset); color: var(--fg-0);
  border: 1px solid var(--line-0); border-radius: var(--r-sm);
  padding: 0.8rem 1rem; font-family: var(--font-body); font-size: 15px; font-weight: 300;
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease);
}
.field-input::placeholder { color: var(--fg-3); }
.field-input:focus { outline: none; border-color: var(--accent); background: var(--bg-1); }
textarea.field-input { resize: none; }

/* /quote runs a touch larger than the default field sizing (admin keeps the defaults) */
#quote-root .field-label { font-size: 12px; margin-bottom: 0.6rem; }
#quote-root .field-input { padding: 0.95rem 1.1rem; font-size: 16px; border-radius: var(--r-md); }
/* Choice-card grids collapse to one column on phones so long option labels stop clipping.
   !important overrides the inline grid-template-columns (desktop keeps its 2-up inline value). */
@media (max-width: 560px) { #quote-root .opt-grid { grid-template-columns: 1fr !important; } }

/* Toasts */
#toast-root { position: fixed; bottom: 1.25rem; right: 1.25rem; z-index: 200; display: flex; flex-direction: column; gap: 0.5rem; }
.toast {
  background: var(--bg-1); border: 1px solid var(--line-1); color: var(--fg-0);
  font-family: var(--font-body); padding: 0.8rem 1.05rem; border-radius: var(--r-md);
  max-width: 330px; box-shadow: var(--shadow-lg); animation: toastIn 0.3s var(--ease);
}
.toast .t-title { font-size: 14px; font-weight: 600; }
.toast .t-desc { font-size: 12px; color: var(--fg-2); margin-top: 3px; font-weight: 300; }
@keyframes toastIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

/* Marquee for logo bar */
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.marquee-track { display: flex; width: max-content; animation: marquee 38s linear infinite; }
.marquee-mask { -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent); }

/* Diamond mark */
.mark { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; }
.mark svg { display: block; }

/* edge fade for hero render */
.edge-fade { -webkit-mask-image: radial-gradient(ellipse 75% 80% at 50% 42%, #000 60%, transparent 100%); mask-image: radial-gradient(ellipse 75% 80% at 50% 42%, #000 60%, transparent 100%); }
