/* ============================================================================
 * Stradl — design system
 *
 * SOURCE OF TRUTH: DESIGN.md + tokens.css at the repo root. Per CLAUDE.md,
 * every UI change MUST reference the tokens in `tokens.css` (imported below)
 * and never hardcode colours / fonts / spacing / radius. See DESIGN.md §2A
 * for theming, §3 for map data semantics, §8 for anti-slop rules.
 *
 * The legacy --color-*, --font-sans, --color-subnav-* aliases below exist so
 * 68 pre-existing pages don't break during the Phase 2 token sweep. Each
 * alias maps to a token from tokens.css. New code MUST use the tokens.css
 * names directly (--surface-*, --accent, --font-ui, etc.), not these aliases.
 * Aliases will be deleted in Phase 4 once every page is migrated.
 * ============================================================================ */

@import url('./tokens.css');

/* ── Legacy --color-* aliases (auto theme-flip via tokens.css) ──────
   The 68 pre-existing pages reference --color-primary, --color-surface,
   --color-text, etc. Each alias here resolves to a token from tokens.css
   so a theme switch via data-theme on <html> flows through automatically.
   No need to declare separate light/dark blocks below — tokens.css owns
   the actual values, this block is pure indirection. New code MUST use
   the tokens.css names directly. Aliases deleted at end of Phase 4. */
:root {
  /* Primary / accent */
  --color-primary:        var(--accent);
  --color-primary-hover:  var(--accent-hover);
  --color-primary-soft:   var(--accent-muted);
  --color-primary-ring:   var(--accent-muted);
  --color-accent:         var(--brand-teal);

  /* Surfaces */
  --color-bg:             var(--surface-900);
  --color-surface:        var(--surface-800);
  --color-surface-alt:    var(--surface-700);

  /* Text */
  --color-text:           var(--text-primary);
  --color-text-muted:     var(--text-muted);
  --color-text-on-dark:   var(--text-primary);
  --color-text-on-primary:#FFFFFF;

  /* Borders */
  --color-border:         var(--surface-500);
  --color-border-strong:  var(--text-secondary);
  --color-input-border:   var(--surface-500);

  /* Status — bg + border variants resolve to the theme-aware tokens
     added to tokens.css 2026-08-09 (they flip with data-theme). */
  --color-success:        var(--positive);
  --color-success-bg:     var(--positive-bg);
  --color-success-border: var(--positive-border);
  --color-danger:         var(--negative);
  --color-danger-bg:      var(--negative-bg);
  --color-danger-border:  var(--negative-border);
  --color-warn:           var(--warning);
  --color-warn-bg:        var(--warning-bg);
  --color-warn-border:    var(--warning-border);
  --color-orange:         var(--orange);
  --color-orange-bg:      var(--orange-bg);
  --color-orange-border:  var(--orange-border);

  /* Top navigation chrome */
  --color-nav-bg:         var(--surface-800);
  --color-nav-bg-alt:     var(--surface-700);
  --color-nav-border:     var(--surface-500);
  --color-nav-text:       var(--text-secondary);
  --color-nav-text-active:var(--text-primary);
  --color-nav-accent:     var(--brand-teal);
  --color-nav-brand:      var(--text-primary);
  --color-nav-brand-sub:  var(--text-muted);

  /* Active accent — the "you are here" colour on tabs + contextual
   * sidebar. 2026-08-13: retired the inherited LodgeForce orange
   * (#d97a3c, preserved 2026-05-15) at Hamish's call. Now the brand
   * teal family — legible per theme via --teal-text — so location
   * reads in Stradl's own palette and blue stays purely interactive. */
  --color-active-accent:        var(--teal-text);
  --color-active-accent-hover:  var(--teal-text-hover);

  /* Contextual sub-nav — recessed panel feel on both themes. */
  --color-subnav-bg:            var(--surface-700);
  --color-subnav-border:        var(--surface-500);
  --color-subnav-text:          var(--text-primary);
  --color-subnav-text-muted:    var(--text-muted);
  --color-subnav-icon:          var(--text-secondary);
  --color-subnav-active-bg:     var(--surface-600);
  --color-subnav-active-text:   var(--text-primary);
}

/* Legacy font / size / radius aliases. tokens.css owns the canonical
 * values (--font-ui, --text-sm, --radius-md, etc); legacy --font-sans
 * / --font-size-* / --radius-* references resolve through here. */
:root {
  /* Typography */
  --font-sans:      var(--font-ui);
  --font-size-xs:   var(--text-xs);
  --font-size-sm:   var(--text-sm);
  --font-size-base: var(--text-base);
  --font-size-md:   15px;                 /* no tokens.css equivalent; kept */
  --font-size-lg:   var(--text-lg);
  --font-size-xl:   var(--text-xl);
  --font-size-2xl:  var(--data-xl);
  --line-height-tight: 1.15;
  --line-height-base:  1.45;

  /* Spacing — tokens.css already defines --space-1..--space-6.
     Legacy added --space-8 / --space-10; keep for backward-compat. */
  --space-8:  32px;                       /* = --space-6 in tokens.css; alias */
  --space-10: 40px;                       /* legacy step, no token equivalent */

  /* Radius — tokens.css defines sm/md/lg ≤ 8px per §5.
     --radius-pill kept ONLY for the .pill badge class, NEVER for buttons. */
  --radius-pill: 999px;

  /* Shadow — tokens.css has --shadow-panel / --shadow-popover; aliases
     here cover legacy --shadow-sm / -md / -lg references. */
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05);
  --shadow-md: 0 6px 16px rgba(15, 23, 42, 0.08);
  --shadow-lg: var(--shadow-popover);

  /* Layout — legacy; tokens.css has --topbar-h / --rail-w / --inspector-w
     for the new map-first layout (Phase 3). Keep these for now.
     2026-08-13: nav trimmed 68→56px (density rule; DESIGN.md target is
     48) — logo scaled with it, sticky offsets follow the var. */
  --content-max-width: 980px;
  --nav-height: 56px;
}

/* Webfonts (Satoshi / IBM Plex Mono) are self-hosted: @font-face in
   tokens.css → /fonts/*.woff2 (2026-08-13, replacing auth-gate.js's CDN
   <link> injection — this comment used to credit nav-user.js, which
   never had font code). DESIGN.md §4: Satoshi = UI/headings, Plex Mono
   = numerics, Poppins = brand register (declared, loaded only when a
   surface first uses it). */

/* ============================================================================
 * Base
 * ============================================================================ */

* { box-sizing: border-box; }

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

