/* ============================================================================
   AMANAH — Components
   ============================================================================ */

/* ---- Buttons ---------------------------------------------------------------- */
.action-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-weight: 500;
  font-size: var(--text-sm);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              transform var(--duration-fast) var(--ease-out),
              opacity var(--duration-base) var(--ease-out);
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn-primary {
  background: var(--emerald-solid);
  color: var(--ink-on-dark);
}
.btn-primary:hover:not(:disabled) { background: var(--emerald-solid-hover); }

.btn-secondary {
  background: var(--ivory-raised);
  color: var(--ink);
  border-color: var(--line);
}
.btn-secondary:hover:not(:disabled) { border-color: var(--ink-soft); }

.btn-ghost {
  background: transparent;
  color: var(--ink-soft);
  border-color: transparent;
}
.btn-ghost:hover:not(:disabled) { background: var(--line-soft); color: var(--ink); }

.btn-danger {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger);
}
.btn-danger:hover:not(:disabled) { background: var(--danger-bg); }

.btn-block { width: 100%; }
.btn-sm { padding: var(--space-2) var(--space-3); font-size: var(--text-xs); }

/* ---- Overflow menu ----------------------------------------------------
   "⋯" trigger for secondary actions (see dom.js's menu()). Deliberately
   NOT given the .btn class — action-bar buttons go full-width at the
   .page-header mobile breakpoint below, and a full-width icon-only
   trigger would look broken. Staying its own small square keeps it from
   inheriting that rule and lets it sit naturally next to (or under) the
   real buttons instead of stretching. */
.menu-wrap { position: relative; display: inline-flex; }
.menu-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  padding-bottom: 0.35em; /* optically centers the ⋯ glyph, which sits high in its own line-box */
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  font-size: 1.35rem;
  line-height: 1;
  letter-spacing: 0.05em;
  cursor: pointer;
  transition: border-color var(--duration-base) var(--ease-out), color var(--duration-base) var(--ease-out);
}
.menu-trigger:hover { border-color: var(--ink-soft); color: var(--ink); }
.menu-trigger[aria-expanded="true"] { border-color: var(--ink-soft); color: var(--ink); background: var(--line-soft); }

.menu-list {
  position: absolute;
  top: calc(100% + var(--space-2));
  /* Anchor side depends on where the trigger actually sits, which
     flips at the same 560px breakpoint .page-header itself stacks at
     (responsive.css). Below it: the trigger sits alone at the far left,
     under the two now-full-width buttons above it (see .page-header
     .btn in responsive.css — .menu-trigger is deliberately NOT .btn, so
     it doesn't go full-width itself, but its stacked position is still
     driven by that same breakpoint). A menu growing leftward from
     something already at the left edge runs straight off-screen — which
     is the bug this replaces. Left-anchored, growing rightward, stays
     on a ~360-400px mobile viewport with room to spare (11rem = 176px).
     Above 560px: the trigger is the trailing item in an inline
     [Mark settled][Send reminder][⋯] row, near the row's right edge —
     right-anchored, growing leftward, is correct there instead. */
  left: 0;
  min-width: 11rem;
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-raised);
  padding: var(--space-2);
  z-index: 150; /* above .nav's sticky z-index:100 (layout.css) — a scroll while the menu's open must not let the nav bar paint over it; still below .toast (400) and .app-splash (500) */
  display: flex;
  flex-direction: column;
  gap: 1px;
}
@media (min-width: 561px) {
  .menu-list {
    left: auto;
    right: 0;
  }
}
.menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.menu-item:hover { background: var(--line-soft); }
.menu-item.is-danger { color: var(--danger); }
.menu-item.is-danger:hover { background: var(--danger-bg); }

/* Plain text link styled as an inline action, not a padded button —
   used for lower-emphasis actions like "use a different email instead". */
.btn-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--text-xs);
  color: var(--emerald);
  cursor: pointer;
  text-decoration: underline;
}
.btn-link:hover { color: var(--emerald-strong); }

/* ---- Cards ------------------------------------------------------------------- */
.card {
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}

