/* ===================================================================
   SimplySolid3D — "Print room"

   Light, warm and product-first. The palette comes straight out of the
   brand: the teal is the colour of the SS3D cube mark, the paper tone
   matches the light studio backgrounds the models are photographed on.

   The one dark passage on the page is the OrbitStudio band, and it uses
   orbitstudio3d.com's own tokens verbatim so the two sites read as
   family rather than as strangers.

   Page order follows what the catalogue data says, not a guess:
   the featured model, then the most printed, then the most complex.
   =================================================================== */

:root {
  --paper:      #F5F3EF;
  --paper-2:    #EDEAE3;
  --paper-3:    #E4E0D6;
  --ink:        #14181C;
  /* Both of these clear WCAG AA (4.5:1) on --paper AND --paper-2. The old
     faint grey sat at 2.8:1, which made every small label on the page too
     light to read comfortably. Hierarchy now comes from size, weight and
     letter-spacing rather than from washing text out. */
  --ink-muted:  #46525A;
  --ink-faint:  #5E6B73;
  --teal:       #1B5A73;
  --teal-deep:  #123E4F;
  --teal-tint:  #E3ECEF;
  --line:       rgba(20, 24, 28, .12);
  --line-soft:  rgba(20, 24, 28, .07);
  --makerworld: #00A680;

  /* OrbitStudio's own values, so the dark band is genuinely the same colour */
  --night:  #05070B;
  --night-2:#0A0E16;
  --bone:   #E9E6DF;
  --silver: #B9C0CA;
  --signal: #5AA9E6;

  --sans: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --r-btn:  6px;
  --r-card: 14px;
  --wrap:   1160px;

  --lift:   0 22px 40px -28px rgba(20, 24, 28, .5);
  --ease:   cubic-bezier(.22, .61, .36, 1);

  color-scheme: light;
}

/* ------------------------------------------------------------------ base */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  font-weight: 300;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, video { max-width: 100%; height: auto; display: block; }

a { color: inherit; }

h1, h2, h3 {
  margin: 0;
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -.025em;
  text-wrap: balance;
}

/* Three sizes that are clearly apart, rather than five that nearly touch. */
h1 { font-size: clamp(2.3rem, 1.3rem + 3.7vw, 4.2rem); letter-spacing: -.032em; line-height: 1.02; }
h2 { font-size: clamp(1.95rem, 1.2rem + 2.7vw, 3.3rem); font-weight: 500; letter-spacing: -.03em; }
h3 { font-size: 1.3rem; letter-spacing: -.015em; }

p { margin: 0 0 1rem; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: 24px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.nb { white-space: nowrap; }

.skip {
  position: absolute; left: 0; top: -100px;
  background: var(--teal); color: var(--paper);
  padding: 12px 20px; border-radius: 0 0 var(--r-btn) 0;
  font-family: var(--mono); font-size: .82rem; text-decoration: none;
  z-index: 100;
}
.skip:focus { top: 0; }

:where(a, button, input):focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
}

/* The mono label that opens every section. Small, wide, quiet. */
.eyebrow {
  margin: 0 0 1rem;
  font-family: var(--mono);
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.eyebrow-signal { color: var(--signal); }

/* the print-index in front of each section label */
.eyebrow-no {
  color: var(--teal);
  margin-right: 12px;
}
.eyebrow-signal .eyebrow-no { color: var(--signal); }

.lead {
  max-width: 46ch;
  font-size: 1.12rem;
  color: var(--ink-muted);
}
.lead-sm { font-size: 1.05rem; }

.text-link {
  font-family: var(--mono);
  font-size: .84rem;
  letter-spacing: .02em;
  text-decoration: none;
  color: var(--teal);
  border-bottom: 1px solid rgba(27, 90, 115, .3);
  padding-bottom: 2px;
  transition: border-color .16s ease, color .16s ease;
}
.text-link:hover { color: var(--teal-deep); border-color: var(--teal); }

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 24px;
  border: 1px solid transparent;
  border-radius: var(--r-btn);
  font-family: var(--sans);
  font-size: .95rem;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .16s ease, border-color .16s ease, color .16s ease;
}
.btn:active { transform: translateY(1px); }
.btn-sm { padding: 9px 16px; font-size: .86rem; }

