/* =====================================================
   PHASE K.E — CLIENT PORTAL
   Reuses .client-* tokens from styles.css for consistency.
   These rules add the portal-specific surfaces:
     - portal-dropzone (upload area)
     - portal-queue (in-flight uploads)
     - portal-properties (submitted properties list)
     - portal-toast (non-blocking error/success messages)
   ===================================================== */

.portal-card {
  /* Slightly wider than the doc-upload card — the property list rows
     have more horizontal content (thumb + address + status). */
  max-width: 640px;
}

/* ── DROPZONE ─────────────────────────────────────── */

.portal-dropzone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.75rem 1rem;
  margin-bottom: 1.25rem;
  background: var(--bg-2);
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: center;
  transition: border-color .15s ease, background .15s ease, transform .15s ease;
}
.portal-dropzone:hover { border-color: var(--accent-strong); }
.portal-dropzone.is-drag-over {
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: scale(1.005);
}

.portal-dropzone-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--avatar-text);
  font-size: 1.5rem; font-weight: 600;
  margin-bottom: 0.25rem;
}
.portal-dropzone-title {
  font-size: 1rem; font-weight: 600;
  color: var(--text-primary);
}
.portal-dropzone-hint {
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.portal-dropzone-warn {
  margin-top: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: rgba(245, 158, 11, 0.10);
  border: 1px solid rgba(245, 158, 11, 0.32);
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  color: var(--st-amber);
  font-weight: 500;
  line-height: 1.35;
  max-width: 380px;
}
.portal-dropzone input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
}

/* ── UPLOAD QUEUE ─────────────────────────────────── */

.portal-queue {
  list-style: none;
  padding: 0;
  margin: 0 0 1.25rem 0;
  display: flex; flex-direction: column;
  gap: 0.5rem;
}
.portal-queue-item {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.75rem 0.875rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  animation: portal-fade-in .25s ease;
}
.portal-queue-item.is-error { border-color: rgba(239, 68, 68, 0.4); background: var(--danger-soft); }
.portal-queue-item.is-added,
.portal-queue-item.is-duplicate {
  border-color: rgba(16, 185, 129, 0.35);
  background: rgba(16, 185, 129, 0.05);
}

.portal-queue-thumb {
  width: 44px; height: 44px;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-3);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.portal-queue-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
}
.portal-queue-thumb-placeholder { font-size: 1.25rem; opacity: 0.6; }

.portal-queue-body {
  flex: 1;
  min-width: 0;
  display: flex; flex-direction: column; gap: 0.125rem;
}
.portal-queue-title {
  font-size: 0.875rem; font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.portal-queue-status {
  font-size: 0.8125rem;
  color: var(--text-secondary);
}

.portal-queue-action { flex-shrink: 0; display: flex; align-items: center; }
.portal-queue-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  background: var(--success);
  color: white;
  border-radius: 999px;
  font-size: 0.875rem; font-weight: 700;
}
.portal-queue-retry {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  padding: 0.375rem 0.75rem;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  cursor: pointer;
}
.portal-queue-retry:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* ── SECTION DIVIDER + TITLE ──────────────────────── */

.portal-section-divider {
  height: 1px;
  background: var(--border);
  margin: 1.25rem 0 1rem 0;
}
.portal-section-title {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 0.75rem;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

/* ── EMPTY STATE ──────────────────────────────────── */

.portal-empty {
  text-align: center;
  padding: 1.5rem 0;
  color: var(--text-secondary);
}
.portal-empty p { margin: 0.125rem 0; }

/* ── HOUSED-CLIENT CLOSED PAGE (2026-07) ──────────── */

.portal-closed {
  text-align: center;
  padding: 2.5rem 1rem 3rem;
}
.portal-closed .portal-closed-title {
  margin: 0.75rem 0 0.5rem;
  font-size: 1.35rem;
  line-height: 1.35;
}
.portal-closed p { margin: 0.25rem 0; }

/* ── PROPERTIES LIST ──────────────────────────────── */

.portal-properties {
  list-style: none;
  padding: 0; margin: 0;
  display: flex; flex-direction: column;
  gap: 0.625rem;
}
.portal-property {
  display: flex;
  gap: 0.875rem;
  padding: 0.75rem;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: background .25s ease, border-color .25s ease, transform .25s ease;
  align-items: stretch;
}
.portal-property:hover { border-color: var(--border-strong); }
.portal-property.is-highlight {
  background: rgba(178, 34, 34, 0.08);
  border-color: var(--accent-strong);
  transform: translateX(2px);
}

.portal-property-thumb {
  /* K.F.3.A.5 — 16:9 thumbnail. OneHome screenshots place the photo
     near the top of the layout, so we cover-fit and pin the crop to
     center-top so the building stays visible. */
  width: 120px;
  aspect-ratio: 16 / 9;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg-3);
  border-radius: var(--radius-sm);
  overflow: hidden;
  align-self: flex-start;
}
.portal-property-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}
.portal-property-thumb-placeholder { font-size: 1.5rem; opacity: 0.4; }
.portal-property-specs { margin-top: 0.0625rem; }

