/*
 * ASENTRA SMART PARKING — mobile-browser demo
 * M00 PART 03 · shared UI components (class system, framework-free).
 * Namespace: .ui-* only. Tokens only — no literal colors/sizes here.
 * Load after tokens.css + base.css. Interaction lives in js/ui/ (modal, toast).
 */

/* ============================================================ Button */
.ui-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--touch-target-min);
  padding: var(--space-2) var(--space-5);
  border: var(--border-width-thin) solid transparent;
  border-radius: var(--radius-full);
  font-family: var(--font-family-base);
  font-size: var(--font-size-300);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-normal);
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background-color var(--duration-fast) var(--easing-standard),
    border-color var(--duration-fast) var(--easing-standard),
    color var(--duration-fast) var(--easing-standard);
}

.ui-button--primary {
  background-color: var(--color-accent);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-card);
}
.ui-button--primary:hover {
  background-color: var(--color-accent-strong);
  box-shadow: none;
}
.ui-button--primary:active {
  background-color: var(--color-accent-strong);
  box-shadow: none;
}

.ui-button--secondary {
  background-color: var(--color-surface-raised);
  border-color: var(--color-border-strong);
  color: var(--color-text-primary);
}
.ui-button--secondary:hover { background-color: var(--color-surface-muted); }
.ui-button--secondary:active { background-color: var(--color-surface-muted); }

.ui-button--ghost {
  background-color: transparent;
  color: var(--color-accent);
}
.ui-button--ghost:hover { background-color: var(--color-accent-soft); }
.ui-button--ghost:active { background-color: var(--color-accent-soft); }

.ui-button:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}

.ui-button[disabled],
.ui-button[aria-disabled='true'] {
  opacity: var(--opacity-disabled);
  cursor: not-allowed;
  background-color: var(--color-surface-muted);
  border-color: var(--color-border);
  color: var(--color-text-faint);
  box-shadow: none;
}
.ui-button[disabled]:hover,
.ui-button[aria-disabled='true']:hover {
  background-color: var(--color-surface-muted);
  box-shadow: none;
}

/* ============================================================ Card */
.ui-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

/* ============================================================ Field (label + input + helper) */
.ui-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
}

.ui-field__label {
  font-size: var(--font-size-200);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-primary);
}

.ui-field__control {
  width: 100%;
  min-height: var(--touch-target-min);
  padding: var(--space-2) var(--space-4);
  border: var(--border-width-thin) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-raised);
  font-family: var(--font-family-base);
  font-size: var(--font-size-300);
  line-height: var(--line-height-normal);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-card);
  transition:
    border-color var(--duration-fast) var(--easing-standard),
    box-shadow var(--duration-fast) var(--easing-standard);
}
.ui-field__control::placeholder {
  color: var(--color-text-faint);
  opacity: 1;
}
.ui-field__control:hover:not(:focus-visible):not([disabled]) {
  border-color: var(--color-text-faint);
}
.ui-field__control:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}

.ui-field__helper {
  margin: 0;
  font-size: var(--font-size-100);
  color: var(--color-text-secondary);
}

/* Error treatment: danger border + helper text, and a non-color
   affordance for assistive tech via aria-invalid. */
.ui-field--error .ui-field__control { border-color: var(--color-danger); }
.ui-field--error .ui-field__control:hover:not(:focus-visible):not([disabled]) { border-color: var(--color-danger); }
.ui-field__control[aria-invalid='true'] { border-color: var(--color-danger); }
.ui-field--error .ui-field__helper,
.ui-field__error-text { color: var(--color-danger); }

