:root {
  color-scheme: light;
  --ink: #111;
  --muted: #666;
  --line: #e2e2e2;
  --paper: #f2f2f7;
  --name: #0a7cff;
  --price: #12b312;
  /* Vertical rhythm. All three scale gently with screen height. */
  --gap: clamp(16px, 2.8svh, 26px);
  --gap-wide: clamp(26px, 4.5svh, 44px);
  --edge: clamp(20px, 5svh, 56px);
}

* { box-sizing: border-box; }

/* Must beat rules that set display on elements we toggle with [hidden]. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

/* 100svh is the viewport at its *smallest*, with browser toolbars showing.
   Sizing to that plus overflow:hidden means nothing can ever be pushed under
   a toolbar or below the fold, and neither page ever scrolls. */
body.billboard,
body.owner {
  height: 100svh;
  overflow: hidden;
  overscroll-behavior: none;
}

/* ---- Written pages: terms, privacy, reporting ---- */

body.page {
  padding: 0 20px calc(48px + env(safe-area-inset-bottom, 0px));
  line-height: 1.55;
}

body.page main {
  max-width: 620px;
  margin: 0 auto;
  padding-top: calc(32px + env(safe-area-inset-top, 0px));
}

body.page h1 {
  margin: 0 0 4px;
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 30px;
  font-weight: 800;
}

body.page h2 {
  margin: 34px 0 10px;
  font-size: 19px;
  font-weight: 700;
}

body.page p,
body.page li { font-size: 16px; }

body.page ul {
  margin: 0 0 16px;
  padding-left: 22px;
}

body.page li { margin-bottom: 8px; }

body.page a { color: var(--name); }

.crumb {
  margin: 0 0 24px;
  font-size: 15px;
}

.crumb a {
  color: var(--muted);
  text-decoration: none;
}

.updated {
  margin: 0 0 24px;
  font-size: 14px;
  color: var(--muted);
}

.cta { margin: 22px 0 30px; }

.mail {
  display: inline-block;
  padding: 14px 22px;
  font-weight: 700;
  color: #fff !important;
  background: var(--ink);
  border-radius: 12px;
  text-decoration: none;
}

.foot {
  margin-top: 40px;
  padding-top: 18px;
  font-size: 14px;
  color: var(--muted);
  border-top: 1px solid var(--line);
}

/* Small print on the billboard itself, in the space below the group. */
.site-links {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(6px + env(safe-area-inset-bottom, 0px));
  display: flex;
  justify-content: center;
  gap: 14px;
  font-size: 11px;
  color: var(--muted);
}

.site-links a {
  color: inherit;
  text-decoration: none;
  padding: 4px;
}

/* A landscape phone has no room to spare under the group. */
@media (max-height: 430px) {
  .site-links { display: none; }
}

/* ---- Shared shell: headline, photo and action as one centered group ---- */

#stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* The three pieces travel together in the middle of the screen, and the
     leftover space is shared evenly above and below the group. */
  justify-content: center;
  gap: var(--gap);
  height: 100svh;
  max-width: 560px;
  margin: 0 auto;
  padding: calc(var(--edge) + env(safe-area-inset-top, 0px)) 20px
           calc(var(--edge) + env(safe-area-inset-bottom, 0px));
}

/* The small print sits in the bottom padding, so the group leaves it room.
   (The photo is the only piece that shrinks, so nothing else moves.) */
body.billboard #stage {
  padding-bottom: calc(var(--edge) + 22px + env(safe-area-inset-bottom, 0px));
}

/* A wide photo leaves height to spare, so the group gets a little more air.
   A tall one needs every pixel, so it keeps the tighter gap. */
#stage.is-wide { --gap: var(--gap-wide); }

/* Two lines, each kept whole. This is the preferred size; app.js shrinks it
   just enough for the longer line to fit the width, so a long name makes the
   text a little smaller instead of breaking a line in the middle. */
#headline {
  flex: none;
  align-self: stretch;
  margin: 0;
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: clamp(22px, 7.2vw, 32px);
  font-weight: 800;
  line-height: 1.25;
  text-align: center;
}

#headline .line {
  display: block;
  width: max-content;
  margin-inline: auto;
  white-space: nowrap;
}

/* Last resort for an unusually long name: wrap, at a still-readable size. */
#headline.wrap .line {
  width: auto;
  white-space: normal;
  text-wrap: balance;
}

#headline .name { color: var(--name); }
#headline .price { color: var(--price); }

/* The photo keeps its own aspect ratio. A tall photo is the ideal; a square
   or landscape one simply fits smaller. */
#photo-wrap {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

/* The frame takes the photo's exact box before the image arrives (from the
   width and height stored at upload), so nothing moves when it loads. The
   stored blur fills the box in the meantime and the photo fades in over it. */
