/* FrameStudio storefront — Sculpted Brand.
   Implements the visual system in web/DESIGN.md: near-black surfaces, a deliberate
   violet accent, 8px cards, 6px controls, and a full-height home hero. */

:root {
  /* Palette — DESIGN.md section 4. */
  --bg: #0b0a12;
  --bg-deep: #07070d;
  --surface: #12111b;
  --surface-raised: #1a1727;

  --text: #f4f3f8;
  --muted: #b5b1c4;
  --dim: #918ba3;

  --primary: #8b5cf6;
  --primary-action: #7540d8;
  --primary-hover: #6934c9;
  --primary-text: #c4b5fd;
  --primary-soft: rgba(139, 92, 246, 0.12);
  --primary-line: rgba(139, 92, 246, 0.42);

  --line: rgba(244, 243, 248, 0.12);
  --line-strong: rgba(244, 243, 248, 0.22);
  --control-line: #756c8c;

  --success: #66d9af;
  --warning: #f0cb79;
  --error: #ff8c99;

  --on-primary: #fbfaff;

  /* Scrollbars — one track/thumb pair for the page and every inner scroll box. */
  --scroll-size: 12px;
  --scroll-size-thin: 8px;
  --scroll-track: rgba(244, 243, 248, 0.04);
  --scroll-thumb: rgba(244, 243, 248, 0.18);
  --scroll-thumb-hover: rgba(139, 92, 246, 0.55);

  /* Spacing scale: 4 8 12 16 24 32 48 64 96 128. */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s6: 1.5rem;
  --s8: 2rem;
  --s12: 3rem;
  --s16: 4rem;
  --s24: 6rem;
  --s32: 8rem;

  --radius-card: 8px;
  --radius-control: 6px;

  --maxw: 1440px;
  /* Wider than the standard container so the hero keeps a little extra air on large
     screens without stretching into ultrawide emptiness. */
  --maxw-hero: 1600px;
  --gutter: clamp(1.25rem, 3.5vw, 3.25rem);
  --header-height: 4.75rem;

  --font-body: Inter, "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --t-control: 170ms var(--ease);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* Dark-only storefront: tells the UA to render form controls and any unstyled
     scrollbar dark rather than defaulting to the light set. */
  color-scheme: dark;
  scroll-behavior: smooth;
  /* Anything with an id lands clear of the sticky header. */
  scroll-padding-top: calc(var(--header-height) + var(--s4));
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

p {
  margin: 0;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 750;
  line-height: 1.12;
  letter-spacing: -0.025em;
}

/* Several components set display on the same elements, which would beat the UA [hidden] rule. */
[hidden] {
  display: none !important;
}

:focus-visible {
  outline: 2px solid var(--primary-text);
  outline-offset: 2px;
  border-radius: 2px;
}

::selection {
  background: var(--primary-action);
  color: var(--on-primary);
}

/* ------------------------------------------------------------- scrollbars */
/* Two engines, two mechanisms for the same bar.

   Chromium and Safari get the ::-webkit- pseudo-elements, which are the only way
   to express a rounded thumb inset from its track. Setting `scrollbar-width` on
   those engines would switch them to the UA's own thin scrollbar and drop the
   pseudo-element rules entirely, so the standard properties are fenced off to
   engines with no ::-webkit- support — Firefox today. Safari before 16.4 has no
   `selector()` either, which makes the condition invalid and leaves it on the
   ::-webkit- path, which is where it belongs. */
@supports not selector(::-webkit-scrollbar) {
  * {
    scrollbar-color: var(--scroll-thumb) var(--scroll-track);
    scrollbar-width: thin;
  }

  .gallery-thumbs,
  .tabs,
  .table-scroll,
  .menu-panel {
    scrollbar-color: var(--scroll-thumb) transparent;
  }
}

::-webkit-scrollbar {
  width: var(--scroll-size);
  height: var(--scroll-size);
}

::-webkit-scrollbar-track {
  background: var(--scroll-track);
}

::-webkit-scrollbar-thumb {
  background-color: var(--scroll-thumb);
  /* The border is transparent and painted over the track, which insets the thumb
     so it reads as a floating pill instead of filling the gutter edge to edge. */
  border: 3px solid transparent;
  background-clip: padding-box;
  border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
  background-color: var(--scroll-thumb-hover);
}

::-webkit-scrollbar-corner {
  background: var(--scroll-track);
}

/* Inner boxes sit on a card surface, so their gutter stays transparent and the
   bar shrinks to keep the content the loudest thing in the box. */
.gallery-thumbs::-webkit-scrollbar,
.tabs::-webkit-scrollbar,
.table-scroll::-webkit-scrollbar,
.menu-panel::-webkit-scrollbar {
  width: var(--scroll-size-thin);
  height: var(--scroll-size-thin);
}

.gallery-thumbs::-webkit-scrollbar-track,
.tabs::-webkit-scrollbar-track,
.table-scroll::-webkit-scrollbar-track,
.menu-panel::-webkit-scrollbar-track {
  background: transparent;
}

.gallery-thumbs::-webkit-scrollbar-thumb,
.tabs::-webkit-scrollbar-thumb,
.table-scroll::-webkit-scrollbar-thumb,
.menu-panel::-webkit-scrollbar-thumb {
  border-width: 2px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--s4);
  top: var(--s2);
  z-index: 100;
  transform: translateY(-200%);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  padding: var(--s3) var(--s4);
  font-weight: 600;
  transition: transform var(--t-control);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ---------------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(3rem, 7vw, 7rem);
}

.section--tight {
  padding-block: clamp(2rem, 4vw, 3.5rem);
}

.section--alt {
  background: var(--bg-deep);
  border-block: 1px solid var(--line);
}

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s6);
  flex-wrap: wrap;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.section-head p {
  color: var(--muted);
  max-width: 60ch;
  margin-top: var(--s3);
}

.stack {
  display: grid;
  gap: var(--s4);
}

/* ------------------------------------------------------------ typography */

.eyebrow {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--primary-text);
  margin-bottom: var(--s3);
}

.h-hero {
  font-size: clamp(2.75rem, 4.6vw, 4.75rem);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.04em;
}

.h-page {
  font-size: clamp(2rem, 3.6vw, 3.5rem);
  letter-spacing: -0.035em;
}

.h-section {
  font-size: clamp(1.625rem, 2.8vw, 2.5rem);
  letter-spacing: -0.032em;
}

.h-sub {
  font-size: clamp(1.125rem, 1.6vw, 1.375rem);
  letter-spacing: -0.02em;
}

.lede {
  color: var(--muted);
  font-size: 1.0625rem;
  line-height: 1.65;
  max-width: 62ch;
}

.accent {
  color: var(--primary-text);
  /* Solid colour above is the fallback; the gradient only paints where it is supported. */
  background: linear-gradient(96deg, #a78bfa 8%, #8b5cf6 55%, #c4b5fd 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.mono-line {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.14em;
  color: var(--dim);
}

.mono-line span {
  color: var(--line-strong);
  margin-inline: var(--s2);
}

/* ---------------------------------------------------------------- header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg-deep);
  border-bottom: 1px solid var(--line);
  /* Height stays fixed while scrolling (DESIGN.md section 8); only the edge changes. */
  transition: border-color var(--t-control), box-shadow var(--t-control);
}

