/*
 * admin-module-utilities.css — shared styles for admin feature modules.
 *
 * These selectors belong to shared admin JavaScript modules, so their styles
 * must live once rather than drift between the super and tenant shells. Keep
 * shell layout in the shell stylesheets and cross-cutting polish in
 * admin-shared.css. This file loads after the shell stylesheet.
 */

/* ───────────────────────────────────────────────────────────────
 * Admin inline-style extraction (2026-06-23)
 *
 * Token-driven utility classes that replace repeated inline style=""
 * attributes in the admin innerHTML templates (admin-revenue.js,
 * admin-overview.js, admin-customers.js, admin-waiting-list.js,
 * admin-notices.js, admin-catalog.js, admin-logs.js). Values are copied
 * verbatim from the inline styles they replace — behaviour-preserving.
 * The JavaScript modules are shared by both admin shells, so these rules are
 * centralized here and must not be copied back into either shell stylesheet.
 * ─────────────────────────────────────────────────────────────── */

.adm-num            { text-align: right; }
.adm-muted          { color: var(--text-muted); }
.adm-warn           { color: var(--danger-text); }
.adm-fs-10          { font-size: var(--fs-12); }
.adm-fs-11          { font-size: var(--fs-12); }
.adm-fs-12          { font-size: var(--fs-12); }
.adm-fs-13          { font-size: var(--fs-13); }
.adm-muted-12       { color: var(--text-muted); font-size: var(--fs-12); }
.adm-muted-11       { color: var(--text-muted); font-size: var(--fs-12); }
.adm-bar            { background: var(--accent); height: 14px; border-radius: 3px; }
.adm-total-row      { font-weight: 600; border-top: 2px solid var(--border); }

/* ── customer-records (admin-customers.js) inline-style extraction ── */
.cust-card-pad      { padding: var(--space-3xl); margin-bottom: var(--space-2xl); }
.cust-card-pad-only { padding: var(--space-3xl); }
.cust-card-pad h2,
.cust-card-pad-only h2 { margin-top: 0; }
.cust-detail-email  { color: var(--text-muted); margin-top: var(--space-2xs); }
.cust-detail-phone  { color: var(--text-muted); margin-top: var(--space-3xs); }
.cust-detail-source { margin-top: var(--space-sm); font-size: var(--fs-12); color: var(--text-light); }
.cust-detail-back   { margin-bottom: var(--space-2xl); }
.cust-section-head  { font-size: var(--fs-meta); font-weight: 600; color: var(--text-muted); margin-top: var(--space-2xl); margin-bottom: var(--space-2xs); }
.cust-mono-12       { font-size: var(--fs-12); font-family: monospace; }
.cust-grid-4        { grid-template-columns: 120px 1fr 80px 100px; }
.cust-grid-3        { grid-template-columns: 1fr 1fr 80px; }
.cust-table-mt      { margin-top: var(--space-lg); }
.cust-empty-mt      { margin-top: var(--space-sm); }
.cust-empty-mt0     { margin-top: var(--space-sm); margin-bottom: 0; }
.cust-status-cancelled { color: var(--danger-text); font-size: var(--fs-12); font-weight: 600; }
.cust-status-checkedin { color: var(--success, var(--c-green-600-c)); font-size: var(--fs-12); font-weight: 600; }
.cust-status-expired   { color: var(--danger-text); font-size: var(--fs-12); font-weight: 600; }
.cust-status-pending   { color: var(--text-muted); font-size: var(--fs-12); }
.cust-row-link      { cursor: pointer; }
.cust-dash          { color: var(--text-muted); }
.cust-error         { color: var(--error-text); }
.cust-notes-textarea { width: 100%; margin-top: var(--space-sm); font-family: inherit; font-size: var(--fs-14); padding: var(--space-sm); border: 1px solid var(--border); border-radius: var(--radius-btn); resize: vertical; }
.cust-notes-save    { margin-top: var(--space-sm); }
/* ── waiting-list (admin-waiting-list.js) inline-style extraction ── */
.adm-meta-line      { margin-top: var(--space-3xs); font-size: var(--fs-12); color: var(--text-muted); }
.adm-actions-row    { display: flex; gap: var(--space-xs); margin-top: var(--space-xs); }
.adm-tag-mt         { margin-top: var(--space-2xs); display: inline-block; }
.adm-event-row      { padding: var(--space-2xs) 0; border-bottom: 1px solid var(--border); }
.adm-pre-meta       { margin: var(--space-2xs) 0 0; font-size: var(--fs-12); color: var(--text-muted); white-space: pre-wrap; }
.adm-detail-body    { font-size: var(--fs-14); line-height: 1.6; }
.adm-events-wrap    { margin-top: var(--space-lg); }
.adm-events-list    { margin-top: var(--space-xs); font-size: var(--fs-13); }
/* ── notices (admin-notices.js) inline-style extraction ── */
.adm-muted-13       { color: var(--text-muted); font-size: var(--fs-13); }
.adm-warn-13        { color: var(--danger-text); font-size: var(--fs-13); }
.adm-scroll-300     { max-height: 300px; overflow-y: auto; }
.adm-mb-6           { margin-bottom: var(--space-xs); }
.adm-notice-confirmed { font-size: var(--fs-12); color: var(--success-text); margin-left: var(--space-xs); }
.adm-h4-flush       { margin: 0 0 var(--space-sm); }
/* ── catalog (admin-catalog.js) inline-style extraction ── */
.cat-loading        { color: var(--text-muted); }
.cat-empty-products { color: var(--text-muted); padding: var(--space-lg) var(--space-xl); margin: 0; }
.cat-name-uncat     { font-weight: 400; }
.cat-chip-sm        { font-size: var(--fs-12); cursor: pointer; }
.cat-chip-all       { font-size: var(--fs-12); background: var(--primary-surface); border-color: var(--primary); }
.cat-edit-fields    { flex: 1; display: flex; flex-direction: column; gap: var(--space-xs); }
.cat-edit-label     { display: grid; gap: 3px; color: var(--text-muted); font-size: var(--fs-12); font-weight: 600; }
.cat-edit-input     { font-size: var(--fs-13); }
.cat-edit-actions   { display: flex; gap: var(--space-xs); justify-content: flex-end; }
.adm-muted-11-mt    { font-size: var(--fs-12); color: var(--text-muted); margin-top: var(--space-3xs); }

/* ───────────────────────────────────────────────────────────────
 * Admin inline-style extraction — phase 0/1 (2026-06-24)
 *
 * Behaviour-preserving CSS for inline style="" attributes removed from
 * admin innerHTML templates. Values copied VERBATIM from the inline
 * styles they replace. The admin JS modules are shared across both shells, so
 * their common presentation is owned by this stylesheet.
 *
 * Covers: admin-email, admin-deferred-revenue, admin-vouchers,
 * admin-auth, admin-revenue, admin-setup,
 * admin-notifications-health, admin-platform-settings, admin-org,
 * admin-status, admin-lightspeed-connection, admin-courses, admin-feedback,
 * admin-teesheet, admin-errors, admin-theme-fields, admin-theme-preview.
 * ─────────────────────────────────────────────────────────────── */

/* ── Shared micro-utilities ── */
.adm-flush          { margin: 0; }
.adm-mt4            { margin-top: var(--space-2xs); }

/* ── channel-specific email settings ── */
/* Health chain: wrap the step→step→step row instead of overflowing on narrow
   panels. Each step stays intact (its icon + label never split across lines). */
#emailHealthChain   { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2xs) var(--space-sm); }
#emailHealthChain > span { white-space: nowrap; }
#emailHealthStatus  { display: block; margin-top: var(--space-2xs); overflow-wrap: anywhere; }
.em-chain-ok        { color: var(--success-text); }
.em-chain-bad       { color: var(--danger-text); }
.em-chain-broken    { font-weight: 700; }
.em-chain-arrow     { color: var(--text-light); }
.em-status-ok       { color: var(--success-text); font-weight: 600; }
.em-status-warn     { color: var(--danger-text); font-weight: 600; }
/* Built-in connector tag — small neutral pill next to the connector name. */
.em-builtin-tag     { display: inline-block; margin-left: var(--space-xs); padding: 1px 7px; border-radius: var(--radius-pill);
                      font-size: var(--fs-12); font-weight: 600; line-height: 1.5; white-space: nowrap;
                      color: var(--text-muted); background: var(--c-neutral-50-b, var(--admin-table-head));
                      border: 1px solid var(--border); cursor: help; vertical-align: middle; }
/* Fallback only — used if gkServiceBadge is unavailable when rendering runtime health. */
.em-health-warn     { color: var(--danger-text); font-weight: 600; }
.em-connector-empty { margin: 0; padding: var(--space-xl) var(--space-xl) 0; }
.em-cell-bold       { font-weight: 600; }
.em-btn-sm          { padding: var(--space-2xs) var(--space-md); font-size: var(--fs-12); }
.em-route-connector { font-size: var(--fs-12); padding: var(--space-2xs) var(--space-xs); max-width: 180px; }
.em-tpl-titlerow    { justify-content: space-between; align-items: center; margin-bottom: var(--space-sm); }
.em-label-note      { font-weight: 400; color: var(--text-muted); font-size: var(--fs-12); }
.em-hint-mt4        { margin-top: var(--space-2xs); }
.em-hint-mt6        { margin-top: var(--space-xs); }
.em-hint-mt10       { margin-top: var(--space-md); }
.em-fg-flush        { margin-top: 0; }
.em-recip-expand    { display: flex; align-items: center; gap: var(--space-xs); cursor: pointer; user-select: none; }
.em-recip-chevron   { font-size: var(--fs-12); transition: transform .15s; }
.em-recip-label     { font-size: var(--fs-12); font-weight: 600; }
.em-recip-note      { font-size: var(--fs-12); color: var(--text-muted); }
.em-cond-header     { border-top: 1px solid var(--border); padding-top: var(--space-lg); margin-top: var(--space-2xs); }
.em-cond-header-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); }
.em-cond-title      { font-weight: 500; font-size: var(--fs-13); }
.em-cond-subtitle   { font-size: var(--fs-12); color: var(--text-muted); margin-top: var(--space-3xs); }
.em-cond-config     { background: var(--surface-raised, var(--c-neutral-50-b)); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--space-lg); margin-top: var(--space-2xs); }
.em-cond-config-row { display: flex; gap: var(--space-sm); align-items: flex-end; flex-wrap: wrap; }
.em-cond-field-path { flex: 2; min-width: 140px; margin: 0; }
.em-cond-field-op   { flex: 1; min-width: 110px; margin: 0; }
.em-cond-field-value { flex: 2; min-width: 140px; margin: 0; }
.em-cond-help       { margin-top: var(--space-sm); margin-bottom: 0; }
.em-preview-panel   { margin-top: var(--space-lg); padding: 0; overflow: hidden; }
.em-error-cell      { max-width: 260px; word-break: break-word; }
.em-hint-flush      { margin: 0; }
.em-hint-warn       { color: var(--danger-text); margin: 0; }

