/* css/qc.css (WP3) — QC overlay (lives in #layer-qc, z 40). The QC photo canvas (.qc-photo) is the only element
   here with touch-action:none (TECH_SPEC §1.6). Tokens come from css/main.css; reduce-motion is handled there.
   Visual pass 2 (2026-10-15): a flat --bg backdrop, the ticket is a --panel block with three text styles and §4 tags,
   the shutter is a white disc with a hard edge whose ring turns green while the circle is on target; no glows. */

.qc {
  --qc-bg: var(--bg);
  --qc-card: #151b2b; /* = --panel (css/sheets.css .panel__ticket mirrors it) */
  --qc-line: var(--line);
  /* white floater text with an ink outline (no blur), like the hall's floaters (render/fx.js) */
  --qc-outline: 0 3px 0 var(--ink), 2px 0 0 var(--ink), -2px 0 0 var(--ink), 0 -2px 0 var(--ink), 2px 2px 0 var(--ink), -2px 2px 0 var(--ink);
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--qc-bg);
  color: var(--text);
  font-family: var(--font);
  overflow: hidden;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

.qc[hidden] {
  display: none !important;
}

/* ------------------------------------------------------------------ top bar */
.qc-top {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding-left: max(12px, var(--safe-left));
  padding-right: max(12px, var(--safe-right));
  padding-bottom: 4px;
}

/* the ✕: a ghost icon button (navy fill + the hard edge), 44 px here, 36 px in the panels */
.qc-x {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--r-md);
  background: var(--raised-2);
  color: var(--text);
  box-shadow: var(--sh-edge);
  transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out);
}

.qc-x:active {
  transform: translateY(2px);
  box-shadow: 0 calc(var(--edge) - 2px) 0 var(--edge-c);
}

.qc-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-title);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* „3 / 10”: a chip */
.qc-count {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  background: var(--raised-2);
  color: var(--text-2);
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
}

.qc-count:empty {
  display: none;
}

/* „Demo”: a default tag */
.qc-demo {
  flex: none;
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-xs);
  background: var(--raised-2);
  color: var(--text-2);
  font-size: var(--fs-12);
  font-weight: var(--fw-label);
}

/* ------------------------------------------------------------------ order ticket
   Three text styles: line 1 the item 16/900 + the tags (right); line 2 the size tag, the colour swatch and the colour
   name 14/700; the buyer line 12/700 dim („@handle: „quote”” after a 20 px avatar). A --panel block, THE border. */
.qc-ticket {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr); /* i18n QA: long item names / comments stay inside the ticket */
  gap: 6px;
  margin: 0 12px;
  padding: var(--sp-2) var(--sp-3);
  border: 2px solid var(--qc-line);
  border-radius: var(--r-md);
  background: var(--qc-card);
}

.qc-ticket.is-in {
  animation: qc-slide-in var(--mo-med) var(--mo-out) both;
}

.qc-tline {
  display: flex;
  flex-wrap: wrap; /* i18n QA: size / colours / tags drop to a 2nd line instead of cutting the item name */
  align-items: center;
  gap: var(--sp-1) 6px;
  min-width: 0;
  white-space: nowrap;
}

.qc-tline--head {
  flex-wrap: nowrap;
  gap: var(--sp-2);
}

.qc-item {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-16);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* fullscreen QC: the item name and the buyer's line wrap to 2 lines instead of „Van Cleef & Arpels…” */
.qc--full .qc-item,
.qc--full .qc-comment {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  white-space: normal;
}

.qc-badges {
  display: flex;
  flex: none;
  flex-wrap: nowrap;
  justify-content: flex-end;
  gap: var(--sp-1);
  margin-left: auto;
  overflow: hidden;
}

.qc-order {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-size: var(--fs-14);
  font-weight: var(--fw-body);
  line-height: var(--lh-tight);
  color: var(--text-2);
}

/* the ordered size: a tag in white (what the photos must show) */
.qc-size {
  color: var(--text);
}

.qc-swatch {
  display: inline-flex;
  gap: var(--sp-1);
}

.qc-swatch i {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--r-xs);
}

.qc-cw {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* the buyer line */
.qc-buyer {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  min-width: 0;
}

.qc-avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  color: #ffffff;
  font-size: var(--fs-12);
  font-weight: var(--fw-num);
}

