/* ============================================================
   AK Technik — design tokens
   Black and red. Surfaces step up from pure black (#000, #111,
   #1A), text is near-white on top of them, and one red carries
   every accent: buttons, links, focus rings, the brand. Green
   stays green on purpose -- "this worked" must not be the same
   colour as "this is on fire".

   The old manila inventory tag is now a dark card with a red
   dashed edge. It still reads as a tag, and the scanned item is
   still the thing that matters most on screen.
   ============================================================ */

:root {
  /* -- color: surfaces (black) -- */
  --bg: #000000;
  --panel: #111111;
  --panel-2: #1A1A1A;
  --line: rgba(255, 255, 255, 0.12);

  /* -- color: the "tag" card -- */
  --tag: #101010;
  --tag-ink: #F4F4F4;
  --tag-muted: #9E9E9E;
  --tag-line: rgba(229, 57, 53, 0.45);

  /* -- color: text on black -- */
  --text: #F4F4F4;
  --muted: #A6A6A6;

  /* -- color: accents (red) -- */
  --amber: #E53935;      /* the red: fills, links, focus, brand */
  --amber-ink: #000000;  /* black on red -- this is a red and black app */
  --rust: #FF5A4F;       /* the brighter red: overdue, short, refused */
  --rust-ink: #000000;   /* black on a rust fill */
  --rust-light: #FF7A70; /* red text that still has to be readable */
  --moss: #6E8B5D;
  --moss-light: #A3C28E;

  /* -- type -- */
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* -- shape -- */
  --radius-s: 6px;
  --radius-m: 10px;
  --radius-l: 18px;

  color-scheme: dark;
}

/* ============================================================
   Reset & base
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, figure { margin: 0; }

a { color: inherit; }

button, input {
  font-family: inherit;
  color: inherit;
}

img { max-width: 100%; display: block; }

:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* ============================================================
   Shell / layout
   ============================================================ */

.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.app-main {
  flex: 1;
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  padding: 28px 20px 64px;
}

.topbar {
  display: flex;
  flex-wrap: wrap;
  row-gap: 12px;
  align-items: center;
  justify-content: space-between;
  max-width: 620px;
  margin: 0 auto;
  padding: 20px 20px 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  color: var(--text);
}

.brand-mark {
  color: var(--amber);
  display: inline-flex;
}

.brand-text {
  font-family: var(--font-mono);
  font-weight: 600;
  letter-spacing: 0.06em;
  font-size: 0.95rem;
}

.brand-accent { color: var(--amber); }

/* The school's own logo, dropped into static/img/school-logo.png. It ships
   as black artwork on an opaque white background -- no transparency -- so it
   gets a white plate of its own rather than pretending to be a cut-out, and
   the rounded corners belong to that plate. The padding is what keeps the
   artwork off them, because the file has almost no margin of its own.

   If the file is not on the server the <img> fires an error and removes
   itself from the DOM (see partials/header.php). It has to be removed and not
   merely hidden: the sibling selector below only stops matching once the img
   is gone, and that is what brings the tag mark back. */
.brand-logo {
  display: block;
  height: 38px;
  width: auto;
  padding: 4px 7px;
  background: #FFFFFF;
  border-radius: 5px;
}

.brand-logo + .brand-mark { display: none; }

.topnav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}

/* 9px rather than 12px, measured: seven tabs need 615px at 12px and 573px at
   9px, and the topbar has 580px of content. At 12px the last tab sat alone on
   a second row. Two pending-request badges will still push it to a second row
   -- that is what flex-wrap on .topnav is for -- but the ordinary case is one
   line. */
.topnav a {
  text-decoration: none;
  color: var(--muted);
  padding: 8px 9px;
  border-radius: var(--radius-s);
  transition: color 0.15s ease, background 0.15s ease;
}

.topnav a:hover { color: var(--text); background: var(--panel); }
.topnav a.active { color: var(--amber-ink); background: var(--amber); }

/* Who you are signed in as. A hairline sets it apart from the tabs so it does
   not read as one more place to go, and it is the one item in the bar that is
   a label as much as a link. */
.topnav-account {
  margin-left: 4px;
  border-left: 1px solid var(--line);
  color: var(--text);
}

.app-footer {
  max-width: 620px;
  margin: 0 auto;
  width: 100%;
  padding: 16px 20px 28px;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
}

.app-footer a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
.app-footer a:hover { color: var(--text); }

/* ============================================================
   Buttons
   ============================================================ */

.btn {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  border: none;
  border-radius: var(--radius-m);
  padding: 13px 20px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: transform 0.12s ease, background 0.15s ease, opacity 0.15s ease;
}

.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; transform: none; }

.btn-block { width: 100%; }

.btn-small { padding: 9px 14px; font-size: 0.72rem; }

