/* Sculptwerk Studio: light product workspace.
   Register: higgsfield/meshy product chrome (10px controls, one solid
   accent, soft translucent layers) on a warm Apple-clean light ground.
   All colors live as tokens on :root so a dark theme can be added later
   as a [data-theme="dark"] override block without touching components.
   System font stack only; no web fonts, no external assets. */

@font-face {
  font-family: "Sculptwerk Manrope";
  src: url("/assets/fonts/Manrope-Variable.ttf") format("truetype");
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
}

:root {
  --bg: #f6f4f1;
  --surface: #ffffff;
  --surface-2: #fbfaf8;
  --ink: #1d1a17;
  --dim: #6f6861;
  /* One step darker than --dim, for text that has to stay legible on the
     lighter surfaces: the disabled primary button is 6.92:1 with it. */
  --dim-strong: #5d564f;
  /* F13: #7a7168 on --bg computes to 4.36:1, under the 4.5:1 AA floor for
     normal-size text, and it carries the placeholder text and six small
     labels here. #6f665d is 5.12:1 on the same ground. The public lane set
     the same value in figuren.css and public.css; this is the third and
     last declaration. */
  --faint: #6f665d;
  --line: rgba(29, 26, 23, 0.12);
  --line-soft: rgba(29, 26, 23, 0.07);
  --hover-tint: rgba(29, 26, 23, 0.04);
  --accent: #d3603e;
  --accent-deep: #b8402c;
  --accent-deeper: #9c3020;
  --accent-tint: rgba(211, 96, 62, 0.10);
  --accent-tint-strong: rgba(211, 96, 62, 0.16);
  --on-accent: #fff8f3;
  --ok: #35693b;
  --ok-tint: rgba(61, 122, 68, 0.10);
  --shadow-sm: 0 1px 2px rgba(29, 26, 23, 0.05), 0 2px 8px rgba(29, 26, 23, 0.04);
  --shadow-md: 0 2px 4px rgba(29, 26, 23, 0.04), 0 12px 32px rgba(29, 26, 23, 0.09);
  --shadow-lg: 0 4px 12px rgba(29, 26, 23, 0.05), 0 24px 64px rgba(29, 26, 23, 0.12);
  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 22px;
  --sans: "Sculptwerk Manrope", -apple-system, BlinkMacSystemFont,
          "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas,
          "Liberation Mono", monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

[hidden] { display: none !important; }

html { background: var(--bg); }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--ink); }

a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

button { font-family: inherit; color: inherit; }

::selection { background: var(--accent-tint-strong); }

/* D09: the studio had no skip link at all and its main region was not
   focusable, so the first Tab on a page with a five-item rail, an account
   card and two nav links landed nowhere useful. Same treatment the public
   pages already carry. */
.skip-link {
  position: fixed;
  top: 0.65rem;
  left: 0.65rem;
  z-index: 100;
  padding: 0.65rem 0.9rem;
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--surface);
  font-weight: 650;
  text-decoration: none;
  transform: translateY(-160%);
}

.skip-link:focus { transform: none; }

.stage:focus { outline: none; }

/* D05: the only thing a browser without JavaScript is shown. */
.noscript-note {
  margin: 0;
  padding: 1.4rem 1.6rem;
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
  color: var(--dim);
  font-size: 0.92rem;
  line-height: 1.6;
}

.noscript-note p { margin: 0 0 0.7rem; max-width: 46rem; }
.noscript-note strong { color: var(--ink); }

@media (prefers-reduced-motion: no-preference) {
  a, button, input, select, .upload-card, .card, .style-card, .consent,
  .step-link, .dropzone, .pick {
    transition: background-color 0.18s ease, border-color 0.18s ease,
                color 0.18s ease, box-shadow 0.22s ease,
                transform 0.22s ease, opacity 0.18s ease;
  }
}

/* --- shell -------------------------------------------------------------- */

.shell {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  min-height: 100vh;
}

/* --- rail --------------------------------------------------------------- */

.rail {
  padding: 1.7rem 1.15rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 1.7rem;
  border-right: 1px solid var(--line-soft);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.68rem;
  padding: 0 0.65rem;
  text-decoration: none;
}

.brand-mark { width: 2.2rem; height: 2.2rem; flex: none; }

.brand-name {
  display: inline-block;
  font-family: "Sculptwerk Manrope", var(--sans);
  font-size: 1.26rem;
  font-weight: 650;
  letter-spacing: -0.055em;
  line-height: 1;
}

.brand-name > span { color: var(--accent-deep); }

.brand-sub {
  display: block;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: var(--accent);
  margin-top: 0.15rem;
  text-transform: uppercase;
}

.steps { display: flex; flex-direction: column; gap: 0.3rem; }

/* Both carry an explicit display, so the hidden attribute alone would not
   take them off screen while the order confirmation is up. */
.steps[hidden], .meta-field[hidden] { display: none; }

.step-link {
  display: grid;
  grid-template-columns: 1.7rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.65rem;
  background: none;
  border: none;
  border-radius: var(--r-sm);
  color: var(--dim);
  text-align: left;
  padding: 0.6rem 0.65rem;
  cursor: pointer;
  font-size: 0.92rem;
  font-weight: 500;
}

.step-link:not(:disabled):hover { background: var(--hover-tint); color: var(--ink); }

.step-link .step-num {
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--dim);
  background: var(--surface);
  border: 1px solid var(--line);
  width: 1.65rem;
  height: 1.65rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.step-link[data-active="true"] {
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.step-link[data-active="true"] .step-num {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  color: var(--on-accent);
}

.step-link[data-done="true"]:not([data-active="true"]) .step-num {
  background: var(--accent-tint);
  border-color: transparent;
  color: var(--accent-deeper);
}

.step-link[data-done="true"] .step-state::before {
  content: "✓";
  color: var(--accent);
  font-size: 0.82rem;
  font-weight: 700;
}

.step-link:disabled { opacity: 0.42; cursor: not-allowed; }

.rail-foot {
  margin-top: 0;
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  padding: 0 0.35rem;
}

.chip-row { display: flex; flex-wrap: wrap; gap: 0.35rem; }

.chip {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  color: var(--dim);
  padding: 0.22rem 0.6rem;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.chip.ok, .chip.warn { padding-left: 0.5rem; }

.chip.ok::before, .chip.warn::before {
  content: "";
  width: 0.42rem;
  height: 0.42rem;
  border-radius: 50%;
}

.chip.ok { background: var(--ok-tint); border-color: transparent; color: var(--ok); }
.chip.ok::before { background: var(--ok); }
.chip.warn { background: var(--accent-tint); border-color: transparent; color: var(--accent-deeper); }
.chip.warn::before { background: var(--accent); }

.rail-note { font-size: 0.75rem; line-height: 1.5; color: var(--dim); }

.rail-links {
  font-size: 0.8rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line-soft);
}

.rail-links a { color: var(--dim); text-decoration: none; }
.rail-links a:hover { color: var(--accent-deep); text-decoration: underline; }
.rail-links a:first-child { color: var(--accent-deeper); font-weight: 650; }

.ghost-btn {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--dim);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 0.55rem 0.8rem;
  cursor: pointer;
}

.ghost-btn:hover { color: var(--ink); border-color: var(--dim); box-shadow: var(--shadow-sm); }

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

.stage {
  padding: 1.7rem 2.6rem 4rem;
  max-width: 1460px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 286px;
  column-gap: 2rem;
  align-items: start;
}

.stage-head {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding-bottom: 1.1rem;
  margin-bottom: 2rem;
  border-bottom: 1px solid var(--line-soft);
}

.step { grid-column: 1; grid-row: 2; min-width: 0; }

.project-meta {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.55rem;
  width: min(100%, 48rem);
}

.meta-field {
  display: flex;
  flex-direction: column;
  gap: 0.32rem;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--dim);
}

.meta-field.grow { flex: 1 1 260px; }

input, select {
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0.6rem 0.75rem;
  min-width: 0;
}

input::placeholder { color: var(--faint); }

input:hover, select:hover { border-color: rgba(29, 26, 23, 0.22); }

input:focus-visible, select:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

#project-title { width: 100%; max-width: none; }

#project-title-field { width: 100%; }

.project-title-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

/* --- project rename ---------------------------------------------------

   The save control appears the moment the field is dirty and holds its
   place afterwards, so the header does not jump while the customer
   types. The answer sits under the field, where the field is. */

.project-title-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.project-title-save {
  background: var(--accent-deep);
  border: none;
  border-radius: var(--r-sm);
  color: var(--on-accent);
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.58rem 0.95rem;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  white-space: nowrap;
}

.project-title-save:hover:not(:disabled) { background: var(--accent-deeper); }

.project-title-save:disabled {
  background: var(--line);
  color: var(--faint);
  box-shadow: none;
  cursor: default;
}

.project-title-state {
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--dim);
  min-height: 1.1em;
  max-width: 34rem;
}

.project-title-state[data-kind="ok"] { color: var(--ok); }
.project-title-state[data-kind="error"] {
  color: var(--accent-deep);
  font-weight: 600;
}

/* 0.64rem drew the project status at 10.2 px; 12 px is the floor for text
   a visitor is meant to read (06.10.2026). */
#project-chip {
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  text-transform: none;
  white-space: nowrap;
}

.status { font-size: 0.85rem; color: var(--dim); min-height: 1.4em; }
.status.error { color: var(--accent-deep); font-weight: 600; }

.title-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
}

.title-suggestions .idea-chip { font-size: 0.72rem; }

.idea-chip {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  color: var(--dim);
  font-size: 0.78rem;
  font-weight: 500;
  padding: 0.32rem 0.75rem;
  cursor: pointer;
}

