/* Family Hub — custom styles (extracted from index.html <style> block) */
html {
  /* 1rem = 16px; rem-based type honours OS font scaling (Android font
     scale / iOS larger-text) because it is relative to the root. Keeping
     this at 100% (not a pixel value) lets the OS scale the whole scale. */
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  /* Slightly airier base line-height (bumped one notch for legibility);
     does not change layout geometry (padding/spacing untouched). */
  line-height: 1.55;
  font-family: 'Outfit', 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
:root {
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);

  /* ═════════════════════════════════════════════════════════════════════
     DESIGN SYSTEM REFACTOR — PHASE 1 TOKENS (12 Sep 2026, staging)
     Single source of truth. No surface may hardcode a colour/size/spacing
     after this phase. Contrast ratios measured against the two card
     surfaces below (see FAMILY_HUB_DESIGN_SYSTEM_REFACTOR.md).
     ═════════════════════════════════════════════════════════════════════ */

  /* ── Foreground hierarchy (contrast vs card #121726 / sub-panel #0b1020) ── */
  --fg-primary:   #f8fafc;   /* 17.1:1 / 18.1:1 — headings, names, body        */
  --fg-secondary: #cbd5e1;   /* 12.0:1 / 12.8:1 — labels, chips               */
  --fg-tertiary:  #94a3b8;   /*  6.96:1 / 7.38:1 — timestamps, meta (AA+AAA)  */

  /* ── Status / urgency (health & urgency signals app-wide) ── */
  --ok:     #34d399;         /*  9.29:1 /  9.85:1 — healthy / safe             */
  --warn:   #fbbf24;         /* 10.69:1 / 11.34:1 — moderate / attention       */
  --danger: #f43f5e;         /*  4.86:1 /  5.16:1 — severe / emergency         */

  /* ── 4px spacing unit (base scale 4/8/12/16/24) ── */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-6: 24px;

  /* ── Fixed geometry for the shared row primitive (Phase 2) ── */
  --row-identity: 32px;  /* fixed identity slot width               */
  --row-gap:      12px;  /* inner row gap (snaps to 4px scale)      */
}

/* ══ PHASE 1 — TYPE SCALE (5 levels; no ad-hoc inline font overrides) ══
   Sizes are REM (relative to html root = 16px) so the OS text-size setting
   scales the whole hierarchy. One-step bump applied to body/label/meta for
   legibility (12 Sep 2026); display/title headings left unchanged to
   preserve hierarchy. (Previous px values shown in comments.) */
.t-display { font-size: 1.25rem;  font-weight: 700; color: var(--fg-primary); }  /* 20px — heading, unchanged   */
.t-title   { font-size: 0.9375rem; font-weight: 600; color: var(--fg-primary); }  /* 15px — heading, unchanged   */
.t-body    { font-size: 1rem;      font-weight: 500; color: var(--fg-primary); }  /* 14→16px — body bumped +1   */
.t-label   { font-size: 0.875rem;  font-weight: 600; color: var(--fg-secondary); } /* 12→14px — label bumped +1   */
.t-meta    { font-size: 0.8125rem; font-weight: 500; color: var(--fg-tertiary); }  /* 11→13px — meta bumped +1    */
/* Status variants (colour only; size/weight come from the base level) */
.t-ok      { color: var(--ok); }
.t-warn    { color: var(--warn); }
.t-danger  { color: var(--danger); }