.site-header[data-scrolled="true"] {
  border-bottom-color: var(--line-strong);
  box-shadow: 0 0.75rem 1.75rem rgba(0, 0, 0, 0.45);
}

.site-header > .wrap {
  display: flex;
  align-items: center;
  gap: var(--s6);
  min-height: var(--header-height);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 1.1875rem;
  font-weight: 750;
  letter-spacing: -0.02em;
  white-space: nowrap;
  flex: none;
}

.brand img {
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
}

.brand b {
  font-weight: 750;
}

.brand i {
  font-style: normal;
  color: var(--primary-text);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--s1);
  margin-inline: auto;
}

.nav a {
  position: relative;
  padding: var(--s3) var(--s4);
  font-size: 0.9375rem;
  color: var(--muted);
  border-radius: var(--radius-control);
  transition: color var(--t-control);
}

.nav a:hover {
  color: var(--text);
}

.nav a[aria-current="page"] {
  color: var(--text);
}

.nav a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: var(--s4);
  right: var(--s4);
  bottom: 0.3rem;
  height: 2px;
  border-radius: 2px;
  background: var(--primary);
}

.nav-drop {
  position: relative;
}

.nav-drop > a {
  padding-right: 1.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23b5b1c4' stroke-width='1.6'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.65rem center;
  background-size: 0.7rem 0.45rem;
}

.nav-drop__menu {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: 55;
  display: none;
  min-width: 15.5rem;
  padding: 0.35rem;
  gap: 0.15rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-card);
  background: var(--surface);
  box-shadow: 0 1.25rem 2.75rem rgba(0, 0, 0, 0.55);
}

.nav-drop__menu::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 0.55rem;
}

.nav-drop:hover .nav-drop__menu,
.nav-drop:focus-within .nav-drop__menu {
  display: grid;
}

.nav-drop__menu a {
  display: grid;
  gap: 0.15rem;
  padding: 0.7rem 0.8rem;
  border-radius: var(--radius-control);
  color: var(--text);
  line-height: 1.25;
  white-space: normal;
}

.nav-drop__menu a:hover,
.nav-drop__menu a:focus-visible {
  background: var(--surface-raised);
  color: var(--text);
}

.nav-drop__menu a[aria-current="page"] {
  background: var(--primary-soft);
  color: var(--primary-text);
}

.nav-drop__menu a[aria-current="page"]::after {
  display: none;
}

.nav-drop__label {
  font-size: 0.9375rem;
  font-weight: 600;
}

.nav-drop__hint {
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--dim);
  letter-spacing: 0.01em;
}

.nav-drop__menu a[aria-current="page"] .nav-drop__hint {
  color: var(--primary-text);
  opacity: 0.72;
}

.menu-panel nav a.menu-sub {
  display: grid;
  gap: 0.15rem;
  padding: 0.85rem 0 0.85rem 1.15rem;
  margin-left: 0.15rem;
  border-left: 2px solid var(--line);
  font-size: 1rem;
  font-weight: 600;
}

.menu-panel nav a.menu-sub .nav-drop__hint {
  font-weight: 400;
}

.menu-panel nav a.menu-sub[aria-current="page"] {
  border-left-color: var(--primary);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: none;
}

.currency-label {
  font-size: 0.875rem;
  color: var(--muted);
  padding-inline: var(--s2);
  border-right: 1px solid var(--line);
  padding-right: var(--s4);
  margin-right: var(--s1);
}

.header-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 2.75rem;
  padding: var(--s2) var(--s3);
  /* Used on both links and buttons, so the button UA chrome has to be cleared. */
  border: 0;
  background: none;
  font: inherit;
  border-radius: var(--radius-control);
  font-size: 0.9375rem;
  color: var(--muted);
  position: relative;
  cursor: pointer;
  transition: color var(--t-control), background var(--t-control);
}

.header-link:hover {
  color: var(--text);
  background: var(--surface-raised);
}

.header-link .ico {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
}

.header-link .count {
  min-width: 1.25rem;
  height: 1.25rem;
  padding-inline: 0.3rem;
  border-radius: 999px;
  background: var(--primary-action);
  color: var(--on-primary);
  font-size: 0.6875rem;
  font-weight: 700;
  display: grid;
  place-items: center;
}

.icon-btn {
  width: 2.75rem;
  height: 2.75rem;
  display: none;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  transition: border-color var(--t-control), color var(--t-control);
}

.icon-btn:hover {
  color: var(--text);
  border-color: var(--line-strong);
}

/* Mobile menu panel */

.menu-panel {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  background: var(--bg);
  /* The body is locked while this is open, so the panel owns the scroll on
     short viewports where the links do not all fit. */
  overflow-y: auto;
  overscroll-behavior: contain;
}

.menu-panel[data-open="true"] {
  display: block;
}

.menu-panel .menu-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-height: var(--header-height);
  border-bottom: 1px solid var(--line);
}

.menu-panel nav {
  display: grid;
  padding-block: var(--s4);
}

.menu-panel nav a {
  padding: var(--s4) 0;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
}

.menu-panel nav a[aria-current="page"] {
  color: var(--primary-text);
}

.menu-foot {
  display: grid;
  gap: var(--s3);
  padding-top: var(--s6);
}

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 3rem;
  padding: 0.75rem 1.375rem;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.9375rem;
  font-weight: 650;
  text-align: center;
  cursor: pointer;
  transition: background var(--t-control), border-color var(--t-control), color var(--t-control),
    box-shadow var(--t-control), transform var(--t-control);
}

.btn--primary {
  background: var(--primary-action);
  border-color: var(--primary-action);
  color: var(--on-primary);
}

.btn--primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.btn--secondary {
  background: var(--surface);
  border-color: var(--control-line);
}

.btn--secondary:hover {
  background: var(--surface-raised);
  border-color: var(--primary-line);
}

.btn--quiet {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--muted);
}

.btn--quiet:hover {
  color: var(--text);
  border-color: var(--control-line);
}

.btn--danger {
  background: transparent;
  border-color: transparent;
  color: var(--error);
  min-height: 2.75rem;
  padding-inline: var(--s3);
  font-size: 0.875rem;
}

.btn--danger:hover {
  background: rgba(255, 140, 153, 0.1);
}

.btn--sm {
  min-height: 2.75rem;
  padding: var(--s2) var(--s4);
  font-size: 0.875rem;
}

.btn--block {
  width: 100%;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.btn[data-busy="true"] {
  cursor: progress;
}

.link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 2.75rem;
  color: var(--primary-text);
  font-size: 0.9375rem;
  font-weight: 600;
  transition: color var(--t-control);
}

.link:hover {
  color: #ddd2ff;
}

.play-mark {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 999px;
  border: 1px solid currentColor;
  display: grid;
  place-items: center;
  flex: none;
}

.play-mark::after {
  content: "";
  border-left: 0.4rem solid currentColor;
  border-block: 0.26rem solid transparent;
  margin-left: 0.15rem;
}

