html {
  -webkit-tap-highlight-color: transparent;
}

:root {
  color-scheme: dark;
  --bg: #0a0e17;
  --surface: #131826;
  --surface-hover: #1a2133;
  --border: #232b40;
  --text: #e8ebf3;
  --text-dim: #8892a6;
  --accent: #6c5cff;
  --accent-hover: #7d6dff;
  --danger: #ff5470;
  --danger-hover: #ff6b82;
  --radius: 14px;

  /* Visual refresh primitives. Colours and type stay as above; these add
     depth and motion on top of them. */

  /* Gradients must get DARKER away from --accent, never lighter: white
     text on --accent is 4.55:1 (barely passes) and on #8a7dff only 3.25:1. */
  --accent-deep: #5a4bf0; /* white on it: 5.68:1 */
  --grad-accent: linear-gradient(135deg, var(--accent) 0%, var(--accent-deep) 100%);
  /* Violet text on navy is only 4.24:1, so glowing TEXT uses this lighter
     violet instead (6.96:1 on --bg). */
  --accent-text: #9a8cff;
  --glow-accent: 0 0 18px rgba(108, 92, 255, 0.45), 0 0 3px rgba(154, 140, 255, 0.7);
  --glow-text: 0 0 12px rgba(154, 140, 255, 0.55);

  /* Glass: a translucent --surface with a light edge. Real blur is only
     worth its GPU cost where something varied scrolls underneath, so
     --glass-filter is applied to a handful of surfaces, never every card.
     Set --glass-filter to none to switch every blur off in one line
     (none, not blur(0px), so no blur layer is created at all). */
  --glass-fill: rgba(19, 24, 38, 0.62);
  --glass-fill-hover: rgba(30, 38, 62, 0.72);
  --glass-fill-strong: rgba(19, 24, 38, 0.82);
  --glass-edge: rgba(255, 255, 255, 0.09);
  --glass-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.07);
  --glass-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  --glass-blur: 14px;
  --glass-filter: blur(var(--glass-blur)) saturate(1.3);
  /* Bars that content scrolls underneath (tab bar, mortgage total). */
  --bar-fill: rgba(10, 14, 23, 0.76);

  /* Violet-only glow blobs behind everything. Fixed in place, so cards
     scrolling over them always have something varied behind them. */
  --bg-glow:
    radial-gradient(460px circle at 10% 0%, rgba(108, 92, 255, 0.28), transparent 70%),
    radial-gradient(460px circle at 100% 30%, rgba(108, 92, 255, 0.2), transparent 70%),
    radial-gradient(540px circle at 0% 78%, rgba(90, 75, 240, 0.15), transparent 70%),
    radial-gradient(400px circle at 92% 100%, rgba(108, 92, 255, 0.16), transparent 70%);

  /* Primary buttons: a top sheen plus a soft glow underneath. */
  --btn-glow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 6px 20px -4px rgba(108, 92, 255, 0.55);
  --btn-glow-hover: inset 0 1px 0 rgba(255, 255, 255, 0.28), 0 8px 28px -2px rgba(108, 92, 255, 0.75);

  /* Glowing numbers use only light violets (>= 4.7:1 even on the brightest
     part of the glow). Stars use a lighter pair still, so a filled star
     stays clearly different from an empty one (#8780cc, below). */
  --text-gradient: linear-gradient(180deg, #d6cfff 0%, #9a8cff 100%);
  --star-gradient: linear-gradient(180deg, #f0edff 0%, #b8adff 100%);

  /* Motion */
  /* Overshoots rest by ~40% of the distance travelled, then settles. (The
     usual 1.56 curve overshoots ~10%: on a 4% button press that's under
     half a pixel, i.e. invisible.) */
  --ease-spring: cubic-bezier(0.3, 2.4, 0.5, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --press-in: 90ms;
  --press-out: 420ms;
  --panel-ms: 320ms;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: "Space Grotesk", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* The glow field, fixed to the screen rather than the page so it stays put
   while the list scrolls over it. Behind .app (z-index -1, not positioned
   content) and under the tab bar. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--bg-glow);
}

.app {
  max-width: 480px;
  margin: 0 auto;
  /* bottom padding clears the fixed tab bar, so the last home can
     always scroll fully into view above it */
  padding: 32px 20px calc(100px + env(safe-area-inset-bottom));
}

.app-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 28px;
}

.app-header-text {
  min-width: 0;
}

.all-homes-btn {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 10px 12px;
  min-height: 42px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .all-homes-btn:hover {
    border-color: var(--accent);
    color: var(--accent-text);
  }
}

.all-homes-btn:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

.app-header h1 {
  font-size: 1.6rem;
  font-weight: 700;
  margin: 0 0 4px;
  letter-spacing: -0.01em;
}

.app-subtitle {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.95rem;
}

.add-home-form {
  display: flex;
  gap: 10px;
  margin-bottom: 24px;
}

.add-home-form[hidden] {
  display: none;
}

.address-error {
  margin: -10px 0 24px;
  color: var(--danger-hover);
  font-size: 0.9rem;
}

.address-confirm {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 24px;
}

.address-confirm-label {
  margin: 0 0 6px;
  color: var(--text-dim);
  font-size: 0.85rem;
  font-weight: 600;
}

.address-confirm-text {
  margin: 0 0 16px;
  font-size: 1rem;
  word-break: break-word;
}

.address-confirm-actions {
  display: flex;
  gap: 10px;
}

.address-confirm-actions .btn-primary {
  flex: 1;
}

.day-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
}