.btn-teal   { background: var(--teal); color: var(--paper); }
.btn-teal:hover { background: var(--teal-deep); }

.btn-signal { background: var(--signal); color: #04121F; }
.btn-signal:hover { background: #8FCDF6; }

.btn-mw     { background: var(--makerworld); color: #02231C; }
.btn-mw:hover { background: #00BF93; }

/* ---------------------------------------------------------------- header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(160%) blur(10px);
  border-bottom: 1px solid var(--line-soft);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  min-height: 68px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  font-weight: 600;
  letter-spacing: -.015em;
}
.brand-name { font-size: 1.05rem; }

.site-nav {
  display: flex;
  align-items: center;
  gap: 24px;
  font-size: .93rem;
}
.site-nav a { text-decoration: none; color: var(--ink-muted); transition: color .16s ease; }
.site-nav a:not(.btn) {
  background: linear-gradient(var(--teal), var(--teal)) left bottom / 0 1px no-repeat;
  padding-bottom: 3px;
  transition: color .16s ease, background-size .22s var(--ease);
}
.site-nav a:not(.btn):hover { background-size: 100% 1px; }
.site-nav a:hover { color: var(--ink); }
.site-nav .btn { color: var(--paper); }

@media (max-width: 860px) {
  .site-nav a:not(.btn) { display: none; }
}

/* ------------------------------------------------------------------ hero */

.hero { padding: clamp(3rem, 1.5rem + 6vw, 6.5rem) 0 clamp(1.5rem, .5rem + 3vw, 3rem); }

.hero-grid {
  display: grid;
  grid-template-columns: 1.02fr .98fr;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  align-items: center;
}
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
}

.hero-copy h1 { margin-bottom: 1.2rem; max-width: 16ch; }
.hero-copy .lead { margin-bottom: 2rem; }

/* The duo: the model MakerWorld featured, with the one everyone downloads
   laid over its corner. Two real reasons to click, labelled as such. The
   paper mats also rescue photos shot on dark or coloured backgrounds,
   which would otherwise punch holes in a light page. */
.hero-duo {
  position: relative;
  padding: 0 7% 17% 0;
}

.duo {
  display: block;
  padding: 10px;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  box-shadow: var(--lift);
  text-decoration: none;
  transition: transform .24s var(--ease), box-shadow .24s ease;
}
.duo img { border-radius: 8px; width: 100%; }

/* The chip hangs off the bottom-right corner: the main card's label sits
   bottom-left and must never be covered, it is the reason the card is there. */
.duo-main { transform: rotate(-.7deg); }
.duo-chip {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 43%;
  padding: 8px;
  transform: rotate(2.4deg);
}

.duo:hover {
  transform: rotate(0deg) translateY(-4px);
  box-shadow: 0 28px 44px -28px rgba(20, 24, 28, .55);
}

.duo-label {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 10px 4px 3px;
}
.duo-tag {
  font-family: var(--mono);
  font-size: .64rem;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--teal);
}
.duo-name {
  font-size: .92rem;
  font-weight: 500;
  color: var(--ink);
}

@media (max-width: 900px) {
  .hero-duo { max-width: 560px; margin-inline: auto; }
}
/* On a phone the label under the main photo runs close to the chip's edge;
   a narrower chip keeps a clear gap between them. */
@media (max-width: 480px) {
  .duo-chip { width: 38%; }
}

/* ----------------------------------------------------------------- stats */

/* The rule above the numbers is a pseudo-element rather than a border, so the
   motion section further down can draw it left to right. */
.stats {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, auto);
  justify-content: start;
  gap: clamp(1.4rem, .6rem + 2.4vw, 2.8rem);
  margin: 0 0 2.2rem;
  padding: 1.4rem 0 0;
  list-style: none;
}
.stats::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--line);
  transform-origin: left center;
}
@media (max-width: 560px) {
  .stats { grid-template-columns: repeat(2, 1fr); gap: 1.2rem 1.4rem; }
}