/* ============================================================ Badge */
.ui-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-05) var(--space-2);
  border: var(--border-width-thin) solid transparent;
  border-radius: var(--radius-full);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  white-space: normal;
  overflow-wrap: anywhere;
  background-color: var(--color-surface-muted);
  color: var(--color-text-secondary);
}
.ui-badge--success { background-color: var(--color-success-soft); border-color: var(--color-success-soft); color: var(--color-success); }
.ui-badge--warning { background-color: var(--color-warning-soft); border-color: var(--color-warning-soft); color: var(--color-warning); }
.ui-badge--danger  { background-color: var(--color-danger-soft);  border-color: var(--color-danger-soft);  color: var(--color-danger); }
.ui-badge--info    { background-color: var(--color-info-soft);    border-color: var(--color-info-soft);    color: var(--color-info); }

/* ============================================================ Status chip (dot + label) */
.ui-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-05) var(--space-3);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-full);
  font-size: var(--font-size-100);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  background-color: var(--color-surface-raised);
  color: var(--color-text-secondary);
}
.ui-chip::before {
  content: '';
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-full);
  background-color: currentColor;
}
.ui-chip--success { color: var(--color-success); border-color: var(--color-success-soft); }
.ui-chip--warning { color: var(--color-warning); border-color: var(--color-warning-soft); }
.ui-chip--danger  { color: var(--color-danger);  border-color: var(--color-danger-soft); }
.ui-chip--info    { color: var(--color-info);    border-color: var(--color-info-soft); }

/* ============================================================ Modal */
.ui-modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding:
    var(--gutter-safe-top)
    var(--gutter-safe-right)
    var(--gutter-safe-bottom)
    var(--gutter-safe-left);
  background-color: var(--color-overlay);
}

.ui-modal__dialog {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: 100%;
  max-width: var(--layout-content-max-width);
  max-height: 100%;
  overflow: auto;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-float);
}
.ui-modal__dialog:focus-visible { outline: none; }

.ui-modal__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.ui-modal__title {
  margin: 0;
  font-size: var(--font-size-500);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
  color: var(--color-text-primary);
}

.ui-modal__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  margin: calc(var(--space-1) * -1) calc(var(--space-1) * -1) calc(var(--space-1) * -1) 0;
  padding: var(--space-0);
  border: var(--border-width-thin) solid transparent;
  border-radius: var(--radius-md);
  background-color: transparent;
  font-family: var(--font-family-base);
  font-size: var(--font-size-500);
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.ui-modal__close:hover { background-color: var(--color-surface-muted); }
.ui-modal__close:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}

.ui-modal__body {
  font-size: var(--font-size-300);
  color: var(--color-text-secondary);
}

.ui-modal__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* Background scroll lock while a modal is open (M00 FINAL FIX, CSS-only;
   :has() is supported across the modern mobile-browser demo targets).
   Locks page scroll and the shell main scroller — the dialog itself stays
   scrollable. No JS changes: open/close/focus behavior is untouched. */
body:has(.ui-modal) {
  overflow: hidden;
}
body:has(.ui-modal) .shell__main {
  overflow: hidden;
}

/* ============================================================ Toast */
.ui-toast-region {
  position: fixed;
  z-index: var(--z-toast);
  right: 0;
  bottom: var(--gutter-safe-bottom);
  left: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-4);
  pointer-events: none;
}

.ui-toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  max-width: var(--layout-app-max-width);
  padding: var(--space-3) var(--space-4);
  border: var(--border-width-thin) solid var(--color-border);
  border-left: var(--border-width-medium) solid var(--color-info);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-float);
  color: var(--color-text-primary);
  animation: ui-toast-in var(--duration-normal) var(--easing-standard);
}
.ui-toast--success { border-left-color: var(--color-success); }
.ui-toast--warning { border-left-color: var(--color-warning); }
.ui-toast--danger  { border-left-color: var(--color-danger); }

.ui-toast__message {
  flex: 1;
  margin: 0;
  font-size: var(--font-size-200);
  line-height: var(--line-height-normal);
}

.ui-toast__close {
  flex: none;
  display: inline-flex;
  padding: var(--space-1);
  border: none;
  border-radius: var(--radius-sm);
  background-color: transparent;
  font-family: var(--font-family-base);
  font-size: var(--font-size-300);
  line-height: 1;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.ui-toast__close:hover { background-color: var(--color-surface-muted); }
.ui-toast__close:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}

