/* Buttons, fields, search rows, the roster picker and the upload dropzone.
 * var(--token) only — see web/theme/tokens.css. */

/* ——— Buttons ————————————————————————————————————————————— */
.btn{
  appearance:none;cursor:pointer;display:inline-flex;align-items:center;gap:var(--sp-2);
  background:var(--paper-2);color:var(--ink);
  border:var(--bw-1) solid var(--rule-strong);
  padding:var(--sp-2) var(--sp-4);min-height:var(--sz-input);
  font-size:var(--fs-1);font-weight:var(--fw-med);letter-spacing:var(--track-wide);
  border-radius:var(--r-1);text-decoration:none;
}
.btn:hover{background:var(--paper-hi);border-color:var(--ink-3);color:var(--ink)}
.btn svg{width:var(--sz-icon);height:var(--sz-icon)}
.btn-primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.btn-primary:hover{background:var(--accent-hi);border-color:var(--accent-hi);color:var(--accent-ink)}
.btn-ghost{background:transparent;border-color:transparent;color:var(--accent)}
.btn-ghost:hover{background:var(--accent-tint);border-color:var(--accent-rule);color:var(--accent)}
.btn-sm{min-height:auto;padding:var(--sp-1) var(--sp-3);font-size:var(--fs-0)}
.btn-danger{color:var(--color-danger);border-color:var(--color-danger)}
.btn-danger:hover{background:var(--color-danger-tint);color:var(--color-danger)}
.btn-wide{width:100%;justify-content:center}
.btn[disabled]{opacity:var(--op-disabled);cursor:not-allowed}
.btnrow{display:flex;gap:var(--sp-3);flex-wrap:wrap;align-items:center}
.btnrow-t{margin-top:var(--sp-4)}
.btnrow-center{justify-content:center}

/* ——— Fields ————————————————————————————————————————————— */
.field{margin-bottom:var(--sp-4)}
.field > label,.legend{
  display:block;font-size:var(--fs-0);letter-spacing:var(--track-caps);text-transform:uppercase;
  font-weight:var(--fw-bold);color:var(--ink-2);margin-bottom:var(--sp-2);
}
.field .req{color:var(--accent)}
.input,.select,.textarea{
  width:100%;background:var(--paper-inset);color:var(--ink);
  border:var(--bw-1) solid var(--rule-strong);border-radius:var(--r-1);
  padding:var(--sp-2) var(--sp-3);min-height:var(--sz-input);
  font-size:var(--fs-2);font-family:var(--font-ui);
}
.textarea{min-height:var(--h-textarea);resize:vertical;line-height:var(--lh-body)}
.input:focus-visible,.select:focus-visible,.textarea:focus-visible{border-color:var(--accent);background:var(--paper-2)}
.input::placeholder,.textarea::placeholder{color:var(--ink-3)}
.field .help{font-size:var(--fs-0);color:var(--ink-3);margin-top:var(--sp-2)}
.field.err .input,.field.err .select,.field.err .textarea{border-color:var(--color-danger);background:var(--color-danger-tint)}
.field .errmsg{
  display:flex;gap:var(--sp-2);align-items:center;
  font-size:var(--fs-1);color:var(--color-danger);margin-top:var(--sp-2);font-weight:var(--fw-med);
}
.field .errmsg svg{width:var(--sz-icon);height:var(--sz-icon);flex:0 0 auto}

/* D0: a form column is prose, so it is measured. A table never is. */
.form-measure{max-width:var(--w-measure)}
.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--w-grid2-min),1fr));gap:var(--sp-0) var(--sp-4)}

.searchrow{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap;margin-bottom:var(--sp-4)}
.searchbox{position:relative;flex:1 1 var(--fx-search);min-width:0}
.searchbox > svg{
  position:absolute;left:var(--sp-3);top:50%;transform:translateY(-50%);
  width:var(--sz-icon);height:var(--sz-icon);color:var(--ink-3);
}
.searchbox .input{padding-left:var(--sp-7)}
.filterlabel{display:flex;align-items:center;gap:var(--sp-3);flex:1 1 var(--fx-search);min-width:0}
.filterlabel .select{flex:1 1 auto}