/* ------------------------------------------------------ surfaces & chips */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  background: rgba(244, 243, 248, 0.03);
}

.chip--primary {
  color: var(--primary-text);
  border-color: var(--primary-line);
  background: var(--primary-soft);
}

.chip--plain {
  border-radius: var(--radius-control);
  text-transform: none;
  letter-spacing: 0.02em;
  font-family: var(--font-body);
  font-size: 0.75rem;
}

.chip-x {
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  line-height: 1;
  padding: 0 0 0 0.2rem;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity var(--t-control);
}

.chip-x:hover {
  opacity: 1;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

.works-with {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.45rem 0.65rem;
}

.works-with__label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
  flex: none;
}

.works-with__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}

.fw-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.22rem 0.55rem 0.22rem 0.28rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(244, 243, 248, 0.045);
  color: var(--text);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.2;
}

.fw-mark {
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 0.28rem;
  flex: none;
  display: block;
}

.works-with--compact .fw-badge {
  font-size: 0.6875rem;
  padding: 0.16rem 0.45rem 0.16rem 0.22rem;
  gap: 0.3rem;
}

.works-with--compact .fw-mark {
  width: 1rem;
  height: 1rem;
}

.works-with--compact .works-with__label {
  font-size: 0.5625rem;
  letter-spacing: 0.12em;
}

.edition {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8125rem;
  color: var(--muted);
}

.edition svg {
  width: 0.9375rem;
  height: 0.9375rem;
  flex: none;
  opacity: 0.8;
}

/* ---------------------------------------------------------------- notice */

.notice {
  border: 1px solid var(--line);
  border-left: 2px solid var(--primary);
  border-radius: var(--radius-control);
  background: var(--primary-soft);
  padding: var(--s3) var(--s4);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--muted);
}

.notice--quiet {
  border-left-color: var(--control-line);
  background: rgba(244, 243, 248, 0.03);
}

.notice--error {
  border-left-color: var(--error);
  background: rgba(255, 140, 153, 0.08);
  color: #ffd2d8;
}

.notice--warning {
  border-left-color: var(--warning);
  background: rgba(240, 203, 121, 0.08);
}

.notice--success {
  border-left-color: var(--success);
  background: rgba(102, 217, 175, 0.08);
}

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--s6);
  transform: translateX(-50%);
  z-index: 70;
  max-width: min(30rem, calc(100vw - 2rem));
  padding: var(--s3) var(--s4);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-left: 2px solid var(--primary);
  border-radius: var(--radius-control);
  font-size: 0.9375rem;
  box-shadow: 0 1rem 2.5rem rgba(0, 0, 0, 0.55);
}

.toast[data-kind="error"] {
  border-left-color: var(--error);
}

/* ------------------------------------------------------- inputs & search */

.field {
  display: grid;
  gap: var(--s2);
}

.field > label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--muted);
}

.input,
.select {
  width: 100%;
  min-height: 3rem;
  padding: 0.7rem 0.875rem;
  background: var(--surface);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: 0.9375rem;
}

.input::placeholder {
  color: var(--dim);
}

.input:focus-visible,
.select:focus-visible {
  outline: 2px solid var(--primary-text);
  outline-offset: 1px;
  border-color: var(--primary);
}

.input.is-invalid {
  border-color: var(--error);
}

.input.is-invalid:focus-visible {
  outline-color: var(--error);
}

.field-error {
  margin: 0;
  font-size: 0.8125rem;
  color: #ffd2d8;
}

.select {
  appearance: none;
  padding-right: 2.25rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1.05rem) 1.35rem, calc(100% - 0.7rem) 1.35rem;
  background-size: 0.35rem 0.35rem;
  background-repeat: no-repeat;
  cursor: pointer;
}

.search-field {
  position: relative;
  flex: 1;
  min-width: 15rem;
}

.search-field .input {
  padding-left: 2.6rem;
  padding-right: 2.6rem;
}

.search-field input[type="search"]::-webkit-search-decoration,
.search-field input[type="search"]::-webkit-search-cancel-button,
.search-field input[type="search"]::-webkit-search-results-button,
.search-field input[type="search"]::-webkit-search-results-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.search-field .search-ico {
  position: absolute;
  left: 0.9rem;
  top: 50%;
  translate: 0 -50%;
  width: 1.05rem;
  height: 1.05rem;
  color: var(--dim);
  pointer-events: none;
}

.search-clear {
  position: absolute;
  right: 0.35rem;
  top: 50%;
  translate: 0 -50%;
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-control);
  background: none;
  color: var(--muted);
  font-size: 1.125rem;
  cursor: pointer;
  transition: color var(--t-control), background var(--t-control);
}

.search-clear:hover {
  color: var(--text);
  background: var(--surface-raised);
}

.inline-form {
  display: flex;
  gap: var(--s2);
  align-items: flex-start;
}

.inline-form .input {
  min-width: 0;
}

/* ----------------------------------------------------------- product card */

.card-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(18.5rem, 1fr));
}

/* Curated rows (the home collection, related products) size to how many real products
   exist, capped at the three-across design default, so a short catalog reads as a
   deliberate row rather than a grid with gaps where products are missing. */
.card-grid--fit {
  grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr));
}

.pkg {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: border-color var(--t-control), transform var(--t-control), box-shadow var(--t-control);
}

.pkg:hover {
  border-color: var(--primary-line);
  transform: translateY(-2px);
  box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.4);
}

.pkg-media {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  background: linear-gradient(140deg, var(--surface-raised), var(--bg-deep));
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

.pkg-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.pkg-media .media-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  text-align: center;
  padding: var(--s4);
}

.pkg-body {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem 1rem 1.125rem;
  flex: 1;
}

.pkg-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
}

.pkg-title {
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.02em;
  min-width: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.6em;
}

.pkg-title a:hover {
  color: var(--primary-text);
}

.pkg-summary {
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.9em;
}

.pkg-foot {
  display: grid;
  gap: 0.85rem;
  margin-top: auto;
  padding-top: 0.25rem;
}

.price {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  white-space: nowrap;
}

.price b {
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.03em;
}

.price s {
  color: var(--dim);
  font-size: 0.875rem;
}

