/* Configurator chrome. The canvas keeps the live page's #f1f1f1 studio
   grey; the panel beside it is the site's cream, and the chrome floating
   over the grey (home link, part tabs, drag notice) is the site's
   near-black. */

:root {
  --ink: #080811;
  --cream: #F5F2ED;
  --hairline: rgba(245, 242, 237, 0.2);
  --muted: rgba(245, 242, 237, 0.55);
  --border: rgba(245, 242, 237, 0.3);
  /* Ink-side mirrors of the three above, for the cream panel and for the
     tabs sitting on the studio grey. */
  --hairline-ink: rgba(8, 8, 17, 0.15);
  --muted-ink: rgba(8, 8, 17, 0.55);
  --border-ink: rgba(8, 8, 17, 0.3);
  --ring: #E37442;
  --panel-w: 350px;
  --display: 'Jost', 'Helvetica Neue', sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--cream);
  font-family: 'Satoshi', 'Helvetica Neue', sans-serif;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

#c {
  position: fixed;
  top: 0;
  /* The panel covers the right --panel-w, so a viewport-wide canvas puts the
     bike in the middle of the *viewport* — which reads as pushed right, since
     you only see the part left of the panel. Running the canvas box past the
     left edge by exactly the panel's width moves its centre, and the bike
     with it, to the middle of the visible grey. The overhang is hidden
     behind the panel. scene.js sizes the drawing buffer from this box, not
     from the window, so nothing stretches. */
  left: calc(-1 * var(--panel-w));
  width: calc(100% + var(--panel-w));
  height: 100%;
  display: block;
  z-index: 0;          /* the panel and tiles sit above it */
  touch-action: none;  /* let OrbitControls own the drag */
}

.eyebrow {
  margin: 0 0 12px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline-ink);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-ink);
}

/* Home link */
.home--btn {
  position: fixed;
  top: 22px;
  left: 24px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 18px;
  background: var(--ink);
  border-radius: 999px;
}
.home--btn img { display: block; height: 20px; width: auto; }
.home--btn:focus-visible { outline: 1px solid var(--ring); outline-offset: 4px; }

/* Part tabs — floating chrome, so they take the home link's pill shape
   rather than the panel's square edges. Centred over the canvas, not the
   viewport, so they sit above the bike. */
.options {
  position: fixed;
  z-index: 3;
  top: 22px;
  left: calc((100% - var(--panel-w)) / 2);
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}
.option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 0 20px;
  border: 1px solid var(--hairline-ink);
  border-radius: 999px;
  background: var(--cream);
  color: rgba(8, 8, 17, 0.72);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 0.12s ease-in, color 0.12s ease-in;
}
.option--image { display: flex; }
/* The part icons ship in brand orange (#eb693e), which carries on both the
   cream tab and the filled one — so no filter in either state. Inverting the
   active one would turn the orange cyan. */
.option--image img {
  width: 24px;
  height: 24px;
  opacity: 0.72;
}
.option:hover { color: var(--ink); border-color: var(--border-ink); }
.option--is-active {
  background: var(--ink);
  color: var(--cream);
  border-color: var(--ink);
}
.option--is-active .option--image img { opacity: 1; }
.option:focus-visible { outline: 1px solid var(--ring); outline-offset: 4px; }

/* The home wordmark owns the top-left corner. Narrower than this and the
   centred tab row reaches it, so the row drops below. */
@media only screen and (max-width: 1180px) {
  .options { top: 88px; }
}

/* Panel — right-hand column on desktop, bottom sheet on small screens.
   Three regions: a compact head, the tray taking the slack and scrolling,
   and the order block pinned to the bottom. */
.panel {
  position: fixed;
  z-index: 3;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--panel-w);
  display: flex;
  flex-direction: column;
  padding: 26px 24px 20px;
  background: var(--cream);
  color: var(--ink);
  border-left: 1px solid var(--hairline-ink);
}
.panel__head h1 {
  margin: 0 0 16px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.15;
  letter-spacing: -0.005em;
}
.panel__head h1 em { font-style: italic; font-weight: 500; }
.panel__toggle { display: none; }

/* Pinned bottom block: what you're buying, then who it's for. */
.panel__body {
  flex: 0 0 auto;
  padding-top: 16px;
  border-top: 1px solid var(--hairline-ink);
}

.chosen { margin: 0 0 18px; }
.chosen__row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 8px 0;
  border-bottom: 1px solid var(--hairline-ink);
  font-size: 13px;
}
.chosen dt { color: var(--muted-ink); font-weight: 700; }
.chosen dd {
  margin: 0;
  font-family: var(--display);
  font-size: 16px;
  text-align: right;
}

/* Swatch tray */
.tray {
  flex: 1;
  min-height: 0;      /* lets the scroll area actually shrink */
  display: flex;
  flex-direction: column;
}
.tray__slide {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-bottom: 6px;
}
.tray__slide--vertical { flex-direction: column; flex-wrap: nowrap; }

