/* ============================================================
   Photo Buttons — picker grid
   ============================================================ */
.pb-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 180px), 1fr));
  gap: 1rem;
}

.pb-photo-item {
  display: block;
  padding: 0;
  border: 0;
  border-radius: var(--border-radius);
  overflow: hidden;
  aspect-ratio: 1/1;
  cursor: pointer;
  background: var(--color-card);
  transition: transform var(--transition), box-shadow var(--transition);
}

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

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

/* ============================================================
   Modal shell
   ============================================================ */
.pb-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2000;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.75);
  padding: 1rem;
}

.pb-modal.open { display: flex; }

/* While the sticker sheet is open, shift the whole modal toward the top of the viewport
   instead of shrinking the crop area to fit around the sheet — the sheet only covers the
   bottom slice of the viewport (see .pb-sticker-sheet), so moving the modal up is usually
   enough on its own to keep the crop circle clear of it, and the circle stays full-size
   and easy to see while picking. */
.pb-modal.pb-stickers-open {
  align-items: flex-start;
}

.pb-modal.pb-stickers-open .pb-modal__dialog {
  margin-top: 1rem;
}

.pb-modal__dialog {
  width: min(100%, 720px);
  /* dvh (dynamic viewport height) tracks the actual visible area as mobile browser chrome
     shows/hides — plain vh on iOS Safari is cached against whichever chrome state was
     active at load, so anything sized against it can visibly shift/resize later even
     though nothing in the DOM changed. vh stays as the fallback for browsers that don't
     support dvh (an unrecognized value is ignored, not applied, so the earlier valid
     declaration wins). */
  max-height: min(92vh, 860px);
  max-height: min(92dvh, 860px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--border-radius-lg);
  box-shadow: var(--shadow-lg);
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  overflow: hidden;
}

.pb-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--color-border);
}

.pb-modal__title {
  margin: 0;
  font-family: var(--font-display);
  color: var(--color-white);
  letter-spacing: 0.02em;
}

.pb-modal__body {
  padding: 1.25rem;
  overflow-y: auto;
}

.pb-modal__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .75rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--color-border);
}

/* ============================================================
   Cropper area
   ============================================================ */
/*
 * Deliberately not display:flex here. Cropper.js measures this element's own box to size
 * its container on init, and a flex parent with align-items/justify-content can make that
 * measurement collapse to near-zero — the crop box then exists but is invisible and
 * non-interactive, while the underlying image still visually drags around on its own.
 * Cropper's viewMode:1 + autoCropArea already center and fit the image without help.
 *
 * A fixed square (aspect-ratio tied to itself) rather than being sized from the loaded
 * photo's own rendered height — a portrait photo could otherwise produce a canvas taller
 * than the space actually available. The padding/border/background live on this outer
 * element only; the actual Cropper target is #pb-cropper-stage below, which has none of
 * those, because Cropper.js sizes its canvas from its *immediate parent's* offsetWidth/
 * Height — a border-box measurement that does NOT subtract that parent's own padding —
 * then positions the canvas inset by the padding while keeping the full unsubtracted
 * size. With the image nested directly in this padded element, the canvas ended up
 * exactly `padding` pixels wider/taller than the space actually left after the padding,
 * overflowing the bottom/right edge and getting clipped by overflow:hidden. Cropper
 * measuring a separate, padding-free stage instead removes that mismatch entirely.
 */
#pb-cropper-area {
  width: 100%;
  max-width: min(420px, 60vh);
  max-width: min(420px, 60dvh);
  aspect-ratio: 1;
  margin: 0 auto;
  padding: .75rem;
  border: 1px dashed var(--color-border-light);
  border-radius: var(--border-radius);
  background: var(--color-bg);
  position: relative;
  box-sizing: border-box;
}

#pb-cropper-stage {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

#pb-crop-image {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
}

.pb-crop-reset-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 20;
  padding: .3rem .7rem;
  font-size: .75rem;
  font-family: var(--font-body);
  color: var(--color-white);
  background: rgba(0, 0, 0, .55);
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 999px;
  cursor: pointer;
  transition: background var(--transition);
}

.pb-crop-reset-btn:hover {
  background: rgba(0, 0, 0, .75);
}

.pb-modal .cropper-crop-box,
.pb-modal .cropper-view-box {
  border-radius: 50% !important;
  overflow: hidden;
}

.pb-modal .cropper-crop-box::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 3px solid var(--color-accent);
  border-radius: 50%;
  pointer-events: none;
  box-sizing: border-box;
}

