/* Mix-matched palette — dark mode is "C · Skill Pick" (navy/blue), light mode
   is "B · Pulse" (warm paper white, violet→coral). The app root div (app.js)
   overrides the bg/text/accent/semantic tokens inline per the theme toggle;
   this fallback just keeps html/body and pre-mount paint from resolving to
   nothing. Card/border/field tokens and the overlay scale live here since
   app.js doesn't set those inline. */
:root {
  --bg0: #020617;
  --bg1: #0F172A;
  --text: #F8FAFC;
  --text-dim: #94A3B8;
  --text-faint: #475569;
  --accent: #2563EB;
  --accent2: #3B82F6;
  --success: #22C55E;
  --danger: #EF4444;
  --warning: #F59E0B;
  --card-bg: #141C30;
  --card-border: rgba(255,255,255,0.08);
  --field-bg: #1B2438;
  /* Neutral overlay scale used for card/border/divider tints throughout the
     app. Dark mode tints off Skill Pick's slate line color so hover/scroll
     surfaces read as part of the same navy system instead of generic white. */
  --w015: rgba(148,163,184,0.02);
  --w02: rgba(148,163,184,0.035);
  --w025: rgba(148,163,184,0.05);
  --w03: rgba(148,163,184,0.07);
  --w04: rgba(148,163,184,0.09);
  --w05: rgba(148,163,184,0.11);
  --w06: rgba(148,163,184,0.14);
  --w07: rgba(148,163,184,0.17);
  --w08: rgba(148,163,184,0.2);
  --w1: rgba(148,163,184,0.26);
  --w12: rgba(148,163,184,0.32);
}
:root[data-theme="light"] {
  --bg0: #FDF8F4;
  --bg1: #FFFFFF;
  --text: #1A1025;
  --text-dim: #7A6F8C;
  --text-faint: #B3A9C4;
  --accent: #8B3DFF;
  --accent2: #FF5C8A;
  --success: #16A34A;
  --danger: #E11D48;
  --warning: #D97706;
  --card-bg: #FFFFFF;
  --card-border: #E7ECF5;
  --field-bg: #EFF3FB;
  --w015: rgba(26,16,37,0.02);
  --w02: rgba(26,16,37,0.03);
  --w025: rgba(26,16,37,0.035);
  --w03: rgba(26,16,37,0.045);
  --w04: rgba(26,16,37,0.06);
  --w05: rgba(26,16,37,0.07);
  --w06: rgba(26,16,37,0.08);
  --w07: rgba(26,16,37,0.09);
  --w08: rgba(26,16,37,0.11);
  --w1: rgba(26,16,37,0.13);
  --w12: rgba(26,16,37,0.16);
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--bg0);
  color: var(--text);
  font-family: 'Inter', 'Segoe UI', sans-serif;
  /* Android's WebView scales every text size by the phone's own system font-scale
     setting (Settings > Display > Font size) unless a page opts out -- reported live as
     "text size is more for a mobile screen" on a device with that setting above 100%.
     Locks the app to its own designed sizes instead, same tradeoff most mobile web apps
     make for predictable layout over deferring to the OS setting. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Backstop, not a licence to overflow: one over-wide element (a long unbroken
     bank subject, a hard pixel min-width in a card) used to make the entire app
     scroll sideways, dragging the header and bottom nav with it and leaving
     action buttons stranded past the right edge. Individual components still fix
     their own layout properly — this only stops a miss becoming app-wide. */
  overflow-x: hidden;
  max-width: 100%;
}

body {
  background: var(--bg0);
}

/* Applied (app.js) only while the authenticated tab shell is mounted -- NOT on the
   landing/login screens, which have no inner scroll container of their own and rely on
   normal document scrolling. The authenticated shell DOES have its own dedicated
   overflow-y:auto content region (a sibling of the fixed header/bottom nav), but without
   this, focusing a textarea (e.g. Track's SMS-paste box) while the on-screen keyboard
   opens let some mobile browsers scroll the DOCUMENT itself to bring the focused element
   into view instead of scrolling within that inner region -- dragging the header
   off-screen with it and leaving raw content flush against the status bar. Locking the
   body itself as unscrollable forces every scroll gesture (including a browser's own
   focus-scroll-into-view behavior) into the one designated inner container.
   position:fixed (not just overflow:hidden) because iOS Safari's rubber-band scroll can
   still move an overflow:hidden body a few pixels; fixed positioning blocks it outright. */
body.app-scroll-locked {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

#root {
  min-height: 100vh;
}

/* Modal.js's card previously used an inline maxHeight:88vh -- vh is computed against the
   LAYOUT viewport, which many mobile browsers do NOT shrink when the on-screen keyboard
   opens (unlike the visual viewport), so a modal open while typing (e.g. Transaction
   Detail's Note field) kept sizing itself to the pre-keyboard height and extended down
   behind/under the keyboard, producing the reported distortion. dvh tracks the visual
   viewport and shrinks with the keyboard on browsers that support it; the plain vh line
   stays first as the fallback for any that don't (an unrecognized dvh value is simply
   ignored, leaving the vh rule in effect). Two declarations, not a min()/CSS var, because
   an unsupported unit inside min() invalidates the whole expression, not just that term. */
