:root {
  --bg: #fff6e4;
  --surface: #ffffff;
  --surface-2: #f7e7c4;
  --nav: #241a10;
  --text: #2b1b0e;
  --text-muted: #7a5b40;
  --border: #e9d3a0;
  --accent: #f0790e;
  --accent-light: #ffb35c;
  --accent-dark: #d9650a;
  --accent-deep: #b8500a;
  --green: #3f7d3a;
  --danger: #b3261e;
  --shadow: 0 2px 0 var(--accent-deep), 0 12px 26px -12px rgba(184, 80, 10, 0.45);
}

* {
  box-sizing: border-box;
}

html,
body,
#root {
  height: 100%;
}

body {
  margin: 0;
  font-family: 'Nunito Sans', -apple-system, sans-serif;
  color: var(--text);
  background: var(--bg);
}

h1,
h2,
h3 {
  font-family: 'Fredoka', sans-serif;
  font-weight: 600;
  margin: 0;
}

p {
  margin: 0;
}

a {
  color: var(--accent-dark);
  text-decoration: none;
}

button,
input,
select {
  font-family: inherit;
}

.mono {
  font-family: 'IBM Plex Mono', monospace;
}

.kicker {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
}

.btn-primary {
  background: linear-gradient(180deg, var(--accent-light) 0%, var(--accent) 55%, var(--accent-dark) 100%);
  border: 1px solid var(--accent-dark);
  color: #fff;
  font-family: 'Fredoka', sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-radius: 11px;
  padding: 0.9em 1.7em;
  font-size: 0.95rem;
  box-shadow: var(--shadow);
  cursor: pointer;
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-secondary {
  background: var(--surface);
  border: 1.5px solid var(--border);
  color: var(--text);
  font-weight: 700;
  border-radius: 11px;
  padding: 0.85em 1.4em;
  font-size: 0.95rem;
  cursor: pointer;
}

.btn-text {
  background: transparent;
  border: none;
  color: var(--accent-dark);
  font-weight: 700;
  font-size: 0.88rem;
  cursor: pointer;
  padding: 0;
}

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

.page-shell {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 56px;
  background: var(--nav);
}

.nav a {
  color: var(--accent-light);
  font-weight: 700;
  font-size: 0.94rem;
  margin-right: 32px;
}

.nav-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #fbf1d9;
  font-family: 'Fredoka', sans-serif;
  font-weight: 700;
  font-size: 1.2rem;
  text-shadow: 0 0 14px rgba(240, 121, 14, 0.55);
}

.nav-logo span {
  color: var(--accent-light);
}

.avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--accent-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85rem;
}

.container {
  max-width: 1100px;
  margin: 0 auto;
  width: 100%;
  padding: 48px 24px 64px;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.field label {
  font-weight: 700;
  font-size: 0.9rem;
}

.field input {
  border: 1.5px solid var(--border);
  border-radius: 9px;
  padding: 0.75em 1em;
  font-size: 1rem;
  background: var(--surface);
  color: var(--text);
}

.error-banner {
  background: #fdecea;
  border: 1px solid var(--danger);
  color: var(--danger);
  border-radius: 10px;
  padding: 12px 16px;
  font-size: 0.9rem;
}

.pill-btn {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: 9px;
  padding: 0.7em 1.3em;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
}

.pill-btn.selected {
  background: var(--surface-2);
  border-color: var(--accent);
  color: var(--accent-dark);
}

.complexity-card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: 14px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
  cursor: pointer;
  text-align: left;
}

.complexity-card.selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--surface-2);
}

.stat-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
}

.stat-num {
  font-family: 'IBM Plex Mono', monospace;
  font-variant-numeric: tabular-nums;
  font-size: 1.6rem;
  font-weight: 500;
  color: var(--accent-dark);
}

/* ---------------------------------------------------------------------------
   Server-rendered pages (Thymeleaf + htmx)

   Everything below styles the markup in src/main/resources/templates. The rules
   above came from the earlier React app and are shared.
   --------------------------------------------------------------------------- */