/* ── admin-deferred-revenue.js ── */
.dr-table-mb        { margin-bottom: var(--space-lg); }
.dr-total-row       { font-weight: 700; border-top: 2px solid var(--border); }
.dr-bar-col         { min-width: 80px; }
.dr-bar             { height: 10px; border-radius: 2px; }
.dr-bar--cash       { background: var(--accent); }
.dr-bar--rev        { background: var(--c-green-400); }
.dr-noshow-count    { font-size: var(--fs-20); font-weight: 700; }
.dr-noshow-pct      { margin-left: var(--space-sm); }

/* ── admin-vouchers.js ── */
.vch-empty          { padding: var(--space-sm) 0; }
/* Base table styling comes from `.gk-table .gk-table--compact` (admin-shared.css);
 * the voucher table self-wraps in a bordered, rounded, clipped card. */
.vch-table          { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.vch-thead-row      { background: var(--admin-table-head); text-align: left; }
/* Scoped under `.vch-table` so these win over the `.gk-table--compact` cell
 * rhythm and keep the voucher table's slightly tighter 5px body rows. The
 * voucher table separates rows with zebra striping (`.vch-row-alt`), not the
 * base per-row border, so the `.gk-table` cell border is neutralised here. */
.vch-table .vch-th  { padding: var(--space-xs) var(--space-sm); border-bottom: none; }
.vch-th-intent      { font-family: var(--mono); font-size: var(--fs-12); }
.vch-row-alt        { background: var(--c-neutral-50-b); }
.vch-table .vch-td  { padding: 5px var(--space-sm); border-bottom: none; }
.vch-mono           { font-family: var(--mono); }
.vch-intent         { font-size: var(--fs-12); max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vch-page-btn       { padding: var(--space-2xs) var(--space-md); }
.vch-error          { color: var(--danger-text); padding: var(--space-sm) 0; }

/* ── admin-auth.js ── */
/* The access card itself (.au-screen, .au-card, .au-signin-btn) is shell
 * chrome and lives in admin-shell-layout.css with the auth-prompt layout. */
/* Venue chooser shown when a tenant admin with multiple venues hits /admin. */
.au-venue-list     { display: flex; flex-direction: column; gap: var(--space-sm); text-align: left; max-width: 24rem; margin: 0 auto; }
.au-venue-item      { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); padding: var(--space-lg) var(--space-2xl); border: 1px solid var(--border); border-radius: var(--radius-btn, var(--radius-btn)); background: var(--card); color: var(--text); font-size: var(--fs-14); font-weight: 600; text-decoration: none; transition: border-color .15s, background .15s; }
.au-venue-item:hover { border-color: var(--primary); background: var(--primary-subtle, var(--card)); }
/* A closed venue is listed but not a link: no hover, muted name. */
.au-venue-item[aria-disabled="true"],
.au-venue-item[aria-disabled="true"]:hover { border-color: var(--border); background: var(--card); color: var(--text-muted); cursor: default; }
.au-venue-go        { color: var(--text-muted); }

/* ── admin-revenue.js ── */
.rev-bar-col        { min-width: 120px; }
.rev-bar-cell       { min-width: 140px; }
.rev-bar-track      { height: 8px; overflow: hidden; border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--primary-surface); }
.rev-bar-fill       { height: 100%; min-width: 0; border-radius: inherit; background: var(--accent); }
/* The booking funnel: one bordered card, a row per step divided by hairlines. The name and its notes on the left, the bar and the
 * count on the right. A step with a note under its count (what it was in the earlier period) keeps the count on the bar's line. */
.rev-funnel         { display: grid; border: var(--hairline) solid var(--border); border-radius: var(--radius-surface); background: var(--card); overflow: hidden; }
.rev-funnel-header  { display: grid; grid-template-columns: minmax(9rem, .9fr) minmax(12rem, 2fr); gap: var(--space-xl); padding: var(--space-md) var(--space-2xl); border-bottom: var(--hairline) solid var(--border); color: var(--text-muted); font-size: var(--fs-body); font-weight: var(--fw-ui); }
.rev-funnel-header span:last-child { text-align: right; }
.rev-funnel-list    { display: grid; margin: 0; padding: 0; list-style: none; }
.rev-funnel-row     { display: grid; grid-template-columns: minmax(9rem, .9fr) minmax(12rem, 2fr); gap: var(--space-2xs) var(--space-xl); align-items: center; padding: var(--space-lg) var(--space-2xl); background: var(--card); }
.rev-funnel-row + .rev-funnel-row { border-top: var(--hairline) solid var(--border); }
.rev-funnel-label   { display: grid; gap: var(--space-3xs); min-width: 0; color: var(--text); font-size: var(--fs-body); }
.rev-funnel-stage   { font-weight: var(--fw-ui); }
.rev-funnel-measure { display: grid; grid-template-columns: minmax(0, 1fr) minmax(5rem, auto); gap: var(--space-lg); align-items: center; }
.rev-funnel-track   { height: var(--space-md); overflow: hidden; border-radius: var(--radius-pill); background: var(--primary-surface); }
.rev-funnel-fill    { height: 100%; min-width: 0; border-radius: inherit; background: var(--accent); transition: width var(--dur-slow, .25s) var(--ease, ease); }
.rev-funnel-count   { display: grid; justify-items: end; color: var(--text); font-size: var(--fs-16); font-weight: var(--fw-title); font-variant-numeric: tabular-nums; }
.rev-funnel-annotation { display: block; margin: 0; color: var(--text-muted); font-size: var(--fs-13); font-weight: 400; line-height: var(--lh-snug); }
.rev-funnel-overall { display: flex; align-items: baseline; gap: var(--space-sm); padding: var(--space-md) var(--space-2xl); border-top: var(--hairline) solid var(--border); color: var(--text-muted); font-size: var(--fs-13); }
.rev-funnel-overall strong { color: var(--text); font-size: var(--fs-16); font-variant-numeric: tabular-nums; }
.rev-funnel-row--offpath .rev-funnel-fill { background: var(--text-muted); opacity: .72; }
@media (max-width: 720px) {
  .rev-funnel-header { display: none; }
  .rev-funnel-row { grid-template-columns: 1fr; gap: var(--space-sm); padding: var(--space-lg); }
  .rev-funnel-overall { flex-wrap: wrap; padding-inline: var(--space-lg); }
}

/* ── admin-setup.js ── */
.setup-link-list    { display: flex; flex-direction: column; gap: var(--space-md); }
.setup-link-card    { padding: var(--space-xl); }
.setup-link-row     { display: flex; justify-content: space-between; gap: var(--space-lg); align-items: flex-start; flex-wrap: wrap; }
.setup-link-provider { font-weight: 600; color: var(--text); }

/* ── admin-notifications-health.js ── */
.nh-link            { white-space: nowrap; }
.nh-link-icon       { font-weight: 700; }
.nh-arrow           { color: var(--text-light); }
.nh-summary-ok      { color: var(--success-text); font-weight: 600; }
.nh-summary-warn    { color: var(--danger-text); font-weight: 600; }

/* ── admin-platform-settings.js ── */
.ps-field gk-field  { min-width: 0; }
.ps-setting         { padding: var(--space-xl); margin-bottom: var(--space-lg); }
.ps-field           { margin-bottom: var(--space-xs); }
.ps-input           { width: 100%; min-width: 0; font-family: monospace; font-size: var(--fs-13); }
.ps-input--choice   { font-family: inherit; }
.ps-default         { margin-top: var(--space-3xs); margin-bottom: var(--space-sm); }
.ps-actions         { display: flex; flex-wrap: wrap; gap: var(--space-sm); }

/* ── admin-org.js ── */
.org-picker-msg     { margin-bottom: var(--space-md); }
.org-picker-list    { position: static; max-height: 240px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-input, var(--radius-btn)); display: block; }
.org-picker-empty   { padding: var(--space-lg); color: var(--text-muted); font-size: var(--fs-13); }

/* ── admin-status.js ── */
.st-dl-dismiss      { flex-shrink: 0; font-size: var(--fs-13); padding: var(--space-2xs) var(--space-md); }
.st-tooltip-meta-mt { margin-top: var(--space-sm); margin-bottom: var(--space-2xs); }

/* ── admin-lightspeed-connection.js ── */
.lightspeed-venue-result { display: block; width: 100%; margin-bottom: var(--space-xs, var(--space-xs)); text-align: left; --btn-pad-y: 8px; --btn-pad-x: 10px; }
.lightspeed-venue-result__content { display: block; width: 100%; text-align: left; white-space: normal; }
.lightspeed-venue-name { display: block; font-size: var(--fs-13); font-weight: 600; }
.lightspeed-venue-id   { display: block; font-size: var(--fs-12); color: var(--text-muted); font-family: var(--mono); }
.lightspeed-venue-warn { display: block; font-size: var(--fs-12); color: var(--danger-text); }
.lightspeed-warn-text  { color: var(--danger-text); }

/* ── admin-courses.js ── */
.crs-overrides      { margin-top: var(--space-sm); }
.crs-field-grid     { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-md); }
.crs-field-grid .crs-field--wide { grid-column: 1 / -1; }
.crs-input          { width: 100%; }
.crs-save-btn       { margin-top: var(--space-xs); }
@media (max-width: 720px) { .crs-field-grid { grid-template-columns: minmax(0, 1fr); } }

/* ── admin-feedback.js ── */
.fb-table           { width: 100%; }
.fb-date            { white-space: nowrap; font-size: var(--fs-12); }
.fb-message         { max-width: 360px; white-space: pre-wrap; word-break: break-word; }

/* ── admin-teesheet.js ── */
.ts-checkin-close   { padding-top: var(--space-sm); }

/* ── admin-errors.js ── */
.err-col-chevron    { width: 24px; }

/* ── admin-theme-fields.js ── */
.tf-section-hint    { margin-bottom: var(--space-md); font-size: var(--fs-12); color: var(--text-muted); }
.tf-field-hint      { color: var(--text-muted); margin-top: var(--space-3xs); }
.tf-field-source    { display: block; margin-top: var(--space-2xs); font-size: var(--fs-12); font-weight: 700; letter-spacing: .01em; }
.tf-field-source--inherited { color: var(--text-muted); }
.tf-field-source--custom    { color: var(--success-text); }
.tf-field-source--pending   { color: var(--danger-text); }
.tf-field-reset     { appearance: none; border: 0; padding: 0; background: transparent; color: var(--link); font: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.tf-field-reset:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset); border-radius: var(--space-3xs); }
.tf-step-labels     { margin-top: var(--space-lg); }
.tf-grid-6          { grid-template-columns: repeat(6, 1fr); }
.tf-input-center    { text-align: center; }
.tf-input-xs        { width: var(--control-width-xs); max-width: 100%; }
.tf-input-sm        { width: var(--control-width-sm); max-width: 100%; }
.tf-subgroup        { margin-top: var(--space-xl); }