body {
  font-family: var(--font-sans);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

/* Numeric identity (DESIGN.md §4): every headline figure in tabular IBM Plex
 * Mono. Complements the tokens.css .num/[data-numeric] rule by catching the
 * per-page value classes that predate it. Added 2026-08-13. */
.kpi-value, .stat-value, .bk-amount, .oc-hero-value {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
}

/* Skeleton loading blocks (DESIGN.md §12): real-layout-shaped
 * --surface-alt blocks with one subtle ≤1.2s pulse, no shimmer sweep.
 * Drop <span class="skel"></span> where the value will land; the JS
 * fill (textContent/innerHTML) wipes it automatically. */
.skel {
  display: inline-block;
  width: 3ch;
  height: 1em;
  border-radius: var(--radius-sm);
  background: var(--color-surface-alt);
  animation: skel-pulse 1.2s ease-in-out infinite;
  vertical-align: baseline;
}
@keyframes skel-pulse { 50% { opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) { .skel { animation: none; } }

/* ============================================================================
 * Top navigation bar
 * ============================================================================ */

.app-nav {
  background: var(--color-nav-bg);
  border-bottom: 1px solid var(--color-nav-border);
  color: var(--color-nav-text);
  height: var(--nav-height);
  position: sticky;
  top: 0;
  z-index: 30;
}

.app-nav-inner {
  max-width: 1480px;
  margin: 0 auto;
  height: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: 0 var(--space-5);
}

.app-brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  color: var(--color-nav-brand);
}
.app-brand:hover { text-decoration: none; color: var(--color-nav-brand); }

/* Stradl wordmark logo. Two variants live side-by-side in the navbar
 * markup; CSS toggles which one is visible based on the active theme
 * (data-theme on <html>). Light navbar → plain logo; dark navbar →
 * bordered variant whose white outline lifts the navy wordmark off
 * the dark-blue background. No flash because data-theme is set by
 * auth-gate.js before first paint. */
.app-brand-logo {
  height: 36px; /* scaled with the 56px nav (was 44px in the 68px bar) */
  width: auto;
  display: block;
}
/* Light mode (default): show the plain logo, hide the bordered one. */
.app-brand-logo-on-dark { display: none; }
/* Dark mode: swap. */
:root[data-theme='dark'] .app-brand-logo-on-light { display: none; }
:root[data-theme='dark'] .app-brand-logo-on-dark  { display: block; }

/* ── White-label mode ───────────────────────────────────────────────
 * When module-sidebar.js detects a tenant logo on /api/branding/current,
 * it injects an `<img class="app-brand-tenant-logo">` and a
 * `<span class="app-brand-powered-by">powered by</span>` into .app-brand,
 * and adds the `has-tenant-logo` class. This block makes the tenant
 * logo the dominant element and shrinks the Stradl wordmark into a
 * subscript "powered by Stradl" mark on the right. */
.app-brand-tenant-logo {
  display: none;                /* hidden until has-tenant-logo applied */
  height: 40px;                 /* fits the 56px nav; was 48px in the 68px bar */
  width: auto;
  max-width: clamp(96px, 12vw, 240px); /* yields width at laptop sizes instead of pushing tabs */
  object-fit: contain;
}
/* The "powered by Stradl" mini-mark — stacked vertically (label
 * above wordmark) so the whole thing takes ~50px wide instead of
 * the 130px the inline version needed. */
.app-brand-stradl-stack {
  display: none;
  flex-direction: column;
  align-items: center;        /* centre label + wordmark horizontally */
  justify-content: center;
  gap: 2px;
  margin-left: var(--space-2, 8px);
  line-height: 1;
}
.app-brand-powered-by {
  font-size: 11px; /* legibility floor — was 10px */
  line-height: 1;
  color: var(--color-text-muted, #6b7a89);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  display: block;
  text-align: center;
}
/* `.app-nav-brand` included defensively — legacy markup variant some
 * 2026-07 pages shipped with; normalised to .app-brand 2026-07-31 but a
 * straggler must still white-label rather than silently keep Stradl. */
.app-nav-brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
}
.app-brand.has-tenant-logo .app-brand-tenant-logo,
.app-nav-brand.has-tenant-logo .app-brand-tenant-logo {
  display: block;
}
.app-brand.has-tenant-logo .app-brand-stradl-stack,
.app-nav-brand.has-tenant-logo .app-brand-stradl-stack {
  display: inline-flex;
}
/* Demote the Stradl wordmark in white-label mode — small subscript
 * sitting centred under the "powered by" label inside the stack.
 * NB: don't touch `display` here — that's owned by the theme-swap
 * rules above which hide the wrong variant per data-theme. Overriding
 * display:block forced BOTH light + dark wordmarks to render (the
 * "stradl appears twice" bug from v147). */
.app-brand.has-tenant-logo .app-brand-logo,
.app-nav-brand.has-tenant-logo .app-brand-logo {
  height: 22px;
  opacity: 0.85;
  margin: 0 auto;
}

/* Legacy mark/text/sub spans — kept so any HTML page that still uses
 * the old shape (e.g. a future page added before its author saw the
 * rebrand) doesn't render with broken styling. */
.app-brand-mark {
  font-size: var(--font-size-lg);
  line-height: 1;
  color: var(--color-nav-accent);
}
.app-brand-text {
  font-size: var(--font-size-md);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.app-brand-sub {
  font-size: var(--font-size-xs);
  font-weight: 500;
  color: var(--color-nav-brand-sub);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Auth-card variant (login + set-password). The card background is
 * white so the logo's natural colours read fine — no pill needed. */
.auth-brand {
  display: flex;
  justify-content: center;
  margin-bottom: var(--space-4);
}
.auth-brand-logo {
  height: 64px;
  width: auto;
  display: block;
}

/* Module tabs must SHRINK, never push the user chrome off-viewport
 * (2026-08-14 P0: at 1280×800 the bell/avatar sat at x=1341 and every
 * page body-scrolled ~173px). The tab strip is the one flexible child:
 * it yields width and scrolls internally; brand + user chrome stay
 * pinned inside the viewport. */
.app-nav-links {
  display: flex;
  align-items: center;
  gap: 2px;
  height: 100%;
  flex: 0 1 auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none; /* strip scrolls but shows no bar */
}
.app-nav-links::-webkit-scrollbar { display: none; }

.app-nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 100%;
  padding: 0 var(--space-4);
  color: var(--color-nav-text);
  font-size: var(--font-size-base);
  font-weight: 500;
  white-space: nowrap;
  flex-shrink: 0; /* individual tabs keep their width; the strip scrolls */
  text-decoration: none;
  border-bottom: 3px solid transparent;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.app-nav-link svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex-shrink: 0;
}
.app-nav-link:hover {
  color: var(--color-nav-text-active);
  background: var(--color-nav-bg-alt);
  text-decoration: none;
}
.app-nav-link.active {
  color: var(--color-nav-text-active);
  border-bottom-color: var(--color-active-accent);
  background: var(--color-nav-bg-alt);
}

.app-nav-spacer { flex: 1; }

.app-nav-user {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--font-size-sm);
  color: var(--color-nav-text);
  flex-shrink: 0; /* never pushed off-viewport by the tab strip */
}
.app-nav-tenant {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--color-nav-brand-sub);
}
.app-nav-avatar {
  /* min-width, not width: single-word users get a 3-letter chip (e.g. JEN)
     which needs the pill to stretch a little past the 32px circle. */
  min-width: 32px;
  height: 32px;
  padding: 0 var(--space-2);
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  background: var(--color-nav-bg-alt);
  border: 1px solid var(--color-nav-border);
  color: var(--color-nav-text-active);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-sm);
  font-weight: 600;
}