.modal-card {
  max-height: 88vh;
  max-height: 88dvh;
}

/* Bank mail is full of unbroken tokens — reference numbers, PRANs, long URLs,
   password rules run together — which no amount of flex tuning will wrap. Let
   them break mid-token inside cards rather than widening the card past the
   viewport and pushing its buttons out of reach. */
.card {
  overflow-wrap: anywhere;
  min-width: 0;
}

input,
select,
textarea,
button {
  font-family: inherit;
}

input,
select,
textarea {
  font-size: 16px !important;
  min-width: 0;
  max-width: 100%;
}

/* Inputs default to an intrinsic (content-based) width and won't shrink, which pushes
   cards wider than the phone and gets them clipped. Make field inputs fill their cell and
   shrink freely so nothing overflows. Inline width styles still override this where set. */
.field-input {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* Native <select> dropdown popups otherwise render as light text on the browser's default
   white popup — unreadable in dark mode. Force option colors to the theme so they stay legible. */
option {
  background: var(--field-bg);
  color: var(--text);
}

button {
  cursor: pointer;
}

button:focus,
input:focus,
select:focus,
textarea:focus {
  outline: none;
}

.tab-pill {
  border-radius: 999px;
  transition: background 0.15s, color 0.15s;
}

.tab-pill:hover {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* Bottom navigation — compact icon-over-label targets. Sized so all five
   destinations sit on one row down to a 320px viewport without wrapping. */
.bottom-nav-btn {
  -webkit-tap-highlight-color: transparent;
}

.bottom-nav-btn:hover .bottom-nav-icon {
  opacity: 1;
}

.bottom-nav-btn:active {
  transform: scale(0.94);
}

.bottom-nav-btn.is-primary:active .bottom-nav-icon {
  transform: scale(0.92);
}

@media screen and (max-width: 360px) {
  .bottom-nav-label {
    font-size: 9px !important;
  }
}

@media screen and (max-width: 640px) {
  .tab-pill {
    font-size: 12px;
    padding: 8px 10px !important;
  }
}

/* Sectioned-page layout (Profile) — a horizontal scrollable pill row on
   mobile, re-flowing into a left sidebar on wider viewports. Same buttons
   and state throughout; only the container CSS changes at the breakpoint. */
.profile-layout {
  display: block;
}

.section-nav {
  display: flex;
  flex-direction: row;
  /* Reported live as Profile/Subscription/Categories & Rules wrapping to two lines on a
     real device -- this said "horizontal scrollable pill row" above but actually used
     flex-wrap:wrap, which is a wrap row, not a scroll row. nowrap + overflow-x:auto is
     what the comment always described; a device too narrow to fit every pill now
     scrolls this row instead of wrapping it. */
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  gap: 8px;
  padding-bottom: 10px;
}

.section-nav-btn {
  flex-shrink: 0;
  white-space: nowrap;
}

@media (min-width: 900px) {
  .profile-layout {
    display: grid;
    grid-template-columns: 150px minmax(0, 1fr);
    gap: 18px;
    align-items: start;
  }
  .section-nav {
    flex-direction: column;
    overflow-x: visible;
    padding-bottom: 0;
  }
  .section-nav-btn {
    width: 100%;
    text-align: left;
    padding: 7px 10px;
    font-size: 12px;
  }
}

.profile-layout > div:last-child {
  min-width: 0;
  overflow-x: hidden;
}

/* Cards must be able to shrink inside flex/grid parents so their content can't force the
   page wider than the viewport. */
.card {
  min-width: 0;
}

.row-hover:hover {
  background: var(--w03) !important;
  cursor: pointer;
}

.del-btn {
  opacity: 0;
  transition: opacity 0.15s;
}

.row-hover:hover .del-btn {
  opacity: 1;
}

.btn-primary {
  background: linear-gradient(135deg, var(--accent), var(--accent2));
  border: none;
  color: #fff;
  border-radius: 12px;
  padding: 10px 22px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--accent) 34%, transparent);
  transition: transform 0.2s cubic-bezier(.2,.7,.2,1), box-shadow 0.2s;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 13px 32px color-mix(in srgb, var(--accent) 55%, transparent);
}

.btn-primary:active {
  transform: translateY(0) scale(0.98);
}

.btn-primary:disabled {
  background: var(--w08);
  color: var(--text-faint) !important;
  opacity: 1;
  transform: none;
  box-shadow: none;
  cursor: not-allowed;
}

/* Reusable inline loading spinner (e.g. Razorpay checkout buttons, while waiting on
   razorpayCreateSubscription + checkout.js's lazy load). Same look as #boot-splash's
   .bs-spin, generalized to a plain class so it can sit inside any button/label. */
.spinner-icon {
  display: inline-block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  animation: spinner-rotate 0.8s linear infinite;
  vertical-align: -2px;
}