.qc-avatar--sm {
  width: 32px;
  height: 32px;
}

.qc-comment {
  min-width: 0;
  font-size: var(--fs-12);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.qc-buyer .qc-comment { padding-top: 1px; }

/* the tags (css/main.css .tag): a glyph carries any meaning beyond the six fills */
.qc-badge {
  flex: none;
}

/* the order details matter while shooting; REVIEW and RESULT drop the same rows, so the ticket (and the photo under
   it) keeps its height when RESULT opens */
.qc[data-phase="REVIEW"] .qc-order,
.qc[data-phase="REVIEW"] .qc-buyer,
.qc[data-phase="REVIEW"] .qc-comment,
.qc[data-phase="RESULT"] .qc-order,
.qc[data-phase="RESULT"] .qc-buyer,
.qc[data-phase="RESULT"] .qc-comment {
  display: none;
}

/* ------------------------------------------------------------------ stage (photo) */
.qc-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 120px;
  margin: 8px max(16px, var(--safe-right)) 0 max(16px, var(--safe-left));
}

.qc-card {
  position: absolute;
  inset: 0;
  will-change: transform;
  transform-origin: 50% 90%;
}

.qc-photo {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none; /* the ONLY QC element with touch-action:none */
  object-fit: contain; /* a frame between a layout change and the resize never stretches the photo */
  cursor: var(--cur-hand); /* BRIEF / UNPACK / RESULT: a click skips, unpacks or goes on */
  -webkit-touch-callout: none;
}

/* the cursor says what a click does: SNAP shoots, REVIEW only drags up (= send), RESULT goes on */
.qc[data-phase="SNAP"] .qc-photo { cursor: crosshair; }
.qc[data-phase="REVIEW"] .qc-photo { cursor: grab; }
.qc[data-phase="REVIEW"].is-grabbing .qc-photo { cursor: grabbing; }

/* swipe-up send preview: a green frame fades in on the photo (stage.js sets its opacity with the drag) */
.qc-tint {
  position: absolute;
  inset: 0;
  border: 6px solid var(--green);
  border-radius: var(--r-md);
  opacity: 0;
  pointer-events: none;
}

.qc-card.is-flip {
  animation: qc-flip var(--mo-med) ease-in-out;
}

.qc-card.is-wobble {
  animation: qc-wobble var(--mo-slow) ease-in-out;
}

.qc-stage.is-shake {
  animation: qc-shake var(--mo-med) linear;
}

.qc.is-summary .qc-stage {
  opacity: 0.7;
}

.qc-fx {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
}

/* the grade splash over the photo: 900, an ink outline (no blur); 32 for PERFECT, 24 for the rest, 20 for hints */
.qc-float {
  position: absolute;
  top: 40%;
  font-size: var(--fs-32);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
  white-space: nowrap;
  color: #ffffff;
  text-shadow: var(--qc-outline);
  animation: qc-float 0.85s var(--mo-out) forwards;
}

.qc-float--perfect { color: var(--yellow); }
.qc-float--good { font-size: var(--fs-24); }
.qc-float--blurry { color: var(--text-2); font-size: var(--fs-24); }
.qc-float--retry { color: var(--red); font-size: var(--fs-20); }
.qc-float--hint { color: var(--yellow); font-size: var(--fs-20); top: 12%; }
/* the photo's random sweep tempo, announced above the ring as the sweep starts (turbo = the red warning) */
.qc-float--tempo { top: 12%; font-size: var(--fs-20); }
.qc-float--tempo-turbo { color: var(--red); font-size: var(--fs-24); }

.qc-flash {
  position: absolute;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  pointer-events: none;
}

.qc-flash.is-on {
  animation: qc-flash 80ms linear;
}

/* the buyer's GL stamp, placed on the photo by stage.js positionStamps (it was in the flow before: under the photo,
   never seen) */
.qc-stamp {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 4;
  width: 210px;
  height: 120px;
  pointer-events: none;
}

/* after the slam the stamp settles to translucent ink so the photo stays visible */
.qc-stamp.is-slam {
  animation: qc-slam var(--mo-med) var(--mo-spring) both, qc-settle 0.45s ease 1.3s forwards;
}