/* ============================================================================
 * Page layout
 * ============================================================================ */

.app-container {
  max-width: var(--content-max-width);
  margin: 0 auto;
}

/* Two-column layout for module pages — LodgeForce-style cream contextual
 * sidebar on the left + main content on the right. The sidebar background
 * extends full-bleed within the 240px column; content stays on the
 * surface. */
.app-section {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 0;
  max-width: 100%;
  margin: 0;
  padding: 0;
}

.app-section > .app-container {
  /* Compact-density doctrine: entity edit + list pages fill the content
   * column rather than centring inside an 980px cap. The form-grid is
   * already a self-limiting 2-column responsive layout, so wider page
   * = more rows visible at once. Pages that want a narrower prose cap
   * can opt in locally. */
  max-width: none;
  padding: var(--space-3) var(--space-4);
  margin: 0;
}

.app-sidebar {
  position: sticky;
  top: var(--nav-height);
  align-self: stretch;
  background: var(--color-subnav-bg);
  /* No right border — the active item bleeds into the content area to
   * create the LodgeForce "connected tab" look. The cream-to-white
   * transition happens at the column edge naturally. */
  padding: var(--space-5) 0 var(--space-6);
  font-size: var(--font-size-base);
  min-height: calc(100vh - var(--nav-height));
}

.app-sidebar-header {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--color-subnav-text-muted);
  padding: 0 var(--space-5);
  margin: 0 0 var(--space-3);
}

.app-sidebar-group {
  margin-bottom: var(--space-4);
}

/* Section heading inside a module's submenu — small uppercase muted label
 * that groups related items underneath (only used when a module has
 * `sections` rather than flat `items`). */
.app-sidebar-section-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5) var(--space-1);
  color: var(--color-subnav-text-muted);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.app-sidebar-section-title .icon {
  display: none;
}

.app-sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px var(--space-5);
  color: var(--color-subnav-text);
  font-weight: 500;
  font-size: var(--font-size-base);
  text-decoration: none;
  position: relative;
  /* Reserve space for the active orange bar on the left so non-active
   * items don't shift horizontally when their state changes. */
  border-left: 3px solid transparent;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.app-sidebar-link .icon {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-subnav-icon);
  font-size: 15px;
  flex-shrink: 0;
}
.app-sidebar-link svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.app-sidebar-link:hover {
  color: var(--color-subnav-active-text);
  background: var(--color-subnav-active-bg);
  text-decoration: none;
}
.app-sidebar-link:hover .icon {
  color: var(--color-active-accent);
}

/* Active = white bg (matches the content surface) + orange left bar +
 * heavier label. The negative right margin pulls the white 1px past the
 * cream column so the item visually merges into the content area as a
 * connected tab — the LodgeForce signature. */
.app-sidebar-link.active {
  color: var(--color-subnav-active-text);
  background: var(--color-surface);
  border-left-color: var(--color-active-accent);
  border-left-width: 3px;
  font-weight: 600;
  margin-right: -1px;
  z-index: 1;
}
.app-sidebar-link.active .icon {
  color: var(--color-active-accent);
}
.app-sidebar-link.active:hover {
  background: var(--color-surface);
}

/* Dark-mode override (2026-08-13, replacing the hardcoded-white chip):
 * the white active surface read as a rendering bug on the dark sidebar
 * — the one element saying "you are here" looked broken. Dark mode now
 * uses the recessed active surface + the accent bar; light keeps the
 * true connected-tab (white-on-cream) look. */
:root[data-theme='dark'] .app-sidebar-link.active,
:root[data-theme='dark'] .app-sidebar-link.active:hover {
  background: var(--color-subnav-active-bg);
}

/* Coming-soon items show the planned shape of a module without being
 * navigable. Rendered as <span> so they can't be clicked; muted + a "Soon"
 * pill on the right. */
.app-sidebar-link-coming {
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: default;
  color: var(--color-text-muted);
  opacity: 0.65;
}
.app-sidebar-link-coming:hover {
  background: transparent;
  color: var(--color-text-muted);
}
.app-sidebar-soon {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--color-surface-alt);
  color: var(--color-text-muted);
  margin-right: 8px;
}

/* Sidebar item badge — small chip rendered after the label. Used to mark
   AI-powered nav items so the operator knows up front. .app-sidebar-badge-ai
   variant gets the brand teal accent + sparkle-friendly soft pop. */
.app-sidebar-badge {
  margin-left: auto;
  margin-right: 8px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  background: var(--color-surface-alt);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.app-sidebar-badge-ai {
  background: var(--ai-gradient); /* one AI identity app-wide (tokens.css) */
  color: #fff;
}
.app-sidebar-link.active .app-sidebar-badge-ai {
  /* On active rows the link background is already brand-coloured — keep
     the badge solid teal so it still pops against the nav highlight. */
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
}

/* Hub shortcut at the top of the sidebar — styled like a section title
 * (icon + bold) but clickable, so it visually stands apart from the
 * indented Capture/Edit sub-links below. */
.app-sidebar-link.app-sidebar-hub {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
  color: var(--color-text);
  font-weight: 700;
  font-size: var(--font-size-base);
  border-left: 2px solid transparent;
  border-radius: var(--radius-md);
}
.app-sidebar-link.app-sidebar-hub .icon {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-primary);
  font-size: 14px;
  flex-shrink: 0;
}
.app-sidebar-link.app-sidebar-hub:hover {
  background: var(--color-surface);
  color: var(--color-text);
}
.app-sidebar-link.app-sidebar-hub.active {
  background: var(--color-primary-soft);
  color: var(--color-primary);
  border-left-color: var(--color-primary);
}

@media (max-width: 900px) {
  .app-section {
    grid-template-columns: 1fr;
    padding: 0 var(--space-4);
  }
  .app-sidebar {
    position: static;
    padding: var(--space-3) 0 var(--space-4);
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-4);
  }
  .app-sidebar-group { margin-bottom: var(--space-3); }
}

/* ── Compact density doctrine ────────────────────────────────────────────
 * Defaults across the app favour density over whitespace. Page headers,
 * cards and forms all use the smaller end of the spacing scale; inputs
 * are 32px tall, sections wrap content tightly, and the form bottom
 * padding is just enough for the sticky toolbar to clear comfortably.
 * If a screen needs more breathing room, opt in locally — the global
 * default is dense. */