.day-bar-label {
  color: var(--text-dim);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.day-select {
  flex: 1;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 12px 14px;
  min-height: 48px;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.day-select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.day-menu-wrap {
  position: relative;
  flex-shrink: 0;
}

.day-menu-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.day-menu-btn[aria-expanded="true"] {
  border-color: var(--accent);
  color: var(--accent-text);
}

@media (hover: hover) {
  .day-menu-btn:hover {
    border-color: var(--accent);
    color: var(--accent-text);
  }
}

.day-menu-btn:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.day-menu-btn:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

/* display:flex needs the [hidden] override (see Lessons Learned). */
.day-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 15;
  display: flex;
  flex-direction: column;
  min-width: 180px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}

.day-menu[hidden] {
  display: none;
}

.day-menu-item {
  min-height: 44px;
  padding: 10px 14px;
  background: none;
  border: none;
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.day-menu-item:focus-visible {
  outline: none;
  background: var(--surface-hover);
}

@media (hover: hover) {
  .day-menu-item:hover {
    outline: none;
    background: var(--surface-hover);
  }
}

.day-menu-item-danger {
  color: var(--danger-hover);
}

#day-delete-confirm .day-select {
  width: 100%;
  margin: 6px 0 14px;
}

.address-input {
  flex: 1;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: inherit;
  font-size: 1rem;
  padding: 14px 16px;
  min-height: 52px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.address-input::placeholder {
  color: var(--text-dim);
}

.address-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.btn-primary {
  background: var(--accent);
  color: white;
  border: none;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 14px 20px;
  min-height: 52px;
  min-width: 96px;
  cursor: pointer;
  transition: background 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .btn-primary:hover {
    background: var(--accent-hover);
  }
}

.btn-primary:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--accent-text);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 14px 20px;
  min-height: 52px;
  min-width: 96px;
  margin-top: 12px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .btn-secondary:hover {
    background: var(--accent);
    color: white;
  }
}

.btn-secondary:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

.btn-secondary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.address-input:disabled {
  opacity: 0.6;
}

.compare-hint {
  margin: 0 0 12px;
  color: var(--text-dim);
  font-size: 0.9rem;
}

.route-btn {
  width: 100%;
  background: var(--accent);
  color: white;
  border: none;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 14px 20px;
  min-height: 52px;
  cursor: pointer;
  transition: background 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .route-btn:hover {
    background: var(--accent-hover);
  }
}

.route-btn:active {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.compare-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.compare-bar[hidden] {
  display: none;
}

.compare-count {
  color: var(--text-dim);
  font-size: 0.9rem;
}

.compare-btn {
  background: transparent;
  color: var(--accent-text);
  border: 1px solid var(--accent);
  border-radius: 10px;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  padding: 10px 18px;
  min-height: 40px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring), opacity 0.15s ease;
}

@media (hover: hover) {
  .compare-btn:hover:not(:disabled) {
    background: var(--accent);
    color: white;
  }
}

.compare-btn:active:not(:disabled) {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

.compare-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.home-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 16px 16px 18px;
  animation: home-item-in 0.25s ease;
  cursor: pointer;
  transition: background 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .home-item:hover {
    background: var(--surface-hover);
  }
}

.home-item:active {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.home-checkbox-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 10px;
  margin: -10px;
  cursor: pointer;
}

.home-checkbox {
  /* Drawn by us: the browser's own checkbox is a flat square that clashes
     with the glass cards. The 40px tap area is the label around it. */
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  margin: 0;
  border: 1.5px solid #8780cc; /* 3.7:1 against the glow, so the empty box stays visible */
  border-radius: 7px;
  background: var(--glass-fill-strong);
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.home-checkbox:checked {
  border-color: transparent;
  background-color: var(--accent-deep);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E"), var(--grad-accent);
  background-size: 80% 80%, auto; /* first layer paints on top: tick over gradient */
  background-position: center;
  background-repeat: no-repeat;
  box-shadow: var(--glow-accent);
}

.home-checkbox:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
}

.home-checkbox:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.home-address {
  flex: 1;
  min-width: 0;
  font-size: 1rem;
  word-break: break-word;
  /* a <button> that should still look like plain row text */
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  color: inherit;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}

.home-address:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 4px;
}

.home-rating {
  flex-shrink: 0;
  color: var(--accent-text);
  font-size: 0.85rem;
  letter-spacing: 1px;
}

.delete-btn {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .delete-btn:hover {
    background: rgba(255, 84, 112, 0.12);
    border-color: var(--danger);
    color: var(--danger-hover);
  }
}

.delete-btn:active {
  transform: scale(0.92);
  transition: transform var(--press-in) ease-out;
}

.empty-state {
  color: var(--text-dim);
  text-align: center;
  padding: 24px 0 8px;
  margin: 0;
}

.empty-text {
  margin: 0;
}

.empty-art {
  display: block;
  width: 112px;
  height: 112px;
  margin: 0 auto 10px;
  overflow: visible;
  filter: drop-shadow(0 0 14px rgba(108, 92, 255, 0.35));
}

/* The pin hovers over the house and the ground ring ripples out. Both
   freeze in a sensible still pose under Reduce Motion (rules at the end). */
.empty-pin {
  animation: empty-bob 2.6s ease-in-out infinite;
}

.empty-ring {
  opacity: 0.4;
  transform-box: fill-box;
  transform-origin: center;
  animation: empty-ripple 2.6s ease-out infinite;
}

@keyframes empty-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-5px);
  }
}

@keyframes empty-ripple {
  0% {
    transform: scale(0.55);
    opacity: 0.8;
  }
  100% {
    transform: scale(1.35);
    opacity: 0;
  }
}

@keyframes home-item-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.detail-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(5, 7, 12, 0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 10;
}

.detail-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.detail-panel {
  position: fixed;
  inset: 0;
  background: var(--bg);
  background-image: var(--bg-glow);
  transform: translateX(100%);
  transition: transform 0.25s ease;
  z-index: 20;
  overflow-y: auto;
  padding: 24px 20px 60px;
}

.detail-panel.is-open {
  transform: translateX(0);
}