/* the tutorial line over the photo: Kim's paper + ink (css/guide-character.css .kim__bubble) */
.qc-coach {
  position: absolute;
  top: 10px;
  left: 50%;
  z-index: 5;
  max-width: 86%;
  padding: var(--sp-2) var(--sp-3);
  border: 2px solid var(--ink);
  border-radius: var(--r-md);
  background: var(--paper);
  color: var(--ink);
  font-size: var(--fs-16);
  font-weight: var(--fw-label);
  line-height: var(--lh-tight);
  text-align: center;
  box-shadow: 0 4px 0 var(--ink);
  transform: translateX(-50%);
  pointer-events: none;
  animation: qc-pop var(--mo-med) var(--mo-out) both;
}

.qc-fly {
  position: absolute;
  z-index: 20;
  border-radius: var(--r-sm);
  pointer-events: none;
  transform-origin: 0 0;
  transition: transform var(--mo-slow) ease-in-out, opacity var(--mo-slow);
}

/* ------------------------------------------------------------------ bottom panels */
.qc-panel {
  flex: none;
  padding: 10px max(16px, var(--safe-right)) 0 max(16px, var(--safe-left));
}

/* SNAP */
.qc-snap {
  display: grid;
  justify-items: center;
  gap: 6px;
}

.qc-shotlabel {
  font-size: var(--fs-16);
  font-weight: var(--fw-label);
  line-height: var(--lh-tight);
}

.qc-dots {
  display: flex;
  gap: 6px;
}

.qc-dots i {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--line);
}

.qc-dots i.is-done { background: var(--green); }
.qc-dots i.is-cur { background: #ffffff; }

.qc-filmrow {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.qc-film {
  display: flex;
  gap: 6px;
  justify-self: start;
}

/* the film slots: dashed = still empty; a filled slot's border is its grade (yellow PERFECT, white GOOD, dim BLURRY) */
.qc-slot {
  width: 46px;
  height: 46px;
  border: 2px dashed var(--line);
  border-radius: var(--r-sm);
  background: transparent;
}

.qc-slot.is-filled { border-style: solid; }
.qc-slot.is-perfect { border-color: var(--yellow); }
.qc-slot.is-good { border-color: #ffffff; }
.qc-slot.is-blurry { border-color: var(--text-dim); }

/* the shutter: a white disc with the hard edge; its ring is --line, green while the moving circle is on target
   (stage.js setShutterOn → .is-on) and while unpacking (every tap counts) */
.qc-shutter {
  display: grid;
  place-items: center;
  width: 88px;
  height: 88px;
  border: 4px solid var(--line);
  border-radius: 50%;
  background: #ffffff;
  color: var(--ink);
  box-shadow: 0 4px 0 var(--edge-c);
  touch-action: manipulation;
  transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out);
}

.qc-shutter.is-on,
.qc.is-unpack .qc-shutter {
  border-color: var(--green);
}

.qc-shutter:active {
  transform: translateY(2px);
  box-shadow: 0 2px 0 var(--edge-c);
}

.qc-filmspacer {
  display: flex;
  align-items: center;
  min-width: 1px;
}

.qc-taphint {
  font-size: var(--fs-12);
  font-weight: var(--fw-body);
  color: var(--text-dim);
}

/* the „how to” lines retire after a few QCs (stage.js is-veteran) — the ring, the shutter and the big green button
   say it by then */
.qc.is-veteran .qc-taphint,
.qc.is-veteran .qc-review .qc-hint {
  display: none;
}

/* the key for mouse players: „Spacja” next to the shutter, „Enter” on the send button (fine pointers only) */
/* a keycap: a 20 px navy key with the small hard edge */
.qc-kbd {
  display: none;
  min-width: 34px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-xs);
  background: var(--raised-2);
  color: var(--text-2);
  font: var(--fw-label) var(--fs-12)/20px var(--font);
  text-align: center;
  white-space: nowrap;
  box-shadow: 0 2px 0 var(--edge-c);
}

.qc-kbd--send {
  margin-left: var(--sp-1);
  background: var(--green-dark);
  color: #ffffff;
  box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
  .qc-kbd { display: inline-block; }
}

/* SPOT */
.qc-spotp {
  display: grid;
  justify-items: center;
  gap: 8px;
}

.qc-hint {
  font-size: var(--fs-14);
  font-weight: var(--fw-body);
  color: var(--text-2);
}

.qc-done {
  min-width: 210px;
}

/* ONE big green „Wyślij QC do klienta” — the buyer stamps GL (DECISIONS 2026-10-11) */
/* the screen's ONE primary: a full-width .btn--lg twin (52 px, 20/900, the 4 px green edge), the same spot as „Dalej” */
.qc-send {
  --edge-col: var(--green-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  width: 100%;
  height: 52px;
  border: 0;
  border-radius: var(--r-md);
  background: var(--green);
  color: #ffffff;
  font-weight: var(--fw-num);
  white-space: nowrap;
  box-shadow: 0 var(--edge) 0 var(--edge-col);
  transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out);
}

.qc-send svg {
  flex: none;
}

.qc-send:active {
  transform: translateY(2px);
  box-shadow: 0 calc(var(--edge) - 2px) 0 var(--edge-col);
}

/* tutorial stain item: still waiting for the circle on the stain — the disabled look until then */
.qc-send.is-wait {
  --edge-col: var(--edge-c);
  background: var(--panel-raised);
  color: var(--text-dim);
}

.qc-send.is-shake {
  animation: qc-shake var(--mo-slow) linear;
}

.qc-send__big {
  font-size: var(--fs-20);
  line-height: var(--lh-tight);
  text-align: left;
}

/* the BUYER's GL / RL stamp in the reply bubble */
.qc-bstamp {
  display: inline-block;
  margin-top: var(--sp-1);
  padding: 0 6px;
  border: 2px solid currentColor;
  border-radius: var(--r-xs);
  font-weight: var(--fw-num);
  font-size: var(--fs-14);
  transform: rotate(-6deg);
}

.qc-bstamp--gl { color: var(--green); }
.qc-bstamp--rl { color: var(--red); }

/* RESULT */
.qc-result {
  display: grid;
  gap: 7px;
}

.qc-res__top {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* QC stars (1–3) instead of the old letter grade (DECISIONS 2026-10-11) */
.qc-res__grade {
  flex: none;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.45));
}

