/* Bulk upload — the staff screen that turns one club's spreadsheet into
   seventy frames. Loaded AFTER admin.css, which it borrows wholesale: the
   palette, the buttons, the cards, the modal and the toast are Gary's desk's,
   because this is the same desk with a different job on it.

   Only what that file has no equivalent for is written here. */

/* admin.css pins .hidden; this pins the ATTRIBUTE. The repo has been bitten
   before by `hidden` losing to a `display:flex` further down the file — the
   attribute is not a strong selector, and every row of this screen is a flex
   row. Pinned once, at the top, so a hidden thing is hidden. */
[hidden]{display:none !important}

/* A button that reads as a link. Used where the action belongs inside a
   sentence — "download the blank template", "show me the 12" — and a boxed
   button would break the line it is explaining. */
button.link{background:none;border:0;color:var(--goldl);text-decoration:underline;
  padding:0 2px;font:inherit}

/* ── the three stages ───────────────────────────────────────────────────── */
/* Named rather than numbered on purpose: a staff member arriving at a
   half-finished batch should read where they are, not count. */
.steps{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0 0 18px}
.steps .step{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--dim);
  border:1px solid var(--line);border-radius:999px;padding:6px 14px}
.steps .step.now{background:var(--gold);color:#14301f;border-color:var(--gold);font-weight:600}
.steps .step.done{color:var(--goldl);border-color:var(--goldl)}
.steps .arrow{color:var(--dim)}

/* ── upload ─────────────────────────────────────────────────────────────── */
.form-row{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:14px}
.form-row label{flex:1;min-width:220px;font-size:13px;color:var(--dim);display:block}
.form-row label input,.form-row label select{margin-top:5px}
/* The club, stated rather than asked — see #upClubFixed in batch.html. Sits in
   the same row and lines up with the label beside it. */
.form-row .fixedclub{flex:1;min-width:220px;font-size:13px;color:var(--dim);margin:0}
.form-row .fixedclub b{display:block;margin-top:5px;font-size:15px;color:var(--ink)}

/* The drop zone. A big obvious target — the file is the whole point of this
   screen — and it says what it accepts rather than making anyone guess. */
.drop{border:2px dashed var(--line);border-radius:12px;padding:34px 20px;text-align:center;
      color:var(--dim);cursor:pointer;background:rgba(0,0,0,.12)}
.drop:hover,.drop.over{border-color:var(--gold);color:var(--cream)}
.drop strong{display:block;font-size:16px;color:var(--cream);margin-bottom:6px}
.drop .hint{font-size:13px}
.drop input[type=file]{display:none}

.filecard{margin-top:14px}
.filecard .name{font-weight:600}

/* What the parser made of the header row, said out loud. Nothing about a
   spreadsheet import goes wrong more quietly than a column read as the wrong
   thing, so the mapping is shown before anything is created. */
.mapping{margin:12px 0;font-size:13px}
.mapping .col{display:inline-block;border:1px solid var(--line);border-radius:8px;
  padding:4px 10px;margin:0 6px 6px 0}
.mapping .col.miss{border-color:var(--bad);color:#f0b6ae}
.mapping .col em{font-style:normal;color:var(--dim)}

/* ── the review table ───────────────────────────────────────────────────── */
/* Wide, and allowed to scroll sideways on a laptop rather than wrapping into
   an unreadable stack of six-word columns. */
.tablewrap{overflow-x:auto}
table.rows{width:100%;min-width:900px;border-collapse:collapse;font-size:14px}
/* Only the REVIEW table's headings stick — the little preview table on the
   upload screen sits inside a cream card, where a dark-green sticky heading
   would be a black bar across somebody's spreadsheet. */
#rowTable th{position:sticky;top:0;background:var(--gd);z-index:1}
table.rows td{vertical-align:top;padding:10px 8px}
table.rows tr.flagged td{background:rgba(192,85,74,.10)}
table.rows tr.done td{background:rgba(76,154,106,.08)}
table.rows input,table.rows select{padding:6px 8px;font-size:13px}
table.rows td.no{color:var(--dim);white-space:nowrap;font-variant-numeric:tabular-nums}

/* What the club typed, above what we resolved it to. The raw line is the one
   that settles an argument on the phone, so it is always on screen — greyed,
   never hidden behind a hover. */
.raw{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;word-break:break-word}
/* --ink is set by admin.css on .card and inherits, so one declaration reads
   correctly on the dark ground AND on a cream card. Every colour below that
   appears in both places is written this way rather than twice. */
.raw b{font-weight:600;color:var(--ink,var(--cream))}
.same{opacity:.55}
/* The date as it will be engraved, spelled out under the picker. */
.longdate{display:block;margin-top:4px;font-size:12px;color:var(--ink,var(--cream))}

.rowerr{margin:6px 0 0;font-size:12px;color:#f0b6ae}
.card .rowerr{color:var(--bad)}
.rowerr li{margin-left:16px}
.rowerr ul{margin:2px 0;padding:0}

/* Candidates from the fuzzy match. Buttons, not a dropdown: a wrong name is
   engraved on a frame the club pays for, so the choice is made by pressing the
   person, with the score shown. */
.cands{margin-top:6px;display:flex;gap:6px;flex-wrap:wrap}
.cands button{padding:4px 10px;font-size:12px}
.cands .score{color:var(--dim);font-size:11px;margin-left:4px}

.pill.matched{border-color:var(--ok);color:#a8dcbf}
.pill.manual{border-color:#6f9ec4;color:#a9cbe6}
.pill.ambiguous{border-color:var(--goldl);color:var(--goldl)}
.pill.unmatched{border-color:var(--bad);color:#f0b6ae;font-weight:700}
.pill.err{border-color:var(--bad);color:#f0b6ae;font-weight:700}
/* The same four pills on a cream card — admin.css does this for its own
   statuses for the same reason: pale-on-dark is invisible on paper. */
.card .pill.matched{color:#256b45}
.card .pill.manual{color:#2a5d84}
.card .pill.unmatched,.card .pill.err{color:var(--bad)}

/* The gate. It says the number and the reason, because a disabled button with
   no explanation is the most annoying object in software. */
.gate{border:1px solid var(--line);border-left:3px solid var(--bad);border-radius:0 10px 10px 0;
  padding:12px 14px;margin:16px 0;font-size:14px;background:rgba(0,0,0,.2)}
.gate.ready{border-left-color:var(--ok)}
.gate strong{color:var(--ink,var(--cream))}
/* The proofs panel is a cream card and carries a gate of its own when a club
   has no photographs — a black wash there would read as a hole in the page. */
.card .gate{background:rgba(22,49,31,.05)}

.saving{font-size:11px;color:var(--dim);margin-left:6px}
.saving.ok{color:#a8dcbf}
.saving.bad{color:#f0b6ae}

/* ── proofs ─────────────────────────────────────────────────────────────── */
.progress{height:8px;border-radius:999px;background:rgba(0,0,0,.35);overflow:hidden;margin:10px 0}
.progress i{display:block;height:100%;background:var(--gold);width:0}

/* The contact sheet. Small pictures, many at once — the point is to spot the
   one that is wrong, not to read them. Each opens full size. */
.sheet{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;margin-top:16px}
.sheet figure{margin:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;
  background:rgba(0,0,0,.25)}
.sheet canvas,.sheet img{display:block;width:100%;height:auto;background:#000;cursor:zoom-in}
.sheet figcaption{padding:8px 10px;font-size:12px;color:var(--dim)}
.sheet figcaption b{display:block;color:var(--ink,var(--cream));font-size:13px;font-weight:600}
/* A frame that could not be drawn. Says which hole and why, in the space the
   picture would have taken — never a blank sheet, which reads as "fine". */
.sheet .noart{padding:28px 14px;text-align:center;color:var(--bad);font-size:13px;
  border-bottom:1px solid var(--line);background:rgba(192,85,74,.08)}

/* ── ordering ───────────────────────────────────────────────────────────── */
/* The three answers a spreadsheet does not carry — size, finish, needed-by —
   asked once for the whole job rather than seventy times. Laid out as one row
   so it reads as a single decision. */
.jobform{display:flex;gap:14px;flex-wrap:wrap;align-items:flex-end;margin:14px 0}
.jobform label{flex:1;min-width:170px;font-size:13px;color:var(--dim);display:block}
.jobform label select,.jobform label input{margin-top:5px;width:100%}
.jobform .note{flex-basis:100%;margin:0;font-size:12px;color:var(--dim)}

/* The number. Shown before the press and not after it, because "approve 68
   frames" without a total is a click rather than a decision. */
.quote{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;
  border:1px solid var(--line);border-left:3px solid var(--gold);border-radius:0 10px 10px 0;
  padding:12px 14px;margin:14px 0;background:rgba(22,49,31,.05)}
.quote .total{font-size:22px;font-weight:600;color:var(--ink,var(--cream))}
.quote .sums{font-size:13px;color:var(--dim)}
.quote .gst{font-size:12px;color:var(--dim)}
.quote .sp{flex:1}

/* What happened, once frames exist. Refs read back so the club can find them
   on an invoice without asking us. */
.refs{font-size:12px;color:var(--dim);margin:8px 0 0;word-break:break-word}

/* Full size, over everything. */
#lightbox{position:fixed;inset:0;background:rgba(0,0,0,.88);display:flex;align-items:center;
  justify-content:center;flex-direction:column;gap:12px;padding:20px;z-index:40}
#lightbox img{max-width:100%;max-height:82vh;object-fit:contain;background:#000}
#lightbox p{margin:0;color:var(--cream);font-size:14px}

/* ── the batch list ─────────────────────────────────────────────────────── */
.batchline{display:flex;gap:12px;align-items:center;flex-wrap:wrap;font-size:14px}
.batchline .sp{flex:1}
.counts{display:flex;gap:10px;flex-wrap:wrap;font-size:13px;color:var(--dim)}
.counts b{color:var(--ink,var(--cream))}