/* ---- Stat card ----------------------------------------------------------------- */
.stat-card {
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.stat-card .stat-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  margin-bottom: var(--space-2);
}
.stat-card .stat-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xl);
  color: var(--ink);
}
.stat-card.is-settled .stat-value { color: var(--emerald); }

/* ---- Money summary card (dashboard: per-currency totals) ------------------------ */
.money-summary-card {
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.money-summary-amounts {
  display: flex;
  flex-direction: column;
}
.money-summary-amount {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xl);
  color: var(--ink);
  padding: var(--space-1) 0;
}
/* A thin ledger-style rule between currencies, not just a gap — only
   between entries (:not(:first-child)), so the single-currency case (the
   overwhelming majority: most people's amanahs are all one currency) gets
   no divider and no extra spacing at all, identical to before this. */
.money-summary-amount:not(:first-child) {
  border-top: 1px solid var(--line-soft);
}
/* "Owed to you" in emerald (money coming in, same tone as a settled pill) —
   "You owe" stays neutral ink rather than --danger red: it's routine
   ledger information, not a warning, and reserving red for genuinely
   overdue items (see .pill-overdue) keeps that color meaningful. */
.money-summary-card.is-positive .money-summary-amount { color: var(--emerald); }
.money-summary-empty {
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  color: var(--ink-faint);
}

/* ---- Nudge banner (dashboard: amanahs outstanding a while) --------------------- */
.nudge-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  background: var(--status-partial-bg);
  border: 1px solid var(--status-partial);
  border-left-width: 3px;
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-6);
}
.nudge-banner p {
  margin: 0;
  color: var(--ink);
  font-size: var(--text-sm);
}
.nudge-banner .btn-link { color: var(--status-partial); white-space: nowrap; }
/* Overdue variant — same layout, --danger tokens instead of --status-partial
   ones, so a real missed deadline reads more urgent than the generic
   "sat untouched a while" nudge it takes priority over (see dashboard.js). */
.nudge-banner.is-danger {
  background: var(--danger-bg);
  border-color: var(--danger);
}
.nudge-banner.is-danger .btn-link { color: var(--danger); }

/* ---- Offline / sync status banner ----------------------------------------------
   Same shape as .nudge-banner (border-left accent, space-between layout) but
   neutral --ink-soft tones rather than warning colors — being offline isn't
   a problem the way an overdue amanah is, it's just a fact worth stating. */
.offline-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-left-color: var(--ink-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-5);
  margin-bottom: var(--space-6);
  font-size: var(--text-sm);
  color: var(--ink-soft);
}
.offline-banner strong { color: var(--ink); font-weight: 600; }

/* ---- Record card --------------------------------------------------------------- */
.record-card {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-4);
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  transition: border-color var(--duration-base) var(--ease-out), box-shadow var(--duration-base) var(--ease-out);
  align-items: center;
}
.record-card:hover {
  border-color: var(--ink-faint);
  box-shadow: var(--shadow-card);
  text-decoration: none;
}
.record-card-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.record-card-name {
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.record-card-meta {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}
.record-card > div:last-child {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  flex-shrink: 0;
}
.record-card-amount {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-md);
  text-align: right;
  white-space: nowrap;
}
.record-card-direction {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  display: block;
  font-family: var(--font-body);
  text-align: right;
}

/* ---- Status pill ----------------------------------------------------------------- */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 0.25rem 0.625rem;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.pill::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.pill-outstanding { background: var(--status-outstanding-bg); color: var(--status-outstanding); }
.pill-partially_settled { background: var(--status-partial-bg); color: var(--status-partial); }
.pill-settled { background: var(--status-settled-bg); color: var(--status-settled); }
/* Not a stored status (see isOverdue() in format.js) — reuses the existing
   --danger tokens, which already adapt between light/dark themes. */
.pill-overdue { background: var(--danger-bg); color: var(--danger); }
/* A queued-but-not-yet-synced amanah — still only in the outbox, no
   server row yet. --status-partial tones: not an error, just "will
   finish shortly," same register as a partially-settled amanah. */