.alert{
  display:flex;gap:var(--sp-3);align-items:flex-start;
  padding:var(--sp-3) var(--sp-4);
  border:var(--bw-1) solid var(--color-danger);border-left:var(--bw-4) solid var(--color-danger);
  background:var(--color-danger-tint);color:var(--ink);font-size:var(--fs-2);
  margin-bottom:var(--sp-5);
}
.alert svg{width:var(--sz-icon-lg);height:var(--sz-icon-lg);color:var(--color-danger);flex:0 0 auto}

/* ——— Radio list of instructors (schedule drawer §2) ——————— */
.radiolist{border:var(--bw-1) solid var(--rule);background:var(--paper-2)}
.radiolist label{
  display:flex;gap:var(--sp-3);align-items:center;
  padding:var(--sp-3) var(--sp-4);border-bottom:var(--bw-1) solid var(--rule);cursor:pointer;
}
.radiolist label:last-child{border-bottom:0}
.radiolist label:hover{background:var(--paper-hi)}
.radiolist input{accent-color:var(--accent);flex:0 0 auto}
.radiolist .nm{flex:1 1 auto;min-width:0;font-weight:var(--fw-med)}
.radiolist .sp{display:block;font-weight:var(--fw-reg);color:var(--ink-3);font-size:var(--fs-1)}
.radiolist input:checked + .nm{color:var(--accent)}

/* ——— RosterPicker — dual pane. Degrades to two <select multiple>
   without JS because both panes are plain checkboxes in one form. ——— */
.picker{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-4);width:var(--w-picker)}
.pane{border:var(--bw-1) solid var(--rule);background:var(--paper-2);display:flex;flex-direction:column;min-width:0}
.pane-h{
  padding:var(--sp-3) var(--sp-4);background:var(--paper-3);
  border-bottom:var(--bw-1) solid var(--rule);
  display:flex;align-items:center;gap:var(--sp-3);
}
.pane-h .caps{color:var(--ink-2)}
.pane-tools{padding:var(--sp-3) var(--sp-4);border-bottom:var(--bw-1) solid var(--rule);display:grid;gap:var(--sp-3)}
.chipfilters{display:flex;gap:var(--sp-2);flex-wrap:wrap}
.fchip{
  appearance:none;cursor:pointer;background:var(--paper-2);border:var(--bw-1) solid var(--rule-strong);
  padding:var(--sp-1) var(--sp-3);border-radius:var(--r-pill);
  font-size:var(--fs-0);letter-spacing:var(--track-wide);color:var(--ink-2);text-decoration:none;
}
.fchip:hover{border-color:var(--ink-3);color:var(--ink-2)}
.fchip[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--accent-ink);font-weight:var(--fw-med)}
.pane-body{overflow-y:auto;max-height:var(--h-pane)}
.prow{
  display:flex;gap:var(--sp-3);align-items:center;
  padding:var(--sp-2) var(--sp-4);border-bottom:var(--bw-1) solid var(--rule);
  font-size:var(--fs-2);cursor:pointer;
}
.prow:last-child{border-bottom:0}
.prow:hover{background:var(--paper-hi)}
.prow input{accent-color:var(--accent);flex:0 0 auto}
.prow .nm{flex:1 1 auto;min-width:0}
.prow .nm small{display:block;color:var(--ink-3);font-size:var(--fs-0)}
.prow .x{
  appearance:none;background:transparent;border:0;cursor:pointer;
  color:var(--ink-3);font-size:var(--fs-4);line-height:var(--lh-flat);padding:var(--sp-0) var(--sp-2);
}
.prow .x:hover{color:var(--color-danger)}
.pane-f{
  padding:var(--sp-2) var(--sp-4);border-top:var(--bw-1) solid var(--rule);background:var(--paper-3);
  font-size:var(--fs-0);color:var(--ink-3);font-family:var(--font-mono);
}

