/* ==========================================================================
   MonitorDirectory - one stylesheet, the whole design system.
   Bump AppInfo.CssVersion on every change: Interactive Server keeps serving
   the old file otherwise, and a plain refresh will not clear it.
   ========================================================================== */

:root {
  /* brand */
  --bright-blue: #12c3f4;  --bright-blue-600: #0fa9d6;
  --bright-blue-100: #d6f4fd;  --bright-blue-050: #ecfaff;
  --dark-blue: #262262;    --dark-blue-400: #4b4789;
  --blood-orange: #f05125; --orange: #f79024;
  --purple: #63288a;       --pink: #ee759b;

  /* neutrals */
  --grey: #f3f3f3; --white: #fff;
  --ink: #262262; --ink-700: #3c3a6b; --ink-500: #6f6d92; --ink-300: #a6a4c0;
  --line: #e4e4ee; --line-200: #eeeef4;

  /* semantic - --accent and its tints are overridden per client on .directory, computed by
     ThemePalette from the one colour the client has stored. These are the fallbacks. */
  --accent: var(--bright-blue);
  --accent-wash: rgba(18,195,244,.10);
  --accent-tint: rgba(18,195,244,.18);
  --accent-soft: rgba(18,195,244,.30);
  --accent-ink: rgb(19,111,136);
  --success: #1fa971; --success-bg: #e4f7ef;
  --warning: var(--orange); --warning-bg: #fdeedd;
  --danger: var(--blood-orange); --danger-bg: #fde6df;

  /* type + shape */
  --font-sans: 'Poppins', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --r-sm: 10px; --r-md: 14px; --r-lg: 20px; --r-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(38,34,98,.06), 0 1px 3px rgba(38,34,98,.08);
  --shadow-md: 0 4px 12px rgba(38,34,98,.08), 0 2px 4px rgba(38,34,98,.06);
  --shadow-lg: 0 12px 32px rgba(38,34,98,.12), 0 4px 8px rgba(38,34,98,.06);

  --bar-height: 64px;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0 0 .4em; font-weight: 600; line-height: 1.25; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code { font-family: var(--font-mono); font-size: .9em; }

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

.muted { color: var(--ink-500); }

/* --- shell --------------------------------------------------------------- */

.directory-shell { height: 100dvh; display: flex; flex-direction: column; }

.directory { display: flex; flex-direction: column; height: 100%; min-height: 0; }

.directory-bar {
  flex: none;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px;
  min-height: var(--bar-height);
  background: var(--white);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  position: relative; z-index: 20;
}

/* The body is the scroll container, not the page: an embedded directory must never make
   the host page scroll. */
.directory-body { flex: 1; min-height: 0; position: relative; overflow: hidden; }

.directory-map { position: absolute; inset: 0; }
.directory-map.is-hidden { visibility: hidden; pointer-events: none; }

/* --- map ----------------------------------------------------------------- */

.map-canvas, .map-surface { position: absolute; inset: 0; }
.map-surface { background: var(--grey); }

.maplibregl-ctrl-group { border-radius: var(--r-sm) !important; box-shadow: var(--shadow-md) !important; }

.info-message {
  position: absolute; top: 12px; left: 12px; z-index: 5;
  max-width: min(360px, calc(100% - 24px));
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px;
  background: var(--white); color: var(--ink-700);
  border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  font-size: 12.5px;
}

/* --- filter bar ---------------------------------------------------------- */

.filter-bar {
  display: flex; align-items: center; gap: 8px;
  flex: 1; min-width: 0; flex-wrap: wrap;
}

.search-box {
  display: flex; align-items: center; gap: 6px;
  padding: 0 10px; height: 38px; min-width: 200px;
  background: var(--grey); border: 1px solid transparent; border-radius: var(--r-pill);
  transition: border-color .15s, background .15s;
}
.search-box:focus-within { background: var(--white); border-color: var(--accent); }
.search-box i { color: var(--ink-300); }
.search-box input {
  flex: 1; min-width: 0; border: 0; background: transparent; outline: none;
  font: inherit; color: inherit;
}
.search-box input::-webkit-search-cancel-button { display: none; }

.result-count { margin-left: auto; color: var(--ink-500); font-size: 12.5px; white-space: nowrap; }

/* --- multiselect --------------------------------------------------------- */

.multiselect { position: relative; }

.multiselect-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  height: 38px; padding: 0 12px;
  background: var(--white); color: var(--ink-700);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  font: inherit; cursor: pointer;
  transition: border-color .15s, color .15s;
}
.multiselect-trigger:hover { border-color: var(--ink-300); }
.multiselect.is-open .multiselect-trigger { border-color: var(--accent); color: var(--ink); }