.price-off {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.4rem;
  border-radius: 0.25rem;
  background: rgba(255, 140, 153, 0.16);
  color: var(--error);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.price .unavailable {
  font-size: 1rem;
  font-weight: 600;
  color: var(--muted);
}

.price--split {
  display: grid;
  gap: 0.35rem;
  white-space: normal;
}

.price-line {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.price-line b {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.03em;
}

.price-line__ed {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

/* ------------------------------------------------------------------ hero */

.hero {
  position: relative;
  overflow: hidden;
  background: var(--bg-deep);
  /* Establishes the blend group the sculpture screens against. Without it the artwork
     would blend against an empty backdrop and disappear. */
  isolation: isolate;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* One glow only, behind the sculpture. A second wash used to sit at 12% 88%, but at
     0.05 alpha over a near-black backdrop it spans barely two 8-bit steps, so it landed
     as a banded smudge in the bottom-left corner instead of ambient light. */
  background: radial-gradient(48rem 34rem at 72% 42%, rgba(139, 92, 246, 0.16), rgba(139, 92, 246, 0) 62%);
  pointer-events: none;
}

/* Decorative perspective grid, faded out well before it reaches the headline. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 5.5rem 5.5rem;
  opacity: 0.5;
  -webkit-mask-image: radial-gradient(60% 55% at 74% 45%, #000 10%, transparent 78%);
  mask-image: radial-gradient(60% 55% at 74% 45%, #000 10%, transparent 78%);
  pointer-events: none;
}

.hero > .wrap {
  position: relative;
  max-width: var(--maxw-hero);
  display: grid;
  grid-template-rows: 1fr auto;
  /* Header, hero and the scroll cue fill the first screen; the collection divider
     strip below gives the fold a visible continuation. DESIGN.md section 6. */
  min-block-size: calc(100vh - var(--header-height) - 3.5rem);
  min-block-size: calc(100svh - var(--header-height) - 3.5rem);
  padding-block: clamp(2.5rem, 5vw, 4.5rem) var(--s4);
}

.hero-main {
  display: grid;
  grid-template-columns: minmax(0, 55fr) minmax(0, 45fr);
  align-items: center;
  gap: clamp(2rem, 4vw, 4rem);
}

.hero-copy {
  max-width: 40rem;
}

.hero-copy .h-hero {
  margin-bottom: var(--s6);
}

.hero-copy .lede {
  max-width: 34ch;
  font-size: 1.125rem;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-block: clamp(1.75rem, 3vw, 2.5rem) var(--s6);
}

.hero-actions .btn {
  min-height: 3.5rem;
  padding-inline: var(--s6);
  font-size: 1rem;
}

.hero-art {
  position: relative;
  justify-self: center;
  width: 100%;
  max-width: 38rem;
}

.hero-art img {
  width: 100%;
  height: auto;
  /* The artwork is emissive on near-black, so screen blending drops its surround
     exactly and costs no alpha channel. That is also what lets the two layers stack:
     each is black outside its own piece, and screen discards black, so neither layer
     ever occludes the other or leaves a hole when it drifts. */
  mix-blend-mode: screen;
}

/* The body layer stays in flow and sets the height; the floor and the satellite
   register on top of it at the same intrinsic size, so all three line up whatever the
   column width. */
.hero-layer--floor,
.hero-layer--piece {
  position: absolute;
  top: 0;
  left: 0;
}

/* Scroll invitation */

.scroll-cue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
  justify-self: center;
  min-height: 2.75rem;
  padding: var(--s2) var(--s4);
  border-radius: var(--radius-control);
  color: var(--dim);
  transition: color var(--t-control), opacity 260ms var(--ease), visibility 260ms;
}

.scroll-cue:hover {
  color: var(--muted);
}

/* The cue has said its piece once the page leaves the top. visibility keeps it out of
   the tab order while it is faded, and neither property affects layout, so the hero
   below it never shifts. site.js sets the attribute. */
.scroll-cue[data-hidden='true'] {
  /* The hero entry animation runs with fill-mode `both`, and a retained animation value
     beats a plain declaration, so the fade only lands once that animation is dropped. */
  animation: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.scroll-cue .mouse {
  width: 1.5rem;
  height: 2.375rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  display: grid;
  justify-items: center;
  padding-top: 0.45rem;
  /* The outline dips and brightens on the same beat as the wheel drop. */
  animation: cue-body 2.1s var(--ease) 900ms infinite;
}

.scroll-cue .wheel {
  width: 0.25rem;
  height: 0.25rem;
  border-radius: 999px;
  background: var(--primary);
  /* Starts after the hero entry has finished so the two motions never overlap. */
  animation: cue-wheel 2.1s var(--ease) 900ms infinite;
}

.scroll-cue .label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.scroll-cue .chev {
  width: 1rem;
  height: 1rem;
  animation: cue-chev 2.1s var(--ease) 900ms infinite;
}

@keyframes cue-wheel {
  0%, 70%, 100% { transform: translateY(0); opacity: 1; }
  35% { transform: translateY(0.75rem); opacity: 0.3; }
}

@keyframes cue-chev {
  0%, 70%, 100% { transform: translateY(0); opacity: 0.75; }
  35% { transform: translateY(0.3rem); opacity: 1; }
}

@keyframes cue-body {
  0%, 70%, 100% { transform: translateY(0); border-color: var(--line-strong); }
  35% { transform: translateY(0.35rem); border-color: var(--muted); }
}

/* Collection divider teaser */

/* Sits immediately under the hero so a strip of the collection is visible at the fold,
   which is what makes the first screen read as continuing rather than ending. */
.section--collection {
  padding-top: 0;
  /* The hero ends on --bg-deep and the page body is --bg. The gradient is painted on the
     full-bleed section, not on the centred .wrap, so the handover has no vertical seams
     where the wrap's max-width ends. */
  background-image: linear-gradient(180deg, var(--bg-deep), var(--bg));
  background-repeat: no-repeat;
  background-size: 100% 18rem;
}

.divider-teaser {
  display: flex;
  align-items: center;
  gap: var(--s6);
  padding-block: clamp(2rem, 4vw, 3.25rem) clamp(2.5rem, 5vw, 4.5rem);
}

.divider-teaser::before,
.divider-teaser::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line), transparent);
}

.divider-teaser span {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

/* --------------------------------------------------------- benefits strip */

.benefits {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-block: 1px solid var(--line);
}

.benefits > div {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s6) var(--s4);
  font-size: 0.9375rem;
  color: var(--muted);
}

.benefits > div + div {
  border-left: 1px solid var(--line);
}

.benefits svg {
  width: 1.375rem;
  height: 1.375rem;
  flex: none;
  color: var(--primary);
}

.benefits b {
  color: var(--text);
  font-weight: 650;
}

/* ------------------------------------------------------------ why panels */

.panel-grid {
  display: grid;
  gap: var(--s6);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.panel {
  padding: clamp(1.5rem, 2.5vw, 2rem);
  display: grid;
  gap: var(--s3);
  align-content: start;
}

.panel .ico {
  width: 2.5rem;
  height: 2.5rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-control);
  border: 1px solid var(--primary-line);
  background: var(--primary-soft);
  color: var(--primary);
  margin-bottom: var(--s1);
}

.panel .ico svg {
  width: 1.25rem;
  height: 1.25rem;
}

.panel h3 {
  font-size: 1.125rem;
}

.panel p {
  color: var(--muted);
  font-size: 0.9375rem;
}

/* -------------------------------------------------------------------- FAQ */

.faq {
  display: grid;
  gap: var(--s3);
  max-width: 54rem;
}

.faq details {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding-inline: var(--s6);
}

.faq summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding-block: var(--s4);
  font-size: 1.0625rem;
  font-weight: 650;
  min-height: 2.75rem;
  transition: color var(--t-control);
}

.faq summary:hover {
  color: var(--primary-text);
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
  border-right: 2px solid var(--primary);
  border-bottom: 2px solid var(--primary);
  rotate: 45deg;
  translate: 0 -0.2rem;
  transition: rotate var(--t-control);
}