.idea-chip:hover {
  border-color: var(--accent);
  color: var(--accent-deeper);
  background: var(--accent-tint);
}

.chip-icon {
  display: inline-block;
  margin-right: 0.35rem;
}

/* --- steps shared -------------------------------------------------------- */

.step h1 {
  font-size: clamp(1.7rem, 3.4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.12;
  margin-bottom: 0.75rem;
}

.heading-with-info {
  display: flex;
  align-items: flex-start;
  gap: 0.45rem;
}

.heading-with-info h1 { flex: 0 1 auto; }

.info-control {
  position: relative;
  display: inline-flex;
  flex: none;
  margin-top: 0.15rem;
}

.info-trigger {
  width: 32px;
  height: 32px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface);
  color: var(--dim);
  font-family: Georgia, serif;
  font-size: 0.92rem;
  font-style: italic;
  font-weight: 700;
  cursor: help;
  box-shadow: var(--shadow-sm);
}

.info-trigger:hover { color: var(--accent-deep); border-color: var(--accent); }

.info-tooltip {
  position: fixed;
  z-index: 20;
  top: var(--tooltip-top, 4rem);
  left: var(--tooltip-left, 1rem);
  right: auto;
  width: min(300px, calc(100vw - 1.5rem));
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--on-accent);
  box-shadow: var(--shadow-md);
  font-size: 0.78rem;
  font-style: normal;
  font-weight: 500;
  line-height: 1.45;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-4px);
}

.info-control[data-open="true"] .info-tooltip {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
}

.studio-callout {
  display: grid;
  grid-template-columns: minmax(180px, 0.8fr) minmax(0, 1.8fr);
  gap: 0.25rem 1.1rem;
  width: 100%;
  margin: -0.5rem 0 1.5rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--dim);
  font-size: 0.86rem;
  box-shadow: var(--shadow-sm);
}

.studio-callout strong { color: var(--ink); }
.studio-callout-title {
  grid-column: 1 / -1;
  color: var(--accent-deep);
  font-size: 0.68rem;
  font-weight: 750;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.transfer-consent {
  margin: 0 0 1rem;
  padding: 0.9rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--dim);
  font-size: 0.82rem;
  line-height: 1.5;
}

/* Recorded likeness permission at the upload step. One short card in the
   page flow: no dialog, no overlay, nothing preselected. */
.likeness-card {
  margin: 0 0 1.4rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
}

/* P20: the questions are legends now. float:none keeps the legend in the
   grid flow instead of the UA's inline placement inside the border. */
.likeness-question {
  float: none;
  width: 100%;
  padding: 0;
  margin: 0 0 0.6rem;
  font-size: 0.9rem;
  font-weight: 650;
  color: var(--ink);
}

.likeness-choices {
  display: grid;
  gap: 0.35rem;
  border: 0;
  padding: 0;
  margin: 0;
  min-inline-size: 0;
}

/* Both carry an explicit display, so the hidden attribute alone would not
   take them off screen. F08 hides the card until the customer has put
   something in and the minors question until the first one is answered. */
.likeness-card[hidden], .likeness-choices[hidden] { display: none; }

/* F08: the second question is a question, not a fourth answer to the
   first one. */
#likeness-minors-block { margin-top: 1.5rem; }

/* P20: the five likeness radios rendered at 13 by 13 CSS pixels. The
   control keeps its size, the label carries the hit area, and the row is
   at least 24 px tall so a finger has something to land on. */
.likeness-option {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--dim);
  cursor: pointer;
  min-height: 24px;
  padding-block: 0.25rem;
}

.likeness-option input {
  margin-top: 0.2rem;
  flex: none;
  width: 18px;
  height: 18px;
}

.likeness-attest { margin-top: 0.6rem; }

.likeness-card .transfer-consent { margin: 0.9rem 0 0; }

.likeness-status {
  margin: 0.7rem 0 0;
  font-size: 0.8rem;
  color: var(--dim);
}

.likeness-status[data-tone="error"] { color: var(--accent-deep); }

/* Provider-neutral notice beside every generated 3D viewer. Quiet, always
   present: it states what the customer is looking at, not a warning. */
.ai-preview-notice {
  margin: 0.8rem 0 0;
  color: var(--faint);
  font-size: 0.78rem;
  line-height: 1.5;
}

.proof-panel {
  grid-column: 2;
  grid-row: 2;
  position: sticky;
  top: 1.2rem;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.86);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  backdrop-filter: blur(14px);
}

.proof-panel > summary {
  list-style: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.9rem 1rem;
  border-bottom: 1px solid transparent;
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 700;
}

.proof-panel > summary::-webkit-details-marker { display: none; }
.proof-panel[open] > summary { border-bottom-color: var(--line-soft); }
.proof-panel > summary small { color: var(--faint); font-size: 0.66rem; font-weight: 600; }

.proof-body { display: grid; gap: 1rem; padding: 1rem; }

.proof-facts { display: grid; gap: 0.45rem; }
.proof-facts div {
  display: grid;
  grid-template-columns: 5.8rem minmax(0, 1fr);
  gap: 0.55rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px solid var(--line-soft);
}
.proof-facts dt {
  color: var(--faint);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.proof-facts dd { color: var(--ink); font-size: 0.78rem; font-weight: 600; }

/* F10: the second progress model is gone. One sentence in its place. */
.proof-next {
  color: var(--dim);
  font-size: 0.74rem;
  line-height: 1.5;
  white-space: normal;
  word-break: normal;
}
.proof-next b { display: block; color: var(--ink); font-weight: 700; }
.proof-facts > div[hidden] { display: none; }

.process-overview { border-top: 1px solid var(--line-soft); padding-top: 0.75rem; }
.process-overview summary { font-size: 0.72rem; color: var(--accent-deep); }
.process-overview ol {
  margin: 0.65rem 0 0 1rem;
  display: grid;
  gap: 0.5rem;
  color: var(--dim);
  font-size: 0.73rem;
}
.proof-boundary,
.proof-panel details p {
  font-family: var(--sans);
  color: var(--dim);
  font-size: 0.72rem;
  line-height: 1.5;
  white-space: normal;
  word-break: normal;
}
.proof-boundary b { color: var(--ink); }

.step-lead {
  color: var(--dim);
  width: 100%;
  max-width: 100%;
  margin-bottom: 1.8rem;
  text-wrap: pretty;
}

.step-actions {
  margin-top: 1.8rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.action-hint { font-size: 0.85rem; color: var(--dim); max-width: 46ch; }

.primary-btn {
  background: var(--accent-deep);
  border: none;
  border-radius: var(--r-sm);
  color: var(--on-accent);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.005em;
  padding: 0.78rem 1.5rem;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}

.primary-btn:hover:not(:disabled) {
  background: var(--accent-deeper);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.primary-btn:active:not(:disabled) { transform: translateY(0); box-shadow: var(--shadow-sm); }

/* F08: the disabled state was a grey field on a grey page, 1.25:1 against
   its own background, so the primary action of step 1 was hard to find at
   all before it became usable. A ring instead of a border, so nothing
   moves when the button enables. */
.primary-btn:disabled {
  background: var(--surface-2);
  /* Nit 5: the one literal colour in a file of tokens, named rather than
     replaced. --dim would have read as the obvious substitute and is
     5.26:1 here against this value's 6.92:1, which is a downgrade on the
     exact element F08 was about. */
  color: var(--dim-strong);
  box-shadow: inset 0 0 0 1px var(--dim);
  cursor: not-allowed;
}

.secondary-btn {
  background: var(--accent-tint);
  border: none;
  border-radius: var(--r-sm);
  color: var(--accent-deeper);
  font-size: 0.9rem;
  font-weight: 600;
  padding: 0.78rem 1.5rem;
  cursor: pointer;
}

.secondary-btn:hover:not(:disabled) { background: var(--accent-tint-strong); }
.secondary-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* --- step 1: uploads ------------------------------------------------------ */

.dropzone {
  border: 1.5px dashed var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  padding: 3rem 1.5rem;
  text-align: center;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
}

.dropzone:hover, .dropzone.dragover {
  border-color: var(--accent);
  background: var(--surface-2);
  box-shadow: var(--shadow-md);
}

.dropzone.dragover { transform: scale(1.005); }

.drop-icon {
  width: 44px;
  height: 44px;
  margin: 0 auto 0.9rem;
  display: block;
  color: var(--accent);
}

.drop-title { font-weight: 650; font-size: 1.05rem; margin-bottom: 0.3rem; }
.drop-sub { font-size: 0.84rem; color: var(--dim); }

/* D08: what an iPhone owner has to change, said where the picker is. */
.drop-hint {
  margin-top: 0.5rem;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--dim);
  max-width: 34rem;
  margin-inline: auto;
}

.upload-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));
  gap: 0.9rem;
  margin-top: 1.3rem;
}

.upload-card {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  padding: 0.45rem;
  box-shadow: var(--shadow-sm);
}

.upload-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.upload-thumb, .upload-video {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  display: block;
  border-radius: calc(var(--r-md) - 5px);
  background: var(--surface-2);
}

/* A photo the worker is still normalizing, or one it refused (08.10.2026):
   the same 4:3 box as the thumbnail, so the grid does not jump when the
   image arrives. */
.upload-state {
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0.6rem;
  border-radius: calc(var(--r-md) - 5px);
  background: var(--surface-2);
  color: var(--dim-strong);
  font-size: 0.8rem;
  line-height: 1.35;
  text-align: center;
}

.upload-card.is-pending .upload-state {
  background: linear-gradient(100deg, var(--surface-2) 30%, var(--bg) 50%,
    var(--surface-2) 70%) 0 0 / 300% 100%;
  animation: upload-wait 1.6s linear infinite;
}