.btn-primary { background: var(--amber); color: var(--amber-ink); }
.btn-primary:hover:not(:disabled) { background: #FF4B42; }

.btn-secondary { background: var(--panel-2); color: var(--text); border: 1px solid var(--line); }
.btn-secondary:hover:not(:disabled) { background: #262626; }

.btn-rust { background: var(--rust); color: var(--rust-ink); }
.btn-rust:hover:not(:disabled) { background: #FF7A72; }

.btn-ghost { background: transparent; color: var(--muted); border: 1px solid var(--line); }
.btn-ghost:hover:not(:disabled) { color: var(--text); border-color: var(--muted); }

/* ============================================================
   Scan page
   ============================================================ */

.scan-intro { text-align: center; margin-bottom: 28px; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.scan-intro h1 {
  font-family: var(--font-mono);
  font-size: 1.7rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--text);
}

.scan-target {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: 0 auto 16px;
}

.scan-trigger {
  position: relative;
  width: min(260px, 72vw);
  aspect-ratio: 1;
  background: var(--panel);
  border: none;
  border-radius: var(--radius-l);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  cursor: pointer;
  color: var(--muted);
  transition: background 0.15s ease;
}

.scan-trigger:hover { background: var(--panel-2); color: var(--text); }

.scan-trigger-icon { color: var(--amber); }

.scan-trigger-label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.corner {
  position: absolute;
  width: 26px;
  height: 26px;
  border: 3px solid var(--amber);
  transition: width 0.15s ease, height 0.15s ease;
}

.corner-tl { top: 10px; left: 10px; border-right: none; border-bottom: none; border-radius: 6px 0 0 0; }
.corner-tr { top: 10px; right: 10px; border-left: none; border-bottom: none; border-radius: 0 6px 0 0; }
.corner-bl { bottom: 10px; left: 10px; border-right: none; border-top: none; border-radius: 0 0 0 6px; }
.corner-br { bottom: 10px; right: 10px; border-left: none; border-top: none; border-radius: 0 0 6px 0; }

.scan-trigger:hover .corner { width: 32px; height: 32px; }

.qr-reader {
  width: min(260px, 72vw);
  border-radius: var(--radius-l);
  overflow: hidden;
  border: 3px solid var(--amber);
  background: #000;
  line-height: 0;
}

.qr-reader video { width: 100%; height: auto; display: block; }

.scan-hint {
  text-align: center;
  color: var(--muted);
  font-size: 0.85rem;
  max-width: 40ch;
  margin: 0 auto 28px;
}

/* ---- manual entry ---- */

.manual-entry { margin-bottom: 32px; }

.manual-entry label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}

.manual-entry-row { display: flex; gap: 8px; }

.manual-entry-row input {
  flex: 1;
  min-width: 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 12px 14px;
  color: var(--text);
  font-size: 0.92rem;
}

.manual-entry-row input::placeholder { color: var(--muted); }
.manual-entry-row input:focus { border-color: var(--amber); }

/* ---- status region ---- */

.state-block {
  text-align: center;
  padding: 24px 20px;
  border-radius: var(--radius-m);
  color: var(--text);
}

.error-tag {
  background: rgba(255, 90, 79, 0.12);
  border: 1px solid rgba(255, 90, 79, 0.4);
}

.error-tag p { margin-bottom: 14px; }

.success-tag {
  background: rgba(110, 139, 93, 0.14);
  border: 1px solid rgba(110, 139, 93, 0.45);
  font-weight: 500;
}

/* ============================================================
   The tag card — signature component
   ============================================================ */

.tag-card {
  background: var(--tag);
  color: var(--tag-ink);
  border-radius: var(--radius-l);
  padding: 8px 24px 24px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);
}

.tag-perforation {
  display: flex;
  justify-content: center;
  padding: 12px 0 16px;
  border-bottom: 1px dashed var(--tag-line);
  margin-bottom: 20px;
}

.tag-punch {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--tag-line);
}

.tag-item {
  display: flex;
  gap: 16px;
  margin-bottom: 22px;
}

.tag-photo {
  flex: none;
  width: 84px;
  height: 84px;
  border-radius: var(--radius-m);
  background: rgba(255, 255, 255, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--tag-muted);
  overflow: hidden;
}

.tag-photo img { width: 100%; height: 100%; object-fit: cover; }

.tag-info { min-width: 0; flex: 1; }

.tag-location {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tag-muted);
  margin-bottom: 4px;
}

.tag-info h2 {
  font-family: var(--font-sans);
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--tag-ink);
}

.tag-description {
  font-size: 0.85rem;
  color: var(--tag-muted);
  margin-top: 6px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.tag-homebox-link {
  display: inline-block;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--rust);
  text-decoration: none;
}

.tag-homebox-link:hover { text-decoration: underline; }

/* ---- forms inside the tag card ---- */

.tag-action .field { margin-bottom: 14px; }

.tag-action label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tag-muted);
  margin-bottom: 6px;
}

.tag-action input[type="text"],
.tag-action input[type="date"] {
  width: 100%;
  background: #1A1A1A;
  border: 1px solid var(--tag-line);
  border-radius: var(--radius-s);
  padding: 11px 12px;
  font-size: 0.95rem;
  color: var(--tag-ink);
}

.tag-action input:focus { border-color: var(--rust); }

.more-options {
  margin-bottom: 16px;
}

.more-options summary {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--tag-muted);
  padding: 4px 0;
}

.more-options[open] summary { margin-bottom: 12px; }

.loan-status {
  font-size: 1.02rem;
  margin-bottom: 6px;
}

.loan-status span { color: var(--tag-muted); font-size: 0.88rem; }

.loan-due {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  background: rgba(255, 90, 79, 0.12);
  color: var(--rust-light);
  border-radius: var(--radius-s);
  padding: 4px 10px;
  margin-bottom: 10px;
}

.loan-note {
  font-size: 0.88rem;
  color: var(--tag-muted);
  font-style: italic;
  margin-bottom: 14px;
}

.tag-card .success-tag {
  background: rgba(110, 139, 93, 0.16);
  border: 1px solid rgba(110, 139, 93, 0.4);
  color: var(--moss-light);
  padding: 18px 16px;
}

.scan-again-link {
  display: block;
  width: 100%;
  text-align: center;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  color: var(--tag-muted);
  cursor: pointer;
  padding-top: 16px;
  margin-top: 4px;
}

.scan-again-link:hover { color: var(--tag-ink); }

/* ============================================================
   Overview page
   ============================================================ */

.overview-page h1 {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  letter-spacing: 0.01em;
  margin-bottom: 20px;
}

.overview-header {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 32px;
}

.search-field input {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 10px 14px;
  color: var(--text);
  font-size: 0.88rem;
  width: 220px;
  max-width: 100%;
}

.search-field input::placeholder { color: var(--muted); }
.search-field input:focus { border-color: var(--amber); }

.ledger { margin-bottom: 40px; }

.ledger h2 {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.count-badge {
  font-family: var(--font-mono);
  background: var(--amber);
  color: var(--amber-ink);
  border-radius: 999px;
  padding: 1px 9px;
  font-size: 0.78rem;
}

.ledger-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }

.ledger-row {
  display: flex;
  gap: 14px;
  align-items: center;
  background: var(--panel);
  border-left: 3px solid var(--amber);
  border-radius: var(--radius-m);
  padding: 14px 16px;
  flex-wrap: wrap;
}

.ledger-row.is-overdue { border-left-color: var(--rust); }
.ledger-row-muted { border-left-color: transparent; opacity: 0.75; }

.ledger-thumb {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-s);
  background: var(--panel-2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  overflow: hidden;
}