@media (max-width: 720px) {
  .tf-grid-6 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tf-grid-6 > gk-field { min-width: 0; }
}

/* ───────────────────────────────────────────────────────────────
 * Admin inline-style extraction — phase 3 (2026-07-23)
 *
 * Behaviour-preserving CSS for the inline style="" attributes the earlier
 * phases deferred. Values copied verbatim. Covers: admin-webhooks,
 * admin commerce (merchant readiness), admin-auth (venue plan banner),
 * admin-customers (attribute editor), admin-service-keys,
 * admin-theme-fields (full-width grid items).
 * ─────────────────────────────────────────────────────────────── */

/* shared micro-utilities. The doubled .hint selector keeps the margin when the
   utility sits on a .hint — admin-shared.css's `.hint { margin: 0 }` loads
   after this file and would otherwise win the cascade tie. */
.adm-center         { text-align: center; }
.adm-mt-12, .hint.adm-mt-12 { margin-top: var(--space-lg); }

/* ── admin-webhooks.js ── */
.wh-empty-note      { padding: var(--space-lg) var(--space-2xl); margin: 0; }
.wh-empty-log       { padding: var(--space-2xl) 0; }
.wh-btn-xs          { padding: 3px var(--space-md); font-size: var(--fs-12); }
.wh-page-label      { font-size: var(--fs-12); color: var(--text-muted); padding: 0 var(--space-lg); }
.wh-copy-btn        { padding: var(--space-3xs) var(--space-sm); font-size: var(--fs-12); margin-left: var(--space-sm); }
.wh-payload-code--clamped { max-height: 160px; }
.wh-drawer-actions  { margin-top: var(--space-2xl); display: flex; gap: var(--space-sm); }

/* ── Booking commerce merchant-readiness status ── */
.pay-step           { margin-bottom: var(--space-xs); }
.pay-status-note    { margin-top: 0.35rem; }

/* ── admin-auth.js (venue plan banner) ── */
.au-plan-row        { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; margin: 0 0 var(--space-md); }
.au-plan-meta       { font-size: var(--fs-13); color: var(--text-muted); }
.au-plan-meta strong { color: var(--text); }

/* ── admin-customers.js (attribute editor) ── */
.cust-attr-editor   { box-sizing: border-box; width: 100%; font-family: monospace; }
.hint.cust-attr-hint { margin-top: var(--space-xs); }

/* ── admin-service-keys.js ── */
.sk-evt--top        { align-items: flex-start; margin-bottom: var(--space-sm); }

/* ── admin-theme-fields.js ── */
.tf-span-all        { grid-column: 1 / -1; }

/* Field captions in every admin console read in sentence case, at 13px semibold
 * in the text colour (owner decision, 19 Oct 2026). gk-field reads these tokens;
 * the native base below uses the same ones, so a bare <label> matches it. Customer
 * pages and the Extras Stock console do not load this file and keep their own. */
:root {
  --field-label-font-size: var(--fs-13);
  --field-label-font-weight: 600;
  --field-label-letter-spacing: normal;
  --field-label-text-transform: none;
  --field-label-color: var(--text);
  /* Alert headings follow the same sentence-case rule. */
  --alert-heading-font-size: var(--fs-14);
  --alert-heading-font-weight: 600;
  --alert-heading-letter-spacing: normal;
  --alert-heading-text-transform: none;
  /* Admin form fields are as wide as their answer (alongside --control-width-xs
   * and -sm in tokens-shared.css): a number or a date, a choice from a list, a
   * line of text. A reason or a description may use the whole row. */
  --control-width-md: 16rem;
  --control-width-lg: 24rem;
  --control-width-xl: 40rem;
}

/* ───────────────────────────────────────────────────────────────
 * Native-element base (opt-in: `.gk-admin-native`)
 *
 * A product console or a mounted product panel that draws bare <table>,
 * <input>, <select>, <textarea>, <fieldset>, <legend> and <label> puts
 * `gk-admin-native` on its root element and gets the same look as gk-field and
 * gk-data-table without a stylesheet of its own. Anything outside that root is
 * untouched: this file is not loaded by the Extras Stock console, and no rule
 * here matches without the class.
 *
 * Every selector is wrapped in :where(), so each rule has zero specificity. A
 * product's own class or element rule always wins, and a product deletes its
 * local control styles by removing them, not by fighting this layer. Values
 * are the same tokens gk-field and gk-data-table read.
 * ─────────────────────────────────────────────────────────────── */
:where(.gk-admin-native) {
  color: var(--text);
  font-family: var(--font-body);
}
:where(.gk-admin-native *, .gk-admin-native *::before, .gk-admin-native *::after) { box-sizing: border-box; }

/* Text-entry controls, matching gk-field's slotted control. */
:where(.gk-admin-native input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"], [type="button"], [type="submit"], [type="reset"], [type="image"]),
.gk-admin-native select,
.gk-admin-native textarea) {
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  background-color: var(--card);
  color: var(--text);
  padding: var(--field-pad-y, var(--space-md)) var(--field-pad-x, var(--space-lg));
  font-family: var(--font-body);
  font-size: var(--field-font-size, var(--fs-14));
  font-weight: 400;
  line-height: var(--lh-normal);
  letter-spacing: normal;
  text-transform: none;
  transition: var(--transition-control);
}
:where(.gk-admin-native input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"], [type="button"], [type="submit"], [type="reset"], [type="image"]):hover,
.gk-admin-native select:hover,
.gk-admin-native textarea:hover) {
  border-color: var(--primary-muted);
}
:where(.gk-admin-native input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"], [type="button"], [type="submit"], [type="reset"], [type="image"]):focus,
.gk-admin-native select:focus,
.gk-admin-native textarea:focus) {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 var(--focus-glow-width) var(--focus-ring);
}
:where(.gk-admin-native input[aria-invalid="true"], .gk-admin-native select[aria-invalid="true"], .gk-admin-native textarea[aria-invalid="true"]) {
  border-color: var(--danger-text);
}
:where(.gk-admin-native input[aria-invalid="true"]:focus, .gk-admin-native select[aria-invalid="true"]:focus, .gk-admin-native textarea[aria-invalid="true"]:focus) {
  box-shadow: 0 0 0 var(--focus-glow-width) var(--danger-bg);
}
:where(.gk-admin-native input:disabled, .gk-admin-native select:disabled, .gk-admin-native textarea:disabled) {
  background-color: var(--primary-surface);
  color: var(--text-muted);
  cursor: not-allowed;
}
:where(.gk-admin-native input::placeholder, .gk-admin-native textarea::placeholder) { color: var(--text-placeholder); }
:where(.gk-admin-native textarea) { min-height: 5rem; resize: vertical; }
/* One height for a text input and a dropdown: --control-height (36px with a pointer, 44px on touch). */
:where(.gk-admin-native input:not([type="checkbox"], [type="radio"], [type="range"], [type="file"], [type="color"], [type="hidden"], [type="button"], [type="submit"], [type="reset"], [type="image"]), .gk-admin-native select) {
  min-height: var(--field-min-height, 0);
  line-height: var(--lh-tight);
}
/* min-tap on a gk-field raises its input or dropdown to the shared touch-target height (--gk-min-tap), as its own stylesheet says. That
 * rule lives in the field's shadow root, so the page-level control height above outranks it inside this root: restore it for every
 * field, or a field with min-tap stays 36px beside a 44px button, a filter bar's Add filter control or a date picker. At touch sizes
 * the control height is already 44px, so this only matters where a console asks for the touch height on a pointer screen. */
:where(.gk-admin-native gk-field[min-tap] > :is(input, select)) { min-height: var(--gk-min-tap); }
/* Tick boxes and radios keep the platform control, drawn in the accent, and
 * take the shared focus ring. */
:where(.gk-admin-native input[type="checkbox"], .gk-admin-native input[type="radio"]) {
  accent-color: var(--accent);
  margin: 0;
  inline-size: var(--choice-control-size, 18px);
  block-size: var(--choice-control-size, 18px);
  flex-shrink: 0;
  vertical-align: middle;
}
:where(.gk-admin-native input[type="checkbox"]:focus-visible, .gk-admin-native input[type="radio"]:focus-visible, .gk-admin-native input[type="range"]:focus-visible, .gk-admin-native input[type="file"]:focus-visible) {
  outline: var(--focus-outline-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-outline-offset);
}

/* A label that wraps a control stacks its caption above it, in gk-field's
 * caption style. A label around a tick box or radio reads inline in body text. */
:where(.gk-admin-native label:has(> input, > select, > textarea)) {
  display: grid;
  gap: var(--field-label-gap, var(--space-xs));
  align-content: start;
  color: var(--field-label-color);
  font-size: var(--field-label-font-size);
  font-weight: var(--field-label-font-weight);
  letter-spacing: var(--field-label-letter-spacing);
  text-transform: var(--field-label-text-transform);
}
/* Native forms carry a usable rhythm even without a product layout recipe.
 * Only labels stacked directly in a form or fieldset take it: inside a grid or
 * row the margin would push every label after the first below its neighbours. */
:where(.gk-admin-native :is(form, fieldset) > label:has(> input, > select, > textarea) + label:has(> input, > select, > textarea)) {
  margin-block-start: var(--space-2xl, 16px);
}

/* Loading is a region between content blocks; individual skeletons stay flush. */
:where(.admin-loading) { margin-block: var(--space-2xl, 16px); }
:where(.gk-admin-native label:has(> input[type="checkbox"], > input[type="radio"])) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--text);
  font-size: var(--fs-14);
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
}
:where(.gk-admin-native label:has(> input, > select, > textarea) > input:not([type="checkbox"], [type="radio"]),
.gk-admin-native label:has(> input, > select, > textarea) > select,
.gk-admin-native label:has(> input, > select, > textarea) > textarea) { width: 100%; }

/* Fieldset: a bordered group with a caption, like a gk-card section. */
:where(.gk-admin-native fieldset) {
  min-width: 0;
  margin: 0;
  padding: var(--space-lg) var(--space-2xl) var(--space-2xl);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}
:where(.gk-admin-native legend) {
  padding: 0 var(--space-xs);
  color: var(--field-label-color);
  font-size: var(--field-label-font-size);
  font-weight: var(--field-label-font-weight);
  letter-spacing: var(--field-label-letter-spacing);
  text-transform: var(--field-label-text-transform);
}