.upload-card.is-rejected .upload-state {
  background: var(--accent-tint);
  color: var(--accent-deeper);
}

@keyframes upload-wait {
  from { background-position: 100% 0; }
  to { background-position: 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .upload-card.is-pending .upload-state { animation: none; }
}

.upload-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.3rem;
  padding: 0.4rem 0.25rem 0.15rem;
}

.upload-name {
  font-size: 0.68rem;
  color: var(--dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.upload-remove {
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: 50%;
  color: var(--dim);
  font-size: 0.8rem;
  line-height: 1;
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: none;
  position: relative;
}

/* A 44 px touch target around the 32 px disc. */
.upload-remove::after {
  content: "";
  position: absolute;
  inset: -6px;
}

.upload-remove:hover {
  background: var(--accent-tint);
  border-color: transparent;
  color: var(--accent-deep);
}

/* consent card sits between the uploads and the continue action */

.consent {
  width: 100%;
  max-width: none;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  padding: 1.05rem 1.2rem;
  margin-top: 1.5rem;
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--dim);
}

.consent:hover { box-shadow: var(--shadow-md); }

.consent-note { color: var(--dim); }

.consent-details {
  margin-top: 0.6rem;
}

.consent-details summary {
  color: var(--accent-deeper);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}

.consent-details summary:hover { text-decoration: underline; }

.consent-details p {
  font-family: var(--sans);
  font-size: 0.8rem;
  color: var(--dim);
  margin-top: 0.45rem;
  white-space: normal;
}

.photo-tips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 0.9rem;
  max-width: 66ch;
  margin-top: 1.1rem;
}

.tip-col {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  padding: 0.9rem 1.1rem;
  font-size: 0.82rem;
  color: var(--dim);
}

.tip-title {
  font-weight: 650;
  font-size: 0.86rem;
  color: var(--ink);
  margin-bottom: 0.4rem;
}

.tip-bad .tip-title { color: var(--accent-deeper); }

.tip-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.7rem;
}

.tip-item {
  margin: 0;
  text-align: center;
  color: var(--ok);
}

.tip-bad .tip-item { color: var(--accent-deeper); }

.tip-item svg {
  width: 52px;
  height: 52px;
  margin: 0 auto 0.35rem;
  display: block;
  background: var(--surface-2);
  border-radius: 16px;
  padding: 8px;
  border: 1px solid color-mix(in srgb, currentColor 16%, transparent);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75);
}

.tip-item figcaption {
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--dim);
}

/* --- step 2: styles + candidates ------------------------------------------ */

.style-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 0.9rem;
  margin: 0 0 1.4rem;
  max-width: 940px;
  /* P20: a fieldset now, so its UA border, padding and intrinsic minimum
     width have to go before the grid behaves like the div it replaced. */
  border: 0;
  padding: 0;
  min-inline-size: 0;
}

.style-row legend {
  float: none;
  grid-column: 1 / -1;
  width: 100%;
  padding: 0;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--dim);
  margin-bottom: 0.55rem;
}

.style-row legend span { display: block; color: var(--ink); font-weight: 700; }

.style-row legend small {
  display: block;
  margin-top: .12rem;
  color: var(--dim);
  font-size: .7rem;
  font-weight: 450;
  line-height: 1.4;
}

.style-option {
  display: grid;
  grid-template-rows: 1fr auto;
  min-width: 0;
}

.style-card {
  position: relative;
  border: 1.5px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface);
  padding: 1rem 1.1rem;
  cursor: pointer;
  display: block;
  box-shadow: var(--shadow-sm);
}

/* P04: both carry an explicit display, so the hidden attribute alone left
   them laid out and, with a src in the markup, would have loaded them.
   Hidden means hidden; renderStyleShowcase reveals the ones the media
   route actually serves. */
.style-img[hidden], .finish-img[hidden], .look-img[hidden] { display: none; }

.style-img {
  width: 100%;
  /* The markup's height="800" beat aspect-ratio, so each picker image was
     an 800 px tall box and step 2 was 6,100 px long on a phone (Studio
     audit, 08.10.2026). */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  display: block;
  border-radius: calc(var(--r-md) - 6px);
  margin-bottom: 0.7rem;
  background: var(--surface-2);
}

.style-more {
  justify-self: center;
  margin-top: -0.2rem;
  border: 0;
  background: transparent;
  color: var(--accent-deep);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 650;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  padding: 0.55rem;
}

.wish-ideas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: -1.1rem 0 1.6rem;
  max-width: 940px;
}

#refine-ideas { margin: -0.3rem 0 0; }

.finish-row {
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  padding: 0.9rem 1rem 1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
  gap: 0.6rem;
  flex-wrap: wrap;
  max-width: 940px;
  margin-bottom: 0.6rem;
}

.look-row {
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  padding: 0.9rem 1rem 1rem;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.6rem;
  flex-wrap: wrap;
  max-width: 940px;
  margin-bottom: 0.75rem;
}

.look-row legend {
  float: none;
  grid-column: 1 / -1;
  width: 100%;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--dim);
  margin-bottom: 0.55rem;
}

.look-row legend span,
.finish-row legend span { display: block; color: var(--ink); font-weight: 700; }
.look-row legend small,
.finish-row legend small {
  display: block;
  margin-top: .12rem;
  color: var(--dim);
  font-size: .7rem;
  font-weight: 450;
  line-height: 1.4;
}

.finish-row legend {
  float: none;
  grid-column: 1 / -1;
  width: 100%;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--dim);
  padding: 0;
  margin-bottom: 0.55rem;
}

/* finish picks become visual material swatches once the generated
   swatch images exist; they degrade to text pills without them */
.finish-pick {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  border-radius: var(--r-md);
  padding: 0.7rem 0.9rem;
  text-align: center;
  width: auto;
  min-width: 0;
  min-height: 154px;
  justify-content: flex-start;
}

.look-row .pick {
  position: relative;
  display: grid;
  grid-template-rows: auto auto;
  justify-items: center;
  align-content: start;
  gap: .6rem;
  min-width: 0;
  padding: 1.65rem .8rem .95rem;   /* top clears the Ausgewählt badge, as .finish-pick does */
  border-radius: var(--r-md);
  text-align: center;
}

.look-row .pick > span:last-child { padding-right: 0; }
.look-row .pick-sub { max-width: 24ch; margin-inline: auto; }

.look-preview {
  position: relative;
  width: 100%;
  max-width: 136px;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--line-soft);
  border-radius: 16px;
  background: var(--surface-2);
  overflow: hidden;
}

.look-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* The blob placeholder stays as the no-image fallback, in percentages so it
   scales with the tile instead of being tuned to the old 54 px box. */
.look-preview::before,
.look-preview::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent-tint-strong);
  border: 2px solid var(--accent-deep);
}
.look-preview::before { top: 15%; width: 37%; height: 41%; border-radius: 48%; }
.look-preview::after  { bottom: 13%; width: 63%; height: 30%; border-radius: 26% 26% 13% 13%; }
.look-preview-comic::before { border-radius: 13%; transform: translateX(-50%) rotate(-3deg); }
.look-preview-comic::after  { width: 70%; border-radius: 9%; }
.look-preview-character::before { top: 9%; width: 52%; height: 54%; }
.look-preview-character::after  { width: 50%; height: 24%; }

/* Found on 11.09.2026 while measuring B3: the blob is a fallback, and it
   was still drawn once the masters landed. All three boxes are absolutely
   positioned with no z-index, so they paint in tree order and ::after, the
   base blob, painted a red-bordered rounded rectangle straight across the
   lower half of every thumbnail. The blob belongs to the no-image state
   only, so it is withdrawn the moment the availability gate unhides the
   image. :has() is already load bearing in this file (the checked ring
   below), so this needs no new support. */
.look-preview:has(.look-img:not([hidden]))::before,
.look-preview:has(.look-img:not([hidden]))::after { content: none; }

/* the checked ring, matching .finish-pick:has(input:checked) .finish-img */
.look-row .pick:has(input:checked) .look-preview {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint-strong), var(--shadow-md);
}

.look-row .pick:has(input:checked)::after,
.finish-pick:has(input:checked)::after {
  content: "Ausgewählt";
  position: absolute;
  top: .45rem;
  right: .5rem;
  color: var(--accent-deeper);
  font-size: .58rem;
  font-weight: 750;
  letter-spacing: .05em;
  text-transform: uppercase;
}

.finish-pick { position: relative; }
.finish-row .finish-pick { padding-top: 1.65rem; }

.finish-color-preview {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: #b8402c;
  border: 5px solid var(--surface);
  box-shadow: 0 0 0 1px var(--line), var(--shadow-sm);
  flex: none;
}

.finish-pick:has(input:checked) .finish-color-preview {
  box-shadow: 0 0 0 3px var(--accent-tint-strong), var(--shadow-md);
}

.finish-color-panel {
  max-width: 940px;
  margin: 0 0 .8rem;
  padding: .9rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface-2);
  display: grid;
  grid-template-columns: minmax(180px, .7fr) minmax(260px, 1.3fr) auto;
  align-items: center;
  gap: 1rem;
}

.finish-color-panel[hidden] { display: none; }
.finish-color-panel p { font-size: .78rem; color: var(--dim); }
.finish-color-panel p strong { display: block; color: var(--ink); }
.finish-color-panel p span { display: block; margin-top: .15rem; }
.finish-color-swatches { display: flex; flex-wrap: wrap; gap: .4rem; }
.finish-color-swatches button {
  width: 28px; height: 28px; border-radius: 50%; border: 3px solid var(--surface);
  box-shadow: 0 0 0 1px var(--line); cursor: pointer;
}
.finish-color-swatches button[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--ink), 0 0 0 5px var(--surface-2);
}
.finish-color-own { font-size: .7rem; color: var(--dim); display: grid; gap: .2rem; }
.finish-color-own input { width: 54px; height: 32px; padding: 0; border: 0; background: none; }
/* P18: the hex readout is a sibling of the label now, not a second
   labelled control inside it. */