@keyframes ui-toast-in {
  from {
    opacity: 0;
    transform: translateY(var(--space-4));
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ============================================================ Empty state */
.ui-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-6);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface-page);
  text-align: center;
}

.ui-empty__glyph {
  width: var(--space-10);
  height: var(--space-10);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
}

.ui-empty__title {
  margin: 0;
  font-size: var(--font-size-section-title);
  font-weight: var(--font-weight-section-title);
  line-height: var(--line-height-section-title);
  color: var(--color-text-primary);
}

.ui-empty__desc {
  margin: 0;
  max-width: var(--layout-content-max-width);
  font-size: var(--font-size-body-secondary);
  font-weight: var(--font-weight-body-secondary);
  line-height: var(--line-height-body-secondary);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.ui-empty__action { margin-top: var(--space-2); }

/* ============================================================ Loading state */
.ui-loading {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--color-text-secondary);
}

.ui-loading__label {
  margin: 0;
  font-size: var(--font-size-body-secondary);
  font-weight: var(--font-weight-body-secondary);
  line-height: var(--line-height-body-secondary);
}

.ui-spinner {
  flex: none;
  width: var(--space-5);
  height: var(--space-5);
  border: var(--border-width-medium) solid var(--color-surface-muted);
  border-top-color: var(--color-accent);
  border-radius: var(--radius-full);
  animation: ui-spin var(--duration-slow) linear infinite;
}

.ui-skeleton {
  border-radius: var(--radius-sm);
  background-color: var(--color-skeleton);
  background-image: linear-gradient(
    90deg,
    var(--color-skeleton),
    var(--color-skeleton-highlight),
    var(--color-skeleton)
  );
  background-size: 200% 100%;
  animation: ui-shimmer var(--duration-slow) var(--easing-standard) infinite;
}
.ui-skeleton--line { height: var(--space-3); }
.ui-skeleton--tile { height: var(--space-10); border-radius: var(--radius-md); }

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

@keyframes ui-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .ui-toast,
  .ui-skeleton {
    animation: none;
  }
}

/* ============================================================
   Login screen (M01 PART 01)
   CR-M08-AUTH-MOBILE-UI-01 · PART 01 — mobile-first presentation only.

   Markup, ids, handlers, validation, submit flow, the password type
   swap and every ARIA contract in js/routes/login.js are untouched;
   this block only restyles the existing nodes. Mobile-first base rules
   below apply at 360 / 390 / 430; the >=520px seam in css/shell.css is
   preserved and only receives a measure cap here so tablet framing is
   unchanged.
   ============================================================ */
.login-screen {
  width: 100%;
  /* Phone: use the available column deliberately instead of squeezing a
     desktop-measure card. The safe horizontal margin comes from
     .shell__main padding — no extra inset, no horizontal overflow. */
  max-width: none;
  margin-inline: auto;
  /* One surface only — no nested decorative cards. Restrained radius and
     the standard card shadow (no gradients/glass/neon). */
  gap: var(--space-5);
  padding: var(--space-5) var(--space-4);
  border-radius: var(--radius-lg);
}

.login-screen__brand {
  display: flex;
  justify-content: center;
}

.login-screen__intro {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  /* Brand -> identity pairing reads as one lead block. */
  margin-block-start: calc(var(--space-2) * -1);
}

.login-screen__intro .shell__page-title,
.login-screen__intro .shell__page-note {
  text-align: center;
}

.login-screen__intro .shell__page-title {
  font-size: var(--font-size-500);
  line-height: var(--line-height-tight);
}

.login-screen__intro .shell__page-note {
  font-size: var(--font-size-body-secondary);
  text-wrap: balance;
}

.login-screen__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Credentials block sits slightly tighter than the surrounding sections so
   the two fields read as one group. */