/* Table: gk-data-table's look on a bare <table>. The frame is the table's own
 * border with clipped corners; the head row is muted bold on the table-head
 * wash; rows are ruled by --border and the last rule is dropped. */
:where(.gk-admin-native table) {
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  font-family: var(--font-body);
  font-size: var(--fs-13);
}
/* A table inside gk-data-table sits in that component's frame, so it draws no frame of its own. */
:where(.gk-admin-native gk-data-table > table) {
  overflow: visible;
  border: 0;
  border-radius: 0;
}
/* A caption names its table for assistive technology only; a visible title
 * belongs before the table, in the page's own heading (owner decision, 19 Oct 2026). */
:where(.gk-admin-native caption) {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
:where(.gk-admin-native th) {
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border);
  background: var(--admin-table-head);
  color: var(--text-muted);
  font-weight: 700;
  text-align: left;
  white-space: nowrap;
}
:where(.gk-admin-native td) {
  padding: var(--space-md) var(--space-lg);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  vertical-align: middle;
}
:where(.gk-admin-native tbody tr:last-child > td, .gk-admin-native tbody tr:last-child > th) { border-bottom: 0; }
:where(.gk-admin-native tbody tr:hover) { background: var(--primary-surface); }

/* Disclosure: a bordered panel whose summary is the caption row. The summary is
 * at least the shared touch-target height, and the body takes the panel's
 * gutter, so a form inside a <details> needs no wrapper of its own. */
:where(.gk-admin-native details) {
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--card);
}
:where(.gk-admin-native summary) {
  display: flex;
  list-style: none;
  align-items: center;
  min-height: var(--gk-min-tap);
  padding: var(--space-md) var(--space-2xl);
  border-radius: var(--radius-card);
  color: var(--text);
  font-size: var(--fs-14);
  font-weight: 700;
  cursor: pointer;
}
:where(.gk-admin-native summary)::before {
  content: "";
  flex: none;
  width: 0.45em;
  height: 0.45em;
  margin-right: var(--space-md);
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(-45deg);
  transition: var(--transition-control);
}
:where(.gk-admin-native details[open] > summary)::before { transform: rotate(45deg); }
:where(.gk-admin-native details[open] > summary) { border-bottom: 1px solid var(--border); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
:where(.gk-admin-native summary:focus-visible) {
  outline: var(--focus-outline-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-outline-offset-inset);
}
:where(.gk-admin-native details > :not(summary)) { margin-inline: var(--space-2xl); }
:where(.gk-admin-native details[open] > summary + *) { margin-block-start: var(--space-lg); }
:where(.gk-admin-native details > :last-child:not(summary)) { margin-block-end: var(--space-2xl); }
:where(.gk-admin-native :is(fieldset, details, table) + :is(fieldset, details, table)) {
  margin-block-start: var(--space-2xl, 16px);
}

/* The shared chevron on every admin disclosure. The base above draws it inside a .gk-admin-native root; this draws the same
 * chevron for a summary the admin frame holds outside one (a console's own chrome, a dialog or a sheet), so no admin page
 * shows the browser's triangle. Only the marker: the summary keeps its own padding and frame. A summary a product styles
 * itself keeps its own marker by hiding this one (display none on its summary::before). Safari ignores list-style on a
 * summary, so its own marker is hidden too. */
:where(:is(.gk-admin-native, .admin-page, gk-modal, gk-sheet) summary)::-webkit-details-marker { display: none; }
:where(:is(.admin-page, gk-modal, gk-sheet) summary:not(.gk-admin-native summary)) {
  display: flex;
  list-style: none;
  align-items: center;
  cursor: pointer;
}
:where(:is(.admin-page, gk-modal, gk-sheet) summary:not(.gk-admin-native summary))::before {
  content: "";
  flex: none;
  width: 0.45em;
  height: 0.45em;
  margin-right: var(--space-md);
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(-45deg);
  transition: var(--transition-control);
}
:where(:is(.admin-page, gk-modal, gk-sheet) details[open] > summary:not(.gk-admin-native summary))::before { transform: rotate(45deg); }

/* Inside an already framed workspace, retain a form group's semantic name and
 * disclosure controls without accumulating another frame and body gutter. */
:where(.gk-admin-native fieldset.admin-form-section) {
  border: 0;
  border-radius: 0;
  padding: 0;
}
:where(.gk-admin-native fieldset.admin-form-section > legend) {
  padding: 0;
  margin-block-end: var(--space-md);
  color: var(--text);
  font-size: var(--fs-14);
  letter-spacing: normal;
  text-transform: none;
}
:where(.gk-admin-native details.admin-disclosure--plain) {
  border: 0;
  border-radius: 0;
  background: transparent;
}
:where(.gk-admin-native details.admin-disclosure--plain > summary) {
  padding-inline: 0;
  border-radius: 0;
}
:where(.gk-admin-native details.admin-disclosure--plain > :not(summary)) { margin-inline: 0; }

/* Action disclosure: a disclosure that opens a set of tools sits among buttons,
 * so its summary is drawn from the same --btn-* and --btn-ghost-* tokens as
 * <gk-button variant="ghost">, at touch height, with a chevron that turns when
 * open. The body flows below without a frame. */
:where(.gk-admin-native details.admin-disclosure--action) {
  min-width: 0;
  max-width: 100%;
  margin-block: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}
:where(.gk-admin-native details.admin-disclosure--action > summary) {
  display: inline-flex;
  gap: var(--space-sm);
  min-height: var(--gk-min-tap);
  padding: var(--btn-pad-y, var(--space-sm)) var(--btn-pad-x, var(--space-lg));
  border: 0;
  border-radius: var(--btn-radius, var(--radius-btn));
  color: var(--btn-ghost-color, var(--text-muted));
  font-size: var(--btn-font-size, var(--fs-13));
  font-weight: var(--btn-font-weight, 500);
  line-height: 1.2;
  white-space: nowrap;
  transition: var(--transition-control);
}
:where(.gk-admin-native details.admin-disclosure--action > summary)::before {
  order: 1;
  width: 0.4em;
  height: 0.4em;
  margin: 0 0 0.15em;
  transform: rotate(45deg);
}
:where(.gk-admin-native details.admin-disclosure--action[open] > summary)::before { margin: 0.2em 0 0; transform: rotate(225deg); }
:where(.gk-admin-native details.admin-disclosure--action > summary)::before { border-color: currentColor; }
:where(.gk-admin-native details.admin-disclosure--action > summary:hover, .gk-admin-native details.admin-disclosure--action[open] > summary) {
  background: var(--btn-ghost-bg-hover, var(--primary-surface));
  color: var(--btn-ghost-color-hover, var(--text));
}
:where(.gk-admin-native details.admin-disclosure--action[open] > summary) {
  border-bottom: 0;
  border-radius: var(--btn-radius, var(--radius-btn));
}
:where(.gk-admin-native details.admin-disclosure--action > :not(summary)) { margin-inline: 0; }
/* Where the browser can style a disclosure's body as one box, the disclosure
 * lets its summary sit in the surrounding row as a button and opens its body
 * as a tray on its own line at the end of that row (a flex row needs
 * flex-wrap; a grid row gives it the full width). Opening a tool therefore
 * never moves the buttons beside it, and the tray pushes the content below
 * rather than covering it. A tray inside a tray opens inline. */
@supports selector(::details-content) {
  :where(.gk-admin-native details.admin-disclosure--action) { display: contents; }
  :where(.gk-admin-native details.admin-disclosure--action:not([open]))::details-content { display: none; }
  :where(.gk-admin-native details.admin-disclosure--action[open])::details-content {
    order: 99;
    flex-basis: 100%;
    grid-column: 1 / -1;
    box-sizing: border-box;
    min-width: 0;
    max-width: 100%;
    margin-block: var(--space-2xs) var(--space-sm);
    padding: var(--space-lg) var(--space-2xl);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--card);
    box-shadow: var(--shadow-xs);
  }
  :where(.gk-admin-native details.admin-disclosure--action details.admin-disclosure--action[open])::details-content {
    margin: 0;
    padding: var(--space-sm) 0 0;
    border: 0;
    box-shadow: none;
  }
  :where(.gk-admin-native details.admin-disclosure--action[open] > summary + *) { margin-block-start: 0; }
  :where(.gk-admin-native details.admin-disclosure--action > :last-child:not(summary)) { margin-block-end: 0; }
}
:where(.gk-admin-native details.admin-disclosure--action[open] > summary + *) { margin-block-start: var(--space-sm); }
:where(.gk-admin-native details.admin-disclosure--action > :last-child:not(summary)) { margin-block-end: var(--space-sm); }

/* Panel: one block of a long page (a task stage, a saved-setup summary) on its
 * own card surface, so the page shows where each block starts and ends. */
:where(.gk-admin-native .admin-panel) {
  min-width: 0;
  padding: var(--space-2xl);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--card);
  box-shadow: var(--shadow-xs);
}
/* A settings row inside a panel shares the panel's gutter instead of adding its own. */
:where(.gk-admin-native .admin-panel > gk-settings-row) { padding-inline: 0; }
/* An inline link action in a table keeps its touch-height target but centres it
 * on the first line of text, so it reads on the same line as its neighbours. */