.faq details[open] summary::after {
  rotate: -135deg;
  translate: 0 0.15rem;
}

/* Smooth open and close. The panel is the browser's own ::details-content box, so the
   answer stays in the accessibility tree and the control keeps working with scripting
   off. `interpolate-size` is scoped to the FAQ rather than :root so no other height
   transition on the page changes behaviour, and browsers without either feature ignore
   the whole block and keep the instant open/close they have today. */
@supports selector(details::details-content) {
  .faq details {
    interpolate-size: allow-keywords;
  }

  .faq details::details-content {
    block-size: 0;
    overflow: hidden;
    transition:
      block-size 280ms var(--ease),
      content-visibility 280ms allow-discrete;
  }

  .faq details[open]::details-content {
    block-size: auto;
  }
}

.faq details > *:not(summary) {
  color: var(--muted);
  font-size: 0.9375rem;
  padding-bottom: var(--s6);
  max-width: 68ch;
}

/* ------------------------------------------------------------ support CTA */

.cta-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s6);
  flex-wrap: wrap;
  padding: clamp(1.75rem, 3vw, 2.5rem);
  background: var(--bg-deep);
}

.cta-panel p {
  color: var(--muted);
  margin-top: var(--s2);
  max-width: 52ch;
}

/* --------------------------------------------------------------- catalog */

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  padding-top: var(--s6);
  font-size: 0.8125rem;
  color: var(--dim);
}

.breadcrumb a:hover {
  color: var(--primary-text);
}

.catalog {
  display: block;
}

.scripts-page {
  background: radial-gradient(ellipse 75% 28rem at 50% 0, rgba(139, 92, 246, 0.13), transparent);
}

.catalog-intro {
  text-align: center;
  padding-block: clamp(3rem, 6vw, 5rem) 2rem;
}

.catalog-eyebrow {
  margin-bottom: var(--s4);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  color: var(--primary-text);
}

.catalog-intro .h-page {
  font-size: clamp(2.5rem, 5.5vw, 4.75rem);
  line-height: 1.06;
  letter-spacing: -0.055em;
}

.catalog-intro .h-page span {
  color: var(--primary-text);
}

.catalog-intro .lede {
  margin-inline: auto;
}

.catalog .toolbar {
  max-width: 36rem;
  margin: 0 auto var(--s8);
}

.catalog .result-bar {
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
}

.catalog .pkg-media img {
  transition: transform 400ms var(--ease);
}

@media (prefers-reduced-motion: no-preference) {
  .catalog .pkg:hover .pkg-media img {
    transform: scale(1.035);
  }
}

.catalog .empty-state {
  grid-column: 1 / -1;
}

.catalog .card-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.toolbar {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: var(--s4);
}

.toolbar .select {
  width: auto;
  min-width: 12rem;
}

.result-bar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  margin-bottom: var(--s6);
  min-height: 2.25rem;
}

.result-count {
  font-size: 0.875rem;
  color: var(--muted);
}

.empty-state {
  display: grid;
  gap: var(--s4);
  justify-items: start;
  padding: clamp(2rem, 4vw, 3rem);
}

.empty-state h2 {
  font-size: 1.375rem;
}

.empty-state p {
  color: var(--muted);
  max-width: 52ch;
}

.empty-state .row {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
}

/* Reserved-size skeletons */

.skeleton {
  border-radius: var(--radius-card);
  background: var(--surface);
  border: 1px solid var(--line);
  overflow: hidden;
}

.skeleton .bar {
  height: 0.75rem;
  border-radius: 4px;
  background: var(--surface-raised);
  margin: var(--s3) var(--s4);
}

.skeleton .media {
  aspect-ratio: 16 / 9;
  background: var(--surface-raised);
}

.skeleton[data-shimmer] .media,
.skeleton[data-shimmer] .bar {
  animation: shimmer 1.4s linear infinite;
  background: linear-gradient(90deg, var(--surface-raised), #221e33, var(--surface-raised));
  background-size: 200% 100%;
}

@keyframes shimmer {
  to { background-position: -200% 0; }
}

/* --------------------------------------------------------------- product */

.product {
  display: grid;
  grid-template-columns: minmax(0, 60fr) minmax(0, 40fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: start;
}

/* The media column stays pinned while the taller buybox column scrolls past it, so the
   screenshots remain visible while the buyer reads the specs. It releases at the end of
   the grid row on its own; no script is involved. */
.product-media {
  position: sticky;
  top: calc(var(--header-height) + var(--s4));
  /* Sticky silently stops working when the element is taller than the space below the
     header, which is the case on short laptop screens. */
  max-height: calc(100vh - var(--header-height) - var(--s8));
  max-height: calc(100svh - var(--header-height) - var(--s8));
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: var(--s3);
}

.gallery-main {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: linear-gradient(140deg, var(--surface-raised), var(--bg-deep));
  overflow: hidden;
  touch-action: pan-y pinch-zoom;
  user-select: none;
}

.gallery-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}

.gallery-thumbs {
  display: flex;
  gap: var(--s3);
  overflow-x: auto;
  padding-bottom: var(--s2);  overscroll-behavior-x: contain;
}

.gallery-thumbs .media-thumb,
.lightbox-thumbs .media-thumb {
  flex: 0 0 auto;
  width: 7rem;
  aspect-ratio: 16 / 9;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  overflow: hidden;
  cursor: pointer;
  position: relative;
  transition: border-color var(--t-control), transform var(--t-control), opacity var(--t-control);
}

.gallery-thumbs .media-thumb:hover {
  border-color: var(--primary-line);
  transform: translateY(-2px);
}

.gallery-thumbs .media-thumb[aria-current="true"],
.lightbox-thumbs .media-thumb[aria-current="true"] {
  border-color: var(--primary);
  outline: 1px solid var(--primary);
  opacity: 1;
}

.lightbox-thumbs .media-thumb {
  opacity: 0.55;
}

.lightbox-thumbs .media-thumb:hover {
  opacity: 1;
}

.gallery-thumbs img,
.lightbox-thumbs img,
.media-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media-thumb--video::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0.45rem 0 0.45rem 0.75rem;
  border-color: transparent transparent transparent #fff;
  transform: translate(-30%, -50%);
  filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.8));
  pointer-events: none;
}

