/* =====================================================================
   Tattoo Lab — one shared stylesheet.
   System fonts, no external assets. Mobile/tablet-first. Light & clean.
   Reusable patterns: cards, buttons, forms, badges, tables, modal,
   slide-out drawer, toasts, app shell (sidebar + topbar).
   ===================================================================== */

:root {
  --bg: #f4f5f7;
  --surface: #ffffff;
  --surface-2: #f7f8fa;
  --border: #e4e7ec;
  --border-strong: #d0d5dd;
  --text: #16191f;
  --muted: #667085;
  --muted-2: #98a2b3;

  --primary: #4f46e5;
  --primary-hover: #4338ca;
  --primary-soft: #eef2ff;
  --on-primary: #ffffff;

  --ok: #067647;      --ok-bg: #ecfdf3;   --ok-border:#abefc6;
  --warn: #b54708;    --warn-bg: #fffaeb; --warn-border:#fedf89;
  --danger: #b42318;  --danger-bg: #fef3f2; --danger-border:#fecdca;
  --info: #175cd3;    --info-bg: #eff8ff; --info-border:#b2ddff;

  --radius: 12px;
  --radius-sm: 9px;
  --radius-lg: 18px;
  --shadow-sm: 0 1px 2px rgba(16,24,40,.06), 0 1px 3px rgba(16,24,40,.05);
  --shadow-md: 0 4px 12px rgba(16,24,40,.08);
  --shadow-lg: 0 12px 40px rgba(16,24,40,.16);

  --sidebar-w: 244px;
  --topbar-h: 58px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
          "Apple Color Emoji", "Segoe UI Emoji", sans-serif;

  --space: 16px;
}

/* ------------------------------ reset ------------------------------ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
h1,h2,h3,h4,p { margin: 0; }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; display: block; }
button { font-family: inherit; }
input, select, textarea, button { font-size: 16px; } /* 16px avoids iOS zoom */

/* ------------------------------ typography ------------------------------ */
.h1 { font-size: 24px; font-weight: 700; letter-spacing: -.02em; }
.h2 { font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
.h3 { font-size: 16px; font-weight: 650; }
.h4 { font-size: 14.5px; font-weight: 650; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.tiny { font-size: 12px; }
.strong { font-weight: 650; }
.mono { font-variant-numeric: tabular-nums; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }

/* ------------------------------ app shell ------------------------------ */
.app { min-height: 100vh; }

.sidebar {
  position: fixed; inset: 0 auto 0 0;
  width: var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  z-index: 40;
  transition: transform .22s ease;
}
.sidebar .brand {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 18px; height: var(--topbar-h);
  border-bottom: 1px solid var(--border);
  font-weight: 750; letter-spacing: -.01em;
}
.brand-mark {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  background: linear-gradient(135deg, var(--primary), #7c6cf0);
  color: #fff; display: grid; place-items: center;
  font-weight: 800; font-size: 15px;
}
.brand-logo {
  width: 30px; height: 30px; border-radius: 8px; flex: none; overflow: hidden;
  display: grid; place-items: center; background: var(--surface-2);
}
.brand-logo img { width: 100%; height: 100%; object-fit: contain; }
.public-brand .brand-logo, .public-brand .brand-mark { width: 34px; height: 34px; }
/* logo shown in the top bar on small screens (sidebar is hidden there) */
.topbar-brand { display: none; align-items: center; }
@media (max-width: 900px) { .topbar-brand { display: inline-flex; } }
.nav { padding: 10px; overflow-y: auto; flex: 1; }
.nav-group-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted-2); font-weight: 700;
  padding: 14px 10px 6px;
}
.nav a, .nav-foot a {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px; border-radius: var(--radius-sm);
  color: var(--text); font-weight: 550; font-size: 14.5px;
  margin-bottom: 2px;
}
.nav a:hover, .nav-foot a:hover { background: var(--surface-2); text-decoration: none; }
.nav a.active, .nav-foot a.active { background: var(--primary-soft); color: var(--primary-hover); }
.nav a .ic, .nav-foot a .ic { flex: none; }
.nav-foot { padding: 10px; border-top: 1px solid var(--border); }

.main { margin-left: var(--sidebar-w); min-height: 100vh; display:flex; flex-direction:column; transition: margin-left .22s ease; }
.topbar {
  position: sticky; top: 0; z-index: 30;
  height: var(--topbar-h);
  background: rgba(255,255,255,.82);
  backdrop-filter: saturate(1.4) blur(8px);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px;
  padding: 0 18px;
}
.topbar .page-title { font-weight: 700; font-size: 16px; }
.topbar .spacer { flex: 1; }
/* The menu button is available on every size now: on desktop it collapses the
   fixed sidebar; on phones/tablets it opens the off-canvas drawer. */
.hamburger { display: inline-flex; }

.content { padding: 22px; width: 100%; max-width: 1180px; margin: 0 auto; flex:1; }
.page-head { display:flex; align-items:flex-start; gap:14px; flex-wrap:wrap; margin-bottom: 18px; }
.page-head .grow { flex: 1; min-width: 200px; }
.page-head p.muted { margin-top: 3px; }

/* user chip */
.userchip { display:flex; align-items:center; gap:9px; }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; flex:none;
  background: var(--primary-soft); color: var(--primary-hover);
  display:grid; place-items:center; font-weight:700; font-size:13px;
  overflow:hidden; object-fit:cover;
}
.avatar.lg { width:56px; height:56px; font-size:20px; }
.avatar img { width:100%; height:100%; object-fit:cover; }