/* SINGLE-LINE header (Hamish 2026-08-04, standing instruction): the
 * breadcrumb, title, and any inline actions share ONE flex row.
 * module-sidebar.js converts a .header-note / <p> into a (?) tooltip on
 * the title, so no page ever stacks crumb / title / note again. Pages
 * with their own flex header styles simply inherit compatible rules. */
.page-header {
  padding: var(--space-3) var(--space-5) var(--space-2);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 12px;
}
.page-header .breadcrumb { margin: 0; }
.page-header h1 { margin: 0; }

.page-header h1 {
  margin: 0 0 var(--space-1);
  font-size: var(--font-size-xl);
  line-height: var(--line-height-tight);
  font-weight: 700;
  color: var(--color-text);
}

.header-note,
.page-header p {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  margin: 0;
}

.breadcrumb {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: var(--space-1);
}

/* ============================================================================
 * Sections / cards
 * ============================================================================ */

.section,
.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-2);
  box-shadow: var(--shadow-sm);
}

.section-title,
.card-title {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-lg); /* 16px — DESIGN.md §4 panel-title size; was base(14px), 1px off body */
  font-weight: 700;
  color: var(--color-text);
}

/* ============================================================================
 * Forms
 * ============================================================================ */

form {
  padding: 0 var(--space-5) var(--space-6);
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2) var(--space-3);
}
/* Drop to 2 columns on narrower laptops / tablets, 1 column on phones
 * (the phone breakpoint also lives in responsive.css with !important). */
@media (max-width: 1100px) {
  .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.form-group {
  margin: 0;
  position: relative; /* anchors any .search-results dropdown directly below the input */
}

.wide,
.form-group.wide { grid-column: 1 / -1; }

label {
  display: block;
  margin-bottom: 2px;
  font-weight: 600;
  font-size: var(--font-size-xs);
  color: var(--color-text);
  letter-spacing: 0.01em;
}

input,
select,
textarea {
  width: 100%;
  min-height: 32px;
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  background: var(--color-surface);
  color: var(--color-text);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

textarea {
  min-height: 64px;
  resize: vertical;
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-ring);
  outline: none;
}

input:disabled,
select:disabled,
textarea:disabled {
  background: var(--color-surface-alt);
  color: var(--color-text-muted);
  cursor: not-allowed;
}

.inline-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text);
  min-height: 32px;
}

.inline-check input {
  width: 16px;
  min-height: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--color-primary);
}

/* Helper text below an input (used widely on capture/edit forms) */
.field-hint {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-top: 2px;
  line-height: 1.3;
}

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

button,
.btn {
  min-height: 32px;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  padding: 6px 14px;
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

button:hover,
.btn:hover {
  background: var(--color-surface-alt);
  border-color: var(--color-text-muted);
}

button:disabled,
.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.primary-btn,
.btn-primary {
  border-color: var(--color-primary);
  background: var(--color-primary);
  color: var(--color-text-on-primary);
}

.primary-btn:hover,
.btn-primary:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-text-on-primary);
}

.danger-btn,
.btn-danger {
  border-color: var(--color-danger);
  background: var(--color-surface);
  color: var(--color-danger);
}
.danger-btn:hover,
.btn-danger:hover {
  background: var(--color-danger-bg);
}

/* ── Design-system additions (2026-06-02) ─────────────────────────── */
/* Secondary / outline button — for cancel, ghost actions, toolbar
   buttons that share visual weight with the page. */
.btn-secondary {
  border-color: var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
}
.btn-secondary:hover {
  background: var(--color-surface-alt);
  border-color: var(--color-text-muted);
}

/* Warning button — amber "place hold" semantic, on the status tokens so
   it theme-flips (was hardcoded #f59e0b/#d97706). Brand-navy text: white
   on amber fails contrast in both themes. */
.btn-warning {
  border-color: var(--warning);
  background: var(--warning);
  color: var(--brand-navy);
}
.btn-warning:hover {
  background: color-mix(in srgb, var(--warning) 85%, black);
  border-color: color-mix(in srgb, var(--warning) 75%, black);
}

/* Size modifier. Default .btn is 32px min-height with 6/14 padding;
   .btn-sm trims to 28px / 4/10 for table-row actions where 32px
   feels heavy. */
.btn-sm {
  min-height: 28px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 500;
}

/* Standard white-card panel. Replaces the per-page .ar-card / .mo-card
   / .gp-card / .tr-card etc. that all wanted the same shape. */
.panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
}
.panel-title {
  margin: 0 0 var(--space-2) 0;
  font-size: 1.05rem;
  font-weight: 600;
}

/* Status pill — neutral utility for tiny labels. Pages add the
   semantic colour via .pill-success / .pill-warning / .pill-danger
   / .pill-info modifiers below. */
/* Canonical .pill (2026-08-13): merged the two competing definitions
   (Tailwind-grey hardcodes here + a primary-soft chip further down) into
   one token-based badge that theme-flips. One pill vocabulary app-wide:
   11px / 600 / uppercase, status tints from tokens.css. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--neutral-bg);
  color: var(--color-text-muted);
}
.pill-success { background: var(--positive-bg); color: var(--positive); }
.pill-warning { background: var(--warning-bg);  color: var(--warning); }
.pill-danger  { background: var(--negative-bg); color: var(--negative); }
.pill-info    { background: var(--accent-muted); color: var(--accent-text); }
.pill-accent  { background: var(--accent-muted); color: var(--accent-text); }

/* ============================================================================
 * Sticky action bar
 * ============================================================================ */

.actions {
  /* Solid surface per DESIGN.md §8 #3 (no glassmorphism). The earlier
     rgba+backdrop-filter pattern was a §8 violation; now a flat
     --surface-800 panel with a hairline border. */
  position: sticky;
  bottom: 0;
  display: flex;
  gap: var(--space-2);
  justify-content: flex-end;
  padding: var(--space-2) var(--space-5);
  border-top: 1px solid var(--surface-500);
  background: var(--surface-800);
  z-index: 10;
}

/* ============================================================================
 * Status messages
 * ============================================================================ */

.message {
  margin: 0 var(--space-5) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: var(--font-size-base);
  display: none;
}

.message.success,
.success {
  display: block !important;
  color: var(--color-success);
  background: var(--color-success-bg);
  border: 1px solid var(--color-success-border);
}

.message.error,
.error {
  display: block !important;
  color: var(--color-danger);
  background: var(--color-danger-bg);
  border: 1px solid var(--color-danger-border);
}

.message.warn,
.warn {
  display: block !important;
  color: var(--color-warn);
  background: var(--color-warn-bg);
  border: 1px solid var(--color-warn-border);
}

/* Pills (used for selected-record indicators in edit screens) */
/* (Second .pill definition removed 2026-08-13 — consolidated into the
   canonical token-based .pill in the badge section above.) */

/* ============================================================================
 * Modal dialog
 * ============================================================================ */

.dialog-backdrop {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  background: rgba(15, 23, 42, 0.5);
  z-index: 50;
}