.finish-color-value {
  font-family: var(--mono);
  font-size: .7rem;
  color: var(--ink);
  align-self: end;
}

.finish-pick span {
  display: block;
  text-align: center;
  white-space: normal;
}

.finish-img {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  box-shadow: var(--shadow-sm);
}

.finish-pick:has(input:checked) .finish-img {
  box-shadow: 0 0 0 3px var(--accent-tint-strong), var(--shadow-md);
}

.finish-note {
  font-size: 0.78rem;
  color: var(--dim);
  max-width: 78ch;
  margin-bottom: 1.4rem;
  line-height: 1.55;
}

.finish-note strong { color: var(--ink); }

.style-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.style-card input { position: absolute; opacity: 0; pointer-events: none; }

.style-card:has(input:checked) {
  border-color: var(--accent);
  background: linear-gradient(0deg, var(--accent-tint), var(--accent-tint)), var(--surface);
}

.style-card:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.style-name { display: block; font-weight: 650; margin-bottom: 0.2rem; }
.style-desc { display: block; font-size: 0.8rem; color: var(--dim); }

.wish-row {
  display: flex;
  gap: 0.9rem;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 1.8rem;
  max-width: 940px;
}

.card-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
  gap: 1.1rem;
  max-width: 1080px;
}

.card {
  background: var(--surface);
  border: 1.5px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }

.card[data-selected="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-tint-strong), 0 18px 42px rgba(55,35,24,.16);
  transform: translateY(-3px);
}

/* The badge lives in the opposite corner from the remove control, which is
   pinned to the card's top right. Both used to sit there and the X landed
   on top of the word. Nothing else occupies the media's top left, and the
   pair stays apart at every card width because each is anchored to its own
   corner rather than to a fixed offset from the other. */
.card[data-selected="true"] .card-media::after {
  content: "Ausgewählt";
  position: absolute;
  top: .7rem;
  left: .7rem;
  max-width: calc(100% - 3.6rem);
  padding: .35rem .65rem;
  border-radius: 999px;
  background: var(--accent-deep);
  color: var(--on-accent);
  font-size: .68rem;
  font-weight: 750;
  box-shadow: var(--shadow-sm);
}

.card-media { position: relative; aspect-ratio: 1; background: var(--surface-2); }

.card-img { width: 100%; height: 100%; object-fit: cover; display: block; }

.card-pending {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 1.2rem;
  text-align: center;
  background: linear-gradient(120deg,
    rgba(211, 96, 62, 0.16), rgba(232, 180, 90, 0.20),
    rgba(196, 121, 168, 0.16), rgba(122, 158, 196, 0.16),
    rgba(211, 96, 62, 0.16));
  background-size: 320% 320%;
}

@media (prefers-reduced-motion: no-preference) {
  .card-pending {
    animation: pending-shift 9s ease-in-out infinite;
  }
}

@keyframes pending-shift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

.card-fact {
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--dim);
  max-width: 26ch;
}

.card-fact, #dreid-progress-text {
  transition: opacity 0.35s ease;
}

.fade-out { opacity: 0; }

.card-wish {
  padding: 0 0.8rem 0.7rem;
  font-size: 0.74rem;
  line-height: 1.45;
  color: var(--dim);
}

.card-error {
  position: absolute;
  inset: 0;
  padding: 1rem;
  font-size: 0.78rem;
  color: var(--accent-deep);
  overflow: auto;
}

.card-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.6rem 0.8rem;
  gap: 0.5rem;
}

.card-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--dim);
  letter-spacing: 0.02em;
}

.pick-btn {
  background: var(--accent-tint);
  border: none;
  border-radius: 999px;
  color: var(--accent-deeper);
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.4rem 0.9rem;
  cursor: pointer;
}

.pick-btn:hover:not(:disabled) { background: var(--accent-deep); color: var(--on-accent); }
.pick-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.spinner {
  width: 18px;
  height: 18px;
  border: 2.5px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}

/* --- step 3: refine -------------------------------------------------------- */

.refine-layout {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 2.2rem;
  align-items: start;
}

.refine-stage { margin: 0; }

#refine-image {
  width: 100%;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  display: block;
  box-shadow: var(--shadow-md);
}

.refine-stage figcaption {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--dim);
  margin-top: 0.7rem;
}

.refine-side { display: flex; flex-direction: column; gap: 1.1rem; }

.lineage {
  list-style: none;
  display: flex;
  gap: 0.55rem;
  flex-wrap: wrap;
  align-items: flex-start;
}

/* The remove control lives inside the same list, so the thumbnail rules
   are scoped to the thumbnails. Unscoped they also sized and restyled the
   remove control, which rendered it as a full 66px block. */
.lineage button:not(.lineage-remove) {
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  background: var(--surface);
  padding: 0;
  cursor: pointer;
  width: 66px;
  height: 66px;
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.lineage button:not(.lineage-remove):hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.lineage button:not(.lineage-remove) img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: calc(var(--r-sm) - 2px);
}

.lineage button[data-active="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-tint-strong), var(--shadow-md);
  transform: translateY(-2px) scale(1.04);
}

.lineage .lineage-pending {
  width: 66px;
  height: 66px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1.5px dashed var(--line);
  border-radius: var(--r-sm);
}

.refine-actions { display: flex; gap: 0.8rem; flex-wrap: wrap; }
.button-icon { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.refine-actions button { display: inline-flex; align-items: center; gap: .5rem; }

/* --- step 4: 3D ------------------------------------------------------------ */

.dreid-layout {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 2.2rem;
  align-items: start;
}

/* Narrow desktops (15.10.2026). Up to 1280px the studio still carries the
   264px rail and, above 1100px, the 286px proof column, so the 7:5 split
   left the viewer about 194px wide at 1133px and the model was cropped.
   In that band the step stacks as it does on a phone: the viewer takes the
   whole step width and the check list with the step's button follows it. */
@media (max-width: 1280px) {
  .dreid-layout { grid-template-columns: minmax(0, 1fr); }
}

.dreid-stage {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  min-height: 360px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 1.5rem;
  box-shadow: var(--shadow-sm);
}

#dreid-video, #dreid-preview {
  width: 100%;
  height: auto;
  max-height: 520px;
  object-fit: contain;
  border-radius: var(--r-md);
  background: var(--surface-2);
}

/* Interactive mesh viewer: the canvas fills a fixed stage; the drag
   hint sits as a quiet pill until the model is loaded and interacted. */
.viewer3d {
  position: relative;
  width: 100%;
  height: min(58vh, 500px);
  border-radius: var(--r-md);
  background:
    radial-gradient(120% 90% at 50% 20%, var(--surface) 0%,
      var(--surface-2) 70%);
  border: 1px solid var(--line-soft);
  overflow: hidden;
  touch-action: none;
}

.viewer3d canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
  cursor: grab;
}

.viewer3d canvas:active { cursor: grabbing; }

.viewer3d-hint {
  position: absolute;
  left: 50%;
  bottom: 0.8rem;
  transform: translateX(-50%);
  z-index: 1;
  pointer-events: auto;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  opacity: 0;
  cursor: pointer;
  transition: opacity 0.3s ease;
}

.viewer3d-finish {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 2;
  padding: 0.3rem 0.62rem;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.88);
  color: var(--dim);
  font-size: 0.68rem;
  font-weight: 600;
  backdrop-filter: blur(8px);
}

.viewer3d.has-model .viewer3d-hint { opacity: 1; }

.viewer3d .viewer-info {
  left: auto;
  right: .8rem;
  bottom: .8rem;
  width: 2rem;
  height: 2rem;
  transform: none;
  display: grid;
  place-items: center;
  padding: 0;
  border-color: rgba(184,64,44,.35);
  color: var(--accent-deep);
  font: 700 .82rem/1 var(--sans);
  letter-spacing: 0;
  text-transform: none;
}

/* Studio backlog (08.10.2026): «Automatisch drehen» and «Vollbild» sit
   bottom left, clear of the finish label above them and of the help
   button on the right, on a 293 px phone canvas as on the desktop. They
   appear with the model, as the help button does; hidden until then they
   are out of the tab order as well. */
.viewer3d-tools {
  position: absolute;
  left: 0.75rem;
  bottom: 0.8rem;
  z-index: 2;
  display: flex;
  gap: 0.5rem;
}

.viewer3d:not(.has-model) .viewer3d-tools { visibility: hidden; }

.viewer3d-tool {
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
  color: var(--dim-strong);
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(8px);
}

.viewer3d-tool:hover { color: var(--accent-deep); border-color: var(--accent); }
/* The global focus ring rounds to a square; these controls stay round. */
.viewer3d-tool:focus-visible,
.viewer3d .viewer-info:focus-visible { border-radius: 50%; }

.viewer3d-tool[aria-pressed="true"] {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
  color: var(--on-accent);
}

.viewer3d-icon {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.viewer3d-tool .icon-exit,
.viewer3d-tool.is-on .icon-enter { display: none; }
.viewer3d-tool.is-on .icon-exit { display: block; }

/* «Vollbild». The browser's fullscreen sizes the viewer itself. Where a
   page may not take it for an element (the iPhone), the viewer becomes a
   fixed layer over the page instead, under the skip link and the status
   toast; the ID outranks the step 5 height. The page behind it does not
   scroll while it is open, and a viewer hidden while open releases it. */
#dreid-viewer:fullscreen { border: 0; border-radius: 0; }
#dreid-viewer:-webkit-full-screen {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 0;
}