.pill-pending { background: var(--status-partial-bg); color: var(--status-partial); }
/* A queued create/settle the server actually rejected (not a network
   problem — flush() only marks this for a non-network failure). Reuses
   the --danger tokens like pill-overdue, under its own class so the name
   says what it means at each call site instead of borrowing an
   unrelated one that happens to look the same. */
.pill-failed { background: var(--danger-bg); color: var(--danger); }

/* ---- Access badge ----------------------------------------------------------------- */
.access-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
.access-badge svg { width: 13px; height: 13px; flex-shrink: 0; }

/* ---- Forms ----------------------------------------------------------------------- */
.field {
  margin-bottom: var(--space-5);
}
.field-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink);
  margin-bottom: var(--space-2);
}
.field-hint {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  margin-top: var(--space-1);
}
.field-error {
  font-size: var(--text-xs);
  color: var(--danger);
  margin-top: var(--space-1);
}
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}

.input,
.textarea,
.select {
  width: 100%;
  background: var(--ivory-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  color: var(--ink);
  transition: border-color var(--duration-base) var(--ease-out);
}
.input:focus,
.textarea:focus,
.select:focus {
  outline: none;
  border-color: var(--emerald);
  box-shadow: 0 0 0 3px var(--emerald-tint);
}
.input.has-error,
.textarea.has-error {
  border-color: var(--danger);
}
.input::placeholder,
.textarea::placeholder { color: var(--ink-faint); }
.textarea { resize: vertical; min-height: 6rem; }

.select {
  appearance: none;
  background-image: var(--icon-chevron-down);
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  padding-right: var(--space-7);
}

.radio-group {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.radio-card {
  flex: 1;
  min-width: 8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  transition: border-color var(--duration-base) var(--ease-out), background var(--duration-base) var(--ease-out);
}
.radio-card:has(input:checked) {
  border-color: var(--emerald);
  background: var(--emerald-tint);
}
.radio-card input { accent-color: var(--emerald); }

.form-actions {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

/* ---- Inline confirmation (not an intrusive alert) --------------------------------- */
.confirm-inline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: var(--danger-bg);
  border: 1px solid var(--danger);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  color: var(--ink);
}
.confirm-inline-actions { display: flex; gap: var(--space-2); flex-shrink: 0; flex-wrap: wrap; }
/* Stacked variant — for banners with a label + textarea above the action
   row (the reminder composer), where the default space-between row layout
   (built for confirmDelete's single line of text + 2 buttons) would put
   everything side-by-side instead of stacked. */
.confirm-inline--stacked { flex-direction: column; align-items: stretch; justify-content: flex-start; }
.confirm-inline--stacked .confirm-inline-actions { justify-content: flex-end; }

/* ---- Toast ------------------------------------------------------------------------- */
.toast {
  position: fixed;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%) translateY(1rem);
  /* Deliberately --charcoal-deep/--ink-on-dark, not --ink/--ink-on-dark —
     those two are the ones held constant across both themes (same as the
     nav bar). A toast is meant to always read as a dark pill with light
     text, not something that inverts with the theme; --ink flips to
     near-white in dark mode (correct for body text, wrong here), which
     is what made this render as a white-on-white toast in dark mode. */
  background: var(--charcoal-deep);
  color: var(--ink-on-dark);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  box-shadow: var(--shadow-raised);
  z-index: 400;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-out), transform var(--duration-base) var(--ease-out);
}
.toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.toast.is-danger { background: var(--danger-solid); }
.toast.is-dismissible {
  /* Base .toast has pointer-events:none so a passive confirmation toast
     never blocks a click on whatever's underneath it — a dismissible
     (danger) toast needs that switched back on, or the tap-to-dismiss
     handler in dom.js's toast() can never actually fire. */
  pointer-events: auto;
  cursor: pointer;
}

/* ---- Toggle row (notifications settings) ----------------------------------------------- */
.toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.toggle-row span { color: var(--ink); font-size: var(--text-sm); }