.ledger-thumb img { width: 100%; height: 100%; object-fit: cover; }

.ledger-main { flex: 1; min-width: 160px; }

.ledger-item-name { font-weight: 600; font-size: 0.95rem; }

.ledger-meta { font-size: 0.82rem; color: var(--muted); margin-top: 2px; }
.ledger-meta strong { color: var(--text); font-weight: 600; }

.ledger-note { font-size: 0.8rem; color: var(--muted); font-style: italic; margin-top: 4px; }

.ledger-side {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

.due-badge {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.03em;
  padding: 4px 9px;
  border-radius: var(--radius-s);
  background: rgba(110, 139, 93, 0.15);
  color: var(--moss-light);
  border: 1px solid rgba(110, 139, 93, 0.4);
  white-space: nowrap;
}

.due-badge-overdue {
  background: rgba(255, 90, 79, 0.18);
  color: var(--rust-light);
  border-color: rgba(255, 90, 79, 0.5);
}

.duration-badge {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

.empty-note { color: var(--muted); font-size: 0.9rem; padding: 8px 2px; }

/* ============================================================
   Health / status page
   ============================================================ */

.health-page h1 {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  margin-bottom: 20px;
}

.status-card {
  background: var(--panel);
  border-radius: var(--radius-m);
  padding: 6px 20px;
}

.status-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 18px 0;
}

.status-row + .status-row { border-top: 1px solid var(--line); }

.status-dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  margin-top: 6px;
  flex: none;
}

.status-dot-ok { background: var(--moss); box-shadow: 0 0 0 3px rgba(110, 139, 93, 0.2); }
.status-dot-bad { background: var(--rust); box-shadow: 0 0 0 3px rgba(255, 90, 79, 0.2); }
/* "Works, but probably not what you meant" -- e.g. a feed that is
   reachable yet has nothing marked, so nothing is being published. */
.status-dot-warn { background: var(--amber); box-shadow: 0 0 0 3px rgba(229, 57, 53, 0.2); }
/* Deliberately switched off. Not a fault, so it is not drawn as one. */
.status-dot-idle { background: var(--muted); box-shadow: 0 0 0 3px rgba(166, 166, 166, 0.15); }

.status-label { font-weight: 600; margin-bottom: 4px; }
.status-detail { color: var(--muted); font-size: 0.9rem; line-height: 1.6; }

.status-detail code {
  background: var(--panel-2);
  border-radius: 4px;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: 0.85em;
}

.health-hint { color: var(--muted); font-size: 0.82rem; margin-top: 20px; }

/* ============================================================
   Login / install
   ============================================================ */

.auth-page {
  display: flex;
  justify-content: center;
  padding-top: 8px;
}

.auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: 28px 24px;
}

.auth-card h1 {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  font-weight: 700;
  margin-bottom: 18px;
}

.auth-form .field { margin-bottom: 16px; }

.auth-form label,
.stack-form label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 7px;
}

.auth-form input[type="text"],
.auth-form input[type="password"],
.stack-form input[type="text"],
.stack-form input[type="password"],
.stack-form input[type="email"],
.stack-form input[type="datetime-local"],
.stack-form input[type="date"],
.stack-form input[type="number"],
.stack-form select,
.stack-form textarea,
.user-email,
.user-class,
.user-role {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 12px 13px;
  font-size: 1rem;
  color: var(--text);
}

/* A textarea should not grow taller than the page it sits on. */
.stack-form textarea {
  resize: vertical;
  min-height: 76px;
  line-height: 1.55;
  font-family: inherit;
}

/* The date and number boxes get platform widgets, which inherit their own
   font and end up looking like a different app pasted into the middle of
   this one. Naming the font here keeps the digits the same as everything
   else. */
.stack-form input[type="datetime-local"],
.stack-form input[type="date"],
.stack-form input[type="number"] {
  font-family: var(--font-mono);
}

.auth-form input:focus,
.stack-form input:focus,
.stack-form select:focus,
.stack-form textarea:focus,
.user-email:focus,
.user-class:focus,
.user-role:focus {
  border-color: var(--amber);
  outline: none;
}

.field-hint {
  color: var(--muted);
  font-size: 0.78rem;
  margin-top: 6px;
  line-height: 1.5;
}

.auth-error,
.auth-note {
  border-radius: var(--radius-s);
  padding: 11px 13px;
  font-size: 0.88rem;
  line-height: 1.55;
  margin-bottom: 18px;
}

.auth-error {
  background: rgba(255, 90, 79, 0.16);
  border: 1px solid rgba(255, 122, 112, 0.4);
  color: var(--rust-light);
}

.auth-note {
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--muted);
}

.auth-note-ok {
  background: rgba(110, 139, 93, 0.16);
  border-color: rgba(163, 194, 142, 0.4);
  color: var(--moss-light);
}

.auth-hint {
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 18px;
  text-align: center;
}

/* A second hint under the first, saying why there is no password box for
   somebody who forgot theirs. Quieter, because the link above is the thing
   to press and this is only there so the absence reads as deliberate. */
.auth-hint-quiet {
  margin-top: 6px;
  font-size: 0.76rem;
  opacity: 0.8;
}

/* The sign-up form is wider than a sign-in card, because it asks four
   things rather than two and a 400px column turns four one-line boxes into a
   corridor. */
.auth-card-wide { max-width: 480px; }

/* A list of things to fix inside one error block. The block itself is the
   same red as any other message; what is new is that the reasons are counted
   and listed, because "one thing to fix" and "three things to fix" call for
   different amounts of patience. */
.auth-error ul {
  margin: 6px 0 0;
  padding-left: 20px;
}

.auth-error li { margin-bottom: 4px; }

.auth-error-lead {
  margin: 0;
  font-weight: 700;
}

.auth-note-quiet {
  background: none;
  border: none;
  padding: 0;
  font-size: 0.82rem;
}

/* The number of waiting requests, next to the heading. Counted out loud on
   purpose: a card that says "Waiting to be added" with no number reads as
   "nothing here", which is the one thing it must never do. */
.pill-count {
  margin-left: 8px;
  vertical-align: middle;
}

/* A line under a row that is a warning rather than information: the request
   is fine, the mail is not. Kept in the muted family so the page does not
   turn entirely red over a mail server. */