.login-screen__form .ui-field + .ui-field {
  margin-block-start: calc(var(--space-2) * -1);
}

.login-screen__form .ui-field__control {
  /* Comfortable tap height on phones; never narrower than its container. */
  min-height: var(--touch-target-comfy);
}

/* Secondary auth links (Create account / Forgot password) — routing and
   hrefs are owned by js/routes/login.js; presentation only. */
.login-screen__form > a {
  align-self: center;
  min-height: var(--touch-target-min);
  display: inline-flex;
  align-items: center;
  font-size: var(--font-size-200);
  color: var(--color-accent);
}

.login-screen__password-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  /* The toggle is visually integrated into the field frame: the row draws
     the border/surface, the native input inside it stays a real input and
     the native button stays a real button. No fake control. */
  border: var(--border-width-thin) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-card);
  padding-inline-end: var(--space-1);
}

.login-screen__password-row .ui-field__control {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  /* Frame moves to the row so the input never double-borders. */
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: none;
  background-color: transparent;
}

/* Focus/error affordances move to the row so the integrated control keeps
   the same visible states the standalone field has.

   CR-M08-AUTH-MOBILE-UI-01 · FINAL FIX 01A (F-02) — the row ring is bound to
   keyboard focus on the INPUT only (:has() is already an accepted capability
   in this stylesheet). When the Show/Hide button is keyboard-focused the row
   stays quiet and the native .ui-button:focus-visible ring is the single
   indicator, so the two rings can no longer stack. Pointer interaction
   raises neither ring. */
.login-screen__password-row:has(.ui-field__control:focus-visible) {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}
.login-screen__password-row .ui-field__control:focus-visible {
  outline: none;
  outline-offset: 0;
}
.ui-field--error .login-screen__password-row {
  border-color: var(--color-danger);
}

.login-screen__password-toggle {
  flex: none;
  /* Compact affordance: the toggle stops eating the input's measure at
     360px while keeping a 44px tap target. */
  min-height: var(--touch-target-min);
  padding-inline: var(--space-2);
  font-size: var(--font-size-200);
}

.login-screen__message {
  min-height: var(--space-4);
  margin: 0;
  font-size: var(--font-size-200);
  /* Supporting copy is secondary text; only the live submit alert and the
     validation summary stay in danger ink (see below). */
  color: var(--color-text-secondary);
}

.login-screen__message[role='alert'] {
  color: var(--color-danger);
}

/* CR-M08-AUTH-MOBILE-UI-01 · FINAL FIX 01A (F-01) — explicit error state for
   reused message nodes that carry no role="alert" (forgot/reset password).
   The owning route toggles this class on its already-existing error/success
   branch; supporting and success copy stay neutral. */
.login-screen__message--error {
  color: var(--color-danger);
}

/* "or" separator between the Google demo action and the credentials form. */
.login-screen__message[role='separator'] {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 0;
  font-size: var(--font-size-caption);
  letter-spacing: var(--letter-spacing-wide);
  color: var(--color-text-faint);
}
.login-screen__message[role='separator']::before,
.login-screen__message[role='separator']::after {
  content: '';
  flex: 1 1 auto;
  height: var(--border-width-thin);
  background-color: var(--color-border);
}

/* Demo staff credentials block: a quiet inset panel, not a second card. */
div.login-screen__message {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-muted);
  color: var(--color-text-secondary);
}
div.login-screen__message > p {
  margin: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-normal);
  overflow-wrap: anywhere;
}
div.login-screen__message > strong {
  font-size: var(--font-size-200);
  color: var(--color-text-primary);
}

.login-screen__submit {
  width: 100%;
  min-height: var(--touch-target-comfy);
}

/* Tablet/desktop seam: restore the inner measure so the login surface keeps
   its existing framed proportions at and above the css/shell.css breakpoint. */
@media (min-width: 520px) {
  .login-screen {
    max-width: var(--layout-content-max-width);
    padding: var(--space-5);
  }
}