.multiselect-label { white-space: nowrap; }

.chip-count {
  min-width: 20px; padding: 0 6px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--white);
  font-size: 11px; font-weight: 600; text-align: center;
}

.multiselect-panel {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
  min-width: 240px; max-width: 320px;
  padding: 8px;
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}

.multiselect-search {
  width: 100%; padding: 7px 10px; margin-bottom: 6px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  font: inherit; outline: none;
}
.multiselect-search:focus { border-color: var(--accent); }

.multiselect-options { max-height: 260px; overflow-y: auto; }

.multiselect-option {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 8px; border-radius: var(--r-sm);
  cursor: pointer;
}
.multiselect-option:hover { background: var(--bright-blue-050); }
.multiselect-option input { accent-color: var(--accent); }

.swatch {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  box-shadow: 0 0 0 1px rgba(20,24,51,.42);
}

.multiselect-empty { margin: 8px; color: var(--ink-500); }

.multiselect-actions {
  display: flex; margin-top: 6px; border-top: 1px solid var(--line-200);
}

.multiselect-action {
  flex: 1; padding: 7px;
  background: none; border: 0;
  color: var(--accent); font: inherit; cursor: pointer;
}

.multiselect-action:hover { background: var(--bright-blue-050); }
.multiselect-action + .multiselect-action { border-left: 1px solid var(--line-200); }

/* --- buttons ------------------------------------------------------------- */

.btn {
  height: 38px; padding: 0 18px;
  background: var(--accent); color: var(--white);
  border: 0; border-radius: var(--r-pill);
  font: inherit; font-weight: 500; cursor: pointer;
}
.btn:hover { filter: brightness(.94); }

.btn-quiet {
  background: none; border: 0; padding: 4px 8px;
  color: var(--accent); font: inherit; cursor: pointer;
}
.btn-quiet:hover { text-decoration: underline; }
.btn-quiet--tiny { font-size: 12px; padding: 0; }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  background: none; border: 0; border-radius: 50%;
  color: var(--ink-500); cursor: pointer;
}
.icon-btn:hover { background: var(--grey); color: var(--ink); }

.view-toggle {
  display: inline-flex; flex: none;
  background: var(--grey); border-radius: var(--r-pill); padding: 3px;
}
.view-toggle-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 14px;
  background: none; border: 0; border-radius: var(--r-pill);
  color: var(--ink-500); font: inherit; cursor: pointer;
  transition: background .15s, color .15s;
}
.view-toggle-btn.is-active { background: var(--white); color: var(--ink); box-shadow: var(--shadow-sm); }

/* --- list + cards -------------------------------------------------------- */

.list-view { position: absolute; inset: 0; overflow-y: auto; background: var(--grey); padding: 16px; }

.card-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.business-card {
  display: flex; flex-direction: column; gap: 10px;
  padding: 16px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: box-shadow .15s, transform .15s;
}
.business-card:hover { box-shadow: var(--shadow-md); }
.business-card.is-compact { border: 0; box-shadow: none; padding: 0; }

.business-body { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.business-name {
  padding: 0; border: 0; background: none;
  font: inherit; font-size: 1rem; font-weight: 600; color: var(--ink);
  text-align: left; cursor: pointer;
}
button.business-name:hover { color: var(--accent); }

.business-description {
  margin: 0; color: var(--ink-700);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.business-description.is-expanded { -webkit-line-clamp: unset; }

.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  padding: 3px 10px;
  background: var(--bright-blue-100); color: var(--dark-blue);
  border-radius: var(--r-pill);
  font-size: 11.5px; font-weight: 500;
}
.chip--tag { color: var(--white); }

/* --- contact actions ------------------------------------------------------ */
/* Whole rows rather than small links in a definition list. This is the part of the card that
   earns the business something, and on a phone a 13px mailto: is not a target. */

.business-actions { display: flex; flex-direction: column; gap: 6px; }

.action-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line-200);
  border-radius: var(--r-sm);
  background: var(--white);
  color: var(--ink);
  text-decoration: none;
  transition: border-color .15s, background .15s, transform .15s;
}
.action-row:hover { border-color: var(--accent-soft); background: var(--accent-wash); transform: translateX(2px); }
.action-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.action-icon {
  flex: none;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: var(--accent-tint);
  color: var(--accent-ink);
  font-size: 16px;
}