.portal-property-body {
  flex: 1;
  min-width: 0;
  display: flex; flex-direction: column; gap: 0.25rem;
  justify-content: center;
}
.portal-property-address {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}
.portal-property-meta {
  display: flex; flex-wrap: wrap;
  gap: 0.625rem;
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.portal-property-price { font-weight: 500; color: var(--text-primary); }
.portal-property-notes {
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  font-style: italic;
}

.portal-property-status {
  flex-shrink: 0;
  display: flex; align-items: flex-start;
}
.portal-status-badge {
  display: inline-flex; align-items: center;
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--bg-3);
  border: 1px solid var(--border);
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}
.portal-status-badge.portal-status-inquired {
  background: rgba(59, 130, 246, 0.10);
  border-color: rgba(59, 130, 246, 0.3);
  color: var(--st-blue);
}
.portal-status-badge.portal-status-outreach_sent,
.portal-status-badge.portal-status-showing_set,
.portal-status-badge.portal-status-submitted {
  background: rgba(245, 158, 11, 0.10);
  border-color: rgba(245, 158, 11, 0.3);
  color: var(--st-amber);
}
.portal-status-badge.portal-status-approved {
  background: rgba(16, 185, 129, 0.10);
  border-color: rgba(16, 185, 129, 0.35);
  color: var(--st-green2);
}
.portal-status-badge.portal-status-denied,
.portal-status-badge.portal-status-withdrawn {
  background: var(--danger-soft);
  border-color: rgba(239, 68, 68, 0.3);
  color: var(--danger);
}

/* ── TOASTS ───────────────────────────────────────── */

.portal-toasts {
  position: fixed;
  bottom: 1rem; left: 1rem; right: 1rem;
  display: flex; flex-direction: column;
  gap: 0.5rem;
  pointer-events: none;
  z-index: 100;
}
.portal-toast {
  pointer-events: auto;
  padding: 0.75rem 1rem;
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 0.875rem;
  color: var(--text-primary);
  animation: portal-fade-in .25s ease;
  max-width: 480px;
  margin: 0 auto;
  width: calc(100% - 0px);
}
.portal-toast-success { border-color: rgba(16, 185, 129, 0.4); }
.portal-toast-error   { border-color: rgba(239, 68, 68, 0.5); background: var(--danger-soft); }
.portal-toast-info    { border-color: var(--border-strong); }

@keyframes portal-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── MOBILE TIGHTENING ────────────────────────────── */

@media (max-width: 480px) {
  .portal-card { padding: 1.5rem 1.125rem; }
  .portal-dropzone { padding: 1.5rem 0.75rem; }
  .portal-property {
    padding: 0.625rem;
    gap: 0.625rem;
  }
  .portal-property-thumb { width: 96px; }
  .portal-property-address { font-size: 0.875rem; }
  .portal-property-meta { font-size: 0.75rem; gap: 0.5rem; }
  .portal-status-badge { font-size: 0.6875rem; padding: 0.125rem 0.4375rem; }
  .portal-pill { font-size: 0.6875rem; padding: 0.1875rem 0.5rem; }
  .portal-section-title { font-size: 0.875rem; }
}

/* ── K.F.3.B.4 ASYNC UPLOAD HINT ──────────────────── */

