/* ============================================================
   GIYA - Design System CSS
   Fonts: Playfair Display (headings) + Lato (body)
   Colors: #8E3B2F (primary) · #D7A94A (gold) · #FFF8ED (bg) · #241C18 (text)
   ============================================================ */

/* ── Fonts ─────────────────────────────────────────────────── */
@font-face{font-family:'Playfair Display';src:url('../fonts/playfair-display-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Playfair Display';src:url('../fonts/playfair-display-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'Lato';src:url('../fonts/lato-400.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Lato';src:url('../fonts/lato-700.woff2') format('woff2');font-weight:700;font-style:normal;font-display:swap}

/* ── hidden ─────────────────────────────────────────────────
   The browser's own rule for [hidden] is display:none at the very bottom
   of the cascade, so any class of ours that sets display beats it - and
   .eyebrow, .back-link and .plan-bar all set display, which meant marking
   them hidden did nothing at all and the itinerary bar sat open on the
   ordinary map. Stated once here, so `el.hidden = true` means hidden
   everywhere in GIYA.
   ───────────────────────────────────────────────────────────── */
[hidden] { display: none !important; }

/* ── Design Tokens ──────────────────────────────────────────── */
:root {
  --primary:       #8E3B2F;
  --primary-dark:  #6B2D22;
  --primary-light: #C0705A;
  --gold:          #D7A94A;
  --gold-light:    #F0C76C;
  --gold-bg:       #FAE8CC;
  --bg:            #FFF8ED;
  --bg-input:      #FDF3E3;
  --bg-secondary:  #FAF0DC;
  --text:          #241C18;
  --text-muted:    #7A6355;
  --border:        rgba(142,59,47,0.15);
  --border-light:  rgba(142,59,47,0.08);
  --shadow-sm:     0 1px 6px rgba(142,59,47,0.07);
  --shadow-md:     0 2px 12px rgba(142,59,47,0.09);
  --shadow-lg:     0 8px 32px rgba(142,59,47,0.14);
  --shadow-xl:     0 20px 60px rgba(0,0,0,0.15);
  --radius-sm:     8px;
  --radius-md:     12px;
  --radius-lg:     16px;
  --radius-xl:     20px;
  --radius-2xl:    24px;
  --font-display:  'Playfair Display', Georgia, serif;
  --font-body:     'Lato', system-ui, -apple-system, sans-serif;

  /* Numbers are GIYA's own second face, not a third one.

     They were set in Playfair, which is a display serif: its figures are
     proportional, so a 1 is narrower than a 0 and a column of counts never
     lines up, and its thin strokes wash out at the sizes a statistic is
     shown at. Lato is already loaded, it is the face the rest of the app
     reads in, and it has tabular lining figures - every digit the same
     width, all sitting on the baseline at cap height. Simple, and already
     part of the system rather than bolted onto it. */
  --font-numeric:  var(--font-body);
}

/* ── Reset & Base ───────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { font-size: 100%; scroll-behavior: smooth; }   /* data-font overrides this */
body {
  margin: 0; padding: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { font-family: var(--font-display); }

/* ── NUMBERS ─────────────────────────────────────────────────
   Anything whose content is a figure rather than a word. Tabular so columns
   align and a count that ticks upward does not shuffle the characters beside
   it; lining so the digits sit level rather than hanging below the baseline. */
.giya-num,
.hero-stat-value,
.profile-stat-val,
.church-stat-value,
.plan-card-num,
.ap-count,
.giya-pagination a,
.giya-pagination span {
  font-family: var(--font-numeric);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}
a { text-decoration: none; color: inherit; }
button { font-family: var(--font-body); cursor: pointer; }
img { max-width: 100%; display: block; }

/* ── NAVBAR ─────────────────────────────────────────────────── */
.giya-nav {
  background: var(--primary);
  position: sticky; top: 0; z-index: 1000;
  box-shadow: 0 2px 20px rgba(36,28,24,0.2);
}
.nav-inner {
  max-width: 1280px; margin: 0 auto;
  padding: 0 20px; height: 64px;
  display: flex; align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.nav-logo {
  display: flex; align-items: center;
  gap: 10px; flex-shrink: 0;
  text-decoration: none;
}
.nav-logo-icon {
  width: 32px; height: 32px;
  border-radius: var(--radius-sm);
  background: rgba(215,169,74,0.25);
  display: flex; align-items: center;
  justify-content: center; flex-shrink: 0;
}
.nav-logo-name {
  font-family: var(--font-display);
  color: #fff; font-size: 1.375rem; line-height: 1;
}
.nav-logo-badge {
  background: rgba(215,169,74,0.2); color: var(--gold);
  font-size: .75rem; padding: 2px 8px;
  border-radius: 999px; font-weight: 700;
}
.nav-links {
  display: flex; align-items: center;
  gap: 2px; flex: 1; justify-content: center;
}
.nav-link {
  padding: 8px 16px; border-radius: var(--radius-sm);
  font-size: 0.875rem; color: rgba(255,255,255,0.85);
  text-decoration: none; display: inline-block;
  white-space: nowrap; transition: all .2s;
  border: none; border-bottom: 2px solid transparent;
  background: transparent;
  font-family: var(--font-body);
}
.nav-link:hover {
  background: rgba(255,255,255,0.08);
  color: #fff;
}
.nav-link.active {
  background: rgba(215,169,74,0.18);
  color: var(--gold); font-weight: 700;
  border-bottom-color: var(--gold);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.nav-right {
  display: flex; align-items: center;
  gap: 12px; flex-shrink: 0;
}
.nav-bell {
  width: 32px; height: 32px; border-radius: 50%;
  background: rgba(215,169,74,0.15);
  display: flex; align-items: center;
  justify-content: center; border: none;
  cursor: pointer; flex-shrink: 0;
}
.nav-avatar-wrap {
  display: flex; align-items: center; gap: 8px;
}
.nav-avatar {
  width: 32px; height: 32px; border-radius: 50%;
  border: 2px solid var(--gold);
  background: var(--gold); overflow: hidden;
  display: flex; align-items: center;
  justify-content: center;
  color: var(--primary-dark); font-weight: 700;
  font-size: 0.8125rem; flex-shrink: 0;
}
.nav-avatar img { width: 100%; height: 100%; object-fit: cover; }
.nav-username {
  color: rgba(255,255,255,0.85);
  font-size: 0.8125rem; font-weight: 500;
}
.btn-signout {
  font-size: 0.75rem; padding: 6px 14px;
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.75);
  border: 1px solid rgba(255,255,255,0.15);
  transition: background .2s;
}
.btn-signout:hover {
  background: rgba(255,255,255,0.18);
  color: #fff;
}
.hamburger-btn {
  width: 36px; height: 36px;
  border-radius: var(--radius-sm);
  background: rgba(215,169,74,0.15);
  display: none; align-items: center;
  justify-content: center; border: none;
  cursor: pointer; flex-shrink: 0;
}
/* Mobile menu */
.mobile-menu {
  display: none; background: var(--primary-dark);
  border-top: 1px solid rgba(215,169,74,0.2);
}
.mobile-menu.open { display: block; }
.mobile-nav-links {
  padding: 8px 20px 16px;
  display: flex; flex-direction: column; gap: 2px;
}
.mobile-nav-link {
  padding: 12px 0; font-size: 0.875rem;
  color: rgba(255,255,255,0.85);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  text-decoration: none; display: block;
  transition: color .15s;
}
.mobile-nav-link:hover { color: var(--gold); }
.mobile-nav-link.active { color: var(--gold); font-weight: 700; }

@media (max-width: 767px) {
  .nav-links     { display: none !important; }
  .nav-username  { display: none; }
  .nav-bell      { display: none; }
  .btn-signout   { display: none; }
  .hamburger-btn { display: flex !important; }

}
@media (min-width: 768px) {
  .mobile-menu { display: none !important; }
}

/* ── BUTTONS ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center;
  justify-content: center; gap: 8px;
  font-family: var(--font-body); cursor: pointer;
  border: none; transition: all .2s;
  text-decoration: none; font-weight: 700;
  white-space: nowrap;
}
.btn-primary {
  background: var(--primary); color: #fff;
  padding: 14px 24px; border-radius: var(--radius-xl);
  font-size: 0.875rem;
}
.btn-primary:hover { background: var(--primary-dark); color: #fff; }
.btn-primary:active { transform: scale(.98); }
.btn-primary:disabled { background: #C0A898; cursor: not-allowed; }

.btn-gold {
  background: var(--gold); color: var(--text);
  padding: 14px 24px; border-radius: var(--radius-xl);
  font-size: 0.875rem;
}
.btn-gold:hover { background: var(--gold-light); color: var(--text); }

.btn-outline {
  background: transparent; color: var(--primary);
  padding: 13px 22px; border-radius: var(--radius-xl);
  font-size: 0.875rem; border: 1.5px solid var(--border);
}
.btn-outline:hover { background: rgba(142,59,47,0.05); color: var(--primary); }

.btn-outline-gold {
  background: rgba(215,169,74,0.08); color: var(--text);
  padding: 13px 22px; border-radius: var(--radius-xl);
  font-size: 0.875rem; border: 1.5px solid var(--gold);
}
.btn-outline-gold:hover { background: rgba(215,169,74,0.16); }

/*
   .btn-ghost defaults to LIGHT surfaces - cards, panels, tables, which is
   where it is used almost everywhere. On a dark background (the hero, the AI
   panel) add .btn-ghost-inverse alongside it.

   It used to default the other way, which made it invisible white-on-white on
   every card in the app.
*/
.btn-ghost {
  background: transparent; color: var(--text);
  padding: 12px 20px; border-radius: var(--radius-xl);
  font-size: 0.875rem; border: 1px solid var(--border);
}
.btn-ghost:hover { background: var(--bg-secondary); color: var(--text); border-color: var(--gold); }

/* For dark backgrounds: <a class="btn btn-ghost btn-ghost-inverse"> */
.btn-ghost-inverse {
  background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.85);
  border-color: rgba(215,169,74,0.5);
}
.btn-ghost-inverse:hover { background: rgba(255,255,255,0.18); color: #fff; }

.btn-danger {
  background: rgba(212,24,61,0.08); color: #D4183D;
  padding: 10px 16px; border-radius: var(--radius-md);
  font-size: 0.8125rem; border: none;
}
.btn-danger:hover { background: rgba(212,24,61,0.15); }

.btn-sm { padding: 8px 16px; font-size: 0.8125rem; border-radius: var(--radius-md); }

.btn-w-full { width: 100%; }

/* ── FORM CONTROLS ──────────────────────────────────────────── */
.form-label {
  display: block; font-size: 0.875rem;
  font-weight: 600; color: var(--text);
  margin-bottom: 6px;
}
.form-label-sm {
  font-size: 0.75rem; font-weight: 700;
  color: var(--text-muted); text-transform: none;
  letter-spacing: .01em;
}
.giya-input {
  width: 100%; padding: 12px 16px;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--border);
  background: var(--bg-input); color: var(--text);
  font-size: 0.875rem; outline: none;
  font-family: var(--font-body);
  transition: border-color .2s, box-shadow .2s;
}
.giya-input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(142,59,47,0.1);
}
.giya-input::placeholder { color: #C0A898; }
.giya-input:disabled { opacity: .6; cursor: not-allowed; }
.giya-input.is-invalid { border-color: #D4183D; }
.input-wrap { position: relative; }
.input-wrap .giya-input { padding-right: 44px; }
.input-suffix {
  position: absolute; right: 14px; top: 50%;
  transform: translateY(-50%); background: none;
  border: none; cursor: pointer; color: var(--text-muted);
  padding: 0; display: flex; align-items: center;
}
.field-error { font-size: 0.75rem; color: #D4183D; margin-top: 4px; display: block; }
.field { margin-bottom: 16px; }

/* ── ALERTS ─────────────────────────────────────────────────── */
/*
   The flash sat at z-index 1200, above the navbar at 1000, and a fixed
   top: 76px did not clear the bar on every screen. Where the two overlapped
   the alert swallowed taps meant for the hamburger.

   Now it sits BELOW the navbar in the stack, so the bar always wins, and it is
   offset by the navbar's real height rather than a guess.
*/
.flash-container {
  position: fixed;
  top: calc(var(--nav-height, 64px) + 12px);
  right: 20px;
  z-index: 900;                     /* under .giya-nav at 1000 */
  width: min(420px, calc(100vw - 40px));
  pointer-events: none;
}
/* Nothing here is interactive, so nothing here should catch a tap. The alert
   used to re-enable pointer events, which is how it blocked the hamburger. */
.flash-container .alert {
  pointer-events: none;
  box-shadow: var(--shadow-lg);
}
.flash-container {
  transition: opacity .28s ease, transform .28s ease;
}
.flash-container.is-going {
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
}
.alert {
  border-radius: var(--radius-md);
  padding: 12px 16px; font-size: 0.8125rem;
  display: flex; align-items: flex-start;
  gap: 10px; line-height: 1.5; margin-bottom: 16px;
}
.alert-success {
  background: var(--primary);
  border: 1px solid var(--gold); color: #fff;
}
.alert-success i {
  color: var(--gold);
}
.alert-danger {
  background: rgba(142,59,47,0.07);
  border: 1px solid rgba(142,59,47,0.22); color: var(--primary);
}
.alert-warning {
  background: rgba(215,169,74,0.1);
  border: 1px solid rgba(215,169,74,0.3); color: #7a5c00;
}
/* Neither good news nor bad - "we are still confirming your payment". */
.alert-info {
  background: rgba(58,90,120,0.08);
  border: 1px solid rgba(58,90,120,0.25); color: #2c4359;
}
html[data-theme="dark"] .alert-info {
  background: rgba(126,170,210,0.12);
  border-color: rgba(126,170,210,0.32); color: #cfe2f2;
}
@media (max-width: 767px) {
  .flash-container { top: 72px; right: 12px; width: calc(100vw - 24px); }
}

/* Marks a church name a guest cannot open yet. Small and quiet - it sets
   expectation before the click without turning the list into a wall. */
.mx-lock {
  font-size: .6875em; color: var(--text-muted);
  margin-left: 5px; vertical-align: 1px;
}

/* ── CARDS ──────────────────────────────────────────────────── */
.card {
  background: #fff; border-radius: var(--radius-xl);
  border: 1px solid var(--border-light);
  box-shadow: var(--shadow-md);
}
.card-body { padding: 20px; }
.card-title {
  font-family: var(--font-display);
  font-size: 1rem; font-weight: 700;
  color: var(--text); margin-bottom: 14px;
}
.card-hover {
  transition: box-shadow .2s, transform .15s;
  cursor: pointer;
}
.card-hover:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-2px);
}

/* ── BADGES ─────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .75rem; font-weight: 700;
  padding: 3px 10px; border-radius: 999px;
}
.badge-primary { background: var(--primary); color: #fff; }
.badge-gold    { background: var(--gold); color: var(--text); }

.badge-brown   { background: rgba(142,59,47,0.1); color: var(--primary); }
.badge-amber   { background: rgba(215,169,74,0.18); color: #8E6A20; }
.badge-green   { background: rgba(34,139,34,0.1); color: #1a6b1a; }

/* ── STATUS BADGES ──────────────────────────────────────────── */
.status-Completed { background: rgba(142,59,47,0.1); color: var(--primary); }
.status-Upcoming  { background: rgba(215,169,74,0.18); color: #8E6A20; }
.status-Active    { background: rgba(34,139,34,0.1); color: #1a6b1a; }
.status-Draft     { background: rgba(122,99,85,0.1); color: var(--text-muted); }

/* ── MODAL ──────────────────────────────────────────────────── */

@keyframes modalIn {
  from { opacity:0; transform: scale(.96) translateY(8px); }
  to   { opacity:1; transform: scale(1) translateY(0); }
}
.modal-title {
  font-family: var(--font-display);
  font-size: 1.25rem; font-weight: 700;
  color: var(--text); margin-bottom: 20px;
  display: flex; align-items: center; gap: 10px;
}
/* The icon in a modal's title. It was coloured inline on every modal in the
   admin panel, which is five copies of one decision. */
.modal-title .bi { color: var(--primary); }
.modal-title.is-flush { margin-bottom: 0; }

.modal-actions { display: flex; gap: 10px; margin-top: 8px; }

/* ── PAGE LAYOUT ────────────────────────────────────────────── */
.page-wrap { max-width: 1280px; margin: 0 auto; padding: 48px 20px; }

/* Gold rather than the default ink, on the dark hero. */
.eyebrow-text.is-gold { color: var(--gold); }

/* Every home section below the hero sits on the same rhythm. */
.home-section { margin-bottom: 56px; }

.home-section-ruled {
  margin-bottom: 56px;
  border-top: 1px solid var(--border-light);
  padding-top: 48px;
}

.section-header {
  display: flex; align-items: flex-end;
  justify-content: space-between; margin-bottom: 24px;
}
.section-title {
  font-family: var(--font-display);
  font-size: 1.625rem; color: var(--text); margin: 0 0 4px;
}
.section-subtitle {
  font-size: 0.875rem; color: var(--text-muted); margin: 0;
}
.section-link {
  font-size: 0.8125rem; font-weight: 700;
  color: var(--primary); background: none; border: none;
  cursor: pointer; text-decoration: none;
}

/* ── EYEBROW ────────────────────────────────────────────────── */
.eyebrow {
  display: flex; align-items: center;
  gap: 8px; margin-bottom: 10px;
}
.eyebrow-bar {
  width: 4px; height: 22px;
  border-radius: 999px; background: var(--gold);
}
.eyebrow-text {
  color: var(--text-muted); font-size: 0.75rem;
  font-weight: 700; letter-spacing: .01em;
  text-transform: none;
}

/* ── EMPTY STATE ────────────────────────────────────────────── */
.empty-state { text-align: center; padding: 60px 20px; }
.empty-icon  { font-size: 3rem; margin-bottom: 14px; }
.empty-title { font-family: var(--font-display); font-size: 1.125rem; color: var(--text); margin-bottom: 8px; }
.empty-desc  { font-size: 0.875rem; color: var(--text-muted); }

/* ── BACK LINK ──────────────────────────────────────────────── */
.back-link {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.8125rem; color: var(--text-muted);
  text-decoration: none; margin-bottom: 20px;
  transition: color .15s;
}
.back-link:hover { color: var(--primary); }
.back-link svg { transition: transform .15s; }
.back-link:hover svg { transform: translateX(-2px); }

/* ── FOOTER ─────────────────────────────────────────────────── */
.giya-footer { background: var(--text); }
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 40px 20px; }
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 32px; margin-bottom: 32px;
}
.footer-logo-name {
  font-family: var(--font-display);
  color: #fff; font-size: 1.25rem;
}
.footer-desc {
  color: rgba(255,255,255,0.45);
  font-size: 0.8125rem; line-height: 1.7; max-width: 280px;
}
.footer-col-title {
  color: var(--gold); font-size: 0.75rem; font-weight: 700;
  text-transform: none; letter-spacing: .01em;
  margin-bottom: 12px;
}
.footer-link {
  color: rgba(255,255,255,0.45); font-size: 0.8125rem;
  display: block; margin-bottom: 8px;
  text-decoration: none; transition: color .15s;
}
.footer-link:hover { color: rgba(255,255,255,0.75); }
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.08);
  padding-top: 24px; display: flex;
  align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 8px;
}
.footer-copy { color: rgba(255,255,255,0.3); font-size: 0.75rem; }

/* ── AUTH LAYOUT ────────────────────────────────────────────── */
.auth-page {
  min-height: 100vh; background: var(--bg);
}
.auth-content {
  min-height: calc(100vh - 64px);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.auth-card {
  width: 100%; max-width: 420px; background: #fff;
  border-radius: var(--radius-2xl); overflow: hidden;
  box-shadow: 0 8px 40px rgba(142,59,47,0.13);
  border: 1px solid var(--border);
}
.auth-header {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
  padding: 32px 28px 28px; position: relative; overflow: hidden;
}
.auth-header::before {
  content: ''; position: absolute;
  top: -24px; right: -24px;
  width: 120px; height: 120px; border-radius: 50%;
  background: var(--gold); opacity: .15;
}
.auth-header-brand {
  display: flex; align-items: center;
  gap: 8px; margin-bottom: 12px; position: relative;
}
.auth-header-brand-name {
  font-family: var(--font-display);
  color: var(--gold); font-size: 1.25rem;
}
.auth-header h1 {
  font-family: var(--font-display);
  color: #fff; font-size: 1.5rem;
  font-weight: 700; margin: 0 0 4px;
  position: relative;
}
.auth-header p {
  color: rgba(255,255,255,0.7); font-size: 0.8125rem;
  margin: 0; position: relative;
}
.auth-body { padding: 28px; }
.auth-tabs {
  display: flex; border-radius: var(--radius-md);
  overflow: hidden; border: 1px solid var(--border);
  margin-bottom: 24px;
}
.auth-tab {
  flex: 1; padding: 11px; font-size: 0.875rem;
  font-weight: 400; border: none;
  background: transparent; color: var(--text-muted);
  cursor: pointer; text-align: center;
  text-decoration: none; display: block;
  transition: all .2s; font-family: var(--font-body);
}
.auth-tab.active {
  background: var(--primary); color: #fff; font-weight: 700;
}
.auth-tab:hover:not(.active) {
  background: rgba(142,59,47,0.05); color: var(--primary);
}

.auth-footer {
  text-align: center; margin-top: 16px;
  font-size: 0.8125rem; color: var(--text-muted);
}
.auth-footer a { color: var(--primary); font-weight: 700; }

/* ── ADMIN LAYOUT ───────────────────────────────────────────── */
.admin-layout { display: flex; min-height: 100vh; }
/* ── ADMIN SIDEBAR ───────────────────────────────────────────
   Two widths, one of them a rail.

   The width is a custom property so the rail is a different value rather
   than a different set of rules: everything below reads --admin-rail, and
   collapsing is one class on the layout. Nothing has to be kept in step by
   hand, which is how a collapsed sidebar ends up with an uncollapsed header
   sitting next to it. */
.admin-layout { --admin-rail: 268px; }
.admin-layout.is-railed { --admin-rail: 78px; }

.admin-sidebar {
  width: var(--admin-rail); flex-shrink: 0;
  background: var(--text); display: flex;
  flex-direction: column;
  position: sticky; top: 0; height: 100vh;
  overflow-y: auto; overflow-x: hidden;
  transition: width .22s cubic-bezier(.2, .8, .2, 1);
}

@media (prefers-reduced-motion: reduce) {
  .admin-sidebar { transition: none; }
}

.admin-sidebar-head {
  padding: 20px 16px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  display: flex; align-items: center; gap: 10px;
}

/* The control that does the collapsing. Always visible, because a hidden
   toggle is a feature nobody finds. */
.admin-rail-toggle {
  margin-left: auto; flex-shrink: 0;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 9px;
  background: rgba(255,255,255,.08); color: rgba(255,255,255,.75);
  cursor: pointer; padding: 0;
  transition: background .15s ease, color .15s ease;
}
.admin-rail-toggle:hover { background: rgba(255,255,255,.16); color: #fff; }
.admin-rail-toggle .bi { font-size: 1rem; transition: transform .22s ease; }

.is-railed .admin-rail-toggle { margin-left: 0; }
.is-railed .admin-rail-toggle .bi { transform: rotate(180deg); }

/* Railed, the wordmark goes and the toggle is the whole header. */
.is-railed .admin-sidebar-head { justify-content: center; padding: 20px 8px; }
.is-railed .admin-brand { display: none; }

.admin-sidebar-nav { padding: 12px 10px; flex: 1; }

.admin-nav-item {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 14px; border-radius: var(--radius-md);
  color: rgba(255,255,255,0.65); font-size: 0.9375rem;
  text-decoration: none; transition: background .15s, color .15s;
  margin-bottom: 3px; font-family: var(--font-body);
  border: none; background: transparent; width: 100%;
  cursor: pointer; text-align: left;
  white-space: nowrap;
}

/* The icon is the thing you aim at in the rail, so it is sized for that
   rather than for sitting beside a word. */
.admin-nav-item .bi { font-size: 1.25rem; flex-shrink: 0; }

.is-railed .admin-nav-item { justify-content: center; padding: 12px 0; gap: 0; }
.is-railed .admin-nav-label { display: none; }
/* Railed, a section heading becomes a rule. "MANAGEMENT" does not fit in
   78px at any size worth reading - it came out as "MANAGEME" - and a line
   says the same thing a heading says here: a new group starts. The text
   stays in the markup for a screen reader. */
.is-railed .admin-nav-section {
  font-size: 0; padding: 0; overflow: hidden;
  height: 1px; margin: 14px 14px 10px;
  background: rgba(255,255,255,.12);
}

/* Except the first, which would draw a line above the very first item. */
.is-railed .admin-sidebar-nav > .admin-nav-section:first-child { display: none; }

.admin-brand {
  display: flex; align-items: center; gap: 10px;
  text-decoration: none; min-width: 0;
}

.admin-brand-name {
  display: block;
  font-family: var(--font-display);
  color: #fff; font-size: 1.25rem; line-height: 1;
}

.admin-brand-sub {
  display: block;
  color: var(--gold); font-size: .75rem;
  letter-spacing: .01em; text-transform: none;
}

.admin-sidebar-foot {
  padding: 12px 10px;
  border-top: 1px solid rgba(255,255,255,0.08);
}

.admin-nav-item.is-signout { color: #E58C8C; }
.admin-nav-item.is-signout:hover { color: #fff; background: rgba(229,140,140,.16); }
.admin-nav-item:hover {
  background: rgba(255,255,255,0.08);
  color: #fff;
}
.admin-nav-item.active {
  background: var(--primary);
  color: #fff; font-weight: 700;
}
.admin-nav-section {
  font-size: .75rem; font-weight: 700; color: rgba(255,255,255,0.3);
  text-transform: none; letter-spacing: .01em;
  padding: 12px 14px 6px; margin-top: 8px;
}
.admin-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.admin-topbar {
  height: 60px; background: #fff;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center;
  justify-content: space-between; padding: 0 24px;
  position: sticky; top: 0; z-index: 100;
}
.admin-content { padding: 24px; flex: 1; }

/* The top bar's two halves. Both were inline, so the page title was set in
   the display serif on every admin screen by repetition rather than by a
   rule anything could change. */
.admin-topbar-titles { min-width: 0; }

.admin-page-title {
  font-family: var(--font-display);
  font-size: 1.125rem; color: var(--text);
  line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.admin-page-sub {
  font-size: .75rem; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.admin-topbar-user { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.admin-topbar-name { font-size: .8125rem; color: var(--text); }

/* Below this the name is the first thing to go: the face already says who
   is signed in, and the page title needs the room more. */
@media (max-width: 560px) { .admin-topbar-name { display: none; } }

.nav-avatar.is-admin { border-color: var(--primary); }

/* ── PROFILE ────────────────────────────────────────────────── */
.profile-header-card {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 55%, #3d1a12 100%);
  position: relative; overflow: hidden;
}
.profile-stat-val {
  color: var(--gold); font-size: 1.5rem;
  font-weight: 700; line-height: 1;
  letter-spacing: -.01em;
}
.profile-stat-lbl {
  color: rgba(255,255,255,0.55);
  font-size: .75rem; margin-top: 4px;
}

/* ── PLAN CARDS ─────────────────────────────────────────────── */
.plan-card {
  border-radius: var(--radius-xl); overflow: hidden;
  cursor: pointer; background: #fff;
  transition: box-shadow .2s, transform .15s;
  box-shadow: var(--shadow-md);
  border: 1px solid var(--border-light);
  display: flex; flex-direction: column;
}
.plan-card:hover {
  box-shadow: var(--shadow-lg); transform: translateY(-2px);
}
.plan-card.featured { border: 2px solid var(--primary); }
/* The bar across the top of a plan card. The colour was an inline style on
   the element; it belongs here with the rest of the card's accent. */
.plan-card-accent { height: 6px; background: var(--card-accent); }
.plan-card-body { padding: 20px; display: flex; flex-direction: column; flex: 1; }
/* Buttons do not wrap anywhere else in GIYA, and should not: a wrapped
   toolbar button looks broken. A plan card is the exception - its call to
   action is a whole sentence, and Filipino runs it to "Tingnan ang mga
   Naka-save na Plano", which walked straight out of the card at 1280px.
   Here the text wraps and the card grows instead. */
.plan-card .btn {
  white-space: normal;
  text-align: center;
  line-height: 1.35;
}

/* ── PREMIUM PLAN CARDS ──────────────────────────────────────
   One colour for all four. They are four ways into the same activity, not
   four categories, and four different browns said they were unrelated.

   The bubble is two organic shapes, not one circle: a pale wave behind and
   the solid accent in front, offset from each other and scaled at slightly
   different rates. That offset is the whole effect - a single shape reads
   as a circle growing, and two overlapping ones read as liquid, because
   the edge between them keeps moving as they travel.

   isolation:isolate is what makes it work. The bubble sits at z-index:-1 so
   the text stays above it, and without a stacking context of its own that
   -1 would put it behind the card's own background instead of on top.

   Only transform animates. Growing width and height would relayout the card
   sixty times a second and drag the whole grid with it. */
.plan-card {
  position: relative;
  isolation: isolate;

  --card-accent: var(--primary);
  --card-ink: #fff;

  /* The pale wave. A fixed value rather than a colour-mix of the accent,
     because --primary is the same hex in both themes and one literal is
     easier to check against the design than an arithmetic result. */
  --card-accent-soft: #B4685B;
}

.liquid-wave {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
}

.liquid-wave i {
  position: absolute;
  display: block;
  transition: transform 1s cubic-bezier(.16, 1, .3, 1);
}

/* Anchored to the top-right corner and bleeding well off it, so at rest the
   card shows a corner wave rather than a shape sitting on it. Most of each
   blob is outside the card and clipped away; what is left is the corner and
   nothing else.

   The pale one is the larger and sits further out on both axes, so its rim
   shows along the bottom-left of the solid one. That rim is the whole point -
   it is what separates two waves from one circle.

   The sizes are properties rather than literals because the two card families
   are different shapes. A wave that is a corner on a 380px-tall plan card is
   two fifths of a 180px-tall home card, and the same numbers on both looked
   like a mistake on the shorter one. */
.liquid-wave .b1 {
  width: var(--b1-size); height: var(--b1-size);
  right: var(--b1-right); top: var(--b1-top);
  background: var(--card-accent-soft);
  border-radius: 46% 54% 43% 57% / 52% 41% 59% 48%;
}

.liquid-wave .b2 {
  width: var(--b2-size); height: var(--b2-size);
  right: var(--b2-right); top: var(--b2-top);
  background: var(--card-accent);
  border-radius: 54% 46% 60% 40% / 44% 56% 44% 56%;
  transition-delay: .05s;
}

/* The default is the plan card's: about 108px of wave across the corner and
   74 down. */
.plan-card, .home-card {
  --b1-size: 160px; --b1-right: -52px; --b1-top: -86px;
  --b2-size: 140px; --b2-right: -44px; --b2-top: -78px;
}

/* Focus counts as hover. The cards are links, and a keyboard user should
   see what a mouse user sees.

   The scale is generous because the blob is not a circle: its border-radius
   makes the effective radius in the worst direction about 40% of its width,
   so covering the far corner of the card needs roughly ten, not the seven a
   circle of the same size would need. The excess is clipped and costs
   nothing - stopping short is what would show.

   --wave-scale lets a shorter card ask for less; the home cards have no list
   of points and are about half the height, so they do not need ten. */
.plan-card, .home-card { --wave-scale: 10; }

.plan-card:hover .b1, .plan-card:focus-visible .b1,
.home-card:hover .b1, .home-card:focus-visible .b1 {
  transform: scale(calc(var(--wave-scale) * .95));
}

.plan-card:hover .b2, .plan-card:focus-visible .b2,
.home-card:hover .b2, .home-card:focus-visible .b2 {
  transform: scale(var(--wave-scale));
}

.plan-card-num {
  position: absolute;
  top: 18px; right: 18px;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .55);
  border-radius: 50%;
  font-size: .75rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #fff;
  transition: color .35s ease, border-color .35s ease;
}

.plan-card-rule {
  display: block;
  width: 34px; height: 3px;
  border-radius: 2px;
  background: var(--card-accent);
  margin: 0 0 12px;
  transition: width .45s cubic-bezier(.2, .9, .3, 1), background-color .35s ease;
}

/* The tile is the accent at rest, the way the reference has it, and the
   shadow is what stops it reading as a flat square of colour. */
.plan-card-icon {
  width: 48px; height: 48px;
  border-radius: 16px;
  background: linear-gradient(145deg, var(--card-accent-soft), var(--card-accent));
  box-shadow: 0 10px 18px rgba(142, 59, 47, .28);
  display: flex; align-items: center; justify-content: center;
  transition: background .35s ease, box-shadow .35s ease;
}

.plan-card-icon i { font-size: 1.375rem; color: #fff; transition: color .35s ease; }

/* The badge sits on its own line under the icon rather than opposite it.
   Opposite the icon it shared the top-right corner with the wave and the
   number, and a dark badge on the dark wave is not a badge - "Traditional
   Route" was rendering as an unreadable "Trac". The corner belongs to the
   wave; the badge has the width of the card to itself down here. */
.plan-card-badge-row { display: block; margin: 14px 0 12px; }

.plan-card-title { display: block; font-size: .9375rem; font-weight: 700; color: var(--text); line-height: 1.3; margin-bottom: 8px; }
.plan-card-desc  { display: block; font-size: .75rem; color: var(--text-muted); line-height: 1.7; margin-bottom: 14px; }
.plan-card-points { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.plan-card-point { font-size: .75rem; color: var(--text); }
.plan-card-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--card-accent); flex-shrink: 0; }

.plan-card-title, .plan-card-desc, .plan-card-point,
.plan-card-dot, .plan-card .badge, .plan-card .btn {
  transition: color .35s ease, background-color .35s ease, border-color .35s ease;
}

.plan-card:hover, .plan-card:focus-visible { border-color: var(--card-accent); }

.plan-card:hover .plan-card-title,
.plan-card:hover .plan-card-desc,
.plan-card:hover .plan-card-point,
.plan-card:focus-visible .plan-card-title,
.plan-card:focus-visible .plan-card-desc,
.plan-card:focus-visible .plan-card-point {
  color: var(--card-ink);
}

.plan-card:hover .plan-card-dot,
.plan-card:focus-visible .plan-card-dot { background: var(--card-ink); }

.plan-card:hover .plan-card-rule,
.plan-card:focus-visible .plan-card-rule { width: 64px; background: var(--card-ink); }

/* The tile inverts so it keeps its own contrast once the card is flooded. */
.plan-card:hover .plan-card-icon,
.plan-card:focus-visible .plan-card-icon {
  background: var(--card-ink);
  box-shadow: 0 10px 18px rgba(0, 0, 0, .18);
}

.plan-card:hover .plan-card-icon i,
.plan-card:focus-visible .plan-card-icon i { color: var(--card-accent); }

.plan-card:hover .badge,
.plan-card:focus-visible .badge {
  background: transparent;
  border: 1px solid var(--card-ink);
  color: var(--card-ink);
}

.plan-card:hover .btn,
.plan-card:focus-visible .btn {
  background: var(--card-ink);
  border-color: var(--card-ink);
  color: var(--card-accent);
}

/* With motion turned down the card still changes - it arrives rather than
   travels. The colour is information; the sweep is not. */
@media (prefers-reduced-motion: reduce) {
  .liquid-wave { display: none; }

  .plan-card:hover, .plan-card:focus-visible,
  .home-card:hover, .home-card:focus-visible { background: var(--card-accent); }

  .plan-card:hover .plan-card-num,
  .plan-card:focus-visible .plan-card-num { border-color: var(--card-ink); color: var(--card-ink); }
}

/* ── HOME JOURNEY CARDS ──────────────────────────────────────
   The four ways in, on the home page. Same wave as the plan hub, because
   two of them lead to the same two screens and a different treatment for
   the same destination is just noise.

   Shorter than the plan cards - an icon, a line, a sentence and a link,
   with no list of points - so they ask the wave for a smaller scale. */
.home-card {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  text-decoration: none;

  --card-accent: var(--primary);
  --card-ink: #fff;
  --card-accent-soft: #B4685B;

  /* Roughly three quarters of the plan card's wave, for a card roughly half
     its height: about 82px across the corner and 56 down. */
  --b1-size: 122px; --b1-right: -40px; --b1-top: -66px;
  --b2-size: 106px; --b2-right: -34px; --b2-top: -60px;

  /* A smaller shape on a smaller card still has less distance to cover, so
     the scale comes down with it rather than staying at the hub's ten. */
  --wave-scale: 8.5;
}

.home-card-icon {
  width: 48px; height: 48px;
  border-radius: 16px;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(145deg, var(--card-accent-soft), var(--card-accent));
  box-shadow: 0 10px 18px rgba(142, 59, 47, .28);
  transition: background .35s ease, box-shadow .35s ease;
}

.home-card-icon i { font-size: 1.375rem; color: #fff; transition: color .35s ease; }

.home-card-title { display: block; font-size: .9375rem; font-weight: 700; color: var(--text); }
.home-card-desc  { display: block; font-size: .75rem; color: var(--text-muted); margin-top: 4px; line-height: 1.6; }

.home-card-go {
  margin-top: auto;
  font-size: .75rem; font-weight: 700;
  color: var(--card-accent);
  display: flex; align-items: center; gap: 4px;
}

.home-card-go i { font-size: .75rem; transition: transform .35s cubic-bezier(.2, .9, .3, 1); }

.home-card-title, .home-card-desc, .home-card-go {
  transition: color .35s ease;
}

.home-card:hover, .home-card:focus-visible { border-color: var(--card-accent); }

.home-card:hover .home-card-title,
.home-card:hover .home-card-desc,
.home-card:hover .home-card-go,
.home-card:focus-visible .home-card-title,
.home-card:focus-visible .home-card-desc,
.home-card:focus-visible .home-card-go { color: var(--card-ink); }

/* The chevron moves the way the card is asking you to. */
.home-card:hover .home-card-go i,
.home-card:focus-visible .home-card-go i { transform: translateX(4px); }

/* The tile inverts so it keeps its own contrast once the card is flooded. */
.home-card:hover .home-card-icon,
.home-card:focus-visible .home-card-icon {
  background: var(--card-ink);
  box-shadow: 0 10px 18px rgba(0, 0, 0, .18);
}

.home-card:hover .home-card-icon i,
.home-card:focus-visible .home-card-icon i { color: var(--card-accent); }

/* A checkbox with an explanation under its label, in the admin forms. The
   label wraps the input so the whole block is the hit area - a 13px tick box
   is not something to ask anyone to aim at. */
.dm-check {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--bg-input);
}
.dm-check input { margin-top: 2px; flex-shrink: 0; }
.dm-check strong { display: block; font-size: 0.8125rem; color: var(--text); }
.dm-check small  { display: block; font-size: .75rem; color: var(--text-muted); margin-top: 2px; }

/* The town a Major result stands for. Gold rather than the neutral grey of a
   category tag, because under that filter the town is the reason the church
   is in the list at all. */
.mx-tag.is-town {
  background: var(--gold-bg); color: var(--primary);
  font-weight: 700;
}

/* ── ITINERARY DETAILS BAR (the map in planning mode) ────────── */
/* A row of fields above the map. Flex rather than a fixed grid so the
   Cebuano labels, which run about twice the English, wrap onto a second
   line instead of squeezing the inputs. */
.plan-bar {
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: flex-end; margin-bottom: 16px;
}
.plan-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.plan-field .form-label-sm { margin: 0; }
.plan-field .giya-input { width: 100%; }

@media (max-width: 640px) {
  /* On a phone every field takes the full width - three of them side by
     side at 390px leaves each one too narrow to read what you typed. */
  .plan-bar { gap: 10px; }
  .plan-field { flex: 1 1 100% !important; }
}

/* ── HISTORY ITEM ───────────────────────────────────────────── */
.history-item {
  display: flex; align-items: center;
  gap: 14px; padding: 16px; border-radius: var(--radius-lg);
  background: #fff; border: 1px solid var(--border-light);
  box-shadow: var(--shadow-sm); margin-bottom: 10px;
  transition: box-shadow .2s;
}
.history-item:hover { box-shadow: var(--shadow-md); }

/* The church name and place. flex-basis 0 with min-width 0 lets it take the
   room left over and lets long names wrap, rather than forcing the row wider
   than the screen. */
.history-body { flex: 1 1 0; min-width: 0; }
.history-name {
  font-size: .875rem; font-weight: 700; color: var(--text);
  overflow-wrap: anywhere;
}

/* Whatever the row lets you do - View Details and the remove cross, or the
   rating and Review. Grouped so it can move as one. */
.history-actions {
  display: flex; align-items: center; gap: 8px; flex-shrink: 0;
}

@media (max-width: 560px) {
  /*
     On a phone the buttons would not shrink, so the name was squeezed into a
     column one word wide - "Cebu / Metropolitan / Cathedral" down the side of
     a View Details button.

     The row wraps instead: photograph and name keep the first line, and the
     controls take a line of their own underneath, right-aligned. flex-basis
     100% is what forces that second line; without it they would simply sit
     back on the first one and crush the name again.
  */
  .history-item { flex-wrap: wrap; row-gap: 12px; padding: 14px; }
  .history-actions { flex: 1 0 100%; justify-content: flex-end; }

  /* Where the row has a main control and a small one after it - View Details
     and the remove cross - the main one takes the width, so the new line
     reads as a bar rather than two buttons floating against a bare left
     half. A lone Review button is last, so it keeps its own size. */
  .history-actions > .btn:not(:last-child) { flex: 1 1 auto; justify-content: center; }
}
/* A pilgrimage tip: icon and sentence on one baseline. The icon gets a box
   exactly one line tall, so it aligns with the first line of the tip rather
   than centring itself against the whole wrapped paragraph. */
.plan-tip {
  display: grid; grid-template-columns: 20px 1fr; gap: 12px;
  align-items: start; padding: 14px; margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
}
.plan-tip-icon {
  font-size: 1.125rem; color: var(--gold);
  height: 1.275rem; align-self: start; justify-self: center;
}
.plan-tip-text {
  font-size: .75rem; color: var(--text-muted);
  line-height: 1.7; margin: 0;
}

/* ── Avatar upload ───────────────────────────────────────────
   The photograph is the control: a label over a hidden file input, with
   the camera surfacing on hover, on keyboard focus, and while a file is
   being dragged over it. Focus is visible because the label is the only
   thing a keyboard user can reach - the input itself is hidden. */
.avatar-edit { display: flex; align-items: center; gap: 16px; }

.avatar-drop {
  position: relative; width: 84px; height: 84px; flex-shrink: 0;
  border-radius: 20px; overflow: hidden; cursor: pointer;
  border: 2px solid var(--border); background: var(--gold-bg);
  transition: border-color .18s, transform .18s;
}
.avatar-drop:hover { border-color: var(--gold); }
.avatar-drop:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
.avatar-drop.is-dragging { border-color: var(--gold); border-style: dashed; transform: scale(1.03); }

.avatar-drop-face, .avatar-drop-face img, .avatar-drop-initials {
  display: block; width: 100%; height: 100%;
}
.avatar-drop-face img { object-fit: cover; }
.avatar-drop-initials {
  display: flex; align-items: center; justify-content: center;
  background: var(--gold); color: var(--primary-dark);
  font-family: var(--font-display); font-size: 1.75rem; font-weight: 700;
}

.avatar-drop-veil {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; background: rgba(36,28,24,.62); color: #fff;
  font-size: .75rem; font-weight: 700; letter-spacing: .04em;
  opacity: 0; transition: opacity .18s;
}
.avatar-drop-veil .bi { font-size: 1.125rem; }
.avatar-drop:hover .avatar-drop-veil,
.avatar-drop:focus-visible .avatar-drop-veil,
.avatar-drop.is-dragging .avatar-drop-veil { opacity: 1; }

.avatar-edit-text { min-width: 0; }
.avatar-edit-lead {
  font-size: .8125rem; font-weight: 600; color: var(--text); margin: 0 0 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.avatar-edit-hint { font-size: .75rem; color: var(--text-muted); margin: 0; line-height: 1.5; }

@media (prefers-reduced-motion: reduce) {
  .avatar-drop, .avatar-drop-veil { transition: none; }
  .avatar-drop.is-dragging { transform: none; }
}

/* A church's photograph in a history or favourites row, sized to match the
   icon tile it replaced so the rows keep their rhythm. */
.history-thumb {
  width: 42px; height: 42px; border-radius: var(--radius-md);
  object-fit: cover; flex-shrink: 0;
  background: var(--gold-bg); border: 1px solid var(--border);
}
.history-thumb-wrap { position: relative; flex-shrink: 0; line-height: 0; }
.history-thumb-badge {
  position: absolute; right: -3px; bottom: -3px;
  font-size: .75rem; color: #D4183D;
  background: var(--bg-card, #fff); border-radius: 50%;
  padding: 3px; box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
html[data-theme="dark"] .history-thumb-badge { background: var(--bg); }

.history-icon {
  width: 42px; height: 42px; border-radius: var(--radius-md);
  background: var(--gold-bg); display: flex;
  align-items: center; justify-content: center;
  flex-shrink: 0; font-size: 1.25rem;
}

/* ── MAP PAGE ───────────────────────────────────────────────── */

.map-wrap { flex: 1; position: relative; }
#map { width: 100%; height: 100%; }

/* ── ACTIVE PILGRIMAGE ──────────────────────────────────────── */
.active-layout { display: flex; overflow: hidden; }

/* The sheet's handle belongs to the phone layout and is given its size and
   its behaviour there. Stated here so that everywhere else it is simply not
   on the page - a button with no styling of its own still renders, and it
   was sitting in the middle of the desktop sidebar. */
.ap-grab { display: none; }
.active-sidebar {
  width: 340px; flex-shrink: 0; background: #fff;
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column; overflow: hidden;
}
.stop-item {
  display: flex; align-items: flex-start;
  gap: 10px; padding: 12px 16px;
  border-left: 3px solid transparent;
  transition: all .15s;
}
.stop-item.is-current {
  background: rgba(142,59,47,0.04);
  border-left-color: var(--primary);
}

/* ── CHATBOT ────────────────────────────────────────────────── */
.chat-bubble {
  padding: 12px 16px; border-radius: 18px;
  max-width: 540px; font-size: 0.875rem;
  line-height: 1.75;
}
.chat-bubble-bot {
  background: var(--bg); color: var(--text);
  border-radius: 18px 18px 18px 4px;
}
.chat-bubble-user {
  background: var(--primary); color: #fff;
  border-radius: 18px 18px 4px 18px;
}
/* The assistant's title bar, inside the panel. */
.chat-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 36px 20px 13px;      /* the top clears the guide's hands */
  border-bottom: 1px solid var(--border-light);
  background: var(--bg-secondary);
}
.chat-bar strong {
  display: block; font-family: var(--font-display);
  font-size: 0.9375rem; color: var(--text);
}
.chat-bar span {
  display: flex; align-items: center; gap: 6px;
  font-size: .75rem; color: var(--text-muted); margin-top: 2px;
}

/* ── UPGRADE CARD ───────────────────────────────────────────── */
.upgrade-card {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  border-radius: var(--radius-xl); padding: 20px;
  position: relative; overflow: hidden;
}
.upgrade-card::before {
  content: ''; position: absolute;
  top: -20px; right: -20px;
  width: 100px; height: 100px; border-radius: 50%;
  background: var(--gold); opacity: .15;
}

/* ── LOADING: the pin, turning ──────────────────────────────────
   GIYA's mark is a map pin, and a pin is a physical thing, so it turns
   like one rather than fading in place. Two planes at right angles - the
   face and a thin edge - give it a thickness that survives the moment it
   is side-on, which is what separates a solid object from a flipping
   sticker. The shadow underneath breathes with the bob, so the pin reads
   as being above the ground rather than drawn on it.

   Everything is CSS. A loading indicator that needs JavaScript to run is
   a loading indicator that is not there for the part of the wait that
   matters.
   ───────────────────────────────────────────────────────────── */
.giya-loading {
  position: absolute; inset: 0; z-index: 700;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 16px;

  /* The map's own frame, borrowed. The panel covers the canvas, so without
     these the border and the rounded corners the devotee is looking at
     simply vanish for the length of the wait and come back afterwards. */
  background: #F3EADB;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);

  transition: opacity .45s ease, visibility .45s ease;
}
/* Full-bleed maps - the pilgrimage, and anything gone fullscreen - have no
   frame of their own to imitate. */
.map-wrap > .giya-loading,
.is-fullscreen .giya-loading { border: 0; border-radius: 0; }
/* Ready is a state, not a moment: the tiles have arrived, the panel
   fades, and the map underneath was never covered by anything opaque
   again. */
.is-map-ready .giya-loading { opacity: 0; visibility: hidden; }

.giya-loading-scene {
  width: 62px; height: 78px;
  perspective: 520px;
  display: flex; align-items: flex-end; justify-content: center;
  position: relative;
}

.giya-loading-pin {
  width: 56px; height: 56px;
  position: absolute; top: 0; left: 3px;
  transform-style: preserve-3d;
  animation: giya-pin-turn 2.6s cubic-bezier(.62,.02,.38,.98) infinite,
             giya-pin-bob 1.3s ease-in-out infinite alternate;
}
/* The two faces, half a thickness either side of the middle.

   Both parts matter. The offset is what keeps the edge - a plane with no
   width when seen end-on, which a browser still draws as a hairline -
   from appearing as a line down the middle of the logo. And hiding each
   face's own reverse is what stops the far one showing through the near
   one as a mirror image while the pin turns. */
.giya-loading-face {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}
/* 5px, against an edge that reaches 4px: a whole pixel of clearance on
   each side. Sitting the faces exactly on the edge's outermost sliver put
   the two at the same depth, and coplanar surfaces fight - which is the
   hairline down the middle of the logo, back again in a subtler form. */
.giya-loading-face.is-front { transform: translateZ(5px); }
.giya-loading-face.is-back  { transform: rotateY(180deg) translateZ(5px); }
.giya-loading-face svg { width: 100%; height: 100%; display: block; }

/* The edge, in the middle, a quarter turn from the faces - so as they go
   side-on this comes round to meet the eye and the pin has a thickness
   instead of vanishing into nothing. */
.giya-loading-edge {
  position: absolute; top: 3px; left: 50%;
  width: 8px; height: 50px; margin-left: -4px;
  border-radius: 4px;
  background: linear-gradient(180deg, #C9993A 0%, #D7A94A 45%, #A87B2C 100%);
  transform: rotateY(90deg);
}

.giya-loading-shadow {
  width: 34px; height: 8px; border-radius: 50%;
  background: rgba(142, 59, 47, .22);
  filter: blur(1px);
  animation: giya-pin-shadow 1.3s ease-in-out infinite alternate;
}

.giya-loading-text {
  font-size: 0.8125rem; color: var(--text-muted);
  font-weight: 600; margin: 0; text-align: center;
}

@keyframes giya-pin-turn { to { transform: rotateY(360deg); } }
@keyframes giya-pin-bob    { from { translate: 0 0; } to { translate: 0 -7px; } }
@keyframes giya-pin-shadow {
  from { transform: scale(1);   opacity: .28; }
  to   { transform: scale(.72); opacity: .16; }
}

html[data-theme="dark"] .giya-loading { background: var(--bg); }

/* ── LOADING: skeletons ─────────────────────────────────────────
   A skeleton is a promise about what is coming, so it is only honest
   where something really is on its way: a list JavaScript has not built
   yet, an image still crossing the network. Server-rendered pages
   arrive complete and get none - a skeleton there is a delay we would
   be inventing.
   ───────────────────────────────────────────────────────────── */
.gs {
  background: linear-gradient(100deg,
    var(--bg-secondary) 30%, rgba(255,255,255,.65) 50%, var(--bg-secondary) 70%);
  background-size: 220% 100%;
  animation: giya-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes giya-shimmer {
  from { background-position: 130% 0; }
  to   { background-position: -30% 0; }
}

.gs-line   { height: 11px; }
.gs-line.is-title { height: 13px; }
.gs-thumb  { width: 44px; height: 44px; border-radius: var(--radius-md); flex-shrink: 0; }
.gs-dot    { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; }

/* One row of a church list, shaped like the real thing so nothing jumps
   when the real thing replaces it. */
.gs-row {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 13px; margin-bottom: 8px;
  border: 1px solid var(--border-light); border-radius: var(--radius-lg);
  background: #fff;
}
.gs-row-body { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 7px; }

/* A section standing in for itself while the page's pictures arrive.

   The skeleton is display:none until the script says otherwise, and the real
   content is untouched until then - so a page whose JavaScript failed shows
   the content, not a grey rectangle that never resolves. That is the whole
   reason the states are this way round. */
.section-skeleton { display: none; }

[data-loading-section].is-loading > .section-skeleton { display: block; }
[data-loading-section].is-loading > .section-real     { display: none; }

/* Shaped like a home card: icon tile, heading, two lines, a link. */
.gs-card {
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px;
  min-height: 200px;
}

.gs-card .gs-thumb { width: 48px; height: 48px; border-radius: 16px; }
.gs-card-go { width: 84px; height: 10px; margin-top: auto; }

/* Shaped like an activity row. */
.gs-row-date { width: 56px; height: 56px; border-radius: 12px; }
.gs-row-when { width: 74px; height: 10px; flex-shrink: 0; }

html[data-theme="dark"] .gs-row { background: transparent; }
html[data-theme="dark"] .gs {
  background: linear-gradient(100deg,
    rgba(255,255,255,.05) 30%, rgba(255,255,255,.12) 50%, rgba(255,255,255,.05) 70%);
  background-size: 220% 100%;
}

/* A photo that has not landed yet.

   The shimmer goes on the image itself, as its own background, rather than
   on a wrapper around it: an unloaded <img> is transparent, so its
   background is exactly what shows through, and it stops showing the moment
   the photo covers it. No extra element, no change to any layout, and
   nothing to go wrong in a page that has already positioned its own image -
   which is what a wrapper here did do. */
img.gs-img {
  background: linear-gradient(100deg,
    var(--bg-secondary) 30%, rgba(255,255,255,.6) 50%, var(--bg-secondary) 70%);
  background-size: 220% 100%;
  animation: giya-shimmer 1.5s ease-in-out infinite;
}
img.gs-img.is-loaded { background: none; animation: none; }

/* Nobody should be made queasy by a loading state. */
@media (prefers-reduced-motion: reduce) {
  .giya-loading-pin, .giya-loading-shadow, .gs, img.gs-img { animation: none; }
  .gs, img.gs-img { background: var(--bg-secondary); }
}

/* ── SPINNER ────────────────────────────────────────────────── */
.spinner {
  width: 20px; height: 20px;
  border: 2px solid rgba(255,255,255,0.3);
  border-top-color: #fff; border-radius: 50%;
  animation: spin .7s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── RESPONSIVE ─────────────────────────────────────────────── */
@media (max-width: 1024px) {
  .footer-grid { grid-template-columns: 1fr 1fr; }

  /* The narrower sidebar sets the PROPERTY, not the width. Setting width
     here overrode var(--admin-rail) outright, so between 768 and 1024 the
     collapse toggle moved the chevron and nothing else. */
  .admin-layout { --admin-rail: 220px; }
}
@media (max-width: 768px) {
  .page-wrap { padding: 24px 16px; }
  .section-title { font-size: 1.375rem; }
  .auth-header h1 { font-size: 1.25rem; }

  /* The pilgrimage, on a phone.

     Stacking the two panes gave the sidebar a fixed 260px, and its header
     alone is 258 of them - so the stop list got 16px and both buttons, Mark
     Visited among them, were clipped away entirely. The one action the
     screen exists for could not be reached.

     So it stops being a two-pane layout squeezed narrow. The map takes the
     whole screen, a compact bar sits over the top of it, and the sidebar
     becomes a sheet along the bottom: the current church and Mark Visited
     always in reach of a thumb, the full route a drag away. --ap-sheet is
     the sheet's height, and anything that has to stay clear of it reads
     that rather than guessing. */
  .active-layout {
    display: block; position: relative; overflow: hidden;

    /* A starting value only. The strip is measured after it renders and the
       real height is published back over this, because a number typed here
       is a number that goes stale: 104px was measured for an 8px progress
       bar, and the 46px candle row that replaced it was sliced by 14px on
       every phone. Anything that has to clear the strip reads the property,
       so re-measuring is the whole fix. */
    --ap-head: 128px;
  }

  /* The strip folds instead of stacking.

     Stacked it was four rows - back link, route, count, candles - and 151px
     of a 664px screen, which is more than a fifth of the phone spent saying
     where you already know you are. Folded it is two: the chevron and the
     route on one line with the candles beside them, the count under. */
  .active-layout .ap-head {
    position: fixed; top: 64px; left: 0; right: 0; z-index: 620;

    /* Sized by its contents, not by the variable it publishes - the variable
       is for everything else. Setting both would be a loop. */
    height: auto; min-height: 0;
    padding: 8px 14px 10px;
    display: flex; flex-direction: column; justify-content: center;
  }

  /* The decorative circle is for a 340px column, not a full-width strip. */
  .active-layout .ap-head::after { display: none; }

  /* The chevron alone. The label is still read aloud; it is the twelve
     characters of "All itineraries" that the strip cannot afford. */
  .active-layout .ap-back {
    position: absolute; left: 0; top: 2px;
    width: 26px; height: 26px;
    margin: 0;
    justify-content: center;
    font-size: .9375rem;
  }

  .active-layout .ap-back-label {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }

  /* Route and candles on one line. The candles take what is left after the
     name, down to a floor - a name long enough to need the whole width still
     leaves the row readable, and the candles shrink rather than wrap. */
  .active-layout .ap-head-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-left: 30px;
  }

  .active-layout .ap-head-titles { flex: 1 1 auto; min-width: 0; }

  .active-layout .ap-title {
    font-size: 1.0625rem; margin: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  .active-layout .ap-count { font-size: .75rem; }

  .active-layout .ap-candles {
    flex: 0 1 auto;
    width: 44%;
    max-width: 190px;
    min-width: 56px;
    height: 34px;
    margin: 0;
    justify-content: flex-end;
  }

  /* Shorter candles for a shorter strip. */
  .active-layout .ap-candles .candle { max-width: 14px; }
  .active-layout .ap-candles .candle-wax { height: 15px; }
  .active-layout .ap-candles .candle-flame { bottom: 19px; width: 7px; height: 10px; margin-left: -3.5px; }
  .active-layout .ap-candles .candle-glow  { bottom: 13px; width: 24px; height: 24px; margin-left: -12px; }

  /* "0 of 3 stops visited" and the candles already say this, and the strip
     has no room for a third way of saying it. */
  .active-layout #progressLabel { display: none; }

  /* Closed, the list is not peeking through the gap - a few pixels of a row
     read as something broken rather than as a hint. */
  .active-sidebar:not(.is-open) .ap-list { display: none; }

  /* The map stops where the sheet starts, rather than running on behind it
     and having every control inside it pushed back up by the sheet's height.

     That push was the old answer and it kept going wrong, because the map and
     the sheet were measuring from different bottoms: the sheet is fixed to
     the visual viewport, the map was absolute inside a layout sized with
     100vh, and on iOS those differ by the height of the browser toolbar.
     Leaflet's attribution was offset by exactly the right amount from exactly
     the wrong place, and landed on the church name.

     Ending the map at the sheet's top removes the question. The attribution
     sits at the bottom of the map, which IS the top of the sheet, and no
     offset is needed anywhere. Nothing is lost visually - the sheet is
     opaque, so the part of the map that used to run behind it was never
     visible. */
  .active-layout .map-wrap {
    position: absolute;
    inset: var(--ap-head) 0 var(--ap-sheet, 196px) 0;

    /* Matched to the sheet's own transition, so the two edges move together
       instead of the map snapping and the sheet sliding. */
    transition: bottom .28s cubic-bezier(.2, .8, .2, 1);
  }

  /* One frame with the height off, so the closed sheet can be measured
     rather than assumed. 196px was the assumption, and it is wrong on any
     phone with a home indicator: .ap-foot adds env(safe-area-inset-bottom)
     to its padding, which is 34px on an iPhone. The map's attribution reads
     the same property, which is how it ended up printed across the church
     name. */
  .active-sidebar.is-measuring {
    height: auto !important;
    transition: none !important;
  }

  /* One frame with the height off, so the closed sheet can be measured
     rather than assumed. 196px was the assumption, and it is wrong on any
     phone with a home indicator: .ap-foot adds env(safe-area-inset-bottom)
     to its padding, which is 34px on an iPhone. */
  .active-sidebar.is-measuring {
    height: auto !important;
    transition: none !important;
  }

  .active-sidebar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 640;
    width: 100%; height: var(--ap-sheet, 196px);
    max-height: calc(100dvh - 64px - var(--ap-head) - 24px);
    border: 0; border-top: 1px solid var(--border);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    box-shadow: 0 -6px 28px rgba(36, 28, 24, .18);
    transition: height .28s cubic-bezier(.2, .8, .2, 1);

    /* Declared here rather than added when the drag starts. A browser
       decides what a gesture is allowed to do from the element it began on,
       at the moment it began - a class added during pointerdown is already
       too late, which is why dragging the sheet downwards was being
       cancelled as though it were the start of a scroll. */
    touch-action: none;
  }
  /* The stop list is the one part that still scrolls. */
  .active-layout .ap-list { touch-action: pan-y; }
  /* While a finger is on it the height follows the finger, and a transition
     would make it lag behind.

     touch-action goes on the whole sheet for the length of the drag, not
     just on the handle: dragging down takes the pointer off the handle and
     over the banner and the list, and the browser reads that as the start of
     a scroll, cancels the gesture, and the sheet stops halfway. */
  .active-sidebar.is-dragging { transition: none; touch-action: none; }

  /* While the sheet is being dragged the map ignores the pointer entirely.

     Not for the map's sake - for the sheet's. Dragging downwards shrinks the
     sheet, which slides Leaflet's attribution up into the path of the finger,
     and a link under a moving pointer is a link the browser starts dragging:
     Chromium fired dragstart on that anchor and stopped sending mouse moves,
     so the gesture died a third of the way down and the sheet sprang back
     open. Dragging upwards moved the link away, which is why only one
     direction ever appeared to be broken. */
  .active-layout:has(.active-sidebar.is-dragging) .map-wrap { pointer-events: none; }

  /* And nothing in here is a thing to drag on its own. */
  .active-layout a, .active-layout img { -webkit-user-drag: none; }

  .ap-grab {
    display: flex; align-items: center; justify-content: center;
    height: 26px; flex-shrink: 0;
    background: none; border: 0; padding: 0; width: 100%;
    cursor: grab; touch-action: none;
    -webkit-user-select: none; user-select: none;
  }
  .ap-grab-bar {
    width: 40px; height: 4px; border-radius: 999px;
    background: var(--border); transition: background .15s;
  }
  .ap-grab:active .ap-grab-bar { background: var(--primary-light); }

  /* Closed, the sheet is the next church and the one button that acts on it.
     Ending the pilgrimage is not a thing to put under a wandering thumb. */
  .active-sidebar:not(.is-open) .ap-end { display: none; }

  .active-layout .ap-banner { padding: 10px 16px 12px; }
  .active-layout .ap-foot { padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); }

  /* Clear of the sheet, whatever height it is at.

     --ap-sheet lives on the document element, not on the layout, because the
     chat button is not inside it - and the fallback is the closed height, so
     both of these are right before a single line of script has run. */
  /* The map already ends at the sheet's top (see .map-wrap above), so the
     pill sits just above the map's own bottom edge. Adding the sheet's
     height again floated it into the middle of the route. */
  .active-layout .ap-summary { bottom: 30px; }   /* clear of the map credits */
  body:has(.active-layout) .giya-fab { bottom: calc(var(--ap-sheet, 196px) + 14px); }

  /* The legend goes to one line here - three stacked labels were taking a
     fifth of a phone-sized map. It still wrapped to two at 390px, which put
     it back to a sixth, so the dashes and the gaps are tighter again and
     nowrap keeps it honest: if it cannot fit on one line it scrolls rather
     than growing downward into the map. */
  .active-layout .ap-legend {
    flex-direction: row; flex-wrap: nowrap;
    gap: 0 7px; padding: 4px 7px;
    top: 8px; left: 8px; max-width: calc(100% - 66px);
    font-size: .5rem;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .active-layout .ap-legend::-webkit-scrollbar { display: none; }
  .active-layout .ap-legend-item { gap: 3px; }
  .active-layout .ap-legend-dash { width: 11px; }

  /* The map's own edge moves with the sheet; under reduced motion it just
     arrives there. */
  @media (prefers-reduced-motion: reduce) {
    .active-layout .map-wrap { transition: none; }
  }
  /* Below this the sidebar is a strip across the top, so there is no rail
     to collapse into and the toggle would be a button that does nothing. */
  .admin-layout { flex-direction: column; }
  .admin-sidebar { width: 100%; height: auto; position: relative; }
  .admin-rail-toggle { display: none; }
  .admin-topbar { padding: 0 16px; }
  .admin-content { padding: 16px 14px; }
}
@media (max-width: 600px) {
  .auth-card { border-radius: 0; min-height: calc(100vh - 64px); }
  .auth-content { min-height: calc(100vh - 64px); padding: 0; align-items: flex-start; }
}

/* ── LEAFLET TOOLTIP ────────────────────────────────────────── */
/* ── PROGRESS BAR ───────────────────────────────────────────── */
/* ── PLAN HUB LAYOUT ─────────────────────────────────────────
   The grid and its breakpoints. These were three !important overrides in a
   @push('head') block inside the template, which is the only way to beat an
   inline grid-template-columns - so the inline one is gone and the
   !importants went with it. */
.hub-title {
  font-family: var(--font-display);
  font-size: 2rem;
  margin: 0 0 10px;
}

.hub-lead {
  color: var(--text-muted);
  font-size: .9375rem;
  line-height: 1.7;
  max-width: 580px;
  margin: 0 0 40px;
}

.plan-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin-bottom: 48px;
}

@media (max-width: 1024px) { .plan-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .plan-grid { grid-template-columns: 1fr; } }

.hub-bottom {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 32px;
  border-top: 1px solid var(--border-light);
  padding-top: 40px;
}

@media (max-width: 900px) { .hub-bottom { grid-template-columns: 1fr; } }

.hub-section-title { font-size: 1.25rem; }

.plan-card-cta { margin-top: auto; justify-content: center; }

/* A link should not have to say it is not underlined at every call site. */
.plan-card, .home-card, .history-item { text-decoration: none; }

/* ── RECENT ITINERARIES ROW ──────────────────────────────────── */
.history-icon .bi { font-size: 1.125rem; color: var(--primary); }
.history-body     { flex: 1; min-width: 0; }

.history-name {
  display: block;
  font-size: .875rem;
  font-weight: 700;
  color: var(--text);
}

.history-meta {
  display: block;
  font-size: .75rem;
  color: var(--text-muted);
  margin-top: 2px;
}

.history-chevron { color: var(--text-muted); }

/* ── HOME: UPCOMING ACTIVITIES ───────────────────────────────
   A row per event: a date tile, the name and church, the date. The soonest
   one carries the primary colour and the rest are gold-on-cream, so the list
   has a "next" rather than five rows shouting equally. */
.home-bottom-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 32px;
}

@media (max-width: 900px) {
  .home-bottom-grid { grid-template-columns: 1fr; }
}

.event-heading { margin-bottom: 4px; }
.event-lead    { margin-bottom: 20px; }

.event-row {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: 14px;
  margin-bottom: 10px;
  box-shadow: var(--shadow-sm);
}

.event-date {
  width: 56px; height: 56px;
  flex-shrink: 0;
  border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: 4px;
  background: var(--gold-bg);
}

.event-date.is-soonest { background: var(--primary); }

.event-kind {
  font-size: .5625rem;
  font-weight: 700;
  text-transform: none;
  line-height: 1.2;
  color: var(--primary);
}

.event-date.is-soonest .event-kind { color: var(--gold); }

.event-body  { flex: 1; min-width: 0; }
.event-name  { font-size: .875rem; font-weight: 700; color: var(--text); }
.event-where { font-size: .75rem; color: var(--text-muted); }
.event-where .bi { font-size: .75rem; }

.event-when {
  font-size: .75rem;
  font-weight: 700;
  color: var(--primary);
  white-space: nowrap;
}

/* ── HOME: CLOSING CARD ──────────────────────────────────────
   position:relative on the copy and the buttons, because upgrade-card paints
   a decorative shape of its own and both need to sit above it. */
.home-cta {
  padding: 28px;
  min-height: 320px;
}

.home-cta-copy,
.home-cta-actions { position: relative; }

.home-cta-icon {
  width: 48px; height: 48px;
  border-radius: 16px;
  background: rgba(215, 169, 74, .2);
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 16px;
}

.home-cta-icon .bi { font-size: 1.375rem; color: var(--gold); }

.home-cta-title {
  font-family: var(--font-display);
  color: #fff;
  font-size: 1.375rem;
  line-height: 1.3;
  margin: 0 0 12px;
}

.home-cta-lead {
  color: rgba(255, 255, 255, .7);
  font-size: .8125rem;
  line-height: 1.7;
  margin: 0;
}

/* ── REVEAL ON SCROLL ────────────────────────────────────────
   Rows arrive in order instead of all at once.

   Note what is NOT here: no rule hides anything by default. The hidden state
   lives behind .is-armed, which only giya-reveal.js adds, so a page whose
   JavaScript failed shows all its content rather than none of it. A reveal
   that hides first and shows later is one script error away from a blank
   screen.

   The stagger is a custom property the script sets per child, rather than a
   long list of :nth-child rules - the map's result list is as long as the
   search makes it, and nth-child stops at whatever number was typed out. */
[data-reveal].is-armed > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .5s ease, transform .55s cubic-bezier(.16, 1, .3, 1);
  transition-delay: calc(var(--reveal-i, 0) * 60ms);
  will-change: opacity, transform;
}

/* is-in is on the item, not on the container: each card arrives as it
   reaches the screen, which on a phone is one at a time. */
[data-reveal].is-armed > .is-in {
  opacity: 1;
  transform: none;
}

/* Once it has arrived, stop asking the compositor to keep a layer for it. */
[data-reveal] > .is-in { will-change: auto; }

/* Only the transition. The script reads the motion setting once at startup
   and never arms a container when it is on, so the hidden state is already
   unreachable in that case; the one path left is the setting being turned on
   while a row is mid-flight, and cutting the transition is what stops it.

   Setting opacity and transform here as well was the first version. Both were
   redundant - the revealed state sets them - and the probe proved it by
   passing with them deleted. Rules that cannot be reached are not safety, they
   are just more to read. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal].is-armed > * { transition: none; }
}

/* ── CANDLE PROGRESS ─────────────────────────────────────────
   One candle per stop, lit as each church is visited.

   Both places this appears sit on the dark brown panel, so the unlit wax is
   a pale warm grey that reads against it and the lit wax warms towards the
   gold rather than changing hue.

   Everything that moves is transform and opacity. The flame sits in a fixed
   box and scales inside it, so a row of twenty flickering candles never asks
   the browser to lay the row out again.

   Each candle flexes: twenty share the row and come out thin, seven come out
   full width. max-width stops three candles becoming three fence posts. */
.candle-row {
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  gap: 6px;
  height: 46px;
}

.candle {
  position: relative;
  flex: 1 1 0;
  max-width: 20px;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
}

/* The wax. Narrower than the candle box so the flame has room to be wider
   than the column it sits on without the columns touching. */
.candle-wax {
  width: 64%;
  height: 22px;
  border-radius: 3px 3px 2px 2px;
  background: linear-gradient(to right, #C9BCAE, #EDE3D6 38%, #C9BCAE);
  transition: background .5s ease;
}

.candle-wick {
  width: 2px;
  height: 4px;
  border-radius: 1px;
  background: #6B5647;
  transition: background .35s ease;
}

/* The flame and its halo are both absolutely placed above the wick, so
   neither one takes part in the column's layout. */
.candle-flame,
.candle-glow {
  position: absolute;
  left: 50%;
  pointer-events: none;
}

/* A teardrop: a circle with one corner pulled to a point, rotated so the
   point is up. Cheaper and sharper at this size than an SVG path. */
.candle-flame {
  bottom: 26px;
  width: 9px; height: 12px;
  margin-left: -4.5px;
  border-radius: 50% 50% 50% 50% / 62% 62% 38% 38%;
  background: radial-gradient(ellipse at 50% 72%, #FFF4D2 0%, var(--gold-light, #E8C06A) 42%, var(--gold, #D7A94A) 78%, rgba(215, 169, 74, 0) 100%);

  /* Unlit: no flame at all, collapsed onto the wick it would rise from. */
  opacity: 0;
  transform: scale(.2);
  transform-origin: 50% 100%;
  transition: opacity .25s ease, transform .45s cubic-bezier(.2, 1.5, .4, 1);
}

.candle-glow {
  bottom: 18px;
  width: 34px; height: 34px;
  margin-left: -17px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(232, 192, 106, .55) 0%, rgba(232, 192, 106, .18) 45%, rgba(232, 192, 106, 0) 70%);
  opacity: 0;
  transform: scale(.4);
  transition: opacity .4s ease, transform .6s cubic-bezier(.16, 1, .3, 1);
}

/* The one that is next. Not lit - just the wick saying where the flame goes,
   so the row reads as a place in a sequence rather than a score. */
.candle.is-next .candle-wick { background: var(--gold, #D7A94A); }

.candle.is-lit .candle-wax {
  background: linear-gradient(to right, #C2A886, #F3E6CE 38%, #C2A886);
}

.candle.is-lit .candle-wick { background: #4A3A2E; }

.candle.is-lit .candle-flame {
  opacity: 1;
  transform: scale(1);
  animation: candle-flicker 2.4s ease-in-out infinite;
}

.candle.is-lit .candle-glow {
  opacity: 1;
  transform: scale(1);
  animation: candle-breathe 2.4s ease-in-out infinite;
}

/* Lighting. Added by the script for one beat when a stop is marked, then
   removed - the bloom is the moment, the flicker is the steady state.

   The delays are staggered by the script so that lighting several at once
   (loading a route that is already half done) runs along the row rather than
   going up all together. */
.candle.is-lighting .candle-flame { animation: candle-catch .55s cubic-bezier(.2, 1.5, .4, 1) both; }
.candle.is-lighting .candle-glow  { animation: candle-bloom .75s cubic-bezier(.16, 1, .3, 1) both; }

@keyframes candle-catch {
  0%   { opacity: 0; transform: scale(.2)  translateY(2px); }
  35%  { opacity: 1; transform: scale(1.5) translateY(-1px); }
  70%  { transform: scale(.92); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes candle-bloom {
  0%   { opacity: 0; transform: scale(.4); }
  30%  { opacity: 1; transform: scale(1.5); }
  100% { opacity: 1; transform: scale(1); }
}

/* A real flame is never still and never symmetric. Both of these are slow
   and shallow on purpose: a row of candles that jitters is a row of candles
   nobody can read the count off. */
@keyframes candle-flicker {
  0%, 100% { transform: scale(1)      skewX(0deg); }
  25%      { transform: scale(1.06, .96) skewX(2deg); }
  50%      { transform: scale(.97, 1.05) skewX(-1.5deg); }
  75%      { transform: scale(1.04, .98) skewX(1deg); }
}

@keyframes candle-breathe {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%      { opacity: .72; transform: scale(1.08); }
}

/* Lit is lit, whether or not it moves. The flame and the glow still appear,
   they simply do not catch, bloom, flicker or breathe. */
@media (prefers-reduced-motion: reduce) {
  .candle-flame, .candle-glow { transition: none; }

  .candle.is-lit .candle-flame,
  .candle.is-lit .candle-glow,
  .candle.is-lighting .candle-flame,
  .candle.is-lighting .candle-glow { animation: none; opacity: 1; transform: scale(1); }
}

/* ── STAR RATING ────────────────────────────────────────────── */
.stars { display: flex; gap: 1px; }
.star-filled { color: var(--gold); }
.star-empty  { color: #E8D5C0; }

/* ── TABLE (admin) ──────────────────────────────────────────── */
.giya-table { width: 100%; border-collapse: collapse; }
.giya-table th {
  background: var(--gold-bg); color: var(--primary);
  font-size: 0.75rem; font-weight: 700; padding: 10px 16px;
  text-align: left;
}
.giya-table td {
  padding: 14px 16px; font-size: 0.875rem;
  border-bottom: 1px solid var(--border-light);
  color: var(--text);
}
.giya-table tr:last-child td { border-bottom: none; }
.giya-table tr:hover td { background: rgba(142,59,47,0.02); }

/* ============================================================
   Layout utilities
   A deliberately small set - only what the GIYA templates use.
   ============================================================ */
.d-flex          { display: flex; }

.d-none          { display: none !important; }
.flex-column     { flex-direction: column; }
.flex-wrap       { flex-wrap: wrap; }
.flex-row-reverse{ flex-direction: row-reverse; }
.align-items-center      { align-items: center; }
.align-items-start       { align-items: flex-start; }

.justify-content-between { justify-content: space-between; }
.justify-content-end     { justify-content: flex-end; }
.text-center { text-align: center; }
.text-start  { text-align: left; }
.w-100 { width: 100%; }
.m-0 { margin: 0; }
.mt-2 { margin-top: .5rem; }  .mt-3 { margin-top: 1rem; }   .mt-4 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: .25rem; } .mb-2 { margin-bottom: .5rem; }
.mb-3 { margin-bottom: 1rem; }   .mb-4 { margin-bottom: 1.5rem; }
.me-2 { margin-right: .5rem; }
.mt-auto { margin-top: auto; }
.pt-4 { padding-top: 1.5rem; }
.gap-1 { gap: .25rem; } .gap-2 { gap: .5rem; } .gap-3 { gap: .75rem; } .gap-4 { gap: 1.5rem; }
.bg-transparent { background: transparent; }
.border-0 { border: 0; }

@media (min-width: 576px) { .d-sm-inline { display: inline; } }
@media (max-width: 575px) { .d-sm-inline { display: none; } }

/* ── Modal (replaces the Bootstrap modal component) ─────────── */
.modal { position: fixed; inset: 0; z-index: 2000; display: none;
         align-items: center; justify-content: center; padding: 16px;
         background: rgba(36,28,24,0.5); backdrop-filter: blur(4px); }
.modal.is-open { display: flex; }
/* 460px is right for a confirmation and wrong for a form.

   The schedule modal has eleven fields and the destination modal has more
   than that plus a map, and both were being squeezed into a column narrower
   than a phone in landscape - so every row was one field tall and the form
   ran off the bottom of the screen.

   The width is a property so a dialog can ask for what it needs, and every
   value is a min() against the viewport, so "wide" on a desktop is still
   inside the screen on a phone. */
.modal-dialog {
  width: 100%;
  max-width: min(var(--modal-w, 460px), calc(100vw - 32px));
}

/* A form with two or three columns of fields. */
.modal-dialog.is-wide  { --modal-w: 880px; }

/* A form with a map in it as well. */
.modal-dialog.is-wider { --modal-w: 1060px; }

@media (max-width: 600px) {
  /* Edge to edge, with the gutter the page keeps everywhere else. */
  .modal-dialog { max-width: calc(100vw - 24px); }
}
/* A modal that holds a form rather than a sentence. The padding was an
   inline style, and so was a max-width that never applied because the
   dialog around it was narrower than the value being set. */
.dm-form-modal {
  border: none;
  border-radius: var(--radius-2xl);
  padding: 28px;
}

@media (max-width: 600px) {
  .dm-form-modal { padding: 20px 16px; }
}

.modal-content { background: #fff; border-radius: var(--radius-2xl);
                 box-shadow: 0 24px 64px rgba(0,0,0,0.22);
                 max-height: 90vh; overflow-y: auto;
                 animation: modalIn .2s ease; }

/* ── Pagination ─────────────────────────────────────────────── */
.giya-pagination { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.giya-pagination a, .giya-pagination span {
    min-width: 34px; height: 34px; padding: 0 10px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm); font-size: 0.8125rem;
    border: 1px solid var(--border); color: var(--text-muted);
    background: #fff; text-decoration: none;
}
.giya-pagination a:hover { border-color: var(--primary); color: var(--primary); }
.giya-pagination .is-active { background: var(--primary); color: #fff; border-color: var(--primary); font-weight: 700; }
.giya-pagination .is-disabled { opacity: .4; pointer-events: none; }

/* ── Chart (inline SVG, no charting library) ────────────────── */
.chart-svg { width: 100%; height: auto; display: block; overflow: visible; }
.chart-grid-line { stroke: rgba(142,59,47,0.09); stroke-width: 1; }
.chart-axis-text { font-size: .75rem; fill: var(--text-muted); font-family: var(--font-body); }
.chart-line { fill: none; stroke: var(--primary); stroke-width: 2.5;
              stroke-linejoin: round; stroke-linecap: round; }
.chart-area { fill: rgba(142,59,47,0.08); }
.chart-dot  { fill: var(--gold); stroke: #fff; stroke-width: 2; }
.chart-bar  { fill: var(--primary); }
.chart-bar:hover { fill: var(--primary-dark); }

/* ── Offline map (SVG projection, no tile server) ───────────── *//* ── Profile preferences ─────────────────────────────────────────────── */

.pref-choice {
    flex: 1;
    text-align: center;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text);
    background: #fff;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.pref-choice:hover { border-color: var(--gold); }
.pref-choice.is-active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
.pref-choice:focus-within { outline: 3px solid var(--gold); outline-offset: 2px; }

.pref-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 2px;
    font-size: 0.875rem;
    color: var(--text);
    cursor: pointer;
}
.pref-toggle-row + .pref-toggle-row { border-top: 1px solid var(--border); }

/* A lone switch that has no sibling above it to draw the line for it - a
   form modal with one toggle in the middle of a column of fields. */
.pref-toggle-row.is-ruled { border-top: 1px solid var(--border); margin-top: 6px; }

.pref-switch { position: relative; flex-shrink: 0; }
.pref-switch input { position: absolute; opacity: 0; width: 44px; height: 24px; margin: 0; cursor: pointer; }
.pref-switch-track {
    display: block;
    width: 44px; height: 24px;
    border-radius: 999px;
    background: #D6CCC0;
    transition: background .18s ease;
    pointer-events: none;
}
.pref-switch-track::after {
    content: "";
    position: absolute;
    top: 3px; left: 3px;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,.25);
    transition: transform .18s ease;
}
.pref-switch input:checked + .pref-switch-track { background: var(--primary); }
.pref-switch input:checked + .pref-switch-track::after { transform: translateX(20px); }
.pref-switch input:focus-visible + .pref-switch-track { outline: 3px solid var(--gold); outline-offset: 2px; }
/* ── Preference: font size ───────────────────────────────────── */
html[data-font="small"]  { font-size: 14px; }
html[data-font="medium"] { font-size: 16px; }
html[data-font="large"]  { font-size: 18px; }

/* ── Preference: dark theme ──────────────────────────────────── */
/*
   Brand tokens (--primary, --gold) stay put: the navbar and buttons are the
   identity of the app and should not turn salmon at night. Only surfaces,
   text, and borders flip. Anything painted with a hardcoded #fff or with
   var(--text) as a BACKGROUND needs an explicit rule below, since those two
   cannot follow a token swap.
*/
html[data-theme="dark"] {
  --primary:       #8E3B2F;
  --primary-dark:  #6B2D22;
  --primary-light: #C0705A;
  --gold:          #D7A94A;
  --gold-light:    #F0C76C;
  --gold-bg:       #3A2A1C;

  --bg:            #17110E;
  --bg-input:      #241C18;
  --bg-secondary:  #2A211C;

  --text:          #F2E8DC;
  --text-muted:    #A99384;

  --border:        rgba(215,169,74,0.20);
  --border-light:  rgba(215,169,74,0.10);

  --shadow-sm:     0 1px 6px rgba(0,0,0,0.40);
  --shadow-md:     0 2px 12px rgba(0,0,0,0.45);
  --shadow-lg:     0 8px 32px rgba(0,0,0,0.55);

  /* Surface colour for anything that was hardcoded white. */
  --surface:       #241C18;
  --surface-2:     #2E241E;
}

html[data-theme="dark"] body { background: var(--bg); color: var(--text); }

/* ── Every light surface in the sheet, enumerated ──────────────
   These paint themselves with a literal #fff, or with var(--text) used as a
   BACKGROUND. Neither can follow a token swap, so each needs its own rule.
   If you add a new white surface later, add it here too.
*/
html[data-theme="dark"] .card,
html[data-theme="dark"] .modal-content,
html[data-theme="dark"] .auth-card,
html[data-theme="dark"] .admin-topbar,
html[data-theme="dark"] .plan-card,
html[data-theme="dark"] .church-card,
html[data-theme="dark"] .history-item,
html[data-theme="dark"] .map-sidebar,
html[data-theme="dark"] .active-sidebar,
html[data-theme="dark"] .giya-pagination a,
html[data-theme="dark"] .giya-pagination span {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}

/* Painted with var(--text) as a background - pin them dark explicitly. */
html[data-theme="dark"] .giya-footer   { background: #100C0A; }
html[data-theme="dark"] .admin-sidebar { background: #100C0A; }
html[data-theme="dark"] .giya-tooltip  { background: #100C0A; color: var(--text); }

/* The knob on a toggle stays white in both themes. */
html[data-theme="dark"] .pref-switch-track::after { background: #fff; }

/* Catch-all for views that set an inline white background. */
html[data-theme="dark"] [style*="background:#fff"],
html[data-theme="dark"] [style*="background: #fff"],
html[data-theme="dark"] [style*="background:white"] { background: var(--surface) !important; }

/* Form controls. */
html[data-theme="dark"] .giya-input,
html[data-theme="dark"] input[type="text"],
html[data-theme="dark"] input[type="email"],
html[data-theme="dark"] input[type="password"],
html[data-theme="dark"] input[type="search"],
html[data-theme="dark"] input[type="time"],
html[data-theme="dark"] input[type="date"],
html[data-theme="dark"] textarea,
html[data-theme="dark"] select {
  background: var(--bg-input);
  color: var(--text);
  border-color: var(--border);
}
html[data-theme="dark"] ::placeholder { color: #7E6A5C; }
html[data-theme="dark"] select option { background: var(--bg-input); color: var(--text); }

/* Buttons that were white-on-light. */
html[data-theme="dark"] .btn-outline,
html[data-theme="dark"] .btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
html[data-theme="dark"] .btn-outline:hover,
html[data-theme="dark"] .btn-ghost:hover { background: var(--surface-2); }

/* Preference pills read as unselected white chips otherwise. */
html[data-theme="dark"] .pref-choice { background: var(--surface-2); color: var(--text); border-color: var(--border); }
html[data-theme="dark"] .pref-choice.is-active { background: var(--primary); border-color: var(--primary); color: #fff; }
html[data-theme="dark"] .pref-switch-track { background: #4A3B32; }

/* Badges and chips. */
html[data-theme="dark"] .badge-gold { background: rgba(215,169,74,0.20); color: var(--gold); }

/* Flash messages. */
html[data-theme="dark"] .alert,
html[data-theme="dark"] .flash { background: var(--surface-2); color: var(--text); border-color: var(--border); }

/* Line-art SVG icons are dark strokes on transparent; lift them. */
html[data-theme="dark"] .church-card-body img[src$=".svg"],
html[data-theme="dark"] .history-item img[src$=".svg"] { filter: invert(1) brightness(1.4); }

/* ── Screen-reader-only utility ──────────────────────────────── */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ── Star rating picker ──────────────────────────────────────── */
.star-picker { display: inline-flex; flex-direction: row-reverse; gap: 4px; }
.star-picker label { cursor: pointer; font-size: 1.625rem; color: var(--border); transition: color .12s ease; }
.star-picker label:hover,
.star-picker label:hover ~ label,
.star-picker input:checked ~ label { color: var(--gold); }
.star-picker input:focus-visible + label { outline: 3px solid var(--gold); outline-offset: 2px; }

/* ── Favorite heart on cards ─────────────────────────────────── *//* ── "Coming soon" tag on a preference that saves but has no effect yet ── */

/* ── Leaflet: photo pins, user marker, popups ───────────────── */
.giya-pin-wrap { background: none; border: 0; }

.giya-pin { position: relative; }
.giya-pin-frame {
  width: 100%; height: 100%;
  border-radius: 50%;
  border: 3px solid #fff;
  overflow: hidden;
  background: var(--gold-bg);
}
.giya-pin-tail {
  position: absolute;
  left: 50%; bottom: -9px;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 11px solid #fff;
}
.giya-pin.is-selected { transform: scale(1.08); transition: transform .12s ease; }

/* Small church badge on the devotee map (photo lives in the popup). */
.giya-dot {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--gold-bg, #F6EBD7);
  color: var(--primary, #8E3B2F);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.35);
  transition: transform .12s ease;
}
.giya-dot svg { width: 58%; height: 58%; display: block; }
/* With the church's photo: the picture fills the circle, the white ring stays. */
.giya-dot.has-photo { background: #fff; overflow: hidden; }
.giya-dot img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.giya-dot.has-photo.is-selected { border-color: var(--gold, #D7A94A); }
.giya-dot:hover { transform: scale(1.12); }
.giya-dot.is-selected {
  background: var(--gold, #D7A94A); color: #fff;
  box-shadow: 0 0 0 3px rgba(215,169,74,.45), 0 1px 4px rgba(0,0,0,.35);
}

/* Numbered bubble for a group of nearby churches. */
.giya-cluster { background: none; border: 0; }
.giya-cluster span {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  border-radius: 50%;
  background: var(--primary, #8E3B2F); color: #fff;
  border: 3px solid var(--gold-bg, #F6EBD7);
  box-shadow: 0 2px 6px rgba(0,0,0,.35);
  font-family: var(--font-body); font-weight: 700; font-size: 0.8125rem;
  cursor: pointer;
}
.giya-cluster:hover span { background: var(--primary-dark, #6B2416); }
html[data-theme="dark"] .giya-dot { border-color: #2E241E; }
html[data-theme="dark"] .giya-cluster span { border-color: #2E241E; }

.giya-me {
  display: block; width: 22px; height: 22px;
  border-radius: 50%; background: rgba(37,99,235,.28);
  display: flex; align-items: center; justify-content: center;
}
.giya-me-dot {
  width: 12px; height: 12px; border-radius: 50%;
  background: #2563EB; border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.4);
}

.giya-popup { min-width: 178px; text-align: left; }
.giya-popup img {
  width: 100%; height: 92px; object-fit: cover;
  border-radius: 10px; margin-bottom: 8px;
}
.giya-popup strong {
  display: block; font-family: var(--font-display);
  font-size: 0.875rem; margin-bottom: 3px;
  color: #241C18;
}
.giya-popup span {
  display: block; font-size: .75rem; color: var(--text-muted); line-height: 1.5;
}
/* One shape for both popup actions: primary filled, secondary outlined. */
.giya-popup-btn {
  display: block; width: 100%;
  margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--primary);
  border-radius: 8px;
  background: var(--primary); color: #fff;
  font-family: var(--font-body);
  font-size: .75rem; font-weight: 700;
  text-align: center; text-decoration: none;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.giya-popup-btn:hover { background: var(--primary-dark); border-color: var(--primary-dark); color: #fff; }

.giya-popup-btn.is-secondary {
  background: transparent; color: var(--primary);
}
.giya-popup-btn.is-secondary:hover { background: var(--gold-bg); color: var(--primary); }
.giya-popup-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.leaflet-container { font-family: var(--font-body); background: #E8DFD1; }
.leaflet-popup-content-wrap { border-radius: 14px; }
.leaflet-popup-content { margin: 12px 13px; }

/* Compact popup for a short (phone) map: about 120px tall instead of 280px,
   so it opens inside the map. Photo beside the name, actions in one row. */
.giya-popup-compact .leaflet-popup-content { margin: 9px 10px; width: 236px !important; }
.giya-popup-compact .giya-popup {
  display: grid; grid-template-columns: 52px 1fr; column-gap: 9px; align-items: start;
}
.giya-popup-compact .giya-popup img {
  grid-row: 1 / span 2; width: 52px; height: 52px; margin: 0; border-radius: 9px;
}
.giya-popup-compact .giya-popup strong { font-size: .8125rem; line-height: 1.25; align-self: end; }
.giya-popup-compact .giya-popup > span:nth-of-type(n+2) { display: none; }  /* hours: on the details page */
.giya-popup-compact .giya-popup .giya-popup-row {
  grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; margin-top: 8px;
}
.giya-popup-compact .giya-popup .giya-popup-btn { margin-top: 0; padding: 8px 2px; font-size: .75rem; }
.giya-popup-compact .giya-popup .giya-popup-row .giya-popup-btn { width: 100%; }

/* Map shells */
.giya-map-canvas {
  height: 520px; border-radius: var(--radius-xl);
  overflow: hidden; border: 1px solid var(--border);
  background: #E8DFD1;
}
.giya-map-canvas.is-admin { height: 380px; }

.map-note {
  font-size: 0.75rem; color: var(--text-muted);
  padding: 8px 12px; border-radius: 10px;
  background: var(--bg-secondary); border: 1px solid var(--border);
  margin-bottom: 10px;
}
.map-note.is-error { background: rgba(212,24,61,.08); color: #B3182F; border-color: rgba(212,24,61,.25); }

html[data-theme="dark"] .giya-pin-frame,
html[data-theme="dark"] .giya-pin-tail { border-color: #2E241E; }
html[data-theme="dark"] .giya-pin-tail { border-top-color: #2E241E; }
html[data-theme="dark"] .leaflet-popup-content-wrap,
html[data-theme="dark"] .leaflet-popup-tip { background: var(--surface); color: var(--text); }
html[data-theme="dark"] .leaflet-container { background: #1D1714; }
html[data-theme="dark"] .leaflet-tile { filter: brightness(.78) saturate(.85) contrast(1.05); }

/* ── Destination Manager (Figma node 157:55) ─────────────────── */

.dm-panel-title {
  font-family: var(--font-display); font-weight: 600;
  font-size: 1.125rem; color: var(--primary); margin: 0;
}
/* A field label, not a heading.

   It was 15px bold in the full text colour - the same weight and nearly the
   same size as the value underneath it - so a form of eight fields read as
   sixteen equally loud lines. A label's job is to be found when looked for
   and to stay out of the way otherwise. */
.dm-label {
  display: block;
  font-weight: 600;
  font-size: .8125rem;
  color: var(--text-muted);
  margin-bottom: 6px;
  letter-spacing: .01em;
}
.dm-row { display: flex; flex-wrap: wrap; gap: 16px; }
.dm-row > .field { margin-bottom: 4px; }

/* A field's width, on a scale rather than per field. Every form on the admin
   side had its own flex-basis written inline beside the label, which is how
   one screen ended up with 180px, 200px and 230px fields in the same row -
   three widths that are the same width once the row wraps.

   The default is inside :where(), which contributes no specificity at all.
   Written plainly as `.um-filters > .field` it was two classes, the same as
   `.field.is-wide`, and it came later in the file - so every is-wide on a
   filter row was silently ignored and the date pair rendered at 90px each,
   showing "Choose a da". A default has to be weaker than the exceptions to
   it, not merely earlier. */
:where(.dm-row, .sm-filters, .um-filters) > .field { flex: 1 1 220px; }

.field.is-full   { flex-basis: 100%; }
.field.is-wide   { flex-basis: 300px; }
.field.is-narrow { flex-basis: 160px; }
.field.is-auto   { flex: 0 0 auto; }

/* Two date inputs sharing one field. Each needs room for a whole date, and
   a flex item will happily shrink below its basis to make a row fit - which
   is how "Choose a date" came out as "Choose a da". The min-width makes the
   row wrap instead of squeezing. */
.field-dates { display: flex; gap: 8px; }
.field-dates > .giya-input { flex: 1 1 150px; min-width: 130px; }

/* A control that lines up with the bottom of taller fields beside it - a
   checkbox next to a two-line input, say - rather than with their labels. */
.field.is-bottom { align-self: flex-end; }

.dm-map-note {
  display: flex; align-items: flex-start; gap: 6px;
  font-size: 0.8125rem; color: #999; margin: 14px 0 0; line-height: 1.5;
}

/* The same note with something following it, and the same note sitting
   directly under the control it explains. */
.dm-map-note.is-spaced { margin: 10px 0 14px; }
.dm-map-note.is-tight  { margin-top: 6px; }

/* The head of a modal that already has the form modal's padding. Without
   this it carries its own 24px inside another 28px. */
.dm-form-modal > .dm-modal-head { padding: 0 0 14px; margin-bottom: 14px; }

/* The sample row shown in the bulk-import dialog. A block of CSV wants a
   fixed pitch so the commas line up under the header above it. */
.dm-csv-sample {
  background: rgba(15,23,42,0.04);
  padding: 12px;
  border-radius: 12px;
  font-family: var(--font-mono, ui-monospace, "SFMono-Regular", Menlo, monospace);
  font-size: 11px; line-height: 1.5;
  overflow: auto; white-space: pre-wrap;
  margin: 0 0 18px;
}
.dm-map-canvas {
  flex: 1 1 420px; min-height: 232px;
  border: 1px solid #EEDCD1; border-radius: var(--radius-md);
  overflow: hidden; background: #E8DFD1;
}

.dm-drop {
  display: flex; align-items: center; justify-content: center;
  height: 208px; cursor: pointer; overflow: hidden;
  background: #fff; border: 2px dashed #EEDCD1;
  border-radius: var(--radius-md); position: relative;
  transition: border-color .15s, background .15s;
}
.dm-drop:hover, .dm-drop.is-over { border-color: var(--primary); background: var(--bg); }
.dm-drop img { width: 100%; height: 100%; object-fit: cover; }
.dm-drop-inner { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; padding: 12px; }
.dm-drop-circle {
  width: 45px; height: 45px; border-radius: 50%;
  border: 1px solid #EEDCD1; background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  color: var(--primary); font-size: 1.25rem;
}
.dm-drop-text { font-size: 0.8125rem; color: var(--text); max-width: 180px; }
.dm-drop-sub { font-size: 0.75rem; color: #999; }

.dm-filters {
  display: flex; flex-wrap: wrap; gap: 16px;
  align-items: flex-end; margin-bottom: 18px;
}

.icon-btn {
  width: 30px; height: 30px; border: 0; cursor: pointer;
  border-radius: 9px; background: rgba(227,176,67,.38);
  color: var(--primary); display: inline-flex;
  align-items: center; justify-content: center;
}
.icon-btn:hover { background: var(--gold); color: #fff; }

html[data-theme="dark"] .dm-drop { background: var(--surface-2); border-color: var(--border); }
html[data-theme="dark"] .dm-drop-circle { background: var(--surface); border-color: var(--border); }

/* ── Map category chips ──────────────────────────────────────
   These sit on a white card, so they cannot reuse .btn-ghost -
   that class is white-on-transparent, meant for the dark hero.
*/
.cat-chip {
  font-family: var(--font-body);
  font-size: 0.8125rem; font-weight: 700;
  padding: 7px 14px; border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text);
  border: 1px solid var(--border);
  cursor: pointer; white-space: nowrap;
  transition: background .15s, color .15s, border-color .15s;
}
.cat-chip:hover { border-color: var(--gold); }
.cat-chip.is-active {
  background: var(--primary); color: #fff; border-color: var(--primary);
}
.cat-chip:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

.icon-btn.is-danger { background: rgba(212,24,61,.10); color: #B3182F; }
.icon-btn.is-danger:hover { background: #B3182F; color: #fff; }

/* ── Schedule Manager (Figma) ────────────────────────────────── */
/* The search and the primary action on one line. They wrap to two on a
   narrow screen, and the button goes full width there rather than sitting
   stranded at one end of an empty row. */
.sm-bar {
  display: flex;
  align-items: stretch;
  gap: 12px;
  margin-bottom: 18px;
}

.sm-bar .sm-search { flex: 1 1 260px; margin-bottom: 0; }

.sm-bar-action { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 620px) {
  .sm-bar { flex-wrap: wrap; }
  .sm-bar-action { flex: 1 1 100%; justify-content: center; }
}

.sm-search {
  display: flex; align-items: center; gap: 10px;
  background: #fff; border: 1px solid var(--border);
  border-radius: 999px; padding: 0 20px; min-height: 52px;
  margin-bottom: 18px;
}
.sm-search i { color: var(--text-muted); font-size: 0.9375rem; }
.sm-search input {
  flex: 1; border: 0; outline: none; background: transparent;
  font-size: 0.875rem; color: var(--text); min-width: 0;
}

.sm-filters { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end; }

.sm-table th {
  font-size: 0.75rem; line-height: 1.3; white-space: nowrap;
  background: var(--gold-bg); color: var(--primary);
}
.sm-table td { font-size: 0.8125rem; vertical-align: middle; }
.sm-table tbody tr:hover { background: var(--bg); }

.badge-published { background: rgba(22,101,52,.12); color: #166534; }

.sm-foot {
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: center; justify-content: space-between;
  margin-top: 16px; font-size: 0.8125rem; color: var(--text-muted);
}

html[data-theme="dark"] .sm-search { background: var(--surface); }
html[data-theme="dark"] .sm-table th { background: var(--surface-2); color: var(--gold); }
html[data-theme="dark"] .sm-table tbody tr:hover { background: var(--surface-2); }

/* ── ADMIN DASHBOARD ─────────────────────────────────────────
   The charts and the activity feed. Both were inline, and the breakpoints
   for the tile row were three !important overrides in a <style> block inside
   the template - which is the only way to beat an inline grid, so the inline
   grid is gone and they went with it. */
.chart-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 24px;
}

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

.activity-row {
  display: flex; align-items: center; gap: .75rem;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-light);
}

.activity-row.is-last { border-bottom: 0; }

.activity-icon {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--gold-bg);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}

.activity-icon .bi { font-size: .9375rem; color: var(--primary); }

.activity-text { flex: 1; font-size: .8125rem; color: var(--text); min-width: 0; }
.activity-when { font-size: .75rem; color: var(--text-muted); flex-shrink: 0; }

/* ── ADMIN STAT TILE ─────────────────────────────────────────
   One headline figure. Written by hand on three screens before this, with
   three different heights and three different number sizes for the same kind
   of fact. */
.stat-tile {
  display: flex;
  flex-direction: column;
  padding: 18px;
  gap: 2px;
}

.stat-tile-icon {
  width: 44px; height: 44px;
  border-radius: 13px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 12px;
  background: var(--gold-bg);
  color: var(--primary);
}

.stat-tile-icon .bi { font-size: 1.25rem; }

/* A tone per tile so a row of five is readable as five things rather than
   one thing repeated. Same family, different weight of the same warmth. */
.stat-tile-icon.is-primary { background: rgba(142,59,47,.10); color: var(--primary); }
.stat-tile-icon.is-gold    { background: var(--gold-bg);      color: #9A7524; }
.stat-tile-icon.is-green   { background: rgba(107,155,90,.14); color: #4B7A3B; }
.stat-tile-icon.is-blue    { background: rgba(37,99,235,.10); color: #2563EB; }

.stat-tile-value {
  font-size: 1.75rem;
  font-weight: 700;
  color: var(--text);
  line-height: 1.05;
  letter-spacing: -.02em;
}

.stat-tile-label {
  font-size: .75rem;
  color: var(--text-muted);
}

/* The second line some tiles carry: a share of the total, or a change since
   last week. Muted, so the figure above it stays the thing you read first. */
.stat-tile-sub {
  font-size: .75rem;
  color: var(--text-muted);
  margin-top: 4px;
}

/* A movement inside that line. Green for up, red for down, gold for a share
   of something bought - the three the panel actually shows. */
.stat-delta { font-weight: 700; font-variant-numeric: tabular-nums lining-nums; }
.stat-delta.is-up   { color: #4B7A3B; }
.stat-delta.is-down { color: #B3182F; }
.stat-delta.is-gold { color: #9A7524; }

/* It lifts under the cursor. Carried over from the reports page, which was
   the only screen whose tiles did this - so now either every module answers
   the cursor or none of them does. */
.stat-tile { transition: transform .14s ease, box-shadow .14s ease; }
.stat-tile:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(36,28,24,.10); }

@media (prefers-reduced-motion: reduce) {
  .stat-tile { transition: none; }
  .stat-tile:hover { transform: none; }
}

/* A row of them. auto-fit rather than a fixed count, so five tiles on a
   laptop become three and then two without a breakpoint per screen. */
.stat-row {
  display: grid;
  /* 150, not 175. Five tiles at 175 need 939px and a 1180px laptop gives the
     content column 864 - so users and reports broke 4 + 1, and a single tile
     alone on a second row is the worst shape this can take. At 150 the five
     fit, and the row still drops to three and then two on the way down. */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 16px;
  margin-bottom: 24px;
}

/* ── ADMIN MODULE FURNITURE ──────────────────────────────────
   The parts every management screen repeats: a table that scrolls sideways
   instead of squashing, a list row, and the four or five cell treatments a
   table of records needs. Each of these was written inline on between three
   and six screens, with a different font size every time. */
.adm-scroller { overflow: hidden; }
.adm-scroller > div { overflow-x: auto; }

/* A module that puts its filters and its table inside one card rather than
   two. Schedules is the only one, but the padding was inline. */
.adm-panel { padding: 22px; }
@media (max-width: 600px) { .adm-panel { padding: 16px 14px; } }

.adm-table-scroll { overflow-x: auto; margin-top: 18px; }

/* The half-line of guidance under a field. Smaller than a label, and it
   hangs off the control above rather than starting a new block. */
.dm-note { font-size: .75rem; color: var(--text-muted); margin: 4px 0 0; }

/* Cell treatments. A reference wants a fixed pitch so the eye can compare
   two of them; a name wants weight; a date wants to recede. */
.giya-table .cell-ref    { font-family: var(--font-mono, ui-monospace, "SFMono-Regular", Menlo, monospace); font-size: .75rem; }
.giya-table .cell-strong { font-weight: 600; }
.giya-table .cell-amount { font-weight: 700; font-variant-numeric: tabular-nums lining-nums; }
.giya-table .cell-muted  { font-size: .8125rem; color: var(--text-muted); }
.giya-table .cell-small  { font-size: .8125rem; }

/* A record shown as a card rather than a row, because it carries a
   paragraph. Feedback is the only one, but it is three screens' worth of
   layout if written by hand. */
.adm-item { display: flex; align-items: flex-start; gap: .75rem; flex-wrap: wrap; }
.adm-item-avatar { border-color: var(--primary); flex-shrink: 0; }
.adm-item-main { flex: 1; min-width: 200px; }
.adm-item-name { font-size: .875rem; font-weight: 700; color: var(--text); }
.adm-item-meta { font-size: .75rem; color: var(--text-muted); margin-top: 2px; }
.adm-item-body { font-size: .8125rem; color: var(--text); line-height: 1.7; margin: 8px 0 0; }

/* A select that sits beside a button rather than filling a form. */
.giya-input.is-compact { width: auto; padding: 8px 12px; font-size: .8125rem; }

.um-filters { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-end; }

/* The filter row's fields sit on the shared width scale below; only the
   margins are special, because a filter row has no space under each label. */
.um-filters .field { margin: 0; }
.um-filters .sm-search { margin: 0; }

/* The pair of buttons at the end of a bar: Reset and Export on two filter
   rows, Import and Add on the destinations map.

   All three were a d-flex that could not wrap, and "Export Schedule" beside
   "Reset Filter" is 367px of buttons - so on a 320px phone the pair pushed
   the whole admin page 77px sideways, sidebar included. It was never the
   twelve-column table, which scrolls inside its own card exactly as it
   should; it was two buttons that had no instruction to stack. */
.adm-actions {
  display: flex; gap: 8px;
  flex: 0 0 auto; align-items: flex-end;
  min-width: 0;
}

@media (max-width: 640px) {
  .adm-actions { flex: 1 1 100%; flex-wrap: wrap; }
  .adm-actions > * { flex: 1 1 140px; justify-content: center; }
}

/* A face in a table cell. Smaller than the one in the top bar, because a row
   is 56px tall and the name beside it is what you are reading. */
.giya-table .cell-avatar {
  width: 36px; height: 36px;
  font-size: .75rem;
  border-color: var(--primary);
}

.giya-table .cell-note { font-size: .75rem; color: var(--text-muted); margin-top: 3px; }
.giya-table .cell-star { color: var(--gold); font-size: .75rem; }

/* A picture standing in for the record: a church in the destinations table.
   Square, cropped, and never allowed to squeeze the name beside it. */
.giya-table .cell-thumb {
  width: 34px; height: 34px;
  border-radius: 8px;
  object-fit: cover;
  flex-shrink: 0;
}

.giya-table td.is-flush { padding: 0; }
.giya-table td.cell-empty { text-align: center; padding: 32px; color: var(--text-muted); }

/* Two columns whose width is a decision rather than a consequence: the row
   number, which is never more than three digits, and the action buttons,
   which must not wrap. */
.giya-table .col-no { width: 56px; }
.giya-table .col-actions { width: 120px; }

.sm-foot .per-page-label { font-size: .8125rem; color: var(--text-muted); }
.sm-foot .per-page-select { width: 74px; padding: 6px 8px; }

/* The same footer, but sitting inside the card rather than under it, so it
   needs the card's own gutter instead of a gap above. */
.sm-foot.is-inset { margin-top: 0; padding: 14px 18px; }

/* A pair of fields sharing a row inside a form modal, and the sentence of
   explanation that sometimes follows them. */
.dm-col { flex: 1 1 180px; }
.dm-hint { font-size: .75rem; color: var(--text-muted); margin: 0 0 14px; }

/* Two buttons splitting the width of a modal footer. */
.btn.is-grow { flex: 1; }

.badge-suspended { background: rgba(179,24,47,.12); color: #B3182F; }

/* ── Fullscreen map ──────────────────────────────────────────── */
.map-grid.is-fullscreen {
  position: fixed; inset: 0; z-index: 1500;
  background: var(--bg); padding: 14px; margin: 0;
  grid-template-columns: 320px 1fr; gap: 14px;
}
.map-grid.is-fullscreen .giya-map-shell { height: 100%; }
.map-grid.is-fullscreen .giya-map-canvas {
  height: 100%; border-radius: var(--radius-md);
}
.map-grid.is-fullscreen .map-sidebar {
  max-height: 100%; overflow-y: auto;
}

/* A floating hint so the exit route is obvious. */
.map-grid.is-fullscreen::after {
  content: "Press Esc to exit";
  position: absolute; right: 22px; bottom: 22px;
  background: rgba(36,28,24,.82); color: #fff;
  font-size: .75rem; padding: 5px 11px; border-radius: 999px;
  pointer-events: none; z-index: 1600;
}

@media (max-width: 900px) {
  .map-grid.is-fullscreen { grid-template-columns: 1fr; }
  .map-grid.is-fullscreen .map-sidebar { display: none; }
}

/* ── Floating map controls (top-right, over the canvas) ──────── */
.giya-map-shell { position: relative; }

.map-tools {
  position: absolute; top: 12px; right: 12px;
  z-index: 600;                 /* above tiles, below the navbar at 1000 */
  display: flex; flex-direction: column; gap: 8px;
}

.map-tool {
  width: 42px; height: 42px;
  display: flex; align-items: center; justify-content: center;
  background: #fff; color: var(--primary);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 2px 10px rgba(36,28,24,.22);
  cursor: pointer; font-size: 1rem;
  transition: background .15s, color .15s, transform .12s;
}
.map-tool:hover { background: var(--primary); color: #fff; }
.map-tool:active { transform: scale(.94); }
.map-tool:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.map-tool.is-busy { opacity: .6; pointer-events: none; }

html[data-theme="dark"] .map-tool { background: var(--surface); color: var(--gold); border-color: var(--border); }
html[data-theme="dark"] .map-tool:hover { background: var(--primary); color: #fff; }

/* Leaflet's own zoom buttons sit top-left, so the two never collide. */

/* ── Route mode notice ───────────────────────────────────────── */
.route-mode {
  font-size: .75rem; margin: 2px 0 8px;
  color: var(--text-muted); line-height: 1.5;
}
.route-mode.is-road   { color: #166534; font-weight: 700; }
.route-mode.is-direct { color: #92610C; }

/* ── Numbered pilgrimage pins ────────────────────────────────── */
.giya-numpin { position: relative; width: 34px; height: 44px; }
.giya-numpin-body {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid #fff; color: #fff;
  font-weight: 800; font-size: 0.8125rem;
  box-shadow: 0 3px 10px rgba(0,0,0,.35);
}
.giya-numpin-tail {
  position: absolute; left: 50%; bottom: 0;
  transform: translateX(-50%);
  width: 0; height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 11px solid #8E3B2F;
}
.giya-numpin.is-current { animation: giya-pulse 1.6s ease-in-out infinite; }
@keyframes giya-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.12); }
}

.ap-summary {
  position: absolute; left: 16px; bottom: 16px; z-index: 600;
  background: rgba(255,255,255,.94); color: var(--text);
  font-size: 0.75rem; font-weight: 600;
  padding: 7px 13px; border-radius: 999px;
  box-shadow: 0 2px 10px rgba(36,28,24,.2);
}
html[data-theme="dark"] .ap-summary { background: rgba(46,36,30,.94); color: var(--text); }

/* The route's key. Top-left, opposite the map tools and clear of the summary
   pill in the bottom corner. Each swatch is drawn with the same colour and
   dash pattern as the leg it names, so it reads as a sample of the line
   rather than as a coloured square that has to be remembered. */
.ap-legend {
  position: absolute; left: 16px; top: 16px; z-index: 600;
  display: flex; flex-direction: column; gap: 5px;
  background: rgba(255,255,255,.94);
  padding: 9px 12px; border-radius: var(--radius-md);
  box-shadow: 0 2px 10px rgba(36,28,24,.2);
  font-size: .75rem; font-weight: 600; color: var(--text);
  max-width: 46%;
}

/* ── ACTIVE PILGRIMAGE: THE SCREEN ───────────────────────────
   These were a <style> block inside the template, which is where a rule goes
   to be forgotten: it cannot be overridden per theme, it is re-sent on every
   page load, and nothing else in the app can see it.

   100dvh, not 100vh. On iOS Safari 100vh is the height the page WOULD be with
   the toolbars hidden, so the layout ran further down than the screen while
   the sheet - position:fixed - sat above the toolbar. The two were measuring
   from different bottoms, which is how the map's attribution ended up printed
   across the church name even after the sheet's height was being published
   correctly. dvh follows the toolbars, so both agree. */
body:has(.active-layout) { overflow: hidden; }

.active-layout {
  height: calc(100vh - 64px);
  height: calc(100dvh - 64px);
}

.ap-head {
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  padding: 20px;
  position: relative;
  overflow: hidden;
  flex-shrink: 0;
}

.ap-head::after {
  content: ''; position: absolute;
  top: -15px; right: -15px;
  width: 96px; height: 96px;
  border-radius: 50%;
  background: var(--gold);
  opacity: .15;
}

.ap-banner {
  padding: 14px 16px;
  background: var(--gold-bg);
  border-bottom: 1px solid rgba(142, 59, 47, .12);
  flex-shrink: 0;
}

.ap-list { flex: 1; overflow-y: auto; padding: 8px 0; }
.ap-list::-webkit-scrollbar { width: 4px; }
.ap-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }

.ap-foot {
  padding: 14px;
  border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 8px;
  flex-shrink: 0;
}
/* ── ACTIVE PILGRIMAGE HEADER ────────────────────────────────
   In the sidebar the strip stacks: the way back, the route, the count, the
   candles. On a phone it folds - see the phone block further down - because
   stacked it was 151px of a 664px screen and the map is the point. */
.ap-head-inner { position: relative; }

.ap-back {
  display: inline-flex; align-items: center; gap: 4px;
  color: rgba(255, 255, 255, .7);
  font-size: .75rem;
  margin-bottom: 10px;
  text-decoration: none;
}

.ap-back:hover { color: #fff; }

.ap-title {
  font-family: var(--font-display);
  color: #fff;
  font-size: 1.1875rem;
  margin: 0 0 2px;
}

.ap-count {
  color: rgba(255, 255, 255, .7);
  font-size: .75rem;
  margin: 0;
}

.ap-candles { margin-top: .5rem; }

.ap-percent {
  color: var(--gold);
  font-size: .75rem;
  font-weight: 700;
  margin-top: 4px;
}

.ap-legend-item { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.ap-legend-dash { width: 22px; height: 0; flex-shrink: 0; border-radius: 999px; }
.ap-legend-dash.is-live     { border-top: 4px solid #2563EB; }
.ap-legend-dash.is-upcoming { border-top: 3px dashed #8E3B2F; opacity: .6; }
.ap-legend-dash.is-done     { border-top: 3px solid #6B9B5A; opacity: .6; }

html[data-theme="dark"] .ap-legend { background: rgba(46,36,30,.94); }

@media (max-width: 560px) {
  /* A phone is where this is actually used, and the map is small. The key
     sits on one line and gives the churches back their space. */
  .ap-legend {
    flex-direction: row; flex-wrap: wrap; gap: 4px 12px;
    max-width: calc(100% - 76px); padding: 7px 10px;
  }
}

/* ════════════════════════════════════════════════════════════════
   MAP PAGE - layout and rhythm
   Spacing follows a 4px scale. Type sizes step 13 / 15 / 26px so the
   hierarchy reads at a glance instead of everything competing.
   ════════════════════════════════════════════════════════════════ */

.mx-head { margin-bottom: 14px; }
.mx-head h1 {
  font-family: var(--font-display);
  font-size: 1.625rem; line-height: 1.2;
  margin: 4px 0 3px; color: var(--text);
}
.mx-head p {
  font-size: 0.8125rem; color: var(--text-muted);
  margin: 0; max-width: 62ch;
}

/* ── Tile notice: informational, so it should not shout ───────── */
.map-note {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 0.75rem; line-height: 1.55;
  color: var(--text-muted);
  background: var(--bg-secondary);
  border: 1px solid var(--border-light);
  border-left: 3px solid var(--gold);
  border-radius: 8px;
  padding: 9px 12px; margin-bottom: 14px;
}
.map-note.is-error {
  color: #B3182F; background: rgba(179,24,47,.06);
  border-color: rgba(179,24,47,.18); border-left-color: #B3182F;
}
.map-note-close {
  margin-left: auto; flex-shrink: 0;
  background: none; border: 0; cursor: pointer;
  color: inherit; opacity: .5; font-size: 1.0625rem;
  line-height: 1; padding: 0 2px;
}
.map-note-close:hover { opacity: 1; }

/* ── Sidebar: one scrolling region, fixed controls ────────────── */
.map-sidebar {
  display: flex; flex-direction: column;
  padding: 0; height: 640px; overflow: hidden;
}

.mx-controls {
  padding: 14px 14px 12px;
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
}

/* Chips scroll sideways instead of wrapping to three rows. */
.mx-chips {
  display: flex; gap: 6px;
  overflow-x: auto; padding-bottom: 2px;
  scrollbar-width: none;
}
.mx-chips::-webkit-scrollbar { display: none; }
.cat-chip { flex: 0 0 auto; }

/* ── Route panel ─────────────────────────────────────────────── */

/* ── Route stop rows ─────────────────────────────────────────── */

.mx-stop-drop {
  width: 22px; height: 22px; flex-shrink: 0;
  background: none; border: 0; border-radius: 6px; cursor: pointer;
  color: var(--text-muted); font-size: 1rem; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.mx-stop-drop:hover { background: rgba(179,24,47,.10); color: #B3182F; }

/* ── Destination list ────────────────────────────────────────── */
.mx-list-head {
  padding: 12px 14px 6px; flex-shrink: 0;
  font-size: .75rem; font-weight: 800; letter-spacing: .01em;
  text-transform: none; color: var(--text-muted);
}
.mx-list {
  flex: 1; min-height: 0;
  overflow-y: auto; padding: 0 14px 14px;
}
.mx-list .history-item { margin-bottom: 6px; }

/* ── Map canvas matches the sidebar height ───────────────────── */
.giya-map-canvas { height: 640px; }

.map-grid.is-fullscreen .map-sidebar { height: 100%; }

.map-grid { display: grid; grid-template-columns: 340px 1fr; gap: 18px; }

/* ── Add-to-route button on destination rows ─────────────────── */

html[data-theme="dark"] .btn-ghost:hover { background: var(--surface-2); }

/* ════════════════════════════════════════════════════════════════
   MAP SIDEBAR - explore, filter, select, plan
   ════════════════════════════════════════════════════════════════ */

.mx-title {
  font-family: var(--font-display);
  font-size: 1.0625rem; color: var(--text);
  margin: 0 0 11px;
}

.mx-search-field {
  display: flex; align-items: center; gap: 9px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 999px; padding: 0 15px; min-height: 42px;
  margin-bottom: 11px;
}
.mx-search-field i { color: var(--text-muted); font-size: 0.8125rem; }
.mx-search-field input {
  flex: 1; min-width: 0; border: 0; outline: none;
  background: transparent; font-size: 0.8125rem; color: var(--text);
}
.mx-search-field:focus-within { border-color: var(--gold); }

/* Chips wrap onto two rows, as in the reference. */
.mx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.cat-chip {
  font-family: var(--font-body);
  font-size: 0.75rem; font-weight: 600;
  padding: 6px 13px; border-radius: 999px;
  background: #fff; color: var(--text);
  border: 1px solid var(--border);
  cursor: pointer; white-space: nowrap;
  transition: background .15s, color .15s, border-color .15s;
}
.cat-chip:hover { border-color: var(--gold); }
.cat-chip.is-active { background: var(--primary); color: #fff; border-color: var(--primary); }
.cat-chip:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* ── Church rows ─────────────────────────────────────────────── */
.mx-row {
  display: flex; align-items: center; gap: 11px;
  padding: 10px; margin-bottom: 8px;
  background: #fff; border: 1px solid var(--border-light);
  border-radius: 12px; cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}
.mx-row:hover { border-color: var(--gold); box-shadow: 0 2px 10px rgba(36,28,24,.07); }
.mx-row.is-picked { border-color: var(--primary); background: var(--gold-bg); }

.mx-thumb {
  position: relative; flex-shrink: 0;
  width: 44px; height: 44px; border-radius: 10px;
  background: var(--gold-bg); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  color: var(--primary); font-size: 1.125rem;
}
.mx-thumb img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  background: var(--bg-secondary);
}
/* Only when the photo fails to load does the fallback glyph appear. */
.mx-thumb.is-empty::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--gold-bg);
}

.mx-row-body { flex: 1; min-width: 0; }
.mx-row-body h3 {
  font-size: 0.8125rem; font-weight: 700; color: var(--text);
  margin: 0 0 3px; line-height: 1.3;
}
.mx-row-place {
  display: flex; align-items: center; gap: 4px;
  font-size: .75rem; color: var(--text-muted); margin: 0 0 5px;
}
.mx-row-place i { color: var(--primary); font-size: .75rem; flex-shrink: 0; }

.mx-row-tags { display: flex; flex-wrap: wrap; gap: 5px; margin: 0; }
.mx-star {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: .75rem; font-weight: 700; color: var(--text-muted);
}
.mx-star i { color: var(--gold); font-size: 0.5625rem; }
.mx-tag {
  font-size: 0.5625rem; font-weight: 700;
  padding: 2px 7px; border-radius: 999px;
  background: var(--bg-secondary); color: var(--text-muted);
}
.mx-tag.is-open { background: #DCF2E3; color: #166534; }

/* ── The select ring ─────────────────────────────────────────── */
.mx-pick {
  width: 26px; height: 26px; flex-shrink: 0;
  border-radius: 50%; cursor: pointer;
  background: transparent; color: transparent;
  border: 2px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.75rem;
  transition: background .15s, border-color .15s, color .15s;
}
.mx-pick:hover { border-color: var(--primary); }
.mx-pick.is-on { background: var(--primary); border-color: var(--primary); color: #fff; }
.mx-pick:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

.mx-empty {
  text-align: center; padding: 30px 14px;
  font-size: 0.8125rem; color: var(--text-muted);
}

/* ── Selection tray ──────────────────────────────────────────── */
.mx-tray {
  flex-shrink: 0; padding: 13px 14px 14px;
  background: var(--gold-bg);
  border-top: 1px solid var(--border);
}
.mx-tray-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px; margin-bottom: 8px;
}
.mx-tray-head span:first-child {
  font-size: 0.75rem; font-weight: 700; color: var(--text);
}
.mx-tray-distance {
  font-size: 0.75rem; font-weight: 800; color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.mx-tray-list {
  list-style: none; margin: 0 0 6px; padding: 0;
  max-height: 116px; overflow-y: auto;
}
.mx-tray-item {
  display: flex; align-items: center; gap: 7px;
  padding: 3px 0; font-size: 0.75rem; color: var(--text);
}
.mx-tray-n { color: var(--primary); font-weight: 700; flex-shrink: 0; }
.mx-tray-name {
  flex: 1; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.mx-tray-actions { display: flex; gap: 8px; }
.mx-plan { flex: 1; justify-content: center; }

html[data-theme="dark"] .mx-row { background: var(--surface); }
html[data-theme="dark"] .mx-row.is-picked { background: var(--surface-2); }
html[data-theme="dark"] .cat-chip { background: var(--surface); }
html[data-theme="dark"] .mx-search-field { background: var(--surface); }
html[data-theme="dark"] .mx-tray { background: var(--surface-2); }

/* ════════════════════════════════════════════════════════════════
   CONFIRMATION DIALOG
   Replaces the browser's native confirm(), which cannot be styled and
   shows the host name to the user.
   ════════════════════════════════════════════════════════════════ */

.gc-backdrop {
  position: fixed; inset: 0; z-index: 3000;
  display: none; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(36,28,24,.55);
  backdrop-filter: blur(2px);
}
.gc-backdrop.is-open { display: flex; animation: gc-fade .14s ease; }

.gc-box {
  width: 100%; max-width: 400px;
  background: #fff; border-radius: var(--radius-2xl);
  padding: 26px 24px 22px; text-align: center;
  box-shadow: 0 24px 60px rgba(36,28,24,.32);
  animation: gc-rise .18s cubic-bezier(.2,.8,.3,1);
}

.gc-icon {
  width: 52px; height: 52px; margin: 0 auto 15px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; font-size: 1.375rem;
  background: var(--gold-bg); color: var(--primary);
}
.gc-icon.is-danger { background: rgba(179,24,47,.10); color: #B3182F; }

.gc-title {
  font-family: var(--font-display);
  font-size: 1.0625rem; color: var(--text);
  margin: 0 0 7px; line-height: 1.35;
}
.gc-message {
  font-size: 0.8125rem; line-height: 1.6;
  color: var(--text-muted); margin: 0 0 20px;
}

.gc-actions { display: flex; gap: 10px; }
.gc-actions > button { flex: 1; justify-content: center; }

.btn-danger-solid {
  background: #B3182F; color: #fff;
  border: 1px solid #B3182F;
  padding: 12px 20px; border-radius: var(--radius-xl);
  font-size: 0.875rem; font-weight: 700; cursor: pointer;
}
.btn-danger-solid:hover { background: #8E1226; color: #fff; }

@keyframes gc-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes gc-rise {
  from { opacity: 0; transform: translateY(10px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

html[data-theme="dark"] .gc-box { background: var(--surface); }

@media (prefers-reduced-motion: reduce) {
  .gc-backdrop.is-open, .gc-box { animation: none; }
}

/* ════════════════════════════════════════════════════════════════
   FLOATING CHATBOT LAUNCHER
   Fixed to the viewport, so it stays put while the page scrolls.
   Collapsed to a circle; the label expands on hover and on focus.
   ════════════════════════════════════════════════════════════════ */

.giya-fab {
  border: 0; font-family: var(--font-body);   /* it is a <button> now */
  position: fixed;
  right: 22px; bottom: 22px;
  z-index: 1400;                /* above content, below modals at 2000+ */

  display: inline-flex; align-items: center; gap: 0;
  height: 56px; padding: 0 4px;
  border-radius: 999px;
  background: var(--primary);
  color: #fff; text-decoration: none;
  box-shadow: 0 6px 22px rgba(36,28,24,.32);
  overflow: hidden;
  transition: gap .18s ease, padding .18s ease,
              background .15s ease, transform .15s ease,
              box-shadow .18s ease;
}
.giya-fab:hover,
.giya-fab:focus-visible {
  background: var(--primary-dark);
  color: #fff;
  gap: 10px; padding: 0 20px 0 4px;
  box-shadow: 0 8px 28px rgba(36,28,24,.40);
}
.giya-fab:active { transform: scale(.96); }
.giya-fab:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* Width animates from zero, so the collapsed state is a clean circle. */

/* A slow breathing ring, so it reads as available without nagging. */
.giya-fab::after {
  content: ""; position: absolute; inset: 0;
  border-radius: 999px;
  border: 2px solid var(--gold);
  opacity: 0;
  animation: giya-fab-ping 3.4s ease-out infinite;
  pointer-events: none;
}
@keyframes giya-fab-ping {
  0%       { opacity: .55; transform: scale(1); }
  55%, 100% { opacity: 0;   transform: scale(1.28); }
}
.giya-fab:hover::after { animation: none; }

@media (max-width: 600px) {
  .giya-fab { right: 16px; bottom: 16px; height: 52px; }

  /* No hover on touch, so never reserve room for the label. */
  .giya-fab:hover { gap: 0; padding: 0 4px; }

}

@media (prefers-reduced-motion: reduce) {
  .giya-fab::after { animation: none; }
  .giya-fab { transition: none; }
}

html[data-theme="dark"] .giya-fab { box-shadow: 0 6px 22px rgba(0,0,0,.55); }

/* ════════════════════════════════════════════════════════════════
   MAP PAGE - responsive
   Three arrangements rather than a hard desktop/mobile split, so the
   in-between widths (tablets, split-screen, small laptops) get a
   layout meant for them instead of a desktop one squeezed down.
   ════════════════════════════════════════════════════════════════ */

.map-grid {
  display: grid;
  grid-template-columns: minmax(300px, 360px) 1fr;
  gap: 18px;
  align-items: start;
}

/* Sidebar and map share one height, so the columns line up. */
.map-sidebar,
.giya-map-canvas { height: clamp(460px, 68vh, 720px); }

.map-sidebar {
  display: flex; flex-direction: column;
  padding: 0; overflow: hidden; width: auto;
}

/* ── Tablet and small laptop: stack, both full width ──────────── */
@media (max-width: 1024px) {
  .map-grid { grid-template-columns: 1fr; gap: 14px; }

  /* The map leads on a touch device; the list follows. */
  .giya-map-shell { order: 1; }
  .map-sidebar    { order: 2; }

  .giya-map-canvas { height: clamp(340px, 46vh, 520px); }
  .map-sidebar     { height: auto; max-height: none; }

  /* No fixed height means the list needs its own cap. */
  .mx-list { max-height: 46vh; }
}

/* ── Tablet: two church columns, since the row is wide ────────── */
@media (min-width: 700px) and (max-width: 1024px) {
  .mx-list {
    display: grid; grid-template-columns: 1fr 1fr;
    gap: 8px; align-content: start;
  }
  .mx-list .mx-row { margin-bottom: 0; }
  .mx-empty { grid-column: 1 / -1; }
}

/* ── Phone ───────────────────────────────────────────────────── */
@media (max-width: 699px) {
  .mx-head h1 { font-size: 1.375rem; }
  .giya-map-canvas { height: clamp(300px, 42vh, 420px); }
  .mx-list { max-height: 52vh; }
  .mx-controls { padding: 12px 12px 10px; }
  .mx-list, .mx-list-head { padding-left: 12px; padding-right: 12px; }
  .mx-tray { padding: 11px 12px 12px; }

  /* Chips scroll sideways rather than eating four rows of height. */
  .mx-chips {
    flex-wrap: nowrap; overflow-x: auto;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .mx-chips::-webkit-scrollbar { display: none; }
}

/* ── Short landscape phones ──────────────────────────────────── */
@media (max-height: 520px) and (orientation: landscape) {
  .giya-map-canvas { height: 300px; }
  .mx-list { max-height: 220px; }
}

/* Fullscreen overrides the stacking above. */
.map-grid.is-fullscreen .map-sidebar,
.map-grid.is-fullscreen .giya-map-canvas { height: 100%; }
.map-grid.is-fullscreen .giya-map-shell { order: 0; }
.map-grid.is-fullscreen .mx-list { max-height: none; }

/* ════════════════════════════════════════════════════════════════
   DATE PICKER
   The browser draws its own calendar inside a shadow DOM that CSS
   cannot reach, so the popup is replaced. The <input type="date">
   itself is untouched - it still holds and validates the value.
   ════════════════════════════════════════════════════════════════ */

/* Hide the native calendar glyph; the field gets our own. */
/* display:none, not opacity:0 - an invisible indicator is still clickable,
   which opened the browser's calendar on top of ours. */
input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
input[type="date"]::-webkit-inner-spin-button,
input[type="date"]::-webkit-clear-button { display: none; }
input[type="date"][readonly] { background-color: var(--bg-input); opacity: 1; }
/* Without this the browser holds the tap back to check for a double-tap zoom,
   which reads as "nothing happened" on the first press. */
input[type="date"] { touch-action: manipulation; -webkit-user-select: none; user-select: none; }
.gdp, .gdp-day, .gdp-nav, .gdp-link { touch-action: manipulation; }

.gdp {
  position: absolute; z-index: 2500;
  display: none;
  width: 278px; padding: 14px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: 0 16px 44px rgba(36,28,24,.22);
  font-family: var(--font-body);
}
.gdp.is-open { display: block; animation: gdp-in .14s ease; }
@keyframes gdp-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

.gdp-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 12px;
}
.gdp-month {
  font-family: var(--font-display);
  font-size: 0.9375rem; font-weight: 700; color: var(--text);
}
.gdp-nav {
  width: 28px; height: 28px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--gold-bg); color: var(--primary);
  border: 0; border-radius: 8px; cursor: pointer; font-size: 0.75rem;
}
.gdp-nav:hover { background: var(--primary); color: #fff; }

.gdp-weekdays,
.gdp-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.gdp-weekdays {
  margin-bottom: 4px;
}
.gdp-weekdays span {
  text-align: center; font-size: .75rem; font-weight: 800;
  letter-spacing: .01em; text-transform: none;
  color: var(--text-muted); padding: 4px 0;
}

.gdp-day {
  aspect-ratio: 1; min-height: 34px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 9px;
  font-size: 0.8125rem; color: var(--text);
  cursor: pointer; font-family: inherit;
  transition: background .12s ease, color .12s ease;
}
.gdp-day:hover:not(:disabled):not(.is-outside) { background: var(--gold-bg); }
.gdp-day.is-outside { color: var(--border); cursor: default; }
.gdp-day.is-today { box-shadow: inset 0 0 0 1.5px var(--gold); font-weight: 700; }
.gdp-day.is-selected {
  background: var(--primary); color: #fff; font-weight: 700;
}
.gdp-day.is-selected:hover { background: var(--primary-dark); }
.gdp-day:disabled { color: var(--border); cursor: not-allowed; }
.gdp-day:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

.gdp-foot {
  display: flex; justify-content: space-between;
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--border-light);
}
.gdp-link {
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 0.75rem; font-weight: 700;
  color: var(--text-muted); padding: 4px 6px; border-radius: 6px;
}
.gdp-link:hover { color: var(--text); background: var(--bg-secondary); }
.gdp-link.is-primary { color: var(--primary); }

html[data-theme="dark"] .gdp { background: var(--surface); }

@media (prefers-reduced-motion: reduce) { .gdp.is-open { animation: none; } }

/* Phone: the calendar is a centred sheet rather than a dropdown, so it never
   hangs off the edge or lands under the on-screen keyboard. */
@media (max-width: 560px) {
  .gdp {
    position: fixed !important;
    left: 50% !important; top: 50% !important;
    transform: translate(-50%, -50%);
    width: min(300px, calc(100vw - 32px));
  }
  .gdp.is-open { animation: gdp-in-center .16s ease; }
  @keyframes gdp-in-center {
    from { opacity: 0; transform: translate(-50%, -46%); }
    to   { opacity: 1; transform: translate(-50%, -50%); }
  }
  /* A huge spread shadow dims everything OUTSIDE the panel. A ::before at
     z-index:-1 cannot work here: .gdp creates its own stacking context, so
     the pseudo-element paints over the panel's white background. */
  .gdp { box-shadow: 0 0 0 100vmax rgba(36,28,24,.45), 0 16px 44px rgba(0,0,0,.3); }
  .gdp-day { min-height: 38px; }
}

/* ── Active pilgrimage: map controls and fullscreen ──────────── */

/* The toolbar sits bottom-right, so the tool stack keeps clear of it. */
.active-layout .map-tools { top: 14px; right: 14px; }

.active-layout.is-fullscreen {
  position: fixed; inset: 0; z-index: 1500;
  height: 100vh !important; background: var(--bg);
}
.active-layout.is-fullscreen .active-sidebar { display: none; }
.active-layout.is-fullscreen .map-wrap { flex: 1; height: 100vh; }
.active-layout.is-fullscreen::after {
  content: "Press Esc to exit";
  position: absolute; left: 16px; bottom: 16px;
  background: rgba(36,28,24,.82); color: #fff;
  font-size: .75rem; padding: 5px 11px; border-radius: 999px;
  pointer-events: none; z-index: 1600;
}

@media (max-width: 768px) {
  .active-layout .map-tools { top: 12px; right: 12px; }
  .active-layout .map-tool { width: 38px; height: 38px; font-size: 0.875rem; }
  /* Clear of the floating chatbot button in the opposite corner. */
  .ap-toolbar { bottom: 18px; right: 78px; }
  .ap-summary { left: 12px; bottom: 14px; max-width: calc(100% - 100px); }
}

/* ════════════════════════════════════════════════════════════════
   FOOTER - responsive
   The brand blurb needs width; the two link lists do not. So the
   brand drops to its own row and the lists stay side by side,
   rather than everything stacking into one long column.
   ════════════════════════════════════════════════════════════════ */

/* Tablet and below: brand on top, Explore and Account share the row. */
@media (max-width: 900px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
    gap: 26px 24px;
  }
  /* The brand block spans both columns above the lists. */
  .footer-grid > *:first-child { grid-column: 1 / -1; }
}

/* Phone: same arrangement, tighter. Two columns is still comfortable -
   "Visita Iglesia" is the longest label and fits at 360px. */
@media (max-width: 560px) {
  .footer-grid { gap: 22px 18px; }
  .footer-col-title { font-size: .75rem; }
  .footer-link { font-size: 0.8125rem; }
}

/* Very narrow only: give in and stack. */
@media (max-width: 340px) {
  .footer-grid { grid-template-columns: 1fr; }
}

/* The bottom bar wraps rather than overflowing. */
.footer-bottom {
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: space-between;
  gap: 8px 16px;
}

@media (max-width: 560px) {
  .footer-bottom { justify-content: flex-start; font-size: .75rem; }
  /* Clear of the floating chatbot button in the corner. */
  .giya-footer { padding-bottom: 88px; }
}

/* ── Church name links through to its own page ───────────────── */
.mx-row-body h3 a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color .12s ease, border-color .12s ease;
}
.mx-row-body h3 a:hover,
.mx-row-body h3 a:focus-visible {
  color: var(--primary);
  border-bottom-color: var(--gold);
}
.mx-row-body h3 a:focus-visible { outline: none; }

/* The tick reads as a toggle, so say so on hover. */
.mx-pick.is-on:hover {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
}

/* ── Date field icon ─────────────────────────────────────────────
   Declared last, and with every background longhand spelled out.

   An earlier rule sets `background: var(--bg-input)` as a shorthand, which
   silently resets background-repeat to `repeat`. A later rule that sets only
   background-image then inherits that reset - and the calendar glyph tiles
   across the field. That is what iOS was showing.
   ---------------------------------------------------------------- */
input[type="date"],
input[type="datetime-local"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%238E3B2F'%3E%3Cpath d='M4.5 1a.75.75 0 0 1 .75.75V2.5h5.5v-.75a.75.75 0 0 1 1.5 0V2.5h.5A1.75 1.75 0 0 1 14.5 4.25v8.5A1.75 1.75 0 0 1 12.75 14.5h-9.5A1.75 1.75 0 0 1 1.5 12.75v-8.5A1.75 1.75 0 0 1 3.25 2.5h.5v-.75A.75.75 0 0 1 4.5 1ZM3 6.5v6.25c0 .14.11.25.25.25h9.5a.25.25 0 0 0 .25-.25V6.5Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 15px 15px;
  background-attachment: scroll;
  background-origin: padding-box;
  background-clip: border-box;
  padding-right: 38px;
  /* iOS shrinks a date field to its content and centres the text. */
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-height: 44px;
  text-align: left;
}

html[data-theme="dark"] input[type="date"],
html[data-theme="dark"] input[type="datetime-local"] {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23D7A94A'%3E%3Cpath d='M4.5 1a.75.75 0 0 1 .75.75V2.5h5.5v-.75a.75.75 0 0 1 1.5 0V2.5h.5A1.75 1.75 0 0 1 14.5 4.25v8.5A1.75 1.75 0 0 1 12.75 14.5h-9.5A1.75 1.75 0 0 1 1.5 12.75v-8.5A1.75 1.75 0 0 1 3.25 2.5h.5v-.75A.75.75 0 0 1 4.5 1ZM3 6.5v6.25c0 .14.11.25.25.25h9.5a.25.25 0 0 0 .25-.25V6.5Z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 15px 15px;
}

/* Safari leaves a readonly empty date field blank, with no hint at all. */
input[type="date"]:not([value]):empty::before { content: none; }
input[type="date"][readonly] { cursor: pointer; }

/* The navbar publishes its height so overlays can sit clear of it.
   Declared on :root - a property set on .giya-nav is invisible to siblings.
   giya.js overwrites this with the measured value on load and on resize. */
:root { --nav-height: 64px; }

@media (max-width: 768px) {
  .flash-container {
    right: 12px; left: 12px;
    width: auto;
    top: calc(var(--nav-height, 56px) + 10px);
  }
}

/* ════════════════════════════════════════════════════════════════
   HERO STATS - equal columns, so labels line up whatever their length
   ════════════════════════════════════════════════════════════════ */
.hero-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px 12px;
  padding-top: 20px; margin-top: 4px;
  border-top: 1px solid rgba(215,169,74,.2);
}
.hero-stat { min-width: 0; }
.hero-stat-value {
  display: block;
  font-weight: 700;
  font-size: 1.5rem; line-height: 1; color: var(--gold);
  letter-spacing: -.01em;
}
.hero-stat-label {
  display: block; margin-top: 5px;
  font-size: 0.75rem; line-height: 1.35;
  color: rgba(255,255,255,.62);
}

/* Three in one row on every phone (it used to drop to 2 + 1 at 380px and
   below - smaller iPhones and any iPhone with Display Zoom on). Narrow
   screens get slightly smaller text instead; a long label wraps under
   its own number. */
@media (max-width: 430px) {
  .hero-stats { gap: 14px 10px; }
  .hero-stat-value { font-size: 1.375rem; }
  .hero-stat-label { font-size: .75rem; }
}
@media (max-width: 360px) {
  .hero-stats { gap: 12px 8px; }
  .hero-stat-value { font-size: 1.25rem; }
  .hero-stat-label { font-size: .75rem; }
}

/* ════════════════════════════════════════════════════════════════
   NOTIFICATION PANEL
   ════════════════════════════════════════════════════════════════ */
.nav-bell-wrap { position: relative; }

.nav-bell {
  position: relative;
  background: rgba(255,255,255,.12); border: 0;
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.nav-bell:hover { background: rgba(255,255,255,.2); }
.nav-bell:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.nav-bell-dot {
  position: absolute; top: -3px; right: -3px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px;
  background: var(--gold); color: var(--primary-dark);
  font-size: 0.5625rem; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--primary);
}
.nav-bell-dot.is-hidden { display: none; }

.notif-panel {
  position: absolute; top: calc(100% + 12px); right: 0;
  z-index: 1300;
  width: min(340px, calc(100vw - 24px));
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: 0 18px 46px rgba(36,28,24,.26);
  display: none; overflow: hidden;
}
.notif-panel.is-open { display: block; animation: notif-in .14s ease; }
@keyframes notif-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.notif-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border-light);
  font-size: 0.8125rem; font-weight: 700; color: var(--text);
}
.notif-readall {
  background: none; border: 0; cursor: pointer;
  font-size: .75rem; font-weight: 700; color: var(--primary);
}
.notif-readall:hover { text-decoration: underline; }

.notif-body { max-height: 340px; overflow-y: auto; }

.notif-item {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 14px; text-decoration: none;
  border-bottom: 1px solid var(--border-light);
  color: var(--text);
}
.notif-item:last-child { border-bottom: 0; }
.notif-item:hover { background: var(--bg); }
.notif-item.is-unread { background: var(--gold-bg); }
.notif-item.is-unread .notif-title { font-weight: 700; }

.notif-icon {
  width: 30px; height: 30px; flex-shrink: 0; border-radius: 9px;
  background: var(--gold-bg); color: var(--primary);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.8125rem;
}
.notif-item.is-unread .notif-icon { background: #fff; }

.notif-text { min-width: 0; }
.notif-title { display: block; font-size: 0.8125rem; line-height: 1.35; color: var(--text); }
.notif-msg   { display: block; font-size: .75rem; color: var(--text-muted); margin-top: 2px; line-height: 1.45; }
.notif-when  { display: block; font-size: .75rem; color: var(--text-muted); margin-top: 3px; }

/* Dismiss sits beside the item rather than inside it: a button inside the
   item's <a> would be invalid HTML and its click would follow the link. */
.notif-row { position: relative; }
.notif-row .notif-item { padding-right: 38px; }
.notif-dismiss {
  position: absolute; top: 8px; right: 8px;
  width: 26px; height: 26px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
  color: var(--text-muted); font-size: 1rem; line-height: 1;
}
.notif-dismiss:hover, .notif-dismiss:focus-visible { background: var(--border-light); color: var(--text); }

.notif-empty {
  padding: 26px 18px; text-align: center;
  font-size: 0.75rem; line-height: 1.6; color: var(--text-muted); margin: 0;
}

@media (max-width: 600px) {
  .notif-panel {
    position: fixed;
    top: calc(var(--nav-height, 56px) + 8px);
    left: 12px; right: 12px;
    width: auto;
    max-height: calc(100vh - var(--nav-height, 56px) - 90px);
    display: none;
  }
  .notif-panel.is-open { display: flex; flex-direction: column; }
  .notif-body { max-height: none; flex: 1; }
}

html[data-theme="dark"] .notif-panel { background: var(--surface); }
html[data-theme="dark"] .notif-item:hover { background: var(--surface-2); }
html[data-theme="dark"] .notif-item.is-unread { background: var(--surface-2); }
html[data-theme="dark"] .notif-item.is-unread .notif-icon { background: var(--surface); }

@media (prefers-reduced-motion: reduce) { .notif-panel.is-open { animation: none; } }

/* The hamburger must stay on top of anything the navbar contains. */
.hamburger-btn { position: relative; z-index: 5; }
.nav-bell-wrap { position: relative; z-index: 4; }

/* The open menu sits above page content. */
.mobile-menu { position: relative; z-index: 3; }

/* Mobile menu styles live in components/navbar.blade.php, beside the markup. */

/* ════════════════════════════════════════════════════════════════
   HOME SEARCH BAR
   The field and its button share a row at every width; the category
   chips sit below and scroll sideways rather than wrapping.
   ════════════════════════════════════════════════════════════════ */

/* Field + button, never separated. */

/* Chips: one row, scrolled, never four lines deep. */

@media (max-width: 480px) {

  /* The button becomes an icon, so the field keeps the width it needs. */

}

/* On the hero the bar has no strip of its own - in either theme. The generic
   dark rule above was painting a black band across the hero image. */

/* ── Home card grids ─────────────────────────────────────────────
   auto-fit rather than auto-fill: with four cards and room for five,
   auto-fill keeps an empty column and the row looks lopsided.
   ---------------------------------------------------------------- */
.home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px;
}
.home-grid-sm {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}

/* Two across on a tablet, one on a phone - chosen, not incidental. */
@media (max-width: 900px) {
  .home-grid, .home-grid-sm { grid-template-columns: repeat(2, 1fr); gap: 14px; }
}
@media (max-width: 560px) {
  .home-grid, .home-grid-sm { grid-template-columns: 1fr; gap: 12px; }
}

/* Section headers stack instead of squeezing their "View all" link. */
@media (max-width: 560px) {
  .section-header {
    flex-direction: column; align-items: flex-start; gap: 8px;
  }
  .section-title { font-size: 1.25rem; }
  .section-subtitle { font-size: 0.8125rem; }
  .page-wrap { padding-left: 16px; padding-right: 16px; }
}

/* ── Preferences save themselves ─────────────────────────────── */
.pref-status {
  min-height: 18px; margin: 4px 0 0;
  font-size: 0.75rem; font-weight: 600;
  color: var(--text-muted);
  transition: color .15s ease;
}
.pref-status.is-ok    { color: #166534; }
.pref-status.is-error { color: #B3182F; }

/* The pills are the control now, so make them feel like one. */
.pref-choice { transition: background .15s ease, border-color .15s ease, color .15s ease; }
.pref-choice:active { transform: scale(.98); }

/* ════════════════════════════════════════════════════════════════
   GIYA AI - chat
   ════════════════════════════════════════════════════════════════ */

.chat-page { display: flex; flex-direction: column; }

.chat-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #16A34A; flex-shrink: 0;
  box-shadow: 0 0 0 3px rgba(22,163,74,.16);
}
.chat-dot.is-off { background: var(--text-muted); box-shadow: none; }

.chat-shell { display: flex; flex-direction: column; overflow: hidden; }

.chat-log {
  flex: 1; min-height: 320px;
  max-height: min(58vh, 560px);
  overflow-y: auto;
  padding: 22px; background: #fff;
  display: flex; flex-direction: column; gap: 15px;
  scroll-behavior: smooth;
}

.chat-row { display: flex; gap: 11px; align-items: flex-start; }
.chat-row.is-user { flex-direction: row-reverse; }

.chat-bot-icon {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 50%;
  background: var(--gold-bg); color: var(--primary);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.875rem;
}
.chat-user-icon { flex-shrink: 0; width: 34px; height: 34px; }

.chat-bubble {
  max-width: 78%;
  padding: 11px 15px; border-radius: 16px;
  font-size: 0.875rem; line-height: 1.65;
  word-break: break-word;
}
.chat-bubble-bot {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border-light);
  border-top-left-radius: 5px;
}
.chat-bubble-bot.is-offline { border-left: 3px solid var(--gold); }
.chat-bubble-user {
  background: var(--primary); color: #fff;
  border-top-right-radius: 5px;
}
.chat-bubble strong { font-weight: 700; }

/* Three dots, so a wait reads as composing rather than loading. */
.chat-typing { display: flex; gap: 5px; align-items: center; padding: 15px; }
.chat-typing span {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-muted);
  animation: chat-blink 1.3s infinite ease-in-out;
}
.chat-typing span:nth-child(2) { animation-delay: .18s; }
.chat-typing span:nth-child(3) { animation-delay: .36s; }
@keyframes chat-blink {
  0%, 60%, 100% { opacity: .25; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-3px); }
}

/* One line that scrolls sideways, rather than three rows of chips. */
.chat-starters {
  display: flex; flex-wrap: nowrap; gap: 8px;
  padding: 0 22px 16px; background: #fff;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}
.chat-starters::-webkit-scrollbar { display: none; }
.chat-starters .chat-starter { flex: 0 0 auto; white-space: nowrap; }
.chat-starter {
  padding: 8px 14px; border-radius: 999px;
  border: 1px solid var(--border); background: #fff;
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 600;
  color: var(--primary); cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.chat-starter:hover { background: var(--primary); border-color: var(--primary); color: #fff; }

/* ── Edge fade on the chip strips ───────────────────────────────
   With the scrollbar hidden there is nothing on a desktop to say the
   row continues, so the edge softens on whichever side still has
   chips off-screen. giya-ai-panel.js toggles the two classes.
   ---------------------------------------------------------------- */
.chat-starters,
.ai-panel-starters {
  --edge-l: 0px;
  --edge-r: 0px;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--edge-l),
                      #000 calc(100% - var(--edge-r)), transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 var(--edge-l),
                      #000 calc(100% - var(--edge-r)), transparent 100%);
}
.chat-starters.can-left,
.ai-panel-starters.can-left  { --edge-l: 30px; }
.chat-starters.can-right,
.ai-panel-starters.can-right { --edge-r: 30px; }

@media (prefers-reduced-motion: reduce) {
  .chat-starters, .ai-panel-starters { scroll-behavior: auto; }
}

.chat-composer {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-top: 1px solid var(--border);
  background: var(--bg);
}
.chat-composer input {
  flex: 1; min-width: 0;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 12px 18px; font-size: 0.875rem;
  background: #fff; color: var(--text);
}
.chat-composer input:focus { outline: none; border-color: var(--gold); }
.chat-composer input:disabled { opacity: .6; }

.chat-send {
  width: 44px; height: 44px; flex-shrink: 0;
  border: 0; border-radius: 50%;
  background: var(--primary); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.9375rem; cursor: pointer;
  transition: background .15s ease, transform .12s ease;
}
.chat-send:hover { background: var(--primary-dark); }
.chat-send:active { transform: scale(.94); }
.chat-send:disabled { opacity: .5; cursor: default; }

.chat-note {
  font-size: .75rem; line-height: 1.6;
  color: var(--text-muted); text-align: center;
  margin: 14px auto 0; max-width: 520px;
}

@media (max-width: 600px) {
  .chat-log { padding: 16px; max-height: none; min-height: 46vh; }
  .chat-bubble { max-width: 84%; font-size: 0.8125rem; }
  .chat-starters { padding: 0 16px 14px; }
  .chat-composer { padding: 12px; }

}

html[data-theme="dark"] .chat-log,
html[data-theme="dark"] .chat-starters,
html[data-theme="dark"] .chat-starter,
html[data-theme="dark"] .chat-composer input { background: var(--surface); }
html[data-theme="dark"] .chat-bubble-bot { background: var(--surface-2); }

@media (prefers-reduced-motion: reduce) {
  .chat-typing span { animation: none; opacity: .6; }
  .chat-log { scroll-behavior: auto; }
}

/* ════════════════════════════════════════════════════════════════
   GIYA AI - slide-out panel
   Desktop: a docked panel beside the content.
   Mobile:  a bottom sheet, which is where a thumb already is.
   ════════════════════════════════════════════════════════════════ */

.ai-panel {
  position: fixed;
  right: 22px; bottom: 92px;
  z-index: 1450;
  width: min(380px, calc(100vw - 44px));
  max-height: min(560px, calc(100vh - 160px));
  display: flex; flex-direction: column;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: 0 24px 60px rgba(36,28,24,.28);
  overflow: hidden;

  opacity: 0; transform: translateY(14px) scale(.98);
  transition: opacity .22s ease, transform .22s cubic-bezier(.2,.8,.3,1);
}
.ai-panel.is-open { opacity: 1; transform: none; }

.ai-panel-scrim {
  position: fixed; inset: 0; z-index: 1440;
  background: rgba(36,28,24,.32);
  opacity: 0; transition: opacity .22s ease;
}
.ai-panel-scrim.is-open { opacity: 1; }

/* ── Head ─────────────────────────────────────────────────────── */
.ai-panel-head {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 14px;
  background: var(--primary); color: #fff;
  flex-shrink: 0;
}
.ai-panel-mark {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 10px;
  background: rgba(255,255,255,.16); color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.9375rem;
}
.ai-panel-title { flex: 1; min-width: 0; line-height: 1.3; }
.ai-panel-title strong { display: block; font-size: 0.875rem; }
.ai-panel-title span   { display: block; font-size: .75rem; color: rgba(255,255,255,.72); }

.ai-panel-expand,
.ai-panel-close {
  width: 30px; height: 30px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 8px; cursor: pointer;
  background: rgba(255,255,255,.12); color: #fff;
  font-size: 0.8125rem; text-decoration: none;
}
.ai-panel-expand:hover,
.ai-panel-close:hover { background: rgba(255,255,255,.24); color: #fff; }

/* ── Body ─────────────────────────────────────────────────────── */
.ai-panel-log {
  flex: 1; min-height: 0;
  overflow-y: auto; padding: 15px;
  display: flex; flex-direction: column; gap: 12px;
  background: #fff; scroll-behavior: smooth;
}
.ai-panel .chat-bubble { max-width: 88%; font-size: 0.8125rem; }
.ai-panel .chat-row.is-user { flex-direction: row-reverse; }

/* Three starters stacked into three rows ate the panel's height. They now
   sit on one line and scroll sideways, the way the map and home chips do. */
.ai-panel-starters {
  display: flex; flex-wrap: nowrap; gap: 6px;
  padding: 0 15px 12px; flex-shrink: 0;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}
.ai-panel-starters::-webkit-scrollbar { display: none; }
.ai-panel-starters .chat-starter {
  flex: 0 0 auto; white-space: nowrap;
  font-size: .75rem; padding: 6px 11px;
}

.ai-panel-composer {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 12px; flex-shrink: 0;
  border-top: 1px solid var(--border); background: var(--bg);
}
.ai-panel-composer input {
  flex: 1; min-width: 0;
  border: 1px solid var(--border); border-radius: 999px;
  padding: 10px 15px; font-size: 0.8125rem;
  background: #fff; color: var(--text);
}
.ai-panel-composer input:focus { outline: none; border-color: var(--gold); }
.ai-panel-composer .chat-send { width: 38px; height: 38px; font-size: 0.8125rem; }

.ai-panel-grab { display: none; }

/* ── Mobile: a bottom sheet ───────────────────────────────────── */
@media (max-width: 640px) {
  .ai-panel {
    right: 0; left: 0; bottom: 0;
    width: 100%;
    max-height: 86vh; height: 86vh;
    border-radius: 20px 20px 0 0;
    border-left: 0; border-right: 0; border-bottom: 0;
    transform: translateY(100%);
  }
  .ai-panel.is-open { transform: translateY(0); }

  /* A grab handle, so it reads as a sheet rather than a stuck dialog. */
  .ai-panel-head { padding-top: 18px; position: relative; }
  .ai-panel-head { touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; }
  .ai-panel-grab {
    display: block;
    position: absolute; top: 0; left: 50%;
    transform: translateX(-50%);
    width: 96px; height: 22px;             /* big enough to hit with a thumb */
    padding: 0; border: 0; background: none; cursor: grab;
  }
  .ai-panel-grab::before {
    content: ""; position: absolute; top: 7px; left: 50%;
    transform: translateX(-50%);
    width: 38px; height: 4px; border-radius: 999px;
    background: rgba(255,255,255,.5);
  }
  .ai-panel-grab:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; border-radius: 8px; }

  .ai-panel-log { padding: 14px; }
  .ai-panel .chat-bubble { max-width: 84%; }

  /* Clear of the home indicator on a modern iPhone. */
  .ai-panel-composer { padding-bottom: max(11px, env(safe-area-inset-bottom)); }
}

/* The page behind must not scroll while the sheet is open. */
body.ai-panel-locked { overflow: hidden; }

html[data-theme="dark"] .ai-panel,
html[data-theme="dark"] .ai-panel-log,
html[data-theme="dark"] .ai-panel-composer input { background: var(--surface); }
html[data-theme="dark"] .ai-panel-composer { background: var(--surface-2); }

@media (prefers-reduced-motion: reduce) {
  .ai-panel, .ai-panel-scrim { transition: none; }
}

/* The button steps aside while the sheet is up. */
body.ai-panel-locked .giya-fab { opacity: 0; pointer-events: none; }
.giya-fab { transition: opacity .2s ease, gap .18s ease, padding .18s ease,
                        background .15s ease, transform .15s ease, box-shadow .18s ease; }

/* ── Zoom pair in the map tool stack ─────────────────────────────
   Zoom in and out are one control with two directions, so they share
   a rounded outline. They match the fullscreen and locate buttons
   above them; Leaflet's own control is switched off in giya-leaflet.js.
   ---------------------------------------------------------------- */
.map-tool-pair {
  display: flex; flex-direction: column;
  border-radius: 12px; overflow: hidden;
  box-shadow: 0 2px 10px rgba(36,28,24,.22);
}
.map-tool-pair .map-tool { border-radius: 0; box-shadow: none; }
.map-tool-pair .map-tool:first-child {
  border-radius: 12px 12px 0 0;
  border-bottom: 1px solid var(--border);
}
.map-tool-pair .map-tool:last-child { border-radius: 0 0 12px 12px; }

/* Safety net: if a map is ever created without zoomControl:false. */
.leaflet-control-zoom { display: none !important; }

/* The home search hands its term to the map rather than showing results in
   place, so the old dropdown styles are gone. The map filters by name,
   location and category - see map.blade.php. */

/* ── Search sitting on the hero ──────────────────────────────────
   On a dark photographic background the bar needs its own contrast:
   a translucent white field with a blur behind it, rather than the
   white strip it used when it had its own row.
   ---------------------------------------------------------------- */

/* Chips read as light-on-dark here, unlike on a white strip. */

/* The results panel stays a solid card - it holds real content. */

/* ── Hero inner spacing ──────────────────────────────────────────
   Was a fixed 48px top, which reads well on a phone and sits far too
   close under the navbar on a wide screen. clamp() scales it with the
   viewport rather than picking one number for every device.
   ---------------------------------------------------------------- */
/* ── HOME HERO ───────────────────────────────────────────────
   The photograph, the scrim over it and the copy on top. All of this used to
   be inline style attributes on the elements themselves, which meant the
   hero's design lived in a Blade template and could not be read, overridden
   per theme, or changed without editing markup. */
.hero {
  position: relative;
  overflow: hidden;
  min-height: 520px;
  display: flex;
  align-items: center;
}

.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* Dark enough that white text clears 4.5:1 over the brightest part of the
   photograph, which is the sky at the top right. */
.hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(142, 59, 47, .9) 0%, rgba(36, 28, 24, .82) 100%);
}

.hero-mark {
  position: absolute;
  top: 32px; right: 64px;
  opacity: .1;
}

.hero-inner {
  position: relative;
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: clamp(32px, 7vh, 72px) 20px clamp(56px, 9vh, 96px);
}

.hero-copy { max-width: 680px; }

.hero-title {
  font-family: var(--font-display);
  color: #fff;
  font-size: clamp(32px, 5vw, 54px);
  line-height: 1.15;
  font-weight: 700;
  margin: 0 0 16px;
}

.hero-lead {
  color: rgba(255, 255, 255, .8);
  font-size: 1rem;
  line-height: 1.75;
  max-width: 560px;
  margin: 0 0 24px;
}

@media (max-width: 560px) {
  .hero-inner { padding-left: 16px; padding-right: 16px; }
}

/* ── Search field icon ───────────────────────────────────────────
   The SVG version was dark artwork on a translucent field over a dark
   hero, so it disappeared while its 44px of padding kept the space -
   the gap on the left of the input. A font glyph inherits colour, so
   it can be tinted to suit whichever background it sits on.
   ---------------------------------------------------------------- */

/* Full width across the hero. */

/* On a very wide screen a full-bleed field looks empty, so cap it there. */

/* A chip stays lit while its word is in the search box, so the devotee can see
   what they picked before pressing Search. */

/* ════════════════════════════════════════════════════════════════
   CHURCH DETAIL - polish
   ════════════════════════════════════════════════════════════════ */

/* The hero photo was washing out under a flat overlay, so the building
   was barely legible. A gradient keeps the top of the image clear and
   darkens only where the text sits. */
.church-hero-overlay {
  background: linear-gradient(
    to bottom,
    rgba(36,28,24,.30) 0%,
    rgba(36,28,24,.45) 45%,
    rgba(36,28,24,.88) 100%
  );
}
.church-hero-image { filter: none; }

/* The title needs its own shadow - a gradient alone cannot carry every photo. */
.church-hero-content h1 { text-shadow: 0 2px 18px rgba(0,0,0,.55); }
.church-hero-address    { text-shadow: 0 1px 10px rgba(0,0,0,.5); }

/* ── Stat strip ─────────────────────────────────────────────────
   Dividers between items, so four values do not read as one row of
   loose text, and each stat now carries an icon.
   ---------------------------------------------------------------- */
.church-stat-strip { gap: 0; }
.church-stat-item {
  position: relative;
  padding: 0 22px;
  text-align: center;
}
.church-stat-item + .church-stat-item::before {
  content: "";
  position: absolute; left: 0; top: 50%;
  transform: translateY(-50%);
  width: 1px; height: 30px;
  background: var(--border);
}
.church-stat-value.is-inline {
  display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.church-stat-value .bi { font-size: 0.8125rem; }
.church-stat-label {
  font-size: .75rem; letter-spacing: .03em;
  color: var(--text-muted); margin-top: 3px;
}

/* ── Tabs ───────────────────────────────────────────────────────
   The active tab was marked by colour alone, which is the one cue a
   colour-blind reader cannot use. A weight change and a thicker rule
   carry it too.
   ---------------------------------------------------------------- */
.church-tab { font-weight: 600; transition: color .15s ease; }
.church-tab.is-active { font-weight: 800; }
.church-tab.is-active::after { height: 3px; }

/* ── Empty states ───────────────────────────────────────────────
   The icon was a bare glyph floating in space. A tinted disc gives it
   an edge, so an empty panel looks designed rather than unfinished.
   ---------------------------------------------------------------- */
.empty-state .empty-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--gold-bg);
  margin: 0 auto 12px;
}
.empty-state .empty-icon .bi {
  font-size: 1.375rem;
  color: var(--primary) !important;   /* overrides the inline gold */
}

@media (max-width: 700px) {
  .church-stat-strip { flex-wrap: wrap; row-gap: 14px; }
  .church-stat-item { padding: 0 14px; flex: 1 1 44%; }
  /* With two per row, the vertical rules land in the wrong places. */
  .church-stat-item + .church-stat-item::before { display: none; }
  .church-hero-content h1 { font-size: 1.75rem; }
}

/* ── Stops carried over from the map ─────────────────────────── */

/* ── Icons inside buttons must not intercept the click ───────────
   A .bi is an inline element with a ::before mask. It sits above the
   button's hit area, so a click landing on the glyph goes to the icon
   rather than the button - which reads as "only the edges work".
   ---------------------------------------------------------------- */
.btn .bi,
.btn .bi::before,
button .bi,
button .bi::before,
.chat-starter .bi,
.map-tool .bi,
.icon-btn .bi {
  pointer-events: none;
}
/* ── Save button on the church detail hero ───────────────────── */
.church-fav .bi { transition: transform .18s cubic-bezier(.2,.8,.3,1); }
.church-fav.is-saved {
  background: rgba(215,169,74,.22);
  border-color: var(--gold);
  color: #fff;
}
.church-fav.is-saved .bi {
  color: #E8607A;
  transform: scale(1.15);
}

/* ── Notify me, beside Save: same shape, gold bell when on ──── */
.church-notify .bi { transition: transform .18s cubic-bezier(.2,.8,.3,1); opacity: .75; }
.church-notify.is-on {
  background: rgba(215,169,74,.22);
  border-color: var(--gold);
  color: #fff;
}
.church-notify.is-on .bi {
  color: var(--gold);
  opacity: 1;
  transform: rotate(-12deg) scale(1.15);
}
@media (prefers-reduced-motion: reduce) {
  .church-notify .bi, .church-fav .bi { transition: none; }
}

/* ── Church card title links through ─────────────────────────── */
html[data-theme="dark"] .church-card-name a:hover { color: var(--gold); }

/* ── Arrival toast on the pilgrimage map ─────────────────────── */
.arrive-toast {
  position: absolute; left: 50%; bottom: 22px;
  transform: translate(-50%, 14px);
  z-index: 700;
  display: flex; align-items: center; gap: 12px;
  max-width: min(420px, calc(100% - 32px));
  padding: 12px 14px;
  background: #fff;
  border: 1px solid var(--border);
  border-left: 3px solid #4E9C6B;
  border-radius: var(--radius-lg);
  box-shadow: 0 14px 38px rgba(36,28,24,.28);
  opacity: 0;
  transition: opacity .28s ease, transform .28s cubic-bezier(.2,.8,.3,1);
}
.arrive-toast.is-open { opacity: 1; transform: translate(-50%, 0); }

.arrive-icon {
  width: 34px; height: 34px; flex-shrink: 0; border-radius: 50%;
  background: #4E9C6B; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.9375rem;
}
.arrive-body { flex: 1; min-width: 0; line-height: 1.35; }
.arrive-body strong { display: block; font-size: 0.8125rem; color: var(--text); }
.arrive-body span   { display: block; font-size: 0.75rem; color: var(--text-muted); }

.arrive-undo {
  flex-shrink: 0;
  background: none; border: 1px solid var(--border);
  border-radius: 999px; padding: 6px 13px;
  font-family: var(--font-body); font-size: .75rem; font-weight: 700;
  color: var(--text-muted); cursor: pointer;
}
.arrive-undo:hover { border-color: var(--primary); color: var(--primary); }

/* "Visited" in a pin popup, in place of the mark button. */
.giya-popup-done {
  display: block; margin-top: 8px; text-align: center;
  font-size: .75rem; font-weight: 700; color: #4E9C6B;
}

@media (max-width: 600px) {
  .arrive-toast { left: 12px; right: 12px; max-width: none; transform: translateY(14px); }
  .arrive-toast.is-open { transform: translateY(0); }
}

html[data-theme="dark"] .arrive-toast { background: var(--surface); }

/* ── Admin destination map: fullscreen ───────────────────────── */
.dm-map-shell { position: relative; flex: 1 1 420px; min-width: 0; }
.dm-map-shell .dm-map-canvas { width: 100%; }

.dm-map-shell.is-fullscreen {
  position: fixed; inset: 0; z-index: 1500;
  flex: none; padding: 0;
  background: var(--bg);
}
.dm-map-shell.is-fullscreen .dm-map-canvas {
  width: 100%; height: 100vh;
  border: 0; border-radius: 0;
}
.dm-map-shell .map-tools { top: 12px; right: 12px; }

/* A line telling the admin what a click will do while expanded. */
.dm-map-hint-bar {
  position: absolute; left: 50%; bottom: 20px;
  transform: translateX(-50%);
  z-index: 700; margin: 0;
  padding: 9px 16px; border-radius: 999px;
  background: rgba(36,28,24,.86); color: #fff;
  font-size: 0.75rem; font-weight: 600;
  white-space: nowrap; pointer-events: none;
}

.quick-coords {
  font-size: 0.75rem; font-weight: 700; color: var(--primary);
  font-variant-numeric: tabular-nums;
  margin: 0 0 14px;
}
.quick-note {
  font-size: .75rem; line-height: 1.6;
  color: var(--text-muted); margin: 0 0 14px;
}

/* The modal must sit above an expanded map. */
#quickAddModal { z-index: 2100; }

@media (max-width: 600px) {
  .dm-map-hint-bar { left: 12px; right: 12px; transform: none; white-space: normal; text-align: center; }
}

/* ════════════════════════════════════════════════════════════════
   ADMIN DESTINATIONS - map first
   The map is the interface; the form is a modal over it.
   ════════════════════════════════════════════════════════════════ */

.dm-mapcard { padding: 20px; margin-bottom: 22px; }
.dm-mapcard-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 14px;
}
.dm-mapcard-sub {
  font-size: 0.8125rem; color: var(--text-muted); margin: 4px 0 0;
}

.dm-map-shell { position: relative; }
.dm-map-shell .dm-map-canvas {
  width: 100%; height: clamp(360px, 52vh, 560px);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden; background: #E8DFD1;
}

.dm-map-shell.is-fullscreen {
  position: fixed; inset: 0; z-index: 1500; background: var(--bg);
}
.dm-map-shell.is-fullscreen .dm-map-canvas {
  height: 100vh; border: 0; border-radius: 0;
}
.dm-map-shell .map-tools { top: 12px; right: 12px; }

.dm-map-hint-bar {
  position: absolute; left: 50%; bottom: 18px;
  transform: translateX(-50%);
  z-index: 700; margin: 0;
  padding: 9px 16px; border-radius: 999px;
  background: rgba(36,28,24,.84); color: #fff;
  font-size: 0.75rem; font-weight: 600;
  white-space: nowrap; pointer-events: none;
  transition: background .2s ease;
}
/* While waiting for a click, the hint stops being a caption and becomes an
   instruction, so the admin knows the map is expecting something. */
.dm-map-hint-bar.is-active {
  background: var(--primary);
  box-shadow: 0 0 0 4px rgba(142,59,47,.22);
}

/* ── The form, as a modal ───────────────────────────────────────
   Tall on purpose: nine fields and an upload need room, and a modal
   that scrolls internally beats one that pushes the page around.
   ---------------------------------------------------------------- */
.dm-modal {
  border: none; border-radius: var(--radius-2xl);
  /* No max-width of its own: the dialog around it decides, and a value here
     only ever fought with that one. */
  padding: 0;
  display: flex; flex-direction: column;
  max-height: min(88vh, 900px);
  overflow: hidden;
}
.dm-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 20px 24px 14px;
  border-bottom: 1px solid var(--border-light);
  flex-shrink: 0;
}
.dm-modal-head .ai-panel-close {
  background: var(--bg-secondary); color: var(--text-muted);
}
.dm-modal-head .ai-panel-close:hover { background: var(--primary); color: #fff; }

.dm-modal-body {
  padding: 18px 24px;
  overflow-y: auto; flex: 1; min-height: 0;
}
.dm-modal-foot {
  display: flex; gap: 10px; flex-wrap: wrap;
  padding-top: 16px; margin-top: 4px;
  border-top: 1px solid var(--border-light);
}

/* Coordinates are filled by the map, so they read as confirmation. */
.dm-coords input[type="number"]::-webkit-outer-spin-button,
.dm-coords input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.dm-coords input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
/* The modal must clear an expanded map. */
#destModal { z-index: 2100; }

@media (max-width: 600px) {
  .dm-mapcard { padding: 14px; }
  .dm-map-shell .dm-map-canvas { height: clamp(300px, 44vh, 420px); }
  .dm-map-hint-bar { left: 12px; right: 12px; transform: none; white-space: normal; text-align: center; }
  .dm-modal { max-height: 94vh; }
  .dm-modal-body { padding: 14px 16px; }
  .dm-modal-head { padding: 16px 16px 12px; }
}

/* ── Destination filters ─────────────────────────────────────────
   The fields used .field, which carries margin-bottom: 16px. In a row
   aligned to flex-end that pushed every input 16px above the buttons
   beside them, which is why nothing lined up.
   ---------------------------------------------------------------- */
.dm-filters {
  display: flex; flex-wrap: wrap;
  gap: 12px; align-items: flex-end;
  margin-bottom: 18px;
}

.dm-filter-field {
  display: flex; flex-direction: column;
  min-width: 0; margin: 0;              /* no stray bottom margin */
  flex: 1 1 190px;
}

/* The search field, which earns more room than the two selects beside it. */
.dm-filter-field.is-wide { flex-basis: 320px; }
.dm-filter-field .dm-label { margin-bottom: 6px; }
.dm-filter-field .giya-input { width: 100%; height: 44px; }

.dm-filter-actions {
  display: flex; gap: 8px;
  flex: 0 0 auto;
}
/* Same height as the inputs, so the whole row shares one baseline. */
.dm-filter-actions .btn {
  height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px; white-space: nowrap;
}

@media (max-width: 700px) {
  .dm-filters { gap: 10px; }
  .dm-filter-field { flex: 1 1 100% !important; }
  .dm-filter-actions { flex: 1 1 100%; }
  .dm-filter-actions .btn { flex: 1; }
}

/* Delete sits apart from Save and Cancel: it is not an alternative to them. */
.dm-modal-foot .dm-delete {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 6px;
}
@media (max-width: 560px) {
  .dm-modal-foot .dm-delete { margin-left: 0; width: 100%; justify-content: center; }
}

html[data-theme="dark"] .giya-popup strong { color: #241C18; }
html[data-theme="dark"] .giya-popup span   { color: #6B5B4E; }
/* ════════════════════════════════════════════════════════════════
   FEATURED DESTINATIONS - a procession
   One church, full frame, six seconds. One layout at every width:
   no measuring, no per-browser arithmetic.
   ════════════════════════════════════════════════════════════════ */

.proc {
  position: relative;
  height: clamp(400px, 64vh, 560px);
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--primary-dark);
  isolation: isolate;
  outline: none;
}
.proc:focus-visible { box-shadow: 0 0 0 3px var(--gold); }

.proc-slide {
  position: absolute; inset: 0; margin: 0;
  background-size: cover; background-position: center;
  background-color: var(--primary-dark);
  opacity: 0;
  transition: opacity 1.1s ease;
}
/* The live image drifts almost imperceptibly. A completely still photograph
   on a page that is otherwise moving reads as a failed load. */
.proc-slide.is-live {
  opacity: 1;
  animation: proc-drift 14s ease-out forwards;
}
@keyframes proc-drift {
  from { transform: scale(1.08); }
  to   { transform: scale(1); }
}

.proc-veil {
  position: absolute; inset: 0; z-index: 2;
  background:
    linear-gradient(100deg,
      rgba(36,28,24,.94) 0%, rgba(36,28,24,.7) 42%,
      rgba(36,28,24,.22) 78%, rgba(36,28,24,.08) 100%),
    linear-gradient(to top,
      rgba(36,28,24,.72) 0%, rgba(36,28,24,0) 34%);
  pointer-events: none;
}

/* ── Copy ─────────────────────────────────────────────────────── */
.proc-copy {
  position: absolute; z-index: 3;
  left: clamp(22px, 5vw, 64px);
  right: clamp(22px, 5vw, 64px);
  bottom: clamp(64px, 12vh, 96px);
  max-width: min(520px, 76%);
}
.proc-copy > * { opacity: 0; transform: translateY(16px); filter: blur(5px); }
.proc-copy.is-in > * { animation: proc-rise .68s cubic-bezier(.2,.8,.3,1) forwards; }

.proc-copy.is-in .proc-cat   { animation-delay: .13s; }
.proc-copy.is-in h3          { animation-delay: .22s; }
.proc-copy.is-in .proc-loc   { animation-delay: .31s; }
.proc-copy.is-in .proc-desc  { animation-delay: .40s; }
.proc-copy.is-in .btn        { animation-delay: .49s; }

@keyframes proc-rise { to { opacity: 1; transform: none; filter: blur(0); } }

.proc-cat {
  display: inline-block;
  padding: 4px 12px; border-radius: 999px;
  background: var(--gold); color: var(--primary-dark);
  font-size: .75rem; font-weight: 800;
  text-transform: none; letter-spacing: .01em;
  margin-bottom: 12px;
}
.proc-copy h3 {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 4.2vw, 3rem);
  color: #fff; margin: 0 0 8px; line-height: 1.08;
}
.proc-loc {
  display: flex; align-items: center; gap: 6px;
  font-size: 0.8125rem; color: rgba(255,255,255,.86); margin: 0 0 10px;
}
.proc-desc {
  font-size: 0.875rem; line-height: 1.6;
  color: rgba(255,255,255,.76); margin: 0 0 20px;
  max-width: 46ch;
}

/* ── The bars ─────────────────────────────────────────────────── */
.proc-bars {
  position: absolute; z-index: 4;
  left: clamp(22px, 5vw, 64px);
  right: clamp(22px, 5vw, 64px);
  bottom: clamp(24px, 4.5vh, 40px);
  display: flex; gap: 8px;
  max-width: 340px;
}
.proc-bar {
  flex: 1 1 0; height: 26px;
  border: 0; padding: 0; background: none; cursor: pointer;
  display: flex; align-items: center;
}
.proc-bar span {
  display: block; position: relative;
  width: 100%; height: 3px;
  border-radius: 999px;
  background: rgba(255,255,255,.3);
  overflow: hidden;
}
.proc-bar span::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--gold);
  transform-origin: left;
  transform: scaleX(0);
}
.proc-bar.is-done span::after { transform: scaleX(1); }
/* Filled, not filling. With no timer there is nothing to count down, so the
   live bar simply shows which destination is on screen. */
.proc-bar.is-live span::after { transform: scaleX(1); }

.proc-bar:hover span { background: rgba(255,255,255,.5); }
.proc-bar:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 4px; }

@media (max-width: 640px) {
  .proc { height: clamp(420px, 66vh, 520px); }
  .proc-copy { max-width: none; bottom: 72px; }
  .proc-copy h3 { font-size: 1.75rem; }
  .proc-desc { display: none; }
  .proc-bars { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .proc-slide.is-live { animation: none; }
  .proc-copy > * { opacity: 1; transform: none; filter: none; animation: none !important; }
  .proc-bar.is-live span::after { animation: none; transform: scaleX(1); }
}

/* ════════════════════════════════════════════════════════════════
   THE GUIDE - the chatbot's face
   Drawn in CSS rather than an image, so every expression is a class
   change with nothing to load and nothing to go stale.
   ════════════════════════════════════════════════════════════════ */

.guide {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  margin: 0 auto 6px;
  width: fit-content;
}

/* ── Speech bubble ─────────────────────────────────────────────── */
.guide-bubble {
  position: relative;
  max-width: 340px; margin-bottom: 14px;
  padding: 11px 16px;
  background: #fff;
  border: 2px solid var(--primary);
  border-radius: 16px;
  font-size: 0.8125rem; line-height: 1.5;
  color: var(--text); text-align: center;
  opacity: 0; transform: translateY(6px) scale(.96);
}
.guide-bubble.is-in {
  animation: guide-pop .38s cubic-bezier(.2,1.3,.4,1) forwards;
}
@keyframes guide-pop { to { opacity: 1; transform: none; } }

/* The tail, drawn twice so the border shows through. */
.guide-bubble::before,
.guide-bubble::after {
  content: "";
  position: absolute; left: 50%; bottom: -9px;
  transform: translateX(-50%);
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 9px solid var(--primary);
}
.guide-bubble::after { bottom: -6px; border-top-color: #fff; }

/* ── Head ─────────────────────────────────────────────────────── */
.guide-face {
  position: relative;
  width: 118px; height: 104px;
  background: #fff;
  border: 3px solid var(--primary-dark);
  border-radius: 26px;
  box-shadow: 0 6px 0 rgba(36,28,24,.12);
  display: flex; align-items: center; justify-content: center;
  transition: transform .3s cubic-bezier(.2,.8,.3,1);
  z-index: 2;
}
.guide.is-typing .guide-face   { transform: translateY(3px) rotate(-2deg); }
.guide.is-thinking .guide-face { transform: translateY(-4px) rotate(3deg); }
.guide.is-happy .guide-face    { transform: translateY(-2px); }

.guide-ear {
  position: absolute; top: 34px;
  width: 11px; height: 26px;
  background: #fff;
  border: 3px solid var(--primary-dark);
}
.guide-ear-l { left: -12px; border-radius: 8px 0 0 8px; border-right: 0; }
.guide-ear-r { right: -12px; border-radius: 0 8px 8px 0; border-left: 0; }

/* The lantern: giya means guide, and a guide carries a light. */
.guide-lamp {
  position: absolute; top: -17px; left: 50%;
  width: 13px; height: 13px; margin-left: -6.5px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 3px var(--primary-dark), 0 0 16px 3px rgba(215,169,74,.6);
}
.guide-lamp::before {
  content: ""; position: absolute; top: 12px; left: 50%;
  width: 3px; height: 8px; margin-left: -1.5px;
  background: var(--primary-dark);
}
.guide.is-thinking .guide-lamp { animation: guide-flicker 1s ease-in-out infinite; }
@keyframes guide-flicker {
  50% { box-shadow: 0 0 0 3px var(--primary-dark), 0 0 26px 6px rgba(215,169,74,.95); }
}

/* ── Screen ───────────────────────────────────────────────────── */
.guide-screen {
  position: relative;
  width: 88px; height: 74px;
  background: var(--primary-dark);
  border-radius: 18px;
  overflow: hidden;
}

.guide-eye {
  position: absolute; top: 24px;
  width: 15px; height: 15px;
  border-radius: 50%;
  background: #F5E9D9;
  transition: transform .28s cubic-bezier(.2,.8,.3,1),
              height .12s ease, border-radius .12s ease;
}
.guide-eye-l { left: 18px; }
.guide-eye-r { right: 18px; }

/* A blink: the eye squashes rather than disappearing. */
.guide.is-blinking .guide-eye { height: 3px; border-radius: 3px; transform: translateY(6px); }

/* Looking down at what is being typed. */
.guide.is-typing .guide-eye { transform: translateY(4px) scaleY(.8); }

/* Glancing up while it searches. */
.guide.is-thinking .guide-eye-l { transform: translate(3px, -5px); }
.guide.is-thinking .guide-eye-r { transform: translate(3px, -5px); }

/* Pleased: the eyes become arcs, which is how a face smiles. */
.guide.is-happy .guide-eye {
  height: 8px;
  border-radius: 15px 15px 0 0;
  transform: translateY(3px);
}

.guide-mouth {
  position: absolute; left: 50%; bottom: 16px;
  width: 18px; height: 4px; margin-left: -9px;
  border-radius: 999px;
  background: #F5E9D9;
  transition: all .28s cubic-bezier(.2,.8,.3,1);
}
.guide.is-typing .guide-mouth  { width: 12px; margin-left: -6px; }
.guide.is-thinking .guide-mouth {
  width: 10px; height: 10px; margin-left: -5px;
  border-radius: 50%; bottom: 13px;
}
.guide.is-happy .guide-mouth {
  width: 26px; height: 13px; margin-left: -13px;
  border-radius: 0 0 26px 26px;
  bottom: 13px;
}
.guide.is-sorry .guide-mouth {
  width: 20px; height: 9px; margin-left: -10px;
  border-radius: 20px 20px 0 0;
  bottom: 12px;
}

.guide-blush {
  position: absolute; top: 40px;
  width: 11px; height: 5px; border-radius: 50%;
  background: #E2725B; opacity: 0;
  transition: opacity .3s ease;
}
.guide-blush-l { left: 8px; }
.guide-blush-r { right: 8px; }
.guide.is-happy .guide-blush { opacity: .85; }

/* ── Hands, for the panel below ───────────────────────────────── */
.guide-hands {
  display: flex; justify-content: space-between;
  width: 148px; margin-top: -12px;
  position: relative; z-index: 3;
}
.guide-hand {
  width: 30px; height: 20px;
  background: #fff;
  border: 3px solid var(--primary-dark);
  border-radius: 8px 8px 4px 4px;
  transition: transform .3s cubic-bezier(.2,1.4,.4,1);
}
.guide.is-happy .guide-hand-l { transform: translateY(-4px) rotate(-12deg); }
.guide.is-happy .guide-hand-r { transform: translateY(-4px) rotate(12deg); }

/* ── Header, now that the avatar has moved into the face ──────── */

/* An offline guide is dimmer, and its lantern is out. */
.guide[data-online="0"] .guide-lamp {
  background: var(--text-muted);
  box-shadow: 0 0 0 3px var(--primary-dark);
}
.guide[data-online="0"] .guide-screen { background: #4A3A31; }

/* ── The head as the button ──────────────────────────────────────
   No pill: the head is the button. A container around it was
   competing with the face for attention.
   ---------------------------------------------------------------- */
.giya-fab {
  background: none !important;
  box-shadow: none !important;
  overflow: visible !important;
  padding: 0 !important;
  gap: 0 !important;
  width: 56px !important;
  height: 52px !important;
  border-radius: 0 !important;
}
.giya-fab:hover { background: none !important; transform: translateY(-3px); }

.fab-head-wrap {
  display: block; position: relative;
  width: 56px; height: 52px;
  filter: drop-shadow(0 6px 14px rgba(36,28,24,.42));
}
.fab-head-wrap .guide-face {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%) scale(.46);
  transform-origin: center;
  box-shadow: none;
}

/* ── The greeting ───────────────────────────────────────────────
   Right-aligned and above, so it opens into the page rather than
   off the edge of the screen.
   ---------------------------------------------------------------- */
.fab-say {
  position: absolute;
  right: 4px; bottom: calc(100% + 14px);
  width: max-content; max-width: 210px;
  padding: 9px 14px;
  background: #fff;
  border: 2px solid var(--primary-dark);
  border-radius: 14px;
  font-size: 0.75rem; font-weight: 600; line-height: 1.4;
  color: var(--primary-dark);
  text-align: left;
  opacity: 0; transform: translateY(6px) scale(.94);
  transform-origin: bottom right;
  pointer-events: none;
  transition: opacity .22s ease, transform .28s cubic-bezier(.2,1.3,.4,1);
}
.fab-say::before {
  content: ""; position: absolute;
  right: 20px; bottom: -8px;
  width: 2px; height: 8px;
  background: var(--primary-dark);
}

.giya-fab:hover .fab-say,
.giya-fab:focus-visible .fab-say { opacity: 1; transform: none; }

/* Hover expressions, driven from the button. */
.giya-fab:hover .guide-eye { height: 8px; border-radius: 15px 15px 0 0; transform: translateY(3px); }
.giya-fab:hover .guide-mouth {
  width: 26px; height: 13px; margin-left: -13px;
  border-radius: 0 0 26px 26px; bottom: 13px;
}
.giya-fab:hover .guide-blush { opacity: .85; }

.giya-fab[aria-expanded="true"] .guide-eye { transform: translateY(4px); }

/* ── The guide's face in the panel head ─────────────────────────
   The head used to float above the panel's top-left corner, which read
   as a stray graphic rather than as the assistant. It now sits in the
   head beside the name - the conversation's profile picture.
   ---------------------------------------------------------------- */
.ai-panel-head { border-radius: 16px 16px 0 0; }
.ai-panel-log:first-of-type { border-radius: 0; }

/* ── The robot head, small ──────────────────────────────────────
   The real .guide-face - ears, screen, lantern and all - scaled down
   rather than redrawn, so there is one robot in the app and it cannot
   drift out of sync. It sits on the head beside "Giya AI" and beside
   each reply, on nothing: no disc, no rounded tile behind it.

   .guide-face is 118x104 with ears at +/-12px and the lantern above,
   so the drawn shape is about 142x121. The wrappers below are sized
   to the scale rather than the other way round.
   ---------------------------------------------------------------- */
.ai-panel-mark,
.chat-bot-icon {
  position: relative; display: block; flex-shrink: 0;
  background: none; border: 0; border-radius: 0;
  padding: 0; overflow: visible;
}
.ai-panel-mark { width: 47px; height: 40px; }
.chat-bot-icon { width: 37px; height: 32px; }

.ai-panel-mark .guide-face,
.chat-bot-icon .guide-face {
  position: absolute; top: 50%; left: 50%;
  transform-origin: center;
  box-shadow: none;
}
.ai-panel-mark  .guide-face { transform: translate(-50%, -50%) scale(.33); }
.chat-bot-icon  .guide-face { transform: translate(-50%, -50%) scale(.26); }

/* The lantern's glow is sized for a 118px head and reads as a smudge
   at a third of that, so it is dialled back here. */
.ai-panel-mark .guide-lamp,
.chat-bot-icon .guide-lamp {
  box-shadow: 0 0 0 3px var(--primary-dark), 0 0 8px 1px rgba(215,169,74,.5);
}

/* The bubble would run off a narrow screen, so it stays hidden there -
   the head is self-explanatory once tapped. */
@media (max-width: 480px) {
  .fab-say { display: none; }
}

html[data-theme="dark"] .fab-say { background: #F5E9D9; }
@media (max-width: 560px) {
  .guide-face { width: 100px; height: 90px; }
  .guide-screen { width: 74px; height: 62px; }
  .guide-eye { width: 13px; height: 13px; top: 20px; }
  .guide-eye-l { left: 15px; } .guide-eye-r { right: 15px; }
  .guide-bubble { max-width: 84vw; font-size: 0.75rem; }
  .guide-hands { width: 126px; }
}

@media (prefers-reduced-motion: reduce) {
  .guide-face, .guide-eye, .guide-mouth, .guide-hand, .guide-bubble { transition: none; }
  .guide-bubble.is-in { animation: none; opacity: 1; transform: none; }
  .guide.is-thinking .guide-lamp { animation: none; }
}

html[data-theme="dark"] .guide-face,
html[data-theme="dark"] .guide-ear,
html[data-theme="dark"] .guide-hand { background: var(--surface); }
html[data-theme="dark"] .guide-bubble { background: var(--surface); color: var(--text); }
html[data-theme="dark"] .guide-bubble::after { border-top-color: var(--surface); }

/* =========================================================
   ADMIN REPORTS
========================================================= */

.report-card,
.report-section-card {
    background: var(--surface, #fff);
    border: 1px solid var(--border-light, #eee);
    border-radius: 16px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .04);
}

.report-card { overflow: hidden; margin-bottom: 18px; }

.report-section-card { overflow: hidden; }

.report-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border-light, #eee);
}

/* BUTTONS */

.report-download-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 36px;
    padding: 8px 14px;
    border: 1px solid var(--primary);
    border-radius: 8px;
    background: var(--primary);
    color: #fff;
    font-size: .72rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
}

.report-download-btn:hover { opacity: .9; color: #fff; }

.report-download-disabled {
    background: #d8d8d8;
    border-color: #d8d8d8;
    color: #888;
    cursor: not-allowed;
}

/* FILTER */

.report-filter {
    display: grid;
    grid-template-columns: 1fr 1.2fr 1.2fr auto;
    gap: 12px;
    align-items: end;
    padding: 16px 18px;
}

.report-field { min-width: 0; }

.report-field label {
    display: block;
    margin-bottom: 5px;
    font-size: .7rem;
    font-weight: 600;
    color: var(--text);
}

.report-field select,
.report-field input {
    width: 100%;
    height: 38px;
    box-sizing: border-box;
    padding: 6px 10px;
    border: 1px solid var(--border, #d9d9d9);
    border-radius: 7px;
    background: #fff;
    color: var(--text);
    font: inherit;
    font-size: .74rem;
    outline: none;
}

.report-field select:focus,
.report-field input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 2px rgba(160, 60, 45, .1);
}

.report-actions { display: flex; align-items: center; gap: 7px; }

.report-generate-btn,
.report-clear-btn {
    height: 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 0 13px;
    border-radius: 7px;
    font-size: .72rem;
    font-weight: 600;
    white-space: nowrap;
}

.report-generate-btn {
    border: 1px solid var(--primary);
    background: var(--primary);
    color: #fff;
    cursor: pointer;
}

.report-clear-btn {
    border: 1px solid var(--border, #ddd);
    background: #fff;
    color: var(--text-muted);
    text-decoration: none;
}

/* ERROR */

.report-error {
    margin: 0 18px 14px;
    padding: 9px 12px;
    border: 1px solid #f2c7c7;
    border-radius: 7px;
    background: #fff2f2;
    color: #a62929;
    font-size: .72rem;
}

/* RESULT */

.report-result-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 18px;
    border-top: 1px solid var(--border-light, #eee);
    border-bottom: 1px solid var(--border-light, #eee);
    background: #faf9f8;
}

.report-result-info { display: flex; align-items: baseline; gap: 7px; }

.report-date-range {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: .75rem;
    color: var(--text-muted);
}

/* TABLE */

.report-table-wrapper { width: 100%; overflow-x: auto; }

.report-table {
    width: 100%;
    border-collapse: collapse;
    background: #fff;
    font-size: .72rem;
}

.report-table thead { background: #f7f6f5; }

.report-table th {
    padding: 9px 12px;
    border-bottom: 1px solid var(--border-light, #eee);
    font-size: .75rem;
    font-weight: 700;
    text-align: left;
    color: var(--text-muted);
    white-space: nowrap;
}

.report-table td {
    padding: 9px 12px;
    border-bottom: 1px solid var(--border-light, #eee);
    color: var(--text);
    vertical-align: middle;
}

.report-table tbody tr:last-child td { border-bottom: none; }

.report-table tbody tr:hover { background: #fcfbfa; }

.report-small-table { min-width: 500px; }

/* PLACEHOLDER */

.report-placeholder {
    min-height: 110px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 18px;
    border-top: 1px solid var(--border-light, #eee);
}

.report-placeholder > i {
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 9px;
    background: var(--gold-bg);
    color: var(--primary);
    font-size: 1rem;
}

/* PREVIEW */

.report-preview-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
    margin-bottom: 18px;
}

/* STATUS */

.report-status {
    display: inline-block;
    padding: 3px 7px;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
}

.report-status-success { background: #e8f6ec; color: #287a40; }

.report-status-warning { background: #fff5d8; color: #886b13; }

.report-status-danger { background: #fce8e8; color: #a32f2f; }

.report-status-neutral { background: #eee; color: #666; }

/* DESTINATIONS */

.report-destination-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-light, #eee);
}

.report-destination-item:last-child { border-bottom: none; }

.report-rank {
    width: 25px;
    height: 25px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--gold-bg);
    color: var(--primary);
    font-size: .75rem;
    font-weight: 700;
}

/* RESPONSIVE */

@media (max-width: 1200px) {
    .report-filter { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 800px) {
    .report-header { flex-direction: column; align-items: stretch; }
    .report-download-btn { width: 100%; }
    .report-filter { grid-template-columns: 1fr; }
    .report-preview-grid { grid-template-columns: 1fr; }
    .report-result-header { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 600px) {
    .report-actions { flex-direction: column; }

    .report-generate-btn,
    .report-clear-btn { width: 100%; }
}

/* ════════════════════════════════════════════════════════════════
   ADMIN - Reports, GIYA dressing
   The page worked; it just looked like any admin panel. These give it
   the app's own vocabulary and make five numbers scannable at a glance
   instead of five identical gold squares.
   ════════════════════════════════════════════════════════════════ */

/* ── Generator header ─────────────────────────────────────────── */
.report-header-title { display: flex; align-items: flex-start; gap: 11px; }

.report-header-icon {
  width: 38px; height: 38px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 11px;
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: var(--gold);
  font-size: 1rem;
}

/* ── Section headings ─────────────────────────────────────────── */
.report-section-title {
  display: flex; align-items: center; gap: 8px;
  margin: 0;
  font-family: var(--font-display); font-size: .85rem; color: var(--text);
}
.report-section-title .bi {
  font-size: .8125rem; color: var(--primary);
}

/* ── The chosen report, beside its title ──────────────────────── */
.report-result-icon {
  font-size: .8125rem; color: var(--primary);
  /* The row is baseline-aligned for the text; an icon needs nudging. */
  position: relative; top: 1px;
}

/*
   ── Ranked destinations ──────────────────────────────────────────
   The top three carry the pilgrimage: giving them medal colours says
   which church the devotees actually walk to, without another column.
   -------------------------------------------------------------- */
.report-destination-item:nth-child(1) .report-rank {
  background: linear-gradient(135deg, #E8C063, #C9973A); color: #4A3410;
  box-shadow: 0 2px 6px rgba(201,151,58,.4);
}
.report-destination-item:nth-child(2) .report-rank {
  background: linear-gradient(135deg, #DCDCDC, #B4B4B4); color: #4A4A4A;
}
.report-destination-item:nth-child(3) .report-rank {
  background: linear-gradient(135deg, #D9A277, #B47A4E); color: #4A2E18;
}

/* ── Reports: alignment ──────────────────────────────────────────
   Four things the eye catches before it can name them. Measured, not
   guessed - the numbers are in the commit message.
   ---------------------------------------------------------------- */

/*
   1. The filter row did not sit on one line. .report-filter aligns to
      `end`, so the bottoms matched while the tops were 6px apart: a
      <select> came out 38px tall next to 44px date fields, and a short
      control beside two tall ones reads as a mistake.

      44px, not 38, because that is what the date fields already carry -
      a global min-height that exists so iOS does not shrink a date
      field to its content. Overriding it to match the select would
      break touch targets on a phone to tidy a desktop row. Everything
      in the row matches the tallest instead, buttons included.
*/
.report-field select,
.report-field input,
.report-generate-btn,
.report-clear-btn {
  box-sizing: border-box;
  min-height: 44px;
  line-height: 1.2;
}

/*
   2. The five summary numbers did not share a line, because the card
      centred its contents and "Paid Revenue - 73 transactions" wrapped
      to two lines while the other four labels did not.

      .stat-tile is a top-aligned column, so the figures line up for any
      label length - which matters, because these are counts and one
      more digit used to make the row ragged again.
*/

/*
   3. The result header aligned its icon on the text baseline, which put
      the icon's middle 5px below the title's. Centring the row is what
      was meant, and it removes the magic 1px nudge that was papering
      over it.
*/
.report-result-info { align-items: center; }
.report-result-icon { top: 0; }

/*
   4. The rank badge was NOT changed, having tried it.

      It centres on the whole row rather than on the church's name, and
      top-aligning it left 4px of drift that only a negative margin
      would close - fragile the moment the font size moves. Centring a
      rank against its row is a standard list pattern and it reads
      fine; the 13px offset from the name is the pattern working, not a
      mistake. Left alone deliberately.
*/

/*
   ── Reports: the empty-state icons were hugging the left edge ────
   giya-icons.css gives .bi an explicit width:1em. The empty states set
   display:block on the icon to put it above its message, which makes
   it a 1em-wide BLOCK box - and text-align centres inline content, not
   block boxes. So the cell's text centred and its icon did not.

   auto margins are what centres a block of known width. Done here
   rather than in the three inline styles so the next empty state added
   to this page is centred without anyone having to know this.
   ---------------------------------------------------------------- */
.report-table td > .bi,
.report-placeholder > .bi {
  margin-left: auto;
  margin-right: auto;
}

/* ── ADMIN REPORTS: the pieces that were written inline ──────────
   Every one of these was a style attribute on the element, which is why
   the same "small muted caption" appeared at .65rem, .67rem, .68rem and
   .69rem on one page. There are three sizes here now, not six. */
.report-card-title {
  margin: 0 0 3px;
  font-family: var(--font-display);
  font-size: 1rem; color: var(--text);
}
.report-card-sub { margin: 0; font-size: .72rem; color: var(--text-muted); }

.report-error-list { margin: 0; padding-left: 18px; }

.report-result-title { font-size: .78rem; color: var(--text); }
.report-result-count { font-size: .75rem; color: var(--text-muted); }
.report-date-range .bi { color: var(--primary); }

/* The head strip of a section card. It was the same three declarations
   three times over, once per card. */
.report-section-head {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-light, #eee);
}

/* "No records match your filters" inside a full-width report table. */
.report-empty-cell { text-align: center; padding: 30px 15px; }
.report-empty-icon { display: block; font-size: 1.5rem; color: #AAA; margin-bottom: 7px; }
.report-empty-title { display: block; font-size: .78rem; margin-bottom: 3px; }
.report-empty-sub { font-size: .75rem; color: var(--text-muted); }

/* The same idea in one of the two small preview tables, which keep a fixed
   height so the pair stays level whether or not either has rows. */
.report-blank-cell { height: 110px; text-align: center; color: var(--text-muted); }
.report-blank-icon { display: block; font-size: 1.2rem; margin-bottom: 5px; }
.report-blank-text { font-size: .75rem; }

.report-placeholder-title { display: block; font-size: .76rem; color: var(--text); margin-bottom: 2px; }
.report-placeholder-sub { margin: 0; font-size: .75rem; color: var(--text-muted); }

.report-table th.is-right,
.report-table td.is-right { text-align: right; }

.report-rating { color: #C99000; font-weight: 700; }

/* The ranked list of destinations under its own heading. */
.report-rank-list { padding: 4px 16px; }
.report-rank-name { flex: 1; font-size: .72rem; font-weight: 500; color: var(--text); }
.report-rank-count { font-size: .75rem; font-weight: 600; color: var(--primary); }

/* ════════════════════════════════════════════════════════════════
   MOBILE POLISH (chat + floating guide)
   Why each rule exists, so it can be explained and defended:
   1. Reply text keeps its line breaks through <br> tags only. The old
      "white-space: pre-line" turned every break into a second gap.
   2. Text boxes are 16px on phones. iPhone Safari zooms the whole page
      in on any field smaller than that, which is what made the chat
      feel cramped once the keyboard opened.
   3. On a phone the robot avatar beside every reply cost ~50px of a
      390px screen. The header already says who is talking, so replies
      take the full width instead.
   4. The conversation scrolls inside its own box, sized to the screen,
      so the message box stays in view after a long answer.
   5. The floating guide is smaller on phones and pages get room at the
      bottom, so it never sits on top of text or buttons.
   ════════════════════════════════════════════════════════════════ */

.chat-bubble-bot em { font-style: italic; color: var(--text-muted); }

@media (max-width: 600px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
  textarea,
  select,
  .chat-composer input { font-size: 16px; }

  .chat-bar { padding: 12px 14px; }

  .chat-log {
    padding: 14px 12px;
    gap: 12px;
    min-height: 280px;
    max-height: calc(100svh - 250px);
  }

  .chat-row { gap: 8px; }

  .chat-bubble {
    font-size: 0.9375rem;      /* 15px: readable without zooming */
    line-height: 1.6;
    padding: 11px 14px;
  }
  .chat-bubble-bot  { max-width: calc(100% - 46px); border-top-left-radius: 6px; }
  .chat-bubble-user { max-width: 85%;  border-top-right-radius: 6px; }

  .chat-composer { padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); }
  .chat-composer input { padding: 11px 16px; }

  .chat-starters { padding: 0 12px 12px; }

  /* Floating guide: smaller, and never covering content. */
  /* Not on an itinerary: there it sits above the bottom sheet (rule near
     .active-layout), or it would cover "Mark Current Visited". */
  body:not(:has(.active-layout)) .giya-fab { right: 12px !important; bottom: calc(12px + env(safe-area-inset-bottom)) !important; }
  body:has(.active-layout) .giya-fab { right: 12px !important; }
  .giya-fab,
  .fab-head-wrap { width: 46px !important; height: 43px !important; }
  .fab-head-wrap .guide-face { transform: translate(-50%, -50%) scale(.38); }
  main { padding-bottom: 72px; }
}

/* ════════════════════════════════════════════════════════════════
   WELCOME SLIDES (first visit)
   Phone: full screen, as designed. Computer: the same slides in a
   phone-width card in the middle of the page.
   ════════════════════════════════════════════════════════════════ */
.onb-page {
  margin: 0; min-height: 100vh; min-height: 100svh;
  background: var(--bg); color: var(--text);
  font-family: var(--font-body);
  display: flex; align-items: center; justify-content: center;
}
.onb {
  position: relative;
  width: 100%; max-width: 430px;
  height: 100vh; height: 100svh; max-height: 900px;
  display: flex; flex-direction: column;
  background: var(--bg);
}
.onb-skip {
  position: absolute; z-index: 2;
  top: calc(18px + env(safe-area-inset-top)); right: 22px;
  padding: 10px 6px;                      /* 44px tall touch target */
  font-size: 0.875rem; font-weight: 700;
  color: var(--text-muted); text-decoration: none;
}
.onb-skip:hover { color: var(--primary); }

.onb-track {
  flex: 1; display: flex;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  outline: none;
}
.onb-track::-webkit-scrollbar { display: none; }

.onb-slide {
  flex: 0 0 100%;
  scroll-snap-align: center; scroll-snap-stop: always;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0 32px 40px; box-sizing: border-box; text-align: center;
}
.onb-icon {
  width: 78px; height: 78px; margin-bottom: 26px;
  display: flex; align-items: center; justify-content: center;
}
.onb-icon img { width: 77px; height: 78px; }
.onb-title {
  font-family: var(--font-display); font-weight: 400;
  font-size: 1.625rem; line-height: 1.25; margin: 0 0 10px; color: var(--text);
}
.onb-text {
  max-width: 300px; margin: 0;
  font-size: 0.875rem; line-height: 1.7; color: var(--text-muted);
}

.onb-foot {
  padding: 0 24px calc(28px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: center;
}
.onb-dots { display: flex; gap: 6px; margin-bottom: 28px; }
.onb-dot {
  width: 10px; height: 10px; padding: 0; border: 0; border-radius: 999px;
  background: #EBD5A9; cursor: pointer;
  transition: width .2s ease, background .2s ease;
}
.onb-dot.is-on { width: 26px; background: var(--primary); }
.onb-dot:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.onb-btn {
  width: 100%; height: 56px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  border-radius: 12px; background: var(--primary); color: #fff;
  font-size: 1rem; font-weight: 700; text-decoration: none;
  transition: background .15s ease, transform .1s ease;
}
.onb-btn:hover { background: var(--primary-dark); color: #fff; }
.onb-btn:active { transform: scale(.985); }
.onb-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

.onb-login {
  margin-top: 14px; padding: 6px;
  font-size: 0.8125rem; font-weight: 700; color: var(--primary); text-decoration: none;
}
/* With script, "I already have an account" appears on the last slide only,
   keeping its space so the button does not jump. */
.onb-js .onb-login { visibility: hidden; }
.onb-js .onb-login.is-shown { visibility: visible; }

/* A computer: the same slides in a phone-width card. */
@media (min-width: 640px) {
  .onb-page { background: var(--bg-alt, #F6EBDD); padding: 24px; box-sizing: border-box; }
  .onb {
    height: min(820px, calc(100vh - 48px));
    border-radius: 24px; overflow: hidden;
    box-shadow: 0 24px 60px rgba(36,28,24,.16);
  }
}

@media (prefers-reduced-motion: reduce) {
  .onb-track { scroll-behavior: auto; }
  .onb-dot, .onb-btn { transition: none; }
}

/* Admin destinations: the coordinates glow briefly after the pin moves. */
@keyframes dm-coords-updated { from { background: #FCEFD2; border-color: var(--gold); } to { background: #fff; } }
.giya-input.is-updated { animation: dm-coords-updated 1.6s ease-out; }
@media (prefers-reduced-motion: reduce) { .giya-input.is-updated { animation: none; } }

/* ════════════════════════════════════════════════════════════════
   BACK BUTTON - one design on every page (components/back-link)
   Rule: every page below Home has one, top-left, pointing to the
   page it belongs to (its parent), always labelled "Back to <page>"
   so the visitor knows where it goes before pressing it.
   ════════════════════════════════════════════════════════════════ */
.giya-back {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 44px;                     /* thumb-sized target */
  margin: 0 0 14px;
  color: var(--text-muted); text-decoration: none;
  font-size: 0.875rem; font-weight: 700;
  -webkit-tap-highlight-color: transparent;
}
.giya-back-icon {
  width: 36px; height: 36px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: #fff; border: 1px solid var(--border);
  color: var(--primary); font-size: 0.9375rem;
  transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
.giya-back:hover { color: var(--primary); }
.giya-back:hover .giya-back-icon { border-color: var(--gold); transform: translateX(-2px); }
.giya-back:focus-visible { outline: none; }
.giya-back:focus-visible .giya-back-icon { outline: 3px solid var(--gold); outline-offset: 2px; }

/* On a photo or a dark bar. */
.giya-back.is-dark { color: rgba(255,255,255,.88); }
.giya-back.is-dark .giya-back-icon {
  background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.5); color: #fff;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.giya-back.is-dark:hover { color: #fff; }
.giya-back.is-dark:hover .giya-back-icon { background: rgba(255,255,255,.24); }

/* Chevron only; the label stays for screen readers. */
.giya-back.is-compact { margin: 0; gap: 0; }
.giya-back.is-compact .giya-back-label,
.active-layout .ap-back .giya-back-label {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Where it sits on particular pages. */
.church-hero-top .giya-back { margin: 0; }
.auth-header .auth-back { position: relative; z-index: 1; margin: -12px 0 10px -4px; }
.admin-topbar .admin-back { margin-right: 12px; }
.admin-topbar .admin-back + .admin-topbar-titles { flex: 1; }
.ap-back.giya-back { margin-bottom: 8px; }
.ap-back.giya-back .giya-back-icon { width: 32px; height: 32px; }
@media (max-width: 768px) {
  .active-layout .ap-back.giya-back {
    position: absolute; left: 0; top: 0; width: 30px; height: 30px; min-height: 0; margin: 0;
  }
  .active-layout .ap-back.giya-back .giya-back-icon { width: 30px; height: 30px; font-size: .8125rem; }
  .active-layout .ap-head-row { padding-left: 0; }
}

html[data-theme="dark"] .giya-back:not(.is-dark) .giya-back-icon { background: var(--surface); }
@media (prefers-reduced-motion: reduce) { .giya-back-icon { transition: none; } }

/* ════════════════════════════════════════════════════════════════
   BIG PHONES FIRST (6" phones and Pro Max: 390-440px wide)
   Checked page by page at 360, 390, 412, 430 and 440px.
   ════════════════════════════════════════════════════════════════ */

/* Home, upcoming activities: a long type ("PROCESSION") ran out of its
   tile. A little wider and tighter, and it may break if it must. */
.event-date { width: 64px; }
.event-kind { font-size: .5rem; letter-spacing: -.01em; overflow-wrap: anywhere; }

@media (max-width: 768px) {
  /* ── Admin: the menu is a drawer, not a screen-filling strip ──── */
  .admin-sidebar {
    position: fixed !important; top: 0; left: 0; bottom: 0;
    width: min(84vw, 300px) !important; height: 100dvh !important;
    z-index: 1600;
    transform: translateX(-102%);
    transition: transform .24s cubic-bezier(.2, .8, .2, 1);
    box-shadow: 0 0 40px rgba(0,0,0,.35);
  }
  .admin-layout.is-drawer-open .admin-sidebar { transform: none; }
  .admin-scrim {
    position: fixed; inset: 0; z-index: 1590;
    background: rgba(20,14,12,.45);
  }
  .admin-menu-btn {
    width: 40px; height: 40px; flex-shrink: 0; margin-right: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--border); border-radius: 12px;
    background: #fff; color: var(--primary); font-size: 1.25rem; cursor: pointer;
  }
  .admin-topbar {
    position: sticky; top: 0; z-index: 900;
    height: auto; min-height: 60px; padding: 8px 14px !important; gap: 0;
  }
  .admin-topbar .admin-back { margin-right: 8px; }
  .admin-topbar-titles { flex: 1; min-width: 0; }
  .admin-page-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* Wide admin tables scroll sideways inside their card, not the page. */
  .admin-content .table-wrap, .admin-content .adm-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
@media (min-width: 769px) {
  .admin-menu-btn, .admin-scrim { display: none !important; }
}
@media (prefers-reduced-motion: reduce) { .admin-sidebar { transition: none; } }

@media (max-width: 767px) {
}

@media (max-width: 600px) {
  /* ── Visita Iglesia: buttons on their own line under the church ── */
  .vi-row { flex-wrap: wrap; row-gap: 10px; }
  .vi-row .vi-info { flex: 1 1 calc(100% - 56px); }
  .vi-row .vi-actions { flex: 1 1 100%; padding-left: 52px; }
  .vi-row .vi-move { flex-direction: row !important; gap: 4px !important; }
  .vi-row .route-btn {
    width: 36px; height: 36px; border-radius: 10px;
    background: #fff; border: 1px solid var(--border);
  }
  .vi-row .vi-mark { flex: 1; min-height: 38px; }
  .vi-row .vi-drop { width: 38px !important; height: 38px !important; border-radius: 10px !important; }

  /* ── Premium: no empty badge row above a card that has no badge ── */
  .plan-badge-row.is-empty { display: none !important; }

  /* ── Profile: the tab strip shows there is more to the right ──── */
  .profile-tabs-strip {
    padding: 0 8px !important; scrollbar-width: none;
    -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent);
            mask-image: linear-gradient(90deg, #000 88%, transparent);
  }
  .profile-tabs-strip::-webkit-scrollbar { display: none; }
  .profile-tabs-strip .profile-tab { padding: 14px 12px; white-space: nowrap; flex-shrink: 0; }
}

/* ════════════════════════════════════════════════════════════════
   GIYA AI PAGE - the guide's message is a message box
   Above the robot's head: a rectangle (square corners) with a pointed
   tail angled down to the robot, instead of a rounded oval.
   ════════════════════════════════════════════════════════════════ */
.chat-page .guide-bubble {
  border-radius: 6px;
  padding: 10px 14px;
  margin-bottom: 24px;
}
.chat-page .guide-bubble::before,
.chat-page .guide-bubble::after { transform: none; border-left: 0; }
.chat-page .guide-bubble::before {
  left: calc(50% + 6px); bottom: -22px;
  border-top: 22px solid var(--primary); border-right: 20px solid transparent;
}
.chat-page .guide-bubble::after {
  left: calc(50% + 8.5px); bottom: -16px;
  border-top: 16px solid #fff; border-right: 15px solid transparent;
}
html[data-theme="dark"] .chat-page .guide-bubble::after { border-top-color: var(--surface); }

/* The small robot beside each reply has its feet too (drawn here, so every
   reply - saved, new, and in the floating panel - gets them). */
.chat-bot-icon { height: 38px; }
.chat-bot-icon .guide-face { top: 42%; }
.chat-bot-icon::before,
.chat-bot-icon::after {
  content: ""; position: absolute; bottom: 0;
  width: 9px; height: 6px; box-sizing: border-box;
  background: #fff; border: 1.5px solid var(--primary-dark);
  border-radius: 3px 3px 2px 2px;
}
.chat-bot-icon::before { left: 4px; }
.chat-bot-icon::after  { right: 4px; }

/* ════════════════════════════════════════════════════════════════
   GIYA AI PAGE - intro, then conversation
   Intro: the guide with its message, the FAQ chips and the text box.
   Chatting: header (small robot + name), conversation, chips, text box.
   ════════════════════════════════════════════════════════════════ */
.chat-page.is-intro .chat-bar,
.chat-page.is-intro .chat-log { display: none !important; }
.chat-page.is-intro .chat-starters { padding-top: 14px; }
.chat-page.is-chatting #guide { display: none !important; }
.chat-page.is-chatting .chat-bar { padding-top: 12px; }

/* Phones: the intro centres the guide in the space above the chips. */
@media (max-width: 767px) {
  .chat-page.is-intro #guide {
    flex: 1 1 auto !important; justify-content: center; margin: 0 auto;
  }
  .chat-page.is-intro .chat-shell { flex: 0 0 auto !important; }
}

/* The small robot beside "Giya AI Assistant". It carries the guide's
   expression classes, so it must not take the big guide's layout or
   movement. */
.chat-bar { justify-content: flex-start; }
.chat-bar-title { flex: 1; min-width: 0; }
.chat-bar > form { margin-left: auto; }
.chat-bar .chat-bar-guide.guide {
  display: block; position: relative; margin: 0 10px 0 0; width: 37px;
}
.chat-bar .chat-bar-guide.guide .guide-face { transform: translate(-50%, -50%) scale(.26) !important; }

/* ── Footer: developer credits (Home page) ─────────────────────── */
.footer-credits {
  border-top: 1px solid rgba(255,255,255,0.08);
  padding-top: 24px; margin-bottom: 24px;
}
.footer-credits-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px 24px;
}
.footer-credit-name {
  display: block; color: rgba(255,255,255,0.82);
  font-size: 0.875rem; font-weight: 700; line-height: 1.35;
}
.footer-credit-role {
  display: block; margin-top: 3px;
  color: rgba(255,255,255,0.45); font-size: 0.75rem;
}
@media (max-width: 768px) {
  .footer-credits-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 18px; }
}

/* ── Admin schedules: how an event repeats ─────────────────────── */
.sched-repeat .field[hidden], .sched-repeat .dm-row[hidden] { display: none !important; }
.sched-days { display: flex; flex-wrap: wrap; gap: 6px; }
.sched-day { position: relative; cursor: pointer; }
.sched-day input { position: absolute; opacity: 0; pointer-events: none; }
.sched-day span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 48px; height: 38px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-secondary, #FBF0E0); color: var(--text);
  font-size: .8125rem; font-weight: 600;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.sched-day input:checked + span { background: var(--primary); border-color: var(--primary); color: #fff; }
.sched-day input:focus-visible + span { outline: 3px solid var(--gold); outline-offset: 2px; }
.sched-next {
  margin: 2px 0 12px; padding: 10px 12px;
  border-radius: 10px; background: rgba(215,169,74,.14);
  font-size: .8125rem; line-height: 1.5; color: var(--text);
}
.sched-next span { color: var(--text-muted); }

/* ════════════════════════════════════════════════════════════════
   ADMIN FILTER ROWS (Schedules, Users)
   Every field and both buttons share one height (45px) and one bottom
   edge. Before, the buttons were 50px tall and dropped onto their own
   row, left-aligned, so they lined up with nothing.
   ════════════════════════════════════════════════════════════════ */
.sm-filters, .um-filters { display: grid; gap: 14px 16px; align-items: end; }
.sm-filters { grid-template-columns: repeat(3, minmax(0, 1fr)) auto; }
.um-filters { grid-template-columns: minmax(0, 1.3fr) minmax(0, .8fr) minmax(0, 1.5fr); }
.sm-filters > .field, .um-filters > .field { margin: 0; min-width: 0; }

.sm-filters .giya-input, .um-filters .giya-input,
.um-filters .sm-search,
.sm-filters .adm-actions .btn, .um-filters .adm-actions .btn {
  height: 45px; box-sizing: border-box;
}
.sm-filters .adm-actions .btn, .um-filters .adm-actions .btn {
  padding-top: 0; padding-bottom: 0; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}

/* Users: three filters, then the buttons on the right of the next line. */
.um-filters .adm-actions { grid-column: 1 / -1; justify-content: flex-end; }

/* Not enough room for five things in one line: same as Users. */
@media (max-width: 1100px) {
  .sm-filters { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .sm-filters .adm-actions { grid-column: 1 / -1; justify-content: flex-end; }
}
@media (min-width: 1500px) {
  .um-filters { grid-template-columns: minmax(0, 1.3fr) minmax(0, .8fr) minmax(0, 1.5fr) auto; }
  .um-filters .adm-actions { grid-column: auto; }
}

/* Phones: one column; the two buttons share the last row equally. */
@media (max-width: 640px) {
  .sm-filters, .um-filters { grid-template-columns: minmax(0, 1fr); }
  .sm-filters .adm-actions, .um-filters .adm-actions { grid-column: 1 / -1; justify-content: stretch; }
}
.um-filters .sm-search { min-height: 0; }
.sched-town-tag {
  display: block; width: fit-content; margin-top: 3px;
  padding: 1px 7px; border-radius: 999px;
  background: rgba(215,169,74,.18); color: #7a5c00;
  font-size: .75rem; font-weight: 700; text-transform: none; letter-spacing: .01em;
}
/* Schedules: "Yearly - 3rd Sunday of January (Jan 17, 2027)" needs room. */
.sm-table td:nth-child(5) { min-width: 170px; }

/* Map list: a published church with no pin yet, and the "show all" line under Major. */
.mx-tag.is-nopin { background: #F3E3D3; color: #8E3B2F; }
.mx-more { margin: 10px 4px 4px; font-size: .85rem; color: var(--text-muted, #6b5b50); text-align: center; }
.mx-more-btn { background: none; border: 0; padding: 0; color: var(--primary, #8E3B2F); font-weight: 700; text-decoration: underline; cursor: pointer; }

/* The church the devotee tapped on the map, marked in the list. */
.mx-row.is-focus { box-shadow: 0 0 0 2px var(--gold, #D7A94A); }

/* ══ Map on phones: full-screen map, floating search, bottom sheet ═══════
   Google Maps shape. The map fills the screen under the navbar; search and
   filters float at the top; the church list is a sheet that slides up from
   the bottom (peek / half / full); tapping a church turns the sheet into
   its card with Directions, See details and Add to route. */
.mx-grab { display: none; }
.mx-card[hidden] { display: none; }

@media (max-width: 768px) {
  body[data-page="map"] { overflow: hidden; overscroll-behavior: none; }
  body[data-page="map"] .giya-footer,
  body[data-page="map"] .giya-fab { display: none !important; }

  body[data-page="map"] main > div { padding: 0 !important; max-width: none !important; }

  body[data-page="map"] .map-grid {
    position: fixed; z-index: 5;
    top: var(--nav-height, 56px); left: 0; right: 0; bottom: 0;
    display: block; margin: 0; gap: 0;
  }
  body[data-page="map"] .giya-map-shell { position: absolute; inset: 0; order: 0; }
  body[data-page="map"] .giya-map-canvas { height: 100% !important; border-radius: 0; border: 0; }
  body[data-page="map"] .giya-loading { border-radius: 0; }

  /* Search and filters, floating on the map. */
  body[data-page="map"] .mx-float {
    position: absolute; z-index: 1001; top: 10px; left: 12px; right: 12px;
    pointer-events: none;                 /* the map stays draggable between chips */
  }
  body[data-page="map"] .mx-float > * { pointer-events: auto; }
  body[data-page="map"] .mx-float .mx-controls { padding: 0; background: none; border: 0; }
  body[data-page="map"] .mx-float .mx-title { display: none; }
  body[data-page="map"] .mx-float .mx-search-field {
    background: #fff; border: 0; min-height: 48px; margin-bottom: 8px;
    box-shadow: 0 2px 10px rgba(36,28,24,.22);
  }
  /* 16px exactly, never rem: iPhone Safari zooms the whole page when a text
     box under 16px gets focus, and with the "Small" text setting 1rem is
     14px - so the page zoomed in as soon as the keyboard opened. */
  body[data-page="map"] .mx-float .mx-search-field input { font-size: 16px !important; }

  /* The chips scroll edge to edge, like Google Maps: the row reaches both
     sides of the screen, so no chip is cut off short of the edge with a
     strip of map beside it. The first chip still starts in line with the
     search bar. */
  body[data-page="map"] .mx-float .mx-chips {
    flex-wrap: nowrap; overflow-x: auto;
    margin: 0 -12px; padding: 0 12px 4px;
    scroll-padding: 0 12px;
    -webkit-overflow-scrolling: touch;
  }
  body[data-page="map"] .mx-float .cat-chip:not(.is-active) {
    background: #fff; box-shadow: 0 1px 5px rgba(36,28,24,.2); border-color: transparent;
  }
  body[data-page="map"] .mx-float .map-note { margin: 6px 56px 0 0; background: #FDF1EF; box-shadow: 0 2px 8px rgba(36,28,24,.18); }

  /* Map buttons sit under the floating bar; full screen is the default here. */
  body[data-page="map"] .map-tools { top: 118px; right: 12px; }
  body[data-page="map"] #btnFullscreen { display: none; }

  /* The sheet. */
  body[data-page="map"] .map-sidebar {
    position: absolute; z-index: 1002; left: 0; right: 0; bottom: 0;   /* over Leaflet's controls (1000) */
    height: 148px; max-height: none; margin: 0;
    display: flex; flex-direction: column;
    border-radius: 18px 18px 0 0; border: 0;
    box-shadow: 0 -4px 18px rgba(36,28,24,.18);
    padding-bottom: env(safe-area-inset-bottom);
    transition: height .26s cubic-bezier(.2,.8,.2,1);
    order: 0;
  }
  body[data-page="map"] .map-sidebar.is-dragging { transition: none; }
  body[data-page="map"] .mx-grab {
    display: block; flex: 0 0 auto; width: 100%; padding: 9px 0 5px;
    background: none; border: 0; cursor: grab; touch-action: none;
  }
  body[data-page="map"] .mx-grab span {
    display: block; width: 40px; height: 5px; margin: 0 auto;
    border-radius: 3px; background: #D5C8BC;
  }
  body[data-page="map"] .mx-list-head { touch-action: none; padding-top: 2px; }
  body[data-page="map"] .mx-list { flex: 1 1 auto; min-height: 0; max-height: none; overscroll-behavior: contain; }

  /* The page heading only rides in the sheet when it carries the way back
     to the Visita Iglesia planner; otherwise the map speaks for itself. */
  body[data-page="map"] .map-sidebar .mx-head { display: none; padding: 0 14px; margin: 0; }
  body[data-page="map"] .map-sidebar .mx-head:has(#planBack) { display: block; }
  body[data-page="map"] .map-sidebar .mx-head :is(h1, p, .eyebrow) { display: none; }

  /* The tray as a cart: one line at the top of the sheet - count, distance,
     Start. Tap the line to see (and drop) the stops. */
  body[data-page="map"] .map-sidebar .mx-tray {
    order: 0; margin: 0 12px 8px; padding: 9px 10px 9px 14px;
    border: 1px solid var(--border); border-radius: 14px;
    display: grid; grid-template-columns: 1fr auto; align-items: center; column-gap: 10px;
  }
  body[data-page="map"] .mx-tray .mx-tray-head {
    margin: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px; cursor: pointer;
  }
  body[data-page="map"] .mx-tray .mx-tray-head::after {
    content: "\25BE"; font-size: .7rem; color: var(--text-muted);
  }
  body[data-page="map"] .mx-tray.is-open .mx-tray-head::after { content: "\25B4"; }
  body[data-page="map"] .mx-tray .mx-tray-list,
  body[data-page="map"] .mx-tray .route-mode { display: none; grid-column: 1 / -1; }
  body[data-page="map"] .mx-tray.is-open .mx-tray-list { display: block; margin-top: 8px; }
  body[data-page="map"] .mx-tray.is-open .route-mode { display: block; }
  body[data-page="map"] .mx-tray .mx-tray-actions { grid-row: 1; grid-column: 2; gap: 6px; }
  body[data-page="map"] .mx-tray .mx-plan { padding: 9px 14px; font-size: .8125rem; white-space: nowrap; }

  /* The church card, in place of the list. */
  body[data-page="map"] .map-sidebar.is-card .mx-list-head,
  body[data-page="map"] .map-sidebar.is-card .mx-list { display: none !important; }
  body[data-page="map"] .mx-card { position: relative; padding: 2px 16px 14px; touch-action: none; }
  .mx-card-close {
    position: absolute; top: 0; right: 12px; width: 34px; height: 34px;
    border-radius: 50%; border: 0; background: var(--bg-secondary, #F3ECE4); color: #241C18;
  }
  .mx-card-top { display: flex; gap: 12px; align-items: flex-start; padding-right: 40px; }
  .mx-card-photo { width: 76px; height: 76px; flex: 0 0 76px; border-radius: 12px; object-fit: cover; background: #F5E9D9; }
  .mx-card-name { font-family: var(--font-display); font-size: 1.1rem; line-height: 1.25; margin: 0 0 4px; color: #241C18; }
  .mx-card-meta { font-size: .8125rem; color: var(--text-muted); margin: 0 0 3px; }
  .mx-card-actions {
    display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin-top: 14px;
  }
  .mx-card-actions .btn {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    padding: 9px 4px; border-radius: 14px; font-size: .75rem; font-weight: 700; line-height: 1.15; text-align: center;
  }
  .mx-card-actions .btn i { font-size: 1.05rem; }
  .mx-card-actions .btn.is-on { background: var(--gold-bg, #F6EBD7); }
}
html[data-theme="dark"] .mx-card-name { color: #F3ECE4; }

/* ══ Trip details pop-up: asked when the devotee presses Start pilgrimage ══ */
.plan-modal {
  position: fixed; inset: 0; z-index: 2100;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(36,28,24,.45);
}
.plan-modal-box {
  width: min(440px, 100%); max-height: calc(100dvh - 32px); overflow-y: auto;
  background: var(--surface, #fff); border-radius: 18px; padding: 20px 18px 16px;
  box-shadow: 0 18px 50px rgba(36,28,24,.35);
}
.plan-modal-title { font-family: var(--font-display); font-size: 1.25rem; margin: 0 0 2px; color: var(--text); }
.plan-modal-sub { font-size: .8125rem; color: var(--text-muted); margin: 0 0 14px; }
.plan-modal-form { display: flex; flex-direction: column; gap: 12px; }
.plan-modal-form .plan-field { display: flex; flex-direction: column; gap: 5px; }
.plan-modal-form .giya-input { font-size: 16px; }      /* no iPhone zoom on focus */
.plan-modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 4px; }
.plan-modal-actions .btn-primary { flex: 1; justify-content: center; }
.plan-modal-limit { font-size: .75rem; color: var(--danger, #B42318); margin: 0; }
body.has-plan-modal { overflow: hidden; }
@media (max-width: 768px) {
  /* A sheet from the bottom on phones, above the keyboard's reach. */
  .plan-modal { align-items: flex-end; padding: 0; }
  .plan-modal-box { width: 100%; border-radius: 20px 20px 0 0; padding-bottom: calc(16px + env(safe-area-inset-bottom)); }
}

/* ══ Active pilgrimage: distance pill fades; notes in a bar along the top ══ */
.ap-summary { transition: opacity .4s ease; }
.ap-summary:empty { display: none; }
.ap-summary.is-quiet { opacity: 0; pointer-events: none; }

.ap-notice {
  position: absolute; z-index: 650; top: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px 8px 12px;
  background: #FFF4E5; color: #6B3A10;
  border-bottom: 1px solid #F1D7B3;
  font-size: .75rem; line-height: 1.35;
  box-shadow: 0 2px 6px rgba(36,28,24,.12);
}
.ap-notice[hidden] { display: none !important; }
.ap-notice span { flex: 1; min-width: 0; }
.ap-notice-close { border: 0; background: none; color: inherit; font-size: 1.1rem; line-height: 1; padding: 2px 4px; }
/* The legend and the map buttons move down below the bar while it shows. */
.active-layout.has-notice .ap-legend,
.active-layout.has-notice .map-tools { top: calc(var(--ap-notice-h, 48px) + 8px); }

/* While "You have arrived" is showing, the Giya AI button steps aside so it
   never covers "Not yet" (the undo). */
body:has(#arriveToast.is-open) .giya-fab { opacity: 0; pointer-events: none; }

/* ══ Rate your pilgrimage ══ */
.rate-box { width: min(480px, 100%); }
.rate-done-icon { color: #3E8E5A; }
.rate-ask { font-weight: 700; font-size: .875rem; margin: 0 0 8px; color: var(--text); }
.rate-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }
.rate-item { padding: 10px 12px; border: 1px solid var(--border); border-radius: 14px; }
.rate-name { font-weight: 700; font-size: .875rem; margin: 0 0 4px; color: var(--text); }
.rate-stars { display: flex; gap: 4px; margin-bottom: 6px; }
.rate-star {
  border: 0; background: none; padding: 0 2px; cursor: pointer;
  font-size: 1.75rem; line-height: 1; color: #D9CFC4;
}
.rate-star.is-on { color: #D7A94A; }
.rate-comment { width: 100%; font-size: 16px; min-height: 40px; resize: vertical; }
.rate-error { color: var(--danger, #B42318); font-size: .8125rem; margin: 0 0 8px; }
.rate-thanks { text-align: center; padding: 8px 0 0; }
.rate-thanks .rate-done-icon { font-size: 2.5rem; }
.rate-thanks p { margin: 8px 0 16px; color: var(--text); }

/* ══ Plan: the trip being walked, two ways to plan, one list of trips ══ */
.hub-active {
  display: flex; align-items: center; gap: 14px;
  margin: 0 0 22px; padding: 16px 18px;
  border-radius: 18px; text-decoration: none; color: #fff;
  background: linear-gradient(135deg, var(--primary, #8E3B2F), var(--primary-dark, #6B2416));
  box-shadow: 0 10px 26px rgba(142,59,47,.28);
}
.hub-active:hover { color: #fff; }
.hub-active-icon {
  width: 46px; height: 46px; flex: 0 0 46px; border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.16); font-size: 1.35rem; color: var(--gold, #D7A94A);
}
.hub-active-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.hub-active-eyebrow { font-size: .75rem; font-weight: 800; letter-spacing: .01em; text-transform: none; color: var(--gold, #D7A94A); }
.hub-active-name { font-family: var(--font-display); font-size: 1.15rem; line-height: 1.25; }
.hub-active-meta { font-size: .8125rem; opacity: .88; }
.hub-active-cta { background: var(--gold, #D7A94A) !important; border-color: var(--gold, #D7A94A) !important; color: #241C18 !important; white-space: nowrap; }
@media (max-width: 560px) {
  .hub-active { flex-wrap: wrap; }
  .hub-active-cta { width: 100%; justify-content: center; }
}
.plan-grid.is-two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 640px) { .plan-grid.is-two { grid-template-columns: 1fr; } }
.history-row { position: relative; }
.history-row .history-item { margin-bottom: 0; }
.history-row { margin-bottom: 10px; }
.history-rate {
  display: inline-flex; align-items: center; gap: 5px; margin: 6px 0 0 62px;
  font-size: .75rem; font-weight: 700; color: var(--primary, #8E3B2F); text-decoration: none;
}
.history-rate i { color: var(--gold, #D7A94A); }
.hub-manage { display: inline-flex; align-items: center; gap: 4px; margin-top: 4px; font-size: .8125rem; font-weight: 700; color: var(--primary, #8E3B2F); text-decoration: none; }

/* ══ Plan: one button to Saved Itineraries; Saved Itineraries in three groups ══ */
.hub-saved {
  display: flex; align-items: center; gap: 14px; margin: 24px 0 8px; padding: 16px 18px;
  border-radius: 18px; background: #fff; border: 1px solid var(--border-light);
  box-shadow: var(--shadow-sm); text-decoration: none; color: var(--text);
}
.hub-saved:hover { border-color: var(--gold, #D7A94A); color: var(--text); }
.hub-saved-icon {
  width: 44px; height: 44px; flex: 0 0 44px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  background: var(--gold-bg, #F6EBD7); color: var(--primary, #8E3B2F); font-size: 1.2rem;
}
.hub-saved-body { flex: 1; display: flex; flex-direction: column; }
.hub-saved-title { font-weight: 700; font-size: .9375rem; }
.hub-saved-meta { font-size: .8125rem; color: var(--text-muted); }
.hub-bottom.is-tips { grid-template-columns: 1fr; }
.hub-bottom.is-tips > div { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 14px; }
.hub-bottom.is-tips > div > h2 { grid-column: 1 / -1; }
@media (max-width: 640px) { .hub-bottom.is-tips > div { grid-template-columns: 1fr; } }

.saved-heading { font-family: var(--font-display); font-size: 1.15rem; margin: 6px 0 12px; display: flex; align-items: center; gap: 8px; }
.saved-heading span { font-family: var(--font-body); font-size: .75rem; font-weight: 700; padding: 2px 9px; border-radius: 999px; background: var(--gold-bg, #F6EBD7); color: var(--primary, #8E3B2F); }
.saved-stars { margin: 0 0 12px; font-size: .8125rem; font-weight: 700; color: var(--text); }
.saved-stars i { color: var(--gold, #D7A94A); }
article.card.is-highlight { box-shadow: 0 0 0 3px var(--gold, #D7A94A), var(--shadow-sm); }

/* Saved Itineraries: cards in a row are the same height and their buttons
   sit on one line, however many stops or ratings each card shows. */
.itin-grid { align-items: stretch; }
.saved-card { display: flex; flex-direction: column; height: 100%; }
.saved-card-body { padding: 20px; flex: 1; display: flex; flex-direction: column; }
.saved-card-actions { margin-top: auto; padding-top: 14px; border-top: 1px solid var(--border-light); }
.saved-card .saved-stars { margin-top: auto; }
.saved-card .saved-stars + .saved-card-actions { margin-top: 0; }
.saved-card .saved-stars { min-height: 38px; display: flex; align-items: center; }

/* Giya is catching its breath: the wait, then "Ask again". */
.chat-again {
  display: inline-flex; align-items: center; margin-top: 10px; padding: 7px 14px;
  border-radius: 999px; border: 1px solid var(--primary, #8E3B2F);
  background: var(--primary, #8E3B2F); color: #fff; font-weight: 700; font-size: .8125rem;
  cursor: pointer;
}
.chat-again:disabled { background: transparent; color: var(--text-muted, #7A6A5E); border-color: var(--border, #E5D9CC); cursor: default; }

/* ══ Dark mode: surfaces added after the first dark pass ═════════
   Found by scanning every page in dark mode for light backgrounds
   (QA, Nov 2026). Each was a hardcoded light colour. */
html[data-theme="dark"] .hub-saved { background: var(--surface); border-color: var(--border); }
html[data-theme="dark"] .hub-saved:hover { border-color: var(--gold); }
html[data-theme="dark"] .ap-notice { background: #3A2A1C; color: #F0D9B5; border-bottom-color: rgba(215,169,74,.25); }
html[data-theme="dark"] .fab-say { background: var(--surface-2); color: var(--text); border-color: var(--gold); }
html[data-theme="dark"] .mx-tag.is-open { background: rgba(74,180,110,.16); color: #8FD9A8; }
html[data-theme="dark"] .map-note.is-error { background: rgba(212,24,61,.16); color: #F3A6B1; border-color: rgba(212,24,61,.35); }
html[data-theme="dark"] .leaflet-control-attribution { background: rgba(23,17,14,.75) !important; color: var(--text-muted); }
html[data-theme="dark"] .leaflet-control-attribution a { color: var(--gold); }
@media (max-width: 767px) {
  html[data-theme="dark"] body[data-page="map"] .mx-float .mx-search-field,
  html[data-theme="dark"] body[data-page="map"] .mx-float .cat-chip:not(.is-active) { background: var(--surface); color: var(--text); }
  html[data-theme="dark"] body[data-page="map"] .mx-float .map-note { background: var(--surface-2); color: var(--text); }
}
@media (max-width: 600px) {
  html[data-theme="dark"] .vi-row .route-btn { background: var(--surface-2); color: var(--text); }
}
html[data-theme="dark"] .mx-card-name,
html[data-theme="dark"] .mx-card-close { color: var(--text); }
html[data-theme="dark"] .mx-card-photo { background: var(--surface-2); }
html[data-theme="dark"] .mx-tag.is-nopin { background: var(--surface-2); color: var(--text-muted); }

/* ══ Dark mode: brand-red text ═══════════════════════════════════
   --primary (#8E3B2F) stays the brand colour for buttons and bars, but as
   TEXT on the dark page it measured 1.8-2.5:1 contrast (QA, Nov 2026),
   under the 4.5:1 people need to read it. Text that used it gets a
   lighter brick tone in dark mode instead. */
html[data-theme="dark"] { --primary-ink: #E59C86; }
html[data-theme="dark"] .section-link,
html[data-theme="dark"] .home-card-go,
html[data-theme="dark"] .event-when,
html[data-theme="dark"] .notif-readall,
html[data-theme="dark"] .chat-starter,
html[data-theme="dark"] .badge-brown,
html[data-theme="dark"] .badge[class*="status-"],
html[data-theme="dark"] .mx-tag.is-town,
html[data-theme="dark"] .church-tab.is-active,
html[data-theme="dark"] .church-next-mass-link,
html[data-theme="dark"] .event-date:not(.is-soonest) .event-kind,
html[data-theme="dark"] .giya-popup-btn.is-secondary,
html[data-theme="dark"] .mx-more-btn,
html[data-theme="dark"] .profile-tab.is-active,
html[data-theme="dark"] .hub-manage,
html[data-theme="dark"] .history-rate,
html[data-theme="dark"] .ap-banner div,
html[data-theme="dark"] [style*="color:var(--primary)"],
html[data-theme="dark"] [style*="color: var(--primary)"] { color: var(--primary-ink) !important; }
html[data-theme="dark"] .badge-brown,
html[data-theme="dark"] .badge[class*="status-"] { background: rgba(229,156,134,.14); }
html[data-theme="dark"] .church-tab.is-active { border-bottom-color: var(--primary-ink); }

/* Gold buttons: dark ink, like the light theme, never light text on gold. */
html[data-theme="dark"] .btn-gold { color: #241C18; }
html[data-theme="dark"] .event-date.is-soonest .event-kind { color: #F5E9D9; }

/* Map pop-ups follow the dark surface. */
html[data-theme="dark"] .leaflet-popup-content-wrapper { background: var(--surface); color: var(--text); }
html[data-theme="dark"] .giya-popup strong { color: var(--text); }
html[data-theme="dark"] .giya-popup span   { color: var(--text-muted); }
html[data-theme="dark"] .leaflet-popup-tip { background: var(--surface); }

/* Stop numbers on the active trip, and the "Best value" ribbon. */
html[data-theme="dark"] [style*="background:#F5E8D0"] { background: var(--surface-2) !important; }
html[data-theme="dark"] [style*="color:#7a5c00"] { color: var(--gold-light) !important; }
/* Disabled buttons in dark mode: muted, but still readable */
html[data-theme="dark"] .btn-primary:disabled,
html[data-theme="dark"] .btn-primary[disabled] { background: #4A3A30; color: #CDBBA8; opacity: 1; }
html[data-theme="dark"] .btn-primary:disabled span { color: inherit; }
/* The chat page title is its h1 for screen readers; it keeps the bar's look. */
.chat-bar-h1 { margin: 0; font: inherit; line-height: inherit; color: inherit; }

/* ── Giya's moods: excited and tired ───────────────────────────
   The model picks a mood per reply; the big guide and each reply's little
   face (.chat-row[data-mood]) show it. Tired is also how Giya "rests"
   while the AI allowance refills. */

/* Excited: big bright eyes, open smile, hands up, lantern glowing. */
.guide.is-excited .guide-face { transform: translateY(-5px); }
.guide.is-excited .guide-eye,
.chat-row[data-mood="excited"] .guide-eye { transform: scale(1.3); box-shadow: 0 0 0 2px rgba(245,233,217,.25); }
.guide.is-excited .guide-mouth,
.chat-row[data-mood="excited"] .guide-mouth {
  width: 24px; height: 16px; margin-left: -12px;
  border-radius: 4px 4px 24px 24px; bottom: 11px;
}
.guide.is-excited .guide-blush { opacity: .9; }
.guide.is-excited .guide-hand-l { transform: translateY(-12px) rotate(-24deg); }
.guide.is-excited .guide-hand-r { transform: translateY(-12px) rotate(24deg); }
.guide.is-excited .guide-lamp { box-shadow: 0 0 0 3px var(--primary-dark), 0 0 28px 8px rgba(215,169,74,.95); }

/* Tired: heavy eyelids, drooping head, small yawn, dim lantern, "z z Z". */
.guide.is-tired .guide-face { transform: translateY(5px) rotate(-5deg); }
.guide.is-tired .guide-eye,
.chat-row[data-mood="tired"] .guide-eye {
  height: 6px; border-radius: 0 0 15px 15px; transform: translateY(6px);
}
.guide.is-tired .guide-mouth,
.chat-row[data-mood="tired"] .guide-mouth {
  width: 9px; height: 9px; margin-left: -4.5px; border-radius: 50%; bottom: 12px;
}
.guide.is-tired .guide-lamp,
.chat-row[data-mood="tired"] .guide-lamp {
  background: #B8955A;
  box-shadow: 0 0 0 3px var(--primary-dark), 0 0 6px 1px rgba(215,169,74,.3);
}
.guide.is-tired .guide-hand-l,
.guide.is-tired .guide-hand-r { transform: translateY(4px); }
.guide.is-tired .guide-face::after {
  content: "z z Z";
  position: absolute; top: -34px; right: -34px;
  font: 700 15px/1 var(--font-body, sans-serif); letter-spacing: 2px;
  color: var(--gold);
}

/* The little face on a sorry reply matches the big one. */
.chat-row[data-mood="sorry"] .guide-mouth {
  width: 20px; height: 9px; margin-left: -10px; border-radius: 20px 20px 0 0; bottom: 12px;
}

@media (prefers-reduced-motion: no-preference) {
  .guide.is-excited .guide-face { animation: guide-bounce .5s ease-in-out 2; }
  .guide.is-tired .guide-face::after { animation: guide-zzz 2.4s ease-in-out infinite; }
}
@keyframes guide-bounce { 50% { transform: translateY(-11px); } }
@keyframes guide-zzz {
  0%   { opacity: 0; transform: translate(0, 6px); }
  40%  { opacity: 1; }
  100% { opacity: 0; transform: translate(6px, -10px); }
}
.chat-bar-guide.is-tired .guide-face::after { display: none; }

/* "Open this route in the planner", under a route Giya AI planned. */
.chat-plan-link {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px; padding: 7px 12px;
  border-radius: 999px; border: 1px solid var(--gold);
  font-size: .8125rem; font-weight: 700; text-decoration: none;
  color: var(--primary-dark); background: rgba(215,169,74,.14);
}
.chat-plan-link:hover { background: rgba(215,169,74,.26); }
html[data-theme="dark"] .chat-plan-link { color: var(--gold-light, #E9C77E); }

/* ── Privacy Policy and Terms ─────────────────────────────── */
.legal-page { max-width: 760px; margin: 0 auto; padding: 32px 20px 56px; color: var(--text); }
.legal-title { font-family: var(--font-display, 'Playfair Display', serif); font-size: 2rem; margin: 0 0 6px; }
.legal-updated, .legal-note { color: var(--text-muted); font-size: .875rem; margin: 0 0 4px; }
.legal-toc { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 14px 18px; margin: 20px 0 8px; }
.legal-toc-title { font-weight: 700; font-size: .8125rem; text-transform: none; letter-spacing: .01em; color: var(--text-muted); margin-bottom: 6px; }
.legal-toc ol { margin: 0; padding-left: 0; list-style: none; columns: 2; column-gap: 24px; }
.legal-toc li { font-size: .875rem; padding: 2px 0; break-inside: avoid; }
.legal-body h2 { font-size: 1.125rem; margin: 28px 0 8px; scroll-margin-top: 80px; }
.legal-body p, .legal-body li { font-size: .9375rem; line-height: 1.65; }
.legal-body ul { padding-left: 20px; }
.legal-page a { color: var(--primary); }
html[data-theme="dark"] .legal-page a { color: var(--primary-ink, #E59C86); }
@media (max-width: 600px) { .legal-toc ol { columns: 1; } .legal-title { font-size: 1.6rem; } }
.footer-legal { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Consent box on sign-up */
.auth-consent-label { display: flex; gap: 10px; align-items: flex-start; font-size: .8125rem; line-height: 1.45; color: var(--text-muted); cursor: pointer; }
.auth-consent-label input { margin-top: 2px; width: 16px; height: 16px; accent-color: var(--primary); flex-shrink: 0; }
.auth-consent-label a { color: var(--primary); font-weight: 600; }
/* "Delete forever": a clear, solid red in both themes. */
#deleteAccountModal .btn-danger { background: #C0283F; border-color: #C0283F; color: #fff; }
#deleteAccountModal .btn-danger:hover { background: #A51F34; }

/* ── Chat history ─────────────────────────────────────────── */
.chat-starters[hidden], .chat-composer[hidden], .chat-history-item[hidden] { display: none !important; }
.chat-history-btn { display: inline-flex; align-items: center; gap: 6px; }
@media (max-width: 420px) { .chat-history-label { display: none; } }
.chat-history-modal { border: none; border-radius: var(--radius-2xl); padding: 24px; }
.chat-history-search { margin: 4px 0 12px; }
.chat-history-list { list-style: none; margin: 0; padding: 0; max-height: 52vh; overflow-y: auto; }
.chat-history-item { display: flex; align-items: center; gap: 8px; border-bottom: 1px solid var(--border); }
.chat-history-item:last-child { border-bottom: 0; }
.chat-history-item.is-current { background: rgba(215,169,74,.10); border-radius: 10px; }
.chat-history-link { flex: 1; min-width: 0; padding: 10px 8px; text-decoration: none; color: var(--text); }
.chat-history-name { display: block; font-weight: 600; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-history-meta { display: block; font-size: .75rem; color: var(--text-muted); margin-top: 2px; }
.chat-history-del { background: none; border: 0; color: #D4183D; padding: 8px 10px; border-radius: 8px; }
.chat-history-del:hover { background: rgba(212,24,61,.08); }
.chat-history-none { color: var(--text-muted); font-size: .875rem; text-align: center; padding: 16px 0; }
.chat-history-clear { color: #D4183D; border-color: rgba(212,24,61,.4); }
.chat-past-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 16px; border-top: 1px solid var(--border); background: var(--surface-2, #F7EFE3); font-size: .875rem; color: var(--text); }
.chat-past-actions { display: flex; gap: 8px; }
.chat-past-actions form { margin: 0; }
html[data-theme="dark"] .chat-history-item.is-current { background: rgba(215,169,74,.14); }
.chat-page { position: relative; }
.chat-intro-history { display: none; }
.chat-page.is-intro .chat-intro-history {
  display: inline-flex; align-items: center; gap: 6px;
  position: absolute; top: 10px; right: 14px; z-index: 3;
  padding: 7px 12px; border-radius: 999px; font-size: .8125rem; font-weight: 600;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
}

/* ── Admin: Insights ──────────────────────────────────────── */
.insights-note { color: var(--text-muted); font-size: .8125rem; margin-top: 12px; }

/* ── Admin in dark mode ─────────────────────────────────────
   Admin pages now follow the admin's Theme setting. These were drawn for
   a white page only; the scan found them too light or too low-contrast. */
html[data-theme="dark"] .giya-table th { background: var(--surface-2); color: var(--gold-light, #E9C77E); }
html[data-theme="dark"] .badge-published { background: rgba(74,180,110,.16); color: #8FD9A8; }
html[data-theme="dark"] .stat-delta.is-down { color: #F08A9B; }
html[data-theme="dark"] .dm-panel-title { color: var(--primary-ink, #E59C86); }
html[data-theme="dark"] .admin-menu-btn { background: var(--surface-2); color: var(--text); }
html[data-theme="dark"] .report-field input,
html[data-theme="dark"] .report-field select { background: var(--surface-2); color: var(--text); border-color: var(--border); color-scheme: dark; }
html[data-theme="dark"] .report-table { background: var(--surface); color: var(--text); }
html[data-theme="dark"] .report-table thead { background: var(--surface-2); }
html[data-theme="dark"] .report-table th { color: var(--gold-light, #E9C77E); border-bottom-color: var(--border); }
html[data-theme="dark"] .report-table td { border-bottom-color: var(--border); }
html[data-theme="dark"] .report-blank-cell,
html[data-theme="dark"] .report-blank-text { color: var(--text-muted); }
html[data-theme="dark"] .report-download-disabled { background: #4A3A30; border-color: #4A3A30; color: #CDBBA8; opacity: 1; }

/* ── Confirm your email ───────────────────────────────────── */
.verify-banner { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px;
  padding: 9px 16px; background: #FBEFD6; color: #5A3B12; font-size: .8125rem; border-bottom: 1px solid rgba(215,169,74,.35); }
.verify-banner a { font-weight: 700; color: var(--primary); }
html[data-theme="dark"] .verify-banner { background: #3A2A1C; color: #F0D9B5; border-bottom-color: rgba(215,169,74,.25); }
html[data-theme="dark"] .verify-banner a { color: var(--gold-light, #E9C77E); }
.verify-page { max-width: 480px; margin: 40px auto; padding: 0 16px; }
.verify-card { text-align: center; padding: 32px 24px; }
.verify-icon { width: 64px; height: 64px; margin: 0 auto 14px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--gold-bg, rgba(215,169,74,.15)); color: var(--primary); font-size: 1.6rem; }
.verify-title { font-family: var(--font-display, serif); font-size: 1.5rem; margin: 0 0 10px; }
.verify-muted { color: var(--text-muted); font-size: .875rem; }

/* ── Shared itineraries ───────────────────────────────────── */
.share-link-row input { font-size: .8125rem; }
.share-status { min-height: 1.2em; font-size: .8125rem; color: var(--text-muted); margin: 8px 0 4px; }
.share-stop { display: block; margin: 12px auto 0; color: #D4183D; }
.shared-page { max-width: 560px; margin: 0 auto; padding: 28px 16px 56px; }
.shared-badge { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  background: var(--gold-bg, rgba(215,169,74,.15)); color: var(--primary); font-size: .8rem; font-weight: 700; }
html[data-theme="dark"] .shared-badge { color: var(--gold-light, #E9C77E); }
.shared-title { font-family: var(--font-display, serif); font-size: 1.75rem; margin: 12px 0 4px; color: var(--text); }
.shared-meta, .shared-notes, .shared-help { color: var(--text-muted); font-size: .9rem; }
.shared-stops { list-style: none; padding: 0; margin: 18px 0; }
.shared-stops li { display: flex; gap: 12px; align-items: center; padding: 12px 14px; margin-bottom: 8px;
  border: 1px solid var(--border); border-radius: 14px; background: var(--surface); }
.shared-num { width: 30px; height: 30px; flex: none; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: var(--primary); color: #fff; font-weight: 700; font-size: .85rem; }
.shared-stop strong { display: block; color: var(--text); font-size: .95rem; }
.shared-stop span { color: var(--text-muted); font-size: .8rem; }
.shared-actions { display: grid; gap: 10px; }

/* ── Type and icon polish ───────────────────────────────────
   One voice for headings: Playfair Display bold for every display line
   (some were regular, some bold). Small labels are sentence case at 12px
   or more, not tracked capitals. Eyebrow labels lose the decorative bar. */
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; }
h1, h2, h3, .onb-title, .hub-active-name, .legal-title, .verify-title, .shared-title { font-weight: 700; letter-spacing: -.005em; }
.eyebrow-bar { display: none; }
.eyebrow-text { font-size: .8125rem; font-weight: 700; letter-spacing: .01em; color: var(--primary); }
.eyebrow-text.is-gold { color: var(--gold); }
html[data-theme="dark"] .eyebrow-text:not(.is-gold) { color: var(--primary-ink, #E59C86); }
.stat-value, .home-stat-value, [class*="-stat-num"] { font-variant-numeric: tabular-nums; }

/* Icon-only buttons: a real target (38px) with a readable icon. */
.saved-card-actions .btn-sm:not(:first-child) { width: 38px; min-width: 38px; padding: 0; justify-content: center; }
.saved-card-actions .btn-sm .bi, .icon-btn .bi { font-size: 1.05rem; }
.icon-btn { width: 34px; height: 34px; border-radius: 10px; }

/* Delete: clearly red, outlined at rest, solid on hover, so it reads as
   "remove" without shouting next to the main action. */
.btn-danger { background: transparent; color: #C0283F; border: 1px solid rgba(192,40,63,.4); }
.btn-danger:hover, .btn-danger:focus-visible { background: #C0283F; border-color: #C0283F; color: #fff; }
.icon-btn.is-danger { background: transparent; color: #C0283F; box-shadow: inset 0 0 0 1px rgba(192,40,63,.4); }
.icon-btn.is-danger:hover, .icon-btn.is-danger:focus-visible { background: #C0283F; color: #fff; box-shadow: none; }
html[data-theme="dark"] .btn-danger, html[data-theme="dark"] .icon-btn.is-danger { color: #F08A9B; border-color: rgba(240,138,155,.45); box-shadow: inset 0 0 0 1px rgba(240,138,155,.45); }
html[data-theme="dark"] .btn-danger { box-shadow: none; }
html[data-theme="dark"] .btn-danger:hover, html[data-theme="dark"] .icon-btn.is-danger:hover { background: #C0283F; color: #fff; }
.verify-steps { text-align: left; margin: 0 auto 18px; padding-left: 20px; max-width: 320px; color: var(--text); font-size: .9rem; line-height: 1.7; }
.auth-verify-hint { text-align: center; margin: 12px 0 0; font-size: .875rem; }
.auth-verify-hint a { font-weight: 700; color: var(--primary); }


/* ── Admin: System maintenance ──────────────────────────────── */
.sys-grid { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 16px; align-items: start; }
@media (max-width: 900px) { .sys-grid { grid-template-columns: minmax(0, 1fr); } }
.sys-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.sys-since { display: flex; align-items: center; gap: 6px; font-size: .8125rem; color: var(--text-muted); margin: 0 0 14px; }
.sys-date { max-width: 260px; }
.sys-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; }
.sys-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; font-size: .875rem; line-height: 1.5; }
.sys-list li { display: flex; gap: 10px; align-items: flex-start; }
.sys-list .bi { color: var(--primary); margin-top: 3px; flex-shrink: 0; }
.sys-gap { margin-top: 22px; }
.admin-maint { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; padding: 9px 16px;
  background: #FBE3E7; color: #7A1226; font-size: .8125rem; font-weight: 600; border-bottom: 1px solid rgba(192,40,63,.3); }
.admin-maint a { color: inherit; font-weight: 700; text-decoration: underline; }
html[data-theme="dark"] .admin-maint { background: #4A1620; color: #FBD5DB; border-bottom-color: rgba(240,138,155,.35); }
html[data-theme="dark"] .sys-list .bi { color: var(--gold-light, #E9C77E); }

/* ── Announcements ──────────────────────────────────────────── */
.ann-banner { display: flex; align-items: flex-start; justify-content: center; gap: 10px; padding: 10px 16px;
  background: #F3E3C3; color: #3F2A0E; font-size: .875rem; line-height: 1.45; border-bottom: 1px solid rgba(215,169,74,.5); }
.ann-banner > .bi { margin-top: 3px; flex-shrink: 0; color: var(--primary); }
.ann-banner-text { max-width: 820px; overflow-wrap: anywhere; }
.ann-banner-close { flex-shrink: 0; width: 28px; height: 28px; margin: -3px -6px -3px 0; border: 0; border-radius: 8px; background: transparent; color: inherit; cursor: pointer; }
.ann-banner-close:hover, .ann-banner-close:focus-visible { background: rgba(0,0,0,.08); }
html[data-theme="dark"] .ann-banner { background: #3A2A1C; color: #F0D9B5; border-bottom-color: rgba(215,169,74,.3); }
html[data-theme="dark"] .ann-banner > .bi { color: var(--gold-light, #E9C77E); }
html[data-theme="dark"] .ann-banner-close:hover { background: rgba(255,255,255,.12); }
.ann-check { display: flex; align-items: flex-start; gap: 10px; font-size: .875rem; margin: 4px 0 16px; cursor: pointer; }
.ann-check input[type="checkbox"] { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--primary); flex-shrink: 0; }
.ann-preview { display: grid; gap: 4px; padding: 12px 14px; border-radius: 12px; background: #F3E3C3; color: #3F2A0E; font-size: .875rem; line-height: 1.45; margin-bottom: 10px; overflow-wrap: anywhere; }
html[data-theme="dark"] .ann-preview { background: #3A2A1C; color: #F0D9B5; }
.ann-list-title { margin: 26px 0 12px; }

/* ── Admin: Activity log ────────────────────────────────────── */
.log-when { white-space: nowrap; }
.log-muted { color: var(--text-muted); font-size: .8125rem; }
.log-filter-actions { display: flex; gap: 8px; align-items: flex-end; }
.log-filters { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; }
.log-filters .field { margin: 0; flex: 1 1 150px; min-width: 0; }
.log-filters .field:first-child { flex: 2 1 220px; }
.log-filters .log-filter-actions { flex: 0 0 auto; }
.log-filters .giya-input, .log-filters .btn { height: 45px; box-sizing: border-box; }