.gallery-zoom {
  position: absolute;
  right: var(--s3);
  top: var(--s3);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: rgba(7, 7, 13, 0.78);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

.gallery-zoom svg {
  width: 0.875rem;
  height: 0.875rem;
}

/* ------------------------------------------------------- screenshot viewer */

.lightbox {
  position: fixed;
  inset: 0;
  /* Above the header (50), the mobile menu (60) and the basket drawer (70), below the
     skip link (100). */
  z-index: 90;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  padding: 0;
  background: rgba(7, 7, 13, 0.88);
  backdrop-filter: blur(10px);
}

.lightbox[hidden] {
  display: none;
}

.lightbox-stage {
  position: relative;
  width: 100%;
  min-height: 0;
  overflow: hidden;
  /* Native phone pinch zoom stays enabled; one-finger horizontal motion swipes. */
  touch-action: pan-y pinch-zoom;
  user-select: none;
}

.lightbox-stage.is-native-zoomed { touch-action: auto; }
.gallery-main.is-swipeable,
.lightbox-stage.is-swipeable { cursor: grab; }
.gallery-main.is-dragging,
.lightbox-stage.is-dragging { cursor: grabbing; }

.media-track {
  display: flex;
  height: 100%;
}

.media-slide {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  min-width: 100%;
  display: grid;
  place-items: center;
}

.media-slide img,
.media-slide iframe {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border: 0;
  user-select: none;
  pointer-events: auto;
}

.gallery-main.is-dragging .media-track,
.lightbox-stage.is-dragging .media-track {
  transition: none;
}

.lightbox-thumbs {
  display: flex;
  gap: var(--s3);
  align-items: center;
  overflow-x: auto;
  padding: var(--s3) var(--s4) var(--s4);
  overscroll-behavior-x: contain;
  list-style: none;
  margin: 0;
}

.lightbox-thumbs li {
  flex: 0 0 auto;
  display: flex;
}

.lightbox-thumbs[hidden] {
  display: none;
}

.gallery-zoom:hover {
  border-color: var(--primary-line);
  background: var(--surface-raised);
}

.lightbox-btn {
  position: absolute;
  z-index: 1;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--t-control), background var(--t-control);
}

.lightbox-btn:hover {
  border-color: var(--primary-line);
  background: var(--surface-raised);
}

.lightbox-btn svg {
  width: 1.25rem;
  height: 1.25rem;
}

.lightbox-btn[hidden] {
  display: none;
}

.lightbox-btn:disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}

.lightbox-close {
  top: var(--s4);
  right: var(--s4);
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-control);
}

.lightbox-step {
  top: 50%;
  transform: translateY(-50%);
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 999px;
}

.lightbox-step--prev {
  left: clamp(0.5rem, 2vw, 1.5rem);
}

.lightbox-step--next {
  right: clamp(0.5rem, 2vw, 1.5rem);
}

.buybox {
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  display: grid;
  gap: var(--s4);
}

.buybox .price b {
  font-size: 2.125rem;
}

.buybox h1 {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
}

.buybox p.summary {
  color: var(--muted);
  font-size: 0.9375rem;
}

.editions {
  display: grid;
  gap: var(--s2);
}

.edition-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  width: 100%;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  font-size: 0.9375rem;
  transition: border-color var(--t-control), background var(--t-control);
}

.edition-option:hover {
  border-color: var(--control-line);
}

.edition-option[data-selected="true"] {
  border-color: var(--primary);
  background: var(--primary-soft);
}

.spec-list {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.spec-list th,
.spec-list td {
  text-align: left;
  vertical-align: top;
  padding: var(--s3) var(--s2);
  border-bottom: 1px solid var(--line);
}

.spec-list th {
  width: 13rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

.spec-list td {
  color: var(--muted);
}

.tabs {
  display: flex;
  gap: var(--s1);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s8);
  overflow-x: auto;  overscroll-behavior-x: contain;
}

.tabs button {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  font: inherit;
  font-weight: 650;
  font-size: 0.9375rem;
  padding: var(--s4) var(--s4);
  min-height: 2.75rem;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--t-control), border-color var(--t-control);
}

.tabs button:hover {
  color: var(--text);
}

.tabs button[aria-selected="true"] {
  color: var(--text);
  border-bottom-color: var(--primary);
}

.prose {
  color: var(--muted);
  max-width: 47.5rem;
  display: grid;
  gap: var(--s4);
  font-size: 1rem;
  line-height: 1.65;
}

.prose h3 {
  color: var(--text);
  font-size: 1.1875rem;
  margin-top: var(--s4);
}

.prose h3:first-child {
  margin-top: 0;
}

.prose ul,
.prose ol {
  margin: 0;
  padding-left: 1.25rem;
  display: grid;
  gap: var(--s2);
}

.prose code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  background: var(--surface-raised);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.1rem 0.35rem;
  color: var(--primary-text);
}

.prose strong {
  color: var(--text);
}

.prose a {
  color: var(--primary-text);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* ---------------------------------------------------------------- basket */

.basket {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 22rem;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
}

.line {
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr) auto;
  gap: var(--s4);
  padding: var(--s4);
  align-items: start;
}

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

.line[data-busy="true"] {
  opacity: 0.55;
}

.line-media {
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: linear-gradient(140deg, var(--surface-raised), var(--bg-deep));
  overflow: hidden;
}

.line-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.line h3 {
  font-size: 1rem;
  line-height: 1.35;
  margin-bottom: var(--s2);
  overflow-wrap: anywhere;
}

.line-side {
  display: grid;
  gap: var(--s2);
  justify-items: end;
}

.qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  overflow: hidden;
}

.qty button {
  width: 2.25rem;
  height: 2.25rem;
  border: 0;
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: background var(--t-control);
}

.qty button:not([disabled]):hover {
  background: var(--primary-soft);
}

.qty button[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

.qty span {
  min-width: 2.25rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.875rem;
}

.qty-fixed {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--dim);
}

.summary {
  padding: var(--s6);
  display: grid;
  gap: var(--s4);
  position: sticky;
  top: calc(var(--header-height) + var(--s4));
}

.summary dl {
  margin: 0;
  display: grid;
  gap: var(--s3);
}

.summary dl > div {
  display: flex;
  justify-content: space-between;
  gap: var(--s4);
  font-size: 0.9375rem;
  color: var(--muted);
}

.summary .total {
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
  color: var(--text);
  font-weight: 700;
}

.summary .total > span:last-child {
  font-size: 1.375rem;
}

.steps {
  display: grid;
  gap: var(--s3);
  margin: 0;
  padding: 0;
  counter-reset: step;
}

.steps li {
  list-style: none;
  display: grid;
  grid-template-columns: 1.625rem minmax(0, 1fr);
  gap: var(--s3);
  align-items: start;
  font-size: 0.875rem;
  color: var(--muted);
}

.steps li::before {
  counter-increment: step;
  content: counter(step);
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--dim);
}

.steps li[data-state="done"] {
  color: var(--text);
}

.steps li[data-state="done"]::before {
  content: "\2713";
  background: var(--primary-action);
  border-color: var(--primary-action);
  color: var(--on-primary);
}

/* ---------------------------------------------------- status / 404 panel */

.status-panel {
  max-width: 40rem;
  margin-inline: auto;
  padding: clamp(2rem, 4vw, 3rem);
  display: grid;
  gap: var(--s6);
  justify-items: center;
  text-align: center;
}

.status-panel .ring {
  width: 4rem;
  height: 4rem;
  border-radius: 999px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-strong);
  background: rgba(244, 243, 248, 0.04);
  color: var(--muted);
}

.status-panel .ring svg {
  width: 1.625rem;
  height: 1.625rem;
}

.status-panel[data-tone="success"] .ring {
  border-color: rgba(102, 217, 175, 0.4);
  background: rgba(102, 217, 175, 0.1);
  color: var(--success);
}