.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #fbf1d9;
  font-family: 'Fredoka', sans-serif;
  font-weight: 700;
  font-size: 1.2rem;
  margin-right: 0;
  text-shadow: 0 0 14px rgba(240, 121, 14, 0.55);
}

/* On the home page the logo is not a link, so it should not invite a click. */
.nav-brand:not([href]) { cursor: default; }

.nav-brand strong { color: var(--accent-light); }
.nav-brand-mark { font-size: 1.3rem; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav-logout { margin: 0; }
.nav-logout .btn-text { color: var(--accent-light); }

.container.narrow { max-width: 520px; }

.page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 32px;
}

.page-title {
  font-size: 2rem;
  color: var(--accent-dark);
}

.page-subtitle {
  color: var(--text-muted);
  font-weight: 600;
  margin-top: 6px;
}

.form-card {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 28px;
  margin-top: 24px;
}

/* The code step is one box: the form, then the way to ask for another code under it. */
.stacked-form { display: flex; flex-direction: column; gap: 20px; }
.resend-form { text-align: center; }

.form-card input {
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 9px;
  font: inherit;
  background: var(--surface);
}

.form-note {
  color: var(--text-muted);
  font-weight: 600;
}

.code-input {
  font-size: 1.5rem;
  letter-spacing: 0.4em;
  text-align: center;
}

.empty-state {
  padding: 40px;
  text-align: center;
  color: var(--text-muted);
}

.stencil-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 24px;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  margin-top: 32px;
}

.pagination-status {
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.92rem;
}