:where(.gk-admin-native :is(td, th) > gk-button[variant="link"][min-tap]) {
  display: inline-block;
  margin-block: calc((1lh - var(--gk-min-tap)) / 2);
  vertical-align: top;
}
:where(.gk-admin-native .admin-panel > :is(h2, h3, h4):first-child) {
  margin: 0 0 var(--space-lg);
  color: var(--text);
  font-size: var(--fs-16);
  font-weight: 600;
}
/* Support references sit beside a record's facts, without another panel frame. */
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) {
  min-width: 0;
  max-width: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) > summary {
  box-sizing: border-box;
  min-block-size: var(--gk-min-tap);
  padding: var(--space-xs) 0;
  border: 0;
  border-radius: 0;
  color: var(--text-muted);
  font-size: var(--fs-13);
  font-weight: 500;
  line-height: 1.4;
  white-space: normal;
  overflow-wrap: anywhere;
}
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) > summary:hover {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 3px;
}
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) > summary:focus-visible {
  outline: var(--focus-outline-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-outline-offset-inset);
}
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) > :not(summary) {
  min-width: 0;
  margin-inline: 0;
  overflow-wrap: anywhere;
}
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) > :last-child:not(summary) { margin-block-end: 0; }
:where(.gk-admin-native) details:is(.admin-reference-details, .admin-support-details, .msg-detail-support) :is(code, pre) {
  max-width: 100%;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.admin-page--narrow,
.admin-page--full { width: 100%; min-width: 0; }
/* A page stops at --admin-content-max at the left edge (the shell panel does this by default; the class is for a page drawn outside a
 * shell panel). Forms and settings stop at the narrower --admin-form-max. .admin-page--full opts a wide table or the Tee Sheet out. */
.admin-page--list { width: 100%; min-width: 0; max-width: var(--admin-content-max); }
.admin-page--form, .admin-settings-index, .admin-settings-section { max-width: var(--admin-form-max); }
.admin-page--full { max-width: none; }
.admin-support-details { margin-block: var(--space-lg); }
.admin-support-details summary { cursor: pointer; }
.admin-support-details dl { margin: 0; }
.admin-support-details dd { margin-inline-start: 0; overflow-wrap: anywhere; white-space: pre-wrap; }

/* ───────────────────────────────────────────────────────────────
 * Recipe: settings (owner standard, 19 Oct 2026; Trybe study 6b in the audit)
 *
 * The index is one page of category cards: an icon and a title over a plain list of noun links, with
 * no descriptions (the Reports hub uses it too). A settings page is a set of sections. A section is NOT
 * a card: it is a three-column grid with a 64px gap, the help in the first column (a heading, at most
 * two sentences, an optional "View guide") and the controls across the other two. Sections are ruled
 * apart with a 1px line and 40px of space either side. Below 1100px the help stacks above the controls.
 * Labels sit above fields (14px weight 500, a 4px gap), help under the field, controls are 36px with
 * the 6px radius, and a field is as wide as a fraction of the control column (.admin-field-w-*).
 * Tenant Admin, Super Admin and the product consoles all use these classes; they replace the settings
 * finder, pointer rows and grab-bag groups. Type, spacing and radius read the admin tokens.
 *
 *   <section class="admin-settings-section">
 *     <header class="admin-settings-section__aside">
 *       <h2 class="admin-settings-section__title">Opening hours</h2>
 *       <p class="admin-settings-section__help">Tell us when you are open.</p>
 *       <gk-button variant="alt" href="/guides/opening-hours">View guide</gk-button>
 *     </header>
 *     <div class="admin-settings-section__body">
 *       <gk-field label="Name" required required-text><input></gk-field>
 *       <gk-field class="admin-field-w-50" label="Postcode"><input></gk-field>
 *       <div class="admin-toggle-row">
 *         <gk-toggle compact aria-labelledby="remind"></gk-toggle>
 *         <span class="admin-toggle-row__label" id="remind">Send reminders</span>
 *         <p class="admin-toggle-row__help">When enabled, guests get an email the day before.</p>
 *         <div class="admin-toggle-row__dependent"><gk-field label="Days before"><select>...</select></gk-field></div>
 *       </div>
 *       <footer class="admin-settings-section__save"><gk-button variant="alt">Save opening hours</gk-button></footer>
 *     </div>
 *   </section>
 * ─────────────────────────────────────────────────────────────── */
.admin-settings-index {
  display: grid;
  /* At most three columns; a card is never narrower than 18rem, so a narrow pane gets fewer. */
  grid-template-columns: repeat(auto-fill, minmax(max(18rem, calc((100% - 2 * var(--space-2xl)) / 3)), 1fr));
  gap: var(--space-2xl);
  /* A card is as tall as its own links: two links beside a six-link card are not stretched to match it. */
  align-items: start;
  min-width: 0;
  margin: 0;
  padding: 0;
}
.admin-settings-card {
  min-width: 0;
  padding: var(--space-2xl);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-surface);
  background: var(--card);
}
.admin-settings-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin: 0 0 var(--space-sm);
  color: var(--text);
  font-size: var(--fs-panel);
  font-weight: var(--fw-title);
  line-height: var(--lh-snug);
}
.admin-settings-card__title > :is(gk-icon, svg) { flex: none; color: var(--text-muted); --icon-size: var(--fs-20); }
.admin-settings-card__links { display: grid; margin: 0; padding: 0; list-style: none; }
.admin-settings-card__links a {
  display: flex;
  align-items: center;
  min-height: var(--control-height);
  /* The hover wash extends past the text, but the text stays on the card's own left edge. */
  margin-inline: calc(-1 * var(--space-sm));
  padding-inline: var(--space-sm);
  border-radius: var(--radius-control);
  color: var(--text);
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  line-height: var(--lh-snug);
  text-decoration: none;
  transition: var(--transition-control);
}
.admin-settings-card__links a:hover { background: var(--primary-surface); }
.admin-settings-card__links a:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }
.admin-settings-card__links a[aria-current="page"] { background: var(--primary-surface); }

/* The flat index (owner, 19 Oct): sections ruled apart like any settings page, no card. Each link is a plain name over one line saying
 * what the page controls; the whole row is the link. The hover wash reaches past the text, which stays on the column's left edge. */
.admin-settings-section__body.admin-settings-links { gap: var(--space-2xs); margin: 0; padding: 0; list-style: none; }
.admin-settings-links a {
  display: grid;
  gap: var(--space-3xs);
  margin-inline: calc(-1 * var(--space-lg));
  padding: var(--space-lg);
  border-radius: var(--radius-control);
  color: var(--text);
  text-decoration: none;
  transition: var(--transition-control);
}
.admin-settings-links a:hover { background: var(--primary-surface); }
.admin-settings-links a:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }
.admin-settings-links__name { color: var(--link); font-size: var(--fs-body); font-weight: var(--fw-ui); line-height: var(--lh-snug); }
/* On the index the first name shares a line with its group heading: the row's own padding is pulled back up. */
.settings-index-sections .admin-settings-section__body.admin-settings-links,
.admin-settings-index .admin-settings-section__body.admin-settings-links { margin-top: calc(-1 * var(--space-sm)); }
.admin-settings-links a:hover .admin-settings-links__name { color: var(--link-hover); text-decoration: underline; text-underline-offset: 2px; }
.admin-settings-links__text { color: var(--text-muted); font-size: var(--fs-body); line-height: var(--lh-snug); }
/* A link row that points at another page ends in a chevron (`<span class="admin-settings-links__go" aria-hidden="true">›</span>`): the name over an
 * optional muted line on the left, the chevron centred at the end. Rows without one are unchanged. */
.admin-settings-links a:has(> .admin-settings-links__go) { grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--space-lg); row-gap: 0; align-items: center; }
.admin-settings-links a:has(> .admin-settings-links__go) > :not(.admin-settings-links__go) { grid-column: 1; }
.admin-settings-links a:has(> .admin-settings-links__go) > .admin-settings-links__text { margin-top: var(--space-3xs); }
.admin-settings-links__go { grid-column: 2; grid-row: 1 / span 2; color: var(--text-muted); font-size: var(--fs-16); line-height: 1; }
/* A page that is only a list of links (Apps): the same rows with no section around them, kept to a readable width. */
.admin-settings-links--page { display: grid; gap: var(--space-2xs); max-width: min(100%, 44rem); margin: 0; padding: 0; list-style: none; }
/* A row that needs a choice before it can open (an app that works on one venue or organisation): the name and line as on any row, but not a
 * link, with the `gk-select` and its Open button beneath in one row. The text stays on the column's left edge like the link rows. */
.admin-settings-links__pick { display: grid; gap: var(--space-3xs); padding-block: var(--space-lg); }
.admin-settings-links__pick > .admin-settings-links__name { color: var(--text); }
.admin-settings-links__choice { display: flex; align-items: center; gap: var(--space-sm); margin-top: var(--space-xs); }
.admin-settings-links__choice > gk-select { flex: 1 1 10rem; max-width: 24rem; min-width: 0; }

.admin-settings-section {
  --field-label-font-size: var(--fs-body);
  --field-label-font-weight: var(--fw-ui);
  --field-label-gap: var(--space-2xs);
  --field-hint-font-size: var(--fs-body);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--space-7xl);
  row-gap: var(--space-2xl);
  align-items: start;
  min-width: 0;
}
/* Sections are ruled apart: 40px, a 1px line, 40px. */
.admin-settings-section + .admin-settings-section, .admin-settings-group + .admin-settings-section { margin-top: 2.5rem; padding-top: 2.5rem; border-top: var(--hairline) solid var(--border); }
/* Sections that one Save covers share an .admin-settings-group, which takes no space of its own; the first is ruled off like any other. */
.admin-settings-group { display: contents; }
:is(.admin-settings-section, .admin-settings-group) + .admin-settings-group > .admin-settings-section:first-child { margin-top: 2.5rem; padding-top: 2.5rem; border-top: var(--hairline) solid var(--border); }
.admin-settings-section__aside { grid-column: 1; display: grid; justify-items: start; gap: var(--space-sm); min-width: 0; }
.admin-settings-section__title { margin: 0; color: var(--text); font-size: var(--fs-section); font-weight: var(--fw-title); line-height: 1.4; }
.admin-settings-section__help { margin: 0; color: var(--text-muted); font-size: var(--fs-body); line-height: calc(10 / 7); }
.admin-settings-section__aside > :is(gk-button, a, button) { margin-top: var(--space-xs); }
.admin-settings-section__body { grid-column: 2 / span 2; display: grid; gap: var(--space-2xl); min-width: 0; }
/* A section that cannot load draws the recovery panel across its column, so it shares the right edge of the blocks beside it. */
.admin-settings-section__body gk-alert[variant="recovery"] { max-width: none; }
/* The fields of a settings edit dialog (a gk-modal size="xs", one to three fields): 16px apart, like the fields of a section. */
.admin-modal-fields { display: grid; gap: var(--space-2xl); min-width: 0; padding-block-end: var(--space-lg); }
/* One Save per section (or per page), at the end of the controls, right aligned. */
.admin-settings-section__save { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-sm); }
/* Tablet landscape (901 to 1100px): two columns, the help beside the controls, a narrower gap. Tablet portrait and phone stack. */
@media (max-width: 1100px) {
  .admin-settings-section { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); column-gap: var(--space-4xl); }
  .admin-settings-section__aside { grid-column: 1; }
  .admin-settings-section__body { grid-column: 2; }
}
@media (max-width: 900px) {
  .admin-settings-section { grid-template-columns: minmax(0, 1fr); }
  .admin-settings-section__aside, .admin-settings-section__body { grid-column: 1; }
}
/* Two short fields side by side (City and County). */
.admin-settings-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2xl); min-width: 0; }
@media (max-width: 720px) { .admin-settings-pair { grid-template-columns: minmax(0, 1fr); } }
/* A field is as wide as its answer, as a fraction of the control column: names, addresses and long
 * selects 100; codes 75; postcodes and URLs of fixed shape 50; short ids 40; small selects and time
 * parts 25; percentages and counts 15. */