/* ------------------------------ cards ------------------------------ */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.card-pad { padding: 18px; }
.card-head {
  padding: 15px 18px; border-bottom: 1px solid var(--border);
  display:flex; align-items:center; gap:10px;
}
.card-head .grow { flex:1; }
.card-body { padding: 18px; }
.card-body.tight { padding: 12px 18px; }

.grid { display: grid; gap: var(--space); }
.grid-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.stack { display:flex; flex-direction:column; gap: var(--space); }
.row { display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
.row.end { justify-content:flex-end; }
.row.between { justify-content:space-between; }

/* stat tiles */
.stat { padding: 16px 18px; }
.stat .label { color: var(--muted); font-size: 13px; font-weight: 600; }
.stat .value { font-size: 26px; font-weight: 750; letter-spacing:-.02em; margin-top:4px; font-variant-numeric: tabular-nums; }
.stat .delta { font-size: 12.5px; font-weight:650; margin-top:4px; }
.delta.up { color: var(--ok); }
.delta.down { color: var(--danger); }
/* An expandable tile is a <button> so it is reachable by keyboard and can
   announce its state. Buttons bring their own UA styling, so reset it back to
   the card the rest of the grid expects. */
button.card.stat {
  display: block; width: 100%; text-align: left; font: inherit; color: inherit;
  background: var(--surface); border: 1px solid var(--border);
}
.stat.clickable { cursor: pointer; transition: box-shadow .15s, transform .1s; }
.stat.clickable[aria-expanded="true"] { border-color: var(--primary); background: var(--primary-soft); }
.stat.clickable:hover { box-shadow: var(--shadow-md); }
.stat.clickable:active { transform: translateY(1px); }

/* ------------------------------ buttons ------------------------------ */
/* The hidden attribute must win over component display rules (.btn is
   inline-flex, .note is flex — both would otherwise override it). */
[hidden] { display: none !important; }

.btn {
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  padding: 9px 15px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); font-weight: 600; font-size: 14.5px;
  cursor: pointer; text-decoration: none; line-height: 1.2;
  transition: background .12s, border-color .12s, box-shadow .12s, opacity .12s;
  min-height: 40px;
}
.btn:hover { background: var(--surface-2); text-decoration:none; }
.btn:disabled, .btn.is-loading { opacity: .7; pointer-events: none; }
/* In-button loading spinner (injected as a .btn-spin child by app.js). */
.btn-spin {
  width: 15px; height: 15px; flex: 0 0 auto; display: inline-block;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  animation: btn-spin .6s linear infinite; vertical-align: middle;
}
.btn-sm .btn-spin { width: 13px; height: 13px; }
.btn-lg .btn-spin { width: 17px; height: 17px; }
@keyframes btn-spin { to { transform: rotate(360deg); } }
.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.btn-danger { background: var(--danger); border-color: var(--danger); color:#fff; }
.btn-danger:hover { background:#912018; border-color:#912018; }
.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover { background: var(--surface-2); }
.btn-sm { padding: 6px 11px; min-height: 34px; font-size: 13.5px; }
.btn-lg { padding: 13px 20px; min-height: 50px; font-size: 16px; width:auto; }
.btn-block { width: 100%; }
.btn-icon { padding: 8px; min-width: 40px; }

/* inline info note / callout */
.note {
  display:flex; align-items:flex-start; gap:9px;
  padding: 11px 13px; border-radius: var(--radius-sm);
  background: var(--primary-soft); border: 1px solid var(--border);
  color: var(--text); font-size: 13.5px; line-height: 1.45;
}
.note .ic { flex: none; color: var(--primary-hover); margin-top: 1px; }
.note-warn { background: var(--warn-bg); border-color: var(--warn-border); }
.note-warn .ic { color: var(--warn); }
.note a { color: var(--primary-hover); font-weight: 600; }

/* small count chip, e.g. on a tab that has items waiting */
.pill {
  display:inline-block; min-width: 18px; padding: 0 6px; margin-left: 5px;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border);
  color: var(--muted); font-size: 11.5px; font-weight: 700; line-height: 17px; text-align:center;
}
.pill-warn { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn); }
.seg a.active .pill { background: var(--surface-2); }