.frame {
  /* --chrome is everything on the page that is not the photo, measured in
     app.js. The photo gets exactly what is left, so the group can never
     overflow the screen. The floor keeps it visible on very short screens. */
  --photo-max: max(80px, calc(100svh - var(--chrome, 280px)));
  max-width: 100%;
  overflow: hidden;
  border-radius: 4px;
  background-color: #e4e4ea;
  background-position: center;
  background-size: cover;
  cursor: zoom-in;
}

/* Known shape: the box is the photo, fitted into the space that is left. */
.frame.sized {
  aspect-ratio: var(--ar);
  width: min(100%, calc(var(--photo-max) * var(--ar)));
}

.frame img {
  display: block;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.frame img.loaded { opacity: 1; }

.frame.sized img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Photos saved before sizes were stored: size from the image itself. */
.frame:not(.sized) img {
  max-width: 100%;
  max-height: var(--photo-max);
  width: auto;
  height: auto;
}

@media (prefers-reduced-motion: reduce) {
  .frame img { transition: none; }
}

#spot-empty,
#empty-note {
  margin: 0;
  padding: 0 24px;
  font-size: 16px;
  color: var(--muted);
  text-align: center;
}

/* Part of the group, directly under the photo. It still can't be pushed off
   screen, because the photo is the only piece allowed to shrink. */
#actions {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 100%;
}

#status {
  margin: 0 0 6px;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}

/* The minute a buyer has to pay once a photo is picked. Tabular figures keep
   the digits from jittering as they count down. */
.countdown {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.timer {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.countdown.urgent,
.timer.urgent { color: #d92d20; }

/* "Hide my name". The whole row is the tap target, at least 44px tall. */
.private-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  margin: 0 0 8px;
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.private-toggle input {
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--ink);
  cursor: pointer;
}

/* The name shown with the photo. 16px keeps iOS from zooming on focus. */
.name-field {
  width: min(100%, 320px);
  min-height: 48px;
  margin: 0 0 4px;
  padding: 10px 14px;
  font: inherit;
  font-size: 16px;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  text-align: center;
}

.name-field:focus {
  outline: none;
  border-color: var(--ink);
}

.name-field.missing { border-color: #d92d20; }

/* Turnstile's widget, when it asks for a tap. Empty (and so no height) otherwise. */
.human-check {
  width: min(100%, 320px);
}

.human-check:empty { display: none; }

/* Stripe's Apple Pay / Google Pay button. */
/* The wallet button's place. While Stripe loads it, the skeleton sits on top
   of the same 55px, so nothing moves when the button arrives. */
.wallet-area {
  position: relative;
  width: min(100%, 320px);
}

.wallet-area.pending { min-height: 55px; }

.wallet-skeleton {
  position: absolute;
  inset: 0 0 auto;
  height: 55px;
}

.wallet-slot {
  width: 100%;
  border-radius: 12px;
}

.wallet-slot.off {
  height: 0;
  overflow: hidden;
  visibility: hidden;
}

/* ---- Primary and secondary actions ---- */

.pay {
  min-height: 60px;
  padding: 16px 44px;
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 23px;
  font-weight: 800;
  color: var(--ink);
  background: #e4e4ea;
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}

.pay:active { background: #d8d8e0; }
.pay:disabled { opacity: 0.5; cursor: default; }
.pay .price { color: var(--price); }

/* While previewing a picked photo, paying is the only step left. */
.pay.ready {
  background: #fff;
  animation: nudge 1.8s ease-in-out infinite;
}

@keyframes nudge {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  }
  50% {
    transform: scale(1.04);
    box-shadow: 0 10px 26px rgba(0, 0, 0, 0.18);
  }
}

@media (prefers-reduced-motion: reduce) {
  .pay.ready { animation: none; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16); }
}

.row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.btn-secondary {
  min-height: 60px;
  padding: 16px 24px;
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 18px;
  font-weight: 700;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}

.btn-secondary:active { background: #e4e4ea; }
.btn-secondary:disabled { opacity: 0.4; cursor: default; }

/* Sits in the dock, replacing the buttons while a commit is pending. */
.undo {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 18px;
  font-size: 15px;
  color: #fff;
  background: var(--ink);
  border-radius: 12px;
}

.undo span { flex: 1; }

.undo button {
  min-height: 32px;
  padding: 0;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  color: #fff;
  background: none;
  border: 0;
  text-decoration: underline;
  cursor: pointer;
}

.link {
  min-height: 44px;
  padding: 10px 0;
  font: inherit;
  font-size: 14px;
  color: var(--muted);
  background: none;
  border: 0;
  text-decoration: underline;
  cursor: pointer;
}

.link.center { display: block; width: 100%; text-align: center; }

/* ---- Owner options ---- */

.menu-btn {
  position: fixed;
  top: calc(10px + env(safe-area-inset-top, 0px));
  right: 10px;
  z-index: 30;
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--muted);
  background: #e4e4ea;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.menu-btn:active { background: #d8d8e0; }

/* Keep the headline clear of the corner button on short screens. */
body.owner #stage {
  padding-top: max(
    calc(var(--edge) + env(safe-area-inset-top, 0px)),
    calc(62px + env(safe-area-inset-top, 0px))
  );
}

.sheet-title {
  margin: 0 0 12px;
  font-size: 20px;
  font-weight: 700;
}

.sheet-title:focus { outline: none; }

.sheet-actions {
  margin-bottom: 8px;
  border-top: 1px solid var(--line);
}

.sheet-action {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 14px 2px;
  font: inherit;
  font-size: 17px;
  text-align: left;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}

.sheet-action.danger { color: #d92d20; }

/* ---- Full screen viewer ---- */

#viewer {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: #000;
  touch-action: none;
}

#viewer img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transform-origin: center center;
  will-change: transform;
  -webkit-user-select: none;
  user-select: none;
}

#viewer button {
  position: absolute;
  top: calc(12px + env(safe-area-inset-top, 0px));
  right: 12px;
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 22px;
  line-height: 1;
  color: #fff;
  background: rgba(0, 0, 0, 0.5);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* ---- Sheets ---- */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 52px;
  padding: 14px;
  font: inherit;
  font-size: 17px;
  font-weight: 600;
  border: 0;
  border-radius: 14px;
  cursor: pointer;
}