#dreid-viewer.is-expanded {
  position: fixed;
  inset: 0;
  z-index: 60;
  width: auto;
  height: auto;
  border: 0;
  border-radius: 0;
}

#dreid-viewer.is-expanded .viewer3d-tools,
#dreid-viewer.is-expanded .viewer-info {
  bottom: calc(0.8rem + env(safe-area-inset-bottom, 0px));
}

html:has(#dreid-viewer.is-expanded:not([hidden])) { overflow: hidden; }

.dreid-iteration-actions {
  width: 100%;
  display: grid;
  justify-items: center;
  gap: .5rem;
}

.dreid-iteration-row {
  display: flex;
  justify-content: center;
  gap: .65rem;
  flex-wrap: wrap;
}

/* The cost and the refusal belong next to the button they describe. */
.dreid-iteration-note {
  margin: 0;
  max-width: 46ch;
  text-align: center;
}

.dreid-iteration-note[hidden] { display: none; }

#dreid-viewer-home { width: 100%; display: grid; gap: .9rem; }
.dreid-reference {
  margin: 0;
  display: grid;
  grid-template-columns: 82px 1fr;
  gap: .8rem;
  align-items: center;
  padding: .65rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.dreid-reference[hidden] { display: none; }
.dreid-reference img { width: 82px; aspect-ratio: 1; object-fit: contain;
  border-radius: var(--r-sm); background: var(--surface); }
.dreid-reference figcaption { font-size: .78rem; color: var(--dim); }

/* Nine saved variants used to live in a 383px horizontal scroller inside
   the viewer column: about 1.8 cards visible, the rest reachable only by
   dragging sideways, and the selected one always the far end of the drag.
   The gallery now spans the whole step and wraps, so every variant is on
   screen at once at desktop and tablet widths. Phones keep the swipe,
   where a horizontal strip is the native gesture and a nine-row stack
   would bury the continue button. */
.dreid-variants {
  grid-column: 1 / -1;
  width: 100%;
  display: grid;
  gap: .65rem;
  padding-top: .35rem;
}
.dreid-variants[hidden] { display: none; }
.dreid-variants-title { margin: 0; font-size: .78rem; font-weight: 700;
  color: var(--ink); }
.dreid-variants-rail {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: .65rem;
  width: 100%;
  padding: .1rem;
}
.dreid-variant { min-width: 0; display: grid;
  grid-template-columns: 52px minmax(0, 1fr); gap: .65rem; align-items: center;
  padding: .55rem; border: 1px solid var(--line-soft); border-radius: var(--r-md);
  background: var(--surface-2); color: var(--dim); text-align: left; }
button.dreid-variant { cursor: pointer; font: inherit; }
button.dreid-variant:hover { border-color: var(--line); background: var(--surface); }
.dreid-variant[data-selected="true"] { border-color: var(--accent);
  background: var(--accent-tint); box-shadow: 0 0 0 2px var(--accent); }
.dreid-variant[data-selected="true"] .dreid-variant-body small {
  color: var(--accent-deep); font-weight: 700; }
.dreid-variant[data-status="failed"],
.dreid-variant[data-status="cancelled"] { opacity: .72; }
.dreid-variant img { width: 52px; height: 52px; object-fit: contain;
  border-radius: var(--r-sm); background: var(--surface); }
.dreid-variant-body { min-width: 0; display: grid; gap: .12rem; }
/* In the old horizontal rail the title had to be clipped to keep the
   cards a fixed width. In the grid it can wrap, so the source round is
   readable instead of ending in an ellipsis. */
.dreid-variant-body strong { color: var(--ink); font-size: .78rem;
  line-height: 1.3; overflow-wrap: anywhere; }
.dreid-variant-body small { color: inherit; font-size: .7rem; }

/* Tablet: the step is one column here, so the gallery is wider than it is
   on the desktop layout, where a proof-panel column and a 7fr/5fr split
   take most of the width. Two columns keep the cards a comfortable
   reading size instead of letting auto-fill pack in a third. */
@media (min-width: 561px) and (max-width: 900px) {
  .dreid-variants-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  .dreid-variants-rail {
    display: flex;
    overflow-x: auto;
    padding: .1rem .1rem .45rem;
    scroll-snap-type: x proximity;
  }
  .dreid-variant { flex: 0 0 min(210px, 72vw); scroll-snap-align: start; }
}

.studio-dialog {
  width: min(760px, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  overflow: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  padding: clamp(1.2rem, 4vw, 2rem);
  box-shadow: 0 30px 90px rgba(29,26,23,.28);
  margin: auto;
}
.studio-dialog::backdrop { background: rgba(29,26,23,.48); backdrop-filter: blur(4px); }
.dialog-close { position: absolute; top: .8rem; right: .8rem; width: 2.2rem;
  height: 2.2rem; border-radius: 50%; border: 1px solid var(--line-soft);
  background: var(--surface-2); font-size: 1.35rem; cursor: pointer; }
.studio-dialog h2 { font-size: clamp(1.55rem, 4vw, 2.2rem); margin: .25rem 0 .6rem; }
.dialog-lead, .dialog-note { color: var(--dim); font-size: .86rem; }
.style-dialog-image { width: 100%; margin: 1rem 0 .65rem; display: block;
  border-radius: var(--r-md); background: var(--surface-2); }
.viewer-help-grid { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: .8rem; margin: 1.25rem 0; }
.viewer-help-grid > div { display: grid; justify-items: center; text-align: center;
  gap: .35rem; padding: 1rem .7rem; border: 1px solid var(--line-soft);
  border-radius: var(--r-md); background: var(--surface-2); }
.viewer-help-grid svg { width: 64px; height: 52px; fill: none; stroke: var(--accent-deep);
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.viewer-help-grid strong { font-size: .9rem; }
.viewer-help-grid span { color: var(--dim); font-size: .74rem; line-height: 1.35; }
.viewer-help-dialog > .primary-btn { width: 100%; }

@media (prefers-reduced-motion: no-preference) {
  .gesture-rotate { animation: gesture-rotate 2.4s ease-in-out infinite; }
  .gesture-zoom { animation: gesture-zoom 2.4s ease-in-out infinite; }
  .gesture-check { animation: gesture-check 2.4s ease-in-out infinite; }
}

@keyframes gesture-rotate {
  0%, 100% { transform: translateX(-4px); }
  50% { transform: translateX(4px); }
}

@keyframes gesture-zoom {
  0%, 100% { transform: scale(.92); }
  50% { transform: scale(1.06); }
}

@keyframes gesture-check {
  0%, 100% { transform: rotate(-2deg); }
  50% { transform: rotate(2deg); }
}

.progress {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--dim);
  font-size: 0.88rem;
  max-width: 40ch;
}

/* Rich 3D waiting state: the chosen motif stays on stage with a light
   sheen sweep while the clock and the honest expectations sit beside it. */

.progress3d {
  display: grid;
  grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  gap: 1.4rem;
  align-items: center;
  width: 100%;
  max-width: 640px;
}

.progress3d-motif {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-2);
  aspect-ratio: 1;
  box-shadow: var(--shadow-sm);
}

.progress3d-motif img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: saturate(0.85);
}

.progress3d-sheen {
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg,
    transparent 30%, rgba(255, 255, 255, 0.35) 50%, transparent 70%);
  background-size: 260% 100%;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .progress3d-sheen { animation: sheen-sweep 2.8s ease-in-out infinite; }
}

/* Names the round the running model is being built from, so the long wait
   never leaves the customer guessing which image is in the machine. */
.progress3d-source {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: .28rem .4rem;
  background: rgba(32, 29, 24, .72);
  color: #fff;
  font: 600 .66rem/1.2 var(--sans);
  text-align: center;
}
.progress3d-source[hidden] { display: none; }

@keyframes sheen-sweep {
  0% { background-position: 120% 0; }
  100% { background-position: -60% 0; }
}

.progress3d-body {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  min-width: 0;
}

.progress3d-line {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
}

.progress3d-bar {
  display: block;
  height: 4px;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
}

.progress3d-bar span {
  display: block;
  height: 100%;
  width: 34%;
  border-radius: 999px;
  background: var(--accent);
}

@media (prefers-reduced-motion: no-preference) {
  .progress3d-bar span { animation: bar-sweep 2.2s ease-in-out infinite; }
}

@keyframes bar-sweep {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(400%); }
}

.progress3d-meta {
  font-size: 0.78rem;
  color: var(--dim);
  line-height: 1.5;
}

.progress3d-clock {
  font-family: var(--mono);
  font-weight: 600;
  color: var(--accent-deep);
}

.progress3d-clock:not(:empty) { margin-right: 0.6em; }

.order-note:empty { display: none; }

/* Studio backlog (08.10.2026, PR 95 review): #refine-hint is a live region
   and has to stay rendered to be heard when its words arrive. Empty, it
   leaves the flex flow instead of adding a gap to the step 3 column. */
#refine-hint:empty {
  display: block;
  position: absolute;
  margin: 0;
}

.progress3d-long {
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--accent-deeper);
  background: var(--accent-tint);
  border-radius: var(--r-sm);
  padding: 0.55rem 0.75rem;
}

/* Always reachable while a job runs: the agent route can wait on a human
   session, so the customer must never be locked into the spinner. */
.progress3d-cancel {
  margin-top: 0.6rem;
  font-size: 0.78rem;
  padding: 0.4rem 0.8rem;
}

@media (max-width: 560px) {
  .progress3d { grid-template-columns: 1fr; }
  .progress3d-motif { max-width: 220px; }
  .studio-callout { grid-template-columns: 1fr; }
  .studio-callout-title { grid-column: 1; }
}

.error-note { color: var(--accent-deep); font-size: 0.84rem; max-width: 52ch; }