/* a section being replaced in place — dim it so the swap is visible */
[data-live-root].is-loading { opacity: .55; transition: opacity .12s ease; }

/* ------------------------------ forms ------------------------------ */
.field { margin-bottom: 15px; }
.field:last-child { margin-bottom: 0; }
/* .lbl is used on a <label> when there is a single control to point at, and on
   a <div> when it heads a GROUP — a radio set, a signature pad, a confirmation
   block. A <label> with no `for` and no control inside it is a label for
   nothing, which is worse for a screen reader than plain text. */
.lbl { display:block; font-weight: 600; font-size: 13.5px; margin-bottom: 6px; }
.lbl .req { color: var(--danger); }
.hint { color: var(--muted); font-size: 12.5px; margin-top: 5px; }
.input, .select, .textarea {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  transition: border-color .12s, box-shadow .12s;
}
.input:focus, .select:focus, .textarea:focus, .btn:focus-visible {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(79,70,229,.15);
}
.textarea { min-height: 92px; resize: vertical; line-height:1.45; }
.select {
  appearance:none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Cpath d='M6 8l4 4 4-4' stroke='%23667085' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 10px center; padding-right: 36px;
}
.field.invalid .input, .field.invalid .select, .field.invalid .textarea { border-color: var(--danger); }
.field.invalid .err { color: var(--danger); font-size: 12.5px; margin-top: 5px; display:block; }
.err { display:none; }

/* number stepper — custom −/+ buttons around a number input (works on touch,
   where native number spinners don't render) */
.stepper { display:flex; align-items:stretch; }
.stepper .input { flex:1 1 auto; min-width:0; text-align:center; border-radius:0; }
.stepper-btn {
  flex:0 0 auto; width:44px; min-height:40px; display:grid; place-items:center;
  border:1px solid var(--border-strong); background:var(--surface); color:var(--text);
  font-size:20px; font-weight:600; line-height:1; cursor:pointer;
  user-select:none; -webkit-user-select:none;
}
.stepper-btn:hover { background:var(--surface-2); }
.stepper-btn:active { background:var(--primary-soft); }
.stepper [data-step-down] { border-radius: var(--radius-sm) 0 0 var(--radius-sm); border-right:none; }
.stepper [data-step-up]   { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; border-left:none; }
.stepper input[type=number] { -moz-appearance:textfield; }
.stepper input[type=number]::-webkit-outer-spin-button,
.stepper input[type=number]::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }

.form-grid { display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }
.form-grid .full { grid-column: 1 / -1; }
fieldset.group { border:1px solid var(--border); border-radius: var(--radius); padding: 16px; margin:0 0 16px; }
fieldset.group > legend { font-weight: 700; font-size: 13px; padding: 0 6px; color: var(--muted); text-transform:uppercase; letter-spacing:.04em; }
/* A whole section can be the thing that is wrong — the payment rows have to
   balance against each other, not one field against a rule. Declared AFTER the
   base rule above, whose `border` shorthand would otherwise reset the colour. */
fieldset.group.invalid { border-color: var(--danger); background: var(--danger-bg); }

/* toggles / choices */
.check {
  display:flex; gap:11px; align-items:flex-start;
  padding: 12px 14px; border:1px solid var(--border-strong);
  border-radius: var(--radius-sm); cursor:pointer; background:var(--surface);
}
.check:hover { background: var(--surface-2); }
.check input { margin-top: 2px; width:18px; height:18px; flex:none; accent-color: var(--primary); }
.check .ct { font-size: 14px; }

.seg { display:inline-flex; background: var(--surface-2); border:1px solid var(--border); border-radius: 10px; padding:3px; gap:2px; flex-wrap:wrap; }
.seg button, .seg a {
  border:0; background:transparent; color:var(--muted); font-weight:600; font-size:13.5px;
  padding:7px 13px; border-radius:7px; cursor:pointer; white-space:nowrap;
}
.seg button.active, .seg a.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }

.yn { display:inline-flex; gap:8px; }
.yn label {
  flex:1; text-align:center; padding:9px 14px; border:1px solid var(--border-strong);
  border-radius: var(--radius-sm); cursor:pointer; font-weight:600; min-width:74px;
}
.yn input { position:absolute; opacity:0; pointer-events:none; }
.yn input:checked + span { }
.yn label:has(input:checked) { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-hover); }