/* min-width:0 so a long address can ellipsis instead of stretching the row */
.action-text { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.action-label {
  font-size: 10.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-300);
}
.action-value {
  font-size: 13.5px; font-weight: 500; color: var(--accent-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.action-go { margin-left: auto; flex: none; font-size: 15px; color: var(--ink-300); }
.action-row:hover .action-go { color: var(--accent-ink); }

/* --- address section ------------------------------------------------------ */

.business-section {
  padding: 10px 12px;
  border-radius: var(--r-sm);
  /* No accent bar down the side and no band across the top of the popup: solid blocks of the
     client's colour read as chrome rather than as energy. The colour stays where it is doing
     work - the icon tiles, the link text, the header wash. */
  background: var(--accent-wash);
}

.section-title {
  margin: 0 0 4px;
  display: flex; align-items: center; gap: 6px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--accent-ink);
}

.section-body { margin: 0; font-size: 13.5px; color: var(--ink-700); }
.address-line { display: block; }

/* --- card footer: labels left, logo right --------------------------------- */

.business-foot {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--line-200);
}

.business-labels { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }

.business-logo { flex: none; display: flex; align-items: flex-end; justify-content: flex-end; }
.business-logo img { max-height: 44px; max-width: 112px; width: auto; object-fit: contain; }

/* Smaller and softer than a real logo: it is filling a gap, not claiming the space. */
.business-logo img.is-placeholder { width: 38px; height: 38px; opacity: .75; }

.list-more { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 24px 0; }

/* --- business popup ------------------------------------------------------- */

.popup-scrim {
  position: absolute; inset: 0; z-index: 45;
  background: rgba(38, 34, 98, .34);
  animation: popup-scrim-in .18s ease;
}

.popup-card {
  position: absolute; z-index: 46;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(420px, calc(100% - 32px));
  /* Capped rather than sized to content: the consent detail list can be long, and a card
     taller than the pane would be clipped by .directory-body's overflow. */
  max-height: min(78%, 620px);
  display: flex; flex-direction: column;
  background: var(--white);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: popup-card-in .2s cubic-bezier(.2, .8, .3, 1);
}

/* Focus is moved here when the card opens, so the ring must not show for a pointer user. */
.popup-card:focus { outline: none; }
.popup-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@keyframes popup-scrim-in { from { opacity: 0; } to { opacity: 1; } }

@keyframes popup-card-in {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 10px)) scale(.97); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.popup-head {
  flex: none;
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 16px 12px;
  background: var(--accent-wash);
  border-bottom: 1px solid var(--accent-soft);
}
/* min-width:0 or a long business name shoves the close button past the overflow edge */
.popup-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 17px; color: var(--accent-ink); }

.popup-body { flex: 1; overflow-y: auto; padding: 16px; }

.detail-list {
  margin: 16px 0 0; padding-top: 16px;
  border-top: 1px solid var(--line-200);
  display: flex; flex-direction: column; gap: 10px;
}
.detail-list dt { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-300); }
.detail-list dd { margin: 0; }

/* --- states -------------------------------------------------------------- */

.state-panel {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 40px 24px; text-align: center;
  color: var(--ink-500);
}
.state-panel--inline { position: absolute; inset: 0; background: var(--grey); }
.state-panel i { font-size: 32px; color: var(--ink-300); }
.state-panel h1, .state-panel h2 { color: var(--ink); margin: 0; }
.state-panel p { margin: 0; max-width: 42ch; }

.spinner {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin .7s linear infinite;
}
.spinner--inline { width: 13px; height: 13px; display: inline-block; vertical-align: -2px; }

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .business-card, .view-toggle-btn { transition: none; }
  .popup-card, .popup-scrim { animation: none; }
  .spinner { animation-duration: 2s; }
}

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