.tray__swatch {
  position: relative;
  flex: 0 0 auto;
  width: 58px;
  height: 46px;
  padding: 0;
  border: none;
  cursor: pointer;
  background-size: cover;
  background-position: center;
  transition: outline-color 0.1s ease-in;
  outline: 1px solid transparent;
  outline-offset: 4px;
}
.tray__slide--vertical .tray__swatch { width: 100%; height: 62px; }
.tray__swatch:hover { outline-color: var(--border-ink); }
/* Selection is a tick in the label bar (below), not a ring — which leaves
   the orange outline meaning one thing only: keyboard focus. The tray is
   where focus is hardest to see, so it needs a mark of its own. */
.tray__swatch:focus-visible { outline-color: var(--ring); }

/* The tick. Drawn as two borders on a rotated box so it needs no asset,
   and sized to sit inside the label bar at the foot of the swatch. */
.tray__swatch[aria-pressed="true"]::after {
  content: '';
  position: absolute;
  right: 7px;
  bottom: 7px;
  width: 9px;
  height: 5px;
  border-left: 2px solid var(--cream);
  border-bottom: 2px solid var(--cream);
  transform: rotate(-45deg);
}

/* Sits on the swatch image itself, so it stays ink-on-cream-text whatever
   the panel behind it does. */
.tray__label {
  position: absolute;
  inset: auto 0 0 0;
  padding: 3px 6px;
  background: rgba(8, 8, 17, 0.82);
  color: var(--cream);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-align: left;
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tray__slide--vertical .tray__label { opacity: 1; }
.tray__swatch:hover .tray__label,
.tray__swatch:focus-visible .tray__label { opacity: 1; }
/* The wheel grid keeps its labels hidden until hover, which would hide the
   tick with them — so the selected swatch always shows its bar. */
.tray__swatch[aria-pressed="true"] .tray__label {
  opacity: 1;
  padding-right: 22px;  /* keeps the name clear of the tick */
}

.panel__foot {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline-ink);
  font-size: 11px;
  color: var(--muted-ink);
}

/* Loader and drag notice */
.loading {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--ink);
}
.loader {
  width: 34px;
  height: 34px;
  border: 1px solid var(--hairline);
  border-top-color: var(--cream);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.loader-progress {
  margin: 14px 0 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.loader-error {
  max-width: 280px;
  margin: 16px 0 0;
  padding: 0 20px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--cream);
  text-align: center;
}
.loader-error a { color: var(--cream); }

.drag-notice {
  position: fixed;
  z-index: 3;
  left: calc((100% - var(--panel-w)) / 2);  /* centred on the bike, as above */
  bottom: 30px;
  transform: translateX(-50%);
  padding: 8px 14px;
  background: var(--ink);
  color: var(--cream);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease-in;
}
.drag-notice.start { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .loader { animation-duration: 3s; }
  .drag-notice { transition: none; }
}

/* Order form */
.order { margin: 0; }
.order__label {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted-ink);
}
.order__input {
  width: 100%;
  min-height: 48px;
  padding: 0 14px;
  border: 1px solid var(--border-ink);
  background: transparent;
  color: var(--ink);
  font-family: var(--display);
  font-size: 18px;
}
.order__input::placeholder { color: rgba(8, 8, 17, 0.35); }
.order__input:focus-visible { outline: 1px solid var(--ring); outline-offset: 2px; }

/* The honeypot has to be in the DOM and fillable to catch a bot, and
   invisible to a person. Clipped rather than display:none, which bots
   are known to skip. */
.order__bot {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Two steps in one form. Fields are hidden between steps, never cleared, and
   every field stays in the markup — Netlify parses deployed HTML, so a
   CSS-hidden field is still recorded and a JS-created one never is. */
.order__contact { display: none; }
body.is-contact .order__step:not(.order__contact) { display: none; }
body.is-contact .order__contact { display: block; }
body.is-contact .tray { display: none; }
/* The tabs drive the tray; with the tray hidden they are a dead control. */
body.is-contact .options { display: none; }
/* The contact step has no tray to give its height budget back, and on a
   short window (~700px and below) its own content — summary, back link,
   lead-in, four fields, submit, hint — is taller than the fixed-height
   panel. Scoped to the contact step only, so the build step still matches
   the baseline pixel for pixel. */
body.is-contact .panel { overflow-y: auto; }

/* Only meaningful once a name has been typed and the tray is out of the way. */
.chosen__row--name { display: none; }
body.is-contact .chosen__row--name { display: flex; }

.order__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  margin: 0 0 14px;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted-ink);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
}
.order__back:hover { color: var(--ink); }
.order__back:focus-visible { outline: 1px solid var(--ring); outline-offset: 3px; }

.order__lead {
  margin: 0 0 14px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted-ink);
}
/* Every label except the first in a block follows an input. */
.order__input + .order__label { margin-top: 14px; }
.order__opt {
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: rgba(8, 8, 17, 0.4);
}
.order__textarea {
  min-height: 84px;
  padding: 12px 14px;
  /* 16px is the floor that stops iOS Safari zooming the page on focus. */
  font-size: 16px;
  line-height: 1.45;
  resize: vertical;
}