/* ══ PHASE 2 — SHARED PRIMITIVES ══ */
/* row: [ identity (fixed 32px) | content (flex, truncate) | right gutter (fixed, tabular) ] */
.fh-row {
  display: grid;
  grid-template-columns: var(--row-identity) minmax(0, 1fr) auto;
  gap: var(--row-gap);
  align-items: center;
  width: 100%;
}
.fh-row__identity {
  width: var(--row-identity);
  min-width: var(--row-identity);
  max-width: var(--row-identity);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.fh-row__content {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Multi-line content variant: disables the single-line truncate so child
   blocks (e.g. crew name + 2-line status) may wrap normally. */
.fh-row__content--stacked {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
/* The right gutter ALWAYS renders (even when empty) so the column never shifts
   when a menu button appears. Fixed geometry, right-aligned, tabular numerals. */
.fh-row__gutter {
  justify-self: end;
  flex: none;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ══ FEED-ROW VARIANT (family-hub-split/docs/reports/FEED-ROW-REFACTOR-SPEC.md) ══
   The activity-feed action-row uses the SAME `.fh-row` primitive as weekly-streaks,
   but its gutter must hold ONLY the 44px ⋯ button so its x is constant by construction
   (the old gutter shared the column with a variable-width monospace timestamp, which
   swung the button x 46px and crushed the content column). The timestamp now lives on
   the meta line below the header. */
.fh-row--feed {
  grid-template-columns: var(--row-identity) minmax(0, 1fr) 44px;
}
.fh-row--feed .fh-row__gutter {
  width: 44px;
  min-width: 44px;
  max-width: 44px;
  justify-self: end;
}

/* Feed meta line: left chips (flex, wrap, truncation-safe) + right-aligned timestamp. */
.fh-feed-meta {
  display: flex;
  align-items: center;
  gap: 8px;
}
.fh-feed-meta__chips {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.fh-feed-meta__stamp {
  flex: none;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* card slots: compose with the EXISTING `.glass-card` shell (DO NOT restyle).
   `.glass-card` is already the unified card surface (rgba(18,23,38,.75) + blur),
   protected by v4 §7. The `card` primitive = `.glass-card rounded-2xl p-4
   border border-slate-800` with these optional slot helpers on top. */
.fh-card__header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.fh-card__body   { margin-top: 12px; }
.fh-card__footer { margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.06); }

/* stat-strip variant: 4 equal cells (collapses 2×2 on narrow screens) */
.fh-stat-strip {
  display: grid;
  grid-template-columns: repeat(2, minmax(0,1fr));
  gap: 10px;
}
@media (min-width: 640px) {
  .fh-stat-strip { grid-template-columns: repeat(4, minmax(0,1fr)); }
}
.fh-stat-cell {
  border-radius: 12px;
  padding: 10px 12px;
  background: rgba(2,6,23,0.6);
  border: 1px solid rgba(255,255,255,0.05);
}

/* Phase 3.2 — Builder submit button states. `.btn-disabled` is the genuine
   disabled look (dimmed, muted border, no CTA gradient); `.btn-primary` is the
   active-CTA gradient. Toggled in updateBuilderSubmitButton(). */
.btn-primary {
  box-shadow: 0 10px 15px -3px rgba(16, 185, 129, 0.2);
}
.btn-disabled {
  background: rgba(30, 41, 59, 0.5);
  color: var(--fg-tertiary);
  border: 1px solid rgba(255, 255, 255, 0.08);
  opacity: 0.6;
  cursor: not-allowed;
  box-shadow: none;
}
body {
  background-color: #0b0d17;
  background-image:
    radial-gradient(at 0% 0%, rgba(16, 185, 129, 0.12) 0px, transparent 50%),
    radial-gradient(at 100% 10%, rgba(56, 189, 248, 0.08) 0px, transparent 50%),
    radial-gradient(at 50% 100%, rgba(139, 92, 246, 0.07) 0px, transparent 50%);
  color: #f8fafc;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  padding-top: max(0.5rem, calc(var(--sat) + 0.25rem));
  padding-bottom: max(4.25rem, calc(var(--sab) + 3.75rem));
}
.glass-card {
  background: rgba(18, 23, 38, 0.75);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.07);
  box-shadow: 0 15px 35px -10px rgba(0,0,0,0.5);
}
.glass-nav {
  background: rgba(13, 17, 28, 0.88);
  backdrop-filter: blur(25px);
  -webkit-backdrop-filter: blur(25px);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  padding-bottom: max(0.75rem, var(--sab));
}
.tab-content { display: none; }
.tab-content.active { display: block; animation: fadeIn 0.2s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
.dir-active {
  background: rgba(16, 185, 129, 0.25);
  border-color: rgba(16, 185, 129, 0.5);
  color: #34d399;
  font-weight: 700;
}
.dir-inactive {
  background: rgba(15, 23, 42, 0.6);
  border-color: rgba(255, 255, 255, 0.05);
  color: #94a3b8;
}
.city-pill {
  background: rgba(15, 23, 42, 0.6);
  border-color: rgba(255,255,255,0.08);
  color: #94a3b8;
}
.city-pill.active {
  background: rgba(16, 185, 129, 0.15);
  border-color: rgba(16, 185, 129, 0.5);
  color: #34d399;
}
#header-refresh-icon { display: inline-block; transition: transform 0.5s ease; }

/* Pulsing colon animation */
@keyframes pulseColon {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
.pulse-colon {
  display: inline-block;
  animation: pulseColon 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

/* ══ Unread activity badge — explicit override ══
   The pre-compiled tailwind.min.css is missing -top-1.5 / -right-2.5 /
   min-w-[16px] and bg-rose-500 does not win the cascade on this element,
   which left the badge transparent and centred on the 💪 icon. These rules
   force the intended look: solid rose, tight rounded pill, top-right offset. */
#nav-activity-badge {
  position: absolute;
  top: -6px;
  right: -10px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 9999px;
  background-color: #f43f5e; /* rose-500 */
  color: #ffffff;
  font-size: 11px;
  font-weight: 800;
  line-height: 16px;
  text-align: center;
  opacity: 1 !important;
  border: 1px solid #0f172a; /* slate-900 */
  box-shadow: 0 1px 2px rgba(0,0,0,0.4);
}

/* Hard-hide guarantee: when the badge is marked hidden, force it fully out of the
   render tree (display:none) and clear any paint-affecting props so Safari/PWA
   leaves no residual red sliver at the icon's top-right after the Activity tab
   is opened. Inline style.display is also set by markActivitiesAsRead(), this
   rule is the CSS-level backstop. */
#nav-activity-badge.hidden {
  display: none !important;
  border: none !important;
  box-shadow: none !important;
  background-color: transparent !important;
  opacity: 0 !important;
}

/* Shake feedback when submit is blocked (no activity selected) */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-4px); }
  40% { transform: translateX(4px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(3px); }
}
.shake { animation: shake 0.4s ease-in-out; }

/* Explicit dot color & size classes */
.w-1\.5, .w-1\.5 { width: 0.375rem; }
.h-1\.5, .h-1\.5 { height: 0.375rem; }
.w-2 { width: 0.5rem; }
.h-2 { height: 0.5rem; }
.w-3 { width: 0.75rem; }
.h-3 { height: 0.75rem; }
.bg-purple-400 { background-color: #c084fc; }
.bg-red-400 { background-color: #f87171; }
.bg-rose-400 { background-color: #fb7185; }
.bg-amber-400 { background-color: #fbbf24; }
.bg-emerald-400 { background-color: #34d399; }
.bg-sky-400 { background-color: #38bdf8; }

/* ══ Weather alert banner — glow (v3) ══
   Severe (rose) = animated halo pulse; moderate (amber) = static border+shadow.
   Halo via box-shadow (not background flash) to preserve text legibility. */
@keyframes alertGlowRose {
  0%, 100% { box-shadow: 0 0 0 0 rgba(244,63,94,0.55), 0 0 18px 2px rgba(244,63,94,0.35); }
  50%      { box-shadow: 0 0 0 6px rgba(244,63,94,0), 0 0 30px 8px rgba(244,63,94,0.65); }
}
@keyframes alertGlowAmber {
  0%, 100% { box-shadow: 0 0 0 0 rgba(245,158,11,0.50), 0 0 16px 2px rgba(245,158,11,0.30); }
  50%      { box-shadow: 0 0 0 6px rgba(245,158,11,0), 0 0 26px 7px rgba(245,158,11,0.55); }
}
.alert-glow-rose  { animation: fadeIn 0.2s ease-out, alertGlowRose  2.2s ease-in-out infinite; }
.alert-glow-amber { animation: fadeIn 0.2s ease-out, alertGlowAmber 2.8s ease-in-out infinite; }

/* Amber (moderate) is STATIC: no glow animation, only a soft static shadow.
   (severity hierarchy — if everything pulses nothing pulses) */
.alert-static-amber {
  box-shadow: 0 0 18px 3px rgba(245,158,11,0.30);
  animation: fadeIn 0.2s ease-out;
}

/* Sticky weather banner (top of scroll container) */
#home-emergency-weather-banner:not(.hidden) {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* Sticky top app header — keeps the per-tab title clear of the translucent
   iOS status bar (black-translucent) when scrolled. Backdrop-blur + opaque body
   colour so feed/card content scrolls BEHIND the bar instead of slithering under
   the clock. The negative top margin + matching padding cancels the body's
   `padding-top: max(0.5rem, calc(var(--sat) + 0.25rem))` so the bar hugs the true
   viewport top while its own padding keeps the title below the notch/clock. */
#app-container > header {
  position: sticky;
  top: 0;
  z-index: 40; /* below modals/sheets (z-50+, z-[90]) */
  margin-top: calc(-1 * max(0.5rem, calc(var(--sat) + 0.25rem)));
  padding-top: max(0.5rem, calc(var(--sat) + 0.25rem));
  background: rgba(11, 13, 23, 0.92);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* Reduced motion: severe → static halo, moderate → static soft shadow. No animation. */
@media (prefers-reduced-motion: reduce) {
  .alert-glow-rose, .alert-glow-amber { animation: none; }
  .alert-glow-rose  { box-shadow: 0 0 22px 4px rgba(244,63,94,0.45); }
  .alert-glow-amber { box-shadow: 0 0 18px 3px rgba(245,158,11,0.35); }
  .alert-static-amber { animation: none; }
}

/* ══ v7 — crew row status: single-line, truncates with ellipsis (item 2 left-item) ══ */
.crew-status {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ══ v5 — inline alert pill on the weather line (reuses v3/v4 glow classes) ══ */
.crew-alert-pill {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 6px;
  border: 1px solid;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
  appearance: none;
  -webkit-appearance: none;
  font-family: inherit;
  min-height: 0;
  height: auto;
}
.crew-alert-pill.alert-glow-rose {
  color: #fda4af; background: rgba(244,63,94,0.16); border-color: rgba(244,63,94,0.40);
}
.crew-alert-pill.alert-static-amber {
  color: #fcd34d; background: rgba(245,158,11,0.14); border-color: rgba(245,158,11,0.38);
}

/* ── P3c accessibility: screen-reader-only text + visible keyboard focus ────── */
/* `sr-only` is in the Tailwind source but NOT in the prebuilt tailwind.min.css,
   so it was a silent no-op; defined here so it actually works. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
/* The app removes focus rings almost everywhere (`focus:outline-none`), which is
   fine for pointer users but leaves keyboard users with no idea where they are.
   `:focus-visible` restores an indicator for keyboard navigation ONLY. */
:focus-visible {
  outline: 2px solid #34d399;
  outline-offset: 2px;
  border-radius: 0.5rem;
}

/* ══ Bruno Hub — staleness pill grading (14 Sep 2026) ══════════════════════════
   The data now declares its own age. Before this, a 177 h-old payload rendered in
   exactly the same neutral grey as a 1-min-old one, so silent rot was invisible to
   the eye. Bands (Kelvin): amber > 60 min, rose > 6 h. Text is unchanged; this is
   styling only. The three pills share one writer in js/bruno.js, so they always
   carry the same grade. Declared after tailwind.min.css so equal-specificity
   utilities (text-slate-400 / border-slate-700 / bg-slate-800\/80) lose to these. */
.pill-stale-warn {
  color: var(--warn);
  border-color: rgba(251, 191, 36, 0.55);
  background-color: rgba(251, 191, 36, 0.12);
}
.pill-stale-bad {
  color: var(--danger);
  border-color: rgba(244, 63, 94, 0.6);
  background-color: rgba(244, 63, 94, 0.14);
}