/* ---- Person row (trusted people list) ----------------------------------------------- */
.person-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line-soft);
}
.person-row:last-child { border-bottom: none; }
.person-name { font-weight: 500; color: var(--ink); }
.person-email { font-size: var(--text-xs); color: var(--ink-faint); }
/* The amount/date pair sits in an unstyled wrapper that shrink-wraps to
   its widest child (amount or date line). .num's text-align:right then
   right-aligns the amount within that shared width — fine when the two
   lines are similar length, but it visibly jumps around row to row once
   the date line (e.g. "· Marked fully settled") gets much longer than
   the amount. Left-align here so the amount sits flush with the date
   below it regardless of either line's length; still monospaced/tabular
   via .num for legibility, just not right-justified in this context. */
.person-row .person-name.num { text-align: left; }
.person-status {
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
.person-status.is-pending { color: var(--status-partial); }

/* ---- Sharing picker item ------------------------------------------------------------- */
.picker-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--line-soft);
}
.picker-item:last-child { border-bottom: none; }
.picker-item.is-disabled { opacity: 0.5; }
.picker-item-label { display: flex; flex-direction: column; }
.picker-item-name { font-weight: 500; color: var(--ink); font-size: var(--text-sm); }
.picker-item-tag { font-size: var(--text-xs); color: var(--status-partial); }

/* ---- Skeleton / loading -------------------------------------------------------------- */
.skeleton {
  background: linear-gradient(90deg, var(--line-soft) 25%, var(--line) 37%, var(--line-soft) 63%);
  background-size: 400% 100%;
  animation: skeleton-shimmer 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
/* One class per size actually used, in place of inline width/height styles.
   All are width:100% except the title variant, which mirrors the one
   call site that needed a narrower block. */
.skeleton--title { width: 60%; height: 2.5rem; }
.skeleton--block { width: 100%; height: 10rem; }
.skeleton--row-lg { width: 100%; height: 5.5rem; }
.skeleton--row { width: 100%; height: 4.25rem; }
.skeleton--row-sm { width: 100%; height: 4rem; }
.skeleton--line { width: 100%; height: 3rem; }
@keyframes skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ---- Auth divider ---------------------------------------------------------------------- */
.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  margin: var(--space-5) 0;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* ---- Utilities -------------------------------------------------------------------------
   Added to replace inline style="" attributes throughout the codebase — CSP's style-src
   no longer needs 'unsafe-inline' as a result (see netlify.toml). Every rule below
   reproduces an inline style that existed at a specific call site; nothing here changes
   how anything looks, only where the rule lives. ------------------------------------- */
.heading-sm { font-size: var(--text-lg); }
.text-md { font-size: var(--text-md); }
.text-sm { font-size: var(--text-sm); }
.text-faint { color: var(--ink-faint); }
.text-soft { color: var(--ink-soft); }
.text-center { text-align: center; }

.mt-2 { margin-top: var(--space-2); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--space-1); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.mb-5 { margin-bottom: var(--space-5); }
.mb-6 { margin-bottom: var(--space-6); }

.is-hidden { display: none; }

.form-inline {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.field-grow {
  flex: 1;
  margin-bottom: 0;
}

.pill-corner {
  margin-top: 6px;
  float: right;
}

[data-status-filter] {
  max-width: 12rem;
}

.picker-item { cursor: pointer; }
.picker-item.is-disabled { cursor: not-allowed; }

/* ---- Contact ---------------------------------------------------------------------------
   Used on the Settings "Contact" section and the public footer. */
.contact-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.contact-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  color: var(--ink);
  text-decoration: none;
}

.contact-link:hover {
  background: var(--line-soft);
  text-decoration: none;
}

.contact-link svg {
  flex-shrink: 0;
  color: var(--ink-soft);
}

.contact-link-label {
  display: flex;
  flex-direction: column;
}

.contact-link-value {
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

.footer-contact {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.footer-contact a {
  display: inline-flex;
  color: var(--ink-on-dark-soft);
}

.footer-contact a:hover {
  color: var(--ink-on-dark);
}

.footer-contact svg {
  width: 18px;
  height: 18px;
}