.order__btn {
  width: 100%;
  min-height: 56px;
  margin-top: 12px;
  border: none;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--display);
  font-weight: 600;
  font-size: 18px;
  cursor: pointer;
  transition: opacity 0.12s ease-in;
}
.order__btn:disabled {
  background: transparent;
  border: 1px solid var(--border-ink);
  color: rgba(8, 8, 17, 0.4);
  cursor: not-allowed;
}
.order__btn:focus-visible { outline: 1px solid var(--ring); outline-offset: 4px; }

.order__hint {
  margin: 10px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--muted-ink);
  transition: opacity 0.2s ease-in;
}
.order__hint a { color: var(--ink); }

/* Below 961px the panel becomes a bottom sheet, collapsed to its header
   until the cart button opens it. Matches the live page's breakpoint. */
@media only screen and (max-width: 960px) {
  /* No right-hand column to dodge here, so the canvas goes back to filling
     the viewport and everything re-centres on it. */
  #c { left: 0; width: 100%; }

  .panel {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    /* Stop short of the tab row at y 78-126 — an open sheet that covers the
       tabs traps you in it, with nothing left to tap to get back to the
       bike. The 60vh floor keeps that sane on very short viewports. */
    max-height: max(60vh, calc(100vh - 140px));
    padding: 18px 18px 14px;
    border-left: none;
    border-top: 1px solid var(--hairline-ink);
  }
  .panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
  }
  .panel__head h1 { margin: 0; font-size: 18px; }
  .panel__toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 0 16px;
    border: 1px solid var(--border-ink);
    background: transparent;
    color: var(--ink);
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.02em;
    cursor: pointer;
  }
  /* cart.svg is white, for the old dark panel — flip it to read on cream. */
  .panel__toggle img { filter: invert(1); }
  /* Open, the button's job is to get back to the bike, so the cart goes. */
  .panel--open .panel__toggle img { display: none; }
  .panel__toggle:focus-visible { outline: 1px solid var(--ring); outline-offset: 4px; }

  /* The recap collapses to a wrapped line. Three stacked rows cost about
     100px that the swatches need more than the summary does. */
  .chosen {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 16px;
    margin: 0 0 10px;
  }
  .chosen__row {
    padding: 0;
    gap: 8px;
    border-bottom: none;
    font-size: 12px;
  }
  .chosen dd { font-size: 14px; }

  /* The sheet's height budget is tight, and the Sarajevo line is the one
     thing in it that isn't part of building or buying the bike. */
  .panel__foot { display: none; }

  /* The active tab names the part on mobile, so the tray's own heading is
     redundant — but it stays in the accessibility tree, since the inactive
     tabs are icon-only and nothing else spells the part out. */
  .tray .eyebrow {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .panel__body { padding-top: 12px; }

  /* Collapsed by default so the bike keeps the screen. */
  .panel__body, .tray { display: none; }
  .panel--open .panel__body { display: block; margin-top: 10px; }
  /* The tray is what the sheet is for, so it takes the slack and holds a
     floor deep enough for all three handlebars (3 x 52px plus their gaps).
     The vh term lets a short viewport scroll the tray rather than push the
     checkout button off the bottom of the screen. */
  .panel--open .tray {
    display: flex;
    margin-top: 10px;
    min-height: min(168px, 30vh);
  }

  /* A finished build keeps its checkout button reachable while the sheet is
     collapsed, so you can look the bike over and still buy it. Everything
     else in the block stays hidden until the sheet opens. */
  .panel--ready:not(.panel--open) .panel__body { display: block; }
  .panel--ready:not(.panel--open) .chosen,
  .panel--ready:not(.panel--open) .order__label,
  .panel--ready:not(.panel--open) .order__input,
  .panel--ready:not(.panel--open) .order__contact,
  .panel--ready:not(.panel--open) .order__hint { display: none; }

  /* Collapsing mid-contact-step should keep Send reachable the same way
     collapsing mid-build keeps Checkout reachable — the customer is
     peeking at the bike, not abandoning a form they were filling in.
     Bouncing them back to the build step on collapse would cost their
     place and make them tap "Proceed to checkout" a second time, even
     though nothing they typed was lost. Every selector needs all four
     classes to outrank the build-oriented rule just above, which would
     otherwise hide the contact step along with the build step. */
  body.is-contact .panel--ready:not(.panel--open) .order__contact { display: block; }
  body.is-contact .panel--ready:not(.panel--open) .order__back,
  body.is-contact .panel--ready:not(.panel--open) .order__lead { display: none; }

  .options {
    left: 12px;
    right: 12px;
    bottom: auto;
    top: 78px;
    transform: none;
    justify-content: center;
  }
  .option { padding: 0 14px; min-height: 48px; }
  .option span:not(.option--image) { display: none; }  /* icons only, to fit */
  /* ...except the active one, which names the part now that the tray's
     heading is gone. All three still fit across 390px. */
  .option--is-active span:not(.option--image) { display: block; }
  .drag-notice { left: 50%; bottom: auto; top: 140px; }
  .tray__slide--vertical .tray__swatch { height: 52px; }
}