.dialog-backdrop.open {
  display: flex;
}

.dialog {
  width: min(440px, 100%);
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-lg);
  padding: var(--space-5);
}

.dialog h2 {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-lg);
}

.dialog p {
  margin: 0 0 var(--space-5);
  color: var(--color-text-muted);
  line-height: var(--line-height-base);
}

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

/* ============================================================================
 * Search-as-you-type results dropdown
 * ============================================================================ */

.search-results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  max-height: 280px;
  overflow-y: auto;
  z-index: 5;
}

.search-result-item {
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--font-size-base);
}
.search-result-item:last-child { border-bottom: none; }
.search-result-item:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary);
}

/* Highlight the matched substring inside search-as-you-type result rows
 * so users can see why an unexpected entry showed up. */
.search-hit {
  /* Amber tint tokens (2026-08-13): the old solid #fff3a3 turned light
     dark-theme text invisible. Translucent tint keeps the row's own text
     colour legible in both themes. */
  background: var(--warning-bg);
  color: inherit;
  padding: 0 1px;
  border-radius: 2px;
  font-weight: 600;
}
.search-result-item:hover .search-hit { background: var(--warning-border); }

/* ============================================================================
 * Tables (used in edit screens, dashboards, reports)
 * ============================================================================ */

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-base);
}

.data-table th,
.data-table td {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.data-table th {
  background: var(--color-surface-alt);
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  font-size: var(--font-size-xs);
  letter-spacing: 0.04em;
}

.data-table tr:hover td {
  background: var(--color-primary-soft);
}

/* Tighter row padding for dense admin tables. Opt-in via
   .data-table.is-compact on the table element. */
.data-table.is-compact th,
.data-table.is-compact td {
  padding: 10px 12px;
  font-size: 0.93em;
}
.data-table.is-compact th {
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.85em;
  color: #374151;
}

/* Unsaved-changes row highlight — pages tag editable rows with
   .is-dirty when the user touches a field, then strip it on save. */
.data-table tr.is-dirty td {
  background: var(--warning-bg); /* theme-flipping amber (was #fffbeb, light-only) */
}
.data-table tr.is-dirty:hover td {
  background: var(--warning-border);
}

/* Muted / inactive row — for soft-deleted or hidden entries. */
.data-table tr.is-muted td {
  color: #9ca3af;
  font-style: italic;
}

/* Rows that act like links (whole row clickable). */
.data-table tr.is-clickable { cursor: pointer; }

/* ============================================================================
 * Responsive
 * ============================================================================ */

@media (max-width: 640px) {
  .app-nav-links { display: none; }

  .app-container { max-width: none; }

  .page-header { padding: var(--space-3) var(--space-4) var(--space-2); }
  .page-header h1 { font-size: var(--font-size-xl); }

  form { padding: 0 var(--space-4) 112px; }

  .section { padding: var(--space-4); }

  .form-grid { grid-template-columns: 1fr; }

  .actions {
    flex-direction: column;
    padding: var(--space-3) var(--space-4);
  }
  .actions button { width: 100%; }

  .message { margin-left: var(--space-4); margin-right: var(--space-4); }

  .dialog-actions { flex-direction: column-reverse; }
  .dialog-actions button { width: 100%; }
}

/* ============================================================================
 * Print (used by site-report, face-report)
 * ============================================================================ */

@media print {
  .app-nav,
  .actions,
  .message { display: none !important; }
  body { background: #ffffff; }
}

/* ============================================================================
 * Photo panel (Phase 2) — reusable photos widget mounted via mountPhotoPanel().
 * Used on landlord/property/site/face edit screens. Self-contained — none of
 * its classes leak outside the .pp-root container.
 * ============================================================================ */

.pp-root {
  position: relative;
  margin-top: var(--space-4);
}

.pp-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.pp-title {
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: 700;
  color: var(--color-text);
}
.pp-header-actions { display: flex; gap: var(--space-2); align-items: center; }

.pp-file-input { display: none; }

.pp-btn {
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: 8px 14px;
  font-weight: 600;
  font-size: var(--font-size-base);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.pp-btn:hover { background: var(--color-surface-alt); border-color: var(--color-text-muted); }
.pp-btn-primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-text-on-primary);
}
.pp-btn-primary:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-text-on-primary);
}
.pp-btn-danger { border-color: #d14343; color: #d14343; background: var(--color-surface); }
.pp-btn-danger:hover { background: #fdecec; border-color: #b83434; }

/* ── Drop zone — compact single-line strip (still drag-friendly) ──────── */
.pp-dropzone {
  border: 1.5px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: 8px var(--space-3);
  margin-bottom: var(--space-3);
  background: var(--color-surface);
  transition: background 0.12s, border-color 0.12s;
}
.pp-root.pp-dragover .pp-dropzone {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
  padding: 14px var(--space-3);
}
.pp-dropzone-inner { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 8px; }
.pp-dropzone-icon  { font-size: 16px; line-height: 1; }
.pp-dropzone-text  { color: var(--color-text-muted); font-size: var(--font-size-sm); }
.pp-dropzone-text a { color: var(--color-primary); font-weight: 600; }

/* ── Upload queue ────────────────────────────────────────────────────────── */
.pp-queue { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-3); }
.pp-queue-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto 120px;
  gap: var(--space-2);
  align-items: center;
  padding: 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  font-size: var(--font-size-sm);
}
.pp-queue-name { font-weight: 600; color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pp-queue-size { color: var(--color-text-muted); }
.pp-queue-status { color: var(--color-text-muted); text-transform: capitalize; }
.pp-queue-bar {
  background: var(--color-surface-alt);
  border-radius: 999px;
  overflow: hidden;
  height: 6px;
}
.pp-queue-bar-fill {
  background: var(--color-primary);
  height: 100%;
  width: 0%;
  transition: width 0.18s ease;
}
.pp-queue-done .pp-queue-bar-fill   { background: #2a9b2a; width: 100% !important; }
.pp-queue-error .pp-queue-bar-fill,
.pp-queue-error-row { background: #fdecec; }
.pp-queue-error-row { color: #b83434; grid-template-columns: 1fr; }
.pp-queue-error { grid-column: 1 / -1; color: #b83434; font-size: var(--font-size-xs); }

/* ── Thumbnail grid ──────────────────────────────────────────────────────── */
.pp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-3);
}
.pp-empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--color-text-muted);
  padding: var(--space-5);
  font-size: var(--font-size-sm);
}
.pp-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
}
.pp-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--color-text-muted);
  transform: translateY(-1px);
}
/* Square 1:1 thumbnails — uniform grid across portrait + landscape source mix.
 * Cover-fit centre-crops the source so the most relevant part stays visible. */