/* ---- Panel open/close animation (see panels.js) ----
   "slide" mode: the transform transition above. "view-transition" and
   "instant" modes: panels.js adds .no-slide so it can't play on top of
   the View Transition (or at all, for reduced motion). */
.detail-panel.no-slide,
.detail-backdrop.no-slide {
  transition: none;
}

@keyframes panel-slide-in {
  from {
    transform: translateX(100%);
  }
}

@keyframes panel-slide-out {
  to {
    transform: translateX(100%);
  }
}

/* The page behind a panel steps back a little as the panel arrives, and
   comes forward as it leaves. */
@keyframes page-recede {
  to {
    transform: scale(0.94);
    filter: brightness(0.55);
  }
}

@keyframes page-return {
  from {
    transform: scale(0.94);
    filter: brightness(0.55);
  }
}

/* The browser cross-fades the old and new page by default (and adds the
   two images together); for a panel we want it to do neither. */
::view-transition-old(root),
::view-transition-new(root),
::view-transition-old(panel),
::view-transition-new(panel) {
  animation: none;
  mix-blend-mode: normal;
}

html[data-panel-motion="open"]::view-transition-new(panel) {
  animation: panel-slide-in var(--panel-ms) var(--ease-out) both;
}

html[data-panel-motion="close"]::view-transition-old(panel) {
  animation: panel-slide-out var(--panel-ms) var(--ease-out) both;
}

html[data-panel-motion="open"]::view-transition-old(root) {
  animation: page-recede var(--panel-ms) var(--ease-out) both;
}

html[data-panel-motion="open"]::view-transition-new(root),
html[data-panel-motion="close"]::view-transition-old(root) {
  opacity: 0;
}

html[data-panel-motion="close"]::view-transition-new(root) {
  animation: page-return var(--panel-ms) var(--ease-out) both;
}

.detail-header {
  margin-bottom: 24px;
}

.detail-header-top {
  display: flex;
  align-items: center;
  gap: 14px;
}

.detail-rating {
  display: flex;
  gap: 4px;
  margin-top: 10px;
}

.save-indicator {
  display: inline-block;
  margin-top: 10px;
  color: var(--accent-text);
  font-size: 0.85rem;
  font-weight: 600;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
}

.save-indicator.is-visible {
  opacity: 1;
}

.compare-static-rating {
  margin-top: 0;
}

.compare-static-rating .star-btn {
  cursor: default;
  padding: 0;
  margin: 0;
}

.star-btn {
  background: none;
  border: none;
  /* ~39px tall to tap, but only 2px of extra width a side: the stars sit
     ~25px apart, so wider boxes would overlap and a tap on one star's
     right edge would rate the next star. */
  padding: 9px 2px;
  margin: -9px -2px;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--border);
  cursor: pointer;
  transition: color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.star-btn.is-filled {
  color: var(--accent-text);
}

.star-btn:active {
  transform: scale(0.85);
  transition: transform var(--press-in) ease-out;
}

.detail-back-btn {
  flex-shrink: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 10px 14px;
  min-height: 44px;
  cursor: pointer;
  transition: background 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .detail-back-btn:hover {
    background: var(--surface-hover);
  }
}

.detail-back-btn:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

.detail-address {
  font-size: 1.15rem;
  font-weight: 700;
  margin: 0;
  word-break: break-word;
}

.compare-content {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.compare-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.compare-columns > * + * {
  border-left: 1px solid var(--border);
  padding-left: 14px;
}

.compare-col-address {
  font-size: 1rem;
  margin: 0;
  word-break: break-word;
}

.compare-photo-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}

.compare-note-text {
  margin: 0;
  font-size: 0.95rem;
  white-space: pre-wrap;
  word-break: break-word;
}

.detail-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 16px;
}

.detail-section-title {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 8px;
}

/* A section that folds away: the whole heading row (title + one-line
   summary) is the tap target. Built on <details>, so the browser
   handles opening, the keyboard (Enter / Space) and telling screen
   readers whether it's expanded. */
.detail-collapsible {
  padding: 0;
}

.detail-collapsible-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 16px 18px;
  cursor: pointer;
  list-style: none;
  border-radius: var(--radius);
}

/* Safari draws its own triangle unless told not to. */
.detail-collapsible-summary::-webkit-details-marker {
  display: none;
}

.detail-collapsible-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.detail-collapsible-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.detail-collapsible-summary .detail-section-title {
  margin: 0;
}