@media (max-width: 640px) {
  .directory-bar { gap: 8px; padding: 8px 12px; }
  .search-box { min-width: 0; flex: 1 1 100%; order: -1; }
  .result-count { display: none; }
  /* A bottom sheet on a phone: a centred card leaves dead space above and below, and the
     thumb is at the bottom of the screen. */
  .popup-card {
    top: auto; bottom: 0; left: 0;
    transform: none;
    width: 100%;
    max-height: 82%;
    border-radius: 14px 14px 0 0;
    animation: popup-sheet-in .22s cubic-bezier(.2, .8, .3, 1);
  }

  @keyframes popup-sheet-in {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
  }
  .card-grid { grid-template-columns: 1fr; }
}

/* --- circuit failure ------------------------------------------------------ */
/* Blazor unhides this when the circuit drops. UseExceptionHandler does not cover circuit
   exceptions, so without it a dropped connection is a silently frozen page. */

#blazor-error-ui {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 1000;
  padding: 12px 16px;
  background: var(--warning-bg); color: var(--ink);
  border-top: 1px solid var(--warning);
  box-shadow: var(--shadow-lg);
  text-align: center;
}
#blazor-error-ui .reload { margin-left: 10px; font-weight: 500; }
#blazor-error-ui .dismiss {
  position: absolute; right: 12px; top: 8px;
  cursor: pointer; font-size: 20px; line-height: 1; color: var(--ink-500);
}

/* ==========================================================================
   Admin. Same tokens, different furniture: the public directory is chrome-free
   because it lives in someone else's iframe; the admin is a normal page.
   ========================================================================== */

.admin-shell { min-height: 100dvh; background: var(--grey); }

.admin-nav {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  padding: 0 16px;
  background: var(--dark-blue); color: var(--white);
  box-shadow: var(--shadow-md);
}
.admin-brand { display: inline-flex; align-items: center; gap: 8px; padding: 14px 16px 14px 0; font-weight: 600; }
.admin-nav a {
  padding: 16px 12px; color: rgba(255,255,255,.72);
  border-bottom: 2px solid transparent; text-decoration: none;
}
.admin-nav a:hover { color: var(--white); text-decoration: none; }
.admin-nav a.active { color: var(--white); border-bottom-color: var(--bright-blue); }

.admin-nav-end { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.admin-nav-end .muted { color: rgba(255,255,255,.6); }
.admin-nav-end .btn-quiet { color: var(--bright-blue); }

.admin-main { max-width: 1100px; margin: 0 auto; padding: 28px 16px 64px; }
.admin-main h1 { margin-bottom: 16px; }

.admin-card {
  padding: 20px; margin-bottom: 20px;
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.admin-card h2 { margin-bottom: 14px; }

.admin-toolbar { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }

.admin-table {
  width: 100%; border-collapse: collapse;
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  overflow: hidden;
}
.admin-table th, .admin-table td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--line-200); }
.admin-table th { font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-300); }
.admin-table tr:last-child td { border-bottom: 0; }
.admin-table tr.is-inactive { color: var(--ink-300); }
.admin-row-actions { display: flex; gap: 12px; white-space: nowrap; }

.mono { font-family: var(--font-mono); font-size: .92em; }

/* --- forms --------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 14px; }
.field > span:first-child { font-size: 12px; font-weight: 500; color: var(--ink-700); }
.field--inline { margin-bottom: 0; }

.field input[type="text"], .field input[type="password"], .field input[type="email"],
.field input[type="number"], .field select, .field textarea {
  padding: 9px 11px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  font: inherit; color: inherit; background: var(--white);
  outline: none;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent); }
.field input[readonly], .field textarea[readonly] { background: var(--grey); font-family: var(--font-mono); font-size: 12px; }
.field input[type="color"] { padding: 2px; width: 56px; height: 36px; }

.btn--block { width: 100%; margin-top: 6px; }
.btn:disabled { opacity: .55; cursor: default; }
.btn-quiet--danger { color: var(--danger); }

.switch-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.switch-grid label { display: flex; align-items: center; gap: 8px; }
.switch-grid input { accent-color: var(--accent); }

.form-error {
  padding: 9px 12px; margin: 0 0 14px;
  background: var(--danger-bg); color: var(--ink);
  border-radius: var(--r-sm);
}
.validation-message { color: var(--danger); font-size: 12px; }

/* --- notices ------------------------------------------------------------- */

