/* ═══════════════════════════════════════════════════════════
   Photo Uploader — the shared upload modal (assets/js/components/photo-uploader.js)
   Every photo upload in the app shows this: one list, one bar, one place
   a refusal is explained. Classes are prefixed pu-.
   ═══════════════════════════════════════════════════════════ */

.pu-back {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(3px);
}

.pu-back.is-on { display: flex; }

.pu {
  width: 100%;
  max-width: 460px;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
  overflow: hidden;
}

/* ── Head ── */
.pu__head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 18px 18px 14px;
  border-bottom: 1px solid var(--border);
}

.pu__heading { flex: 1; min-width: 0; }

.pu__title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.3;
}

.pu__note {
  margin: 3px 0 0;
  font-size: 13px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pu__note.is-hidden { display: none; }

.pu__x {
  flex: none;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-muted);
  cursor: pointer;
}

.pu__x:hover:not(:disabled) { color: var(--text-primary); border-color: var(--text-muted); }
.pu__x:disabled { opacity: 0.4; cursor: default; }

/* ── Progress ── */
.pu__track {
  height: 4px;
  background: var(--border);
  overflow: hidden;
}

.pu__fill {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.18s linear;
}

/* No news from the browser yet — sweep rather than sit at 0%, which reads
   as stuck on shop wifi. */
.pu.is-wait .pu__fill {
  width: 40% !important;
  animation: pu-sweep 1.1s ease-in-out infinite;
}

@keyframes pu-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

.pu.is-stage .pu__track,
.pu.is-bad .pu__track { display: none; }

.pu__status {
  margin: 0;
  padding: 12px 18px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}

/* ── Where the next photo comes from ──
   A camera input hands back exactly one shot, so several shots means several
   trips through it. These two buttons are that trip, and the list below is
   what holds the pile in between. */
.pu__srcs {
  display: flex;
  gap: 8px;
  padding: 0 18px 12px;
}

.pu__srcs.is-hidden { display: none; }

.pu__src {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 44px;
  padding: 0 10px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-input);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.pu__src:hover { border-color: var(--accent); color: var(--accent); }
.pu__src.is-hidden { display: none; }
.pu__src svg { flex: none; }

.pu__src__t {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── The photos ── */
.pu__list {
  margin: 0;
  padding: 0 10px 10px;
  list-style: none;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.pu__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--radius-md);
}

.pu__row + .pu__row { border-top: 1px solid var(--border); }

.pu__thumb {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--border);
}

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

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

.pu__name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pu__sub {
  font-size: 11.5px;
  color: var(--text-muted);
  line-height: 1.35;
}

/* A reason is the useful part of a refusal — let it wrap and read. */
.pu__sub--why {
  color: var(--btn-destructive-bg);
  white-space: normal;
}

.pu__state {
  flex: none;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
}

.pu__state--wait { color: var(--text-muted); border-color: var(--border); }
.pu__state--go   { color: #1d4ed8; background: rgba(37, 99, 235, 0.1); border-color: rgba(37, 99, 235, 0.25); }
.pu__state--ok   { color: #15803d; background: rgba(22, 163, 74, 0.1); border-color: rgba(22, 163, 74, 0.25); }
.pu__state--no   { color: #b91c1c; background: rgba(220, 38, 38, 0.1); border-color: rgba(220, 38, 38, 0.25); }

/* A mis-tap on the camera is one shot out of five; before this the only undo
   was to close the lot and start the set again. Photos go up the moment they
   are taken now, so this is the undo for that too — see pu__drop--del. */
.pu__drop {
  flex: none;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-muted);
  cursor: pointer;
}

.pu__drop:hover { color: var(--btn-destructive-bg); border-color: var(--btn-destructive-bg); }
.pu__drop.is-hidden { display: none; }

/* The photo is already on the job, so this ✕ deletes it off the server rather
   than dropping a row off a list. It wears that weight without waiting for a
   hover, which a phone never gives it anyway. */
.pu__drop--del {
  color: var(--btn-destructive-bg);
  border-color: color-mix(in srgb, var(--btn-destructive-bg) 40%, var(--border));
}

/* ── Foot ── */
.pu__foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-input);
}