/* ============================================================
   M07 PART 01B · connectivity indicator
   Compact, non-interactive status pill mounted in the app bar by the
   composition root. Tokens only — no literal colors, sizes, or spacing.
   ============================================================ */
.ui-netbar {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: var(--space-05) var(--space-2);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-full);
  background-color: var(--color-surface-muted);
  font-size: var(--font-size-100);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

.ui-netbar__dot {
  flex: none;
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-full);
  background-color: var(--color-border-strong);
}

.ui-netbar--online {
  border-color: var(--color-success-soft);
  background-color: var(--color-success-soft);
  color: var(--color-success);
}

.ui-netbar--online .ui-netbar__dot { background-color: var(--color-success); }

.ui-netbar--offline {
  border-color: var(--color-danger-soft);
  background-color: var(--color-danger-soft);
  color: var(--color-danger);
}

.ui-netbar--offline .ui-netbar__dot { background-color: var(--color-danger); }

.ui-netbar--unknown {
  border-color: var(--color-warning-soft);
  background-color: var(--color-warning-soft);
  color: var(--color-warning);
}

.ui-netbar--unknown .ui-netbar__dot { background-color: var(--color-warning); }

/* M07 PART 02C · compact pending offline-action fact, adjacent to .ui-netbar.
   Visually absent while the queue is empty (no children → :empty). Tokens
   only; wraps instead of overflowing on narrow screens. */
.ui-pendq:empty { display: none; }

.ui-pendq {
  flex: none;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-05) var(--space-2);
  margin: 0;
  padding: var(--space-05) var(--space-2);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-full);
  background-color: var(--color-surface-muted);
  font-size: var(--font-size-100);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-secondary);
  max-width: 100%;
}

.ui-pendq__count {
  color: var(--color-text-primary);
  font-weight: var(--font-weight-semibold);
}

.ui-pendq__items {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-05) var(--space-2);
}

.ui-pendq__item {
  white-space: nowrap;
  color: var(--color-text-faint);
}

/* ============================================================
   CR-M08-UI-REFRESH-01 · PART 01B — premium card & metric foundation
   Additive presentation layer only. `.ui-card` above stays the exact
   default/base contract — no existing rule in this file is changed or
   removed, and existing screens keep today's look until a later PART
   opts into these classes. Semantic PART 01A type/rhythm tokens are
   consumed here but never redefined; every literal stays in
   css/tokens.css (shadows/radii/paddings come from existing tokens).
   No content widths and no min-width traps: surfaces shrink with the
   mobile column (safe from 360px up) and text wraps instead of
   overflowing. Large stat values use the numeric family + tabular
   figures so digit columns align. No numbers/content live in CSS.
   ============================================================ */

/* --------------------------------------------
   1. Card hierarchy — opt-in eminence modifiers.
   Always used together with the base `.ui-card`.
   -------------------------------------------- */

/* Featured / hero card — top of the surface hierarchy for a future
   parking-availability hero: largest radius, roomier padding/rhythm,
   stronger definition edge, and the top (still restrained) elevation.
   No gradient, no glow, no glassmorphism. */
.ui-card--featured {
  padding: var(--space-6);
  gap: var(--space-3);
  border-color: var(--color-border-strong);
  border-radius: var(--radius-xl);
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-float);
}

/* Standard emphasized card — a quiet middle tier between the base card
   and the hero: base rhythm/elevation kept, definition edge strengthened. */
.ui-card--emphasized {
  border-color: var(--color-border-strong);
}

/* Compact / low-emphasis surface — denser rhythm, muted neutral tone,
   no elevation; base hairline border is retained so the inset stays
   legible on both page and raised parents. */
.ui-card--compact {
  padding: var(--space-3) var(--space-4);
  gap: var(--space-row-compact);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-muted);
  box-shadow: none;
}

/* Interactive hero cards keep an inset-friendly visible focus ring. */
.ui-card--featured:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: calc(var(--space-1) * -1);
}