.stats li { display: flex; flex-direction: column; gap: 3px; }

.stat-num {
  font-size: 1.7rem;
  font-weight: 600;
  letter-spacing: -.025em;
  font-variant-numeric: tabular-nums;
}
.stat-static { font-size: 1.36rem; }

.stat-label {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ------------------------------------------------------------ signup form */

.signup { margin: 0; }

.signup-inline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  max-width: 460px;
}
.signup-inline .form-note { grid-column: 1 / -1; }

.signup input[type="email"] {
  width: 100%;
  padding: 14px 16px;
  background: #FFFDFA;
  border: 1px solid var(--line);
  border-radius: var(--r-btn);
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 300;
  color: var(--ink);
  transition: border-color .16s ease;
}
.signup input[type="email"]::placeholder { color: var(--ink-faint); }
.signup input[type="email"]:hover { border-color: rgba(20, 24, 28, .22); }

.signup[data-state="error"] input[type="email"] { border-color: #B0432F; }

/* Honeypot. Off-screen rather than display:none, because some bots skip
   fields that are hidden the obvious way. */
.pot {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  opacity: 0;
}

.form-note {
  margin: .7rem 0 0;
  font-size: .85rem;
  color: var(--ink-faint);
}
.signup[data-state="error"] .form-note { color: #B0432F; }
.signup[data-state="done"]  .form-note { color: var(--teal); }

.signup[data-state="busy"] button { opacity: .6; pointer-events: none; }

@media (max-width: 480px) {
  .signup-inline { grid-template-columns: 1fr; }
}

/* -------------------------------------------------------------- sections */

.section { padding: clamp(4.5rem, 2.5rem + 6vw, 8rem) 0; }
.section-tint { background: var(--paper-2); }

.section-title { margin-bottom: 1.2rem; }

/* Title on the left, a one-line explanation hanging off the right edge.
   Reads like the header of a spec sheet. */
.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 21rem);
  gap: 1rem 3rem;
  align-items: end;
  margin-bottom: clamp(2rem, 1rem + 2.4vw, 3.2rem);
}
.section-head .section-title { margin: 0; }
.section-sub {
  margin: 0;
  font-size: .96rem;
  color: var(--ink-muted);
}
@media (max-width: 820px) {
  .section-head { grid-template-columns: 1fr; }
}

/* Anchor targets would otherwise scroll their heading underneath the sticky
   header. 84px = header height plus a breath. */
#featured, #popular, #builds, #orbitstudio, #models, #sellers, #newsletter { scroll-margin-top: 84px; }

/* --------------------------------------------------- 01 featured build */

/* The one model with its own spread, the way a product company treats its
   flagship: a large picture, then plain facts in a spec list, then one
   button. Everything else on the page is shown in groups. */
.feature { padding-top: clamp(2rem, 1rem + 3vw, 4rem); }

.feature-grid {
  display: grid;
  grid-template-columns: 1.08fr .92fr;
  gap: clamp(2.5rem, 1rem + 5vw, 6rem);
  align-items: center;
}
@media (max-width: 900px) {
  .feature-grid { grid-template-columns: 1fr; }
}

.feature-media {
  position: relative;
  padding: 0 14% 14% 0;
}

/* Loose pieces underneath, the finished puzzle on top. With scroll-driven
   animation the finished one fades in as the section passes; without it,
   the finished puzzle is simply what shows. */
.assemble {
  display: grid;
  margin: 0;
  border-radius: var(--r-card);
  overflow: hidden;
  background: #1E2226;
  box-shadow: var(--lift);
}
.assemble img {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

.feature-detail {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 40%;
  margin: 0;
  padding: 8px;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  box-shadow: var(--lift);
  transform: rotate(2deg);
}
.feature-detail img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 8px;
}