.portal-upload-busy {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: 0.75rem;
  padding: 0.625rem 0.875rem;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-3);
  color: var(--text-secondary);
  font-size: 0.875rem;
}

/* ── K.F.3.A.8 STATUS PILLS ────────────────────────── */

/* Locked color spec from SPEC §A.8. Background at ~14% alpha, border
   at full alpha, text at full alpha. Mirrors the property header
   "Available" badge style on the admin side. */
.portal-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.1875rem 0.625rem;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  border: 1px solid transparent;
  line-height: 1.4;
}
.portal-pill-gray {
  background: rgba(148, 163, 184, 0.14);
  border-color: rgba(148, 163, 184, 0.55);
  color: rgb(148, 163, 184);
}
.portal-pill-yellow {
  background: rgba(245, 158, 11, 0.14);
  border-color: rgba(245, 158, 11, 0.65);
  color: rgb(245, 158, 11);
}
.portal-pill-green {
  background: rgba(16, 185, 129, 0.14);
  border-color: rgba(16, 185, 129, 0.65);
  color: rgb(16, 185, 129);
}
.portal-pill-red {
  background: rgba(239, 68, 68, 0.14);
  border-color: rgba(239, 68, 68, 0.65);
  color: rgb(239, 68, 68);
}
.portal-pill-leased {
  background: rgba(71, 85, 105, 0.20);
  border-color: rgba(71, 85, 105, 0.85);
  color: rgb(148, 163, 184);
}
/* UI/UX overhaul — off-market now reads as a "not available" state and
   shares the red treatment with declined / not-pursuing. data-pill-state
   stays "off_market" so the client filter can still bucket it. */
.portal-pill-off-market {
  background: rgba(239, 68, 68, 0.14);
  border-color: rgba(239, 68, 68, 0.65);
  color: rgb(239, 68, 68);
}

/* ── UI/UX overhaul: client status filter + status display ─────────── */

/* Client status filter chips (separate label set from admin's 4-color).
   RAP-125 — the row is [ scrollable front chips ][ More ▾ ]. The front
   chips scroll horizontally on a phone rather than wrapping into a wall
   (Mauricio's Jul 28 review); "More" sits OUTSIDE the scroller so it is
   always reachable AND so its dropdown isn't clipped by the overflow. */
.portal-filter-bar { margin: 0.25rem 0 0.875rem; }
.portal-filter-row {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}
.portal-filter-chips {
  display: flex;
  gap: 0.375rem;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  /* Momentum swipe without a visible scrollbar eating vertical space. */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 1px;
}
.portal-filter-chips::-webkit-scrollbar { display: none; }
.portal-filter-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  flex: 0 0 auto;
  border: 1px solid var(--border-strong);
  background: var(--bg-2);
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 0.3125rem 0.75rem;
  min-height: 2rem;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
.portal-filter-chip:hover { border-color: var(--accent); color: var(--text-primary); }
.portal-filter-chip.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
/* Counts read as secondary weight inside the chip, full-contrast when
   the chip is the active one. */
.portal-filter-count { opacity: 0.72; font-weight: 500; }
.portal-filter-chip.is-active .portal-filter-count { opacity: 0.85; }

/* ── RAP-125: "More ▾" dropdown for the rare, high-stakes states ───── */
.portal-filter-more { position: relative; flex: 0 0 auto; }
.portal-filter-more-toggle { max-width: 11rem; }
.portal-filter-more-toggle > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
}
.portal-filter-caret { flex: 0 0 auto; font-size: 0.75rem; line-height: 1; }
/* Tap-anywhere-to-dismiss layer. Sits under the menu, over everything
   else; transparent so the list stays visible while choosing. */
.portal-filter-more-backdrop {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: transparent;
}
.portal-filter-more-menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  z-index: 31;
  min-width: 14rem;
  max-width: min(18rem, calc(100vw - 2.5rem));
  display: flex;
  flex-direction: column;
  padding: 0.25rem;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