.detail-collapsible-line {
  color: var(--text-dim);
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

/* A chevron that points down when closed and up when open. */
.detail-collapsible-chevron {
  flex: none;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: translateY(-3px) rotate(45deg);
  transition: transform 0.2s ease;
}

.detail-collapsible[open] .detail-collapsible-chevron {
  transform: translateY(3px) rotate(-135deg);
}

.detail-collapsible-body {
  padding: 0 18px 18px;
}

@media (prefers-reduced-motion: reduce) {
  .detail-collapsible-chevron {
    transition: none;
  }
}

.detail-placeholder {
  color: var(--text-dim);
  margin: 0;
  font-size: 0.9rem;
}

.capture {
  margin-bottom: 16px;
}

.capture-row {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a plain 1fr column grows to fit a long
     label ("Transcribing…"), making the two buttons uneven on narrow
     phones. Same fix as the photo grid. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

/* Matches the Voice Note button beside it. */
.capture-photo {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 52px;
  padding: 12px 8px;
  background: rgba(108, 92, 255, 0.12);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.capture-photo:active {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.capture-photo:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.capture-photo-icon {
  width: 20px;
  height: 20px;
  flex: none;
}

.voice-note-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 52px;
  padding: 12px 8px;
  text-align: center;
  background: rgba(108, 92, 255, 0.12);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.voice-note-btn[hidden] {
  display: none;
}

.voice-note-btn:active {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.voice-note-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* A microphone: round-topped capsule plus a stand, drawn in CSS so it
   picks up the button's colour. */
.voice-note-icon {
  position: relative;
  flex: none;
  width: 12px;
  height: 18px;
  border: 2px solid currentColor;
  border-radius: 6px;
  margin-bottom: 4px;
}

.voice-note-icon::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -7px;
  width: 2px;
  height: 5px;
  background: currentColor;
  transform: translateX(-50%);
}

/* Live: a solid red square (Stop) pulsing, and a red outline, so it's
   obvious at a glance that the mic is on. */
.voice-note-btn.is-recording {
  background: rgba(255, 84, 112, 0.14);
  border-color: var(--danger);
  color: var(--danger-hover);
}

.voice-note-btn.is-recording:focus-visible {
  outline-color: var(--danger);
}

.voice-note-btn.is-recording .voice-note-icon {
  width: 14px;
  height: 14px;
  margin-bottom: 0;
  border: none;
  border-radius: 3px;
  background: var(--danger);
  animation: voice-pulse 1.2s ease-in-out infinite;
}

.voice-note-btn.is-recording .voice-note-icon::after {
  display: none;
}

.voice-note-btn[aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}

@keyframes voice-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 84, 112, 0.6); }
  50% { box-shadow: 0 0 0 8px rgba(255, 84, 112, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .voice-note-btn.is-recording .voice-note-icon {
    animation: none;
  }
}

.voice-note-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0 10px;
}

.voice-note-actions[hidden] {
  display: none;
}

.voice-note-status {
  margin: 10px 0 0;
  font-size: 0.85rem;
  color: var(--text-dim);
}

/* Dashed and unfilled, so it doesn't pass for a box that can be typed
   in. */
.voice-note-transcript {
  margin: 8px 0 0;
  padding: 12px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  font-size: 0.95rem;
  line-height: 1.45;
  white-space: pre-wrap;
}

.note-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-dim);
  margin: 14px 0 6px;
}

.note-label:first-of-type {
  margin-top: 0;
}

.note-textarea {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  padding: 10px 12px;
  resize: vertical;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.note-textarea::placeholder {
  color: var(--text-dim);
}

.note-textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.detail-fields-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 12px;
}

.detail-field-label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-dim);
  margin-bottom: 6px;
}

.detail-field-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  padding: 12px;
  min-height: 44px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.detail-field-input::placeholder {
  color: var(--text-dim);
}

.detail-field-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.detail-field-hint {
  margin: 6px 0 0;
  font-size: 0.8rem;
  color: var(--text-dim);
}

.detail-field-input-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.detail-field-prefix {
  position: absolute;
  left: 12px;
  color: var(--text-dim);
  font-size: 0.95rem;
  pointer-events: none;
}

.detail-field-input.has-prefix {
  padding-left: 22px;
}

.detail-field-suffix {
  position: absolute;
  right: 12px;
  color: var(--text-dim);
  font-size: 0.95rem;
  pointer-events: none;
}

.detail-field-input.has-suffix {
  padding-right: 30px;
}

/* A field that spans both columns of the details grid. */
.detail-field-wide {
  grid-column: 1 / -1;
}

.mortgage-btn {
  width: 100%;
  margin-top: 0;
}

/* Dimmed until the home has a price; aria-disabled rather than
   disabled so it stays focusable and can say why. */
.mortgage-btn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

@media (hover: hover) {
  .mortgage-btn[aria-disabled="true"]:hover {
    background: transparent;
    color: var(--accent-text);
  }
}

/* A button that reads as a text link, for small secondary actions. */
.link-btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  background: none;
  border: none;
  padding: 0 4px;
  /* Tall enough to tap, without pushing the lines around it apart. */
  margin: -14px 0;
  color: var(--accent-text);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.link-btn[hidden] {
  display: none;
}

/* Slides in over Home Detail, the way Route Map sits over Route. */
.mortgage-panel {
  z-index: 30;
}

.mortgage-address {
  margin: 8px 0 0;
  color: var(--text-dim);
  font-size: 0.9rem;
}

.mortgage-total-bar {
  position: sticky;
  top: -24px; /* the panel's own top padding, so it sits flush at the edge */
  z-index: 1;
  margin: 0 -20px 16px;
  padding: 14px 20px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 12px;
}

.mortgage-total-label {
  color: var(--text-dim);
  font-size: 0.9rem;
  font-weight: 600;
}

.mortgage-total {
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.mortgage-error {
  flex-basis: 100%;
  margin: 0;
  color: var(--danger-hover);
  font-size: 0.85rem;
}

.mortgage-field {
  margin: 0 0 16px;
}

.mortgage-field:last-child {
  margin-bottom: 0;
}

.mortgage-field .detail-field-hint {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 8px;
}

.mortgage-term {
  border: none;
  padding: 0;
  min-width: 0;
}

.mortgage-term-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

.mortgage-term-option {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--text-dim);
  font-weight: 600;
  cursor: pointer;
}

/* The real radio sits invisibly over the whole option, so a tap
   anywhere on it counts and arrow keys still move between them. */
.mortgage-term-option input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

/* Set from mortgage.js rather than with :has(input:checked), which
   older iPhones (before iOS 16.4) don't support — there both options
   would look unselected. */
.mortgage-term-option.is-selected {
  border-color: var(--accent);
  background: rgba(108, 92, 255, 0.15);
  color: var(--text);
}

.mortgage-term-option:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.mortgage-lines {
  margin: 0;
}