.admin-field-w-100 { width: 100%; max-width: 100%; }
.admin-field-w-75 { width: 75%; max-width: 100%; }
.admin-field-w-50 { width: 50%; max-width: 100%; }
.admin-field-w-40 { width: 40%; max-width: 100%; }
.admin-field-w-25 { width: 25%; max-width: 100%; }
.admin-field-w-15 { width: 15%; max-width: 100%; }
@media (max-width: 720px) {
  .admin-field-w-75, .admin-field-w-50, .admin-field-w-40 { width: 100%; }
  .admin-field-w-25, .admin-field-w-15 { width: 50%; }
}

/* Toggle row: a 36 x 20px switch, its label to the right, help under the label. Dependent fields sit
 * under the label, indented, and show only while the switch is on. */
.admin-toggle-row { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--space-lg); row-gap: var(--space-2xs); align-items: start; min-width: 0; }
/* The switch is the label's first line tall, so it stays beside the first line however far a long label wraps. */
.admin-toggle-row__label { color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); line-height: calc(10 / 7); overflow-wrap: anywhere; }
.admin-toggle-row > :is(gk-toggle, input[type="checkbox"]) { align-self: start; }
.admin-toggle-row__help { grid-column: 2; margin: 0; color: var(--text-muted); font-size: var(--fs-body); line-height: calc(10 / 7); }
.admin-toggle-row__dependent { grid-column: 2; display: grid; gap: var(--space-lg); margin-block-start: var(--space-lg); margin-inline-start: var(--space-sm); }
.admin-toggle-row:not(:has(> gk-toggle[checked], > input:checked)) > .admin-toggle-row__dependent { display: none; }

/* Choice cards: labelled native choices with descriptions aligned beneath the title.
 * Use the stacked layout for independent options or longer explanations. */
.admin-choice-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); gap: var(--space-lg); min-width: 0; margin: 0; padding: 0; border: 0; }
.admin-choice-cards--stacked { grid-template-columns: minmax(0, 1fr); }
.admin-choice-cards > legend { padding: 0; margin-block-end: var(--space-sm); }
.admin-choice-cards__hint { grid-column: 1 / -1; margin: 0; color: var(--text-muted); font-size: var(--fs-body); line-height: var(--lh-snug); }
.admin-choice-card {
  display: grid;
  margin: 0;
  grid-template-columns: auto minmax(0, 1fr);
  align-content: start;
  align-items: start;
  min-width: 0;
  min-height: var(--gk-min-tap, 44px);
  box-sizing: border-box;
  gap: var(--space-2xs) var(--space-md);
  padding: var(--space-lg);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-surface);
  background: var(--card);
  cursor: pointer;
}
.admin-choice-card > input { margin: calc((1.5em - var(--choice-control-size, 18px)) / 2) 0 0; font-size: var(--fs-body); }
.admin-choice-card__title { min-width: 0; color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); line-height: 1.5; overflow-wrap: anywhere; }
.admin-choice-card__text { min-width: 0; grid-column: 2; color: var(--text-muted); font-size: var(--fs-body); font-weight: 400; line-height: var(--lh-snug); overflow-wrap: anywhere; }
.admin-choice-card:hover:not(:has(> input:disabled, > input:checked)) { border-color: var(--primary-muted); }
.admin-choice-card:has(> input:checked) { border-color: var(--accent); box-shadow: 0 0 0 var(--hairline) var(--accent); }
.admin-choice-card:has(> input:focus-visible) { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset); }
.admin-choice-card:has(> input:disabled) { cursor: not-allowed; background: var(--primary-surface); }
.admin-choice-card:has(> input:disabled) .admin-choice-card__title { color: var(--text-muted); }

/* Checkbox rows: independent choices that need no card. Each is an admin-toggle-row whose control is a native checkbox (its label, then one muted
 * line), and a hairline rules the rows apart. The whole label is the target. */
.admin-check-rows { display: grid; min-width: 0; margin: 0; padding: 0; border: 0; }
.admin-check-rows > legend { padding: 0; margin-block-end: var(--space-sm); color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); }
.admin-check-rows__hint { margin: 0 0 var(--space-sm); color: var(--text-muted); font-size: var(--fs-body); line-height: var(--lh-snug); }
.admin-check-rows > .admin-toggle-row { padding-block: var(--space-lg); cursor: pointer; }
.admin-check-rows > .admin-toggle-row + .admin-toggle-row { border-top: var(--hairline) solid var(--border); }
.admin-check-rows > .admin-toggle-row > input[type="checkbox"] { margin: calc((calc(10 / 7) * 1em - var(--choice-control-size, 18px)) / 2) 0 0; font-size: var(--fs-body); }
.admin-check-rows > .admin-toggle-row:has(> input:disabled) { cursor: not-allowed; }
.admin-check-rows > .admin-toggle-row:has(> input:disabled) .admin-toggle-row__label { color: var(--text-muted); }
.admin-check-rows > .admin-toggle-row:has(> input:focus-visible) { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }

/* Save bar: the one Save of a page or section, last in the page container (or the section), so it spans the whole content column and no
 * content shows beside it. It exists only while there are unsaved changes (principle 25): hidden unless it, or an ancestor the helper
 * marks, carries data-dirty, so nothing ever prints "No changes to save". Solid, in the page background (a panel can set --admin-save-bar-bg), a
 * top rule and nothing else; it sticks to the foot of the screen. Where a console cannot place it at the foot of its content column,
 * it adds .admin-save-bar--static, which keeps it at the end of the content and never floats. */
.admin-save-bar {
  --_bar-bg: var(--admin-save-bar-bg, var(--bg));
  position: sticky;
  inset-block-end: 0;
  z-index: 10;
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-sm);
  box-sizing: border-box;
  /* A panel that pads its content (the phone panel) says by how much, and the bar bleeds across that padding to the panel's edges. */
  margin-inline: calc(-1 * var(--admin-save-bar-bleed, 0rem));
  margin-block-start: var(--space-2xl);
  padding: var(--space-lg) var(--space-2xl);
  padding-block-end: max(var(--space-lg), env(safe-area-inset-bottom));
  border-top: var(--hairline) solid var(--border);
  background: var(--_bar-bg);
}
.admin-save-bar--static { position: static; }
.admin-save-bar:not([data-dirty]):not(:where([data-dirty] *)) { display: none; }

/* Settings list: one bordered card of rows (a search on top, a footer with the total and pager), and
 * under it, right aligned, "Reorder" and "Add <noun>" with the "Archived <noun>" toggle at the start.
 * A row is a name with at most two pills, one muted line of facts, and a kebab menu. */
.admin-settings-list { min-width: 0; overflow: hidden; border: var(--hairline) solid var(--border); border-radius: var(--radius-surface); background: var(--card); }
.admin-settings-list__search { padding: var(--space-2xl); border-bottom: var(--hairline) solid var(--border); }
.admin-settings-list__rows { margin: 0; padding: 0; list-style: none; }
.admin-settings-list__rows > li { display: flex; align-items: center; gap: var(--space-2xl); min-width: 0; padding: var(--space-2xl); }
.admin-settings-list__rows > li + li { border-top: var(--hairline) solid var(--border); }
.admin-settings-list__main { display: grid; flex: 1 1 auto; gap: var(--space-2xs); min-width: 0; }
.admin-settings-list__name { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); }
.admin-settings-list__facts { margin: 0; color: var(--text-muted); font-size: var(--fs-body); overflow-wrap: anywhere; }
.admin-settings-list__footer { --pagination-font-size: var(--fs-meta); padding: var(--space-lg) var(--space-2xl); border-top: var(--hairline) solid var(--border); color: var(--text-muted); font-size: var(--fs-meta); }
.admin-settings-list__below { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-sm); margin-top: var(--space-lg); }
.admin-settings-list__below > :first-child:not(:last-child) { margin-inline-end: auto; }
/* Inside a section body the body's own gap is the distance under the card, so Add does not add its margin on top. */
.admin-settings-section__body > .admin-settings-list__below { margin-top: 0; }
/* State (a failed load, an unavailable service, an empty result): ONE sentence and, where trying again can help, one Retry, drawn once
 * where the content would be. Never a banner plus a card plus tiles saying the same thing: the tiles show a dash (gk-metric-card
 * unavailable) and this sentence says why. data-state is failed, unavailable or empty; the layout is the same. */
.admin-state { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-lg); min-width: 0; padding: var(--space-4xl) var(--space-2xl); color: var(--text-muted); font-size: var(--fs-body); text-align: center; }
.admin-state__text { max-width: 40rem; margin: 0; line-height: calc(10 / 7); overflow-wrap: anywhere; }
.admin-state[data-state="failed"] .admin-state__text, .admin-state[data-state="unavailable"] .admin-state__text { color: var(--text); }
/* Empty: a dashed box with one sentence that names the thing and ends "yet"; Add sits under it. */
.admin-empty-box {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--space-sm);
  min-height: 9.5rem;
  padding: var(--space-2xl);
  border: var(--hairline) dashed var(--border-strong);
  border-radius: var(--radius-surface);
  color: var(--text-muted);
  font-size: var(--fs-panel);
  font-weight: var(--fw-ui);
  text-align: center;
}
/* One line of help under the sentence, then the one action that fills it, inside the box. */
.admin-empty-box__help { max-width: 40rem; margin: 0; font-size: var(--fs-body); font-weight: 400; line-height: calc(10 / 7); }
/* A short empty list (the one sentence and nothing else): about 80px high, with its Add under it. */
.admin-empty-box--compact { min-height: 5rem; padding-block: var(--space-lg); }
/* Status block: a 12px dot, a 14px weight 500 label, one muted line saying what to do next, and an action. */
.admin-status-block { --dot: var(--text-muted); display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--space-lg); row-gap: var(--space-2xs); align-items: center; min-width: 0; }
.admin-status-block[data-tone="success"] { --dot: var(--success-solid); }
.admin-status-block[data-tone="caution"] { --dot: var(--caution-solid); }
.admin-status-block[data-tone="danger"] { --dot: var(--danger-solid); }
.admin-status-block[data-tone="info"] { --dot: var(--info-solid); }
.admin-status-block__dot { inline-size: 0.75rem; block-size: 0.75rem; border-radius: var(--radius-pill); background: var(--dot); }
.admin-status-block__label { color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); }
.admin-status-block__next { grid-column: 2; margin: 0; color: var(--text-muted); font-size: var(--fs-body); }
.admin-status-block__action { grid-column: 3; grid-row: 1 / span 2; }

/* Dock: a panel docked to the foot (or the right edge) of the content column, over the lower part of the page, not a modal: there is no
 * veil, the page behind stays usable and is padded so nothing is covered. On a phone it takes the whole screen. It is for an editor or
 * a detail that belongs beside the thing it edits (the Tee Sheet's booking editor). Solid card, one rule on the edge it docks from.
 * GkAdminShellPieces.bindDock opens and closes it, moves focus in and back, and tells the page how much room it takes. */