.stencil-card {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Just the stencil, on its pumpkin. */
.stencil-thumbs {
  display: grid;
  grid-template-columns: 1fr;
  height: 220px;
}

.thumb {
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}

.thumb + .thumb { border-left: 1px solid var(--border); }

.thumb img {
  max-width: 90%;
  max-height: 90%;
  object-fit: contain;
  border-radius: 12px;
}

.thumb-label {
  position: absolute;
  left: 8px;
  bottom: 6px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: rgba(255, 250, 240, 0.82);
  padding: 1px 6px;
  border-radius: 4px;
}

/* The body fills the card and the View button sits at its foot, so every card's button lines up. */
.stencil-card-body {
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.stencil-card-body > :last-child { margin-top: auto; }

.stencil-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

/* Always two lines tall -- a longer name ends in an ellipsis, the full name on hover -- so cards in different rows match too. */
.stencil-name {
  font-weight: 700;
  word-break: break-word;
  line-height: 1.35;
  min-height: 2.7em;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.btn-secondary.full {
  text-align: center;
  margin-top: 6px;
}

/* Confirmation dialog ------------------------------------------------------ */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(31, 20, 8, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 50;
}

.modal {
  padding: 28px;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.modal-head {
  display: flex;
  gap: 16px;
  align-items: center;
}

.modal-thumb {
  width: 64px;
  height: 64px;
  object-fit: contain;
  background: var(--surface-2);
  border-radius: 8px;
}

.modal-title {
  font-size: 1.2rem;
  color: var(--accent-dark);
}

.modal-filename {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.9rem;
  word-break: break-word;
}

.modal-body {
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.9rem;
  line-height: 1.5;
}

.modal-actions {
  display: flex;
  gap: 12px;
  justify-content: flex-end;
}

/* Home ---------------------------------------------------------------------- */

/* The headline sits as far below the header as the image sits below it. */
.hero {
  display: flex;
  flex-direction: column;
  gap: 32px;
  padding-top: 32px;
  max-width: 1280px;
}

/* 80% of the page width, centred; a phone keeps the full width, where the picture is small already. */
.hero-image { width: 80%; height: auto; border-radius: 16px; display: block; }
@media (max-width: 600px) {
  .hero-image { width: 100%; }
}

/* The image with its caption close under it. */
.hero-figure { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.hero-caption { font-size: 0.85rem; color: var(--text-muted); text-align: center; }

.hero-copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.hero-title {
  text-align: center;
  font-size: clamp(1.4rem, 3.4vw, 2.6rem);
  line-height: 1.1;
  color: var(--accent-dark);
  white-space: nowrap;
}

.hero-text {
  font-size: 1.1rem;
  color: var(--text-muted);
  font-weight: 600;
}

/* Start carving: a little larger than other primary buttons, with more room above it than between the other lines. */
.hero-copy > :last-child { margin-top: 12px; }
.hero-copy .btn-primary { display: inline-block; font-size: 1.1rem; padding: 1em 2.2em; }

/* How it works, as three numbered steps: left-aligned so the numbers line up, the list itself centred under the image. */
.hero-steps {
  text-align: left;
  margin: 0;
  padding-left: 1.4em;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* On a phone the whole pitch has to fit above the fold, button included, so the hero closes up:
   a smaller headline and tighter gaps, which together save about 60px. */
@media (max-width: 600px) {
  .hero-title {
    white-space: normal;
    font-size: 1.6rem;
  }

  .hero { gap: 20px; padding-top: 20px; }
  .hero-copy { gap: 16px; }
  .hero-copy > :last-child { margin-top: 4px; }
}

.section-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 32px;
}

.section-title { font-size: 1.7rem; color: var(--accent-dark); }

.step-grid,
.example-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.example-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; }

.step-card { padding: 28px; display: flex; flex-direction: column; gap: 12px; }

.step-number {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
}

.step-title { font-size: 1.05rem; }
.step-text { color: var(--text-muted); font-weight: 600; font-size: 0.92rem; line-height: 1.5; }

.band { background: var(--surface-2); padding: 8px 0 48px; }

.example-card { padding: 18px; display: flex; flex-direction: column; gap: 8px; }
.example-title { font-weight: 700; }
.example-meta { font-size: 0.82rem; color: var(--text-muted); }

.site-footer {
  background: var(--nav);
  color: #c9b896;
  padding: 28px 56px;
  font-weight: 600;
  font-size: 0.85rem;
}

/* Preview ------------------------------------------------------------------- */

.preview-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  gap: 32px;
}

@media (max-width: 820px) {
  .preview-layout { grid-template-columns: 1fr; }
}

.panel { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.panel-head { padding: 16px 20px; border-bottom: 1px solid var(--border); }

.panel-body {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  background: var(--surface-2);
  min-height: 160px;
}

.panel-body.large { padding: 40px; min-height: 320px; }
.panel-body img { max-width: 100%; max-height: 200px; }
.panel-body.large img { max-height: 420px; }

.preview-side { display: flex; flex-direction: column; gap: 24px; }
.download-block { display: flex; flex-direction: column; gap: 12px; }

.download-note {
  font-size: 0.8rem;
  color: var(--text-muted);
  text-align: center;
  font-weight: 600;
}

.btn-primary.full,
.btn-secondary.full { display: block; text-align: center; }

/* Upload -------------------------------------------------------------------- */

.upload-page { max-width: 720px; display: flex; flex-direction: column; gap: 40px; }
.upload-form { display: flex; flex-direction: column; gap: 40px; }
.form-section { display: flex; flex-direction: column; gap: 14px; }

.form-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; }
@media (max-width: 640px) { .form-columns { grid-template-columns: 1fr; } }

.dropzone {
  padding: 18px;
  display: block;
  cursor: pointer;
  border: 1px solid var(--border);
}

/* Empty: one dashed invitation with a single instruction. */
.dropzone:has(#dropzone-empty:not([hidden])) {
  border: 2px dashed var(--border);
  background: var(--surface);
}

.dropzone.is-dragging {
  border: 2px dashed var(--accent);
  background: var(--surface-2);
}

.dropzone-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 26px 18px;
  text-align: center;
}

.dropzone-icon {
  width: 34px;
  height: 34px;
  fill: none;
  stroke: var(--text-muted);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.dropzone-lead {
  margin: 0;
  font-family: 'Fredoka', sans-serif;
  font-size: 1.05rem;
  color: var(--text);
}

.dropzone-hint { margin: 0; font-size: 0.85rem; color: var(--text-muted); font-weight: 600; }

/* Chosen: a compact row saying which picture is going up. */
.dropzone-chosen {
  display: flex;
  align-items: center;
  gap: 18px;
}

/* Both halves lay themselves out with flex, which would otherwise beat [hidden]. */
.dropzone-empty[hidden],
.dropzone-chosen[hidden] { display: none; }

.dropzone-preview {
  width: 64px;
  height: 64px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--surface-2);
  flex-shrink: 0;
}

.dropzone-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }

/* On a phone the row is tight, so the Replace button gives its room to the filename. */
@media (max-width: 640px) {
  .dropzone-chosen { gap: 12px; }
  .dropzone-preview { width: 52px; height: 52px; }
  .dropzone-replace { padding: 0.55em 0.9em; font-size: 0.85rem; }
}
.dropzone-name { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dropzone-size { font-size: 0.85rem; color: var(--text-muted); font-weight: 600; }
.dropzone-replace { flex-shrink: 0; }

.dropzone-error {
  margin: 14px 0 0;
  color: var(--danger);
  font-size: 0.9rem;
  font-weight: 600;
  text-align: center;
}

.choice-row { display: flex; gap: 18px; flex-wrap: wrap; }

/* Radios are hidden; the label itself is the control, and :has() styles the
   selected one. */
.choice-row input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }

.complexity-card { cursor: pointer; flex: 1 1 180px; }
.complexity-card:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.complexity-card.is-disabled { opacity: 0.45; cursor: not-allowed; }

.choice-title {
  font-family: 'Fredoka', sans-serif;
  font-weight: 600;
  font-size: 1.05rem;
  display: block;
}

.complexity-card:has(input:checked) .choice-title { color: var(--accent-dark); }
.choice-text { color: var(--text-muted); font-size: 0.9rem; line-height: 1.5; font-weight: 600; }

.pill-btn { cursor: pointer; }
.pill-btn:has(input:checked) { border-color: var(--accent); color: var(--accent-dark); }

/* The button with its status line beneath it, both to the right. The column direction used to live in a bare rule
   stranded after the phone media query, where it read as a mistake and quietly contradicted this block. */
.form-footer {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: flex-end;
}

/* What to expect from your own picture: three lines below the button. Deliberately plain -- it is there to be
   read before choosing a picture, not to compete with the one thing to do (choose one) or the one thing to press.
   It sits after the button so that button stays above the fold on a phone, where these three lines wrap to nine.
   The upload form's own 40px gap spaces it. */
.guidance {
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-top: 1px solid var(--border);
  padding-top: 32px;
}

.guidance-title {
  margin: 0;
  font-family: 'Fredoka', sans-serif;
  font-size: 1rem;
  color: var(--text);
}

.guidance-list {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.45;
}

/* The lead sits on its own line, the explanation under it: three short headings to scan, each with its detail
   below, rather than three run-on sentences. */
.guidance-list strong { display: block; color: var(--text); font-weight: 700; }

/* The marker sits beside the lead, so it takes the lead's colour rather than the muted one the list carries for
   the explanation underneath. */
.guidance-list li::marker { color: var(--text); }

/* The upload and the start of the job take a few seconds on a phone, so the button says it is working. */
.submit-btn {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
}

/* How much of the picture has gone up, painted behind the button's label. */
.submit-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  background: rgba(255, 255, 255, 0.28);
  transition: width 0.2s linear;
}