.pu__btn {
  height: 36px;
  padding: 0 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.pu__btn:hover:not(:disabled) { border-color: var(--text-muted); }
.pu__btn:disabled { opacity: 0.45; cursor: default; }
.pu__btn.is-hidden { display: none; }

.pu__btn--go {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.pu__btn--go:hover:not(:disabled) { filter: brightness(1.06); }

/* Retry only ever offers the photos that failed on the wire — a shot the
   server refused on its merits comes back refused however many times it goes.
   When it is on screen it is the button worth pressing, so it takes the fill
   and Done steps back: Done only ever closes a sheet, since nothing waits
   behind it — every photo goes up as it is taken. */
.pu__btn--retry {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.pu__btn--retry:hover:not(:disabled) { filter: brightness(1.06); }

.pu__btn--retry:not(.is-hidden) ~ .pu__btn--go {
  background: var(--bg-card);
  border-color: var(--border);
  color: var(--text-primary);
}

.pu__btn--retry:not(.is-hidden) ~ .pu__btn--go:hover:not(:disabled) {
  filter: none;
  border-color: var(--text-muted);
}

/* On a phone the footer is thumbs, not a toolbar. */
.pu__foot { flex-wrap: wrap; }

[data-theme="dark"] .pu-back { background: rgba(0, 0, 0, 0.65); }
[data-theme="dark"] .pu__src:hover { color: var(--accent); }
[data-theme="dark"] .pu__state--go { color: #93c5fd; }
[data-theme="dark"] .pu__state--ok { color: #86efac; }
[data-theme="dark"] .pu__state--no { color: #fca5a5; }
[data-theme="dark"] .pu__sub--why { color: #fca5a5; }

/* ── Touch ──
   Nothing anyone has to hit here should be under ~44px. This modal is worked
   with a thumb, in a shop, often in gloves: the remove ✕ on a row was 26px
   and the close was 30px, which on a phone is a coin toss between removing
   the wrong photo and hitting nothing at all. */
@media (pointer: coarse) {
  .pu__x { width: 44px; height: 44px; }
  .pu__x svg { width: 19px; height: 19px; }
  .pu__drop { width: 44px; height: 44px; }
  .pu__drop svg { width: 16px; height: 16px; }
  .pu__src { height: 56px; font-size: 14.5px; }
  .pu__src svg { width: 22px; height: 22px; }
  .pu__btn { height: 48px; padding: 0 18px; font-size: 14px; }
  .pu__row { padding: 10px 8px; gap: 12px; }
  .pu__thumb { width: 56px; height: 56px; }
  .pu__name { font-size: 14.5px; }
  .pu__sub { font-size: 12.5px; }
  .pu__state { padding: 5px 10px; font-size: 12px; }
  .pu__status { font-size: 14px; }
}

/* The shop tablet has the room — a 460px card in the middle of it was sized
   for a laptop window. */
@media (pointer: coarse) and (min-width: 521px) {
  .pu { max-width: 560px; }
}

/* ── The phone sheet ──
   Full screen, not a card floating in the middle of one. There is a whole
   display here and the work being shown — which photos, how far along, what
   was refused and why — is exactly the kind of thing that was being read
   through a letterbox. Header and footer are pinned; only the photos scroll. */
@media (max-width: 520px) {
  .pu-back { padding: 0; align-items: flex-end; }
  /* A sheet off the bottom edge that grows with what is in it, up to almost
     the whole screen. Full-height regardless left three photos marooned above
     a void; this way the room goes to the photos when there are photos, and
     the buttons stay under the thumb either way. */
  .pu {
    max-width: none;
    width: 100%;
    max-height: 92vh;
    max-height: 92dvh;
    border: 0;
    border-bottom: 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }

  /* Sized by its contents, scrolling only once there is no more room. */
  .pu__list { flex: 0 1 auto; }

  /* env() is 0 in a browser tab and the notch inset in the installed app. */
  .pu__head { padding: calc(16px + env(safe-area-inset-top)) 16px 14px; gap: 14px; }
  .pu__title { font-size: 19px; }
  .pu__note { font-size: 14px; white-space: normal; }   /* which car, which job — worth reading */

  .pu__status { padding: 14px 16px; font-size: 14.5px; }

  .pu__srcs { gap: 10px; padding: 0 16px 14px; }
  .pu__src svg { width: 22px; height: 22px; }

  /* Nothing picked yet — the two ways in are the only business on the screen,
     so they are stacked and as big as a thumb could want.
     `flex: 1` is a width instruction in the row layout and a height one here,
     where flex-basis:0 beats the height outright and left a 26px sliver to
     aim at; stacked, they are simply as tall as they are told. */
  .pu.is-empty .pu__srcs { flex-direction: column; }
  .pu.is-empty .pu__src { flex: none; height: 64px; justify-content: flex-start; gap: 13px; padding: 0 18px; font-size: 15.5px; }
  .pu.is-empty .pu__src svg { width: 24px; height: 24px; }

  /* Photos on the list — now they are the business. The two buttons step back
     into one short row and hand the room to the pile they are filling. */
  .pu:not(.is-empty) .pu__src { height: 50px; font-size: 13.5px; gap: 8px; padding: 0 10px; }

  .pu__list { padding: 0 12px 12px; }
  .pu__row { padding: 12px 8px; gap: 13px; }
  .pu__thumb { width: 68px; height: 68px; border-radius: var(--radius-md); }
  .pu__name { font-size: 15px; }
  .pu__sub { font-size: 13px; }
  .pu__state { padding: 6px 11px; font-size: 12.5px; }

  .pu__foot { padding: 14px 16px calc(14px + env(safe-area-inset-bottom)); gap: 10px; }
  /* Full width, stacked. Colour is what says which one matters, so there is
     nothing to gain by making the other one smaller and harder to hit. */
  .pu__btn { flex: 1 1 100%; height: 52px; font-size: 15px; }
  /* The primary gets a row of its own, at the bottom where the thumb already
     is. */
  .pu__btn--go { flex: 1 1 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .pu.is-wait .pu__fill { animation: none; width: 100% !important; }
  .pu__fill { transition: none; }
}