.user-meta-warn { color: var(--rust-light); }

/* The closed-requests list. Folded because it only ever grows and nobody
   needs it to answer "who is waiting" -- but it matters when somebody asks
   in six weeks whether they ever asked at all. */
.fold {
  margin-top: 20px;
  border-top: 1px solid var(--line);
  padding-top: 14px;
}

.fold > summary {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  padding: 4px 0;
}

.fold > summary:hover { color: var(--text); }

.fold > summary:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

/* ============================================================
   Account & admin
   ============================================================ */

.account-page h1,
.admin-page h1 {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 700;
  margin-bottom: 8px;
}

.account-meta {
  color: var(--muted);
  font-size: 0.85rem;
  margin-bottom: 22px;
}

.account-meta code {
  background: var(--panel-2);
  border-radius: 4px;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: 0.85em;
}

.panel-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  padding: 22px 20px;
  margin-bottom: 18px;
}

.panel-card h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.panel-hint {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.6;
  margin-bottom: 16px;
}

.stack-form .field { margin-bottom: 16px; }

.stack-form select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 36px;
}

.text-link { color: var(--amber); text-decoration: none; font-size: 0.9rem; }
.text-link:hover { text-decoration: underline; }

/* -- the one-time password box -- */

.one-time-card {
  background: var(--amber);
  color: var(--amber-ink);
  border-radius: var(--radius-l);
  padding: 18px 20px;
  margin-bottom: 20px;
}

.one-time-title {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 12px;
}

.one-time-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
}

.one-time-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.75;
  min-width: 78px;
}

.one-time-row code {
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.10);
  border-radius: var(--radius-s);
  padding: 4px 10px;
  word-break: break-all;
}

.one-time-foot {
  font-size: 0.8rem;
  line-height: 1.55;
  margin-top: 12px;
  opacity: 0.85;
}

/* -- the account list -- */

.user-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.user-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 13px 15px;
}

.user-row-off { opacity: 0.6; }

/* -- attendance overview: one big count on the right -- */

.attendance-list { margin-top: 12px; }

.attendance-score {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  min-width: 72px;
}

.attendance-number {
  font-family: var(--font-mono);
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1;
  color: var(--amber);
}

.attendance-label {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 4px;
}

.user-main { min-width: 0; flex: 1 1 190px; }

.user-name {
  font-weight: 600;
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

.user-meta {
  color: var(--muted);
  font-size: 0.78rem;
  margin-top: 3px;
}

.user-meta code {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

.pill {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
}

.pill-you { background: var(--amber); color: var(--amber-ink); }
.pill-off { background: var(--rust); color: var(--rust-ink); }

.user-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
}

.user-role {
  width: auto;
  padding: 8px 30px 8px 10px;
  font-size: 0.82rem;
  background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%;
}

/* ---------- email and class on an account ----------

   Two separate forms per row rather than one form with everything in it.
   Nesting is invalid HTML, and a single form would mean "Save role" could
   also quietly save an email that was being typed into at the time. */

.user-side {
  flex: 2 1 340px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
}

.user-details {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 7px;
}

.user-email,
.user-class {
  width: auto;
  padding: 8px 10px;
  font-size: 0.82rem;
}

.user-email { min-width: 190px; }
.user-class { min-width: 90px; max-width: 140px; }

.field-optional {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 400;
}

/* ---------- footer, now with a sign-out form in it ---------- */

.app-footer-links {
  display: flex;
  align-items: center;
  gap: 14px;
}

.logout-form { margin: 0; }

.logout-btn {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.logout-btn:hover { color: var(--text); }

/* -- keyboard-only skip label -- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   Small screens: tighten up
   ============================================================ */

@media (max-width: 420px) {
  .app-main { padding: 22px 16px 56px; }
  .scan-intro h1 { font-size: 1.4rem; }
  .tag-card { padding: 8px 16px 20px; }
  .ledger-side { margin-left: 0; width: 100%; justify-content: space-between; }
  .auth-card { padding: 24px 18px; }
  .user-row { flex-direction: column; align-items: stretch; }
  .attendance-score { align-items: flex-start; }
  .user-actions { justify-content: flex-start; }
  .app-footer { flex-direction: column; gap: 10px; text-align: center; }
}

/* ============================================================
   Case page (case.php) -- renting out a whole case at once
   ============================================================ */

.case-page { max-width: 640px; margin: 0 auto; }

.scan-intro-sub {
  margin-top: 10px;
  color: var(--muted);
  font-size: 0.92rem;
  max-width: 34ch;
  margin-left: auto;
  margin-right: auto;
}

.case-head { text-align: center; margin-bottom: 24px; }

.case-head h1 {
  font-size: 1.6rem;
  margin: 6px 0 0;
}

.case-breadcrumb {
  margin-top: 6px;
  color: var(--tag-muted);
  font-size: 0.85rem;
}

/* The tall breadcrumb line sits on the dark chrome, not the tag card. */
.case-head .case-breadcrumb { color: var(--muted); }

.case-counts {
  display: flex;
  justify-content: center;
  gap: 18px;
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.case-counts strong { color: var(--text); font-size: 0.95rem; }
.case-count-out strong { color: var(--rust-light); }

.case-section {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 28px 0 10px;
}

/* ---------- the list of things ---------- */

.case-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--tag-line);
  border-radius: var(--radius-m);
  overflow: hidden;
  background: var(--panel);
}

.case-row {
  display: flex;
  align-items: center;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  padding: 4px 6px 4px 0;
}

.case-row:last-child { border-bottom: none; }

/* Whole row is the tick target -- a 14px checkbox on a phone is miserable. */
.case-tick {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
  min-width: 0;
  padding: 10px 4px 10px 12px;
  cursor: pointer;
}

.case-tick:hover { background: var(--panel-2); }

.case-tick-box {
  flex: none;
  width: 22px;
  height: 22px;
  accent-color: var(--amber);
  cursor: pointer;
}

.case-tick-body {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  flex: 1;
}

.case-row-thumb {
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-s);
  background: var(--bg);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.case-row-thumb img { width: 100%; height: 100%; object-fit: cover; }

.case-row-text,
.case-row-body { min-width: 0; flex: 1; }

.case-row-name {
  display: block;
  font-size: 0.98rem;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
}

.case-row-meta {
  display: block;
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 2px;
}

.case-row-note {
  color: var(--tag-muted);
  font-size: 0.85rem;
  margin-top: 4px;
}

.case-row-link {
  flex: none;
  color: var(--muted);
  text-decoration: none;
  padding: 8px 10px;
  border-radius: var(--radius-s);
}

.case-row-link:hover { color: var(--amber); background: var(--panel-2); }

/* ---------- quantities ----------
   Only rendered for items HomeBox says there is more than one of, so
   everything else keeps the plain tick box it had before. */

.case-qty {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0 2px;
}

.case-qty-box {
  width: 58px;
  padding: 7px 6px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  text-align: center;
  -moz-appearance: textfield;
  appearance: textfield;
}

/* The spinner is noise next to a tick box and eats the width the number
   needs on a phone. */
.case-qty-box::-webkit-outer-spin-button,
.case-qty-box::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.case-qty-box:focus {
  outline: none;
  border-color: var(--amber);
}

.case-qty-label {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.case-qty-meta { color: var(--moss-light); }

/* Partial return, on a row that is still out. Deliberately narrow and
   dim: it is the less common action, and "Take back all" sits above the
   whole list for the common one. */
.case-return-part {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}

.case-return-part .case-qty-box { width: 52px; }

/* ---------- what's out ---------- */

.case-list-out .case-row { padding: 10px 12px; }
.case-list-out .case-row-out { border-left: 3px solid var(--rust); }
.case-row-out .case-row-meta { color: var(--tag-muted); }

/* ---------- quantity as part of a name ----------
   "Kreissaege x7" reads better than a name with the number buried at the
   end, and it makes the xN greppable in the browser's find. */

.ledger-qty {
  margin-left: 6px;
  font-family: var(--font-mono);
  font-size: 0.78em;
  font-weight: 400;
  color: var(--amber);
  white-space: nowrap;
}

.gear-qty-field { margin-top: 10px; }

.gear-qty-box {
  width: 72px;
  padding: 7px 8px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  -moz-appearance: textfield;
  appearance: textfield;
}

.gear-qty-box::-webkit-outer-spin-button,
.gear-qty-box::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.gear-qty-box:focus { outline: none; border-color: var(--amber); }

.badge-overdue {
  display: inline-block;
  background: var(--rust);
  color: var(--rust-ink);
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 3px;
  margin-left: 4px;
}

/* ---------- the form ---------- */

.case-actions {
  margin-top: 20px;
  background: var(--tag);
  color: var(--tag-ink);
  border-radius: var(--radius-l);
  padding: 18px 16px 20px;
}

.case-actions .field { margin-bottom: 14px; }

.case-actions label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tag-muted);
  margin-bottom: 7px;
}