/* ——— UploadDropzone: idle · dragover · uploading · success · error ——— */
.dz-zone{min-width:0}
.dz{border:var(--bw-2) dashed var(--rule-strong);background:var(--paper-inset);padding:var(--sp-6) var(--sp-5);text-align:center}
/* The whole zone is the drop target, so the dragover state is announced from it. */
.dz-zone.drag .dz,.dz-zone.drag .dz-state{border-color:var(--accent);background:var(--accent-tint);border-style:solid}
.dz-zone.drag .dz .t,.dz-zone.drag .dz svg.big{color:var(--accent)}
.dz svg.big{width:var(--sp-7);height:var(--sp-7);color:var(--ink-3)}
.dz .t{font-family:var(--font-display);font-size:var(--fs-4);margin-top:var(--sp-3)}
.dz .s{font-size:var(--fs-1);color:var(--ink-3);margin-top:var(--sp-2)}
.dz-state{border:var(--bw-1) solid var(--rule);background:var(--paper-2);padding:var(--sp-4)}
.dz-state.ok{border-color:var(--color-ok);background:var(--color-ok-tint)}
.dz-state.bad{border-color:var(--color-danger);background:var(--color-danger-tint)}
.filerow{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap}
.filerow > svg{width:var(--sz-icon-lg);height:var(--sz-icon-lg);color:var(--ink-3);flex:0 0 auto}
.dz-state.ok .filerow > svg{color:var(--color-ok)}
.dz-state.bad .filerow > svg{color:var(--color-danger)}
.filerow .fn{
  font-family:var(--font-mono);font-size:var(--fs-1);flex:1 1 var(--fx-file);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.filerow .fs{font-family:var(--font-num);font-variant-numeric:tabular-nums;color:var(--ink-3);font-size:var(--fs-1)}
.bar{height:var(--h-progress);background:var(--paper-inset);border:var(--bw-1) solid var(--rule);margin-top:var(--sp-3)}
.bar > i{display:block;height:100%;background:var(--accent);width:var(--w,0%)}
.pct{font-family:var(--font-num);font-variant-numeric:tabular-nums;font-size:var(--fs-1);color:var(--ink-2)}

/* ——— The issued password, shown once (task #31) ————————————————————
 *
 * ★ NO INLINE STYLE ANYWHERE. The Content-Security-Policy is style-src 'self'
 * with no 'unsafe-inline', so a style attribute is dropped silently — which is
 * how a four-star rating once rendered as five (finding C3). Everything the
 * credential sheet needs is a class, here.
 *
 * The value is deliberately the largest, plainest thing on the sheet: it is
 * read aloud down a depot telephone and copied onto paper, and it is never
 * shown again.
 */
.credbox{
  border:var(--bw-1) solid var(--rule-ink);background:var(--paper-inset);
  padding:var(--sp-4) var(--sp-5);
  display:grid;grid-template-columns:auto 1fr;gap:var(--sp-3) var(--sp-5);align-items:baseline;
}
.credbox dt{
  font-size:var(--fs-0);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);font-weight:var(--fw-med);
}
.credbox dd{font-family:var(--font-mono);font-size:var(--fs-2);word-break:break-all}
/* The two notes under the box are separate paragraphs; without this they butt
 * straight against it and read as part of the value. */
.credbox + .help{margin-top:var(--sp-4)}
.help + .help{margin-top:var(--sp-2)}
.credvalue{
  font-family:var(--font-mono);font-size:var(--fs-5);letter-spacing:var(--track-wide);
  line-height:var(--lh-snug);color:var(--accent);
  user-select:all;word-break:break-all;
}

/* Row-level account controls: they wrap rather than widen the table column. */
.rowactions{display:flex;gap:var(--sp-2);justify-content:flex-end;flex-wrap:wrap}

/* ── The CSV import's problem report ──────────────────────────────────────
 *
 * One line per refused row. It is a list rather than a table because each
 * entry is one sentence about one line of the uploaded file, and the line
 * number belongs inside that sentence.
 *
 * The list can be two hundred entries long — a file where every row is wrong
 * is exactly the file somebody uploads first — so it scrolls in its own box
 * rather than making the dialog taller than the screen.
 */
.problems{
  max-height:var(--h-report);
  overflow-y:auto;
  border:var(--bw-1) solid var(--rule);
  background:var(--paper-2);
  font-size:var(--fs-2);
  line-height:var(--lh-body);
}
.problems li{
  display:flex;
  gap:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  border-bottom:var(--bw-1) solid var(--rule);
}
.problems li:last-child{border-bottom:0}
.problems li strong{flex:0 0 var(--w-folio);color:var(--color-danger);white-space:nowrap}
.problems li span{flex:1 1 auto;min-width:0}

/* The issued-credentials table on the import-complete sheet is the same
 * ledger every other table uses; it only needs a ceiling, for the same reason
 * the report does. */
.sheet-b .tablewrap{max-height:var(--h-report);overflow-y:auto}