.qc-res__money {
  display: grid;
  min-width: 0;
}

/* the fee: a hero number, green like every income */
.qc-res__fee {
  font-size: var(--fs-24);
  line-height: var(--lh-tight);
  color: var(--green);
}

.qc-res__xp {
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  color: var(--text-2);
}

.qc-res__facts {
  font-size: var(--fs-12);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
  color: var(--text-2);
}

/* the buyer's reply: a raised card with the avatar (no border inside the panel) */
.qc-bubble {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-2);
  border-radius: var(--r-md);
  background: var(--raised);
}

.qc-bubble.is-in {
  animation: qc-pop var(--mo-med) var(--mo-out) 0.35s both;
}

.qc-bubble__handle {
  font-size: var(--fs-12);
  font-weight: var(--fw-label);
  color: var(--text-2);
}

.qc-bubble__text {
  font-size: var(--fs-14);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
}

.qc-res__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.qc-stars {
  display: flex;
  gap: 2px;
  color: var(--line);
}

.qc-stars > span {
  display: inline-flex;
}

.qc-stars .is-on {
  color: var(--yellow);
}

/* earned stars pop in one by one (stage.js staggers the delay and plays a rising ding per star) */
.qc-stars .is-pop {
  animation: qc-star var(--mo-slow) var(--mo-spring) both;
}

.qc-streak {
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  color: var(--green);
}

.qc-streak.is-down {
  color: var(--text-dim);
}

/* ONE full-width „Dalej” (the ✕ closes); it ignores taps for a moment after RESULT opens (stage.js is-armed), so
   the second tap of a double-tap on „Wyślij” never skips the stars */
.qc-res__actions {
  display: grid;
  grid-template-columns: 1fr;
}

.qc[data-phase="RESULT"]:not(.is-armed) .qc-res__actions {
  pointer-events: none;
}

.qc-next {
  position: relative;
  overflow: hidden;
}