.case-actions input[type="text"],
.case-actions input[type="date"] {
  width: 100%;
  background: #1A1A1A;
  border: 1px solid var(--tag-line);
  border-radius: var(--radius-s);
  padding: 12px 13px;
  font-size: 1rem;
  font-family: inherit;
  color: var(--tag-ink);
}

.case-actions input:focus { outline: 2px solid var(--rust); outline-offset: 1px; }

.case-actions .more-options summary {
  color: var(--tag-muted);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  cursor: pointer;
  margin-bottom: 4px;
}

.case-hint {
  margin-top: 12px;
  font-size: 0.82rem;
  color: var(--tag-muted);
  text-align: center;
}

.case-return-all { margin-bottom: 4px; }
.case-return-all .case-hint { color: var(--muted); }

.case-foot {
  margin-top: 28px;
  text-align: center;
}

@media (max-width: 420px) {
  .case-head h1 { font-size: 1.35rem; }
  .case-counts { gap: 12px; font-size: 0.64rem; }
  .case-tick { padding-left: 9px; }
}

/* Marks items that were rented in one go from a case, so a run of identical
   rows reads as "one action" rather than "twenty separate loans". */
.ledger-group-note {
  display: inline-block;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 1px 6px;
  margin-bottom: 5px;
}

/* ============================================================
   Shared catalogue (catalogue.php)

   The partner-school view. It gets its own grid rather than reusing
   .ledger-row because the unit here is a *thing you might want*, not a
   transaction that's already happened -- so each card carries a little
   state ("Available" / "Out on loan") and, for partners, an ask button.
   ============================================================ */

.catalogue-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}

.catalogue-header h1 {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  letter-spacing: -0.01em;
  margin-top: 2px;
}

.catalogue-lead {
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
  max-width: 52ch;
  margin-top: 8px;
}

.catalogue-search {
  display: flex;
  align-items: center;
  gap: 12px;
}

.catalogue-search input { min-width: 220px; }

.catalogue-count {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 14px;
}

/* --- the grid of gear cards --- */

.catalogue-grid {
  list-style: none;
  margin: 0 0 26px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
}

.gear-card {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  overflow: hidden;
}

/* Dimmed rather than hidden: a partner still needs to see that the
   thing exists, so they can plan around waiting for it. */
.gear-card.is-out { opacity: 0.68; }
/* Lighter touch than .is-out: this one is still there, it just has a queue
   in front of it, so dimming it as hard as a missing item would lie. */
.gear-card.is-contested { opacity: 0.82; }

.gear-thumb {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--panel-2);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

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

.gear-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 13px 14px 14px;
  flex: 1;
}

.gear-name {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.3;
}

.gear-location {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tag-muted);
}

.gear-description {
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--muted);
  /* Descriptions can be a whole paragraph in HomeBox. The card must not
     grow to match, or one chatty item breaks the grid's rhythm. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.gear-state {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: auto;
  padding-top: 10px;
}

.gear-state-ok { color: var(--moss-light); }
.gear-state-out { color: var(--tag-muted); }
/* Contested: free to collect, but somebody is already ahead of you. Amber
   rather than grey, because the crew can still say yes -- it is not gone. */
.gear-state-busy { color: var(--amber); }

.gear-waiting {
  font-size: 0.74rem;
  line-height: 1.4;
  color: var(--muted);
  margin: 0;
}

/* --- ask / withdraw, pinned to the bottom of every card --- */