.status-panel[data-tone="warning"] .ring {
  border-color: rgba(240, 203, 121, 0.4);
  background: rgba(240, 203, 121, 0.1);
  color: var(--warning);
}

.status-panel .lede {
  margin-inline: auto;
}

.status-panel .row {
  display: flex;
  gap: var(--s3);
  flex-wrap: wrap;
  justify-content: center;
}

.status-panel .spec-list,
.status-panel .steps,
.status-panel .notice {
  text-align: left;
  width: 100%;
}

/* --------------------------------------------------------------- support */

.support-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s6);
}

.support-card {
  padding: clamp(1.5rem, 2.5vw, 2rem);
  display: grid;
  gap: var(--s3);
  align-content: start;
}

.support-card p {
  color: var(--muted);
  font-size: 0.9375rem;
}

.support-card .btn,
.support-card .link {
  justify-self: start;
  margin-top: var(--s1);
}

/* ------------------------------------ scrollable table (compatibility) */

.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

/* ---------------------------------------------------------------- footer */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--bg-deep);
  padding-block: var(--s16) var(--s8);
  margin-top: var(--s8);
}

.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
  gap: var(--s8);
}

.footer-top p {
  color: var(--muted);
  font-size: 0.9375rem;
  margin-top: var(--s4);
  max-width: 34ch;
}

.footer-col h2 {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: var(--s3);
}

.footer-col a {
  display: block;
  padding: var(--s2) 0;
  color: var(--muted);
  font-size: 0.9375rem;
  transition: color var(--t-control), translate var(--t-control);
}

.footer-col a:hover {
  translate: 2px 0;
  color: var(--primary-text);
}

.turnstile-slot {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 40;
}

.footer-legal {
  margin-top: var(--s12);
  padding-top: var(--s6);
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  font-size: 0.8125rem;
  color: var(--dim);
  line-height: 1.6;
}

.footer-legal strong {
  color: var(--muted);
  font-weight: 600;
}

/* ---------------------------------------------------------------- motion
   DESIGN.md section 16 is the budget for everything below: control states move in
   150-200ms, entry movement runs 300-450ms over at most 12px and plays once per
   element, and nothing ornamental loops forever. The page is fully readable before
   any of it runs, and the reduced-motion block at the end of this file switches all
   of it off. */

/* Shared entry: fade up from --rise. The keyframe deliberately has no `to`, so each
   element animates towards whatever its own resting style already is. */
@keyframes fs-rise {
  from {
    opacity: 0;
    transform: translateY(var(--rise, 12px));
  }
}

@keyframes fs-fade {
  from {
    opacity: 0;
  }
}

/* Hero intro. Runs on load rather than on scroll, because the hero is already on
   screen; total run is under 700ms so it never holds up reading or the first click. */
.hero-copy > *,
.scroll-cue {
  animation: fs-rise 420ms var(--ease) both;
}

/* The image and not its wrapper: an animated opacity on .hero-art would make it a
   stacking context and the sculpture's screen blend would stop reaching the hero.
   The idle float uses the `translate` property rather than `transform` so it
   composites next to fs-rise, which owns `transform` on this same element. */
/* Both layers fade in together, then drift apart. The cycles are deliberately not a
   whole-number ratio, so the two pieces drift in and out of phase instead of locking
   into one shared bob that would look like a single image moving.

   The entry is fs-fade, not fs-rise, specifically so that `transform` stays free for
   the float below. Sharing one element between a translate-based entry and a
   translate-based loop forces one of them onto the `translate` property, which needs
   Chrome/Edge 104+ and fails silently on anything older. */
.hero-art img:not(.hero-layer--floor) {
  animation:
    fs-fade 420ms var(--ease) 60ms both,
    hero-float var(--float-cycle) ease-in-out var(--float-offset) infinite;
}

/* The ground plane is anchored. It fades in with the rest and then never moves: a floor
   that drifts with the object turns the hover into the whole scene sliding, which is the
   opposite of the effect. */