/* ------------------------------ badges ------------------------------ */
.badge {
  display:inline-flex; align-items:center; gap:6px;
  padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 650;
  border:1px solid transparent; white-space:nowrap;
}
.badge .dot { width:7px; height:7px; border-radius:50%; background: currentColor; }
.badge-ok { color: var(--ok); background: var(--ok-bg); border-color: var(--ok-border); }
.badge-warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-border); }
.badge-danger { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-border); }
.badge-info { color: var(--info); background: var(--info-bg); border-color: var(--info-border); }
.badge-muted { color: var(--muted); background: var(--surface-2); border-color: var(--border); }

/* ------------------------------ tables ------------------------------ */
.table-wrap { width:100%; overflow-x:auto; -webkit-overflow-scrolling: touch; }
table.tbl { width:100%; border-collapse: collapse; font-size: 14px; }
table.tbl th, table.tbl td { padding: 11px 14px; text-align:left; border-bottom:1px solid var(--border); vertical-align: middle; }
table.tbl th { font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-weight:700; background:var(--surface-2); }
table.tbl tr:last-child td { border-bottom: none; }
table.tbl tbody tr:hover { background: var(--surface-2); }
table.tbl td.num, table.tbl th.num { text-align:right; font-variant-numeric: tabular-nums; }