.notice { padding: 12px 14px; margin-bottom: 18px; border-radius: var(--r-md); }
.notice--warn { background: var(--warning-bg); border: 1px solid var(--warning); }
.notice--ok { background: var(--success-bg); border: 1px solid var(--success); }
.notice p:first-child { margin-top: 0; }
.notice p:last-child { margin-bottom: 0; }

.chip--ok { background: var(--success-bg); color: var(--success); }
.chip--warn { background: var(--warning-bg); color: #8a5200; }
.chip--danger { background: var(--danger-bg); color: var(--danger); }

/* --- auth ---------------------------------------------------------------- */

.auth-panel {
  max-width: 380px; margin: 12vh auto; padding: 28px;
  background: var(--white);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.auth-panel h1 { font-size: 1.25rem; }
.auth-panel > .muted { margin: 0 0 20px; }

.auth-note {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 18px 0 0; padding-top: 16px;
  border-top: 1px solid var(--line-200);
  font-size: 12.5px; color: var(--ink-500);
}

/* --- diagnostics --------------------------------------------------------- */

.diag-list { margin: 0; display: flex; flex-direction: column; gap: 16px; }
.diag-list > div { display: grid; grid-template-columns: 200px 1fr; gap: 14px; align-items: start; }
.diag-list dt { font-weight: 500; color: var(--ink-700); }
.diag-list dd { margin: 0; }
.diag-list ul { margin: 6px 0 0; padding-left: 18px; }

@media (max-width: 640px) {
  .diag-list > div { grid-template-columns: 1fr; gap: 4px; }
  .admin-main { padding: 20px 12px 48px; }
}

/* --- login: the master-code panel ---------------------------------------- */

.auth-master { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line-200); }

.auth-master summary {
  cursor: pointer; list-style: none;
  text-align: center; font-size: 12.5px; color: var(--ink-500);
}
.auth-master summary::-webkit-details-marker { display: none; }
.auth-master summary:hover { color: var(--ink); }
.auth-master[open] summary { margin-bottom: 14px; }

/* Deliberately not the accent colour: this is the break-glass route, not the front door. */
.btn--master { background: var(--dark-blue); }

.auth-devhint {
  display: flex; align-items: center; gap: 6px;
  margin: -4px 0 12px; padding: 6px 10px;
  background: var(--bright-blue-050); color: var(--bright-blue-600);
  border: 1px solid var(--bright-blue-100); border-radius: 8px;
  font-size: 11.5px;
}

/* --- two-column form rows ------------------------------------------------ */

.form-grid {
  display: grid; gap: 0 16px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

@media (max-width: 560px) { .form-grid { grid-template-columns: 1fr; } }

/* Development-only failure detail on the directory page. */
.failure-detail {
  max-width: min(680px, 100%);
  margin: 4px 0 12px; padding: 12px 14px;
  background: var(--dark-blue); color: #ffd9cf;
  border-radius: var(--r-sm);
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1.6;
  text-align: left; white-space: pre-wrap; word-break: break-word;
  overflow-x: auto;
}

/* Shown over the map pane when the style itself fails to load - otherwise a bad
   Map:StyleUrl just looks like a broken app. */
.map-overlay {
  position: absolute; inset: 0; z-index: 4;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; text-align: center;
  background: var(--grey); color: var(--ink-500);
}

/* --- basemap picker ------------------------------------------------------ */

.provider-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.provider-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  padding: 14px 16px; text-align: left;
  background: var(--white); color: inherit;
  border: 1px solid var(--line); border-radius: var(--r-md);
  font: inherit; cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.provider-card:hover { border-color: var(--ink-300); }
.provider-card.is-selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.provider-name { font-weight: 600; }
.provider-blurb { font-size: 12.5px; color: var(--ink-500); }

.map-preview {
  position: relative;
  height: 340px; margin-top: 16px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  overflow: hidden;
}

/* The "fit to area" control, beside MapLibre's own zoom buttons. Its icon is inline SVG in a
   data URI so it needs no icon font inside the map's own control markup. */
.map-fit-btn {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23262262' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8V5a1 1 0 0 1 1-1h3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 18px 18px;
}

/* --- admin: tabs and page furniture -------------------------------------- */

.page-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 4px; }
.page-head h1 { margin: 4px 0 2px; }
.page-head p { margin: 0; }

.back-link {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12.5px; color: var(--ink-500);
}
.back-link:hover { color: var(--ink); text-decoration: none; }

.tabstrip {
  display: flex; gap: 2px; flex-wrap: wrap;
  margin: 18px 0 22px;
  border-bottom: 1px solid var(--line);
}

.tabstrip-tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 10px 14px;
  color: var(--ink-500); text-decoration: none;
  border-bottom: 2px solid transparent;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}