.feature-copy h2 { margin-bottom: 1.2rem; }

.spec {
  margin: 2rem 0 2.2rem;
  border-top: 1px solid var(--line);
}
.spec > div {
  display: grid;
  grid-template-columns: 7.2rem minmax(0, 1fr);
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line);
}
.spec dt {
  padding-top: .22rem;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.spec dd {
  margin: 0;
  font-size: 1rem;
  color: var(--ink);
}
.spec a {
  color: var(--teal);
  text-decoration: underline;
  text-decoration-color: rgba(27, 90, 115, .35);
  text-underline-offset: 3px;
}
.spec a:hover { text-decoration-color: var(--teal); }

.spec-tight { margin: 0 0 1.6rem; }
.spec-tight > div { grid-template-columns: 6.4rem minmax(0, 1fr); padding: .6rem 0; }
.spec-tight dd { font-size: .95rem; }

.feature-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 26px;
}

/* ---------------------------------------------------- 02 most printed */

/* A mosaic where size means something: the model people print most by far
   gets four cells, the rest one each, in the order people print them. */
.popular { position: relative; padding-top: clamp(3.5rem, 2rem + 4vw, 6rem); }

.bento {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}
.b-lead { grid-column: span 2; grid-row: span 2; }

.b-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--paper-2);
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  overflow: hidden;
  text-decoration: none;
  transition: border-color .18s ease, transform .18s ease;
}
.b-tile:hover { border-color: rgba(27, 90, 115, .35); transform: translateY(-2px); }

.b-img {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}
/* The lead's picture fills whatever height the two rows beside it settle
   on, so it must not push the rows taller with its own intrinsic size. */
.b-lead .b-img { aspect-ratio: auto; flex: 1; min-height: 14rem; }
.b-lead .b-img img { position: absolute; inset: 0; }

.b-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .4s var(--ease), opacity .35s ease;
}
.b-tile:hover .b-img img { transform: scale(1.04); }

/* Second photo on hover: the capybara on a fingertip answers "how big is
   it", the group shot answers "why would I print more than one". */
.b-alt { position: absolute; inset: 0; opacity: 0; }
@media (hover: hover) {
  .b-lead:hover .b-alt,
  .b-lead:focus-visible .b-alt { opacity: 1; }
}

.b-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 10px 13px 12px;
}
.b-lead .b-meta { padding: 16px 20px 18px; gap: 3px; }

.b-cat,
.b-tag {
  font-family: var(--mono);
  font-size: .64rem;
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.b-tag { color: var(--teal); }

.b-name {
  font-size: .95rem;
  font-weight: 500;
  color: var(--ink);
}
.b-lead .b-name { font-size: clamp(1.25rem, 1rem + .8vw, 1.6rem); font-weight: 600; letter-spacing: -.02em; }
.b-line { font-size: .95rem; color: var(--ink-muted); }
.b-tile:hover .b-name { color: var(--teal); }

.b-more {
  justify-content: space-between;
  padding: 18px 18px 16px;
  background: var(--teal);
  border-color: transparent;
  color: var(--paper);
}
.b-more:hover { background: var(--teal-deep); border-color: transparent; }
.b-more-text { font-size: 1.08rem; font-weight: 500; line-height: 1.3; }
.b-more-arrow {
  align-self: flex-end;
  font-size: 1.6rem;
  line-height: 1;
  transition: transform .22s var(--ease);
}
.b-more:hover .b-more-arrow { transform: translateX(5px); }

@media (max-width: 900px) {
  .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .b-lead { grid-row: auto; }
  .b-lead .b-img { flex: none; aspect-ratio: 4 / 3; min-height: 0; }
  .b-more { min-height: 9rem; }
}

/* ------------------------------------------------------ 03 big builds */

.solar {
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  grid-template-areas:
    "parts copy"
    "wide  wide";
  gap: 18px clamp(2rem, 1rem + 3vw, 4rem);
  align-items: center;
  margin: 0 0 clamp(3rem, 2rem + 3vw, 5rem);
}
.solar-parts { grid-area: parts; margin: 0; }
.solar-copy  { grid-area: copy; }
.solar-wide  { grid-area: wide; margin: 0; }

.solar figure img {
  width: 100%;
  border-radius: 12px;
  box-shadow: var(--lift);
}
.solar-wide img {
  aspect-ratio: 3 / 1;
  object-fit: cover;
  object-position: 50% 74%;
}
.solar-copy h3 {
  margin-bottom: .9rem;
  font-size: clamp(1.5rem, 1.1rem + 1.3vw, 2.1rem);
  font-weight: 600;
  letter-spacing: -.025em;
}
.solar-copy p {
  max-width: 42ch;
  margin-bottom: 1.4rem;
  color: var(--ink-muted);
}

@media (max-width: 820px) {
  .solar {
    grid-template-columns: 1fr;
    grid-template-areas: "parts" "copy" "wide";
  }
}

.build-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px 22px;
}