/* ------------------------------ toasts ------------------------------ */
.toast-wrap { position: fixed; z-index: 90; right: 16px; bottom: 16px; display:flex; flex-direction:column; gap:10px; max-width: min(92vw, 380px); }
.toast {
  background: var(--text); color:#fff; padding: 12px 15px; border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg); font-size: 14px; font-weight:550;
  display:flex; gap:10px; align-items:flex-start;
  animation: toast-in .2s ease;
}
.toast.ok { background:#054f31; }
.toast.error, .toast.danger { background:#7a271a; }
.toast.warn { background:#7a4100; }
.toast.info { background:#194185; }
@keyframes toast-in { from { opacity:0; transform: translateY(8px); } to { opacity:1; transform:none; } }

/* ------------------------------ modal ------------------------------ */
.overlay {
  position: fixed; inset: 0; z-index: 70;
  background: rgba(16,24,40,.45);
  display: none; align-items:center; justify-content:center; padding: 18px;
}
.overlay.open { display:flex; }
.modal {
  background: var(--surface); border-radius: var(--radius-lg);
  width: 100%; max-width: 480px; box-shadow: var(--shadow-lg);
  max-height: 90vh; overflow:auto;
  animation: modal-in .18s ease;
}
@keyframes modal-in { from { opacity:0; transform: translateY(10px) scale(.99);} to {opacity:1; transform:none;} }
.modal .modal-head { padding:16px 18px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:10px; }
.modal .modal-body { padding:18px; }
.modal .modal-foot { padding:14px 18px; border-top:1px solid var(--border); display:flex; gap:10px; justify-content:flex-end; }

/* ------------------------------ slide-out drawer ------------------------------ */
.drawer-overlay { position: fixed; inset:0; z-index:60; background: rgba(16,24,40,.4); display:none; }
.drawer-overlay.open { display:block; }
.drawer {
  position: fixed; top:0; right:0; bottom:0; z-index:65;
  width: min(460px, 92vw); background: var(--surface);
  box-shadow: var(--shadow-lg); transform: translateX(100%);
  transition: transform .24s ease; display:flex; flex-direction:column;
}
.drawer.open { transform: none; }
.drawer .drawer-head { padding: 16px 18px; border-bottom:1px solid var(--border); display:flex; align-items:center; gap:10px; }
.drawer .drawer-body { padding: 18px; overflow-y:auto; flex:1; }
.drawer .drawer-foot { padding: 14px 18px; border-top:1px solid var(--border); display:flex; gap:10px; }

/* ------------------------------ misc ------------------------------ */
.divider { height:1px; background: var(--border); border:0; margin: 16px 0; }
.empty { text-align:center; padding: 40px 20px; color: var(--muted); }
.empty-emoji { font-size: 34px; margin-bottom: 8px; }
.kv { display:grid; grid-template-columns: 130px 1fr; gap: 8px 14px; font-size: 14px; }
.kv dt { color: var(--muted); font-weight:600; }
.kv dd { margin:0; }
.pill-list { display:flex; flex-wrap:wrap; gap:8px; }
.list-row { display:flex; align-items:center; gap:12px; padding:13px 0; border-bottom:1px solid var(--border); }
.list-row:last-child { border-bottom:none; }
.list-row .grow { flex:1; min-width:0; }
.truncate { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.grow { flex: 1; min-width: 0; }
/* Visible to a screen reader, invisible on screen. For labels on controls
   whose purpose is obvious by sight but nameless without one. */
.sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
}
/* The compact form control used in filter bars. Replaces the inline
   min-height/padding/font-size that was being copied from page to page. */
.input-sm, .select.input-sm {
  min-height: 34px; padding: 6px 11px; font-size: 13.5px;
}
.select.input-sm { padding-right: 32px; }
.mt-0{margin-top:0}.mt-1{margin-top:8px}.mt-2{margin-top:16px}.mt-3{margin-top:24px}
.mb-0{margin-bottom:0}.mb-1{margin-bottom:8px}.mb-2{margin-bottom:16px}
.hide { display:none !important; }

/* icons (inline stroke svg) */
.ic { width:20px; height:20px; stroke: currentColor; stroke-width:1.7; fill:none; stroke-linecap:round; stroke-linejoin:round; flex:none; }
.ic-sm { width:16px; height:16px; }

/* signature pad */
.sigpad-wrap { border:1px solid var(--border-strong); border-radius: var(--radius-sm); background:#fff; position:relative; touch-action:none; }
.sigpad-wrap canvas { width:100%; height:200px; display:block; border-radius: var(--radius-sm); }
.sigpad-baseline { position:absolute; left:16px; right:16px; bottom:44px; border-bottom:1px dashed var(--border-strong); pointer-events:none; }
.sigpad-hint { position:absolute; left:16px; bottom:22px; color:var(--muted-2); font-size:12px; pointer-events:none; }

/* legal disclosure (scroll-to-accept) */
.disclosure-scroll {
  max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 14px 16px; background: var(--surface-2);
}
.disclosure-h { font-size: 15px; font-weight: 700; margin: 14px 0 6px; }
.disclosure-h:first-child { margin-top: 0; }
.disclosure-ul { margin: 0 0 6px; padding-left: 20px; }
.disclosure-ul li { margin: 3px 0; font-size: 14px; }
.disclosure-p { font-size: 14px; margin: 6px 0; }
.disclosure-scrollhint {
  text-align: center; font-size: 12.5px; color: var(--muted);
  padding: 8px; margin-top: 6px; border-radius: 8px; background: var(--warn-bg); color: var(--warn);
}
.check.is-disabled { opacity: .55; pointer-events: none; }

/* The same disclosure text, read back on a staff page rather than accepted.
   No scroll box here: on the consent form the height cap is what forces the
   client to scroll to the end, and someone reviewing the record afterwards
   wants to read it, print it or search it, not scroll a window inside a page. */
.disclosure-read {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 16px; background: var(--surface-2);
}

/* ---------------- repeatable rows (needle records) ---------------- */
/* Item name gets the room it needs; lot and expiry are narrow and fixed. The
   delete button sits on the baseline of the inputs, not the labels. */
.needle-row {
  display: grid;
  grid-template-columns: minmax(0, 2.2fr) minmax(0, 1.2fr) minmax(0, 1fr) 40px;
  gap: 12px;
  align-items: end;
  padding: 12px 0;
  border-bottom: 1px dashed var(--border);
}
.needle-row:first-child { padding-top: 0; }
.needle-row:last-child { border-bottom: none; }
.needle-row .field { margin-bottom: 0; }
.needle-row .needle-del { color: var(--muted); margin-bottom: 1px; }
.needle-row .needle-del:hover { color: var(--danger); background: var(--danger-bg); }
@media (max-width: 720px) {
  .needle-row {
    grid-template-columns: 1fr 40px;
    grid-template-areas: "a a" "b c" "d d";
    gap: 10px;
    padding: 14px 0;
  }
  .needle-row .field:nth-of-type(1) { grid-area: a; }
  .needle-row .field:nth-of-type(2) { grid-area: b; }
  .needle-row .field:nth-of-type(3) { grid-area: c; }
  .needle-row .needle-del { grid-area: d; justify-self: start; }
}

/* ---------------- bulk selection bar (consent archive) ---------------- */
.bulk-bar {
  position: sticky; bottom: 0; z-index: 20;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; margin-top: 16px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-md);
}
.bulk-bar .bulk-count { font-weight: 700; }
.bulk-bar .grow { flex: 1; min-width: 120px; }
.check-cell { width: 38px; }
.check-cell input { width: 18px; height: 18px; accent-color: var(--primary); }

/* a compact month grid for the archive's year view */
.month-grid { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 10px; }
.month-cell {
  display: block; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface); color: var(--text);
}
.month-cell:hover { background: var(--surface-2); text-decoration: none; border-color: var(--border-strong); }
.month-cell.is-empty { opacity: .5; }
.month-cell .m-name { font-weight: 650; font-size: 13.5px; }
.month-cell .m-count { font-size: 20px; font-weight: 750; letter-spacing: -.02em; }
@media (max-width: 780px) { .month-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 520px) { .month-grid { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* progress steps */
.steps { display:flex; gap:6px; margin-bottom: 6px; }
.steps .step { flex:1; height:5px; border-radius:999px; background: var(--border); }
.steps .step.done { background: var(--primary); }
.steps .step.current { background: var(--primary); opacity:.55; }

/* ------------------------------ responsive: tablet & phone ------------------------------ */
@media (max-width: 980px) {
  /* iPad portrait is 768px. Two data tables side by side there are unreadable,
     and .grid-2 was not collapsing until 640px. */
  .grid-2 { grid-template-columns: 1fr; }
  :root { --sidebar-w: 230px; }
  .grid-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .grid-3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
/* Desktop: the sidebar is fixed and open by default; the menu button collapses
   it (slides off-canvas) and reflows the content to full width. */
@media (min-width: 901px) {
  .app.nav-collapsed .sidebar { transform: translateX(-100%); box-shadow: none; }
  .app.nav-collapsed .main { margin-left: 0; }
}
/* Collapse the sidebar to an overlay for iPad portrait (768) and phones. */
@media (max-width: 900px) {
  /* Every row action in the admin area is a .btn-sm. 34px is comfortable with
     a mouse and awkward with a thumb, and the studio works on iPads. */
  .btn-sm { min-height: 40px; padding: 9px 13px; }
  .sidebar { transform: translateX(-100%); box-shadow: var(--shadow-lg); }
  .sidebar.open { transform: none; }
  .main { margin-left: 0; }
}
@media (max-width: 640px) {
  .content { padding: 16px; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .kv { grid-template-columns: 1fr; gap:2px 0; }
  .kv dt { margin-top:8px; }
  .modal { max-width: 100%; }
  .h1 { font-size: 21px; }
}
/* nav backdrop when the sidebar is an overlay */
.nav-backdrop { position:fixed; inset:0; background:rgba(16,24,40,.4); z-index:39; display:none; }
.nav-backdrop.open { display:block; }
@media (min-width: 901px) { .nav-backdrop { display:none !important; } }

/* public wizard is centered, no sidebar */
.public-shell { min-height:100vh; display:flex; flex-direction:column; align-items:center; padding: 20px 16px 60px; }
.public-card { width:100%; max-width: 560px; }
.public-brand { display:flex; align-items:center; gap:10px; font-weight:750; margin: 6px 0 18px; }

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* =====================================================================
   Photo capture, identity check, needle suggestions, storage library
   and the Owner's advanced settlement view.
   ===================================================================== */

/* --- file inputs that say what they actually do -----------------------
   A browser renders <input type="file"> with its own button reading "Choose
   File", and neither CSS nor an attribute can change that text. On the consent
   form that label is actively wrong: the client is not choosing an old photo
   off their phone, they are taking one now, and "Choose File" invites exactly
   the wrong behaviour.

   So the input is hidden from sight (not from the accessibility tree, and not
   from the tab order) and a <label for> is styled as the button. A label needs
   no JavaScript to focus and activate its input, so this works with JS off. */
.filefield { position: relative; display: block; }
.filefield > input[type="file"] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.filebtn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; min-height: 54px; padding: 14px 18px;
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius);
  background: var(--surface-2); color: var(--text);
  font-weight: 650; font-size: 15px; cursor: pointer; text-align: center;
  transition: background .15s, border-color .15s, color .15s;
}
.filebtn:hover { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-hover); }
.filebtn .ic { flex: none; width: 20px; height: 20px; }
/* The hidden input still takes focus, so the button has to show it. */
.filefield > input[type="file"]:focus-visible + .filebtn {
  outline: 2px solid var(--primary); outline-offset: 2px; border-style: solid;
}
/* Once a photo exists the control stops inviting and starts confirming. */
.filefield.has-file .filebtn {
  border-style: solid; border-color: var(--ok-border);
  background: var(--ok-bg); color: var(--ok);
}
.filefield.has-file .filebtn:hover { border-color: var(--primary); background: var(--primary-soft); color: var(--primary-hover); }
.file-state { font-size: 12.5px; color: var(--muted); margin-top: 6px; }
.filefield.has-file + .file-state { color: var(--ok); font-weight: 600; }
.field.invalid .filebtn { border-color: var(--danger); background: var(--danger-bg); color: var(--danger); }

/* --- client-side preview of a just-taken photo (consent wizard) --- */
.photo-preview { margin-top: 9px; }
.photo-preview img {
  display:block; width:100%; max-height: 240px; object-fit: contain;
  background: var(--surface-2); border:1px solid var(--border); border-radius: 9px;
}

/* --- ID and selfie shown side by side, so they can be compared --- */
.idpair { display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; }
.idshot {
  display:block; width:100%; background:#fff;
  border:1px solid var(--border); border-radius:9px;
}
@media (max-width: 560px) { .idpair { grid-template-columns: 1fr; } }

/* --- needle rows + the suggestion menu attached to a field --- */
.needle-suggest-wrap { position: relative; }
.needle-suggest {
  position:absolute; z-index: 45; left:0; right:0; top: calc(100% + 4px);
  background: var(--surface); border:1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-lg); max-height: 288px; overflow-y:auto; padding: 4px;
}
.needle-suggest[hidden] { display:none !important; }
.needle-suggest button {
  display:block; width:100%; text-align:left; background:none; border:0;
  padding: 8px 10px; border-radius: 7px; cursor:pointer; font: inherit; color: var(--text);
}
.needle-suggest button:hover, .needle-suggest button.is-active { background: var(--primary-soft); }
.needle-suggest .ns-name { font-weight: 620; font-size: 13.5px; }
.needle-suggest .ns-meta { color: var(--muted); font-size: 12px; margin-top: 2px; }
.needle-suggest .ns-warn { color: var(--danger); font-weight: 620; }
.needle-suggest .ns-empty { padding: 10px; color: var(--muted); font-size: 12.5px; }
.needle-suggest-hint { color: var(--muted); font-size: 12px; margin-top: 5px; }

/* --- record form: repeatable payment + tip rows ----------------------
   Declared here rather than inline on the element, so the media query below
   can actually reach them. On a phone five tracks gave each select about 60px
   and the amount field could not show a four-figure total. */
.pc-row  { display:grid; grid-template-columns: 1.1fr 1fr 1fr .9fr auto; gap:8px; align-items:end; margin-bottom:8px; }
.tip-row { display:grid; grid-template-columns: .9fr 1fr 1fr auto;      gap:8px; align-items:end; margin-bottom:8px; }
@media (max-width: 720px) {
  .pc-row, .tip-row { grid-template-columns: 1fr 1fr; }
  /* The remove button keeps to the end of the last row rather than taking a
     whole track of its own. */
  .pc-row > :last-child, .tip-row > :last-child { grid-column: 2; justify-self: end; }
}

/* --- storage: the photo gallery ---------------------------------------
   You cannot tell whether a photo is worth keeping, or which of two licences
   belongs to whom, from a 32-character hex filename. The gallery is the
   default view for that reason; the list is for the "what is big" question. */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
  padding: 16px;
}
.gcard {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: box-shadow .15s, border-color .15s;
}
.gcard:hover { box-shadow: var(--shadow-md); }
.gcard.is-worth { border-color: var(--warn-border); }
/* The whole image is the checkbox label, so a tap anywhere on the picture
   selects it — the natural gesture on a tablet.
   A SQUARE tile, because the library mixes landscape licences with portrait
   selfies and a 4:3 window makes one of the two look wrong whichever way round
   it is set. The image is absolutely positioned: aspect-ratio only expresses a
   preferred size, and an in-flow portrait image taller than the box simply
   stretches it — which is what made the first version of this grid ragged. */
.gcard-shot {
  position: relative; display: block; cursor: pointer;
  aspect-ratio: 1 / 1; overflow: hidden;
  background: repeating-conic-gradient(var(--surface-2) 0% 25%, var(--surface) 0% 50%) 50% / 16px 16px;
  border-bottom: 1px solid var(--border);
}
.gcard-shot img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: contain; display: block;
}
.gcard-pick {
  position: absolute; top: 8px; left: 8px;
  width: 22px; height: 22px; accent-color: var(--primary);
  border-radius: 5px; cursor: pointer; z-index: 2;
  box-shadow: 0 0 0 3px rgba(255,255,255,.85);
}
.gcard-size {
  position: absolute; right: 8px; bottom: 8px;
  background: rgba(16,24,40,.72); color: #fff;
  font-size: 11.5px; font-weight: 600;
  padding: 2px 7px; border-radius: 999px;
}
/* Zoom sits top-right, out of the checkbox's way, and is a real 40px target. */
.gcard-zoom {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 9px; cursor: pointer;
  background: rgba(16,24,40,.6); color: #fff;
  opacity: 0; transition: opacity .15s;
}
.gcard:hover .gcard-zoom, .gcard-zoom:focus-visible { opacity: 1; }
@media (hover: none) { .gcard-zoom { opacity: .85; } }   /* touch: no hover to reveal it */
.gcard-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 2px; }
.gcard-title { font-size: 13.5px; font-weight: 650; }
.gcard-sub { font-size: 12px; color: var(--muted); }
.gcard-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.gcard-foot .btn { margin-left: auto; }
/* A selected card has to be obvious without reading the checkbox.
   Done with a sibling selector on an overlay inside the label rather than
   :has() on the card: Safari only gained :has() in 15.4, and the studio works
   on iPads that may not have it. The ring is the reliable signal; the :has()
   rule is a bonus where it is supported. */