.portal-filter-more-item {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  /* Thumb-sized: 44px is the iOS minimum touch target. */
  min-height: 2.75rem;
  padding: 0.5rem 0.6875rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.portal-filter-more-item:hover { background: var(--bg-3); color: var(--text-primary); }
.portal-filter-more-item.is-active { background: var(--accent); color: #fff; }

/* ── RAP-125: "Showing: Ready to view (3)" active-filter indicator ──── */
.portal-filter-showing {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.5rem;
  font-size: 0.8125rem;
}
.portal-filter-showing-label { color: var(--text-secondary); }
.portal-filter-showing-value {
  font-weight: 700;
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.portal-filter-clear {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Thumb-sized clear target — the chip row is small, this must not be. */
  width: 2.25rem;
  height: 2.25rem;
  margin-left: auto;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--bg-2);
  color: var(--text-secondary);
  font: inherit;
  font-size: 0.875rem;
  line-height: 1;
  cursor: pointer;
}
.portal-filter-clear:hover {
  border-color: var(--accent);
  color: var(--text-primary);
}

/* Above phone width there is room to wrap into two tidy rows, which
   beats a hidden horizontal scroller for mouse users who get no swipe
   affordance. The 640px card can't fit all five front chips on one line
   even on desktop, so this rule earns its keep. */
@media (min-width: 481px) {
  .portal-filter-chips {
    flex-wrap: wrap;
    overflow-x: visible;
    row-gap: 0.375rem;
  }
}

/* Thumb targets grow on the surface that is actually thumbed. The chip
   row swipes horizontally here (no wrap), so chips stay compact while the
   two things a finger must hit — the More toggle and the clear ✕ — get
   bigger, and the menu rows clear 44px comfortably.

   These MUST stay below the base .portal-filter-* rules above: the
   MOBILE TIGHTENING block earlier in this file sits before them, and at
   equal specificity source order decides. Putting them there silently
   loses the cascade (caught live at 375px — the ✕ measured 36px, not
   40px). */
@media (max-width: 480px) {
  .portal-filter-more-toggle { min-height: 2.25rem; max-width: 9.5rem; }
  .portal-filter-clear { width: 2.5rem; height: 2.5rem; }
  .portal-filter-more-item { min-height: 3rem; }
}

/* Not-available rows: dim + strike the address (pill stays solid red). */
.portal-property-unavailable { opacity: 0.62; }
.portal-property-unavailable .portal-property-address { text-decoration: line-through; }

/* "Getting access" subtext + plain-language access-type line. */
.portal-getting-access-sub { margin-top: 0.125rem; font-style: italic; }
.portal-access-hint {
  margin-top: 0.1875rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--st-amber, #f59e0b);
}

/* Amber tour / open-house badge (prominent, NOT red). */
.portal-tour-badge {
  display: inline-flex;
  align-items: center;
  margin-top: 0.25rem;
  padding: 0.1875rem 0.5rem;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  font-weight: 700;
  background: rgba(245, 158, 11, 0.14);
  border: 1px solid rgba(245, 158, 11, 0.5);
  color: var(--st-amber, #f59e0b);
  white-space: nowrap;
}
.portal-tour-badge-done {
  background: rgba(16, 185, 129, 0.12);
  border-color: rgba(16, 185, 129, 0.45);
  color: var(--st-green2, #10b981);
  font-weight: 600;
}

/* Detail status section extras. */
.portal-detail-status-sub { margin-top: 0.25rem; font-style: italic; }
.portal-detail-access-hint {
  margin-top: 0.375rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--st-amber, #f59e0b);
}
.portal-detail-status .portal-tour-badge { margin-top: 0.5rem; }
/* Off-market detail accent (red) — mirrors the recolored pill. */
.portal-detail-status-off-market { border-color: rgba(239, 68, 68, 0.5); background: rgba(239, 68, 68, 0.08); }

/* ── PHASE K.F: STATUS DASHBOARD ───────────────────── */

/* Top bar: logo + language toggle (list view) / back link + language toggle (detail).
   K.F.1 — center the logo. The toggle and (in detail view) the back
   link are positioned absolutely against the topbar, leaving the
   centered child (the logo on list view, empty space on detail view)
   to sit dead-center regardless of toggle width. */
.portal-topbar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1rem;
  min-height: 2.25rem;
}
.portal-topbar .client-brand {
  margin-bottom: 0;  /* override the 1.75rem default used on the login page */
}
.portal-lang-toggle {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.875rem;
  font-weight: 600;
}
.portal-lang-btn {
  background: none;
  border: 0;
  padding: 0.25rem 0.5rem;
  color: var(--text-secondary);
  cursor: pointer;
  font: inherit;
  letter-spacing: 0.04em;
}
.portal-lang-btn.is-active {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.portal-lang-sep { color: var(--text-muted); }
.portal-back-link {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.875rem;
}
.portal-back-link:hover { color: var(--accent); }

/* Property card status badges */
.portal-property-link {
  display: flex;
  gap: 0.75rem;
  text-decoration: none;
  color: inherit;
  width: 100%;
  align-items: center;
}
.portal-property-mls { margin-top: 0.125rem; }
.portal-property-tour {
  display: inline-block;
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  color: var(--text-secondary);
}
.portal-status-locked       { color: #10b981; border-color: rgba(16, 185, 129, 0.5); }
.portal-status-declined     { color: #ef4444; border-color: rgba(239, 68, 68, 0.5); }
.portal-status-unavailable  { color: #f59e0b; border-color: rgba(245, 158, 11, 0.5); }
.portal-status-pending      { color: var(--text-secondary); border-color: var(--border-strong); }

/* ── RAP-126: phone-width card balance ─────────────────────────────
   The three columns are thumb | body | status, and BOTH outer columns
   are flex-shrink:0 — so at 375px they took 96px + 105px of a 297px
   row and the address was left with ~50px, wrapping one word per line.
   Spanish made it worse: "Solicitud enviada" is a wider pill than
   "App submitted", so the status column demanded even more.

   The fix drops the status out of the horizontal contest: a 2-column
   grid where the badge sits BELOW the address in the same column, so
   the address gets the whole width the photo doesn't use (~167px, up
   from ~50px). Chosen over shrinking the thumbnail because the photo is
   how clients recognise a home in a list, and over letting the status
   column shrink because a wrapped pill reads worse than a wrapped
   address.

   MUST live below .portal-property-link above: the MOBILE TIGHTENING
   block sits earlier in this file, and at equal specificity source
   order decides — putting these there would silently lose (the same
   trap RAP-125 hit with its tap targets). */
@media (max-width: 480px) {
  .portal-property-link {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr);
    grid-template-areas:
      "thumb body"
      "thumb status";
    align-content: start;
    align-items: start;
    gap: 0.25rem 0.625rem;
  }
  .portal-property-thumb  { grid-area: thumb; }
  .portal-property-body   { grid-area: body; }
  .portal-property-status { grid-area: status; }
}

/* Detail view */
.portal-detail-address {
  font-size: 1.25rem;
  margin: 0 0 0.25rem;
}
.portal-detail-section {
  margin-top: 1.25rem;
}
.portal-detail-section-title {
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 0.5rem;
}
.portal-detail-status {
  padding: 0.875rem 1rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--bg-1);
}
.portal-detail-status-headline {
  font-weight: 600;
  font-size: 1rem;
}
.portal-detail-status-detail {
  margin-top: 0.25rem;
}
.portal-detail-status-locked       { border-color: rgba(16, 185, 129, 0.5); background: rgba(16, 185, 129, 0.08); }
.portal-detail-status-declined     { border-color: rgba(239, 68, 68, 0.5); background: rgba(239, 68, 68, 0.08); }
.portal-detail-status-unavailable  { border-color: rgba(245, 158, 11, 0.5); background: rgba(245, 158, 11, 0.08); }
.portal-detail-status-pending      { }

/* K.F.4.A.1 — five-state pill on the detail page mirrors the list. */
.portal-detail-status-gray   { }
.portal-detail-status-yellow { border-color: rgba(234, 179, 8, 0.5); background: rgba(234, 179, 8, 0.08); }
.portal-detail-status-green  { border-color: rgba(16, 185, 129, 0.5); background: rgba(16, 185, 129, 0.08); }
.portal-detail-status-red    { border-color: rgba(239, 68, 68, 0.5); background: rgba(239, 68, 68, 0.08); }
.portal-detail-status-leased { border-color: rgba(245, 158, 11, 0.5); background: rgba(245, 158, 11, 0.08); }
.portal-detail-status-pill   { font-size: 0.875rem; padding: 0.25rem 0.625rem; }

.portal-detail-tour,
.portal-detail-fields {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 0.375rem;
  font-size: 0.9375rem;
}

.portal-detail-actions {
  display: flex;
  gap: 0.625rem;
  margin-top: 1.5rem;
  flex-wrap: wrap;
}
.portal-detail-actions button {
  flex: 1 1 160px;
  min-height: 48px;
  font-weight: 600;
}

/* K.F.5.A — portal approve/decline buttons. Both stack on .primary
   to inherit shape (border-radius, padding, font-size); these
   modifiers override only the background + hover so the two buttons
   are visually paired and color-coded to their target state. The
   green/red tokens match the K.F.3 status pill spec. */
.portal-action-approve {
  background: rgb(16, 185, 129);
}
.portal-action-approve:hover:not(:disabled) {
  background: rgb(5, 150, 105);
  color: white;
}
.portal-action-decline {
  background: rgb(239, 68, 68);
}
.portal-action-decline:hover:not(:disabled) {
  background: rgb(220, 38, 38);
  color: white;
}

/* RAP-127 — the single decline control carries a one-line explanation of
   what it will actually do, because the same button means two different
   things depending on the state (end the interest now vs. ask staff to
   remove it). Stating it before the tap, not only in the modal. */
.portal-decline-sub {
  margin: 0.5rem 0 0;
  text-align: center;
  line-height: 1.4;
}
/* The notify-only states render one button alone, so it should not
   stretch the full width the way the paired approve/decline row does. */
.portal-detail-actions-single {
  flex-direction: column;
  align-items: stretch;
}
.portal-detail-actions-single .portal-decline-sub { margin-top: 0.125rem; }

/* ── K.F.5.B — ACCESS DETAILS SECTION ────────────────────────────
   Renders only when admin has opted in AND the interest is yellow
   AND lockbox info is set (server-side gating). Distinct subtle
   green tint draws the eye since this is the only actionable info
   on the page when ready-to-view. */
.portal-detail-access {
  border: 1px solid rgba(16, 185, 129, 0.4);
  background: rgba(16, 185, 129, 0.06);
  border-radius: var(--radius-md);
  padding: 0.875rem 1rem;
}
.portal-detail-access .portal-detail-section-title {
  color: rgb(16, 185, 129);
  margin-bottom: 0.625rem;
}
.portal-detail-access-fields {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.875rem;
  row-gap: 0.375rem;
  margin: 0;
  font-size: 0.9375rem;
}
.portal-detail-access-fields dt {
  color: var(--text-secondary);
  font-size: 0.8125rem;
  align-self: center;
}
.portal-detail-access-fields dd {
  margin: 0;
  color: var(--text-primary);
  word-break: break-word;
}
.portal-access-code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.0625rem 0.375rem;
  background: rgba(16, 185, 129, 0.16);
  color: rgb(16, 185, 129);
  border-radius: 4px;
}
/* UI/UX overhaul Increment C — free-text notes live inside the green
   "How to view this home" box, with a subtle divider so the free text and the
   structured rows read as one section rather than a jumble.

   RAP-128 flipped the order: "Showing details" (the free text) now comes FIRST
   and the structured rows follow, because the text says what to do and the rows
   are reference data. So the divider had to move to the other side of the pair —
   with only the old `fields + body` rule, the rows crowded straight up against
   the last line of the text with no gap at all. Both directions are declared so
   whichever order renders, the pair stays legible. */
.portal-detail-access .portal-detail-access-fields + .portal-detail-client-notes-body,
.portal-detail-access .portal-detail-client-notes-body + .portal-detail-access-fields {
  margin-top: 0.625rem;
  padding-top: 0.625rem;
  border-top: 1px solid rgba(16, 185, 129, 0.18);
}

@media (max-width: 480px) {
  .portal-detail-access { padding: 0.75rem 0.875rem; }
  .portal-detail-access-fields {
    grid-template-columns: 1fr;
    row-gap: 0.1875rem;
  }
  .portal-detail-access-fields dt { margin-top: 0.375rem; }
  .portal-detail-access-fields dt:first-child { margin-top: 0; }
}

/* Notes thread */
.portal-notes-thread {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.portal-notes-add-btn {
  align-self: flex-start;
}
.portal-notes-composer {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.portal-notes-textarea {
  width: 100%;
  min-height: 84px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-1);
  color: var(--text-primary);
  padding: 0.5rem 0.625rem;
  font: inherit;
  resize: vertical;
}
.portal-notes-composer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.portal-notes-composer-actions {
  display: flex;
  gap: 0.5rem;
}
.portal-notes-counter {
  font-variant-numeric: tabular-nums;
}
.portal-notes-error {
  color: var(--danger);
  font-size: 0.8125rem;
  margin: 0;
}
.portal-notes-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}
.portal-note {
  padding: 0.625rem 0.75rem;
  border-radius: var(--radius-sm);
  background: var(--bg-1);
  border: 1px solid var(--border-strong);
}
.portal-note-client {
  background: rgba(99, 102, 241, 0.08);
  border-color: rgba(99, 102, 241, 0.3);
}
.portal-note-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}
.portal-note-author {
  font-weight: 600;
  font-size: 0.875rem;
}
.portal-note-body {
  font-size: 0.9375rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.portal-notes-empty { margin: 0; }

/* Modal overlay (used by both lock-in and decline modals) */
.portal-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 200;
  animation: portal-fade-in .15s ease;
}
.portal-modal {
  background: var(--bg-0);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 1.25rem;
  max-width: 480px;
  width: 100%;
}
.portal-modal-title {
  font-size: 1.125rem;
  margin: 0 0 0.75rem;
}
.portal-modal-en-only {
  font-size: 0.8125rem;
  color: var(--text-muted);
  margin: 0 0 0.75rem;
}
.portal-modal-intro {
  margin: 0 0 0.5rem;
}
.portal-modal-bullets {
  margin: 0 0 1rem;
  padding-left: 1.25rem;
  font-size: 0.9375rem;
}
.portal-modal-bullets li + li { margin-top: 0.5rem; }
.portal-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.625rem;
  flex-wrap: wrap;
}
.portal-modal-actions button {
  min-height: 44px;
  padding: 0.5rem 0.875rem;
  /* Overrides the global `button.primary { width: 100% }` — a login-form
     rule that also caught these. It forced the confirm button to fill the
     row, so it ALWAYS wrapped to its own line and left "Cancel" stranded
     above it, at every width. Auto width lets the pair sit side by side;
     the phone block below re-stretches them deliberately. */
  width: auto;
}

/* RAP-127 — at phone width the two buttons don't fit one line, and the
   flex-end + wrap left "Cancel" stranded above a full-width confirm.
   Now an explicit stack. column-reverse puts the destructive confirm on
   top and Cancel nearest the thumb, so the easy tap is the safe one.
   This modal is the decline confirmation for every state since the raw
   window.confirm() was retired, so it has to hold up at 375px. */
@media (max-width: 480px) {
  .portal-modal-actions {
    flex-direction: column-reverse;
    flex-wrap: nowrap;
  }
  .portal-modal-actions button { width: 100%; }
}

/* ─────────────────────────────────────────────────────────────────
   K.F.4.B — sticky failure banners (top of portal card)
   ───────────────────────────────────────────────────────────────── */

.portal-banner-stack {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0;
  margin: 0 0 1rem;
}
.portal-banner-stack:empty { margin: 0; }

.portal-banner {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  padding: 0.75rem 0.875rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  font-size: 0.9375rem;
  line-height: 1.4;
}
.portal-banner-info {
  border-color: rgba(59, 130, 246, 0.45);
  background: rgba(59, 130, 246, 0.10);
}
.portal-banner-warn {
  border-color: rgba(234, 179, 8, 0.5);
  background: rgba(234, 179, 8, 0.10);
}
.portal-banner-icon { font-size: 1.125rem; line-height: 1.2; }
.portal-banner-text { flex: 1; min-width: 0; }
.portal-banner-close {
  background: transparent;
  border: none;
  padding: 0 0.375rem;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  flex-shrink: 0;
}
.portal-banner-close:hover { color: var(--text-primary); }
.portal-banner-more {
  text-align: center;
  border: none;
  background: transparent;
  padding: 0.25rem;
}