.cost-note-prominent { width: 100%; max-width: 64ch; margin: 0;
  padding: .65rem .8rem; border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); background: var(--surface-2); color: var(--dim); }

.card-error details, .error-note details, #dreid-error details {
  margin-top: 0.5rem;
}

details summary {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--dim);
  cursor: pointer;
}

details p {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--dim);
  white-space: pre-wrap;
  word-break: break-word;
  margin-top: 0.35rem;
}

.dreid-side { display: flex; flex-direction: column; gap: 1.2rem; }

.check-list {
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.check-list li {
  padding: 0.8rem 1rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.88rem;
  color: var(--dim);
}

.check-list li:last-child { border-bottom: none; }

.dreid-links a { color: var(--accent-deep); font-size: 0.86rem; font-weight: 600; }

/* --- step 5: order ---------------------------------------------------------- */

.order-layout {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 2.2rem;
  align-items: start;
}

/* The summary column reads like the product card of the piece being
   ordered and stays in view while the form scrolls. */

.order-summary {
  position: sticky;
  top: 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.order-product {
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
}

.order-product img {
  width: 100%;
  display: block;
  background: var(--surface-2);
}

.order-viewer-slot { min-height: 0; }

.order-product .viewer3d {
  height: min(54vh, 480px);
  border: 0;
  border-radius: 0;
}

.order-product figcaption {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.85rem 1rem 1rem;
}

.order-product-title {
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.order-product-title:empty { display: none; }

.order-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.order-note { font-size: 0.82rem; color: var(--dim); margin-top: 0.7rem; }

.order-form { display: flex; flex-direction: column; gap: 1.15rem;
  width: 100%; max-width: none; min-width: 0; }

.order-form fieldset {
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface);
  padding: 0.9rem 1rem 1rem;
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  box-shadow: var(--shadow-sm);
  min-width: 0;
  overflow: hidden;
}

.order-form legend {
  float: left;
  width: 100%;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--dim);
  padding: 0;
  margin-bottom: 0.55rem;
}

.check-icon {
  display: inline-block;
  margin-right: 0.45rem;
}

.pick {
  border: 1.5px solid var(--line-soft);
  border-radius: 999px;
  background: var(--surface);
  padding: 0.5rem 1.05rem;
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 500;
}

.pick:hover { border-color: var(--line); }

.pick input { position: absolute; opacity: 0; pointer-events: none; }

.pick:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-deeper);
  font-weight: 600;
}

.pick:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The size chooser is the landing page's control, rendered from the same
   shared fragment and styled by size-demo.css. This page had a second one
   of its own (.order-size-choices) until 31.08.2026; it is gone rather
   than restyled, because two controls for one decision is the defect.

   The scene's surface, labels, silhouette fills, scale and ground shadows
   are shared too. This page carried its own drifted copy of all of them,
   which is what made the order step's version look nothing like the one
   on the landing page. Nothing about the component is written here. */

.material-pick span { display: inline-block; text-align: left; white-space: normal; }

.pick-sub {
  display: block;
  font-style: normal;
  font-size: 0.68rem;
  font-weight: 400;
  color: var(--dim);
  margin-top: 0.1rem;
  max-width: 20ch;
}

.order-price {
  background: var(--accent-tint);
  border-radius: var(--r-md);
  padding: 0.9rem 1.1rem;
}

.order-price-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: 650;
}

#order-price-total { font-size: 1.25rem; letter-spacing: -0.01em; }

.order-price-note {
  font-size: 0.74rem;
  color: var(--dim);
  margin-top: 0.35rem;
}

.order-contact { flex-direction: column; }
.order-contact .meta-field { width: 100%; }

.order-qty { flex-basis: 100%; max-width: 130px; }

.order-submit { width: 100%; max-width: none; font-size: 1rem; }

.order-submit-note { margin-top: -0.35rem; text-align: center; }

/* The receipt.

   It used to be an outlined div with a 4px accent bar on top, wedged
   flush under the product card, and the owner called it exactly that on
   31.08.2026. What it needed was separation and a shape of its own, not a
   heavier border: the outline is gone, the elevation and the warm head
   band carry the edge, and a full step of air plus a hairline rule put
   real distance between it and whatever the order layout left standing.

   Register per the design map: warm ground, ink, one accent, mono for
   references and numerals. */
.order-done {
  margin-top: clamp(2.2rem, 6vw, 3.4rem);
  padding-top: clamp(2.2rem, 6vw, 3.4rem);
  border-top: 1px solid var(--line-soft);
  max-width: 640px;
  /* the reveal scrolls this to the top of the viewport; the rule above
     the card has to come with it rather than being cropped away */
  scroll-margin-top: 1.2rem;
}

.order-done-head,
.order-done-body { padding: clamp(1.4rem, 4vw, 2rem); }

.order-done-head {
  background: var(--surface-2);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: inset 0 -1px 0 var(--line-soft);
}

.order-done-body {
  background: var(--surface);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  box-shadow: var(--shadow-md);
}

/* Focus is moved here deliberately on the reveal, so the container must
   not draw a keyboard ring the customer never asked for. */
.order-done:focus { outline: none; }
.order-done:focus-visible { outline: 2px solid var(--accent);
  outline-offset: 3px; border-radius: var(--r-lg); }

.done-kicker {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-bottom: 0.35rem;
}