.qc-next__bar {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 4px;
  background: rgba(255, 255, 255, 0.6);
  /* BACKLOG 28: the auto-next countdown fills with scaleX (compositor) instead of an animated width (layout) */
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.qc-next.is-auto .qc-next__bar {
  animation: qc-bar linear forwards;
}

/* SUMMARY / EMPTY */
.qc-summary,
.qc-emptyp {
  display: grid;
  justify-items: center;
  gap: 10px;
  padding-bottom: 6px;
  text-align: center;
}

.qc-sum__title {
  font-size: var(--fs-title);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
}

.qc-sum__sub,
.qc-sum__extra,
.qc-empty__body {
  font-size: var(--fs-14);
  font-weight: var(--fw-body);
  color: var(--text-2);
}

.qc-sum__sub:empty,
.qc-sum__extra:empty {
  display: none;
}

.qc-sum__line {
  font-size: var(--fs-20);
  color: var(--green);
}

.qc-sum__grades {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}

/* graded shots: 36 px chips (3★ yellow / 2★ green / 1★ navy) */
.qc-gchip {
  display: grid;
  place-items: center;
  min-width: 36px;
  padding: 0 var(--sp-2);
  height: 36px;
  border-radius: var(--r-sm);
  font-weight: var(--fw-num);
}

.qc-gchip--3 { background: var(--yellow); color: var(--ink); }
.qc-gchip--2 { background: var(--green); color: #ffffff; }
.qc-gchip--1 { background: var(--raised-2); color: var(--text-2); }

.qc-sum__close {
  min-width: 220px;
}

/* Bottom safe area */
.qc-panel.safe-bottom {
  padding-bottom: max(12px, var(--safe-bottom));
}

/* RESULT on phones: „Zakończ / Dalej” is the last row (no link under it any more) — a comfortable gap above the
   home indicator / screen edge, so nothing tappable sits right under „Dalej” (owner 2026-10-12) */
@media not all and (min-width: 900px) and (orientation: landscape) {
  .qc--full[data-phase="RESULT"] .qc-panel.safe-bottom {
    padding-bottom: max(20px, calc(var(--safe-bottom) + 12px));
  }
}

/* big phones / tablets held sideways get the side panel, which ends just above the bottom nav: the same gap there, so
   a thumb under „Dalej” does not land on a nav tab */
@media (min-width: 900px) and (orientation: landscape) and (pointer: coarse) {
  .qc--full[data-phase="RESULT"] .qc-panel.safe-bottom {
    padding-bottom: 20px;
  }
}

/* Narrow phones: „Zlecenie #KB-10092” + count chip fit next to the close button */
@media (max-width: 379px) {
  .qc-top { gap: 8px; }
  .qc-title { font-size: var(--fs-16); }
}

/* Small phones (360 × 640) */
@media (max-height: 700px) {
  .qc-top { min-height: 46px; }
  .qc-ticket { padding-top: 6px; padding-bottom: 6px; }
  .qc-shutter { width: 76px; height: 76px; }
  .qc-taphint { display: none; }
}

/* ------------------------------------------------------------------ debug gallery */
.qc-gallery {
  display: block;
  padding: 12px;
  overflow: auto;
  touch-action: pan-y;
}

.qc-gallery__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

.qc-gallery__label {
  width: 100%;
  font-size: var(--fs-12);
  color: var(--text-2);
}

.qc-gallery__img {
  border-radius: var(--r-sm);
}

/* ------------------------------------------------------------------ animations */
@keyframes qc-slide-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

@keyframes qc-float {
  0% { opacity: 0; transform: translateY(10px) scale(0.8); }
  18% { opacity: 1; transform: translateY(0) scale(1.12); }
  70% { opacity: 1; transform: translateY(-20px) scale(1); }
  100% { opacity: 0; transform: translateY(-36px) scale(1); }
}

@keyframes qc-flash {
  from { opacity: var(--qc-flash, 0.35); }
  to { opacity: 0; }
}

@keyframes qc-slam {
  0% { opacity: 0; transform: scale(1.6) rotate(-4deg); }
  60% { opacity: 1; }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

@keyframes qc-settle {
  to { opacity: 0.42; }
}

@keyframes qc-shake {
  0%, 100% { transform: none; }
  25% { transform: translate(-3px, 1px); }
  50% { transform: translate(3px, -1px); }
  75% { transform: translate(-2px, 0); }
}

@keyframes qc-wobble {
  0%, 100% { transform: none; }
  30% { transform: rotate(-1.5deg); }
  60% { transform: rotate(1.2deg); }
}

@keyframes qc-flip {
  0%, 100% { transform: perspective(900px) rotateY(0); }
  50% { transform: perspective(900px) rotateY(14deg) scale(0.97); }
}

@keyframes qc-pop {
  from { opacity: 0; transform: translateY(6px) scale(0.96); }
  to { opacity: 1; transform: none; }
}

.qc-coach {
  animation-name: qc-coach-pop;
}

@keyframes qc-coach-pop {
  from { opacity: 0; transform: translateX(-50%) translateY(-6px); }
  to { opacity: 1; transform: translateX(-50%); }
}

@keyframes qc-bar {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@keyframes qc-star {
  from { opacity: 0; transform: scale(0.3) rotate(-30deg); }
  to { opacity: 1; transform: none; }
}

@keyframes qc-nope {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  45% { transform: translateX(5px); }
  70% { transform: translateX(-3px); }
}

/* a click on the dimmed hall next to the side panel: the ✕ shakes (qc.js → stage.nudgeClose) */
.qc-x.is-nope {
  animation: qc-nope var(--mo-slow) ease;
}

/* ------------------------------------------------------------------ mouse / trackpad: hover says „clickable”
   (the same lift as every pressable: 1 px up, the hard edge 1 px taller) */
@media (hover: hover) and (pointer: fine) {
  .qc-shutter {
    transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out), border-color var(--mo-fast) var(--mo-out);
  }

  .qc-shutter:hover {
    transform: translateY(-1px);
    box-shadow: 0 5px 0 var(--edge-c);
  }

  .qc-shutter:active {
    transform: translateY(2px);
    box-shadow: 0 2px 0 var(--edge-c);
  }

  .qc-send:hover {
    filter: brightness(1.06);
    transform: translateY(-1px);
    box-shadow: 0 calc(var(--edge) + 1px) 0 var(--edge-col);
  }

  .qc-send:active {
    filter: none;
    transform: translateY(2px);
    box-shadow: 0 calc(var(--edge) - 2px) 0 var(--edge-col);
  }

  .qc-x {
    cursor: var(--cur-hand);
  }

  .qc-x:hover {
    transform: translateY(-1px);
    box-shadow: 0 calc(var(--edge) + 1px) 0 var(--edge-c);
  }

  .qc-x:active {
    transform: translateY(2px);
    box-shadow: 0 calc(var(--edge) - 2px) 0 var(--edge-c);
  }
}

:root[data-reduce-motion="true"] :is(.qc-shutter, .qc-send, .qc-x):hover {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-reduce-motion="false"]) :is(.qc-shutter, .qc-send, .qc-x):hover {
    transform: none;
  }
}

/* ================================================================== GDD_V2 (merged review, unpack, Stół QC panel) */

/* „Przegląd i wysyłka” — SPOT + VERDICT merged */
.qc-review {
  display: grid;
  gap: 6px;
}

.qc-review .qc-hint {
  text-align: center;
}

/* REVIEW and RESULT share one panel height (fullscreen): REVIEW reserves the RESULT panel's height (measured by
   stage.js → --qc-res-h; the fallback ≈ a phone's result panel), both bottom-aligned — the photo and the GL stamp
   never jump when RESULT opens, and „Wyślij” / „Dalej” sit in the same thumb spot */
.qc--full[data-phase="REVIEW"] .qc-panel,
.qc--full[data-phase="RESULT"] .qc-panel {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* the fallback (before the first RESULT at this width is measured) covers a phone's result panel with the streak
   line, for BOTH phases — a smaller REVIEW reserve flipped a 3-photo layout (stacked → side by side) on the first send */
.qc--full[data-phase="REVIEW"] .qc-panel,
.qc--full[data-phase="RESULT"] .qc-panel {
  min-height: var(--qc-res-h, 272px);
}

.qc-snaphead {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 28px;
}

/* the stamp turns into a chat bubble (panel) */
.qc-sent {
  position: absolute;
  right: 6px;
  bottom: 10px;
  z-index: 6;
  pointer-events: none;
}

/* the player's own message: the one speech-bubble shape (--r-lg + a tail), in the act colour */
.qc-sent__bubble {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-lg);
  background: var(--green);
  color: #ffffff;
  font-weight: var(--fw-label);
  font-size: var(--fs-14);
  line-height: var(--lh-tight);
}

.qc-sent__bubble::after {
  content: '';
  position: absolute;
  right: 16px;
  bottom: -5px;
  width: 12px;
  height: 12px;
  background: inherit;
  transform: rotate(45deg);
}

.qc-sent.is-in .qc-sent__bubble {
  animation: qc-pop var(--mo-med) var(--mo-out) both;
}

.qc-sent__typing {
  opacity: 0.8;
}

/* Stół QC panel result: the stars + the tip in one row (the photo and the GL stamp stay above) */
.qc-res__panel {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  min-height: 56px;
  font-weight: var(--fw-label);
  font-size: var(--fs-14);
  color: var(--text-2);
  text-align: center;
}

.qc-res__pstars {
  filter: drop-shadow(0 2px 0 rgba(0, 0, 0, 0.45));
}

/* the tip: income, so green */
.qc-res__ptip {
  font-size: var(--fs-20);
  font-weight: var(--fw-num);
  color: var(--green);
}

/* ------------------------------------------------------------------ Stół QC panel mode (mountTableQc) */
.qc--panel {
  position: relative;
  inset: auto;
  width: 100%;
  height: 100%;
  min-height: 0;
  background: transparent;
}

.qc--panel .qc-top--panel {
  min-height: 44px;
  padding: 0 12px;
  gap: 8px;
}

/* the panels' ✕: a small ghost icon button (36 px, --r-sm, the small edge) */
.qc--panel .qc-x {
  width: 36px;
  height: 36px;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-edge-sm);
}