.gear-action { margin-top: 10px; }
.gear-action form { margin: 0; }
.gear-action .field { margin-bottom: 10px; }
.gear-action .field label { font-size: 0.7rem; }

.gear-request-form .more-options { margin-bottom: 10px; }
.gear-request-form .more-options summary { font-size: 0.74rem; }

.gear-asked {
  font-size: 0.78rem;
  color: var(--muted);
  margin-bottom: 8px;
}

/* --- the partner's own request list under the grid --- */

/* --- whole cases (catalogue.php) ------------------------------------------

   A "case" is a HomeBox location that holds shareable gear. The block groups
   the very same items the grid above lists, so a partner can ask for a box in
   one action instead of one per thing inside it. */

.catalogue-cases { margin-top: 30px; }

.catalogue-cases h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 8px;
}

.cases-head .catalogue-lead { margin-bottom: 14px; }

.cases-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}

.case-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.case-name { font-size: 1rem; margin: 0 0 4px; }

.case-meta {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 0;
}

.case-contents-box { font-size: 0.78rem; }

.case-contents-box > summary {
  cursor: pointer;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.case-contents {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  font-size: 0.8rem;
}

.case-contents li { color: var(--text); }

.case-tag {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 6px;
  margin-left: 5px;
  color: var(--muted);
}

.case-tag-out {
  color: var(--rust-light);
  border-color: rgba(229, 57, 53, 0.45);
}

.case-tag-asked {
  color: var(--amber);
  border-color: rgba(229, 57, 53, 0.4);
}

.case-request-form { margin-top: auto; }

/* The partner's own list shows a whole-case ask as one row, so the contents
   sit inline instead of taking a line each. */
.mine-row-case {
  align-items: flex-start;
}

.mine-row-case .ledger-item-name {
  display: flex;
  align-items: center;
  gap: 8px;
}

.catalogue-mine { margin-top: 28px; }

.catalogue-mine h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 12px;
}

.mine-list { list-style: none; margin: 0; padding: 0; }

.mine-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 11px 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  margin-bottom: 9px;
}

.mine-row .ledger-thumb {
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border-radius: var(--radius-s);
}

.mine-main { flex: 1; min-width: 0; }

.mine-side {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.mine-side form { margin: 0; }

/* --- status pills --- */

.status-badge {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  white-space: nowrap;
}

.status-badge.status-pending {
  background: rgba(229, 57, 53, 0.16);
  border-color: rgba(229, 57, 53, 0.4);
  color: var(--amber);
}

.status-badge.status-granted {
  background: rgba(110, 139, 93, 0.16);
  border-color: rgba(163, 194, 142, 0.4);
  color: var(--moss-light);
}

/* ============================================================
   Requests (requests.php)

   Reuses .ledger-row for consistency with the overview, but the action
   column carries two stacked forms instead of one button -- the crew is
   answering a question here, not clicking "next".
   ============================================================ */

.requests-page .overview-header { margin-bottom: 18px; }

.requests-page .ledger-row {
  align-items: flex-start;
  flex-wrap: wrap;
}

.request-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 260px;
  flex: 0 0 260px;
  align-items: stretch;
}

.request-actions form { margin: 0; }
.request-actions .field { margin-bottom: 8px; }
.request-actions .field label { font-size: 0.7rem; }

.request-grant-form {
  padding: 11px 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
}

.request-decline-form {
  padding: 9px 12px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-s);
}

/* A whole-case request on the crew's page. The list of what is inside sits
   under the row's text; the two forms are the same pair as every other row,
   just wrapped around the group instead of one item. */
.ledger-row-case .case-contents { margin-top: 9px; }

.case-tag-case {
  color: var(--amber);
  border-color: rgba(229, 57, 53, 0.45);
}

.case-asset {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-left: 6px;
  white-space: nowrap;
}

/* "3 other schools are waiting for this one." Only rendered when true, and
   amber rather than a button colour -- it is a warning about the queue,
   not the primary action on the row. */
.request-rest-hint {
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--amber);
  margin: 0 0 8px;
}

.field-row {
  display: flex;
  gap: 10px;
}

.field-row .field { flex: 1; min-width: 0; }

/* --- unread counter on the Requests tab --- */

.nav-badge {
  display: inline-block;
  min-width: 17px;
  padding: 0 5px;
  margin-left: 5px;
  border-radius: 999px;
  background: var(--amber);
  color: var(--amber-ink);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  vertical-align: 1px;
}

/* --- narrow screens --- */

@media (max-width: 720px) {
  .catalogue-header { align-items: flex-start; }
  .catalogue-search { width: 100%; }
  .catalogue-search input { min-width: 0; flex: 1; }
  .catalogue-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
  .mine-row { flex-wrap: wrap; }
  .mine-side { width: 100%; justify-content: space-between; margin-left: 0; }
  .request-actions { width: 100%; flex: 1 1 100%; margin-left: 0; }
  .field-row { flex-direction: column; gap: 0; }
}

/* ============================================================
   Step 4 -- when the crew are busy
   ============================================================ */

.calendar-page .overview-header { margin-bottom: 4px; }

.code-block {
  display: block;
  margin: 10px 0 14px;
  padding: 12px 14px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1.6;
  color: var(--text);
  overflow-x: auto;
  white-space: pre;
}

/* The subscribe link. It is long and has to be copyable, so it wraps
   rather than being truncated -- a half-copied calendar URL silently
   fails to subscribe. */
.copy-target {
  display: inline-block;
  padding: 8px 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--amber);
  word-break: break-all;
  user-select: all;
}

.calendar-page .ledger .panel-card { margin-bottom: 4px; }

/* --- the partner API panel, last thing on the catalogue page --- */

.catalogue-api { margin-top: 28px; }

.catalogue-api h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.catalogue-api-lead {
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
  max-width: 56ch;
  margin-bottom: 14px;
}

/* Shown the moment a key is minted and never again, so it is the one thing
   on the page that has to be read before anything else is done with it. */
.api-key-alert {
  font-weight: 700;
  color: var(--amber);
  margin-bottom: 8px;
}

.api-key-value { letter-spacing: 0.04em; }

.api-key-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 4px;
}

.api-key-actions form { margin: 0; }

