/* 내컷네컷 — TABLET tier, also used for desktop: iPad mini to Pro 13" in both orientations,
   portrait monitors, and every landscape screen from 768 wide up (laptops, 16:9 and 16:10
   monitors, kiosks look the same as a tablet).
   Loaded after style.css.
   One top-level wrapper only (final-decisions.md 1.1); sub-variants
   (portrait / landscape / compact / tiny / landscape large) are nested inside it.
   Geometry only: no chrome colours, visibility-of-behaviour rules or copy.

   Sizing model: every "as large as possible at the print ratio" block is sized
   with container queries (cqw / cqh) against a screen or wrapper that carries
   container-type: size. Only the outer width of output canvases is ever set;
   aspect-ratio: var(--output-ratio) and the inline slot percentages do the rest. */

@media (orientation: portrait) and (min-width: 700px),
       (orientation: landscape) and (min-width: 768px) and (max-width: 999.98px) and (min-height: 501px),
       (orientation: landscape) and (min-width: 1000px) {
  :root {
    --tier-tablet: 1;

    /* the stepper includes the home-indicator inset (viewport-fit=cover);
       every CTA / select / frame formula reads --stepper-h */
    --stepper-base: 80px;
    --stepper-h: calc(var(--stepper-base) + env(safe-area-inset-bottom, 0px));
    --pad-x: 32px;
    --pad-top: 24px;
    --pad-bottom: 16px;
    --col-gap: 24px;
    --row-gap: 16px;
    --cta-w: 200px;
    --cta-h: 60px;
    --cta-gap: 16px;
    --h2: 32px;
    --shutter: 132px;
    --qr: 160px;
    --sel-head-h: 68px;
    --sel-gap: 12px;
    --type-card-max: 184px;
    --fs-head-h: 68px;
    --tools-h: 256px;
    --fs-side-w: 280px;
  }

  /* ------------------------------------------------------ persistent chrome */
  /* ⚙ sits in the bottom-left bay of the stepper band (look and visibility are shared) */
  .settings-button,
  .home-button {
    top: auto;
    left: 16px;
    bottom: calc((var(--stepper-h) - env(safe-area-inset-bottom, 0px) - 44px) / 2 + env(safe-area-inset-bottom, 0px));
  }

  .settings-button span {
    font-size: 20px;
  }

  .stepper {
    height: var(--stepper-h);
    padding: 10px 76px calc(12px + env(safe-area-inset-bottom, 0px));
    gap: 12px;
  }

  .step {
    align-content: center;
    gap: 6px;
    font-size: 14px;
    font-weight: 800;
  }

  .step strong {
    white-space: nowrap;
  }

  .step span {
    width: 30px;
    height: 30px;
    font-size: 14px;
  }

  .next-button {
    right: var(--pad-x);
    bottom: calc(var(--stepper-h) + var(--cta-gap));
    width: var(--cta-w);
    min-height: var(--cta-h);
    font-size: 18px;
  }

  .type-intro p {
    margin: 0 0 6px;
    font-size: 15px;
  }

  .type-intro h2,
  .section-heading h2 {
    font-size: var(--h2);
    line-height: 1.2;
    word-break: keep-all;
  }

  .section-heading p {
    margin-top: 4px;
    font-size: 16px;
    line-height: 1.35;
    word-break: keep-all;
    text-wrap: pretty; /* no lone "빠져요." under the multi4/multi6 select hint */
  }

  /* ------------------------------------------------------------------ setup */
  .setup-layout {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) var(--cta-h);
    gap: 20px var(--col-gap);
    align-content: stretch;
    padding: var(--pad-top) var(--pad-x) var(--cta-gap);
  }

  .setup-intro { grid-area: intro; }

  .setup-preview {
    grid-area: preview;
    container-type: size;
    min-width: 0;
    min-height: 0;
  }

  .setup-preview .sample-frame,
  .type-preview .sample-frame {
    width: min(100cqw, calc(100cqh * var(--output-ratio)));
    height: auto;
    max-width: none;
    max-height: none;
  }

  .setup-panel {
    grid-area: panel;
    align-self: start;
    max-height: 100%;
    overflow-y: auto;
    padding: 20px;
    gap: 14px 16px;
    grid-auto-flow: row dense;
  }

  .field-group {
    font-size: 14px;
  }

  .field-group input,
  .field-group select {
    min-height: 48px;
    font-size: 16px; /* >= 16px stops iPadOS Safari from zooming on focus */
  }

  .field-group input[type="file"] {
    padding: 11px 10px;
    font-size: 14px;
  }

  .field-help {
    font-size: 13px;
  }

  .setup-panel > div.field-group {
    grid-column: 1 / -1;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 12px;
  }

  #frameDesignInput { grid-column: 1; grid-row: 2; }
  #clearFrameDesignButton { grid-column: 2; grid-row: 2; min-height: 48px; padding: 0 18px; font-size: 15px; }
  .setup-panel .field-help { grid-column: 1 / -1; grid-row: 3; }

  /* ------------------------------------------------------------------- type */
  .type-layout {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) var(--cta-h);
    gap: 20px var(--col-gap);
    align-content: stretch;
    padding: var(--pad-top) var(--pad-x) var(--cta-gap);
  }

  .type-layout > .type-intro { grid-area: intro; }

  .type-preview {
    grid-area: preview;
    container-type: size;
    min-width: 0;
    min-height: 0;
  }

  /* The three cards share the options cell (no filter panel any more): each card
     takes a third of it, up to --type-card-max, so the column has no hole beside
     the sample. The cell is a size container, so the mini frame grows with the
     card height (its column is sized for the widest, 2:3 multi frame). */
  .type-options {
    --type-gap: 12px;
    --type-pad-y: 14px;
    --type-card-h: calc((100cqh - 2 * var(--type-gap)) / 3);
    --mini-h: clamp(40px, calc(var(--type-card-h) - 2 * var(--type-pad-y) - 12px), 148px);
    grid-area: options;
    container-type: size;
    height: min(100%, calc(3 * var(--type-card-max) + 2 * var(--type-gap)));
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: repeat(3, minmax(0, 1fr));
    gap: var(--type-gap);
    align-self: center;
    min-width: 0;
  }

  /* padding-right keeps the shared ✓ badge (top-right 8px, 24px) clear of the
     one-line title; set on every card so nothing jumps on selection */
  .type-card {
    min-height: 0;
    padding: var(--type-pad-y) 40px var(--type-pad-y) 18px;
    grid-template-columns: calc(var(--mini-h) * 2 / 3) minmax(0, 1fr);
    column-gap: 18px;
    row-gap: 4px;
  }

  .mini-frame {
    height: var(--mini-h);
  }

  .type-card strong {
    align-self: end;
    font-size: clamp(18px, calc(var(--type-card-h) * 0.11), 21px);
    white-space: nowrap;
  }

  .type-card small {
    align-self: start;
    font-size: clamp(14px, calc(var(--type-card-h) * 0.085), 16px);
  }

  .type-note {
    grid-area: note;
    align-self: center;
    padding: 0;
    background: none;
    font-size: 15px;
    line-height: 1.4;
  }

  /* ------------------------------------------------------------------ timer */
  .timer-layout {
    flex: 1;
    min-height: 0;
    width: min(100%, calc(720px + 2 * var(--pad-x)));
    margin: 0 auto;
    padding: var(--pad-top) var(--pad-x) calc(var(--cta-h) + 2 * var(--cta-gap));
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    gap: 28px;
  }

  .timer-layout .type-intro {
    text-align: center;
  }

  .timer-panel {
    padding: 24px;
    border-radius: 20px;
    gap: 20px;
  }

  .timer-panel strong { font-size: 18px; }
  .timer-panel span { margin-top: 4px; font-size: 16px; }

  .timer-options {
    gap: 12px;
  }

  .timer-choice {
    min-height: clamp(80px, 11dvh, 140px);
    border-radius: 18px;
    font-size: 28px;
    white-space: nowrap;
  }

  /* ---------------------------------------------------------------- capture */
  #captureScreen.active {
    container-type: size;
    display: grid;
    column-gap: var(--col-gap);
    row-gap: 0;
    padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
  }

  #captureScreen .capture-panel {
    display: contents;
  }

  #captureScreen .camera-stage {
    grid-area: camera;
    height: auto;
    min-height: 0;
    justify-self: center;
  }

  #captureScreen .shot-track {
    grid-area: track;
    margin: 0;
    gap: 10px;
    grid-template-columns: repeat(var(--cap-track-cols), minmax(0, 1fr));
  }

  .shot-slot {
    font-size: 16px;
  }

  #captureScreen .capture-actions {
    grid-area: shutter;
    margin: 0;
    grid-template-columns: 1fr;
    justify-items: center;
    align-self: start;
  }

  .shutter {
    width: var(--shutter);
    height: var(--shutter);
    font-size: calc(var(--shutter) * 0.36);
  }

  #captureScreen .center-copy {
    grid-area: status;
    min-height: 0;
    margin: 8px 0 0;
    font-size: 18px;
    line-height: 1.4;
  }

  /* ----------------------------------------------------------------- select */
  /* The heading, the 8 photos as large as they fit, and 다음 (the fixed pill at the
     bottom right of the content box). --sel-cols x --sel-rows is chosen per frame
     type and shape below; the card width is the best of three placements, each
     one clear of the pill:
       A  the grid ends one gap above the pill's row (full width);
       B  the grid ends one gap left of the pill (full height);
       E  3x3 only (--sel-e: 1): 8 photos leave the ninth cell, bottom right, to
          the pill, so the grid runs full height and full width.
     The grid is left-aligned under the heading, so whichever placement wins
     stays clear of the pill. All 8 fit: no page or grid scroll. */
  #selectScreen.active {
    container-type: size;
    padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
  }

  .select-layout {
    --sel-cols: 2;
    --sel-rows: 4;
    --sel-e: 0;
    --sel-h: calc(100cqh - var(--sel-head-h) - var(--row-gap));
    --sel-wa: min(
      calc((100cqw - (var(--sel-cols) - 1) * var(--sel-gap)) / var(--sel-cols)),
      calc((var(--sel-h) - var(--cta-h) - var(--row-gap) - (var(--sel-rows) - 1) * var(--sel-gap)) / var(--sel-rows) * var(--shot-ratio))
    );
    --sel-wb: min(
      calc((100cqw - var(--cta-w) - var(--row-gap) - (var(--sel-cols) - 1) * var(--sel-gap)) / var(--sel-cols)),
      calc((var(--sel-h) - (var(--sel-rows) - 1) * var(--sel-gap)) / var(--sel-rows) * var(--shot-ratio))
    );
    --sel-we: calc(var(--sel-e) * min(
      calc((100cqw - 2 * var(--sel-gap)) / 3),
      calc((var(--sel-h) - 2 * var(--sel-gap)) / 3 * var(--shot-ratio)),
      calc((100cqw - var(--cta-w) - var(--row-gap) - var(--sel-gap)) / 2),
      calc((var(--sel-h) - var(--cta-h) - var(--row-gap) - var(--sel-gap)) / 2 * var(--shot-ratio))
    ));
    --sel-card-w: max(var(--sel-wa), var(--sel-wb), var(--sel-we));
    flex: 1;
    min-height: 0;
    padding: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: var(--sel-head-h) minmax(0, 1fr);
    gap: var(--row-gap) 0;
    overflow: visible;
  }

  .select-layout .section-heading {
    grid-row: 1;
    align-items: flex-start;
  }

  .select-layout .photo-grid {
    grid-row: 2;
    grid-template-columns: repeat(var(--sel-cols), var(--sel-card-w));
    gap: var(--sel-gap);
    justify-content: start;
    align-content: start;
    overflow: visible;
  }

  .select-layout .photo-grid .photo-card {
    width: 100%;
    height: auto;
  }

  /* order badge: top-left, read at arm's length, about a sixth of the card width */
  .photo-card span {
    --badge: clamp(40px, calc(var(--sel-card-w) * 0.15), 56px);
    top: calc(var(--badge) * 0.25);
    left: calc(var(--badge) * 0.25);
    min-width: var(--badge);
    height: var(--badge);
    padding: 0 calc(var(--badge) * 0.18);
    font-size: calc(var(--badge) * 0.55);
  }

  /* ------------------------------------------------------------ frame + QR */
  #frameScreen.active {
    container-type: size;
    display: grid;
    gap: var(--row-gap) var(--col-gap);
    padding: var(--pad-top) var(--pad-x) var(--pad-bottom);
  }

  #frameScreen .section-heading {
    grid-area: heading;
    align-items: flex-start;
  }

  #frameScreen .composer {
    display: contents;
  }

  #frameScreen .print-frame {
    height: auto;
    max-width: none;
    max-height: none;
    margin: 0;
    align-self: center;
  }

  #printFrame { grid-area: photo; }
  #videoPrintFrame { grid-area: video; }

  .frame-tools {
    grid-area: tools;
    order: 0;
    min-height: 0;
    display: grid;
    gap: 12px var(--col-gap);
  }

  /* the visitor's choices (.frame-tools > div:first-child, hidden after the QR and
     on the download page by shared rules): frame colour, then photo filter.
     Filters are 3x2, or 6x1 once the block is wide enough (container query). */
  .frame-choices {
    grid-area: choices;
    container: choices / inline-size;
    min-width: 0;
    align-self: start;
    gap: 14px;
  }

  .choice-group {
    gap: 8px;
  }

  .choice-group > strong {
    font-size: 16px;
    line-height: 1.25;
  }

  .swatches {
    --swatch-size: 52px;
    /* the multi-photo tools column is 320px wide: six circles of 45px fit in it */
    gap: 10px;
  }

  .filter-options {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
  }

  .filter-choice {
    min-height: 52px;
    font-size: 17px;
    white-space: nowrap;
  }

  @container choices (min-width: 560px) {
    .filter-options {
      grid-template-columns: repeat(6, minmax(0, 1fr));
    }
  }

  /* same pill, same bottom-right spot as 다음 */
  #uploadButton {
    grid-area: cta;
    justify-self: end;
    width: var(--cta-w);
    min-height: var(--cta-h);
    border-radius: 999px;
    font-size: 18px;
  }

  .upload-status {
    grid-area: status;
    min-height: 0;
    padding: 0;
    background: none;
    font-size: 16px;
    line-height: 1.4;
    word-break: keep-all;
  }

  .qr-box {
    grid-area: qr;
  }

  /* on the booth the QR box holds the QR alone (links are hidden, shared rule) */
  .qr-box.show {
    grid-template-columns: var(--qr);
    gap: 12px;
    padding: 12px;
  }

  .qr-box img {
    width: var(--qr);
    height: var(--qr);
  }

  /* after the QR: swatches and #uploadButton are hidden (shared); QR and the
     scan instruction share the tools area */
  .frame-tools:has(.qr-box.show) .upload-status {
    align-self: center;
  }

  /* the scan instruction is the one thing to do at the handoff, read from 1-2 m:
     it reads as an instruction, not a footnote (colour stays shared) */
  body:not([data-mode="download"]) .frame-tools:has(.qr-box.show) .upload-status {
    font-size: var(--handoff-fs, 20px);
    font-weight: 800;
    line-height: 1.35;
    text-wrap: balance;
  }

  /* 처음으로 (#resetButton): a secondary pill with the CTA's footprint. After the
     QR it takes the CTA spot (#uploadButton is hidden then); after a failed
     upload it sits directly above the CTA, which stays for a staff retry.
     Each orientation's .frame-tools template names the "reset" cell.
     Width is at least the CTA's and fits the armed label ("한 번 더 누르면
     사진이 지워져요", about 250px at 18px) on one line, growing leftwards only,
     so arming never changes the height. Same type size as the CTA beside it.
     Visibility is shared: never set display here. */
  #resetButton {
    grid-area: reset;
    justify-self: end;
    width: auto;
    min-width: var(--cta-w);
    min-height: var(--cta-h);
    border-radius: 999px;
    font-size: 18px;
  }

  /* download page: the links take the CTA spot */
  body[data-mode="download"] .qr-box {
    width: 100%;
    gap: 0;
  }

  body[data-mode="download"] .download-links {
    width: 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
  }

  /* =============================================================== PORTRAIT */
  @media (orientation: portrait) {
    .setup-layout {
      grid-template-columns: minmax(0, 0.6fr) minmax(0, 1fr);
      grid-template-areas:
        "intro intro"
        "preview panel"
        "preview .";
    }

    .setup-panel {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .type-layout {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
      grid-template-areas:
        "intro intro"
        "preview options"
        "note .";
    }

    #captureScreen.active {
      --cap-action-w: 148px;
      grid-template-columns: minmax(0, 1fr) var(--cap-action-w);
      grid-template-rows: auto auto auto;
      grid-template-areas:
        "camera camera"
        "track shutter"
        "status status";
      align-content: center;
    }

    #captureScreen {
      --cap-track-cols: 4;
      --cap-track-rows: 2;
    }

    body[data-frame-type="multi4"] #captureScreen,
    body[data-frame-type="multi6"] #captureScreen {
      --cap-track-cols: 8;
      --cap-track-rows: 1;
    }

    #captureScreen .camera-stage {
      --cap-track-w: calc(100cqw - var(--cap-action-w) - var(--col-gap));
      --cap-slot-w: calc((var(--cap-track-w) - (var(--cap-track-cols) - 1) * 10px) / var(--cap-track-cols));
      --cap-track-h: calc(var(--cap-slot-w) / var(--shot-ratio) * var(--cap-track-rows) + (var(--cap-track-rows) - 1) * 10px);
      --cap-panel-h: calc(max(var(--cap-track-h), var(--shutter)) + 8px + 52px);
      width: min(calc(100cqw + 2 * var(--pad-x)), calc((100cqh - 24px - var(--cap-panel-h)) * var(--shot-ratio)));
      margin: 0 calc(-1 * var(--pad-x)) 24px;
      align-self: start;
    }

    #captureScreen .center-copy {
      min-height: 52px;
      text-align: right;
    }

    /* select: landscape (3:2) photos 2 across x 4 down; portrait (3:4) and square
       photos 3 x 3 with the pill in the ninth cell */
    body[data-frame-type="multi4"] .select-layout,
    body[data-frame-type="multi6"] .select-layout {
      --sel-cols: 3;
      --sel-rows: 3;
      --sel-e: 1;
    }

    #frameScreen.active {
      --fs-strip-h: calc(100cqh - var(--fs-head-h) - var(--tools-h) - 2 * var(--row-gap));
      --fs-strip-wmax: calc((100cqw - var(--col-gap)) / 2);
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      grid-template-rows: var(--fs-head-h) minmax(0, 1fr) var(--tools-h);
      grid-template-areas:
        "heading heading"
        "photo video"
        "tools tools";
    }

    #frameScreen .print-frame {
      width: min(var(--fs-strip-wmax), calc(var(--fs-strip-h) * var(--output-ratio)));
    }

    #printFrame { justify-self: end; }
    #videoPrintFrame { justify-self: start; }

    /* the tools band keeps its fixed --tools-h in every state, so the strips never
       jump. Before the QR: the choices (colour, filter) on the left; the status
       line sits on the CTA in the right column, which 처음으로 joins after a
       failed upload. */
    .frame-tools {
      grid-template-columns: minmax(0, 1fr) minmax(0, var(--fs-side-w));
      grid-template-rows: minmax(0, 1fr) var(--cta-h);
      grid-template-areas:
        "choices status"
        "choices cta";
    }

    .upload-status {
      align-self: end;
    }

    .frame-tools:not(:has(.qr-box.show)) .upload-status {
      text-align: right;
    }

    .qr-box.show {
      align-self: end;
      justify-self: end;
    }

    /* failed upload (처음으로 shown, no QR): 처음으로 sits directly above the CTA */
    body:not([data-mode="download"]) #frameScreen:is([data-upload="done"], [data-upload="failed"]) .frame-tools:not(:has(.qr-box.show)) {
      grid-template-rows: minmax(0, 1fr) var(--cta-h) var(--cta-h);
      grid-template-areas:
        "choices status"
        "choices reset"
        "choices cta";
    }

    /* after the QR: QR and the scan instruction centred side by side (the empty
       outer columns centre the pair); 처음으로 takes the CTA spot below them */
    .frame-tools:has(.qr-box.show) {
      grid-template-columns: minmax(0, 1fr) auto minmax(0, 360px) minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr) var(--cta-h);
      grid-template-areas:
        ". qr status ."
        ". . reset reset";
      align-items: center;
      column-gap: 24px;
    }

    .frame-tools:has(.qr-box.show) .qr-box.show {
      align-self: center;
    }

    body[data-mode="download"] .frame-tools,
    body[data-mode="download"] .frame-tools:has(.qr-box.show) {
      grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
      grid-template-rows: minmax(0, 1fr) auto;
      grid-template-areas:
        "status ."
        "status qr";
      justify-content: stretch;
      align-items: stretch;
    }

    body[data-mode="download"] .frame-tools .qr-box.show {
      align-self: end;
    }

    body[data-mode="download"] .frame-tools .upload-status {
      align-self: end;
    }
  }

  /* select column count by window shape (largest cards, checked from 700x1000 to
     1200x1600): very tall portrait (portrait monitors, w/h <= 2/3) fits square
     photos larger 2 across; near-square portrait fits 3:4 photos larger 4 x 2
     (w/h >= 0.9) and 3:2 photos larger 3 x 3 (w/h >= 0.95) */
  @media (orientation: portrait) and (max-aspect-ratio: 2/3) {
    body[data-frame-type="multi6"] .select-layout {
      --sel-cols: 2;
      --sel-rows: 4;
      --sel-e: 0;
    }
  }

  @media (orientation: portrait) and (min-aspect-ratio: 9/10) {
    body[data-frame-type="multi4"] .select-layout {
      --sel-cols: 4;
      --sel-rows: 2;
      --sel-e: 0;
    }
  }

  @media (orientation: portrait) and (min-aspect-ratio: 19/20) {
    body[data-frame-type="classic"] .select-layout {
      --sel-cols: 3;
      --sel-rows: 3;
      --sel-e: 1;
    }
  }

  /* near-square portrait (w/h >= 0.9: square windows, 1000x1000): a portrait-shaped
     or square (multi) camera is larger beside a rail, as in landscape, than above
     the track. Classic (3:2) keeps the stacked portrait capture. */
  @media (orientation: portrait) and (min-aspect-ratio: 9/10) {
    body[data-frame-type="multi4"] #captureScreen.active,
    body[data-frame-type="multi6"] #captureScreen.active {
      --rail-min: 220px;
      grid-template-columns: auto minmax(var(--rail-min), 480px);
      justify-content: center;
      grid-template-rows: auto minmax(0, 1fr) auto auto;
      grid-template-areas:
        "camera track"
        "camera ."
        "camera shutter"
        "camera status";
      align-content: stretch;
    }

    body[data-frame-type="multi4"] #captureScreen,
    body[data-frame-type="multi6"] #captureScreen {
      --cap-track-cols: 2;
      --cap-track-rows: 4;
    }

    body[data-frame-type="multi4"] #captureScreen .camera-stage,
    body[data-frame-type="multi6"] #captureScreen .camera-stage {
      grid-row: 1 / -1;
      width: min(calc(100cqw - var(--col-gap) - var(--rail-min)), calc(100cqh * var(--shot-ratio)));
      margin: 0;
      align-self: center;
    }

    body[data-frame-type="multi4"] #captureScreen .shot-track,
    body[data-frame-type="multi6"] #captureScreen .shot-track {
      width: 100%;
      max-width: min(440px, calc((100cqh - var(--shutter) - 48px - (var(--cap-track-rows) - 1) * 10px) / var(--cap-track-rows) * var(--shot-ratio) * var(--cap-track-cols) + (var(--cap-track-cols) - 1) * 10px));
      justify-self: center;
    }

    body[data-frame-type="multi4"] #captureScreen .center-copy,
    body[data-frame-type="multi6"] #captureScreen .center-copy {
      min-height: 0;
      text-align: center;
    }
  }

  /* portrait large: iPad Pro 12.9"/13" and portrait monitors */
  @media (orientation: portrait) and (min-width: 1000px) {
    :root {
      --pad-x: 40px;
      --h2: 38px;
      --shutter: 148px;
      --cta-w: 220px;
      --cta-h: 64px;
      --qr: 180px;
      --tools-h: 284px;
      --handoff-fs: 22px;
      --sel-head-h: 76px;
      --sel-gap: 16px;
      --type-card-max: 208px;
      --fs-head-h: 76px;
      --fs-side-w: 300px;
    }

    .filter-choice {
      min-height: 56px;
      font-size: 18px;
    }
  }

  /* ============================================================== LANDSCAPE */
  @media (orientation: landscape) {
    :root {
      --stepper-base: 64px;
      --pad-x: 24px;
      --pad-top: 16px;
      --h2: 28px;
      --shutter: 120px;
      --qr: 200px;
      --sel-head-h: 60px;
    }

    .step {
      grid-auto-flow: column;
      justify-content: center;
      align-items: center;
      gap: 10px;
    }

    .stepper {
      padding: 8px 76px calc(8px + env(safe-area-inset-bottom, 0px));
    }

    .setup-layout {
      grid-template-columns: minmax(0, 0.42fr) minmax(0, 1fr);
      grid-template-areas:
        "preview intro"
        "preview panel"
        "preview .";
    }

    .setup-panel {
      grid-template-columns: repeat(3, minmax(0, 1fr));
      max-width: 880px;
    }

    .type-layout {
      grid-template-columns: minmax(0, 1fr) minmax(380px, 1.05fr);
      grid-template-areas:
        "preview intro"
        "preview options"
        "preview note";
    }

    .type-layout > .type-intro {
      align-self: end;
    }

    .type-options {
      max-width: 620px;
    }

    .type-note {
      margin-right: calc(var(--cta-w) + 16px);
    }

    #captureScreen.active {
      --rail-min: clamp(208px, 20vw, 300px);
      grid-template-columns: auto minmax(var(--rail-min), 480px);
      justify-content: center;
      grid-template-rows: auto minmax(0, 1fr) auto auto;
      grid-template-areas:
        "camera track"
        "camera ."
        "camera shutter"
        "camera status";
    }

    #captureScreen {
      --cap-track-cols: 2;
      --cap-track-rows: 4;
    }

    body[data-frame-type="multi4"] #captureScreen,
    body[data-frame-type="multi6"] #captureScreen {
      --cap-track-cols: 4;
      --cap-track-rows: 2;
    }

    #captureScreen .camera-stage {
      grid-row: 1 / -1;
      width: min(calc(100cqw - var(--col-gap) - clamp(208px, 20vw, 300px)), calc(100cqh * var(--shot-ratio)));
      align-self: center;
    }

    /* the rail (track + shutter + status) always fits the screen height:
       on short screens the track narrows instead of pushing the shutter out */
    #captureScreen .shot-track {
      --track-max: 440px;
      width: 100%;
      max-width: min(var(--track-max), calc((100cqh - var(--shutter) - 48px - (var(--cap-track-rows) - 1) * 10px) / var(--cap-track-rows) * var(--shot-ratio) * var(--cap-track-cols) + (var(--cap-track-cols) - 1) * 10px));
      justify-self: center;
    }

    #captureScreen .center-copy {
      text-align: center;
    }

    /* select: 3:2 photos 3 x 3 with the pill in the ninth cell; 3:4 and square
       photos 4 x 2 */
    .select-layout {
      --sel-cols: 3;
      --sel-rows: 3;
      --sel-e: 1;
    }

    body[data-frame-type="multi4"] .select-layout,
    body[data-frame-type="multi6"] .select-layout {
      --sel-cols: 4;
      --sel-rows: 2;
      --sel-e: 0;
    }

    #frameScreen.active {
      --tools-min: 320px;
      --fs-strip-wmax: calc((100cqw - 2 * var(--col-gap) - var(--tools-min)) / 2);
      grid-template-columns: auto auto minmax(var(--tools-min), 1fr);
      grid-template-rows: auto minmax(0, 1fr);
      grid-template-areas:
        "photo video heading"
        "photo video tools";
    }

    #frameScreen .print-frame {
      grid-row: 1 / -1;
      width: min(var(--fs-strip-wmax), calc(100cqh * var(--output-ratio)));
    }

    /* before the QR the controls sit together at the foot of the column, next to
       the CTA they feed into (thumb zone); no empty QR row is reserved, the QR
       takes the column when it appears */
    .frame-tools {
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: auto auto var(--cta-h);
      grid-template-areas:
        "choices"
        "status"
        "cta";
      align-content: end;
    }

    /* failed upload (처음으로 shown, no QR): 처음으로 directly above the CTA */
    body:not([data-mode="download"]) #frameScreen:is([data-upload="done"], [data-upload="failed"]) .frame-tools:not(:has(.qr-box.show)) {
      grid-template-rows: auto auto auto var(--cta-h);
      grid-template-areas:
        "choices"
        "status"
        "reset"
        "cta";
    }

    .qr-box.show {
      justify-self: center;
      align-self: center;
    }

    /* after the QR: QR centred in the tools column, the scan instruction directly
       under it; 처음으로 takes the CTA spot at the foot of the column */
    .frame-tools:has(.qr-box.show) {
      grid-template-rows: minmax(0, 1fr) auto auto minmax(0, 1fr) var(--cta-h);
      grid-template-areas:
        "."
        "qr"
        "status"
        "."
        "reset";
      align-content: stretch;
      row-gap: 16px;
    }

    .frame-tools:has(.qr-box.show) .upload-status {
      text-align: center;
    }

    body[data-mode="download"] .frame-tools,
    body[data-mode="download"] .frame-tools:has(.qr-box.show) {
      grid-template-rows: minmax(0, 1fr) auto auto;
      grid-template-areas:
        "."
        "status"
        "qr";
      align-content: stretch;
      row-gap: 12px;
    }
  }

  /* landscape short: Safari with toolbars (1024x700, 1133x670), home-screen app with status bar */
  @media (orientation: landscape) and (max-height: 720px) {
    :root {
      --stepper-base: 60px;
      --pad-top: 12px;
      --h2: 26px;
      --shutter: 112px;
      --qr: 168px;
    }

    .stepper {
      padding: 6px 76px calc(6px + env(safe-area-inset-bottom, 0px));
    }

    .type-options {
      --type-pad-y: 10px;
    }
  }

  /* landscape wide (w/h >= 1.6: 1200x750, 1133x670, short windows): 3:2 photos
     are larger 4 x 2 than 3 x 3 */
  @media (orientation: landscape) and (min-aspect-ratio: 8/5) {
    body[data-frame-type="classic"] .select-layout {
      --sel-cols: 4;
      --sel-rows: 2;
      --sel-e: 0;
    }
  }

  /* landscape compact (501-619 tall: netbooks, small windows). Coverage only:
     graceful fit; setup may scroll inside .setup-panel. */
  @media (orientation: landscape) and (max-height: 619.98px) {
    :root {
      --pad-top: 10px;
      --h2: 24px;
      --cta-h: 52px;
      --qr: 140px;
      --sel-head-h: 56px;
    }

    .type-layout > .type-intro p {
      display: none;
    }

    .type-layout {
      row-gap: 12px;
    }

    .type-options {
      --type-gap: 8px;
      --type-pad-y: 8px;
    }

    .type-card {
      padding-left: 14px;
    }

    /* frame: 48px choices; the filters go 6 across from 440px */
    .frame-choices {
      gap: 12px;
    }

    .swatches {
      --swatch-size: 48px;
    }

    .filter-choice {
      min-height: 48px;
    }

    @container choices (min-width: 440px) {
      .filter-options {
        grid-template-columns: repeat(6, minmax(0, 1fr));
      }
    }

    .timer-layout {
      gap: 16px;
    }

    .timer-panel {
      padding: 16px 20px;
      gap: 14px;
    }
  }

  /* compact with 3:4 or square photos (2:3 strips): the 320px tools column under
     the heading is too short for colour + filter + status + 처음으로 + CTA after a
     failed upload, so the heading moves over the strips and the tools column
     takes the full height (the strips lose about a tenth of their height) */
  @media (orientation: landscape) and (min-height: 501px) and (max-height: 619.98px) {
    body[data-frame-type="multi4"] #frameScreen.active,
    body[data-frame-type="multi6"] #frameScreen.active {
      --fs-head-c: 56px;
      grid-template-areas:
        "heading heading tools"
        "photo video tools";
    }

    body[data-frame-type="multi4"] #frameScreen .print-frame,
    body[data-frame-type="multi6"] #frameScreen .print-frame {
      grid-row: 2;
      width: min(var(--fs-strip-wmax), calc((100cqh - var(--fs-head-c) - var(--row-gap)) * var(--output-ratio)));
    }
  }

  /* landscape tiny (1000-1279 wide, 500 or less tall: short laptop windows).
     Coverage only: the active screen may scroll. Rows size to their content
     instead of squeezing into the viewport, and the CTA row stays last. */
  @media (orientation: landscape) and (max-height: 500.98px) {
    .screen.active {
      overflow-y: auto;
    }

    /* type: tighter cards so the whole screen still fits (it scrolls only as a safety net) */
    .type-layout {
      row-gap: 10px;
    }

    .type-options {
      --type-gap: 6px;
      --type-pad-y: 6px;
    }

    /* frame: tighter rows; if the column still overflows it does so downwards,
       into the screen scroll, never up over the heading */
    .frame-tools {
      row-gap: 8px;
      align-content: safe end;
    }

    .frame-choices {
      gap: 10px;
    }

    .choice-group {
      gap: 6px;
    }

    .timer-layout {
      flex: none;
      min-height: 100%;
    }

    .timer-choice {
      min-height: 64px;
    }
  }

  /* landscape large: iPad Pro 12.9"/13" landscape, 4:3 and 5:4 monitors
     (1280 wide or more, aspect below 1.4) viewed from 1-2 m */
  @media (orientation: landscape) and (min-width: 1280px) {
    :root {
      --stepper-base: 72px;
      --pad-x: 32px;
      --pad-top: 24px;
      --h2: 32px;
      --cta-w: 220px;
      --cta-h: 64px;
      --shutter: 140px;
      --qr: 240px;
      --sel-head-h: 68px;
      --sel-gap: 16px;
      --type-card-max: 200px;
      --handoff-fs: 22px;
    }

    .stepper {
      padding: 10px 76px calc(10px + env(safe-area-inset-bottom, 0px));
    }

    .step {
      font-size: 15px;
    }

    .step span {
      width: 34px;
      height: 34px;
      font-size: 15px;
    }

    .type-options {
      max-width: 680px;
    }

    .filter-choice {
      min-height: 56px;
      font-size: 18px;
    }

    .type-note {
      font-size: 16px;
    }

    .timer-layout {
      width: min(100%, calc(820px + 2 * var(--pad-x)));
    }

    #captureScreen .center-copy {
      font-size: 20px;
    }

    #captureScreen .shot-track {
      --track-max: 480px;
    }
  }
}