.qc--panel .qc-x:active { box-shadow: 0 1px 0 var(--edge-c); }

/* the panel header is a sub-header under the sheet: a row title (16/900) */
.qc--panel .qc-title {
  font-size: var(--fs-16);
}

.qc--panel .qc-ticket {
  gap: var(--sp-1);
  min-height: 44px;
  padding: 6px var(--sp-3);
}

/* the panel's line 1: item + size + swatch + tags (wraps: the tags drop to a 2nd line before the name is cut) */
.qc--panel .qc-tline--head {
  flex-wrap: wrap;
  gap: var(--sp-1) 6px;
}

.qc--panel .qc-item {
  flex: 0 1 auto;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  white-space: normal;
}

.qc--panel .qc-comment {
  flex: 1 1 100%;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  white-space: normal;
}

.qc--panel .qc-stage {
  margin: 6px 12px 0;
  min-height: 160px;
}

.qc--panel .qc-panel {
  padding: 6px 12px 10px;
}

/* RESULT keeps the send row's height (stage.js), the stars + tip in its middle: the photo never moves */
.qc--panel[data-phase="RESULT"] .qc-panel {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.qc--panel .qc-snap {
  gap: 4px;
}

.qc--panel .qc-shutter {
  width: 72px;
  height: 72px;
  border-width: 4px;
}

.qc--panel .qc-slot {
  width: 38px;
  height: 38px;
}

.qc--panel .qc-taphint {
  display: none;
}

.qc--panel .qc-coach {
  font-size: var(--fs-14);
}

/* ------------------------------------------------------------------ debug stand-in for WP4a's bottom sheet */
.qc-debugsheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 63%;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--panel);
  padding-bottom: max(8px, var(--safe-bottom));
}