.tabstrip-tab:hover { color: var(--ink); background: var(--white); text-decoration: none; }
.tabstrip-tab.is-active {
  color: var(--ink); font-weight: 500;
  background: var(--white);
  border-bottom-color: var(--accent);
}

/* The save bar follows you down a long settings tab, so "Save changes" is never scrolled
   off while you are still editing. */
.admin-toolbar--sticky {
  position: sticky; bottom: 0; z-index: 10;
  margin: 0 -16px -16px; padding: 14px 16px;
  background: color-mix(in srgb, var(--grey) 88%, transparent);
  backdrop-filter: blur(6px);
  border-top: 1px solid var(--line);
}

.admin-table tr.is-clickable { cursor: pointer; }
.admin-table tr.is-clickable:hover { background: var(--bright-blue-050); }

/* Revealed embed link: a full-width row under its own token row. The last field loses its
   bottom margin so the row does not gain a dead strip under the iframe box. */
.admin-table tr.token-reveal > td { background: var(--grey); padding: 14px; }
.admin-table tr.token-reveal .field:last-child { margin-bottom: 0; }
.admin-table tr.token-reveal .field textarea { max-width: 780px; }
.admin-table tr.token-reveal .field input[type="text"] { max-width: 780px; }

/* --- rotating map labels -------------------------------------------------- */
/* Sizes here are load-bearing and mirrored in directory-map.js: .map-label is LABEL_W x
   LABEL_H (120x36), .map-label-name is LABEL_ROW_H (28) because the slide moves the track by
   exactly one row, and the tail is TAIL_PX (6). --map-accent* are set on the map container by
   applyAccent(); font-size and line-height on .map-label-text are set per name by
   fontSizeFor(), and the values here are the fallback if that never runs. */

.map-label {
  /* MUST be absolute, and must stay that way.
     This element IS the MapLibre marker, and MapLibre positions markers by writing a
     translate() into their transform - which is measured from wherever the element already
     sits. maplibre-gl.css takes them out of the flow with position:absolute so that origin is
     the container's corner; app.css loads after it, so a `position: relative` here (added to
     give the tail a containing block) silently put every label back INTO the flow. Each one
     then started 36px - one label height - below the last, and MapLibre's translate was
     applied from there: label n was displaced n x 36px down the page. Zoomed right in there
     are only one or two labels and it looked perfect; zoomed out there are a dozen and they
     appeared to fly off with the zoom.
     `absolute` is a containing block for the tail just as `relative` was, and it is what
     MapLibre needs. */
  position: absolute;
  top: 0;
  left: 0;
  width: 120px;
  height: 36px;
  /* The root takes no pointer events so its transparent corners do not eat map drags; the
     bubble takes them. :hover still matches here, because an ancestor of the hovered
     element counts as hovered. */
  pointer-events: none;
  user-select: none;
  /* No fill mode: a finished animation holding opacity would outrank .is-hidden in the
     cascade and the label could never fade out again. */
  animation: map-label-in .35s ease;
  transition: opacity .4s ease;

  --tail-edge: var(--map-accent-soft, rgba(38, 34, 98, .38));
  --tail-fill: rgba(255, 255, 255, .96);
}

/* Not every marker can carry a label at once, so which ones do rotates on a slower beat than
   the names inside them. Faded rather than removed, so a label returning does not pop. */
.map-label.is-hidden { opacity: 0; }
.map-label.is-hidden .map-label-bubble { pointer-events: none; }

/* Held for the whole of a zoom, and released only once the labels have been laid out for the
   new scale. Cluster centroids are recomputed at every zoom level, so a label that has not
   caught up is anchored to a point that no longer exists - and the further you zoom, the
   further it sits from the bubble it belongs to. Better to show none than to show one in the
   wrong place. Declared after .is-hidden so it wins at equal specificity. */