/* --------------------------------------------
   2. Stat / metric foundation.
   Presentational only — consumers supply real content markup later;
   no semantic HTML change is made by these classes.
   -------------------------------------------- */

/* Stat block: vertical stack that always leads with the label/value edge.
   As a grid/flex child it never resists shrinking (no overflow trap). */
.ui-stat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

/* Large stat value — headline numbers such as `119`. Numeric family +
   tabular figures keep digit columns aligned. No fixed width: long
   values wrap (break-anywhere) instead of overflowing. */
.ui-stat__value {
  font-family: var(--font-family-numeric);
  font-variant-numeric: var(--font-variant-numeric-tabular);
  font-size: var(--font-size-hero);
  font-weight: var(--font-weight-hero);
  line-height: var(--line-height-hero);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
  max-width: 100%;
}

/* Stat label — secondary caption voice; wraps (never nowrap). */
.ui-stat__label {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
  max-width: 100%;
}

/* Stat supporting text / metadata — body-secondary voice under a value. */
.ui-stat__meta {
  font-size: var(--font-size-body-secondary);
  font-weight: var(--font-weight-body-secondary);
  line-height: var(--line-height-body-secondary);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
  max-width: 100%;
}

/* ============================================================
   CR-M08-UI-REFRESH-01 · PART 01C — meter & compact layout foundation
   Additive, inert presentation classes for future occupancy meters,
   compact info rows, zone/floor rows, and quick-action tiles. Not yet
   adopted by any screen. Existing rules are untouched; every value is
   an existing token (no literal colors/sizes/radii/shadows, no custom
   properties). CSS computes nothing: no occupancy math, no status
   inference, no thresholds, no percentages — meter fill width and zone
   ordering are declared later by the renderer. Tones never carry the
   status alone: modifier colors only tint the meter while the future
   printed percentage/status text stays visible (nothing is clipped).
   Layouts shrink with the mobile column (360–430px): no fixed content
   widths, no positive min-width traps, text wraps, and trailing slots
   are flex-none so they never stretch the row.
   ============================================================ */

/* --------------------------------------------
   A. Occupancy / progress meter
   Renderer contract (later PART): supply the fill width inline
   (style="width: …") and pair a visible text label with the tone so
   color is never the only status signal. Unknown stays neutral gray.
   -------------------------------------------- */
.ui-meter {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
}

.ui-meter__track {
  display: block;
  width: 100%;
  height: var(--space-2);
  border-radius: var(--radius-full);
  background-color: var(--color-surface-muted);
}

.ui-meter__fill {
  display: block;
  width: var(--space-0);           /* renderer supplies the actual width */
  height: 100%;
  border-radius: var(--radius-full);
  background-color: var(--color-text-faint);   /* default: neutral gray */
  transition: width var(--duration-normal) var(--easing-standard);
}

/* Tone modifiers — applied on the container, inherited by the fill.
   Track picks up the matching -soft tint; fill the readable status ink.
   Gray modifiers stay neutral (no success/danger implication). */
.ui-meter--success .ui-meter__track { background-color: var(--color-success-soft); }
.ui-meter--success .ui-meter__fill  { background-color: var(--color-success); }

.ui-meter--warning .ui-meter__track { background-color: var(--color-warning-soft); }
.ui-meter--warning .ui-meter__fill  { background-color: var(--color-warning); }

.ui-meter--danger .ui-meter__track { background-color: var(--color-danger-soft); }
.ui-meter--danger .ui-meter__fill  { background-color: var(--color-danger); }

/* Neutral / unknown — muted grays only, deliberately non-signaling. */
.ui-meter--neutral .ui-meter__fill { background-color: var(--color-text-faint); }
.ui-meter--unknown .ui-meter__fill { background-color: var(--color-border-strong); }

/* --------------------------------------------
   B. Compact information row
   lead (flex-none) · main title + meta (wraps, shrinks) · trail (flex-none).
   -------------------------------------------- */
