/* WeReportYourRent - Custom Styles */

* { box-sizing: border-box; }

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ── Scrollbar ─────────────────────────────── */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #f1f5f9; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }

/* ── Smooth scroll ─────────────────────────── */
html { scroll-behavior: smooth; }

/* ── Navy color utilities ──────────────────── */
.bg-navy-900 { background-color: #0d1f36; }
.bg-navy-800 { background-color: #162d4a; }
.bg-navy-700 { background-color: #1e3a5f; }
.text-navy-800 { color: #162d4a; }
.from-navy-900 { --tw-gradient-from: #0d1f36; }
.from-navy-800 { --tw-gradient-from: #162d4a; }
.to-navy-800  { --tw-gradient-to: #162d4a; }
.via-navy-800 { --tw-gradient-via: #162d4a; }

/* ── Brand color utilities ─────────────────── */
/* BL-211 (Irv 2026-06-22): canonical brand is NAVY (#0F4C81), not emerald. Navy
   scale below; 400/500/600 match the millennial theme's --brand-* vars so the
   product brand is consistent however it's themed. Landing skins (genz teal /
   budget amber) stay per-theme via the inline tailwind.config in baseHead.ts. */
.bg-brand-50  { background-color: #eef3f9; }
.bg-brand-100 { background-color: #d5e3f0; }
.bg-brand-400 { background-color: #1E6FB8; }
.bg-brand-500 { background-color: #0F4C81; }
.bg-brand-600 { background-color: #0A3A66; }
.bg-brand-700 { background-color: #082c4d; }
.text-brand-100 { color: #d5e3f0; }
.text-brand-300 { color: #7aa6cf; }
.text-brand-400 { color: #1E6FB8; }
.text-brand-500 { color: #0F4C81; }
.text-brand-600 { color: #0A3A66; }
.text-brand-700 { color: #082c4d; }
.border-brand-100 { border-color: #d5e3f0; }
.border-brand-200 { border-color: #aecae1; }
.border-brand-300 { border-color: #7aa6cf; }
.border-brand-400 { border-color: #1E6FB8; }
.border-brand-500 { border-color: #0F4C81; }
.hover\:bg-brand-50:hover  { background-color: #eef3f9; }
.hover\:bg-brand-100:hover { background-color: #d5e3f0; }
.hover\:bg-brand-400:hover { background-color: #1E6FB8; }
.hover\:bg-brand-600:hover { background-color: #0A3A66; }
.hover\:border-brand-200:hover { border-color: #aecae1; }
.hover\:border-brand-300:hover { border-color: #7aa6cf; }
.hover\:text-brand-600:hover { color: #0A3A66; }
.focus\:ring-brand-300:focus { --tw-ring-color: #7aa6cf; }
.from-brand-700 { --tw-gradient-from: #082c4d; }
.to-brand-700   { --tw-gradient-to: #082c4d; }

/* ── Gradient helpers ──────────────────────── */
.bg-gradient-to-br { background-image: linear-gradient(to bottom right, var(--tw-gradient-from), var(--tw-gradient-via, var(--tw-gradient-from)), var(--tw-gradient-to)); }
.from-purple-900 { --tw-gradient-from: #4c1d95; }

/* ── Animations ────────────────────────────── */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes pulse-ring {
  0%   { transform: scale(1);    opacity: 1; }
  100% { transform: scale(1.4);  opacity: 0; }
}

.animate-fade-in-up {
  animation: fadeInUp 0.6s ease-out both;
}

/* ── Hero section enhancements ─────────────── */
section.hero-gradient {
  background: linear-gradient(135deg, #0d1f36 0%, #162d4a 50%, #047857 100%);
}

/* ── Card hover effects ────────────────────── */
.card-hover {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-hover:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 40px rgba(0,0,0,0.12);
}

/* ── Credit Score gauge ────────────────────── */
.score-gauge {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ── Progress bar ──────────────────────────── */
.progress-bar {
  height: 8px;
  border-radius: 99px;
  background: #e2e8f0;
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  border-radius: 99px;
  background: linear-gradient(90deg, #0F4C81, #1E6FB8); /* BL-211: navy brand */
  transition: width 1s ease;
}

/* ── Chat widget ───────────────────────────── */
#chat-box {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
#chat-messages {
  scroll-behavior: smooth;
}

/* ── Form input focus ──────────────────────── */
input:focus, select:focus, textarea:focus {
  outline: none;
}

/* ── Card chrome restore (2026-05-24, Jay batch 3) ─────────────────────
   42 sites across src/pages/ ship as <div class="" style="background:
   var(--c-surface)"> — empty class + themed surface bg. That combination
   is a strong regression marker: an automated find/replace stripped the
   Tailwind chrome (rounded, shadow, padding, border) but left the
   background. Result: white "cards" with no edges on light themes
   (millennial/budget) and indistinguishable card edges in genz dark.
   This rule gives them sensible default chrome without touching the 14
   source files. <section> and <nav> with the same pattern (faqs.ts:248,
   millennialLanding.ts:113) are intentionally skipped — those are full-
   width containers, not cards. */
div[class=""][style*="background:var(--c-surface)"],
div[class=""][style*="background: var(--c-surface)"] {
  padding: 1.75rem;
  border-radius: var(--radius-card, 1rem);
  box-shadow: var(--c-card-shadow, 0 1px 3px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.06));
  border: 1px solid var(--c-border);
}

/* ── Themed inputs (2026-05-24, Irv feedback batch 2) ──────────────────
   Without this rule, input/select/textarea inherit text color from the
   parent section (often white inside dark hero gradients) while the
   browser keeps the input background white — resulting in white-on-white
   typed text on /login and /landlord (dark theme). No !important: pages
   with their own inline color/background still win. */
input[type="text"],
input[type="email"],
input[type="tel"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
input:not([type]),
textarea,
select {
  color: var(--c-text, #1F2937);
  background-color: var(--c-surface, #FFFFFF);
}
input::placeholder,
textarea::placeholder {
  color: var(--c-text-muted, #6B7280);
  opacity: 1;
}

/* Dark theme: lift inputs to surface2 so they stand out from card chrome. */
.theme-genz input[type="text"],
.theme-genz input[type="email"],
.theme-genz input[type="tel"],
.theme-genz input[type="number"],
.theme-genz input[type="password"],
.theme-genz input[type="search"],
.theme-genz input[type="url"],
.theme-genz input[type="date"],
.theme-genz input[type="datetime-local"],
.theme-genz input:not([type]),
.theme-genz textarea,
.theme-genz select {
  background-color: var(--c-surface2);
  color: var(--c-text);
  border-color: var(--c-border);
}

/* Budget + millennial: same trick. --c-border (#E8DCC4 / #E7E2D7) has ~1.2:1
   contrast on pure white inputs, so the border is technically present but
   invisible. Lift inputs to --c-surface2 (warm cream) so the border has
   ~1.7:1 — visible without changing the palette Irv praised. Jay 2026-05-24. */
.theme-budget input[type="text"],
.theme-budget input[type="email"],
.theme-budget input[type="tel"],
.theme-budget input[type="number"],
.theme-budget input[type="password"],
.theme-budget input[type="search"],
.theme-budget input[type="url"],
.theme-budget input[type="date"],
.theme-budget input[type="datetime-local"],
.theme-budget input:not([type]),
.theme-budget textarea,
.theme-budget select,
.theme-millennial input[type="text"],
.theme-millennial input[type="email"],
.theme-millennial input[type="tel"],
.theme-millennial input[type="number"],
.theme-millennial input[type="password"],
.theme-millennial input[type="search"],
.theme-millennial input[type="url"],
.theme-millennial input[type="date"],
.theme-millennial input[type="datetime-local"],
.theme-millennial input:not([type]),
.theme-millennial textarea,
.theme-millennial select {
  background-color: var(--c-surface2);
  color: var(--c-text);
  border-color: var(--c-border);
}

/* Chrome / Edge autofill: the browser yellow override re-hides text.
   Force fill color + background to match the active theme. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
select:-webkit-autofill {
  -webkit-text-fill-color: var(--c-text);
  -webkit-box-shadow: 0 0 0 1000px var(--c-surface) inset;
  caret-color: var(--c-text);
  transition: background-color 9999s ease-in-out 0s;
}
.theme-genz input:-webkit-autofill,
.theme-genz input:-webkit-autofill:hover,
.theme-genz input:-webkit-autofill:focus,
.theme-genz input:-webkit-autofill:active,
.theme-genz textarea:-webkit-autofill,
.theme-genz select:-webkit-autofill,
.theme-budget input:-webkit-autofill,
.theme-budget input:-webkit-autofill:hover,
.theme-budget input:-webkit-autofill:focus,
.theme-budget input:-webkit-autofill:active,
.theme-budget textarea:-webkit-autofill,
.theme-budget select:-webkit-autofill,
.theme-millennial input:-webkit-autofill,
.theme-millennial input:-webkit-autofill:hover,
.theme-millennial input:-webkit-autofill:focus,
.theme-millennial input:-webkit-autofill:active,
.theme-millennial textarea:-webkit-autofill,
.theme-millennial select:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px var(--c-surface2) inset;
}

/* ── Badge pulse ───────────────────────────── */
.badge-pulse::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: rgba(16, 185, 129, 0.4);
  animation: pulse-ring 1.5s ease-out infinite;
}

/* ── CTA pulse (Jay 2026-07-09, $1-trial pill) ────────────────────────────
   A soft outer-ring pulse to draw the eye to a CTA — deliberately NOT a rapid
   color-flash. On a paid-trial CTA ($1 -> full price), a blinking/flashing
   treatment reads as manufactured urgency (FTC/CFPB dark-pattern territory
   for negative-option trials); this is the same restrained, ring-fade
   language already used for .badge-pulse, sized to a pill instead of a dot.
   Respects prefers-reduced-motion (WCAG 2.3.3) — the ring simply doesn't
   render for users who've asked for less motion, no layout shift either way. */
.cta-pulse { position: relative; }
.cta-pulse::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.55);
  animation: cta-pulse-ring 2.2s cubic-bezier(0.4,0,0.6,1) infinite;
  pointer-events: none;
}
@keyframes cta-pulse-ring {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.45); }
  70%  { box-shadow: 0 0 0 10px rgba(16, 185, 129, 0); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .cta-pulse::before { animation: none; }
}

/* ── Tooltip ───────────────────────────────── */
.tooltip {
  position: relative;
}
.tooltip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: #1e293b;
  color: #fff;
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 6px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}
.tooltip:hover::after { opacity: 1; }

/* ── Admin sidebar active state ────────────── */
.admin-nav.active {
  background: rgba(255,255,255,0.12);
  color: #fff;
}

/* ── Table ─────────────────────────────────── */
table { border-collapse: collapse; width: 100%; }
th { font-weight: 600; }

/* ── Responsive nav ────────────────────────── */
@media (max-width: 768px) {
  .hero-text { font-size: 2.25rem; }
}

/* ── Upload area drag active ───────────────── */
#upload-area.drag-active {
  border-color: #1E6FB8; /* BL-211: navy brand */
  background: #eef3f9;
}

/* ── Score meter bar gradient ──────────────── */
.score-meter-fill {
  background: linear-gradient(90deg, #ef4444, #f59e0b 40%, #10b981 80%);
}

/* ── Pricing card toggle ───────────────────── */
#billing-dot {
  transition: left 0.2s ease;
}

/* ── Notification dot ──────────────────────── */
.notif-dot {
  width: 8px; height: 8px;
  background: #ef4444;
  border-radius: 50%;
  position: absolute;
  top: -2px; right: -2px;
}

/* ── Footer gradient ───────────────────────── */
footer.footer-gradient {
  background: linear-gradient(180deg, #0d1f36 0%, #081526 100%);
}

/* ── Shimmer loading effect ────────────────── */
@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.skeleton {
  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 8px;
}

/* ── Confetti-like success state ───────────── */
.success-glow {
  box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
  animation: success-pulse 1s ease-out;
}
@keyframes success-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
  70%  { box-shadow: 0 0 0 20px rgba(16, 185, 129, 0); }
  100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
}

/* ── Drag-and-drop file upload ─────────────── */
#upload-area {
  transition: all 0.2s ease;
}

/* ── Chart container ───────────────────────── */
canvas {
  max-width: 100%;
}

/* ── Gen Z dark theme overrides ────────────── */
.theme-genz ::-webkit-scrollbar-track { background: #18181F; }
.theme-genz ::-webkit-scrollbar-thumb { background: #2A2A38; border-radius: 99px; }
.theme-genz ::-webkit-scrollbar-thumb:hover { background: #3A3A50; }

/* ── Ticker / marquee ───────────────────────── */
@keyframes ticker {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
#ticker { will-change: transform; }

/* ── Gen Z glassmorphism card ───────────────── */
.gz-card {
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.gz-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 16px 48px rgba(0,0,0,0.4);
}

/* ── Score arc SVG transition ───────────────── */
#score-arc {
  transition: stroke-dashoffset 1.5s cubic-bezier(0.4,0,0.2,1);
}

/* ── Neon text glow utilities ───────────────── */
.neon-teal   { text-shadow: 0 0 20px rgba(0,212,170,0.6); }
.neon-violet { text-shadow: 0 0 20px rgba(124,58,237,0.6); }
.neon-lime   { text-shadow: 0 0 20px rgba(132,204,22,0.6); }

/* ── Floating glow orbs (Gen Z hero) ────────── */
.gz-orb {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(0px);
}

/* ── FIX-3 (UX_STABILITY_FIXES): reserve Font Awesome icon slots ──────────────
   Empty <i class="fas …"> collapses to ~0 width before the webfont loads then
   expands, nudging text (the "twitch"). FA6 already sets icons display:inline-block,
   so min-width:1em alone reserves the slot. CRITICAL: do NOT set `display` here —
   an `i.fas` selector (specificity 0,1,1) beats Tailwind's `.hidden` (0,1,0) and
   would force-show every spinner/toggle icon hidden via `.hidden` (this regressed
   the signup Continue spinner — it spun forever; fixed 2026-06-22). min-width never
   affects `display`, so `.hidden` icons stay hidden. Solid woff2 preloaded in baseHead.ts. */
i.fas, i.far, i.fal, i.fab,
i.fa-solid, i.fa-regular, i.fa-brands {
  min-width: 1em;
}

/* ── Critical above-the-fold utilities — interim until FIX-1 precompiles Tailwind
   (BL-211). These Tailwind utilities are NOT in this hand-rolled sheet and otherwise
   arrive only with the runtime Play CDN, a frame after first paint. That late arrival
   is why the sticky navbar scrolled away on first load but stuck on a warm revisit,
   and why `hidden` elements (spinners, mobile menu, member links) flashed visible
   before the CDN compiled. Porting just these layout-critical, colour-independent
   ones into the render-blocking sheet fixes both without the full precompile (which
   is blocked on the navy-vs-emerald brand decision). Exact Tailwind definitions. */
.hidden { display: none; }
.sticky { position: -webkit-sticky; position: sticky; }
.top-0  { top: 0; }
.z-50   { z-index: 50; }

/* ── FIX-2 (UX_STABILITY_FIXES): pre-paint nav auth chrome ────────────────────
   baseHead.ts stamps html.is-authed from localStorage BEFORE first paint; these
   rules flip the DESKTOP navbar to the member set immediately so logged-in users
   don't watch the logged-out bar rebuild. Anon needs no rules (markup defaults
   ARE the logged-out chrome). The mobile menu is collapsed at load so it never
   flashes — left to navbar.ts. navbar.ts JS still runs and is the source of
   truth; these only remove the pre-JS flash (worst case = the current behaviour).
   gap/align on member-links mirror the md:items-center md:gap-1 the JS adds. */
html.is-authed #nav-login-btn,
html.is-authed #nav-signup-btn { display: none !important; }
@media (min-width: 768px) {
  html.is-authed #nav-public-links { display: none !important; }
  html.is-authed #nav-member-links { display: flex !important; align-items: center; gap: 0.25rem; }
  html.is-authed #nav-user-info    { display: flex !important; }
  html.is-authed #nav-notif-bell   { display: inline-flex !important; }
}

/* ── FIX-4 (UX_STABILITY_FIXES, BL-214): backdrop blur on desktop only ────────
   The sticky header's backdrop-filter:blur re-composites every scroll frame and
   stutters on mid/low-end Android (testers read the jank as "jumpy"). The nav's
   solid --c-surface background already covers mobile, so we only enable the blur
   at >=768px. Moved off the inline style in navbar.ts onto the .nav-blur class so
   it can be media-queried. */
@media (min-width: 768px) {
  .nav-blur {
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
  }
}

/* ═══ App shell (Play-Store TWA / installed-PWA prep, 2026-07-11) ═════════════
   Goal: the same pages feel native inside an Android WebView / Custom Tab
   shell (no browser chrome) while staying normal in a mobile browser.
   baseHead stamps two pre-paint <html> classes these rules key off:
     .is-authed      — wryr_user snapshot present (existing stamp)
     .is-standalone  — running inside an installed shell (display-mode media
                       query / navigator.standalone / window.Capacitor /
                       ?utm_source=pwa from the manifest start_url)          */

/* Sticky navbar clears the status bar / notch once viewport-fit=cover lets the
   page draw under it. env() is 0 in a browser tab, so no change there.
   NOTE: any sticky element that offsets by the navbar height must use
   calc(3.5rem + env(safe-area-inset-top, 0px)) — dashboard tab bar and the
   signup sticky header already do. */
nav.nav-blur { padding-top: env(safe-area-inset-top, 0px); }

/* iOS Safari (and some Android keyboards) auto-zoom the page when a focused
   input's font-size is under 16px. Every small-text form control gets 16px on
   phone widths. input.text-sm (0,1,1) out-specifies Tailwind's .text-sm
   (0,1,0), so no !important is needed and larger sizes (.text-lg OTP boxes)
   are untouched. */
@media (max-width: 767px) {
  /* min-width:0 pairs with the font bump: a flex-item input's default
     min-width:auto is its intrinsic ~20-character width, which GROWS with
     font-size and pushed edge-tight flex rows (landlord phone + "Send code")
     past the viewport. Allowing shrink keeps flex-row inputs in bounds. */
  input.text-sm, select.text-sm, textarea.text-sm { font-size: 1rem; min-width: 0; }
}

/* Touch targets: the navbar hamburger is w-10 (40px); bump to 44px on touch
   devices per the platform minimum. */
@media (pointer: coarse) {
  #nav-mobile-btn { width: 2.75rem; height: 2.75rem; }
}

/* ── Global bottom tab bar (appTabBar component) ─────────────────────────────
   Fixed bottom nav for the primary logged-in destinations (Home / Earn /
   Profile) on member pages other than /dashboard — the dashboard keeps its
   own richer .dash-bottom-nav. Same visual language as that bar (surface,
   hairline top border, icon-over-label buttons) so the shell reads as one
   app. Hidden on desktop and for logged-out visitors. */
.app-tab-bar { display: none; }
@media (max-width: 640px) {
  html.is-authed .app-tab-bar {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    z-index: 60;
    background: var(--c-surface, #ffffff);
    border-top: 1px solid var(--c-border, #e5e7eb);
    padding: 0.375rem 0.25rem calc(0.375rem + env(safe-area-inset-bottom, 0px));
    justify-content: space-around;
    align-items: flex-start;
  }
  .app-tab-btn {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.15rem;
    min-width: 3.25rem;
    min-height: 3rem;               /* >=48px total tap target */
    padding: 0.35rem 0.75rem;
    border-radius: 0.625rem;
    color: var(--c-text-muted, #94a3b8);
    font-size: 0.625rem; font-weight: 600;
    text-decoration: none;
  }
  .app-tab-btn i { font-size: 1.15rem; }
  .app-tab-btn.active { color: var(--c-primary, #0F4C81); }
  .app-tab-btn:active { background: var(--c-surface2, #f3f4f6); }

  /* Page content + floating widgets clear the fixed bar. Progressive
     enhancement: without :has() support the bar simply overlaps the last
     ~4rem of the page, same class of degradation as before this feature. */
  html.is-authed body:has(.app-tab-bar) { padding-bottom: calc(4.25rem + env(safe-area-inset-bottom, 0px)); }
  html.is-authed body:has(.app-tab-bar) #chat-widget,
  html.is-authed body:has(.app-tab-bar) #profile-toast {
    bottom: calc(4.5rem + env(safe-area-inset-bottom, 0px)) !important;
  }
}

/* ── Standalone shell chrome ─────────────────────────────────────────────────
   Inside the installed shell the app supplies its own navigation, so the
   4-column marketing footer goes away (legal links stay reachable from
   /profile and the dashboard billing card), and pull-to-refresh / scroll
   chaining are contained — a full page reload on an over-scroll reads as
   "wrapped webpage", not app. A normal browser tab keeps both behaviours. */
html.is-standalone { overscroll-behavior-y: contain; }
html.is-standalone body { overscroll-behavior-y: contain; }
html.is-standalone footer.wryr-footer { display: none; }

/* ── Skeleton shimmer — shared loading placeholder (no more full-page
   spinner flash on /profile; dashboard Array hosts have their own). ── */
.wryr-skel {
  position: relative; overflow: hidden;
  background: var(--c-surface2, #eef1f5);
  border-radius: 0.75rem;
}
.wryr-skel::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent);
  animation: wryr-skel-sweep 1.4s infinite;
}
@keyframes wryr-skel-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .wryr-skel::after { animation: none; } }

/* ── Mobile top-bar identity chip (Jay 2026-07-12) ───────────────────────────
   On phone/tablet widths a logged-in member had no visible account identity —
   the desktop user-info block is lg+ only and the bottom bars carry no name.
   The chip (avatar initial + first name + plan badge → /profile) renders in
   navbar.ts and shows only below lg for authed sessions; navbar JS fills it.
   display:inline-flex here (not a Tailwind class) so the pre-paint is-authed
   stamp controls it with zero flash and no JS display toggling. */
#nav-mob-identity { display: none; }
@media (max-width: 1023.98px) {
  html.is-authed #nav-mob-identity { display: inline-flex; }
}

/* Signed-in phone/tablet: collapse the navbar wordmark to the mark so the
   identity chip + hamburger fit the row (the full wordmark is ~250px and
   cannot share 360-390px with them). Logged-out keeps the full wordmark. */
@media (max-width: 1023.98px) {
  html.is-authed #nav-logo-wordmark { display: none; }
}