.gcard-ring {
  position: absolute; inset: 0; pointer-events: none;
  border: 2px solid transparent; border-radius: inherit;
  transition: border-color .12s, background .12s;
}
.gcard-pick:checked ~ .gcard-ring {
  border-color: var(--primary);
  background: var(--primary-soft);
  opacity: .65;
}
@supports selector(:has(*)) {
  .gcard:has(.gcard-pick:checked) { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-soft); }
}

/* --- storage: the list view --- */
.media-thumb {
  width: 56px; height: 44px; object-fit: cover; border-radius: 6px;
  border:1px solid var(--border); background: var(--surface-2); flex:none;
}
.media-thumb-btn { padding: 0; border: 0; background: none; cursor: zoom-in; line-height: 0; }
.media-thumb-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 7px; }
.bar-track { height:8px; border-radius:999px; background:var(--surface-2); overflow:hidden; }
.bar-fill { height:100%; background: var(--primary); }

/* The storage filter row: chips on the left, controls on the right, wrapping
   to two rows on a phone rather than squeezing. */
.storage-toolbar { display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap; }
.storage-toolbar .seg { flex-wrap: wrap; }

/* --- lightbox: one on the page, filled in by app.js ------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 95; display: none;
  align-items: center; justify-content: center;
  background: rgba(12,16,26,.88); padding: 20px;
}
.lightbox.open { display: flex; }
.lightbox-inner { max-width: min(94vw, 1000px); max-height: 92vh; display: flex; flex-direction: column; gap: 10px; }
.lightbox img {
  max-width: 100%; max-height: 78vh; object-fit: contain;
  background: #fff; border-radius: var(--radius); display: block; margin: 0 auto;
}
.lightbox-bar { display: flex; align-items: center; gap: 12px; color: #fff; }
.lightbox-bar .grow { min-width: 0; }
.lightbox-title { font-weight: 650; font-size: 15px; }
.lightbox-meta { font-size: 12.5px; opacity: .72; }
.lightbox-close {
  border: 0; background: rgba(255,255,255,.14); color: #fff;
  width: 40px; height: 40px; border-radius: 10px; cursor: pointer; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.lightbox-close:hover { background: rgba(255,255,255,.24); }

@media (max-width: 640px) {
  .gallery { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; padding: 12px; }
  .hide-xs { display: none; }
}

/* --- advanced (Owner) money table: dense, readable, scrolls on its own --- */
.tbl.tbl-dense th, .tbl.tbl-dense td { padding: 7px 9px; font-size: 12.8px; }
/* Thirteen columns will always scroll sideways. Pin the client name, or by the
   time the money is on screen you no longer know whose money it is. */