.pp-card-imgwrap {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--color-surface-alt);
}
.pp-card-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
.pp-card-badges { position: absolute; top: 8px; left: 8px; display: flex; gap: 4px; flex-wrap: wrap; }
/* .pp-badge colours are deliberately theme-INVARIANT: they overlay
   photographs, which don't flip with the app theme (same rationale as
   the map entity palette, DESIGN.md §2A). Not drift. */
.pp-badge {
  background: rgba(0,0,0,0.78);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 7px;
  border-radius: 4px;
  line-height: 1.4;
  letter-spacing: 0.02em;
}
.pp-badge-primary { background: #f0ad14; color: #000; }
.pp-badge-before  { background: #1f7a8c; }
.pp-badge-private { background: #555; }

/* PDF tile (lease document gallery) — replaces image preview with a clear
 * red "PDF" badge + filename so users instantly recognise documents. */
.pp-card-pdf {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 12px;
  background: linear-gradient(180deg, #fff 0%, #f7f9fb 100%);
  border-bottom: 1px solid var(--color-border);
}
.pp-pdf-icon {
  background: #d93025;
  color: #fff;
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.04em;
  padding: 14px 18px;
  border-radius: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.18);
}
.pp-pdf-filename {
  font-size: var(--font-size-xs);
  color: var(--color-text);
  text-align: center;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 100%;
}

/* Drawer PDF preview — vertical stack, prominent button */
.pp-drawer-pdf {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 24px 16px;
  background: var(--color-surface-alt);
  border-radius: var(--radius-md);
}
.pp-drawer-pdf-icon {
  background: #d93025;
  color: #fff;
  font-weight: 700;
  font-size: 24px;
  padding: 20px 28px;
  border-radius: 8px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.20);
}
.pp-drawer-pdf-name {
  font-size: var(--font-size-sm);
  color: var(--color-text);
  text-align: center;
  word-break: break-word;
}

.pp-card-meta    { padding: 10px 12px; display: flex; flex-direction: column; gap: 4px; min-height: 0; }
.pp-card-type    { font-weight: 600; font-size: var(--font-size-sm); color: var(--color-text); line-height: 1.3; }
.pp-card-caption {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── Edit drawer ─────────────────────────────────────────────────────────── */
.pp-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(440px, 100vw);
  background: var(--color-surface);
  border-left: 1px solid var(--color-border);
  box-shadow: -8px 0 24px rgba(0,0,0,0.15);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.18s ease;
  z-index: 1000;
}
.pp-drawer.open { transform: translateX(0); }
.pp-drawer-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-weight: 700;
  font-size: var(--font-size-lg);
}
.pp-drawer-close {
  border: none; background: transparent;
  font-size: 24px; line-height: 1;
  cursor: pointer; color: var(--color-text-muted);
  padding: 4px 8px;
}
.pp-drawer-close:hover { color: var(--color-text); }

.pp-drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* Drawer preview shows the FULL uploaded image (no crop) so the user can
 * inspect what they've actually attached. Capped at 50vh so a tall portrait
 * doesn't push the form fields out of the drawer. */
.pp-drawer-preview {
  background: var(--color-surface-alt);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: center;
  max-height: 50vh;
}
.pp-drawer-preview img {
  max-width: 100%;
  max-height: 50vh;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}

.pp-label   { font-weight: 600; font-size: var(--font-size-sm); margin-top: var(--space-2); }
.pp-input   {
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: 8px 10px;
  font-family: inherit;
  font-size: var(--font-size-base);
  width: 100%;
  background: var(--color-surface);
  color: var(--color-text);
}
.pp-input:focus { outline: 2px solid var(--color-primary); outline-offset: -1px; border-color: var(--color-primary); }
/* Drawer flag checkboxes: fixed-size, top-aligned with the label so multi-line
 * text doesn't push the checkbox to the visual middle of the row. accent-color
 * keeps the checked state on-brand instead of inheriting the OS default. */
.pp-checklabel {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--font-size-sm);
  color: var(--color-text);
  padding: 6px 0;
  line-height: 1.4;
  cursor: pointer;
}
.pp-checklabel input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 2px 0 0 0;          /* nudge down 2px so it lines up with the first text line */
  flex-shrink: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

.pp-drawer-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
}
.pp-drawer-foot .pp-btn-danger { margin-right: auto; }

@media (max-width: 720px) {
  .pp-queue-row { grid-template-columns: minmax(0,1fr) auto; }
  .pp-queue-bar { grid-column: 1 / -1; }
  .pp-queue-status { display: none; }
  .pp-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* Inline percent-suffix on a number input. Wrap input in a div with this
 * class; the "%" appears inside the right edge of the input. */
.input-suffix-pct {
  position: relative;
  display: block;
}
.input-suffix-pct input { padding-right: 28px; }
.input-suffix-pct::after {
  content: '%';
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  font-weight: 600;
  pointer-events: none;
}

/* ============================================================================
 * Module sidebar (cross-page) — icon-only nav strip auto-injected by
 * /module-sidebar.js on every page. Mirrors the Dashboard's inline sidebar
 * so users never have to bounce back to Dashboard to switch modules.
 *
 * .ms-* namespace; doesn't collide with the Dashboard's existing .sb-* or
 * the Portfolio Admin sub-sidebar's .app-sidebar-*.
 * ============================================================================ */

/* Note: the previous left icon-strip (.ms-sidebar / .ms-btn / .ms-label)
 * was removed in the LodgeForce redesign. Module navigation moved to the
 * top tabs (`.app-nav-link`) injected by module-sidebar.js into
 * `.app-nav-inner`; per-module submenus stay in the cream contextual
 * sidebar (`.app-sidebar*`) injected by sidebar.js. */

/* ============================================================================
 * Sticky entity-edit toolbar (.entity-toolbar)
 *
 * Reusable across landlord-edit, property-edit, site-edit and face-edit so
 * the user always has Save + key actions in one click without scrolling.
 * Status pills (.bar-pill) and toolbar buttons (.bar-btn) sit alongside the
 * record's identity badge + name + parent context.
 *
 * Hidden by default (`hidden` attribute on the markup) and revealed by
 * page-level JS once an entity has been selected.
 * ============================================================================ */

.entity-toolbar {
  position: sticky;
  top: var(--nav-height);
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: 12px var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 1px 2px rgba(29, 39, 51, 0.04);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}
.entity-toolbar-info {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  flex: 1;
  min-width: 0;
}
.entity-toolbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.entity-code-badge {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--font-size-sm);
  color: var(--color-primary);
  background: var(--color-primary-soft);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  white-space: nowrap;
}
.entity-name-text {
  font-size: var(--font-size-md);
  font-weight: 600;
  color: var(--color-text);
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.entity-context-text {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
}
.entity-context-text a { color: var(--color-primary); font-weight: 600; }
.entity-context-text a:hover { color: var(--color-primary-hover); text-decoration: underline; }

.bar-pill {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  font-weight: 600;
  text-transform: capitalize;
  white-space: nowrap;
}
.bar-pill.green  { background: var(--color-success-bg); color: var(--color-success); }
.bar-pill.amber  { background: var(--color-warn-bg);    color: var(--color-warn); }
.bar-pill.orange { background: var(--color-orange-bg);  color: var(--color-orange); }
.bar-pill.red    { background: var(--color-danger-bg);  color: var(--color-danger); }
.bar-pill.muted  { background: var(--color-surface-alt); color: var(--color-text-muted); border: 1px solid var(--color-border); }

/* Clickable child-record pills (pill-links.js): "1 face →" links straight
   to the record; "3 sites ▾" drops a list of links. */
.bar-pill-link a { color: inherit; text-decoration: none; }
.bar-pill-link a:hover { text-decoration: underline; }
.bar-pill-menu { position: relative; }
.bar-pill-menu .bar-pill-toggle {
  border: 0; background: none; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer;
}
.bar-pill-menu .bar-pill-toggle:hover { text-decoration: underline; }
.bar-pill-menu .bar-pill-list {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  display: flex; flex-direction: column;
  min-width: 220px; max-height: 300px; overflow-y: auto;
  padding: 4px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md, 0 4px 16px rgba(0,0,0,.14));
  text-transform: none;
}
.bar-pill-menu .bar-pill-list a {
  padding: 6px 10px;
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--color-text);
  text-decoration: none;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.bar-pill-menu .bar-pill-list a:hover { background: var(--color-surface-alt); }