.qc-debugsheet__body {
  flex: 1 1 auto;
  min-height: 0;
}

@media (max-height: 700px) {
  .qc-debugsheet { height: 72%; }
  .qc--panel .qc-shutter { width: 64px; height: 64px; }
}

/* ================================================================== BACKLOG 27: photo as big as it fits
   Phones (fullscreen session): the photo runs almost edge to edge; while shooting (BRIEF / UNPACK / SNAP) the stage
   is a square as wide as the screen and the spare height goes around the shutter row instead of empty bands
   above and below the photo. REVIEW / RESULT keep the flexible stage (bigger main photo + insets). */
.qc--full .qc-ticket {
  margin: 0 10px;
}

.qc--full .qc-stage {
  margin: 8px max(10px, var(--safe-right)) 0 max(10px, var(--safe-left));
}

.qc--full[data-phase="BRIEF"] .qc-stage,
.qc--full[data-phase="UNPACK"] .qc-stage,
.qc--full[data-phase="SNAP"] .qc-stage {
  flex: 0 1 auto;
  aspect-ratio: 1 / 1;
  min-height: 120px;
}

.qc--full[data-phase="BRIEF"] .qc-panel,
.qc--full[data-phase="UNPACK"] .qc-panel,
.qc--full[data-phase="SNAP"] .qc-panel {
  margin-top: auto;
  margin-bottom: auto;
}