.admin-dock {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  background: var(--card);
  color: var(--text);
  inset-inline: var(--admin-shell-rail-reserve, 0rem) 0;
  inset-block-end: 0;
  max-block-size: 60dvh;
  border-top: var(--hairline) solid var(--border);
}
.admin-dock[data-side="right"] {
  inset-block: var(--admin-header-h) 0;
  inset-inline: auto 0;
  inline-size: min(28rem, 100%);
  max-block-size: none;
  border-top: 0;
  border-inline-start: var(--hairline) solid var(--border);
}
.admin-dock[hidden] { display: none; }
.admin-dock__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); padding: var(--space-md) var(--space-2xl); border-bottom: var(--hairline) solid var(--border); }
.admin-dock__title { margin: 0; color: var(--text); font-size: var(--fs-panel); font-weight: var(--fw-title); line-height: var(--lh-snug); }
.admin-dock__body { flex: 1 1 auto; min-block-size: 0; overflow: auto; padding: var(--space-2xl); }
.admin-dock__foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-sm); padding: var(--space-lg) var(--space-2xl); border-top: var(--hairline) solid var(--border); }

/* Date control: one control for a day. The date is a large day number with its weekday and month, and tapping it opens the calendar straight away (no
 * "Choose date" step); previous, next and Today sit beside it. GkAdminShellPieces.bindDateNav draws the label and runs the calendar. */
.admin-date-nav { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); min-width: 0; }
.admin-date-nav__date { display: inline-flex; align-items: center; gap: var(--space-md); min-block-size: var(--control-height); padding: 0 var(--space-md); border: 0; border-radius: var(--radius-control); background: transparent; color: var(--text); font: inherit; text-align: start; cursor: pointer; }
.admin-date-nav__date:hover { background: var(--primary-surface); }
.admin-date-nav__date:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset); }
.admin-date-nav__day { font-size: var(--fs-page-title); font-weight: var(--fw-title); line-height: 1; font-variant-numeric: tabular-nums; }
.admin-date-nav__meta { display: grid; line-height: var(--lh-snug); }
.admin-date-nav__weekday { font-size: var(--fs-body); font-weight: var(--fw-ui); }
.admin-date-nav__month { color: var(--text-muted); font-size: var(--fs-caption); }
.admin-date-nav__popover { position: absolute; z-index: 30; inset-block-start: calc(100% + var(--space-2xs)); inset-inline-start: 0; border: var(--hairline) solid var(--border); border-radius: var(--radius-surface); background: var(--card); box-shadow: var(--elevation-2); }
/* In the top layer (see bindDateNav) the browser's popover frame is replaced by this one. */
.admin-date-nav__popover[popover] { margin: 0; padding: 0; overflow: visible; color: var(--text); }
.admin-date-nav__popover[hidden] { display: none; }

/* Reorder (principle 24): drag is never the only way. Every row has Move up and Move down buttons, which are 44px targets on touch
 * (--control-height), and GkAdminShellPieces.bindReorder moves the row, keeps focus on the button, and says where it went. */
.admin-reorder { display: grid; margin: 0; padding: 0; list-style: none; border: var(--hairline) solid var(--border); border-radius: var(--radius-surface); background: var(--card); }
.admin-reorder__item { display: flex; align-items: center; justify-content: space-between; gap: var(--space-lg); min-width: 0; padding: var(--space-md) var(--space-lg); }
.admin-reorder__item + .admin-reorder__item { border-top: var(--hairline) solid var(--border); }
.admin-reorder__name { min-width: 0; color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); overflow-wrap: anywhere; }
.admin-reorder__moves { display: flex; flex: none; gap: var(--space-xs); }
.admin-reorder__moves > button:disabled { opacity: 0.4; cursor: default; }

/* A setting asks the user's question (principle 28): the section title is the plain question, its fields follow, then a preview and
 * Save. The current condition is one small line under the question, never conditions spelled out in system terms. */
.admin-settings-section__condition { margin: 0; color: var(--text-muted); font-size: var(--fs-caption); line-height: calc(10 / 7); }
.admin-settings-section__preview { min-width: 0; padding: var(--space-lg); border: var(--hairline) dashed var(--border); border-radius: var(--radius-surface); background: var(--bg); }

/* Dividers do not stack (principle 29): a rule never sits directly after another, and a section's top rule is the only one between sections. */
:is(hr, .admin-divider) + :is(hr, .admin-divider) { display: none; }
.admin-settings-section + hr, hr + .admin-settings-section { border-top: 0; margin-top: 0; padding-top: 0; }

/* Quiet status line (principle 19): steady state ("Customer booking live") is a muted line with a small dot, not a headline, a pill
 * or an alarm. Use .admin-status-block only where there is a next step and an action. */
.admin-status-line { --dot: var(--text-muted); display: flex; align-items: center; gap: var(--space-sm); margin: 0; color: var(--text-muted); font-size: var(--fs-body); }
.admin-status-line::before { content: ""; flex: none; inline-size: var(--space-sm); block-size: var(--space-sm); border-radius: var(--radius-pill); background: var(--dot); }
.admin-status-line[data-tone="success"] { --dot: var(--success-solid); }
.admin-status-line[data-tone="caution"] { --dot: var(--caution-solid); }
.admin-status-line[data-tone="danger"] { --dot: var(--danger-solid); }

/* Attention item (principles 15 and 18): it leads with the fact ("1 new unassigned group enquiry"), the whole item is the one link to
 * where it is handled, and its one action is a plain label, not a second button. No lead-in heading, no instruction. */
.admin-attention-list { display: grid; margin: 0; padding: 0; list-style: none; }
.admin-attention { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-sm) var(--space-2xl); min-block-size: var(--control-height); padding: var(--space-md) var(--space-lg); border-radius: var(--radius-control); color: var(--text); text-decoration: none; }
.admin-attention:hover { background: var(--primary-surface); }
.admin-attention:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }
.admin-attention__fact { min-width: 0; font-size: var(--fs-body); font-weight: var(--fw-ui); overflow-wrap: anywhere; }
.admin-attention__action { color: var(--link); font-size: var(--fs-body); font-weight: var(--fw-ui); white-space: nowrap; }
.admin-attention:hover .admin-attention__action { color: var(--link-hover); text-decoration: underline; }

/* Nothing is not shown (principle 16). A section, block or list marked data-hide-when-empty disappears when it has no content: no
 * children, or only children that are hidden, marked data-empty or an unavailable tile. A count marked data-hide-zero disappears at
 * data-count="0". The page needs no script to leave an empty section out. */
[data-hide-when-empty]:empty,
[data-hide-when-empty]:not(:has(> :not([hidden], [data-empty], [unavailable]))),
[data-hide-zero][data-count="0"] { display: none; }

/* ───────────────────────────────────────────────────────────────
 * Recipe: record layout (owner standard, 19 Oct 2026)
 *
 * A booking, customer, enquiry or member opens as a record: a back link, a tab row (one tab per
 * concern, nothing buried in a long form), a summary card on the left (about a third) and the
 * work area on the right. The summary holds a key and value list, a money breakdown, a person
 * card and the state actions stacked full width. The work area has a heading with a toolbar and a
 * row of small "Add ..." actions over the record's items. One model for every record; the pane
 * switching of gk-record-layout (a directory beside an editor) is a different job.
 *
 *   <div class="admin-record">
 *     <a class="admin-record__back" href="/orders">Back to orders</a>
 *     <nav class="admin-record__tabs" aria-label="Order">
 *       <a href="#overview" aria-current="page">Overview</a><a href="#payments">Payments</a>
 *     </nav>
 *     <div class="admin-record__body">
 *       <aside class="admin-record__summary" aria-label="Order summary">
 *         <dl class="admin-record__facts">
 *           <div><dt>Reference</dt><dd>ABXDKN</dd></div>
 *           <div><dt>Status</dt><dd><gk-badge variant="status" tone="success">Confirmed</gk-badge></dd></div>
 *         </dl>
 *         <dl class="admin-record__facts admin-record__money">
 *           <div><dt>Subtotal</dt><dd>£66.00</dd></div>
 *           <div class="admin-record__total"><dt>Total</dt><dd>£66.00</dd></div>
 *         </dl>
 *         <div class="admin-record__party"><span class="admin-avatar">AB</span><div>...</div></div>
 *         <div class="admin-record__actions"><gk-button variant="alt">Cancel order</gk-button>...</div>
 *       </aside>
 *       <section class="admin-record__work" aria-labelledby="items">
 *         <header class="admin-record__toolbar"><h2 id="items">Items</h2><div>...buttons...</div></header>
 *         <div class="admin-record__adds"><gk-button variant="alt" size="sm">Add session</gk-button></div>
 *         <article class="admin-record__item">...</article>
 *       </section>
 *     </div>
 *   </div>
 *
 * Use gk-tabs instead of the nav when the tabs switch panels on the page. Every gap is 16px.
 * ─────────────────────────────────────────────────────────────── */
.admin-record { display: grid; gap: var(--space-2xl); min-width: 0; }
.admin-record__back {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  min-height: var(--control-height);
  color: var(--text-muted);
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  text-decoration: none;
}
.admin-record__back:hover { color: var(--text); }
.admin-record__back::before { content: "\2039"; margin-inline-end: var(--space-xs); font-size: var(--fs-section); line-height: 1; }
.admin-record__tabs {
  display: flex;
  gap: var(--space-2xs);
  min-width: 0;
  overflow-x: auto;
  border-bottom: var(--hairline) solid var(--border);
}
.admin-record__tabs > :is(a, button) {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  min-height: var(--gk-min-tap);
  padding: 0 var(--space-lg);
  border: 0;
  border-bottom: var(--space-3xs) solid transparent;
  margin-bottom: calc(-1 * var(--hairline));
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.admin-record__tabs > :is(a, button):hover { color: var(--text); }
.admin-record__tabs > :is([aria-current="page"], [aria-selected="true"]) { border-bottom-color: var(--primary); color: var(--text); }
.admin-record__tabs > :is(a, button):focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }
.admin-record__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: var(--space-2xl);
  align-items: start;
  min-width: 0;
}
.admin-record__summary,
.admin-record__item {
  min-width: 0;
  padding: var(--space-2xl);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-surface);
  background: var(--card);
}
.admin-record__facts { display: grid; gap: var(--space-sm); margin: 0; }
.admin-record__facts > div { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2xl); min-width: 0; }
.admin-record__facts dt { color: var(--text-muted); font-size: var(--fs-body); }
.admin-record__facts dd { margin: 0; min-width: 0; color: var(--text); font-size: var(--fs-body); font-weight: var(--fw-ui); text-align: end; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
.admin-record__money > .admin-record__total { padding-top: var(--space-sm); border-top: var(--hairline) solid var(--border); }
.admin-record__money > .admin-record__total :is(dt, dd) { color: var(--text); font-weight: var(--fw-title); }
.admin-record__party { display: flex; align-items: center; gap: var(--space-lg); min-width: 0; }
.admin-record__party > :last-child { min-width: 0; overflow-wrap: anywhere; font-size: var(--fs-body); }
.admin-record__actions { display: grid; gap: var(--space-sm); }
/* The summary's blocks (facts, money, person, actions) are ruled apart, 16px either side of the rule. */
.admin-record__summary > * + * { margin-top: var(--space-2xl); padding-top: var(--space-2xl); border-top: var(--hairline) solid var(--border); }
.admin-record__work { display: grid; gap: var(--space-2xl); min-width: 0; }
.admin-record__toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-lg) var(--space-2xl); min-width: 0; }
.admin-record__toolbar > h2 { margin: 0; color: var(--text); font-size: var(--fs-section); font-weight: var(--fw-title); line-height: var(--lh-snug); }
.admin-record__toolbar > :not(h2) { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }
.admin-record__adds { display: flex; flex-wrap: wrap; gap: var(--space-sm); }
.admin-record__adds:empty { display: none; }
/* Column captions are sentence case, 12px, above a 14px value; the grid fits as many as the card holds. */
.admin-record__fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: var(--space-2xl); margin: 0; }
.admin-record__fields dt { margin-bottom: var(--space-2xs); color: var(--text-muted); font-size: var(--fs-meta); font-weight: var(--fw-ui); }
.admin-record__fields dd { margin: 0; color: var(--text); font-size: var(--fs-body); overflow-wrap: anywhere; }
@media (max-width: 900px) {
  .admin-record__body { grid-template-columns: minmax(0, 1fr); }
}