.submit-spinner,
.submit-label { position: relative; }

.submit-btn.is-sending .submit-spinner { display: none; }

.submit-status {
  margin: 12px 0 0;
  color: var(--danger);
  font-size: 0.9rem;
  text-align: right;
}

.submit-spinner { display: none; }

.submit-btn.is-busy .submit-spinner {
  display: block;
  width: 1em;
  height: 1em;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: submit-spin 0.7s linear infinite;
}

.submit-btn.is-busy:disabled { opacity: 0.85; }

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

@media (prefers-reduced-motion: reduce) {
  .submit-btn.is-busy .submit-spinner { animation-duration: 2.5s; }
}

@media (max-width: 640px) {
  .guidance { padding-top: 24px; }
  .submit-btn { width: 100%; }
}

/* Generating ---------------------------------------------------------------- */

.generating {
  padding: 40px;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
}

/* The step being done reads as a sentence, so it takes the page's heading face rather than the
   monospace reserved for the small uppercase labels. */
.generating-title { font-family: 'Fredoka', sans-serif; font-weight: 600; color: var(--text-muted); }
.generating-note { color: var(--text-muted); font-size: 0.85rem; }

/* ---------- Generating: the stencil being made, step by step ---------- */
.progress-page { max-width: 1240px; }

.generating-progress {
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.progress-head { display: flex; flex-direction: column; gap: 4px; }

.progress-bar {
  height: 12px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent-light), var(--accent));
  transition: width 0.6s ease;
}