.pb-modal .cropper-modal { background-color: var(--color-bg); opacity: .8; }
.pb-modal .cropper-dashed { display: none; }
.pb-modal .cropper-point {
  background-color: #ffffff;
  border: 2px solid #000000;
  box-shadow: 0 0 3px rgba(0, 0, 0, .6);
  width: 14px;
  height: 14px;
  opacity: 1;
}

/* Live sticker preview — positioned/sized in JS to track the crop box as it's dragged.
   No object-fit: stretching (not letterboxing) to match the server's imagecopyresampled
   behavior exactly, so this is a true WYSIWYG preview of the final composite. */
.pb-sticker-preview-overlay {
  display: none;
  position: absolute;
  pointer-events: none;
  z-index: 10;
}

/* ============================================================
   Stickers
   ============================================================ */
.pb-sticker-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 100010;
  background: rgba(0, 0, 0, .6);
}

.pb-sticker-backdrop.open { display: block; }

.pb-sticker-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 100011;
  /* Kept well under half the screen (was 80vh) so it doesn't cover the crop area above it
     — the whole point of the live sticker preview is being able to see it update while
     picking, which a nearly full-screen sheet defeated. Internal scrolling (see
     .pb-sticker-sheet__body) handles a sticker list too long to fit. */
  max-height: 38vh;
  max-height: 38dvh;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-bottom: none;
  border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
  box-shadow: var(--shadow-lg);
  transform: translateY(100%);
  transition: transform .25s ease;
}

.pb-sticker-sheet.open { transform: translateY(0); }

.pb-sticker-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}

.pb-sticker-sheet__title {
  margin: 0;
  font-family: var(--font-display);
  color: var(--color-white);
  letter-spacing: .02em;
}

.pb-sticker-sheet__body {
  padding: 1rem 1.25rem 1.5rem;
  overflow-y: auto;
}

.pb-sticker-options {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

.pb-sticker-label {
  font-size: .85rem;
  color: var(--color-text-muted);
}

.pb-sticker-sides {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.pb-sticker-side {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
}

.pb-sticker-side-label {
  flex-basis: 100%;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-white);
  padding-bottom: .4rem;
  margin-bottom: .3rem;
  border-bottom: 2px solid var(--color-primary);
}

.pb-sticker-choice {
  display: flex;
  align-items: center;
  gap: .4rem;
  cursor: pointer;
  font-size: .85rem;
  color: var(--color-text);
}

.pb-sticker-choice--none {
  color: var(--color-text-muted);
}

.pb-sticker-choice img {
  border-radius: 4px;
  background: rgba(255,255,255,0.08);
}

@media (max-width: 560px) {
  .pb-sticker-sides {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
   Spinner
   ============================================================ */
.pb-spinner {
  width: 20px;
  height: 20px;
  border: 3px solid var(--color-border-light);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: pb-spin 1s linear infinite;
  display: none;
  margin-right: auto;
}

.pb-spinner.active { display: block; }

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

/* ============================================================
   Step 2 — preview + order form
   ============================================================ */
.pb-preview-wrap {
  text-align: center;
  margin-bottom: 1.25rem;
}

.pb-preview-wrap img {
  /* The theme's global `img { display: block }` reset means text-align:center on the
     wrapper has no effect here — block-level elements need their own auto margins. */
  display: block;
  margin: 0 auto;
  max-width: 260px;
  border-radius: 50%;
  border: 3px solid var(--color-border-light);
  transition: transform .15s ease;
}

.pb-preview-wrap img:hover {
  transform: scale(1.03);
}

.pb-preview-hint {
  margin: .5rem 0 0;
  font-size: .8rem;
  color: var(--color-text-muted);
  text-align: center;
}

#pb-order-form {
  display: grid;
  gap: 1rem;
}

#pb-order-form .pb-field-row {
  display: flex;
  gap: .75rem;
}

#pb-order-form label {
  flex: 1;
  display: block;
  font-size: .85rem;
  color: var(--color-text-muted);
  margin-bottom: .3rem;
}

#pb-order-form input {
  width: 100%;
  margin-top: .3rem;
  padding: .6rem .75rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border-light);
  border-radius: var(--border-radius);
  color: var(--color-text);
  font-size: 1rem;
}

#pb-order-form input:focus {
  outline: none;
  border-color: var(--color-primary);
}

/* ============================================================
   Casual-download deterrents — blocks the common right-click "Save Image
   As" / drag-to-desktop path on source photos and the finished preview.
   Not meant to stop a determined user (DevTools, screenshots, etc. still
   work) — just removes the easy route for typical visitors.
   ============================================================ */
#pb-photo-grid img,
#pb-cropper-area img,
#pb-preview-image {
  -webkit-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
