/* Order a print — the studio.
   =========================================================================
   This page used to be a black glass-cockpit panel, borrowed from the free
   tool at /3dprint. That reads as an instrument: correct for a tool you fly
   behind, unfriendly for a shop where somebody is deciding to spend $35.

   So the chrome is now the sectional-chart daylight palette the rest of
   wewentflying.com uses — cream paper, chart blue, waypoint magenta — and only
   the viewport stays dark, because a green relief map on cream disappears and
   on slate it glows. Light panel, dark stage.

   Every class name the old stylesheet defined survives, so the markup and the
   JS did not have to move.
   ========================================================================= */

:root {
  /* Paper, ink, and the two chart colours. Straight from web/public/css/app.css
     so the studio and the logbook are recognisably one site. */
  --paper:        #F5F2E9;
  --paper-raised: #FFFDF7;
  --paper-sunk:   #EBE7DA;
  --contour:      #E3DCC8;
  --ink:          #1F2A33;
  --ink-soft:     #5B6570;
  --ink-faint:    #8B939C;

  --blue:         #2B7BBF;
  --blue-dark:    #215E92;
  --blue-wash:    #E8F1F8;
  --waypoint:     #C2447C;
  --waypoint-wash:#FBEEF4;
  --amber:        #C8811A;
  --warn:         #B3402F;
  --warn-wash:    #FBEDEA;

  /* The viewport. Deep enough that the print reads, warm enough that it does
     not look like a different website. */
  --stage:        #C8CDD3;
  --stage-edge:   #B4BAC2;

  --sans: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --display: "Barlow Condensed", "Arial Narrow", var(--sans);
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --bar-h: 52px;
  --r-sm: 6px;
  --r: 10px;
  --r-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(31, 42, 51, .07), 0 1px 1px rgba(31, 42, 51, .04);
  --shadow: 0 2px 8px rgba(31, 42, 51, .09), 0 1px 2px rgba(31, 42, 51, .06);
  --shadow-lift: 0 10px 30px rgba(31, 42, 51, .16);
  --ring: 0 0 0 3px rgba(43, 123, 191, .32);
}

* { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` is a single type selector, so
   any class that sets display beats it. .btn does exactly that, which shipped a
   "Buy it on Etsy" link to a shop that did not exist and an AR button onto
   desktops. One rule, once, rather than remembering per component. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 15px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  /* iOS: stop the whole page rubber-banding when the sheet scrolls. */
  overscroll-behavior-y: none;
}

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

:where(a, button, input, select, [tabindex]):focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-sm);
}

/* ---- the We Went Flying bar --------------------------------------------- */

.site-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  height: var(--bar-h);
  padding: 0 clamp(14px, 3vw, 24px);
  background: var(--paper-raised);
  border-bottom: 1px solid var(--contour);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink);
  text-decoration: none;
  font: 700 17px/1 var(--display);
  letter-spacing: .12em;
  text-transform: uppercase;
}
.brand svg { color: var(--waypoint); flex: none; }

.site-nav {
  display: flex;
  align-items: center;
  gap: clamp(10px, 2.4vw, 22px);
  font: 600 12px/1 var(--display);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.site-nav a {
  color: var(--ink-soft);
  text-decoration: none;
  padding: 6px 2px;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { color: var(--ink); }
.site-nav a[aria-current] {
  color: var(--waypoint);
  border-bottom-color: var(--waypoint);
}

/* ---- the three steps ----------------------------------------------------
   Design, copy the code, order on Etsy. This sits above everything because
   the handover is the one thing a first-time visitor cannot guess: without it
   they design a print, press save, and only then discover a second website is
   involved. Telling them up front costs one strip of chrome. */

.steps-bar {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 10px clamp(14px, 3vw, 24px);
  background: var(--paper);
  border-bottom: 1px solid var(--contour);
  overflow-x: auto;
  scrollbar-width: none;
}
.steps-bar::-webkit-scrollbar { display: none; }

.step-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: none;
  color: var(--ink-faint);
  font: 600 12px/1 var(--display);
  letter-spacing: .13em;
  text-transform: uppercase;
  white-space: nowrap;
}

.step-n {
  display: grid;
  place-items: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  font: 600 11px/1 var(--mono);
  letter-spacing: 0;
}

/* A hairline between chips, which is the whole progress indicator. */
.step-rule {
  flex: 1 1 18px;
  min-width: 14px;
  height: 1px;
  margin: 0 10px;
  background: var(--contour);
}

.step-chip[data-state="active"] {
  color: var(--waypoint);
}
.step-chip[data-state="active"] .step-n {
  background: var(--waypoint);
  border-color: var(--waypoint);
  color: #fff;
}
.step-chip[data-state="done"] { color: var(--blue); }
.step-chip[data-state="done"] .step-n {
  background: var(--blue);
  border-color: var(--blue);
  color: #fff;
}

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

.studio-shell {
  display: grid;
  /* The panel is a fixed, readable column; the stage takes the rest. */
  grid-template-columns: minmax(330px, 380px) 1fr;
  height: calc(100dvh - var(--bar-h) - 43px);
  min-height: 420px;
}

.panel {
  position: relative;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--paper);
  border-right: 1px solid var(--contour);
  padding: 0 0 8px;
  /* The order bar is sticky at the bottom of this column, so the scrollable
     content needs room to clear it. */
  scrollbar-width: thin;
}

.panel-head {
  margin: 0;
  padding: 18px clamp(14px, 2vw, 20px) 12px;
  font: 700 13px/1 var(--display);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  gap: 9px;
}
.panel-head::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--waypoint);
  flex: none;
}

.panel section {
  padding: 16px clamp(14px, 2vw, 20px);
  border-top: 1px solid var(--contour);
}
.panel section:first-of-type { border-top: 0; padding-top: 4px; }

/* ---- fields -------------------------------------------------------------- */

.field { margin-bottom: 14px; }
.field:last-child { margin-bottom: 0; }

.field-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
  font: 600 11px/1.3 var(--display);
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.num {
  font: 400 11px/1 var(--mono);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-faint);
}

.hint {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* Ticking a symbol on or off. Sits in the right-hand slot of .field-label, the
   same place "optional" and the character count sit, so it reads as a property
   of the field rather than a second control stacked above it. */
.check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  /* Inherits the label row's condensed caps; only the weight drops, so the
     field's name stays the loudest thing in the row. */
  font-weight: 400;
  color: var(--ink-faint);
  /* A 16px box next to 11px text otherwise pushes the row taller than its
     neighbours and the fields stop lining up. */
  margin: -3px 0;
}
.check input {
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--blue);
  cursor: pointer;
  flex: none;
}
.check:hover { color: var(--ink-soft); }
.check:has(input:checked) { color: var(--blue-dark); }

/* The label row holds two labels now, so neither may add its own spacing. */
.field-label > label { margin: 0; cursor: pointer; }

input:disabled {
  background: var(--paper-sunk);
  color: var(--ink-faint);
  cursor: not-allowed;
}

input[type="text"],
input[type="email"],
select {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--contour);
  border-radius: var(--r-sm);
  background: var(--paper-raised);
  color: var(--ink);
  font: 400 14px/1.4 var(--mono);
}
input::placeholder { color: var(--ink-faint); }
input:hover, select:hover { border-color: #CFC7B2; }

select {
  font-family: var(--sans);
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' fill='none' stroke='%235B6570' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  padding-right: 30px;
}

/* ---- the drop zone ------------------------------------------------------- */

.drop {
  display: block;
  padding: 20px 14px;
  border: 1.5px dashed #CFC7B2;
  border-radius: var(--r);
  background: var(--paper-raised);
  text-align: center;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.drop:hover { border-color: var(--blue); background: var(--blue-wash); }
.drop strong {
  display: block;
  font: 600 14px/1.3 var(--sans);
  color: var(--ink);
  /* A long file name must never widen the panel. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drop span {
  display: block;
  margin-top: 4px;
  font-size: 12.5px;
  color: var(--ink-soft);
}
.drop-over .drop,
.drop.drop-over {
  border-color: var(--waypoint);
  border-style: solid;
  background: var(--waypoint-wash);
}

/* ---- flight stats -------------------------------------------------------- */

dl#stats {
  margin: 12px 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0;
  border: 1px solid var(--contour);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--paper-raised);
}
dl#stats dt,
dl#stats dd {
  margin: 0;
  padding: 7px 11px;
  border-top: 1px solid var(--contour);
}
dl#stats dt:first-of-type, dl#stats dt:first-of-type + dd { border-top: 0; }
dl#stats dt {
  font: 600 10.5px/1.5 var(--display);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
dl#stats dd {
  text-align: right;
  font: 400 13px/1.5 var(--mono);
  color: var(--blue-dark);
}

/* ---- swatches ------------------------------------------------------------ */

/* Two per row, not three. Three left about 70px for the name, which truncated
   every filament Bambu gives two words to. */
[role="radiogroup"] {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 6px;
}

.swatch {
  display: flex;
  align-items: center;
  min-height: 34px;
  padding: 7px 9px 7px 28px;
  position: relative;
  border: 1px solid var(--contour);
  border-radius: var(--r-sm);
  background: var(--paper-raised);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  font: inherit;
  transition: border-color .12s, background .12s;
}
/* The filament chip. Its background is set inline by buildSwatches from the
   spool's real hex, so this only gives it a shape. Drawn as an element rather
   than a ::before because a pseudo-element cannot take an inline style, which
   is how the color gets here. */
.swatch-dot {
  position: absolute;
  left: 8px; top: 50%;
  transform: translateY(-50%);
  width: 13px; height: 13px;
  border-radius: 3px;
  /* Jade White on cream needs an edge, and Black needs one too on a dark row. */
  box-shadow: inset 0 0 0 1px rgba(31, 42, 51, .28);
}
.swatch:hover { border-color: #CFC7B2; background: var(--paper-sunk); }
.swatch.is-active {
  border-color: var(--blue);
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 1px var(--blue);
}
/* Wraps rather than truncates. A filament's name is how it gets ordered off
   the shelf, so losing the end of it is worse than a second line. */
.swatch-name {
  font: 600 12px/1.25 var(--sans);
  text-align: left;
}

/* The thickness group has no filament chip, so it reclaims the left inset and
   stacks a description under the name. It borrows .swatch-hex for that
   description, which is why the rule below exists after the filament cards
   stopped using the class. */
#swWidth .swatch {
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding-left: 9px;
}
#swWidth .swatch-hex {
  font: 400 10.5px/1.3 var(--sans);
  color: var(--ink-faint);
  text-align: left;
}
#swWidth .swatch.is-active .swatch-hex { color: var(--blue-dark); }

/* ---- the style switch ---------------------------------------------------- */

/* Two cards, side by side. They look like the swatch cards so the panel keeps
   one vocabulary, but they carry a native radio: the switch is the first
   decision on the page and a screen reader should hear it as one. */
.style-cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.style-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 10px;
  border: 1px solid var(--contour);
  border-radius: var(--r-sm);
  background: var(--paper-raised);
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.style-card input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
.style-card:hover { border-color: #CFC7B2; background: var(--paper-sunk); }
.style-card:has(input:checked) {
  border-color: var(--blue);
  background: var(--blue-wash);
  box-shadow: inset 0 0 0 1px var(--blue);
}
.style-card:has(input:focus-visible) { outline: 2px solid var(--blue); outline-offset: 2px; }
.style-name {
  font: 600 12.5px/1.25 var(--sans);
  color: var(--ink);
}
.style-blurb {
  font: 400 10.5px/1.35 var(--sans);
  color: var(--ink-faint);
}
.style-card:has(input:checked) .style-blurb { color: var(--blue-dark); }

/* Which controls a style shows. The relief controls hide in chart mode and
   the chart controls hide in relief mode, so each mode sees only what it
   prints. */
.panel[data-style="relief"] .chart-only,
.panel[data-style="chart"] .relief-only,
.panel:not([data-frame="plaque"]) .plaque-only,
.panel:not([data-frame="lines"]) .frame-lines { display: none; }

/* Two short inputs on one row: the tail number and the aircraft type. */
.field.two-up {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

/* ---- slider -------------------------------------------------------------- */

.range, input[type="range"] {
  width: 100%;
  appearance: none;
  height: 22px;
  background: transparent;
  cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--contour);
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 17px; height: 17px;
  margin-top: -6.5px;
  border-radius: 50%;
  background: var(--blue);
  border: 2px solid var(--paper-raised);
  box-shadow: var(--shadow-sm);
}
input[type="range"]::-moz-range-track {
  height: 4px; border-radius: 2px; background: var(--contour);
}
input[type="range"]::-moz-range-thumb {
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--blue); border: 2px solid var(--paper-raised);
}

/* ---- messages and progress ---------------------------------------------- */

.msg {
  margin: 10px 0 0;
  padding: 9px 11px;
  border-radius: var(--r-sm);
  font-size: 13px;
  line-height: 1.5;
  border: 1px solid var(--contour);
  background: var(--paper-raised);
  color: var(--ink-soft);
}
.msg-error {
  border-color: #E4BDB5;
  background: var(--warn-wash);
  color: var(--warn);
}

.spinner {
  display: inline-block;
  width: 13px; height: 13px;
  margin-right: 7px;
  vertical-align: -2px;
  border: 2px solid rgba(43, 123, 191, .25);
  border-top-color: var(--blue);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.progress {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: rgba(255, 255, 255, .1);
  overflow: hidden;
}
.progress-bar {
  display: block;
  height: 100%;
  width: 34%;
  background: var(--waypoint);
}
.progress.indeterminate .progress-bar { animation: sweep 1.15s ease-in-out infinite; }
@keyframes sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(390%); }
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 11px 16px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font: 600 12px/1 var(--display);
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background .13s, border-color .13s, transform .06s;
}
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; }

.btn-primary { background: var(--blue); color: #fff; }
.btn-primary:hover:not([disabled]) { background: var(--blue-dark); }

.btn-ghost {
  background: var(--paper-raised);
  border-color: var(--contour);
  color: var(--ink);
}
.btn-ghost:hover:not([disabled]) { background: var(--paper-sunk); border-color: #CFC7B2; }

/* ---- the order bar ------------------------------------------------------- */

.order-bar {
  position: sticky;
  bottom: 0;
  margin-top: 8px;
  padding: 13px clamp(14px, 2vw, 20px) 15px;
  background: var(--paper-raised);
  border-top: 1px solid var(--contour);
  box-shadow: 0 -6px 18px rgba(31, 42, 51, .06);
}

.order-lines { list-style: none; margin: 0 0 9px; padding: 0; }
.order-lines li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
  font-size: 12.5px;
  color: var(--ink-soft);
}
.order-lines li span:last-child {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
  text-align: right;
}

.order-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 9px 0 11px;
  border-top: 1px solid var(--contour);
}
.order-total span:first-child {
  font: 600 11px/1 var(--display);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.order-total span:last-child {
  font: 600 21px/1 var(--mono);
  color: var(--ink);
}

.order-bar .hint { text-align: center; }

/* Nothing here takes money, and a stranger's site asking you to design
   something invites the question. Answer it where the button is. */
.trust {
  margin: 9px 0 0;
  padding-top: 9px;
  border-top: 1px dashed var(--contour);
  font-size: 11.5px;
  line-height: 1.5;
  text-align: center;
  color: var(--ink-faint);
}
.trust strong { color: var(--ink-soft); font-weight: 600; }

/* ---- the handover -------------------------------------------------------- */

.handover {
  margin-top: 13px;
  padding: 14px;
  border: 1px solid var(--blue);
  border-radius: var(--r);
  background: var(--blue-wash);
}

.handover-lede {
  margin: 0 0 11px;
  font: 600 13.5px/1.4 var(--sans);
  color: var(--ink);
  text-align: center;
}

.code-row { display: flex; gap: 8px; align-items: stretch; }
.code-row .btn { flex: 0 0 auto; width: auto; }

.design-code {
  flex: 1;
  display: block;
  padding: 11px 12px;
  border: 1.5px dashed var(--blue);
  border-radius: var(--r-sm);
  background: var(--paper-raised);
  color: var(--blue-dark);
  font: 700 19px/1.2 var(--mono);
  letter-spacing: .13em;
  text-align: center;
  user-select: all;
}

.handover-step {
  margin: 11px 0 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.handover-step strong { color: var(--ink); }

.handover .btn-primary { width: 100%; }

/* ---- the stage ----------------------------------------------------------- */

.canvas-wrap {
  position: relative;
  background: var(--stage);
  background-image: radial-gradient(ellipse at 50% 40%, #D6DADF 0%, var(--stage) 55%, var(--stage-edge) 100%);
  overflow: hidden;
}
.canvas-wrap canvas { display: block; width: 100%; height: 100%; }

/* Sits over the stage, bottom-right, so it never covers the model's label. */
.stage-tools {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
}

.btn-ar {
  width: auto;
  padding: 10px 15px;
  background: rgba(245, 242, 233, .94);
  border-color: rgba(31, 42, 51, .18);
  color: var(--ink);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow);
}
.btn-ar:hover:not([disabled]) { background: #fff; }
.btn-ar svg { flex: none; }

.stage-note {
  margin: 0;
  padding: 7px 11px;
  max-width: 260px;
  border-radius: var(--r-sm);
  background: rgba(14, 22, 30, .82);
  color: #D7DEE5;
  font-size: 12px;
  line-height: 1.45;
  text-align: right;
  backdrop-filter: blur(6px);
}

/* model-viewer is only inserted when AR is asked for. Full-bleed over the
   stage, with its own close button. */
.ar-host {
  position: absolute;
  inset: 0;
  background: var(--stage);
  z-index: 5;
}
.ar-host model-viewer { width: 100%; height: 100%; background: var(--stage); }
.ar-close {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 6;
  width: auto;
  padding: 8px 13px;
  background: rgba(245, 242, 233, .94);
  border-color: rgba(31, 42, 51, .18);
  color: var(--ink);
}

/* ---- phones -------------------------------------------------------------- */

.sheet-handle { display: none; }
.grip { display: none; }

@media (max-width: 900px) {
  .studio-shell {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
    height: calc(100dvh - var(--bar-h) - 43px);
  }

  /* The stage stops where the closed sheet begins. Left full height, the
     model centres itself behind the sheet and the customer looks at a frame
     with its bottom third covered. */
  .canvas-wrap {
    grid-row: 1;
    height: calc(100dvh - var(--bar-h) - 43px - var(--sheet-closed, 210px));
  }

  .panel {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    max-height: 88dvh;
    border-right: 0;
    border-top: 1px solid var(--contour);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: 0 -12px 34px rgba(31, 42, 51, .22);
    /* Closed, only the top --sheet-closed pixels are on screen. JS measures
       that from the handle and the order bar rather than guessing, because a
       guess that is too large shows a strip of half-cut controls and one that
       is too small hides the price. */
    transform: translateY(calc(100% - var(--sheet-closed, 210px)));
    transition: transform .28s cubic-bezier(.32, .72, 0, 1);
    padding-bottom: 0;
  }
  .panel.open { transform: translateY(0); }

  /* The order bar lives at the end of the panel, which when closed puts it
     below the fold with a stretch of blank paper where the price should be.
     Flex order floats it up under the handle without moving it in the DOM,
     so the tab order and the reading order stay as written. */
  .panel:not(.open) .sheet-handle { order: -2; }
  .panel:not(.open) .order-bar { order: -1; position: static; }

  /* Closed, the order bar is a summary: what it costs and the button. The
     itemisation and the reassurance are worth reading once, not permanently,
     and every line of them is a line of the model covered up. */
  .panel:not(.open) .order-lines,
  .panel:not(.open) .trust { display: none; }
  .panel:not(.open) > section,
  .panel:not(.open) > .panel-head { visibility: hidden; }
  .panel:not(.open) { overflow: hidden; }

  .sheet-handle {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    width: 100%;
    padding: 9px 16px 7px;
    border: 0;
    background: transparent;
    color: var(--ink-soft);
    font: 600 11px/1 var(--display);
    letter-spacing: .15em;
    text-transform: uppercase;
    cursor: pointer;
  }
  .grip {
    display: block;
    width: 38px; height: 4px;
    border-radius: 2px;
    background: #CFC7B2;
  }

  .order-bar { box-shadow: none; }

  /* No bottom offset. .stage-tools is positioned against .canvas-wrap, and the
     canvas already stops where the closed sheet begins, so lifting it by the
     sheet height a second time lands the button in the middle of the model. */
  .stage-tools { right: 12px; bottom: 12px; }

  .steps-bar { padding: 9px 12px; }
  .step-chip { font-size: 11px; letter-spacing: .1em; }
  .step-rule { margin: 0 7px; min-width: 10px; }

  [role="radiogroup"] { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }

  /* 16px stops iOS zooming the whole page when a field takes focus. */
  input[type="text"], input[type="email"], select { font-size: 16px; }
}

/* Nothing in this bar may wrap. An iPhone 17 is 402pt wide, and at that width
   "3D Print" and "Order a print" each broke across two lines and doubled the
   header's height. The breakpoint below used to be 400px and missed it by two
   points, which is the kind of margin a real device finds and a round number
   does not. */
.brand span, .site-nav a { white-space: nowrap; }

@media (max-width: 460px) {
  /* Keep the current page's label and the way home; drop the rest rather than
     shrink everything until it is unreadable. */
  .site-nav a:not([aria-current]) { display: none; }
  .brand span { font-size: 15px; }
  .site-nav { gap: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ---------------------------------------------------------------------------
   Legs — one row per dropped file, up to three
--------------------------------------------------------------------------- */

.legs {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  display: grid;
  gap: 6px;
  min-width: 0;
}

.leg {
  display: flex;
  align-items: center;
  gap: 8px;
  /* A grid item sizes to its content unless told not to; the name inside
     must be the thing that shrinks, not the panel that grows. */
  min-width: 0;
  padding: 7px 10px;
  background: var(--paper-sunk);
  border-radius: 8px;
  font-size: 0.82rem;
}

.leg-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
}

.leg-meta { color: var(--ink-faint); white-space: nowrap; }

.leg-remove {
  border: 0;
  background: none;
  color: var(--ink-faint);
  font-size: 1rem;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 6px;
}

.leg-remove:hover { color: var(--ink); background: var(--paper); }

/* ---------------------------------------------------------------------------
   Fulfilment view — the admin-only export bar
--------------------------------------------------------------------------- */

.admin-bar {
  display: grid;
  gap: 8px;
  padding: 12px;
  margin: 0 0 14px;
  background: var(--paper-sunk);
  border: 1px dashed var(--ink-faint);
  border-radius: 10px;
}

.nozzle-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 10px;
  align-items: center;
}

.nozzle-grid .field-label { margin: 0; }

.admin-head {
  margin: 0;
  font-weight: 600;
  color: var(--ink);
}

.admin-head code {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.95em;
}

/* The note over the empty preview: a dark pill so it reads on the wood and
   on the see-through frame behind it. */
.empty-note {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  padding: 12px 20px;
  max-width: min(88%, 480px);
  text-align: center;
  pointer-events: none;
  color: #fff;
  background: rgba(16, 18, 22, 0.78);
  border-radius: 14px;
  font-size: 15px;
  line-height: 1.45;
  box-shadow: 0 3px 14px rgba(0, 0, 0, 0.3);
}

/* A stop row's tools: the Show tick and the Remove link, side by side. */
.field-tools { display: inline-flex; align-items: center; gap: 12px; }
.linkish {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-soft); cursor: pointer;
}
.linkish:hover { color: var(--blue-dark); text-decoration: underline; }
/* In running text the link reads as a link, not as a label. */
.handover-step .linkish { font-size: inherit; letter-spacing: 0; text-transform: none; color: var(--blue-dark); text-decoration: underline; }