.order-done h2 {
  font-size: clamp(1.3rem, 3.4vw, 1.6rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
  text-wrap: balance;
}

/* The reference is the part a customer copies into an email, so it reads
   as data: the studio's mono face, tabular figures, its own hairline. */
.done-reference {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line-soft);
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.done-reference:empty { display: none; }

.done-mark {
  width: 46px;
  height: 46px;
  color: var(--accent);
  display: block;
  margin-bottom: 1rem;
}

.done-mark .done-circle,
.done-mark .done-check {
  stroke-dasharray: 160;
  stroke-dashoffset: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .done-mark .done-circle {
    stroke-dashoffset: 160;
    animation: draw-stroke 0.7s ease-out forwards;
  }
  .done-mark .done-check {
    stroke-dashoffset: 160;
    animation: draw-stroke 0.5s ease-out 0.55s forwards;
  }
}

@keyframes draw-stroke { to { stroke-dashoffset: 0; } }

.done-ledger-title {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.9rem;
}

/* A numbered ledger rather than a bulleted list: the four rows are a
   sequence the workshop actually walks, so they are numbered, ruled and
   read in order. */
.done-steps {
  list-style: none;
  counter-reset: done-step;
  padding-left: 0;
  font-size: 0.88rem;
  color: var(--dim);
}

/* The number is placed out of flow rather than given a grid or flex
   column: a row's copy is one <b> followed by a bare text node, and both
   a grid and a flex container would make those two separate items and
   wrap the sentence into the number's column, one word per line. */
.done-steps li {
  counter-increment: done-step;
  position: relative;
  padding: 0.7rem 0 0.7rem 2.4rem;
  border-top: 1px solid var(--line-soft);
  line-height: 1.55;
}

.done-steps li:first-child { border-top: 0; padding-top: 0; }
.done-steps li:last-child { padding-bottom: 0; }

.done-steps li::before {
  content: counter(done-step, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0.7rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--accent-deep);
  line-height: 1.9;
}

.done-steps li:first-child::before { top: 0; }

.done-steps b { color: var(--ink); font-weight: 600; }

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

@media (max-width: 1100px) {
  .stage { grid-template-columns: minmax(0, 1fr); }
  .proof-panel {
    grid-column: 1;
    grid-row: 2;
    position: static;
    margin-bottom: 1.2rem;
  }
  .step { grid-column: 1; grid-row: 3; }
}

/* F03 (10.09.2026) + landscape band (11.09.2026): on a phone the studio
   buried its only task. The 10.09 phone work stopped at 760px, so 761-900px
   (every common phone in landscape and iPad portrait) fell back to a stacked
   rail with the drop target far down the page, and above 900px a phone got
   the literal desktop rail. This one block now covers every short viewport
   up to 1100px wide as well, so a phone in landscape gets the same 44px bar.

   Three moves, all inside this breakpoint, so desktop is untouched: the rail
   becomes one sticky 44px bar carrying the step chips and the account line;
   the stage lays its children out in an explicit order so the H1 and the
   drop target come first; and the account card becomes a single row that can
   give width back, so a long saved project title in the switcher can no
   longer widen the whole page and make a phone zoom the studio out. */
/* Phone project actions (08.10.2026): shown only where the rail hides
   them. */
.phone-actions { display: none; }

@media (max-width: 900px), (max-height: 520px) and (max-width: 1100px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .phone-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 1.2rem;
    margin: 2.5rem 0 1rem;
    padding-top: 1.2rem;
    border-top: 1px solid var(--line-soft);
  }
  .phone-actions .ghost-btn { flex: 1 0 100%; min-height: 44px; }
  .phone-actions a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: var(--dim);
    font-size: 0.9rem;
  }
  /* The bar scrolls sideways with its scrollbar hidden; a fade at the end
     says there is more (phone audit 19). */
  .steps {
    -webkit-mask-image: linear-gradient(90deg, #000 85%, transparent);
    mask-image: linear-gradient(90deg, #000 85%, transparent);
  }
  .rail {
    position: sticky;
    top: 0;
    z-index: 20;
    flex-direction: row;
    align-items: center;
    gap: 0.6rem;
    padding: 0.25rem 0.7rem;
    background: var(--bg);
    border-right: none;
    border-bottom: 1px solid var(--line-soft);
  }
  .rail .brand-mark { width: 1.6rem; height: 1.6rem; }
  .rail .brand { padding: 0; gap: 0.4rem; }
  .rail .brand-name, .rail .brand-sub { display: none; }
  .steps {
    flex: 1 1 auto;
    min-width: 0;
    flex-direction: row;
    gap: 0.1rem;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .steps::-webkit-scrollbar { display: none; }
  .step-link {
    grid-template-columns: auto auto;
    flex: none;
    min-height: 44px;
    padding: 0 0.4rem;
    gap: 0.3rem;
    font-size: 0.78rem;
  }
  .step-link .step-state { display: none; }
  .step-link .step-name { display: none; }
  .step-link[data-active="true"] .step-name { display: inline; }
  /* The rail's card stack collapses to one 44px bar. `flex: none` meant a
     <select> sized to the longest saved project title could not give any
     width back, so the step chips absorbed the whole loss and the page grew
     wider than the phone. Measured at 390x812 with an 80-character title:
     the layout the phone had to show was 953px, drawn at 41 percent. */
  .rail-foot {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 62%;
    overflow: hidden;
    margin: 0;
    padding: 0;
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
  }
  /* #abandon-project-create-btn is injected at runtime after #new-project-btn
     (studio.js), so it was never in this hide list and joined the 44px bar in
     the creation-failure state while its paired retry button was hidden. */
  .rail-foot #integration-chips,
  .rail-foot #new-project-btn,
  .rail-foot #abandon-project-create-btn,
  .rail-foot .rail-links,
  .rail-foot #account-card .account-card-title,
  .rail-foot #account-card #project-remove,
  .rail-foot #signin-card .account-card-title { display: none; }
  /* A grid inside a 44px bar stacked the credit chip, the identity line and
     the switcher, which made the sticky header 117px tall. One row instead. */
  .rail-foot .account-card {
    display: flex;
    flex-direction: row;
    align-items: center;
    width: auto;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    box-shadow: none;
    gap: 0.4rem;
  }
  .rail-foot .account-line {
    display: flex;
    align-items: center;
    flex: 0 1 auto;
    gap: 0.4rem;
    font-size: 0.74rem;
    white-space: nowrap;
    min-width: 0;
    width: auto;
    overflow: hidden;
  }
  /* 06.10.2026: the sign-in control was clipped to "Anmelder". The words
     before it are drawn at font-size 0, but the flex gap after them still
     took 0.4rem, so the line held 69 px of content in a 66 px box and
     overflow: hidden cut the last letter off; at 320 the bar squeezed the
     line to 52 px. Signed out, this button is the only thing in the right
     half of the bar, so the bar keeps the width it needs and the step chips,
     which scroll by design, give way instead. The long-title case above is
     a signed-in one and still gives width back. 44 px tall like the step
     chips beside it. */
  .rail-foot:has(> #signin-card:not([hidden])) { flex-shrink: 0; }
  .rail-foot #signin-card { flex: none; }
  #signin-card .account-line { font-size: 0; gap: 0; overflow: visible; }
  #signin-card #signin-btn {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 0.8rem;
    font-weight: 650;
  }
  .rail-foot #account-card .chip { flex: none; }
  .rail-foot #account-email {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 6rem;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The label text is dropped to font-size 0; the select keeps its aria-label.
     A select with an explicit width does not grow to its longest option, and
     the native picker still shows every title in full. */
  .rail-foot #project-switcher-wrap {
    display: flex;
    align-items: center;
    flex: 1 1 7rem;
    min-width: 4.5rem;
    max-width: 9rem;
    font-size: 0;
  }
  .rail-foot #project-switcher-wrap select {
    width: 100%;
    min-width: 0;
    font-size: 0.72rem;
    text-overflow: ellipsis;
  }
  .rail-note { display: none; }
  /* Four-eyes B3 (11.09.2026): grid, not flex. A column flex container
     sizes its items on their max-content width, so step 2, the Look
     picker, pushed the whole page wide the moment this block started
     covering 761-1100px. Measured with isMobile: 271px of horizontal
     overflow at 768, 278 at 761, 219 at 820, 196 at 844x390, 139 at 900,
     104 at 932x430, 80 at 956x440, 12 at 1024x500, and 661 at 375, which
     the 760px block had carried all along. An explicit single-column grid
     track of minmax(0, 1fr) gives every child a definite maximum instead:
     0 at every one of those widths. The `order` declarations below need
     the placement reset that comes with them to survive the switch. */
  .stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    padding: 1.1rem 1rem 3rem;
  }
  /* The head is two unrelated things on a phone: a live status line, which
     belongs at the top, and the project name, which belongs under the work.
     display:contents lets each take its own place. */
  .stage-head { display: contents; }
  /* Every stage child is auto-placed here, so `order` is what decides the
     sequence. A flex container ignored the explicit `grid-column` and
     `grid-row` the 1100px block gives `.step` and `.proof-panel`; a grid
     container obeys them, and an explicitly placed item ignores `order`,
     which put the proof panel above the H1 the moment this block became
     grid. Resetting the placement to auto is what keeps the phone order. */
  .stage .status {
    order: 1;
    grid-column: auto;
    grid-row: auto;
    margin: 0 0 0.4rem;
  }
  .stage .step { order: 2; grid-column: auto; grid-row: auto; }
  .stage .project-meta {
    order: 3;
    grid-column: auto;
    grid-row: auto;
    width: 100%;
    margin-top: 1.6rem;
    padding-top: 1.2rem;
    border-top: 1px solid var(--line-soft);
  }
  .stage .proof-panel {
    order: 4;
    grid-column: auto;
    grid-row: auto;
    margin: 1.2rem 0 0;
  }
  .refine-layout, .dreid-layout, .order-layout { grid-template-columns: 1fr; }
  .order-summary { position: static; }
  .dropzone { padding: 2.2rem 1.2rem; }
  .finish-color-panel { grid-template-columns: 1fr; }
  #step-fotos:not([hidden]) { display: flex; flex-direction: column; }
  #step-fotos > * { order: 4; }
  #step-fotos > h1 { order: 1; }
  #step-fotos > #dropzone { order: 2; margin-bottom: 1rem; }
  /* Studio audit D20 (08.10.2026): the lead sat below the continue button
     and the photo tips; it belongs right under the upload box. */
  #step-fotos > .step-lead { order: 3; }
  #step-fotos > h1 { font-size: 1.35rem; margin-bottom: 0.8rem; }
  /* The 44 px tap floor for the small controls a phone visitor meets first,
     measured at 390x844 on 06.10.2026: the title suggestion chips were
     27 px tall with 11.5 px text, "Ohne Fotos gestalten" 38 px and the
     consent disclosure's summary 20 px. The summary keeps its list-item marker, so its height
     comes from padding and the disclosure's own top margin moves into it. */
  .title-suggestions .idea-chip { min-height: 44px; font-size: 0.75rem; }
  .text-start .ghost-btn { min-height: 44px; }
  .consent-details { margin-top: 0; }
  .consent-details summary {
    min-height: 44px;
    padding-block: 0.7rem;
  }
}

/* 06.10.2026: signed in on a phone, the bar held the credit chip, the e-mail,
   "Abmelden" and the project switcher in a box capped at 62 percent, and the
   line gave way first: "Abmelden" was cut at every width from 320 to 430 and
   the switcher with it (measured with credits on, the hosted default). In
   this bar the actions stay whole. The e-mail leaves the bar, the chip shows
   only its number and keeps "Guthaben:" for screen readers, and the step
   chips, which scroll by design, give way instead, as they do signed out. */
@media (max-width: 560px) {
  .rail-foot:has(> #account-card:not([hidden])) { flex-shrink: 0; }
  .rail-foot #account-card .account-line { flex: none; overflow: visible; }
  .rail-foot #account-email { display: none; }
  .rail-foot #credits-chip .chip-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .rail-foot #logout-btn {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  /* At 320 the minimum widths summed to about 194 px in a 185 px bar and the
     switcher's arrow was cut; it grows back from its 7rem basis when there
     is room, and the native picker shows every title in full. */
  .rail-foot #project-switcher-wrap { min-width: 3.5rem; }
}

/* Between 761 and 900px there is room for the step names and a wider stage,
   so restore them on top of the shared phone block above. */
@media (min-width: 761px) and (max-width: 900px) {
  .step-link .step-name { display: inline; }
  .stage { padding: 1.4rem 1.15rem 3rem; }
}

@media (max-width: 560px) {
  .viewer-help-grid { grid-template-columns: 1fr; }
}


/* --- account, auth gate, credits -------------------------------------- */

.account-card {
  display: grid;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  width: 100%;
  min-width: 0;
}

.account-card-title {
  color: var(--ink);
  font-size: .68rem;
  font-weight: 750;
  letter-spacing: .08em;
  text-transform: uppercase;
}

@media (max-width: 700px) {
  .look-row { grid-template-columns: 1fr; gap: .5rem; }
  .look-row .pick {
    grid-template-columns: 88px minmax(0, 1fr);
    grid-template-rows: auto;
    justify-items: start;
    align-items: center;
    text-align: left;
    gap: .8rem;
    padding: .8rem .85rem;
    min-height: 104px;
  }
  .look-preview { max-width: 88px; }
  .look-row .pick > span:last-child { padding-right: 4.3rem; }  /* clears the badge */
  .look-row .pick-sub { max-width: none; margin-inline: 0; }
  .finish-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .finish-row legend { grid-column: 1 / -1; }
  .finish-pick { min-height: 148px; padding-inline: .5rem; }
  .project-title-row { align-items: stretch; }
  .project-title-save { width: 100%; }
}

.account-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 8px;
  font-size: 0.78rem;
  color: var(--dim);
  min-width: 0;
  width: 100%;
}

#account-email {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-line .linklike { flex: none; }