.build {
  display: flex;
  flex-direction: column;
  text-decoration: none;
}
.build img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
  transition: transform .3s var(--ease), box-shadow .3s ease;
}
.build:hover img { transform: translateY(-4px); box-shadow: var(--lift); }

.build-name {
  margin-top: 14px;
  font-size: 1.06rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--ink);
  transition: color .16s ease;
}
.build:hover .build-name { color: var(--teal); }

/* The spec line: the numbers that say how much model this is, set like the
   label on a drawing. Taken from the model's own MakerWorld description and
   its standard print profile, never the largest figure across profiles. */
.build-spec {
  margin-top: 3px;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .03em;
  color: var(--teal);
}
.build-line {
  margin-top: 3px;
  font-size: .93rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

@media (max-width: 900px) {
  .build-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* On phones the builds become a rail you slide along, one snap per model,
   so six big photos do not turn into an endless column. */
@media (max-width: 620px) {
  .build-grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 14px;
    margin-inline: -24px;
    padding: 0 24px 10px;
    scrollbar-width: thin;
  }
  .build { flex: 0 0 78%; scroll-snap-align: start; }
}

/* -------------------------------------------------------- OrbitStudio band */

.band {
  view-timeline-name: --band;
  view-timeline-axis: block;
  background: var(--night);
  color: var(--bone);
  padding: clamp(4.5rem, 2.5rem + 6vw, 7.5rem) 0;
  position: relative;
  overflow: hidden;
}

/* A very faint wash so the black is not flat. No glow, no gradient mesh. */
.band::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 80% at 78% 30%, rgba(90, 169, 230, .10), transparent 62%);
  pointer-events: none;
}

.band-grid {
  position: relative;
  display: grid;
  grid-template-columns: .92fr 1.08fr;
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  align-items: center;
}
@media (max-width: 900px) {
  .band-grid { grid-template-columns: 1fr; }
}

.band h2 { margin-bottom: 1rem; color: var(--bone); }
.band p  { color: var(--silver); max-width: 44ch; }

.band-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 1.8rem;
}

.band-teaser {
  margin: 1.6rem 0 0;
  padding-top: 1.2rem;
  border-top: 1px solid rgba(255, 255, 255, .10);
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: .04em;
  color: #8B96A4;
}

.band-media {
  border: 1px solid rgba(255, 255, 255, .10);
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--night-2);
}
.band-media video { width: 100%; display: block; }

/* ------------------------------------------------------ 05 collections */

/* A plain index instead of three more photo cards: the page has already
   shown twenty pictures by now. Thumbnails on touch screens; on a mouse,
   a larger preview follows the pointer instead (main.js). */
.index {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line);
}
.index li { border-bottom: 1px solid var(--line); }