.bar-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  background: var(--color-surface);
  border: 1px solid var(--color-input-border);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font-size: var(--font-size-sm);
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s, border-color 0.12s;
  font-family: inherit;
}
.bar-btn:hover {
  background: var(--color-surface-alt);
  border-color: var(--color-border-strong);
  color: var(--color-text);
  text-decoration: none;
}
.bar-btn.primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-text-on-primary);
}
.bar-btn.primary:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-text-on-primary);
}
.bar-btn.accent {
  background: var(--color-active-accent);
  border-color: var(--color-active-accent);
  color: var(--color-text-on-primary);
}
.bar-btn.accent:hover {
  background: var(--color-active-accent-hover);
  border-color: var(--color-active-accent-hover);
}
.bar-btn svg {
  width: 14px; height: 14px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ============================================================================
 * Maintenance log panel (Phase 3) — list-style widget mounted via
 * mountMaintenancePanel(). Self-contained .ml-* namespace so styles
 * cannot leak.
 * ============================================================================ */

.ml-root {
  position: relative;
  margin-top: var(--space-4);
}

.ml-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.ml-title {
  margin: 0;
  font-size: var(--font-size-lg);
  font-weight: 700;
  color: var(--color-text);
}
.ml-header-actions { display: flex; gap: var(--space-2); align-items: center; }

.ml-btn {
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: 8px 14px;
  font-weight: 600;
  font-size: var(--font-size-base);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.ml-btn:hover { background: var(--color-surface-alt); border-color: var(--color-text-muted); }
.ml-btn-primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-text-on-primary);
}
.ml-btn-primary:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-text-on-primary);
}
.ml-btn-danger { border-color: #d14343; color: #d14343; background: var(--color-surface); }
.ml-btn-danger:hover { background: #fdecec; border-color: #b83434; }

/* ── List ───────────────────────────────────────────────────────────────── */
.ml-list { display: flex; flex-direction: column; gap: 8px; }

.ml-empty {
  text-align: center;
  color: var(--color-text-muted);
  padding: var(--space-5);
  font-size: var(--font-size-sm);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
}

.ml-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-3);
  align-items: flex-start;
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  cursor: pointer;
  transition: box-shadow 0.12s ease, border-color 0.12s ease;
}
.ml-row:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--color-text-muted);
}