.api-examples-body { margin-top: 10px; }

.api-example-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 16px 0 6px;
}

/* Wrapped rather than scrolled: a command that runs off the side of the box
   is a command nobody can check before running it. Copying still gives the
   line as written -- the wrap is visual only. */
.api-example {
  margin: 0;
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1.6;
  color: var(--text);
}

.api-examples-body code {
  font-family: var(--font-mono);
  color: var(--amber);
}

/* The partner-facing busy panel on the catalogue. Deliberately quieter
   than the gear grid: it is advice, not something to act on. */
.catalogue-busy { margin-top: 28px; }

.catalogue-busy h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.catalogue-busy-lead {
  color: var(--muted);
  font-size: 0.88rem;
  line-height: 1.6;
  max-width: 46ch;
  margin-bottom: 12px;
}

.busy-list { list-style: none; margin: 0 0 12px; padding: 0; }

.busy-row {
  padding: 9px 13px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 2px solid var(--rust);
  border-radius: var(--radius-s);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text);
  margin-bottom: 6px;
}

/* A busy row is now two things: when, and what. The time is the part a
   partner acts on, so it keeps a fixed width and stays in the accent
   colour; the title takes whatever is left and wraps, because these are
   German event names and roughly half of them are longer than a phone is
   wide. Reversing that order -- title first, time second -- is what the
   fixed label used to look like, and it is why nobody could scan it. */
.busy-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 10px;
}

.busy-when {
  flex: 0 0 auto;
  color: var(--rust-light);
  white-space: nowrap;
}

.busy-what {
  flex: 1 1 12ch;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The key-rotation form is a single button under a paragraph of warning, so
   it needs no box of its own -- just a little air so it does not read as part
   of the paragraph above it. */
.rotate-key-form {
  margin: 14px 0 8px;
}

/* The copy button app.js adds next to every .copy-target.
   It is deliberately quiet next to the address it belongs to -- the address is
   the thing to look at, the button is the thing to press once -- and it is
   inline rather than below, because a button on its own line under a wrapped
   100-character address reads as belonging to the paragraph under it. */
.copy-btn {
  display: inline-block;
  margin-left: 8px;
  padding: 3px 9px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  line-height: 1.4;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  cursor: pointer;
  white-space: nowrap;
  vertical-align: baseline;
  transition: color 120ms ease, border-color 120ms ease;
}

.copy-btn:hover,
.copy-btn:focus-visible {
  color: var(--text);
  border-color: var(--muted);
}

/* The one piece of feedback the button gives, so it needs to be unmissable
   without being alarming: green rather than a colour change of the address
   itself, which would suggest the address had been altered. */
.copy-btn-done {
  color: #4CAF50;
  border-color: #4CAF50;
}

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

@media (max-width: 560px) {
  .copy-target { font-size: 0.72rem; }
  .busy-row { font-size: 0.74rem; }
}

/* ============================================================
   Events
   ============================================================ */

.event-section { margin-bottom: 30px; }

.event-section h2 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 12px;
}

.event-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.event-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 14px 16px;
}

/* A cancelled or past event stays readable but stops shouting. */
.event-row-off { opacity: 0.6; }
.event-row-off .event-row-name a { color: var(--muted); }

.event-row-main { flex: 1 1 260px; min-width: 0; }

.event-row-name {
  font-size: 1.05rem;
  font-weight: 600;
  margin-bottom: 4px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.event-row-name a { color: var(--text); text-decoration: none; }
.event-row-name a:hover { color: var(--amber); }

.event-row-meta {
  color: var(--muted);
  font-size: 0.82rem;
  font-family: var(--font-mono);
}

.event-row-side {
  flex: 0 0 auto;
  text-align: right;
  min-width: 108px;
}

.event-row-crew {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--muted);
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 5px;
}

/* The number of people actually down. Big enough to read across a room,
   which is the only time anybody looks at this page -- in the corridor,
   the night before. */
.event-count {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1;
}

.event-count-label { font-size: 0.76rem; }

/* Amber, not rust: not enough people is a thing to sort out today, not a
   thing that went wrong. Rust is reserved for gear that is out late. */
.event-short {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--amber);
  margin-top: 3px;
}

.event-row-actions { flex: 0 0 auto; display: flex; gap: 7px; }

/* --- the three headcount boxes on the form --- */

.event-needs {
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 16px 16px 4px;
  margin: 0 0 16px;
}

.event-needs legend {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 6px;
}

.event-needs > .field-hint { margin: 0 0 14px; }

.event-need { margin-bottom: 14px; }

.event-need label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 7px;
}

/* A wide box would put a two-digit number in the middle of a metre of
   empty space, which makes it harder to read, not easier. */
.event-need input[type="number"] { max-width: 120px; }

.event-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  align-items: center;
}

/* --- one event: the roster --- */

.event-page h1 {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  margin-bottom: 4px;
}

.event-when {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 14px;
}

.event-description {
  background: var(--panel);
  border-left: 2px solid var(--amber);
  border-radius: var(--radius-s);
  padding: 12px 15px;
  margin-bottom: 20px;
  line-height: 1.6;
  color: var(--text);
}

/* Not an error box: being short of people is the normal state of an event
   two weeks out, not something that went wrong. Caution and error now share
   a hue, so they are told apart by weight instead -- this is a pale wash
   carrying plain white text, where an error box is a stronger wash carrying
   --rust-light text. */
.event-warning {
  background: rgba(229, 57, 53, 0.10);
  border: 1px solid rgba(229, 57, 53, 0.32);
  color: var(--text);
  border-radius: var(--radius-s);
  padding: 11px 14px;
  font-size: 0.88rem;
  line-height: 1.55;
  margin-bottom: 22px;
}

.event-phase {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 15px 16px;
  margin-bottom: 12px;
}

.event-phase-head {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 4px;
}