/* ------------------------------------------------------------------ wide screens (BACKLOG 27 UPDATE)
   Desktop / big monitor in landscape: EVERY QC session (nav QC tab, boss requests, Sprint, Daily QC) opens in the
   same right-side panel as the Stół QC panel (css/sheets.css: ~460 px, from under the top bar down to the bottom
   nav, sliding in from the right) over a lightly dimmed hall — never fullscreen. The photo fills the panel width;
   the ticket is compact above it, shutter + filmstrip below. js/qc/qc.js adds the scrim (swallows taps). */
.qc-scrim {
  display: none;
}

@media (min-width: 900px) and (orientation: landscape) {
  .qc-scrim {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(5, 8, 16, 0.1);
    animation: qc-fade var(--mo-med) var(--mo-out) both;
  }

  /* id-qualified: main.css "#layer-qc > * { pointer-events: auto }" beat the plain class, so the fading scrim still
     swallowed drags / taps on the hall while the side panel slid out */
  .qc-scrim.is-out,
  #layer-qc > .qc-scrim.is-out {
    animation: qc-fade-out 180ms ease-in both;
    pointer-events: none;
  }

  /* the side panel grows with the window height (≈ 460 px at 900 px tall → ≈ 760 px at 1440 px), so the photo — the
     core mini-game — is never a small square in a tall empty column */
  .qc--full {
    inset: auto;
    top: calc(max(8px, var(--safe-top)) + 58px);
    right: max(12px, var(--safe-right));
    bottom: calc(var(--nav-space, 0px) + 4px);
    width: min(clamp(460px, calc(100dvh - 380px), min(780px, 42vw)), calc(100% - 24px));
    border: 2px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--panel);
    animation: qc-side-in var(--mo-med) var(--mo-out) both;
  }

  .qc--full.is-out {
    animation: qc-side-out 180ms ease-in both;
    pointer-events: none;
  }

  /* the bottom nav stays visible next to the panel (css/hud.css hides it for the phone fullscreen); a tab tap
     closes the session first (js/ui/nav.js → 'qc:dismiss') */
  #app > #layer-qc.qc-open ~ .hud-nav-wrap {
    display: block;
  }

  /* header like the sheets: title left, ✕ top right */
  .qc--full .qc-top,
  .qc--full .qc-top.safe-top {
    min-height: 46px;
    padding: 4px 8px 2px 14px;
  }

  .qc--full .qc-x {
    order: 9;
    width: 36px;
    height: 36px;
    border-radius: var(--r-sm);
    box-shadow: var(--sh-edge-sm);
  }

  /* compact ticket: what was ordered + the badges share one row */

  .qc--full .qc-stage {
    margin: 8px 10px 0;
  }

  .qc--full .qc-panel {
    padding: 8px 12px 0;
  }

  .qc--full .qc-panel.safe-bottom {
    padding-bottom: 12px;
  }

  /* the shutter row sits right under the photo (the phone rule centres it in the spare height, which put the
     controls far below the photo in a tall panel) */
  .qc--full[data-phase] .qc-panel {
    margin-top: 0;
  }

  /* the dimmed hall next to the panel is not clickable (a click shakes the ✕) */
  .qc-scrim {
    cursor: default;
  }
}

/* laptops (≈ 768 px tall): a smaller shutter row leaves the photo more height */
@media (min-width: 900px) and (orientation: landscape) and (max-height: 820px) {
  .qc--full .qc-shutter {
    width: 72px;
    height: 72px;
    border-width: 4px;
  }

  .qc--full .qc-slot {
    width: 40px;
    height: 40px;
  }

  .qc--full .qc-taphint {
    display: none;
  }

  .qc--full .qc-item,
  .qc--full .qc-comment {
    -webkit-line-clamp: 1;
    line-clamp: 1;
  }
}

@keyframes qc-side-in {
  from { transform: translateX(calc(100% + 24px)); }
  to { transform: none; }
}

@keyframes qc-side-out {
  from { transform: none; }
  to { transform: translateX(calc(100% + 24px)); }
}

@keyframes qc-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes qc-fade-out {
  from { opacity: 1; }
  to { opacity: 0; }
}