/* Avatar: a person's initials in a circle, at the start of a list row or a person card. */
.admin-avatar {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-pill);
  background: var(--primary-surface);
  color: var(--text);
  font-size: var(--fs-meta);
  font-weight: var(--fw-title);
  line-height: 1;
}

/* ───────────────────────────────────────────────────────────────
 * Recipes: shell pieces (owner standard, 19 Oct 2026). Recipes only: the shells adopt them later.
 *
 * Top bar, 55px: a breadcrumb at the start; at the end a global search trigger with its Ctrl K hint,
 * a "+" quick-create menu, then icon buttons (notifications) and the avatar. Sidebar: about seven
 * task sections with two or three children each, items 36px tall, 14px weight 500, 6px radius, the
 * current one tinted, and Settings pinned at the bottom apart from daily work. Colours come from the
 * existing --admin-nav-* tokens; only shape, type and spacing are new.
 *
 *   <header class="admin-topbar">
 *     <nav aria-label="Breadcrumb"><ol class="admin-breadcrumb">
 *       <li><a href="/settings">Settings</a></li><li aria-current="page">Opening hours</li>
 *     </ol></nav>
 *     <div class="admin-topbar__end">
 *       <button type="button" class="admin-search-trigger" aria-keyshortcuts="Control+K">
 *         <svg aria-hidden="true">...</svg><span>Search</span><kbd>Ctrl K</kbd>
 *       </button>
 *       <div class="admin-quick-create">
 *         <button type="button" class="admin-quick-create__button" aria-haspopup="menu" aria-expanded="false" aria-label="Create">+</button>
 *         <div class="admin-quick-create__menu" role="menu" hidden>
 *           <a role="menuitem" class="admin-quick-create__item" href="/orders/new">New order</a>
 *         </div>
 *       </div>
 *       <button type="button" class="admin-topbar__icon-button" aria-label="Notifications">...</button>
 *       <span class="admin-avatar">SG</span>
 *     </div>
 *   </header>
 *   <nav class="admin-sidebar" aria-label="Main">
 *     <div class="admin-sidebar__main">
 *       <a class="admin-sidebar__item" href="/home" aria-current="page">Home</a>
 *       <button class="admin-sidebar__item" aria-expanded="true">Orders</button>
 *       <div class="admin-sidebar__children"><a class="admin-sidebar__item" href="/orders">All orders</a></div>
 *     </div>
 *     <div class="admin-sidebar__footer"><a class="admin-sidebar__item" href="/settings">Settings</a></div>
 *   </nav>
 *
 * admin-shell-pieces.js binds the quick-create menu (open, arrows, Escape, outside click) and the
 * Ctrl K shortcut.
 * ─────────────────────────────────────────────────────────────── */
.admin-topbar {
  display: flex;
  align-items: center;
  gap: var(--space-2xl);
  box-sizing: border-box;
  height: var(--admin-topbar-h);
  padding-inline: var(--space-2xl);
  border-bottom: var(--hairline) solid var(--border);
  background: var(--card);
  color: var(--text);
}
.admin-topbar__end { display: flex; align-items: center; gap: var(--space-sm); margin-inline-start: auto; min-width: 0; }
.admin-breadcrumb { display: flex; align-items: center; min-width: 0; margin: 0; padding: 0; list-style: none; font-size: var(--fs-body); }
.admin-breadcrumb > li { min-width: 0; overflow: hidden; color: var(--text); font-weight: var(--fw-ui); text-overflow: ellipsis; white-space: nowrap; }
.admin-breadcrumb > li + li::before { content: "/"; margin-inline: var(--space-sm); color: var(--text-muted); font-weight: 400; }
.admin-breadcrumb a { color: var(--text-muted); text-decoration: none; }
.admin-breadcrumb a:hover { color: var(--text); text-decoration: underline; }
.admin-search-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  box-sizing: border-box;
  inline-size: 16rem;
  max-inline-size: 100%;
  min-height: var(--control-height);
  padding: 0 var(--space-lg);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-control);
  background: var(--card);
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  text-align: start;
  cursor: pointer;
}
.admin-search-trigger:hover { border-color: var(--primary-muted); }
.admin-search-trigger > svg { flex: none; inline-size: var(--space-2xl); block-size: var(--space-2xl); }
.admin-search-trigger > kbd {
  margin-inline-start: auto;
  padding: 0 var(--space-xs);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--fs-meta);
  line-height: var(--lh-normal);
}
.admin-quick-create, .admin-menu-anchor { position: relative; }
.admin-quick-create__button,
.admin-topbar__icon-button,
.admin-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  inline-size: var(--control-height);
  block-size: var(--control-height);
  padding: 0;
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-control);
  background: var(--card);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-section);
  line-height: 1;
  cursor: pointer;
}
.admin-quick-create__button { border-color: var(--btn-border); background: var(--btn-bg); color: var(--btn-color); }
.admin-quick-create__button:hover { background: var(--btn-bg-hover); }
.admin-topbar__icon-button:hover,
.admin-icon-button:hover { background: var(--primary-surface); }
.admin-quick-create__menu,
.admin-menu {
  position: absolute;
  z-index: 20;
  inset-block-start: calc(100% + var(--space-2xs));
  inset-inline-end: 0;
  min-inline-size: 14rem;
  padding: var(--space-2xs);
  border: var(--hairline) solid var(--border);
  border-radius: var(--radius-surface);
  background: var(--card);
  box-shadow: var(--elevation-2);
}
/* In the top layer (see bindMenu) the browser's popover frame is replaced by the menu's own. */
.admin-quick-create__menu[popover],
.admin-menu[popover] { margin: 0; overflow: visible; color: var(--text); }
.admin-quick-create__item,
.admin-menu__item {
  /* Reset so an item can be an <a> or a <button> with the same look: no browser button chrome, left aligned, full width. */
  appearance: none;
  inline-size: 100%;
  margin: 0;
  border: 0;
  background: transparent;
  font-family: inherit;
  line-height: inherit;
  text-align: start;
  cursor: pointer;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  min-block-size: var(--control-height);
  padding: 0 var(--space-lg);
  border-radius: var(--radius-control);
  color: var(--text);
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  text-decoration: none;
}
.admin-quick-create__item:hover,
.admin-quick-create__item:focus-visible,
.admin-menu__item:hover,
.admin-menu__item:focus-visible { background: var(--primary-surface); }
.admin-topbar :is(button, a):focus-visible,
.admin-sidebar :is(button, a):focus-visible,
.admin-quick-create__item:focus-visible,
.admin-menu__item:focus-visible { outline: var(--focus-outline-width) solid var(--focus-ring-color); outline-offset: var(--focus-outline-offset-inset); }
/* On a phone the search is an icon and the shortcut hint goes. */
@media (max-width: 720px) {
  /* A docked editor takes the whole screen on a phone. */
  .admin-dock, .admin-dock[data-side="right"] { inset: 0; inline-size: auto; max-block-size: none; border: 0; }
  .admin-search-trigger { inline-size: var(--gk-min-tap); block-size: var(--gk-min-tap); justify-content: center; padding: 0; }
  .admin-search-trigger > :not(svg) { display: none; }
  .admin-quick-create__button, .admin-topbar__icon-button { inline-size: var(--gk-min-tap); block-size: var(--gk-min-tap); }
}

.admin-sidebar {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  inline-size: var(--admin-sidebar-w);
  min-block-size: 0;
  padding: var(--space-sm);
  background: var(--admin-nav-bg);
  color: var(--admin-nav-item-text);
}
.admin-sidebar__main { display: grid; flex: 1 1 auto; align-content: start; gap: var(--space-3xs); min-block-size: 0; overflow-y: auto; }
.admin-sidebar__children { display: grid; gap: var(--space-3xs); padding-inline-start: var(--space-4xl); }
.admin-sidebar__item[aria-expanded="false"] + .admin-sidebar__children { display: none; }
.admin-sidebar__item {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  box-sizing: border-box;
  inline-size: 100%;
  min-block-size: var(--control-height);
  padding: var(--space-sm);
  border: 0;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--admin-nav-item-text);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-ui);
  line-height: var(--lh-snug);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.admin-sidebar__item > :is(gk-icon, svg) { flex: none; --icon-size: var(--fs-20); }
.admin-sidebar__item[aria-expanded]::after { content: "\203a"; margin-inline-start: auto; font-size: var(--fs-section); line-height: 1; transition: transform var(--dur) var(--ease); }
.admin-sidebar__item[aria-expanded="true"]::after { transform: rotate(90deg); }
.admin-sidebar__item:hover { background: var(--admin-nav-hover-bg); color: var(--admin-nav-hover-text); }
.admin-sidebar__item[aria-current="page"] { background: var(--admin-nav-active-bg); color: var(--admin-nav-active-text); }
/* Settings sits alone at the foot, apart from daily work. */
.admin-sidebar__footer { margin-block-start: var(--space-sm); padding-block-start: var(--space-sm); border-block-start: var(--hairline) solid rgba(var(--admin-nav-text-rgb), 0.12); }