.index a {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr) auto 28px;
  align-items: center;
  gap: 22px;
  padding: 18px 6px;
  text-decoration: none;
  transition: padding .25s var(--ease), background-color .2s ease;
}
.index a:hover { background: var(--paper-2); padding-left: 16px; }

.index-thumb {
  width: 64px;
  height: 48px;
  object-fit: cover;
  border-radius: 6px;
}
.index-name {
  font-size: clamp(1.3rem, 1rem + 1.3vw, 2.1rem);
  font-weight: 500;
  letter-spacing: -.025em;
  line-height: 1.15;
  color: var(--ink);
  transition: color .16s ease;
}
.index-note {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .05em;
  color: var(--ink-faint);
  text-align: right;
}
.index-arrow {
  font-size: 1.3rem;
  color: var(--teal);
  transition: transform .22s var(--ease);
}
.index a:hover .index-name { color: var(--teal); }
.index a:hover .index-arrow { transform: translateX(5px); }

@media (max-width: 700px) {
  .index a {
    grid-template-columns: 56px minmax(0, 1fr) 22px;
    grid-template-areas: "thumb name arrow" "thumb note arrow";
    gap: 2px 16px;
  }
  .index-thumb { grid-area: thumb; width: 56px; height: 44px; }
  .index-name  { grid-area: name; }
  .index-note  { grid-area: note; text-align: left; }
  .index-arrow { grid-area: arrow; }
}

.index-preview {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 40;
  width: 250px;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 10px;
  box-shadow: 0 24px 50px -24px rgba(20, 24, 28, .55);
  pointer-events: none;
  opacity: 0;
  transition: opacity .2s ease;
}
.index-preview.on { opacity: 1; }

@media (hover: hover) and (pointer: fine) {
  .index-thumb { display: none; }
  .index a { grid-template-columns: minmax(0, 1fr) auto 28px; }
}

/* --------------------------------------------------------------- sellers */

.sellers-grid {
  display: grid;
  grid-template-columns: 1.25fr .75fr;
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  align-items: center;
}
@media (max-width: 820px) { .sellers-grid { grid-template-columns: 1fr; } }

.sellers-grid .lead { margin-bottom: 1.8rem; }

/* The seller badge is a loud, glossy object next to a very quiet page. Putting
   it on its own mat with a caption frames it as an artefact being shown,
   rather than letting it read as page decoration. */
.sellers-badge {
  margin: 0;
  padding: 22px 22px 0;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  text-align: center;
}
.sellers-badge img {
  width: 100%;
  max-width: 240px;
  margin-inline: auto;
}
.sellers-badge figcaption {
  padding: 14px 0 16px;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .04em;
  color: var(--ink-faint);
}

/* ------------------------------------------------------------ newsletter */

/* Same trick as .stats: a pseudo-element instead of a border, so the motion
   section can draw it. */
.newsletter { position: relative; }
.newsletter::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--line-soft);
  transform-origin: left center;
}

.newsletter-inner {
  max-width: 640px;
  text-align: center;
}
.newsletter-inner .eyebrow { text-align: center; }
.newsletter-inner .lead { margin-inline: auto; margin-bottom: 2rem; }

.signup-block {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px;
  max-width: 470px;
  margin-inline: auto;
}
.signup-block .form-note { grid-column: 1 / -1; text-align: center; }