.stage-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 12px;
}

.stage-slot { display: flex; flex-direction: column; gap: 6px; }

.stage-frame {
  aspect-ratio: 1 / 1;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stage-frame img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  opacity: 0;
  transform: scale(0.97);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.stage-slot.revealed .stage-frame { background: var(--surface); }
.stage-slot.revealed .stage-frame img { opacity: 1; transform: none; }
.stage-slot.current .stage-frame { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-light); }
.stage-slot.skipped .stage-frame { opacity: 0.4; }

.stage-caption { font-size: 0.75rem; color: var(--text-muted); line-height: 1.25; }
.stage-slot.revealed .stage-caption { color: var(--text); }

.generating-failed { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }

@media (max-width: 960px) {
  .stage-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
  .stage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .generating-progress { padding: 18px; }
}

/* The next step to be shown: a bar sliding back and forth across its frame until its picture appears. */
.stage-frame { position: relative; }

.stage-slot.working .stage-frame { border-color: var(--accent-light); }

.stage-slot.working .stage-frame::before,
.stage-slot.working .stage-frame::after {
  content: "";
  position: absolute;
  left: 12%;
  right: 12%;
  top: 50%;
  height: 6px;
  margin-top: -3px;
  border-radius: 999px;
}

.stage-slot.working .stage-frame::before { background: var(--border); }

.stage-slot.working .stage-frame::after {
  right: auto;
  width: 30%;
  background: var(--accent);
  animation: stage-working 1.2s ease-in-out infinite alternate;
}

@keyframes stage-working {
  from { transform: translateX(0); }
  to { transform: translateX(153%); }
}

@media (prefers-reduced-motion: reduce) {
  .stage-slot.working .stage-frame::after { animation-duration: 3s; }
}

/* The failure message is hidden until a job fails; its display:flex above would otherwise override [hidden]. */
.generating-failed[hidden] { display: none; }

button.btn-primary.full { width: 100%; }

/* The stencil (glowing cuts on a transparent ground) over a darkened pumpkin, centred on its face, like a lit carving. */
.stencil-view {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: 12px;
  overflow: hidden;
}
.stencil-view::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("/img/pumpkin-f1f33755d5fc7beb556bd14dafab1de9.png") center / cover no-repeat;
  filter: brightness(0.68) saturate(1.15);
}
.stencil-view img,
.thumb .stencil-view img,
.panel-body .stencil-view img,
.panel-body.large .stencil-view img {
  position: absolute;
  left: 50%;
  top: 53%;
  transform: translate(-50%, -50%);
  max-width: 72%;
  max-height: 72%;
  object-fit: contain;
  border-radius: 0;
  filter: drop-shadow(0 0 2px rgba(255, 170, 40, 0.9)) drop-shadow(0 0 6px rgba(255, 140, 20, 0.6));
}
.thumb .stencil-view { height: 90%; }
.panel-body .stencil-view { width: min(100%, 480px); }
.modal-thumb.stencil-view { flex: none; border-radius: 8px; }