@keyframes spinner-rotate {
  to { transform: rotate(360deg); }
}

/* Landing-style gradient text + entrance animation, reusable across screens. */
.grad-text {
  background: linear-gradient(100deg, var(--accent), var(--accent2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

@keyframes riseIn {
  from { opacity: 0; transform: translateY(14px); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: blur(0); }
}

.rise-in { animation: riseIn 0.6s cubic-bezier(.2,.75,.25,1) both; }

/* Secondary button — every non-primary action (Dismiss, Cancel, icon buttons in the
   header, toolbar actions) uses this. Previously styled as a near-duplicate of
   .btn-primary (same gradient fill, same white text) "so every fill reads
   uniformly" -- in practice that erased the whole point of having a second button
   style: a row with one primary + several ghost buttons had no visual hierarchy at
   all, and a genuinely destructive ghost button (e.g. Track's "Dismiss") read as
   the MOST prominent button in the row. Now a real low-emphasis style: transparent
   fill, a hairline border, body-color text -- present without competing with
   whatever the actual primary action in view is. See .btn-danger-outline below for
   destructive actions that need their own distinct (but still non-primary) weight. */
/* Shared collapse/expand chevron button (CollapseChevron in components.js) --
   Dashboard widgets, Accounts, Track's statement/transaction-email groups, and Admin
   statements all used a bare "▾" text glyph with no real hit target before this;
   option B from the design review (rounded square, same shape as the eye/privacy
   toggle button, just bigger) replaces all of them. 44x44px meets the standard
   minimum touch-target size for an icon-only button. */
.chevron-btn {
  width: 44px;
  height: 44px;
  border-radius: 12px;
  background: var(--w06);
  border: 1px solid var(--w1);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
  transition: background 0.15s, border-color 0.15s;
}
.chevron-btn:hover {
  background: var(--w08);
}
.chevron-btn:focus-visible {
  outline: 2px solid var(--accent2);
  outline-offset: 2px;
}
.chevron-btn svg {
  width: 18px;
  height: 18px;
  stroke: var(--text);
  transition: transform 0.2s ease;
}
.chevron-btn.is-open svg {
  transform: rotate(180deg);
}

.btn-ghost {
  background: transparent;
  border: 1px solid var(--card-border);
  color: var(--text);
  border-radius: 12px;
  padding: 8px 16px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.2s cubic-bezier(.2,.7,.2,1);
}

.btn-ghost:hover {
  background: var(--w04);
  border-color: var(--w12);
}

.btn-ghost:active {
  transform: scale(0.98);
}

.btn-ghost:disabled {
  color: var(--text-faint);
  opacity: 0.5;
  border-color: var(--card-border);
  background: transparent;
  cursor: not-allowed;
}

/* Destructive secondary action (Dismiss, Remove, Disconnect, Reject, Clear all) --
   same low-emphasis footprint as .btn-ghost, but a red hairline + red text marks it
   as the one button in the row that undoes/deletes something, without going as
   loud as a filled .btn-primary would (a destructive action shouldn't be the most
   visually inviting one on screen). */
.btn-danger-outline {
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  color: var(--danger);
  border-radius: 12px;
  padding: 8px 16px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.2s cubic-bezier(.2,.7,.2,1);
}

.btn-danger-outline:hover {
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: var(--danger);
}

.btn-danger-outline:active {
  transform: scale(0.98);
}

.btn-danger-outline:disabled {
  color: var(--text-faint);
  opacity: 0.5;
  border-color: var(--card-border);
  background: transparent;
  cursor: not-allowed;
}

/* Pill/segmented filter chips (e.g. Statistics 3M/6M/12M) — same interaction
   language as the buttons above, tuned for the accent-filled active state. */
.chip-btn {
  cursor: pointer;
  transition: transform 0.18s cubic-bezier(.2,.7,.2,1), box-shadow 0.18s, background 0.15s, border-color 0.15s, color 0.15s;
}

.chip-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px color-mix(in srgb, var(--accent) 32%, transparent);
}

.chip-btn:active {
  transform: translateY(0) scale(0.96);
  box-shadow: 0 3px 10px color-mix(in srgb, var(--accent) 22%, transparent);
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 16px;
  padding: 18px;
}

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes slideIn {
  from { opacity: 0; transform: translateX(16px); }
  to { opacity: 1; transform: translateX(0); }
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* Indeterminate progress fill -- used where there's an in-flight fetch but no real
   done/total count to show (e.g. the transaction-email sync, a single fast server
   round-trip rather than a per-message client loop like the statement fetch). */
@keyframes indeterminate-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
.indeterminate-bar {
  animation: indeterminate-slide 1.1s ease-in-out infinite;
}

::-webkit-scrollbar {
  width: 3px;
}

::-webkit-scrollbar-thumb {
  background: var(--w1);
  border-radius: 2px;
}

input, select, textarea {
  background: var(--field-bg);
  border: 1px solid var(--card-border);
  border-radius: 8px;
  color: var(--text);
  padding: 9px 12px;
  font-size: 13px;
}

select {
  appearance: none;
}