.hero-layer--floor {
  animation: fs-fade 420ms var(--ease) 60ms both;
  /* Remove the floor asset's hard horizon rather than duplicating its glow. */
  -webkit-mask-image: linear-gradient(transparent 74%, #000 82%);
  mask-image: linear-gradient(transparent 74%, #000 82%);
}

.hero-layer--body {
  --float-cycle: 6s;
  --float-offset: 700ms;
  --float-rise: -1.25rem;
  /* The silhouette mask keeps the lower bar crisp while fading the background
     above its horizontal cut. A full-width gradient would erase the bar too. */
  -webkit-mask-image: url("../img/hero-body-mask.svg");
  mask-image: url("../img/hero-body-mask.svg");
  -webkit-mask-size: 100% 100%;
  mask-size: 100% 100%;
}

/* The satellite is a small object, so it carries the larger travel and the faster
   cycle - that contrast is what sells the two as separate things. */
.hero-layer--piece {
  --float-cycle: 4.2s;
  --float-offset: 1100ms;
  --float-rise: -2.25rem;
}

/* 20px on the body. Below roughly this amplitude the drift reads as a rendering
   artifact rather than as deliberate hovering, so there is no point running it
   smaller. */
@keyframes hero-float {
  50% {
    transform: translateY(var(--float-rise, -1.25rem));
  }
}

.hero-copy .h-hero {
  animation-delay: 70ms;
}

.hero-copy .lede {
  animation-delay: 140ms;
}

.hero-copy .hero-actions {
  animation-delay: 210ms;
}

.hero-copy .mono-line {
  animation-delay: 280ms;
}

.scroll-cue {
  animation-delay: 360ms;
}

/* Scroll reveal. site.js arms only the elements that are below the first screen, so
   nothing that has already been painted can flash away, and it never arms anything at
   all without IntersectionObserver or under reduced motion. */
[data-reveal="out"] {
  opacity: 0;
}

[data-reveal="in"] {
  animation: fs-rise 420ms var(--ease) var(--reveal-delay, 0ms) both;
}

/* Arrow affordances. Scoped to a bare trailing span so icon marks such as .play-mark
   and inline SVGs stay put. */
.btn > span[aria-hidden="true"]:not([class]):last-child,
.link > span[aria-hidden="true"]:not([class]):last-child {
  transition: translate var(--t-control);
}

.btn:hover > span[aria-hidden="true"]:not([class]):last-child,
.link:hover > span[aria-hidden="true"]:not([class]):last-child {
  translate: 3px 0;
}

.btn--primary:hover {
  box-shadow: 0 0.5rem 1.5rem rgba(139, 92, 246, 0.28);
}

.btn:not([disabled]):not([aria-disabled="true"]):active {
  transform: translateY(1px);
}

/* Basket count. Fires on a real change so the header confirms an add that happened
   further down the page. */
@keyframes fs-pop {
  0% { scale: 1; }
  40% { scale: 1.3; }
  100% { scale: 1; }
}

.header-link .count[data-bump] {
  animation: fs-pop 380ms var(--ease);
}

.toast[data-enter] {
  animation: fs-toast 300ms var(--ease) both;
}

/* The toast is centred with a transform, so the keyframe has to carry that offset. */
@keyframes fs-toast {
  from {
    opacity: 0;
    transform: translate(-50%, 0.75rem);
  }
}

.menu-panel[data-open="true"] {
  animation: fs-fade 200ms var(--ease) both;
}

.menu-panel[data-open="true"] nav a {
  --rise: 8px;
  animation: fs-rise 300ms var(--ease) both;
}

.menu-panel[data-open="true"] nav a:nth-child(2) {
  animation-delay: 45ms;
}

.menu-panel[data-open="true"] nav a:nth-child(3) {
  animation-delay: 90ms;
}

.menu-panel[data-open="true"] nav a:nth-child(4) {
  animation-delay: 135ms;
}

/* Disclosure content, on open only: `details` shows it immediately either way, so this
   is decoration over an already-working control. Where the panel itself can be
   transitioned (the ::details-content block in the FAQ section), that growth is the
   motion and this rise would fight it inside the clipped box, so it is skipped. */
@supports not selector(details::details-content) {
  .faq details[open] > *:not(summary) {
    --rise: 8px;
    animation: fs-rise 300ms var(--ease) both;
  }
}

/* ----------------------------------------------------------- responsive */

@media (max-width: 74.9375rem) {
  /* Tablet: the split hero only survives while the headline and sculpture both fit. */
  .hero-main {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
    gap: var(--s8);
  }

  .hero-copy {
    max-width: 44rem;
  }

  .hero-copy .lede,
  .hero-actions,
  .hero-copy .mono-line {
    margin-inline: auto;
  }

  .hero-actions {
    justify-content: center;
  }

  .hero-art {
    order: 2;
    max-width: 26rem;
  }

  .hero > .wrap {
    min-block-size: 0;
  }

  .hero::after {
    -webkit-mask-image: radial-gradient(75% 60% at 50% 60%, #000 5%, transparent 80%);
    mask-image: radial-gradient(75% 60% at 50% 60%, #000 5%, transparent 80%);
  }

  .card-grid--fit {
    grid-template-columns: repeat(auto-fill, minmax(18.5rem, 1fr));
  }

  .basket {
    grid-template-columns: minmax(0, 1fr) 19rem;
  }
}

@media (max-width: 63.9375rem) {
  :root {
    --header-height: 4.5rem;
  }

  .nav,
  .header-link--account,
  .currency-label {
    display: none;
  }

  /* The nav carries the auto margins that push the actions right on desktop. It is
     hidden here, so the actions have to claim the free space themselves. */
  .header-actions {
    margin-left: auto;
  }

  .icon-btn {
    display: grid;
  }

  .catalog,
  .product,
  .basket {
    grid-template-columns: minmax(0, 1fr);
  }

  .catalog .card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .buybox,
  .product-media,
  .summary {
    position: static;
  }

  .product-media {
    max-height: none;
    display: block;
  }

  .gallery-thumbs {
    margin-top: var(--s3);
  }

  .support-grid,
  .benefits {
    grid-template-columns: minmax(0, 1fr);
  }

  .benefits > div + div {
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  .footer-top {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 47.9375rem) {
  .lightbox-step--prev {
    left: 0.5rem;
  }

  .lightbox-step--next {
    right: 0.5rem;
  }

  :root {
    --header-height: 4rem;
    --gutter: 1.25rem;
  }

  /* Brand, basket and the menu button are the whole header row here, and at 360px and
     under they were wide enough together to push the menu button past the gutter. */
  .site-header > .wrap {
    gap: var(--s3);
  }

  .brand {
    font-size: 1.0625rem;
  }

  .header-link {
    padding-inline: var(--s2);
  }

  body {
    font-size: 1rem;
  }

  .h-hero {
    font-size: clamp(2.25rem, 9vw, 2.75rem);
  }

  .hero > .wrap {
    padding-block: var(--s8) var(--s3);
  }

  /* The framework list is one continuous credential, so it reads as a single line at
     every phone width rather than breaking after the third name. */
  .hero-copy .mono-line {
    white-space: nowrap;
    font-size: clamp(0.4rem, 2.35vw, 0.8125rem);
    letter-spacing: 0.08em;
  }

  .hero-copy .mono-line span {
    margin-inline: 0.4em;
  }

  .hero-art {
    max-width: 18.75rem;
  }

  .hero-actions .btn {
    width: 100%;
  }

  .product,
  .card-grid {
    gap: var(--s4);
  }

  .catalog .card-grid,
  .card-grid--fit {
    grid-template-columns: minmax(0, 1fr);
  }

  .line {
    grid-template-columns: 4.5rem minmax(0, 1fr);
  }

  .line-side {
    grid-column: 1 / -1;
    justify-items: stretch;
    grid-auto-flow: column;
    align-items: center;
    justify-content: space-between;
  }

  .footer-top {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s6);
  }

  .spec-list th {
    width: auto;
  }

  .cta-panel .btn {
    width: 100%;
  }
}

@media (max-width: 22.5rem) {
  :root {
    --gutter: 1rem;
  }
}

/* Short landscape screens: shed vertical space before shrinking type. */
@media (max-height: 44rem) and (min-width: 48rem) {
  .hero > .wrap {
    min-block-size: 0;
    padding-block: var(--s8);
  }

  .scroll-cue {
    display: none;
  }
}

/* Under 360px the basket word no longer fits beside the brand and the menu button.
   The icon carries the meaning; the word stays in the accessibility tree. */
@media (max-width: 22.5rem) {
  .header-link[href$="basket.html"] > span:not(.count) {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* Deliberate exemption, requested by the site owner: the two ambient hero loops keep
     running even when the visitor has asked for reduced motion. Everything else above
     still stops - entry animations, scroll reveals, transitions and smooth scrolling.

     This is a knowing departure from WCAG 2.3.3, which expects looping decorative
     movement to stop for visitors who set this preference. Higher specificity than the
     universal rule above, so these win despite both carrying !important. To restore
     standard behaviour, delete this block and nothing else. */
  .hero-art img:not(.hero-layer--floor) {
    animation:
      fs-fade 420ms var(--ease) 60ms both,
      hero-float var(--float-cycle) ease-in-out var(--float-offset) infinite !important;
  }

  .scroll-cue .mouse {
    animation: cue-body 2.1s var(--ease) 900ms infinite !important;
  }

  .scroll-cue .wheel {
    animation: cue-wheel 2.1s var(--ease) 900ms infinite !important;
  }

  .scroll-cue .chev {
    animation: cue-chev 2.1s var(--ease) 900ms infinite !important;
  }

  /* site.js does not arm reveals under reduced motion; this covers the case where the
     preference is turned on after the page has loaded. */
  [data-reveal] {
    opacity: 1 !important;
  }

  .skeleton[data-shimmer] .media,
  .skeleton[data-shimmer] .bar {
    background: var(--surface-raised);
  }
}

/* Printing never scrolls, so revealed-on-scroll sections have to come back. */
@media print {
  [data-reveal] {
    opacity: 1 !important;
    animation: none !important;
  }
}

@media (forced-colors: active) {
  .btn,
  .card,
  .chip,
  .fw-badge {
    border: 1px solid;
  }
}