.tbl-dense th:nth-child(2), .tbl-dense td:nth-child(2) {
  position: sticky; left: 0; z-index: 1; background: var(--surface);
}
.tbl-dense th:nth-child(2) { background: var(--surface-2); z-index: 2; }
.tbl-dense tbody tr:hover td:nth-child(2) { background: var(--surface-2); }
.tbl th.grp {
  border-left: 1px solid var(--border);
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
}
.tbl td.grp { border-left: 1px solid var(--border); }
.money-grid {
  display:grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;
}
.money-grid .cell {
  background: var(--surface-2); border:1px solid var(--border);
  border-radius: 10px; padding: 11px 13px;
}
.money-grid .cell .k { color: var(--muted); font-size: 12px; font-weight: 600; }
.money-grid .cell .v { font-size: 17px; font-weight: 700; margin-top: 3px; font-variant-numeric: tabular-nums; }
.money-grid .cell.accent { background: var(--primary-soft); border-color: var(--primary-soft); }
/* Payment-method totals (includes/_payment_methods_summary.php). Boxed, so it
   reads as its own figure inside the Financial breakdown card. The Total row is
   ruled off as the period's overall total across every method; it is NOT a
   reconciliation figure. Each method row is what gets checked against its own
   source (Card: terminal, Cash: drawer, E-transfer: bank). */
.method-totals { border: 1px solid var(--border); border-radius: 10px; }
.method-totals tfoot td { border-top: 1px solid var(--border-strong); background: var(--surface-2); }