.ml-row-l { display: flex; gap: var(--space-3); align-items: flex-start; min-width: 0; }
.ml-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ml-row-title { font-weight: 600; color: var(--color-text); font-size: var(--font-size-base); }
.ml-row-meta  { font-size: var(--font-size-xs); color: var(--color-text-muted); }
.ml-row-desc  {
  font-size: var(--font-size-sm); color: var(--color-text);
  margin-top: 4px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ml-row-r { display: flex; align-items: center; }

/* Severity badge — colour-coded so ops scan a long list quickly */
.ml-badge {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 4px 8px;
  border-radius: 4px;
  line-height: 1.4;
  white-space: nowrap;
  flex-shrink: 0;
}
.ml-badge-critical { background: #d14343; color: #fff; }
.ml-badge-high     { background: #f0801c; color: #fff; }
.ml-badge-medium   { background: #f0c14b; color: #1a1a1a; }
.ml-badge-low      { background: #999; color: #fff; }

/* Status pill */
.ml-status {
  font-size: 12px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--color-surface-alt);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.ml-status-open         { background: #fdecec; color: #b83434; }
.ml-status-assigned     { background: #fff4e3; color: #b8651b; }
.ml-status-in-progress  { background: #e3edff; color: #1f4ab8; }
.ml-status-resolved     { background: #e3f5e3; color: #2a7a2a; }
.ml-status-cannot-fix   { background: #f0f0f0; color: #555; }
.ml-status-deferred     { background: #f0f0f0; color: #555; }

/* ── Drawer ─────────────────────────────────────────────────────────────── */
.ml-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(440px, 100vw);
  background: var(--color-surface);
  border-left: 1px solid var(--color-border);
  box-shadow: -8px 0 24px rgba(0,0,0,0.15);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.18s ease;
  z-index: 1000;
}
.ml-drawer.open { transform: translateX(0); }
.ml-drawer-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-weight: 700;
  font-size: var(--font-size-lg);
}
.ml-drawer-title { font-weight: 700; }
.ml-drawer-close {
  border: none; background: transparent;
  font-size: 24px; line-height: 1;
  cursor: pointer; color: var(--color-text-muted);
  padding: 4px 8px;
}
.ml-drawer-close:hover { color: var(--color-text); }

.ml-drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ml-label   {
  font-weight: 600;
  font-size: var(--font-size-sm);
  margin-top: var(--space-2);
  color: var(--color-text);
}
.ml-input   {
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: 8px 10px;
  font-family: inherit;
  font-size: var(--font-size-base);
  width: 100%;
  background: var(--color-surface);
  color: var(--color-text);
}
.ml-input:focus { outline: 2px solid var(--color-primary); outline-offset: -1px; border-color: var(--color-primary); }

.ml-checklabel {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--font-size-sm);
  color: var(--color-text);
  padding: 6px 0;
  line-height: 1.4;
  cursor: pointer;
}
.ml-checklabel input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin: 2px 0 0 0;
  flex-shrink: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

.ml-edit-only {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}

/* Severity hint shown under the severity dropdown */
.ml-severity-hint {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-top: 4px;
  min-height: 1em;
}
.ml-severity-hint-critical {
  color: #b83434;
  font-weight: 600;
}
.ml-severity-hint-warn {
  color: #b8651b;
}

.ml-drawer-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
}
.ml-drawer-foot .ml-btn-danger { margin-right: auto; }

/* Error banner — appears above the list when an action fails */
.ml-error-banner {
  padding: 10px 14px;
  margin-bottom: var(--space-3);
  border-radius: var(--radius-md);
  background: #fdecec;
  color: #b83434;
  font-size: var(--font-size-sm);
  font-weight: 600;
}

/* ============================================================================
 * Permit document panel — list-style widget mounted via mountPermitPanel().
 * Self-contained .pmt-* namespace — NOT .pp-*, which photo-panel.js already
 * claims (mountPhotoPanel's drawer/root/checklabel classes above); both
 * panels mount on the same face/site pages so a shared prefix would collide.
 * Structurally a copy of .ml-* above (header + list + slide-in drawer).
 * ============================================================================ */

.pmt-root { position: relative; margin-top: var(--space-4); }

.pmt-header { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.pmt-title { margin: 0; font-size: var(--font-size-lg); font-weight: 700; color: var(--color-text); }
.pmt-header-actions { display: flex; gap: var(--space-2); align-items: center; }

.pmt-btn {
  border: 1px solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-md);
  padding: 6px 12px;
  font-weight: 600;
  font-size: var(--font-size-sm);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.pmt-btn:hover { background: var(--color-surface-alt); border-color: var(--color-text-muted); }
.pmt-btn:disabled { opacity: 0.6; cursor: default; }
.pmt-btn-primary { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-text-on-primary); }
.pmt-btn-primary:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: var(--color-text-on-primary); }
.pmt-btn-danger { border-color: var(--color-danger); color: var(--color-danger); background: var(--color-surface); }
.pmt-btn-danger:hover { background: var(--color-danger-bg); }

.pmt-group-title {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--color-text-muted); margin: var(--space-3) 0 var(--space-2);
}
.pmt-group-title:first-child { margin-top: 0; }

.pmt-list { display: flex; flex-direction: column; gap: 8px; }
.pmt-empty {
  text-align: center; color: var(--color-text-muted); padding: var(--space-4);
  font-size: var(--font-size-sm); border: 1px dashed var(--color-border); border-radius: var(--radius-md);
}
.pmt-error {
  padding: 10px 14px; border-radius: var(--radius-md);
  background: var(--color-danger-bg); color: var(--color-danger);
  font-size: var(--font-size-sm); font-weight: 600;
}

.pmt-row {
  display: grid; grid-template-columns: 1fr auto auto;
  gap: var(--space-3); align-items: center;
  padding: 10px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.pmt-row-l { display: flex; gap: var(--space-3); align-items: flex-start; min-width: 0; }
.pmt-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pmt-row-title { font-weight: 600; color: var(--color-text); font-size: var(--font-size-base); }
.pmt-row-meta  { font-size: var(--font-size-xs); color: var(--color-text-muted); }
.pmt-row-r { display: flex; align-items: center; }
.pmt-row-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* Expiry-grade badge — the four-tier colour set (permits). */
.pmt-badge {
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  padding: 4px 9px; border-radius: var(--radius-pill);
  white-space: nowrap; flex-shrink: 0;
}
.pmt-badge-green  { background: var(--color-success-bg); color: var(--color-success); }
.pmt-badge-amber  { background: var(--color-warn-bg);    color: var(--color-warn); }
.pmt-badge-orange { background: var(--color-orange-bg);  color: var(--color-orange); }
.pmt-badge-red    { background: var(--color-danger-bg);  color: var(--color-danger); }
.pmt-badge-muted  { background: var(--color-surface-alt); color: var(--color-text-muted); border: 1px solid var(--color-border); }

/* ── Drawer — same shape as .ml-drawer ─────────────────────────────────── */
.pmt-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(440px, 100vw);
  background: var(--color-surface); border-left: 1px solid var(--color-border);
  box-shadow: -8px 0 24px rgba(0,0,0,0.15);
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform 0.18s ease;
  z-index: 1000;
}
.pmt-drawer.open { transform: translateX(0); }
.pmt-drawer-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-weight: 700; font-size: var(--font-size-lg);
}
.pmt-drawer-title { font-weight: 700; }
.pmt-drawer-close {
  border: none; background: transparent; font-size: 24px; line-height: 1;
  cursor: pointer; color: var(--color-text-muted); padding: 4px 8px;
}
.pmt-drawer-close:hover { color: var(--color-text); }
.pmt-drawer-body { flex: 1; overflow-y: auto; padding: var(--space-4); display: flex; flex-direction: column; gap: 4px; }
.pmt-label { font-weight: 600; font-size: var(--font-size-sm); margin-top: var(--space-2); color: var(--color-text); }
.pmt-input {
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  padding: 8px 10px; font-family: inherit; font-size: var(--font-size-base);
  width: 100%; background: var(--color-surface); color: var(--color-text);
}
.pmt-input:focus { outline: 2px solid var(--color-primary); outline-offset: -1px; border-color: var(--color-primary); }
.pmt-supersede-note {
  font-size: var(--font-size-sm); color: var(--color-text-muted);
  padding: 8px 10px; margin-bottom: var(--space-2);
  background: var(--color-surface-alt); border-radius: var(--radius-md);
}
.pmt-supersede-note:empty { display: none; }
.pmt-drawer-foot { display: flex; justify-content: flex-end; gap: var(--space-2); padding: var(--space-3) var(--space-4); border-top: 1px solid var(--color-border); }

/* ── Check Permits — logged, dated colour boxes ────────────────────────── */
.pmt-audit-section { margin-top: var(--space-5); }
.pmt-audit-log { display: flex; flex-direction: column; gap: 8px; }
.pmt-audit-box {
  border-radius: var(--radius-md);
  padding: 10px 14px;
  border: 1px solid var(--color-border);
}
.pmt-audit-box-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); margin-bottom: 4px; }
.pmt-audit-status-label { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
.pmt-audit-date { font-size: var(--font-size-xs); color: var(--color-text-muted); white-space: nowrap; }
.pmt-audit-comment { font-size: var(--font-size-sm); color: var(--color-text); line-height: 1.45; }
.pmt-audit-green  { background: var(--color-success-bg); border-color: var(--color-success-border); }
.pmt-audit-green  .pmt-audit-status-label { color: var(--color-success); }
.pmt-audit-amber  { background: var(--color-warn-bg); border-color: var(--color-warn-border); }
.pmt-audit-amber  .pmt-audit-status-label { color: var(--color-warn); }
.pmt-audit-orange { background: var(--color-orange-bg); border-color: var(--color-orange-border); }
.pmt-audit-orange .pmt-audit-status-label { color: var(--color-orange); }
.pmt-audit-red    { background: var(--color-danger-bg); border-color: var(--color-danger-border); }
.pmt-audit-red    .pmt-audit-status-label { color: var(--color-danger); }