.btn.ink {
  margin-top: 22px;
  color: #fff;
  background: var(--ink);
}

.btn.danger {
  margin-top: 22px;
  color: #fff;
  background: #d92d20;
}

.btn:disabled { opacity: 0.5; cursor: default; }

/* --kb is how much of the viewport the on-screen keyboard is covering, set
   from the VisualViewport API. iOS shrinks the visual viewport without moving
   the layout viewport, so a bottom-anchored sheet would otherwise sit behind
   the keyboard. Lifting by --kb keeps the fields in view. */
dialog {
  position: fixed;
  /* The UA stylesheet gives dialog:modal BOTH inset-block-start:0 and
     inset-block-end:0. Overriding only `bottom` leaves `top:0` in place, which
     stretches the sheet the full height and then clips it with max-height —
     pinning it to the top with a gap below. It has to be released explicitly. */
  top: auto;
  left: 0;
  right: 0;
  bottom: var(--kb, 0px);
  width: 100%;
  max-width: 480px;
  max-height: calc(100svh - var(--kb, 0px) - 24px);
  margin: 0 auto;
  padding: 20px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  color: inherit;
  background: #fff;
  border: 0;
  border-radius: 18px 18px 0 0;
}

dialog::backdrop { background: rgba(0, 0, 0, 0.5); }

@media (min-width: 560px) {
  dialog {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    border-radius: 18px;
  }
}

.wallet-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}

.wallet-title {
  flex: 1;
  font-size: 20px;
  font-weight: 700;
}

.sim {
  padding: 4px 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  background: #f2f2f2;
  border-radius: 6px;
}

.wallet-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}

.wallet-row span,
.wallet-row label {
  flex: 0 0 28%;
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

.wallet-row b { font-size: 15px; }

.wallet-row input {
  flex: 1;
  min-width: 0;
  padding: 8px 0;
  font: inherit;
  font-size: 16px; /* keeps iOS from zooming on focus */
  color: inherit;
  background: none;
  border: 0;
  text-align: left;
}

.wallet-row.total b { font-size: 20px; font-weight: 700; }

.card-area {
  position: relative;
  margin-top: 16px;
}

.card-form { min-height: 150px; }

/* Stripe's card fields load into .card-form; this covers it until they do. */
.skeleton-group {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: #fff;
}

.skeleton-group[hidden] { display: none; }

.skeleton-group .skeleton { height: 44px; }
.skeleton-group .skeleton.short { width: 60%; }

/* ---- Loading placeholders ---- */

.skeleton {
  display: block;
  border-radius: 12px;
  background: linear-gradient(90deg, #e4e4ea 0%, #eeeef3 40%, #e4e4ea 80%) 0 0 / 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

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

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

.result-title {
  margin: 4px 0 0;
  font-size: 22px;
  font-weight: 700;
}

.result-body { margin: 10px 0 0; color: var(--muted); }

.note {
  margin: 14px 0 0;
  font-size: 13px;
  color: var(--muted);
}

.note.center { text-align: center; }

/* Takes no space until there is something to say, so it doesn't leave a gap
   above the button the rest of the time. */
.error:empty { display: none; }

.error {
  margin: 12px 0 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