/* Read by screen readers, not shown. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.mortgage-lines[hidden] {
  display: none;
}

.mortgage-lines > div {
  display: flex;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.mortgage-lines > div:last-child {
  border-bottom: none;
}

.mortgage-lines dt {
  color: var(--text-dim);
}

.mortgage-lines dd {
  margin: 0;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.photo-grid {
  display: grid;
  /* minmax(0, 1fr), not plain 1fr: a plain 1fr column may grow to fit
     its content, and the square "+" tile used to widen its column to
     match the row's height (photo + caption), shrinking the photos. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.photo-add-tile {
  position: relative;
  /* Don't stretch to the row's height (a photo plus its caption line);
     sized by its column instead, it's the same square as the photos. */
  align-self: start;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px dashed var(--border);
  border-radius: 10px;
  color: var(--text-dim);
  font-size: 1.6rem;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.photo-add-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.photo-add-tile:focus-within {
  border-color: var(--accent);
  color: var(--accent-text);
}

@media (hover: hover) {
  .photo-add-tile:hover {
    border-color: var(--accent);
    color: var(--accent-text);
  }
}

.photo-thumb {
  position: relative;
}

.photo-thumb-img-wrap {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--surface);
  cursor: pointer;
}

.photo-thumb-img-wrap img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-remove-btn {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background-color: rgba(5, 7, 12, 0.8);
  color: var(--text);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.photo-remove-btn::before {
  /* the visible button is 32px; the tappable area is ~44px */
  content: "";
  position: absolute;
  inset: -6px;
}

.photo-remove-btn:focus-visible {
  background-color: var(--danger);
  color: white;
}

@media (hover: hover) {
  .photo-remove-btn:hover {
    background-color: var(--danger);
    color: white;
  }
}

.photo-caption-area {
  margin-top: 6px;
}

.photo-caption-text {
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-dim);
  word-break: break-word;
  cursor: pointer;
}

.photo-caption-text.is-empty {
  opacity: 0.6;
  font-style: italic;
}

/* Home Detail's editable caption is a <button>; Compare's read-only
   caption is a plain <p> with the same class, so reset only buttons. */
button.photo-caption-text {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  text-align: left;
}

button.photo-caption-text:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.photo-caption-input {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--accent);
  border-radius: 8px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.78rem;
  padding: 6px 8px;
  margin-bottom: 4px;
}

.photo-caption-actions {
  display: flex;
  gap: 6px;
}

.photo-caption-save-btn,
.photo-caption-cancel-btn {
  flex: 1;
  border: none;
  border-radius: 8px;
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 5px 0;
  cursor: pointer;
}

.photo-caption-save-btn {
  background: var(--accent);
  color: white;
}

.photo-caption-cancel-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
}

.route-loading,
.route-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 80px 0;
  color: var(--text-dim);
  text-align: center;
}

.route-error .btn-primary {
  min-width: 140px;
}

.route-error-text {
  margin: 0;
  color: var(--danger-hover);
}

.route-spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: route-spin 0.8s linear infinite;
}

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

.route-full-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: white;
  border: none;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  padding: 14px 20px;
  min-height: 52px;
  margin-bottom: 20px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

@media (hover: hover) {
  .route-full-btn:hover {
    background: var(--accent-hover);
  }
}

.route-calendar-btn {
  display: flex;
  width: 100%;
  margin: 0 0 20px;
}