@media (max-width: 480px) {
  .signup-block { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------- footer */

.site-footer {
  background: var(--paper-2);
  border-top: 1px solid var(--line-soft);
  padding-top: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.footer-inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: clamp(2rem, 1rem + 4vw, 4rem);
  padding-bottom: 2.6rem;
}
@media (max-width: 820px) { .footer-inner { grid-template-columns: 1fr; } }

.footer-brand { display: flex; flex-direction: column; gap: 1.1rem; }

.tagline {
  margin: 0;
  font-size: 1.06rem;
  font-weight: 500;
  line-height: 1.42;
  letter-spacing: -.012em;
}

.footer-links {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.6rem;
}
@media (max-width: 560px) { .footer-links { grid-template-columns: repeat(2, 1fr); } }

.footer-links div { display: flex; flex-direction: column; gap: .5rem; }

.footer-head {
  margin: 0 0 .25rem;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.footer-links a {
  font-size: .95rem;
  color: var(--ink-muted);
  text-decoration: none;
  transition: color .16s ease;
  width: fit-content;
}
.footer-links a:hover { color: var(--teal); }

.footer-base {
  border-top: 1px solid var(--line-soft);
  padding-top: 1.2rem;
  padding-bottom: 1.6rem;
}
.footer-base p {
  margin: 0;
  font-family: var(--mono);
  font-size: .74rem;
  letter-spacing: .06em;
  color: var(--ink-faint);
}

/* ===================================================================
   Motion — "the page prints itself"

   The tagline is a three-act structure, so the motion is one too:

     Easy to print        the mark builds up in discrete layers
     Simple to assemble   loose puzzle pieces become the finished Moon,
                          and the big builds converge into their grid
     Simply solid         contour lines survey the terrain

   Movement is saved for places where it shows how a model goes
   together. Text never moves; photographs arrive.

   All of it is progressive enhancement. The base rules leave every
   element visible and correct, and main.js keeps handling the reveals
   through IntersectionObserver for browsers that have not shipped
   scroll-driven animations — Firefox, as of now. Nothing here is load
   bearing; remove the whole section and the page still reads fine.
   =================================================================== */

/* --- Act 1 — Easy to print ----------------------------------------- */

/* steps() instead of a smooth wipe. It has to read as fourteen layers
   being laid down, not as an image sliding into frame. */
@keyframes mark-build {
  from { clip-path: inset(100% 0 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* the hot line riding the top of the build */
@keyframes nozzle-pass {
  0%   { top: 100%; opacity: 0; }
  12%  { opacity: .75; }
  86%  { opacity: .75; }
  100% { top: 0%; opacity: 0; }
}

.brand-mark-wrap { position: relative; display: block; line-height: 0; }
.brand-mark { animation: mark-build .95s steps(14, end) both; }
.brand-mark-wrap::after {
  content: "";
  position: absolute;
  left: -3px;
  right: -3px;
  height: 1px;
  background: var(--teal);
  pointer-events: none;
  animation: nozzle-pass .95s steps(14, end) both;
}

/* --- the print-head pass: rules get drawn, not just placed --------- */

@keyframes line-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes seam-draw { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0% 0 0); } }

/* The seam closing the collections is a puzzle tab, because that is what this
   brand actually makes. The tab is a fixed-size SVG between two flexible rules
   rather than one stretched path: a stretched viewBox would squash the knob
   into a spike at phone widths. The whole strip is revealed by a left-to-right
   wipe, which sequences line, tab, line for free. */
.seam {
  pointer-events: none;
  display: flex;
  align-items: center;
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 0 24px 1.6rem;
}
.seam-line {
  flex: 1;
  height: 1px;
  background: var(--line);
}
.seam-tab {
  flex: none;
  display: block;
}
.seam-tab path {
  fill: none;
  stroke: var(--line);
  stroke-width: 1;
}

/* --- Act 3 — Simply solid: elevation behind the dark band ---------- */

.contours {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.contours path {
  fill: none;
  stroke: var(--signal);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  opacity: .08;
  transform-box: fill-box;
  transform-origin: center;
}
@keyframes contour-out {
  from { transform: scale(calc(.86 + var(--i) * .004)); opacity: .03; }
  to   { transform: scale(calc(1.05 + var(--i) * .012)); opacity: .13; }
}

/* --- reveals: fallback path (no scroll timelines) ------------------ */

.reveal {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity .6s ease, transform .6s var(--ease);
}
.reveal.in { opacity: 1; transform: none; }
.bento .reveal:nth-child(2),  .build-grid .reveal:nth-child(2) { transition-delay: .06s; }
.bento .reveal:nth-child(3),  .build-grid .reveal:nth-child(3) { transition-delay: .12s; }
.bento .reveal:nth-child(4),  .build-grid .reveal:nth-child(4) { transition-delay: .06s; }
.bento .reveal:nth-child(5),  .build-grid .reveal:nth-child(5) { transition-delay: .12s; }
.bento .reveal:nth-child(6),  .build-grid .reveal:nth-child(6) { transition-delay: .18s; }
.bento .reveal:nth-child(7) { transition-delay: .06s; }
.bento .reveal:nth-child(8) { transition-delay: .12s; }
.bento .reveal:nth-child(9) { transition-delay: .18s; }

/* --- reveals: scroll-driven path ----------------------------------- */

@keyframes reveal-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* Simple to assemble: the builds come in from apart and close the gaps, the
   way pieces meet. Tied to scroll position, so scrolling back up takes them
   apart again. */
@keyframes piece-in {
  from { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 14px)); }
  to   { opacity: 1; transform: translate(0, 0); }
}

/* the loose pieces become the finished Moon */
@keyframes assemble { from { opacity: 0; } to { opacity: 1; } }

/* photographs settle from a slight zoom as they arrive */
@keyframes settle { from { scale: 1.08; } to { scale: 1; } }

/* the dark band arrives as a card and opens out to the full width */
@keyframes band-open {
  from { clip-path: inset(0 3.5% round 28px); }
  to   { clip-path: inset(0 0% round 0px); }
}

/* Longhands on purpose, never the `animation` shorthand. The shorthand resets
   animation-duration to 0s, which makes a scroll-driven animation snap straight
   to its end state instead of scrubbing. `animation-duration: auto` is what
   hands timing over to the scroll position. And never mix range names such as
   `entry 5% cover 30%` in one range: that resolves to zero length and the
   animation silently does nothing. */
@supports (animation-timeline: view()) {
  .reveal,
  .assemble-to,
  .b-img img,
  .band,
  .stats::before,
  .newsletter::before,
  .seam,
  .contours path {
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: view();
  }

  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
    animation-name: reveal-in;
    animation-range: entry 10% entry 90%;
  }

  .build-grid .reveal { animation-name: piece-in; animation-range: entry 5% entry 85%; }
  .build-grid .reveal:nth-child(3n+1) { --dx: -30px; --dy: 10px; }
  .build-grid .reveal:nth-child(3n+2) { --dx:   0px; --dy: 26px; }
  .build-grid .reveal:nth-child(3n)   { --dx:  30px; --dy: 10px; }

  /* Two columns or a rail: a sideways convergence would read as a glitch. */
  @media (max-width: 900px) {
    .build-grid .reveal:nth-child(n) { --dx: 0px; --dy: 18px; }
  }

  .assemble-to {
    animation-name: assemble;
    animation-range: cover 16% cover 40%;
  }

  .b-img img {
    animation-name: settle;
    animation-range: entry 0% entry 100%;
  }

  .band {
    animation-name: band-open;
    animation-range: entry 0% entry 75%;
  }

  .stats::before,
  .newsletter::before {
    animation-name: line-draw;
    animation-range: entry 0% entry 90%;
  }

  /* entry on a strip this thin is barely 50px of scroll, which reads as a
     snap rather than a draw. cover gives the wipe room to actually be seen. */
  .seam {
    animation-name: seam-draw;
    animation-range: cover 0% cover 45%;
  }

  .contours path {
    animation-name: contour-out;
    animation-timeline: --band;
    animation-range: cover 0% cover 100%;
  }
}

/* --- opt out ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .brand-mark { animation: none; clip-path: none; }
  .brand-mark-wrap::after { display: none; }
  .stats::before,
  .newsletter::before { transform: none; }
  .seam { clip-path: none; }
  .assemble-to { opacity: 1; }
  .b-img img { scale: 1; }
  .band { clip-path: none; }
  .contours path { transform: none; opacity: .08; }
}