.event-phase-head h3 {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.event-phase-count { font-family: var(--font-mono); font-size: 0.76rem; }

/* Enough people. Green is the one colour in this app that means "you can
   stop worrying about this". */
.event-enough { color: var(--moss); }
.event-over { color: var(--moss); }
.event-phase-none { color: var(--muted); }

.event-people {
  list-style: none;
  margin: 10px 0 12px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.event-person {
  display: flex;
  align-items: center;
  gap: 7px;
  background: var(--panel-2);
  border-radius: 999px;
  padding: 5px 11px;
  font-size: 0.82rem;
}

.event-person-name { color: var(--text); }

.event-person-class {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--muted);
}

.event-person-ago {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
}

.event-signup-form { margin-top: 4px; }

/* --- one event: the tasks --- */

.event-page .panel-card h3 {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 10px;
}

.task-list { list-style: none; margin: 10px 0 0; padding: 0; }

.task-row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}

.task-row:first-child { border-top: none; }

.task-tick-form { flex: 0 0 auto; margin-top: 1px; }

/* A real button, not a styled span, so it works without JavaScript and can
   be reached by keyboard. The tick character sits in the ::after so the
   button itself is empty and screen readers read the aria-label instead. */
.task-tick {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-s);
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--amber);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  cursor: pointer;
  padding: 0;
  line-height: 1;
}

.task-tick:hover { border-color: var(--amber); }
.task-row-done .task-tick { background: var(--amber); color: var(--amber-ink); border-color: var(--amber); }

.task-body { flex: 1 1 auto; min-width: 0; }

.task-title { font-size: 0.95rem; color: var(--text); }

/* Struck through and dimmed rather than hidden: somebody still needs to know
   the job existed and got done. */
.task-row-done .task-title {
  text-decoration: line-through;
  color: var(--muted);
}

.task-detail {
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.55;
  margin-top: 3px;
}

.task-owner {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--muted);
  margin-top: 5px;
  display: flex;
  align-items: center;
  gap: 7px;
}

/* --- one event: the items --- */

.event-items { list-style: none; margin: 0 0 16px; padding: 0; }

.event-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  padding: 12px 15px;
  margin-bottom: 9px;
}

.event-item-main { flex: 1 1 220px; min-width: 0; }
.event-item-name { font-size: 0.98rem; color: var(--text); margin-bottom: 3px; }
.event-item-meta {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* Rust, because this really is a shortage -- gear the event needs that is
   not on the shelf. Same colour as an overdue item, on purpose. */
.event-item-short { color: var(--rust); }
.event-item-ok { color: var(--moss); }

.event-item-side { flex: 0 0 auto; display: flex; align-items: center; gap: 7px; }
.event-item-qty-form { display: flex; align-items: center; gap: 7px; }
.event-item-qty-form input[type="number"] {
  width: 74px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  padding: 8px 9px;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--text);
}

.event-item-qty-form input:focus { border-color: var(--amber); outline: none; }

/* The heading between the search box and its two result groups. */
.event-search-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 18px 0 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
}

@media (max-width: 560px) {
  .event-row-side { text-align: left; min-width: 0; }
  .event-row-crew { justify-content: flex-start; }
  .event-phase-head { gap: 4px; }
}

/* The answer buttons on the events list. They sit in the row's main column,
   under the date, so the row reads: what and when, then what you can do about
   it. Wrapping rather than a fixed width, because a long phase name plus a
   phone screen is the normal case, not the exception. */
.event-row-signup {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.event-row-signup-lead {
  width: 100%;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
}

/* Forms are block-level by default, which would put each button on its own
   line and make a three-phase event three rows tall. */
.event-row-signup-form { display: inline-block; margin: 0; }

.event-row-mine {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
}

.event-row-mine a {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  margin-left: 4px;
}

.event-row-mine a:hover { color: var(--text); }

/* ============================================================
   Email settings
   ============================================================ */

/* Two buttons for one form (save, and save-and-test) have to sit on one line
   on a phone too, so this is a flex row that wraps rather than a block. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* 200px is chosen so the two sit side by side on a desktop and stack on a
   phone: at 140px they fitted a narrow screen by squeezing until the label
   wrapped to three lines, which is worse than being one above the other. */
.btn-row .btn { flex: 1 1 200px; }

/* The honest answer to "is mail even configured", shown before anything is
   typed rather than only after a send has failed. */
.mail-status-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  font-size: 0.9rem;
  line-height: 1.6;
  margin: 0 0 12px;
}

.mail-status-tag {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 3px 10px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
}

/* SMTP is a socket LendBox opened and can therefore be told about when it
   stops working; mail() is a hand-off to the host that cannot. The tag says
   which one is in use, so nobody has to guess why a failure is silent. */
.mail-status-tag-smtp {
  background: rgba(110, 139, 93, 0.18);
  border-color: rgba(163, 194, 142, 0.42);
  color: var(--moss-light);
}

.mail-page code {
  background: var(--panel-2);
  border-radius: 4px;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: 0.85em;
}

/* ------------------------------------------- the school calendar, copied in */

/* A row the school has taken off its calendar. Kept, because people had said
   they would come, and dimmed rather than struck through: it is history, not
   a mistake. */
.event-row-gone { opacity: 0.72; }
.event-row-gone .event-row-name a { color: var(--muted); }

.pill-gone {
  background: var(--muted);
  color: var(--bg);
}

.event-row-warn {
  margin: 5px 0 0;
  font-size: 0.8rem;
  color: var(--amber);
  background: var(--panel-2);
  border-left: 3px solid var(--amber);
  padding: 5px 9px;
  border-radius: 0 4px 4px 0;
}

/* The note the school wrote in the calendar, shown apart from the date so it
   reads as the school's line and not as this app's. */
.event-row-note,
.event-when-note { font-style: italic; }

/* What the read did, said once, plainly. */
.event-sync {
  margin: 0 0 10px;
  font-size: 0.85rem;
}

.event-sync-form {
  margin: 0 0 12px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.event-sync-form .field-hint {
  margin: 0;
  flex: 1 1 220px;
}

.event-from-feed {
  margin: 0 0 10px;
  font-size: 0.82rem;
  color: var(--muted);
  border-left: 3px solid var(--line);
  padding-left: 9px;
}

/* A box the school calendar owns. Not `disabled` -- a disabled input is not
   submitted at all, and these still have to reach the form so saving an event
   does not quietly blank them. Read-only says "you cannot change this", and
   the panel-hint above says who can. */
.field-from-feed {
  background: var(--panel-2);
  color: var(--muted);
  cursor: default;
}