/* Terms of Service and Privacy Policy. */
.legal { max-width: 760px; padding-bottom: 64px; }
.legal h2 { font-size: 1.15rem; margin: 32px 0 8px; }
.legal p, .legal li { line-height: 1.6; }
.legal ul { padding-left: 22px; margin: 8px 0 12px; }
.legal li + li { margin-top: 6px; }
.legal .caps { text-transform: uppercase; font-size: 0.86rem; letter-spacing: 0.01em; }
.legal a, .legal-consent a { color: var(--accent); }
.legal-consent { font-size: 0.85rem; color: var(--text-muted); margin: 0; }
mark.placeholder { background: #ffe58a; padding: 0 3px; border-radius: 3px; }
.site-footer a { color: inherit; }

/* "Keep this stencil" on an unsaved stencil. */
/* Like the Stencil and PDF Preview boxes: a white header bar over a tinted body. */
.save-card .panel-body {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 12px;
  padding: 20px;
  min-height: 0;
}
.save-text { margin: 0; color: var(--text-muted); line-height: 1.5; font-size: 0.92rem; }
button.btn-secondary.full { width: 100%; }

/* Account page. */
.account-actions { display: flex; gap: 12px; justify-content: flex-end; align-items: center; }
.btn-primary.danger { background: var(--danger); box-shadow: none; }
.notice-banner { background: #eaf6ea; border: 1px solid #9cc79c; color: #2d5a2d; padding: 12px 16px; border-radius: 10px; font-weight: 600; margin: 16px 0 0; }
.download-note a { color: var(--accent); }

/* Logo banner on the left, filling the nav bar top to bottom, with the links on the right. */
.nav { padding-top: 0; padding-bottom: 0; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 16px; }
.nav > .nav-brand { grid-column: 1; justify-self: start; min-width: 0; margin: 0; }
.nav > .nav-links { grid-column: 2; gap: 24px; }
.nav .nav-links a { margin-right: 0; white-space: nowrap; }
.nav .nav-links .btn-text { white-space: nowrap; }
.nav-logo-image { display: block; height: 72px; width: auto; max-width: 100%; object-fit: contain; object-position: left center; }

/* Phones: the logo across the top on its own row when signed in (more links), beside Sign in otherwise. */
@media (max-width: 600px) {
  .nav { padding-left: 12px; padding-right: 12px; }
  .nav-logo-image { height: auto; max-height: 56px; }
  .nav.nav-signed-in { grid-template-columns: 1fr; padding: 0; }
  .nav.nav-signed-in > .nav-brand { justify-self: stretch; }
  .nav.nav-signed-in .nav-logo-image { width: 100%; max-height: 80px; object-position: center; }
  .nav.nav-signed-in > .nav-links { grid-column: 1; justify-self: center; gap: 18px; padding: 8px 12px; flex-wrap: wrap; justify-content: center; }
}

/* Footer at the bottom of the window on short pages, after the content on long ones. */
body { display: flex; flex-direction: column; min-height: 100vh; height: auto; }
body > main { flex: 1 0 auto; }

/* Error page (404, 500, ...) */
.error-page { text-align: center; padding-top: 64px; padding-bottom: 64px; }
.error-page-code { font-family: 'IBM Plex Mono', monospace; font-size: 0.95rem; color: var(--text-muted); margin: 0 0 8px; }
.error-page .page-subtitle { margin-bottom: 28px; }
.error-page .btn-primary { display: inline-block; text-decoration: none; }

/* ---------- Stencil page: the finished stencil above the steps that made it ---------- */
.stencil-page .preview-layout { margin-bottom: 48px; }
/* "How it was made" is a box like Stencil and PDF Preview: the same header bar and kicker, the same tinted body. */
.stencil-steps-body { background: var(--surface-2); }
.progress-head[hidden], .stencil-steps > .panel-head[hidden] { display: none; }

/* Side by side, the download controls sit at the bottom of the side column, so the Download PDF button (or, before the
   stencil is saved, the save card) ends level with the preview box. The terms note hangs below the button so it does
   not count. */
@media (min-width: 821px) {
  /* The save card sits under the side column in its own row, so the Stencil box ends level with the
     Download PDF button whether or not the stencil is saved. */
  .stencil-page .preview-layout > .panel:not(.save-card),
  .stencil-page .preview-side { grid-row: 1; }
  .stencil-page .save-card { grid-column: 2; grid-row: 2; }
  .stencil-page .preview-side { justify-content: flex-end; }
  /* The PDF preview box grows to fill the column, so it starts level with the Stencil box as well. */
  .stencil-page .preview-side .pdf-preview { flex: 1; }
}
/* Uploaded filenames can be one long word; let it wrap instead of pushing the page sideways on a phone. */
.stencil-page .page-head > div { min-width: 0; }
.stencil-page .page-title {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- PDF preview: the printed Letter sheets at the chosen pumpkin size (see pdf-preview.js) ---------- */
.pdf-preview .panel-body { flex-direction: column; gap: 10px; }
/* The size chooser and the download button share the printed sheet's box, which is narrower than the
   column was, so the three sizes share its width evenly rather than wrapping. */
.pdf-preview .download-block { width: 100%; margin-top: 6px; }
.pdf-preview .choice-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.pdf-preview .pill-btn { padding: 0.6em 0.4em; text-align: center; font-size: 0.9rem; }
/* The sheets are drawn inside a stage of fixed height, scaled to fit it, so choosing a size never changes the height of
   the column -- and so of the row, and everything below it. */
.pdf-stage {
  width: 100%;
  height: 320px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
}
.pdf-pages {
  display: grid;
  grid-template-columns: repeat(var(--columns, 1), minmax(0, 1fr));
  gap: 6px;
  width: 240px;
}
.pdf-sheet {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 8.5 / 11;
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: 0 2px 10px rgba(60, 30, 0, 0.12);
}
.pdf-print-area { position: absolute; overflow: hidden; }
.pdf-print-area img.pdf-stencil {
  position: absolute;
  max-width: none;
  max-height: none;
  filter: brightness(0);
  transition: width 0.25s ease, height 0.25s ease, left 0.25s ease, top 0.25s ease;
}
.pdf-guide { position: absolute; display: none; }
.pdf-guide.shown { display: block; }
.pdf-guide.across { top: 0; bottom: 0; border-left: 1px dashed #777; }
.pdf-guide.down { left: 0; right: 0; border-top: 1px dashed #777; }
.pdf-footer {
  position: absolute;
  font-family: Helvetica, Arial, sans-serif;
  font-size: calc(900cqw / var(--sheet-w-pt, 612));
  line-height: 1;
  color: #000;
  white-space: nowrap;
}
/* The caption is always two lines tall, whether its text takes one line or two. */
.pdf-caption {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  text-align: center;
  line-height: 1.3;
  height: 2.6em;
}
@media (prefers-reduced-motion: reduce) {
  .pdf-print-area img.pdf-stencil { transition: none; }
}

/* The pumpkin and the printed sheet both sit at the top of their boxes, level with each other. */
.stencil-page .preview-layout > .panel .panel-body.large { align-items: flex-start; padding: 24px; }
.stencil-page .pdf-preview .panel-body { justify-content: flex-start; padding: 24px; }

/* ---------- A stencil being made: one picture turning into the carving (stencil-steps.js) ---------- */
.morph .progress-head { align-items: center; text-align: center; }
/* The dots under the picture already count the steps, so the line carries a spinner rather than a second bar. */
.progress-line { display: inline-flex; align-items: center; gap: 8px; }
.progress-spinner {
  width: 0.95em;
  height: 0.95em;
  flex: none;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: submit-spin 0.8s linear infinite;
}
.progress-spinner[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .progress-spinner { animation-duration: 3s; }
}
.morph-stage {
  position: relative;
  width: min(100%, 640px);
  aspect-ratio: 4 / 3;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
.morph-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transform: scale(1.03);
  transition: opacity 0.9s ease, transform 1.4s ease;
}
.morph-layer.shown { opacity: 1; transform: none; }
.morph-layer.pumpkin.shown { filter: drop-shadow(0 0 22px rgba(255, 160, 40, 0.55)); }

/* While the physics runs: the cuts of the draft slowly glow like candlelight (screen blending leaves the rind white),
   and the rind near them is pressed, with a ripple and a tick where it holds. */
.morph-stage { isolation: isolate; }
.morph-glow {
  position: absolute;
  pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(ellipse at 50% 45%, #ffe29a 0%, #ffae42 45%, #ff7a1a 100%);
  --reveal: 0%;
  -webkit-mask-image: linear-gradient(90deg, #000 var(--reveal), transparent calc(var(--reveal) + 14%));
  mask-image: linear-gradient(90deg, #000 var(--reveal), transparent calc(var(--reveal) + 14%));
  transition: opacity 0.9s ease;
}
.morph-glow.leaving { opacity: 0; }
.tap { position: absolute; width: 28px; height: 28px; margin: -14px 0 0 -14px; pointer-events: none; }
.tap::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 138, 31, 0.9);
  animation: tap-ring 1.1s ease-out forwards;
}
.tap::after {
  content: "\2713";
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: #1b8f3e;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  animation: tap-tick 1.3s ease forwards;
}
@keyframes tap-ring {
  from { transform: scale(0.3); opacity: 1; }
  to { transform: scale(1.6); opacity: 0; }
}
@keyframes tap-tick {
  0%, 45% { opacity: 0; transform: scale(0.5); }
  60% { opacity: 1; transform: scale(1.1); }
  85% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; }
}

/* As the final stencil appears, each bridge lights up in turn. */
.spark {
  position: absolute;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, #fff 0%, #ffd27a 35%, rgba(255, 138, 31, 0) 70%);
  animation: spark-pop 1.2s ease-out forwards;
}
@keyframes spark-pop {
  0% { transform: scale(0.2); opacity: 0; }
  30% { transform: scale(1.5); opacity: 1; }
  100% { transform: scale(0.8); opacity: 0; }
}


.morph-dots { list-style: none; margin: 0; padding: 0; display: flex; justify-content: center; gap: 10px; }
.morph-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: var(--surface);
  transition: background 0.4s ease, border-color 0.4s ease;
}
.morph-dot.done { background: var(--accent); border-color: var(--accent); }
.morph-dot.next { border-color: var(--accent); animation: dot-pulse 1.2s ease-in-out infinite alternate; }
@keyframes dot-pulse { from { transform: scale(1); } to { transform: scale(1.25); } }

/* Under "How it was made", the strength check's picture carries a tick. */
.stage-slot.checked .stage-frame::after {
  content: "\2713";
  position: absolute;
  right: 6px;
  bottom: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 0.8rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
@media (prefers-reduced-motion: reduce) {
  .morph-layer { transition: none; transform: none; }
  .morph-stage.checking::after, .morph-dot.next { animation: none; }
}

/* The stencil being made sits in the Stencil box, filling it; when it is done it hands over to the finished stencil. */
.stencil-page .panel-body.large .stencil-making { width: 100%; display: flex; flex-direction: column; gap: 14px; }

/* Phones: the stencil page closes up, so the printed sheet and its Download button are not a screen away.
   The picture of the carved pumpkin is the page's showpiece, but it should not push everything else off. */
@media (max-width: 600px) {
  .stencil-page.container { padding-top: 20px; padding-bottom: 32px; }
  .stencil-page .page-title { font-size: 1.35rem; line-height: 1.2; }
  .stencil-page .page-head { margin-bottom: 12px; gap: 4px; }
  .stencil-page .preview-layout { gap: 14px; margin-bottom: 24px; }
  .stencil-page .panel-head { padding: 10px 14px; }
  .stencil-page .preview-layout > .panel .panel-body.large { padding: 12px; min-height: 0; }
  .stencil-page .pdf-preview .panel-body { padding: 12px; gap: 8px; }
  .stencil-page .panel-body.large .stencil-view { width: min(100%, 300px); }
  .stencil-page .save-card .panel-body { padding: 14px; }
}
.panel-body .morph-stage img.morph-layer { max-width: none; max-height: none; border-radius: 0; }
.fade-in { transition: opacity 0.7s ease, transform 0.7s ease; }
.fade-in.leaving { opacity: 0; }
.fade-in.arriving { opacity: 0; transform: translateY(8px); }
.preview-side.fade-in { transition-delay: 0.25s; }
.stencil-steps.fade-in { transition-delay: 0.5s; }
@media (prefers-reduced-motion: reduce) {
  .fade-in { transition: none; }
}