.route-full-btn:active {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.route-stops {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.route-stop {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
}

.route-stop-number {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.95rem;
}

.route-stop-address {
  flex: 1;
  min-width: 0;
  font-size: 1rem;
  word-break: break-word;
}

.route-stop-nav {
  flex-shrink: 0;
  background: transparent;
  color: var(--accent-text);
  border: 1px solid var(--accent);
  border-radius: 10px;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  padding: 8px 14px;
  min-height: 40px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.route-stop-nav:active {
  transform: scale(0.96);
  transition: transform var(--press-in) ease-out;
}

@media (hover: hover) {
  .route-stop-nav:hover {
    background: var(--accent);
    color: white;
  }
}

.route-failed {
  background: rgba(255, 84, 112, 0.08);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  padding: 14px 16px;
}

.route-failed-title {
  color: var(--danger-hover);
  font-weight: 600;
  font-size: 0.9rem;
  margin: 0 0 8px;
}

.route-failed-list {
  margin: 0;
  padding-left: 20px;
  color: var(--text-dim);
  font-size: 0.9rem;
}

.route-failed-list li {
  margin-bottom: 4px;
}

.all-homes-day {
  margin-bottom: 24px;
}

.all-homes-day-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 10px;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.all-homes-day-count {
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
}

.all-homes-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px;
  margin-bottom: 10px;
}

.all-homes-address {
  display: block;
  margin-bottom: 10px;
  font-size: 0.95rem;
  word-break: break-word;
}

.all-homes-move-label {
  display: block;
  margin-bottom: 6px;
  color: var(--text-dim);
  font-size: 0.8rem;
  font-weight: 600;
}

.all-homes-move {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  padding: 10px 12px;
  min-height: 44px;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.all-homes-move:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(108, 92, 255, 0.25);
}

.all-homes-empty {
  color: var(--text-dim);
  font-size: 0.95rem;
}

.summary-btn {
  width: 100%;
  background: transparent;
  color: var(--accent-text);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 14px 20px;
  min-height: 52px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform var(--press-out) var(--ease-spring), opacity 0.15s ease;
}

@media (hover: hover) {
  .summary-btn:hover:not(:disabled) {
    background: var(--accent);
    color: white;
  }
}

.summary-btn:active:not(:disabled) {
  transform: scale(0.98);
  transition: transform var(--press-in) ease-out;
}

.summary-btn:disabled {
  opacity: 0.6;
  cursor: progress;
}

.summary-status {
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
}

.summary-error {
  background: var(--surface);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}

.summary-error-text {
  margin: 0 0 14px;
  color: var(--danger-hover);
  font-size: 0.95rem;
  line-height: 1.5;
}

.summary-cached {
  margin: 0 0 16px;
  color: var(--text-dim);
  font-size: 0.8rem;
}

.summary-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
}

.summary-card-address {
  margin: 0 0 8px;
  font-size: 1rem;
  font-weight: 700;
  word-break: break-word;
}

.summary-card-line {
  margin: 0 0 14px;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
}

.summary-group-label {
  margin: 0 0 6px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.summary-list {
  margin: 0 0 14px;
  padding-left: 18px;
  font-size: 0.95rem;
  line-height: 1.6;
}

.summary-list:last-child {
  margin-bottom: 0;
}

.summary-list-pros li::marker {
  color: var(--accent-text);
}

.summary-list-cons li::marker {
  color: var(--danger);
}

.summary-consider {
  background: linear-gradient(180deg, rgba(108, 92, 255, 0.12), transparent);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 16px;
  margin-top: 20px;
}

.summary-consider-title {
  margin: 0 0 10px;
  font-size: 1rem;
  font-weight: 700;
}

/* Route map panel. Needs display:flex for a full-height map, so it
   also needs the [hidden] override (see Lessons Learned). */
.map-panel {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
  z-index: 30;
}

.map-panel[hidden] {
  display: none;
}

.map-panel-header {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 20px 20px 14px;
}

.map-panel-header .detail-address {
  margin: 0;
}

.map-panel-body {
  position: relative;
  flex: 1;
  min-height: 0;
}

/* Two classes deep on purpose: mapbox-gl.css loads after this file and
   gives every map container .mapboxgl-map { position: relative }. At
   equal specificity the later file wins, which silently drops inset:0
   and collapses the map to zero height. */
.map-panel-body > .route-map {
  position: absolute;
  inset: 0;
}

.route-map-status {
  position: absolute;
  left: 20px;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  text-align: center;
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.5;
}

.map-panel-footer {
  padding: 14px 20px 24px;
}

.route-map-note {
  margin: 0 0 10px;
  color: var(--text-dim);
  font-size: 0.85rem;
  line-height: 1.4;
}

.route-map-marker {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  color: white;
  font-family: "Space Grotesk", system-ui, sans-serif;
  font-size: 0.9rem;
  font-weight: 700;
}

.route-map-traveler {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: white;
  box-shadow: 0 0 0 4px rgba(108, 92, 255, 0.5), 0 0 14px rgba(108, 92, 255, 0.9);
}

.route-map-btn {
  display: flex;
  width: 100%;
  margin: 0 0 12px;
}

.backup-section {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.backup-help {
  margin: 0 0 14px;
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.5;
}

.backup-btn {
  display: flex;
  width: 100%;
  margin: 0 0 10px;
}

.backup-save-btn {
  margin-bottom: 10px;
}

.backup-status {
  margin: 4px 0 0;
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.5;
}

.install-banner {
  background: linear-gradient(180deg, rgba(108, 92, 255, 0.16), rgba(108, 92, 255, 0.04));
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 20px;
}

.install-title {
  margin: 0 0 6px;
  font-size: 1rem;
  font-weight: 700;
}

.install-text {
  margin: 0 0 12px;
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.5;
}

.install-text strong {
  color: var(--text);
}

.share-glyph {
  vertical-align: -3px;
  color: var(--accent-text);
}

.install-actions {
  display: flex;
  gap: 10px;
}

.install-actions .btn-primary {
  flex: 1;
  min-height: 48px;
}

.install-dismiss-btn {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 12px 18px;
  min-height: 48px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

@media (hover: hover) {
  .install-dismiss-btn:hover {
    border-color: var(--accent);
    color: var(--accent-text);
  }
}

/* Bottom tab bar. Sits under every panel (panels are z-index 20+ and
   cover the full screen), so it's only ever seen on the Homes view. */
.tab-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  display: flex;
  justify-content: center;
  gap: 4px;
  padding: 6px 12px calc(6px + env(safe-area-inset-bottom));
  background: rgba(10, 14, 23, 0.92);
  border-top: 1px solid var(--border);
}

.tab-btn {
  flex: 1;
  max-width: 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 54px;
  background: none;
  border: none;
  border-radius: 12px;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease, transform var(--press-out) var(--ease-spring);
}

.tab-btn svg {
  width: 22px;
  height: 22px;
}

.tab-btn.is-current {
  color: var(--accent-text);
}

@media (hover: hover) {
  .tab-btn:hover:not([aria-disabled="true"]):not(:disabled) {
    color: var(--text);
    background: var(--surface);
  }
}

@media (hover: hover) {
  .tab-btn.is-current:hover {
    color: var(--accent-text);
  }
}

.tab-btn:active:not([aria-disabled="true"]):not(:disabled) {
  transform: scale(0.95);
  transition: transform var(--press-in) ease-out;
}

/* Not enough homes yet (aria-disabled keeps it focusable and lets it
   announce why), or busy generating (disabled). */
.tab-btn[aria-disabled="true"],
.tab-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.tab-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ---- Glass, glow and gradient (visual refresh) ----
   Pass 1: the home list and Home Detail. Kept after the component rules on
   purpose: it restyles surfaces defined above, and at equal specificity the
   later rule wins. To move another panel over (pass 2), add its selectors
   to these lists rather than editing the original rules.

   Glass reads as glass from translucency + a light edge + a soft shadow
   over the fixed glow field. Real blur is applied only where content
   scrolls underneath (tab bar, Home Detail sections, the day menu). */

/* Cards */
.home-item,
.detail-panel .detail-section,
.address-confirm,
.day-menu {
  background: var(--glass-fill);
  border-color: var(--glass-edge);
  box-shadow: var(--glass-highlight), var(--glass-shadow);
}

@media (hover: hover) {
  .home-item:hover {
    background: var(--glass-fill-hover);
  }
}

/* Blur only where content actually scrolls underneath (the tab bar and
   the day menu). Cards sit in normal flow over a smooth, static glow;
   blurring a smooth gradient looks identical to not blurring it, and it
   costs a re-composited layer per card, so cards get the translucent
   fill + edge + shadow above and no blur. */
.day-menu,
.tab-bar {
  -webkit-backdrop-filter: var(--glass-filter);
  backdrop-filter: var(--glass-filter);
}

.day-menu {
  background: var(--glass-fill-strong);
  box-shadow: var(--glass-highlight), 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* Controls sitting on glass stay a little more solid, so text stays easy
   to read. Focus rings and hover borders are more specific and still win. */
.address-input,
.day-select,
.day-menu-btn,
.detail-back-btn {
  background: var(--glass-fill-strong);
  border-color: var(--glass-edge);
  box-shadow: var(--glass-highlight);
}

@media (hover: hover) {
  .detail-back-btn:hover {
    background: var(--glass-fill-hover);
  }
}

/* Voice Note / Add Photo */
.voice-note-btn,
.capture-photo {
  background: linear-gradient(180deg, rgba(108, 92, 255, 0.24), rgba(108, 92, 255, 0.08));
  box-shadow: var(--glass-highlight), 0 6px 18px -8px rgba(108, 92, 255, 0.5);
}

/* Primary buttons: gradient + glow. The gradient only ever gets darker
   than --accent, so white text keeps >= 4.55:1. Hover adds glow rather
   than lightening the fill, which would drop white text below 4.5:1. */
.btn-primary,
.route-btn {
  background: var(--grad-accent);
  box-shadow: var(--btn-glow);
}

@media (hover: hover) {
  .btn-primary:hover,
  .route-btn:hover {
    background: var(--grad-accent);
    box-shadow: var(--btn-glow-hover);
  }
}

.btn-primary:disabled {
  box-shadow: none;
}

/* Tab bar */
.tab-bar {
  background: var(--bar-fill);
  border-top-color: var(--glass-edge);
  box-shadow: var(--glass-highlight), 0 -8px 24px rgba(0, 0, 0, 0.25);
}

.tab-btn.is-current {
  color: var(--accent-text);
}

@media (hover: hover) {
  .tab-btn.is-current:hover {
    color: var(--accent-text);
  }
}

.tab-btn.is-current svg {
  filter: drop-shadow(0 0 6px rgba(154, 140, 255, 0.6));
}

/* Stars and $/sqft: gradient lettering with a soft glow. Only where the
   browser can clip a gradient to text; elsewhere they stay a plain light
   violet, because without clipping the gradient would paint a solid box. */
.star-btn.is-filled,
.home-rating {
  color: var(--accent-text);
}

/* Unfilled: 3.7:1 on the brightest part of the glow (where Home Detail's
   stars sit) and 3.15:1 where two glows overlap, so an empty star is
   findable. The first guess, #5f5a9e, was 3.2:1 on flat navy but only
   2.14:1 on the actual glow. */
.star-btn {
  color: #8780cc;
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .star-btn {
    position: relative;
  }

  /* The glowing fill is an overlay star, so it can animate in on top of
     the dim one underneath. */
  .star-btn::after {
    content: "★";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--star-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    filter: drop-shadow(0 0 6px rgba(154, 140, 255, 0.65));
    opacity: 0;
    transform: scale(0.5);
    pointer-events: none;
    /* Fills with a pop: scales up past full size, then settles. */
    transition: opacity 0.18s ease, transform var(--press-out) var(--ease-spring);
  }

  .star-btn.is-filled {
    color: #8780cc;
  }

  .star-btn.is-filled::after {
    opacity: 1;
    transform: scale(1);
  }

  /* Several stars filling at once (0 -> 4) light up left to right. Only the
     filling direction is staggered; emptying has no delay. */
  .star-btn.is-filled:nth-child(2)::after {
    transition-delay: 45ms;
  }

  .star-btn.is-filled:nth-child(3)::after {
    transition-delay: 90ms;
  }

  .star-btn.is-filled:nth-child(4)::after {
    transition-delay: 135ms;
  }

  .star-btn.is-filled:nth-child(5)::after {
    transition-delay: 180ms;
  }

  .home-rating,
  .stat-glow,
  .mortgage-total {
    background: var(--text-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    filter: drop-shadow(0 0 7px rgba(154, 140, 255, 0.5));
  }
}

.stat-glow {
  width: fit-content;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--accent-text);
}

/* Windows high-contrast and similar: use the user's own colours. */
@media (forced-colors: active) {
  .star-btn::after,
  .home-rating,
  .stat-glow,
  .mortgage-total {
    background: none;
    -webkit-text-fill-color: currentColor;
    color: CanvasText;
    filter: none;
  }
}

/* No backdrop-filter at all: make the fills more solid so text stays
   legible over the glow. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root {
    --glass-fill: rgba(19, 24, 38, 0.88);
    --glass-fill-hover: rgba(30, 38, 62, 0.92);
    --bar-fill: rgba(10, 14, 23, 0.94);
  }
}

/* Where "reduce transparency" is supported (patchy; harmless elsewhere):
   solid surfaces, no blur layers. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --glass-filter: none;
    --glass-fill: var(--surface);
    --glass-fill-hover: var(--surface-hover);
    --glass-fill-strong: var(--surface);
    --bar-fill: var(--bg);
  }
}

/* Remaining panels: Summary, Route, All Properties and Mortgage get the
   same translucent cards as Home Detail. */
.summary-card,
.route-stop,
.all-homes-item {
  background: var(--glass-fill);
  border-color: var(--glass-edge);
  box-shadow: var(--glass-highlight), var(--glass-shadow);
}

/* keeps its red border: it's an error */
.summary-error {
  background: var(--glass-fill);
  box-shadow: var(--glass-highlight), var(--glass-shadow);
}

.all-homes-move,
.mortgage-term-option {
  background: var(--glass-fill-strong);
  border-color: var(--glass-edge);
  box-shadow: var(--glass-highlight);
}

/* more specific, so it still beats the line above */
.mortgage-term-option.is-selected {
  border-color: var(--accent);
  background: rgba(108, 92, 255, 0.2);
}

/* The one place real content scrolls underneath: the sticky total bar. */
.mortgage-total-bar {
  background: var(--bar-fill);
  border-bottom-color: var(--glass-edge);
  box-shadow: var(--glass-highlight), 0 8px 20px -10px rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: var(--glass-filter);
  backdrop-filter: var(--glass-filter);
}

/* Leftover flat elements: same glass treatment as the cards. */
.all-homes-btn,
.photo-add-tile {
  background: var(--glass-fill);
  border-color: var(--glass-edge);
  box-shadow: var(--glass-highlight);
}

.photo-add-tile {
  border-style: dashed;
  border-color: rgba(255, 255, 255, 0.22);
}

.delete-btn {
  border-color: var(--glass-edge);
  background: var(--glass-fill);
}

.btn-secondary {
  background: linear-gradient(180deg, rgba(108, 92, 255, 0.16), rgba(108, 92, 255, 0.04));
  box-shadow: var(--glass-highlight);
}

.install-banner {
  box-shadow: var(--glass-highlight), 0 6px 18px -8px rgba(108, 92, 255, 0.5);
}

/* Pressing the checkbox or delete button inside a card must not squash the
   whole card as well (:active matches every ancestor of what's pressed).
   Where :has() isn't supported this whole rule is ignored, which is fine. */
.home-item:has(.home-checkbox-wrap:active, .delete-btn:active) {
  transform: none;
}

/* ---- Branded waiting state ----
   A bobbing violet map-pin beside the message and a light sweeping across
   the button that's busy. (The message itself stays still: it's the one
   thing that has to be read, and animating text repaints every frame.)
   All of it is switched on by adding .is-waiting to an element that
   already exists, never by inserting nodes: screens like Voice Note
   redraw constantly, and a re-inserted element would restart its
   animation every time. Used by Voice Note and Summary's "generating" line.

   The pin is a CSS mask, so it has no text and nothing for a screen
   reader to announce; the message beside it (inside role="status") is
   what's read out. */
.voice-note-status.is-waiting,
.summary-status.is-waiting {
  display: flex;
  align-items: center;
  gap: 10px;
}

.voice-note-status.is-waiting[hidden] {
  display: none;
}

@supports ((-webkit-mask: url("x")) or (mask: url("x"))) {
  .voice-note-status.is-waiting::before,
  .summary-status.is-waiting::before {
    content: "";
    flex: none;
    width: 22px;
    height: 22px;
    background: var(--text-gradient);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2C8.1 2 5 5.1 5 9c0 5.2 7 13 7 13s7-7.8 7-13c0-3.9-3.1-7-7-7zm0 9.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2C8.1 2 5 5.1 5 9c0 5.2 7 13 7 13s7-7.8 7-13c0-3.9-3.1-7-7-7zm0 9.5a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z'/%3E%3C/svg%3E") center / contain no-repeat;
    filter: drop-shadow(0 0 6px rgba(154, 140, 255, 0.7));
    animation: wait-bob 1.4s ease-in-out infinite;
  }
}

/* The busy button: dimmed buttons read as "can't use", so while it's
   waiting it stays bright and a sheen sweeps across it instead. */
.voice-note-btn.is-waiting {
  position: relative;
  overflow: hidden;
}

.voice-note-btn[aria-disabled="true"].is-waiting {
  opacity: 0.92;
}

.voice-note-btn.is-waiting::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 35%, rgba(214, 207, 255, 0.3) 50%, transparent 65%);
  transform: translateX(-100%);
  pointer-events: none;
  animation: wait-sheen 1.5s ease-in-out infinite;
}

@keyframes wait-bob {
  0%,
  100% {
    transform: translateY(2px);
  }
  50% {
    transform: translateY(-3px);
  }
}

@keyframes wait-sheen {
  to {
    transform: translateX(100%);
  }
}

@media (forced-colors: active) {
  .voice-note-status.is-waiting::before,
  .summary-status.is-waiting::before {
    background: CanvasText;
    filter: none;
  }
}

/* ---- Reduced motion ----
   Kept last on purpose: at equal specificity the later rule wins, so this
   block can override any animation defined above. Every NEW animation in
   the app gets its reduced-motion rule added here too.

   Not here yet: the panel slide. Its hide-after-slide cleanup waits for a
   transitionend that never fires when there's no transition, so panels
   get their reduced-motion handling together with the shared open/close
   helper in panels.js. */
@media (prefers-reduced-motion: reduce) {
  /* Button presses: the shrink still shows, but instantly, with no bounce. */
  :root {
    --press-in: 0ms;
    --press-out: 0ms;
  }

  .home-item {
    animation: none;
  }

  /* Waiting states: the pin holds still and the sheen is gone (it rests
     off the button). These repeat the full selectors the animations were
     set with: a shorter ".is-waiting::before" is less specific than
     ".voice-note-status.is-waiting::before", so it would lose however
     late it comes. */
  .is-waiting,
  .voice-note-status.is-waiting::before,
  .summary-status.is-waiting::before,
  .voice-note-btn.is-waiting::after {
    animation: none;
  }

  /* The empty-state pin and ring hold still. */
  .empty-pin,
  .empty-ring {
    animation: none;
  }

  /* Stars just appear filled, with no pop or fade. */
  .star-btn::after {
    transition: none;
  }

  /* The text beside the spinner ("Finding best route… 2 of 5") already
     carries the status, so a still ring loses nothing. */
  .route-spinner {
    animation: none;
  }

  /* Panels need no rule here: showPanel/hidePanel (panels.js) read
     prefers-reduced-motion themselves and open/close instantly. */
}