.ui-info-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.ui-info-row__lead {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.ui-info-row__main {
  flex: 1 1 auto;
  min-width: 0;            /* 0 lets the text column shrink; anti-overflow, not a fixed min-width */
}

.ui-info-row__title {
  font-size: var(--font-size-300);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}

.ui-info-row__meta {
  font-size: var(--font-size-body-secondary);
  font-weight: var(--font-weight-body-secondary);
  line-height: var(--line-height-body-secondary);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.ui-info-row__trail {
  flex: none;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
}

/* --------------------------------------------
   C. Zone / floor row — presentation scaffold for future zone rows.
   Grid tolerates any subset of slots: label/availability on the top
   line, meta/badge underneath, meter spanning the full width last.
   Adapter-declared order, grouping, and availability math stay with the
   future renderer — CSS only lays the row out.
   -------------------------------------------- */
.ui-zone-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-1) var(--space-3);
}

.ui-zone-row__label {
  grid-column: 1;
  grid-row: 1;
  font-size: var(--font-size-300);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}

.ui-zone-row__availability {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  font-family: var(--font-family-numeric);
  font-variant-numeric: var(--font-variant-numeric-tabular);
  font-size: var(--font-size-body-secondary);
  font-weight: var(--font-weight-body-secondary);
  line-height: var(--line-height-body-secondary);
  color: var(--color-text-primary);
  text-align: end;
  overflow-wrap: anywhere;
}

.ui-zone-row__meta {
  grid-column: 1;
  grid-row: 2;
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

.ui-zone-row__badge {
  grid-column: 2;
  grid-row: 2;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
}

.ui-zone-row__meter {
  grid-column: 1 / -1;
  grid-row: 3;
  min-width: 0;
}

/* --------------------------------------------
   D. Quick-action tile shell — usable as <a> or <button> later.
   Two-up grid on a mobile column; no icon/SVG shipped yet, the icon
   slot only centers whatever the future renderer puts inside.
   -------------------------------------------- */
.ui-action-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.ui-action-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-1);
  min-height: var(--touch-target-min);   /* ≥44px usable action height */
  padding: var(--space-3) var(--space-2);
  border: var(--border-width-thin) solid var(--color-border);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-raised);
  color: var(--color-text-primary);
  text-decoration: none;                 /* anchor/button agnostic */
  font-family: var(--font-family-base);
  -webkit-tap-highlight-color: transparent;
}

.ui-action-tile:hover { border-color: var(--color-border-strong); }

.ui-action-tile:focus-visible {
  outline: var(--border-width-medium) solid var(--color-accent);
  outline-offset: var(--space-05);
}

.ui-action-tile__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-10);
  height: var(--space-10);
}

.ui-action-tile__label {
  font-size: var(--font-size-200);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  color: var(--color-text-primary);
  overflow-wrap: anywhere;
}

.ui-action-tile__support {
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}

/* --------------------------------------------
   Reduced-motion: keep the meter fill switch instant when the user
   prefers less motion (additive companion to the existing M00 block).
   -------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .ui-meter__fill { transition: none; }
}

/* ============================================================
   CR-M08-UI-REFRESH-01 · PART 01D — shared primitive polish
   Premium mobility / smart-city presentation pass over the shared
   primitives only (button, field, badge, empty, loading/spinner/
   skeleton). Foundation still: PART 01A/01B/01C classes stay unadopted.
   Polish happened by editing the rules above in place — nothing was
   removed or renamed, markup contracts are unchanged, and token
   discipline holds: every value is an existing token, including the
   elevation (--shadow-card) used on the primary button and the field
   surface, with no custom properties and no literal colors. Success/
   warning/danger/info keep their exact semantic tone mapping and
   remain paired with readable text labels, so color is never the only
   status signal (error fields also accept aria-invalid). All motion
   stays as before and honours prefers-reduced-motion.
   ============================================================ */