.map-labels-held .map-label { opacity: 0; transition: none; animation: none; }
.map-labels-held .map-label-bubble { pointer-events: none; }

@keyframes map-label-in { from { opacity: 0; } to { opacity: 1; } }

/* A speech bubble rather than haloed text. The basemap is already full of its own labels in
   greys and blues, and plain dark text among them reads as more of the map rather than as the
   thing the directory is advertising. The border and text take the tenant's accent, so a name
   is visibly the same object as the marker it names. */
.map-label-bubble {
  height: 36px;
  overflow: hidden;
  padding: 3px 8px;
  border-radius: 9px;
  background: rgba(255, 255, 255, .96);
  border: 1px solid var(--map-accent-soft, rgba(38, 34, 98, .38));
  box-shadow: 0 1px 5px rgba(16, 16, 38, .2);
  pointer-events: auto;
  cursor: pointer;
}

.map-label:hover .map-label-bubble {
  background: #fff;
  border-color: var(--map-accent, #12c3f4);
  box-shadow: 0 2px 9px rgba(16, 16, 38, .28);
}

.map-label:hover { --tail-edge: var(--map-accent, #12c3f4); --tail-fill: #fff; }

.map-label-track {
  transition: transform .5s cubic-bezier(.4, 0, .2, 1);
  will-change: transform;
}

.map-label-name {
  height: 28px;
  display: flex; align-items: center; justify-content: center;
  transition: opacity .16s ease;
}

/* Much quicker than the slide: the outgoing name should be gone by the time the incoming one
   is properly in the window, not sharing it. */
.map-label-name.is-leaving { opacity: 0; }

.map-label-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 100%;
  text-align: center;
  font-size: 12.5px; line-height: 14px; font-weight: 600;
  color: var(--map-accent-ink, #262262);
}

/* Off-screen twin used to pick the font size for a name. Must inherit the same font as the
   labels, which is why directory-map.js parents it to the map container. */
.map-label-probe {
  position: absolute; left: -9999px; top: 0;
  visibility: hidden; pointer-events: none;
  text-align: center; font-weight: 600;
  white-space: normal;
}

/* The tail. Two triangles per direction: the outer one is the border colour, the inner one is
   the fill shifted 1px over it, which leaves the 1px outline showing. It hangs off the root
   rather than the bubble so the bubble can keep overflow:hidden for the slide. */
.map-label-tail { position: absolute; width: 0; height: 0; }

.map-label-tail::before,
.map-label-tail::after {
  content: '';
  position: absolute;
  width: 0; height: 0;
  border: 6px solid transparent;
}

.map-label[data-tail="top"] .map-label-tail { top: 0; }
.map-label[data-tail="top"] .map-label-tail::before { bottom: 0; left: -6px; border-bottom-color: var(--tail-edge); }
.map-label[data-tail="top"] .map-label-tail::after { bottom: -1px; left: -6px; border-bottom-color: var(--tail-fill); }

.map-label[data-tail="bottom"] .map-label-tail { top: 100%; }
.map-label[data-tail="bottom"] .map-label-tail::before { top: 0; left: -6px; border-top-color: var(--tail-edge); }
.map-label[data-tail="bottom"] .map-label-tail::after { top: -1px; left: -6px; border-top-color: var(--tail-fill); }

.map-label[data-tail="left"] .map-label-tail { left: 0; }
.map-label[data-tail="left"] .map-label-tail::before { right: 0; top: -6px; border-right-color: var(--tail-edge); }
.map-label[data-tail="left"] .map-label-tail::after { right: -1px; top: -6px; border-right-color: var(--tail-fill); }

.map-label[data-tail="right"] .map-label-tail { left: 100%; }
.map-label[data-tail="right"] .map-label-tail::before { left: 0; top: -6px; border-left-color: var(--tail-edge); }
.map-label[data-tail="right"] .map-label-tail::after { left: -1px; top: -6px; border-left-color: var(--tail-fill); }

@media (prefers-reduced-motion: reduce) {
  .map-label-track { transition: none; }
  .map-label { animation: none; transition: none; }
  .map-label-name { transition: none; }
}