/* Journey M6, reversed 11.09.2026: the studio account card no longer renders
   the two data-subject links, so .account-privacy is gone. /datenschutz names
   the served procedure; the export and delete endpoints stay routed. */

.project-switcher {
  display: grid;
  gap: .25rem;
  min-width: 0;
  color: var(--dim);
  font-size: .68rem;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
}

.project-switcher select {
  width: 100%;
  min-width: 0;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--ink);
  padding: .45rem .5rem;
  font: 500 .72rem/1.2 var(--sans);
}

.text-start {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .85rem;
  margin: 1rem 0 0;
  padding: .9rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface);
}

.text-start-icon { color: var(--accent-deep); font-size: 1.2rem; }
.text-start small { display: block; margin-top: .15rem; color: var(--dim); }

.card { position: relative; }
.candidate-remove,
.lineage-remove {
  position: absolute;
  top: .45rem;
  right: .45rem;
  z-index: 3;
  width: 1.8rem;
  height: 1.8rem;
  display: grid;
  place-items: center;
  border: 1px solid rgba(32,29,24,.14);
  border-radius: 50%;
  background: rgba(255,255,255,.92);
  color: var(--dim);
  cursor: pointer;
  font: 600 1rem/1 var(--sans);
  box-shadow: var(--shadow-sm);
}

/* Each round owns its control. Positioning it against the list put every
   round's control in the same spot beside the strip, detached from the
   thumbnail it belongs to. */
.lineage-item { position: relative; display: inline-flex; }

/* Studio backlog (08.10.2026): a branch read «Runde 1, 2, 3, 2». Each
   round now carries its name and, for a refinement, the round it was
   refined from under the thumbnail; the control sits on the thumbnail's
   frame. The item is exactly as wide as the thumbnail («aus Runde 12»
   measures 63 px at 12 px), so the strip keeps four rounds a row in the
   308 px desktop column and the 358 px phone column. */
.lineage-item {
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  width: 66px;
}

.lineage-frame { position: relative; display: inline-flex; }

.lineage-label {
  display: grid;
  justify-items: center;
  max-width: 100%;
  text-align: center;
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--dim);
}

.lineage-label b { color: var(--ink); font-weight: 650; }
.lineage-item:has([aria-current="true"]) .lineage-label b {
  color: var(--accent-deep);
}

.lineage-remove {
  top: .2rem;
  right: .2rem;
  width: 1.35rem;
  height: 1.35rem;
  font-size: .78rem;
}

/* Hover and keyboard focus reveal it on a pointer device so the small
   thumbnail stays readable; touch has no hover, so there it always shows. */
@media (hover: hover) and (pointer: fine) {
  .lineage-remove { opacity: 0; pointer-events: none;
    transition: opacity .12s ease; }
  .lineage-item:hover .lineage-remove,
  .lineage-item:focus-within .lineage-remove {
    opacity: 1;
    pointer-events: auto;
  }
}

.candidate-remove:hover,
.lineage-remove:hover { color: var(--accent-deep); border-color: var(--accent); }

@media (max-width: 640px) {
  .text-start { grid-template-columns: auto minmax(0, 1fr); }
  .text-start .ghost-btn { grid-column: 1 / -1; width: 100%; }
}

/* Removing a project is deliberate, so the confirm sits in the rail
   itself rather than in a browser dialog the customer can dismiss by
   reflex. The question replaces the button in the same place: nothing
   moves under the pointer between the two steps. */
.project-remove {
  display: grid;
  gap: .35rem;
  justify-items: start;
  min-width: 0;
}

.remove-confirm {
  display: grid;
  gap: .4rem;
  width: 100%;
  min-width: 0;
}

#project-remove-question {
  margin: 0;
  color: var(--dim);
  font-size: .76rem;
  line-height: 1.35;
}

.remove-confirm-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
}

.remove-confirm-actions .ghost-btn {
  padding: .3rem .7rem;
  font-size: .76rem;
}

.linklike {
  border: 0;
  background: none;
  font-size: 0.78rem;
  color: var(--accent-deep);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}

.production-note {
  padding: 0.85rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--dim);
  font-size: 0.84rem;
}

.production-note strong { color: var(--ink); }

.auth-gate {
  margin-top: 14px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-md);
}

.auth-gate-lead {
  color: var(--dim);
  font-size: 0.92rem;
  margin-bottom: 12px;
}

.auth-gate-row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}

/* The login honeypot, same rule as business.css. Off-screen, not
   display:none and not hidden: a form filler that only completes rendered
   fields is the one this is meant to catch, and the field has to be there
   for it to find. It keeps its own label, which travels off-screen with
   it, so the input is never an unlabelled control in the tree. */
.honeypot, .honeypot-label {
  position: absolute !important;
  left: -10000px !important;
  width: 1px !important;
  height: 1px !important;
  min-height: 1px !important;
  opacity: 0 !important;
}

.auth-waiting-line {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--dim);
  font-size: 0.92rem;
}

.auth-waiting-actions {
  display: flex;
  gap: 10px;
  margin-top: 12px;
  flex-wrap: wrap;
}

.auth-code-row {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin-top: 14px;
  flex-wrap: wrap;
}

.auth-code-row input {
  width: 9rem;
  font: 700 1.15rem/1 var(--sans);
  letter-spacing: .2em;
  text-align: center;
}

.cost-note {
  margin-top: 8px;
  font-size: 0.8rem;
  color: var(--faint);
}

.credits-panel {
  margin-top: 14px;
  padding: 16px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  display: grid;
  gap: 8px;
  font-size: 0.92rem;
}

.credits-panel-lead { font-weight: 600; }

.credits-panel-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.share-panel {
  margin-top: 18px;
  padding: 14px;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-md);
  background: var(--surface-2);
  text-align: left;
}

.share-panel-lead {
  font-size: 0.88rem;
  color: var(--dim);
  margin-bottom: 10px;
}

.share-panel-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.share-link-input {
  flex: 1 1 220px;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  font-family: var(--mono);
  font-size: 0.8rem;
  color: var(--dim);
}

.share-list {
  display: grid;
  gap: 9px;
  margin-top: 10px;
}

.share-link-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.quiet-danger-btn {
  border: 0;
  background: transparent;
  color: var(--accent-dark);
  font: inherit;
  font-size: 0.8rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.quiet-danger-btn:disabled { opacity: 0.5; cursor: wait; }

/* Audit 11.09.2026: iOS Safari zooms the page whenever a focused control
   carries a font smaller than 16px, and that zoom survives the blur. The
   sheet's control size is 0.95rem, so 15.2px, and the first tap on the
   e-mail field in the auth gate left a phone visitor zoomed in with the
   sticky 44px rail and the stage off screen, at every step of the funnel.
   The viewport meta carries no maximum-scale, so nothing else held it back.
   This block sits at the end of the sheet on purpose: .share-link-input is
   defined further down than the phone block above and would otherwise keep
   its 0.8rem. The project switcher in the rail keeps its own 0.72rem, since
   it wins on specificity and 16px there would widen the 44px bar. */
@media (max-width: 900px), (max-height: 520px) and (max-width: 1100px) {
  input, select { font-size: 16px; }
  .share-link-input { font-size: 16px; }
}

/* The status line's words, repeated at the bottom of the screen while the
   line itself is scrolled away (08.10.2026). */
.status-toast {
  position: fixed;
  left: 50%;
  bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  z-index: 80;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(32rem, calc(100vw - 2rem));
  margin: 0;
  padding: 0.75rem 1rem;
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--surface);
  font-size: 0.92rem;
  line-height: 1.4;
  box-shadow: var(--shadow-lg);
  pointer-events: none;
}

.status-toast.error { background: var(--accent-deeper); color: var(--on-accent); }
.status-toast[hidden] { display: none; }

/* --- phone pass, 08.10.2026: touch targets and the 12 px floor ------------- */

/* The Studio audit measured these under 44 px on a phone: idea chips 29,
   «Auswählen» 30, «Inspiration ansehen» 35, the retry 38, the first
   likeness option 29, the round removes 22 to 29, the info buttons 32 and
   the colour swatches 28. The small round controls keep their size and get
   a larger invisible hit area. */
@media (pointer: coarse) {
  .idea-chip,
  .pick-btn,
  .style-more,
  #dreid-retry-same,
  .likeness-option {
    min-height: 44px;
  }
  .idea-chip,
  .pick-btn,
  .style-more { display: inline-flex; align-items: center; }
  /* Studio backlog (08.10.2026): the round's remove control and the 3D
     help button are absolutely positioned, which already anchors their
     hit area. Made relative here they fell into the flow: the remove
     control sat beside its thumbnail instead of on it and pushed the
     strip apart, and the help button moved to the viewer's top left
     corner, half cut off. Step 2's remove control keeps the in-flow place
     in the card footer it took then. */
  .candidate-remove,
  .info-trigger,
  .viewer3d-tool,
  .finish-color-swatches button { position: relative; }
  .candidate-remove::after,
  .lineage-remove::after,
  .info-trigger::after,
  .viewer-info::after,
  .finish-color-swatches button::after {
    content: "";
    position: absolute;
    inset: -8px;
  }
  /* 36 px buttons with 8 px between them: 44 px each without overlap. */
  .viewer3d-tool::after {
    content: "";
    position: absolute;
    inset: -4px;
  }
}

/* Text under 12 px: the credit and order chips at 10.9, the photo tips at
   11.5. */
.chip { font-size: 0.75rem; }
.tip-item figcaption { font-size: 0.78rem; }

/* «Was als Nächstes passiert» took the mono face from the global details
   rule. */
.proof-next { font-family: var(--sans); }

.drop-touch { display: none; }
@media (hover: none) and (pointer: coarse) {
  .drop-pointer { display: none; }
  .drop-touch { display: inline; }
}
